mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 08:25:02 +01:00
many things
This commit is contained in:
parent
f582a5b4c3
commit
924236532c
13 changed files with 264 additions and 85 deletions
30
src/ast.ts
30
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<P extends Phase>(
|
||||
crate: Crate<P>,
|
||||
id: ItemId
|
||||
): Item<P> {
|
||||
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<From, To> = (value: From) => To;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -13,10 +13,12 @@ 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);
|
||||
);
|
||||
`;
|
||||
|
||||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
82
src/lower.ts
82
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<Typecked> {
|
||||
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<Typecked>,
|
||||
|
|
@ -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<Typecked>[]) {
|
|||
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" },
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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<Parsed>] {
|
|||
|
||||
const contents: Item<Parsed>[] = [];
|
||||
|
||||
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<Parsed>] {
|
||||
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<Parsed>] {
|
|||
let lhs: Expr<Parsed>;
|
||||
[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<Parsed>] {
|
|||
// 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<Parsed>] {
|
|||
// `()` 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<R>(
|
|||
|
||||
// () | (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<R>(
|
|||
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 extends BaseToken>(
|
|||
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 extends BaseToken>(
|
||||
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<T extends BaseToken>(
|
|||
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<Built> {
|
||||
const itemId = new Ids();
|
||||
itemId.next(); // crate root ID
|
||||
const loopId = new Ids();
|
||||
|
||||
const ast: Crate<Built> = {
|
||||
|
|
@ -789,5 +800,7 @@ function buildCrate(
|
|||
},
|
||||
};
|
||||
|
||||
return foldAst(ast, assigner);
|
||||
const crate = foldAst(ast, assigner);
|
||||
|
||||
return crate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Built> {
|
||||
const crate = unwrap(
|
||||
[cx.ast, ...cx.crates].find((crate) => crate.id === id.crateId)
|
||||
);
|
||||
return findCrateItem(crate, id);
|
||||
}
|
||||
|
||||
function resolveModItem(
|
||||
cx: Context,
|
||||
mod: ModItem<Built> | 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(
|
||||
|
|
|
|||
|
|
@ -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<Resolved> {
|
||||
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 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,10 +439,17 @@ 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 (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;
|
||||
}
|
||||
case "call_indirect":
|
||||
|
|
|
|||
64
std.nil
64
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
|
||||
);
|
||||
);
|
||||
|
||||
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);
|
||||
);
|
||||
|
|
@ -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. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue