mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Move classes implementation outside of parser module
This commit is contained in:
parent
d9ae2c7e82
commit
ec04e5faca
@ -6,11 +6,6 @@ public final class UnknownClassException extends OwnLangRuntimeException {
|
||||
|
||||
private final String className;
|
||||
|
||||
public UnknownClassException(String name) {
|
||||
super("Unknown class " + name);
|
||||
this.className = name;
|
||||
}
|
||||
|
||||
public UnknownClassException(String name, Range range) {
|
||||
super("Unknown class " + name, range);
|
||||
this.className = name;
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.annimon.ownlang.lib;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record ClassDeclaration(
|
||||
String name,
|
||||
List<ClassField> classFields,
|
||||
List<ClassMethod> classMethods) implements Instantiable {
|
||||
|
||||
/**
|
||||
* Create an instance and put evaluated fields with method declarations
|
||||
* @return new {@link ClassInstance}
|
||||
*/
|
||||
public ClassInstance newInstance(Value[] args) {
|
||||
final var instance = new ClassInstance(name);
|
||||
for (ClassField f : classFields) {
|
||||
instance.addField(f);
|
||||
}
|
||||
for (ClassMethod m : classMethods) {
|
||||
instance.addMethod(m);
|
||||
}
|
||||
return instance.callConstructor(args);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.annimon.ownlang.lib;
|
||||
|
||||
public record ClassField(
|
||||
String name,
|
||||
EvaluableValue evaluableValue
|
||||
) {
|
||||
}
|
@ -1,16 +1,18 @@
|
||||
package com.annimon.ownlang.lib;
|
||||
|
||||
import com.annimon.ownlang.exceptions.OwnLangRuntimeException;
|
||||
import com.annimon.ownlang.exceptions.TypeException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ClassInstanceValue implements Value {
|
||||
|
||||
public class ClassInstance implements Value {
|
||||
|
||||
private final String className;
|
||||
private final MapValue thisMap;
|
||||
private ClassMethod constructor;
|
||||
private UserDefinedFunction toString;
|
||||
private ClassMethod toString;
|
||||
private boolean isInstantiated;
|
||||
|
||||
public ClassInstanceValue(String name) {
|
||||
public ClassInstance(String name) {
|
||||
this.className = name;
|
||||
thisMap = new MapValue(10);
|
||||
}
|
||||
@ -19,31 +21,33 @@ public class ClassInstanceValue implements Value {
|
||||
return thisMap;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
public void addField(ClassField f) {
|
||||
thisMap.set(f.name(), f.evaluableValue().eval());
|
||||
}
|
||||
|
||||
public void addField(String name, Value value) {
|
||||
thisMap.set(name, value);
|
||||
}
|
||||
|
||||
public void addMethod(String name, ClassMethod method) {
|
||||
if (name.equals("toString")) {
|
||||
toString = method;
|
||||
}
|
||||
public void addMethod(ClassMethod method) {
|
||||
method.setClassInstance(this);
|
||||
final String name = method.getName();
|
||||
thisMap.set(name, method);
|
||||
if (name.equals(className)) {
|
||||
constructor = method;
|
||||
} else if (name.equals("toString")) {
|
||||
toString = method;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void callConstructor(Value[] args) {
|
||||
public ClassInstance callConstructor(Value[] args) {
|
||||
if (isInstantiated) {
|
||||
throw new OwnLangRuntimeException(
|
||||
"Class %s was already instantiated".formatted(className));
|
||||
}
|
||||
if (constructor != null) {
|
||||
CallStack.enter("class " + className, constructor, null);
|
||||
constructor.execute(args);
|
||||
CallStack.exit();
|
||||
}
|
||||
isInstantiated = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Value access(Value value) {
|
||||
@ -53,15 +57,16 @@ public class ClassInstanceValue implements Value {
|
||||
public void set(Value key, Value value) {
|
||||
final Value v = thisMap.get(key);
|
||||
if (v == null) {
|
||||
throw new RuntimeException("Unable to add new field "
|
||||
+ key.asString() + " to class " + className);
|
||||
throw new OwnLangRuntimeException(
|
||||
"Unable to add new field %s to class %s"
|
||||
.formatted(key.asString(), className));
|
||||
}
|
||||
thisMap.set(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object raw() {
|
||||
return null;
|
||||
return thisMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -77,9 +82,9 @@ public class ClassInstanceValue implements Value {
|
||||
@Override
|
||||
public String asString() {
|
||||
if (toString != null) {
|
||||
return toString.execute(new Value[0]).asString();
|
||||
return toString.execute().asString();
|
||||
}
|
||||
return className + "@" + thisMap;
|
||||
return className + "@" + thisMap.asString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,7 +105,7 @@ public class ClassInstanceValue implements Value {
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
final ClassInstanceValue other = (ClassInstanceValue) obj;
|
||||
final ClassInstance other = (ClassInstance) obj;
|
||||
return Objects.equals(this.className, other.className)
|
||||
&& Objects.equals(this.thisMap, other.thisMap);
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.annimon.ownlang.lib;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ClassMethod implements Function {
|
||||
private final String name;
|
||||
private final Function function;
|
||||
private ClassInstance classInstance;
|
||||
|
||||
public ClassMethod(String name, Function function) {
|
||||
this.name = name;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setClassInstance(ClassInstance classInstance) {
|
||||
this.classInstance = classInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value execute(Value... args) {
|
||||
ScopeHandler.push();
|
||||
ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap());
|
||||
|
||||
try {
|
||||
return function.execute(args);
|
||||
} finally {
|
||||
ScopeHandler.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getArgsCount() {
|
||||
return function.getArgsCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||
var that = (ClassMethod) obj;
|
||||
return Objects.equals(this.name, that.name) &&
|
||||
Objects.equals(this.function, that.function);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, function);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClassMethod[" +
|
||||
"name=" + name + ", " +
|
||||
"function=" + function + ']';
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.annimon.ownlang.lib;
|
||||
|
||||
public interface EvaluableValue {
|
||||
|
||||
Value eval();
|
||||
}
|
@ -8,11 +8,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
final class RootScope extends Scope {
|
||||
private final Map<String, Value> constants;
|
||||
private final Map<String, Function> functions;
|
||||
private final Map<String, ClassDeclaration> classDeclarations;
|
||||
private final Set<String> loadedModules;
|
||||
|
||||
RootScope() {
|
||||
functions = new ConcurrentHashMap<>();
|
||||
constants = new ConcurrentHashMap<>();
|
||||
classDeclarations = new ConcurrentHashMap<>();
|
||||
constants.put("true", NumberValue.ONE);
|
||||
constants.put("false", NumberValue.ZERO);
|
||||
loadedModules = new CopyOnWriteArraySet<>();
|
||||
@ -71,6 +73,18 @@ final class RootScope extends Scope {
|
||||
}
|
||||
|
||||
|
||||
public ClassDeclaration getClassDeclaration(String name) {
|
||||
return classDeclarations.get(name);
|
||||
}
|
||||
|
||||
public void setClassDeclaration(ClassDeclaration classDeclaration) {
|
||||
classDeclarations.put(classDeclaration.name(), classDeclaration);
|
||||
}
|
||||
|
||||
public Map<String, ClassDeclaration> getClassDeclarations() {
|
||||
return classDeclarations;
|
||||
}
|
||||
|
||||
public Set<String> getLoadedModules() {
|
||||
return loadedModules;
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ public final class ScopeHandler {
|
||||
return rootScope.getFunctions();
|
||||
}
|
||||
|
||||
public static Map<String, ClassDeclaration> classDeclarations() {
|
||||
return rootScope.getClassDeclarations();
|
||||
}
|
||||
|
||||
|
||||
static RootScope rootScope() {
|
||||
return rootScope;
|
||||
}
|
||||
@ -75,6 +80,15 @@ public final class ScopeHandler {
|
||||
}
|
||||
|
||||
|
||||
public static ClassDeclaration getClassDeclaration(String name) {
|
||||
return rootScope.getClassDeclaration(name);
|
||||
}
|
||||
|
||||
public static void setClassDeclaration(ClassDeclaration classDeclaration) {
|
||||
rootScope.setClassDeclaration(classDeclaration);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isVariableOrConstantExists(String name) {
|
||||
if (rootScope().containsConstant(name)) {
|
||||
return true;
|
||||
|
@ -1,31 +0,0 @@
|
||||
package com.annimon.ownlang.lib;
|
||||
|
||||
import com.annimon.ownlang.parser.ast.ClassDeclarationStatement;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public final class ClassDeclarations {
|
||||
|
||||
private static final Map<String, ClassDeclarationStatement> declarations;
|
||||
static {
|
||||
declarations = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
private ClassDeclarations() { }
|
||||
|
||||
public static void clear() {
|
||||
declarations.clear();
|
||||
}
|
||||
|
||||
public static Map<String, ClassDeclarationStatement> getAll() {
|
||||
return declarations;
|
||||
}
|
||||
|
||||
public static ClassDeclarationStatement get(String key) {
|
||||
return declarations.get(key);
|
||||
}
|
||||
|
||||
public static void set(String key, ClassDeclarationStatement classDef) {
|
||||
declarations.put(key, classDef);
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package com.annimon.ownlang.lib;
|
||||
|
||||
import com.annimon.ownlang.parser.ast.Arguments;
|
||||
import com.annimon.ownlang.parser.ast.Statement;
|
||||
import com.annimon.ownlang.util.Range;
|
||||
|
||||
public class ClassMethod extends UserDefinedFunction {
|
||||
|
||||
public final ClassInstanceValue classInstance;
|
||||
|
||||
public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue classInstance, Range range) {
|
||||
super(arguments, body, range);
|
||||
this.classInstance = classInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value execute(Value[] values) {
|
||||
ScopeHandler.push();
|
||||
ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap());
|
||||
|
||||
try {
|
||||
return super.execute(values);
|
||||
} finally {
|
||||
ScopeHandler.pop();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package com.annimon.ownlang.lib;
|
||||
|
||||
import com.annimon.ownlang.exceptions.UnknownClassException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Classes {
|
||||
|
||||
private static final Map<String, ClassInstanceValue> classes;
|
||||
static {
|
||||
classes = new HashMap<>();
|
||||
}
|
||||
|
||||
private Classes() { }
|
||||
|
||||
public static void clear() {
|
||||
classes.clear();
|
||||
}
|
||||
|
||||
public static Map<String, ClassInstanceValue> getFunctions() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
public static boolean isExists(String key) {
|
||||
return classes.containsKey(key);
|
||||
}
|
||||
|
||||
public static ClassInstanceValue get(String key) {
|
||||
if (!isExists(key)) throw new UnknownClassException(key);
|
||||
return classes.get(key);
|
||||
}
|
||||
|
||||
public static void set(String key, ClassInstanceValue classDef) {
|
||||
classes.put(key, classDef);
|
||||
}
|
||||
}
|
@ -815,7 +815,8 @@ public final class Parser {
|
||||
final var startTokenIndex = index - 1;
|
||||
final Arguments arguments = arguments();
|
||||
final Statement statement = statementBody();
|
||||
return new ValueExpression(new UserDefinedFunction(arguments, statement, getRange(startTokenIndex, index - 1)));
|
||||
final Range range = getRange(startTokenIndex, index - 1);
|
||||
return new ValueExpression(new UserDefinedFunction(arguments, statement, range));
|
||||
}
|
||||
return variable();
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
package com.annimon.ownlang.parser.ast;
|
||||
|
||||
import com.annimon.ownlang.lib.EvaluableValue;
|
||||
import com.annimon.ownlang.lib.Value;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public final class AssignmentExpression extends InterruptableNode implements Statement {
|
||||
public final class AssignmentExpression extends InterruptableNode implements Statement, EvaluableValue {
|
||||
|
||||
public final Accessible target;
|
||||
public final BinaryExpression.Operator operation;
|
||||
|
@ -1,8 +1,6 @@
|
||||
package com.annimon.ownlang.parser.ast;
|
||||
|
||||
import com.annimon.ownlang.lib.ClassDeclarations;
|
||||
import com.annimon.ownlang.lib.NumberValue;
|
||||
import com.annimon.ownlang.lib.Value;
|
||||
import com.annimon.ownlang.lib.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -28,9 +26,27 @@ public final class ClassDeclarationStatement implements Statement {
|
||||
|
||||
@Override
|
||||
public Value eval() {
|
||||
ClassDeclarations.set(name, this);
|
||||
final var classFields = fields.stream()
|
||||
.map(this::toClassField)
|
||||
.toList();
|
||||
final var classMethods = methods.stream()
|
||||
.map(this::toClassMethod)
|
||||
.toList();
|
||||
final var declaration = new ClassDeclaration(name, classFields, classMethods);
|
||||
ScopeHandler.setClassDeclaration(declaration);
|
||||
return NumberValue.ZERO;
|
||||
}
|
||||
|
||||
private ClassField toClassField(AssignmentExpression f) {
|
||||
// TODO check only variable assignments
|
||||
final String fieldName = ((VariableExpression) f.target).name;
|
||||
return new ClassField(fieldName, f);
|
||||
}
|
||||
|
||||
private ClassMethod toClassMethod(FunctionDefineStatement m) {
|
||||
final var function = new UserDefinedFunction(m.arguments, m.body, m.getRange());
|
||||
return new ClassMethod(m.name, function);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
|
@ -50,7 +50,7 @@ public final class ContainerAccessExpression implements Node, Accessible {
|
||||
case Types.ARRAY -> ((ArrayValue) container).get(lastIndex);
|
||||
case Types.MAP -> ((MapValue) container).get(lastIndex);
|
||||
case Types.STRING -> ((StringValue) container).access(lastIndex);
|
||||
case Types.CLASS -> ((ClassInstanceValue) container).access(lastIndex);
|
||||
case Types.CLASS -> ((ClassInstance) container).access(lastIndex);
|
||||
default -> throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type()));
|
||||
};
|
||||
}
|
||||
@ -62,7 +62,7 @@ public final class ContainerAccessExpression implements Node, Accessible {
|
||||
switch (container.type()) {
|
||||
case Types.ARRAY -> ((ArrayValue) container).set(lastIndex.asInt(), value);
|
||||
case Types.MAP -> ((MapValue) container).set(lastIndex, value);
|
||||
case Types.CLASS -> ((ClassInstanceValue) container).set(lastIndex, value);
|
||||
case Types.CLASS -> ((ClassInstance) container).set(lastIndex, value);
|
||||
default -> throw new TypeException("Array or map expected. Got " + container.type());
|
||||
}
|
||||
return value;
|
||||
|
@ -48,8 +48,8 @@ public final class FunctionDefineStatement implements Statement, SourceLocation
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (body instanceof ReturnStatement) {
|
||||
return String.format("def %s%s = %s", name, arguments, ((ReturnStatement)body).expression);
|
||||
if (body instanceof ReturnStatement rs) {
|
||||
return String.format("def %s%s = %s", name, arguments, rs.expression);
|
||||
}
|
||||
return String.format("def %s%s %s", name, arguments, body);
|
||||
}
|
||||
|
@ -29,41 +29,29 @@ public final class ObjectCreationExpression implements Node, SourceLocation {
|
||||
|
||||
@Override
|
||||
public Value eval() {
|
||||
final ClassDeclarationStatement cd = ClassDeclarations.get(className);
|
||||
if (cd == null) {
|
||||
// Is Instantiable?
|
||||
if (ScopeHandler.isVariableOrConstantExists(className)) {
|
||||
final Value variable = ScopeHandler.getVariableOrConstant(className);
|
||||
if (variable instanceof Instantiable instantiable) {
|
||||
return instantiable.newInstance(ctorArgs());
|
||||
}
|
||||
final ClassDeclaration cd = ScopeHandler.getClassDeclaration(className);
|
||||
if (cd != null) {
|
||||
return cd.newInstance(constructorArgs());
|
||||
}
|
||||
|
||||
// Is Instantiable?
|
||||
if (ScopeHandler.isVariableOrConstantExists(className)) {
|
||||
final Value variable = ScopeHandler.getVariableOrConstant(className);
|
||||
if (variable instanceof Instantiable instantiable) {
|
||||
return instantiable.newInstance(constructorArgs());
|
||||
}
|
||||
throw new UnknownClassException(className, range);
|
||||
}
|
||||
|
||||
// Create an instance and put evaluated fields with method declarations
|
||||
final ClassInstanceValue instance = new ClassInstanceValue(className);
|
||||
for (AssignmentExpression f : cd.fields) {
|
||||
// TODO check only variable assignments
|
||||
final String fieldName = ((VariableExpression) f.target).name;
|
||||
instance.addField(fieldName, f.eval());
|
||||
}
|
||||
for (FunctionDefineStatement m : cd.methods) {
|
||||
instance.addMethod(m.name, new ClassMethod(m.arguments, m.body, instance, m.getRange()));
|
||||
}
|
||||
|
||||
// Call a constructor
|
||||
instance.callConstructor(ctorArgs());
|
||||
return instance;
|
||||
throw new UnknownClassException(className, range);
|
||||
}
|
||||
|
||||
private Value[] ctorArgs() {
|
||||
|
||||
private Value[] constructorArgs() {
|
||||
final int argsSize = constructorArguments.size();
|
||||
final Value[] ctorArgs = new Value[argsSize];
|
||||
for (int i = 0; i < argsSize; i++) {
|
||||
ctorArgs[i] = constructorArguments.get(i).eval();
|
||||
final Value[] args = new Value[argsSize];
|
||||
int i = 0;
|
||||
for (Node argument : constructorArguments) {
|
||||
args[i++] = argument.eval();
|
||||
}
|
||||
return ctorArgs;
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,6 +75,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
||||
|
||||
@Override
|
||||
public Node visit(ClassDeclarationStatement s, T t) {
|
||||
// TODO fields and methods
|
||||
return s;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user