Skip to content

Commit eab2d4d

Browse files
committed
interpolated strings, v2
better implementation, doing all the tokenizing at the start, an interpolated string being multiple tokens
1 parent 5a92808 commit eab2d4d

8 files changed

Lines changed: 648 additions & 24 deletions

File tree

src/dparse/ast.d

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ shared static this()
7373
typeMap[typeid(TypeofExpression)] = 46;
7474
typeMap[typeid(UnaryExpression)] = 47;
7575
typeMap[typeid(XorExpression)] = 48;
76+
typeMap[typeid(InterpolatedStringExpression)] = 49;
77+
typeMap[typeid(InterpolatedStringText)] = 50;
78+
typeMap[typeid(InterpolatedStringVariable)] = 51;
7679
}
7780

7881
/// Describes which syntax was used in a list of declarations in the containing AST node
@@ -167,6 +170,19 @@ abstract class ASTVisitor
167170
case 46: visit(cast(TypeofExpression) n); break;
168171
case 47: visit(cast(UnaryExpression) n); break;
169172
case 48: visit(cast(XorExpression) n); break;
173+
// skip 49, 50, 51 (used for InterpolatedStringPart)
174+
default: assert(false, __MODULE__ ~ " has a bug");
175+
}
176+
}
177+
178+
/// ditto
179+
void dynamicDispatch(const InterpolatedStringPart n)
180+
{
181+
switch (typeMap.get(typeid(n), 0))
182+
{
183+
case 49: visit(cast(InterpolatedStringExpression) n); break;
184+
case 50: visit(cast(InterpolatedStringText) n); break;
185+
case 51: visit(cast(InterpolatedStringVariable) n); break;
170186
default: assert(false, __MODULE__ ~ " has a bug");
171187
}
172188
}
@@ -289,6 +305,10 @@ abstract class ASTVisitor
289305
/** */ void visit(const Initialize initialize) { initialize.accept(this); }
290306
/** */ void visit(const Initializer initializer) { initializer.accept(this); }
291307
/** */ void visit(const InterfaceDeclaration interfaceDeclaration) { interfaceDeclaration.accept(this); }
308+
/** */ void visit(const InterpolatedString interpolatedString) { interpolatedString.accept(this); }
309+
/** */ void visit(const InterpolatedStringExpression interpolatedStringExpression) { interpolatedStringExpression.accept(this); }
310+
/** */ void visit(const InterpolatedStringText interpolatedStringText) { interpolatedStringText.accept(this); }
311+
/** */ void visit(const InterpolatedStringVariable interpolatedStringVariable) { interpolatedStringVariable.accept(this); }
292312
/** */ void visit(const Invariant invariant_) { invariant_.accept(this); }
293313
/** */ void visit(const IsExpression isExpression) { isExpression.accept(this); }
294314
/** */ void visit(const KeyValuePair keyValuePair) { keyValuePair.accept(this); }
@@ -426,7 +446,7 @@ template visitIfNotNull(fields ...)
426446
}
427447
}
428448

429-
mixin template OpEquals(bool print = false)
449+
private mixin template OpEquals(extraFields...)
430450
{
431451
override bool opEquals(Object other) const
432452
{
@@ -443,6 +463,9 @@ mixin template OpEquals(bool print = false)
443463
if (field != obj.tupleof[i])
444464
return false;
445465
}
466+
static foreach (field; extraFields)
467+
if (mixin("this." ~ field ~ " != obj." ~ field))
468+
return false;
446469
return true;
447470
}
448471
return false;
@@ -2318,6 +2341,109 @@ final class InterfaceDeclaration : BaseNode
23182341
mixin OpEquals;
23192342
}
23202343

2344+
///
2345+
final class InterpolatedString : BaseNode
2346+
{
2347+
override void accept(ASTVisitor visitor) const
2348+
{
2349+
mixin (visitIfNotNull!(parts));
2350+
}
2351+
2352+
/** */ InterpolatedStringPart[] parts;
2353+
2354+
inout(Token) startQuote() inout pure nothrow @nogc @safe scope
2355+
{
2356+
return tokens.length ? tokens[0] : Token.init;
2357+
}
2358+
2359+
inout(Token) endQuote() inout pure nothrow @nogc @safe scope
2360+
{
2361+
return tokens.length && tokens[$ - 1].type == tok!"istringLiteralEnd"
2362+
? tokens[$ - 1]
2363+
: Token.init;
2364+
}
2365+
2366+
/// '\0'/'c'/'w'/'d' for `i""`, `i""c`, `i""w` and `i""d` respectively.
2367+
char postfixType() inout pure nothrow @nogc @safe scope
2368+
{
2369+
auto end = endQuote.text;
2370+
auto endChar = end.length ? end[$ - 1] : ' ';
2371+
switch (endChar)
2372+
{
2373+
case 'c':
2374+
case 'w':
2375+
case 'd':
2376+
return endChar;
2377+
default:
2378+
return '\0';
2379+
}
2380+
}
2381+
2382+
mixin OpEquals!("startQuote.text", "postfixType");
2383+
}
2384+
2385+
///
2386+
abstract class InterpolatedStringPart : BaseNode
2387+
{
2388+
}
2389+
2390+
///
2391+
final class InterpolatedStringText : InterpolatedStringPart
2392+
{
2393+
override void accept(ASTVisitor visitor) const
2394+
{
2395+
}
2396+
2397+
/// The token containing the plain text part in its `.text` property.
2398+
inout(Token) text() inout pure nothrow @nogc @safe scope
2399+
{
2400+
return tokens.length ? tokens[0] : Token.init;
2401+
}
2402+
2403+
mixin OpEquals!("text.text");
2404+
}
2405+
2406+
///
2407+
final class InterpolatedStringVariable : InterpolatedStringPart
2408+
{
2409+
override void accept(ASTVisitor visitor) const
2410+
{
2411+
}
2412+
2413+
/// The dollar token.
2414+
inout(Token) dollar() inout pure nothrow @nogc @safe scope
2415+
{
2416+
return tokens.length == 2 ? tokens[0] : Token.init;
2417+
}
2418+
2419+
/// The variable name token.
2420+
inout(Token) name() inout pure nothrow @nogc @safe scope
2421+
{
2422+
return tokens.length == 2 ? tokens[1] : Token.init;
2423+
}
2424+
2425+
mixin OpEquals!("name.text");
2426+
}
2427+
2428+
///
2429+
final class InterpolatedStringExpression : InterpolatedStringPart
2430+
{
2431+
override void accept(ASTVisitor visitor) const
2432+
{
2433+
mixin (visitIfNotNull!(expression));
2434+
}
2435+
2436+
/** */ Expression expression;
2437+
2438+
/// The dollar token.
2439+
inout(Token) dollar() inout pure nothrow @nogc @safe scope
2440+
{
2441+
return tokens.length ? tokens[0] : Token.init;
2442+
}
2443+
2444+
mixin OpEquals;
2445+
}
2446+
23212447
///
23222448
final class Invariant : BaseNode
23232449
{
@@ -2798,7 +2924,7 @@ final class PrimaryExpression : ExpressionNode
27982924
typeofExpression, typeidExpression, arrayLiteral, assocArrayLiteral,
27992925
expression, dot, identifierOrTemplateInstance, isExpression,
28002926
functionLiteralExpression,traitsExpression, mixinExpression,
2801-
importExpression, vector, arguments));
2927+
importExpression, vector, arguments, interpolatedString));
28022928
}
28032929
/** */ Token dot;
28042930
/** */ Token primary;
@@ -2818,6 +2944,7 @@ final class PrimaryExpression : ExpressionNode
28182944
/** */ Type type;
28192945
/** */ Token typeConstructor;
28202946
/** */ Arguments arguments;
2947+
/** */ InterpolatedString interpolatedString;
28212948
mixin OpEquals;
28222949
}
28232950

src/dparse/astprinter.d

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,33 @@ class XMLPrinter : ASTVisitor
582582
output.writeln("</interfaceDeclaration>");
583583
}
584584

585+
override void visit(const InterpolatedString interpolatedString)
586+
{
587+
output.writeln("<interpolatedString startQuote=\"",
588+
xmlAttributeEscape(interpolatedString.startQuote.text),
589+
"\" endQuote=\"",
590+
xmlAttributeEscape(interpolatedString.endQuote.text),
591+
"\">");
592+
foreach (part; interpolatedString.parts)
593+
dynamicDispatch(part);
594+
output.writeln("</interpolatedString>");
595+
}
596+
597+
override void visit(const InterpolatedStringText interpolatedStringText)
598+
{
599+
output.writeln("<text>", xmlEscape(interpolatedStringText.text.text), "</text>");
600+
}
601+
602+
override void visit(const InterpolatedStringVariable interpolatedStringVariable)
603+
{
604+
output.writeln("<variable>", xmlEscape(interpolatedStringVariable.name.text), "</variable>");
605+
}
606+
607+
override void visit(const InterpolatedStringExpression interpolatedStringExpression)
608+
{
609+
visit(interpolatedStringExpression.expression);
610+
}
611+
585612
override void visit(const Invariant invariant_)
586613
{
587614
output.writeln("<invariant>");

src/dparse/formatter.d

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ version (unittest)
1212
{
1313
import dparse.parser;
1414
import dparse.rollback_allocator;
15-
import std.array : Appender;
1615
import std.algorithm : canFind;
16+
import std.array : Appender;
1717
}
1818

1919
//debug = verbose;
@@ -2013,6 +2013,36 @@ class Formatter(Sink)
20132013
}
20142014
}
20152015

2016+
void format(const InterpolatedString interpolatedString)
2017+
{
2018+
put(interpolatedString.startQuote.text);
2019+
foreach (part; interpolatedString.parts)
2020+
{
2021+
if (cast(InterpolatedStringText) part) format(cast(InterpolatedStringText) part);
2022+
else if (cast(InterpolatedStringVariable) part) format(cast(InterpolatedStringVariable) part);
2023+
else if (cast(InterpolatedStringExpression) part) format(cast(InterpolatedStringExpression) part);
2024+
}
2025+
put(interpolatedString.endQuote.text);
2026+
}
2027+
2028+
void format(const InterpolatedStringText interpolatedStringText)
2029+
{
2030+
put(interpolatedStringText.text.text);
2031+
}
2032+
2033+
void format(const InterpolatedStringVariable interpolatedStringVariable)
2034+
{
2035+
put("$");
2036+
put(interpolatedStringVariable.name.text);
2037+
}
2038+
2039+
void format(const InterpolatedStringExpression interpolatedStringExpression)
2040+
{
2041+
put("$(");
2042+
format(interpolatedStringExpression.expression);
2043+
put(")");
2044+
}
2045+
20162046
void format(const Invariant invariant_, const Attribute[] attrs = null)
20172047
{
20182048
debug(verbose) writeln("Invariant");
@@ -2572,6 +2602,7 @@ class Formatter(Sink)
25722602
Type type;
25732603
Token typeConstructor;
25742604
Arguments arguments;
2605+
InterpolatedString interpolatedString;
25752606
**/
25762607

25772608
with(primaryExpression)
@@ -2606,6 +2637,7 @@ class Formatter(Sink)
26062637
else if (vector) format(vector);
26072638
else if (type) format(type);
26082639
else if (arguments) format(arguments);
2640+
else if (interpolatedString) format(interpolatedString);
26092641
}
26102642
}
26112643

@@ -4367,4 +4399,11 @@ y, /// Documentation for y
43674399
z /// Documentation for z
43684400
43694401
}");
4402+
testFormatNode!(VariableDeclaration)(`T x = i"hello";`);
4403+
testFormatNode!(VariableDeclaration)(`T x = i" hello ";`);
4404+
testFormatNode!(VariableDeclaration)(`T x = i" hello $name ";`);
4405+
testFormatNode!(VariableDeclaration)(`T x = i" hello $(name) ";`);
4406+
testFormatNode!(VariableDeclaration)(`T x = i" hello $( name ) ";`, `T x = i" hello $(name) ";`);
4407+
testFormatNode!(VariableDeclaration)(`auto a = iq{ "}" hi };`, `auto a = iq{ "}" hi };`);
4408+
testFormatNode!(VariableDeclaration)("T x = iq{\n};", "T x = iq{\n};");
43704409
}

0 commit comments

Comments
 (0)