%{ #include #include #include extern int csv_lineno; extern char* csv_text; void csv_error(const char* s); /* Data structures for CSV representation */ typedef struct field_node { char* content; struct field_node* next; } field_node_t; typedef struct record_node { field_node_t* fields; struct record_node* next; } record_node_t; typedef struct csv_file { record_node_t* header; record_node_t* records; } csv_file_t; /* Global variables */ csv_file_t* csv_data = NULL; /* Helper functions */ field_node_t* create_field(char* content); record_node_t* create_record(field_node_t* fields); field_node_t* append_field(field_node_t* list, field_node_t* new_field); record_node_t* append_record(record_node_t* list, record_node_t* new_record); void print_csv(csv_file_t* csv); void free_csv(csv_file_t* csv); %} %union { char* str; struct field_node* field_list; struct record_node* record_node; struct csv_file* csv_file; } %define api.prefix {csv_} %define api.pure full %token FIELD_TOK %token COMMA_TOK %token CRLF_TOK %type field_list %type field_opt %type record %type consumed_record %type file %start file %% file: consumed_record { csv_data = malloc(sizeof(csv_file_t)); if (!csv_data) YYERROR; csv_data->header = NULL; csv_data->records = $1; $$ = csv_data; } | file CRLF_TOK consumed_record { csv_data->records = append_record(csv_data->records, $3); $$ = csv_data; } ; consumed_record: record { $$ = $1; } ; record: field_list { $$ = create_record($1); if (!$$) YYERROR; } ; field_list: field_opt { $$ = $1; } | field_list COMMA_TOK field_opt { $$ = append_field($1, $3); } ; field_opt: FIELD_TOK { $$ = create_field($1); if (!$$) YYERROR; } | %empty { /* Empty field - create field with empty string */ char* empty_content = strdup(""); if (!empty_content) YYERROR; $$ = create_field(empty_content); if (!$$) YYERROR; } ; %% void csv_error(const char* s) { fprintf(stderr, "Parse error at line %d: %s\n", csv_lineno, s); if (csv_text) { fprintf(stderr, "Near token: %s\n", csv_text); } } field_node_t* create_field(char* content) { field_node_t* field = malloc(sizeof(field_node_t)); if (!field) return NULL; /* Use the content directly without duplicating since lexer already allocated it */ field->content = content; if (!field->content) { field->content = strdup(""); if (!field->content) { free(field); return NULL; } } field->next = NULL; return field; } record_node_t* create_record(field_node_t* fields) { record_node_t* record = malloc(sizeof(record_node_t)); if (!record) return NULL; record->fields = fields; record->next = NULL; return record; } field_node_t* append_field(field_node_t* list, field_node_t* new_field) { if (!list) return new_field; field_node_t* current = list; while (current->next) { current = current->next; } current->next = new_field; return list; } record_node_t* append_record(record_node_t* list, record_node_t* new_record) { if (!list) return new_record; record_node_t* current = list; while (current->next) { current = current->next; } current->next = new_record; return list; } void print_csv(csv_file_t* csv) { if (!csv) return; if (csv->header) { printf("Header: "); field_node_t* field = csv->header->fields; while (field) { printf("[%s]", field->content ? field->content : ""); if (field->next) printf(", "); field = field->next; } printf("\n"); } record_node_t* record = csv->records; int record_num = 1; while (record) { printf("Record %d: ", record_num++); field_node_t* field = record->fields; while (field) { printf("[%s]", field->content ? field->content : ""); if (field->next) printf(", "); field = field->next; } printf("\n"); record = record->next; } } void free_field_list(field_node_t* fields) { while (fields) { field_node_t* next = fields->next; free(fields->content); free(fields); fields = next; } } void free_record_list(record_node_t* records) { while (records) { record_node_t* next = records->next; free_field_list(records->fields); free(records); records = next; } } void free_csv(csv_file_t* csv) { if (!csv) return; if (csv->header) { free_field_list(csv->header->fields); free(csv->header); } free_record_list(csv->records); free(csv); } int main() { printf("CSV Parser - Enter CSV data (Ctrl+D to end):\n"); int result = csv_parse(); if (result == 0) { printf("\nParsing successful!\n\n"); print_csv(csv_data); } else { printf("\nParsing failed!\n"); } free_csv(csv_data); return result; }