summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Dolan <mu@netsoc.tcd.ie>2012-10-20 00:09:20 +0100
committerStephen Dolan <mu@netsoc.tcd.ie>2012-10-20 00:09:20 +0100
commitafec2544f1793d11c47356c32ef74ce8c4035263 (patch)
tree91bbf823e4ae9d1ea212cd2b3c27cab26eef0b47
parent6828ec9852583e1b1a84878235e9d61ab99930eb (diff)
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"
-rw-r--r--lexer.l24
-rw-r--r--opcode_list.h1
-rw-r--r--parser.y18
-rw-r--r--testdata14
4 files changed, 29 insertions, 28 deletions
diff --git a/lexer.l b/lexer.l
index a353d89b..1921f380 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;
}
<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;
}
diff --git a/opcode_list.h b/opcode_list.h
index 30e7203d..306373fb 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 8c4b4566..0effe07c 100644
--- a/parser.y
+++ b/parser.y
@@ -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) \
@@ -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 c37dee93..ea0b1cc6 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