gravlax
Advanced tools
Comparing version 0.11.1 to 0.12.0
@@ -34,2 +34,3 @@ import { | ||
set: (expr) => parenthesize("set", expr.object, expr.name.lexeme, expr.value), | ||
super: () => "super", | ||
this: () => parenthesize("this"), | ||
@@ -36,0 +37,0 @@ unary: (expr) => parenthesize(expr.operator.lexeme, expr.right), |
import { RuntimeError } from "./interpreter.js"; | ||
class Environment { | ||
#enclosing; | ||
#values; | ||
enclosing; | ||
constructor(enclosing) { | ||
this.#values = /* @__PURE__ */ new Map(); | ||
this.#enclosing = enclosing; | ||
this.enclosing = enclosing; | ||
} | ||
@@ -12,3 +12,3 @@ ancestor(distance) { | ||
for (let i = 0; i < distance; i++) { | ||
const enclosing = env.#enclosing; | ||
const enclosing = env.enclosing; | ||
if (!enclosing) { | ||
@@ -15,0 +15,0 @@ throw new Error("Tried to go past last ancestor!"); |
@@ -130,3 +130,17 @@ import { LoxCallable } from "./callable.js"; | ||
class(stmt) { | ||
let superclass = null; | ||
if (stmt.superclass) { | ||
superclass = this.evaluate(stmt.superclass); | ||
if (!(superclass instanceof LoxClass)) { | ||
throw new RuntimeError( | ||
stmt.superclass.name, | ||
"Superclass must be a class." | ||
); | ||
} | ||
} | ||
this.#environment.define(stmt.name.lexeme, null); | ||
if (superclass) { | ||
this.#environment = new Environment(this.#environment); | ||
this.#environment.define("super", superclass); | ||
} | ||
const methods = /* @__PURE__ */ new Map(); | ||
@@ -141,3 +155,6 @@ for (const method of stmt.methods) { | ||
} | ||
const klass = new LoxClass(stmt.name.lexeme, methods); | ||
const klass = new LoxClass(stmt.name.lexeme, superclass, methods); | ||
if (superclass) { | ||
this.#environment = this.#environment.enclosing; | ||
} | ||
this.#environment.assign(stmt.name, klass); | ||
@@ -192,2 +209,21 @@ } | ||
} | ||
case "super": { | ||
const distance2 = this.#locals.get(expr); | ||
const superclass = this.#environment.getAt( | ||
distance2, | ||
"super" | ||
); | ||
const object = this.#environment.getAt( | ||
distance2 - 1, | ||
"this" | ||
); | ||
const method = superclass.findMethod(expr.method.lexeme); | ||
if (!method) { | ||
throw new RuntimeError( | ||
expr.method, | ||
`Undefined property ${expr.method.lexeme}.` | ||
); | ||
} | ||
return method.bindThis(object); | ||
} | ||
case "unary": | ||
@@ -194,0 +230,0 @@ const right = this.evaluate(expr.right); |
@@ -6,6 +6,8 @@ import { LoxCallable } from "./callable.js"; | ||
name; | ||
constructor(name, methods) { | ||
superclass; | ||
constructor(name, superclass, methods) { | ||
super(); | ||
this.name = name; | ||
this.#methods = methods; | ||
this.superclass = superclass; | ||
} | ||
@@ -25,3 +27,4 @@ arity() { | ||
findMethod(name) { | ||
return this.#methods.get(name); | ||
const meth = this.#methods.get(name); | ||
return meth ?? this.superclass?.findMethod(name); | ||
} | ||
@@ -28,0 +31,0 @@ toString() { |
@@ -77,2 +77,7 @@ import { errorOnToken } from "./main.js"; | ||
const name = consume("identifier", "Expect class name."); | ||
let superclass = null; | ||
if (match("<")) { | ||
consume("identifier", "Expect superclass name."); | ||
superclass = { kind: "var-expr", name: previous() }; | ||
} | ||
consume("{", "Expect '{' before class body."); | ||
@@ -84,3 +89,3 @@ const methods = []; | ||
consume("}", "Expect '}' after class body."); | ||
return { kind: "class", methods, name }; | ||
return { kind: "class", methods, name, superclass }; | ||
}; | ||
@@ -288,2 +293,7 @@ const func = (kind) => { | ||
return { kind: "var-expr", name: previous() }; | ||
} else if (match("super")) { | ||
const keyword = previous(); | ||
consume(".", "Expect '.' after 'super'."); | ||
const method = consume("identifier", "Expect superclass method name."); | ||
return { keyword, kind: "super", method }; | ||
} | ||
@@ -290,0 +300,0 @@ throw error(peek(), "Expect expression."); |
@@ -79,2 +79,16 @@ import { | ||
define(stmt.name); | ||
if (stmt.superclass && stmt.name.lexeme === stmt.superclass.name.lexeme) { | ||
errorOnToken( | ||
stmt.superclass.name, | ||
"A class can't inherit from itself." | ||
); | ||
} | ||
if (stmt.superclass) { | ||
currentClass = "subclass"; | ||
resolveExpr(stmt.superclass); | ||
} | ||
if (stmt.superclass) { | ||
beginScope(); | ||
scopes.at(-1).set("super", true); | ||
} | ||
beginScope(); | ||
@@ -89,2 +103,5 @@ scopes.at(-1)?.set("this", true); | ||
endScope(); | ||
if (stmt.superclass) { | ||
endScope(); | ||
} | ||
currentClass = encClass; | ||
@@ -140,2 +157,13 @@ }, | ||
}, | ||
super(expr) { | ||
if (currentClass === "none") { | ||
errorOnToken(expr.keyword, "Can't use 'super' outside of a class."); | ||
} else if (currentClass === "class") { | ||
errorOnToken( | ||
expr.keyword, | ||
"Can't use 'super' in a class with no superclass." | ||
); | ||
} | ||
resolveLocal(expr, expr.keyword); | ||
}, | ||
this(expr) { | ||
@@ -142,0 +170,0 @@ if (currentClass === "none") { |
{ | ||
"name": "gravlax", | ||
"version": "0.11.1", | ||
"version": "0.12.0", | ||
"description": "A Lox interpreter with tasty TypeScript seasoning", | ||
@@ -87,3 +87,3 @@ "repository": { | ||
}, | ||
"packageManager": "pnpm@8.14.3", | ||
"packageManager": "pnpm@8.15.1", | ||
"engines": { | ||
@@ -90,0 +90,0 @@ "node": ">=20" |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
141541
1557