/* * This file is a part of Alchemy OS project. * Copyright (C) 2014, Sergey Basalaev <sbasalaev@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package alchemy.nec; import alchemy.nec.syntax.Function; import alchemy.nec.syntax.statement.*; /** * Returns flow status of the statement. * Prints errors if statement defines erroneous flow. * Argument is Boolean value. * @author Sergey Basalaev */ public final class FlowAnalyzer implements StatementVisitor { /* == Return states for visitor methods. == */ /** Indicates that execution continues after this statement. */ public final Object NEXT = new Object(); /** Indicates that function returns normally after this statement. */ public final Object RETURN = new Object(); /** Indicates that control breaks outside innermost loop after this statement. */ public final Object BREAK = new Object(); /** Indicates that error is thrown in this statement. */ public final Object THROW = new Object(); private final CompilerEnv env; private Function function; public FlowAnalyzer(CompilerEnv env) { this.env = env; } public Object visitFunction(Function f) { this.function = f; Object status = f.body.accept(this, Boolean.FALSE); this.function = null; return status; } private Object common(Object flow1, Object flow2) { if (flow1 == NEXT || flow2 == NEXT) { return NEXT; } else if (flow1 == BREAK || flow2 == BREAK) { return BREAK; } else if (flow1 == THROW || flow2 == THROW) { return THROW; } else { return RETURN; } } public Object visitArraySetStatement(ArraySetStatement stat, Object inLoop) { return NEXT; } public Object visitAssignStatement(AssignStatement assign, Object inLoop) { return NEXT; } public Object visitBlockStatement(BlockStatement block, Object inLoop) { Object result = NEXT; for (int i=0; i<block.statements.size(); i++) { Statement stat = (Statement) block.statements.get(i); if (result != NEXT && function != null) { env.warn(function.source, stat.lineNumber(), CompilerEnv.W_ERROR, "Unreachable statement"); return result; } result = stat.accept(this, inLoop); } return result; } public Object visitBreakStatement(BreakStatement brk, Object inLoop) { if (inLoop != Boolean.TRUE && function != null) { env.warn(function.source, brk.lineNumber(), CompilerEnv.W_ERROR, "'break' outside of loop"); } return BREAK; } public Object visitCompoundAssignStatement(CompoundAssignStatement stat, Object inLoop) { return NEXT; } public Object visitContinueStatement(ContinueStatement cnt, Object inLoop) { if (inLoop != Boolean.TRUE && function != null) { env.warn(function.source, cnt.lineNumber(), CompilerEnv.W_ERROR, "'continue' outside of loop"); } return BREAK; } public Object visitEmptyStatement(EmptyStatement stat, Object inLoop) { return NEXT; } public Object visitExprStatement(ExprStatement stat, Object inLoop) { return NEXT; } public Object visitForLoopStatement(ForLoopStatement stat, Object inLoop) { Object incrResult = stat.increment.accept(this, Boolean.TRUE); Object bodyResult = stat.body.accept(this, Boolean.TRUE); if (incrResult == RETURN && bodyResult == RETURN) { return RETURN; } else if (incrResult == THROW && bodyResult == THROW) { return THROW; } else { return NEXT; } } public Object visitIfStatement(IfStatement stat, Object inLoop) { Object ifResult = stat.ifstat.accept(this, inLoop); Object elseResult = stat.elsestat.accept(this, inLoop); return common(ifResult, elseResult); } public Object visitLoopStatement(LoopStatement stat, Object inLoop) { Object preResult = stat.preBody.accept(this, Boolean.TRUE); Object postResult = stat.postBody.accept(this, Boolean.TRUE); if (preResult == RETURN && postResult == RETURN) { return RETURN; } else if (preResult == THROW && postResult == THROW) { return THROW; } else { return NEXT; } } public Object visitReturnStatement(ReturnStatement stat, Object inLoop) { return RETURN; } public Object visitSwitchStatement(SwitchStatement stat, Object inLoop) { Object result = stat.elseStat.accept(this, inLoop); for (int i=0; i<stat.statements.length; i++) { result = common(result, stat.statements[i].accept(this, inLoop)); } return result; } public Object visitThrowStatement(ThrowStatement stat, Object inLoop) { return THROW; } public Object visitTryCatchStatement(TryCatchStatement stat, Object inLoop) { Object tryResult = stat.tryStat.accept(this, inLoop); Object catchResult = stat.catchStat.accept(this, inLoop); if (tryResult == THROW) { return catchResult; } if (tryResult == NEXT || catchResult == NEXT) { return NEXT; } else if (tryResult == BREAK || catchResult == BREAK) { return BREAK; } else { return catchResult; } } }