Skip to content

Commit fe2e726

Browse files
committed
Add scope levels.
Signed-off-by: Felix Klauke <info@felix-klauke.de>
1 parent 45cfc28 commit fe2e726

16 files changed

Lines changed: 149 additions & 37 deletions
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package net.jackwhite20.slug.ast;
2+
3+
/**
4+
* A specific node that is aware of its current scope level stored in the {@link #currentScopeLevel}.
5+
*
6+
* @author Felix Klauke <info@felix-klauke.de>
7+
*/
8+
public class ScopeAwareNode extends Node {
9+
10+
/**
11+
* The current scope level of the parsed node.
12+
*/
13+
private final int currentScopeLevel;
14+
15+
/**
16+
* Create a new block node by its current scope level.
17+
*
18+
* @param currentScopeLevel The scope level.
19+
*/
20+
public ScopeAwareNode(int currentScopeLevel) {
21+
this.currentScopeLevel = currentScopeLevel;
22+
}
23+
24+
/**
25+
* Get the current scope level of the node.
26+
*
27+
* @return The scope level.
28+
*/
29+
public int getCurrentScopeLevel() {
30+
return currentScopeLevel;
31+
}
32+
}

src/main/java/net/jackwhite20/slug/ast/VariableAssignNode.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@
1616

1717
package net.jackwhite20.slug.ast;
1818

19-
public class VariableAssignNode extends Node {
19+
/**
20+
* @author Felix Klauke <info@felix-klauke.de>
21+
*/
22+
public class VariableAssignNode extends ScopeAwareNode {
2023

2124
private String variableName;
2225

2326
private Node right;
2427

25-
public VariableAssignNode(String variableName, Node right) {
28+
public VariableAssignNode(int currentScopeLevel, String variableName, Node right) {
29+
super(currentScopeLevel);
2630
this.variableName = variableName;
2731
this.right = right;
2832
}

src/main/java/net/jackwhite20/slug/ast/VariableDeclarationAssignNode.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,19 @@
1818

1919
import net.jackwhite20.slug.lexer.TokenType;
2020

21-
public class VariableDeclarationAssignNode extends Node {
21+
/**
22+
* @author Felix Klauke <info@felix-klauke.de>
23+
*/
24+
public class VariableDeclarationAssignNode extends ScopeAwareNode {
2225

2326
private String variableName;
2427

2528
private TokenType variableType;
2629

2730
private Node right;
2831

29-
public VariableDeclarationAssignNode(String variableName, TokenType variableType, Node right) {
32+
public VariableDeclarationAssignNode(int currentScopeLevel, String variableName, TokenType variableType, Node right) {
33+
super(currentScopeLevel);
3034
this.variableName = variableName;
3135
this.variableType = variableType;
3236
this.right = right;

src/main/java/net/jackwhite20/slug/ast/VariableDeclarationNode.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818

1919
import net.jackwhite20.slug.lexer.TokenType;
2020

21-
public class VariableDeclarationNode extends Node {
21+
/**
22+
* @author Felix Klauke <info@felix-klauke.de>
23+
*/
24+
public class VariableDeclarationNode extends ScopeAwareNode {
2225

2326
private String variableName;
2427

2528
private TokenType variableType;
2629

27-
public VariableDeclarationNode(String variableName, TokenType variableType) {
30+
public VariableDeclarationNode(int currentScopeLevel, String variableName, TokenType variableType) {
31+
super(currentScopeLevel);
2832
this.variableName = variableName;
2933
this.variableType = variableType;
3034
}

src/main/java/net/jackwhite20/slug/ast/VariableUsageNode.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@
1818

1919
import net.jackwhite20.slug.lexer.Token;
2020

21-
public class VariableUsageNode extends Node {
21+
/**
22+
* @author Felix Klauke <info@felix-klauke.de>
23+
*/
24+
public class VariableUsageNode extends ScopeAwareNode {
2225

2326
private String variableName;
2427

25-
public VariableUsageNode(Token token) {
28+
public VariableUsageNode(int currentScopeLevel, Token token) {
29+
super(currentScopeLevel);
2630
this.variableName = token.getValue();
2731
}
2832

src/main/java/net/jackwhite20/slug/interpreter/NodeVisitor.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class NodeVisitor {
2828

2929
private GlobalVariableRegistry globalVariableRegistry = new GlobalVariableRegistry();
3030

31+
private int currentScopeLevel = 0;
32+
3133
/**
3234
* Visits the NumberNode and returns it's value.
3335
*
@@ -62,26 +64,34 @@ private void visitMain(MainNode node) {
6264
}
6365

6466
private void visitFunction(FunctionNode functionNode) {
67+
currentScopeLevel++;
68+
6569
for (Node node : functionNode.getChildren()) {
6670
visit(node);
6771
}
72+
73+
currentScopeLevel--;
6874
}
6975

7076
private void visitVariableDeclarationAssign(VariableDeclarationAssignNode node) {
7177
// Get the value from the right expression
7278
Object value = visit(node.getRight());
7379

74-
globalVariableRegistry.register(node.getVariableName(), node.getVariableType(), value);
80+
globalVariableRegistry.register(currentScopeLevel, node.getVariableName(), node.getVariableType(), value);
7581
}
7682

7783
private void visitVariableAssign(VariableAssignNode node) {
7884
// Get the value from the right expression
7985
Object value = visit(node.getRight());
8086

81-
globalVariableRegistry.update(node.getVariableName(), value);
87+
globalVariableRegistry.update(currentScopeLevel, node.getVariableName(), value);
8288
}
8389

8490
Object visit(Node node) {
91+
if (node instanceof ScopeAwareNode && ((ScopeAwareNode) node).getCurrentScopeLevel() < currentScopeLevel) {
92+
throw new IllegalStateException("Illegal scope access.");
93+
}
94+
8595
if (node instanceof MainNode) {
8696
visitMain(((MainNode) node));
8797
return null;

src/main/java/net/jackwhite20/slug/interpreter/variable/VariableRegistry.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import java.util.HashMap;
2424
import java.util.Map;
2525

26+
/**
27+
* @author Felix Klauke <info@felix-klauke.de>
28+
*/
2629
public abstract class VariableRegistry {
2730

2831
private static Logger logger = LoggerFactory.getLogger(VariableRegistry.class);
@@ -33,26 +36,45 @@ public abstract class VariableRegistry {
3336

3437
private Map<String, TokenType> variableTypes = new HashMap<>();
3538

39+
private Map<String, Integer> variableScopeLevels = new HashMap<>();
40+
3641
public VariableRegistry(String name) {
3742
this.name = name;
3843
}
3944

40-
public void register(String variableName, TokenType variableType, Object value) {
45+
public void register(int currentScopeLevel, String variableName, TokenType variableType, Object value) {
4146
variables.put(variableName, value);
4247
variableTypes.put(variableName, variableType);
48+
variableScopeLevels.put(variableName, currentScopeLevel);
4349

4450
logger.debug("Registered variable {} ({})", variableName, variableType);
4551
}
4652

47-
public void update(String variableName, Object newValue) {
53+
public void update(int currentScopeLevel, String variableName, Object newValue) {
54+
int scopeLevel = variableScopeLevels.get(variableName);
55+
56+
if (currentScopeLevel < scopeLevel) {
57+
throw new IllegalStateException("Illegal variable access.");
58+
}
59+
4860
variables.put(variableName, newValue);
4961

5062
logger.debug("Updated variable {} with the new value {}", variableName, newValue);
5163
}
5264

53-
public <T> T lookup(String name) {
65+
public <T> T lookup(int currentScopeLevel, String name) {
5466
//noinspection unchecked
55-
return (T) variables.get(name);
67+
T instance = (T) variables.get(name);
68+
69+
if (instance != null) {
70+
int scopeLevel = variableScopeLevels.get(name);
71+
72+
if (currentScopeLevel < scopeLevel) {
73+
throw new IllegalStateException("Illegal variable access.");
74+
}
75+
}
76+
77+
return instance;
5678
}
5779

5880
public boolean checkType(String variableName, TokenType expectedVarType) {

src/main/java/net/jackwhite20/slug/parser/Parser.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@
2727

2828
/**
2929
* @author Philip 'JackWhite20' <silencephil@gmail.com>
30+
* @author Felix Klauke <info@felix-klauke.de>
3031
*/
3132
public class Parser {
3233

3334
private Lexer lexer;
3435

3536
private Token currentToken;
3637

38+
private int currentScopeLevel = 0;
39+
3740
public Parser(Lexer lexer) {
3841
this.lexer = lexer;
3942
this.currentToken = lexer.nextToken();
@@ -50,6 +53,8 @@ private void eat(TokenType tokenType) {
5053
}
5154

5255
private Node parseFunction() {
56+
currentScopeLevel++;
57+
5358
eat(TokenType.FUNC);
5459
String functionName = currentToken.getValue();
5560
// Call because CALL is used when the next char is "(", which is when calling a function (possible parameters etc.)
@@ -69,6 +74,8 @@ private Node parseFunction() {
6974
// Register the global function
7075
FunctionRegistry.register(functionNode);
7176

77+
currentScopeLevel--;
78+
7279
return functionNode;
7380
}
7481

@@ -116,10 +123,10 @@ private Node parseDeclareOrAndAssignStatement() {
116123
eat(TokenType.NAME);
117124

118125
if (currentToken.getTokenType() != TokenType.ASSIGN) {
119-
return new VariableDeclarationNode(varName, varType);
126+
return new VariableDeclarationNode(currentScopeLevel, varName, varType);
120127
} else {
121128
eat(TokenType.ASSIGN);
122-
return new VariableDeclarationAssignNode(varName, varType, expression());
129+
return new VariableDeclarationAssignNode(currentScopeLevel, varName, varType, expression());
123130
}
124131
}
125132

@@ -130,13 +137,13 @@ private Node parseVariableAssign() {
130137

131138
Node right = expression();
132139

133-
return new VariableAssignNode(varName, right);
140+
return new VariableAssignNode(currentScopeLevel, varName, right);
134141
}
135142

136143
private Node parseVariableUsage() {
137144
Token variableNameToken = currentToken;
138145
eat(TokenType.NAME);
139-
return new VariableUsageNode(variableNameToken);
146+
return new VariableUsageNode(currentScopeLevel, variableNameToken);
140147
}
141148

142149
private Node statement() {

src/test/java/net/jackwhite20/slug/ast/FunctionNodeTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class FunctionNodeTest {
3030

3131
private static final String NAME = "MyOtherFunction";
3232

33-
private static final List<Node> CHILDREN = Collections.singletonList(new VariableUsageNode(new Token(TokenType.NAME, "a")));
33+
private static final List<Node> CHILDREN = Collections.singletonList(new VariableUsageNode(0, new Token(TokenType.NAME, "a")));
3434

3535
private static final List<Node> PARAMETERS = Arrays.asList(new NumberNode("65"), new BoolNode("true"));
3636

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package net.jackwhite20.slug.ast;
2+
3+
import org.junit.Before;
4+
import org.junit.Test;
5+
6+
import static org.junit.Assert.assertEquals;
7+
8+
/**
9+
* @author Felix Klauke <fklauke@itemis.de>
10+
*/
11+
public class ScopeAwareNodeTest {
12+
13+
private static final int TEST_SCOPE_LEVEL = 80000;
14+
private ScopeAwareNode blockNode;
15+
16+
@Before
17+
public void setUp() {
18+
blockNode = new ScopeAwareNode(TEST_SCOPE_LEVEL);
19+
}
20+
21+
@Test
22+
public void getCurrentScopeLevel() {
23+
assertEquals(TEST_SCOPE_LEVEL, blockNode.getCurrentScopeLevel());
24+
}
25+
}

0 commit comments

Comments
 (0)