From afec2544f1793d11c47356c32ef74ce8c4035263 Mon Sep 17 00:00:00 2001 From: Stephen Dolan Date: Sat, 20 Oct 2012 00:09:20 +0100 Subject: [PATCH] A string interpolation syntax that I don't hate. Also tests. You can interpolate values into strings using \(this syntax), e.g. "best \("str" + "ing") ever" --- lexer.l | 24 +++++------------------- opcode_list.h | 1 + parser.y | 18 ++++++++++-------- testdata | 14 +++++++++++++- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/lexer.l b/lexer.l index a353d89bf2..1921f380fb 100644 --- a/lexer.l +++ b/lexer.l @@ -61,45 +61,31 @@ } -\"(\\.|[^\\\"])*\" | -?[0-9.]+([eE][+-]?[0-9]+)? { yylval->literal = jv_parse_sized(yytext, yyleng); return LITERAL; } -\"(\\.|[^\\\"])* { - yylval->literal = jv_invalid_with_msg(jv_string("Unterminated string")); - return LITERAL; -} - -"@(" { +"\"" { yy_push_state(IN_QQSTRING, yyscanner); return QQSTRING_START; } { - "%(" { + "\\(" { return enter(QQSTRING_INTERP_START, YY_START, yyscanner); } - "%"[^(] { - return INVALID_CHARACTER; - } - ")" { + "\"" { yy_pop_state(yyscanner); return QQSTRING_END; } - "\\"[)%] { - char text[2] = {yytext[1], 0}; - yylval->literal = jv_string(text); - return QQSTRING_TEXT; - } - (\\[^u)%]|\\u[a-zA-Z0-9]{0,4})+ { + (\\[^u(]|\\u[a-zA-Z0-9]{0,4})+ { /* pass escapes to the json parser */ jv escapes = jv_string_fmt("\"%.*s\"", yyleng, yytext); yylval->literal = jv_parse_sized(jv_string_value(escapes), jv_string_length(jv_copy(escapes))); jv_free(escapes); return QQSTRING_TEXT; } - [^\\)%]+ { + [^\\\"]+ { yylval->literal = jv_string_sized(yytext, yyleng); return QQSTRING_TEXT; } diff --git a/opcode_list.h b/opcode_list.h index 30e7203d6f..306373fbf0 100644 --- a/opcode_list.h +++ b/opcode_list.h @@ -18,6 +18,7 @@ OP(INSERT, NONE, 4, 2) OP(ASSIGN, VARIABLE, 3, 0) OP(CALL_BUILTIN_1_1, CFUNC, 1, 1) +OP(CALL_BUILTIN_2_1, CFUNC, 2, 1) OP(CALL_BUILTIN_3_1, CFUNC, 3, 1) OP(CALL_1_1, UFUNC, 1, 1) diff --git a/parser.y b/parser.y index 8c4b45663a..0effe07cdf 100644 --- a/parser.y +++ b/parser.y @@ -81,7 +81,7 @@ %left '*' '/' -%type Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs +%type Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs String %{ #include "lexer.gen.h" #define FAIL(loc, msg) \ @@ -295,14 +295,19 @@ Exp ">=" Exp { $$ = gen_binop($1, $3, GREATEREQ); } | -QQSTRING_START QQString QQSTRING_END { - $$ = $2; +String { + $$ = $1; } | Term { $$ = $1; } +String: +QQSTRING_START QQString QQSTRING_END { + $$ = $2; +} + FuncDef: "def" IDENT ':' Exp ';' { block body = block_join($4, gen_op_simple(RET)); @@ -416,11 +421,8 @@ 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); +| String ':' ExpD { + $$ = gen_dictpair($1, $3); } | IDENT { $$ = gen_dictpair(gen_op_const(LOADK, jv_copy($1)), diff --git a/testdata b/testdata index c37dee9304..ea0b1cc6f1 100644 --- a/testdata +++ b/testdata @@ -31,7 +31,19 @@ null null [] -# FIXME: string literals +# We test escapes by matching them against Unicode codepoints +# FIXME: more tests needed for weird unicode stuff (e.g. utf16 pairs) +"Aa\r\n\t\b\f\u03bc" +null +"Aa\u000d\u000a\u0009\u0008\u000c\u03bc" + +. +"Aa\r\n\t\b\f\u03bc" +"Aa\u000d\u000a\u0009\u0008\u000c\u03bc" + +"inter\("pol" + "ation")" +null +"interpolation" # # Dictionary construction syntax