Skip to content

Commit 842de6e

Browse files
committed
implement DIP1027 - String Interpolation
1 parent 8f5764e commit 842de6e

15 files changed

+465
-176
lines changed

compiler/src/dmd/astbase.d

+17
Original file line numberDiff line numberDiff line change
@@ -4575,6 +4575,7 @@ struct ASTBase
45754575
inout(SuperExp) isSuperExp() { return op == EXP.super_ ? cast(typeof(return))this : null; }
45764576
inout(NullExp) isNullExp() { return op == EXP.null_ ? cast(typeof(return))this : null; }
45774577
inout(StringExp) isStringExp() { return op == EXP.string_ ? cast(typeof(return))this : null; }
4578+
inout(IStringExp) isIStringExp() { return op == EXP.istring ? cast(typeof(return))this : null; }
45784579
inout(TupleExp) isTupleExp() { return op == EXP.tuple ? cast(typeof(return))this : null; }
45794580
inout(ArrayLiteralExp) isArrayLiteralExp() { return op == EXP.arrayLiteral ? cast(typeof(return))this : null; }
45804581
inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == EXP.assocArrayLiteral ? cast(typeof(return))this : null; }
@@ -4980,6 +4981,22 @@ struct ASTBase
49804981
}
49814982
}
49824983

4984+
extern (C++) final class IStringExp : Expression
4985+
{
4986+
const(char)[] istring;
4987+
4988+
extern (D) this(const ref Loc loc, const(char)[] istring) scope
4989+
{
4990+
super(loc, EXP.istring, __traits(classInstanceSize, IStringExp));
4991+
this.istring = istring;
4992+
}
4993+
4994+
override void accept(Visitor v)
4995+
{
4996+
v.visit(this);
4997+
}
4998+
}
4999+
49835000
extern (C++) class NewExp : Expression
49845001
{
49855002
Expression thisexp; // if !=null, 'this' for class being allocated

compiler/src/dmd/expression.d

+21
Original file line numberDiff line numberDiff line change
@@ -1605,6 +1605,7 @@ extern (C++) abstract class Expression : ASTNode
16051605
inout(SuperExp) isSuperExp() { return op == EXP.super_ ? cast(typeof(return))this : null; }
16061606
inout(NullExp) isNullExp() { return op == EXP.null_ ? cast(typeof(return))this : null; }
16071607
inout(StringExp) isStringExp() { return op == EXP.string_ ? cast(typeof(return))this : null; }
1608+
inout(IStringExp) isIStringExp() { return op == EXP.istring ? cast(typeof(return))this : null; }
16081609
inout(TupleExp) isTupleExp() { return op == EXP.tuple ? cast(typeof(return))this : null; }
16091610
inout(ArrayLiteralExp) isArrayLiteralExp() { return op == EXP.arrayLiteral ? cast(typeof(return))this : null; }
16101611
inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == EXP.assocArrayLiteral ? cast(typeof(return))this : null; }
@@ -2834,6 +2835,25 @@ extern (C++) final class StringExp : Expression
28342835
}
28352836
}
28362837

2838+
/***********************************************************
2839+
* https://dlang.org/spec/expression.html#string_literals
2840+
*/
2841+
extern (C++) final class IStringExp : Expression
2842+
{
2843+
const(char)[] istring;
2844+
2845+
extern (D) this(const ref Loc loc, const(char)[] istring) scope
2846+
{
2847+
super(loc, EXP.istring);
2848+
this.istring = istring;
2849+
}
2850+
2851+
override void accept(Visitor v)
2852+
{
2853+
v.visit(this);
2854+
}
2855+
}
2856+
28372857
/***********************************************************
28382858
* A sequence of expressions
28392859
*
@@ -7461,6 +7481,7 @@ private immutable ubyte[EXP.max+1] expSize = [
74617481
EXP.preMinusMinus: __traits(classInstanceSize, PreExp),
74627482
EXP.identifier: __traits(classInstanceSize, IdentifierExp),
74637483
EXP.string_: __traits(classInstanceSize, StringExp),
7484+
EXP.istring: __traits(classInstanceSize, IStringExp),
74647485
EXP.this_: __traits(classInstanceSize, ThisExp),
74657486
EXP.super_: __traits(classInstanceSize, SuperExp),
74667487
EXP.halt: __traits(classInstanceSize, HaltExp),

compiler/src/dmd/expression.h

+9
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class TemplateDeclaration;
3838
class ClassDeclaration;
3939
class OverloadSet;
4040
class StringExp;
41+
class IStringExp;
4142
class LoweredAssignExp;
4243
struct UnionExp;
4344
#ifdef IN_GCC
@@ -100,6 +101,7 @@ class Expression : public ASTNode
100101
virtual real_t toImaginary();
101102
virtual complex_t toComplex();
102103
virtual StringExp *toStringExp();
104+
virtual IStringExp *toIStringExp();
103105
virtual bool isLvalue();
104106
virtual Expression *toLvalue(Scope *sc, Expression *e);
105107
virtual Expression *modifiableLvalue(Scope *sc, Expression *e);
@@ -139,6 +141,7 @@ class Expression : public ASTNode
139141
SuperExp* isSuperExp();
140142
NullExp* isNullExp();
141143
StringExp* isStringExp();
144+
IStringExp* isIStringExp();
142145
TupleExp* isTupleExp();
143146
ArrayLiteralExp* isArrayLiteralExp();
144147
AssocArrayLiteralExp* isAssocArrayLiteralExp();
@@ -396,6 +399,12 @@ class StringExp final : public Expression
396399
void writeTo(void* dest, bool zero, int tyto = 0) const;
397400
};
398401

402+
class IStringExp final : public Expression
403+
{
404+
public:
405+
DString istring;
406+
};
407+
399408
// Tuple
400409

401410
class TupleExp final : public Expression

compiler/src/dmd/expressionsem.d

+142
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
module dmd.expressionsem;
1515

1616
import core.stdc.stdio;
17+
import core.stdc.ctype;
1718

1819
import dmd.access;
1920
import dmd.aggregate;
@@ -41,6 +42,7 @@ import dmd.dstruct;
4142
import dmd.dsymbolsem;
4243
import dmd.dtemplate;
4344
import dmd.errors;
45+
import dmd.errorsink;
4446
import dmd.escape;
4547
import dmd.expression;
4648
import dmd.file_manager;
@@ -3225,6 +3227,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
32253227
result = e;
32263228
}
32273229

3230+
override void visit(IStringExp exp)
3231+
{
3232+
static if (LOGSEMANTIC)
3233+
{
3234+
printf("IStringExp::semantic() %s\n", exp.toChars());
3235+
}
3236+
Expression e = istringToTuple(exp, global.errorSink);
3237+
e = e.expressionSemantic(sc);
3238+
result = e;
3239+
return;
3240+
}
3241+
32283242
override void visit(TupleExp exp)
32293243
{
32303244
static if (LOGSEMANTIC)
@@ -14265,3 +14279,131 @@ Expression toBoolean(Expression exp, Scope* sc)
1426514279
return e;
1426614280
}
1426714281
}
14282+
14283+
/*****************************************************
14284+
* Convert istring to a tuple of a format string followed by arguments.
14285+
* Params:
14286+
* ise = istring expression
14287+
* eSink = error message sink
14288+
* Returns:
14289+
* generated tuple expression
14290+
*/
14291+
Expression istringToTuple(IStringExp ise, ErrorSink eSink)
14292+
{
14293+
//printf("istringToTuple() %s\n", ise.toChars());
14294+
14295+
OutBuffer fmt; // generated format string
14296+
OutBuffer arg; // current arg
14297+
Expressions* exps = new Expressions(); // expressions for tuple
14298+
14299+
for (size_t i; i < ise.istring.length; ++i)
14300+
{
14301+
bool increment()
14302+
{
14303+
if (i + 1 == ise.istring.length)
14304+
{
14305+
eSink.error(ise.loc, "istring ended prematurely");
14306+
return true;
14307+
}
14308+
else
14309+
++i;
14310+
return false;
14311+
}
14312+
14313+
char c = ise.istring[i];
14314+
if (c != '$')
14315+
{
14316+
fmt.writeByte(c);
14317+
continue;
14318+
}
14319+
if (increment())
14320+
return ErrorExp.get();
14321+
14322+
c = ise.istring[i];
14323+
if (c == '$') // $$
14324+
{
14325+
fmt.writeByte(c);
14326+
continue;
14327+
}
14328+
14329+
if (c == '{') // FormatString /*}*/
14330+
{
14331+
int braces = 1; // brace nesting level
14332+
while (1)
14333+
{
14334+
if (increment())
14335+
return ErrorExp.get();
14336+
c = ise.istring[i];
14337+
if (c == '{')
14338+
{
14339+
++braces;
14340+
}
14341+
if (c == '}' && !--braces)
14342+
{
14343+
if (increment())
14344+
return ErrorExp.get();
14345+
c = ise.istring[i];
14346+
break;
14347+
}
14348+
fmt.writeByte(c);
14349+
}
14350+
}
14351+
else
14352+
fmt.writestring("%s"); // default format
14353+
14354+
if (c == '(') // '(' Expression ')'
14355+
{
14356+
// Note: parsing is crude; not handling parens in literals
14357+
arg.reset(); // we will put the Expression string into arg
14358+
int parens = 1; // parens nesting level
14359+
while (1)
14360+
{
14361+
if (increment())
14362+
return ErrorExp.get();
14363+
c = ise.istring[i];
14364+
if (c == '(')
14365+
{
14366+
++parens;
14367+
}
14368+
else if (c == ')' && !--parens)
14369+
{
14370+
break;
14371+
}
14372+
arg.writeByte(c);
14373+
}
14374+
14375+
/* Create a MixinExp of the text captured by arg
14376+
*/
14377+
StringExp estr = new StringExp(ise.loc, arg.extractSlice());
14378+
Expressions* emixins = new Expressions();
14379+
emixins.push(estr);
14380+
Expression emixin = new MixinExp(ise.loc, emixins);
14381+
exps.push(emixin);
14382+
}
14383+
else if (isalpha(c) || c == '_')
14384+
{
14385+
// Note: doesn't deal with Unicode code units in identifiers
14386+
arg.reset(); // recycle buffer usage
14387+
while (isalnum(c) || c == '_') // Identifier
14388+
{
14389+
arg.writeByte(c);
14390+
++i;
14391+
if (i == ise.istring.length)
14392+
break;
14393+
c = ise.istring[i];
14394+
}
14395+
--i;
14396+
Identifier id = Identifier.idPool(arg[]);
14397+
Expression eid = new IdentifierExp(ise.loc, id);
14398+
exps.push(eid);
14399+
}
14400+
else
14401+
{
14402+
eSink.error(ise.loc, "identifier expected after $");
14403+
return ErrorExp.get();
14404+
}
14405+
}
14406+
StringExp se = new StringExp(ise.loc, fmt.extractSlice[]);
14407+
exps.insert(0, se);
14408+
return new TupleExp(ise.loc, exps);
14409+
}

0 commit comments

Comments
 (0)