mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
globals
This commit is contained in:
parent
b779a51ef5
commit
f582a5b4c3
9 changed files with 248 additions and 85 deletions
25
src/ast.ts
25
src/ast.ts
|
|
@ -81,6 +81,9 @@ export class ItemId {
|
|||
}
|
||||
|
||||
toString(): string {
|
||||
if (this.crateId === 0) {
|
||||
return `${this.itemIdx}`;
|
||||
}
|
||||
return `[${this.crateId}@${this.itemIdx}]`;
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +108,10 @@ export type ItemKind<P extends Phase> =
|
|||
| {
|
||||
kind: "extern";
|
||||
node: ExternItem;
|
||||
}
|
||||
| {
|
||||
kind: "global";
|
||||
node: GlobalItem<P>;
|
||||
};
|
||||
|
||||
export type Item<P extends Phase> = ItemKind<P> & {
|
||||
|
|
@ -153,6 +160,13 @@ export type ModItem<P extends Phase> = {
|
|||
|
||||
export type ExternItem = { name: string };
|
||||
|
||||
export type GlobalItem<P extends Phase> = {
|
||||
name: string;
|
||||
type: Type<P>;
|
||||
init: Expr<P>;
|
||||
ty?: Ty;
|
||||
};
|
||||
|
||||
export type ExprEmpty = { kind: "empty" };
|
||||
|
||||
export type ExprLet<P extends Phase> = {
|
||||
|
|
@ -634,6 +648,17 @@ export function superFoldItem<From extends Phase, To extends Phase>(
|
|||
case "extern": {
|
||||
return { ...item, kind: "extern" };
|
||||
}
|
||||
case "global": {
|
||||
return {
|
||||
...item,
|
||||
kind: "global",
|
||||
node: {
|
||||
name: item.node.name,
|
||||
type: folder.type(item.node.type),
|
||||
init: folder.expr(item.node.init),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import { Crate, Built, Typecked } from "./ast";
|
|||
import { Ids } from "./utils";
|
||||
|
||||
const INPUT = `
|
||||
extern mod std;
|
||||
global HELLO: I32 = 0_I32;
|
||||
|
||||
function main() = (
|
||||
std.printlnInt(10000);
|
||||
HELLO = 1_I32;
|
||||
);
|
||||
`;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export type DatalessToken =
|
|||
| "import"
|
||||
| "extern"
|
||||
| "mod"
|
||||
| "global"
|
||||
| "("
|
||||
| ")"
|
||||
| "{"
|
||||
|
|
@ -316,6 +317,7 @@ const KEYOWRDS: DatalessToken[] = [
|
|||
"import",
|
||||
"extern",
|
||||
"mod",
|
||||
"global",
|
||||
];
|
||||
|
||||
const KEYWORD_SET = new Set<string>(KEYOWRDS);
|
||||
|
|
|
|||
126
src/lower.ts
126
src/lower.ts
|
|
@ -3,8 +3,10 @@ import {
|
|||
Expr,
|
||||
ExprBlock,
|
||||
FunctionDef,
|
||||
GlobalItem,
|
||||
ImportDef,
|
||||
Item,
|
||||
ItemId,
|
||||
LoopId,
|
||||
Resolution,
|
||||
Ty,
|
||||
|
|
@ -13,6 +15,7 @@ import {
|
|||
Typecked,
|
||||
varUnreachable,
|
||||
} from "./ast";
|
||||
import { printTy } from "./printer";
|
||||
import { ComplexMap, encodeUtf8, unwrap } from "./utils";
|
||||
import * as wasm from "./wasm/defs";
|
||||
|
||||
|
|
@ -25,10 +28,19 @@ const STRING_ABI: ArgRetAbi = STRING_TYPES;
|
|||
|
||||
const WASM_PAGE = 65536;
|
||||
|
||||
type Relocation = {
|
||||
kind: "funccall";
|
||||
instr: wasm.Instr & { func: wasm.FuncIdx };
|
||||
} & { res: Resolution };
|
||||
const DUMMY_IDX = 9999999;
|
||||
|
||||
type RelocationKind =
|
||||
| {
|
||||
kind: "funccall";
|
||||
instr: wasm.Instr & { func: wasm.FuncIdx };
|
||||
}
|
||||
| {
|
||||
kind: "globalref";
|
||||
instr: wasm.Instr & { imm: wasm.GlobalIdx };
|
||||
};
|
||||
|
||||
type Relocation = RelocationKind & { res: Resolution };
|
||||
|
||||
type FuncOrImport =
|
||||
| { kind: "func"; idx: wasm.FuncIdx }
|
||||
|
|
@ -39,6 +51,7 @@ export type Context = {
|
|||
funcTypes: ComplexMap<wasm.FuncType, wasm.TypeIdx>;
|
||||
reservedHeapMemoryStart: number;
|
||||
funcIndices: ComplexMap<Resolution, FuncOrImport>;
|
||||
globalIndices: ComplexMap<Resolution, wasm.GlobalIdx>;
|
||||
crates: Crate<Typecked>[];
|
||||
relocations: Relocation[];
|
||||
};
|
||||
|
|
@ -89,6 +102,12 @@ 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)
|
||||
);
|
||||
}
|
||||
|
||||
export function lower(crates: Crate<Typecked>[]): wasm.Module {
|
||||
const mod: wasm.Module = {
|
||||
types: [],
|
||||
|
|
@ -118,6 +137,7 @@ export function lower(crates: Crate<Typecked>[]): wasm.Module {
|
|||
mod,
|
||||
funcTypes: new ComplexMap(),
|
||||
funcIndices: new ComplexMap(),
|
||||
globalIndices: new ComplexMap(),
|
||||
reservedHeapMemoryStart: 0,
|
||||
crates,
|
||||
relocations: [],
|
||||
|
|
@ -136,6 +156,17 @@ export function lower(crates: Crate<Typecked>[]): wasm.Module {
|
|||
}
|
||||
case "mod": {
|
||||
lowerMod(item.node.contents);
|
||||
break;
|
||||
}
|
||||
case "global": {
|
||||
lowerGlobal(cx, item, item.node);
|
||||
break;
|
||||
}
|
||||
case "extern":
|
||||
case "type":
|
||||
break;
|
||||
default: {
|
||||
const _: never = item;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -162,6 +193,20 @@ export function lower(crates: Crate<Typecked>[]): wasm.Module {
|
|||
);
|
||||
}
|
||||
rel.instr.func = idx.kind === "func" ? offset + idx.idx : idx.idx;
|
||||
break;
|
||||
}
|
||||
case "globalref": {
|
||||
const idx = cx.globalIndices.get(rel.res);
|
||||
if (idx === undefined) {
|
||||
throw new Error(
|
||||
`no global found for relocation '${JSON.stringify(rel.res)}'`
|
||||
);
|
||||
}
|
||||
rel.instr.imm = idx;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const _: never = rel;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -201,6 +246,43 @@ function lowerImport(
|
|||
cx.funcIndices.set({ kind: "item", id: item.id }, { kind: "import", idx });
|
||||
}
|
||||
|
||||
function lowerGlobal(
|
||||
cx: Context,
|
||||
item: Item<Typecked>,
|
||||
def: GlobalItem<Typecked>
|
||||
) {
|
||||
const globalIdx = cx.mod.globals.length;
|
||||
|
||||
let valtype: "i32" | "i64";
|
||||
switch (def.init.ty.kind) {
|
||||
case "i32":
|
||||
valtype = "i32";
|
||||
break;
|
||||
case "int":
|
||||
valtype = "i64";
|
||||
break;
|
||||
default:
|
||||
throw new Error(`invalid global ty: ${printTy(def.init.ty)}`);
|
||||
}
|
||||
|
||||
if (def.init.kind !== "literal" || def.init.value.kind !== "int") {
|
||||
throw new Error(`invalid global init: ${JSON.stringify(def)}`);
|
||||
}
|
||||
|
||||
const init: wasm.Instr = {
|
||||
kind: `${valtype}.const`,
|
||||
imm: def.init.value.value,
|
||||
};
|
||||
|
||||
cx.mod.globals.push({
|
||||
_name: mangleDefPath(item.defPath),
|
||||
type: { type: valtype, mut: "var" },
|
||||
init: [init],
|
||||
});
|
||||
|
||||
cx.globalIndices.set({ kind: "item", id: item.id }, globalIdx);
|
||||
}
|
||||
|
||||
type FuncContext = {
|
||||
cx: Context;
|
||||
item: Item<Typecked>;
|
||||
|
|
@ -304,7 +386,18 @@ function lowerExpr(
|
|||
break;
|
||||
}
|
||||
case "item": {
|
||||
throw new Error("cannot store to item");
|
||||
const item = findItem(fcx.cx, res.id);
|
||||
if (item.kind !== "global") {
|
||||
throw new Error("cannot store to non-global item");
|
||||
}
|
||||
|
||||
const instr: wasm.Instr = { kind: "global.set", imm: DUMMY_IDX };
|
||||
const rel: Relocation = { kind: "globalref", instr, res };
|
||||
|
||||
fcx.cx.relocations.push(rel);
|
||||
instrs.push(instr);
|
||||
|
||||
break;
|
||||
}
|
||||
case "builtin": {
|
||||
throw new Error("cannot store to builtin");
|
||||
|
|
@ -374,8 +467,23 @@ function lowerExpr(
|
|||
loadVariable(instrs, location);
|
||||
break;
|
||||
}
|
||||
case "item":
|
||||
todo("item ident res");
|
||||
case "item": {
|
||||
const item = findItem(fcx.cx, res.id);
|
||||
switch (item.kind) {
|
||||
case "global": {
|
||||
const instr: wasm.Instr = { kind: "global.get", imm: DUMMY_IDX };
|
||||
const rel: Relocation = { kind: "globalref", instr, res };
|
||||
instrs.push(instr);
|
||||
fcx.cx.relocations.push(rel);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
todo("non-global item ident res");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "builtin":
|
||||
switch (res.name) {
|
||||
case "false":
|
||||
|
|
@ -561,7 +669,7 @@ function lowerExpr(
|
|||
}
|
||||
}
|
||||
|
||||
const callInstr: wasm.Instr = { kind: "call", func: 9999999999 };
|
||||
const callInstr: wasm.Instr = { kind: "call", func: DUMMY_IDX };
|
||||
fcx.cx.relocations.push({
|
||||
kind: "funccall",
|
||||
instr: callInstr,
|
||||
|
|
@ -865,7 +973,7 @@ function addRt(cx: Context, crates: Crate<Typecked>[]) {
|
|||
|
||||
const crate0 = unwrap(crates.find((crate) => crate.id === 0));
|
||||
|
||||
const mainCall: wasm.Instr = { kind: "call", func: 9999999 };
|
||||
const mainCall: wasm.Instr = { kind: "call", func: DUMMY_IDX };
|
||||
cx.relocations.push({
|
||||
kind: "funccall",
|
||||
instr: mainCall,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import {
|
|||
Parsed,
|
||||
ExternItem,
|
||||
ItemId,
|
||||
GlobalItem,
|
||||
} from "./ast";
|
||||
import { CompilerError, Span, spanMerge } from "./error";
|
||||
import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer";
|
||||
|
|
@ -180,6 +181,24 @@ function parseItem(t: Token[]): [Token[], Item<Parsed>] {
|
|||
};
|
||||
|
||||
return [t, { kind: "mod", node, span: name.span, id: ItemId.dummy() }];
|
||||
} else if (tok.kind === "global") {
|
||||
let name;
|
||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||
[t] = expectNext(t, ":");
|
||||
let type;
|
||||
[t, type] = parseType(t);
|
||||
[t] = expectNext(t, "=");
|
||||
let init;
|
||||
[t, init] = parseExpr(t);
|
||||
[t] = expectNext(t, ";");
|
||||
|
||||
const node: GlobalItem<Parsed> = {
|
||||
name: name.ident,
|
||||
type,
|
||||
init,
|
||||
};
|
||||
|
||||
return [t, { kind: "global", node, span: name.span, id: ItemId.dummy() }];
|
||||
} else {
|
||||
unexpectedToken(tok, "item");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ function printStringLiteral(lit: StringLiteral): string {
|
|||
}
|
||||
|
||||
function printItem(item: Item<AnyPhase>): string {
|
||||
const id = `/*${item.id}*/ `;
|
||||
const id = `/*${item.id.toString()}*/ `;
|
||||
|
||||
switch (item.kind) {
|
||||
case "function": {
|
||||
|
|
@ -42,6 +42,15 @@ function printItem(item: Item<AnyPhase>): string {
|
|||
case "extern": {
|
||||
return id + `extern mod ${item.node.name};`;
|
||||
}
|
||||
case "global": {
|
||||
return (
|
||||
id +
|
||||
`global ${item.node.name}: ${printType(item.node.type)} = ${printExpr(
|
||||
item.node.init,
|
||||
0
|
||||
)};`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +216,7 @@ function printRes(res: Resolution): string {
|
|||
case "local":
|
||||
return `#${res.index}`;
|
||||
case "item":
|
||||
return `#G${res.id}`;
|
||||
return `#I${res.id.toString()}`;
|
||||
case "builtin": {
|
||||
return `#B`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ export function typeck(
|
|||
otherCrates: Crate<Typecked>[]
|
||||
): Crate<Typecked> {
|
||||
const itemTys = new ComplexMap<ItemId, Ty | null>();
|
||||
|
||||
function typeOfItem(itemId: ItemId, cause: Span): Ty {
|
||||
if (itemId.crateId !== ast.id) {
|
||||
console.log(otherCrates);
|
||||
|
|
@ -132,6 +133,7 @@ export function typeck(
|
|||
case "function":
|
||||
case "import":
|
||||
case "type":
|
||||
case "global":
|
||||
return item.node.ty!;
|
||||
case "mod": {
|
||||
throw new CompilerError(
|
||||
|
|
@ -203,6 +205,11 @@ export function typeck(
|
|||
cause
|
||||
);
|
||||
}
|
||||
case "global": {
|
||||
const ty = lowerAstTy(item.node.type);
|
||||
itemTys.set(item.id, ty);
|
||||
return ty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -227,13 +234,24 @@ export function typeck(
|
|||
);
|
||||
}
|
||||
|
||||
function findItem(itemId: ItemId): Item<Resolved> {
|
||||
if (itemId.crateId === ast.id) {
|
||||
return unwrap(ast.itemsById.get(itemId));
|
||||
}
|
||||
return unwrap(
|
||||
unwrap(
|
||||
otherCrates.find((crate) => crate.id === itemId.crateId)
|
||||
).itemsById.get(itemId)
|
||||
);
|
||||
}
|
||||
|
||||
const checker: Folder<Resolved, Typecked> = {
|
||||
...mkDefaultFolder(),
|
||||
itemInner(item: Item<Resolved>): Item<Typecked> {
|
||||
switch (item.kind) {
|
||||
case "function": {
|
||||
const fnTy = typeOfItem(item.id, item.span) as TyFn;
|
||||
const body = checkBody(item.node.body, fnTy, typeOfItem);
|
||||
const body = checkBody(item.node.body, fnTy, typeOfItem, findItem);
|
||||
|
||||
const returnType = item.node.returnType && {
|
||||
...item.node.returnType,
|
||||
|
|
@ -349,6 +367,30 @@ export function typeck(
|
|||
node: { ...item.node },
|
||||
};
|
||||
}
|
||||
case "global": {
|
||||
const ty = typeOfItem(item.id, item.span);
|
||||
const { init } = item.node;
|
||||
|
||||
if (init.kind !== "literal" || init.value.kind !== "int") {
|
||||
throw new CompilerError(
|
||||
"globals must be initialized with an integer literal",
|
||||
init.span
|
||||
);
|
||||
}
|
||||
|
||||
const initTy = init.value.type === "I32" ? TY_I32 : TY_INT;
|
||||
const infcx = new InferContext();
|
||||
infcx.assign(ty, initTy, init.span);
|
||||
|
||||
return {
|
||||
...item,
|
||||
node: {
|
||||
...item.node,
|
||||
ty,
|
||||
init: { ...init, ty },
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
expr(_expr) {
|
||||
|
|
@ -560,7 +602,8 @@ export class InferContext {
|
|||
export function checkBody(
|
||||
body: Expr<Resolved>,
|
||||
fnTy: TyFn,
|
||||
typeOfItem: (itemId: ItemId, cause: Span) => Ty
|
||||
typeOfItem: (itemId: ItemId, cause: Span) => Ty,
|
||||
findItem: (itemId: ItemId) => Item<Resolved>
|
||||
): Expr<Typecked> {
|
||||
const localTys = [...fnTy.params];
|
||||
const loopState: { hasBreak: boolean; loopId: LoopId }[] = [];
|
||||
|
|
@ -644,11 +687,26 @@ export function checkBody(
|
|||
infcx.assign(lhs.ty, rhs.ty, expr.span);
|
||||
|
||||
switch (lhs.kind) {
|
||||
case "ident":
|
||||
if (lhs.value.res.kind !== "local") {
|
||||
throw new CompilerError("cannot assign to items", expr.span);
|
||||
case "ident": {
|
||||
const { res } = lhs.value;
|
||||
switch (res.kind) {
|
||||
case "local":
|
||||
break;
|
||||
case "item": {
|
||||
const item = findItem(res.id);
|
||||
if (item.kind !== "global") {
|
||||
throw new CompilerError("cannot assign to item", expr.span);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "builtin":
|
||||
throw new CompilerError(
|
||||
"cannot assign to builtins",
|
||||
expr.span
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new CompilerError(
|
||||
"invalid left-hand side of assignment",
|
||||
|
|
|
|||
6
std.nil
6
std.nil
|
|
@ -57,3 +57,9 @@ function println(s: String) = (
|
|||
print(s);
|
||||
print("\n");
|
||||
);
|
||||
|
||||
mod alloc (
|
||||
function allocateItem(size: I32): I32 = (
|
||||
|
||||
);
|
||||
)
|
||||
70
test.nil
70
test.nil
|
|
@ -1,69 +1,5 @@
|
|||
mod owo ();
|
||||
extern mod std;
|
||||
|
||||
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");
|
||||
);
|
||||
std.printInt(10);
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue