Files
jq/parser.y
T

480 lines
11 KiB
Plaintext
Raw Normal View History

2012-08-16 01:00:30 +01:00
%{
#include <stdio.h>
#include <string.h>
#include "compile.h"
struct lexer_param;
2012-08-16 01:00:30 +01:00
%}
%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)
}
2012-08-16 01:00:30 +01:00
%locations
%error-verbose
2012-08-16 01:00:30 +01:00
%define api.pure
%union {
jv literal;
2012-08-16 01:00:30 +01:00
block blk;
}
%destructor { jv_free($$); } <literal>
%destructor { block_free($$); } <blk>
2012-08-16 01:00:30 +01:00
%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}
2012-08-16 01:00:30 +01:00
%token INVALID_CHARACTER
%token <literal> IDENT
%token <literal> LITERAL
2012-08-16 01:00:30 +01:00
%token EQ "=="
2012-10-23 17:01:39 +02:00
%token NEQ "!="
2012-09-04 15:34:34 +01:00
%token DEFINEDOR "//"
2012-08-16 01:00:30 +01:00
%token AS "as"
2012-08-21 18:14:13 +01:00
%token DEF "def"
2012-09-04 15:34:34 +01:00
%token IF "if"
%token THEN "then"
%token ELSE "else"
2012-09-04 16:05:24 +01:00
%token ELSE_IF "elif"
2012-09-04 15:34:34 +01:00
%token END "end"
%token AND "and"
%token OR "or"
%token SETPIPE "|="
%token SETPLUS "+="
%token SETMINUS "-="
%token SETMULT "*="
%token SETDIV "/="
%token SETDEFINEDOR "//="
2012-10-07 22:34:12 +01:00
%token LESSEQ "<="
%token GREATEREQ ">="
2012-10-24 11:42:25 -07:00
%token CONTAINS "contains"
2012-09-17 20:41:57 +01:00
%token QQSTRING_START
%token <literal> QQSTRING_TEXT
%token QQSTRING_INTERP_START
%token QQSTRING_INTERP_END
%token QQSTRING_END
/* revolting hack */
%left ';'
%left '|'
%left ','
2012-09-04 15:34:34 +01:00
%right "//"
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETDEFINEDOR
%left OR
%left AND
2012-10-24 11:42:25 -07:00
%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ CONTAINS
%left '+' '-'
2012-09-10 16:57:17 +01:00
%left '*' '/'
2012-08-16 01:00:30 +01:00
2012-09-04 15:34:34 +01:00
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs String
2012-08-16 01:00:30 +01:00
%{
#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)++;
locfile_locate(locations, *loc, "error: %s", s);
2012-08-16 01:00:30 +01:00
}
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;
while (1) {
int tok = jq_yylex(yylval, yylloc, lexer);
if (tok == INVALID_CHARACTER) {
FAIL(*yylloc, "Invalid character");
} else {
2012-09-18 13:22:22 +01:00
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;
}
}
}
2012-08-16 01:00:30 +01:00
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;
2012-09-10 16:57:17 +01:00
case '*': funcname = "_multiply"; break;
case '/': funcname = "_divide"; break;
case EQ: funcname = "_equal"; break;
2012-10-23 17:01:39 +02:00
case NEQ: funcname = "_notequal"; break;
2012-10-07 22:34:12 +01:00
case '<': funcname = "_less"; break;
case '>': funcname = "_greater"; break;
case LESSEQ: funcname = "_lesseq"; break;
case GREATEREQ: funcname = "_greatereq"; break;
2012-10-24 11:42:25 -07:00
case CONTAINS: funcname = "_contains"; 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;
}
2012-09-18 13:22:22 +01:00
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);
}
2012-08-16 01:00:30 +01:00
%}
%%
2012-09-18 10:17:38 +01:00
TopLevel:
Exp {
*answer = $1;
} |
FuncDefs {
*answer = $1;
}
FuncDefs:
/* empty */ {
$$ = gen_noop();
} |
FuncDef FuncDefs {
$$ = block_join($1, $2);
}
2012-08-16 01:00:30 +01:00
Exp:
2012-09-18 10:17:38 +01:00
FuncDef Exp %prec ';' {
$$ = block_bind($1, $2, OP_IS_CALL_PSEUDO);
2012-08-26 14:25:56 +01:00
} |
2012-08-16 01:00:30 +01:00
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);
2012-08-16 01:00:30 +01:00
} |
2012-09-04 16:05:24 +01:00
"if" Exp "then" Exp ElseBody {
$$ = gen_cond($2, $4, $5);
2012-09-04 15:34:34 +01:00
} |
2012-09-11 10:12:25 +01:00
"if" Exp error {
FAIL(@$, "Possibly unterminated 'if' statment");
$$ = $2;
} |
2012-09-04 15:34:34 +01:00
2012-08-27 10:11:55 +01:00
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);
} |
2012-09-04 15:34:34 +01:00
Exp "//" Exp {
$$ = gen_definedor($1, $3);
} |
Exp "//=" Exp {
$$ = gen_update($1, gen_definedor(gen_noop(), $3), 0);
} |
2012-08-27 10:11:55 +01:00
Exp "|=" Exp {
$$ = gen_update($1, $3, 0);
2012-08-27 10:11:55 +01:00
} |
2012-08-16 01:00:30 +01:00
Exp '|' Exp {
$$ = block_join($1, $3);
} |
Exp ',' Exp {
$$ = gen_both($1, $3);
} |
2012-08-16 01:16:08 +01:00
Exp '+' Exp {
$$ = gen_binop($1, $3, '+');
} |
Exp "+=" Exp {
$$ = gen_update($1, $3, '+');
} |
Exp '-' Exp {
$$ = gen_binop($1, $3, '-');
2012-08-16 01:16:08 +01:00
} |
Exp "-=" Exp {
$$ = gen_update($1, $3, '-');
} |
2012-09-10 16:57:17 +01:00
Exp '*' Exp {
$$ = gen_binop($1, $3, '*');
} |
Exp "*=" Exp {
$$ = gen_update($1, $3, '*');
} |
2012-09-10 16:57:17 +01:00
Exp '/' Exp {
$$ = gen_binop($1, $3, '/');
} |
Exp "/=" Exp {
$$ = gen_update($1, $3, '/');
} |
Exp "==" Exp {
$$ = gen_binop($1, $3, EQ);
} |
2012-10-23 17:01:39 +02:00
Exp "!=" Exp {
$$ = gen_binop($1, $3, NEQ);
} |
2012-10-07 22:34:12 +01:00
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);
} |
2012-10-24 11:42:25 -07:00
Exp "contains" Exp {
$$ = gen_binop($1, $3, CONTAINS);
} |
2012-08-16 01:00:30 +01:00
Term {
$$ = $1;
}
String:
QQSTRING_START QQString QQSTRING_END {
$$ = $2;
}
2012-09-18 10:17:38 +01:00
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);
}
2012-09-11 10:12:25 +01:00
2012-09-17 20:41:57 +01:00
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 {
2012-09-18 13:22:22 +01:00
$$ = gen_binop($1, gen_format($3), '+');
2012-09-17 20:41:57 +01:00
}
2012-09-11 10:12:25 +01:00
2012-09-04 16:05:24 +01:00
ElseBody:
"elif" Exp "then" Exp ElseBody {
$$ = gen_cond($2, $4, $5);
} |
"else" Exp "end" {
$$ = $2;
}
2012-08-16 01:00:30 +01:00
ExpD:
ExpD '|' ExpD {
$$ = block_join($1, $3);
} |
Term {
$$ = $1;
}
Term:
'.' {
$$ = gen_noop();
} |
Term '.' IDENT {
$$ = gen_index($1, gen_op_const(LOADK, $3));
2012-08-16 01:00:30 +01:00
} |
'.' IDENT {
$$ = gen_index(gen_noop(), gen_op_const(LOADK, $2));
2012-08-16 01:00:30 +01:00
} |
/* FIXME: string literals */
Term '[' Exp ']' {
$$ = gen_index($1, $3);
} |
Term '[' ']' {
$$ = block_join($1, gen_op_simple(EACH));
} |
LITERAL {
$$ = gen_op_const(LOADK, $1);
2012-08-16 01:00:30 +01:00
} |
2012-10-25 00:13:39 +01:00
String {
$$ = $1;
} |
2012-08-16 01:00:30 +01:00
'(' Exp ')' {
$$ = $2;
} |
'[' Exp ']' {
$$ = gen_collect($2);
} |
'[' ']' {
$$ = gen_op_const(LOADK, jv_array());
2012-08-16 01:00:30 +01:00
} |
'{' MkDict '}' {
$$ = gen_subexp(gen_op_const(LOADK, jv_object()));
2012-08-16 01:00:30 +01:00
block_append(&$$, $2);
block_append(&$$, gen_op_simple(POP));
} |
'$' IDENT {
$$ = gen_location(@$, gen_op_var_unbound(LOADV, jv_string_value($2)));
jv_free($2);
2012-08-21 18:14:13 +01:00
} |
IDENT {
$$ = gen_location(@$, gen_op_call(CALL_1_1, gen_op_block_unbound(CLOSURE_REF, jv_string_value($1))));
jv_free($1);
2012-08-26 14:25:56 +01:00
} |
IDENT '(' Exp ')' {
2012-08-26 14:25:56 +01:00
$$ = gen_op_call(CALL_1_1,
block_join(gen_op_block_unbound(CLOSURE_REF, jv_string_value($1)),
2012-08-26 14:25:56 +01:00
block_bind(gen_op_block_defn(CLOSURE_CREATE,
"lambda",
block_join($3, gen_op_simple(RET))),
2012-08-26 14:25:56 +01:00
gen_noop(), OP_IS_CALL_PSEUDO)));
$$ = gen_location(@1, $$);
jv_free($1);
2012-09-11 10:12:25 +01:00
} |
'(' error ')' { $$ = gen_noop(); } |
'[' error ']' { $$ = gen_noop(); } |
Term '[' error ']' { $$ = $1; } |
'{' error '}' { $$ = gen_noop(); }
2012-08-16 01:00:30 +01:00
MkDict:
{
$$=gen_noop();
2012-09-11 10:12:25 +01:00
} |
MkDictPair { $$ = $1; }
2012-08-16 01:00:30 +01:00
| MkDictPair ',' MkDict { $$=block_join($1, $3); }
2012-09-11 10:12:25 +01:00
| error ',' MkDict { $$ = $3; }
2012-08-16 01:00:30 +01:00
MkDictPair
: IDENT ':' ExpD {
$$ = gen_dictpair(gen_op_const(LOADK, $1), $3);
2012-08-16 01:00:30 +01:00
}
| String ':' ExpD {
$$ = gen_dictpair($1, $3);
2012-09-03 13:36:12 +01:00
}
2012-08-16 01:00:30 +01:00
| IDENT {
$$ = gen_dictpair(gen_op_const(LOADK, jv_copy($1)),
gen_index(gen_noop(), gen_op_const(LOADK, $1)));
2012-08-16 01:00:30 +01:00
}
| '(' Exp ')' ':' ExpD {
$$ = gen_dictpair($2, $5);
}
2012-09-11 10:12:25 +01:00
| '(' error ')' ':' ExpD { $$ = $5; }
2012-08-16 01:00:30 +01:00
%%
int jq_parse(struct locfile* locations, block* answer) {
struct lexer_param scanner;
2012-08-16 01:00:30 +01:00
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;
2012-08-16 01:00:30 +01:00
}
2012-09-18 10:17:38 +01:00
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;
}