Добавлена поддержка классов

This commit is contained in:
Victor 2019-08-07 13:35:13 +03:00
parent 0bd2aa10d5
commit 6774dc1004
20 changed files with 491 additions and 19 deletions

View File

@ -0,0 +1,27 @@
use ["std"]
class Point {
def Point(x = 0, y = 0) {
this.x = x
this.y = y
}
def moveBy(p) {
this.move(p.x, p.y)
}
def move(dx, dy) {
this.x += dx
this.y += dy
}
def toString() = "(" + this.x + ", " + this.y + ")"
}
p = new Point(20, 30)
p.move(10, -5)
println p.toString()
p2 = new Point(1, 1)
p2.moveBy(p)
println p2.toString()

View File

@ -0,0 +1,15 @@
package com.annimon.ownlang.exceptions;
public final class UnknownClassException extends RuntimeException {
private final String className;
public UnknownClassException(String name) {
super("Unknown class " + name);
this.className = name;
}
public String getClassName() {
return className;
}
}

View File

@ -0,0 +1,37 @@
package com.annimon.ownlang.lib;
import com.annimon.ownlang.exceptions.UnknownFunctionException;
import com.annimon.ownlang.parser.ast.ClassDeclarationStatement;
import java.util.HashMap;
import java.util.Map;
public final class ClassDeclarations {
private static final Map<String, ClassDeclarationStatement> declarations;
static {
declarations = new HashMap<>();
}
private ClassDeclarations() { }
public static void clear() {
declarations.clear();
}
public static Map<String, ClassDeclarationStatement> getAll() {
return declarations;
}
public static boolean isExists(String key) {
return declarations.containsKey(key);
}
public static ClassDeclarationStatement get(String key) {
if (!isExists(key)) throw new UnknownFunctionException(key);
return declarations.get(key);
}
public static void set(String key, ClassDeclarationStatement classDef) {
declarations.put(key, classDef);
}
}

View File

@ -0,0 +1,98 @@
package com.annimon.ownlang.lib;
import com.annimon.ownlang.exceptions.TypeException;
import java.util.Objects;
public class ClassInstanceValue implements Value {
private final String className;
private final MapValue thisMap;
private ClassMethod constructor;
public ClassInstanceValue(String name) {
this.className = name;
thisMap = new MapValue(10);
}
public MapValue getThisMap() {
return thisMap;
}
public String getClassName() {
return className;
}
public void addField(String name, Value value) {
thisMap.set(name, value);
}
public void addMethod(String name, ClassMethod method) {
thisMap.set(name, method);
if (name.equals(className)) {
constructor = method;
}
}
public void callConstructor(Value[] args) {
if (constructor != null) {
constructor.execute(args);
}
}
public Value access(Value value) {
return thisMap.get(value);
}
@Override
public Object raw() {
return null;
}
@Override
public int asInt() {
throw new TypeException("Cannot cast class to integer");
}
@Override
public double asNumber() {
throw new TypeException("Cannot cast class to integer");
}
@Override
public String asString() {
return "class " + className + "@" + thisMap;
}
@Override
public int type() {
return Types.CLASS;
}
@Override
public int hashCode() {
int hash = 5;
hash = 37 * hash + Objects.hash(className, thisMap);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass())
return false;
final ClassInstanceValue other = (ClassInstanceValue) obj;
return Objects.equals(this.className, other.className)
&& Objects.equals(this.thisMap, other.thisMap);
}
@Override
public int compareTo(Value o) {
return asString().compareTo(o.asString());
}
@Override
public String toString() {
return asString();
}
}

View File

@ -0,0 +1,26 @@
package com.annimon.ownlang.lib;
import com.annimon.ownlang.parser.ast.Arguments;
import com.annimon.ownlang.parser.ast.Statement;
public class ClassMethod extends UserDefinedFunction {
public final ClassInstanceValue classInstance;
public ClassMethod(Arguments arguments, Statement body, ClassInstanceValue classInstance) {
super(arguments, body);
this.classInstance = classInstance;
}
@Override
public Value execute(Value[] values) {
Variables.push();
Variables.define("this", classInstance.getThisMap());
try {
return super.execute(values);
} finally {
Variables.pop();
}
}
}

View File

@ -0,0 +1,36 @@
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);
}
}

View File

@ -8,11 +8,14 @@ public final class Types {
STRING = 2,
ARRAY = 3,
MAP = 4,
FUNCTION = 5;
FUNCTION = 5,
CLASS = 6;
private static final int FIRST = OBJECT;
private static final int LAST = FUNCTION;
private static final String[] NAMES = {"object", "number", "string", "array", "map", "function"};
private static final int LAST = CLASS;
private static final String[] NAMES = {
"object", "number", "string", "array", "map", "function", "class"
};
public static String typeToString(int type) {
if (FIRST <= type && type <= LAST) {

View File

@ -10,7 +10,7 @@ import com.annimon.ownlang.parser.ast.Statement;
*
* @author aNNiMON
*/
public final class UserDefinedFunction implements Function {
public class UserDefinedFunction implements Function {
public final Arguments arguments;
public final Statement body;

View File

@ -105,6 +105,8 @@ public final class Lexer {
KEYWORDS.put("case", TokenType.CASE);
KEYWORDS.put("extract", TokenType.EXTRACT);
KEYWORDS.put("include", TokenType.INCLUDE);
KEYWORDS.put("class", TokenType.CLASS);
KEYWORDS.put("new", TokenType.NEW);
}
public static Set<String> getKeywords() {

View File

@ -159,6 +159,9 @@ public final class Parser {
if (match(TokenType.MATCH)) {
return match();
}
if (match(TokenType.CLASS)) {
return classDeclaration();
}
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
return new ExprStatement(functionChain(qualifiedName()));
}
@ -438,6 +441,30 @@ public final class Parser {
return new MatchExpression(expression, patterns);
}
private Statement classDeclaration() {
// class Name {
// x = 123
// str = ""
// def method() = str
// }
final String name = consume(TokenType.WORD).getText();
final ClassDeclarationStatement classDeclaration = new ClassDeclarationStatement(name);
consume(TokenType.LBRACE);
do {
if (match(TokenType.DEF)) {
classDeclaration.addMethod(functionDefine());
} else {
final AssignmentExpression fieldDeclaration = assignmentStrict();
if (fieldDeclaration != null) {
classDeclaration.addField(fieldDeclaration);
} else {
throw new ParseException("Class can contain only assignments and function declarations");
}
}
} while (!match(TokenType.RBRACE));
return classDeclaration;
}
private Expression expression() {
return assignment();
}
@ -450,7 +477,7 @@ public final class Parser {
return ternary();
}
private Expression assignmentStrict() {
private AssignmentExpression assignmentStrict() {
// x[0].prop += ...
final int position = pos;
final Expression targetExpr = qualifiedName();
@ -667,23 +694,23 @@ public final class Parser {
}
private Expression multiplicative() {
Expression result = unary();
Expression result = objectCreation();
while (true) {
if (match(TokenType.STAR)) {
result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, unary());
result = new BinaryExpression(BinaryExpression.Operator.MULTIPLY, result, expression());
continue;
}
if (match(TokenType.SLASH)) {
result = new BinaryExpression(BinaryExpression.Operator.DIVIDE, result, unary());
result = new BinaryExpression(BinaryExpression.Operator.DIVIDE, result, expression());
continue;
}
if (match(TokenType.PERCENT)) {
result = new BinaryExpression(BinaryExpression.Operator.REMAINDER, result, unary());
result = new BinaryExpression(BinaryExpression.Operator.REMAINDER, result, expression());
continue;
}
if (match(TokenType.STARSTAR)) {
result = new BinaryExpression(BinaryExpression.Operator.POWER, result, unary());
result = new BinaryExpression(BinaryExpression.Operator.POWER, result, expression());
continue;
}
break;
@ -692,6 +719,21 @@ public final class Parser {
return result;
}
private Expression objectCreation() {
if (match(TokenType.NEW)) {
final String className = consume(TokenType.WORD).getText();
final List<Expression> args = new ArrayList<>();
consume(TokenType.LPAREN);
while (!match(TokenType.RPAREN)) {
args.add(expression());
match(TokenType.COMMA);
}
return new ObjectCreationExpression(className, args);
}
return unary();
}
private Expression unary() {
if (match(TokenType.PLUSPLUS)) {
return new UnaryExpression(UnaryExpression.Operator.INCREMENT_PREFIX, primary());

View File

@ -28,6 +28,8 @@ public enum TokenType {
CASE,
EXTRACT,
INCLUDE,
CLASS,
NEW,
PLUS, // +
MINUS, // -

View File

@ -0,0 +1,46 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.ClassDeclarations;
import java.util.ArrayList;
import java.util.List;
public final class ClassDeclarationStatement implements Statement {
public final String name;
public final List<FunctionDefineStatement> methods;
public final List<AssignmentExpression> fields;
public ClassDeclarationStatement(String name) {
this.name = name;
methods = new ArrayList<>();
fields = new ArrayList<>();
}
public void addField(AssignmentExpression expr) {
fields.add(expr);
}
public void addMethod(FunctionDefineStatement statement) {
methods.add(statement);
}
@Override
public void execute() {
ClassDeclarations.set(name, this);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T t) {
return visitor.visit(this, t);
}
@Override
public String toString() {
return String.format("class %s {\n %s %s}", name, fields, methods);
}
}

View File

@ -51,6 +51,9 @@ public final class ContainerAccessExpression implements Expression, Accessible {
case Types.STRING:
return ((StringValue) container).access(lastIndex);
case Types.CLASS:
return ((ClassInstanceValue) container).access(lastIndex);
default:
throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type()));
}

View File

@ -0,0 +1,67 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.*;
import java.util.Iterator;
import java.util.List;
public final class ObjectCreationExpression implements Expression {
public final String className;
public final List<Expression> constructorArguments;
public ObjectCreationExpression(String className, List<Expression> constructorArguments) {
this.className = className;
this.constructorArguments = constructorArguments;
}
@Override
public Value eval() {
final ClassDeclarationStatement cd = ClassDeclarations.get(className);
// 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));
}
// Call a constructor
final int argsSize = constructorArguments.size();
final Value[] ctorArgs = new Value[argsSize];
for (int i = 0; i < argsSize; i++) {
ctorArgs[i] = constructorArguments.get(i).eval();
}
instance.callConstructor(ctorArgs);
return instance;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T t) {
return visitor.visit(this, t);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("new ").append(className).append(' ');
final Iterator<Expression> it = constructorArguments.iterator();
if (it.hasNext()) {
sb.append(it.next());
while (it.hasNext()) {
sb.append(", ").append(it.next());
}
}
sb.append(')');
return sb.toString();
}
}

View File

@ -13,6 +13,7 @@ public interface ResultVisitor<R, T> {
R visit(BinaryExpression s, T t);
R visit(BlockStatement s, T t);
R visit(BreakStatement s, T t);
R visit(ClassDeclarationStatement s, T t);
R visit(ConditionalExpression s, T t);
R visit(ContainerAccessExpression s, T t);
R visit(ContinueStatement s, T t);
@ -29,6 +30,7 @@ public interface ResultVisitor<R, T> {
R visit(IncludeStatement s, T t);
R visit(MapExpression s, T t);
R visit(MatchExpression s, T t);
R visit(ObjectCreationExpression s, T t);
R visit(PrintStatement s, T t);
R visit(PrintlnStatement s, T t);
R visit(ReturnStatement s, T t);

View File

@ -11,6 +11,7 @@ public interface Visitor {
void visit(BinaryExpression s);
void visit(BlockStatement s);
void visit(BreakStatement s);
void visit(ClassDeclarationStatement s);
void visit(ConditionalExpression s);
void visit(ContainerAccessExpression s);
void visit(ContinueStatement s);
@ -27,6 +28,7 @@ public interface Visitor {
void visit(IncludeStatement s);
void visit(MapExpression s);
void visit(MatchExpression s);
void visit(ObjectCreationExpression s);
void visit(PrintStatement s);
void visit(PrintlnStatement s);
void visit(ReturnStatement s);

View File

@ -75,6 +75,11 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
return s;
}
@Override
public Node visit(ClassDeclarationStatement s, T t) {
return s;
}
@Override
public Node visit(ConditionalExpression s, T t) {
final Node expr1 = s.expr1.accept(this, t);
@ -315,6 +320,24 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
return s;
}
@Override
public Node visit(ObjectCreationExpression s, T t) {
final List<Expression> args = new ArrayList<>();
boolean changed = false;
for (Expression argument : s.constructorArguments) {
final Node expr = argument.accept(this, t);
if (expr != argument) {
changed = true;
}
args.add((Expression) expr);
}
if (changed) {
return new ObjectCreationExpression(s.className, args);
}
return s;
}
@Override
public Node visit(PrintStatement s, T t) {
final Node expression = s.expression.accept(this, t);

View File

@ -39,6 +39,11 @@ public abstract class AbstractVisitor implements Visitor {
public void visit(BreakStatement s) {
}
@Override
public void visit(ClassDeclarationStatement s) {
}
@Override
public void visit(ConditionalExpression s) {
s.expr1.accept(this);
@ -137,6 +142,13 @@ public abstract class AbstractVisitor implements Visitor {
s.expression.accept(this);
}
@Override
public void visit(ObjectCreationExpression s) {
for (Expression argument : s.constructorArguments) {
argument.accept(this);
}
}
@Override
public void visit(PrintStatement s) {
s.expression.accept(this);

View File

@ -7,6 +7,7 @@ import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.UserDefinedFunction;
import com.annimon.ownlang.parser.ast.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder> {
@ -79,6 +80,23 @@ public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder>
return t;
}
@Override
public StringBuilder visit(ClassDeclarationStatement s, StringBuilder t) {
t.append("class ").append(s.name).append(" {");
newLine(t);
increaseIndent();
for (AssignmentExpression field : s.fields) {
field.accept(this, t);
}
for (FunctionDefineStatement method : s.methods) {
method.accept(this, t);
}
decreaseIndent();
t.append("}");
return t;
}
@Override
public StringBuilder visit(ConditionalExpression s, StringBuilder t) {
s.expr1.accept(this, t);
@ -209,14 +227,7 @@ public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder>
} else {
s.functionExpr.accept(this, t);
}
t.append("(");
boolean firstElement = true;
for (Expression expr : s.arguments) {
if (firstElement) firstElement = false;
else t.append(", ");
expr.accept(this, t);
}
t.append(")");
printArgs(t, s.arguments);
return t;
}
@ -291,6 +302,13 @@ public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder>
return t;
}
@Override
public StringBuilder visit(ObjectCreationExpression s, StringBuilder t) {
t.append("new ").append(s.className);
printArgs(t, s.constructorArguments);
return t;
}
@Override
public StringBuilder visit(PrintStatement s, StringBuilder t) {
t.append("print ");
@ -423,6 +441,17 @@ public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder>
return t;
}
private void printArgs(StringBuilder t, List<Expression> args) {
t.append("(");
boolean firstElement = true;
for (Expression expr : args) {
if (firstElement) firstElement = false;
else t.append(", ");
expr.accept(this, t);
}
t.append(")");
}
private void newLine(StringBuilder t) {
t.append(Console.newline());
}