]> begriffs open source - sa-parse/blob - src/csv.y
"pure" parser
[sa-parse] / src / csv.y
1 %{
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 extern int csv_lineno;
7 extern char* csv_text;
8
9 void csv_error(const char* s);
10
11 /* Data structures for CSV representation */
12 typedef struct field_node {
13     char* content;
14     struct field_node* next;
15 } field_node_t;
16
17 typedef struct record_node {
18     field_node_t* fields;
19     struct record_node* next;
20 } record_node_t;
21
22 typedef struct csv_file {
23     record_node_t* header;
24     record_node_t* records;
25 } csv_file_t;
26
27 /* Global variables */
28 csv_file_t* csv_data = NULL;
29
30 /* Helper functions */
31 field_node_t* create_field(char* content);
32 record_node_t* create_record(field_node_t* fields);
33 field_node_t* append_field(field_node_t* list, field_node_t* new_field);
34 record_node_t* append_record(record_node_t* list, record_node_t* new_record);
35 void print_csv(csv_file_t* csv);
36 void free_csv(csv_file_t* csv);
37 %}
38
39 %union {
40     char* str;
41     struct field_node* field_list;
42     struct record_node* record_node;
43     struct csv_file* csv_file;
44 }
45
46 %define api.prefix {csv_}
47 %define api.pure full
48
49 %token <str> FIELD_TOK
50 %token COMMA_TOK
51 %token CRLF_TOK
52
53 %type <field_list> field_list
54 %type <field_list> field_opt
55 %type <record_node> record
56 %type <record_node> consumed_record
57 %type <csv_file> file
58
59 %start file
60
61 %%
62
63 file:
64     consumed_record
65     {
66         csv_data = malloc(sizeof(csv_file_t));
67         if (!csv_data) YYERROR;
68         csv_data->header = NULL;
69         csv_data->records = $1;
70         $$ = csv_data;
71     }
72     | file CRLF_TOK consumed_record
73     {
74         csv_data->records = append_record(csv_data->records, $3);
75         $$ = csv_data;
76     }
77     ;
78
79 consumed_record:
80     record
81     {
82         $$ = $1;
83     }
84     ;
85
86 record:
87     field_list
88     {
89         $$ = create_record($1);
90         if (!$$) YYERROR;
91     }
92     ;
93
94 field_list:
95     field_opt
96     {
97         $$ = $1;
98     }
99     | field_list COMMA_TOK field_opt
100     {
101         $$ = append_field($1, $3);
102     }
103     ;
104
105 field_opt:
106     FIELD_TOK
107     {
108         $$ = create_field($1);
109         if (!$$) YYERROR;
110     }
111     | %empty
112     {
113         /* Empty field - create field with empty string */
114         char* empty_content = strdup("");
115         if (!empty_content) YYERROR;
116         $$ = create_field(empty_content);
117         if (!$$) YYERROR;
118     }
119     ;
120
121 %%
122
123 void csv_error(const char* s) {
124     fprintf(stderr, "Parse error at line %d: %s\n", csv_lineno, s);
125     if (csv_text) {
126         fprintf(stderr, "Near token: %s\n", csv_text);
127     }
128 }
129
130 field_node_t* create_field(char* content) {
131     field_node_t* field = malloc(sizeof(field_node_t));
132     if (!field) return NULL;
133     /* Use the content directly without duplicating since lexer already allocated it */
134     field->content = content;
135     if (!field->content) {
136         field->content = strdup("");
137         if (!field->content) {
138             free(field);
139             return NULL;
140         }
141     }
142     field->next = NULL;
143     return field;
144 }
145
146 record_node_t* create_record(field_node_t* fields) {
147     record_node_t* record = malloc(sizeof(record_node_t));
148     if (!record) return NULL;
149     record->fields = fields;
150     record->next = NULL;
151     return record;
152 }
153
154 field_node_t* append_field(field_node_t* list, field_node_t* new_field) {
155     if (!list) return new_field;
156     
157     field_node_t* current = list;
158     while (current->next) {
159         current = current->next;
160     }
161     current->next = new_field;
162     return list;
163 }
164
165 record_node_t* append_record(record_node_t* list, record_node_t* new_record) {
166     if (!list) return new_record;
167     
168     record_node_t* current = list;
169     while (current->next) {
170         current = current->next;
171     }
172     current->next = new_record;
173     return list;
174 }
175
176 void print_csv(csv_file_t* csv) {
177     if (!csv) return;
178     
179     if (csv->header) {
180         printf("Header: ");
181         field_node_t* field = csv->header->fields;
182         while (field) {
183             printf("[%s]", field->content ? field->content : "");
184             if (field->next) printf(", ");
185             field = field->next;
186         }
187         printf("\n");
188     }
189     
190     record_node_t* record = csv->records;
191     int record_num = 1;
192     while (record) {
193         printf("Record %d: ", record_num++);
194         field_node_t* field = record->fields;
195         while (field) {
196             printf("[%s]", field->content ? field->content : "");
197             if (field->next) printf(", ");
198             field = field->next;
199         }
200         printf("\n");
201         record = record->next;
202     }
203 }
204
205 void free_field_list(field_node_t* fields) {
206     while (fields) {
207         field_node_t* next = fields->next;
208         free(fields->content);
209         free(fields);
210         fields = next;
211     }
212 }
213
214 void free_record_list(record_node_t* records) {
215     while (records) {
216         record_node_t* next = records->next;
217         free_field_list(records->fields);
218         free(records);
219         records = next;
220     }
221 }
222
223 void free_csv(csv_file_t* csv) {
224     if (!csv) return;
225     
226     if (csv->header) {
227         free_field_list(csv->header->fields);
228         free(csv->header);
229     }
230     
231     free_record_list(csv->records);
232     free(csv);
233 }
234
235 int main() {
236     printf("CSV Parser - Enter CSV data (Ctrl+D to end):\n");
237     
238     int result = csv_parse();
239     
240     if (result == 0) {
241         printf("\nParsing successful!\n\n");
242         print_csv(csv_data);
243     } else {
244         printf("\nParsing failed!\n");
245     }
246     
247     free_csv(csv_data);
248     return result;
249 }