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;
}
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);
return { type: "error" };
}
if (!vtypesEqual(stmt.kind.subject.kind.param!.vtype!, value)) {
if (
!vtypesEqual(stmt.kind.subject.kind.sym.param.vtype!, value)
) {
this.report(
`cannot assign to incompatible type` +
`, got '${vtypeToString(value)}'` +
`, expected '${
vtypeToString(
stmt.kind.subject.kind.param!.vtype!,
stmt.kind.subject.kind.sym.param.vtype!,
)
}'`,
pos,
@ -266,12 +268,11 @@ export class Checker {
if (expr.kind.type !== "sym") {
throw new Error();
}
const pos = expr.pos;
switch (expr.kind.defType) {
switch (expr.kind.sym.type) {
case "let":
return expr.kind.param?.vtype!;
return expr.kind.sym.param.vtype!;
case "fn": {
const fnStmt = expr.kind.stmt!;
const fnStmt = expr.kind.sym.stmt!;
if (fnStmt.kind.type !== "fn") {
throw new Error();
}
@ -283,9 +284,13 @@ export class Checker {
return { type: "fn", params, returnType };
}
case "fn_param":
return expr.kind.param!.vtype!;
return expr.kind.sym.param.vtype!;
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 { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
import { Ops } from "./mod.ts";
import { VType } 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);
}
}
import { VType, vtypeToString } from "./vtypes.ts";
export class Lowerer {
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) {
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) {
case "error":
case "break":
case "return":
case "fn":
break;
case "fn":
return this.lowerFnStmt(stmt);
case "let":
return this.lowerLetStmt(stmt);
case "assign":
break;
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") {
throw new Error();
}
@ -97,55 +87,88 @@ export class Lowerer {
this.program.push(this.locals.symId(stmt.kind.param.ident));
}
lowerExpr(expr: Expr) {
private lowerExpr(expr: Expr) {
switch (expr.kind.type) {
case "string":
case "error":
break;
case "sym":
return this.lowerSymExpr(expr);
case "null":
break;
case "int":
return this.lowerInt(expr);
return this.lowerIntExpr(expr);
case "bool":
break;
case "string":
return this.lowerStringExpr(expr);
case "ident":
break;
case "group":
break;
case "field":
break;
case "index":
break;
case "call":
return this.lowerCallExpr(expr);
case "unary":
break;
case "binary":
return this.lowerBinaryExpr(expr);
case "if":
case "bool":
case "null":
return this.lowerIfExpr(expr);
case "loop":
case "block":
break;
case "sym":
return this.lowerSym(expr);
case "block":
return this.lowerBlockExpr(expr);
}
throw new Error(`Unhandled expr ${expr.kind.type}`);
throw new Error(`unhandled expr '${expr.kind.type}'`);
}
lowerInt(expr: Expr) {
if (expr.kind.type !== "int") {
throw new Error();
}
this.program.push(Ops.PushInt);
this.program.push(expr.kind.value);
}
lowerSym(expr: Expr) {
private lowerSymExpr(expr: Expr) {
if (expr.kind.type !== "sym") {
throw new Error();
}
if (expr.kind.defType == "let") {
this.program.push(Ops.LoadLocal);
this.program.push(this.locals.symId(expr.kind.ident));
if (expr.kind.sym.type === "let") {
this.program.push(
Ops.LoadLocal,
this.locals.symId(expr.kind.ident),
);
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") {
throw new Error();
}
@ -171,10 +194,57 @@ export class Lowerer {
case "and":
}
}
if (expr.vtype!.type === "string") {
if (expr.kind.binaryType === "+") {
this.program.push(Ops.Builtin, Builtins.StringAdd);
return;
}
}
throw new Error(
`Unhandled vtype/binaryType '${
expr.vtype!.type
}/${expr.kind.binaryType}'`,
`unhandled binaryType` +
` '${vtypeToString(expr.kind.left.vtype!)}'` +
` ${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";
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 {
private root = new Syms();
private root = new GlobalSyms();
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) {
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) {
if (expr.kind.type === "error") {
return;
@ -51,7 +52,8 @@ export class Resolver {
return;
}
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) {
this.resolveStmt(stmt, childSyms);
}
@ -119,14 +121,8 @@ export class Resolver {
expr.kind = {
type: "sym",
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) {
@ -187,18 +183,7 @@ export class Resolver {
if (stmt.kind.type !== "fn") {
throw new Error("expected fn statement");
}
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,
});
const fnScopeSyms = new Syms(syms);
const fnScopeSyms = new FnSyms(syms);
for (const param of stmt.kind.params) {
if (fnScopeSyms.definedLocally(param.ident)) {
this.reportAlreadyDefined(param.ident, param.pos, syms);
@ -214,7 +199,7 @@ export class Resolver {
this.resolveExpr(stmt.kind.body, fnScopeSyms);
}
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}`,
);

View File

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

View File

@ -71,18 +71,21 @@ export type ExprKind =
| {
type: "sym";
ident: string;
defType: "let" | "fn" | "fn_param" | "builtin";
stmt?: Stmt;
param?: Param;
sym: Sym;
};
export type Sym = {
ident: string;
type: "let" | "fn" | "fn_param" | "builtin";
pos?: Pos;
stmt?: Stmt;
param?: Param;
};
} & SymKind;
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 = {
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
}
sum(2,3); // -> 5
fn main() {
sum(2,3); // -> 5
let a: string = "Hello";
let b = "world";
let a: string = "Hello";
let b = "world";
println(+ + + a " " b "!"); // -> "Hello world!"
println(+ + + a " " b "!"); // -> "Hello world!"
if == a b {
println("whaaaat");
}
else {
println(":o");
}
loop {
let i = 0;
if >= i 10 {
break;
if == a b {
println("whaaaat");
}
else {
println(":o");
}
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 { Lexer } from "./Lexer.ts";
import { Lowerer } from "./Lowerer.ts";
import { Parser } from "./Parser.ts";
import { Resolver } from "./Resolver.ts";
const text = await Deno.readTextFile("example.slg");
// const text = await Deno.readTextFile("example.slg");
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 ast = parser.parseStmts();
new Resolver().resolve(ast);
new Checker().check(ast);
// 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);
}
}