Skip to content

Commit

Permalink
A string interpolation syntax that I don't hate. Also tests.
Browse files Browse the repository at this point in the history
You can interpolate values into strings using \(this syntax), e.g.

    "best \("str" + "ing") ever"
  • Loading branch information
stedolan committed Oct 19, 2012
1 parent 6828ec9 commit afec254
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 28 deletions.
24 changes: 5 additions & 19 deletions lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

<IN_QQSTRING>{
"%(" {
"\\(" {
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;
}
Expand Down
1 change: 1 addition & 0 deletions opcode_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 10 additions & 8 deletions parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
%left '*' '/'


%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs String
%{
#include "lexer.gen.h"
#define FAIL(loc, msg) \
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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)),
Expand Down
14 changes: 13 additions & 1 deletion testdata
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit afec254

Please sign in to comment.