mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Define functions and constants in root scope
This commit is contained in:
parent
16de63f720
commit
fe39c1ac00
@ -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<String, Function> functions;
|
||||
static {
|
||||
functions = new HashMap<>();
|
||||
}
|
||||
|
||||
private Functions() { }
|
||||
|
||||
public static void clear() {
|
||||
functions.clear();
|
||||
}
|
||||
|
||||
public static Map<String, Function> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<String, Value> constants;
|
||||
private final Map<String, Function> 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<String, Value> 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<String, Function> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
}
|
@ -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<String, Value> 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<String, Value> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
|
||||
static class ScopeFindData {
|
||||
final boolean isFound;
|
||||
final Scope scope;
|
||||
|
||||
ScopeFindData(boolean isFound, Scope scope) {
|
||||
this.isFound = isFound;
|
||||
this.scope = scope;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<String, Value> variables() {
|
||||
return scope.getVariables();
|
||||
}
|
||||
|
||||
public static Map<String, Value> constants() {
|
||||
return rootScope.getConstants();
|
||||
}
|
||||
|
||||
public static Map<String, Function> 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);
|
||||
}
|
||||
}
|
@ -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<String, Value> 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<String, Value> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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<Value, Value> 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() }
|
||||
));
|
||||
}
|
||||
|
@ -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<Value, Value> entry : containerValue) {
|
||||
Variables.set(variable, new ArrayValue(new Value[] {
|
||||
ScopeHandler.setVariable(variable, new ArrayValue(new Value[] {
|
||||
entry.getKey(),
|
||||
entry.getValue()
|
||||
}));
|
||||
|
@ -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<Value, Value> 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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<String> 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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
|
Loading…
Reference in New Issue
Block a user