diff --git a/src/dparse/ast.d b/src/dparse/ast.d index 8362c715..127e061a 100644 --- a/src/dparse/ast.d +++ b/src/dparse/ast.d @@ -221,6 +221,8 @@ public: /** */ void visit(const Finally finally_) { finally_.accept(this); } /** */ void visit(const ForStatement forStatement) { forStatement.accept(this); } /** */ void visit(const ForeachStatement foreachStatement) { foreachStatement.accept(this); } + /** */ void visit(const StaticForeachDeclaration staticForeachDeclaration) { staticForeachDeclaration.accept(this); } + /** */ void visit(const StaticForeachStatement staticForeachStatement) { staticForeachStatement.accept(this); } /** */ void visit(const ForeachType foreachType) { foreachType.accept(this); } /** */ void visit(const ForeachTypeList foreachTypeList) { foreachTypeList.accept(this); } /** */ void visit(const FunctionAttribute functionAttribute) { functionAttribute.accept(this); } @@ -1304,7 +1306,7 @@ public: SharedStaticConstructor, SharedStaticDestructor, StaticAssertDeclaration, StaticConstructor, StaticDestructor, StructDeclaration, TemplateDeclaration, UnionDeclaration, Unittest, VariableDeclaration, - VersionSpecification); + VersionSpecification, StaticForeachDeclaration); private Algebraic!(DeclarationTypes) storage; @@ -1347,6 +1349,7 @@ public: mixin(generateProperty("Unittest", "unittest_")); mixin(generateProperty("VariableDeclaration", "variableDeclaration")); mixin(generateProperty("VersionSpecification", "versionSpecification")); + mixin(generateProperty("StaticForeachDeclaration", "staticForeachDeclaration")); override bool opEquals(Object other) const { @@ -1629,22 +1632,47 @@ public: mixin OpEquals; } + /// -final class ForeachStatement : ASTNode +final class Foreach(bool declOnly) : ASTNode { public: override void accept(ASTVisitor visitor) const { - mixin (visitIfNotNull!(foreachType, foreachTypeList, low, high, - declarationOrStatement)); + mixin (visitIfNotNull!(foreachType, foreachTypeList, low, high)); + static if (declOnly) + mixin (visitIfNotNull!(declarations)); + else + mixin (visitIfNotNull!(declarationOrStatement)); } /** */ IdType type; /** */ ForeachTypeList foreachTypeList; /** */ ForeachType foreachType; /** */ Expression low; /** */ Expression high; - /** */ DeclarationOrStatement declarationOrStatement; /** */ size_t startIndex; + static if (declOnly) + /** */ Declaration[] declarations; + else + /** */ DeclarationOrStatement declarationOrStatement; + mixin OpEquals; +} + +/// +alias StaticForeachDeclaration = Foreach!true; + +/// +alias ForeachStatement = Foreach!false; + +/// +final class StaticForeachStatement : ASTNode +{ +public: + override void accept(ASTVisitor visitor) const + { + mixin (visitIfNotNull!(foreachStatement)); + } + /** */ ForeachStatement foreachStatement; mixin OpEquals; } @@ -1656,6 +1684,8 @@ public: { mixin (visitIfNotNull!(type, identifier)); } + /** */ bool isAlias; + /** */ bool isEnum; /** */ bool isRef; /** */ IdType[] typeConstructors; /** */ Type type; @@ -2264,7 +2294,7 @@ public: synchronizedStatement, tryStatement, throwStatement, scopeGuardStatement, asmStatement, pragmaStatement, conditionalStatement, staticAssertStatement, versionSpecification, - debugSpecification, expressionStatement)); + debugSpecification, expressionStatement, staticForeachStatement)); } /** */ LabeledStatement labeledStatement; /** */ BlockStatement blockStatement; @@ -2273,6 +2303,7 @@ public: /** */ DoStatement doStatement; /** */ ForStatement forStatement; /** */ ForeachStatement foreachStatement; + /** */ StaticForeachStatement staticForeachStatement; /** */ SwitchStatement switchStatement; /** */ FinalSwitchStatement finalSwitchStatement; /** */ ContinueStatement continueStatement; diff --git a/src/dparse/parser.d b/src/dparse/parser.d index 6a3a41b9..96c5aeb0 100644 --- a/src/dparse/parser.d +++ b/src/dparse/parser.d @@ -2097,6 +2097,8 @@ class Parser mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict)`); else if (peekIs(tok!"assert")) mixin(parseNodeQ!(`node.staticAssertDeclaration`, `StaticAssertDeclaration`)); + else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse")) + mixin(parseNodeQ!(`node.staticForeachDeclaration`, `StaticForeachDeclaration`)); else goto type; break; @@ -2741,6 +2743,37 @@ class Parser return node; } + /** + * Parses a StaticForeachDeclaration + * + * $(GRAMMAR $(RULEDEF staticForeachDeclaration): + * $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachTypeList) $(LITERAL ';') $(RULE expression) $(LITERAL '$(RPAREN)') + * ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')) + * | $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachType) $(LITERAL ';') $(RULE expression) $(LITERAL '..') $(RULE expression) $(LITERAL '$(RPAREN)') + * ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')) + * ;) + */ + StaticForeachDeclaration parseStaticForeachDeclaration() + { + mixin(traceEnterAndExit!(__FUNCTION__)); + mixin(tokenCheck!"static"); + return parseForeach!true(); + } + + /** + * Parses a StaticForeachStatement + * + * $(GRAMMAR $(RULEDEF staticForeachStatement): + * $(LITERAL 'static') $(RULE foreachStatement) + * ;) + */ + StaticForeachStatement parseStaticForeachStatement() + { + mixin(traceEnterAndExit!(__FUNCTION__)); + mixin(simpleParse!(StaticForeachStatement, + tok!"static", "foreachStatement|parseForeachStatement")); + } + /** * Parses a ForeachStatement * @@ -2752,7 +2785,13 @@ class Parser ForeachStatement parseForeachStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); - ForeachStatement node = allocator.make!ForeachStatement; + return parseForeach!false(); + } + + Foreach!declOnly parseForeach(bool declOnly = false)() + { + mixin(traceEnterAndExit!(__FUNCTION__)); + Foreach!declOnly node = allocator.make!(Foreach!declOnly); if (currentIsOneOf(tok!"foreach", tok!"foreach_reverse")) node.type = advance().type; else @@ -2791,7 +2830,33 @@ class Parser error("Statement expected", false); return node; // this line makes DCD better } - mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); + static if (declOnly) + { + StackBuffer declarations; + if (currentIs(tok!"{")) + { + advance(); + while (moreTokens() && !currentIs(tok!"}")) + { + immutable b = setBookmark(); + immutable c = allocator.setCheckpoint(); + if (declarations.put(parseDeclaration(true, true))) + abandonBookmark(b); + else + { + goToBookmark(b); + allocator.rollback(c); + return null; + } + } + mixin(tokenCheck!"}"); + } + else if (!declarations.put(parseDeclaration(true, true))) + return null; + ownArray(node.declarations, declarations); + } + else + mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); return node; } @@ -2799,29 +2864,38 @@ class Parser * Parses a ForeachType * * $(GRAMMAR $(RULEDEF foreachType): - * $(LITERAL 'ref')? $(RULE typeConstructors)? $(RULE type)? $(LITERAL Identifier) - * | $(RULE typeConstructors)? $(LITERAL 'ref')? $(RULE type)? $(LITERAL Identifier) + * ($(LITERAL 'ref') | $(LITERAL 'alias') | $(LITERAL 'enum') | $(RULE typeConstructor))* $(RULE type)? $(LITERAL Identifier) * ;) */ ForeachType parseForeachType() { mixin(traceEnterAndExit!(__FUNCTION__)); auto node = allocator.make!ForeachType; - if (currentIs(tok!"ref")) - { - node.isRef = true; - advance(); - } - if (currentIsOneOf(tok!"const", tok!"immutable", - tok!"inout", tok!"shared") && !peekIs(tok!"(")) - { - trace("\033[01;36mType constructor"); - mixin(parseNodeQ!(`node.typeConstructors`, `TypeConstructors`)); - } - if (currentIs(tok!"ref")) + while (moreTokens()) { - node.isRef = true; - advance(); + IdType typeConstructor; + if (currentIs(tok!"ref")) + { + node.isRef = true; + advance(); + } + else if (currentIs(tok!"alias")) + { + node.isAlias = true; + advance(); + } + else if (currentIs(tok!"enum")) + { + node.isEnum = true; + advance(); + } + else if (tok!"" != (typeConstructor = parseTypeConstructor(false))) + { + trace("\033[01;36mType constructor"); + node.typeConstructors ~= typeConstructor; + } + else + break; } if (currentIs(tok!"identifier") && peekIsOneOf(tok!",", tok!";")) { @@ -4966,9 +5040,11 @@ class Parser mixin(parseNodeQ!(`node.conditionalStatement`, `ConditionalStatement`)); else if (peekIs(tok!"assert")) mixin(parseNodeQ!(`node.staticAssertStatement`, `StaticAssertStatement`)); + else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse")) + mixin(parseNodeQ!(`node.staticForeachStatement`, `StaticForeachStatement`)); else { - error("'if' or 'assert' expected."); + error("'if' or 'assert' or 'foreach' or 'foreach_reverse' expected."); return null; } break; @@ -6861,6 +6937,8 @@ protected: case tok!"static": if (peekIs(tok!"if")) return false; + else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse")) + goto default; goto case; case tok!"scope": if (peekIs(tok!"(")) @@ -6975,7 +7053,7 @@ protected: case tok!"scope": return !peekIs(tok!"("); case tok!"static": - return !peekIsOneOf(tok!"assert", tok!"this", tok!"if", tok!"~"); + return !peekIsOneOf(tok!"assert", tok!"this", tok!"if", tok!"~", tok!"foreach", tok!"foreach_reverse"); case tok!"shared": return !(startsWith(tok!"shared", tok!"static", tok!"this") || startsWith(tok!"shared", tok!"static", tok!"~") diff --git a/test/pass_files/declarations.d b/test/pass_files/declarations.d index 9ba11ec5..eb4b1c88 100644 --- a/test/pass_files/declarations.d +++ b/test/pass_files/declarations.d @@ -70,3 +70,18 @@ idouble a = 4Li; idouble a = 4i; ifloat a = 4fi; ifloat a = 4Fi; + +static foreach (n; ['a', 'b', 'c']) +{ + mixin("char " ~ n ~ ";"); +} + +static foreach_reverse (i; '0' .. '5') +{ + mixin("int _" ~ i ~ ";"); +} + +static foreach (enum i, alias T; AliasSeq!(int, bool)) +{ + T a = i; +} diff --git a/test/pass_files/statements.d b/test/pass_files/statements.d index ca7379bd..aac229ba 100644 --- a/test/pass_files/statements.d +++ b/test/pass_files/statements.d @@ -60,4 +60,15 @@ deprecated void foo() i++; } label: + + + static foreach (n; ['a', 'b', 'c']) + {{ + mixin(n ~ "++;"); + }} + + static foreach_reverse (i; 1 .. 10) + { + assert(i-- > 0); + } }