add lowererer stuff

This commit is contained in:
SimonFJ20 2024-12-10 14:36:41 +01:00
parent 632e8385f3
commit bf9c0aa866
10 changed files with 432 additions and 215 deletions

View File

@ -196,17 +196,19 @@ export class Checker {
return; return;
} }
case "sym": { case "sym": {
if (stmt.kind.subject.kind.defType !== "let") { if (stmt.kind.subject.kind.sym.type !== "let") {
this.report("cannot only assign to let-symbol", pos); this.report("cannot only assign to let-symbol", pos);
return { type: "error" }; return { type: "error" };
} }
if (!vtypesEqual(stmt.kind.subject.kind.param!.vtype!, value)) { if (
!vtypesEqual(stmt.kind.subject.kind.sym.param.vtype!, value)
) {
this.report( this.report(
`cannot assign to incompatible type` + `cannot assign to incompatible type` +
`, got '${vtypeToString(value)}'` + `, got '${vtypeToString(value)}'` +
`, expected '${ `, expected '${
vtypeToString( vtypeToString(
stmt.kind.subject.kind.param!.vtype!, stmt.kind.subject.kind.sym.param.vtype!,
) )
}'`, }'`,
pos, pos,
@ -266,12 +268,11 @@ export class Checker {
if (expr.kind.type !== "sym") { if (expr.kind.type !== "sym") {
throw new Error(); throw new Error();
} }
const pos = expr.pos; switch (expr.kind.sym.type) {
switch (expr.kind.defType) {
case "let": case "let":
return expr.kind.param?.vtype!; return expr.kind.sym.param.vtype!;
case "fn": { case "fn": {
const fnStmt = expr.kind.stmt!; const fnStmt = expr.kind.sym.stmt!;
if (fnStmt.kind.type !== "fn") { if (fnStmt.kind.type !== "fn") {
throw new Error(); throw new Error();
} }
@ -283,9 +284,13 @@ export class Checker {
return { type: "fn", params, returnType }; return { type: "fn", params, returnType };
} }
case "fn_param": case "fn_param":
return expr.kind.param!.vtype!; return expr.kind.sym.param.vtype!;
case "builtin": case "builtin":
throw new Error(); case "let_static":
case "closure":
throw new Error(
`not implemented, sym type '${expr.kind.sym.type}'`,
);
} }
} }

View File

@ -1,93 +1,83 @@
import { Builtins } from "./arch.ts";
import { BinaryType, Expr, Stmt } from "./ast.ts"; import { BinaryType, Expr, Stmt } from "./ast.ts";
import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
import { Ops } from "./mod.ts"; import { Ops } from "./mod.ts";
import { VType } from "./vtypes.ts"; import { VType, vtypeToString } from "./vtypes.ts";
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 symLocalMap: { [key: string]: number } = {};
constructor(private parent?: Locals) {
}
reserveId(id: number): void {
this.localsAmount = Math.max(id + 1, this.localsAmount);
}
allocSym(ident: string) {
this.symLocalMap[ident] = this.localIdCounter;
this.localIdCounter++;
this.reserveId(this.localIdCounter);
}
symId(ident: string): number {
if (ident in this.symLocalMap) {
return this.symLocalMap[ident];
}
if (this.parent) {
return this.parent.symId(ident);
}
throw new Error(`undefined symbol '${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++;
this.reserveId(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 LocalsFnRoot(); private locals: Locals = new LocalsFnRoot();
private fnStmtIdAddrMap: { [key: number]: number } = {};
lower(stmts: Stmt[]) { public lower(stmts: Stmt[]) {
for (const stmt of stmts) { for (const stmt of stmts) {
this.lowerStmt(stmt); this.lowerStaticStmt(stmt);
} }
} }
lowerStmt(stmt: Stmt) { public finish(): number[] {
return this.program;
}
private lowerStaticStmt(stmt: Stmt) {
switch (stmt.kind.type) {
case "fn":
return this.lowerFnStmt(stmt);
case "error":
case "break":
case "return":
case "let":
case "assign":
case "expr":
}
throw new Error(`unhandled static statement '${stmt.kind.type}'`);
}
private lowerStmt(stmt: Stmt) {
switch (stmt.kind.type) { switch (stmt.kind.type) {
case "error": case "error":
case "break": case "break":
case "return": case "return":
case "fn":
break; break;
case "fn":
return this.lowerFnStmt(stmt);
case "let": case "let":
return this.lowerLetStmt(stmt); return this.lowerLetStmt(stmt);
case "assign": case "assign":
break;
case "expr": case "expr":
this.lowerExpr(stmt.kind.expr);
this.program.push(Ops.Pop);
return;
} }
throw new Error(`Unhandled stmt ${stmt.kind.type}`); throw new Error(`unhandled stmt '${stmt.kind.type}'`);
} }
lowerLetStmt(stmt: Stmt) { private lowerFnStmt(stmt: Stmt) {
if (stmt.kind.type !== "fn") {
throw new Error();
}
const outerLocals = this.locals;
this.locals = new LocalsFnRoot(outerLocals);
const outerProgram = this.program;
this.program = [];
for (const { ident } of stmt.kind.params) {
this.locals.allocSym(ident);
this.program.push(
Ops.StoreLocal,
this.locals.symId(ident),
);
}
this.lowerExpr(stmt.kind.body);
this.program.push(Ops.Return);
this.locals = outerLocals;
outerProgram.push(...this.program);
this.program = outerProgram;
}
private lowerLetStmt(stmt: Stmt) {
if (stmt.kind.type !== "let") { if (stmt.kind.type !== "let") {
throw new Error(); throw new Error();
} }
@ -97,55 +87,88 @@ export class Lowerer {
this.program.push(this.locals.symId(stmt.kind.param.ident)); this.program.push(this.locals.symId(stmt.kind.param.ident));
} }
lowerExpr(expr: Expr) { private lowerExpr(expr: Expr) {
switch (expr.kind.type) { switch (expr.kind.type) {
case "string":
case "error": case "error":
break; break;
case "sym":
return this.lowerSymExpr(expr);
case "null":
break;
case "int": case "int":
return this.lowerInt(expr); return this.lowerIntExpr(expr);
case "bool":
break;
case "string":
return this.lowerStringExpr(expr);
case "ident": case "ident":
break;
case "group": case "group":
break;
case "field": case "field":
break;
case "index": case "index":
break;
case "call": case "call":
return this.lowerCallExpr(expr);
case "unary": case "unary":
break; break;
case "binary": case "binary":
return this.lowerBinaryExpr(expr); return this.lowerBinaryExpr(expr);
case "if": case "if":
case "bool": return this.lowerIfExpr(expr);
case "null":
case "loop": case "loop":
case "block":
break; break;
case "sym": case "block":
return this.lowerSym(expr); return this.lowerBlockExpr(expr);
} }
throw new Error(`Unhandled expr ${expr.kind.type}`); throw new Error(`unhandled expr '${expr.kind.type}'`);
} }
lowerInt(expr: Expr) { private lowerSymExpr(expr: Expr) {
if (expr.kind.type !== "int") {
throw new Error();
}
this.program.push(Ops.PushInt);
this.program.push(expr.kind.value);
}
lowerSym(expr: Expr) {
if (expr.kind.type !== "sym") { if (expr.kind.type !== "sym") {
throw new Error(); throw new Error();
} }
if (expr.kind.defType == "let") { if (expr.kind.sym.type === "let") {
this.program.push(Ops.LoadLocal); this.program.push(
this.program.push(this.locals.symId(expr.kind.ident)); Ops.LoadLocal,
this.locals.symId(expr.kind.ident),
);
return; return;
} }
throw new Error(`Unhandled sym deftype ${expr.kind.defType}`); if (expr.kind.sym.type === "fn_param") {
this.program.push(
Ops.LoadLocal,
this.locals.symId(expr.kind.ident),
);
return;
}
if (expr.kind.sym.type === "fn") {
const addr = this.fnStmtIdAddrMap[expr.kind.sym.stmt.id];
this.program.push(Ops.PushPtr, addr);
return;
}
throw new Error(`unhandled sym type '${expr.kind.sym.type}'`);
} }
lowerBinaryExpr(expr: Expr) { private lowerIntExpr(expr: Expr) {
if (expr.kind.type !== "int") {
throw new Error();
}
this.program.push(Ops.PushInt, expr.kind.value);
}
private lowerStringExpr(expr: Expr) {
if (expr.kind.type !== "string") {
throw new Error();
}
this.program.push(Ops.PushString);
for (let i = 0; i < expr.kind.value.length; ++i) {
this.program.push(expr.kind.value.charCodeAt(i));
}
}
private lowerBinaryExpr(expr: Expr) {
if (expr.kind.type !== "binary") { if (expr.kind.type !== "binary") {
throw new Error(); throw new Error();
} }
@ -171,10 +194,57 @@ export class Lowerer {
case "and": case "and":
} }
} }
if (expr.vtype!.type === "string") {
if (expr.kind.binaryType === "+") {
this.program.push(Ops.Builtin, Builtins.StringAdd);
return;
}
}
throw new Error( throw new Error(
`Unhandled vtype/binaryType '${ `unhandled binaryType` +
expr.vtype!.type ` '${vtypeToString(expr.kind.left.vtype!)}'` +
}/${expr.kind.binaryType}'`, ` ${expr.kind.binaryType}` +
` '${vtypeToString(expr.kind.left.vtype!)}'`,
); );
} }
private lowerCallExpr(expr: Expr) {
if (expr.kind.type !== "call") {
throw new Error();
}
for (const arg of expr.kind.args) {
this.lowerExpr(arg);
}
this.lowerExpr(expr.kind.subject);
}
private lowerIfExpr(expr: Expr) {
if (expr.kind.type !== "if") {
throw new Error();
}
this.lowerExpr(expr.kind.cond);
this.lowerExpr(expr.kind.truthy);
const falsyIndex = this.program.length;
if (expr.kind.falsy) {
this.lowerExpr(expr.kind.falsy);
}
const doneIndex = this.program.length;
}
private lowerBlockExpr(expr: Expr) {
if (expr.kind.type !== "block") {
throw new Error();
}
const outerLocals = this.locals;
this.locals = new LocalLeaf(this.locals);
for (const stmt of expr.kind.stmts) {
this.lowerStmt(stmt);
}
if (expr.kind.expr) {
this.lowerExpr(expr.kind.expr);
} else {
this.program.push(Ops.PushNull);
}
this.locals = outerLocals;
}
} }

View File

@ -1,42 +1,43 @@
import { Expr, Stmt, Sym } from "./ast.ts"; import { Expr, Stmt } from "./ast.ts";
import {
FnSyms,
GlobalSyms,
LeafSyms,
StaticSyms,
Syms,
} from "./resolver_syms.ts";
import { Pos } from "./Token.ts"; import { Pos } from "./Token.ts";
type SymMap = { [ident: string]: Sym };
class Syms {
private syms: SymMap = {};
public constructor(private parent?: Syms) {}
public define(ident: string, sym: Sym) {
this.syms[ident] = sym;
}
public definedLocally(ident: string): boolean {
return ident in this.syms;
}
public get(ident: string): { ok: true; sym: Sym } | { ok: false } {
if (ident in this.syms) {
return { ok: true, sym: this.syms[ident] };
}
if (this.parent) {
return this.parent.get(ident);
}
return { ok: false };
}
}
export class Resolver { export class Resolver {
private root = new Syms(); private root = new GlobalSyms();
public resolve(stmts: Stmt[]) { public resolve(stmts: Stmt[]) {
const scopeSyms = new Syms(this.root); const scopeSyms = new StaticSyms(this.root);
this.scoutFnStmts(stmts, scopeSyms);
for (const stmt of stmts) { for (const stmt of stmts) {
this.resolveStmt(stmt, scopeSyms); this.resolveStmt(stmt, scopeSyms);
} }
} }
private scoutFnStmts(stmts: Stmt[], syms: Syms) {
for (const stmt of stmts) {
if (stmt.kind.type !== "fn") {
continue;
}
if (syms.definedLocally(stmt.kind.ident)) {
this.reportAlreadyDefined(stmt.kind.ident, stmt.pos, syms);
return;
}
const ident = stmt.kind.ident;
syms.define(ident, {
ident: stmt.kind.ident,
type: "fn",
pos: stmt.pos,
stmt,
});
}
}
private resolveExpr(expr: Expr, syms: Syms) { private resolveExpr(expr: Expr, syms: Syms) {
if (expr.kind.type === "error") { if (expr.kind.type === "error") {
return; return;
@ -51,7 +52,8 @@ export class Resolver {
return; return;
} }
if (expr.kind.type === "block") { if (expr.kind.type === "block") {
const childSyms = new Syms(syms); const childSyms = new LeafSyms(syms);
this.scoutFnStmts(expr.kind.stmts, childSyms);
for (const stmt of expr.kind.stmts) { for (const stmt of expr.kind.stmts) {
this.resolveStmt(stmt, childSyms); this.resolveStmt(stmt, childSyms);
} }
@ -119,14 +121,8 @@ export class Resolver {
expr.kind = { expr.kind = {
type: "sym", type: "sym",
ident: ident.value, ident: ident.value,
defType: sym.type, sym,
}; };
if (sym.stmt) {
expr.kind.stmt = sym.stmt;
}
if (sym.param) {
expr.kind.param = sym.param;
}
} }
private resolveStmt(stmt: Stmt, syms: Syms) { private resolveStmt(stmt: Stmt, syms: Syms) {
@ -187,18 +183,7 @@ export class Resolver {
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)) { const fnScopeSyms = new FnSyms(syms);
this.reportAlreadyDefined(stmt.kind.ident, stmt.pos, syms);
return;
}
const ident = stmt.kind.ident;
syms.define(ident, {
ident: stmt.kind.ident,
type: "fn",
pos: stmt.pos,
stmt,
});
const fnScopeSyms = new Syms(syms);
for (const param of stmt.kind.params) { for (const param of stmt.kind.params) {
if (fnScopeSyms.definedLocally(param.ident)) { if (fnScopeSyms.definedLocally(param.ident)) {
this.reportAlreadyDefined(param.ident, param.pos, syms); this.reportAlreadyDefined(param.ident, param.pos, syms);
@ -214,7 +199,7 @@ export class Resolver {
this.resolveExpr(stmt.kind.body, fnScopeSyms); this.resolveExpr(stmt.kind.body, fnScopeSyms);
} }
private reportUseOfUndefined(ident: string, pos: Pos, syms: Syms) { private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) {
console.error( console.error(
`use of undefined symbol '${ident}' at ${pos.line}${pos.col}`, `use of undefined symbol '${ident}' at ${pos.line}${pos.col}`,
); );

View File

@ -30,4 +30,13 @@ export const Ops = {
Xor: 22, Xor: 22,
Not: 23, Not: 23,
SourceMap: 24, SourceMap: 24,
Builtin: 25,
ReserveStatics: 26,
LoadStatic: 27,
StoreStatic: 28,
} as const;
export type Builtins = typeof Builtins;
export const Builtins = {
StringAdd: 0,
} as const; } as const;

View File

@ -71,18 +71,21 @@ export type ExprKind =
| { | {
type: "sym"; type: "sym";
ident: string; ident: string;
defType: "let" | "fn" | "fn_param" | "builtin"; sym: Sym;
stmt?: Stmt;
param?: Param;
}; };
export type Sym = { export type Sym = {
ident: string; ident: string;
type: "let" | "fn" | "fn_param" | "builtin";
pos?: Pos; pos?: Pos;
stmt?: Stmt; } & SymKind;
param?: Param;
}; export type SymKind =
| { type: "let"; stmt: Stmt; param: Param }
| { type: "let_static"; stmt: Stmt; param: Param }
| { type: "fn"; stmt: Stmt }
| { type: "fn_param"; param: Param }
| { type: "closure"; inner: Sym }
| { type: "builtin" };
export type EType = { export type EType = {
kind: ETypeKind; kind: ETypeKind;

View File

@ -1,29 +0,0 @@
fn sum(a, b) {
+ a b;
}
sum(2,3); // -> 5
let a = "Hello";
let b = "world";
println(+ + + a " " b "!"); // -> "Hello world!"
if == a b {
println("whaaaat");
}
else {
println(":o");
}
loop {
let i = 0;
if >= i 10 {
break;
}
i = + i 1;
}

View File

@ -6,27 +6,29 @@ fn sum(a: int, b: int) -> int {
+ a b + a b
} }
sum(2,3); // -> 5 fn main() {
sum(2,3); // -> 5
let a: string = "Hello"; let a: string = "Hello";
let b = "world"; let b = "world";
println(+ + + a " " b "!"); // -> "Hello world!" println(+ + + a " " b "!"); // -> "Hello world!"
if == a b { if == a b {
println("whaaaat"); println("whaaaat");
} }
else { else {
println(":o"); println(":o");
}
loop {
let i = 0;
if >= i 10 {
break;
} }
i = + i 1; loop {
} let i = 0;
if >= i 10 {
break;
}
i = + i 1;
}
}

View File

@ -0,0 +1,59 @@
export interface Locals {
reserveId(id: number): void;
allocSym(ident: string): void;
symId(ident: string): number;
}
export class LocalsFnRoot implements Locals {
private localsAmount = 0;
private localIdCounter = 0;
private symLocalMap: { [key: string]: number } = {};
constructor(private parent?: Locals) {
}
public reserveId(id: number): void {
this.localsAmount = Math.max(id + 1, this.localsAmount);
}
public allocSym(ident: string) {
this.symLocalMap[ident] = this.localIdCounter;
this.localIdCounter++;
this.reserveId(this.localIdCounter);
}
public symId(ident: string): number {
if (ident in this.symLocalMap) {
return this.symLocalMap[ident];
}
if (this.parent) {
return this.parent.symId(ident);
}
throw new Error(`undefined symbol '${ident}'`);
}
}
export class LocalLeaf implements Locals {
private localIdCounter = 0;
private symLocalMap: { [key: string]: number } = {};
constructor(private parent: Locals) {
}
public reserveId(id: number): void {
this.parent.reserveId(id);
}
public allocSym(ident: string) {
this.symLocalMap[ident] = this.localIdCounter;
this.localIdCounter++;
this.reserveId(this.localIdCounter);
}
public symId(ident: string): number {
if (ident in this.symLocalMap) {
return this.symLocalMap[ident];
}
return this.parent.symId(ident);
}
}

View File

@ -1,21 +1,19 @@
import { Checker } from "./Checker.ts"; import { Checker } from "./Checker.ts";
import { Lexer } from "./Lexer.ts"; import { Lexer } from "./Lexer.ts";
import { Lowerer } from "./Lowerer.ts";
import { Parser } from "./Parser.ts"; import { Parser } from "./Parser.ts";
import { Resolver } from "./Resolver.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 lexer = new Lexer(text); const lexer = new Lexer(text);
// while (token !== null) {
// const value = token.identValue ?? token.intValue ?? token.stringValue ?? "";
// console.log(`${token.type}\t${value}`)
// 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))
const lowerer = new Lowerer();
lowerer.lower(ast);
const program = lowerer.finish();
console.log(JSON.stringify(program, null, 4));

115
compiler/resolver_syms.ts Normal file
View File

@ -0,0 +1,115 @@
import { Sym } from "./ast.ts";
export type SymMap = { [ident: string]: Sym };
export interface Syms {
define(ident: string, sym: Sym): void;
definedLocally(ident: string): boolean;
get(ident: string): { ok: true; sym: Sym } | { ok: false };
}
export class GlobalSyms implements Syms {
private syms: SymMap = {};
public constructor() {}
public define(ident: string, sym: Sym) {
if (sym.type === "let") {
this.define(ident, {
...sym,
type: "let_static",
});
return;
}
this.syms[ident] = sym;
}
public definedLocally(ident: string): boolean {
return ident in this.syms;
}
public get(ident: string): { ok: true; sym: Sym } | { ok: false } {
if (ident in this.syms) {
return { ok: true, sym: this.syms[ident] };
}
return { ok: false };
}
}
export class StaticSyms implements Syms {
private syms: SymMap = {};
public constructor(private parent: GlobalSyms) {}
public define(ident: string, sym: Sym) {
if (sym.type === "let") {
this.define(ident, {
...sym,
type: "let_static",
});
return;
}
this.syms[ident] = sym;
}
public definedLocally(ident: string): boolean {
return ident in this.syms;
}
public get(ident: string): { ok: true; sym: Sym } | { ok: false } {
if (ident in this.syms) {
return { ok: true, sym: this.syms[ident] };
}
return this.parent.get(ident);
}
}
export class FnSyms implements Syms {
private syms: SymMap = {};
public constructor(private parent: Syms) {}
public define(ident: string, sym: Sym) {
if (sym.type === "let") {
this.define(ident, {
...sym,
type: "closure",
inner: sym,
});
return;
}
this.syms[ident] = sym;
}
public definedLocally(ident: string): boolean {
return ident in this.syms;
}
public get(ident: string): { ok: true; sym: Sym } | { ok: false } {
if (ident in this.syms) {
return { ok: true, sym: this.syms[ident] };
}
return this.parent.get(ident);
}
}
export class LeafSyms implements Syms {
private syms: SymMap = {};
public constructor(private parent: Syms) {}
public define(ident: string, sym: Sym) {
this.syms[ident] = sym;
}
public definedLocally(ident: string): boolean {
return ident in this.syms;
}
public get(ident: string): { ok: true; sym: Sym } | { ok: false } {
if (ident in this.syms) {
return { ok: true, sym: this.syms[ident] };
}
return this.parent.get(ident);
}
}