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. */