%code requires { /* Required to break a circular dependency introduced with bison 2.6 */ typedef void* yyscan_t; } %code top { #include #include "lang.h" #include "lexer.h" static int yyerror(struct lgfs2_lang_state *state, yyscan_t lexer, const char *errorstr) { fprintf(stderr, "%d:%d: %s\n", state->ls_linenum, state->ls_colnum, errorstr); return 1; } } %defines %debug %define api.pure %parse-param { struct lgfs2_lang_state *state } %parse-param { yyscan_t lexer } %lex-param { yyscan_t lexer } %start script %token TOK_COLON %token TOK_COMMA %token TOK_ID %token TOK_LBRACE %token TOK_LBRACKET %token TOK_NUMBER %token TOK_OFFSET %token TOK_RBRACE %token TOK_RBRACKET %token TOK_SEMI %token TOK_SET %token TOK_GET %token TOK_STATE %token TOK_STRING %token TOK_PATH %% script: statements { state->ls_ast_root = $1; state->ls_interp_curr = $1; } | statements TOK_SEMI { state->ls_ast_root = $1; state->ls_interp_curr = $1; } ; statements: statements TOK_SEMI statement { state->ls_ast_tail->ast_left = $3; state->ls_ast_tail = $3; $$ = $1; } | statement { if (state->ls_ast_tail == NULL) state->ls_ast_tail = $1; $$ = $1; } ; statement: set_stmt { $$ = $1;} | get_stmt { $$ = $1; } ; set_stmt: TOK_SET blockspec structspec { $1->ast_right = $2; $2->ast_right = $3; $$ = $1; } | TOK_SET blockspec typespec structspec { $1->ast_right = $2; $2->ast_right = $3; $3->ast_right = $4; $$ = $1; } ; get_stmt: TOK_GET blockspec { $1->ast_right = $2; $$ = $1; } | TOK_GET blockspec TOK_STATE { $1->ast_right = $2; $2->ast_right = $3; $$ = $1; } ; blockspec: offset { $$ = $1; } | address { $$ = $1; } | path { $$ = $1; } | block_literal { $$ = $1; } | subscript { $$ = $1; } ; offset: blockspec TOK_OFFSET { $2->ast_left = $1; $$ = $2; } ; typespec: identifier { $1->ast_type = AST_EX_TYPESPEC; $$ = $1; } ; block_literal: identifier { $$ = $1; } ; subscript: block_literal TOK_LBRACKET index TOK_RBRACKET { $4->ast_left = $1; $1->ast_left = $3; $$ = $4; } ; index: number { $$ = $1; } | identifier { $$ = $1; } ; address: number { $1->ast_type = AST_EX_ADDRESS; $$ = $1; } ; structspec: TOK_LBRACE fieldspecs TOK_RBRACE { $$ = $2; } | TOK_LBRACE TOK_RBRACE { $$ = NULL; } ; fieldspecs: fieldspecs TOK_COMMA fieldspec { $1->ast_left = $3; $$ = $1; } | fieldspec { $$ = $1; } ; fieldspec: identifier TOK_COLON fieldvalue { $2->ast_right = $1; $1->ast_right = $3; $$ = $2; } ; fieldvalue: number { $$ = $1; } | string { $$ = $1; } ; number: TOK_NUMBER { $$ = $1; } string: TOK_STRING { $$ = $1; } identifier: TOK_ID { $$ = $1; } path: TOK_PATH { $$ = $1; } %% /** * Allocate and initialize a new parse state structure. The caller must free the * memory returned by this function. */ struct lgfs2_lang_state *lgfs2_lang_init(void) { struct lgfs2_lang_state *state; state = calloc(1, sizeof(struct lgfs2_lang_state)); if (state == NULL) { return NULL; } state->ls_linenum = 1; return state; } void lgfs2_lang_free(struct lgfs2_lang_state **state) { ast_destroy(&(*state)->ls_ast_root); free(*state); *state = NULL; } int lgfs2_lang_parsef(struct lgfs2_lang_state *state, FILE *src) { int ret = 0; yyscan_t lexer; ret = yylex_init_extra(state, &lexer); if (ret != 0) { fprintf(stderr, "Failed to initialize lexer.\n"); return ret; } yyset_in(src, lexer); ret = yyparse(state, lexer); yylex_destroy(lexer); return ret; } int lgfs2_lang_parses(struct lgfs2_lang_state *state, const char *cstr) { int ret; FILE *src; char *str = strdup(cstr); if (str == NULL) { perror("Failed to duplicate source string"); return 1; } src = fmemopen(str, strlen(str), "r"); if (src == NULL) { perror("Failed to open string as source file"); free(str); return 1; } ret = lgfs2_lang_parsef(state, src); fclose(src); free(str); if (ret != 0 || state->ls_errnum != 0) { return 1; } return 0; }