mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 10:06:31 +00:00
fix compilter
This commit is contained in:
parent
9221f62f92
commit
f3523e486b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
out.slgbc
|
@ -43,14 +43,23 @@ export class Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public assemble(): number[] {
|
public assemble(): number[] {
|
||||||
|
console.log("Assembling...");
|
||||||
let ip = 0;
|
let ip = 0;
|
||||||
const output: number[] = [];
|
const output: number[] = [];
|
||||||
const locs: { [key: string]: number } = {};
|
const locs: { [key: string]: number } = {};
|
||||||
const refs: { [key: number]: string } = {};
|
const refs: { [key: number]: string } = {};
|
||||||
|
|
||||||
|
const debugLines: {
|
||||||
|
startIp: number;
|
||||||
|
endIp: number;
|
||||||
|
insString: string;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
for (const line of this.lines) {
|
for (const line of this.lines) {
|
||||||
for (const label of line.labels ?? []) {
|
for (const label of line.labels ?? []) {
|
||||||
locs[label] = ip;
|
locs[label] = ip;
|
||||||
}
|
}
|
||||||
|
const startIp = ip;
|
||||||
for (const lit of line.ins as Lit[]) {
|
for (const lit of line.ins as Lit[]) {
|
||||||
if (typeof lit === "number") {
|
if (typeof lit === "number") {
|
||||||
output.push(lit);
|
output.push(lit);
|
||||||
@ -69,6 +78,11 @@ export class Assembler {
|
|||||||
ip += 1;
|
ip += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
debugLines.push({
|
||||||
|
startIp,
|
||||||
|
endIp: ip,
|
||||||
|
insString: this.insToString(line.ins),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
for (let i = 0; i < output.length; ++i) {
|
for (let i = 0; i < output.length; ++i) {
|
||||||
if (!(i in refs)) {
|
if (!(i in refs)) {
|
||||||
@ -82,6 +96,14 @@ export class Assembler {
|
|||||||
}
|
}
|
||||||
output[i] = locs[refs[i]];
|
output[i] = locs[refs[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const line of debugLines) {
|
||||||
|
console.log(
|
||||||
|
line.startIp.toString().padStart(3, " ") + " " +
|
||||||
|
output.slice(line.startIp, line.endIp).join(", ") + "\n" +
|
||||||
|
line.insString + "\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,4 +132,25 @@ export class Assembler {
|
|||||||
console.log(` ${op} ${args}`);
|
console.log(` ${op} ${args}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private insToString(ins: Ins): string {
|
||||||
|
const op = opToString(ins[0] as number)
|
||||||
|
.padEnd(13, " ");
|
||||||
|
const args = (ins.slice(1) as Lit[]).map((lit) => {
|
||||||
|
if (typeof lit === "number") {
|
||||||
|
return lit;
|
||||||
|
} else if (typeof lit === "boolean") {
|
||||||
|
return lit.toString();
|
||||||
|
} else if (typeof lit === "string") {
|
||||||
|
return '"' +
|
||||||
|
lit.replaceAll("\\", "\\\\").replaceAll("\0", "\\0")
|
||||||
|
.replaceAll("\n", "\\n").replaceAll("\t", "\\t")
|
||||||
|
.replaceAll("\r", "\\r") +
|
||||||
|
'"';
|
||||||
|
} else {
|
||||||
|
return lit.label;
|
||||||
|
}
|
||||||
|
}).join(", ");
|
||||||
|
return ` ${op} ${args}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { StmtKind } from "./ast.ts";
|
import { StmtKind } from "./ast.ts";
|
||||||
import { EType, Expr, Stmt } from "./ast.ts";
|
import { EType, Expr, Stmt } from "./ast.ts";
|
||||||
|
import { printStackTrace, Reporter } from "./info.ts";
|
||||||
import { Pos } from "./token.ts";
|
import { Pos } from "./token.ts";
|
||||||
import { VType, VTypeParam, vtypesEqual, vtypeToString } from "./vtype.ts";
|
import { VType, VTypeParam, vtypesEqual, vtypeToString } from "./vtype.ts";
|
||||||
|
|
||||||
@ -7,12 +8,37 @@ export class Checker {
|
|||||||
private fnReturnStack: VType[] = [];
|
private fnReturnStack: VType[] = [];
|
||||||
private loopBreakStack: VType[][] = [];
|
private loopBreakStack: VType[][] = [];
|
||||||
|
|
||||||
|
public constructor(private reporter: Reporter) {}
|
||||||
|
|
||||||
public check(stmts: Stmt[]) {
|
public check(stmts: Stmt[]) {
|
||||||
|
this.checkFnHeaders(stmts);
|
||||||
for (const stmt of stmts) {
|
for (const stmt of stmts) {
|
||||||
this.checkStmt(stmt);
|
this.checkStmt(stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkFnHeaders(stmts: Stmt[]) {
|
||||||
|
for (const stmt of stmts) {
|
||||||
|
if (stmt.kind.type !== "fn") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const returnType: VType = stmt.kind.returnType
|
||||||
|
? this.checkEType(stmt.kind.returnType)
|
||||||
|
: { type: "null" };
|
||||||
|
const params: VTypeParam[] = [];
|
||||||
|
for (const param of stmt.kind.params) {
|
||||||
|
if (param.etype === undefined) {
|
||||||
|
this.report("parameter types must be defined", param.pos);
|
||||||
|
stmt.kind.vtype = { type: "error" };
|
||||||
|
}
|
||||||
|
const vtype = this.checkEType(param.etype!);
|
||||||
|
param.vtype = vtype;
|
||||||
|
params.push({ ident: param.ident, vtype });
|
||||||
|
}
|
||||||
|
stmt.kind.vtype = { type: "fn", params, returnType };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public checkStmt(stmt: Stmt) {
|
public checkStmt(stmt: Stmt) {
|
||||||
switch (stmt.kind.type) {
|
switch (stmt.kind.type) {
|
||||||
case "error":
|
case "error":
|
||||||
@ -90,28 +116,18 @@ export class Checker {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const pos = stmt.pos;
|
const pos = stmt.pos;
|
||||||
const returnType: VType = stmt.kind.returnType
|
if (stmt.kind.vtype!.type !== "fn") {
|
||||||
? this.checkEType(stmt.kind.returnType)
|
throw new Error();
|
||||||
: { type: "null" };
|
|
||||||
const params: VTypeParam[] = [];
|
|
||||||
for (const param of stmt.kind.params) {
|
|
||||||
if (param.etype === undefined) {
|
|
||||||
this.report("parameter types must be defined", param.pos);
|
|
||||||
stmt.kind.vtype = { type: "error" };
|
|
||||||
}
|
}
|
||||||
const vtype = this.checkEType(param.etype!);
|
const { returnType } = stmt.kind.vtype!;
|
||||||
param.vtype = vtype;
|
|
||||||
params.push({ ident: param.ident, vtype });
|
|
||||||
}
|
|
||||||
stmt.kind.vtype = { type: "fn", params, returnType };
|
|
||||||
this.fnReturnStack.push(returnType);
|
this.fnReturnStack.push(returnType);
|
||||||
const body = this.checkExpr(stmt.kind.body);
|
const body = this.checkExpr(stmt.kind.body);
|
||||||
this.fnReturnStack.pop();
|
this.fnReturnStack.pop();
|
||||||
if (!vtypesEqual(returnType, body)) {
|
if (!vtypesEqual(returnType, body)) {
|
||||||
this.report(
|
this.report(
|
||||||
`incompatible return type` +
|
`incompatible return type` +
|
||||||
`, got ${body}` +
|
`, expected '${vtypeToString(returnType)}'` +
|
||||||
`, expected ${returnType}`,
|
`, got '${vtypeToString(body)}'`,
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -206,7 +222,8 @@ export class Checker {
|
|||||||
this.report(
|
this.report(
|
||||||
`cannot assign to incompatible type` +
|
`cannot assign to incompatible type` +
|
||||||
`, got '${vtypeToString(value)}'` +
|
`, got '${vtypeToString(value)}'` +
|
||||||
`, expected '${vtypeToString(
|
`, expected '${
|
||||||
|
vtypeToString(
|
||||||
stmt.kind.subject.kind.sym.param.vtype!,
|
stmt.kind.subject.kind.sym.param.vtype!,
|
||||||
)
|
)
|
||||||
}'`,
|
}'`,
|
||||||
@ -409,7 +426,8 @@ export class Checker {
|
|||||||
}
|
}
|
||||||
this.report(
|
this.report(
|
||||||
`cannot apply binary operation '${expr.kind.binaryType}' ` +
|
`cannot apply binary operation '${expr.kind.binaryType}' ` +
|
||||||
`on types '${vtypeToString(left)}' and '${vtypeToString(right)
|
`on types '${vtypeToString(left)}' and '${
|
||||||
|
vtypeToString(right)
|
||||||
}'`,
|
}'`,
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
@ -501,7 +519,7 @@ export class Checker {
|
|||||||
if (expr.kind.type !== "block") {
|
if (expr.kind.type !== "block") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const pos = expr.pos;
|
this.checkFnHeaders(expr.kind.stmts);
|
||||||
for (const stmt of expr.kind.stmts) {
|
for (const stmt of expr.kind.stmts) {
|
||||||
this.checkStmt(stmt);
|
this.checkStmt(stmt);
|
||||||
}
|
}
|
||||||
@ -574,20 +592,8 @@ export class Checker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private report(msg: string, pos: Pos) {
|
private report(msg: string, pos: Pos) {
|
||||||
console.error(`Checker: ${msg} at ${pos.line}:${pos.col}`);
|
this.reporter.reportError({ reporter: "Checker", msg, pos });
|
||||||
class ReportNotAnError extends Error {
|
printStackTrace();
|
||||||
constructor() {
|
|
||||||
super("ReportNotAnError");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
throw new ReportNotAnError();
|
|
||||||
} catch (error) {
|
|
||||||
if (!(error instanceof ReportNotAnError)) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
compiler/info.ts
Normal file
55
compiler/info.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { Pos } from "./token.ts";
|
||||||
|
|
||||||
|
export type Report = {
|
||||||
|
type: "error" | "note";
|
||||||
|
reporter: string;
|
||||||
|
pos?: Pos;
|
||||||
|
msg: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Reporter {
|
||||||
|
private reports: Report[] = [];
|
||||||
|
private errorSet = false;
|
||||||
|
|
||||||
|
public reportError(report: Omit<Report, "type">) {
|
||||||
|
this.reports.push({ ...report, type: "error" });
|
||||||
|
this.printReport({ ...report, type: "error" });
|
||||||
|
this.errorSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private printReport({ reporter, type, pos, msg }: Report) {
|
||||||
|
console.error(
|
||||||
|
`${reporter}: ${type}: ${msg}${
|
||||||
|
pos ? ` at ${pos.line}:${pos.col}` : ""
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addNote(report: Omit<Report, "type">) {
|
||||||
|
this.reports.push({ ...report, type: "note" });
|
||||||
|
this.printReport({ ...report, type: "note" });
|
||||||
|
}
|
||||||
|
|
||||||
|
public errorOccured(): boolean {
|
||||||
|
return this.errorSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function printStackTrace() {
|
||||||
|
class ReportNotAnError extends Error {
|
||||||
|
constructor() {
|
||||||
|
super("ReportNotAnError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
throw new ReportNotAnError();
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof ReportNotAnError)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
error.stack?.replace("Error: ReportNotAnError", "Stack trace:") ??
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Reporter } from "./info.ts";
|
||||||
import { Pos, Token } from "./token.ts";
|
import { Pos, Token } from "./token.ts";
|
||||||
|
|
||||||
export class Lexer {
|
export class Lexer {
|
||||||
@ -5,7 +6,7 @@ export class Lexer {
|
|||||||
private line = 1;
|
private line = 1;
|
||||||
private col = 1;
|
private col = 1;
|
||||||
|
|
||||||
public constructor(private text: string) {}
|
public constructor(private text: string, private reporter: Reporter) {}
|
||||||
|
|
||||||
public next(): Token | null {
|
public next(): Token | null {
|
||||||
if (this.done()) {
|
if (this.done()) {
|
||||||
@ -58,10 +59,7 @@ export class Lexer {
|
|||||||
if (this.test("0")) {
|
if (this.test("0")) {
|
||||||
this.step();
|
this.step();
|
||||||
if (!this.done() && this.test(/[0-9]/)) {
|
if (!this.done() && this.test(/[0-9]/)) {
|
||||||
console.error(
|
this.report("invalid number", pos);
|
||||||
`Lexer: invalid number` +
|
|
||||||
` at ${pos.line}:${pos.col}`,
|
|
||||||
);
|
|
||||||
return this.token("error", pos);
|
return this.token("error", pos);
|
||||||
}
|
}
|
||||||
return { ...this.token("int", pos), intValue: 0 };
|
return { ...this.token("int", pos), intValue: 0 };
|
||||||
@ -87,10 +85,7 @@ export class Lexer {
|
|||||||
this.step();
|
this.step();
|
||||||
}
|
}
|
||||||
if (this.done() || !this.test('"')) {
|
if (this.done() || !this.test('"')) {
|
||||||
console.error(
|
this.report("unclosed/malformed string", pos);
|
||||||
`Lexer: unclosed/malformed string` +
|
|
||||||
` at ${pos.line}:${pos.col}`,
|
|
||||||
);
|
|
||||||
return this.token("error", pos);
|
return this.token("error", pos);
|
||||||
}
|
}
|
||||||
this.step();
|
this.step();
|
||||||
@ -189,9 +184,7 @@ export class Lexer {
|
|||||||
this.step();
|
this.step();
|
||||||
return this.token("return", pos);
|
return this.token("return", pos);
|
||||||
}
|
}
|
||||||
console.error(
|
this.report(`illegal character '${this.current()}'`, pos);
|
||||||
`Lexer: illegal character '${this.current()}' at ${pos.line}:${pos.col}`,
|
|
||||||
);
|
|
||||||
this.step();
|
this.step();
|
||||||
return this.next();
|
return this.next();
|
||||||
}
|
}
|
||||||
@ -241,4 +234,12 @@ export class Lexer {
|
|||||||
return pattern.test(this.current());
|
return pattern.test(this.current());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private report(msg: string, pos: Pos) {
|
||||||
|
this.reporter.reportError({
|
||||||
|
msg,
|
||||||
|
pos,
|
||||||
|
reporter: "Lexer",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,15 +12,34 @@ export class Lowerer {
|
|||||||
private breakStack: Label[] = [];
|
private breakStack: Label[] = [];
|
||||||
|
|
||||||
public lower(stmts: Stmt[]) {
|
public lower(stmts: Stmt[]) {
|
||||||
|
this.program.add(Ops.PushPtr, { label: "_start" });
|
||||||
|
this.program.add(Ops.Jump);
|
||||||
|
this.scoutFnHeaders(stmts);
|
||||||
for (const stmt of stmts) {
|
for (const stmt of stmts) {
|
||||||
this.lowerStaticStmt(stmt);
|
this.lowerStaticStmt(stmt);
|
||||||
}
|
}
|
||||||
|
this.program.setLabel({ label: "_start" });
|
||||||
|
this.program.add(Ops.PushPtr, { label: "main" });
|
||||||
|
this.program.add(Ops.Call, 0);
|
||||||
|
this.program.add(Ops.Pop);
|
||||||
}
|
}
|
||||||
|
|
||||||
public finish(): number[] {
|
public finish(): number[] {
|
||||||
return this.program.assemble();
|
return this.program.assemble();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private scoutFnHeaders(stmts: Stmt[]) {
|
||||||
|
for (const stmt of stmts) {
|
||||||
|
if (stmt.kind.type !== "fn") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const label = stmt.kind.ident === "main"
|
||||||
|
? "main"
|
||||||
|
: `${stmt.kind.ident}_${stmt.id}`;
|
||||||
|
this.fnStmtIdLabelMap[stmt.id] = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private lowerStaticStmt(stmt: Stmt) {
|
private lowerStaticStmt(stmt: Stmt) {
|
||||||
switch (stmt.kind.type) {
|
switch (stmt.kind.type) {
|
||||||
case "fn":
|
case "fn":
|
||||||
@ -101,26 +120,43 @@ export class Lowerer {
|
|||||||
if (stmt.kind.type !== "fn") {
|
if (stmt.kind.type !== "fn") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const label = `${stmt.kind.ident}_${stmt.id}`;
|
const label = stmt.kind.ident === "main"
|
||||||
this.fnStmtIdLabelMap[stmt.id] = label;
|
? "main"
|
||||||
|
: `${stmt.kind.ident}_${stmt.id}`;
|
||||||
this.program.setLabel({ label });
|
this.program.setLabel({ label });
|
||||||
|
|
||||||
const outerLocals = this.locals;
|
const outerLocals = this.locals;
|
||||||
this.locals = new LocalsFnRoot(outerLocals);
|
const fnRoot = new LocalsFnRoot(outerLocals);
|
||||||
const outerProgram = this.program;
|
const outerProgram = this.program;
|
||||||
this.program = new Assembler();
|
|
||||||
|
|
||||||
|
this.program = new Assembler();
|
||||||
|
this.locals = fnRoot;
|
||||||
for (const { ident } of stmt.kind.params) {
|
for (const { ident } of stmt.kind.params) {
|
||||||
this.locals.allocSym(ident);
|
this.locals.allocSym(ident);
|
||||||
this.program.add(
|
|
||||||
Ops.StoreLocal,
|
|
||||||
this.locals.symId(ident),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
this.locals.allocSym("::return");
|
||||||
|
this.program.add(Ops.PushNull);
|
||||||
this.lowerExpr(stmt.kind.body);
|
this.lowerExpr(stmt.kind.body);
|
||||||
|
this.program.add(Ops.StoreLocal, this.locals.symId("::return"));
|
||||||
|
this.locals = outerLocals;
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < fnRoot.stackReserved() - 1 - stmt.kind.params.length;
|
||||||
|
++i
|
||||||
|
) {
|
||||||
|
outerProgram.add(Ops.PushNull);
|
||||||
|
this.program.add(Ops.Pop);
|
||||||
|
}
|
||||||
|
if (stmt.kind.params.length >= 1) {
|
||||||
|
this.program.add(Ops.StoreLocal, 0);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < stmt.kind.params.length - 1; ++i) {
|
||||||
|
this.program.add(Ops.Pop);
|
||||||
|
}
|
||||||
|
|
||||||
this.program.add(Ops.Return);
|
this.program.add(Ops.Return);
|
||||||
|
|
||||||
this.locals = outerLocals;
|
|
||||||
outerProgram.concat(this.program);
|
outerProgram.concat(this.program);
|
||||||
this.program = outerProgram;
|
this.program = outerProgram;
|
||||||
}
|
}
|
||||||
@ -195,7 +231,7 @@ export class Lowerer {
|
|||||||
}
|
}
|
||||||
if (expr.kind.sym.type === "fn") {
|
if (expr.kind.sym.type === "fn") {
|
||||||
const label = this.fnStmtIdLabelMap[expr.kind.sym.stmt.id];
|
const label = this.fnStmtIdLabelMap[expr.kind.sym.stmt.id];
|
||||||
this.program.add(Ops.PushPtr, label);
|
this.program.add(Ops.PushPtr, { label });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(`unhandled sym type '${expr.kind.sym.type}'`);
|
throw new Error(`unhandled sym type '${expr.kind.sym.type}'`);
|
||||||
@ -272,6 +308,7 @@ export class Lowerer {
|
|||||||
this.lowerExpr(arg);
|
this.lowerExpr(arg);
|
||||||
}
|
}
|
||||||
this.lowerExpr(expr.kind.subject);
|
this.lowerExpr(expr.kind.subject);
|
||||||
|
this.program.add(Ops.Call, expr.kind.args.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private lowerIfExpr(expr: Expr) {
|
private lowerIfExpr(expr: Expr) {
|
||||||
@ -285,11 +322,13 @@ export class Lowerer {
|
|||||||
this.lowerExpr(expr.kind.cond);
|
this.lowerExpr(expr.kind.cond);
|
||||||
|
|
||||||
this.program.add(Ops.Not);
|
this.program.add(Ops.Not);
|
||||||
this.program.add(Ops.JumpIfTrue, falseLabel);
|
this.program.add(Ops.PushPtr, falseLabel);
|
||||||
|
this.program.add(Ops.JumpIfTrue);
|
||||||
|
|
||||||
this.lowerExpr(expr.kind.truthy);
|
this.lowerExpr(expr.kind.truthy);
|
||||||
|
|
||||||
this.program.add(Ops.Jump, doneLabel);
|
this.program.add(Ops.PushPtr, doneLabel);
|
||||||
|
this.program.add(Ops.Jump);
|
||||||
|
|
||||||
this.program.setLabel(falseLabel);
|
this.program.setLabel(falseLabel);
|
||||||
|
|
||||||
@ -311,7 +350,8 @@ export class Lowerer {
|
|||||||
|
|
||||||
this.program.setLabel(contineLabel);
|
this.program.setLabel(contineLabel);
|
||||||
this.lowerExpr(expr.kind.body);
|
this.lowerExpr(expr.kind.body);
|
||||||
this.program.add(Ops.Jump, breakLabel);
|
this.program.add(Ops.PushPtr, breakLabel);
|
||||||
|
this.program.add(Ops.Jump);
|
||||||
this.program.setLabel(breakLabel);
|
this.program.setLabel(breakLabel);
|
||||||
if (expr.vtype!.type === "null") {
|
if (expr.vtype!.type === "null") {
|
||||||
this.program.add(Ops.PushNull);
|
this.program.add(Ops.PushNull);
|
||||||
@ -326,6 +366,7 @@ export class Lowerer {
|
|||||||
}
|
}
|
||||||
const outerLocals = this.locals;
|
const outerLocals = this.locals;
|
||||||
this.locals = new LocalLeaf(this.locals);
|
this.locals = new LocalLeaf(this.locals);
|
||||||
|
this.scoutFnHeaders(expr.kind.stmts);
|
||||||
for (const stmt of expr.kind.stmts) {
|
for (const stmt of expr.kind.stmts) {
|
||||||
this.lowerStmt(stmt);
|
this.lowerStmt(stmt);
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,28 @@
|
|||||||
export interface Locals {
|
export interface Locals {
|
||||||
reserveId(id: number): void;
|
reserveAmount(id: number): void;
|
||||||
allocSym(ident: string): void;
|
allocSym(ident: string): void;
|
||||||
symId(ident: string): number;
|
symId(ident: string): number;
|
||||||
|
|
||||||
|
currentLocalIdCounter(): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LocalsFnRoot implements Locals {
|
export class LocalsFnRoot implements Locals {
|
||||||
private localsAmount = 0;
|
private localsAmount = 0;
|
||||||
private localIdCounter = 0;
|
|
||||||
private symLocalMap: { [key: string]: number } = {};
|
private symLocalMap: { [key: string]: number } = {};
|
||||||
|
private localIdCounter: number;
|
||||||
|
|
||||||
constructor(private parent?: Locals) {
|
constructor(private parent?: Locals) {
|
||||||
|
this.localIdCounter = parent?.currentLocalIdCounter() ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public reserveId(id: number): void {
|
public reserveAmount(amount: number): void {
|
||||||
this.localsAmount = Math.max(id + 1, this.localsAmount);
|
this.localsAmount = Math.max(amount, this.localsAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public allocSym(ident: string) {
|
public allocSym(ident: string) {
|
||||||
this.symLocalMap[ident] = this.localIdCounter;
|
this.symLocalMap[ident] = this.localIdCounter;
|
||||||
this.localIdCounter++;
|
this.localIdCounter++;
|
||||||
this.reserveId(this.localIdCounter);
|
this.reserveAmount(this.localIdCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public symId(ident: string): number {
|
public symId(ident: string): number {
|
||||||
@ -31,23 +34,32 @@ export class LocalsFnRoot implements Locals {
|
|||||||
}
|
}
|
||||||
throw new Error(`undefined symbol '${ident}'`);
|
throw new Error(`undefined symbol '${ident}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public stackReserved(): number {
|
||||||
|
return this.localsAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public currentLocalIdCounter(): number {
|
||||||
|
return this.localIdCounter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LocalLeaf implements Locals {
|
export class LocalLeaf implements Locals {
|
||||||
private localIdCounter = 0;
|
|
||||||
private symLocalMap: { [key: string]: number } = {};
|
private symLocalMap: { [key: string]: number } = {};
|
||||||
|
private localIdCounter: number;
|
||||||
|
|
||||||
constructor(private parent: Locals) {
|
constructor(private parent: Locals) {
|
||||||
|
this.localIdCounter = parent.currentLocalIdCounter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public reserveId(id: number): void {
|
public reserveAmount(amount: number): void {
|
||||||
this.parent.reserveId(id);
|
this.parent.reserveAmount(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public allocSym(ident: string) {
|
public allocSym(ident: string) {
|
||||||
this.symLocalMap[ident] = this.localIdCounter;
|
this.symLocalMap[ident] = this.localIdCounter;
|
||||||
this.localIdCounter++;
|
this.localIdCounter++;
|
||||||
this.reserveId(this.localIdCounter);
|
this.reserveAmount(this.localIdCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public symId(ident: string): number {
|
public symId(ident: string): number {
|
||||||
@ -56,4 +68,8 @@ export class LocalLeaf implements Locals {
|
|||||||
}
|
}
|
||||||
return this.parent.symId(ident);
|
return this.parent.symId(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public currentLocalIdCounter(): number {
|
||||||
|
return this.localIdCounter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,33 @@
|
|||||||
import { Checker } from "./checker.ts";
|
import { Checker } from "./checker.ts";
|
||||||
|
import { Reporter } from "./info.ts";
|
||||||
import { Lexer } from "./lexer.ts";
|
import { Lexer } from "./lexer.ts";
|
||||||
import { Lowerer } from "./lowerer.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(Deno.args[0]);
|
||||||
|
|
||||||
const lexer = new Lexer(text);
|
const reporter = new Reporter();
|
||||||
|
|
||||||
const parser = new Parser(lexer);
|
const lexer = new Lexer(text, reporter);
|
||||||
|
|
||||||
|
const parser = new Parser(lexer, reporter);
|
||||||
const ast = parser.parseStmts();
|
const ast = parser.parseStmts();
|
||||||
new Resolver().resolve(ast);
|
new Resolver(reporter).resolve(ast);
|
||||||
new Checker().check(ast);
|
new Checker(reporter).check(ast);
|
||||||
// console.log(JSON.stringify(ast, null, 4))
|
// console.log(JSON.stringify(ast, null, 4))
|
||||||
|
|
||||||
|
if (reporter.errorOccured()) {
|
||||||
|
console.error("Errors occurred, stopping compilation.");
|
||||||
|
Deno.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
const lowerer = new Lowerer();
|
const lowerer = new Lowerer();
|
||||||
lowerer.lower(ast);
|
lowerer.lower(ast);
|
||||||
lowerer.printProgram();
|
lowerer.printProgram();
|
||||||
const program = lowerer.finish();
|
const program = lowerer.finish();
|
||||||
//console.log(JSON.stringify(program, null, 4));
|
//console.log(JSON.stringify(program, null, 4));
|
||||||
console.log(JSON.stringify(program));
|
console.log(JSON.stringify(program));
|
||||||
|
|
||||||
|
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
Stmt,
|
Stmt,
|
||||||
StmtKind,
|
StmtKind,
|
||||||
} from "./ast.ts";
|
} from "./ast.ts";
|
||||||
|
import { printStackTrace, Reporter } from "./info.ts";
|
||||||
import { Lexer } from "./lexer.ts";
|
import { Lexer } from "./lexer.ts";
|
||||||
import { Pos, Token } from "./token.ts";
|
import { Pos, Token } from "./token.ts";
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ export class Parser {
|
|||||||
private currentToken: Token | null;
|
private currentToken: Token | null;
|
||||||
private nextNodeId = 0;
|
private nextNodeId = 0;
|
||||||
|
|
||||||
public constructor(private lexer: Lexer) {
|
public constructor(private lexer: Lexer, private reporter: Reporter) {
|
||||||
this.currentToken = lexer.next();
|
this.currentToken = lexer.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,19 +42,12 @@ export class Parser {
|
|||||||
|
|
||||||
private report(msg: string, pos = this.pos()) {
|
private report(msg: string, pos = this.pos()) {
|
||||||
console.log(`Parser: ${msg} at ${pos.line}:${pos.col}`);
|
console.log(`Parser: ${msg} at ${pos.line}:${pos.col}`);
|
||||||
class ReportNotAnError extends Error {
|
this.reporter.reportError({
|
||||||
constructor() {
|
msg,
|
||||||
super("ReportNotAnError");
|
pos,
|
||||||
}
|
reporter: "Parser",
|
||||||
}
|
});
|
||||||
try {
|
printStackTrace();
|
||||||
throw new ReportNotAnError();
|
|
||||||
} catch (error) {
|
|
||||||
if (!(error instanceof ReportNotAnError)) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private stmt(kind: StmtKind, pos: Pos): Stmt {
|
private stmt(kind: StmtKind, pos: Pos): Stmt {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Expr, Stmt } from "./ast.ts";
|
import { Expr, Stmt } from "./ast.ts";
|
||||||
|
import { Reporter } from "./info.ts";
|
||||||
import {
|
import {
|
||||||
FnSyms,
|
FnSyms,
|
||||||
GlobalSyms,
|
GlobalSyms,
|
||||||
@ -11,6 +12,8 @@ import { Pos } from "./token.ts";
|
|||||||
export class Resolver {
|
export class Resolver {
|
||||||
private root = new GlobalSyms();
|
private root = new GlobalSyms();
|
||||||
|
|
||||||
|
public constructor(private reporter: Reporter) {}
|
||||||
|
|
||||||
public resolve(stmts: Stmt[]) {
|
public resolve(stmts: Stmt[]) {
|
||||||
const scopeSyms = new StaticSyms(this.root);
|
const scopeSyms = new StaticSyms(this.root);
|
||||||
this.scoutFnStmts(stmts, scopeSyms);
|
this.scoutFnStmts(stmts, scopeSyms);
|
||||||
@ -200,15 +203,19 @@ export class Resolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) {
|
private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) {
|
||||||
console.error(
|
this.reporter.reportError({
|
||||||
`use of undefined symbol '${ident}' at ${pos.line}${pos.col}`,
|
reporter: "Resolver",
|
||||||
);
|
msg: `use of undefined symbol '${ident}'`,
|
||||||
|
pos,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) {
|
private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) {
|
||||||
console.error(
|
this.reporter.reportError({
|
||||||
`symbol already defined '${ident}', at ${pos.line}${pos.col}`,
|
reporter: "Resolver",
|
||||||
);
|
msg: `symbol already defined '${ident}'`,
|
||||||
|
pos,
|
||||||
|
});
|
||||||
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");
|
||||||
@ -216,9 +223,10 @@ export class Resolver {
|
|||||||
if (!prev.sym.pos) {
|
if (!prev.sym.pos) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { line: prevLine, col: prevCol } = prev.sym.pos;
|
this.reporter.addNote({
|
||||||
console.error(
|
reporter: "Resolver",
|
||||||
`previous definition of '${ident}' at ${prevLine}:${prevCol}`,
|
msg: `previous definition of '${ident}'`,
|
||||||
);
|
pos: prev.sym.pos,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() -> int {
|
||||||
add(1, 2)
|
add(1, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(a: int, b: int) -> int {
|
fn add(a: int, b: int) -> int {
|
||||||
+ a b
|
let c = a;
|
||||||
|
let d = b;
|
||||||
|
{
|
||||||
|
let a = c;
|
||||||
|
+ a d
|
||||||
|
}
|
||||||
|
//+ a b
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
fn main() {
|
fn main() -> int {
|
||||||
+ 1 2
|
+ 1 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
fn main() {
|
fn main() -> int {
|
||||||
let a = 5;
|
let a = 5;
|
||||||
let b = 3;
|
let b = 3;
|
||||||
+ a b
|
+ a b
|
||||||
|
@ -1,17 +1,56 @@
|
|||||||
#include "actions.hpp"
|
#include "actions.hpp"
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include "rpc_server.hpp"
|
#include "rpc_server.hpp"
|
||||||
|
#include "vm.hpp"
|
||||||
#include "vm_provider.hpp"
|
#include "vm_provider.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
int main()
|
int execute_file_and_exit(std::string filename)
|
||||||
{
|
{
|
||||||
|
auto file = std::ifstream();
|
||||||
|
file.open(filename.c_str());
|
||||||
|
if (!file) {
|
||||||
|
std::cout << std::format("error: could not open file '{}'\n", filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
auto text = std::string(std::istreambuf_iterator<char> { file }, {});
|
||||||
|
auto parsed = sliger::json::parse_json(text);
|
||||||
|
if (not parsed.ok()) {
|
||||||
|
std::cout << std::format("error: {} at {}:{}\n", parsed.err().msg,
|
||||||
|
parsed.err().pos.line, parsed.err().pos.col);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
auto program = std::vector<uint32_t>();
|
||||||
|
for (auto& v : parsed.val()->as<sliger::json::Array>().values) {
|
||||||
|
program.push_back(
|
||||||
|
static_cast<uint32_t>(v->as<sliger::json::Number>().value));
|
||||||
|
}
|
||||||
|
auto vm = sliger::VM(program,
|
||||||
|
{
|
||||||
|
.flame_graph = false,
|
||||||
|
.code_coverage = false,
|
||||||
|
});
|
||||||
|
vm.run_until_done();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc >= 3 && std::string(argv[1]) == "run") {
|
||||||
|
return execute_file_and_exit(argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
auto state = sliger::rpc::vm_provider::VmProvider();
|
auto state = sliger::rpc::vm_provider::VmProvider();
|
||||||
|
|
||||||
auto rpc = sliger::rpc::RpcServer(
|
auto rpc = sliger::rpc::RpcServer(
|
||||||
[&](std::unique_ptr<sliger::json::Value> req,
|
[&](std::unique_ptr<sliger::json::Value> req,
|
||||||
std::unique_ptr<sliger::rpc::BufferedWriter> writer) {
|
std::unique_ptr<sliger::rpc::BufferedWriter> writer) {
|
||||||
auto action = sliger::rpc::action::action_from_json(std::move(req));
|
auto action = sliger::rpc::action::action_from_json(std::move(req));
|
||||||
action->perform_action(std::move(writer));
|
action->perform_action(std::move(writer), state);
|
||||||
});
|
});
|
||||||
rpc.listen();
|
rpc.listen();
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,8 @@ void VM::run_n_instructions(size_t amount)
|
|||||||
|
|
||||||
void VM::run_instruction()
|
void VM::run_instruction()
|
||||||
{
|
{
|
||||||
/*std::cout << std::format(" {:>4}: {:<12}{}\n", this->pc,*/
|
std::cout << std::format(" {:>4}: {:<12}{}\n", this->pc,
|
||||||
/* maybe_op_to_string(this->program[this->pc]), stack_repr_string(4));*/
|
maybe_op_to_string(this->program[this->pc]), stack_repr_string(6));
|
||||||
auto op = eat_op();
|
auto op = eat_op();
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Op::Nop:
|
case Op::Nop:
|
||||||
@ -183,7 +183,8 @@ void VM::run_instruction()
|
|||||||
stack_push(std::move(arguments.at(i - 1)));
|
stack_push(std::move(arguments.at(i - 1)));
|
||||||
}
|
}
|
||||||
this->pc = fn_ptr.as_ptr().value;
|
this->pc = fn_ptr.as_ptr().value;
|
||||||
this->bp = static_cast<uint32_t>(this->stack.size());
|
this->bp
|
||||||
|
= static_cast<uint32_t>(this->stack.size() - arguments.size());
|
||||||
if (this->opts.flame_graph) {
|
if (this->opts.flame_graph) {
|
||||||
this->flame_graph.report_call(
|
this->flame_graph.report_call(
|
||||||
fn_ptr.as_ptr().value, this->instruction_counter);
|
fn_ptr.as_ptr().value, this->instruction_counter);
|
||||||
|
12
slige-run.sh
Executable file
12
slige-run.sh
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo Compiling $1...
|
||||||
|
|
||||||
|
deno run --allow-read --allow-write compiler/main.ts $1
|
||||||
|
|
||||||
|
echo "Running out.slgbc..."
|
||||||
|
|
||||||
|
./runtime/build/sliger run out.slgbc
|
||||||
|
|
Loading…
Reference in New Issue
Block a user