diff --git a/src/ast.ts b/src/ast.ts
index fd74c0c..0b7f7c0 100644
--- a/src/ast.ts
+++ b/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
=
| {
kind: "extern";
node: ExternItem;
+ }
+ | {
+ kind: "global";
+ node: GlobalItem
;
};
export type Item
= ItemKind
& {
@@ -153,6 +160,13 @@ export type ModItem
= {
export type ExternItem = { name: string };
+export type GlobalItem
= {
+ name: string;
+ type: Type
;
+ init: Expr
;
+ ty?: Ty;
+};
+
export type ExprEmpty = { kind: "empty" };
export type ExprLet
= {
@@ -634,6 +648,17 @@ export function superFoldItem(
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),
+ },
+ };
+ }
}
}
diff --git a/src/index.ts b/src/index.ts
index 124895f..0e7edc1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -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;
);
`;
diff --git a/src/lexer.ts b/src/lexer.ts
index 902c923..f6d0dda 100644
--- a/src/lexer.ts
+++ b/src/lexer.ts
@@ -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(KEYOWRDS);
diff --git a/src/lower.ts b/src/lower.ts
index 15adbd2..dc78b97 100644
--- a/src/lower.ts
+++ b/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;
reservedHeapMemoryStart: number;
funcIndices: ComplexMap;
+ globalIndices: ComplexMap;
crates: Crate[];
relocations: Relocation[];
};
@@ -89,6 +102,12 @@ 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)
+ );
+}
+
export function lower(crates: Crate[]): wasm.Module {
const mod: wasm.Module = {
types: [],
@@ -118,6 +137,7 @@ export function lower(crates: Crate[]): 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[]): 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[]): 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,
+ def: GlobalItem
+) {
+ 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;
@@ -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[]) {
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,
diff --git a/src/parser.ts b/src/parser.ts
index 2783ca0..8976efd 100644
--- a/src/parser.ts
+++ b/src/parser.ts
@@ -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] {
};
return [t, { kind: "mod", node, span: name.span, id: ItemId.dummy() }];
+ } else if (tok.kind === "global") {
+ let name;
+ [t, name] = expectNext(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 = {
+ name: name.ident,
+ type,
+ init,
+ };
+
+ return [t, { kind: "global", node, span: name.span, id: ItemId.dummy() }];
} else {
unexpectedToken(tok, "item");
}
diff --git a/src/printer.ts b/src/printer.ts
index 76ea7cf..2101ae9 100644
--- a/src/printer.ts
+++ b/src/printer.ts
@@ -24,7 +24,7 @@ function printStringLiteral(lit: StringLiteral): string {
}
function printItem(item: Item): string {
- const id = `/*${item.id}*/ `;
+ const id = `/*${item.id.toString()}*/ `;
switch (item.kind) {
case "function": {
@@ -42,6 +42,15 @@ function printItem(item: Item): 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`;
}
diff --git a/src/typeck.ts b/src/typeck.ts
index 2d2dc75..b9f8ebe 100644
--- a/src/typeck.ts
+++ b/src/typeck.ts
@@ -120,6 +120,7 @@ export function typeck(
otherCrates: Crate[]
): Crate {
const itemTys = new ComplexMap();
+
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 {
+ 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 = {
...mkDefaultFolder(),
itemInner(item: Item): Item {
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,
fnTy: TyFn,
- typeOfItem: (itemId: ItemId, cause: Span) => Ty
+ typeOfItem: (itemId: ItemId, cause: Span) => Ty,
+ findItem: (itemId: ItemId) => Item
): Expr {
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",
diff --git a/std.nil b/std.nil
index 2692372..95d99d2 100644
--- a/std.nil
+++ b/std.nil
@@ -57,3 +57,9 @@ function println(s: String) = (
print(s);
print("\n");
);
+
+mod alloc (
+ function allocateItem(size: I32): I32 = (
+
+ );
+)
\ No newline at end of file
diff --git a/test.nil b/test.nil
index f169233..1b59e0d 100644
--- a/test.nil
+++ b/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);
+);
\ No newline at end of file