diff options
author | David Tolnay <dtolnay@gmail.com> | 2015-08-23 20:36:11 -0700 |
---|---|---|
committer | David Tolnay <dtolnay@gmail.com> | 2015-08-23 20:36:11 -0700 |
commit | 0c93eb3379241dc4775718a9d39f54a6c4de20d6 (patch) | |
tree | 67bb5510adb707d54c6f72b51b0718578a2caf5c /src/parser.y | |
parent | 891f28ef5e406a8d2156ad88d0244ab03fe490eb (diff) |
Move source files to src/
Diffstat (limited to 'src/parser.y')
-rw-r--r-- | src/parser.y | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/src/parser.y b/src/parser.y new file mode 100644 index 00000000..d57e5c8a --- /dev/null +++ b/src/parser.y @@ -0,0 +1,935 @@ +%{ +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <string.h> +#include "compile.h" +#include "jv_alloc.h" +#define YYMALLOC jv_mem_alloc +#define YYFREE jv_mem_free +%} +%code requires { +#include "locfile.h" +struct lexer_param; + +#define YYLTYPE location +#define YYLLOC_DEFAULT(Loc, Rhs, N) \ + do { \ + if (N) { \ + (Loc).start = YYRHSLOC(Rhs, 1).start; \ + (Loc).end = YYRHSLOC(Rhs, N).end; \ + } else { \ + (Loc).start = YYRHSLOC(Rhs, 0).end; \ + (Loc).end = YYRHSLOC(Rhs, 0).end; \ + } \ + } while (0) +} + +%locations +%error-verbose +%define api.pure +%union { + jv literal; + block blk; +} + +%destructor { jv_free($$); } <literal> +%destructor { block_free($$); } <blk> + +%parse-param {block* answer} +%parse-param {int* errors} +%parse-param {struct locfile* locations} +%parse-param {struct lexer_param* lexer_param_ptr} +%lex-param {block* answer} +%lex-param {int* errors} +%lex-param {struct locfile* locations} +%lex-param {struct lexer_param* lexer_param_ptr} + + +%token INVALID_CHARACTER +%token <literal> IDENT +%token <literal> FIELD +%token <literal> LITERAL +%token <literal> FORMAT +%token REC ".." +%token SETMOD "%=" +%token EQ "==" +%token NEQ "!=" +%token DEFINEDOR "//" +%token AS "as" +%token DEF "def" +%token MODULE "module" +%token IMPORT "import" +%token INCLUDE "include" +%token IF "if" +%token THEN "then" +%token ELSE "else" +%token ELSE_IF "elif" +%token REDUCE "reduce" +%token FOREACH "foreach" +%token END "end" +%token AND "and" +%token OR "or" +%token TRY "try" +%token CATCH "catch" +%token LABEL "label" +%token BREAK "break" +%token LOC "__loc__" +%token SETPIPE "|=" +%token SETPLUS "+=" +%token SETMINUS "-=" +%token SETMULT "*=" +%token SETDIV "/=" +%token SETDEFINEDOR "//=" +%token LESSEQ "<=" +%token GREATEREQ ">=" + +%token QQSTRING_START +%token <literal> QQSTRING_TEXT +%token QQSTRING_INTERP_START +%token QQSTRING_INTERP_END +%token QQSTRING_END + +/* Instead of raising this, find a way to use precedence to resolve + * shift-reduce conflicts. */ +%expect 0 + +%precedence FUNCDEF +%right '|' +%left ',' +%right "//" +%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETMOD SETDEFINEDOR +%left OR +%left AND +%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ +%left '+' '-' +%left '*' '/' '%' +%precedence NONOPT /* non-optional; rules for which a specialized + '?' rule should be preferred over Exp '?' */ +%precedence '?' +%precedence "try" +%precedence "catch" + + +%type <blk> Exp Term +%type <blk> MkDict MkDictPair ExpD +%type <blk> ElseBody +%type <blk> String QQString +%type <blk> FuncDef FuncDefs +%type <blk> Module Import Imports ImportWhat ImportFrom +%type <blk> Param Params Arg Args +%type <blk> Pattern ArrayPats ObjPats ObjPat +%type <literal> Keyword +%{ +#include "lexer.h" +struct lexer_param { + yyscan_t lexer; +}; +#define FAIL(loc, msg) \ + do { \ + location l = loc; \ + yyerror(&l, answer, errors, locations, lexer_param_ptr, msg); \ + /*YYERROR*/; \ + } while (0) + +void yyerror(YYLTYPE* loc, block* answer, int* errors, + struct locfile* locations, struct lexer_param* lexer_param_ptr, const char *s){ + (*errors)++; + if (strstr(s, "unexpected")) { +#ifdef WIN32 + locfile_locate(locations, *loc, "jq: error: %s (Windows cmd shell quoting issues?)", s); +#else + locfile_locate(locations, *loc, "jq: error: %s (Unix shell quoting issues?)", s); +#endif + } else { + locfile_locate(locations, *loc, "jq: error: %s", s); + } +} + +int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors, + struct locfile* locations, struct lexer_param* lexer_param_ptr) { + yyscan_t lexer = lexer_param_ptr->lexer; + int tok = jq_yylex(yylval, yylloc, lexer); + if ((tok == LITERAL || tok == QQSTRING_TEXT) && !jv_is_valid(yylval->literal)) { + jv msg = jv_invalid_get_msg(jv_copy(yylval->literal)); + if (jv_get_kind(msg) == JV_KIND_STRING) { + FAIL(*yylloc, jv_string_value(msg)); + } else { + FAIL(*yylloc, "Invalid literal"); + } + jv_free(msg); + jv_free(yylval->literal); + yylval->literal = jv_null(); + } + return tok; +} + +static block gen_dictpair(block k, block v) { + return BLOCK(gen_subexp(k), gen_subexp(v), gen_op_simple(INSERT)); +} + +static block gen_index(block obj, block key) { + return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX)); +} + +static block gen_index_opt(block obj, block key) { + return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX_OPT)); +} + +static block gen_slice_index(block obj, block start, block end, opcode idx_op) { + block key = BLOCK(gen_subexp(gen_const(jv_object())), + gen_subexp(gen_const(jv_string("start"))), + gen_subexp(start), + gen_op_simple(INSERT), + gen_subexp(gen_const(jv_string("end"))), + gen_subexp(end), + gen_op_simple(INSERT)); + return BLOCK(key, obj, gen_op_simple(idx_op)); +} + +static block constant_fold(block a, block b, int op) { + if (!block_is_single(a) || !block_is_const(a) || + !block_is_single(b) || !block_is_const(b)) + return gen_noop(); + if (op == '+') { + if (block_const_kind(a) == JV_KIND_NULL) { + block_free(a); + return b; + } + if (block_const_kind(b) == JV_KIND_NULL) { + block_free(b); + return a; + } + } + if (block_const_kind(a) != block_const_kind(b)) + return gen_noop(); + + jv res = jv_invalid(); + + if (block_const_kind(a) == JV_KIND_NUMBER) { + double na = jv_number_value(block_const(a)); + double nb = jv_number_value(block_const(b)); + switch (op) { + case '+': res = jv_number(na + nb); break; + case '-': res = jv_number(na - nb); break; + case '*': res = jv_number(na * nb); break; + case '/': res = jv_number(na / nb); break; + case EQ: res = (na == nb ? jv_true() : jv_false()); break; + case NEQ: res = (na != nb ? jv_true() : jv_false()); break; + case '<': res = (na < nb ? jv_true() : jv_false()); break; + case '>': res = (na > nb ? jv_true() : jv_false()); break; + case LESSEQ: res = (na <= nb ? jv_true() : jv_false()); break; + case GREATEREQ: res = (na >= nb ? jv_true() : jv_false()); break; + default: break; + } + } else if (op == '+' && block_const_kind(a) == JV_KIND_STRING) { + res = jv_string_concat(block_const(a), block_const(b)); + } else { + return gen_noop(); + } + + if (jv_get_kind(res) == JV_KIND_INVALID) + return gen_noop(); + + block_free(a); + block_free(b); + return gen_const(res); +} + +static block gen_binop(block a, block b, int op) { + block folded = constant_fold(a, b, op); + if (!block_is_noop(folded)) + return folded; + + const char* funcname = 0; + switch (op) { + case '+': funcname = "_plus"; break; + case '-': funcname = "_minus"; break; + case '*': funcname = "_multiply"; break; + case '/': funcname = "_divide"; break; + case '%': funcname = "_mod"; break; + case EQ: funcname = "_equal"; break; + case NEQ: funcname = "_notequal"; break; + case '<': funcname = "_less"; break; + case '>': funcname = "_greater"; break; + case LESSEQ: funcname = "_lesseq"; break; + case GREATEREQ: funcname = "_greatereq"; break; + } + assert(funcname); + + return gen_call(funcname, BLOCK(gen_lambda(a), gen_lambda(b))); +} + +static block gen_format(block a, jv fmt) { + return BLOCK(a, gen_call("format", gen_lambda(gen_const(fmt)))); +} + +static block gen_definedor_assign(block object, block val) { + block tmp = gen_op_var_fresh(STOREV, "tmp"); + return BLOCK(gen_op_simple(DUP), + val, tmp, + gen_call("_modify", BLOCK(gen_lambda(object), + gen_lambda(gen_definedor(gen_noop(), + gen_op_bound(LOADV, tmp)))))); +} + +static block gen_update(block object, block val, int optype) { + block tmp = gen_op_var_fresh(STOREV, "tmp"); + return BLOCK(gen_op_simple(DUP), + val, + tmp, + gen_call("_modify", BLOCK(gen_lambda(object), + gen_lambda(gen_binop(gen_noop(), + gen_op_bound(LOADV, tmp), + optype))))); +} + +%} + +%% +TopLevel: +Module Imports Exp { + *answer = BLOCK($1, $2, gen_op_simple(TOP), $3); +} | +Module Imports FuncDefs { + *answer = BLOCK($1, $2, $3); +} + +Module: +%empty { + $$ = gen_noop(); +} | +"module" Exp ';' { + if (!block_is_const($2)) { + FAIL(@$, "Module metadata must be constant"); + $$ = gen_noop(); + block_free($2); + } else { + $$ = gen_module($2); + } +} + +Imports: +%empty { + $$ = gen_noop(); +} | +Import Imports { + $$ = BLOCK($1, $2); +} + +FuncDefs: +%empty { + $$ = gen_noop(); +} | +FuncDef FuncDefs { + $$ = block_bind($1, $2, OP_IS_CALL_PSEUDO); +} + +Exp: +FuncDef Exp %prec FUNCDEF { + $$ = block_bind_referenced($1, $2, OP_IS_CALL_PSEUDO); +} | + +Term "as" Pattern '|' Exp { + $$ = gen_destructure($1, $3, $5); +} | + +"reduce" Term "as" Pattern '(' Exp ';' Exp ')' { + $$ = gen_reduce($2, $4, $6, $8); +} | + +"foreach" Term "as" Pattern '(' Exp ';' Exp ';' Exp ')' { + $$ = gen_foreach($2, $4, $6, $8, $10); +} | + +"foreach" Term "as" Pattern '(' Exp ';' Exp ')' { + $$ = gen_foreach($2, $4, $6, $8, gen_noop()); +} | + +"if" Exp "then" Exp ElseBody { + $$ = gen_cond($2, $4, $5); +} | +"if" Exp "then" error { + FAIL(@$, "Possibly unterminated 'if' statement"); + $$ = $2; +} | + +"try" Exp "catch" Exp { + //$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, $4); + $$ = gen_try($2, gen_try_handler($4)); +} | +"try" Exp { + //$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, gen_op_simple(BACKTRACK)); + $$ = gen_try($2, gen_op_simple(BACKTRACK)); +} | +"try" Exp "catch" error { + FAIL(@$, "Possibly unterminated 'try' statement"); + $$ = $2; +} | + +"label" '$' IDENT '|' Exp { + jv v = jv_string_fmt("*label-%s", jv_string_value($3)); + $$ = gen_location(@$, locations, gen_label(jv_string_value(v), $5)); + jv_free($3); + jv_free(v); +} | + +Exp '?' { + $$ = gen_try($1, gen_op_simple(BACKTRACK)); +} | + +Exp '=' Exp { + $$ = gen_call("_assign", BLOCK(gen_lambda($1), gen_lambda($3))); +} | + +Exp "or" Exp { + $$ = gen_or($1, $3); +} | + +Exp "and" Exp { + $$ = gen_and($1, $3); +} | + +Exp "//" Exp { + $$ = gen_definedor($1, $3); +} | + +Exp "//=" Exp { + $$ = gen_definedor_assign($1, $3); +} | + +Exp "|=" Exp { + $$ = gen_call("_modify", BLOCK(gen_lambda($1), gen_lambda($3))); +} | + +Exp '|' Exp { + $$ = block_join($1, $3); +} | + +Exp ',' Exp { + $$ = gen_both($1, $3); +} | + +Exp '+' Exp { + $$ = gen_binop($1, $3, '+'); +} | + +Exp "+=" Exp { + $$ = gen_update($1, $3, '+'); +} | + +'-' Exp { + $$ = BLOCK($2, gen_call("_negate", gen_noop())); +} | + +Exp '-' Exp { + $$ = gen_binop($1, $3, '-'); +} | + +Exp "-=" Exp { + $$ = gen_update($1, $3, '-'); +} | + +Exp '*' Exp { + $$ = gen_binop($1, $3, '*'); +} | + +Exp "*=" Exp { + $$ = gen_update($1, $3, '*'); +} | + +Exp '/' Exp { + $$ = gen_binop($1, $3, '/'); + if (block_is_const_inf($$)) + FAIL(@$, "Division by zero?"); +} | + +Exp '%' Exp { + $$ = gen_binop($1, $3, '%'); + if (block_is_const_inf($$)) + FAIL(@$, "Remainder by zero?"); +} | + +Exp "/=" Exp { + $$ = gen_update($1, $3, '/'); +} | + +Exp SETMOD Exp { + $$ = gen_update($1, $3, '%'); +} | + +Exp "==" Exp { + $$ = gen_binop($1, $3, EQ); +} | + +Exp "!=" Exp { + $$ = gen_binop($1, $3, NEQ); +} | + +Exp '<' Exp { + $$ = gen_binop($1, $3, '<'); +} | + +Exp '>' Exp { + $$ = gen_binop($1, $3, '>'); +} | + +Exp "<=" Exp { + $$ = gen_binop($1, $3, LESSEQ); +} | + +Exp ">=" Exp { + $$ = gen_binop($1, $3, GREATEREQ); +} | + +Term { + $$ = $1; +} + +Import: +ImportWhat ';' { + $$ = $1; +} | +ImportWhat Exp ';' { + if (!block_is_const($2)) { + FAIL(@$, "Module metadata must be constant"); + $$ = gen_noop(); + block_free($1); + block_free($2); + } else if (block_const_kind($2) != JV_KIND_OBJECT) { + FAIL(@$, "Module metadata must be an object"); + $$ = gen_noop(); + block_free($1); + block_free($2); + } else { + $$ = gen_import_meta($1, $2); + } +} + +ImportWhat: +"import" ImportFrom "as" '$' IDENT { + jv v = block_const($2); + // XXX Make gen_import take only blocks and the int is_data so we + // don't have to free so much stuff here + $$ = gen_import(jv_string_value(v), jv_string_value($5), 1); + block_free($2); + jv_free($5); + jv_free(v); +} | +"import" ImportFrom "as" IDENT { + jv v = block_const($2); + $$ = gen_import(jv_string_value(v), jv_string_value($4), 0); + block_free($2); + jv_free($4); + jv_free(v); +} | +"include" ImportFrom { + jv v = block_const($2); + $$ = gen_import(jv_string_value(v), NULL, 0); + block_free($2); + jv_free(v); +} + +ImportFrom: +String { + if (!block_is_const($1)) { + FAIL(@$, "Import path must be constant"); + $$ = gen_const(jv_string("")); + block_free($1); + } else { + $$ = $1; + } +} + +FuncDef: +"def" IDENT ':' Exp ';' { + $$ = gen_function(jv_string_value($2), gen_noop(), $4); + jv_free($2); +} | + +"def" IDENT '(' Params ')' ':' Exp ';' { + $$ = gen_function(jv_string_value($2), $4, $7); + jv_free($2); +} + +Params: +Param { + $$ = $1; +} | +Params ';' Param { + $$ = BLOCK($1, $3); +} + +Param: +'$' IDENT { + $$ = gen_param_regular(jv_string_value($2)); + jv_free($2); +} | + +IDENT { + $$ = gen_param(jv_string_value($1)); + jv_free($1); +} + + +String: +QQSTRING_START { $<literal>$ = jv_string("text"); } QQString QQSTRING_END { + $$ = $3; + jv_free($<literal>2); +} | +FORMAT QQSTRING_START { $<literal>$ = $1; } QQString QQSTRING_END { + $$ = $4; + jv_free($<literal>3); +} + + +QQString: +%empty { + $$ = gen_const(jv_string("")); +} | +QQString QQSTRING_TEXT { + $$ = gen_binop($1, gen_const($2), '+'); +} | +QQString QQSTRING_INTERP_START Exp QQSTRING_INTERP_END { + $$ = gen_binop($1, gen_format($3, jv_copy($<literal>0)), '+'); +} + + +ElseBody: +"elif" Exp "then" Exp ElseBody { + $$ = gen_cond($2, $4, $5); +} | +"else" Exp "end" { + $$ = $2; +} + +ExpD: +ExpD '|' ExpD { + $$ = block_join($1, $3); +} | +'-' ExpD { + $$ = BLOCK($2, gen_call("_negate", gen_noop())); +} | +Term { + $$ = $1; +} + + +Term: +'.' { + $$ = gen_noop(); +} | +REC { + $$ = gen_call("recurse", gen_noop()); +} | +BREAK '$' IDENT { + jv v = jv_string_fmt("*label-%s", jv_string_value($3)); // impossible symbol + $$ = gen_location(@$, locations, + BLOCK(gen_op_unbound(LOADV, jv_string_value(v)), + gen_call("error", gen_noop()))); + jv_free(v); + jv_free($3); +} | +BREAK error { + FAIL(@$, "break requires a label to break to"); + $$ = gen_noop(); +} | +Term FIELD '?' { + $$ = gen_index_opt($1, gen_const($2)); +} | +FIELD '?' { + $$ = gen_index_opt(gen_noop(), gen_const($1)); +} | +Term '.' String '?' { + $$ = gen_index_opt($1, $3); +} | +'.' String '?' { + $$ = gen_index_opt(gen_noop(), $2); +} | +Term FIELD %prec NONOPT { + $$ = gen_index($1, gen_const($2)); +} | +FIELD %prec NONOPT { + $$ = gen_index(gen_noop(), gen_const($1)); +} | +Term '.' String %prec NONOPT { + $$ = gen_index($1, $3); +} | +'.' String %prec NONOPT { + $$ = gen_index(gen_noop(), $2); +} | +'.' error { + FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields"); + $$ = gen_noop(); +} | +'.' IDENT error { + jv_free($2); + FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields"); + $$ = gen_noop(); +} | +/* FIXME: string literals */ +Term '[' Exp ']' '?' { + $$ = gen_index_opt($1, $3); +} | +Term '[' Exp ']' %prec NONOPT { + $$ = gen_index($1, $3); +} | +Term '[' ']' '?' { + $$ = block_join($1, gen_op_simple(EACH_OPT)); +} | +Term '[' ']' %prec NONOPT { + $$ = block_join($1, gen_op_simple(EACH)); +} | +Term '[' Exp ':' Exp ']' '?' { + $$ = gen_slice_index($1, $3, $5, INDEX_OPT); +} | +Term '[' Exp ':' ']' '?' { + $$ = gen_slice_index($1, $3, gen_const(jv_null()), INDEX_OPT); +} | +Term '[' ':' Exp ']' '?' { + $$ = gen_slice_index($1, gen_const(jv_null()), $4, INDEX_OPT); +} | +Term '[' Exp ':' Exp ']' %prec NONOPT { + $$ = gen_slice_index($1, $3, $5, INDEX); +} | +Term '[' Exp ':' ']' %prec NONOPT { + $$ = gen_slice_index($1, $3, gen_const(jv_null()), INDEX); +} | +Term '[' ':' Exp ']' %prec NONOPT { + $$ = gen_slice_index($1, gen_const(jv_null()), $4, INDEX); +} | +LITERAL { + $$ = gen_const($1); +} | +String { + $$ = $1; +} | +FORMAT { + $$ = gen_format(gen_noop(), $1); +} | +'(' Exp ')' { + $$ = $2; +} | +'[' Exp ']' { + $$ = gen_collect($2); +} | +'[' ']' { + $$ = gen_const(jv_array()); +} | +'{' MkDict '}' { + block o = gen_const_object($2); + if (o.first != NULL) + $$ = o; + else + $$ = BLOCK(gen_subexp(gen_const(jv_object())), $2, gen_op_simple(POP)); +} | +'$' LOC { + $$ = gen_const(JV_OBJECT(jv_string("file"), jv_copy(locations->fname), + jv_string("line"), jv_number(locfile_get_line(locations, @$.start) + 1))); +} | +'$' IDENT { + $$ = gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_value($2))); + jv_free($2); +} | +IDENT { + const char *s = jv_string_value($1); + if (strcmp(s, "false") == 0) + $$ = gen_const(jv_false()); + else if (strcmp(s, "true") == 0) + $$ = gen_const(jv_true()); + else if (strcmp(s, "null") == 0) + $$ = gen_const(jv_null()); + else + $$ = gen_location(@$, locations, gen_call(s, gen_noop())); + jv_free($1); +} | +IDENT '(' Args ')' { + $$ = gen_call(jv_string_value($1), $3); + $$ = gen_location(@1, locations, $$); + jv_free($1); +} | +'(' error ')' { $$ = gen_noop(); } | +'[' error ']' { $$ = gen_noop(); } | +Term '[' error ']' { $$ = $1; } | +'{' error '}' { $$ = gen_noop(); } + +Args: +Arg { + $$ = $1; +} | +Args ';' Arg { + $$ = BLOCK($1, $3); +} + +Arg: +Exp { + $$ = gen_lambda($1); +} + +Pattern: +'$' IDENT { + $$ = gen_op_unbound(STOREV, jv_string_value($2)); + jv_free($2); +} | +'[' ArrayPats ']' { + $$ = BLOCK($2, gen_op_simple(POP)); +} | +'{' ObjPats '}' { + $$ = BLOCK($2, gen_op_simple(POP)); +} + +ArrayPats: +Pattern { + $$ = gen_array_matcher(gen_noop(), $1); +} | +ArrayPats ',' Pattern { + $$ = gen_array_matcher($1, $3); +} + +ObjPats: +ObjPat { + $$ = $1; +} | +ObjPats ',' ObjPat { + $$ = BLOCK($1, $3); +} + +ObjPat: +'$' IDENT { + $$ = gen_object_matcher(gen_const($2), gen_op_unbound(STOREV, jv_string_value($2))); +} | +IDENT ':' Pattern { + $$ = gen_object_matcher(gen_const($1), $3); +} | +Keyword ':' Pattern { + $$ = gen_object_matcher(gen_const($1), $3); +} | +String ':' Pattern { + $$ = gen_object_matcher($1, $3); +} | +'(' Exp ')' ':' Pattern { + $$ = gen_object_matcher($2, $5); +} + +Keyword: +"as" { + $$ = jv_string("as"); +} | +"def" { + $$ = jv_string("def"); +} | +"module" { + $$ = jv_string("module"); +} | +"import" { + $$ = jv_string("import"); +} | +"include" { + $$ = jv_string("include"); +} | +"if" { + $$ = jv_string("if"); +} | +"then" { + $$ = jv_string("then"); +} | +"else" { + $$ = jv_string("else"); +} | +"elif" { + $$ = jv_string("elif"); +} | +"reduce" { + $$ = jv_string("reduce"); +} | +"foreach" { + $$ = jv_string("foreach"); +} | +"end" { + $$ = jv_string("end"); +} | +"and" { + $$ = jv_string("and"); +} | +"or" { + $$ = jv_string("or"); +} | +"try" { + $$ = jv_string("try"); +} | +"catch" { + $$ = jv_string("catch"); +} | +"label" { + $$ = jv_string("label"); +} | +"break" { + $$ = jv_string("break"); +} | +"__loc__" { + $$ = jv_string("__loc__"); +} + +MkDict: +%empty { + $$=gen_noop(); +} | + MkDictPair { $$ = $1; } +| MkDictPair ',' MkDict { $$=block_join($1, $3); } +| error ',' MkDict { $$ = $3; } + +MkDictPair: +IDENT ':' ExpD { + $$ = gen_dictpair(gen_const($1), $3); + } +| Keyword ':' ExpD { + $$ = gen_dictpair(gen_const($1), $3); + } +| String ':' ExpD { + $$ = gen_dictpair($1, $3); + } +| String { + $$ = gen_dictpair($1, BLOCK(gen_op_simple(POP), gen_op_simple(DUP2), + gen_op_simple(DUP2), gen_op_simple(INDEX))); + } +| '$' IDENT { + $$ = gen_dictpair(gen_const($2), + gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_value($2)))); + } +| IDENT { + $$ = gen_dictpair(gen_const(jv_copy($1)), + gen_index(gen_noop(), gen_const($1))); + } +| '(' Exp ')' ':' ExpD { + $$ = gen_dictpair($2, $5); + } +| '(' error ')' ':' ExpD { $$ = $5; } +%% + +int jq_parse(struct locfile* locations, block* answer) { + struct lexer_param scanner; + YY_BUFFER_STATE buf; + jq_yylex_init_extra(0, &scanner.lexer); + buf = jq_yy_scan_bytes(locations->data, locations->length, scanner.lexer); + int errors = 0; + *answer = gen_noop(); + yyparse(answer, &errors, locations, &scanner); + jq_yy_delete_buffer(buf, scanner.lexer); + jq_yylex_destroy(scanner.lexer); + if (errors > 0) { + block_free(*answer); + *answer = gen_noop(); + } + return errors; +} + +int jq_parse_library(struct locfile* locations, block* answer) { + int errs = jq_parse(locations, answer); + if (errs) return errs; + if (block_has_main(*answer)) { + locfile_locate(locations, UNKNOWN_LOCATION, "jq: error: library should only have function definitions, not a main expression"); + return 1; + } + assert(block_has_only_binders_and_imports(*answer, OP_IS_CALL_PSEUDO)); + return 0; +} |