%define api.pure full %define api.prefix {kalmia} %locations %define parse.error verbose %param { yyscan_t scanner } %code top { #include #include #include #include } %code requires { typedef void* yyscan_t; struct kai_attr_t { char *key; char *value; struct kai_attr_t *next; }; struct kai_tag_t { char *type; struct kai_attr_t *attrs; struct kai_tag_t *children; char *content; struct kai_tag_t *next; }; struct kai_tag_t * kai_parse_file(const char *filename); void kai_tag_destroy(struct kai_tag_t *tag); } %parse-param { struct kai_tag_t **document } %code { /* flex definitions */ int yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t scanner); int kalmialex_init(yyscan_t *); void kalmiaset_in(FILE *, yyscan_t); int kalmialex_destroy(yyscan_t); int yyerror(YYLTYPE *yyllocp, yyscan_t unused, struct kai_tag_t **unused2, const char *msg); #define TAG_MISMATCH(a, b) \ do { \ char buf[1024]; \ snprintf(buf, 1024, "Tag name mismatch: \"%s\" != \"%s\"", a, b); \ yyerror(&yylloc, NULL, NULL, (const char*) buf); \ YYABORT; \ } while (0) /* create/destroy kai_attr_t */ struct kai_attr_t * kai_attr_new(char *key, char *value); struct kai_attr_t * kai_attr_last(struct kai_attr_t *head); void kai_attr_destroy(struct kai_attr_t *attr); /* create/destroy kai_tag_t */ struct kai_tag_t * kai_tag_new(char *type, struct kai_attr_t *attrs); struct kai_tag_t * kai_tag_last(struct kai_tag_t *head); void kai_tag_destroy(struct kai_tag_t *tag); } %union { char *string; struct kai_attr_t *attr; struct kai_tag_t *tag; } /* tokens */ %token PROLOG %token S_TAG_OPEN E_TAG_OPEN TAG_CLOSE EMPTY_TAG_CLOSE %token NAME %token ATTR %token TEXT %token CONTENT /* tag targets */ %type start_tag %type end_tag %type empty_tag %type tag %type tags /* attribute targets */ %type attribute %type attributes %% document: tag { *document = $1; } | PROLOG tag { *document = $2; } tags: tag { $$ = $1; } | tags tag { $$ = $1; kai_tag_last($$)->next = $2; } ; tag: start_tag end_tag { $$ = $1; if (strcmp($$->type, $2) != 0) TAG_MISMATCH($$->type, $2); free($2); } | start_tag CONTENT end_tag { $$ = $1; $$->content = $2; if (strcmp($$->type, $3) != 0) TAG_MISMATCH($$->type, $3); free($3); } | start_tag tags end_tag { $$ = $1; $$->children = $2; if (strcmp($$->type, $3) != 0) TAG_MISMATCH($$->type, $3); free($3); } | empty_tag { $$ = $1; } ; start_tag: S_TAG_OPEN NAME TAG_CLOSE { $$ = kai_tag_new($2, NULL); if ($$ == NULL) YYNOMEM; } | S_TAG_OPEN NAME attributes TAG_CLOSE { $$ = kai_tag_new($2, $3); if ($$ == NULL) YYNOMEM; } ; empty_tag: S_TAG_OPEN NAME EMPTY_TAG_CLOSE { $$ = kai_tag_new($2, NULL); if ($$ == NULL) YYNOMEM; } | S_TAG_OPEN NAME attributes EMPTY_TAG_CLOSE { $$ = kai_tag_new($2, $3); if ($$ == NULL) YYNOMEM; } ; end_tag: E_TAG_OPEN NAME TAG_CLOSE { $$ = $2; } ; attributes: attribute { $$ = $1; } | attributes attribute { $$ = $1; kai_attr_last($$)->next = $2; } ; attribute: ATTR '=' '"' TEXT '"' { $$ = kai_attr_new($1, $4); if ($$ == NULL) YYNOMEM; } ; %% /* print an error message */ int yyerror( YYLTYPE *yyllocp, yyscan_t unused, struct kai_tag_t **unused2, const char *msg) { fprintf( stderr, "[%d:%d]: %s\n", yyllocp->first_line, yyllocp->first_column, msg ); return 1; } /* create a new kai_attr_t object */ struct kai_attr_t * kai_attr_new(char *key, char *value) { struct kai_attr_t *attr = malloc(sizeof(struct kai_attr_t)); if (attr == NULL) { return NULL; } attr->key = key; attr->value = value; attr->next = NULL; return attr; } /* get the end of a kai_attr_t list */ struct kai_attr_t * kai_attr_last(struct kai_attr_t *head) { struct kai_attr_t *ptr = head; while (ptr->next != NULL) { ptr = ptr->next; } return ptr; } /* destroy a kai_attr_t list */ void kai_attr_destroy(struct kai_attr_t *attr) { if (attr == NULL) { return; } kai_attr_destroy(attr->next); free(attr->key); free(attr->value); free(attr); } /* create a kai_tag_t object */ struct kai_tag_t * kai_tag_new(char *type, struct kai_attr_t *attrs) { struct kai_tag_t *tag = malloc(sizeof(struct kai_tag_t)); if (tag == NULL) { return NULL; } tag->type = type; tag->attrs = attrs; tag->children = NULL; tag->content = NULL; tag->next = NULL; return tag; } /* get the end of a kai_tag_t list */ struct kai_tag_t * kai_tag_last(struct kai_tag_t *head) { struct kai_tag_t *ptr = head; while (ptr->next != NULL) { ptr = ptr->next; } return ptr; } /* destroy a kai_tag_t list */ void kai_tag_destroy(struct kai_tag_t *tag) { if (tag == NULL) { return; } free(tag->type); kai_attr_destroy(tag->attrs); kai_tag_destroy(tag->children); free(tag->content); kai_tag_destroy(tag->next); free(tag); } /* parse a file into a document */ struct kai_tag_t * kai_parse_file(const char *filename) { FILE *file = fopen(filename, "r"); if (file == NULL) { fprintf(stderr, "Could not open file \"%s\"\n", filename); return NULL; } yyscan_t scanner; struct kai_tag_t *document; kalmialex_init(&scanner); kalmiaset_in(file, scanner); int result = kalmiaparse(scanner, &document); kalmialex_destroy(scanner); fclose(file); if (result != 0) { /* parse function will print error message */ return NULL; } return document; }