mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 08:44: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;
|
package com.annimon.ownlang.lib;
|
||||||
|
|
||||||
import com.annimon.ownlang.exceptions.UnknownFunctionException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -9,32 +7,21 @@ import java.util.Map;
|
|||||||
* @author aNNiMON
|
* @author aNNiMON
|
||||||
*/
|
*/
|
||||||
public final class Functions {
|
public final class Functions {
|
||||||
|
|
||||||
private static final Map<String, Function> functions;
|
|
||||||
static {
|
|
||||||
functions = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Functions() { }
|
private Functions() { }
|
||||||
|
|
||||||
public static void clear() {
|
|
||||||
functions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, Function> getFunctions() {
|
public static Map<String, Function> getFunctions() {
|
||||||
return functions;
|
return ScopeHandler.functions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExists(String key) {
|
public static boolean isExists(String name) {
|
||||||
return functions.containsKey(key);
|
return ScopeHandler.isFunctionExists(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Function get(String key) {
|
public static Function get(String name) {
|
||||||
if (!isExists(key)) throw new UnknownFunctionException(key);
|
return ScopeHandler.getFunction(name);
|
||||||
return functions.get(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void set(String key, Function function) {
|
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;
|
package com.annimon.ownlang.lib;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author aNNiMON
|
* @author aNNiMON
|
||||||
*/
|
*/
|
||||||
public final class Variables {
|
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() { }
|
private Variables() { }
|
||||||
|
|
||||||
public static Map<String, Value> variables() {
|
public static Map<String, Value> variables() {
|
||||||
return scope.variables;
|
return ScopeHandler.variables();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clear() {
|
public static boolean isExists(String name) {
|
||||||
scope = new Scope();
|
return ScopeHandler.isVariableOrConstantExists(name);
|
||||||
scope.variables.clear();
|
|
||||||
scope.variables.put("true", NumberValue.ONE);
|
|
||||||
scope.variables.put("false", NumberValue.ZERO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void push() {
|
public static Value get(String name) {
|
||||||
synchronized (lock) {
|
return ScopeHandler.getVariableOrConstant(name);
|
||||||
scope = new Scope(scope);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void pop() {
|
public static void set(String name, Value value) {
|
||||||
synchronized (lock) {
|
ScopeHandler.setVariable(name, value);
|
||||||
if (scope.parent != null) {
|
|
||||||
scope = scope.parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExists(String key) {
|
/**
|
||||||
synchronized (lock) {
|
* For compatibility with other modules
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
private static ScopeFindData findScope(String variable) {
|
public static void define(String name, Value value) {
|
||||||
final ScopeFindData result = new ScopeFindData();
|
ScopeHandler.setConstant(name, value);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,13 @@ public class ClassMethod extends UserDefinedFunction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value execute(Value[] values) {
|
public Value execute(Value[] values) {
|
||||||
Variables.push();
|
ScopeHandler.push();
|
||||||
Variables.define("this", classInstance.getThisMap());
|
ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return super.execute(values);
|
return super.execute(values);
|
||||||
} finally {
|
} finally {
|
||||||
Variables.pop();
|
ScopeHandler.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,21 +45,21 @@ public class UserDefinedFunction implements Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Variables.push();
|
ScopeHandler.push();
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
Variables.define(getArgsName(i), values[i]);
|
ScopeHandler.defineVariableInCurrentScope(getArgsName(i), values[i]);
|
||||||
}
|
}
|
||||||
// Optional args if exists
|
// Optional args if exists
|
||||||
for (int i = size; i < totalArgsCount; i++) {
|
for (int i = size; i < totalArgsCount; i++) {
|
||||||
final Argument arg = arguments.get(i);
|
final Argument arg = arguments.get(i);
|
||||||
Variables.define(arg.name(), arg.valueExpr().eval());
|
ScopeHandler.defineVariableInCurrentScope(arg.name(), arg.valueExpr().eval());
|
||||||
}
|
}
|
||||||
body.execute();
|
body.execute();
|
||||||
return NumberValue.ZERO;
|
return NumberValue.ZERO;
|
||||||
} catch (ReturnStatement rt) {
|
} catch (ReturnStatement rt) {
|
||||||
return rt.getResult();
|
return rt.getResult();
|
||||||
} finally {
|
} finally {
|
||||||
Variables.pop();
|
ScopeHandler.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package com.annimon.ownlang.parser;
|
package com.annimon.ownlang.parser;
|
||||||
|
|
||||||
import com.annimon.ownlang.Console;
|
import com.annimon.ownlang.Console;
|
||||||
import com.annimon.ownlang.parser.linters.AssignValidator;
|
import com.annimon.ownlang.lib.ScopeHandler;
|
||||||
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.parser.ast.Statement;
|
import com.annimon.ownlang.parser.ast.Statement;
|
||||||
import com.annimon.ownlang.parser.ast.Visitor;
|
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 {
|
public final class Linter {
|
||||||
|
|
||||||
@ -36,7 +35,6 @@ public final class Linter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resetState() {
|
private void resetState() {
|
||||||
Variables.clear();
|
ScopeHandler.resetScope();
|
||||||
Functions.getFunctions().clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
import com.annimon.ownlang.lib.ArrayValue;
|
import com.annimon.ownlang.lib.*;
|
||||||
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 java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -42,7 +39,7 @@ public final class DestructuringAssignmentStatement extends InterruptableNode im
|
|||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
final String variable = variables.get(i);
|
final String variable = variables.get(i);
|
||||||
if (variable != null) {
|
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) {
|
for (Map.Entry<Value, Value> entry : map) {
|
||||||
final String variable = variables.get(i);
|
final String variable = variables.get(i);
|
||||||
if (variable != null) {
|
if (variable != null) {
|
||||||
Variables.define(variable, new ArrayValue(
|
ScopeHandler.defineVariableInCurrentScope(variable, new ArrayValue(
|
||||||
new Value[] { entry.getKey(), entry.getValue() }
|
new Value[] { entry.getKey(), entry.getValue() }
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ public final class ForeachArrayStatement extends InterruptableNode implements St
|
|||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
super.interruptionCheck();
|
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();
|
final Value containerValue = container.eval();
|
||||||
switch (containerValue.type()) {
|
switch (containerValue.type()) {
|
||||||
@ -42,15 +43,15 @@ public final class ForeachArrayStatement extends InterruptableNode implements St
|
|||||||
|
|
||||||
// Restore variables
|
// Restore variables
|
||||||
if (previousVariableValue != null) {
|
if (previousVariableValue != null) {
|
||||||
Variables.set(variable, previousVariableValue);
|
ScopeHandler.setVariable(variable, previousVariableValue);
|
||||||
} else {
|
} else {
|
||||||
Variables.remove(variable);
|
ScopeHandler.removeVariable(variable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void iterateString(String str) {
|
private void iterateString(String str) {
|
||||||
for (char ch : str.toCharArray()) {
|
for (char ch : str.toCharArray()) {
|
||||||
Variables.set(variable, new StringValue(String.valueOf(ch)));
|
ScopeHandler.setVariable(variable, new StringValue(String.valueOf(ch)));
|
||||||
try {
|
try {
|
||||||
body.execute();
|
body.execute();
|
||||||
} catch (BreakStatement bs) {
|
} catch (BreakStatement bs) {
|
||||||
@ -63,7 +64,7 @@ public final class ForeachArrayStatement extends InterruptableNode implements St
|
|||||||
|
|
||||||
private void iterateArray(ArrayValue containerValue) {
|
private void iterateArray(ArrayValue containerValue) {
|
||||||
for (Value value : containerValue) {
|
for (Value value : containerValue) {
|
||||||
Variables.set(variable, value);
|
ScopeHandler.setVariable(variable, value);
|
||||||
try {
|
try {
|
||||||
body.execute();
|
body.execute();
|
||||||
} catch (BreakStatement bs) {
|
} catch (BreakStatement bs) {
|
||||||
@ -76,7 +77,7 @@ public final class ForeachArrayStatement extends InterruptableNode implements St
|
|||||||
|
|
||||||
private void iterateMap(MapValue containerValue) {
|
private void iterateMap(MapValue containerValue) {
|
||||||
for (Map.Entry<Value, Value> entry : 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.getKey(),
|
||||||
entry.getValue()
|
entry.getValue()
|
||||||
}));
|
}));
|
||||||
|
@ -24,8 +24,9 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat
|
|||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
super.interruptionCheck();
|
super.interruptionCheck();
|
||||||
final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null;
|
// TODO removing without checking shadowing is dangerous
|
||||||
final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null;
|
final Value previousVariableValue1 = ScopeHandler.getVariable(key);
|
||||||
|
final Value previousVariableValue2 = ScopeHandler.getVariable(value);
|
||||||
|
|
||||||
final Value containerValue = container.eval();
|
final Value containerValue = container.eval();
|
||||||
switch (containerValue.type()) {
|
switch (containerValue.type()) {
|
||||||
@ -44,21 +45,21 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat
|
|||||||
|
|
||||||
// Restore variables
|
// Restore variables
|
||||||
if (previousVariableValue1 != null) {
|
if (previousVariableValue1 != null) {
|
||||||
Variables.set(key, previousVariableValue1);
|
ScopeHandler.setVariable(key, previousVariableValue1);
|
||||||
} else {
|
} else {
|
||||||
Variables.remove(key);
|
ScopeHandler.removeVariable(key);
|
||||||
}
|
}
|
||||||
if (previousVariableValue2 != null) {
|
if (previousVariableValue2 != null) {
|
||||||
Variables.set(value, previousVariableValue2);
|
ScopeHandler.setVariable(value, previousVariableValue2);
|
||||||
} else {
|
} else {
|
||||||
Variables.remove(value);
|
ScopeHandler.removeVariable(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void iterateString(String str) {
|
private void iterateString(String str) {
|
||||||
for (char ch : str.toCharArray()) {
|
for (char ch : str.toCharArray()) {
|
||||||
Variables.set(key, new StringValue(String.valueOf(ch)));
|
ScopeHandler.setVariable(key, new StringValue(String.valueOf(ch)));
|
||||||
Variables.set(value, NumberValue.of(ch));
|
ScopeHandler.setVariable(value, NumberValue.of(ch));
|
||||||
try {
|
try {
|
||||||
body.execute();
|
body.execute();
|
||||||
} catch (BreakStatement bs) {
|
} catch (BreakStatement bs) {
|
||||||
@ -72,8 +73,8 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat
|
|||||||
private void iterateArray(ArrayValue containerValue) {
|
private void iterateArray(ArrayValue containerValue) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (Value v : containerValue) {
|
for (Value v : containerValue) {
|
||||||
Variables.set(key, v);
|
ScopeHandler.setVariable(key, v);
|
||||||
Variables.set(value, NumberValue.of(index++));
|
ScopeHandler.setVariable(value, NumberValue.of(index++));
|
||||||
try {
|
try {
|
||||||
body.execute();
|
body.execute();
|
||||||
} catch (BreakStatement bs) {
|
} catch (BreakStatement bs) {
|
||||||
@ -86,8 +87,8 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat
|
|||||||
|
|
||||||
private void iterateMap(MapValue containerValue) {
|
private void iterateMap(MapValue containerValue) {
|
||||||
for (Map.Entry<Value, Value> entry : containerValue) {
|
for (Map.Entry<Value, Value> entry : containerValue) {
|
||||||
Variables.set(key, entry.getKey());
|
ScopeHandler.setVariable(key, entry.getKey());
|
||||||
Variables.set(value, entry.getValue());
|
ScopeHandler.setVariable(value, entry.getValue());
|
||||||
try {
|
try {
|
||||||
body.execute();
|
body.execute();
|
||||||
} catch (BreakStatement bs) {
|
} catch (BreakStatement bs) {
|
||||||
|
@ -64,11 +64,11 @@ public final class FunctionalExpression extends InterruptableNode implements Exp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Function getFunction(String key) {
|
private Function getFunction(String key) {
|
||||||
if (Functions.isExists(key)) {
|
if (ScopeHandler.isFunctionExists(key)) {
|
||||||
return Functions.get(key);
|
return ScopeHandler.getFunction(key);
|
||||||
}
|
}
|
||||||
if (Variables.isExists(key)) {
|
if (ScopeHandler.isVariableOrConstantExists(key)) {
|
||||||
final Value variable = Variables.get(key);
|
final Value variable = ScopeHandler.getVariableOrConstant(key);
|
||||||
if (variable.type() == Types.FUNCTION) {
|
if (variable.type() == Types.FUNCTION) {
|
||||||
return ((FunctionValue)variable).getValue();
|
return ((FunctionValue)variable).getValue();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
import com.annimon.ownlang.exceptions.PatternMatchingException;
|
import com.annimon.ownlang.exceptions.PatternMatchingException;
|
||||||
import com.annimon.ownlang.lib.ArrayValue;
|
import com.annimon.ownlang.lib.*;
|
||||||
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 java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -44,18 +41,18 @@ public final class MatchExpression extends InterruptableNode implements Expressi
|
|||||||
final VariablePattern pattern = (VariablePattern) p;
|
final VariablePattern pattern = (VariablePattern) p;
|
||||||
if (pattern.variable.equals("_")) return evalResult(p.result);
|
if (pattern.variable.equals("_")) return evalResult(p.result);
|
||||||
|
|
||||||
if (Variables.isExists(pattern.variable)) {
|
if (ScopeHandler.isVariableOrConstantExists(pattern.variable)) {
|
||||||
if (match(value, Variables.get(pattern.variable)) && optMatches(p)) {
|
if (match(value, ScopeHandler.getVariableOrConstant(pattern.variable)) && optMatches(p)) {
|
||||||
return evalResult(p.result);
|
return evalResult(p.result);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Variables.define(pattern.variable, value);
|
ScopeHandler.defineVariableInCurrentScope(pattern.variable, value);
|
||||||
if (optMatches(p)) {
|
if (optMatches(p)) {
|
||||||
final Value result = evalResult(p.result);
|
final Value result = evalResult(p.result);
|
||||||
Variables.remove(pattern.variable);
|
ScopeHandler.removeVariable(pattern.variable);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
Variables.remove(pattern.variable);
|
ScopeHandler.removeVariable(pattern.variable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((value.type() == Types.ARRAY) && (p instanceof ListPattern)) {
|
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
|
// Clean up variables if matched
|
||||||
final Value result = evalResult(p.result);
|
final Value result = evalResult(p.result);
|
||||||
for (String var : pattern.parts) {
|
for (String var : pattern.parts) {
|
||||||
Variables.remove(var);
|
ScopeHandler.removeVariable(var);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -105,11 +102,11 @@ public final class MatchExpression extends InterruptableNode implements Expressi
|
|||||||
|
|
||||||
case 1: // match arr { case [x]: x = arr ... }
|
case 1: // match arr { case [x]: x = arr ... }
|
||||||
final String variable = parts.get(0);
|
final String variable = parts.get(0);
|
||||||
Variables.define(variable, array);
|
ScopeHandler.defineVariableInCurrentScope(variable, array);
|
||||||
if (optMatches(p)) {
|
if (optMatches(p)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Variables.remove(variable);
|
ScopeHandler.removeVariable(variable);
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
default: { // match arr { case [...]: .. }
|
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) {
|
private boolean matchListPatternEqualsSize(ListPattern p, List<String> parts, int partsSize, ArrayValue array) {
|
||||||
// Set variables
|
// Set variables
|
||||||
for (int i = 0; i < partsSize; i++) {
|
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)) {
|
if (optMatches(p)) {
|
||||||
// Clean up will be provided after evaluate result
|
// 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
|
// Clean up variables if no match
|
||||||
for (String var : parts) {
|
for (String var : parts) {
|
||||||
Variables.remove(var);
|
// TODO removing without checking shadowing is dangerous
|
||||||
|
ScopeHandler.removeVariable(var);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -145,14 +143,14 @@ public final class MatchExpression extends InterruptableNode implements Expressi
|
|||||||
// Set element variables
|
// Set element variables
|
||||||
final int lastPart = partsSize - 1;
|
final int lastPart = partsSize - 1;
|
||||||
for (int i = 0; i < lastPart; i++) {
|
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
|
// Set tail variable
|
||||||
final ArrayValue tail = new ArrayValue(arraySize - partsSize + 1);
|
final ArrayValue tail = new ArrayValue(arraySize - partsSize + 1);
|
||||||
for (int i = lastPart; i < arraySize; i++) {
|
for (int i = lastPart; i < arraySize; i++) {
|
||||||
tail.set(i - lastPart, array.get(i));
|
tail.set(i - lastPart, array.get(i));
|
||||||
}
|
}
|
||||||
Variables.define(parts.get(lastPart), tail);
|
ScopeHandler.defineVariableInCurrentScope(parts.get(lastPart), tail);
|
||||||
// Check optional condition
|
// Check optional condition
|
||||||
if (optMatches(p)) {
|
if (optMatches(p)) {
|
||||||
// Clean up will be provided after evaluate result
|
// Clean up will be provided after evaluate result
|
||||||
@ -160,7 +158,7 @@ public final class MatchExpression extends InterruptableNode implements Expressi
|
|||||||
}
|
}
|
||||||
// Clean up variables
|
// Clean up variables
|
||||||
for (String var : parts) {
|
for (String var : parts) {
|
||||||
Variables.remove(var);
|
ScopeHandler.removeVariable(var);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
import com.annimon.ownlang.exceptions.VariableDoesNotExistsException;
|
import com.annimon.ownlang.exceptions.VariableDoesNotExistsException;
|
||||||
|
import com.annimon.ownlang.lib.ScopeHandler;
|
||||||
import com.annimon.ownlang.lib.Value;
|
import com.annimon.ownlang.lib.Value;
|
||||||
import com.annimon.ownlang.lib.Variables;
|
import com.annimon.ownlang.lib.Variables;
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ public final class VariableExpression extends InterruptableNode implements Expre
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value set(Value value) {
|
public Value set(Value value) {
|
||||||
Variables.set(name, value);
|
ScopeHandler.setVariable(name, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package com.annimon.ownlang.parser;
|
package com.annimon.ownlang.parser;
|
||||||
|
|
||||||
import com.annimon.ownlang.Console;
|
import com.annimon.ownlang.Console;
|
||||||
import com.annimon.ownlang.lib.FunctionValue;
|
import com.annimon.ownlang.lib.*;
|
||||||
import com.annimon.ownlang.lib.Functions;
|
|
||||||
import com.annimon.ownlang.lib.NumberValue;
|
|
||||||
import com.annimon.ownlang.lib.Variables;
|
|
||||||
import com.annimon.ownlang.outputsettings.OutputSettings;
|
import com.annimon.ownlang.outputsettings.OutputSettings;
|
||||||
import com.annimon.ownlang.outputsettings.StringOutputSettings;
|
import com.annimon.ownlang.outputsettings.StringOutputSettings;
|
||||||
import com.annimon.ownlang.parser.ast.FunctionDefineStatement;
|
import com.annimon.ownlang.parser.ast.FunctionDefineStatement;
|
||||||
@ -33,8 +30,7 @@ public class ProgramsTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
Variables.clear();
|
ScopeHandler.resetScope();
|
||||||
Functions.clear();
|
|
||||||
// Let's mock junit methods as ounit functions
|
// Let's mock junit methods as ounit functions
|
||||||
Functions.set("assertEquals", (args) -> {
|
Functions.set("assertEquals", (args) -> {
|
||||||
assertEquals(args[0], args[1]);
|
assertEquals(args[0], args[1]);
|
||||||
|
Loading…
Reference in New Issue
Block a user