mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 19:16:35 +00:00
rename syms
This commit is contained in:
parent
b9c174d96a
commit
fa81c17c25
@ -1,38 +1,72 @@
|
|||||||
import { Expr, Stmt } from "./ast.ts";
|
import { BinaryType, Expr, Stmt } from "./ast.ts";
|
||||||
import { Ops } from "./mod.ts";
|
import { Ops } from "./mod.ts";
|
||||||
|
import { VType } from "./vtypes.ts";
|
||||||
|
|
||||||
class Locals {
|
interface Locals {
|
||||||
|
reserveId(id: number): void;
|
||||||
|
allocSym(ident: string): void;
|
||||||
|
symId(ident: string): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalsFnRoot implements Locals {
|
||||||
|
private localsAmount = 0;
|
||||||
private localIdCounter = 0;
|
private localIdCounter = 0;
|
||||||
private symLocalMap: {[key: string]: number} = {}
|
private symLocalMap: { [key: string]: number } = {};
|
||||||
|
|
||||||
constructor (private parent?: Locals) {}
|
constructor(private parent?: Locals) {
|
||||||
|
|
||||||
defineSym(ident: string) {
|
|
||||||
this.symLocalMap[ident] = this.localIdCounter
|
|
||||||
this.localIdCounter++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
symLocalId(ident: string): number {
|
reserveId(id: number): void {
|
||||||
|
this.localsAmount = Math.max(id + 1, this.localsAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
allocSym(ident: string) {
|
||||||
|
this.symLocalMap[ident] = this.localIdCounter;
|
||||||
|
this.localIdCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
symId(ident: string): number {
|
||||||
if (ident in this.symLocalMap) {
|
if (ident in this.symLocalMap) {
|
||||||
return this.symLocalMap[ident]
|
return this.symLocalMap[ident];
|
||||||
}
|
}
|
||||||
if (!this.parent) {
|
if (this.parent) {
|
||||||
throw new Error(`Could not find syn local id with ident ${ident}`)
|
return this.parent.symId(ident);
|
||||||
}
|
}
|
||||||
else {
|
throw new Error(`undefined symbol '${ident}'`);
|
||||||
return this.parent.symLocalId(ident)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalLeaf implements Locals {
|
||||||
|
private localIdCounter = 0;
|
||||||
|
private symLocalMap: { [key: string]: number } = {};
|
||||||
|
|
||||||
|
constructor(private parent: Locals) {
|
||||||
|
}
|
||||||
|
|
||||||
|
reserveId(id: number): void {
|
||||||
|
this.parent.reserveId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
allocSym(ident: string) {
|
||||||
|
this.symLocalMap[ident] = this.localIdCounter;
|
||||||
|
this.localIdCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
symId(ident: string): number {
|
||||||
|
if (ident in this.symLocalMap) {
|
||||||
|
return this.symLocalMap[ident];
|
||||||
|
}
|
||||||
|
return this.parent.symId(ident);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Lowerer {
|
export class Lowerer {
|
||||||
private program: number[] = []
|
private program: number[] = [];
|
||||||
private locals = new Locals()
|
private locals = new LocalsFnRoot();
|
||||||
|
|
||||||
|
|
||||||
lower(stmts: Stmt[]) {
|
lower(stmts: Stmt[]) {
|
||||||
for(const stmt of stmts) {
|
for (const stmt of stmts) {
|
||||||
this.lowerStmt(stmt)
|
this.lowerStmt(stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,18 +82,17 @@ export class Lowerer {
|
|||||||
case "assign":
|
case "assign":
|
||||||
case "expr":
|
case "expr":
|
||||||
}
|
}
|
||||||
throw new Error(`Unhandled stmt ${stmt.kind.type}`)
|
throw new Error(`Unhandled stmt ${stmt.kind.type}`);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerLetStmt(stmt: Stmt) {
|
lowerLetStmt(stmt: Stmt) {
|
||||||
if (stmt.kind.type !== "let") {
|
if (stmt.kind.type !== "let") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
this.lowerExpr(stmt.kind.value)
|
this.lowerExpr(stmt.kind.value);
|
||||||
this.locals.defineSym(stmt.kind.param.ident),
|
this.locals.allocSym(stmt.kind.param.ident),
|
||||||
this.program.push(Ops.StoreLocal)
|
this.program.push(Ops.StoreLocal);
|
||||||
this.program.push(this.locals.symLocalId(stmt.kind.param.ident))
|
this.program.push(this.locals.symId(stmt.kind.param.ident));
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerExpr(expr: Expr) {
|
lowerExpr(expr: Expr) {
|
||||||
@ -68,7 +101,7 @@ export class Lowerer {
|
|||||||
case "error":
|
case "error":
|
||||||
break;
|
break;
|
||||||
case "int":
|
case "int":
|
||||||
return this.lowerInt(expr)
|
return this.lowerInt(expr);
|
||||||
case "ident":
|
case "ident":
|
||||||
case "group":
|
case "group":
|
||||||
case "field":
|
case "field":
|
||||||
@ -77,7 +110,7 @@ export class Lowerer {
|
|||||||
case "unary":
|
case "unary":
|
||||||
break;
|
break;
|
||||||
case "binary":
|
case "binary":
|
||||||
return this.lowerBinaryExpr(expr)
|
return this.lowerBinaryExpr(expr);
|
||||||
case "if":
|
case "if":
|
||||||
case "bool":
|
case "bool":
|
||||||
case "null":
|
case "null":
|
||||||
@ -85,17 +118,17 @@ export class Lowerer {
|
|||||||
case "block":
|
case "block":
|
||||||
break;
|
break;
|
||||||
case "sym":
|
case "sym":
|
||||||
return this.lowerSym(expr)
|
return this.lowerSym(expr);
|
||||||
}
|
}
|
||||||
throw new Error(`Unhandled expr ${expr.kind.type}`)
|
throw new Error(`Unhandled expr ${expr.kind.type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerInt(expr: Expr) {
|
lowerInt(expr: Expr) {
|
||||||
if (expr.kind.type !== "int") {
|
if (expr.kind.type !== "int") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
this.program.push(Ops.PushInt)
|
this.program.push(Ops.PushInt);
|
||||||
this.program.push(expr.kind.value)
|
this.program.push(expr.kind.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerSym(expr: Expr) {
|
lowerSym(expr: Expr) {
|
||||||
@ -103,8 +136,8 @@ export class Lowerer {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
if (expr.kind.defType == "let") {
|
if (expr.kind.defType == "let") {
|
||||||
this.program.push(Ops.LoadLocal)
|
this.program.push(Ops.LoadLocal);
|
||||||
this.program.push(this.locals.symLocalId(expr.kind.ident));
|
this.program.push(this.locals.symId(expr.kind.ident));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(`Unhandled sym deftype ${expr.kind.defType}`);
|
throw new Error(`Unhandled sym deftype ${expr.kind.defType}`);
|
||||||
@ -116,14 +149,14 @@ export class Lowerer {
|
|||||||
}
|
}
|
||||||
this.lowerExpr(expr.kind.left);
|
this.lowerExpr(expr.kind.left);
|
||||||
this.lowerExpr(expr.kind.right);
|
this.lowerExpr(expr.kind.right);
|
||||||
if (expr.vtype?.type == "int") {
|
if (expr.vtype!.type === "int") {
|
||||||
switch (expr.kind.binaryType) {
|
switch (expr.kind.binaryType) {
|
||||||
case "+":
|
case "+":
|
||||||
this.program.push(Ops.Add);
|
this.program.push(Ops.Add);
|
||||||
return
|
return;
|
||||||
case "*":
|
case "*":
|
||||||
this.program.push(Ops.Multiply);
|
this.program.push(Ops.Multiply);
|
||||||
return
|
return;
|
||||||
case "==":
|
case "==":
|
||||||
case "-":
|
case "-":
|
||||||
case "/":
|
case "/":
|
||||||
@ -135,8 +168,12 @@ export class Lowerer {
|
|||||||
case "or":
|
case "or":
|
||||||
case "and":
|
case "and":
|
||||||
}
|
}
|
||||||
throw new Error("Unhandled binary type")
|
|
||||||
}
|
}
|
||||||
|
throw new Error(
|
||||||
|
`Unhandled vtype/binaryType '${
|
||||||
|
expr.vtype!.type
|
||||||
|
}/${expr.kind.binaryType}'`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Expr, Stmt, Sym } from "./ast.ts";
|
import { Expr, Stmt, Sym } from "./ast.ts";
|
||||||
import { Pos } from "./Token.ts";
|
import { Pos } from "./Token.ts";
|
||||||
|
|
||||||
type SymMap = { [ident: string]: Sym }
|
type SymMap = { [ident: string]: Sym };
|
||||||
|
|
||||||
class Syms {
|
class Syms {
|
||||||
private syms: SymMap = {};
|
private syms: SymMap = {};
|
||||||
@ -16,11 +16,13 @@ class Syms {
|
|||||||
return ident in this.syms;
|
return ident in this.syms;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(ident: string): { ok: true, sym: Sym } | { ok: false } {
|
public get(ident: string): { ok: true; sym: Sym } | { ok: false } {
|
||||||
if (ident in this.syms)
|
if (ident in this.syms) {
|
||||||
return { ok: true, sym: this.syms[ident] };
|
return { ok: true, sym: this.syms[ident] };
|
||||||
if (this.parent)
|
}
|
||||||
|
if (this.parent) {
|
||||||
return this.parent.get(ident);
|
return this.parent.get(ident);
|
||||||
|
}
|
||||||
return { ok: false };
|
return { ok: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,7 +37,6 @@ export class Resolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private resolveExpr(expr: Expr, syms: Syms) {
|
private resolveExpr(expr: Expr, syms: Syms) {
|
||||||
if (expr.kind.type === "error") {
|
if (expr.kind.type === "error") {
|
||||||
return;
|
return;
|
||||||
@ -60,66 +61,73 @@ export class Resolver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.type === "group") {
|
if (expr.kind.type === "group") {
|
||||||
this.resolveExpr(expr.kind.expr, syms)
|
this.resolveExpr(expr.kind.expr, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.type === "field") {
|
if (expr.kind.type === "field") {
|
||||||
this.resolveExpr(expr.kind.subject, syms)
|
this.resolveExpr(expr.kind.subject, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.type === "index") {
|
if (expr.kind.type === "index") {
|
||||||
this.resolveExpr(expr.kind.subject, syms)
|
this.resolveExpr(expr.kind.subject, syms);
|
||||||
this.resolveExpr(expr.kind.value, syms)
|
this.resolveExpr(expr.kind.value, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.type === "call") {
|
if (expr.kind.type === "call") {
|
||||||
this.resolveExpr(expr.kind.subject, syms)
|
this.resolveExpr(expr.kind.subject, syms);
|
||||||
for (const e of expr.kind.args) {
|
for (const e of expr.kind.args) {
|
||||||
this.resolveExpr(e, syms)
|
this.resolveExpr(e, syms);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.type === "unary") {
|
if (expr.kind.type === "unary") {
|
||||||
this.resolveExpr(expr.kind.subject, syms)
|
this.resolveExpr(expr.kind.subject, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.type === "if") {
|
if (expr.kind.type === "if") {
|
||||||
this.resolveExpr(expr.kind.cond, syms)
|
this.resolveExpr(expr.kind.cond, syms);
|
||||||
this.resolveExpr(expr.kind.truthy, syms)
|
this.resolveExpr(expr.kind.truthy, syms);
|
||||||
if (expr.kind.falsy !== undefined) {
|
if (expr.kind.falsy !== undefined) {
|
||||||
this.resolveExpr(expr.kind.falsy, syms)
|
this.resolveExpr(expr.kind.falsy, syms);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.type === "loop") {
|
if (expr.kind.type === "loop") {
|
||||||
this.resolveExpr(expr.kind.body, syms)
|
this.resolveExpr(expr.kind.body, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expr.kind.type === "int" || expr.kind.type === "bool" || expr.kind.type === "null" || expr.kind.type === "string" || expr.kind.type === "sym") {
|
if (
|
||||||
|
expr.kind.type === "int" || expr.kind.type === "bool" ||
|
||||||
|
expr.kind.type === "null" || expr.kind.type === "string" ||
|
||||||
|
expr.kind.type === "sym"
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveIdentExpr(expr: Expr, syms: Syms) {
|
private resolveIdentExpr(expr: Expr, syms: Syms) {
|
||||||
if (expr.kind.type !== "ident")
|
if (expr.kind.type !== "ident") {
|
||||||
throw new Error("expected ident");
|
throw new Error("expected ident");
|
||||||
|
}
|
||||||
const ident = expr.kind;
|
const ident = expr.kind;
|
||||||
const symResult = syms.get(ident.value);
|
const symResult = syms.get(ident.value);
|
||||||
if (!symResult.ok) {
|
if (!symResult.ok) {
|
||||||
this.reportUseOfUndefined(ident.value, expr.pos, syms);
|
this.reportUseOfUndefined(ident.value, expr.pos, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sym = symResult.sym
|
const sym = symResult.sym;
|
||||||
expr.kind = {
|
expr.kind = {
|
||||||
type: "sym",
|
type: "sym",
|
||||||
ident: ident.value,
|
ident: ident.value,
|
||||||
defType: sym.type,
|
defType: sym.type,
|
||||||
};
|
};
|
||||||
if (sym.stmt)
|
if (sym.stmt) {
|
||||||
expr.kind.stmt = sym.stmt;
|
expr.kind.stmt = sym.stmt;
|
||||||
if (sym.param)
|
}
|
||||||
|
if (sym.param) {
|
||||||
expr.kind.param = sym.param;
|
expr.kind.param = sym.param;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private resolveStmt(stmt: Stmt, syms: Syms) {
|
private resolveStmt(stmt: Stmt, syms: Syms) {
|
||||||
if (stmt.kind.type === "error") {
|
if (stmt.kind.type === "error") {
|
||||||
@ -134,30 +142,32 @@ export class Resolver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stmt.kind.type === "return") {
|
if (stmt.kind.type === "return") {
|
||||||
if (stmt.kind.expr)
|
if (stmt.kind.expr) {
|
||||||
this.resolveExpr(stmt.kind.expr, syms);
|
this.resolveExpr(stmt.kind.expr, syms);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stmt.kind.type === "break") {
|
if (stmt.kind.type === "break") {
|
||||||
if (stmt.kind.expr !== undefined) {
|
if (stmt.kind.expr !== undefined) {
|
||||||
this.resolveExpr(stmt.kind.expr, syms)
|
this.resolveExpr(stmt.kind.expr, syms);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stmt.kind.type === "assign") {
|
if (stmt.kind.type === "assign") {
|
||||||
this.resolveExpr(stmt.kind.subject, syms)
|
this.resolveExpr(stmt.kind.subject, syms);
|
||||||
this.resolveExpr(stmt.kind.value, syms)
|
this.resolveExpr(stmt.kind.value, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stmt.kind.type === "expr") {
|
if (stmt.kind.type === "expr") {
|
||||||
this.resolveExpr(stmt.kind.expr, syms)
|
this.resolveExpr(stmt.kind.expr, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveLetStmt(stmt: Stmt, syms: Syms) {
|
private resolveLetStmt(stmt: Stmt, syms: Syms) {
|
||||||
if (stmt.kind.type !== "let")
|
if (stmt.kind.type !== "let") {
|
||||||
throw new Error("expected let statement");
|
throw new Error("expected let statement");
|
||||||
|
}
|
||||||
this.resolveExpr(stmt.kind.value, syms);
|
this.resolveExpr(stmt.kind.value, syms);
|
||||||
const ident = stmt.kind.param.ident;
|
const ident = stmt.kind.param.ident;
|
||||||
if (syms.definedLocally(ident)) {
|
if (syms.definedLocally(ident)) {
|
||||||
@ -174,8 +184,9 @@ export class Resolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private resolveFnStmt(stmt: Stmt, syms: Syms) {
|
private resolveFnStmt(stmt: Stmt, syms: Syms) {
|
||||||
if (stmt.kind.type !== "fn")
|
if (stmt.kind.type !== "fn") {
|
||||||
throw new Error("expected fn statement");
|
throw new Error("expected fn statement");
|
||||||
|
}
|
||||||
if (syms.definedLocally(stmt.kind.ident)) {
|
if (syms.definedLocally(stmt.kind.ident)) {
|
||||||
this.reportAlreadyDefined(stmt.kind.ident, stmt.pos, syms);
|
this.reportAlreadyDefined(stmt.kind.ident, stmt.pos, syms);
|
||||||
return;
|
return;
|
||||||
@ -204,19 +215,25 @@ export class Resolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private reportUseOfUndefined(ident: string, pos: Pos, syms: Syms) {
|
private reportUseOfUndefined(ident: string, pos: Pos, syms: Syms) {
|
||||||
console.error(`use of undefined symbol '${ident}' at ${pos.line}${pos.col}`);
|
console.error(
|
||||||
|
`use of undefined symbol '${ident}' at ${pos.line}${pos.col}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) {
|
private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) {
|
||||||
console.error(`symbol already defined '${ident}', at ${pos.line}${pos.col}`);
|
console.error(
|
||||||
|
`symbol already defined '${ident}', at ${pos.line}${pos.col}`,
|
||||||
|
);
|
||||||
const prev = syms.get(ident);
|
const prev = syms.get(ident);
|
||||||
if (!prev.ok)
|
if (!prev.ok) {
|
||||||
throw new Error("expected to be defined");
|
throw new Error("expected to be defined");
|
||||||
if (!prev.sym.pos)
|
|
||||||
return;
|
|
||||||
const { line: prevLine, col: prevCol } = prev.sym.pos;
|
|
||||||
console.error(`previous definition of '${ident}' at ${prevLine}:${prevCol}`);
|
|
||||||
}
|
}
|
||||||
|
if (!prev.sym.pos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { line: prevLine, col: prevCol } = prev.sym.pos;
|
||||||
|
console.error(
|
||||||
|
`previous definition of '${ident}' at ${prevLine}:${prevCol}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { Checker } from "./Checker.ts";
|
import { Checker } from "./Checker.ts";
|
||||||
import { Lexer } from "./Lexer.ts";
|
import { Lexer } from "./Lexer.ts";
|
||||||
import { Parser } from "./Parser.ts";
|
import { Parser } from "./Parser.ts";
|
||||||
import { Resolver } from "./Syms.ts";
|
import { Resolver } from "./Resolver.ts";
|
||||||
|
|
||||||
const text = await Deno.readTextFile("example.slg");
|
const text = await Deno.readTextFile("example.slg");
|
||||||
// const text = await Deno.readTextFile("example.slg");
|
// const text = await Deno.readTextFile("example.slg");
|
||||||
@ -14,8 +14,8 @@ const lexer = new Lexer(text);
|
|||||||
// token = lexer.next();
|
// token = lexer.next();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const parser = new Parser(lexer)
|
const parser = new Parser(lexer);
|
||||||
const ast = parser.parseStmts()
|
const ast = parser.parseStmts();
|
||||||
new Resolver().resolve(ast)
|
new Resolver().resolve(ast);
|
||||||
new Checker().check(ast)
|
new Checker().check(ast);
|
||||||
// console.log(JSON.stringify(ast, null, 4))
|
// console.log(JSON.stringify(ast, null, 4))
|
||||||
|
Loading…
Reference in New Issue
Block a user