diff options
-rw-r--r-- | c/lexer.l | 77 | ||||
-rw-r--r-- | c/parser.y | 23 |
2 files changed, 98 insertions, 2 deletions
@@ -11,11 +11,23 @@ %} +%s IN_PAREN +%s IN_BRACKET +%s IN_BRACE +%s IN_QQINTERP +%x IN_QQSTRING +%{ + static int enter(int opening, int state, yyscan_t yyscanner); + static int try_exit(int closing, int state, yyscan_t yyscanner); +%} + %option noyywrap nounput noinput nodefault %option reentrant %option extra-type="int" %option bison-bridge bison-locations %option prefix="jq_yy" +%option stack + %% @@ -37,7 +49,16 @@ "*=" { return SETMULT; } "/=" { return SETDIV; } "//=" { return SETDEFINEDOR; } -"."|"="|";"|"["|"]"|","|":"|"("|")"|"{"|"}"|"|"|"+"|"-"|"*"|"/"|"\$" { return yytext[0];} +"."|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"\$" { return yytext[0];} + +"["|"{"|"(" { + return enter(yytext[0], YY_START, yyscanner); +} + +"]"|"}"|")" { + return try_exit(yytext[0], YY_START, yyscanner); +} + \"(\\.|[^\\\"])*\" | -?[0-9.]+([eE][+-]?[0-9]+)? { @@ -48,6 +69,25 @@ yylval->literal = jv_invalid_with_msg(jv_string("Unterminated string")); return LITERAL; } + +"@(" { + yy_push_state(IN_QQSTRING, yyscanner); + return QQSTRING_START; +} + +<IN_QQSTRING>{ + "%(" { + return enter(QQSTRING_INTERP_START, YY_START, yyscanner); + } + [a-z]+ { + yylval->literal = jv_string_sized(yytext, yyleng); + return QQSTRING_TEXT; + } + ")" { + yy_pop_state(yyscanner); + return QQSTRING_END; + } +} [[:alnum:]]+ { yylval->literal = jv_string(yytext); return IDENT;} @@ -63,3 +103,38 @@ "false" { return FALSE; } "null" { return NULL; } */ +static int try_exit(int c, int state, yyscan_t yyscanner) { + char match = 0; + int ret; + switch (state) { + case IN_PAREN: match = ret = ')'; break; + case IN_BRACKET: match = ret = ']'; break; + case IN_BRACE: match = ret = '}'; break; + + case IN_QQINTERP: + match = ')'; + ret = QQSTRING_INTERP_END; + break; + } + assert(match); + if (match == c) { + yy_pop_state(yyscanner); + return ret; + } else { + // FIXME: should we pop? Give a better error at least + return INVALID_CHARACTER; + } +} + +static int enter(int c, int currstate, yyscan_t yyscanner) { + int state = 0; + switch (c) { + case '(': state = IN_PAREN; break; + case '[': state = IN_BRACKET; break; + case '{': state = IN_BRACE; break; + case QQSTRING_INTERP_START: state = IN_QQINTERP; break; + } + assert(state); + yy_push_state(state, yyscanner); + return c; +} @@ -61,6 +61,12 @@ %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 '|' @@ -74,7 +80,7 @@ %left '*' '/' -%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody +%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString %{ #include "lexer.yy.h" #define FAIL(loc, msg) \ @@ -264,11 +270,26 @@ Exp "==" Exp { $$ = gen_binop($1, $3, EQ); } | +QQSTRING_START QQString QQSTRING_END { + $$ = $2; +} | + Term { $$ = $1; } +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, $3, '+'); +} + ElseBody: "elif" Exp "then" Exp ElseBody { |