Skip to content

Commit 2216ab6

Browse files
authored
Merge pull request #14 from PetrPytelka/improved_select_case_parsing
Improved select case parsing
2 parents b4fe95e + bf69d35 commit 2216ab6

9 files changed

Lines changed: 90 additions & 30 deletions

File tree

src/main/java/com/scriptbasic/context/ContextBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ private static void createReaderDependentComponents(final SourceReader reader, f
9898
ctx.lexicalAnalyzer = new ScriptBasicLexicalAnalyzer(reader);
9999
ctx.nestedStructureHouseKeeper = new GenericNestedStructureHouseKeeper(ctx.lexicalAnalyzer);
100100
final var commandFactory = new BasicCommandFactory(ctx);
101-
ctx.syntaxAnalyzer = new BasicSyntaxAnalyzer(ctx.lexicalAnalyzer, commandFactory);
101+
ctx.syntaxAnalyzer = new BasicSyntaxAnalyzer(ctx.lexicalAnalyzer, commandFactory,
102+
ctx.nestedStructureHouseKeeper);
102103
}
103104

104105
private static void createReusableComponents(final Context ctx) {

src/main/java/com/scriptbasic/interfaces/NestedStructureHouseKeeper.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,14 @@ public interface NestedStructureHouseKeeper {
5151
*/
5252
<T extends NestedStructure> T pop(Class<T> expectedClass)
5353
throws AnalysisException;
54+
55+
/**
56+
* Check final state of nested structures.
57+
*
58+
* Check if there are no opened nested structures
59+
* or any other pending blocks.
60+
*
61+
* @throws AnalysisException when there are some elements on the stack
62+
*/
63+
void checkFinalState() throws AnalysisException;
5464
}

src/main/java/com/scriptbasic/syntax/AbstractNestedStructureHouseKeeper.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,11 @@ public <T> boolean match(final Class<T> expectedClass) {
8383
return expectedClass.isAssignableFrom(getElementType());
8484
}
8585
}
86+
87+
@Override
88+
public void checkFinalState() throws AnalysisException {
89+
if (stack.size() > 0) {
90+
throw new BasicSyntaxException("There is at least one opened block on the stack. Block is not properly closed.");
91+
}
92+
}
8693
}

src/main/java/com/scriptbasic/syntax/BasicSyntaxAnalyzer.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
public final class BasicSyntaxAnalyzer implements SyntaxAnalyzer {
66
private final LexicalAnalyzer lexicalAnalyzer;
77
private final CommandFactory commandFactory;
8-
private LexicalElement lexicalElement;
8+
private final NestedStructureHouseKeeper nestedStructureHouseKeeper;
9+
private LexicalElement lexicalElement;
910

10-
public BasicSyntaxAnalyzer(final LexicalAnalyzer lexicalAnalyzer, final CommandFactory commandFactory) {
11+
public BasicSyntaxAnalyzer(final LexicalAnalyzer lexicalAnalyzer, final CommandFactory commandFactory,
12+
final NestedStructureHouseKeeper nestedStructureHouseKeeper) {
1113
this.lexicalAnalyzer = lexicalAnalyzer;
1214
this.commandFactory = commandFactory;
15+
this.nestedStructureHouseKeeper = nestedStructureHouseKeeper;
1316
}
1417

15-
private static boolean lineToIgnore(final String lexString) {
18+
public static boolean lineToIgnore(final String lexString) {
1619
return lexString.equals("\n") || lexString.equals("'")
1720
|| lexString.equalsIgnoreCase(ScriptBasicKeyWords.KEYWORD_REM);
1821
}
@@ -49,11 +52,12 @@ public BuildableProgram analyze() throws AnalysisException {
4952
}
5053
this.lexicalElement = lexicalAnalyzer.peek();
5154
}
55+
nestedStructureHouseKeeper.checkFinalState();
5256
buildableProgram.postprocess();
5357
return buildableProgram;
5458
}
5559

56-
private void consumeIgnoredLine(final LexicalAnalyzer lexicalAnalyzer, String lexString) throws AnalysisException {
60+
public static void consumeIgnoredLine(final LexicalAnalyzer lexicalAnalyzer, String lexString) throws AnalysisException {
5761
while (!lexString.equals("\n")) {
5862
final var le = lexicalAnalyzer.get();
5963
if (le == null) {

src/main/java/com/scriptbasic/syntax/commands/CommandAnalyzerSelect.java

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,48 @@
33
import com.scriptbasic.context.Context;
44
import com.scriptbasic.executors.commands.CommandSelect;
55
import com.scriptbasic.interfaces.AnalysisException;
6+
import com.scriptbasic.interfaces.BasicSyntaxException;
67
import com.scriptbasic.interfaces.ScriptBasicKeyWords;
78
import com.scriptbasic.spi.Command;
9+
import com.scriptbasic.syntax.BasicSyntaxAnalyzer;
810

911
public class CommandAnalyzerSelect
10-
extends AbstractCommandAnalyzer
11-
{
12-
13-
public CommandAnalyzerSelect(Context ctx)
14-
{
15-
super(ctx);
16-
}
17-
18-
@Override
19-
public Command analyze() throws AnalysisException {
20-
final var lexicalElement = ctx.lexicalAnalyzer.peek();
21-
// consume optional case statement
22-
if(lexicalElement.isSymbol(ScriptBasicKeyWords.KEYWORD_CASE))
23-
ctx.lexicalAnalyzer.get();
24-
// read expression till end of line
25-
final var expression = analyzeExpression();
26-
consumeEndOfLine();
27-
28-
final var node = new CommandSelect();
29-
node.setExpression(expression);
30-
pushNode(node);
31-
32-
return node;
33-
}
12+
extends AbstractCommandAnalyzer {
13+
14+
public CommandAnalyzerSelect(Context ctx) {
15+
super(ctx);
16+
}
17+
18+
@Override
19+
public Command analyze() throws AnalysisException {
20+
var lexicalElement = ctx.lexicalAnalyzer.peek();
21+
// consume optional case statement
22+
if (lexicalElement.isSymbol(ScriptBasicKeyWords.KEYWORD_CASE))
23+
ctx.lexicalAnalyzer.get();
24+
// read expression till end of line
25+
final var expression = analyzeExpression();
26+
consumeEndOfLine();
27+
28+
final var node = new CommandSelect();
29+
node.setExpression(expression);
30+
pushNode(node);
31+
32+
// next command has to be 'case'
33+
// first skip any comments
34+
lexicalElement = ctx.lexicalAnalyzer.peek();
35+
while (lexicalElement != null && BasicSyntaxAnalyzer.lineToIgnore(lexicalElement.getLexeme())) {
36+
ctx.lexicalAnalyzer.get();
37+
BasicSyntaxAnalyzer.consumeIgnoredLine(ctx.lexicalAnalyzer, lexicalElement.getLexeme());
38+
lexicalElement = ctx.lexicalAnalyzer.peek();
39+
}
40+
if (lexicalElement == null) {
41+
throw new BasicSyntaxException("Preliminary end of file");
42+
}
43+
if (!lexicalElement.isSymbol(ScriptBasicKeyWords.KEYWORD_CASE)) {
44+
throw new BasicSyntaxException("Expected case statement, but found: " + lexicalElement.getLexeme());
45+
}
46+
47+
return node;
48+
}
3449

3550
}

src/test/java/com/scriptbasic/testprograms/TestPrograms.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,13 @@ public void testPrograms() throws Exception {
6161
codeTest("TestEmpty.bas", "");
6262
codeTest("TestPrintHello.bas", "hello");
6363
codeTest("TestIf.bas", "111");
64-
codeTest("TestSelect1.bas", "111111");
64+
codeTest("TestSelect1.bas", "1111111");
6565
codeTest("TestSelect2.bas", "2468");
6666
codeTest("TestSelect3.bas", "0 1 1 2 3 5 8");
6767
testSyntaxFail("TestSelectBadSyntax1.bas");
6868
testSyntaxFail("TestSelectBadSyntax2.bas");
6969
testSyntaxFail("TestSelectBadSyntax3.bas");
70+
testSyntaxFail("TestSelectBadSyntax4.bas");
7071
codeTest("TestBooleanConversions.bas", "111111");
7172
codeTest("TestArrays.bas", "OK");
7273
try {

src/test/resources/com/scriptbasic/testprograms/TestSelect1.bas

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,15 @@ case is "1": print "1"
4545
case is "2": print "2"
4646
end select
4747

48+
' Test comments inside select case
49+
select case v
50+
rem comment before first case
51+
case "0": print "0"
52+
rem comment between case
53+
case "1": print "1"
54+
case "2": print "2"
55+
rem comment before end select
56+
end select
57+
rem comment after end select
58+
4859
print "1"

src/test/resources/com/scriptbasic/testprograms/TestSelectBadSyntax1.bas

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55

66
select case v
77
if true then print "1"
8+
end select
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
'
2+
' This program is used to test the bad syntax of
3+
' command select case ... end select
4+
' when the 'end select' is missing
5+
'
6+
7+
select case "1"
8+
case "0": print "0"
9+
case "1": print "1"
10+
case "2": print "2"

0 commit comments

Comments
 (0)