dvander@195: /* dvander@0: * Copyright (C) 2012 David Anderson dvander@0: * dvander@195: * This file is part of SourcePawn. dvander@0: * dvander@195: * SourcePawn is free software: you can redistribute it and/or modify it under dvander@0: * the terms of the GNU General Public License as published by the Free dvander@0: * Software Foundation, either version 3 of the License, or (at your option) dvander@0: * any later version. dvander@0: * dvander@195: * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY dvander@0: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@0: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. dvander@0: * dvander@0: * You should have received a copy of the GNU General Public License along with dvander@195: * SourcePawn. If not, see http://www.gnu.org/licenses/. dvander@0: */ dvander@195: #ifndef _include_sp2_sema_h_ dvander@195: #define _include_sp2_sema_h_ dvander@0: dvander@0: #include "AST.h" dvander@195: #include "HIR.h" dvander@195: #include "Scopes.h" dvander@195: #include "BytecodeEmitter.h" dvander@0: dvander@59: namespace ke { dvander@0: dvander@195: // Semantic analysis occurs after all names and types have been bound. It dvander@195: // performs the important task of validating the semantics and type safety dvander@195: // of a parse tree. dvander@195: // dvander@195: // The semantic analysis phase also produces bytecode. This occurs on a dvander@195: // statement-by-statement basis. The result of analyzing an expression is a dvander@195: // tree of operations which is then flattened. dvander@0: class SemanticAnalysis : public AstVisitor dvander@0: { dvander@195: public: dvander@195: SemanticAnalysis(Zone *zone, CompileContext &cc, TranslationUnit *unit); dvander@195: dvander@195: void analyze(); dvander@195: dvander@195: private: dvander@195: SemanticAnalysis(SemanticAnalysis *parent, Handle fun); dvander@195: Function *compile(FunctionStatement *stmt); dvander@195: dvander@195: public: dvander@195: void visitFunctionStatement(FunctionStatement *node); dvander@195: void visitBlockStatement(BlockStatement *node); dvander@195: void visitExpressionStatement(ExpressionStatement *node); dvander@195: void visitReturnStatement(ReturnStatement *node); dvander@233: void visitEnumStatement(EnumStatement *node); dvander@0: dvander@195: void visitCallExpression(CallExpression *node); dvander@195: void visitNameProxy(NameProxy *name); dvander@195: void visitIntegerLiteral(IntegerLiteral *node); dvander@195: void visitFloatLiteral(FloatLiteral *node); dvander@195: void visitVariableDeclaration(VariableDeclaration *node); dvander@195: void visitForStatement(ForStatement *node); dvander@203: void visitWhileStatement(WhileStatement *node); dvander@195: void visitAssignment(Assignment *node); dvander@195: void visitBinaryExpression(BinaryExpression *node); dvander@208: void visitBreakStatement(BreakStatement *node); dvander@208: void visitContinueStatement(ContinueStatement *node); dvander@208: void visitIfStatement(IfStatement *node); dvander@212: void visitIndexExpression(IndexExpression *node); dvander@217: void visitIncDecExpression(IncDecExpression *node); dvander@234: void visitStringLiteral(StringLiteral *node); dvander@245: void visitUnaryExpression(UnaryExpression *node); dvander@245: void visitBooleanLiteral(BooleanLiteral *node); dvander@195: #if 0 dvander@195: void visitFunctionTypeStatement(FunctionTypeStatement *node); dvander@195: void visitImportStatement(ImportStatement *node); dvander@195: void visitStructureStatement(StructureStatement *node); dvander@195: void visitSwitchStatement(SwitchStatement *node); dvander@195: dvander@195: void visitTernaryExpression(TernaryExpression *node); dvander@195: void visitFieldExpression(FieldExpression *node); dvander@195: void visitArrayLiteral(ArrayLiteral *node); dvander@195: #endif dvander@195: dvander@195: private: dvander@195: bool checkArgumentCount(Handle type, unsigned actual); dvander@195: dvander@195: private: dvander@195: enum CoercionKind { dvander@195: Coerce_Arg, dvander@195: Coerce_Return, dvander@236: Coerce_Decl, dvander@195: Coerce_Assign dvander@71: }; dvander@71: dvander@232: // For a given control flow path, whether it returns. dvander@232: enum ReturnStatus dvander@232: { dvander@232: Returns_Never, dvander@232: Returns_Sometimes, dvander@232: Returns_Always dvander@232: }; dvander@232: dvander@232: // For a given control flow path, whether it terminates, dvander@232: // and if it terminates, whether it returns. dvander@232: class ControlStatus dvander@232: { dvander@232: bool terminates_; dvander@232: ReturnStatus returns_; dvander@232: dvander@232: public: dvander@232: ControlStatus() dvander@232: { dvander@232: } dvander@232: ControlStatus(bool terminates, ReturnStatus returns) dvander@232: : terminates_(terminates), dvander@232: returns_(returns) dvander@232: { dvander@232: } dvander@232: dvander@232: bool terminates() const { dvander@232: return terminates_; dvander@232: } dvander@232: ReturnStatus returns() const { dvander@232: return returns_; dvander@232: } dvander@232: }; dvander@232: dvander@232: class ControlScope dvander@232: { dvander@232: ReturnStatus returns_; dvander@232: ControlScope **prevp_; dvander@232: ControlScope *prev_; dvander@232: bool terminates_; dvander@232: dvander@232: public: dvander@232: ControlScope(ControlScope **prevp) dvander@232: : returns_(Returns_Never), dvander@232: prevp_(prevp), dvander@232: prev_(*prevp), dvander@232: terminates_(false) dvander@232: { dvander@232: *prevp_ = this; dvander@232: } dvander@232: ~ControlScope() { dvander@232: *prevp_ = prev_; dvander@232: } dvander@232: ControlStatus status() { dvander@232: return ControlStatus(terminates_, returns_); dvander@232: } dvander@232: dvander@232: void returns(ReturnStatus status) { dvander@232: // We allow multiple terminates, but only the first one takes dvander@232: // effect. For example, |break; continue;| is legal, but the dvander@232: // continue is unreachable. dvander@232: if (terminates_) dvander@232: return; dvander@232: if (status > returns_) dvander@232: returns_ = status; dvander@232: } dvander@232: void terminate(ReturnStatus status) { dvander@232: if (terminates_) dvander@232: return; dvander@232: terminates_ = true; dvander@232: returns_ = status; dvander@232: } dvander@232: void merge(const ControlStatus &left, const ControlStatus &right); dvander@232: void inherit(const ControlStatus &other); dvander@232: }; dvander@232: dvander@232: dvander@230: void requireLifoStack(); dvander@232: ControlStatus visitForControl(Statement *statement); dvander@232: ControlStatus visitLoopBody(Statement *stmt); dvander@195: void visitForTest(Expression *expr, dvander@195: BytecodeEmitter::Label *trueBranch, dvander@195: BytecodeEmitter::Label *falseBranch, dvander@195: BytecodeEmitter::Label *fallthrough); dvander@195: void set(VariableSymbol *sym); dvander@195: HIR *eval(Expression *expr); dvander@221: HIR *binary(AstNode *node, TokenKind token, HIR *left, HIR *right); dvander@195: HIR *coerce(HIR *from, Handle to, CoercionKind kind); dvander@195: Type *coercionType(TokenKind token, HIR *left, HIR *right); dvander@195: HLValue *lvalue(Expression *expr); dvander@195: void discharge(HIR *hir); dvander@233: HIR *typeFailure(const SourcePosition &pos, Handle from, Handle to); dvander@71: dvander@234: HIR *typeCheckArray(const SourcePosition &pos, dvander@234: HIR *from, dvander@241: Handle argActual, dvander@220: CoercionKind kind); dvander@220: dvander@195: private: dvander@195: // Used to target break/catch statements. dvander@195: class LoopScope dvander@71: { dvander@195: Scope *scope_; dvander@195: BytecodeEmitter::Label *bk_; dvander@195: BytecodeEmitter::Label *ct_; dvander@195: LoopScope **prevp_; dvander@195: LoopScope *prev_; dvander@230: bool usesLifoStack_; dvander@232: bool breaks_; dvander@71: dvander@71: public: dvander@195: LoopScope(Scope *scope, BytecodeEmitter::Label *bk, BytecodeEmitter::Label *ct, dvander@195: LoopScope **prevp) dvander@195: : scope_(scope), dvander@195: bk_(bk), dvander@195: ct_(ct), dvander@195: prevp_(prevp), dvander@230: prev_(*prevp), dvander@232: usesLifoStack_(false), dvander@232: breaks_(false) dvander@71: { dvander@71: *prevp_ = this; dvander@71: } dvander@71: dvander@71: ~LoopScope() dvander@71: { dvander@71: *prevp_ = prev_; dvander@71: } dvander@71: dvander@195: BytecodeEmitter::Label *break_() { dvander@195: return bk_; dvander@71: } dvander@195: BytecodeEmitter::Label *continue_() { dvander@195: return ct_; dvander@71: } dvander@195: Scope *scope() { dvander@195: return scope_; dvander@71: } dvander@230: void setUsesLifoStack() { dvander@230: usesLifoStack_ = true; dvander@230: } dvander@230: bool usesLifoStack() const { dvander@230: return usesLifoStack_; dvander@230: } dvander@232: void setBreaks() { dvander@232: breaks_ = true; dvander@232: } dvander@232: bool breaks() const { dvander@232: return breaks_; dvander@232: } dvander@71: }; dvander@195: dvander@0: private: dvander@195: Zone *zone_; dvander@195: PoolAllocator &pool_; dvander@195: CompileContext &cc_; dvander@195: TranslationUnit *unit_; dvander@195: HIR *hir_; dvander@195: Scope *scope_; dvander@195: BytecodeEmitter emitter_; dvander@195: ScopedRoot fun_; dvander@195: LoopScope *loop_; dvander@232: ControlScope *control_; dvander@0: }; dvander@0: dvander@59: } // namespace ke dvander@0: dvander@195: #endif // _include_sp2_sema_h_