Skip to content

Commit

Permalink
Added interpolated strings
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Marler committed Mar 8, 2018
1 parent bc85c44 commit 37a6f8f
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 13 deletions.
4 changes: 3 additions & 1 deletion src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -4470,6 +4470,7 @@ struct ASTBase
size_t len; // number of code units
ubyte sz = 1; // 1: char, 2: wchar, 4: dchar
char postfix = 0; // 'c', 'w', 'd'
bool interpolate = false; // true if prefixed with 'i'

extern (D) this(Loc loc, char* string)
{
Expand All @@ -4487,12 +4488,13 @@ struct ASTBase
this.sz = 1; // work around LDC bug #1286
}

extern (D) this(Loc loc, void* string, size_t len, char postfix)
extern (D) this(Loc loc, void* string, size_t len, char postfix, bool interpolate = false)
{
super(loc, TOK.string_, __traits(classInstanceSize, StringExp));
this.string = cast(char*)string;
this.len = len;
this.postfix = postfix;
this.interpolate = interpolate;
this.sz = 1; // work around LDC bug #1286
}

Expand Down
4 changes: 3 additions & 1 deletion src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -3004,6 +3004,7 @@ extern (C++) final class StringExp : Expression
ubyte sz = 1; // 1: char, 2: wchar, 4: dchar
ubyte committed; // !=0 if type is committed
char postfix = 0; // 'c', 'w', 'd'
bool interpolate = false; // true if prefixed with 'i'
OwnedBy ownedByCtfe = OwnedBy.code;

extern (D) this(const ref Loc loc, char* string)
Expand All @@ -3022,12 +3023,13 @@ extern (C++) final class StringExp : Expression
this.sz = 1; // work around LDC bug #1286
}

extern (D) this(const ref Loc loc, void* string, size_t len, char postfix)
extern (D) this(const ref Loc loc, void* string, size_t len, char postfix, bool interpolate = false)
{
super(loc, TOK.string_, __traits(classInstanceSize, StringExp));
this.string = cast(char*)string;
this.len = len;
this.postfix = postfix;
this.interpolate = interpolate;
this.sz = 1; // work around LDC bug #1286
}

Expand Down
3 changes: 2 additions & 1 deletion src/dmd/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,11 @@ class StringExp : public Expression
unsigned char sz; // 1: char, 2: wchar, 4: dchar
unsigned char committed; // !=0 if type is committed
utf8_t postfix; // 'c', 'w', 'd'
bool interpolate; // true if prefixed with 'i'
OwnedBy ownedByCtfe;

static StringExp *create(Loc loc, char *s);
static StringExp *create(Loc loc, void *s, size_t len);
static StringExp *create(Loc loc, void *s, size_t len, bool interpolate = false);
bool equals(RootObject *o);
StringExp *toStringExp();
StringExp *toUTF8(Scope *sc);
Expand Down
49 changes: 47 additions & 2 deletions src/dmd/lexer.d
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ class Lexer
Loc startLoc;
t.blockComment = null;
t.lineComment = null;
bool interpolate = false;

while (1)
{
Expand Down Expand Up @@ -374,7 +375,8 @@ class Lexer
p++;
goto case '`';
case '`':
t.value = wysiwygStringConstant(t, *p);
t.value = wysiwygStringConstant(t, p[0]);
t.interpolate = interpolate;
return;
case 'x':
if (p[1] != '"')
Expand All @@ -388,19 +390,62 @@ class Lexer
{
p++;
t.value = delimitedStringConstant(t);
t.interpolate = interpolate;
return;
}
else if (p[1] == '{')
{
p++;
t.value = tokenStringConstant(t);
t.interpolate = interpolate;
return;
}
else
goto case_ident;
case '"':
t.value = escapeStringConstant(t);
t.interpolate = interpolate;
return;
case 'i':
if (p[1] == 'r')
{
if (p[2] == '"')
{
p += 2;
interpolate = true;
goto case '`';
}
}
else if (p[1] == '`')
{
p++;
interpolate = true;
goto case '`';
}
else if (p[1] == '"')
{
p++;
interpolate = true;
goto case '"';
}
else if (p[1] == 'q')
{
if (p[2] == '"')
{
p += 2;
t.value = delimitedStringConstant(t);
t.interpolate = true;
return;
}
else if (p[2] == '{')
{
p += 2;
t.value = tokenStringConstant(t);
t.interpolate = true;
return;
}
}
goto case_ident;
case 'a':
case 'b':
case 'c':
Expand All @@ -409,7 +454,6 @@ class Lexer
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
Expand Down Expand Up @@ -513,6 +557,7 @@ class Lexer
Lstr:
t.value = TOK.string_;
t.postfix = 0;
t.interpolate = false;
t.len = cast(uint)strlen(t.ustring);
}
else if (id == Id.VERSIONX)
Expand Down
103 changes: 102 additions & 1 deletion src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -7312,6 +7312,7 @@ final class Parser(AST) : Lexer
auto s = token.ustring;
auto len = token.len;
auto postfix = token.postfix;
auto interpolate = token.interpolate;
while (1)
{
const prev = token;
Expand All @@ -7324,6 +7325,11 @@ final class Parser(AST) : Lexer
error("mismatched string literal postfixes `'%c'` and `'%c'`", postfix, token.postfix);
postfix = token.postfix;
}
if (interpolate != token.interpolate)
{
error("cannot concatenate interpolated strings with non-interpolated strings");
interpolate = true;
}

deprecation("Implicit string concatenation is deprecated, use %s ~ %s instead",
prev.toChars(), token.toChars());
Expand All @@ -7339,7 +7345,10 @@ final class Parser(AST) : Lexer
else
break;
}
e = new AST.StringExp(loc, cast(char*)s, len, postfix);
if (interpolate)
e = new AST.TupleExp(loc, parseInterpolatedString(loc, s, len, postfix));
else
e = new AST.StringExp(loc, cast(char*)s, len, postfix);
break;
}
case TOK.void_:
Expand Down Expand Up @@ -8560,6 +8569,98 @@ final class Parser(AST) : Lexer
token.lineComment = null;
}
}

/**
Parse the given interpolated string `str` into an array of expressions.
Params:
loc = the location of the interpolated string
str = the interpolated string to parse
len = the length of the interpolated string
postfix = the interpolated string postix, i.e 'c', 'w' or 'd'
Returns:
An array of expressions representing the interpolated string.
*/
AST.Expressions* parseInterpolatedString(ref const(Loc) loc, const(char)* str, uint len, ubyte postfix)
{
//printf("parseInterpolatedString '%.*s'\n", len, str);
auto parts = new AST.Expressions();

auto mark = 0;
auto next = 0;
void addMarkToNext()
{
if (next > mark)
parts.push(new AST.StringExp(loc, cast(char*)str + mark, next - mark, postfix));
}
MainLoop:
for(; next < len;)
{
//printf("[DEBUG] str[%d] = '%c'\n", next, str[next]);
if (str[next] != '$')
{
next++;
}
else
{
addMarkToNext();
if (next + 1 >= len)
{
error("unfinished interpolated string expression '$'");
mark = next;
break;
}
if (str[next + 1] == '(')
{
next += 2;
mark = next;
for(uint depth = 1;; next++)
{
if (next >= len)
{
error("unfinished interpolated string expression '$(...)'");
mark = next;
break MainLoop;
}
auto c = str[next];
if (c == ')')
{
depth--;
if (depth == 0)
break;
}
else if (c == '(')
{
depth++;
}
}
{
auto expr = str[mark .. next];
//printf("[DEBUG] parsing the expression '%.*s'\n", expr.length, expr.ptr);
scope tempParser = new Parser!AST(/*loc, */mod, expr, false);
tempParser.nextToken();
auto result = tempParser.parseExpression();
//printf("[DEBUG] parsed to '%s'\n", result.toChars());
if (tempParser.token.value != TOK.rightParentheses)
{
error("invalid expression '%.*s' inside interpolated string", expr.length, expr.ptr);
}
parts.push(result);
}
next++;
mark = next;
}
else
{
assert(0, "not implemented");
}
}
}
addMarkToNext();

return parts;
}
}

enum PREC : int
Expand Down
1 change: 1 addition & 0 deletions src/dmd/tokens.d
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ extern (C++) struct Token
const(char)* ustring; // UTF8 string
uint len;
ubyte postfix; // 'c', 'w', 'd'
bool interpolate;
}

Identifier ident;
Expand Down
1 change: 1 addition & 0 deletions src/dmd/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ struct Token
{ utf8_t *ustring; // UTF8 string
unsigned len;
unsigned char postfix; // 'c', 'w', 'd'
bool interpolate;
};

Identifier *ident;
Expand Down
14 changes: 7 additions & 7 deletions test/d_do_test.d
Original file line number Diff line number Diff line change
Expand Up @@ -688,13 +688,13 @@ int tryMain(string[] args)
string objfile = output_dir ~ envData.sep ~ test_name ~ "_" ~ to!string(permuteIndex) ~ envData.obj;
toCleanup ~= objfile;

string command = format("%s -conf= -m%s -I%s %s %s -od%s -of%s %s %s%s %s", envData.dmd, envData.model, input_dir,
reqArgs, permutedArgs, output_dir,
(testArgs.mode == TestMode.RUN || testArgs.link ? test_app_dmd : objfile),
argSet,
(testArgs.mode == TestMode.RUN || testArgs.link ? "" : "-c "),
join(testArgs.sources, " "),
(autoCompileImports ? "-i" : join(testArgs.compiledImports, " ")));
string command = text(
i"$(envData.dmd) -conf= -m$(envData.model) -I$(input_dir) $(reqArgs) ",
i"$(permutedArgs) -od$(output_dir) -of",
(testArgs.mode == TestMode.RUN || testArgs.link) ? test_app_dmd : objfile,
i` $(argSet) $(testArgs.mode == TestMode.RUN || testArgs.link ? "" : "-c ") `,
join(testArgs.sources, " "), " ",
(autoCompileImports ? "-i" : join(testArgs.compiledImports, " ")));
version(Windows) command ~= " -map nul.map";

compile_output = execute(fThisRun, command, testArgs.mode != TestMode.FAIL_COMPILE, result_path);
Expand Down
31 changes: 31 additions & 0 deletions test/runnable/istring.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
PERMUTE_ARGS:
*/
import std.conv : text;
void main()
{
int a = 42;
assert("a is 42" == text(i"a is $(a)"));
assert("a + 23 is 65" == text(i"a + 23 is $(a + 23)"));

// test each type of string literal
int b = 93;
assert("42 + 93 = 135" == text( i"$(a) + $(b) = $(a + b)")); // double-quote
assert("42 + 93 = 135" == text( ir"$(a) + $(b) = $(a + b)")); // wysiwyg
assert("42 + 93 = 135" == text( i`$(a) + $(b) = $(a + b)`)); // wysiwyg (alt)
assert("42 + 93 = 135" == text( iq{$(a) + $(b) = $(a + b)})); // token
assert("42 + 93 = 135" == text(iq"!$(a) + $(b) = $(a + b)!")); // delimited

assert(928 == add(900, 28));
}

string funcCode(string attributes, string returnType, string name, string args, string body)
{
return text(iq{
$(attributes) $(returnType) $(name)($(args))
{
$(body)
}
});
}
mixin(funcCode("pragma(inline)", "int", "add", "int a, int b", "return a + b;"));

0 comments on commit 37a6f8f

Please sign in to comment.