1 /* csv.y - CSV parser using Bison */
9 /* Simple linked list structures for CSV parsing */
10 typedef struct field_node {
12 struct field_node *next;
15 typedef struct record_node {
17 struct record_node *next;
22 /* Function declarations */
24 int yyerror(const char *s);
25 bool one_empty_field(field_node_t *fields);
26 field_node_t *create_field(char *content);
27 void append_field_to_list(field_node_t **head, char *content);
28 void free_field_list(field_node_t *fields);
29 size_t count_fields(field_node_t *fields);
31 /* Interface with the CSV parser library */
32 void csv_parser_add_record(field_node_t *fields);
33 void csv_parser_set_error(const char *error);
47 /* Bison memory management - automatically free memory on errors */
48 %destructor { free($$); } <str>
49 %destructor { free_field_list($$); } <fields>
55 | file CRLF consumed_record
58 /* A record can be constructed in two ways, but we want to
59 run the same side effect for either case. We add an
60 intermediate non-terminal symbol "consumed_record" just
61 to perform the action. In library code, this would be a
62 good place to send the the record to a callback function. */
66 /* a record comprised of exactly one blank field is a
67 blank record, which we can skip */
68 if (!one_empty_field($1))
70 /* Send the record to the parser library */
71 csv_parser_add_record($1);
73 /* Memory is automatically freed by %destructor */
79 /* Create first field node */
80 $$ = create_field($1);
82 | record ',' field.opt {
83 /* Append field to existing list */
84 append_field_to_list(&$1, $3);
90 %empty { $$ = calloc(1,1); }
96 field_node_t *create_field(char *content)
98 field_node_t *field = malloc(sizeof(field_node_t));
104 field->content = content; /* Take ownership of the string */
109 void append_field_to_list(field_node_t **head, char *content)
111 field_node_t *new_field = create_field(content);
112 if (!new_field) return;
119 /* Find the last field in the list */
120 field_node_t *current = *head;
121 while (current->next) {
122 current = current->next;
124 current->next = new_field;
127 void free_field_list(field_node_t *fields)
129 field_node_t *current = fields;
131 field_node_t *next = current->next;
132 free(current->content);
138 size_t count_fields(field_node_t *fields)
141 field_node_t *current = fields;
144 current = current->next;
149 bool one_empty_field(field_node_t *fields)
151 return fields && !fields->next &&
152 fields->content && fields->content[0] == '\0';
155 int yyerror(const char *s)
157 csv_parser_set_error(s);