1 // This file is part of Visual D
2 //
3 // Visual D integrates the D programming language into Visual Studio
4 // Copyright (c) 2010-2011 by Rainer Schuetze, All Rights Reserved
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
8 
9 module vdc.parser.mod;
10 
11 import vdc.util;
12 import vdc.lexer;
13 import vdc.parser.engine;
14 import vdc.parser.decl;
15 import vdc.parser.expr;
16 import vdc.parser.misc;
17 import vdc.parser.aggr;
18 import vdc.parser.tmpl;
19 import vdc.parser.stmt;
20 
21 import ast = vdc.ast.mod;
22 
23 import std.conv;
24 
25 ////////////////////////////////////////////////////////////////
26 
27 //-- GRAMMAR_BEGIN --
28 //Module:
29 //    ModuleDeclaration DeclDefs_opt
30 //    DeclDefs_opt
31 //
32 class Module
33 {
34     static Action enter(Parser p)
35     {
36         p.pushNode(new ast.Module(p.tok));
37 
38         if(p.tok.id == TOK_module)
39         {
40             p.pushRecoverState(&recoverModuleDeclaration);
41 
42             p.pushState(&shiftModuleDeclaration);
43             return ModuleDeclaration.enter(p);
44         }
45 
46         if(p.tok.id == TOK_EOF)
47             return Accept;
48 
49         p.pushState(&shiftModuleDeclDefs);
50         return DeclDefs.enter(p);
51     }
52 
53     static Action shiftModuleDeclaration(Parser p)
54     {
55         p.popAppendTopNode!(ast.Module)();
56         if(p.tok.id == TOK_EOF)
57             return Accept;
58 
59         p.pushState(&shiftModuleDeclDefs);
60         return DeclDefs.enter(p);
61     }
62 
63     static Action shiftModuleDeclDefs(Parser p)
64     {
65         if(p.tok.id != TOK_EOF)
66         {
67             // recover by assuming the mismatched braces, trying to add more declarations to the module
68             p.pushRecoverState(&recoverModuleDeclaration);
69 
70             return p.parseError("EOF expected");
71         }
72         return Accept;
73     }
74 
75     static Action recoverModuleDeclaration(Parser p)
76     {
77         p.pushState(&afterRecover);
78         return Parser.recoverSemiCurly(p);
79     }
80 
81     static Action recoverDeclDef(Parser p)
82     {
83         p.pushState(&afterRecover);
84             return Parser.recoverSemiCurly(p);
85     }
86 
87     static Action afterRecover(Parser p)
88     {
89         if(p.tok.id == TOK_EOF)
90             return Accept;
91 
92         if(p.tok.id == TOK_rcurly)
93         {
94             // eat pending '}'
95             p.pushState(&afterRecover);
96             return Accept;
97         }
98         p.pushState(&shiftModuleDeclDefs);
99         return DeclDefs.enter(p);
100     }
101 }
102 
103 //-- GRAMMAR_BEGIN --
104 //ModuleDeclaration:
105 //    module ModuleFullyQualifiedName ;
106 class ModuleDeclaration
107 {
108     static Action enter(Parser p)
109     {
110         if(p.tok.id != TOK_module)
111             return p.parseError("module expected");
112 
113         p.pushNode(new ast.ModuleDeclaration(p.tok));
114 
115         p.pushState(&shiftName);
116         p.pushState(&ModuleFullyQualifiedName.enter);
117         return Accept;
118     }
119 
120     static Action shiftName(Parser p)
121     {
122         if(p.tok.id != TOK_semicolon)
123             return p.parseError("semicolon expected");
124         p.popAppendTopNode!(ast.ModuleDeclaration)();
125         return Accept;
126     }
127 }
128 
129 //-- GRAMMAR_BEGIN --
130 //ModuleFullyQualifiedName:
131 //    ModuleName
132 //    Packages . ModuleName
133 //
134 //ModuleName:
135 //    Identifier
136 //
137 //Packages:
138 //    PackageName
139 //    Packages . PackageName
140 //
141 //PackageName:
142 //    Identifier
143 class ModuleFullyQualifiedName
144 {
145     mixin ListNode!(ast.ModuleFullyQualifiedName, Identifier, TOK_dot);
146 }
147 
148 /* might also be empty, but the grammar reflects this by the _opt suffix where used */
149 //-- GRAMMAR_BEGIN --
150 //DeclDefs:
151 //    DeclDef
152 //    DeclDef DeclDefs
153 class DeclDefs
154 {
155     enum doNotPopNode = true;
156 
157     // does not create new Node, but inserts DeclDef into module or block
158     static Action enter(Parser p)
159     {
160         if(p.tok.id == TOK_rcurly || p.tok.id == TOK_EOF)
161             return Forward;
162 
163         p.pushRecoverState(&recover);
164         p.pushState(&Parser.keepRecover);   // add a "guard" state to avoid popping recover
165 
166         p.pushState(&shiftDeclDef);
167         return DeclDef.enter(p);
168     }
169 
170     static Action next(Parser p)
171     {
172         if(p.tok.id == TOK_rcurly || p.tok.id == TOK_EOF || p.tok.id == TOK_RECOVER)
173             return Forward;
174 
175         p.pushState(&shiftDeclDef);
176         return DeclDef.enter(p);
177     }
178 
179     static Action shiftDeclDef(Parser p)
180     {
181         p.popAppendTopNode();
182         return next(p);
183     }
184 
185     static Action recover(Parser p)
186     {
187         auto node = new ast.ParseRecoverNode(p.tok);
188         if(p.nodeStack.depth)
189             node.fulspan.start = p.topNode().fulspan.end; // record span of removed text
190         p.pushNode(node);
191         p.pushState(&afterRecover);
192         return Parser.recoverSemiCurly(p);
193     }
194 
195     static Action afterRecover(Parser p)
196     {
197         p.popAppendTopNode!(ast.Node, ast.ParseRecoverNode)();
198 
199         p.pushRecoverState(&recover);
200         p.pushState(&Parser.keepRecover);   // add a "guard" state to avoid popping recover
201 
202         return next(p);
203     }
204 }
205 
206 //-- GRAMMAR_BEGIN --
207 //DeclDef:
208 //    AttributeSpecifier
209 //    PragmaSpecifier
210 //    ImportDeclaration
211 //    EnumDeclaration
212 //    ClassDeclaration
213 //    InterfaceDeclaration
214 //    AggregateDeclaration
215 //    Declaration
216 //    Constructor
217 //    Destructor
218 //    Invariant
219 //    UnitTest
220 //    StaticConstructor
221 //    StaticDestructor
222 //    SharedStaticConstructor
223 //    SharedStaticDestructor
224 //    DebugSpecification
225 //    VersionSpecification
226 //    ConditionalDeclaration
227 //    StaticAssert
228 //    TemplateDeclaration
229 //    TemplateMixinDeclaration
230 //    TemplateMixin
231 //    MixinDeclaration
232 //    ClassAllocator
233 //    ClassDeallocator
234 //    ;
235 class DeclDef
236 {
237     // does not create new Node, but inserts DeclDef into module or block
238     static Action enter(Parser p)
239     {
240         switch(p.tok.id)
241         {
242             case TOK_pragma:
243                 return Pragma.enter(p);
244             case TOK_mixin:
245                 p.pushState(&shiftMixin);
246                 return Accept;
247             case TOK_import:
248                 return ImportDeclaration.enter(p);
249             case TOK_enum:
250                 return EnumDeclaration.enter(p);
251 
252             case TOK_struct:
253             case TOK_union:
254             case TOK_class:
255             case TOK_interface:
256                 return AggregateDeclaration.enter(p);
257 
258             case TOK_this:
259                 return Constructor.enter(p);
260             case TOK_tilde:
261                 return Destructor.enter(p);
262             case TOK_invariant:
263                 return Invariant.enter(p);
264             case TOK_unittest:
265                 return Unittest.enter(p);
266             case TOK_debug:
267                 return DebugCondOrSpec.enter(p);
268             case TOK_version:
269                 return VersionCondOrSpec.enter(p);
270             case TOK_template:
271                 return TemplateDeclaration.enter(p);
272             case TOK_new:
273                 return ClassAllocator.enter(p);
274             case TOK_delete:
275                 return ClassDeallocator.enter(p);
276 
277             case TOK_semicolon:
278                 p.pushNode(new ast.EmptyDeclDef(p.tok));
279                 return Accept;
280 
281             case TOK_static:
282                 p.pushToken(p.tok);
283                 p.pushState(&shiftStatic);
284                 return Accept;
285 
286             default:
287                 if(AttributeSpecifier.tryenter(p) == Accept)
288                     return Accept;
289                 return Declaration.enter(p);
290         }
291     }
292 
293     static Action shiftStatic(Parser p)
294     {
295         switch(p.tok.id)
296         {
297             case TOK_if: // only after static
298                 p.popToken();
299                 return ConditionalDeclaration.enterAfterStatic(p);
300             case TOK_assert: // only after static
301                 p.popToken();
302                 return StaticAssert.enterAfterStatic(p);
303             default:
304                 return AttributeSpecifier.enterAfterStatic(p);
305         }
306     }
307 
308     static Action shiftMixin(Parser p)
309     {
310         switch(p.tok.id)
311         {
312             case TOK_template:
313                 return TemplateMixinDeclaration.enterAfterMixin(p);
314             case TOK_lparen:
315                 return MixinDeclaration.enterAfterMixin(p);
316             default:
317                 return TemplateMixin.enterAfterMixin(p);
318         }
319     }
320 }
321 
322 //-- GRAMMAR_BEGIN --
323 //AttributeSpecifier:
324 //    Attribute :
325 //    Attribute DeclarationBlock
326 //
327 //Attribute:
328 //    LinkageAttribute
329 //    AlignAttribute
330 //    AttributeOrStorageClass
331 //    ProtectionAttribute
332 //    @disable
333 //    @property
334 //    @safe
335 //    @system
336 //    @trusted
337 class AttributeSpecifier
338 {
339     // no members means "Attribute:"
340 
341     static Action enter(Parser p)
342     {
343         if(tryenter(p) != Accept)
344             return p.parseError("attribute specifier expected");
345         return Accept;
346     }
347 
348     static Action tryenter(Parser p)
349     {
350         switch(p.tok.id)
351         {
352             case TOK_align:
353                 p.pushState(&shiftAttribute);
354                 return AlignAttribute.enter(p);
355             case TOK_extern:
356                 p.pushState(&shiftAttribute);
357                 return LinkageAttribute.enter(p);
358             case TOK_Identifier:
359                 if(p.tok.txt[0] != '@')
360                     return Forward;
361                 return UserAttributeSpecifier.enter(p);
362             case TOK_deprecated:
363                 return UserAttributeSpecifier.enter(p);
364 
365             default:
366                 if(tokenToAnnotation(p.tok.id) == 0 &&
367                    tokenToProtection(p.tok.id) == 0 &&
368                    tokenToAttribute(p.tok.id) == 0)
369                     return Forward;
370 
371                 p.pushToken(p.tok);
372                 p.pushState(&shiftColonOrLcurly);
373                 return Accept;
374         }
375     }
376 
377     // assumes attribute on the token stack
378     static Action enterAfterStatic(Parser p)
379     {
380         return shiftColonOrLcurly(p);
381     }
382 
383     static ast.AttributeSpecifier createFromToken(Token tok)
384     {
385         TokenId id = tok.id;
386         ast.AttributeSpecifier n;
387         Annotation annot = tokenToAnnotation(id);
388         if(annot == 0)
389             annot = tokenToProtection(id);
390         if(annot != 0)
391         {
392             n = new ast.AttributeSpecifier(tok);
393             n.annotation = annot;
394         }
395         else
396         {
397             Attribute attr = tokenToAttribute(id);
398             if(attr != 0)
399             {
400                 n = new ast.AttributeSpecifier(tok);
401                 n.attr = attr;
402             }
403         }
404         assert(n);
405         return n;
406     }
407 
408     static Action shiftColonOrLcurly(Parser p)
409     {
410         switch(p.tok.id)
411         {
412             case TOK_lparen:
413                 if(isTypeModifier(p.topToken().id))
414                     // running into a type instead of an attribute, so switch to Declaration
415                     return Decl!true.enterAttributeSpecifier(p);
416                 goto default;
417             default:
418                 Token tok = p.popToken();
419                 auto attr = createFromToken(tok);
420                 p.pushNode(attr);
421                 return shiftAttribute(p);
422         }
423     }
424 
425     static Action shiftAttribute(Parser p)
426     {
427         auto attr = p.topNode!(ast.AttributeSpecifier);
428         switch(p.tok.id)
429         {
430             case TOK_colon:
431                 attr.id = TOK_colon;
432                 return Accept;
433 
434             case TOK_lcurly:
435                 attr.id = TOK_lcurly;
436                 p.pushState(&shiftRcurly);
437                 p.pushState(&DeclDefs.enter);
438                 p.pushNode(new ast.DeclarationBlock(p.tok));
439                 return Accept;
440 
441             case TOK_Identifier:
442                 // identifier can be basic type or identifier of an AutoDeclaration
443                 p.pushToken(p.tok);
444                 p.pushState(&shiftIdentifier);
445                 return Accept;
446 
447             case TOK_alias:
448             case TOK_typedef:
449                 p.pushState(&shiftDeclDef);
450                 return Declaration.enter(p);
451 
452             case TOK_align:
453                 p.pushState(&shiftDeclDef);
454                 p.pushState(&shiftAttribute);
455                 return AlignAttribute.enter(p);
456             case TOK_extern:
457                 p.pushState(&shiftDeclDef);
458                 p.pushState(&shiftAttribute);
459                 return LinkageAttribute.enter(p);
460 
461             case TOK_pragma:
462             case TOK_mixin:
463             case TOK_import:
464             case TOK_enum:
465             case TOK_struct:
466             case TOK_union:
467             case TOK_class:
468             case TOK_interface:
469             case TOK_this:
470             case TOK_tilde:
471             case TOK_invariant:
472             case TOK_unittest:
473             case TOK_debug:
474             case TOK_version:
475             case TOK_template:
476             case TOK_semicolon:
477             case TOK_static:
478                 p.pushState(&shiftDeclDef);
479                 return DeclDef.enter(p);
480             default:
481                 if(isTypeModifier(p.tok.id))
482                 {
483                     p.pushState(&shiftDeclDef);
484                     p.pushToken(p.tok);
485                     p.pushState(&shiftColonOrLcurly);
486                     return Accept;
487                 }
488                 int annot = tokenToAnnotation(p.tok.id) | tokenToProtection(p.tok.id);
489                 int attrib = tokenToAttribute(p.tok.id);
490                 if(annot || attrib)
491                 {
492                     p.combineAnnotations(attr.annotation, annot);
493                     p.combineAttributes(attr.attr, attrib);
494                     p.pushState(&shiftAttribute);
495                     return Accept;
496                 }
497                 p.pushState(&shiftDeclDef);
498                 return Decl!true.enter(p);
499         }
500     }
501 
502     static Action shiftRcurly(Parser p)
503     {
504         if(p.tok.id != TOK_rcurly)
505             return p.parseError("closing brace expected for declaration block");
506 
507         p.popAppendTopNode!(ast.AttributeSpecifier)();
508         return Accept;
509     }
510 
511     static Action shiftDeclDef(Parser p)
512     {
513         p.popAppendTopNode!(ast.AttributeSpecifier)();
514         return Forward;
515     }
516 
517     static Action shiftIdentifier(Parser p)
518     {
519         switch(p.tok.id)
520         {
521             case TOK_assign:
522                 p.pushState(&shiftDeclDef);
523                 return Decl!true.enterIdentifier(p);
524             case TOK_lparen:
525                 p.pushState(&shiftDeclDef);
526                 return Decl!true.enterAutoReturn(p);
527             default:
528                 p.pushState(&shiftDeclDef);
529                 return Decl!true.enterTypeIdentifier(p);
530         }
531     }
532 }
533 
534 //-- GRAMMAR_BEGIN --
535 //UserAttributeSpecifier:
536 //    deprectated
537 //    @identifier
538 //    deprecated ( ArgumentList )
539 //    @identifier ( ArgumentList )
540 class UserAttributeSpecifier
541 {
542     static Action enter(Parser p)
543     {
544         switch(p.tok.id)
545         {
546             case TOK_deprecated:
547             case TOK_Identifier:
548                 auto attr = new ast.UserAttributeSpecifier(p.tok);
549                 attr.ident = p.tok.txt;
550                 p.pushNode(attr);
551                 p.pushState(&shiftIdentifier);
552                 return Accept;
553             default:
554                 return p.parseError("@identifier expected in user attribute");
555         }
556     }
557 
558     static Action shiftIdentifier(Parser p)
559     {
560         switch(p.tok.id)
561         {
562             case TOK_lparen:
563                 p.pushState(&shiftArgumentList);
564                 return Arguments.enter(p);
565             default:
566                 return Forward;
567         }
568     }
569 
570     static Action shiftArgumentList(Parser p)
571     {
572         p.popAppendTopNode!(ast.UserAttributeSpecifier, ast.ArgumentList)();
573         return Forward;
574     }
575 }
576 
577 //-- GRAMMAR_BEGIN --
578 //DeclarationBlock:
579 //    DeclDef
580 //    { DeclDefs_opt }
581 class DeclarationBlock
582 {
583     static Action enter(Parser p)
584     {
585         switch(p.tok.id)
586         {
587             case TOK_lcurly:
588                 auto decl = new ast.DeclarationBlock(p.tok);
589                 p.pushNode(decl);
590                 p.pushState(&shiftLcurly);
591                 return Accept;
592             default:
593                 return DeclDef.enter(p);
594         }
595     }
596     static Action shiftLcurly(Parser p)
597     {
598         switch(p.tok.id)
599         {
600             case TOK_rcurly:
601                 return Accept;
602             default:
603                 p.pushState(&shiftDeclDefs);
604                 return DeclDefs.enter(p);
605         }
606     }
607 
608     static Action shiftDeclDefs(Parser p)
609     {
610         switch(p.tok.id)
611         {
612             case TOK_rcurly:
613                 return Accept;
614             default:
615                 return p.parseError("'}' expected");
616         }
617     }
618 }
619 
620 
621 //-- GRAMMAR_BEGIN --
622 //LinkageAttribute:
623 //    extern ( LinkageType )
624 //
625 //LinkageType:
626 //    "C"
627 //    "C" ++
628 //    "D"
629 //    "Windows"
630 //    "System"
631 class LinkageAttribute
632 {
633     static Action enter(Parser p)
634     {
635         if(p.tok.id != TOK_extern)
636             return p.parseError("extern expected");
637         p.pushState(&shiftLparen);
638         return Accept;
639     }
640 
641     static Action shiftLparen(Parser p)
642     {
643         switch(p.tok.id)
644         {
645             case TOK_lparen:
646                 p.pushNode(new ast.LinkageAttribute(p.tok));
647                 p.pushState(&shiftLinkageType);
648                 return Accept;
649             default:
650                 auto attr = new ast.AttributeSpecifier(p.tok);
651                 attr.attr = Attr_Extern;
652                 p.pushNode(attr);
653                 return Forward;
654         }
655     }
656 
657     static Action shiftLinkageType(Parser p)
658     {
659         Attribute attr = tokenToLinkageType(p.tok);
660         if(attr == 0)
661             return p.parseError("linkage type expected in extern");
662         p.topNode!(ast.LinkageAttribute)().attr = attr;
663 
664         p.pushState(&shiftRParen);
665         return Accept;
666     }
667 
668     static Action shiftRParen(Parser p)
669     {
670         assert(cast(ast.LinkageAttribute)p.topNode());
671         if(p.tok.id == TOK_rparen)
672             return Accept;
673 
674         if(p.topNode().attr != Attr_ExternC || p.tok.id != TOK_plusplus)
675             return p.parseError("closing parenthesis expected after linkage type");
676 
677         p.topNode().attr = Attr_ExternCPP;
678         p.pushState(&shiftRParen);
679         return Accept;
680     }
681 }
682 
683 Attribute tokenToLinkageType(Token tok)
684 {
685     if(tok.id != TOK_Identifier)
686         return 0;
687 
688     switch(tok.txt)
689     {
690         case "C":       return Attr_ExternC;
691         case "D":       return Attr_ExternD;
692         case "Windows": return Attr_ExternWindows;
693         case "System":  return Attr_ExternSystem;
694         default:        return 0;
695     }
696 }
697 
698 //-- GRAMMAR_BEGIN --
699 //AlignAttribute:
700 //    align
701 //    align ( Integer )
702 class AlignAttribute
703 {
704     static Action enter(Parser p)
705     {
706         if(p.tok.id != TOK_align)
707             return p.parseError("align expected");
708 
709         p.pushNode(new ast.AlignAttribute(p.tok));
710         p.pushState(&shiftLparen);
711         return Accept;
712     }
713 
714     static Action shiftLparen(Parser p)
715     {
716         if(p.tok.id != TOK_lparen)
717             return Forward;
718 
719         p.pushState(&shiftInteger);
720         return Accept;
721     }
722 
723     static Action shiftInteger(Parser p)
724     {
725         if(p.tok.id != TOK_IntegerLiteral)
726             return p.parseError("integer expected in align");
727         int algn = parse!int(p.tok.txt);
728         Attribute attr = alignmentToAttribute(algn);
729         if(attr == 0)
730             return p.parseError("alignment not supported: " ~ p.tok.txt);
731 
732         p.topNode!(ast.AlignAttribute)().attr = attr;
733         p.pushState(&shiftRParen);
734         return Accept;
735     }
736 
737     static Action shiftRParen(Parser p)
738     {
739         assert(cast(ast.AlignAttribute)p.topNode());
740         if(p.tok.id != TOK_rparen)
741             return p.parseError("closing parenthesis expected after align");
742         return Accept;
743     }
744 }
745 
746 Attribute alignmentToAttribute(int algn)
747 {
748     switch(algn)
749     {
750         case 1:  return Attr_Align1;
751         case 2:  return Attr_Align2;
752         case 4:  return Attr_Align4;
753         case 8:  return Attr_Align8;
754         case 16: return Attr_Align16;
755         default: return 0;
756     }
757 }
758 
759 //-- GRAMMAR_BEGIN --
760 //ProtectionAttribute:
761 //    private
762 //    package
763 //    protected
764 //    public
765 //    export
766 
767 //PragmaSpecifier:
768 //    Pragma DeclDef
769 //Pragma:
770 //    pragma ( Identifier )
771 //    pragma ( Identifier , TemplateArgumentList )
772 class Pragma
773 {
774     static Action enter(Parser p)
775     {
776         if(p.tok.id != TOK_pragma)
777             return p.parseError("pragma expected");
778 
779         p.pushNode(new ast.Pragma(p.tok));
780         p.pushState(&shiftLparen);
781         return Accept;
782     }
783 
784     static Action shiftLparen(Parser p)
785     {
786         if(p.tok.id != TOK_lparen)
787             return Forward;
788 
789         p.pushState(&shiftIdentifier);
790         return Accept;
791     }
792 
793     static Action shiftIdentifier(Parser p)
794     {
795         if(p.tok.id != TOK_Identifier)
796             return p.parseError("integer expected in align");
797 
798         p.topNode!(ast.Pragma)().ident = p.tok.txt;
799 
800         p.pushState(&shiftCommaOrRParen);
801         return Accept;
802     }
803 
804     static Action shiftCommaOrRParen(Parser p)
805     {
806         if(p.tok.id == TOK_comma)
807         {
808             p.pushState(&shiftArgumentList);
809             p.pushState(&TemplateArgumentList.enter);
810             return Accept;
811         }
812         return shiftRParen(p);
813     }
814 
815     static Action shiftRParen(Parser p)
816     {
817         assert(cast(ast.Pragma)p.topNode());
818         if(p.tok.id != TOK_rparen)
819             return p.parseError("closing parenthesis expected for pragma");
820         return Accept;
821     }
822 
823     static Action shiftArgumentList(Parser p)
824     {
825         p.popAppendTopNode!(ast.Pragma)();
826         return shiftRParen(p);
827     }
828 }
829 
830 
831 //-- GRAMMAR_BEGIN --
832 //ImportDeclaration:
833 //    import ImportList ;
834 class ImportDeclaration
835 {
836     mixin SequenceNode!(ast.ImportDeclaration, TOK_import, ImportList, TOK_semicolon);
837 }
838 
839 //-- GRAMMAR_BEGIN --
840 //ImportList:
841 //    Import
842 //    ImportBindings
843 //    Import , ImportList
844 //
845 //ImportBindings:
846 //    Import : ImportBindList
847 
848 class ImportList
849 {
850     static Action enter(Parser p)
851     {
852         p.pushNode(new ast.ImportList(p.tok));
853         p.pushState(&shiftImport);
854         return Import.enter(p);
855     }
856 
857     static Action shiftImport(Parser p)
858     {
859         if(p.tok.id == TOK_colon)
860         {
861             p.pushState(&shiftImportBindList);
862             p.pushState(&ImportBindList.enter);
863             return Accept;
864         }
865         p.popAppendTopNode!(ast.ImportList)();
866 
867         if(p.tok.id == TOK_comma)
868         {
869             p.pushState(&shiftImport);
870             p.pushState(&Import.enter);
871             return Accept;
872         }
873         return Forward;
874     }
875 
876     static Action shiftImportBindList(Parser p)
877     {
878         p.popAppendTopNode!(ast.Import)();
879         p.popAppendTopNode!(ast.ImportList)();
880         return Forward;
881     }
882 }
883 
884 //-- GRAMMAR_BEGIN --
885 //Import:
886 //    ModuleFullyQualifiedName
887 //    ModuleAliasIdentifier = ModuleFullyQualifiedName
888 //
889 //ModuleAliasIdentifier:
890 //    Identifier
891 class Import
892 {
893     static Action enter(Parser p)
894     {
895         if(p.tok.id != TOK_Identifier)
896             return p.parseError("identifier expected in import");
897 
898         p.pushToken(p.tok);
899         p.pushNode(new ast.Import(p.tok));
900         p.pushState(&shiftIdentifier);
901         return Accept;
902     }
903 
904     static Action shiftIdentifier(Parser p)
905     {
906         p.pushState(&shiftFullyQualifiedName);
907 
908         if(p.tok.id == TOK_assign)
909         {
910             p.topNode!(ast.Import)().aliasIdent = p.popToken().txt;
911             p.pushState(&ModuleFullyQualifiedName.enter);
912             return Accept;
913         }
914 
915         // delegate into ModuleFullyQualifiedName
916         p.pushNode(new ast.ModuleFullyQualifiedName(p.tok));
917         auto id = new ast.Identifier(p.popToken());
918         p.pushNode(id);
919         return ModuleFullyQualifiedName.shift(p);
920     }
921 
922     static Action shiftFullyQualifiedName(Parser p)
923     {
924         p.popAppendTopNode!(ast.Import)();
925         return Forward;
926     }
927 }
928 
929 //-- GRAMMAR_BEGIN --
930 //ImportBindList:
931 //    ImportBind
932 //    ImportBind , ImportBindList
933 class ImportBindList
934 {
935     mixin ListNode!(ast.ImportBindList, ImportBind, TOK_comma);
936 }
937 
938 
939 //-- GRAMMAR_BEGIN --
940 //ImportBind:
941 //    Identifier
942 //    Identifier = Identifier
943 class ImportBind
944 {
945     mixin OptionalNode!(ast.ImportBind, Identifier, TOK_assign, Identifier);
946 }
947 
948 //-- GRAMMAR_BEGIN --
949 //MixinDeclaration:
950 //    mixin ( AssignExpression ) ;
951 class MixinDeclaration
952 {
953     mixin SequenceNode!(ast.MixinDeclaration, TOK_mixin, TOK_lparen, AssignExpression, TOK_rparen, TOK_semicolon);
954 
955     static Action enterAfterMixin(Parser p)
956     {
957         p.pushNode(new ast.MixinDeclaration(p.tok));
958         return shift1.shift(p);
959     }
960 }
961 
962 //-- GRAMMAR_BEGIN --
963 //Unittest:
964 //    unittest BlockStatement
965 class Unittest
966 {
967     mixin SequenceNode!(ast.Unittest, TOK_unittest, BlockStatement);
968 }