Skip to content

Commit 19a8489

Browse files
authored
Add IfCondition to while (since DMD-FE 2.097.0) (#462)
1 parent c3fa4e6 commit 19a8489

File tree

7 files changed

+230
-69
lines changed

7 files changed

+230
-69
lines changed

src/dparse/ast.d

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ abstract class ASTVisitor
253253
/** */ void visit(const IdentifierOrTemplateInstance identifierOrTemplateInstance) { identifierOrTemplateInstance.accept(this); }
254254
/** */ void visit(const IdentityExpression identityExpression) { identityExpression.accept(this); }
255255
/** */ void visit(const IfStatement ifStatement) { ifStatement.accept(this); }
256+
/** */ void visit(const IfCondition ifCondition) { ifCondition.accept(this); }
256257
/** */ void visit(const ImportBind importBind) { importBind.accept(this); }
257258
/** */ void visit(const ImportBindings importBindings) { importBindings.accept(this); }
258259
/** */ void visit(const ImportDeclaration importDeclaration) { importDeclaration.accept(this); }
@@ -1933,13 +1934,9 @@ final class IfStatement : BaseNode
19331934
{
19341935
override void accept(ASTVisitor visitor) const
19351936
{
1936-
mixin (visitIfNotNull!(identifier, type, expression, thenStatement,
1937-
elseStatement));
1937+
mixin (visitIfNotNull!(condition, thenStatement, elseStatement));
19381938
}
1939-
/** */ IdType[] typeCtors;
1940-
/** */ Type type;
1941-
/** */ Token identifier;
1942-
/** */ Expression expression;
1939+
/** */ IfCondition condition;
19431940
/** */ DeclarationOrStatement thenStatement;
19441941
/** */ DeclarationOrStatement elseStatement;
19451942
/** */ size_t startIndex;
@@ -1948,6 +1945,42 @@ final class IfStatement : BaseNode
19481945
mixin OpEquals;
19491946
}
19501947

1948+
/**
1949+
In an if (or while) condition this represents:
1950+
```
1951+
if (auto x = readln)
1952+
^^^^^^^^^^^^^^^
1953+
1954+
if (a == b || c == d)
1955+
^^^^^^^^^^^^^^^^
1956+
```
1957+
*/
1958+
final class IfCondition : BaseNode
1959+
{
1960+
override void accept(ASTVisitor visitor) const
1961+
{
1962+
mixin (visitIfNotNull!(type, identifier, expression));
1963+
}
1964+
/// In an assignment-condition, these are the optional type constructors
1965+
IdType[] typeCtors;
1966+
/// In an assignment-condition, this is the optional explicit type
1967+
Type type;
1968+
/// In an assignment-condition, in `if (auto x = ...)` this is the `x`
1969+
Token identifier;
1970+
/**
1971+
In an assignment-condition, this is true if `scope` is used to construct
1972+
the variable.
1973+
*/
1974+
bool scope_;
1975+
/**
1976+
In an assignment-condition, this is the part after the equals sign.
1977+
Otherwise this is any other expression that is evaluated to be a boolean.
1978+
(e.g. UnaryExpression, AndAndExpression, CmpExpression, etc.)
1979+
*/
1980+
Expression expression;
1981+
mixin OpEquals;
1982+
}
1983+
19511984
///
19521985
final class ImportBind : BaseNode
19531986
{
@@ -3447,10 +3480,10 @@ final class WhileStatement : BaseNode
34473480
{
34483481
override void accept(ASTVisitor visitor) const
34493482
{
3450-
mixin (visitIfNotNull!(expression, declarationOrStatement));
3483+
mixin (visitIfNotNull!(condition, declarationOrStatement));
34513484
}
34523485

3453-
/** */ Expression expression;
3486+
/** */ IfCondition condition;
34543487
/** */ DeclarationOrStatement declarationOrStatement;
34553488
/** */ size_t startIndex;
34563489
mixin OpEquals;

src/dparse/astprinter.d

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -461,17 +461,7 @@ class XMLPrinter : ASTVisitor
461461
{
462462
output.writeln("<ifStatement>");
463463

464-
output.writeln("<condition>");
465-
if (ifStatement.identifier.type != tok!"")
466-
{
467-
if (ifStatement.type is null)
468-
output.writeln("<auto/>");
469-
else
470-
visit(ifStatement.type);
471-
visit(ifStatement.identifier);
472-
}
473-
ifStatement.expression.accept(this);
474-
output.writeln("</condition>");
464+
ifStatement.condition.accept(this);
475465

476466
output.writeln("<then>");
477467
ifStatement.thenStatement.accept(this);
@@ -486,6 +476,29 @@ class XMLPrinter : ASTVisitor
486476
output.writeln("</ifStatement>");
487477
}
488478

479+
override void visit(const IfCondition ifCondition)
480+
{
481+
output.writeln("<condition>");
482+
foreach (constructor; ifCondition.typeCtors)
483+
{
484+
output.writeln("<typeConstructor>", str(constructor), "</typeConstructor>");
485+
}
486+
if (ifCondition.identifier.type != tok!"")
487+
{
488+
if (ifCondition.scope_)
489+
output.writeln("<scope/>");
490+
else if (ifCondition.type is null)
491+
output.writeln("<auto/>");
492+
493+
// in case `scope T` is possible eventually, check type separately
494+
if (ifCondition.type)
495+
visit(ifCondition.type);
496+
visit(ifCondition.identifier);
497+
}
498+
ifCondition.expression.accept(this);
499+
output.writeln("</condition>");
500+
}
501+
489502
override void visit(const ImportBind importBind)
490503
{
491504
if (importBind.right.type == tok!"")

src/dparse/formatter.d

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,29 +1708,8 @@ class Formatter(Sink)
17081708

17091709
with(ifStatement)
17101710
{
1711-
bool isAuto = identifier != tok!"" && !type;
1712-
bool isAssign = isAuto || type || typeCtors.length;
1713-
17141711
put("if (");
1715-
1716-
if (isAuto) put("auto ");
1717-
foreach(tct; typeCtors)
1718-
{
1719-
put(str(tct));
1720-
space();
1721-
}
1722-
if (type)
1723-
{
1724-
format(type);
1725-
space();
1726-
}
1727-
if (identifier != tok!"")
1728-
{
1729-
format(identifier);
1730-
space();
1731-
}
1732-
if (isAssign) put("= ");
1733-
if (expression) format(expression);
1712+
format(condition);
17341713
put(")");
17351714

17361715
if (thenStatement)
@@ -1755,6 +1734,38 @@ class Formatter(Sink)
17551734
}
17561735
}
17571736

1737+
void format(const IfCondition ifCondition)
1738+
{
1739+
debug(verbose) writeln("IfCondition");
1740+
1741+
with(ifCondition)
1742+
{
1743+
bool isAuto = identifier != tok!"" && !type && !scope_;
1744+
bool isScope = identifier != tok!"" && scope_;
1745+
bool isAssign = isAuto || isScope || type || typeCtors.length;
1746+
1747+
if (isAuto) put("auto ");
1748+
if (isScope) put("scope ");
1749+
foreach(tct; typeCtors)
1750+
{
1751+
put(str(tct));
1752+
space();
1753+
}
1754+
if (type)
1755+
{
1756+
format(type);
1757+
space();
1758+
}
1759+
if (identifier != tok!"")
1760+
{
1761+
format(identifier);
1762+
space();
1763+
}
1764+
if (isAssign) put("= ");
1765+
if (expression) format(expression);
1766+
}
1767+
}
1768+
17581769
void format(const ImportBind importBind)
17591770
{
17601771
debug(verbose) writeln("ImportBind");
@@ -3731,14 +3742,9 @@ class Formatter(Sink)
37313742
{
37323743
debug(verbose) writeln("WhileStatement");
37333744

3734-
/**
3735-
Expression expression;
3736-
DeclarationOrStatement declarationOrStatement;
3737-
**/
3738-
37393745
newThing(What.other);
37403746
put("while (");
3741-
format(stmt.expression);
3747+
format(stmt.condition);
37423748
put(")");
37433749
maybeIndent(stmt.declarationOrStatement);
37443750
}
@@ -4260,4 +4266,29 @@ do
42604266
foo(a, throw b, c);
42614267
return throw new Exception("", "");
42624268
}});
4269+
4270+
testFormatNode!(IfCondition)(q{void foo()
4271+
{
4272+
if (scope x = readln())
4273+
{
4274+
}
4275+
}}, `scope x = readln()`);
4276+
testFormatNode!(IfCondition)(q{void foo()
4277+
{
4278+
if (auto x = readln())
4279+
{
4280+
}
4281+
}}, `auto x = readln()`);
4282+
testFormatNode!(IfCondition)(q{void foo()
4283+
{
4284+
while (const inout string x = readln())
4285+
{
4286+
}
4287+
}}, `const inout string x = readln()`);
4288+
testFormatNode!(IfStatement)(q{void foo()
4289+
{
4290+
if (a == b && c == d)
4291+
{
4292+
}
4293+
}}, `a == b && c == d`);
42634294
}

src/dparse/parser.d

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3993,11 +3993,6 @@ class Parser
39933993
*
39943994
* $(GRAMMAR $(RULEDEF ifStatement):
39953995
* $(LITERAL 'if') $(LITERAL '$(LPAREN)') $(RULE ifCondition) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) ($(LITERAL 'else') $(RULE declarationOrStatement))?
3996-
*$(RULEDEF ifCondition):
3997-
* $(LITERAL 'auto') $(LITERAL Identifier) $(LITERAL '=') $(RULE expression)
3998-
* | $(RULE typeConstructors) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression)
3999-
* | $(RULE typeConstructors)? $(RULE type) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression)
4000-
* | $(RULE expression)
40013996
* ;)
40023997
*/
40033998
IfStatement parseIfStatement()
@@ -4011,12 +4006,46 @@ class Parser
40114006
if (moreTokens)
40124007
node.startIndex = current().index;
40134008
mixin(tokenCheck!"(");
4009+
mixin(parseNodeQ!(`node.condition`, `IfCondition`));
4010+
mixin(tokenCheck!")");
4011+
if (currentIs(tok!"}"))
4012+
{
4013+
error("Statement expected", false);
4014+
node.tokens = tokens[startIndex .. index];
4015+
return node; // this line makes DCD better
4016+
}
4017+
mixin(parseNodeQ!(`node.thenStatement`, `DeclarationOrStatement`));
4018+
if (currentIs(tok!"else"))
4019+
{
4020+
advance();
4021+
mixin(parseNodeQ!(`node.elseStatement`, `DeclarationOrStatement`));
4022+
}
4023+
node.tokens = tokens[startIndex .. index];
4024+
return node;
4025+
}
4026+
4027+
/**
4028+
* Parses an IfCondition
4029+
*
4030+
* $(GRAMMAR $(RULEDEF ifCondition):
4031+
* $(LITERAL 'auto') $(LITERAL Identifier) $(LITERAL '=') $(RULE expression)
4032+
* | $(LITERAL 'scope') $(LITERAL Identifier) $(LITERAL '=') $(RULE expression)
4033+
* | $(RULE typeConstructors) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression)
4034+
* | $(RULE typeConstructors)? $(RULE type) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression)
4035+
* | $(RULE expression)
4036+
* ;)
4037+
*/
4038+
IfCondition parseIfCondition()
4039+
{
4040+
IfCondition node = allocator.make!IfCondition;
40144041
const b = setBookmark();
40154042

40164043
// ex. case:
40174044
// `if (auto identifier = exp)`
4018-
if (currentIs(tok!"auto") && peekIs(tok!"identifier"))
4045+
// `if (scope identifier = exp)`
4046+
if (currentIsOneOf(tok!"auto", tok!"scope") && peekIs(tok!"identifier"))
40194047
{
4048+
node.scope_ = currentIs(tok!"scope");
40204049
abandonBookmark(b);
40214050
advance();
40224051
node.identifier = advance();
@@ -4080,21 +4109,6 @@ class Parser
40804109
{
40814110
error("expression or declaration expected");
40824111
}
4083-
4084-
mixin(tokenCheck!")");
4085-
if (currentIs(tok!"}"))
4086-
{
4087-
error("Statement expected", false);
4088-
node.tokens = tokens[startIndex .. index];
4089-
return node; // this line makes DCD better
4090-
}
4091-
mixin(parseNodeQ!(`node.thenStatement`, `DeclarationOrStatement`));
4092-
if (currentIs(tok!"else"))
4093-
{
4094-
advance();
4095-
mixin(parseNodeQ!(`node.elseStatement`, `DeclarationOrStatement`));
4096-
}
4097-
node.tokens = tokens[startIndex .. index];
40984112
return node;
40994113
}
41004114

@@ -7960,7 +7974,7 @@ class Parser
79607974
if (moreTokens)
79617975
node.startIndex = current().index;
79627976
mixin(tokenCheck!"(");
7963-
mixin(parseNodeQ!(`node.expression`, `Expression`));
7977+
mixin(parseNodeQ!(`node.condition`, `IfCondition`));
79647978
mixin(tokenCheck!")");
79657979
if (currentIs(tok!"}"))
79667980
{

test/ast_checks/while_condition.d

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
void a()
2+
{
3+
while (auto line = readln)
4+
{
5+
line.strip;
6+
}
7+
}
8+
9+
void b()
10+
{
11+
while (scope line = readln)
12+
{
13+
line.strip;
14+
}
15+
}
16+
17+
void c()
18+
{
19+
while (const line = readln)
20+
{
21+
line.strip;
22+
}
23+
}
24+
25+
void d()
26+
{
27+
while (const inout string line = readln)
28+
{
29+
line.strip;
30+
}
31+
}

test/ast_checks/while_condition.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//functionDeclaration[name = 'a']//functionBody//whileStatement/condition/auto
2+
not(//functionDeclaration[name = 'a']//functionBody//whileStatement/condition/scope)
3+
//functionDeclaration[name = 'a']//functionBody//whileStatement/condition/identifier[text()='line']
4+
//functionDeclaration[name = 'a']//functionBody//whileStatement/condition/unaryExpression//identifier[text()='readln']
5+
//functionDeclaration[name = 'a']//functionBody//whileStatement/declarationOrStatement//identifier[text()='line']
6+
//functionDeclaration[name = 'a']//functionBody//whileStatement/declarationOrStatement//identifier[text()='strip']
7+
not(//functionDeclaration[name = 'b']//functionBody//whileStatement/condition/auto)
8+
//functionDeclaration[name = 'b']//functionBody//whileStatement/condition/scope
9+
//functionDeclaration[name = 'b']//functionBody//whileStatement/condition/identifier[text()='line']
10+
//functionDeclaration[name = 'b']//functionBody//whileStatement/condition/unaryExpression//identifier[text()='readln']
11+
//functionDeclaration[name = 'b']//functionBody//whileStatement/declarationOrStatement//identifier[text()='line']
12+
//functionDeclaration[name = 'b']//functionBody//whileStatement/declarationOrStatement//identifier[text()='strip']
13+
not(//functionDeclaration[name = 'c']//functionBody//whileStatement/condition/auto)
14+
not(//functionDeclaration[name = 'c']//functionBody//whileStatement/condition/scope)
15+
//functionDeclaration[name = 'c']//functionBody//whileStatement/condition/typeConstructor[text()='const']
16+
//functionDeclaration[name = 'c']//functionBody//whileStatement/condition/identifier[text()='line']
17+
//functionDeclaration[name = 'c']//functionBody//whileStatement/condition/unaryExpression//identifier[text()='readln']
18+
//functionDeclaration[name = 'c']//functionBody//whileStatement/declarationOrStatement//identifier[text()='line']
19+
//functionDeclaration[name = 'c']//functionBody//whileStatement/declarationOrStatement//identifier[text()='strip']
20+
not(//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/auto)
21+
not(//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/scope)
22+
//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/typeConstructor[text()='const']
23+
//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/typeConstructor[text()='inout']
24+
//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/type[@pretty='string']
25+
//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/identifier[text()='line']
26+
//functionDeclaration[name = 'd']//functionBody//whileStatement/condition/unaryExpression//identifier[text()='readln']
27+
//functionDeclaration[name = 'd']//functionBody//whileStatement/declarationOrStatement//identifier[text()='line']
28+
//functionDeclaration[name = 'd']//functionBody//whileStatement/declarationOrStatement//identifier[text()='strip']

0 commit comments

Comments
 (0)