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