]> begriffs open source - sa-parse/blob - src/csv.y
Basics CSV parser and test cases
[sa-parse] / src / csv.y
1 %{
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 extern int yylex();
7 extern int yylineno;
8 extern char* yytext;
9
10 void yyerror(const char* s);
11
12 /* Data structures for CSV representation */
13 typedef struct field_node {
14     char* content;
15     struct field_node* next;
16 } field_node_t;
17
18 typedef struct record_node {
19     field_node_t* fields;
20     struct record_node* next;
21 } record_node_t;
22
23 typedef struct csv_file {
24     record_node_t* header;
25     record_node_t* records;
26 } csv_file_t;
27
28 /* Global variables */
29 csv_file_t* csv_data = NULL;
30
31 /* Helper functions */
32 field_node_t* create_field(char* content);
33 record_node_t* create_record(field_node_t* fields);
34 field_node_t* append_field(field_node_t* list, field_node_t* new_field);
35 record_node_t* append_record(record_node_t* list, record_node_t* new_record);
36 void print_csv(csv_file_t* csv);
37 void free_csv(csv_file_t* csv);
38 %}
39
40 %union {
41     char* str;
42     struct field_node* field_list;
43     struct record_node* record_node;
44     struct csv_file* csv_file;
45 }
46
47 %token <str> FIELD_TOK
48 %token COMMA_TOK
49 %token CRLF_TOK
50
51 %type <field_list> field_list
52 %type <field_list> field
53 %type <field_list> header
54 %type <record_node> record
55 %type <record_node> record_list
56 %type <csv_file> file
57
58 %start file
59
60 %%
61
62 file:
63     /* empty file */
64     {
65         csv_data = malloc(sizeof(csv_file_t));
66         if (!csv_data) YYERROR;
67         csv_data->header = NULL;
68         csv_data->records = NULL;
69         $$ = csv_data;
70     }
71     | header
72     {
73         csv_data = malloc(sizeof(csv_file_t));
74         if (!csv_data) YYERROR;
75         csv_data->header = create_record($1);
76         if (!csv_data->header) {
77             free(csv_data);
78             YYERROR;
79         }
80         csv_data->records = NULL;
81         $$ = csv_data;
82     }
83     | header CRLF_TOK
84     {
85         csv_data = malloc(sizeof(csv_file_t));
86         if (!csv_data) YYERROR;
87         csv_data->header = create_record($1);
88         if (!csv_data->header) {
89             free(csv_data);
90             YYERROR;
91         }
92         csv_data->records = NULL;
93         $$ = csv_data;
94     }
95     | record_list
96     {
97         csv_data = malloc(sizeof(csv_file_t));
98         if (!csv_data) YYERROR;
99         csv_data->header = NULL;
100         csv_data->records = $1;
101         $$ = csv_data;
102     }
103     | record_list CRLF_TOK
104     {
105         csv_data = malloc(sizeof(csv_file_t));
106         if (!csv_data) YYERROR;
107         csv_data->header = NULL;
108         csv_data->records = $1;
109         $$ = csv_data;
110     }
111     | header CRLF_TOK record_list
112     {
113         csv_data = malloc(sizeof(csv_file_t));
114         if (!csv_data) YYERROR;
115         csv_data->header = create_record($1);
116         if (!csv_data->header) {
117             free(csv_data);
118             YYERROR;
119         }
120         csv_data->records = $3;
121         $$ = csv_data;
122     }
123     | header CRLF_TOK record_list CRLF_TOK
124     {
125         csv_data = malloc(sizeof(csv_file_t));
126         if (!csv_data) YYERROR;
127         csv_data->header = create_record($1);
128         if (!csv_data->header) {
129             free(csv_data);
130             YYERROR;
131         }
132         csv_data->records = $3;
133         $$ = csv_data;
134     }
135     ;
136
137 header:
138     field_list
139     {
140         $$ = $1;
141     }
142     ;
143
144 record_list:
145     record
146     {
147         $$ = $1;
148     }
149     | record_list CRLF_TOK record
150     {
151         $$ = append_record($1, $3);
152     }
153     ;
154
155 record:
156     field_list
157     {
158         $$ = create_record($1);
159         if (!$$) YYERROR;
160     }
161     ;
162
163 field_list:
164     field
165     {
166         $$ = $1;
167     }
168     | field_list COMMA_TOK field
169     {
170         $$ = append_field($1, $3);
171     }
172     ;
173
174 field:
175     FIELD_TOK
176     {
177         $$ = create_field($1);
178         if (!$$) YYERROR;
179     }
180     | /* empty field */
181     {
182         $$ = create_field("");
183         if (!$$) YYERROR;
184     }
185     ;
186
187 %%
188
189 void yyerror(const char* s) {
190     fprintf(stderr, "Parse error at line %d: %s\n", yylineno, s);
191     if (yytext) {
192         fprintf(stderr, "Near token: %s\n", yytext);
193     }
194 }
195
196 field_node_t* create_field(char* content) {
197     field_node_t* field = malloc(sizeof(field_node_t));
198     if (!field) return NULL;
199     field->content = content ? strdup(content) : strdup("");
200     if (!field->content) {
201         free(field);
202         return NULL;
203     }
204     field->next = NULL;
205     return field;
206 }
207
208 record_node_t* create_record(field_node_t* fields) {
209     record_node_t* record = malloc(sizeof(record_node_t));
210     if (!record) return NULL;
211     record->fields = fields;
212     record->next = NULL;
213     return record;
214 }
215
216 field_node_t* append_field(field_node_t* list, field_node_t* new_field) {
217     if (!list) return new_field;
218     
219     field_node_t* current = list;
220     while (current->next) {
221         current = current->next;
222     }
223     current->next = new_field;
224     return list;
225 }
226
227 record_node_t* append_record(record_node_t* list, record_node_t* new_record) {
228     if (!list) return new_record;
229     
230     record_node_t* current = list;
231     while (current->next) {
232         current = current->next;
233     }
234     current->next = new_record;
235     return list;
236 }
237
238 void print_csv(csv_file_t* csv) {
239     if (!csv) return;
240     
241     if (csv->header) {
242         printf("Header: ");
243         field_node_t* field = csv->header->fields;
244         while (field) {
245             printf("[%s]", field->content ? field->content : "");
246             if (field->next) printf(", ");
247             field = field->next;
248         }
249         printf("\n");
250     }
251     
252     record_node_t* record = csv->records;
253     int record_num = 1;
254     while (record) {
255         printf("Record %d: ", record_num++);
256         field_node_t* field = record->fields;
257         while (field) {
258             printf("[%s]", field->content ? field->content : "");
259             if (field->next) printf(", ");
260             field = field->next;
261         }
262         printf("\n");
263         record = record->next;
264     }
265 }
266
267 void free_field_list(field_node_t* fields) {
268     while (fields) {
269         field_node_t* next = fields->next;
270         free(fields->content);
271         free(fields);
272         fields = next;
273     }
274 }
275
276 void free_record_list(record_node_t* records) {
277     while (records) {
278         record_node_t* next = records->next;
279         free_field_list(records->fields);
280         free(records);
281         records = next;
282     }
283 }
284
285 void free_csv(csv_file_t* csv) {
286     if (!csv) return;
287     
288     if (csv->header) {
289         free_field_list(csv->header->fields);
290         free(csv->header);
291     }
292     
293     free_record_list(csv->records);
294     free(csv);
295 }
296
297 int main() {
298     printf("CSV Parser - Enter CSV data (Ctrl+D to end):\n");
299     
300     int result = yyparse();
301     
302     if (result == 0) {
303         printf("\nParsing successful!\n\n");
304         print_csv(csv_data);
305     } else {
306         printf("\nParsing failed!\n");
307     }
308     
309     free_csv(csv_data);
310     return result;
311 }