]> begriffs open source - sa-parse/blob - src/csv_parser.c
Simplify
[sa-parse] / src / csv_parser.c
1 #include "csv_parser.h"
2 #include "csv.tab.h"
3 #include <stdlib.h>
4 #include <string.h>
5
6 /* External declarations from Bison/Flex - updated for reentrant API */
7 typedef void *yyscan_t;
8
9 extern int csvlex_init(yyscan_t *scanner);
10 extern int csvlex_destroy(yyscan_t scanner);
11 extern void csvset_in(FILE *file, yyscan_t scanner);
12 extern int csvparse(void *parser_state, yyscan_t scanner);
13
14 /* CSV parser structure - now contains all the state that was previously global */
15 struct csv_parser {
16     char error_message[256];
17     bool has_error;
18     csv_document_t *current_document;
19     bool parse_error;
20     char parse_error_message[256];
21     int error_line;
22     int error_column;
23 };
24
25 /* Helper functions */
26 static csv_field_t *convert_field_list(field_node_t *field_nodes);
27 static csv_record_t *create_record_from_fields(field_node_t *field_nodes);
28 static void free_document_internal(csv_document_t *doc);
29
30 /* Functions called by the Bison parser */
31 void csv_parser_add_record(void *parser_state, field_node_t *fields);
32 void csv_parser_set_error_with_location(void *parser_state, const char *error, int line, int column);
33
34 /* Implementation */
35
36 csv_parser_t *csv_parser_create(void)
37 {
38     csv_parser_t *parser = malloc(sizeof(csv_parser_t));
39     if (!parser) {
40         return NULL;
41     }
42     
43     parser->error_message[0] = '\0';
44     parser->has_error = false;
45     parser->current_document = NULL;
46     parser->parse_error = false;
47     parser->parse_error_message[0] = '\0';
48     parser->error_line = 0;
49     parser->error_column = 0;
50     
51     return parser;
52 }
53
54 void csv_parser_destroy(csv_parser_t *parser)
55 {
56     if (parser) {
57         free(parser);
58     }
59 }
60
61 csv_error_t csv_parser_parse_string(csv_parser_t *parser, const char *input, csv_document_t **document)
62 {
63     if (!parser || !input || !document) {
64         return CSV_ERROR_INVALID_PARAMETER;
65     }
66     
67     *document = NULL;
68     
69     /* Create a temporary file from the string input */
70     FILE *temp_file = tmpfile();
71     if (!temp_file) {
72         strncpy(parser->error_message, "Failed to create temporary file", sizeof(parser->error_message) - 1);
73         parser->has_error = true;
74         return CSV_ERROR_IO;
75     }
76     
77     /* Write input to temporary file */
78     if (fputs(input, temp_file) == EOF) {
79         fclose(temp_file);
80         strncpy(parser->error_message, "Failed to write to temporary file", sizeof(parser->error_message) - 1);
81         parser->has_error = true;
82         return CSV_ERROR_IO;
83     }
84     
85     /* Reset file position to beginning */
86     rewind(temp_file);
87     
88     /* Parse from the temporary file */
89     csv_error_t result = csv_parser_parse_file(parser, temp_file, document);
90     fclose(temp_file);
91     
92     return result;
93 }
94
95 csv_error_t csv_parser_parse_file(csv_parser_t *parser, FILE *file, csv_document_t **document)
96 {
97     if (!parser || !file || !document) {
98         return CSV_ERROR_INVALID_PARAMETER;
99     }
100     
101     *document = NULL;
102     parser->has_error = false;
103     parser->error_message[0] = '\0';
104     
105     /* Initialize parser state */
106     parser->current_document = malloc(sizeof(csv_document_t));
107     if (!parser->current_document) {
108         strncpy(parser->error_message, "Memory allocation failed", sizeof(parser->error_message) - 1);
109         parser->has_error = true;
110         return CSV_ERROR_MEMORY;
111     }
112     
113     parser->current_document->records = NULL;
114     parser->current_document->record_count = 0;
115     parser->parse_error = false;
116     parser->parse_error_message[0] = '\0';
117     parser->error_line = 0;
118     parser->error_column = 0;
119     
120     /* Initialize the reentrant scanner */
121     yyscan_t scanner;
122     int scanner_init_result = csvlex_init(&scanner);
123     if (scanner_init_result != 0) {
124         free(parser->current_document);
125         parser->current_document = NULL;
126         return CSV_ERROR_MEMORY;
127     }
128     
129     /* Set up the lexer input */
130     csvset_in(file, scanner);
131     
132     /* Parse the input */
133     int parse_result = csvparse(parser, scanner);
134     
135     /* Clean up lexer state */
136     csvlex_destroy(scanner);
137     
138     /* Check for errors */
139     if (parse_result != 0 || parser->parse_error) {
140         csv_error_t error_code;
141         
142         switch (parse_result) {
143             case 2:
144                 /* Memory exhaustion in parser */
145                 strncpy(parser->error_message, "Parser memory exhaustion", sizeof(parser->error_message) - 1);
146                 error_code = CSV_ERROR_MEMORY;
147                 break;
148                 
149             case 1:
150                 /* Parse error (syntax error or YYABORT) */
151                 if (parser->parse_error_message[0] != '\0') {
152                     strncpy(parser->error_message, parser->parse_error_message, sizeof(parser->error_message) - 1);
153                 } else {
154                     strncpy(parser->error_message, "Syntax error", sizeof(parser->error_message) - 1);
155                 }
156                 error_code = CSV_ERROR_PARSE;
157                 break;
158                 
159             default:
160                 /* Other non-zero return or parser->parse_error flag set */
161                 if (parser->parse_error_message[0] != '\0') {
162                     strncpy(parser->error_message, parser->parse_error_message, sizeof(parser->error_message) - 1);
163                 } else {
164                     strncpy(parser->error_message, "Parse error", sizeof(parser->error_message) - 1);
165                 }
166                 error_code = CSV_ERROR_PARSE;
167                 break;
168         }
169         
170         parser->has_error = true;
171         free_document_internal(parser->current_document);
172         parser->current_document = NULL;
173         return error_code;
174     }
175     
176     /* Success - transfer ownership of document */
177     *document = parser->current_document;
178     parser->current_document = NULL;
179     
180     return CSV_SUCCESS;
181 }
182
183 csv_error_info_t csv_parser_get_error_info(csv_parser_t *parser)
184 {
185     csv_error_info_t info = {0};
186     
187     if (!parser) {
188         info.message = "Invalid parser";
189         info.line = 0;
190         info.column = 0;
191         info.has_location = false;
192         return info;
193     }
194     
195     if (parser->has_error) {
196         info.message = parser->error_message;
197         info.line = parser->error_line;
198         info.column = parser->error_column;
199         info.has_location = (parser->error_line > 0);
200     } else {
201         info.message = "No error";
202         info.line = 0;
203         info.column = 0;
204         info.has_location = false;
205     }
206     
207     return info;
208 }
209
210 void csv_document_free(csv_document_t *document)
211 {
212     free_document_internal(document);
213 }
214
215 const char *csv_error_string(csv_error_t error)
216 {
217     switch (error) {
218         case CSV_SUCCESS:
219             return "Success";
220         case CSV_ERROR_MEMORY:
221             return "Memory allocation error";
222         case CSV_ERROR_PARSE:
223             return "Parse error";
224         case CSV_ERROR_INVALID_PARAMETER:
225             return "Invalid parameter";
226         case CSV_ERROR_IO:
227             return "I/O error";
228         default:
229             return "Unknown error";
230     }
231 }
232
233 /* Data access helper functions */
234
235 csv_record_t *csv_document_get_first_record(const csv_document_t *document)
236 {
237     return document ? document->records : NULL;
238 }
239
240 csv_record_t *csv_record_get_next(const csv_record_t *record)
241 {
242     return record ? record->next : NULL;
243 }
244
245 csv_field_t *csv_record_get_first_field(const csv_record_t *record)
246 {
247     return record ? record->fields : NULL;
248 }
249
250 csv_field_t *csv_field_get_next(const csv_field_t *field)
251 {
252     return field ? field->next : NULL;
253 }
254
255 const char *csv_field_get_value(const csv_field_t *field)
256 {
257     return field ? field->value : NULL;
258 }
259
260 /* Internal helper functions */
261
262 static csv_field_t *convert_field_list(field_node_t *field_nodes)
263 {
264     if (!field_nodes) {
265         return NULL;
266     }
267     
268     csv_field_t *first_field = NULL;
269     csv_field_t *last_field = NULL;
270     
271     field_node_t *current_node = field_nodes;
272     while (current_node) {
273         csv_field_t *field = malloc(sizeof(csv_field_t));
274         if (!field) {
275             /* Clean up already allocated fields */
276             while (first_field) {
277                 csv_field_t *next = first_field->next;
278                 free(first_field->value);
279                 free(first_field);
280                 first_field = next;
281             }
282             return NULL;
283         }
284         
285         /* Copy content (take ownership) */
286         field->value = current_node->content;
287         current_node->content = NULL; /* Transfer ownership */
288         field->next = NULL;
289         
290         if (!first_field) {
291             first_field = field;
292             last_field = field;
293         } else {
294             last_field->next = field;
295             last_field = field;
296         }
297         
298         current_node = current_node->next;
299     }
300     
301     return first_field;
302 }
303
304 static csv_record_t *create_record_from_fields(field_node_t *field_nodes)
305 {
306     csv_record_t *record = malloc(sizeof(csv_record_t));
307     if (!record) {
308         return NULL;
309     }
310     
311     record->fields = convert_field_list(field_nodes);
312     record->next = NULL;
313     
314     /* Count fields */
315     record->field_count = 0;
316     csv_field_t *current = record->fields;
317     while (current) {
318         record->field_count++;
319         current = current->next;
320     }
321     
322     return record;
323 }
324
325 static void free_document_internal(csv_document_t *doc)
326 {
327     if (!doc) {
328         return;
329     }
330     
331     /* Free records */
332     csv_record_t *record = doc->records;
333     while (record) {
334         csv_record_t *next_record = record->next;
335         
336         csv_field_t *field = record->fields;
337         while (field) {
338             csv_field_t *next_field = field->next;
339             free(field->value);
340             free(field);
341             field = next_field;
342         }
343         
344         free(record);
345         record = next_record;
346     }
347     
348     free(doc);
349 }
350
351 /* Functions called by the Bison parser */
352
353 void csv_parser_add_record(void *parser_state, field_node_t *fields)
354 {
355     csv_parser_t *parser = (csv_parser_t *)parser_state;
356     if (!parser || !parser->current_document || parser->parse_error) {
357         return;
358     }
359     
360     /* Skip empty records (single empty field) */
361     if (fields && !fields->next && fields->content && fields->content[0] == '\0') {
362         return;
363     }
364     
365     csv_record_t *record = create_record_from_fields(fields);
366     if (!record) {
367         parser->parse_error = true;
368         strncpy(parser->parse_error_message, "Memory allocation failed while creating record", sizeof(parser->parse_error_message) - 1);
369         return;
370     }
371     
372     /* Add to document */
373     if (!parser->current_document->records) {
374         parser->current_document->records = record;
375     } else {
376         /* Find the last record and append */
377         csv_record_t *last = parser->current_document->records;
378         while (last->next) {
379             last = last->next;
380         }
381         last->next = record;
382     }
383     
384     parser->current_document->record_count++;
385 }
386
387 void csv_parser_set_error_with_location(void *parser_state, const char *error, int line, int column)
388 {
389     csv_parser_t *parser = (csv_parser_t *)parser_state;
390     if (!parser) {
391         return;
392     }
393     
394     parser->parse_error = true;
395     parser->error_line = line;
396     parser->error_column = column;
397     if (error) {
398         strncpy(parser->parse_error_message, error, sizeof(parser->parse_error_message) - 1);
399         parser->parse_error_message[sizeof(parser->parse_error_message) - 1] = '\0';
400     }
401 }
402