From 138670220293587f1707d9d54c7130c3f7658109 Mon Sep 17 00:00:00 2001 From: Franco Montenegro Date: Wed, 25 May 2016 01:27:40 -0300 Subject: [PATCH] Implement continue statement --- .../expression/ContinueStatement.java | 21 +++++ .../translator/expression/LoopStatement.java | 88 ++++++++++++++++++- .../translator/expression/StatementBody.java | 19 +++- .../vrjassc/vrjassc/phase/ReferencePhase.java | 11 ++- .../vrjassc/phase/TranslationPhase.java | 7 +- .../vrjassc/compiler/FunctionTest.java | 48 ++++++++++ .../vrjassc/validator/FunctionTest.java | 9 ++ vrjass.g4 | 4 + 8 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/ruke/vrjassc/translator/expression/ContinueStatement.java diff --git a/src/main/java/com/ruke/vrjassc/translator/expression/ContinueStatement.java b/src/main/java/com/ruke/vrjassc/translator/expression/ContinueStatement.java new file mode 100644 index 0000000..9db2dd1 --- /dev/null +++ b/src/main/java/com/ruke/vrjassc/translator/expression/ContinueStatement.java @@ -0,0 +1,21 @@ +package com.ruke.vrjassc.translator.expression; + +public class ContinueStatement extends Statement { + + @Override + public String translate() { + LoopStatement loop = ((StatementBody) this.getParent()).getLoop(); + + if (loop == null || loop.getContinueSymbol() == null) { + return ""; + } + + Expression continueTrue = new AssignmentStatement( + new VariableExpression(loop.getContinueSymbol(), null), + new RawExpression("true") + ); + + return continueTrue.translate() + "\nexitwhen true\n"; + } + +} diff --git a/src/main/java/com/ruke/vrjassc/translator/expression/LoopStatement.java b/src/main/java/com/ruke/vrjassc/translator/expression/LoopStatement.java index ac27eed..fcf278f 100644 --- a/src/main/java/com/ruke/vrjassc/translator/expression/LoopStatement.java +++ b/src/main/java/com/ruke/vrjassc/translator/expression/LoopStatement.java @@ -1,10 +1,96 @@ package com.ruke.vrjassc.translator.expression; +import com.ruke.vrjassc.vrjassc.symbol.BuiltInTypeSymbol; +import com.ruke.vrjassc.vrjassc.symbol.Symbol; + +/** + * Thanks to Aniki for providing the continue translation! + * http://www.hiveworkshop.com/forums/warcraft-editing-tools-277/vrjass-264114/index9.html#post2788578 + */ public class LoopStatement extends StatementBody { + protected static int LOOP_CONTINUE_COUNTER = 0; + protected Symbol _continue; + + protected void registerContinue() { + if (this._continue != null) { + return; + } + + this._continue = new Symbol("vr_c_" + LOOP_CONTINUE_COUNTER, null, null); + this._continue.setType(new BuiltInTypeSymbol("boolean", null, null)); + + this.add(new VariableStatement(this._continue, null)); + + LOOP_CONTINUE_COUNTER++; + } + + @Override + public void add(Statement e) { + super.add(e); + + if (this._continue == null && this.containsContinue(e)) { + this.registerContinue(); + } + } + + protected boolean containsContinue(Statement e) { + if (e == null) { + return false; + } + + if (e instanceof LoopStatement) { + return false; + } + + if (e instanceof ContinueStatement) { + return true; + } + + if (e instanceof StatementBody) { + for (Statement s : ((StatementBody) e).getStatements()) { + if (this.containsContinue(s)) { + return true; + } + } + } + + return false; + } + + public Symbol getContinueSymbol() { + return this._continue; + } + @Override public String translate() { - return "loop\n"+ super.translate() + "endloop"; + String continueHeader = ""; + String body = super.translate(); + String continueFooter = ""; + + if (this._continue != null) { + continueHeader = new AssignmentStatement( + new VariableExpression(this._continue, null), + new RawExpression("false") + ).translate() + "\n"; + + body = "loop\n" + body + "endloop\n"; + + continueFooter = new ExitWhenStatement( + new BooleanExpression( + new VariableExpression(this._continue, null), + BooleanExpression.Operator.EQUAL_EQUAL, + new RawExpression("false") + ) + ).translate() + "\n"; + } + + return + "loop\n" + + continueHeader + + body + + continueFooter + + "endloop"; } } diff --git a/src/main/java/com/ruke/vrjassc/translator/expression/StatementBody.java b/src/main/java/com/ruke/vrjassc/translator/expression/StatementBody.java index 4549949..a7638f0 100644 --- a/src/main/java/com/ruke/vrjassc/translator/expression/StatementBody.java +++ b/src/main/java/com/ruke/vrjassc/translator/expression/StatementBody.java @@ -43,6 +43,24 @@ protected void addVariableStatement(VariableStatement e) { } } } + + public LoopStatement getLoop() { + Expression loop = this; + + while (loop instanceof LoopStatement == false) { + if (loop == null) { + break; + } + + loop = loop.getParent(); + } + + if (loop instanceof LoopStatement) { + return (LoopStatement) loop; + } + + return null; + } public void add(Statement e) { if (e == null) { @@ -50,7 +68,6 @@ public void add(Statement e) { } e.setParent(this); - this.getUsedFunctions().addAll(e.getUsedFunctions()); if (e instanceof VariableStatement) { diff --git a/src/main/java/com/ruke/vrjassc/vrjassc/phase/ReferencePhase.java b/src/main/java/com/ruke/vrjassc/vrjassc/phase/ReferencePhase.java index a20c4c0..71b4829 100644 --- a/src/main/java/com/ruke/vrjassc/vrjassc/phase/ReferencePhase.java +++ b/src/main/java/com/ruke/vrjassc/vrjassc/phase/ReferencePhase.java @@ -693,7 +693,16 @@ public Symbol visitExitWhenStatement(ExitWhenStatementContext ctx) { return this.visit(ctx.expression()); } - + + @Override + public Symbol visitContinueStatement(ContinueStatementContext ctx) { + if (!this.validator.mustBeInsideOfLoop(ctx, ctx.getStart())) { + throw this.validator.getException(); + } + + return null; + } + @Override public Symbol visitBreakStatement(BreakStatementContext ctx) { if (!this.validator.mustBeInsideOfLoop(ctx, ctx.getStart())) { diff --git a/src/main/java/com/ruke/vrjassc/vrjassc/phase/TranslationPhase.java b/src/main/java/com/ruke/vrjassc/vrjassc/phase/TranslationPhase.java index 290e51e..0ae3a85 100644 --- a/src/main/java/com/ruke/vrjassc/vrjassc/phase/TranslationPhase.java +++ b/src/main/java/com/ruke/vrjassc/vrjassc/phase/TranslationPhase.java @@ -424,7 +424,12 @@ public Expression visitExitWhenStatement(ExitWhenStatementContext ctx) { BooleanExpression condition = new BooleanExpression(this.visit(ctx.expression())); return new ExitWhenStatement(condition); } - + + @Override + public Expression visitContinueStatement(ContinueStatementContext ctx) { + return new ContinueStatement(); + } + @Override public Expression visitBreakStatement(BreakStatementContext ctx) { return new ExitWhenStatement(new RawExpression("true")); diff --git a/src/test/java/com/ruke/vrjassc/vrjassc/compiler/FunctionTest.java b/src/test/java/com/ruke/vrjassc/vrjassc/compiler/FunctionTest.java index 24731c3..c031433 100644 --- a/src/test/java/com/ruke/vrjassc/vrjassc/compiler/FunctionTest.java +++ b/src/test/java/com/ruke/vrjassc/vrjassc/compiler/FunctionTest.java @@ -7,6 +7,54 @@ import static org.junit.Assert.assertEquals; public class FunctionTest extends TestHelper { + + @Test + public void continueStatement() { + String code = + "function foo\n" + + "local integer i\n" + + "set i = 1\n" + + "loop\n" + + "exitwhen i > 9\n" + + "if i == 3 then\n" + + "continue\n" + + "endif\n" + + "if i == 5 then\n" + + "continue\n" + + "endif\n" + + "call BJDebugMsg(I2S(i))\n" + + "set i = i + 1\n" + + "endloop\n" + + "end"; + + String expected = + "globals\n" + + "endglobals\n" + + "function foo takes nothing returns nothing\n" + + "boolean vr_c_0=false\n" + + "local integer i=0\n" + + "set i=1\n" + + "loop\n" + + "set vr_c_0=false\n" + + "loop\n" + + "exitwhen i>9\n" + + "if i==3 then\n" + + "set vr_c_0=true\n" + + "exitwhen true\n" + + "endif\n" + + "if i==5 then\n" + + "set vr_c_0=true\n" + + "exitwhen true\n" + + "endif\n" + + "call BJDebugMsg(I2S(i))\n" + + "set i=i+1\n" + + "endloop\n" + + "exitwhen vr_c_0==false\n" + + "endloop\n" + + "endfunction"; + + assertEquals(expected, this.run(code)); + } @Test public void anonymousFunction() { diff --git a/src/test/java/com/ruke/vrjassc/vrjassc/validator/FunctionTest.java b/src/test/java/com/ruke/vrjassc/vrjassc/validator/FunctionTest.java index 61ccf38..76ef83d 100644 --- a/src/test/java/com/ruke/vrjassc/vrjassc/validator/FunctionTest.java +++ b/src/test/java/com/ruke/vrjassc/vrjassc/validator/FunctionTest.java @@ -68,6 +68,15 @@ public void validExitwhen() { + "endif\n" + "endfunction"); } + + @Test + public void invalidContinue() { + this.expectedEx.expect(InvalidStatementException.class); + this.expectedEx.expectMessage("2:0 Can only be used inside of loops"); + this.run("function foo takes nothing returns nothing\n" + + "continue\n" + + "endfunction"); + } @Test public void invalidBreak() { diff --git a/vrjass.g4 b/vrjass.g4 index 0db709b..925c223 100644 --- a/vrjass.g4 +++ b/vrjass.g4 @@ -191,6 +191,7 @@ statement | whileLoopStatement | breakStatement | exitWhenStatement + | continueStatement | ifStatement | returnStatement | NL @@ -207,6 +208,8 @@ breakStatement: BREAK NL; exitWhenStatement: EXITWHEN expression NL; +continueStatement: CONTINUE NL; + loopStatement: LOOP NL statement* @@ -264,6 +267,7 @@ ARRAY: 'array'; LOCAL: 'local'; SET: 'set'; EXITWHEN: 'exitwhen'; +CONTINUE: 'continue'; CALL: 'call'; GLOBALS: 'globals'; ENDGLOBALS: 'endglobals';