]> begriffs open source - sa-parse/blob - src/csv.y
Fix empty field parsing and resolve bison conflicts
[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_opt
53 %type <record_node> record
54 %type <record_node> consumed_record
55 %type <csv_file> file
56
57 %start file
58
59 %%
60
61 file:
62     consumed_record
63     {
64         csv_data = malloc(sizeof(csv_file_t));
65         if (!csv_data) YYERROR;
66         csv_data->header = NULL;
67         csv_data->records = $1;
68         $$ = csv_data;
69     }
70     | file CRLF_TOK consumed_record
71     {
72         csv_data->records = append_record(csv_data->records, $3);
73         $$ = csv_data;
74     }
75     ;
76
77 consumed_record:
78     record
79     {
80         $$ = $1;
81     }
82     ;
83
84 record:
85     field_list
86     {
87         $$ = create_record($1);
88         if (!$$) YYERROR;
89     }
90     ;
91
92 field_list:
93     field_opt
94     {
95         $$ = $1;
96     }
97     | field_list COMMA_TOK field_opt
98     {
99         $$ = append_field($1, $3);
100     }
101     ;
102
103 field_opt:
104     FIELD_TOK
105     {
106         $$ = create_field($1);
107         if (!$$) YYERROR;
108     }
109     | %empty
110     {
111         /* Empty field - create field with empty string */
112         char* empty_content = strdup("");
113         if (!empty_content) YYERROR;
114         $$ = create_field(empty_content);
115         if (!$$) YYERROR;
116     }
117     ;
118
119 %%
120
121 void yyerror(const char* s) {
122     fprintf(stderr, "Parse error at line %d: %s\n", yylineno, s);
123     if (yytext) {
124         fprintf(stderr, "Near token: %s\n", yytext);
125     }
126 }
127
128 field_node_t* create_field(char* content) {
129     field_node_t* field = malloc(sizeof(field_node_t));
130     if (!field) return NULL;
131     /* Use the content directly without duplicating since lexer already allocated it */
132     field->content = content;
133     if (!field->content) {
134         field->content = strdup("");
135         if (!field->content) {
136             free(field);
137             return NULL;
138         }
139     }
140     field->next = NULL;
141     return field;
142 }
143
144 record_node_t* create_record(field_node_t* fields) {
145     record_node_t* record = malloc(sizeof(record_node_t));
146     if (!record) return NULL;
147     record->fields = fields;
148     record->next = NULL;
149     return record;
150 }
151
152 field_node_t* append_field(field_node_t* list, field_node_t* new_field) {
153     if (!list) return new_field;
154     
155     field_node_t* current = list;
156     while (current->next) {
157         current = current->next;
158     }
159     current->next = new_field;
160     return list;
161 }
162
163 record_node_t* append_record(record_node_t* list, record_node_t* new_record) {
164     if (!list) return new_record;
165     
166     record_node_t* current = list;
167     while (current->next) {
168         current = current->next;
169     }
170     current->next = new_record;
171     return list;
172 }
173
174 void print_csv(csv_file_t* csv) {
175     if (!csv) return;
176     
177     if (csv->header) {
178         printf("Header: ");
179         field_node_t* field = csv->header->fields;
180         while (field) {
181             printf("[%s]", field->content ? field->content : "");
182             if (field->next) printf(", ");
183             field = field->next;
184         }
185         printf("\n");
186     }
187     
188     record_node_t* record = csv->records;
189     int record_num = 1;
190     while (record) {
191         printf("Record %d: ", record_num++);
192         field_node_t* field = record->fields;
193         while (field) {
194             printf("[%s]", field->content ? field->content : "");
195             if (field->next) printf(", ");
196             field = field->next;
197         }
198         printf("\n");
199         record = record->next;
200     }
201 }
202
203 void free_field_list(field_node_t* fields) {
204     while (fields) {
205         field_node_t* next = fields->next;
206         free(fields->content);
207         free(fields);
208         fields = next;
209     }
210 }
211
212 void free_record_list(record_node_t* records) {
213     while (records) {
214         record_node_t* next = records->next;
215         free_field_list(records->fields);
216         free(records);
217         records = next;
218     }
219 }
220
221 void free_csv(csv_file_t* csv) {
222     if (!csv) return;
223     
224     if (csv->header) {
225         free_field_list(csv->header->fields);
226         free(csv->header);
227     }
228     
229     free_record_list(csv->records);
230     free(csv);
231 }
232
233 int main() {
234     printf("CSV Parser - Enter CSV data (Ctrl+D to end):\n");
235     
236     int result = yyparse();
237     
238     if (result == 0) {
239         printf("\nParsing successful!\n\n");
240         print_csv(csv_data);
241     } else {
242         printf("\nParsing failed!\n");
243     }
244     
245     free_csv(csv_data);
246     return result;
247 }