From fe39c1ac00952f0b804dbd28bc15eeb7b2eec00d Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 3 Sep 2023 21:42:23 +0300 Subject: [PATCH] Define functions and constants in root scope --- .../com/annimon/ownlang/lib/Functions.java | 25 +--- .../com/annimon/ownlang/lib/RootScope.java | 68 +++++++++ .../java/com/annimon/ownlang/lib/Scope.java | 64 +++++++++ .../com/annimon/ownlang/lib/ScopeHandler.java | 129 ++++++++++++++++++ .../com/annimon/ownlang/lib/Variables.java | 118 +++------------- .../com/annimon/ownlang/lib/ClassMethod.java | 6 +- .../ownlang/lib/UserDefinedFunction.java | 8 +- .../com/annimon/ownlang/parser/Linter.java | 12 +- .../ast/DestructuringAssignmentStatement.java | 11 +- .../parser/ast/ForeachArrayStatement.java | 13 +- .../parser/ast/ForeachMapStatement.java | 25 ++-- .../parser/ast/FunctionalExpression.java | 8 +- .../ownlang/parser/ast/MatchExpression.java | 34 +++-- .../parser/ast/VariableExpression.java | 3 +- .../annimon/ownlang/parser/ProgramsTest.java | 8 +- 15 files changed, 344 insertions(+), 188 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java index f43d029..ed40469 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java @@ -1,7 +1,5 @@ package com.annimon.ownlang.lib; -import com.annimon.ownlang.exceptions.UnknownFunctionException; -import java.util.HashMap; import java.util.Map; /** @@ -9,32 +7,21 @@ import java.util.Map; * @author aNNiMON */ public final class Functions { - - private static final Map functions; - static { - functions = new HashMap<>(); - } - private Functions() { } - public static void clear() { - functions.clear(); - } - public static Map getFunctions() { - return functions; + return ScopeHandler.functions(); } - public static boolean isExists(String key) { - return functions.containsKey(key); + public static boolean isExists(String name) { + return ScopeHandler.isFunctionExists(name); } - public static Function get(String key) { - if (!isExists(key)) throw new UnknownFunctionException(key); - return functions.get(key); + public static Function get(String name) { + return ScopeHandler.getFunction(name); } public static void set(String key, Function function) { - functions.put(key, function); + ScopeHandler.setFunction(key, function); } } diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java new file mode 100644 index 0000000..82ded80 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java @@ -0,0 +1,68 @@ +package com.annimon.ownlang.lib; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +final class RootScope extends Scope { + private final Map constants; + private final Map functions; + + RootScope() { + functions = new ConcurrentHashMap<>(); + constants = new ConcurrentHashMap<>(); + constants.put("true", NumberValue.ONE); + constants.put("false", NumberValue.ZERO); + } + + @Override + public boolean isRoot() { + return true; + } + + @Override + public boolean contains(String name) { + return super.containsVariable(name) + || containsConstant(name); + } + + public boolean containsConstant(String name) { + return constants.containsKey(name); + } + + @Override + public Value get(String name) { + if (containsConstant(name)) { + return getConstant(name); + } + return super.get(name); + } + + public Value getConstant(String name) { + return constants.get(name); + } + + public void setConstant(String name, Value value) { + constants.put(name, value); + } + + public Map getConstants() { + return constants; + } + + + public boolean containsFunction(String name) { + return functions.containsKey(name); + } + + public Function getFunction(String name) { + return functions.get(name); + } + + public void setFunction(String name, Function function) { + functions.put(name, function); + } + + public Map getFunctions() { + return functions; + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java new file mode 100644 index 0000000..a92d604 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Scope.java @@ -0,0 +1,64 @@ +package com.annimon.ownlang.lib; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +sealed class Scope permits RootScope { + final Scope parent; + private final Map variables; + + Scope() { + this(null); + } + + Scope(Scope parent) { + this.parent = parent; + variables = new ConcurrentHashMap<>(); + } + + public boolean isRoot() { + return !hasParent(); + } + + public boolean hasParent() { + return parent != null; + } + + public boolean contains(String name) { + return containsVariable(name); + } + + public final boolean containsVariable(String name) { + return variables.containsKey(name); + } + + public Value get(String name) { + return getVariable(name); + } + + public final Value getVariable(String name) { + return variables.get(name); + } + + public final void setVariable(String name, Value value) { + variables.put(name, value); + } + + public final void removeVariable(String name) { + variables.remove(name); + } + + public Map getVariables() { + return variables; + } + + static class ScopeFindData { + final boolean isFound; + final Scope scope; + + ScopeFindData(boolean isFound, Scope scope) { + this.isFound = isFound; + this.scope = scope; + } + } +} \ No newline at end of file diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java new file mode 100644 index 0000000..179c401 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java @@ -0,0 +1,129 @@ +package com.annimon.ownlang.lib; + +import com.annimon.ownlang.exceptions.UnknownFunctionException; +import com.annimon.ownlang.lib.Scope.ScopeFindData; + +import java.util.Map; + +public final class ScopeHandler { + + private static final Object lock = new Object(); + + private static volatile RootScope rootScope; + private static volatile Scope scope; + + static { + ScopeHandler.resetScope(); + } + + public static Map variables() { + return scope.getVariables(); + } + + public static Map constants() { + return rootScope.getConstants(); + } + + public static Map functions() { + return rootScope.getFunctions(); + } + + /** + * Resets a scope for new program execution + */ + public static void resetScope() { + rootScope = new RootScope(); + scope = rootScope; + } + + public static void push() { + synchronized (lock) { + scope = new Scope(scope); + } + } + + public static void pop() { + synchronized (lock) { + if (!scope.isRoot()) { + scope = scope.parent; + } + } + } + + + + public static boolean isFunctionExists(String name) { + return rootScope.containsFunction(name); + } + + public static Function getFunction(String name) { + final var function = rootScope.getFunction(name); + if (function == null) throw new UnknownFunctionException(name); + return function; + } + + public static void setFunction(String name, Function function) { + rootScope.setFunction(name, function); + } + + + public static boolean isVariableOrConstantExists(String name) { + synchronized (lock) { + return findScope(name).isFound; + } + } + + public static Value getVariableOrConstant(String name) { + synchronized (lock) { + final ScopeFindData scopeData = findScope(name); + if (scopeData.isFound) { + return scopeData.scope.get(name); + } + } + return NumberValue.ZERO; + } + + public static Value getVariable(String name) { + synchronized (lock) { + final ScopeFindData scopeData = findScope(name); + if (scopeData.isFound) { + return scopeData.scope.getVariable(name); + } + } + // TODO should be strict + return NumberValue.ZERO; + } + + public static void setVariable(String name, Value value) { + synchronized (lock) { + findScope(name).scope.setVariable(name, value); + } + } + + public static void setConstant(String name, Value value) { + rootScope.setConstant(name, value); + } + + public static void defineVariableInCurrentScope(String name, Value value) { + synchronized (lock) { + scope.setVariable(name, value); + } + } + + public static void removeVariable(String name) { + synchronized (lock) { + findScope(name).scope.removeVariable(name); + } + } + + private static ScopeFindData findScope(String name) { + Scope current = scope; + do { + if (current.contains(name)) { + return new ScopeFindData(true, current); + } + } while ((current = current.parent) != null); + + return new ScopeFindData(false, scope); + } +} diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java index 4033fa3..a3b4121 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java @@ -1,118 +1,34 @@ package com.annimon.ownlang.lib; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * * @author aNNiMON */ public final class Variables { - - private static final Object lock = new Object(); - - private static class Scope { - final Scope parent; - final Map variables; - - Scope() { - this(null); - } - - Scope(Scope parent) { - this.parent = parent; - variables = new ConcurrentHashMap<>(); - } - } - - private static class ScopeFindData { - boolean isFound; - Scope scope; - } - - private static volatile Scope scope; - static { - Variables.clear(); - } - private Variables() { } public static Map variables() { - return scope.variables; + return ScopeHandler.variables(); + } + + public static boolean isExists(String name) { + return ScopeHandler.isVariableOrConstantExists(name); + } + + public static Value get(String name) { + return ScopeHandler.getVariableOrConstant(name); + } + + public static void set(String name, Value value) { + ScopeHandler.setVariable(name, value); } - public static void clear() { - scope = new Scope(); - scope.variables.clear(); - scope.variables.put("true", NumberValue.ONE); - scope.variables.put("false", NumberValue.ZERO); - } - - public static void push() { - synchronized (lock) { - scope = new Scope(scope); - } - } - - public static void pop() { - synchronized (lock) { - if (scope.parent != null) { - scope = scope.parent; - } - } - } - - public static boolean isExists(String key) { - synchronized (lock) { - return findScope(key).isFound; - } - } - - public static Value get(String key) { - synchronized (lock) { - final ScopeFindData scopeData = findScope(key); - if (scopeData.isFound) { - return scopeData.scope.variables.get(key); - } - } - return NumberValue.ZERO; - } - - public static void set(String key, Value value) { - synchronized (lock) { - findScope(key).scope.variables.put(key, value); - } - } - - public static void define(String key, Value value) { - synchronized (lock) { - scope.variables.put(key, value); - } - } - - public static void remove(String key) { - synchronized (lock) { - findScope(key).scope.variables.remove(key); - } - } - - /* - * Find scope where variable exists. + /** + * For compatibility with other modules */ - private static ScopeFindData findScope(String variable) { - final ScopeFindData result = new ScopeFindData(); - - Scope current = scope; - do { - if (current.variables.containsKey(variable)) { - result.isFound = true; - result.scope = current; - return result; - } - } while ((current = current.parent) != null); - - result.isFound = false; - result.scope = scope; - return result; + public static void define(String name, Value value) { + ScopeHandler.setConstant(name, value); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java index a595039..6155a64 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java @@ -14,13 +14,13 @@ public class ClassMethod extends UserDefinedFunction { @Override public Value execute(Value[] values) { - Variables.push(); - Variables.define("this", classInstance.getThisMap()); + ScopeHandler.push(); + ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap()); try { return super.execute(values); } finally { - Variables.pop(); + ScopeHandler.pop(); } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java index 352008c..b0a92be 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -45,21 +45,21 @@ public class UserDefinedFunction implements Function { } try { - Variables.push(); + ScopeHandler.push(); for (int i = 0; i < size; i++) { - Variables.define(getArgsName(i), values[i]); + ScopeHandler.defineVariableInCurrentScope(getArgsName(i), values[i]); } // Optional args if exists for (int i = size; i < totalArgsCount; i++) { final Argument arg = arguments.get(i); - Variables.define(arg.name(), arg.valueExpr().eval()); + ScopeHandler.defineVariableInCurrentScope(arg.name(), arg.valueExpr().eval()); } body.execute(); return NumberValue.ZERO; } catch (ReturnStatement rt) { return rt.getResult(); } finally { - Variables.pop(); + ScopeHandler.pop(); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java index 999236a..ba2dee1 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Linter.java @@ -1,13 +1,12 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.Console; -import com.annimon.ownlang.parser.linters.AssignValidator; -import com.annimon.ownlang.parser.linters.UseWithNonStringValueValidator; -import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.Statement; import com.annimon.ownlang.parser.ast.Visitor; +import com.annimon.ownlang.parser.linters.AssignValidator; +import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator; +import com.annimon.ownlang.parser.linters.UseWithNonStringValueValidator; public final class Linter { @@ -36,7 +35,6 @@ public final class Linter { } private void resetState() { - Variables.clear(); - Functions.getFunctions().clear(); + ScopeHandler.resetScope(); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java index 5848b35..edf0885 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java @@ -1,10 +1,7 @@ package com.annimon.ownlang.parser.ast; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.MapValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; + import java.util.Iterator; import java.util.List; import java.util.Map; @@ -42,7 +39,7 @@ public final class DestructuringAssignmentStatement extends InterruptableNode im for (int i = 0; i < size; i++) { final String variable = variables.get(i); if (variable != null) { - Variables.define(variable, array.get(i)); + ScopeHandler.defineVariableInCurrentScope(variable, array.get(i)); } } } @@ -51,7 +48,7 @@ public final class DestructuringAssignmentStatement extends InterruptableNode im for (Map.Entry entry : map) { final String variable = variables.get(i); if (variable != null) { - Variables.define(variable, new ArrayValue( + ScopeHandler.defineVariableInCurrentScope(variable, new ArrayValue( new Value[] { entry.getKey(), entry.getValue() } )); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java index fbc5aa6..921eda9 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -23,7 +23,8 @@ public final class ForeachArrayStatement extends InterruptableNode implements St @Override public void execute() { super.interruptionCheck(); - final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null; + // TODO removing without checking shadowing is dangerous + final Value previousVariableValue = ScopeHandler.getVariable(variable); final Value containerValue = container.eval(); switch (containerValue.type()) { @@ -42,15 +43,15 @@ public final class ForeachArrayStatement extends InterruptableNode implements St // Restore variables if (previousVariableValue != null) { - Variables.set(variable, previousVariableValue); + ScopeHandler.setVariable(variable, previousVariableValue); } else { - Variables.remove(variable); + ScopeHandler.removeVariable(variable); } } private void iterateString(String str) { for (char ch : str.toCharArray()) { - Variables.set(variable, new StringValue(String.valueOf(ch))); + ScopeHandler.setVariable(variable, new StringValue(String.valueOf(ch))); try { body.execute(); } catch (BreakStatement bs) { @@ -63,7 +64,7 @@ public final class ForeachArrayStatement extends InterruptableNode implements St private void iterateArray(ArrayValue containerValue) { for (Value value : containerValue) { - Variables.set(variable, value); + ScopeHandler.setVariable(variable, value); try { body.execute(); } catch (BreakStatement bs) { @@ -76,7 +77,7 @@ public final class ForeachArrayStatement extends InterruptableNode implements St private void iterateMap(MapValue containerValue) { for (Map.Entry entry : containerValue) { - Variables.set(variable, new ArrayValue(new Value[] { + ScopeHandler.setVariable(variable, new ArrayValue(new Value[] { entry.getKey(), entry.getValue() })); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java index 2a24393..32bfd49 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java @@ -24,8 +24,9 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat @Override public void execute() { super.interruptionCheck(); - final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null; - final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null; + // TODO removing without checking shadowing is dangerous + final Value previousVariableValue1 = ScopeHandler.getVariable(key); + final Value previousVariableValue2 = ScopeHandler.getVariable(value); final Value containerValue = container.eval(); switch (containerValue.type()) { @@ -44,21 +45,21 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat // Restore variables if (previousVariableValue1 != null) { - Variables.set(key, previousVariableValue1); + ScopeHandler.setVariable(key, previousVariableValue1); } else { - Variables.remove(key); + ScopeHandler.removeVariable(key); } if (previousVariableValue2 != null) { - Variables.set(value, previousVariableValue2); + ScopeHandler.setVariable(value, previousVariableValue2); } else { - Variables.remove(value); + ScopeHandler.removeVariable(value); } } private void iterateString(String str) { for (char ch : str.toCharArray()) { - Variables.set(key, new StringValue(String.valueOf(ch))); - Variables.set(value, NumberValue.of(ch)); + ScopeHandler.setVariable(key, new StringValue(String.valueOf(ch))); + ScopeHandler.setVariable(value, NumberValue.of(ch)); try { body.execute(); } catch (BreakStatement bs) { @@ -72,8 +73,8 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat private void iterateArray(ArrayValue containerValue) { int index = 0; for (Value v : containerValue) { - Variables.set(key, v); - Variables.set(value, NumberValue.of(index++)); + ScopeHandler.setVariable(key, v); + ScopeHandler.setVariable(value, NumberValue.of(index++)); try { body.execute(); } catch (BreakStatement bs) { @@ -86,8 +87,8 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat private void iterateMap(MapValue containerValue) { for (Map.Entry entry : containerValue) { - Variables.set(key, entry.getKey()); - Variables.set(value, entry.getValue()); + ScopeHandler.setVariable(key, entry.getKey()); + ScopeHandler.setVariable(value, entry.getValue()); try { body.execute(); } catch (BreakStatement bs) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java index eb4c5c4..84c4144 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -64,11 +64,11 @@ public final class FunctionalExpression extends InterruptableNode implements Exp } private Function getFunction(String key) { - if (Functions.isExists(key)) { - return Functions.get(key); + if (ScopeHandler.isFunctionExists(key)) { + return ScopeHandler.getFunction(key); } - if (Variables.isExists(key)) { - final Value variable = Variables.get(key); + if (ScopeHandler.isVariableOrConstantExists(key)) { + final Value variable = ScopeHandler.getVariableOrConstant(key); if (variable.type() == Types.FUNCTION) { return ((FunctionValue)variable).getValue(); } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java index 4a33440..8151bf7 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -1,11 +1,8 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.exceptions.PatternMatchingException; -import com.annimon.ownlang.lib.ArrayValue; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Types; -import com.annimon.ownlang.lib.Value; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -44,18 +41,18 @@ public final class MatchExpression extends InterruptableNode implements Expressi final VariablePattern pattern = (VariablePattern) p; if (pattern.variable.equals("_")) return evalResult(p.result); - if (Variables.isExists(pattern.variable)) { - if (match(value, Variables.get(pattern.variable)) && optMatches(p)) { + if (ScopeHandler.isVariableOrConstantExists(pattern.variable)) { + if (match(value, ScopeHandler.getVariableOrConstant(pattern.variable)) && optMatches(p)) { return evalResult(p.result); } } else { - Variables.define(pattern.variable, value); + ScopeHandler.defineVariableInCurrentScope(pattern.variable, value); if (optMatches(p)) { final Value result = evalResult(p.result); - Variables.remove(pattern.variable); + ScopeHandler.removeVariable(pattern.variable); return result; } - Variables.remove(pattern.variable); + ScopeHandler.removeVariable(pattern.variable); } } if ((value.type() == Types.ARRAY) && (p instanceof ListPattern)) { @@ -64,7 +61,7 @@ public final class MatchExpression extends InterruptableNode implements Expressi // Clean up variables if matched final Value result = evalResult(p.result); for (String var : pattern.parts) { - Variables.remove(var); + ScopeHandler.removeVariable(var); } return result; } @@ -105,11 +102,11 @@ public final class MatchExpression extends InterruptableNode implements Expressi case 1: // match arr { case [x]: x = arr ... } final String variable = parts.get(0); - Variables.define(variable, array); + ScopeHandler.defineVariableInCurrentScope(variable, array); if (optMatches(p)) { return true; } - Variables.remove(variable); + ScopeHandler.removeVariable(variable); return false; default: { // match arr { case [...]: .. } @@ -128,7 +125,7 @@ public final class MatchExpression extends InterruptableNode implements Expressi private boolean matchListPatternEqualsSize(ListPattern p, List parts, int partsSize, ArrayValue array) { // Set variables for (int i = 0; i < partsSize; i++) { - Variables.define(parts.get(i), array.get(i)); + ScopeHandler.defineVariableInCurrentScope(parts.get(i), array.get(i)); } if (optMatches(p)) { // Clean up will be provided after evaluate result @@ -136,7 +133,8 @@ public final class MatchExpression extends InterruptableNode implements Expressi } // Clean up variables if no match for (String var : parts) { - Variables.remove(var); + // TODO removing without checking shadowing is dangerous + ScopeHandler.removeVariable(var); } return false; } @@ -145,14 +143,14 @@ public final class MatchExpression extends InterruptableNode implements Expressi // Set element variables final int lastPart = partsSize - 1; for (int i = 0; i < lastPart; i++) { - Variables.define(parts.get(i), array.get(i)); + ScopeHandler.defineVariableInCurrentScope(parts.get(i), array.get(i)); } // Set tail variable final ArrayValue tail = new ArrayValue(arraySize - partsSize + 1); for (int i = lastPart; i < arraySize; i++) { tail.set(i - lastPart, array.get(i)); } - Variables.define(parts.get(lastPart), tail); + ScopeHandler.defineVariableInCurrentScope(parts.get(lastPart), tail); // Check optional condition if (optMatches(p)) { // Clean up will be provided after evaluate result @@ -160,7 +158,7 @@ public final class MatchExpression extends InterruptableNode implements Expressi } // Clean up variables for (String var : parts) { - Variables.remove(var); + ScopeHandler.removeVariable(var); } return false; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java index 35653a1..4d4bac0 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/VariableExpression.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.exceptions.VariableDoesNotExistsException; +import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; @@ -30,7 +31,7 @@ public final class VariableExpression extends InterruptableNode implements Expre @Override public Value set(Value value) { - Variables.set(name, value); + ScopeHandler.setVariable(name, value); return value; } diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index e9bb36f..a860c47 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -1,10 +1,7 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.Console; -import com.annimon.ownlang.lib.FunctionValue; -import com.annimon.ownlang.lib.Functions; -import com.annimon.ownlang.lib.NumberValue; -import com.annimon.ownlang.lib.Variables; +import com.annimon.ownlang.lib.*; import com.annimon.ownlang.outputsettings.OutputSettings; import com.annimon.ownlang.outputsettings.StringOutputSettings; import com.annimon.ownlang.parser.ast.FunctionDefineStatement; @@ -33,8 +30,7 @@ public class ProgramsTest { @BeforeEach public void initialize() { - Variables.clear(); - Functions.clear(); + ScopeHandler.resetScope(); // Let's mock junit methods as ounit functions Functions.set("assertEquals", (args) -> { assertEquals(args[0], args[1]);