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:
Stephen Dolan committed 2012-09-17 20:41:57 +01:00
1 parent 50ebb036c4
commit 3db27b01a1
2 files changed
+98 -2

No files matched your search

+76 -1
View File
@@ -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
View File
@@ -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 {