From 924236532c585ef1b3cf4c9c6767832d7609821b Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:39:17 +0200 Subject: [PATCH] many things --- src/ast.ts | 30 ++++++++++++++-- src/error.ts | 6 +++- src/index.ts | 10 ++++-- src/lexer.ts | 6 +++- src/lower.ts | 82 +++++++++++++++++++++++++++++++++----------- src/parser.ts | 63 ++++++++++++++++++++-------------- src/resolve.ts | 24 +++++++++---- src/typeck.ts | 28 +++++++++------ src/wasm/defs.ts | 4 +-- src/wasm/wat.test.ts | 8 ++--- src/wasm/wat.ts | 20 ++++++++--- std.nil | 66 ++++++++++++++++++++++++++++++++--- tsconfig.json | 2 +- 13 files changed, 264 insertions(+), 85 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index 0b7f7c0..9139997 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,6 +1,6 @@ -import { Span } from "./error"; +import { DUMMY_SPAN, Span } from "./error"; import { LitIntType } from "./lexer"; -import { ComplexMap } from "./utils"; +import { ComplexMap, unwrap } from "./utils"; export type Phase = { res: unknown; @@ -430,6 +430,9 @@ export const BUILTINS = [ "__i64_load", "__string_ptr", "__string_len", + "__memory_size", + "__memory_grow", + "__i32_extend_to_i64_u" ] as const; export type BuiltinName = (typeof BUILTINS)[number]; @@ -521,6 +524,29 @@ export type TypeckResults = { main: Resolution | undefined; }; +export function findCrateItem

( + crate: Crate

, + id: ItemId +): Item

{ + if (id.crateId !== crate.id) { + throw new Error("trying to get item from the wrong crate"); + } + if (id.itemIdx === 0) { + // Return a synthetic module representing the crate root. + return { + kind: "mod", + node: { + contents: crate.rootItems, + name: crate.packageName, + }, + span: DUMMY_SPAN, + id, + }; + } + + return unwrap(crate.itemsById.get(id)); +} + // folders export type FoldFn = (value: From) => To; diff --git a/src/error.ts b/src/error.ts index 677d5e1..f640dee 100644 --- a/src/error.ts +++ b/src/error.ts @@ -11,6 +11,7 @@ export function spanMerge(a: Span, b: Span): Span { } export const DUMMY_SPAN = { start: 0, end: 0 }; +export const EOF_SPAN = {start: Number.MAX_SAFE_INTEGER, end: Number.MAX_SAFE_INTEGER}; export class CompilerError extends Error { msg: string; @@ -59,7 +60,10 @@ function renderError(input: string, filename: string, e: CompilerError) { 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; + const spanLength = + e.span.start === Number.MAX_SAFE_INTEGER + ? 1 + : min(e.span.end, line.end) - e.span.start; console.error( `${" ".repeat(String(lineNo).length)} ${" ".repeat( diff --git a/src/index.ts b/src/index.ts index 0e7edc1..c09e0a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,11 +13,13 @@ import { Crate, Built, Typecked } from "./ast"; import { Ids } from "./utils"; const INPUT = ` -global HELLO: I32 = 0_I32; +extern mod std; + +type A = { a: String }; function main() = ( - HELLO = 1_I32; -); + std.rt.allocateItem(0_I32, 0_I32); +); `; function main() { @@ -135,6 +137,8 @@ function loadCrate( const tokens = tokenize(input); const ast = parse(name, tokens, crateId.next()); const [resolved, crates] = resolve(ast, loadCrate); + console.log(resolved); + const typecked = typeck(resolved, [...existingCrates, ...crates]); return [typecked, crates]; diff --git a/src/lexer.ts b/src/lexer.ts index f6d0dda..aa08105 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -37,7 +37,8 @@ export type DatalessToken = | "==" | "<=" | ">=" - | "!="; + | "!=" + | "end of file"; export type TokenIdent = { kind: "identifier"; ident: string }; @@ -109,6 +110,9 @@ export function tokenize(input: string): Token[] { throw new CompilerError("unterminated block comment", span); } } + i++; + i++; + continue; } if (SINGLE_PUNCT.includes(next)) { diff --git a/src/lower.ts b/src/lower.ts index dc78b97..e9485ff 100644 --- a/src/lower.ts +++ b/src/lower.ts @@ -13,6 +13,7 @@ import { TyFn, TyTuple, Typecked, + findCrateItem, varUnreachable, } from "./ast"; import { printTy } from "./printer"; @@ -30,6 +31,8 @@ const WASM_PAGE = 65536; const DUMMY_IDX = 9999999; +const ALLOCATE_SYMBOL = "nil__std__rt__allocateItem"; + type RelocationKind = | { kind: "funccall"; @@ -86,7 +89,7 @@ function appendData(cx: Context, newData: Uint8Array): number { mode: { kind: "active", memory: 0, - offset: [{ kind: "i32.const", imm: 0 }], + offset: [{ kind: "i32.const", imm: 0n }], }, _name: "staticdata", }); @@ -103,8 +106,9 @@ function appendData(cx: Context, newData: Uint8Array): number { } function findItem(cx: Context, id: ItemId): Item { - return unwrap( - unwrap(cx.crates.find((crate) => crate.id === id.crateId)).itemsById.get(id) + return findCrateItem( + unwrap(cx.crates.find((crate) => crate.id === id.crateId)), + id ); } @@ -271,7 +275,7 @@ function lowerGlobal( const init: wasm.Instr = { kind: `${valtype}.const`, - imm: def.init.value.value, + imm: BigInt(def.init.value.value), }; cx.mod.globals.push({ @@ -300,6 +304,11 @@ type ArgRetAbi = wasm.ValType[]; type VarLocation = { localIdx: number; types: wasm.ValType[] }; +type StructLayout = { + size: number, + align: number, +} + function lowerFunc( cx: Context, item: Item, @@ -438,18 +447,18 @@ function lowerExpr( const utf8 = encodeUtf8(expr.value.value); const idx = appendData(fcx.cx, utf8); - instrs.push({ kind: "i32.const", imm: idx }); - instrs.push({ kind: "i32.const", imm: utf8.length }); + instrs.push({ kind: "i32.const", imm: BigInt(idx) }); + instrs.push({ kind: "i32.const", imm: BigInt(utf8.length) }); break; } case "int": switch (expr.value.type) { case "Int": - instrs.push({ kind: "i64.const", imm: expr.value.value }); + instrs.push({ kind: "i64.const", imm: BigInt(expr.value.value) }); break; case "I32": - instrs.push({ kind: "i32.const", imm: expr.value.value }); + instrs.push({ kind: "i32.const", imm: BigInt(expr.value.value) }); break; } break; @@ -487,10 +496,10 @@ function lowerExpr( case "builtin": switch (res.name) { case "false": - instrs.push({ kind: "i32.const", imm: 0 }); + instrs.push({ kind: "i32.const", imm: 0n }); break; case "true": - instrs.push({ kind: "i32.const", imm: 1 }); + instrs.push({ kind: "i32.const", imm: 1n }); break; case "print": todo("print function"); @@ -603,11 +612,18 @@ function lowerExpr( case "!": if (ty.kind === "bool") { // `xor RHS, 1` flips the lowermost bit. - instrs.push({ kind: "i64.const", imm: 1 }); + instrs.push({ kind: "i64.const", imm: 1n }); instrs.push({ kind: "i64.xor" }); } else if (ty.kind === "int") { // `xor RHS, -1` flips all bits. - todo("Thanks to JS, we cannot represent -1 i64 yet"); + instrs.push({ kind: "i64.const", imm: -1n }); + instrs.push({ kind: "i64.xor" }); + } else if (ty.kind === "i32") { + // `xor RHS, -1` flips all bits. + instrs.push({ kind: "i32.const", imm: -1n }); + instrs.push({ kind: "i32.xor" }); + } else { + throw new Error("invalid type for !"); } break; case "-": @@ -621,36 +637,44 @@ function lowerExpr( } const res = expr.lhs.kind === "ident" ? expr.lhs.value.res : expr.lhs.res; - if (res.kind === "builtin") { + const assertArgs = (n: number) => { + if (expr.args.length !== n) throw new Error("nope"); + }; switch (res.name) { case "trap": { + assertArgs(0); instrs.push({ kind: "unreachable" }); break exprKind; } case "__i32_load": { + assertArgs(1); lowerExpr(fcx, instrs, expr.args[0]); instrs.push({ kind: "i64.load", imm: {} }); break exprKind; } case "__i64_load": { + assertArgs(1); lowerExpr(fcx, instrs, expr.args[0]); instrs.push({ kind: "i64.load", imm: {} }); break exprKind; } case "__i32_store": { + assertArgs(2); lowerExpr(fcx, instrs, expr.args[0]); lowerExpr(fcx, instrs, expr.args[1]); instrs.push({ kind: "i32.store", imm: {} }); break exprKind; } case "__i64_store": { + assertArgs(3); lowerExpr(fcx, instrs, expr.args[0]); lowerExpr(fcx, instrs, expr.args[1]); instrs.push({ kind: "i64.store", imm: {} }); break exprKind; } case "__string_ptr": { + assertArgs(1); lowerExpr(fcx, instrs, expr.args[0]); // ptr, len instrs.push({ kind: "drop" }); @@ -658,14 +682,32 @@ function lowerExpr( break exprKind; } case "__string_len": { + assertArgs(1); lowerExpr(fcx, instrs, expr.args[0]); // ptr, len - instrs.push({ kind: "i32.const", imm: 0 }); + instrs.push({ kind: "i32.const", imm: 0n }); // ptr, len, 0 instrs.push({ kind: "select" }); // len break exprKind; } + case "__memory_size": { + assertArgs(0); + instrs.push({ kind: "memory.size" }); + break exprKind; + } + case "__memory_grow": { + assertArgs(1); + lowerExpr(fcx, instrs, expr.args[0]); + instrs.push({ kind: "memory.grow" }); + break exprKind; + } + case "__i32_extend_to_i64_u": { + assertArgs(1); + lowerExpr(fcx, instrs, expr.args[0]); + instrs.push({ kind: "i64.extend_i32_u" }); + break exprKind; + } } } @@ -1016,18 +1058,18 @@ function addRt(cx: Context, crates: Crate[]) { type: internFuncType(cx, { params: [POINTER, USIZE], returns: [] }), body: [ // get the pointer and store it in the iovec - { kind: "i32.const", imm: iovecArray }, + { kind: "i32.const", imm: BigInt(iovecArray) }, { kind: "local.get", imm: 0 }, { kind: "i32.store", imm: { offset: 0, align: 4 } }, // get the length and store it in the iovec - { kind: "i32.const", imm: iovecArray + 4 }, + { kind: "i32.const", imm: BigInt(iovecArray + 4) }, { kind: "local.get", imm: 1 }, { kind: "i32.store", imm: { offset: 0, align: 4 } }, // now call stuff - { kind: "i32.const", imm: /*stdout*/ 1 }, - { kind: "i32.const", imm: iovecArray }, - { kind: "i32.const", imm: /*iovec len*/ 1 }, - { kind: "i32.const", imm: /*out ptr*/ printReturnValue }, + { kind: "i32.const", imm: /*stdout*/ 1n }, + { kind: "i32.const", imm: BigInt(iovecArray) }, + { kind: "i32.const", imm: /*iovec len*/ 1n }, + { kind: "i32.const", imm: /*out ptr*/ BigInt(printReturnValue) }, { kind: "call", func: 0 }, { kind: "drop" }, ], diff --git a/src/parser.ts b/src/parser.ts index 8976efd..512eb35 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -31,7 +31,7 @@ import { ItemId, GlobalItem, } from "./ast"; -import { CompilerError, Span, spanMerge } from "./error"; +import { CompilerError, DUMMY_SPAN, EOF_SPAN, Span, spanMerge } from "./error"; import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer"; import { ComplexMap, ComplexSet, Ids } from "./utils"; @@ -165,7 +165,7 @@ function parseItem(t: Token[]): [Token[], Item] { const contents: Item[] = []; - while (next(t)[1].kind !== ")") { + while (peekKind(t) !== ")") { let item; [t, item] = parseItem(t); @@ -287,13 +287,15 @@ function mkParserExprBinary( let lhs; [t, lhs] = lower(t); - const [, peek] = next(t); - if (kinds.includes(peek.kind)) { - [t] = next(t); + const peek = peekKind(t); + if (peek && kinds.includes(peek)) { + let tok; + [t, tok] = next(t); let rhs; [t, rhs] = parser(t); const span = spanMerge(lhs.span, rhs.span); - return [t, mkExpr(lhs, rhs, span, peek.kind)]; + + return [t, mkExpr(lhs, rhs, span, tok.kind)]; } return [t, lhs]; @@ -326,17 +328,19 @@ const parseExprAssignment = mkParserExprBinary( ); function parseExprUnary(t: Token[]): [Token[], Expr] { - const [, peak] = next(t); - if (peak.kind in UNARY_KINDS) { + const peek = peekKind(t); + if (peek && UNARY_KINDS.includes(peek as UnaryKind)) { + let tok: Token; + [t, tok] = expectNext(t, peek); let rhs; [t, rhs] = parseExprUnary(t); return [ t, { kind: "unary", - unaryKind: peak.kind as UnaryKind, + unaryKind: tok.kind as UnaryKind, rhs, - span: peak.span, + span: tok.span, }, ]; } @@ -347,7 +351,7 @@ function parseExprCall(t: Token[]): [Token[], Expr] { let lhs: Expr; [t, lhs] = parseExprAtom(t); - while (next(t)[1].kind === "(" || next(t)[1].kind === ".") { + while (peekKind(t) === "(" || peekKind(t) === ".") { let tok; [t, tok] = next(t); @@ -402,7 +406,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] { // It's a block. if (peek.kind === ";") { const exprs = [expr]; - while (next(t)[1].kind !== ")") { + while (peekKind(t) !== ")") { [t] = expectNext(t, ";"); [t, expr] = parseExpr(t); exprs.push(expr); @@ -578,14 +582,14 @@ function parseType(t: Token[]): [Token[], Type] { // `()` is a the unit type, an empty tuple. // `(T)` is just `T` // `(T,)` is a tuple - if (next(t)[1]?.kind === ")") { + if (peekKind(t) === ")") { [t] = next(t); return [t, { kind: "tuple", elems: [], span }]; } let head; [t, head] = parseType(t); - if (next(t)[1]?.kind === ")") { + if (peekKind(t) === ")") { [t] = next(t); // Just a type inside parens, not a tuple. `(T,)` is a tuple. return [t, head]; @@ -618,7 +622,7 @@ function parseCommaSeparatedList( // () | (a) | (a,) | (a, b) - while (next(t)[1]?.kind !== terminator) { + while (peekKind(t) !== terminator) { let nextValue; [t, nextValue] = parser(t); @@ -629,7 +633,7 @@ function parseCommaSeparatedList( if (!comma) { // No comma? Fine, you don't like trailing commas. // But this better be the end. - if (next(t)[1]?.kind !== terminator) { + if (peekKind(t) !== terminator) { unexpectedToken(next(t)[1], `, or ${terminator}`); } break; @@ -645,19 +649,28 @@ function eat( t: Token[], kind: T["kind"] ): [Token[], T | undefined] { - const [tnext, tok] = next(t); - if (tok.kind === kind) { - return [tnext, tok as unknown as T]; + if (peekKind(t) === kind) { + return expectNext(t, kind); } return [t, undefined]; } +function peekKind(t: Token[]): Token["kind"] | undefined { + return maybeNextT(t)?.[1]?.kind; +} + function expectNext( t: Token[], kind: T["kind"] ): [Token[], T & Token] { let tok; - [t, tok] = next(t); + [t, tok] = maybeNextT(t); + if (!tok) { + throw new CompilerError( + `expected \`${kind}\`, found end of file`, + EOF_SPAN + ); + } if (tok.kind !== kind) { throw new CompilerError( `expected \`${kind}\`, found \`${tok.kind}\``, @@ -670,10 +683,7 @@ function expectNext( function next(t: Token[]): [Token[], Token] { const [rest, next] = maybeNextT(t); if (!next) { - throw new CompilerError("unexpected end of file", { - start: Number.MAX_SAFE_INTEGER, - end: Number.MAX_SAFE_INTEGER, - }); + throw new CompilerError("unexpected end of file", EOF_SPAN); } return [rest, next]; } @@ -757,6 +767,7 @@ function buildCrate( crateId: number ): Crate { const itemId = new Ids(); + itemId.next(); // crate root ID const loopId = new Ids(); const ast: Crate = { @@ -789,5 +800,7 @@ function buildCrate( }, }; - return foldAst(ast, assigner); + const crate = foldAst(ast, assigner); + + return crate; } diff --git a/src/resolve.ts b/src/resolve.ts index fd47ec8..dd7a763 100644 --- a/src/resolve.ts +++ b/src/resolve.ts @@ -18,6 +18,7 @@ import { superFoldType, ExternItem, Typecked, + findCrateItem, } from "./ast"; import { CompilerError, Span, spanMerge } from "./error"; import { ComplexMap, Ids, unwrap } from "./utils"; @@ -40,6 +41,13 @@ type Context = { crateId: Ids; }; +function findItem(cx: Context, id: ItemId): Item { + const crate = unwrap( + [cx.ast, ...cx.crates].find((crate) => crate.id === id.crateId) + ); + return findCrateItem(crate, id); +} + function resolveModItem( cx: Context, mod: ModItem | ExternItem, @@ -56,21 +64,15 @@ function resolveModItem( if ("contents" in mod) { contents = new Map(mod.contents.map((item) => [item.node.name, item.id])); } else { - console.log("resolve..."); - const [loadedCrate, itsDeps] = cx.crateLoader( item.node.name, item.span, cx.crateId, cx.crates ); - console.log("hmm"); - cx.crates.push(loadedCrate); cx.crates.push(...itsDeps); - console.log(cx.crates); - contents = new Map( loadedCrate.rootItems.map((item) => [item.node.name, item.id]) ); @@ -157,6 +159,13 @@ function resolveModule( }; } + if (ident.name === cx.ast.packageName) { + return { + kind: "item", + id: new ItemId(cx.ast.id, 0), + }; + } + if (BUILTIN_SET.has(ident.name)) { return { kind: "builtin", name: ident.name as BuiltinName }; } @@ -271,7 +280,8 @@ function resolveModule( lhs.kind === "ident" ? [lhs.value.name] : lhs.segments; if (res.kind === "item") { - const module = unwrap(cx.ast.itemsById.get(res.id)); + const module = findItem(cx, res.id); + if (module.kind === "mod" || module.kind === "extern") { if (typeof expr.field.value === "number") { throw new CompilerError( diff --git a/src/typeck.ts b/src/typeck.ts index b9f8ebe..737f5da 100644 --- a/src/typeck.ts +++ b/src/typeck.ts @@ -29,6 +29,7 @@ import { Typecked, TyStruct, Item, + findCrateItem, } from "./ast"; import { CompilerError, Span } from "./error"; import { printTy } from "./printer"; @@ -79,6 +80,12 @@ function typeOfBuiltinValue(name: BuiltinName, span: Span): Ty { return mkTyFn([TY_STRING], TY_I32); case "__string_len": return mkTyFn([TY_STRING], TY_I32); + case "__memory_size": + return mkTyFn([], TY_I32); + case "__memory_grow": + return mkTyFn([TY_I32], TY_I32); + case "__i32_extend_to_i64_u": + return mkTyFn([TY_I32], TY_INT); default: { throw new CompilerError(`\`${name}\` cannot be used as a value`, span); } @@ -123,12 +130,10 @@ export function typeck( function typeOfItem(itemId: ItemId, cause: Span): Ty { if (itemId.crateId !== ast.id) { - console.log(otherCrates); - const crate = unwrap( otherCrates.find((crate) => crate.id === itemId.crateId) ); - const item = unwrap(crate.itemsById.get(itemId)); + const item = findCrateItem(crate, itemId); switch (item.kind) { case "function": case "import": @@ -150,7 +155,7 @@ export function typeck( } } - const item = unwrap(ast.itemsById.get(itemId)); + const item = findCrateItem(ast, itemId); const ty = itemTys.get(itemId); if (ty) { return ty; @@ -236,12 +241,11 @@ export function typeck( function findItem(itemId: ItemId): Item { if (itemId.crateId === ast.id) { - return unwrap(ast.itemsById.get(itemId)); + return findCrateItem(ast, itemId); } - return unwrap( - unwrap( - otherCrates.find((crate) => crate.id === itemId.crateId) - ).itemsById.get(itemId) + return findCrateItem( + unwrap(otherCrates.find((crate) => crate.id === itemId.crateId)), + itemId ); } @@ -1056,6 +1060,10 @@ function checkBinary( return { ...expr, lhs, rhs, ty: TY_BOOL }; } + if (lhsTy.kind === "i32" && rhsTy.kind === "i32") { + return { ...expr, lhs, rhs, ty: TY_BOOL }; + } + if (lhsTy.kind === "string" && rhsTy.kind === "string") { return { ...expr, lhs, rhs, ty: TY_BOOL }; } @@ -1096,7 +1104,7 @@ function checkUnary( if ( expr.unaryKind === "!" && - (rhsTy.kind === "int" || rhsTy.kind === "bool") + (rhsTy.kind === "int" || rhsTy.kind === "i32" || rhsTy.kind === "bool") ) { return { ...expr, rhs, ty: rhsTy }; } diff --git a/src/wasm/defs.ts b/src/wasm/defs.ts index 303ed3f..1a9a5a7 100644 --- a/src/wasm/defs.ts +++ b/src/wasm/defs.ts @@ -4,7 +4,7 @@ // Base types. export type u32 = number; -export type u64 = number; +export type u64 = bigint; export type f32 = number; export type f64 = number; export type VecByte = Uint8Array; @@ -73,7 +73,7 @@ export type BitWidth = "32" | "64"; export type Sign = "u" | "s"; export type NumericInstr = - | { kind: "i32.const"; imm: u32 } + | { kind: "i32.const"; imm: bigint } | { kind: "i64.const"; imm: u64 } | { kind: "f32.const"; imm: f32 } | { kind: "f64.const"; imm: f64 } diff --git a/src/wasm/wat.test.ts b/src/wasm/wat.test.ts index a2cd690..a3dc574 100644 --- a/src/wasm/wat.test.ts +++ b/src/wasm/wat.test.ts @@ -29,7 +29,7 @@ const EXAMPLE_MODULE: Module = { type: { kind: "typeidx", idx: 1 }, instrs: [{ kind: "local.get", imm: 0 }], }, - { kind: "i32.const", imm: 1 }, + { kind: "i32.const", imm: 1n }, { kind: "i32.add" }, ], }, @@ -49,7 +49,7 @@ const EXAMPLE_MODULE: Module = { globals: [ { type: { mut: "const", type: "i32" }, - init: [{ kind: "i32.const", imm: 0 }], + init: [{ kind: "i32.const", imm: 0n }], _name: "globalling", }, ], @@ -70,7 +70,7 @@ const EXAMPLE_MODULE: Module = { mode: { kind: "active", memory: 0, - offset: [{ kind: "i32.const", imm: 0 }], + offset: [{ kind: "i32.const", imm: 0n }], }, init: new Uint8Array(), _name: "very-active-data", @@ -86,7 +86,7 @@ it("should print a Wasm module with the correct formatting", () => { (type (func (param i32) (result i32))) (type (func (param) (result i32))) (import "left-pad" "padLeft" (func (type 0))) - (func $addOne (type 0) + (func $addOne (;1;) (type 0) (local i32 i32) local.set 0 block (type 1) diff --git a/src/wasm/wat.ts b/src/wasm/wat.ts index 551afad..a7710fc 100644 --- a/src/wasm/wat.ts +++ b/src/wasm/wat.ts @@ -72,7 +72,7 @@ class FmtCtx { this.word(word, chalk.blue); } - type(word: string | number) { + type(word: string | number | bigint) { this.word(word, chalk.green); } @@ -90,7 +90,10 @@ class FmtCtx { } } - word(word: string | number, color: (s: string) => string = identity) { + word( + word: string | number | bigint, + color: (s: string) => string = identity + ) { const last = this.wordsInSexpr.length - 1; if (this.wordsInSexpr[last] > 0 && !this.freshLinebreak) { // The first word hugs the left parenthesis. @@ -436,9 +439,16 @@ function printInstr(instr: Instr, f: FmtCtx) { case "call": { f.controlFlow(instr.kind); f.word(instr.func); - const name = f.mod.funcs[instr.func]?._name; - if (name !== undefined) { - f.comment(name); + if (instr.func < f.mod.imports.length) { + const name = f.mod.imports[instr.func]?.name; + if (name !== undefined) { + f.comment(name); + } + } else { + const name = f.mod.funcs[instr.func - f.mod.imports.length]?._name; + if (name !== undefined) { + f.comment(name); + } } break; } diff --git a/std.nil b/std.nil index 95d99d2..d140594 100644 --- a/std.nil +++ b/std.nil @@ -1,8 +1,15 @@ +function printlnI32(x: I32) = ( + printI32(x); + print("\n"); +); + function printlnInt(x: Int) = ( printInt(x); print("\n"); ); +function printI32(x: I32) = printInt(i32ToInt(x)); + function printInt(x: Int) = ( let mag = log10(x); @@ -58,8 +65,59 @@ function println(s: String) = ( print("\n"); ); -mod alloc ( - function allocateItem(size: I32): I32 = ( - +mod rt ( + // Start the heap at 1024. In practice this could probably be as low as we want. + global BASE_PTR: I32 = 1024_I32; + global HEAD_PTR: I32 = 1024_I32; + + /* + Every struct has a header of an I32 as a refcount. + */ + + // Allocate a new item. We do not deallocate anything yet. + // lol. + function allocateItem(objSize: I32, align: I32): I32 = ( + if align < 4_I32 then std.abort("invalid alignment"); + + // Include the refcount header. + let actualSize = 4_I32 + objSize; + + // Let's see whether we can fit the refcount into the align bits. + // I happen to know that everything will always be at least 4 bytes aligned. + let alignedPtr = std.alignUp(HEAD_PTR, align); + let actualObjPtr = if (alignedPtr - HEAD_PTR) > align then ( + alignedPtr - 4_I32 + ) else ( + // Take up the next spot. + alignedPtr + align - 4_I32 + ); + + let newHeadPtr = actualObjPtr + actualSize; + + if newHeadPtr > __memory_size() then ( + // 16 pages, very arbitrary. + let result = __memory_grow(16_I32); + // If allocation failed we get -1. We don't have negative numbers yet, lol. + if result > 100000000_I32 then ( + std.abort("failed to grow memory"); + ); + ); + + 0_I32 ); -) \ No newline at end of file +); + +function alignUp(x: I32, align: I32): I32 = (x + (align - 1_I32)) & !(align - 1_I32); + +function i32ToInt(x: I32): Int = __i32_extend_to_i64_u(x); + +function abort(message: String) = ( + print("fatal error: "); + print(message); + println(".. aborting"); + trap(); +); + +function main() = ( + std.rt.allocateItem(100000000_I32, 8_I32); +); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index b520cbf..4eee238 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */