%{ #include #include #include "compile.h" %} %code requires { #include "locfile.h" #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($$); } %destructor { block_free($$); } %parse-param {block* answer} %parse-param {int* errors} %parse-param {struct locfile* locations} %parse-param {yyscan_t lexer} %lex-param {block* answer} %lex-param {int* errors} %lex-param {struct locfile* locations} %lex-param {yyscan_t lexer} %token INVALID_CHARACTER %token IDENT %token LITERAL %token EQ "==" %token DEFINEDOR "//" %token AS "as" %token DEF "def" %token IF "if" %token THEN "then" %token ELSE "else" %token ELSE_IF "elif" %token END "end" %token AND "and" %token OR "or" %token SETPIPE "|=" %token SETPLUS "+=" %token SETMINUS "-=" %token SETMULT "*=" %token SETDIV "/=" %token SETDEFINEDOR "//=" %token LESSEQ "<=" %token GREATEREQ ">=" %token QQSTRING_START %token QQSTRING_TEXT %token QQSTRING_INTERP_START %token QQSTRING_INTERP_END %token QQSTRING_END /* revolting hack */ %left ';' %left '|' %left ',' %right "//" %nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETDEFINEDOR %left OR %left AND %nonassoc EQ '<' '>' LESSEQ GREATEREQ %left '+' '-' %left '*' '/' %type Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs String %{ #include "lexer.gen.h" #define FAIL(loc, msg) \ do { \ location l = loc; \ yyerror(&l, answer, errors, locations, lexer, msg); \ /*YYERROR*/; \ } while (0) void yyerror(YYLTYPE* loc, block* answer, int* errors, struct locfile* locations, yyscan_t lexer, const char *s){ (*errors)++; locfile_locate(locations, *loc, "error: %s", s); } int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors, struct locfile* locations, yyscan_t lexer) { while (1) { int tok = jq_yylex(yylval, yylloc, lexer); if (tok == INVALID_CHARACTER) { FAIL(*yylloc, "Invalid character"); } else { 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) { block b = gen_subexp(k); block_append(&b, gen_subexp(v)); block_append(&b, gen_op_simple(INSERT)); return b; } static block gen_index(block obj, block key) { return block_join(obj, block_join(gen_subexp(key), gen_op_simple(INDEX))); } static block gen_binop(block a, block b, int op) { const char* funcname = 0; switch (op) { case '+': funcname = "_plus"; break; case '-': funcname = "_minus"; break; case '*': funcname = "_multiply"; break; case '/': funcname = "_divide"; break; case EQ: funcname = "_equal"; break; case '<': funcname = "_less"; break; case '>': funcname = "_greater"; break; case LESSEQ: funcname = "_lesseq"; break; case GREATEREQ: funcname = "_greatereq"; break; } assert(funcname); block c = gen_noop(); block_append(&c, gen_subexp(a)); block_append(&c, gen_subexp(b)); block_append(&c, gen_op_call(CALL_1_1, gen_op_block_unbound(CLOSURE_REF, funcname))); return c; } static block gen_format(block a) { return block_join(a, gen_op_call(CALL_1_1, gen_op_block_unbound(CLOSURE_REF, "tostring"))); } static block gen_update(block a, block op, int optype) { block assign = a; block_append(&assign, gen_op_simple(DUP)); if (optype) { op = gen_binop(gen_noop(), op, optype); } block_append(&assign, op); return gen_assign(assign); } %} %% TopLevel: Exp { *answer = $1; } | FuncDefs { *answer = $1; } FuncDefs: /* empty */ { $$ = gen_noop(); } | FuncDef FuncDefs { $$ = block_join($1, $2); } Exp: FuncDef Exp %prec ';' { $$ = block_bind($1, $2, OP_IS_CALL_PSEUDO); } | Term "as" '$' IDENT '|' Exp { $$ = gen_op_simple(DUP); block_append(&$$, $1); block_append(&$$, block_bind(gen_op_var_unbound(STOREV, jv_string_value($4)), $6, OP_HAS_VARIABLE)); jv_free($4); } | "if" Exp "then" Exp ElseBody { $$ = gen_cond($2, $4, $5); } | "if" Exp error { FAIL(@$, "Possibly unterminated 'if' statment"); $$ = $2; } | Exp '=' Exp { block assign = gen_op_simple(DUP); block_append(&assign, $3); block_append(&assign, gen_op_simple(SWAP)); block_append(&assign, $1); block_append(&assign, gen_op_simple(SWAP)); $$ = gen_assign(assign); } | Exp "or" Exp { $$ = gen_or($1, $3); } | Exp "and" Exp { $$ = gen_and($1, $3); } | Exp "//" Exp { $$ = gen_definedor($1, $3); } | Exp "//=" Exp { $$ = gen_update($1, gen_definedor(gen_noop(), $3), 0); } | Exp "|=" Exp { $$ = gen_update($1, $3, 0); } | 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 '-' 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, '/'); } | Exp "/=" Exp { $$ = gen_update($1, $3, '/'); } | Exp "==" Exp { $$ = gen_binop($1, $3, EQ); } | 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); } | String { $$ = $1; } | Term { $$ = $1; } String: QQSTRING_START QQString QQSTRING_END { $$ = $2; } FuncDef: "def" IDENT ':' Exp ';' { block body = block_join($4, gen_op_simple(RET)); $$ = gen_op_block_defn_rec(CLOSURE_CREATE, jv_string_value($2), body); jv_free($2); } | "def" IDENT '(' IDENT ')' ':' Exp ';' { block body = block_bind(gen_op_block_unbound(CLOSURE_PARAM, jv_string_value($4)), block_join($7, gen_op_simple(RET)), OP_IS_CALL_PSEUDO); $$ = gen_op_block_defn_rec(CLOSURE_CREATE, jv_string_value($2), body); jv_free($2); jv_free($4); } QQString: /* empty */ { $$ = gen_op_const(LOADK, jv_string("")); } | QQString QQSTRING_TEXT { $$ = gen_binop($1, gen_op_const(LOADK, $2), '+'); } | QQString QQSTRING_INTERP_START Exp QQSTRING_INTERP_END { $$ = gen_binop($1, gen_format($3), '+'); } ElseBody: "elif" Exp "then" Exp ElseBody { $$ = gen_cond($2, $4, $5); } | "else" Exp "end" { $$ = $2; } ExpD: ExpD '|' ExpD { $$ = block_join($1, $3); } | Term { $$ = $1; } Term: '.' { $$ = gen_noop(); } | Term '.' IDENT { $$ = gen_index($1, gen_op_const(LOADK, $3)); } | '.' IDENT { $$ = gen_index(gen_noop(), gen_op_const(LOADK, $2)); } | /* FIXME: string literals */ Term '[' Exp ']' { $$ = gen_index($1, $3); } | Term '[' ']' { $$ = block_join($1, gen_op_simple(EACH)); } | LITERAL { $$ = gen_op_const(LOADK, $1); } | '(' Exp ')' { $$ = $2; } | '[' Exp ']' { $$ = gen_collect($2); } | '[' ']' { $$ = gen_op_const(LOADK, jv_array()); } | '{' MkDict '}' { $$ = gen_subexp(gen_op_const(LOADK, jv_object())); block_append(&$$, $2); block_append(&$$, gen_op_simple(POP)); } | '$' IDENT { $$ = gen_location(@$, gen_op_var_unbound(LOADV, jv_string_value($2))); jv_free($2); } | IDENT { $$ = gen_location(@$, gen_op_call(CALL_1_1, gen_op_block_unbound(CLOSURE_REF, jv_string_value($1)))); jv_free($1); } | IDENT '(' Exp ')' { $$ = gen_op_call(CALL_1_1, block_join(gen_op_block_unbound(CLOSURE_REF, jv_string_value($1)), block_bind(gen_op_block_defn(CLOSURE_CREATE, "lambda", block_join($3, gen_op_simple(RET))), gen_noop(), OP_IS_CALL_PSEUDO))); $$ = gen_location(@1, $$); jv_free($1); } | '(' error ')' { $$ = gen_noop(); } | '[' error ']' { $$ = gen_noop(); } | Term '[' error ']' { $$ = $1; } | '{' error '}' { $$ = gen_noop(); } MkDict: { $$=gen_noop(); } | MkDictPair { $$ = $1; } | MkDictPair ',' MkDict { $$=block_join($1, $3); } | error ',' MkDict { $$ = $3; } MkDictPair : IDENT ':' ExpD { $$ = gen_dictpair(gen_op_const(LOADK, $1), $3); } | String ':' ExpD { $$ = gen_dictpair($1, $3); } | IDENT { $$ = gen_dictpair(gen_op_const(LOADK, jv_copy($1)), gen_index(gen_noop(), gen_op_const(LOADK, $1))); } | '(' Exp ')' ':' ExpD { $$ = gen_dictpair($2, $5); } | '(' error ')' ':' ExpD { $$ = $5; } %% int jq_parse(struct locfile* locations, block* answer) { yyscan_t scanner; YY_BUFFER_STATE buf; jq_yylex_init_extra(0, &scanner); buf = jq_yy_scan_bytes(locations->data, locations->length, scanner); int errors = 0; *answer = gen_noop(); yyparse(answer, &errors, locations, scanner); jq_yy_delete_buffer(buf, scanner); jq_yylex_destroy(scanner); 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_only_binders(*answer, OP_IS_CALL_PSEUDO)) { locfile_locate(locations, UNKNOWN_LOCATION, "error: library should only have function definitions, not a main expression"); return 1; } return 0; }