Skip to content

implement DIP1027 - Easy String Interpolation #15722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions compiler/src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -4575,6 +4575,7 @@ struct ASTBase
inout(SuperExp) isSuperExp() { return op == EXP.super_ ? cast(typeof(return))this : null; }
inout(NullExp) isNullExp() { return op == EXP.null_ ? cast(typeof(return))this : null; }
inout(StringExp) isStringExp() { return op == EXP.string_ ? cast(typeof(return))this : null; }
inout(IStringExp) isIStringExp() { return op == EXP.istring ? cast(typeof(return))this : null; }
inout(TupleExp) isTupleExp() { return op == EXP.tuple ? cast(typeof(return))this : null; }
inout(ArrayLiteralExp) isArrayLiteralExp() { return op == EXP.arrayLiteral ? cast(typeof(return))this : null; }
inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == EXP.assocArrayLiteral ? cast(typeof(return))this : null; }
Expand Down Expand Up @@ -4980,6 +4981,22 @@ struct ASTBase
}
}

extern (C++) final class IStringExp : Expression
{
const(char)[] istring;

extern (D) this(const ref Loc loc, const(char)[] istring) scope
{
super(loc, EXP.istring, __traits(classInstanceSize, IStringExp));
this.istring = istring;
}

override void accept(Visitor v)
{
v.visit(this);
}
}

extern (C++) class NewExp : Expression
{
Expression thisexp; // if !=null, 'this' for class being allocated
Expand Down
21 changes: 21 additions & 0 deletions compiler/src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ extern (C++) abstract class Expression : ASTNode
inout(SuperExp) isSuperExp() { return op == EXP.super_ ? cast(typeof(return))this : null; }
inout(NullExp) isNullExp() { return op == EXP.null_ ? cast(typeof(return))this : null; }
inout(StringExp) isStringExp() { return op == EXP.string_ ? cast(typeof(return))this : null; }
inout(IStringExp) isIStringExp() { return op == EXP.istring ? cast(typeof(return))this : null; }
inout(TupleExp) isTupleExp() { return op == EXP.tuple ? cast(typeof(return))this : null; }
inout(ArrayLiteralExp) isArrayLiteralExp() { return op == EXP.arrayLiteral ? cast(typeof(return))this : null; }
inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == EXP.assocArrayLiteral ? cast(typeof(return))this : null; }
Expand Down Expand Up @@ -1880,6 +1881,25 @@ extern (C++) final class StringExp : Expression
}
}

/***********************************************************
* https://dlang.org/spec/expression.html#string_literals
*/
extern (C++) final class IStringExp : Expression
{
const(char)[] istring;

extern (D) this(const ref Loc loc, const(char)[] istring) scope
{
super(loc, EXP.istring);
this.istring = istring;
}

override void accept(Visitor v)
{
v.visit(this);
}
}

/***********************************************************
* A sequence of expressions
*
Expand Down Expand Up @@ -5527,6 +5547,7 @@ private immutable ubyte[EXP.max+1] expSize = [
EXP.preMinusMinus: __traits(classInstanceSize, PreExp),
EXP.identifier: __traits(classInstanceSize, IdentifierExp),
EXP.string_: __traits(classInstanceSize, StringExp),
EXP.istring: __traits(classInstanceSize, IStringExp),
EXP.this_: __traits(classInstanceSize, ThisExp),
EXP.super_: __traits(classInstanceSize, SuperExp),
EXP.halt: __traits(classInstanceSize, HaltExp),
Expand Down
10 changes: 10 additions & 0 deletions compiler/src/dmd/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class TemplateDeclaration;
class ClassDeclaration;
class OverloadSet;
class StringExp;
class IStringExp;
class LoweredAssignExp;
#ifdef IN_GCC
typedef union tree_node Symbol;
Expand Down Expand Up @@ -129,6 +130,7 @@ class Expression : public ASTNode
SuperExp* isSuperExp();
NullExp* isNullExp();
StringExp* isStringExp();
IStringExp* isIStringExp();
TupleExp* isTupleExp();
ArrayLiteralExp* isArrayLiteralExp();
AssocArrayLiteralExp* isAssocArrayLiteralExp();
Expand Down Expand Up @@ -370,6 +372,14 @@ class StringExp final : public Expression
void writeTo(void* dest, bool zero, int tyto = 0) const;
};

class IStringExp final : public Expression
{
public:
DString istring;

void accept(Visitor *v) override { v->visit(this); }
};

// Tuple

class TupleExp final : public Expression
Expand Down
141 changes: 141 additions & 0 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
module dmd.expressionsem;

import core.stdc.stdio;
import core.stdc.ctype;

import dmd.access;
import dmd.aggregate;
Expand Down Expand Up @@ -4198,6 +4199,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = e;
}

override void visit(IStringExp exp)
{
static if (LOGSEMANTIC)
{
printf("IStringExp::semantic() %s\n", exp.toChars());
}
Expression e = istringToTuple(exp, global.errorSink);
e = e.expressionSemantic(sc);
result = e;
return;
}

override void visit(TupleExp exp)
{
static if (LOGSEMANTIC)
Expand Down Expand Up @@ -16314,3 +16327,131 @@ Expression toBoolean(Expression exp, Scope* sc)
return e;
}
}

/*****************************************************
* Convert istring to a tuple of a format string followed by arguments.
* Params:
* ise = istring expression
* eSink = error message sink
* Returns:
* generated tuple expression
*/
Expression istringToTuple(IStringExp ise, ErrorSink eSink)
{
//printf("istringToTuple() %s\n", ise.toChars());

OutBuffer fmt; // generated format string
OutBuffer arg; // current arg
Expressions* exps = new Expressions(); // expressions for tuple

for (size_t i; i < ise.istring.length; ++i)
{
bool increment()
{
if (i + 1 == ise.istring.length)
{
eSink.error(ise.loc, "istring ended prematurely");
return true;
}
else
++i;
return false;
}

char c = ise.istring[i];
if (c != '$')
{
fmt.writeByte(c);
continue;
}
if (increment())
return ErrorExp.get();

c = ise.istring[i];
if (c == '$') // $$
{
fmt.writeByte(c);
continue;
}

if (c == '{') // FormatString /*}*/
{
int braces = 1; // brace nesting level
while (1)
{
if (increment())
return ErrorExp.get();
c = ise.istring[i];
if (c == '{')
{
++braces;
}
if (c == '}' && !--braces)
{
if (increment())
return ErrorExp.get();
c = ise.istring[i];
break;
}
fmt.writeByte(c);
}
}
else
fmt.writestring("%s"); // default format

if (c == '(') // '(' Expression ')'
{
// Note: parsing is crude; not handling parens in literals
arg.reset(); // we will put the Expression string into arg
int parens = 1; // parens nesting level
while (1)
{
if (increment())
return ErrorExp.get();
c = ise.istring[i];
if (c == '(')
{
++parens;
}
else if (c == ')' && !--parens)
{
break;
}
arg.writeByte(c);
}

/* Create a MixinExp of the text captured by arg
*/
StringExp estr = new StringExp(ise.loc, arg.extractSlice());
Expressions* emixins = new Expressions();
emixins.push(estr);
Expression emixin = new MixinExp(ise.loc, emixins);
exps.push(emixin);
}
else if (isalpha(c) || c == '_')
{
// Note: doesn't deal with Unicode code units in identifiers
arg.reset(); // recycle buffer usage
while (isalnum(c) || c == '_') // Identifier
{
arg.writeByte(c);
++i;
if (i == ise.istring.length)
break;
c = ise.istring[i];
}
--i;
Identifier id = Identifier.idPool(arg[]);
Expression eid = new IdentifierExp(ise.loc, id);
exps.push(eid);
}
else
{
eSink.error(ise.loc, "identifier expected after $");
return ErrorExp.get();
}
}
StringExp se = new StringExp(ise.loc, fmt.extractSlice[]);
exps.insert(0, se);
return new TupleExp(ise.loc, exps);
}
Loading