First pass at string interpolation.
Requires a lexer hack, but a surprisingly un-hideous one. The lexer maintains a stack of bracket characters so that it can match parens/brackets/braces to tell where a subexp nested in a string actually ends.
This commit is contained in:
1 parent
50ebb036c4
commit
3db27b01a1
2 files changed
+98
-2
No files matched your search
@@ -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 noyywrap nounput noinput nodefault
|
||||||
%option reentrant
|
%option reentrant
|
||||||
%option extra-type="int"
|
%option extra-type="int"
|
||||||
%option bison-bridge bison-locations
|
%option bison-bridge bison-locations
|
||||||
%option prefix="jq_yy"
|
%option prefix="jq_yy"
|
||||||
|
%option stack
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
@@ -37,7 +49,16 @@
|
|||||||
"*=" { return SETMULT; }
|
"*=" { return SETMULT; }
|
||||||
"/=" { return SETDIV; }
|
"/=" { return SETDIV; }
|
||||||
"//=" { return SETDEFINEDOR; }
|
"//=" { 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]+)? {
|
-?[0-9.]+([eE][+-]?[0-9]+)? {
|
||||||
@@ -49,6 +70,25 @@
|
|||||||
return LITERAL;
|
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;}
|
[[:alnum:]]+ { yylval->literal = jv_string(yytext); return IDENT;}
|
||||||
|
|
||||||
@@ -63,3 +103,38 @@
|
|||||||
"false" { return FALSE; }
|
"false" { return FALSE; }
|
||||||
"null" { return NULL; }
|
"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;
|
||||||
|
}
|
||||||
+22
-1
@@ -61,6 +61,12 @@
|
|||||||
%token SETDIV "/="
|
%token SETDIV "/="
|
||||||
%token SETDEFINEDOR "//="
|
%token SETDEFINEDOR "//="
|
||||||
|
|
||||||
|
%token QQSTRING_START
|
||||||
|
%token <literal> QQSTRING_TEXT
|
||||||
|
%token QQSTRING_INTERP_START
|
||||||
|
%token QQSTRING_INTERP_END
|
||||||
|
%token QQSTRING_END
|
||||||
|
|
||||||
/* revolting hack */
|
/* revolting hack */
|
||||||
%left ';'
|
%left ';'
|
||||||
%left '|'
|
%left '|'
|
||||||
@@ -74,7 +80,7 @@
|
|||||||
%left '*' '/'
|
%left '*' '/'
|
||||||
|
|
||||||
|
|
||||||
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody
|
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString
|
||||||
%{
|
%{
|
||||||
#include "lexer.yy.h"
|
#include "lexer.yy.h"
|
||||||
#define FAIL(loc, msg) \
|
#define FAIL(loc, msg) \
|
||||||
@@ -264,11 +270,26 @@ Exp "==" Exp {
|
|||||||
$$ = gen_binop($1, $3, EQ);
|
$$ = gen_binop($1, $3, EQ);
|
||||||
} |
|
} |
|
||||||
|
|
||||||
|
QQSTRING_START QQString QQSTRING_END {
|
||||||
|
$$ = $2;
|
||||||
|
} |
|
||||||
|
|
||||||
Term {
|
Term {
|
||||||
$$ = $1;
|
$$ = $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:
|
ElseBody:
|
||||||
"elif" Exp "then" Exp ElseBody {
|
"elif" Exp "then" Exp ElseBody {
|
||||||
|
|||||||
Reference in new issue
Block a user