Skip to content

Commit 6774dc1

Browse files
committed
Добавлена поддержка классов
1 parent 0bd2aa1 commit 6774dc1

20 files changed

+491
-19
lines changed

examples/basics/classes.own

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use ["std"]
2+
3+
class Point {
4+
def Point(x = 0, y = 0) {
5+
this.x = x
6+
this.y = y
7+
}
8+
9+
def moveBy(p) {
10+
this.move(p.x, p.y)
11+
}
12+
13+
def move(dx, dy) {
14+
this.x += dx
15+
this.y += dy
16+
}
17+
18+
def toString() = "(" + this.x + ", " + this.y + ")"
19+
}
20+
21+
p = new Point(20, 30)
22+
p.move(10, -5)
23+
println p.toString()
24+
25+
p2 = new Point(1, 1)
26+
p2.moveBy(p)
27+
println p2.toString()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.annimon.ownlang.exceptions;
2+
3+
public final class UnknownClassException extends RuntimeException {
4+
5+
private final String className;
6+
7+
public UnknownClassException(String name) {
8+
super("Unknown class " + name);
9+
this.className = name;
10+
}
11+
12+
public String getClassName() {
13+
return className;
14+
}
15+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.annimon.ownlang.lib;
2+
3+
import com.annimon.ownlang.exceptions.UnknownFunctionException;
4+
import com.annimon.ownlang.parser.ast.ClassDeclarationStatement;
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
8+
public final class ClassDeclarations {
9+
10+
private static final Map<String, ClassDeclarationStatement> declarations;
11+
static {
12+
declarations = new HashMap<>();
13+
}
14+
15+
private ClassDeclarations() { }
16+
17+
public static void clear() {
18+
declarations.clear();
19+
}
20+
21+
public static Map<String, ClassDeclarationStatement> getAll() {
22+
return declarations;
23+
}
24+
25+
public static boolean isExists(String key) {
26+
return declarations.containsKey(key);
27+
}
28+
29+
public static ClassDeclarationStatement get(String key) {
30+
if (!isExists(key)) throw new UnknownFunctionException(key);
31+
return declarations.get(key);
32+
}
33+
34+
public static void set(String key, ClassDeclarationStatement classDef) {
35+
declarations.put(key, classDef);
36+
}
37+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package com.annimon.ownlang.lib;
2+
3+
import com.annimon.ownlang.exceptions.TypeException;
4+
import java.util.Objects;
5+
6+
public class ClassInstanceValue implements Value {
7+
8+
private final String className;
9+
private final MapValue thisMap;
10+
private ClassMethod constructor;
11+
12+
public ClassInstanceValue(String name) {
13+
this.className = name;
14+
thisMap = new MapValue(10);
15+
}
16+
17+
public MapValue getThisMap() {
18+
return thisMap;
19+
}
20+
21+
public String getClassName() {
22+
return className;
23+
}
24+
25+
public void addField(String name, Value value) {
26+
thisMap.set(name, value);
27+
}
28+
29+
public void addMethod(String name, ClassMethod method) {
30+
thisMap.set(name, method);
31+
if (name.equals(className)) {
32+
constructor = method;
33+
}
34+
}
35+
36+
public void callConstructor(Value[] args) {
37+
if (constructor != null) {
38+
constructor.execute(args);
39+
}
40+
}
41+
42+
public Value access(Value value) {
43+
return thisMap.get(value);
44+
}
45+
46+
@Override
47+
public Object raw() {
48+
return null;
49+
}
50+
51+
@Override
52+
public int asInt() {
53+
throw new TypeException("Cannot cast class to integer");
54+
}
55+
56+
@Override
57+
public double asNumber() {
58+
throw new TypeException("Cannot cast class to integer");
59+
}
60+
61+
@Override
62+
public String asString() {
63+
return "class " + className + "@" + thisMap;
64+
}
65+
66+
@Override
67+
public int type() {
68+
return Types.CLASS;
69+
}
70+
71+
@Override
72+
public int hashCode() {
73+
int hash = 5;
74+
hash = 37 * hash + Objects.hash(className, thisMap);
75+
return hash;
76+
}
77+
78+
@Override
79+
public boolean equals(Object obj) {
80+
if (this == obj) return true;
81+
if (obj == null) return false;
82+
if (getClass() != obj.getClass())
83+
return false;
84+
final ClassInstanceValue other = (ClassInstanceValue) obj;
85+
return Objects.equals(this.className, other.className)
86+
&& Objects.equals(this.thisMap, other.thisMap);
87+
}
88+
89+
@Override
90+
public int compareTo(Value o) {
91+
return asString().compareTo(o.asString());
92+
}
93+
94+
@Override
95+
public String toString() {
96+
return asString();
97+
}
98+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.annimon.ownlang.lib;
2+
3+
import com.annimon.ownlang.parser.ast.Arguments;
4+
import com.annimon.ownlang.parser.ast.Statement;
5+
6+
public class ClassMethod extends UserDefinedFunction {
7+
8+
public final ClassInstanceValue classInstance;
9+
10+
public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue classInstance) {
11+
super(arguments, body);
12+
this.classInstance = classInstance;
13+
}
14+
15+
@Override
16+
public Value execute(Value[] values) {
17+
Variables.push();
18+
Variables.define("this", classInstance.getThisMap());
19+
20+
try {
21+
return super.execute(values);
22+
} finally {
23+
Variables.pop();
24+
}
25+
}
26+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.annimon.ownlang.lib;
2+
3+
import com.annimon.ownlang.exceptions.UnknownClassException;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
7+
public final class Classes {
8+
9+
private static final Map<String, ClassInstanceValue> classes;
10+
static {
11+
classes = new HashMap<>();
12+
}
13+
14+
private Classes() { }
15+
16+
public static void clear() {
17+
classes.clear();
18+
}
19+
20+
public static Map<String, ClassInstanceValue> getFunctions() {
21+
return classes;
22+
}
23+
24+
public static boolean isExists(String key) {
25+
return classes.containsKey(key);
26+
}
27+
28+
public static ClassInstanceValue get(String key) {
29+
if (!isExists(key)) throw new UnknownClassException(key);
30+
return classes.get(key);
31+
}
32+
33+
public static void set(String key, ClassInstanceValue classDef) {
34+
classes.put(key, classDef);
35+
}
36+
}

src/main/java/com/annimon/ownlang/lib/Types.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ public final class Types {
88
STRING = 2,
99
ARRAY = 3,
1010
MAP = 4,
11-
FUNCTION = 5;
11+
FUNCTION = 5,
12+
CLASS = 6;
1213

1314
private static final int FIRST = OBJECT;
14-
private static final int LAST = FUNCTION;
15-
private static final String[] NAMES = {"object", "number", "string", "array", "map", "function"};
15+
private static final int LAST = CLASS;
16+
private static final String[] NAMES = {
17+
"object", "number", "string", "array", "map", "function", "class"
18+
};
1619

1720
public static String typeToString(int type) {
1821
if (FIRST <= type && type <= LAST) {

src/main/java/com/annimon/ownlang/lib/UserDefinedFunction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
* @author aNNiMON
1212
*/
13-
public final class UserDefinedFunction implements Function {
13+
public class UserDefinedFunction implements Function {
1414

1515
public final Arguments arguments;
1616
public final Statement body;

src/main/java/com/annimon/ownlang/parser/Lexer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ public static List<Token> tokenize(String input) {
105105
KEYWORDS.put("case", TokenType.CASE);
106106
KEYWORDS.put("extract", TokenType.EXTRACT);
107107
KEYWORDS.put("include", TokenType.INCLUDE);
108+
KEYWORDS.put("class", TokenType.CLASS);
109+
KEYWORDS.put("new", TokenType.NEW);
108110
}
109111

110112
public static Set<String> getKeywords() {

src/main/java/com/annimon/ownlang/parser/Parser.java

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ private Statement statement() {
159159
if (match(TokenType.MATCH)) {
160160
return match();
161161
}
162+
if (match(TokenType.CLASS)) {
163+
return classDeclaration();
164+
}
162165
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
163166
return new ExprStatement(functionChain(qualifiedName()));
164167
}
@@ -437,6 +440,30 @@ private MatchExpression match() {
437440

438441
return new MatchExpression(expression, patterns);
439442
}
443+
444+
private Statement classDeclaration() {
445+
// class Name {
446+
// x = 123
447+
// str = ""
448+
// def method() = str
449+
// }
450+
final String name = consume(TokenType.WORD).getText();
451+
final ClassDeclarationStatement classDeclaration = new ClassDeclarationStatement(name);
452+
consume(TokenType.LBRACE);
453+
do {
454+
if (match(TokenType.DEF)) {
455+
classDeclaration.addMethod(functionDefine());
456+
} else {
457+
final AssignmentExpression fieldDeclaration = assignmentStrict();
458+
if (fieldDeclaration != null) {
459+
classDeclaration.addField(fieldDeclaration);
460+
} else {
461+
throw new ParseException("Class can contain only assignments and function declarations");
462+
}
463+
}
464+
} while (!match(TokenType.RBRACE));
465+
return classDeclaration;
466+
}
440467

441468
private Expression expression() {
442469
return assignment();
@@ -450,7 +477,7 @@ private Expression assignment() {
450477
return ternary();
451478
}
452479

453-
private Expression assignmentStrict() {
480+
private AssignmentExpression assignmentStrict() {
454481
// x[0].prop += ...
455482
final int position = pos;
456483
final Expression targetExpr = qualifiedName();
@@ -667,30 +694,45 @@ private Expression additive() {
667694
}
668695

669696
private Expression multiplicative() {
670-
Expression result = unary();
697+
Expression result = objectCreation();
671698

672699
while (true) {
673700
if (match(TokenType.STAR)) {
674-
result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, unary());
701+
result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, expression());
675702
continue;
676703
}
677704
if (match(TokenType.SLASH)) {
678-
result = new BinaryExpression(BinaryExpression.Operator.DIVIDE, result, unary());
705+
result = new BinaryExpression(BinaryExpression.Operator.DIVIDE, result, expression());
679706
continue;
680707
}
681708
if (match(TokenType.PERCENT)) {
682-
result = new BinaryExpression(BinaryExpression.Operator.REMAINDER, result, unary());
709+
result = new BinaryExpression(BinaryExpression.Operator.REMAINDER, result, expression());
683710
continue;
684711
}
685712
if (match(TokenType.STARSTAR)) {
686-
result = new BinaryExpression(BinaryExpression.Operator.POWER, result, unary());
713+
result = new BinaryExpression(BinaryExpression.Operator.POWER, result, expression());
687714
continue;
688715
}
689716
break;
690717
}
691718

692719
return result;
693720
}
721+
722+
private Expression objectCreation() {
723+
if (match(TokenType.NEW)) {
724+
final String className = consume(TokenType.WORD).getText();
725+
final List<Expression> args = new ArrayList<>();
726+
consume(TokenType.LPAREN);
727+
while (!match(TokenType.RPAREN)) {
728+
args.add(expression());
729+
match(TokenType.COMMA);
730+
}
731+
return new ObjectCreationExpression(className, args);
732+
}
733+
734+
return unary();
735+
}
694736

695737
private Expression unary() {
696738
if (match(TokenType.PLUSPLUS)) {

0 commit comments

Comments
 (0)