Move everything around - delete old Haskell code, clean up build.
This commit is contained in:
1 parent
25cbab056b
commit
a4eea165bb
35 files changed
+16
-376
No files matched your search
@@ -0,0 +1,438 @@
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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($$); } <literal>
|
||||
%destructor { block_free($$); } <blk>
|
||||
|
||||
%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 <literal> IDENT
|
||||
%token <literal> 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 QQSTRING_START
|
||||
%token <literal> 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
|
||||
%left '+' '-'
|
||||
%left '*' '/'
|
||||
|
||||
|
||||
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs
|
||||
%{
|
||||
#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;
|
||||
}
|
||||
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);
|
||||
} |
|
||||
|
||||
QQSTRING_START QQString QQSTRING_END {
|
||||
$$ = $2;
|
||||
} |
|
||||
|
||||
Term {
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
| LITERAL ':' ExpD {
|
||||
if (jv_get_kind($1) != JV_KIND_STRING) {
|
||||
FAIL(@1, "Object keys must be strings");
|
||||
}
|
||||
$$ = gen_dictpair(gen_op_const(LOADK, $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;
|
||||
}
|
||||
Reference in new issue
Block a user