Skip to content

Commit ec04e5f

Browse files
committed
Move classes implementation outside of parser module
1 parent d9ae2c7 commit ec04e5f

18 files changed

+199
-161
lines changed

ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ public final class UnknownClassException extends OwnLangRuntimeException {
66

77
private final String className;
88

9-
public UnknownClassException(String name) {
10-
super("Unknown class " + name);
11-
this.className = name;
12-
}
13-
149
public UnknownClassException(String name, Range range) {
1510
super("Unknown class " + name, range);
1611
this.className = name;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.annimon.ownlang.lib;
2+
3+
import java.util.List;
4+
5+
public record ClassDeclaration(
6+
String name,
7+
List<ClassField> classFields,
8+
List<ClassMethod> classMethods) implements Instantiable {
9+
10+
/**
11+
* Create an instance and put evaluated fields with method declarations
12+
* @return new {@link ClassInstance}
13+
*/
14+
public ClassInstance newInstance(Value[] args) {
15+
final var instance = new ClassInstance(name);
16+
for (ClassField f : classFields) {
17+
instance.addField(f);
18+
}
19+
for (ClassMethod m : classMethods) {
20+
instance.addMethod(m);
21+
}
22+
return instance.callConstructor(args);
23+
}
24+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.annimon.ownlang.lib;
2+
3+
public record ClassField(
4+
String name,
5+
EvaluableValue evaluableValue
6+
) {
7+
}

ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassInstanceValue.java renamed to ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassInstance.java

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package com.annimon.ownlang.lib;
22

3+
import com.annimon.ownlang.exceptions.OwnLangRuntimeException;
34
import com.annimon.ownlang.exceptions.TypeException;
45
import java.util.Objects;
56

6-
public class ClassInstanceValue implements Value {
7-
7+
public class ClassInstance implements Value {
8+
89
private final String className;
910
private final MapValue thisMap;
1011
private ClassMethod constructor;
11-
private UserDefinedFunction toString;
12+
private ClassMethod toString;
13+
private boolean isInstantiated;
1214

13-
public ClassInstanceValue(String name) {
15+
public ClassInstance(String name) {
1416
this.className = name;
1517
thisMap = new MapValue(10);
1618
}
@@ -19,31 +21,33 @@ public MapValue getThisMap() {
1921
return thisMap;
2022
}
2123

22-
public String getClassName() {
23-
return className;
24-
}
25-
26-
public void addField(String name, Value value) {
27-
thisMap.set(name, value);
24+
public void addField(ClassField f) {
25+
thisMap.set(f.name(), f.evaluableValue().eval());
2826
}
2927

30-
public void addMethod(String name, ClassMethod method) {
31-
if (name.equals("toString")) {
32-
toString = method;
33-
}
28+
public void addMethod(ClassMethod method) {
29+
method.setClassInstance(this);
30+
final String name = method.getName();
3431
thisMap.set(name, method);
3532
if (name.equals(className)) {
3633
constructor = method;
34+
} else if (name.equals("toString")) {
35+
toString = method;
3736
}
3837
}
3938

40-
41-
public void callConstructor(Value[] args) {
39+
public ClassInstance callConstructor(Value[] args) {
40+
if (isInstantiated) {
41+
throw new OwnLangRuntimeException(
42+
"Class %s was already instantiated".formatted(className));
43+
}
4244
if (constructor != null) {
4345
CallStack.enter("class " + className, constructor, null);
4446
constructor.execute(args);
4547
CallStack.exit();
4648
}
49+
isInstantiated = true;
50+
return this;
4751
}
4852

4953
public Value access(Value value) {
@@ -53,15 +57,16 @@ public Value access(Value value) {
5357
public void set(Value key, Value value) {
5458
final Value v = thisMap.get(key);
5559
if (v == null) {
56-
throw new RuntimeException("Unable to add new field "
57-
+ key.asString() + " to class " + className);
60+
throw new OwnLangRuntimeException(
61+
"Unable to add new field %s to class %s"
62+
.formatted(key.asString(), className));
5863
}
5964
thisMap.set(key, value);
6065
}
6166

6267
@Override
6368
public Object raw() {
64-
return null;
69+
return thisMap;
6570
}
6671

6772
@Override
@@ -77,9 +82,9 @@ public double asNumber() {
7782
@Override
7883
public String asString() {
7984
if (toString != null) {
80-
return toString.execute(new Value[0]).asString();
85+
return toString.execute().asString();
8186
}
82-
return className + "@" + thisMap;
87+
return className + "@" + thisMap.asString();
8388
}
8489

8590
@Override
@@ -100,7 +105,7 @@ public boolean equals(Object obj) {
100105
if (obj == null) return false;
101106
if (getClass() != obj.getClass())
102107
return false;
103-
final ClassInstanceValue other = (ClassInstanceValue) obj;
108+
final ClassInstance other = (ClassInstance) obj;
104109
return Objects.equals(this.className, other.className)
105110
&& Objects.equals(this.thisMap, other.thisMap);
106111
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.annimon.ownlang.lib;
2+
3+
import java.util.Objects;
4+
5+
public class ClassMethod implements Function {
6+
private final String name;
7+
private final Function function;
8+
private ClassInstance classInstance;
9+
10+
public ClassMethod(String name, Function function) {
11+
this.name = name;
12+
this.function = function;
13+
}
14+
15+
public String getName() {
16+
return name;
17+
}
18+
19+
public void setClassInstance(ClassInstance classInstance) {
20+
this.classInstance = classInstance;
21+
}
22+
23+
@Override
24+
public Value execute(Value... args) {
25+
ScopeHandler.push();
26+
ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap());
27+
28+
try {
29+
return function.execute(args);
30+
} finally {
31+
ScopeHandler.pop();
32+
}
33+
}
34+
35+
@Override
36+
public int getArgsCount() {
37+
return function.getArgsCount();
38+
}
39+
40+
@Override
41+
public boolean equals(Object obj) {
42+
if (obj == this) return true;
43+
if (obj == null || obj.getClass() != this.getClass()) return false;
44+
var that = (ClassMethod) obj;
45+
return Objects.equals(this.name, that.name) &&
46+
Objects.equals(this.function, that.function);
47+
}
48+
49+
@Override
50+
public int hashCode() {
51+
return Objects.hash(name, function);
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return "ClassMethod[" +
57+
"name=" + name + ", " +
58+
"function=" + function + ']';
59+
}
60+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.annimon.ownlang.lib;
2+
3+
public interface EvaluableValue {
4+
5+
Value eval();
6+
}

ownlang-core/src/main/java/com/annimon/ownlang/lib/RootScope.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
final class RootScope extends Scope {
99
private final Map<String, Value> constants;
1010
private final Map<String, Function> functions;
11+
private final Map<String, ClassDeclaration> classDeclarations;
1112
private final Set<String> loadedModules;
1213

1314
RootScope() {
1415
functions = new ConcurrentHashMap<>();
1516
constants = new ConcurrentHashMap<>();
17+
classDeclarations = new ConcurrentHashMap<>();
1618
constants.put("true", NumberValue.ONE);
1719
constants.put("false", NumberValue.ZERO);
1820
loadedModules = new CopyOnWriteArraySet<>();
@@ -71,6 +73,18 @@ public Map<String, Function> getFunctions() {
7173
}
7274

7375

76+
public ClassDeclaration getClassDeclaration(String name) {
77+
return classDeclarations.get(name);
78+
}
79+
80+
public void setClassDeclaration(ClassDeclaration classDeclaration) {
81+
classDeclarations.put(classDeclaration.name(), classDeclaration);
82+
}
83+
84+
public Map<String, ClassDeclaration> getClassDeclarations() {
85+
return classDeclarations;
86+
}
87+
7488
public Set<String> getLoadedModules() {
7589
return loadedModules;
7690
}

ownlang-core/src/main/java/com/annimon/ownlang/lib/ScopeHandler.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public static Map<String, Function> functions() {
2828
return rootScope.getFunctions();
2929
}
3030

31+
public static Map<String, ClassDeclaration> classDeclarations() {
32+
return rootScope.getClassDeclarations();
33+
}
34+
35+
3136
static RootScope rootScope() {
3237
return rootScope;
3338
}
@@ -75,6 +80,15 @@ public static void setFunction(String name, Function function) {
7580
}
7681

7782

83+
public static ClassDeclaration getClassDeclaration(String name) {
84+
return rootScope.getClassDeclaration(name);
85+
}
86+
87+
public static void setClassDeclaration(ClassDeclaration classDeclaration) {
88+
rootScope.setClassDeclaration(classDeclaration);
89+
}
90+
91+
7892
public static boolean isVariableOrConstantExists(String name) {
7993
if (rootScope().containsConstant(name)) {
8094
return true;

ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassDeclarations.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

ownlang-parser/src/main/java/com/annimon/ownlang/lib/ClassMethod.java

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)