Skip to content

Commit 1dc1f62

Browse files
authored
Merge pull request #10 from PetrPytelka/add_select_case
Support for select/case statement
2 parents 33eab4d + 52e6fbe commit 1dc1f62

19 files changed

Lines changed: 616 additions & 4 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.scriptbasic.executors.commands;
2+
3+
/**
4+
* Part of select command
5+
*
6+
* Part can be select declaration or case declaration
7+
*/
8+
public abstract class AbstractCommandSelectPart
9+
extends AbstractCommand {
10+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.scriptbasic.executors.commands;
2+
3+
import com.scriptbasic.api.ScriptBasicException;
4+
import com.scriptbasic.executors.operators.RightSideEqualsOperator;
5+
import com.scriptbasic.executors.operators.RightSideGreaterOrEqualOperator;
6+
import com.scriptbasic.executors.operators.RightSideLessOrEqualOperator;
7+
import com.scriptbasic.executors.rightvalues.AbstractPrimitiveRightValue;
8+
import com.scriptbasic.executors.rightvalues.BasicBooleanValue;
9+
import com.scriptbasic.interfaces.BasicRuntimeException;
10+
import com.scriptbasic.interfaces.Expression;
11+
import com.scriptbasic.spi.Interpreter;
12+
import com.scriptbasic.spi.RightValue;
13+
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
17+
public class CommandCase extends AbstractCommandSelectPart {
18+
19+
/**
20+
* One condition in the case statement
21+
*
22+
* Single case statement can contain multiple conditions.
23+
*/
24+
public interface CaseCondition {
25+
26+
boolean matchCase(Interpreter interpreter, RightValue selectExpressionValue) throws ScriptBasicException;
27+
}
28+
29+
public static class EqualCaseCondition implements CaseCondition {
30+
final Expression expression;
31+
32+
public EqualCaseCondition(final Expression expression) {
33+
this.expression = expression;
34+
}
35+
36+
@Override
37+
public boolean matchCase(Interpreter interpreter, RightValue selectExpressionValue) throws ScriptBasicException {
38+
final var operator = new RightSideEqualsOperator(selectExpressionValue);
39+
operator.setRightOperand(expression);
40+
final var conditionValue = operator.evaluate(interpreter);
41+
if (conditionValue instanceof AbstractPrimitiveRightValue<?>) {
42+
return BasicBooleanValue.asBoolean(conditionValue);
43+
} else {
44+
throw new BasicRuntimeException(
45+
"Case condition can not be evaluated to boolean");
46+
}
47+
}
48+
}
49+
50+
public static class FromToCaseCondition implements CaseCondition {
51+
final private Expression fromExpression;
52+
final private Expression toExpression;
53+
54+
public FromToCaseCondition(final Expression fromExpression, final Expression toExpression) {
55+
this.fromExpression = fromExpression;
56+
this.toExpression = toExpression;
57+
}
58+
59+
@Override
60+
public boolean matchCase(Interpreter interpreter, RightValue selectExpressionValue)
61+
throws ScriptBasicException {
62+
final var leftOperator = new RightSideGreaterOrEqualOperator(selectExpressionValue);
63+
leftOperator.setRightOperand(fromExpression);
64+
var conditionValue = leftOperator.evaluate(interpreter);
65+
if (conditionValue instanceof AbstractPrimitiveRightValue<?>) {
66+
if(!BasicBooleanValue.asBoolean(conditionValue)) {
67+
return false;
68+
}
69+
// evaluate to condition
70+
final var rightOperator = new RightSideLessOrEqualOperator(selectExpressionValue);
71+
rightOperator.setRightOperand(toExpression);
72+
conditionValue = rightOperator.evaluate(interpreter);
73+
if (conditionValue instanceof AbstractPrimitiveRightValue<?>) {
74+
return BasicBooleanValue.asBoolean(conditionValue);
75+
}
76+
}
77+
78+
throw new BasicRuntimeException("Case condition can not be evaluated to boolean");
79+
}
80+
81+
}
82+
83+
/**
84+
* List of case conditions to be evaluated
85+
*/
86+
private List<CaseCondition> expressionList = new ArrayList<>();
87+
private CommandEndSelect commandEndSelect;
88+
89+
@Override
90+
public void execute(Interpreter interpreter) throws ScriptBasicException {
91+
92+
// Check if other case already applied
93+
Boolean caseApplied = (Boolean) interpreter.getMap().get(CommandSelect.CASE_APPLIED);
94+
if(caseApplied) {
95+
// skip to end
96+
interpreter.setNextCommand(commandEndSelect);
97+
} else {
98+
// set as applied and execute this case
99+
100+
interpreter.getMap().put(CommandSelect.CASE_APPLIED, true);
101+
102+
interpreter.setNextCommand(getNextCommand());
103+
}
104+
105+
}
106+
107+
public void addCaseCondition(final CaseCondition caseCondition) {
108+
expressionList.add(caseCondition);
109+
}
110+
111+
public boolean matches(Interpreter interpreter, RightValue expressionValue) throws ScriptBasicException {
112+
// default case has empty list of expressions
113+
if(expressionList.size()==0) {
114+
return true;
115+
}
116+
117+
// check case specific conditions
118+
for(final var caseCondition: expressionList) {
119+
if(caseCondition.matchCase(interpreter, expressionValue)) {
120+
return true;
121+
}
122+
}
123+
124+
// any condition do not math
125+
return false;
126+
}
127+
128+
public void addCaseEqualCondition(Expression expression) {
129+
final var caseCondition = new EqualCaseCondition(expression);
130+
addCaseCondition(caseCondition);
131+
}
132+
133+
public void addCaseFromToCondition(Expression fromExpression, Expression toExpression) {
134+
final var caseCondition = new FromToCaseCondition(fromExpression, toExpression);
135+
addCaseCondition(caseCondition);
136+
137+
}
138+
139+
public void setEndSelect(CommandEndSelect commandEndSelect) {
140+
this.commandEndSelect = commandEndSelect;
141+
}
142+
143+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.scriptbasic.executors.commands;
2+
3+
import com.scriptbasic.api.ScriptBasicException;
4+
import com.scriptbasic.spi.Interpreter;
5+
6+
public class CommandEndSelect
7+
extends AbstractCommand
8+
{
9+
10+
@Override
11+
public void execute(Interpreter interpreter) throws ScriptBasicException {
12+
// do nothing
13+
}
14+
15+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.scriptbasic.executors.commands;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import com.scriptbasic.api.ScriptBasicException;
7+
import com.scriptbasic.interfaces.Expression;
8+
import com.scriptbasic.spi.Interpreter;
9+
10+
public class CommandSelect extends AbstractCommandSelectPart {
11+
12+
static public final String CASE_APPLIED = "SELECT_CASE_APPLIED";
13+
14+
/**
15+
* Expression in select statement
16+
*/
17+
private Expression expression;
18+
19+
private CommandEndSelect commandEndSelect;
20+
21+
/**
22+
* List of possible cases
23+
*/
24+
private List<CommandCase> cases = new ArrayList<>();
25+
26+
public void setExpression(final Expression expression) {
27+
this.expression = expression;
28+
}
29+
30+
@Override
31+
public void execute(Interpreter interpreter) throws ScriptBasicException {
32+
final var expressionValue = expression.evaluate(interpreter);
33+
// Mark as not finished
34+
interpreter.getMap().put(CASE_APPLIED, false);
35+
36+
// Iterate over all the cases and sets the next
37+
// command pointer/index on the first matching case
38+
for(final var singleCase: cases) {
39+
if(singleCase.matches(interpreter, expressionValue)) {
40+
interpreter.setNextCommand(singleCase);
41+
return;
42+
}
43+
}
44+
// no case found -> continue with endSelect
45+
interpreter.setNextCommand(commandEndSelect.getNextCommand());
46+
}
47+
48+
/**
49+
* Set command for End Select statement
50+
*
51+
* Method is called by parser at the end
52+
* of select statement.
53+
*
54+
* @param commandEndSelect command
55+
*/
56+
public void setEndSelectNode(CommandEndSelect commandEndSelect) {
57+
this.commandEndSelect = commandEndSelect;
58+
59+
for(final var singleCase: cases) {
60+
singleCase.setEndSelect(commandEndSelect);
61+
}
62+
}
63+
64+
public void registerCase(CommandCase node) {
65+
cases.add(node);
66+
}
67+
68+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.scriptbasic.executors.operators;
2+
3+
import com.scriptbasic.api.ScriptBasicException;
4+
import com.scriptbasic.spi.Interpreter;
5+
import com.scriptbasic.spi.RightValue;
6+
7+
public class RightSideEqualsOperator
8+
extends EqualsOperator {
9+
10+
/**
11+
* Evaluated left operand
12+
*/
13+
final private RightValue leftOperandEvaluated;
14+
15+
public RightSideEqualsOperator(final RightValue leftOperandEvaluated) {
16+
this.leftOperandEvaluated = leftOperandEvaluated;
17+
}
18+
19+
@Override
20+
protected RightValue getLeftOperandEvaluated(final Interpreter interpreter) throws ScriptBasicException {
21+
return this.leftOperandEvaluated;
22+
}
23+
24+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.scriptbasic.executors.operators;
2+
3+
import com.scriptbasic.api.ScriptBasicException;
4+
import com.scriptbasic.spi.Interpreter;
5+
import com.scriptbasic.spi.RightValue;
6+
7+
public class RightSideGreaterOrEqualOperator extends GreaterOrEqualOperator {
8+
9+
/**
10+
* Evaluated left operand
11+
*/
12+
private RightValue leftOperandEvaluated;
13+
14+
public RightSideGreaterOrEqualOperator(final RightValue leftOperandEvaluated) {
15+
this.leftOperandEvaluated = leftOperandEvaluated;
16+
}
17+
18+
@Override
19+
protected RightValue getLeftOperandEvaluated(final Interpreter interpreter) throws ScriptBasicException {
20+
return this.leftOperandEvaluated;
21+
}
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.scriptbasic.executors.operators;
2+
3+
import com.scriptbasic.api.ScriptBasicException;
4+
import com.scriptbasic.spi.Interpreter;
5+
import com.scriptbasic.spi.RightValue;
6+
7+
public class RightSideLessOrEqualOperator extends LessOrEqualOperator {
8+
9+
/**
10+
* Evaluated left operand
11+
*/
12+
final private RightValue leftOperandEvaluated;
13+
14+
public RightSideLessOrEqualOperator(final RightValue leftOperandEvaluated) {
15+
this.leftOperandEvaluated = leftOperandEvaluated;
16+
}
17+
18+
@Override
19+
protected RightValue getLeftOperandEvaluated(final Interpreter interpreter) throws ScriptBasicException {
20+
return this.leftOperandEvaluated;
21+
}
22+
23+
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ public interface ScriptBasicKeyWords {
88
String KEYWORD_AND = "and";
99
String KEYWORD_AS = "as";
1010
String KEYWORD_CALL = "call";
11+
String KEYWORD_CASE = "case";
1112
String KEYWORD_DIV = "div";
1213
String KEYWORD_ELSE = "else";
1314
String KEYWORD_ELSEIF = "elseif";
14-
String KEYWORD_ENDIF = "endif";
1515
String KEYWORD_END = "end";
16+
String KEYWORD_ENDIF = "endif";
1617
String KEYWORD_ENDSUB = "endsub";
1718
String KEYWORD_FALSE = "false";
1819
String KEYWORD_FOR = "for";
1920
String KEYWORD_FROM = "from";
20-
String KEYWORD_GLOBAL = "global";
21+
String KEYWORD_GLOBAL = "global";
2122
String KEYWORD_IF = "if";
2223
String KEYWORD_IS = "is";
2324
String KEYWORD_LET = "let";
@@ -30,6 +31,7 @@ public interface ScriptBasicKeyWords {
3031
String KEYWORD_REM = "rem";
3132
String KEYWORD_REPEAT = "repeat";
3233
String KEYWORD_RETURN = "return";
34+
String KEYWORD_SELECT = "select";
3335
String KEYWORD_SENTENCE = "sentence";
3436
String KEYWORD_STEP = "step";
3537
String KEYWORD_SUB = "sub";
@@ -75,8 +77,9 @@ public interface ScriptBasicKeyWords {
7577
KEYWORD_REM,
7678
KEYWORD_SUB, KEYWORD_ENDSUB,
7779
KEYWORD_RETURN, KEYWORD_PRINT,
78-
KEYWORD_CALL,
80+
KEYWORD_CALL,
81+
KEYWORD_CASE, KEYWORD_SELECT,
7982
KEYWORD_SENTENCE};
8083
String[] BASIC_OPERATORS = new String[]{OPERATOR_LESS_OR_EQUAL, OPERATOR_GREATER_OR_EQUAL, OPERATOR_NOT_EQUALS };
81-
int BASIC_OPERATOR_LEXEME_MAX_LENGTH = 2;
84+
int BASIC_OPERATOR_LEXEME_MAX_LENGTH = 2;
8285
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public BasicCommandFactory(final Context ctx) {
5050
Map.entry(ScriptBasicKeyWords.KEYWORD_LET, new CommandAnalyzerLet(ctx)),
5151
Map.entry(ScriptBasicKeyWords.KEYWORD_FOR, new CommandAnalyzerFor(ctx)),
5252
Map.entry(ScriptBasicKeyWords.KEYWORD_NEXT, new CommandAnalyzerNext(ctx)),
53+
Map.entry(ScriptBasicKeyWords.KEYWORD_SELECT, new CommandAnalyzerSelect(ctx)),
54+
Map.entry(ScriptBasicKeyWords.KEYWORD_END, new CommandAnalyzerEnd(ctx)),
55+
Map.entry(ScriptBasicKeyWords.KEYWORD_CASE, new CommandAnalyzerCase(ctx)),
5356
Map.entry(ScriptBasicKeyWords.KEYWORD_SENTENCE, dslAnalyzer));
5457
classList = List.of(
5558
new CommandAnalyzerLet(ctx),

0 commit comments

Comments
 (0)