parser.y (jq-1.5) | : | parser.y (jq-1.6) | ||
---|---|---|---|---|
skipping to change at line 85 | skipping to change at line 85 | |||
%token BREAK "break" | %token BREAK "break" | |||
%token LOC "__loc__" | %token LOC "__loc__" | |||
%token SETPIPE "|=" | %token SETPIPE "|=" | |||
%token SETPLUS "+=" | %token SETPLUS "+=" | |||
%token SETMINUS "-=" | %token SETMINUS "-=" | |||
%token SETMULT "*=" | %token SETMULT "*=" | |||
%token SETDIV "/=" | %token SETDIV "/=" | |||
%token SETDEFINEDOR "//=" | %token SETDEFINEDOR "//=" | |||
%token LESSEQ "<=" | %token LESSEQ "<=" | |||
%token GREATEREQ ">=" | %token GREATEREQ ">=" | |||
%token ALTERNATION "?//" | ||||
%token QQSTRING_START | %token QQSTRING_START | |||
%token <literal> QQSTRING_TEXT | %token <literal> QQSTRING_TEXT | |||
%token QQSTRING_INTERP_START | %token QQSTRING_INTERP_START | |||
%token QQSTRING_INTERP_END | %token QQSTRING_INTERP_END | |||
%token QQSTRING_END | %token QQSTRING_END | |||
/* Instead of raising this, find a way to use precedence to resolve | /* Instead of raising this, find a way to use precedence to resolve | |||
* shift-reduce conflicts. */ | * shift-reduce conflicts. */ | |||
%expect 0 | %expect 0 | |||
skipping to change at line 112 | skipping to change at line 113 | |||
%left AND | %left AND | |||
%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ | %nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ | |||
%left '+' '-' | %left '+' '-' | |||
%left '*' '/' '%' | %left '*' '/' '%' | |||
%precedence NONOPT /* non-optional; rules for which a specialized | %precedence NONOPT /* non-optional; rules for which a specialized | |||
'?' rule should be preferred over Exp '?' */ | '?' rule should be preferred over Exp '?' */ | |||
%precedence '?' | %precedence '?' | |||
%precedence "try" | %precedence "try" | |||
%precedence "catch" | %precedence "catch" | |||
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString | %type <blk> Exp Term | |||
%type <blk> FuncDef FuncDefs String Import Imports Param Params | %type <blk> MkDict MkDictPair ExpD | |||
%type <blk> Arg Args Module Pattern ArrayPats ObjPats ObjPat | %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> Patterns RepPatterns Pattern ArrayPats ObjPats ObjPat | ||||
%type <literal> Keyword | %type <literal> Keyword | |||
%{ | %{ | |||
#include "lexer.h" | #include "lexer.h" | |||
struct lexer_param { | struct lexer_param { | |||
yyscan_t lexer; | yyscan_t lexer; | |||
}; | }; | |||
#define FAIL(loc, msg) \ | #define FAIL(loc, msg) \ | |||
do { \ | do { \ | |||
location l = loc; \ | location l = loc; \ | |||
yyerror(&l, answer, errors, locations, lexer_param_ptr, msg); \ | yyerror(&l, answer, errors, locations, lexer_param_ptr, msg); \ | |||
skipping to change at line 160 | skipping to change at line 166 | |||
} else { | } else { | |||
FAIL(*yylloc, "Invalid literal"); | FAIL(*yylloc, "Invalid literal"); | |||
} | } | |||
jv_free(msg); | jv_free(msg); | |||
jv_free(yylval->literal); | jv_free(yylval->literal); | |||
yylval->literal = jv_null(); | yylval->literal = jv_null(); | |||
} | } | |||
return tok; | return tok; | |||
} | } | |||
static block gen_dictpair(block k, block v) { | /* Returns string message if the block is a constant that is not valid as an | |||
return BLOCK(gen_subexp(k), gen_subexp(v), gen_op_simple(INSERT)); | * object key. */ | |||
static jv check_object_key(block k) { | ||||
if (block_is_const(k) && block_const_kind(k) != JV_KIND_STRING) { | ||||
char errbuf[15]; | ||||
return jv_string_fmt("Cannot use %s (%s) as object key", | ||||
jv_kind_name(block_const_kind(k)), | ||||
jv_dump_string_trunc(jv_copy(block_const(k)), errbuf, sizeof(errbuf))); | ||||
} | ||||
return jv_invalid(); | ||||
} | } | |||
static block gen_index(block obj, block key) { | static block gen_index(block obj, block key) { | |||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX)); | return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX)); | |||
} | } | |||
static block gen_index_opt(block obj, block key) { | static block gen_index_opt(block obj, block key) { | |||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX_OPT)); | return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX_OPT)); | |||
} | } | |||
skipping to change at line 187 | skipping to change at line 201 | |||
gen_subexp(gen_const(jv_string("end"))), | gen_subexp(gen_const(jv_string("end"))), | |||
gen_subexp(end), | gen_subexp(end), | |||
gen_op_simple(INSERT)); | gen_op_simple(INSERT)); | |||
return BLOCK(key, obj, gen_op_simple(idx_op)); | return BLOCK(key, obj, gen_op_simple(idx_op)); | |||
} | } | |||
static block constant_fold(block a, block b, int op) { | static block constant_fold(block a, block b, int op) { | |||
if (!block_is_single(a) || !block_is_const(a) || | if (!block_is_single(a) || !block_is_const(a) || | |||
!block_is_single(b) || !block_is_const(b)) | !block_is_single(b) || !block_is_const(b)) | |||
return gen_noop(); | 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)) | if (block_const_kind(a) != block_const_kind(b)) | |||
return gen_noop(); | return gen_noop(); | |||
jv res = jv_invalid(); | jv res = jv_invalid(); | |||
if (block_const_kind(a) == JV_KIND_NUMBER) { | if (block_const_kind(a) == JV_KIND_NUMBER) { | |||
double na = jv_number_value(block_const(a)); | double na = jv_number_value(block_const(a)); | |||
double nb = jv_number_value(block_const(b)); | double nb = jv_number_value(block_const(b)); | |||
switch (op) { | switch (op) { | |||
case '+': res = jv_number(na + nb); break; | case '+': res = jv_number(na + nb); break; | |||
skipping to change at line 247 | skipping to change at line 271 | |||
case '>': funcname = "_greater"; break; | case '>': funcname = "_greater"; break; | |||
case LESSEQ: funcname = "_lesseq"; break; | case LESSEQ: funcname = "_lesseq"; break; | |||
case GREATEREQ: funcname = "_greatereq"; break; | case GREATEREQ: funcname = "_greatereq"; break; | |||
} | } | |||
assert(funcname); | assert(funcname); | |||
return gen_call(funcname, BLOCK(gen_lambda(a), gen_lambda(b))); | return gen_call(funcname, BLOCK(gen_lambda(a), gen_lambda(b))); | |||
} | } | |||
static block gen_format(block a, jv fmt) { | static block gen_format(block a, jv fmt) { | |||
return BLOCK(a, gen_call("format", BLOCK(gen_lambda(gen_const(fmt))))); | return BLOCK(a, gen_call("format", gen_lambda(gen_const(fmt)))); | |||
} | } | |||
static block gen_definedor_assign(block object, block val) { | static block gen_definedor_assign(block object, block val) { | |||
block tmp = gen_op_var_fresh(STOREV, "tmp"); | block tmp = gen_op_var_fresh(STOREV, "tmp"); | |||
return BLOCK(gen_op_simple(DUP), | return BLOCK(gen_op_simple(DUP), | |||
val, tmp, | val, tmp, | |||
gen_call("_modify", BLOCK(gen_lambda(object), | gen_call("_modify", BLOCK(gen_lambda(object), | |||
gen_lambda(gen_definedor(gen_noop(), | gen_lambda(gen_definedor(gen_noop(), | |||
gen_op_bound(L OADV, tmp)))))); | gen_op_bound(L OADV, tmp)))))); | |||
} | } | |||
skipping to change at line 287 | skipping to change at line 311 | |||
Module Imports FuncDefs { | Module Imports FuncDefs { | |||
*answer = BLOCK($1, $2, $3); | *answer = BLOCK($1, $2, $3); | |||
} | } | |||
Module: | Module: | |||
%empty { | %empty { | |||
$$ = gen_noop(); | $$ = gen_noop(); | |||
} | | } | | |||
"module" Exp ';' { | "module" Exp ';' { | |||
if (!block_is_const($2)) { | if (!block_is_const($2)) { | |||
FAIL(@$, "Module metadata must be constant."); | FAIL(@$, "Module metadata must be constant"); | |||
$$ = gen_noop(); | $$ = gen_noop(); | |||
block_free($2); | ||||
} else { | } else { | |||
$$ = gen_module($2); | $$ = gen_module($2); | |||
} | } | |||
} | } | |||
Imports: | Imports: | |||
%empty { | %empty { | |||
$$ = gen_noop(); | $$ = gen_noop(); | |||
} | | } | | |||
Import Imports { | Import Imports { | |||
skipping to change at line 315 | skipping to change at line 340 | |||
} | | } | | |||
FuncDef FuncDefs { | FuncDef FuncDefs { | |||
$$ = block_bind($1, $2, OP_IS_CALL_PSEUDO); | $$ = block_bind($1, $2, OP_IS_CALL_PSEUDO); | |||
} | } | |||
Exp: | Exp: | |||
FuncDef Exp %prec FUNCDEF { | FuncDef Exp %prec FUNCDEF { | |||
$$ = block_bind_referenced($1, $2, OP_IS_CALL_PSEUDO); | $$ = block_bind_referenced($1, $2, OP_IS_CALL_PSEUDO); | |||
} | | } | | |||
Term "as" Pattern '|' Exp { | Term "as" Patterns '|' Exp { | |||
$$ = gen_destructure($1, $3, $5); | $$ = gen_destructure($1, $3, $5); | |||
} | | } | | |||
"reduce" Term "as" Patterns '(' Exp ';' Exp ')' { | ||||
"reduce" Term "as" Pattern '(' Exp ';' Exp ')' { | ||||
$$ = gen_reduce($2, $4, $6, $8); | $$ = gen_reduce($2, $4, $6, $8); | |||
} | | } | | |||
"foreach" Term "as" Pattern '(' Exp ';' Exp ';' Exp ')' { | "foreach" Term "as" Patterns '(' Exp ';' Exp ';' Exp ')' { | |||
$$ = gen_foreach($2, $4, $6, $8, $10); | $$ = gen_foreach($2, $4, $6, $8, $10); | |||
} | | } | | |||
"foreach" Term "as" Pattern '(' Exp ';' Exp ')' { | "foreach" Term "as" Patterns '(' Exp ';' Exp ')' { | |||
$$ = gen_foreach($2, $4, $6, $8, gen_noop()); | $$ = gen_foreach($2, $4, $6, $8, gen_noop()); | |||
} | | } | | |||
"if" Exp "then" Exp ElseBody { | "if" Exp "then" Exp ElseBody { | |||
$$ = gen_cond($2, $4, $5); | $$ = gen_cond($2, $4, $5); | |||
} | | } | | |||
"if" Exp "then" error { | "if" Exp "then" error { | |||
FAIL(@$, "Possibly unterminated 'if' statement"); | FAIL(@$, "Possibly unterminated 'if' statement"); | |||
$$ = $2; | $$ = $2; | |||
} | | } | | |||
skipping to change at line 472 | skipping to change at line 496 | |||
Exp ">=" Exp { | Exp ">=" Exp { | |||
$$ = gen_binop($1, $3, GREATEREQ); | $$ = gen_binop($1, $3, GREATEREQ); | |||
} | | } | | |||
Term { | Term { | |||
$$ = $1; | $$ = $1; | |||
} | } | |||
Import: | Import: | |||
"import" String "as" '$' IDENT ';' { | 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); | jv v = block_const($2); | |||
// XXX Make gen_import take only blocks and the int is_data so we | // XXX Make gen_import take only blocks and the int is_data so we | |||
// don't have to free so much stuff here | // don't have to free so much stuff here | |||
$$ = gen_import(jv_string_value(v), gen_noop(), jv_string_value($5), 1); | $$ = gen_import(jv_string_value(v), jv_string_value($5), 1); | |||
block_free($2); | block_free($2); | |||
jv_free($5); | jv_free($5); | |||
jv_free(v); | jv_free(v); | |||
} | | } | | |||
"import" String "as" IDENT ';' { | "import" ImportFrom "as" IDENT { | |||
jv v = block_const($2); | jv v = block_const($2); | |||
$$ = gen_import(jv_string_value(v), gen_noop(), jv_string_value($4), 0); | $$ = gen_import(jv_string_value(v), jv_string_value($4), 0); | |||
block_free($2); | block_free($2); | |||
jv_free($4); | jv_free($4); | |||
jv_free(v); | jv_free(v); | |||
} | | } | | |||
"include" String ';' { | "include" ImportFrom { | |||
jv v = block_const($2); | jv v = block_const($2); | |||
$$ = gen_import(jv_string_value(v), gen_noop(), NULL, 0); | $$ = gen_import(jv_string_value(v), NULL, 0); | |||
block_free($2); | block_free($2); | |||
jv_free(v); | jv_free(v); | |||
} | | } | |||
"import" String "as" IDENT Exp ';' { | ||||
if (!block_is_const($5)) { | ImportFrom: | |||
FAIL(@$, "Module metadata must be constant."); | String { | |||
$$ = gen_noop(); | if (!block_is_const($1)) { | |||
} else { | FAIL(@$, "Import path must be constant"); | |||
jv v = block_const($2); | $$ = gen_const(jv_string("")); | |||
$$ = gen_import(jv_string_value(v), $5, jv_string_value($4), 0); | block_free($1); | |||
jv_free(v); | ||||
} | ||||
block_free($2); | ||||
jv_free($4); | ||||
} | | ||||
"include" String Exp ';' { | ||||
if (!block_is_const($3)) { | ||||
FAIL(@$, "Module metadata must be constant."); | ||||
$$ = gen_noop(); | ||||
} else { | ||||
jv v = block_const($2); | ||||
$$ = gen_import(jv_string_value(v), $3, NULL, 0); | ||||
jv_free(v); | ||||
} | ||||
block_free($2); | ||||
} | | ||||
"import" String "as" '$' IDENT Exp ';' { | ||||
if (!block_is_const($6)) { | ||||
FAIL(@$, "Module metadata must be constant."); | ||||
$$ = gen_noop(); | ||||
} else { | } else { | |||
jv v = block_const($2); | $$ = $1; | |||
$$ = gen_import(jv_string_value(v), $6, jv_string_value($5), 1); | ||||
jv_free(v); | ||||
} | } | |||
block_free($2); | ||||
jv_free($5); | ||||
} | } | |||
FuncDef: | FuncDef: | |||
"def" IDENT ':' Exp ';' { | "def" IDENT ':' Exp ';' { | |||
$$ = gen_function(jv_string_value($2), gen_noop(), $4); | $$ = gen_function(jv_string_value($2), gen_noop(), $4); | |||
jv_free($2); | jv_free($2); | |||
} | | } | | |||
"def" IDENT '(' Params ')' ':' Exp ';' { | "def" IDENT '(' Params ')' ':' Exp ';' { | |||
$$ = gen_function(jv_string_value($2), $4, $7); | $$ = gen_function(jv_string_value($2), $4, $7); | |||
skipping to change at line 751 | skipping to change at line 771 | |||
} | | } | | |||
Args ';' Arg { | Args ';' Arg { | |||
$$ = BLOCK($1, $3); | $$ = BLOCK($1, $3); | |||
} | } | |||
Arg: | Arg: | |||
Exp { | Exp { | |||
$$ = gen_lambda($1); | $$ = gen_lambda($1); | |||
} | } | |||
RepPatterns: | ||||
RepPatterns "?//" Pattern { | ||||
$$ = BLOCK($1, gen_destructure_alt($3)); | ||||
} | | ||||
Pattern { | ||||
$$ = gen_destructure_alt($1); | ||||
} | ||||
Patterns: | ||||
RepPatterns "?//" Pattern { | ||||
$$ = BLOCK($1, $3); | ||||
} | | ||||
Pattern { | ||||
$$ = $1; | ||||
} | ||||
Pattern: | Pattern: | |||
'$' IDENT { | '$' IDENT { | |||
$$ = gen_op_unbound(STOREV, jv_string_value($2)); | $$ = gen_op_unbound(STOREV, jv_string_value($2)); | |||
jv_free($2); | jv_free($2); | |||
} | | } | | |||
'[' ArrayPats ']' { | '[' ArrayPats ']' { | |||
$$ = BLOCK($2, gen_op_simple(POP)); | $$ = BLOCK($2, gen_op_simple(POP)); | |||
} | | } | | |||
'{' ObjPats '}' { | '{' ObjPats '}' { | |||
$$ = BLOCK($2, gen_op_simple(POP)); | $$ = BLOCK($2, gen_op_simple(POP)); | |||
skipping to change at line 783 | skipping to change at line 819 | |||
$$ = $1; | $$ = $1; | |||
} | | } | | |||
ObjPats ',' ObjPat { | ObjPats ',' ObjPat { | |||
$$ = BLOCK($1, $3); | $$ = BLOCK($1, $3); | |||
} | } | |||
ObjPat: | ObjPat: | |||
'$' IDENT { | '$' IDENT { | |||
$$ = gen_object_matcher(gen_const($2), gen_op_unbound(STOREV, jv_string_value( $2))); | $$ = gen_object_matcher(gen_const($2), gen_op_unbound(STOREV, jv_string_value( $2))); | |||
} | | } | | |||
'$' IDENT ':' Pattern { | ||||
$$ = gen_object_matcher(gen_const($2), BLOCK(gen_op_simple(DUP), gen_op_unboun | ||||
d(STOREV, jv_string_value($2)), $4)); | ||||
} | | ||||
IDENT ':' Pattern { | IDENT ':' Pattern { | |||
$$ = gen_object_matcher(gen_const($1), $3); | $$ = gen_object_matcher(gen_const($1), $3); | |||
} | | } | | |||
Keyword ':' Pattern { | Keyword ':' Pattern { | |||
$$ = gen_object_matcher(gen_const($1), $3); | $$ = gen_object_matcher(gen_const($1), $3); | |||
} | | } | | |||
String ':' Pattern { | String ':' Pattern { | |||
$$ = gen_object_matcher($1, $3); | $$ = gen_object_matcher($1, $3); | |||
} | | } | | |||
'(' Exp ')' ':' Pattern { | '(' Exp ')' ':' Pattern { | |||
jv msg = check_object_key($2); | ||||
if (jv_is_valid(msg)) { | ||||
FAIL(@$, jv_string_value(msg)); | ||||
} | ||||
jv_free(msg); | ||||
$$ = gen_object_matcher($2, $5); | $$ = gen_object_matcher($2, $5); | |||
} | | ||||
error ':' Pattern { | ||||
FAIL(@$, "May need parentheses around object key expression"); | ||||
$$ = $3; | ||||
} | } | |||
Keyword: | Keyword: | |||
"as" { | "as" { | |||
$$ = jv_string("as"); | $$ = jv_string("as"); | |||
} | | } | | |||
"def" { | "def" { | |||
$$ = jv_string("def"); | $$ = jv_string("def"); | |||
} | | } | | |||
"module" { | "module" { | |||
skipping to change at line 886 | skipping to change at line 934 | |||
} | } | |||
| '$' IDENT { | | '$' IDENT { | |||
$$ = gen_dictpair(gen_const($2), | $$ = gen_dictpair(gen_const($2), | |||
gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_ value($2)))); | gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_ value($2)))); | |||
} | } | |||
| IDENT { | | IDENT { | |||
$$ = gen_dictpair(gen_const(jv_copy($1)), | $$ = gen_dictpair(gen_const(jv_copy($1)), | |||
gen_index(gen_noop(), gen_const($1))); | gen_index(gen_noop(), gen_const($1))); | |||
} | } | |||
| '(' Exp ')' ':' ExpD { | | '(' Exp ')' ':' ExpD { | |||
jv msg = check_object_key($2); | ||||
if (jv_is_valid(msg)) { | ||||
FAIL(@$, jv_string_value(msg)); | ||||
} | ||||
jv_free(msg); | ||||
$$ = gen_dictpair($2, $5); | $$ = gen_dictpair($2, $5); | |||
} | } | |||
| '(' error ')' ':' ExpD { $$ = $5; } | | error ':' ExpD { | |||
FAIL(@$, "May need parentheses around object key expression"); | ||||
$$ = $3; | ||||
} | ||||
%% | %% | |||
int jq_parse(struct locfile* locations, block* answer) { | int jq_parse(struct locfile* locations, block* answer) { | |||
struct lexer_param scanner; | struct lexer_param scanner; | |||
YY_BUFFER_STATE buf; | YY_BUFFER_STATE buf; | |||
jq_yylex_init_extra(0, &scanner.lexer); | jq_yylex_init_extra(0, &scanner.lexer); | |||
buf = jq_yy_scan_bytes(locations->data, locations->length, scanner.lexer); | buf = jq_yy_scan_bytes(locations->data, locations->length, scanner.lexer); | |||
int errors = 0; | int errors = 0; | |||
*answer = gen_noop(); | *answer = gen_noop(); | |||
yyparse(answer, &errors, locations, &scanner); | yyparse(answer, &errors, locations, &scanner); | |||
End of changes. 26 change blocks. | ||||
52 lines changed or deleted | 109 lines changed or added |