| /* Compiler implementation of the D programming language | |
| * Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * written by Walter Bright | |
| * https://www.digitalmars.com | |
| * Distributed under the Boost Software License, Version 1.0. | |
| * https://www.boost.org/LICENSE_1_0.txt | |
| * https://github.com/dlang/dmd/blob/master/src/dmd/statement.h | |
| */ | |
| #pragma once | |
| #include "arraytypes.h" | |
| #include "ast_node.h" | |
| #include "dsymbol.h" | |
| #include "visitor.h" | |
| #include "tokens.h" | |
| struct Scope; | |
| class Expression; | |
| class LabelDsymbol; | |
| class Identifier; | |
| class IfStatement; | |
| class ExpStatement; | |
| class DefaultStatement; | |
| class VarDeclaration; | |
| class Condition; | |
| class ErrorStatement; | |
| class ReturnStatement; | |
| class CompoundStatement; | |
| class Parameter; | |
| class StaticAssert; | |
| class AsmStatement; | |
| class GotoStatement; | |
| class ScopeStatement; | |
| class TryCatchStatement; | |
| class TryFinallyStatement; | |
| class CaseStatement; | |
| class DefaultStatement; | |
| class LabelStatement; | |
| class StaticForeach; | |
| // Back end | |
| struct code; | |
| /* How a statement exits; this is returned by blockExit() | |
| */ | |
| enum BE : int32_t | |
| { | |
| BEnone = 0, | |
| BEfallthru = 1, | |
| BEthrow = 2, | |
| BEreturn = 4, | |
| BEgoto = 8, | |
| BEhalt = 0x10, | |
| BEbreak = 0x20, | |
| BEcontinue = 0x40, | |
| BEerrthrow = 0x80, | |
| BEany = (BEfallthru | BEthrow | BEreturn | BEgoto | BEhalt) | |
| }; | |
| typedef unsigned char STMT; | |
| enum | |
| { | |
| STMTerror, | |
| STMTpeel, | |
| STMTexp, STMTdtorExp, | |
| STMTcompile, | |
| STMTcompound, STMTcompoundDeclaration, STMTcompoundAsm, | |
| STMTunrolledLoop, | |
| STMTscope, | |
| STMTforwarding, | |
| STMTwhile, | |
| STMTdo, | |
| STMTfor, | |
| STMTforeach, | |
| STMTforeachRange, | |
| STMTif, | |
| STMTconditional, | |
| STMTstaticForeach, | |
| STMTpragma, | |
| STMTstaticAssert, | |
| STMTswitch, | |
| STMTcase, | |
| STMTcaseRange, | |
| STMTdefault, | |
| STMTgotoDefault, | |
| STMTgotoCase, | |
| STMTswitchError, | |
| STMTreturn, | |
| STMTbreak, | |
| STMTcontinue, | |
| STMTsynchronized, | |
| STMTwith, | |
| STMTtryCatch, | |
| STMTtryFinally, | |
| STMTscopeGuard, | |
| STMTthrow, | |
| STMTdebug, | |
| STMTgoto, | |
| STMTlabel, | |
| STMTasm, STMTinlineAsm, STMTgccAsm, | |
| STMTimport | |
| }; | |
| class Statement : public ASTNode | |
| { | |
| public: | |
| Loc loc; | |
| STMT stmt; | |
| virtual Statement *syntaxCopy(); | |
| const char *toChars() const; | |
| void error(const char *format, ...); | |
| void warning(const char *format, ...); | |
| void deprecation(const char *format, ...); | |
| virtual Statement *getRelatedLabeled() { return this; } | |
| virtual bool hasBreak() const; | |
| virtual bool hasContinue() const; | |
| bool usesEH(); | |
| bool comeFrom(); | |
| bool hasCode(); | |
| virtual Statement *last(); | |
| virtual ReturnStatement *endsWithReturnStatement() { return NULL; } | |
| ErrorStatement *isErrorStatement() { return stmt == STMTerror ? (ErrorStatement*)this : NULL; } | |
| ScopeStatement *isScopeStatement() { return stmt == STMTscope ? (ScopeStatement*)this : NULL; } | |
| ExpStatement *isExpStatement() { return stmt == STMTexp ? (ExpStatement*)this : NULL; } | |
| CompoundStatement *isCompoundStatement() { return stmt == STMTcompound ? (CompoundStatement*)this : NULL; } | |
| ReturnStatement *isReturnStatement() { return stmt == STMTreturn ? (ReturnStatement*)this : NULL; } | |
| IfStatement *isIfStatement() { return stmt == STMTif ? (IfStatement*)this : NULL; } | |
| ConditionalStatement *isConditionalStatement() { return stmt == STMTconditional ? (ConditionalStatement*)this : NULL; } | |
| StaticForeachStatement *isStaticForeachStatement() { return stmt == STMTstaticForeach ? (StaticForeachStatement*)this : NULL; } | |
| CaseStatement *isCaseStatement() { return stmt == STMTcase ? (CaseStatement*)this : NULL; } | |
| DefaultStatement *isDefaultStatement() { return stmt == STMTdefault ? (DefaultStatement*)this : NULL; } | |
| LabelStatement *isLabelStatement() { return stmt == STMTlabel ? (LabelStatement*)this : NULL; } | |
| GotoDefaultStatement *isGotoDefaultStatement() { return stmt == STMTgotoDefault ? (GotoDefaultStatement*)this : NULL; } | |
| GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : NULL; } | |
| BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : NULL; } | |
| DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : NULL; } | |
| CompileStatement *isCompileStatement() { return stmt == STMTcompile ? (CompileStatement*)this : NULL; } | |
| ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : NULL; } | |
| DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : NULL; } | |
| ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : NULL; } | |
| ForeachStatement *isForeachStatement() { return stmt == STMTforeach ? (ForeachStatement*)this : NULL; } | |
| SwitchStatement *isSwitchStatement() { return stmt == STMTswitch ? (SwitchStatement*)this : NULL; } | |
| ContinueStatement *isContinueStatement() { return stmt == STMTcontinue ? (ContinueStatement*)this : NULL; } | |
| WithStatement *isWithStatement() { return stmt == STMTwith ? (WithStatement*)this : NULL; } | |
| TryCatchStatement *isTryCatchStatement() { return stmt == STMTtryCatch ? (TryCatchStatement*)this : NULL; } | |
| ThrowStatement *isThrowStatement() { return stmt == STMTthrow ? (ThrowStatement*)this : NULL; } | |
| DebugStatement *isDebugStatement() { return stmt == STMTdebug ? (DebugStatement*)this : NULL; } | |
| TryFinallyStatement *isTryFinallyStatement() { return stmt == STMTtryFinally ? (TryFinallyStatement*)this : NULL; } | |
| ScopeGuardStatement *isScopeGuardStatement() { return stmt == STMTscopeGuard ? (ScopeGuardStatement*)this : NULL; } | |
| SwitchErrorStatement *isSwitchErrorStatement() { return stmt == STMTswitchError ? (SwitchErrorStatement*)this : NULL; } | |
| UnrolledLoopStatement *isUnrolledLoopStatement() { return stmt == STMTunrolledLoop ? (UnrolledLoopStatement*)this : NULL; } | |
| ForeachRangeStatement *isForeachRangeStatement() { return stmt == STMTforeachRange ? (ForeachRangeStatement*)this : NULL; } | |
| CompoundDeclarationStatement *isCompoundDeclarationStatement() { return stmt == STMTcompoundDeclaration ? (CompoundDeclarationStatement*)this : NULL; } | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| /** Any Statement that fails semantic() or has a component that is an ErrorExp or | |
| * a TypeError should return an ErrorStatement from semantic(). | |
| */ | |
| class ErrorStatement : public Statement | |
| { | |
| public: | |
| ErrorStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class PeelStatement : public Statement | |
| { | |
| public: | |
| Statement *s; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ExpStatement : public Statement | |
| { | |
| public: | |
| Expression *exp; | |
| static ExpStatement *create(const Loc &loc, Expression *exp); | |
| ExpStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class DtorExpStatement : public ExpStatement | |
| { | |
| public: | |
| /* Wraps an expression that is the destruction of 'var' | |
| */ | |
| VarDeclaration *var; | |
| DtorExpStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class CompileStatement : public Statement | |
| { | |
| public: | |
| Expressions *exps; | |
| CompileStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class CompoundStatement : public Statement | |
| { | |
| public: | |
| Statements *statements; | |
| static CompoundStatement *create(const Loc &loc, Statement *s1, Statement *s2); | |
| CompoundStatement *syntaxCopy(); | |
| ReturnStatement *endsWithReturnStatement(); | |
| Statement *last(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class CompoundDeclarationStatement : public CompoundStatement | |
| { | |
| public: | |
| CompoundDeclarationStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| /* The purpose of this is so that continue will go to the next | |
| * of the statements, and break will go to the end of the statements. | |
| */ | |
| class UnrolledLoopStatement : public Statement | |
| { | |
| public: | |
| Statements *statements; | |
| UnrolledLoopStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ScopeStatement : public Statement | |
| { | |
| public: | |
| Statement *statement; | |
| Loc endloc; // location of closing curly bracket | |
| ScopeStatement *syntaxCopy(); | |
| ReturnStatement *endsWithReturnStatement(); | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ForwardingStatement : public Statement | |
| { | |
| public: | |
| ForwardingScopeDsymbol *sym; | |
| Statement *statement; | |
| ForwardingStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class WhileStatement : public Statement | |
| { | |
| public: | |
| Parameter *param; | |
| Expression *condition; | |
| Statement *_body; | |
| Loc endloc; // location of closing curly bracket | |
| WhileStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class DoStatement : public Statement | |
| { | |
| public: | |
| Statement *_body; | |
| Expression *condition; | |
| Loc endloc; // location of ';' after while | |
| DoStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ForStatement : public Statement | |
| { | |
| public: | |
| Statement *_init; | |
| Expression *condition; | |
| Expression *increment; | |
| Statement *_body; | |
| Loc endloc; // location of closing curly bracket | |
| // When wrapped in try/finally clauses, this points to the outermost one, | |
| // which may have an associated label. Internal break/continue statements | |
| // treat that label as referring to this loop. | |
| Statement *relatedLabeled; | |
| ForStatement *syntaxCopy(); | |
| Statement *getRelatedLabeled() { return relatedLabeled ? relatedLabeled : this; } | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ForeachStatement : public Statement | |
| { | |
| public: | |
| TOK op; // TOKforeach or TOKforeach_reverse | |
| Parameters *parameters; // array of Parameter*'s | |
| Expression *aggr; | |
| Statement *_body; | |
| Loc endloc; // location of closing curly bracket | |
| VarDeclaration *key; | |
| VarDeclaration *value; | |
| FuncDeclaration *func; // function we're lexically in | |
| Statements *cases; // put breaks, continues, gotos and returns here | |
| ScopeStatements *gotos; // forward referenced goto's go here | |
| ForeachStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ForeachRangeStatement : public Statement | |
| { | |
| public: | |
| TOK op; // TOKforeach or TOKforeach_reverse | |
| Parameter *prm; // loop index variable | |
| Expression *lwr; | |
| Expression *upr; | |
| Statement *_body; | |
| Loc endloc; // location of closing curly bracket | |
| VarDeclaration *key; | |
| ForeachRangeStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class IfStatement : public Statement | |
| { | |
| public: | |
| Parameter *prm; | |
| Expression *condition; | |
| Statement *ifbody; | |
| Statement *elsebody; | |
| VarDeclaration *match; // for MatchExpression results | |
| Loc endloc; // location of closing curly bracket | |
| IfStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ConditionalStatement : public Statement | |
| { | |
| public: | |
| Condition *condition; | |
| Statement *ifbody; | |
| Statement *elsebody; | |
| ConditionalStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class StaticForeachStatement : public Statement | |
| { | |
| public: | |
| StaticForeach *sfe; | |
| StaticForeachStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class PragmaStatement : public Statement | |
| { | |
| public: | |
| Identifier *ident; | |
| Expressions *args; // array of Expression's | |
| Statement *_body; | |
| PragmaStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class StaticAssertStatement : public Statement | |
| { | |
| public: | |
| StaticAssert *sa; | |
| StaticAssertStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class SwitchStatement : public Statement | |
| { | |
| public: | |
| Expression *condition; | |
| Statement *_body; | |
| bool isFinal; | |
| DefaultStatement *sdefault; | |
| Statement *tryBody; // set to TryCatchStatement or TryFinallyStatement if in _body portion | |
| TryFinallyStatement *tf; | |
| GotoCaseStatements gotoCases; // array of unresolved GotoCaseStatement's | |
| CaseStatements *cases; // array of CaseStatement's | |
| int hasNoDefault; // !=0 if no default statement | |
| int hasVars; // !=0 if has variable case values | |
| VarDeclaration *lastVar; | |
| SwitchStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class CaseStatement : public Statement | |
| { | |
| public: | |
| Expression *exp; | |
| Statement *statement; | |
| int index; // which case it is (since we sort this) | |
| VarDeclaration *lastVar; | |
| void* extra; // for use by Statement_toIR() | |
| CaseStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class CaseRangeStatement : public Statement | |
| { | |
| public: | |
| Expression *first; | |
| Expression *last; | |
| Statement *statement; | |
| CaseRangeStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class DefaultStatement : public Statement | |
| { | |
| public: | |
| Statement *statement; | |
| VarDeclaration *lastVar; | |
| DefaultStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class GotoDefaultStatement : public Statement | |
| { | |
| public: | |
| SwitchStatement *sw; | |
| GotoDefaultStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class GotoCaseStatement : public Statement | |
| { | |
| public: | |
| Expression *exp; // NULL, or which case to goto | |
| CaseStatement *cs; // case statement it resolves to | |
| GotoCaseStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class SwitchErrorStatement : public Statement | |
| { | |
| public: | |
| Expression *exp; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ReturnStatement : public Statement | |
| { | |
| public: | |
| Expression *exp; | |
| size_t caseDim; | |
| ReturnStatement *syntaxCopy(); | |
| ReturnStatement *endsWithReturnStatement() { return this; } | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class BreakStatement : public Statement | |
| { | |
| public: | |
| Identifier *ident; | |
| BreakStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ContinueStatement : public Statement | |
| { | |
| public: | |
| Identifier *ident; | |
| ContinueStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class SynchronizedStatement : public Statement | |
| { | |
| public: | |
| Expression *exp; | |
| Statement *_body; | |
| SynchronizedStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class WithStatement : public Statement | |
| { | |
| public: | |
| Expression *exp; | |
| Statement *_body; | |
| VarDeclaration *wthis; | |
| Loc endloc; | |
| WithStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class TryCatchStatement : public Statement | |
| { | |
| public: | |
| Statement *_body; | |
| Catches *catches; | |
| Statement *tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion | |
| TryCatchStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class Catch : public RootObject | |
| { | |
| public: | |
| Loc loc; | |
| Type *type; | |
| Identifier *ident; | |
| Statement *handler; | |
| VarDeclaration *var; | |
| // set if semantic processing errors | |
| bool errors; | |
| // was generated by the compiler, | |
| // wasn't present in source code | |
| bool internalCatch; | |
| Catch *syntaxCopy(); | |
| }; | |
| class TryFinallyStatement : public Statement | |
| { | |
| public: | |
| Statement *_body; | |
| Statement *finalbody; | |
| Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion | |
| bool bodyFallsThru; // true if _body falls through to finally | |
| static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody); | |
| TryFinallyStatement *syntaxCopy(); | |
| bool hasBreak() const; | |
| bool hasContinue() const; | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ScopeGuardStatement : public Statement | |
| { | |
| public: | |
| TOK tok; | |
| Statement *statement; | |
| ScopeGuardStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ThrowStatement : public Statement | |
| { | |
| public: | |
| Expression *exp; | |
| // was generated by the compiler, | |
| // wasn't present in source code | |
| bool internalThrow; | |
| ThrowStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class DebugStatement : public Statement | |
| { | |
| public: | |
| Statement *statement; | |
| DebugStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class GotoStatement : public Statement | |
| { | |
| public: | |
| Identifier *ident; | |
| LabelDsymbol *label; | |
| Statement *tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion | |
| TryFinallyStatement *tf; | |
| ScopeGuardStatement *os; | |
| VarDeclaration *lastVar; | |
| GotoStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class LabelStatement : public Statement | |
| { | |
| public: | |
| Identifier *ident; | |
| Statement *statement; | |
| Statement *tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion | |
| TryFinallyStatement *tf; | |
| ScopeGuardStatement *os; | |
| VarDeclaration *lastVar; | |
| Statement *gotoTarget; // interpret | |
| void* extra; // used by Statement_toIR() | |
| bool breaks; // someone did a 'break ident' | |
| LabelStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class LabelDsymbol : public Dsymbol | |
| { | |
| public: | |
| LabelStatement *statement; | |
| bool deleted; // set if rewritten to return in foreach delegate | |
| bool iasm; // set if used by inline assembler | |
| static LabelDsymbol *create(Identifier *ident); | |
| LabelDsymbol *isLabel(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| Statement* asmSemantic(AsmStatement *s, Scope *sc); | |
| class AsmStatement : public Statement | |
| { | |
| public: | |
| Token *tokens; | |
| AsmStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class InlineAsmStatement : public AsmStatement | |
| { | |
| public: | |
| code *asmcode; | |
| unsigned asmalign; // alignment of this statement | |
| unsigned regs; // mask of registers modified (must match regm_t in back end) | |
| bool refparam; // true if function parameter is referenced | |
| bool naked; // true if function is to be naked | |
| InlineAsmStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| // A GCC asm statement - assembler instructions with D expression operands | |
| class GccAsmStatement : public AsmStatement | |
| { | |
| public: | |
| StorageClass stc; // attributes of the asm {} block | |
| Expression *insn; // string expression that is the template for assembler code | |
| Expressions *args; // input and output operands of the statement | |
| unsigned outputargs; // of the operands in 'args', the number of output operands | |
| Identifiers *names; // list of symbolic names for the operands | |
| Expressions *constraints; // list of string constants specifying constraints on operands | |
| Expressions *clobbers; // list of string constants specifying clobbers and scratch registers | |
| Identifiers *labels; // list of goto labels | |
| GotoStatements *gotos; // of the goto labels, the equivalent statements they represent | |
| GccAsmStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| // a complete asm {} block | |
| class CompoundAsmStatement : public CompoundStatement | |
| { | |
| public: | |
| StorageClass stc; // postfix attributes like nothrow/pure/@trusted | |
| CompoundAsmStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; | |
| class ImportStatement : public Statement | |
| { | |
| public: | |
| Dsymbols *imports; // Array of Import's | |
| ImportStatement *syntaxCopy(); | |
| void accept(Visitor *v) { v->visit(this); } | |
| }; |