From a0c6d17f1d36683bfb2c2d1d4579c3af11e42ce1 Mon Sep 17 00:00:00 2001 From: Petr Pytelka Date: Thu, 2 Jan 2020 22:25:33 +0100 Subject: [PATCH 1/3] Rename consumeEndOfLine to consumeEndOfStatement and allow to use colon as a statement separator --- .../scriptbasic/interfaces/LexicalElement.java | 7 +++++++ .../scriptbasic/lexer/BasicLexicalElement.java | 6 ++++++ .../commands/AbstractCommandAnalyzer.java | 11 +++++------ .../AbstractCommandAnalyzerGlobalLocal.java | 2 +- .../commands/AbstractCommandAnalyzerIfKind.java | 2 +- .../syntax/commands/CommandAnalyzerCall.java | 4 ++-- .../syntax/commands/CommandAnalyzerCase.java | 2 +- .../syntax/commands/CommandAnalyzerDSL.java | 2 +- .../syntax/commands/CommandAnalyzerElse.java | 2 +- .../syntax/commands/CommandAnalyzerEnd.java | 2 +- .../syntax/commands/CommandAnalyzerEndIf.java | 2 +- .../syntax/commands/CommandAnalyzerEndSub.java | 2 +- .../syntax/commands/CommandAnalyzerFor.java | 2 +- .../syntax/commands/CommandAnalyzerLet.java | 2 +- .../syntax/commands/CommandAnalyzerMethod.java | 2 +- .../syntax/commands/CommandAnalyzerNext.java | 2 +- .../syntax/commands/CommandAnalyzerPrint.java | 2 +- .../syntax/commands/CommandAnalyzerReturn.java | 2 +- .../syntax/commands/CommandAnalyzerSelect.java | 2 +- .../syntax/commands/CommandAnalyzerSub.java | 2 +- .../syntax/commands/CommandAnalyzerUse.java | 2 +- .../syntax/commands/CommandAnalyzerWend.java | 2 +- .../syntax/commands/CommandAnalyzerWhile.java | 2 +- .../lexer/TestBasicLexicalAnalyzer.java | 17 +++++++++-------- .../scriptbasic/syntax/NullLexicalElement.java | 5 +++++ .../scriptbasic/testprograms/TestPrograms.java | 1 + .../scriptbasic/testprograms/TestSingleLine.bas | 8 ++++++++ 27 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 src/test/resources/com/scriptbasic/testprograms/TestSingleLine.bas diff --git a/src/main/java/com/scriptbasic/interfaces/LexicalElement.java b/src/main/java/com/scriptbasic/interfaces/LexicalElement.java index 22523109..9d068119 100644 --- a/src/main/java/com/scriptbasic/interfaces/LexicalElement.java +++ b/src/main/java/com/scriptbasic/interfaces/LexicalElement.java @@ -79,5 +79,12 @@ public interface LexicalElement extends SourceLocationBound { */ Boolean isSymbol(String lexeme); + /** + * Return true if the lexical element is colon + * + * @return true if the lexical element is colon + */ + Boolean isStatementSeparator(); + Boolean isLineTerminator(); } diff --git a/src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java b/src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java index da111926..b649b529 100644 --- a/src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java +++ b/src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java @@ -179,4 +179,10 @@ public Boolean isLineTerminator() { && CharUtils.isNewLine(getLexeme().codePointAt(0)); } + @Override + public Boolean isStatementSeparator() { + return getType() == TYPE_SYMBOL && getLexeme().length() == 1 + && getLexeme().codePointAt(0)==':'; + } + } diff --git a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzer.java b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzer.java index 768d352c..d674a720 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzer.java +++ b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzer.java @@ -93,17 +93,16 @@ protected boolean isKeyWord(final String keyword) throws AnalysisException { } /** - * Checks that there are no extra characters on a program line when the line - * analyzer thinks that it has finished analyzing the line. If there are + * Checks that there are no extra characters when the line analyzer + * expects it has finished analyzing the statement. If there are * some extra characters on the line then throws syntax error exception. - * Otherwise it simply steps the lexical analyzer iterator over the EOL - * symbol. + * Otherwise it simply steps the lexical analyzer iterator over the symbol. * * @throws AnalysisException when there are extra character on the actual line */ - protected void consumeEndOfLine() throws AnalysisException { + protected void consumeEndOfStatement() throws AnalysisException { final var le = ctx.lexicalAnalyzer.get(); - if (le != null && !le.isLineTerminator()) { + if (le != null && !(le.isLineTerminator() || le.isStatementSeparator())) { SyntaxExceptionUtility.throwSyntaxException( "There are extra characters following the expression after the '" + getName() + "' keyword", le); diff --git a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerGlobalLocal.java b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerGlobalLocal.java index 0ca96688..d22cb798 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerGlobalLocal.java +++ b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerGlobalLocal.java @@ -27,7 +27,7 @@ public Command analyze() throws AnalysisException { final var node = newNode(); final var list = analyzeSimpleLeftValueList(); node.setLeftValueList(list); - consumeEndOfLine(); + consumeEndOfStatement(); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfKind.java b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfKind.java index 244d1e35..b93fb6fe 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfKind.java +++ b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfKind.java @@ -31,7 +31,7 @@ public Command analyze() throws AnalysisException { protected Expression analizeLine() throws AnalysisException { final var condition = analyzeExpression(); assertKeyWord(ScriptBasicKeyWords.KEYWORD_THEN); - consumeEndOfLine(); + consumeEndOfStatement(); return condition; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerCall.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerCall.java index ea7910a9..3bbb4902 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerCall.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerCall.java @@ -26,7 +26,7 @@ public Command analyze() throws AnalysisException { ctx.lexicalAnalyzer.resetLine(); final var commandLet = new CommandLet(); commandLet.setExpression(ctx.expressionAnalyzer.analyze()); - consumeEndOfLine(); + consumeEndOfStatement(); return commandLet; } else { final var functionName = lv.getIdentifier(); @@ -47,7 +47,7 @@ public Command analyze() throws AnalysisException { if (needClosingParenthesis) { consumeClosingParenthesis(ctx.lexicalAnalyzer); } - consumeEndOfLine(); + consumeEndOfStatement(); return new CommandCall(functionCall); } } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerCase.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerCase.java index 5de00473..9a39bcb3 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerCase.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerCase.java @@ -27,7 +27,7 @@ public Command analyze() throws AnalysisException { } else { analyzeCaseConditions(node); } - consumeEndOfLine(); + consumeEndOfStatement(); var lastSelectPart = ctx.nestedStructureHouseKeeper.pop(AbstractCommandSelectPart.class); CommandSelect commandSelect = null; diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerDSL.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerDSL.java index e2958eec..8497ae11 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerDSL.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerDSL.java @@ -86,7 +86,7 @@ private void defineDSLRule() throws AnalysisException { if (!functionNameLexicalElement.isIdentifier()) { throw new BasicSyntaxException("there should be a function name after the keyword 'call' defining a sentenceó"); } - consumeEndOfLine(); + consumeEndOfStatement(); final String[] syntaxElements = sentence.split("\\s+"); if (syntaxElements.length == 0) { throw new BasicSyntaxException("sentence can not be empty"); diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElse.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElse.java index dd34e390..85162ed6 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElse.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElse.java @@ -14,7 +14,7 @@ public CommandAnalyzerElse(final Context ctx) { @Override public Command analyze() throws AnalysisException { final var node = new CommandElse(); - consumeEndOfLine(); + consumeEndOfStatement(); registerAndSwapNode(node); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEnd.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEnd.java index 9a315a5a..632149b1 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEnd.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEnd.java @@ -25,7 +25,7 @@ public Command analyze() throws AnalysisException { throw new BasicSyntaxException( "Select has to be terminated with End Select statement"); } - consumeEndOfLine(); + consumeEndOfStatement(); var lastSelectPart = ctx.nestedStructureHouseKeeper.pop(AbstractCommandSelectPart.class); diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEndIf.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEndIf.java index b8c00dfb..bf85d413 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEndIf.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEndIf.java @@ -14,7 +14,7 @@ public CommandAnalyzerEndIf(final Context ctx) { @Override public Command analyze() throws AnalysisException { final var node = new CommandEndIf(); - consumeEndOfLine(); + consumeEndOfStatement(); registerAndPopNode(node); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEndSub.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEndSub.java index 0b33805e..386a4f99 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEndSub.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerEndSub.java @@ -15,7 +15,7 @@ public CommandAnalyzerEndSub(final Context ctx) { @Override public Command analyze() throws AnalysisException { final var node = new CommandEndSub(); - consumeEndOfLine(); + consumeEndOfStatement(); final var commandSub = ctx.nestedStructureHouseKeeper.pop(CommandSub.class); commandSub.setCommandEndSub(node); return node; diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerFor.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerFor.java index bf265bfe..deb7eff8 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerFor.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerFor.java @@ -31,7 +31,7 @@ public Command analyze() throws AnalysisException { node.setLoopStepValue(null); } pushNode(node); - consumeEndOfLine(); + consumeEndOfStatement(); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerLet.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerLet.java index ca88aee5..7fd1759f 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerLet.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerLet.java @@ -27,7 +27,7 @@ public Command analyze() throws AnalysisException { lexicalElement, null); } commandLet.setExpression(ctx.expressionAnalyzer.analyze()); - consumeEndOfLine(); + consumeEndOfStatement(); return commandLet; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerMethod.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerMethod.java index b58eaed2..afc1e4d1 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerMethod.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerMethod.java @@ -62,7 +62,7 @@ public Command analyze() throws AnalysisException { node.setKlass(KlassUtility.forNameEx(className)); node.setMethodName(methodName); node.setAlias(alias); - consumeEndOfLine(); + consumeEndOfStatement(); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerNext.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerNext.java index 7762c3bc..9e226603 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerNext.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerNext.java @@ -35,7 +35,7 @@ public Command analyze() throws AnalysisException { lexicalElement); } } - consumeEndOfLine(); + consumeEndOfStatement(); return node; } } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerPrint.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerPrint.java index 19ba6fad..2fa8f96b 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerPrint.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerPrint.java @@ -24,7 +24,7 @@ public Command analyze() throws AnalysisException { final var node = new CommandPrint(); final var expressionList = analyzeExpressionList(); node.setExpressionList(expressionList); - consumeEndOfLine(); + consumeEndOfStatement(); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerReturn.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerReturn.java index b3ec4ed0..ced1c6f2 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerReturn.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerReturn.java @@ -29,7 +29,7 @@ public Command analyze() throws AnalysisException { } else { node.setReturnExpression(null); } - consumeEndOfLine(); + consumeEndOfStatement(); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSelect.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSelect.java index 9fc02c2a..c4b65d9b 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSelect.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSelect.java @@ -23,7 +23,7 @@ public Command analyze() throws AnalysisException { ctx.lexicalAnalyzer.get(); // read expression till end of line final var expression = analyzeExpression(); - consumeEndOfLine(); + consumeEndOfStatement(); final var node = new CommandSelect(); node.setExpression(expression); diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSub.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSub.java index 864193fc..07dfbd70 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSub.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSub.java @@ -37,7 +37,7 @@ public Command analyze() throws AnalysisException { node.setArguments(null); } pushNode(node); - consumeEndOfLine(); + consumeEndOfStatement(); return node; } } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerUse.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerUse.java index a745b2cc..8da786db 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerUse.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerUse.java @@ -39,7 +39,7 @@ public Command analyze() throws AnalysisException { } else { aliasName = className; } - consumeEndOfLine(); + consumeEndOfStatement(); if (className.indexOf('.') != -1 || aliasName.indexOf('.') != -1) { throw new BasicSyntaxException( "class name and alias name should not contain dot in command USE"); diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerWend.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerWend.java index 95821f95..5970296f 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerWend.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerWend.java @@ -15,7 +15,7 @@ public CommandAnalyzerWend(final Context ctx) { @Override public Command analyze() throws AnalysisException { final var node = new CommandWend(); - consumeEndOfLine(); + consumeEndOfStatement(); final var commandWhile = ctx.nestedStructureHouseKeeper.pop(CommandWhile.class); node.setCommandWhile(commandWhile); commandWhile.setWendNode(node); diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerWhile.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerWhile.java index 5afc5647..c4f8f7c5 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerWhile.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerWhile.java @@ -15,7 +15,7 @@ public CommandAnalyzerWhile(final Context ctx) { public Command analyze() throws AnalysisException { final var node = new CommandWhile(); final var condition = analyzeExpression(); - consumeEndOfLine(); + consumeEndOfStatement(); node.setCondition(condition); pushNode(node); return node; diff --git a/src/test/java/com/scriptbasic/lexer/TestBasicLexicalAnalyzer.java b/src/test/java/com/scriptbasic/lexer/TestBasicLexicalAnalyzer.java index f7ff7dc7..fc79fdb6 100644 --- a/src/test/java/com/scriptbasic/lexer/TestBasicLexicalAnalyzer.java +++ b/src/test/java/com/scriptbasic/lexer/TestBasicLexicalAnalyzer.java @@ -97,22 +97,23 @@ public void differentStringsAreAnalyzedNicely() throws AnalysisException { @Test public void newLineIsAnalyzedAs_surprise_surprise_newLine() throws AnalysisException { - assertLexicals(from("\n"), SYMBOL("\n") - ); + assertLexicals(from("\n"), SYMBOL("\n")); + } + + @Test + public void colonIsAnalyzedAs_statementSeparator() throws AnalysisException { + assertLexicals(from(":"), SYMBOL(":")); } @Test public void integerNumbersAreAnalyzedNicely() throws AnalysisException { - assertLexicals(from("12"), LONG("12") - ); + assertLexicals(from("12"), LONG("12")); } @Test public void floatingNumbersAreAnalyzedNicely() throws AnalysisException { - assertLexicals(from("13e3"), DOUBLE("13e3") - ); - assertLexicals(from("13.8"), DOUBLE("13.8") - ); + assertLexicals(from("13e3"), DOUBLE("13e3")); + assertLexicals(from("13.8"), DOUBLE("13.8")); assertLexicals(from("13.8e2"), DOUBLE("13.8e2")); assertLexicals(from("13.8e+2"), DOUBLE("13.8e+2")); assertLexicals(from("13.8e-2"), DOUBLE("13.8e-2")); diff --git a/src/test/java/com/scriptbasic/syntax/NullLexicalElement.java b/src/test/java/com/scriptbasic/syntax/NullLexicalElement.java index 69fe2f54..8543d248 100644 --- a/src/test/java/com/scriptbasic/syntax/NullLexicalElement.java +++ b/src/test/java/com/scriptbasic/syntax/NullLexicalElement.java @@ -97,4 +97,9 @@ public int getLineNumber() { public int getPosition() { return 0; } + + @Override + public Boolean isStatementSeparator() { + return null; + } } diff --git a/src/test/java/com/scriptbasic/testprograms/TestPrograms.java b/src/test/java/com/scriptbasic/testprograms/TestPrograms.java index fddd53b5..96b93c02 100644 --- a/src/test/java/com/scriptbasic/testprograms/TestPrograms.java +++ b/src/test/java/com/scriptbasic/testprograms/TestPrograms.java @@ -68,6 +68,7 @@ public void testPrograms() throws Exception { testSyntaxFail("TestSelectBadSyntax2.bas"); testSyntaxFail("TestSelectBadSyntax3.bas"); testSyntaxFail("TestSelectBadSyntax4.bas"); + codeTest("TestSingleLine.bas", "12345678910"); codeTest("TestBooleanConversions.bas", "111111"); codeTest("TestArrays.bas", "OK"); try { diff --git a/src/test/resources/com/scriptbasic/testprograms/TestSingleLine.bas b/src/test/resources/com/scriptbasic/testprograms/TestSingleLine.bas new file mode 100644 index 00000000..de8fc1c2 --- /dev/null +++ b/src/test/resources/com/scriptbasic/testprograms/TestSingleLine.bas @@ -0,0 +1,8 @@ +' +' This program is used to test multiple statements on one line +' +print "1": print "2" +print "3":print "4" +print "5" :print "6" +print "7" : print "8" +let v=9 : print v : print v+1 From f7463f8b0da137b61de4e220615aa199712647c1 Mon Sep 17 00:00:00 2001 From: Petr Pytelka Date: Mon, 6 Jan 2020 17:08:02 +0100 Subject: [PATCH 2/3] Fixed formatting --- src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java b/src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java index b649b529..d5ef1a80 100644 --- a/src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java +++ b/src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java @@ -182,7 +182,7 @@ public Boolean isLineTerminator() { @Override public Boolean isStatementSeparator() { return getType() == TYPE_SYMBOL && getLexeme().length() == 1 - && getLexeme().codePointAt(0)==':'; + && getLexeme().codePointAt(0) == ':'; } } From 103d740fcdfd4dfd7b6cd10081b84b4a7ae4f2c0 Mon Sep 17 00:00:00 2001 From: Petr Pytelka Date: Mon, 6 Jan 2020 16:42:45 +0100 Subject: [PATCH 3/3] Add support for single line statement (if .... then ... [else ...]) see https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/if-then-else-statement --- .../interfaces/LexicalElement.java | 2 - .../NestedStructureHouseKeeper.java | 32 ++++++++ .../interfaces/SyntaxAnalyzer.java | 8 ++ .../AbstractNestedStructureHouseKeeper.java | 46 +++++++++++ .../syntax/BasicSyntaxAnalyzer.java | 19 ++++- .../commands/AbstractCommandAnalyzer.java | 8 +- .../AbstractCommandAnalyzerIfElseKind.java | 6 +- .../AbstractCommandAnalyzerIfKind.java | 19 ++--- .../syntax/commands/CommandAnalyzerElse.java | 8 +- .../commands/CommandAnalyzerElseIf.java | 8 +- .../syntax/commands/CommandAnalyzerIf.java | 80 ++++++++++++++++++- .../com/scriptbasic/main/TestCommandLine.java | 2 +- .../testprograms/TestPrograms.java | 9 ++- .../testprograms/{TestIf.bas => TestIf1.bas} | 0 .../com/scriptbasic/testprograms/TestIf2.bas | 10 +++ .../com/scriptbasic/testprograms/TestIf3.bas | 7 ++ .../com/scriptbasic/testprograms/TestIf4.bas | 9 +++ .../testprograms/TestIncorrectIf1.bas | 5 ++ .../testprograms/TestIncorrectIf2.bas | 6 ++ .../testprograms/TestIncorrectIf3.bas | 10 +++ .../testprograms/TestIncorrectIf4.bas | 5 ++ 21 files changed, 266 insertions(+), 33 deletions(-) rename src/test/resources/com/scriptbasic/testprograms/{TestIf.bas => TestIf1.bas} (100%) create mode 100644 src/test/resources/com/scriptbasic/testprograms/TestIf2.bas create mode 100644 src/test/resources/com/scriptbasic/testprograms/TestIf3.bas create mode 100644 src/test/resources/com/scriptbasic/testprograms/TestIf4.bas create mode 100644 src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf1.bas create mode 100644 src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf2.bas create mode 100644 src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf3.bas create mode 100644 src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf4.bas diff --git a/src/main/java/com/scriptbasic/interfaces/LexicalElement.java b/src/main/java/com/scriptbasic/interfaces/LexicalElement.java index 9d068119..66e911e6 100644 --- a/src/main/java/com/scriptbasic/interfaces/LexicalElement.java +++ b/src/main/java/com/scriptbasic/interfaces/LexicalElement.java @@ -80,8 +80,6 @@ public interface LexicalElement extends SourceLocationBound { Boolean isSymbol(String lexeme); /** - * Return true if the lexical element is colon - * * @return true if the lexical element is colon */ Boolean isStatementSeparator(); diff --git a/src/main/java/com/scriptbasic/interfaces/NestedStructureHouseKeeper.java b/src/main/java/com/scriptbasic/interfaces/NestedStructureHouseKeeper.java index 537db80d..54072f02 100644 --- a/src/main/java/com/scriptbasic/interfaces/NestedStructureHouseKeeper.java +++ b/src/main/java/com/scriptbasic/interfaces/NestedStructureHouseKeeper.java @@ -10,6 +10,24 @@ * date June 8, 2012 */ public interface NestedStructureHouseKeeper { + + static public enum EndOfStatementResult { + /** + * End of statement was consumed. Other processors should not be called. + */ + CONSUMED, + + /** + * End of statement was processed. Other processors should be notified. + */ + PROCESSED + } + + static public interface EndOfStatementProcessor { + + EndOfStatementResult consumeEndOfStatement() throws AnalysisException; + + } /** * Push a nested structure object on the housekeeping stack. @@ -61,4 +79,18 @@ T pop(Class expectedClass) * @throws AnalysisException when there are some elements on the stack */ void checkFinalState() throws AnalysisException; + + /** + * Checks that there are no extra characters when the line analyzer + * expects it has finished analyzing the statement. If there are + * some extra characters on the line then throws syntax error exception. + * Otherwise it simply steps the lexical analyzer iterator over the symbol. + * + * @throws AnalysisException when there are extra character on the actual line + */ + void consumeEndOfStatement() throws AnalysisException; + + void pushEndOfStatementProcessor(EndOfStatementProcessor endOfStatementProcessor); + + EndOfStatementProcessor popEndOfStatementProcessor(); } diff --git a/src/main/java/com/scriptbasic/interfaces/SyntaxAnalyzer.java b/src/main/java/com/scriptbasic/interfaces/SyntaxAnalyzer.java index 9359bc07..a1c25f57 100644 --- a/src/main/java/com/scriptbasic/interfaces/SyntaxAnalyzer.java +++ b/src/main/java/com/scriptbasic/interfaces/SyntaxAnalyzer.java @@ -1,5 +1,6 @@ package com.scriptbasic.interfaces; +import com.scriptbasic.spi.Command; /** * A syntax analyzer analyzes a program source using the result of the lexical @@ -17,4 +18,11 @@ public interface SyntaxAnalyzer { */ BuildableProgram analyze() throws AnalysisException; + /** + * Add command to the currently build program + * + * @param command Command to be added to the current program + */ + void addCommand(Command command); + } diff --git a/src/main/java/com/scriptbasic/syntax/AbstractNestedStructureHouseKeeper.java b/src/main/java/com/scriptbasic/syntax/AbstractNestedStructureHouseKeeper.java index 0357b75d..d13875df 100644 --- a/src/main/java/com/scriptbasic/syntax/AbstractNestedStructureHouseKeeper.java +++ b/src/main/java/com/scriptbasic/syntax/AbstractNestedStructureHouseKeeper.java @@ -4,7 +4,10 @@ import com.scriptbasic.interfaces.*; import com.scriptbasic.log.Logger; import com.scriptbasic.log.LoggerFactory; +import com.scriptbasic.utility.SyntaxExceptionUtility; +import java.util.ArrayList; +import java.util.List; import java.util.Stack; public abstract class AbstractNestedStructureHouseKeeper implements NestedStructureHouseKeeper { @@ -17,6 +20,7 @@ public boolean match(final Class expectedClass) { private final Stack stack = new Stack<>(); private final LexicalAnalyzer analyzer; private boolean stackIsHealthy = true; + private final Stack endOfStatementProcessors = new Stack<>(); protected AbstractNestedStructureHouseKeeper(final LexicalAnalyzer analyzer) { this.analyzer = analyzer; @@ -89,5 +93,47 @@ public void checkFinalState() throws AnalysisException { if (stack.size() > 0) { throw new BasicSyntaxException("There is at least one opened block on the stack. Block is not properly closed."); } + if (endOfStatementProcessors.size() > 0) { + throw new BasicSyntaxException("There is at least one unfinished statement."); + } + } + + @Override + public void consumeEndOfStatement() throws AnalysisException { + + final var numOfProcessors = endOfStatementProcessors.size(); + if(numOfProcessors>0) { + // Copy processors and revert order + // Note: Processors might be unregistered while iterating them + List processors = new ArrayList<>(numOfProcessors); + final var iter = endOfStatementProcessors.listIterator(endOfStatementProcessors.size()); + while(iter.hasPrevious()) { + processors.add(iter.previous()); + } + // Run processors + for(final var processor: processors) { + final var result = processor.consumeEndOfStatement(); + if(result==EndOfStatementResult.CONSUMED) { + return; + } + } + } + + final var le = analyzer.get(); + if (le != null && !(le.isLineTerminator() || le.isStatementSeparator())) { + SyntaxExceptionUtility.throwSyntaxException( + "There are extra characters following the expression.", le); + } + } + + @Override + public void pushEndOfStatementProcessor(EndOfStatementProcessor endOfStatementProcessor) + { + endOfStatementProcessors.push(endOfStatementProcessor); + } + + @Override + public EndOfStatementProcessor popEndOfStatementProcessor() { + return endOfStatementProcessors.pop(); } } diff --git a/src/main/java/com/scriptbasic/syntax/BasicSyntaxAnalyzer.java b/src/main/java/com/scriptbasic/syntax/BasicSyntaxAnalyzer.java index 7cdcd7fb..7c686274 100644 --- a/src/main/java/com/scriptbasic/syntax/BasicSyntaxAnalyzer.java +++ b/src/main/java/com/scriptbasic/syntax/BasicSyntaxAnalyzer.java @@ -1,12 +1,17 @@ package com.scriptbasic.syntax; +import java.util.ArrayList; +import java.util.List; + import com.scriptbasic.interfaces.*; +import com.scriptbasic.spi.Command; public final class BasicSyntaxAnalyzer implements SyntaxAnalyzer { private final LexicalAnalyzer lexicalAnalyzer; private final CommandFactory commandFactory; private final NestedStructureHouseKeeper nestedStructureHouseKeeper; - private LexicalElement lexicalElement; + private LexicalElement lexicalElement; + private List additionalCommands; public BasicSyntaxAnalyzer(final LexicalAnalyzer lexicalAnalyzer, final CommandFactory commandFactory, final NestedStructureHouseKeeper nestedStructureHouseKeeper) { @@ -50,12 +55,24 @@ public BuildableProgram analyze() throws AnalysisException { buildableProgram.addCommand(newCommand); } } + if(additionalCommands!=null) { + additionalCommands.forEach(buildableProgram::addCommand); + additionalCommands = null; + } this.lexicalElement = lexicalAnalyzer.peek(); } nestedStructureHouseKeeper.checkFinalState(); buildableProgram.postprocess(); return buildableProgram; } + + @Override + public void addCommand(final Command command) { + if(additionalCommands==null) { + additionalCommands = new ArrayList<>(); + } + additionalCommands.add(command); + } public static void consumeIgnoredLine(final LexicalAnalyzer lexicalAnalyzer, String lexString) throws AnalysisException { while (!lexString.equals("\n")) { diff --git a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzer.java b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzer.java index d674a720..36537922 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzer.java +++ b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzer.java @@ -7,7 +7,6 @@ import com.scriptbasic.spi.LeftValue; import com.scriptbasic.spi.LeftValueList; import com.scriptbasic.syntax.AbstractAnalyzer; -import com.scriptbasic.utility.SyntaxExceptionUtility; public abstract class AbstractCommandAnalyzer extends AbstractAnalyzer implements CommandAnalyzer { @@ -101,11 +100,6 @@ protected boolean isKeyWord(final String keyword) throws AnalysisException { * @throws AnalysisException when there are extra character on the actual line */ protected void consumeEndOfStatement() throws AnalysisException { - final var le = ctx.lexicalAnalyzer.get(); - if (le != null && !(le.isLineTerminator() || le.isStatementSeparator())) { - SyntaxExceptionUtility.throwSyntaxException( - "There are extra characters following the expression after the '" - + getName() + "' keyword", le); - } + ctx.nestedStructureHouseKeeper.consumeEndOfStatement(); } } diff --git a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfElseKind.java b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfElseKind.java index 4e63ace6..c64049ad 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfElseKind.java +++ b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfElseKind.java @@ -21,9 +21,11 @@ protected void registerAndSwapNode(final AbstractCommandIfElseKind node) pushNode(node); } - protected void registerAndPopNode(final AbstractCommandIfElseKind node) + protected AbstractCommandIfElseKind registerAndPopNode(final AbstractCommandIfElseKind node) throws AnalysisException { - ctx.nestedStructureHouseKeeper.pop(AbstractCommandIfElseKind.class).setNext(node); + var command = ctx.nestedStructureHouseKeeper.pop(AbstractCommandIfElseKind.class); + command.setNext(node); + return command; } } diff --git a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfKind.java b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfKind.java index b93fb6fe..65b62a1c 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfKind.java +++ b/src/main/java/com/scriptbasic/syntax/commands/AbstractCommandAnalyzerIfKind.java @@ -4,7 +4,6 @@ import com.scriptbasic.interfaces.AnalysisException; import com.scriptbasic.interfaces.Expression; import com.scriptbasic.interfaces.ScriptBasicKeyWords; -import com.scriptbasic.spi.Command; /** * @author Peter Verhas @@ -16,22 +15,14 @@ public AbstractCommandAnalyzerIfKind(final Context ctx) { super(ctx); } - protected abstract Command createNode(Expression condition) throws AnalysisException; - - /* - * (non-Javadoc) - * - * @see com.scriptbasic.interfaces.Analyzer#analyze() + /** + * Analyse expression and THEN keyword + * @return expression for IF statement + * @throws AnalysisException error when missing then keyword or failed to parse expression */ - @Override - public Command analyze() throws AnalysisException { - return createNode(analizeLine()); - } - - protected Expression analizeLine() throws AnalysisException { + protected Expression analyzeCondition() throws AnalysisException { final var condition = analyzeExpression(); assertKeyWord(ScriptBasicKeyWords.KEYWORD_THEN); - consumeEndOfStatement(); return condition; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElse.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElse.java index 85162ed6..576a4a4e 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElse.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElse.java @@ -1,8 +1,10 @@ package com.scriptbasic.syntax.commands; import com.scriptbasic.context.Context; +import com.scriptbasic.executors.commands.AbstractCommandIfElseKind; import com.scriptbasic.executors.commands.CommandElse; import com.scriptbasic.interfaces.AnalysisException; +import com.scriptbasic.interfaces.BasicSyntaxException; import com.scriptbasic.spi.Command; public class CommandAnalyzerElse extends AbstractCommandAnalyzerIfElseKind { @@ -14,8 +16,12 @@ public CommandAnalyzerElse(final Context ctx) { @Override public Command analyze() throws AnalysisException { final var node = new CommandElse(); + AbstractCommandIfElseKind prevNode = registerAndPopNode(node); + if(prevNode instanceof CommandElse) { + throw new BasicSyntaxException("Multiple 'else' statements", ctx.lexicalAnalyzer.peek()); + } + pushNode(node); consumeEndOfStatement(); - registerAndSwapNode(node); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElseIf.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElseIf.java index 2ebb09f9..9fb97013 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElseIf.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerElseIf.java @@ -3,7 +3,6 @@ import com.scriptbasic.context.Context; import com.scriptbasic.executors.commands.CommandElseIf; import com.scriptbasic.interfaces.AnalysisException; -import com.scriptbasic.interfaces.Expression; import com.scriptbasic.spi.Command; /** @@ -16,10 +15,15 @@ public CommandAnalyzerElseIf(final Context ctx) { super(ctx); } - protected Command createNode(final Expression condition) throws AnalysisException { + @Override + public Command analyze() throws AnalysisException { + + final var condition = analyzeCondition(); + final var node = new CommandElseIf(); node.setCondition(condition); registerAndSwapNode(node); + consumeEndOfStatement(); return node; } diff --git a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerIf.java b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerIf.java index 9ad330fe..ac87015c 100644 --- a/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerIf.java +++ b/src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerIf.java @@ -1,24 +1,100 @@ package com.scriptbasic.syntax.commands; import com.scriptbasic.context.Context; +import com.scriptbasic.executors.commands.AbstractCommandIfElseKind; +import com.scriptbasic.executors.commands.CommandEndIf; import com.scriptbasic.executors.commands.CommandIf; -import com.scriptbasic.interfaces.Expression; +import com.scriptbasic.interfaces.AnalysisException; +import com.scriptbasic.interfaces.BasicSyntaxException; +import com.scriptbasic.interfaces.ScriptBasicKeyWords; +import com.scriptbasic.interfaces.NestedStructureHouseKeeper.EndOfStatementProcessor; +import com.scriptbasic.interfaces.NestedStructureHouseKeeper.EndOfStatementResult; import com.scriptbasic.spi.Command; +import com.scriptbasic.syntax.BasicSyntaxAnalyzer; /** * @author Peter Verhas * date June 16, 2012 */ public class CommandAnalyzerIf extends AbstractCommandAnalyzerIfKind { + + enum InlineProcessorState { + EXPECTING_THEN_CLAUSE, + EXPECTING_ELSE, + EXPECTING_ELSE_CLAUSE, + CLAUSE_DEFINED + }; + + /** + * Processor for inline 'if' statement + * + */ + class InlineProcessor implements EndOfStatementProcessor { + + InlineProcessorState state = InlineProcessorState.EXPECTING_THEN_CLAUSE; + + @Override + public EndOfStatementResult consumeEndOfStatement() throws AnalysisException { + // else statement was processed + if(state == InlineProcessorState.EXPECTING_ELSE) { + state = InlineProcessorState.EXPECTING_ELSE_CLAUSE; + return EndOfStatementResult.CONSUMED; + } + final var nextElement = ctx.lexicalAnalyzer.peek(); + + // handle multiple statements separated with colon + if(nextElement!=null && nextElement.getLexeme().equals(":")) { + ctx.lexicalAnalyzer.get(); + return EndOfStatementResult.CONSUMED; + } + + if (!nextElement.isLineTerminator() && !nextElement.getLexeme().equals("'") + && state == InlineProcessorState.EXPECTING_THEN_CLAUSE) { + // only else statement is allowed + if(nextElement.isSymbol(ScriptBasicKeyWords.KEYWORD_ELSE)) { + state = InlineProcessorState.EXPECTING_ELSE; + return EndOfStatementResult.CONSUMED; + } + throw new BasicSyntaxException("Unexpexted element: "+nextElement.getLexeme()); + } + + state = InlineProcessorState.CLAUSE_DEFINED; + + // finish this if statement + final var proccessor = ctx.nestedStructureHouseKeeper.popEndOfStatementProcessor(); + if(proccessor!=this) { + throw new BasicSyntaxException("Unexpexted state."); + } + final var cmd = ctx.nestedStructureHouseKeeper.pop(AbstractCommandIfElseKind.class); + + final var commandEndIf = new CommandEndIf(); + cmd.setNext(commandEndIf); + ctx.syntaxAnalyzer.addCommand(commandEndIf); + return EndOfStatementResult.PROCESSED; + } + + } public CommandAnalyzerIf(final Context ctx) { super(ctx); } - protected Command createNode(final Expression condition) { + @Override + public Command analyze() throws AnalysisException { + var condition = analyzeCondition(); + final var node = new CommandIf(); node.setCondition(condition); pushNode(node); + + final var nextElement = ctx.lexicalAnalyzer.peek(); + // check if inline version of if statement + if (nextElement != null && !nextElement.isLineTerminator() + && !BasicSyntaxAnalyzer.lineToIgnore(nextElement.getLexeme())) { + ctx.nestedStructureHouseKeeper.pushEndOfStatementProcessor(new InlineProcessor()); + } else { + consumeEndOfStatement(); + } return node; } } diff --git a/src/test/java/com/scriptbasic/main/TestCommandLine.java b/src/test/java/com/scriptbasic/main/TestCommandLine.java index 9954c0e4..4c1e2110 100644 --- a/src/test/java/com/scriptbasic/main/TestCommandLine.java +++ b/src/test/java/com/scriptbasic/main/TestCommandLine.java @@ -25,7 +25,7 @@ public void testNoArgs() throws Exception { @Test public void testProgram() throws Exception { - main(new String[]{"src/test/resources/com/scriptbasic/testprograms/TestIf.bas"}); + main(new String[]{"src/test/resources/com/scriptbasic/testprograms/TestIf1.bas"}); } @Test diff --git a/src/test/java/com/scriptbasic/testprograms/TestPrograms.java b/src/test/java/com/scriptbasic/testprograms/TestPrograms.java index 96b93c02..7defc576 100644 --- a/src/test/java/com/scriptbasic/testprograms/TestPrograms.java +++ b/src/test/java/com/scriptbasic/testprograms/TestPrograms.java @@ -60,7 +60,14 @@ public void testPrograms() throws Exception { final Map map; codeTest("TestEmpty.bas", ""); codeTest("TestPrintHello.bas", "hello"); - codeTest("TestIf.bas", "111"); + codeTest("TestIf1.bas", "111"); + codeTest("TestIf2.bas", "12568"); + codeTest("TestIf3.bas", "12456"); + codeTest("TestIf4.bas", "135910"); + testSyntaxFail("TestIncorrectIf1.bas"); + testSyntaxFail("TestIncorrectIf2.bas"); + testSyntaxFail("TestIncorrectIf3.bas"); + testSyntaxFail("TestIncorrectIf4.bas"); codeTest("TestSelect1.bas", "1111111"); codeTest("TestSelect2.bas", "2468"); codeTest("TestSelect3.bas", "0 1 1 2 3 5 8"); diff --git a/src/test/resources/com/scriptbasic/testprograms/TestIf.bas b/src/test/resources/com/scriptbasic/testprograms/TestIf1.bas similarity index 100% rename from src/test/resources/com/scriptbasic/testprograms/TestIf.bas rename to src/test/resources/com/scriptbasic/testprograms/TestIf1.bas diff --git a/src/test/resources/com/scriptbasic/testprograms/TestIf2.bas b/src/test/resources/com/scriptbasic/testprograms/TestIf2.bas new file mode 100644 index 00000000..f019a050 --- /dev/null +++ b/src/test/resources/com/scriptbasic/testprograms/TestIf2.bas @@ -0,0 +1,10 @@ +' +' This program is used in unit testing the jScriptBasic interpreter to test the functionality of the +' single line if-then-else +' +print "1" +if true then print "2" else print "3" +if false then print "4" else print "5" +if true then print "6" +if false then print "7" +print "8" diff --git a/src/test/resources/com/scriptbasic/testprograms/TestIf3.bas b/src/test/resources/com/scriptbasic/testprograms/TestIf3.bas new file mode 100644 index 00000000..c85dfa78 --- /dev/null +++ b/src/test/resources/com/scriptbasic/testprograms/TestIf3.bas @@ -0,0 +1,7 @@ +' +' This program is used in unit testing the jScriptBasic interpreter to test the functionality of the +' single line if-then-else +' +print "1" +If True Then print "2":if "b"="a" then print "3" else print "4": print "5" +print "6" diff --git a/src/test/resources/com/scriptbasic/testprograms/TestIf4.bas b/src/test/resources/com/scriptbasic/testprograms/TestIf4.bas new file mode 100644 index 00000000..18833ba5 --- /dev/null +++ b/src/test/resources/com/scriptbasic/testprograms/TestIf4.bas @@ -0,0 +1,9 @@ +' +' This program is used in unit testing the jScriptBasic interpreter to test the functionality of the +' single line if-then-else +' +print "1" +If True Then if False then print "2" else print "3" +If True Then if False then print "4" else print "5" else print "6" +If False Then if False then print "7" else print "8" else print "9" +print "10" diff --git a/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf1.bas b/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf1.bas new file mode 100644 index 00000000..aa8d87b9 --- /dev/null +++ b/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf1.bas @@ -0,0 +1,5 @@ +' +' ElseIf is not allowed on single line statements +' + +if true then print "2" elseif false then print "3" diff --git a/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf2.bas b/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf2.bas new file mode 100644 index 00000000..630e4ae5 --- /dev/null +++ b/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf2.bas @@ -0,0 +1,6 @@ +' +' Single line if on multiple line +' + +if true then print "2" +else print "3" diff --git a/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf3.bas b/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf3.bas new file mode 100644 index 00000000..a36e9310 --- /dev/null +++ b/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf3.bas @@ -0,0 +1,10 @@ +' +' If with multiple else +' + +if true then + print "2" +else + print "3" +else + print "4" diff --git a/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf4.bas b/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf4.bas new file mode 100644 index 00000000..43449a23 --- /dev/null +++ b/src/test/resources/com/scriptbasic/testprograms/TestIncorrectIf4.bas @@ -0,0 +1,5 @@ +' +' If without commands +' + +if true then rem Test