%define api.pure full %define api.prefix {kalmia} %locations %define parse.error verbose %param { yyscan_t scanner } %code top { #include #include #include } %code requires { typedef void* yyscan_t; struct kalmia_t { struct kai_tag_t *tag; }; 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; }; } %parse-param { struct kalmia_t *result } %code { int yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t scanner); int yyerror(YYLTYPE *yyllocp, yyscan_t unused, struct kalmia_t *unused2, const char *msg); 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); 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; } %token PROLOG %token S_TAG_OPEN E_TAG_OPEN TAG_CLOSE EMPTY_TAG_CLOSE %token NAME %token ATTR %token TEXT %token CONTENT %type start_tag %type empty_tag %type tag %type tags %type attribute %type attributes %% document: PROLOG tag { result->tag = $2; } tags: tag { $$ = $1; } | tags tag { $$ = $1; kai_tag_last($$)->next = $2; } ; tag: start_tag end_tag { $$ = $1; } | start_tag CONTENT end_tag { $$ = $1; $$->content = $2; } | start_tag tags end_tag { $$ = $1; $$->children = $2; } | 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 ; attributes: attribute { $$ = $1; } | attributes attribute { $$ = $1; kai_attr_last($$)->next = $2; } ; attribute: ATTR '=' '"' TEXT '"' { $$ = kai_attr_new($1, $4); if ($$ == NULL) YYNOMEM; } ; %% int yyerror( YYLTYPE *yyllocp, yyscan_t unused, struct kalmia_t *unused2, const char *msg) { fprintf( stderr, "[%d:%d]: %s\n", yyllocp->first_line, yyllocp->first_column, msg ); return 1; } 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; } 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; } 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); } 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; } 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; } 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); }