mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 19:16:35 +00:00
add lowererer stuff
This commit is contained in:
parent
632e8385f3
commit
bf9c0aa866
@ -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}'`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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}`,
|
||||||
);
|
);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
59
compiler/lowerer_locals.ts
Normal file
59
compiler/lowerer_locals.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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
115
compiler/resolver_syms.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user