From 072e44eaa8f1ebb4eb56983c32f48649839ba4b7 Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Wed, 11 Dec 2024 12:36:19 +0100 Subject: [PATCH] everything --- compiler/arch.ts | 3 ++ compiler/assembler.ts | 51 ++++------------------- compiler/ast.ts | 9 +++- compiler/checker.ts | 3 +- compiler/lexer.ts | 2 +- compiler/lowerer.ts | 24 ++++------- compiler/mod.ts | 9 ---- compiler/parser.ts | 62 +++++++++++++++++++++++++++ compiler/resolver.ts | 13 +++++- examples/add_fn.slg | 3 +- examples/example_1.slg | 10 ++--- examples/example_2.slg | 19 +++++---- runtime/value.hpp | 26 +++++++++++- runtime/vm.cpp | 95 +++++++++++++++++------------------------- slige-run.sh | 5 ++- 15 files changed, 186 insertions(+), 148 deletions(-) diff --git a/compiler/arch.ts b/compiler/arch.ts index 1ab81c1..da2ec53 100644 --- a/compiler/arch.ts +++ b/compiler/arch.ts @@ -42,6 +42,7 @@ export const Builtins = { StringEqual: 0x11, ArraySet: 0x20, StructSet: 0x30, + Print: 0x40, } as const; export function opToString(op: number): string { @@ -119,6 +120,8 @@ export function builtinToString(builtin: number): string { return "ArraySet"; case Builtins.StructSet: return "StructSet"; + case Builtins.Print: + return "Print"; default: return ``; } diff --git a/compiler/assembler.ts b/compiler/assembler.ts index b4336cd..361153d 100644 --- a/compiler/assembler.ts +++ b/compiler/assembler.ts @@ -49,17 +49,10 @@ export class Assembler { const locs: { [key: string]: number } = {}; const refs: { [key: number]: string } = {}; - const debugLines: { - startIp: number; - endIp: number; - insString: string; - }[] = []; - for (const line of this.lines) { for (const label of line.labels ?? []) { locs[label] = ip; } - const startIp = ip; for (const lit of line.ins as Lit[]) { if (typeof lit === "number") { output.push(lit); @@ -68,6 +61,8 @@ export class Assembler { output.push(lit ? 1 : 0); ip += 1; } else if (typeof lit === "string") { + output.push(lit.length); + ip += 1; for (let i = 0; i < lit.length; ++i) { output.push(lit.charCodeAt(i)); ip += 1; @@ -78,11 +73,6 @@ export class Assembler { ip += 1; } } - debugLines.push({ - startIp, - endIp: ip, - insString: this.insToString(line.ins), - }); } for (let i = 0; i < output.length; ++i) { if (!(i in refs)) { @@ -96,21 +86,14 @@ export class Assembler { } 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; } public printProgram() { + let ip = 0; for (const line of this.lines) { for (const label of line.labels ?? []) { - console.log(`${label}:`); + console.log(` ${label}:`); } const op = opToString(line.ins[0] as number) .padEnd(13, " "); @@ -129,28 +112,10 @@ export class Assembler { return lit.label; } }).join(", "); - console.log(` ${op} ${args}`); + console.log(`${ip.toString().padStart(8, " ")}: ${op} ${args}`); + ip += line.ins.map((lit) => + typeof lit === "string" ? lit.length : 1 + ).reduce((acc, curr) => acc + curr, 0); } } - - 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}`; - } } diff --git a/compiler/ast.ts b/compiler/ast.ts index 7b4e3bd..810c083 100644 --- a/compiler/ast.ts +++ b/compiler/ast.ts @@ -39,6 +39,7 @@ export type StmtKind = params: Param[]; returnType?: EType; body: Expr; + annos?: Anno[]; vtype?: VType; } | { type: "let"; param: Param; value: Expr } @@ -85,7 +86,7 @@ export type SymKind = | { type: "fn"; stmt: Stmt } | { type: "fn_param"; param: Param } | { type: "closure"; inner: Sym } - | { type: "builtin" }; + | { type: "builtin"; builtinId: number }; export type EType = { kind: ETypeKind; @@ -98,3 +99,9 @@ export type ETypeKind = | { type: "ident"; value: string } | { type: "array"; inner: EType } | { type: "struct"; fields: Param[] }; + +export type Anno = { + ident: string; + values: Expr[]; + pos: Pos; +}; diff --git a/compiler/checker.ts b/compiler/checker.ts index 6fc1522..12110c8 100644 --- a/compiler/checker.ts +++ b/compiler/checker.ts @@ -1,4 +1,4 @@ -import { StmtKind } from "./ast.ts"; +import { Builtins } from "./arch.ts"; import { EType, Expr, Stmt } from "./ast.ts"; import { printStackTrace, Reporter } from "./info.ts"; import { Pos } from "./token.ts"; @@ -356,6 +356,7 @@ export class Checker { } const pos = expr.pos; const subject = this.checkExpr(expr.kind.subject); + console.log(expr); if (subject.type !== "fn") { this.report("cannot call non-fn", pos); return { type: "error" }; diff --git a/compiler/lexer.ts b/compiler/lexer.ts index fabd11c..3853040 100644 --- a/compiler/lexer.ts +++ b/compiler/lexer.ts @@ -91,7 +91,7 @@ export class Lexer { this.step(); return { ...this.token("string", pos), stringValue: value }; } - if (this.test(/[\+\{\};=\-\*\(\)\.,:;\[\]>= 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); @@ -334,6 +322,8 @@ export class Lowerer { if (expr.kind.falsy) { this.lowerExpr(expr.kind.falsy!); + } else { + this.program.add(Ops.PushNull); } this.program.setLabel(doneLabel); diff --git a/compiler/mod.ts b/compiler/mod.ts index ee80a77..b6af563 100644 --- a/compiler/mod.ts +++ b/compiler/mod.ts @@ -1,14 +1,5 @@ -import { Stmt } from "./ast.ts"; -import { Lexer } from "./lexer.ts"; -import { Parser } from "./parser.ts"; - export * from "./parser.ts"; export * from "./ast.ts"; export * from "./arch.ts"; export * from "./lexer.ts"; export * from "./token.ts"; - -export async function compileWithDebug(filepath: string): Promise { - const text = await Deno.readTextFile(filepath); - return new Parser(new Lexer(text)).parseStmts(); -} diff --git a/compiler/parser.ts b/compiler/parser.ts index ebb7d8b..17dfd44 100644 --- a/compiler/parser.ts +++ b/compiler/parser.ts @@ -1,4 +1,5 @@ import { + Anno, BinaryType, EType, ETypeKind, @@ -203,6 +204,11 @@ export class Parser { this.step(); returnType = this.parseEType(); } + + let annos: Anno[] | null = null; + if (this.test("#")) { + annos = this.parseAnnoArgs(); + } if (!this.test("{")) { this.report("expected block"); return this.stmt({ type: "error" }, pos); @@ -214,6 +220,62 @@ export class Parser { return this.stmt({ type: "fn", ident, params, returnType, body }, pos); } + public parseAnnoArgs(): Expr[] { + this.step(); + if (!this.test("(")) { + this.report("expected '('"); + return []; + } + this.step(); + const annoArgs: Expr[] = []; + if (!this.test(")")) { + annoArgs.push(this.parseExpr()); + while (this.test(",")) { + this.step(); + if (this.test(")")) { + break; + } + annoArgs.push(this.parseExpr()); + } + } + if (!this.test(")")) { + this.report("expected ')'"); + return []; + } + this.step(); + return annoArgs; + } + + public parseAnnos(): Anno[] { + this.step(); + if (!this.test("[")) { + this.report("expected '['"); + return []; + } + this.step(); + const annoArgs: Expr[] = []; + if (!this.test(")")) { + if (!this.test("ident")) { + this.report("expected identifier"); + return []; + } + annoArgs.push(this.parseExpr()); + while (this.test(",")) { + this.step(); + if (this.test(")")) { + break; + } + annoArgs.push(this.parseExpr()); + } + } + if (!this.test(")")) { + this.report("expected ')'"); + return []; + } + this.step(); + return annoArgs; + } + public parseFnParams(): Param[] { this.step(); if (this.test(")")) { diff --git a/compiler/resolver.ts b/compiler/resolver.ts index fa106cd..607e4d3 100644 --- a/compiler/resolver.ts +++ b/compiler/resolver.ts @@ -1,5 +1,6 @@ +import { Builtins } from "./arch.ts"; import { Expr, Stmt } from "./ast.ts"; -import { Reporter } from "./info.ts"; +import { printStackTrace, Reporter } from "./info.ts"; import { FnSyms, GlobalSyms, @@ -12,7 +13,13 @@ import { Pos } from "./token.ts"; export class Resolver { private root = new GlobalSyms(); - public constructor(private reporter: Reporter) {} + public constructor(private reporter: Reporter) { + this.root.define("print", { + type: "builtin", + ident: "print", + builtinId: Builtins.Print, + }); + } public resolve(stmts: Stmt[]) { const scopeSyms = new StaticSyms(this.root); @@ -208,6 +215,7 @@ export class Resolver { msg: `use of undefined symbol '${ident}'`, pos, }); + printStackTrace(); } private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) { @@ -228,5 +236,6 @@ export class Resolver { msg: `previous definition of '${ident}'`, pos: prev.sym.pos, }); + printStackTrace(); } } diff --git a/examples/add_fn.slg b/examples/add_fn.slg index 38e55b0..8382df5 100644 --- a/examples/add_fn.slg +++ b/examples/add_fn.slg @@ -1,5 +1,4 @@ - fn main() -> int { add(1, 2) } @@ -11,5 +10,5 @@ fn add(a: int, b: int) -> int { let a = c; + a d } - //+ a b } + diff --git a/examples/example_1.slg b/examples/example_1.slg index f502665..3cbda50 100644 --- a/examples/example_1.slg +++ b/examples/example_1.slg @@ -1,6 +1,4 @@ - -fn println(str: string) { -} +fn print(msg: string) #[builtin(print)] {} fn sum(a: int, b: int) -> int { + a b @@ -13,13 +11,13 @@ fn main() { let b = "world"; - println(+ + + a " " b "!"); // -> "Hello world!" + print(+ + + a " " b "!\n"); // -> "Hello world!" if == a b { - println("whaaaat"); + print("whaaaat\n"); } else { - println(":o"); + print(":o\n"); } loop { diff --git a/examples/example_2.slg b/examples/example_2.slg index 927726f..19ad008 100644 --- a/examples/example_2.slg +++ b/examples/example_2.slg @@ -1,14 +1,17 @@ -fn add(a, b) { +fn add(a: int, b: int) -> int { + a b } -let result = 0; -let i = 0; -loop { - if >= i 10 { - break; +fn main() -> int { + let result = 0; + let i = 0; + loop { + if >= i 10 { + break; + } + result = add(result, 5); + i = + i 1; } - result = add(result, 5); - i = + i 1; + result } diff --git a/runtime/value.hpp b/runtime/value.hpp index bbb28db..0798072 100644 --- a/runtime/value.hpp +++ b/runtime/value.hpp @@ -10,6 +10,30 @@ namespace sliger { +inline auto escape_string(std::string str) -> std::string +{ + auto result = std::string(); + for (auto ch : str) { + switch (ch) { + case '\n': + result += "\\n"; + break; + case '\t': + result += "\\t"; + break; + case '\0': + result += "\\0"; + break; + case '\\': + result += "\\\\"; + break; + default: + result += ch; + } + } + return result; +} + enum class ValueType { Null, Int, @@ -137,7 +161,7 @@ public: case ValueType::Bool: return as_bool().value ? "true" : "false"; case ValueType::String: - return std::format("\"{}\"", as_string().value); + return std::format("\"{}\"", escape_string(as_string().value)); case ValueType::Ptr: return std::to_string(as_ptr().value); } diff --git a/runtime/vm.cpp b/runtime/vm.cpp index 2b433e5..d65ecdc 100644 --- a/runtime/vm.cpp +++ b/runtime/vm.cpp @@ -13,56 +13,37 @@ using namespace sliger; inline auto maybe_op_to_string(uint32_t value) -> std::string { switch (static_cast(value)) { - case Op::Nop: - return "Nop"; - case Op::PushNull: - return "PushNull"; - case Op::PushInt: - return "PushInt"; - case Op::PushBool: - return "PushBool"; - case Op::PushString: - return "PushString"; - case Op::PushPtr: - return "PushPtr"; - case Op::Pop: - return "Pop"; - case Op::LoadLocal: - return "LoadLocal"; - case Op::StoreLocal: - return "StoreLocal"; - case Op::Call: - return "Call"; - case Op::Return: - return "Return"; - case Op::Jump: - return "Jump"; - case Op::JumpIfTrue: - return "JumpIfTrue"; - case Op::Add: - return "Add"; - case Op::Subtract: - return "Subtract"; - case Op::Multiply: - return "Multiply"; - case Op::Divide: - return "Divide"; - case Op::Remainder: - return "Remainder"; - case Op::Equal: - return "Equal"; - case Op::LessThan: - return "LessThan"; - case Op::And: - return "And"; - case Op::Or: - return "Or"; - case Op::Xor: - return "Xor"; - case Op::Not: - return "Not"; - case Op::SourceMap: - return "SourceMap"; + /* clang-format off */ + case Op::Nop: return "Nop"; + case Op::PushNull: return "PushNull"; + case Op::PushInt: return "PushInt"; + case Op::PushBool: return "PushBool"; + case Op::PushString: return "PushString"; + case Op::PushPtr: return "PushPtr"; + case Op::Pop: return "Pop"; + case Op::ReserveStatic: return "ReserveStatic"; + case Op::LoadStatic: return "LoadStatic"; + case Op::StoreStatic: return "StoreStatic"; + case Op::LoadLocal: return "LoadLocal"; + case Op::StoreLocal: return "StoreLocal"; + case Op::Call: return "Call"; + case Op::Return: return "Return"; + case Op::Jump: return "Jump"; + case Op::JumpIfTrue: return "JumpIfTrue"; + case Op::Builtin: return "Builtin"; + case Op::Add: return "Add"; + case Op::Subtract: return "Subtract"; + case Op::Multiply: return "Multiply"; + case Op::Divide: return "Divide"; + case Op::Remainder: return "Remainder"; + case Op::Equal: return "Equal"; + case Op::LessThan: return "LessThan"; + case Op::And: return "And"; + case Op::Or: return "Or"; + case Op::Xor: return "Xor"; + case Op::Not: return "Not"; + case Op::SourceMap: return "SourceMap"; + /* clang-format on */ default: return std::to_string(value); } @@ -88,7 +69,7 @@ void VM::run_n_instructions(size_t amount) void VM::run_instruction() { std::cout << std::format(" {:>4}: {:<12}{}\n", this->pc, - maybe_op_to_string(this->program[this->pc]), stack_repr_string(6)); + maybe_op_to_string(this->program[this->pc]), stack_repr_string(8)); auto op = eat_op(); switch (op) { case Op::Nop: @@ -179,12 +160,11 @@ void VM::run_instruction() } stack_push(Ptr { .value = this->pc }); stack_push(Ptr { .value = this->bp }); + this->pc = fn_ptr.as_ptr().value; + this->bp = static_cast(this->stack.size()); for (size_t i = arguments.size(); i > 0; --i) { stack_push(std::move(arguments.at(i - 1))); } - this->pc = fn_ptr.as_ptr().value; - this->bp - = static_cast(this->stack.size() - arguments.size()); if (this->opts.flame_graph) { this->flame_graph.report_call( fn_ptr.as_ptr().value, this->instruction_counter); @@ -194,6 +174,9 @@ void VM::run_instruction() case Op::Return: { assert_stack_has(3); auto ret_val = stack_pop(); + while (this->stack.size() > this->bp) { + stack_pop(); + } auto bp_val = stack_pop(); auto pc_val = stack_pop(); this->bp = bp_val.as_ptr().value; @@ -331,16 +314,16 @@ void VM::run_builtin(Builtin builtin_id) switch (builtin_id) { case Builtin::StringConcat: { assert_stack_has(2); - auto right = stack_pop(); auto left = stack_pop(); + auto right = stack_pop(); stack_push( String(right.as_string().value + left.as_string().value)); break; } case Builtin::StringEqual: { assert_stack_has(2); - auto right = stack_pop(); auto left = stack_pop(); + auto right = stack_pop(); stack_push(Bool(right.as_string().value == left.as_string().value)); break; } diff --git a/slige-run.sh b/slige-run.sh index 6bc5c26..f0fc597 100755 --- a/slige-run.sh +++ b/slige-run.sh @@ -2,11 +2,14 @@ set -e +echo Text: +cat $1 + echo Compiling $1... deno run --allow-read --allow-write compiler/main.ts $1 -echo "Running out.slgbc..." +echo Running out.slgbc... ./runtime/build/sliger run out.slgbc