// This file is part of Visual D // // Visual D integrates the D programming language into Visual Studio // Copyright (c) 2010-2011 by Rainer Schuetze, All Rights Reserved // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt module vdc.parser.mod; import vdc.util; import vdc.lexer; import vdc.parser.engine; import vdc.parser.decl; import vdc.parser.expr; import vdc.parser.misc; import vdc.parser.aggr; import vdc.parser.tmpl; import vdc.parser.stmt; import ast = vdc.ast.mod; import std.conv; //////////////////////////////////////////////////////////////// //-- GRAMMAR_BEGIN -- //Module: // ModuleDeclaration DeclDefs_opt // DeclDefs_opt // class Module { static Action enter(Parser p) { p.pushNode(new ast.Module(p.tok)); if(p.tok.id == TOK_module) { p.pushRecoverState(&recoverModuleDeclaration); p.pushState(&shiftModuleDeclaration); return ModuleDeclaration.enter(p); } if(p.tok.id == TOK_EOF) return Accept; p.pushState(&shiftModuleDeclDefs); return DeclDefs.enter(p); } static Action shiftModuleDeclaration(Parser p) { p.popAppendTopNode!(ast.Module)(); if(p.tok.id == TOK_EOF) return Accept; p.pushState(&shiftModuleDeclDefs); return DeclDefs.enter(p); } static Action shiftModuleDeclDefs(Parser p) { if(p.tok.id != TOK_EOF) { // recover by assuming the mismatched braces, trying to add more declarations to the module p.pushRecoverState(&recoverModuleDeclaration); return p.parseError("EOF expected"); } return Accept; } static Action recoverModuleDeclaration(Parser p) { p.pushState(&afterRecover); return Parser.recoverSemiCurly(p); } static Action recoverDeclDef(Parser p) { p.pushState(&afterRecover); return Parser.recoverSemiCurly(p); } static Action afterRecover(Parser p) { if(p.tok.id == TOK_EOF) return Accept; if(p.tok.id == TOK_rcurly) { // eat pending '}' p.pushState(&afterRecover); return Accept; } p.pushState(&shiftModuleDeclDefs); return DeclDefs.enter(p); } } //-- GRAMMAR_BEGIN -- //ModuleDeclaration: // module ModuleFullyQualifiedName ; class ModuleDeclaration { static Action enter(Parser p) { if(p.tok.id != TOK_module) return p.parseError("module expected"); p.pushNode(new ast.ModuleDeclaration(p.tok)); p.pushState(&shiftName); p.pushState(&ModuleFullyQualifiedName.enter); return Accept; } static Action shiftName(Parser p) { if(p.tok.id != TOK_semicolon) return p.parseError("semicolon expected"); p.popAppendTopNode!(ast.ModuleDeclaration)(); return Accept; } } //-- GRAMMAR_BEGIN -- //ModuleFullyQualifiedName: // ModuleName // Packages . ModuleName // //ModuleName: // Identifier // //Packages: // PackageName // Packages . PackageName // //PackageName: // Identifier class ModuleFullyQualifiedName { mixin ListNode!(ast.ModuleFullyQualifiedName, Identifier, TOK_dot); } /* might also be empty, but the grammar reflects this by the _opt suffix where used */ //-- GRAMMAR_BEGIN -- //DeclDefs: // DeclDef // DeclDef DeclDefs class DeclDefs { enum doNotPopNode = true; // does not create new Node, but inserts DeclDef into module or block static Action enter(Parser p) { if(p.tok.id == TOK_rcurly || p.tok.id == TOK_EOF) return Forward; p.pushRecoverState(&recover); p.pushState(&Parser.keepRecover); // add a "guard" state to avoid popping recover p.pushState(&shiftDeclDef); return DeclDef.enter(p); } static Action next(Parser p) { if(p.tok.id == TOK_rcurly || p.tok.id == TOK_EOF || p.tok.id == TOK_RECOVER) return Forward; p.pushState(&shiftDeclDef); return DeclDef.enter(p); } static Action shiftDeclDef(Parser p) { p.popAppendTopNode(); return next(p); } static Action recover(Parser p) { auto node = new ast.ParseRecoverNode(p.tok); if(p.nodeStack.depth) node.fulspan.start = p.topNode().fulspan.end; // record span of removed text p.pushNode(node); p.pushState(&afterRecover); return Parser.recoverSemiCurly(p); } static Action afterRecover(Parser p) { p.popAppendTopNode!(ast.Node, ast.ParseRecoverNode)(); p.pushRecoverState(&recover); p.pushState(&Parser.keepRecover); // add a "guard" state to avoid popping recover return next(p); } } //-- GRAMMAR_BEGIN -- //DeclDef: // AttributeSpecifier // PragmaSpecifier // ImportDeclaration // EnumDeclaration // ClassDeclaration // InterfaceDeclaration // AggregateDeclaration // Declaration // Constructor // Destructor // Invariant // UnitTest // StaticConstructor // StaticDestructor // SharedStaticConstructor // SharedStaticDestructor // DebugSpecification // VersionSpecification // ConditionalDeclaration // StaticAssert // TemplateDeclaration // TemplateMixinDeclaration // TemplateMixin // MixinDeclaration // ClassAllocator // ClassDeallocator // ; class DeclDef { // does not create new Node, but inserts DeclDef into module or block static Action enter(Parser p) { switch(p.tok.id) { case TOK_pragma: return Pragma.enter(p); case TOK_mixin: p.pushState(&shiftMixin); return Accept; case TOK_import: return ImportDeclaration.enter(p); case TOK_enum: return EnumDeclaration.enter(p); case TOK_struct: case TOK_union: case TOK_class: case TOK_interface: return AggregateDeclaration.enter(p); case TOK_this: return Constructor.enter(p); case TOK_tilde: return Destructor.enter(p); case TOK_invariant: return Invariant.enter(p); case TOK_unittest: return Unittest.enter(p); case TOK_debug: return DebugCondOrSpec.enter(p); case TOK_version: return VersionCondOrSpec.enter(p); case TOK_template: return TemplateDeclaration.enter(p); case TOK_new: return ClassAllocator.enter(p); case TOK_delete: return ClassDeallocator.enter(p); case TOK_semicolon: p.pushNode(new ast.EmptyDeclDef(p.tok)); return Accept; case TOK_static: p.pushToken(p.tok); p.pushState(&shiftStatic); return Accept; default: if(AttributeSpecifier.tryenter(p) == Accept) return Accept; return Declaration.enter(p); } } static Action shiftStatic(Parser p) { switch(p.tok.id) { case TOK_if: // only after static p.popToken(); return ConditionalDeclaration.enterAfterStatic(p); case TOK_assert: // only after static p.popToken(); return StaticAssert.enterAfterStatic(p); default: return AttributeSpecifier.enterAfterStatic(p); } } static Action shiftMixin(Parser p) { switch(p.tok.id) { case TOK_template: return TemplateMixinDeclaration.enterAfterMixin(p); case TOK_lparen: return MixinDeclaration.enterAfterMixin(p); default: return TemplateMixin.enterAfterMixin(p); } } } //-- GRAMMAR_BEGIN -- //AttributeSpecifier: // Attribute : // Attribute DeclarationBlock // //Attribute: // LinkageAttribute // AlignAttribute // AttributeOrStorageClass // ProtectionAttribute // @disable // @property // @safe // @system // @trusted class AttributeSpecifier { // no members means "Attribute:" static Action enter(Parser p) { if(tryenter(p) != Accept) return p.parseError("attribute specifier expected"); return Accept; } static Action tryenter(Parser p) { switch(p.tok.id) { case TOK_align: p.pushState(&shiftAttribute); return AlignAttribute.enter(p); case TOK_extern: p.pushState(&shiftAttribute); return LinkageAttribute.enter(p); case TOK_Identifier: if(p.tok.txt[0] != '@') return Forward; return UserAttributeSpecifier.enter(p); case TOK_deprecated: return UserAttributeSpecifier.enter(p); default: if(tokenToAnnotation(p.tok.id) == 0 && tokenToProtection(p.tok.id) == 0 && tokenToAttribute(p.tok.id) == 0) return Forward; p.pushToken(p.tok); p.pushState(&shiftColonOrLcurly); return Accept; } } // assumes attribute on the token stack static Action enterAfterStatic(Parser p) { return shiftColonOrLcurly(p); } static ast.AttributeSpecifier createFromToken(Token tok) { TokenId id = tok.id; ast.AttributeSpecifier n; Annotation annot = tokenToAnnotation(id); if(annot == 0) annot = tokenToProtection(id); if(annot != 0) { n = new ast.AttributeSpecifier(tok); n.annotation = annot; } else { Attribute attr = tokenToAttribute(id); if(attr != 0) { n = new ast.AttributeSpecifier(tok); n.attr = attr; } } assert(n); return n; } static Action shiftColonOrLcurly(Parser p) { switch(p.tok.id) { case TOK_lparen: if(isTypeModifier(p.topToken().id)) // running into a type instead of an attribute, so switch to Declaration return Decl!true.enterAttributeSpecifier(p); goto default; default: Token tok = p.popToken(); auto attr = createFromToken(tok); p.pushNode(attr); return shiftAttribute(p); } } static Action shiftAttribute(Parser p) { auto attr = p.topNode!(ast.AttributeSpecifier); switch(p.tok.id) { case TOK_colon: attr.id = TOK_colon; return Accept; case TOK_lcurly: attr.id = TOK_lcurly; p.pushState(&shiftRcurly); p.pushState(&DeclDefs.enter); p.pushNode(new ast.DeclarationBlock(p.tok)); return Accept; case TOK_Identifier: // identifier can be basic type or identifier of an AutoDeclaration p.pushToken(p.tok); p.pushState(&shiftIdentifier); return Accept; case TOK_alias: case TOK_typedef: p.pushState(&shiftDeclDef); return Declaration.enter(p); case TOK_align: p.pushState(&shiftDeclDef); p.pushState(&shiftAttribute); return AlignAttribute.enter(p); case TOK_extern: p.pushState(&shiftDeclDef); p.pushState(&shiftAttribute); return LinkageAttribute.enter(p); case TOK_pragma: case TOK_mixin: case TOK_import: case TOK_enum: case TOK_struct: case TOK_union: case TOK_class: case TOK_interface: case TOK_this: case TOK_tilde: case TOK_invariant: case TOK_unittest: case TOK_debug: case TOK_version: case TOK_template: case TOK_semicolon: case TOK_static: p.pushState(&shiftDeclDef); return DeclDef.enter(p); default: if(isTypeModifier(p.tok.id)) { p.pushState(&shiftDeclDef); p.pushToken(p.tok); p.pushState(&shiftColonOrLcurly); return Accept; } int annot = tokenToAnnotation(p.tok.id) | tokenToProtection(p.tok.id); int attrib = tokenToAttribute(p.tok.id); if(annot || attrib) { p.combineAnnotations(attr.annotation, annot); p.combineAttributes(attr.attr, attrib); p.pushState(&shiftAttribute); return Accept; } p.pushState(&shiftDeclDef); return Decl!true.enter(p); } } static Action shiftRcurly(Parser p) { if(p.tok.id != TOK_rcurly) return p.parseError("closing brace expected for declaration block"); p.popAppendTopNode!(ast.AttributeSpecifier)(); return Accept; } static Action shiftDeclDef(Parser p) { p.popAppendTopNode!(ast.AttributeSpecifier)(); return Forward; } static Action shiftIdentifier(Parser p) { switch(p.tok.id) { case TOK_assign: p.pushState(&shiftDeclDef); return Decl!true.enterIdentifier(p); case TOK_lparen: p.pushState(&shiftDeclDef); return Decl!true.enterAutoReturn(p); default: p.pushState(&shiftDeclDef); return Decl!true.enterTypeIdentifier(p); } } } //-- GRAMMAR_BEGIN -- //UserAttributeSpecifier: // deprectated // @identifier // deprecated ( ArgumentList ) // @identifier ( ArgumentList ) class UserAttributeSpecifier { static Action enter(Parser p) { switch(p.tok.id) { case TOK_deprecated: case TOK_Identifier: auto attr = new ast.UserAttributeSpecifier(p.tok); attr.ident = p.tok.txt; p.pushNode(attr); p.pushState(&shiftIdentifier); return Accept; default: return p.parseError("@identifier expected in user attribute"); } } static Action shiftIdentifier(Parser p) { switch(p.tok.id) { case TOK_lparen: p.pushState(&shiftArgumentList); return Arguments.enter(p); default: return Forward; } } static Action shiftArgumentList(Parser p) { p.popAppendTopNode!(ast.UserAttributeSpecifier, ast.ArgumentList)(); return Forward; } } //-- GRAMMAR_BEGIN -- //DeclarationBlock: // DeclDef // { DeclDefs_opt } class DeclarationBlock { static Action enter(Parser p) { switch(p.tok.id) { case TOK_lcurly: auto decl = new ast.DeclarationBlock(p.tok); p.pushNode(decl); p.pushState(&shiftLcurly); return Accept; default: return DeclDef.enter(p); } } static Action shiftLcurly(Parser p) { switch(p.tok.id) { case TOK_rcurly: return Accept; default: p.pushState(&shiftDeclDefs); return DeclDefs.enter(p); } } static Action shiftDeclDefs(Parser p) { switch(p.tok.id) { case TOK_rcurly: return Accept; default: return p.parseError("'}' expected"); } } } //-- GRAMMAR_BEGIN -- //LinkageAttribute: // extern ( LinkageType ) // //LinkageType: // "C" // "C" ++ // "D" // "Windows" // "Pascal" // "System" class LinkageAttribute { static Action enter(Parser p) { if(p.tok.id != TOK_extern) return p.parseError("extern expected"); p.pushState(&shiftLparen); return Accept; } static Action shiftLparen(Parser p) { switch(p.tok.id) { case TOK_lparen: p.pushNode(new ast.LinkageAttribute(p.tok)); p.pushState(&shiftLinkageType); return Accept; default: auto attr = new ast.AttributeSpecifier(p.tok); attr.attr = Attr_Extern; p.pushNode(attr); return Forward; } } static Action shiftLinkageType(Parser p) { Attribute attr = tokenToLinkageType(p.tok); if(attr == 0) return p.parseError("linkage type expected in extern"); p.topNode!(ast.LinkageAttribute)().attr = attr; p.pushState(&shiftRParen); return Accept; } static Action shiftRParen(Parser p) { assert(cast(ast.LinkageAttribute)p.topNode()); if(p.tok.id == TOK_rparen) return Accept; if(p.topNode().attr != Attr_ExternC || p.tok.id != TOK_plusplus) return p.parseError("closing parenthesis expected after linkage type"); p.topNode().attr = Attr_ExternCPP; p.pushState(&shiftRParen); return Accept; } } Attribute tokenToLinkageType(Token tok) { if(tok.id != TOK_Identifier) return 0; switch(tok.txt) { case "C": return Attr_ExternC; case "D": return Attr_ExternD; case "Windows": return Attr_ExternWindows; case "Pascal": return Attr_ExternPascal; case "System": return Attr_ExternSystem; default: return 0; } } //-- GRAMMAR_BEGIN -- //AlignAttribute: // align // align ( Integer ) class AlignAttribute { static Action enter(Parser p) { if(p.tok.id != TOK_align) return p.parseError("align expected"); p.pushNode(new ast.AlignAttribute(p.tok)); p.pushState(&shiftLparen); return Accept; } static Action shiftLparen(Parser p) { if(p.tok.id != TOK_lparen) return Forward; p.pushState(&shiftInteger); return Accept; } static Action shiftInteger(Parser p) { if(p.tok.id != TOK_IntegerLiteral) return p.parseError("integer expected in align"); int algn = parse!int(p.tok.txt); Attribute attr = alignmentToAttribute(algn); if(attr == 0) return p.parseError("alignment not supported: " ~ p.tok.txt); p.topNode!(ast.AlignAttribute)().attr = attr; p.pushState(&shiftRParen); return Accept; } static Action shiftRParen(Parser p) { assert(cast(ast.AlignAttribute)p.topNode()); if(p.tok.id != TOK_rparen) return p.parseError("closing parenthesis expected after align"); return Accept; } } Attribute alignmentToAttribute(int algn) { switch(algn) { case 1: return Attr_Align1; case 2: return Attr_Align2; case 4: return Attr_Align4; case 8: return Attr_Align8; case 16: return Attr_Align16; default: return 0; } } //-- GRAMMAR_BEGIN -- //ProtectionAttribute: // private // package // protected // public // export //PragmaSpecifier: // Pragma DeclDef //Pragma: // pragma ( Identifier ) // pragma ( Identifier , TemplateArgumentList ) class Pragma { static Action enter(Parser p) { if(p.tok.id != TOK_pragma) return p.parseError("pragma expected"); p.pushNode(new ast.Pragma(p.tok)); p.pushState(&shiftLparen); return Accept; } static Action shiftLparen(Parser p) { if(p.tok.id != TOK_lparen) return Forward; p.pushState(&shiftIdentifier); return Accept; } static Action shiftIdentifier(Parser p) { if(p.tok.id != TOK_Identifier) return p.parseError("integer expected in align"); p.topNode!(ast.Pragma)().ident = p.tok.txt; p.pushState(&shiftCommaOrRParen); return Accept; } static Action shiftCommaOrRParen(Parser p) { if(p.tok.id == TOK_comma) { p.pushState(&shiftArgumentList); p.pushState(&TemplateArgumentList.enter); return Accept; } return shiftRParen(p); } static Action shiftRParen(Parser p) { assert(cast(ast.Pragma)p.topNode()); if(p.tok.id != TOK_rparen) return p.parseError("closing parenthesis expected for pragma"); return Accept; } static Action shiftArgumentList(Parser p) { p.popAppendTopNode!(ast.Pragma)(); return shiftRParen(p); } } //-- GRAMMAR_BEGIN -- //ImportDeclaration: // import ImportList ; class ImportDeclaration { mixin SequenceNode!(ast.ImportDeclaration, TOK_import, ImportList, TOK_semicolon); } //-- GRAMMAR_BEGIN -- //ImportList: // Import // ImportBindings // Import , ImportList // //ImportBindings: // Import : ImportBindList class ImportList { static Action enter(Parser p) { p.pushNode(new ast.ImportList(p.tok)); p.pushState(&shiftImport); return Import.enter(p); } static Action shiftImport(Parser p) { if(p.tok.id == TOK_colon) { p.pushState(&shiftImportBindList); p.pushState(&ImportBindList.enter); return Accept; } p.popAppendTopNode!(ast.ImportList)(); if(p.tok.id == TOK_comma) { p.pushState(&shiftImport); p.pushState(&Import.enter); return Accept; } return Forward; } static Action shiftImportBindList(Parser p) { p.popAppendTopNode!(ast.Import)(); p.popAppendTopNode!(ast.ImportList)(); return Forward; } } //-- GRAMMAR_BEGIN -- //Import: // ModuleFullyQualifiedName // ModuleAliasIdentifier = ModuleFullyQualifiedName // //ModuleAliasIdentifier: // Identifier class Import { static Action enter(Parser p) { if(p.tok.id != TOK_Identifier) return p.parseError("identifier expected in import"); p.pushToken(p.tok); p.pushNode(new ast.Import(p.tok)); p.pushState(&shiftIdentifier); return Accept; } static Action shiftIdentifier(Parser p) { p.pushState(&shiftFullyQualifiedName); if(p.tok.id == TOK_assign) { p.topNode!(ast.Import)().aliasIdent = p.popToken().txt; p.pushState(&ModuleFullyQualifiedName.enter); return Accept; } // delegate into ModuleFullyQualifiedName p.pushNode(new ast.ModuleFullyQualifiedName(p.tok)); auto id = new ast.Identifier(p.popToken()); p.pushNode(id); return ModuleFullyQualifiedName.shift(p); } static Action shiftFullyQualifiedName(Parser p) { p.popAppendTopNode!(ast.Import)(); return Forward; } } //-- GRAMMAR_BEGIN -- //ImportBindList: // ImportBind // ImportBind , ImportBindList class ImportBindList { mixin ListNode!(ast.ImportBindList, ImportBind, TOK_comma); } //-- GRAMMAR_BEGIN -- //ImportBind: // Identifier // Identifier = Identifier class ImportBind { mixin OptionalNode!(ast.ImportBind, Identifier, TOK_assign, Identifier); } //-- GRAMMAR_BEGIN -- //MixinDeclaration: // mixin ( AssignExpression ) ; class MixinDeclaration { mixin SequenceNode!(ast.MixinDeclaration, TOK_mixin, TOK_lparen, AssignExpression, TOK_rparen, TOK_semicolon); static Action enterAfterMixin(Parser p) { p.pushNode(new ast.MixinDeclaration(p.tok)); return shift1.shift(p); } } //-- GRAMMAR_BEGIN -- //Unittest: // unittest BlockStatement class Unittest { mixin SequenceNode!(ast.Unittest, TOK_unittest, BlockStatement); }