commit 436ebdb358c0c907ac0253c9ad42a2b98244d73a
parent 4902b342c1d5a728ff5219f3d9bb0074c9f1135a
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Sat, 15 Oct 2022 20:02:48 +0200
Add class methods/fields
Diffstat:
6 files changed, 76 insertions(+), 31 deletions(-)
diff --git a/source/ast.d b/source/ast.d
@@ -13,7 +13,7 @@ static immutable string[][] expressions = [
["Logical", "Expr left", "TokenI operator", "Expr right"],
["Call", "Expr callee", "TokenI paren", "Expr[] arguments"],
["Function", "TokenI[] params", "Stmt[] body"],
- ["Class", "Var[] methods"],
+ ["Class", "Var[] methods", "Var[] classmethods"],
["Get", "Expr object", "TokenI name"],
["Set", "Expr object", "TokenI name", "Expr value"],
["This", "TokenI keyword"],
diff --git a/source/cls.d b/source/cls.d
@@ -6,11 +6,12 @@ import ast;
import fun;
import std.array;
-class Cls : Callable {
+class Cls : Instance, Callable {
private Var[] props;
private ulong _arity;
+ private Cls meta;
- this(Var[] props) {
+ this(Var[] props, Var[] classprops = null, Interpreter interpreter = null) {
this.props = props;
_arity = 0;
foreach(prop; props) {
@@ -20,30 +21,37 @@ class Cls : Callable {
}
}
}
+ if (classprops !is null) {
+ this.meta = new Cls(classprops);
+ super(meta.evalProps(interpreter));
+ super.construct([], interpreter);
+ } else {
+ this.meta = null;
+ super();
+ }
}
Variant call(Interpreter interpreter, Variant[] arguments) {
+ Variant[string] fields = evalProps(interpreter);
+ Instance instance = new Instance(fields);
+ instance.addFields(super.getFields());
+ instance.construct(arguments, interpreter);
+ return Variant(instance);
+ }
+
+ private Variant[string] evalProps(Interpreter interpreter) {
Variant[string] fields = null;
foreach(prop; props) {
fields[prop.name.lexeme] = interpreter.evaluate(prop.initializer);
}
- Instance instance = new Instance(this, fields);
- if (auto fun = "init" in fields) {
- if (fun.convertsTo!(Fun)) {
- auto ifun = fun.get!(Fun);
- ifun.setInitializer();
- ifun.bind(instance).call(interpreter, arguments);
- *fun = Variant(ifun);
- }
- }
- return Variant(instance);
+ return fields;
}
ulong arity() {
return _arity;
}
- void toString(scope void delegate(const(char)[]) sink) const {
+ override void toString(scope void delegate(const(char)[]) sink) const {
sink("<class>");
}
}
diff --git a/source/instance.d b/source/instance.d
@@ -1,28 +1,29 @@
-import cls;
import token;
import error;
import std.variant;
import fun;
+import interpreter;
+import std.range;
+import std.algorithm;
class Instance {
- private Cls cls;
private Variant[string] fields;
+ private Fun constructor;
- this(Cls cls, Variant[string] fields) {
- this.cls = cls;
+ this(Variant[string] fields = null) {
this.fields = fields;
+ this.constructor = null;
+ if (this.fields) bindMethods();
}
this(Instance inst) {
- this.cls = inst.cls;
this.fields = inst.fields.dup;
+ this.constructor = null;
+ if (this.fields) bindMethods();
}
Variant get(TokenI name) {
if (auto field = name.lexeme in fields) {
- if (field.convertsTo!(Fun)) {
- return Variant(field.get!(Fun).bind(this));
- }
return *field;
}
throw new RuntimeError(name,
@@ -36,4 +37,31 @@ class Instance {
void toString(scope void delegate(const(char)[]) sink) const {
sink("<class instance>");
}
+
+ void addFields(Variant[string] newf) {
+ foreach(name, value; newf.byPair) {
+ fields.require(name, value);
+ }
+ }
+
+ Variant[string] getFields() {
+ return fields;
+ }
+
+ private void bindMethods() {
+ foreach(name, field; fields.byPair) {
+ if (field.convertsTo!(Fun)) {
+ auto ifun = field.get!(Fun).bind(this);
+ if (name == "init") {
+ ifun.setInitializer();
+ constructor = ifun;
+ }
+ fields[name] = Variant(ifun);
+ }
+ }
+ }
+
+ void construct(Variant[] arguments, Interpreter interpreter) {
+ if(constructor) constructor.call(interpreter, arguments);
+ }
}
diff --git a/source/interpreter.d b/source/interpreter.d
@@ -144,7 +144,7 @@ class Interpreter : StmtVisitor, ExprVisitor {
}
void visit(Class expr) {
- value = new Cls(expr.methods);
+ value = new Cls(expr.methods, expr.classmethods, this);
}
void visit(Get expr) {
diff --git a/source/parser.d b/source/parser.d
@@ -24,7 +24,7 @@ returnStmt → "return" expression? ";" ;
funDecl → "fun" IDENTIFIER function ;
function → "(" parameters? ")" block ;
classDecl → "class" IDENTIFIER class ;
-class → "{" ( IDENTIFIER ( function | "=" expression ) )* "}" ;
+class → "{" ( "class"? IDENTIFIER ( function | "=" expression ) )* "}" ;
parameters → IDENTIFIER ( "," IDENTIFIER )* ;
forStmt → "for" "(" ( varDecl | exprStmt | ";" )
expression? ";"
@@ -119,12 +119,16 @@ class Parser {
consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");
Var[] fields;
- while (!isAtEnd() && match(TokenType.IDENTIFIER)) {
+ Var[] classfields;
+
+ while (!isAtEnd() && !check(TokenType.RIGHT_BRACE)) {
+ Var[]* lfields = match(TokenType.CLASS) ? &classfields : &fields;
+ consume(TokenType.IDENTIFIER, "Expect field name.");
TokenI name = previous();
if (check(TokenType.LEFT_PAREN)) {
- fields ~= statement!(Var)(name, fun());
+ *lfields ~= statement!(Var)(name, fun());
} else if (match(TokenType.EQUAL)) {
- fields ~= statement!(Var)(name, expression());
+ *lfields ~= statement!(Var)(name, expression());
} else {
Lox.error(name, "Expect field declaration");
}
@@ -132,7 +136,7 @@ class Parser {
consume(TokenType.RIGHT_BRACE, "Expect '}' after class.");
- return new Class(fields);
+ return new Class(fields, classfields);
}
private Stmt varDeclaration() {
diff --git a/source/resolver.d b/source/resolver.d
@@ -215,11 +215,16 @@ class Resolver : StmtVisitor, ExprVisitor {
ClassType enclosingClass = currentClass;
currentClass = ClassType.CLASS;
beginScope();
- foreach(method; cl.methods) {
+ foreach(i, method; chain(cl.methods, cl.classmethods).enumerate()) {
if (auto fun = cast(Function) method.initializer) {
scopes.front()["this"] = VarRef(VarState.REFERENCED, 0);
- FunctionType declaration = method.name.lexeme == "init" ?
- FunctionType.INITIALIZER : FunctionType.METHOD;
+ FunctionType declaration = FunctionType.METHOD;
+ if (method.name.lexeme == "init") {
+ declaration = FunctionType.INITIALIZER;
+ if (i >= cl.methods.length && fun.params.length > 0) {
+ Lox.error(fun.params[0], "class initializer cannot have arguments");
+ }
+ }
resolveFunction(fun, declaration);
} else {
resolve(method.initializer);