diff --git a/opt.wat b/opt.wat deleted file mode 100644 index 90d212b..0000000 --- a/opt.wat +++ /dev/null @@ -1,25 +0,0 @@ -(module - (type (;0;) (func (param i32 i32 i32 i32) (result i32))) - (type (;1;) (func)) - (import "wasi_snapshot_preview1" "fd_write" (func (;0;) (type 0))) - (func (;1;) (type 1) - i32.const 1028 - i32.const 0 - i32.store - i32.const 1032 - i32.const 2 - i32.store - i32.const 1 - i32.const 1028 - i32.const 2 - i32.const 1024 - call 0 - drop - ) - (table (;0;) 0 0 funcref) - (memory (;0;) 65536 65536) - (export "memory" (memory 0)) - (export "__indirect_function_table" (table 0)) - (export "_start" (func 1)) - (data (;0;) (i32.const 0) "a\0a") -) \ No newline at end of file diff --git a/src/ast.ts b/src/ast.ts index 45e203d..e0933d5 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -300,6 +300,7 @@ export const BUILTINS = [ "Bool", "true", "false", + "trap", // Intrinsics: "__i32_store", "__i64_store", diff --git a/src/error.ts b/src/error.ts index fce66b5..d171714 100644 --- a/src/error.ts +++ b/src/error.ts @@ -47,16 +47,17 @@ function renderError(input: string, e: CompilerError) { throw Error(`Span out of bounds: ${e.span.start}..${e.span.end}`); } const lineIdx = lineSpans.indexOf(line); + const lineNo = lineIdx + 1; console.error(`error: ${e.message}`); - console.error(`${lineIdx} | ${spanToSnippet(input, line)}`); + console.error(`${lineNo} | ${spanToSnippet(input, line)}`); const startRelLine = e.span.start === Number.MAX_SAFE_INTEGER ? 0 : e.span.start - line.start; const spanLength = min(e.span.end, line.end) - e.span.start; console.error( - `${" ".repeat(String(lineIdx).length)} ${" ".repeat( + `${" ".repeat(String(lineNo).length)} ${" ".repeat( startRelLine )}${"^".repeat(spanLength)}` ); diff --git a/src/index.ts b/src/index.ts index d8541ac..045eb1f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,23 +9,84 @@ import { writeModuleWatToString } from "./wasm/wat"; import fs from "fs"; import { exec } from "child_process"; -const input = ` +const INPUT = ` function main() = ( - prInt(0); - prInt(1); - prInt(9); - prInt(10); - prInt(100); + prIntln(0); + prIntln(1); + prIntln(9); + prIntln(2352353); + prIntln(100); ); -function prInt(a: Int) = ( - +function prIntln(x: Int) = ( + prInt(x); + print("\n"); ); -function uwu(): (Int, Int) = (0, 0); +function stringForDigit(x: Int): String = + if x == 0 then "0" + else if x == 1 then "1" + else if x == 2 then "2" + else if x == 3 then "3" + else if x == 4 then "4" + else if x == 5 then "5" + else if x == 6 then "6" + else if x == 7 then "7" + else if x == 8 then "8" + else if x == 9 then "9" + else trap(); + +function log10(x: Int): Int = ( + let i = 0; + loop ( + if x < 10 then break; + i = i + 1; + x = x / 10; + ); + i +); + +function pow(base: Int, exp: Int): Int = ( + let acc = 1; + loop ( + if exp == 0 then break; + acc = acc * base; + exp = exp - 1; + ); + acc +); + +function prInt(x: Int) = ( + let mag = log10(x); + + loop ( + if mag == 0 then break; + let base = pow(10, mag); + + let digit = x / base; + print(stringForDigit(digit)); + + x = x % base; + mag = mag - 1; + ); + + print(stringForDigit(x % 10)); +); + +function println(s: String) = ( + print(s); + print("\n"); +); `; function main() { + let input: string; + if (process.argv.length > 2) { + input = fs.readFileSync(process.argv[2], { encoding: "utf-8" }); + } else { + input = INPUT; + } + withErrorHandler(input, () => { const start = Date.now(); diff --git a/src/lower.ts b/src/lower.ts index 58f9d31..e8d1e4a 100644 --- a/src/lower.ts +++ b/src/lower.ts @@ -203,6 +203,7 @@ type FuncContext = { cx: Context; item: Item; func: FunctionDef; + wasmType: wasm.FuncType; wasm: wasm.Func; varLocations: VarLocation[]; loopDepths: Map; @@ -231,6 +232,7 @@ function lowerFunc(cx: Context, item: Item, func: FunctionDef) { cx, item, func, + wasmType, wasm: wasmFunc, varLocations: paramLocations, loopDepths: new Map(), @@ -265,7 +267,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) { lowerExpr(fcx, instrs, expr.rhs); const types = wasmTypeForBody(expr.rhs.ty!); - const local = fcx.wasm.locals.length; + const local = fcx.wasm.locals.length + fcx.wasmType.params.length; fcx.wasm.locals.push(...types); @@ -498,6 +500,10 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) { if (expr.lhs.value.res!.kind === "builtin") { switch (expr.lhs.value.res!.name) { + case "trap": { + instrs.push({ kind: "unreachable" }); + break exprKind; + } case "__i32_load": { lowerExpr(fcx, instrs, expr.args[0]); instrs.push({ kind: "i64.load", imm: {} }); diff --git a/src/typeck.ts b/src/typeck.ts index 0a0dcb2..cdedebc 100644 --- a/src/typeck.ts +++ b/src/typeck.ts @@ -61,6 +61,8 @@ function typeOfBuiltinValue(name: BuiltinName, span: Span): Ty { return TY_BOOL; case "print": return mkTyFn([TY_STRING], TY_UNIT); + case "trap": + return mkTyFn([], TY_NEVER); case "__i32_store": return mkTyFn([TY_I32, TY_I32], TY_UNIT); case "__i64_store": diff --git a/src/wasm/wat.ts b/src/wasm/wat.ts index e39ebc0..48dc5d6 100644 --- a/src/wasm/wat.ts +++ b/src/wasm/wat.ts @@ -541,11 +541,13 @@ function printImport(import_: Import, f: Formatter) { }); } -function printFunction(func: Func, f: Formatter) { +function printFunction(func: Func, idx: number, f: Formatter) { f.sexpr(() => { f.keyword("func"); printId(func._name, f); + f.word(`(;${idx};)`, chalk.gray); + f.sexpr(() => { f.keyword("type"); f.type(func.type); @@ -678,9 +680,9 @@ function printModule(module: Module, f: Formatter) { printImport(import_, f); }); - module.funcs.forEach((func) => { + module.funcs.forEach((func, i) => { breakIfAny(); - printFunction(func, f); + printFunction(func, i + module.imports.length, f); }); module.tables.forEach((table) => { diff --git a/test.nil b/test.nil new file mode 100644 index 0000000..67b19cf --- /dev/null +++ b/test.nil @@ -0,0 +1,67 @@ +function main() = ( + prIntln(0); + prIntln(1); + prIntln(9); + prIntln(2352353); + prIntln(100); +); + +function prIntln(x: Int) = ( + prInt(x); + print("\n"); +); + +function stringForDigit(x: Int): String = + if x == 0 then "0" + else if x == 1 then "1" + else if x == 2 then "2" + else if x == 3 then "3" + else if x == 4 then "4" + else if x == 5 then "5" + else if x == 6 then "6" + else if x == 7 then "7" + else if x == 8 then "8" + else if x == 9 then "9" + else trap(); + +function log10(x: Int): Int = ( + let i = 0; + loop ( + if x < 10 then break; + i = i + 1; + x = x / 10; + ); + i +); + +function pow(base: Int, exp: Int): Int = ( + let acc = 1; + loop ( + if exp == 0 then break; + acc = acc * base; + exp = exp - 1; + ); + acc +); + +function prInt(x: Int) = ( + let mag = log10(x); + + loop ( + if mag == 0 then break; + let base = pow(10, mag); + + let digit = x / base; + print(stringForDigit(digit)); + + x = x % base; + mag = mag - 1; + ); + + print(stringForDigit(x % 10)); +); + +function println(s: String) = ( + print(s); + print("\n"); +); \ No newline at end of file