>;
packageName: string;
};
-export type Identifier = {
+export type Phase = {
+ res: unknown;
+};
+
+type NoRes = object;
+type HasRes = { res: Resolution };
+
+export type Parsed = {
+ res: NoRes;
+};
+export type Built = {
+ res: NoRes;
+};
+export type Resolved = {
+ res: HasRes;
+};
+export type Typecked = {
+ res: HasRes;
+};
+export type AnyPhase = {
+ res: NoRes | HasRes;
+};
+
+export type Ident = {
name: string;
span: Span;
- res?: Resolution;
};
+export type IdentWithRes = {
+ name: string;
+ span: Span;
+} & P["res"];
+
export type ItemId = number;
-export type ItemKind =
+export type ItemKind
=
| {
kind: "function";
- node: FunctionDef;
+ node: FunctionDef
;
}
| {
kind: "type";
- node: TypeDef;
+ node: TypeDef
;
}
| {
kind: "import";
- node: ImportDef;
+ node: ImportDef
;
}
| {
kind: "mod";
- node: ModItem;
+ node: ModItem
;
};
-export type Item = ItemKind & {
+export type Item
= ItemKind
& {
span: Span;
id: ItemId;
defPath?: string[];
};
-export type FunctionDef = {
+export type FunctionDef
= {
name: string;
- params: FunctionArg[];
- body: Expr;
- returnType?: Type;
+ params: FunctionArg
[];
+ body: Expr
;
+ returnType?: Type
;
ty?: TyFn;
};
-export type FunctionArg = {
+export type FunctionArg
= {
name: string;
- type: Type;
+ type: Type
;
span: Span;
};
-export type TypeDef = {
+export type TypeDef
= {
name: string;
- fields: FieldDef[];
+ fields: FieldDef
[];
ty?: TyStruct;
};
-export type FieldDef = {
- name: Identifier;
- type: Type;
+export type FieldDef
= {
+ name: Ident;
+ type: Type
;
};
-export type ImportDef = {
+export type ImportDef
= {
module: StringLiteral;
func: StringLiteral;
name: string;
- params: FunctionArg[];
- returnType?: Type;
+ params: FunctionArg
[];
+ returnType?: Type
;
ty?: TyFn;
};
-export type ModItem = {
+export type ModItem
= {
name: string;
- modKind: ModItemKind;
+ modKind: ModItemKind
;
};
-export type ModItemKind =
+export type ModItemKind
=
| {
kind: "inline";
- contents: Item[];
+ contents: Item
[];
}
| {
kind: "extern";
@@ -90,11 +117,11 @@ export type ModItemKind =
export type ExprEmpty = { kind: "empty" };
-export type ExprLet = {
+export type ExprLet
= {
kind: "let";
- name: Identifier;
- type?: Type;
- rhs: Expr;
+ name: Ident;
+ type?: Type
;
+ rhs: Expr
;
// IMPORTANT: This is (sadly) shared with ExprBlock.
// TODO: Stop this sharing and just store the stack of blocks in typeck.
local?: LocalInfo;
@@ -102,15 +129,15 @@ export type ExprLet = {
// A bit like ExprBinary except there are restrictions
// on the LHS and precedence is unrestricted.
-export type ExprAssign = {
+export type ExprAssign
= {
kind: "assign";
- lhs: Expr;
- rhs: Expr;
+ lhs: Expr
;
+ rhs: Expr
;
};
-export type ExprBlock = {
+export type ExprBlock
= {
kind: "block";
- exprs: Expr[];
+ exprs: Expr
[];
// IMPORTANT: This is (sadly) shared with ExprLet.
locals?: LocalInfo[];
};
@@ -120,9 +147,9 @@ export type ExprLiteral = {
value: Literal;
};
-export type ExprIdent = {
+export type ExprIdent
= {
kind: "ident";
- value: Identifier;
+ value: IdentWithRes
;
};
/**
@@ -139,28 +166,28 @@ export type ExprPath = {
res: Resolution;
};
-export type ExprBinary = {
+export type ExprBinary
= {
kind: "binary";
binaryKind: BinaryKind;
- lhs: Expr;
- rhs: Expr;
+ lhs: Expr
;
+ rhs: Expr
;
};
-export type ExprUnary = {
+export type ExprUnary
= {
kind: "unary";
unaryKind: UnaryKind;
- rhs: Expr;
+ rhs: Expr
;
};
-export type ExprCall = {
+export type ExprCall
= {
kind: "call";
- lhs: Expr;
- args: Expr[];
+ lhs: Expr
;
+ args: Expr
[];
};
-export type ExprFieldAccess = {
+export type ExprFieldAccess
= {
kind: "fieldAccess";
- lhs: Expr;
+ lhs: Expr
;
field: {
value: string | number;
span: Span;
@@ -168,18 +195,18 @@ export type ExprFieldAccess = {
};
};
-export type ExprIf = {
+export type ExprIf
= {
kind: "if";
- cond: Expr;
- then: Expr;
- else?: Expr;
+ cond: Expr
;
+ then: Expr
;
+ else?: Expr
;
};
export type LoopId = number;
-export type ExprLoop = {
+export type ExprLoop
= {
kind: "loop";
- body: Expr;
+ body: Expr
;
loopId: LoopId;
};
@@ -188,36 +215,36 @@ export type ExprBreak = {
target?: LoopId;
};
-export type ExprStructLiteral = {
+export type ExprStructLiteral
= {
kind: "structLiteral";
- name: Identifier;
- fields: [Identifier, Expr][];
+ name: IdentWithRes
;
+ fields: [Ident, Expr
][];
};
-export type TupleLiteral = {
+export type TupleLiteral
= {
kind: "tupleLiteral";
- fields: Expr[];
+ fields: Expr
[];
};
-export type ExprKind =
+export type ExprKind
=
| ExprEmpty
- | ExprLet
- | ExprAssign
- | ExprBlock
+ | ExprLet
+ | ExprAssign
+ | ExprBlock
| ExprLiteral
- | ExprIdent
+ | ExprIdent
| ExprPath
- | ExprBinary
- | ExprUnary
- | ExprCall
- | ExprFieldAccess
- | ExprIf
- | ExprLoop
+ | ExprBinary
+ | ExprUnary
+ | ExprCall
+ | ExprFieldAccess
+ | ExprIf
+ | ExprLoop
| ExprBreak
- | ExprStructLiteral
- | TupleLiteral;
+ | ExprStructLiteral
+ | TupleLiteral
;
-export type Expr = ExprKind & {
+export type Expr
= ExprKind
& {
span: Span;
ty?: Ty;
};
@@ -291,22 +318,22 @@ export function binaryExprPrecedenceClass(k: BinaryKind): number {
export type UnaryKind = "!" | "-";
export const UNARY_KINDS: UnaryKind[] = ["!", "-"];
-export type TypeKind =
+export type TypeKind
=
| {
kind: "ident";
- value: Identifier;
+ value: IdentWithRes
;
}
| {
kind: "list";
- elem: Type;
+ elem: Type
;
}
| {
kind: "tuple";
- elems: Type[];
+ elems: Type
[];
}
| { kind: "never" };
-export type Type = TypeKind & {
+export type Type
= TypeKind
& {
span: Span;
ty?: Ty;
};
@@ -446,60 +473,68 @@ export type TypeckResults = {
// folders
-export type FoldFn = (value: T) => T;
+export type FoldFn = (value: From) => To;
-export type Folder = {
- ast: () => Ast;
+export type Folder = {
+ newItemsById: Map>;
/**
* This should not be overridden.
*/
- item: FoldFn- ;
- itemInner: FoldFn
- ;
- expr: FoldFn;
- ident: FoldFn;
- type: FoldFn;
+ item: FoldFn
- , Item>;
+ itemInner: FoldFn
- , Item>;
+ expr: FoldFn, Expr>;
+ ident: FoldFn, IdentWithRes>;
+ type: FoldFn, Type>;
+};
+
+type ItemFolder = {
+ newItemsById: Map>;
+ item: FoldFn
- , Item>;
+ itemInner: FoldFn
- , Item>;
};
const ITEM_DEFAULT = Symbol("item must not be overriden");
-export const DEFAULT_FOLDER: Folder = {
- ast() {
- throw new Error("folders need to implement `ast`");
- },
- item(item) {
- const newItem = this.itemInner(item);
- this.ast().itemsById.set(item.id, newItem);
- return newItem;
- },
- itemInner(item) {
- return superFoldItem(item, this);
- },
- expr(expr) {
- return superFoldExpr(expr, this);
- },
- ident(ident) {
- return ident;
- },
- type(type) {
- return superFoldType(type, this);
- },
-};
-(DEFAULT_FOLDER.item as any)[ITEM_DEFAULT] = ITEM_DEFAULT;
+export function mkDefaultFolder<
+ From extends Phase,
+ To extends Phase
+>(): ItemFolder {
+ const folder: ItemFolder = {
+ newItemsById: new Map(),
+ item(item) {
+ const newItem = this.itemInner(item);
+ this.newItemsById.set(item.id, newItem);
+ return newItem;
+ },
+ itemInner(_item) {
+ throw new Error("unimplemented");
+ },
+ };
+ (folder.item as any)[ITEM_DEFAULT] = ITEM_DEFAULT;
-export function foldAst(ast: Ast, folder: Folder): Ast {
+ return folder;
+}
+
+export function foldAst(
+ ast: Ast,
+ folder: Folder
+): Ast {
if ((folder.item as any)[ITEM_DEFAULT] !== ITEM_DEFAULT) {
throw new Error("must not override `item` on folders");
}
return {
rootItems: ast.rootItems.map((item) => folder.item(item)),
- itemsById: ast.itemsById,
+ itemsById: folder.newItemsById,
typeckResults: ast.typeckResults,
packageName: ast.packageName,
};
}
-export function superFoldItem(item: Item, folder: Folder): Item {
+export function superFoldItem(
+ item: Item,
+ folder: Folder
+): Item {
switch (item.kind) {
case "function": {
const args = item.node.params.map(({ name, type, span }) => ({
@@ -550,7 +585,7 @@ export function superFoldItem(item: Item, folder: Folder): Item {
};
}
case "mod": {
- let kind: ModItemKind;
+ let kind: ModItemKind;
const { modKind: itemKind } = item.node;
switch (itemKind.kind) {
case "inline":
@@ -576,7 +611,10 @@ export function superFoldItem(item: Item, folder: Folder): Item {
}
}
-export function superFoldExpr(expr: Expr, folder: Folder): Expr {
+export function superFoldExpr(
+ expr: Expr,
+ folder: Folder
+): Expr {
const span = expr.span;
switch (expr.kind) {
case "empty": {
@@ -687,7 +725,10 @@ export function superFoldExpr(expr: Expr, folder: Folder): Expr {
}
}
-export function superFoldType(type: Type, folder: Folder): Type {
+export function superFoldType(
+ type: Type,
+ folder: Folder
+): Type {
const span = type.span;
switch (type.kind) {
case "ident": {
diff --git a/src/index.ts b/src/index.ts
index f1c80f1..c64164a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,6 +9,7 @@ import { writeModuleWatToString } from "./wasm/wat";
import fs from "fs";
import path from "path";
import { exec } from "child_process";
+import { Ast, Built, Typecked } from "./ast";
const INPUT = `
function main() = (
@@ -55,7 +56,7 @@ function main() {
console.log("-----TOKENS------------");
console.log(tokens);
- const ast = parse(packageName, tokens);
+ const ast: Ast = parse(packageName, tokens);
console.log("-----AST---------------");
console.dir(ast.rootItems, { depth: 50 });
@@ -70,7 +71,7 @@ function main() {
console.log(resolvedPrinted);
console.log("-----AST typecked------");
- const typecked = typeck(resolved);
+ const typecked: Ast = typeck(resolved);
const typeckPrinted = printAst(typecked);
console.log(typeckPrinted);
diff --git a/src/lower.ts b/src/lower.ts
index d16fde2..f207c6e 100644
--- a/src/lower.ts
+++ b/src/lower.ts
@@ -10,6 +10,7 @@ import {
Ty,
TyFn,
TyTuple,
+ Typecked,
varUnreachable,
} from "./ast";
import { ComplexMap, encodeUtf8, unwrap } from "./utils";
@@ -38,7 +39,7 @@ export type Context = {
funcTypes: ComplexMap;
reservedHeapMemoryStart: number;
funcIndices: ComplexMap;
- ast: Ast;
+ ast: Ast;
relocations: Relocation[];
};
@@ -88,7 +89,7 @@ function appendData(cx: Context, newData: Uint8Array): number {
}
}
-export function lower(ast: Ast): wasm.Module {
+export function lower(ast: Ast): wasm.Module {
const mod: wasm.Module = {
types: [],
funcs: [],
@@ -122,7 +123,7 @@ export function lower(ast: Ast): wasm.Module {
relocations: [],
};
- function lowerMod(items: Item[]) {
+ function lowerMod(items: Item[]) {
items.forEach((item) => {
switch (item.kind) {
case "function": {
@@ -172,7 +173,11 @@ export function lower(ast: Ast): wasm.Module {
return mod;
}
-function lowerImport(cx: Context, item: Item, def: ImportDef) {
+function lowerImport(
+ cx: Context,
+ item: Item,
+ def: ImportDef
+) {
const existing = cx.mod.imports.findIndex(
(imp) => imp.module === def.module.value && imp.name === def.func.value
);
@@ -201,8 +206,8 @@ function lowerImport(cx: Context, item: Item, def: ImportDef) {
type FuncContext = {
cx: Context;
- item: Item;
- func: FunctionDef;
+ item: Item;
+ func: FunctionDef;
wasmType: wasm.FuncType;
wasm: wasm.Func;
varLocations: VarLocation[];
@@ -216,7 +221,11 @@ type ArgRetAbi = wasm.ValType[];
type VarLocation = { localIdx: number; types: wasm.ValType[] };
-function lowerFunc(cx: Context, item: Item, func: FunctionDef) {
+function lowerFunc(
+ cx: Context,
+ item: Item,
+ func: FunctionDef
+) {
const abi = computeAbi(func.ty!);
const { type: wasmType, paramLocations } = wasmTypeForAbi(abi);
const type = internFuncType(cx, wasmType);
@@ -255,7 +264,11 @@ Expression lowering.
- the result of an expression evaluation is stored on the top of the stack
*/
-function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
+function lowerExpr(
+ fcx: FuncContext,
+ instrs: wasm.Instr[],
+ expr: Expr
+) {
const ty = expr.ty!;
exprKind: switch (expr.kind) {
@@ -284,7 +297,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
const { lhs } = expr;
switch (lhs.kind) {
case "ident": {
- const res = lhs.value.res!;
+ const res = lhs.value.res;
switch (res.kind) {
case "local": {
@@ -355,7 +368,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
}
case "path":
case "ident": {
- const res = expr.kind === "ident" ? expr.value.res! : expr.res;
+ const res = expr.kind === "ident" ? expr.value.res : expr.res;
switch (res.kind) {
case "local": {
@@ -502,8 +515,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
todo("non constant calls");
}
- const res =
- expr.lhs.kind === "ident" ? expr.lhs.value.res! : expr.lhs.res;
+ const res = expr.lhs.kind === "ident" ? expr.lhs.value.res : expr.lhs.res;
if (res.kind === "builtin") {
switch (res.name) {
@@ -575,7 +587,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
// TODO: Actually do this instead of being naive.
- const _isPlace = (expr: Expr) =>
+ const _isPlace = (expr: Expr) =>
expr.kind === "ident" || expr.kind === "fieldAccess";
lowerExpr(fcx, instrs, expr.lhs);
@@ -711,7 +723,7 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
function lowerExprBlockBody(
fcx: FuncContext,
- expr: ExprBlock & Expr
+ expr: ExprBlock & Expr
): wasm.Instr[] {
fcx.currentBlockDepth++;
const innerInstrs: wasm.Instr[] = [];
@@ -853,7 +865,7 @@ function todo(msg: string): never {
}
// Make the program runnable using wasi-preview-1
-function addRt(cx: Context, ast: Ast) {
+function addRt(cx: Context, ast: Ast) {
const { mod } = cx;
const mainCall: wasm.Instr = { kind: "call", func: 9999999 };
diff --git a/src/parser.ts b/src/parser.ts
index be8ca1b..a646b75 100644
--- a/src/parser.ts
+++ b/src/parser.ts
@@ -4,7 +4,7 @@ import {
Ast,
BinaryKind,
COMPARISON_KINDS,
- DEFAULT_FOLDER,
+ mkDefaultFolder,
Expr,
ExprLoop,
ExprStructLiteral,
@@ -12,7 +12,7 @@ import {
Folder,
FunctionArg,
FunctionDef,
- Identifier,
+ Ident,
ImportDef,
Item,
LOGICAL_KINDS,
@@ -25,6 +25,8 @@ import {
foldAst,
superFoldExpr,
superFoldItem,
+ Built,
+ Parsed,
} from "./ast";
import { CompilerError, Span, spanMerge } from "./error";
import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer";
@@ -32,8 +34,8 @@ import { Ids } from "./utils";
type Parser = (t: Token[]) => [Token[], T];
-export function parse(packageName: string, t: Token[]): Ast {
- const items: Item[] = [];
+export function parse(packageName: string, t: Token[]): Ast {
+ const items: Item[] = [];
while (t.length > 0) {
let item;
@@ -48,7 +50,7 @@ export function parse(packageName: string, t: Token[]): Ast {
return ast;
}
-function parseItem(t: Token[]): [Token[], Item] {
+function parseItem(t: Token[]): [Token[], Item] {
let tok;
[t, tok] = next(t);
if (tok.kind === "function") {
@@ -62,7 +64,7 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, ";");
- const def: FunctionDef = {
+ const def: FunctionDef = {
...sig,
body,
};
@@ -84,7 +86,7 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, "{");
let fields;
- [t, fields] = parseCommaSeparatedList(t, "}", (t) => {
+ [t, fields] = parseCommaSeparatedList>(t, "}", (t) => {
let name;
[t, name] = expectNext(t, "identifier");
[t] = expectNext(t, ":");
@@ -104,7 +106,7 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, ";");
- const def: TypeDef = {
+ const def: TypeDef = {
name: name.ident,
fields,
};
@@ -123,7 +125,7 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, ";");
- const def: ImportDef = {
+ const def: ImportDef = {
module: { kind: "str", value: module.value, span: module.span },
func: { kind: "str", value: func.value, span: func.span },
...sig,
@@ -135,7 +137,7 @@ function parseItem(t: Token[]): [Token[], Item] {
let name;
[t, name] = expectNext(t, "identifier");
- const node: ModItem = {
+ const node: ModItem = {
name: name.ident,
modKind: { kind: "extern" },
};
@@ -149,7 +151,7 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, "(");
- const contents: Item[] = [];
+ const contents: Item[] = [];
while (next(t)[1].kind !== ")") {
let item;
@@ -161,7 +163,7 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, ")");
[t] = expectNext(t, ";");
- const node: ModItem = {
+ const node: ModItem = {
name: name.ident,
modKind: { kind: "inline", contents },
};
@@ -174,8 +176,8 @@ function parseItem(t: Token[]): [Token[], Item] {
type FunctionSig = {
name: string;
- params: FunctionArg[];
- returnType?: Type;
+ params: FunctionArg[];
+ returnType?: Type;
};
function parseFunctionSig(t: Token[]): [Token[], FunctionSig] {
@@ -184,7 +186,7 @@ function parseFunctionSig(t: Token[]): [Token[], FunctionSig] {
[t] = expectNext(t, "(");
- let params: FunctionArg[];
+ let params: FunctionArg[];
[t, params] = parseCommaSeparatedList(t, ")", (t) => {
let name;
[t, name] = expectNext(t, "identifier");
@@ -205,7 +207,7 @@ function parseFunctionSig(t: Token[]): [Token[], FunctionSig] {
return [t, { name: name.ident, params, returnType }];
}
-function parseExpr(t: Token[]): [Token[], Expr] {
+function parseExpr(t: Token[]): [Token[], Expr] {
/*
EXPR = ASSIGNMENT
@@ -237,16 +239,21 @@ function parseExpr(t: Token[]): [Token[], Expr] {
return parseExprAssignment(t);
}
-function mkBinaryExpr(lhs: Expr, rhs: Expr, span: Span, kind: string): Expr {
+function mkBinaryExpr(
+ lhs: Expr,
+ rhs: Expr,
+ span: Span,
+ kind: string
+): Expr {
return { kind: "binary", binaryKind: kind as BinaryKind, lhs, rhs, span };
}
function mkParserExprBinary(
- lower: Parser,
+ lower: Parser>,
kinds: string[],
mkExpr = mkBinaryExpr
-): Parser {
- function parser(t: Token[]): [Token[], Expr] {
+): Parser> {
+ function parser(t: Token[]): [Token[], Expr] {
let lhs;
[t, lhs] = lower(t);
@@ -288,7 +295,7 @@ const parseExprAssignment = mkParserExprBinary(
(lhs, rhs, span) => ({ kind: "assign", lhs, rhs, span })
);
-function parseExprUnary(t: Token[]): [Token[], Expr] {
+function parseExprUnary(t: Token[]): [Token[], Expr] {
const [, peak] = next(t);
if (peak.kind in UNARY_KINDS) {
let rhs;
@@ -306,8 +313,8 @@ function parseExprUnary(t: Token[]): [Token[], Expr] {
return parseExprCall(t);
}
-function parseExprCall(t: Token[]): [Token[], Expr] {
- let lhs: Expr;
+function parseExprCall(t: Token[]): [Token[], Expr] {
+ let lhs: Expr;
[t, lhs] = parseExprAtom(t);
while (next(t)[1].kind === "(" || next(t)[1].kind === ".") {
@@ -343,13 +350,13 @@ function parseExprCall(t: Token[]): [Token[], Expr] {
return [t, lhs];
}
-function parseExprAtom(startT: Token[]): [Token[], Expr] {
+function parseExprAtom(startT: Token[]): [Token[], Expr] {
// eslint-disable-next-line prefer-const
let [t, tok] = next(startT);
const span = tok.span;
if (tok.kind === "(") {
- let expr: Expr;
+ let expr: Expr;
[t, expr] = parseExpr(t);
// This could be a block or a tuple literal. We can only know after
@@ -447,7 +454,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
let rhs;
[t, rhs] = parseExpr(t);
- const nameIdent: Identifier = { name: name.ident, span: name.span };
+ const nameIdent: Ident = { name: name.ident, span: name.span };
return [
t,
@@ -493,11 +500,13 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
return [startT, { kind: "empty", span }];
}
-function parseStructInit(t: Token[]): [Token[], ExprStructLiteral["fields"]] {
+function parseStructInit(
+ t: Token[]
+): [Token[], ExprStructLiteral["fields"]] {
[t] = expectNext(t, "{");
let fields;
- [t, fields] = parseCommaSeparatedList<[Identifier, Expr]>(t, "}", (t) => {
+ [t, fields] = parseCommaSeparatedList<[Ident, Expr]>(t, "}", (t) => {
let name;
[t, name] = expectNext(t, "identifier");
[t] = expectNext(t, ":");
@@ -510,7 +519,7 @@ function parseStructInit(t: Token[]): [Token[], ExprStructLiteral["fields"]] {
return [t, fields];
}
-function parseType(t: Token[]): [Token[], Type] {
+function parseType(t: Token[]): [Token[], Type] {
let tok;
[t, tok] = next(t);
const span = tok.span;
@@ -649,22 +658,19 @@ function unexpectedToken(token: Token, expected: string): never {
throw new CompilerError(`unexpected token, expected ${expected}`, token.span);
}
-function validateAst(ast: Ast) {
+function validateAst(ast: Ast) {
const seenItemIds = new Set();
- const validator: Folder = {
- ...DEFAULT_FOLDER,
- ast() {
- return ast;
- },
- itemInner(item: Item): Item {
+ const validator: Folder = {
+ ...mkDefaultFolder(),
+ itemInner(item: Item): Item {
if (seenItemIds.has(item.id)) {
throw new Error(`duplicate item id: ${item.id} for ${item.node.name}`);
}
seenItemIds.add(item.id);
return superFoldItem(item, this);
},
- expr(expr: Expr): Expr {
+ expr(expr: Expr): Expr {
if (expr.kind === "block") {
expr.exprs.forEach((inner) => {
if (inner.kind === "let") {
@@ -680,7 +686,7 @@ function validateAst(ast: Ast) {
} else if (expr.kind === "let") {
throw new CompilerError("let is only allowed in blocks", expr.span);
} else if (expr.kind === "binary") {
- const checkPrecedence = (inner: Expr, side: string) => {
+ const checkPrecedence = (inner: Expr, side: string) => {
if (inner.kind === "binary") {
const ourClass = binaryExprPrecedenceClass(expr.binaryKind);
const innerClass = binaryExprPrecedenceClass(inner.binaryKind);
@@ -702,40 +708,49 @@ function validateAst(ast: Ast) {
return superFoldExpr(expr, this);
}
},
+ ident(ident) {
+ return ident;
+ },
+ type(type) {
+ return type;
+ },
};
foldAst(ast, validator);
}
-function buildAst(packageName: string, rootItems: Item[]): Ast {
+function buildAst(packageName: string, rootItems: Item[]): Ast {
const itemId = new Ids();
const loopId = new Ids();
- const ast: Ast = {
+ const ast: Ast = {
rootItems,
itemsById: new Map(),
packageName,
};
- const assigner: Folder = {
- ...DEFAULT_FOLDER,
- ast() {
- return ast;
- },
- itemInner(item: Item): Item {
+ const assigner: Folder = {
+ ...mkDefaultFolder(),
+ itemInner(item: Item): Item {
const id = itemId.next();
ast.itemsById.set(id, item);
return { ...superFoldItem(item, this), id };
},
- expr(expr: Expr): Expr {
+ expr(expr: Expr): Expr {
if (expr.kind === "loop") {
return {
- ...(superFoldExpr(expr, this) as ExprLoop & Expr),
+ ...(superFoldExpr(expr, this) as ExprLoop & Expr),
loopId: loopId.next(),
};
}
return superFoldExpr(expr, this);
},
+ ident(ident) {
+ return ident;
+ },
+ type(type) {
+ return type;
+ },
};
return foldAst(ast, assigner);
diff --git a/src/printer.ts b/src/printer.ts
index 321e3cd..bc7b531 100644
--- a/src/printer.ts
+++ b/src/printer.ts
@@ -1,8 +1,9 @@
import {
+ AnyPhase,
Ast,
Expr,
FunctionDef,
- Identifier,
+ IdentWithRes,
ImportDef,
Item,
ModItem,
@@ -14,7 +15,7 @@ import {
tyIsUnit,
} from "./ast";
-export function printAst(ast: Ast): string {
+export function printAst(ast: Ast): string {
return ast.rootItems.map(printItem).join("\n");
}
@@ -22,7 +23,7 @@ function printStringLiteral(lit: StringLiteral): string {
return `"${lit.value.replace("\n", "\\n")}"`;
}
-function printItem(item: Item): string {
+function printItem(item: Item): string {
const id = `/*${item.id}*/ `;
switch (item.kind) {
@@ -41,7 +42,7 @@ function printItem(item: Item): string {
}
}
-function printFunction(func: FunctionDef): string {
+function printFunction(func: FunctionDef): string {
const args = func.params
.map(({ name, type }) => `${name}: ${printType(type)}`)
.join(", ");
@@ -49,7 +50,7 @@ function printFunction(func: FunctionDef): string {
return `function ${func.name}(${args})${ret} = ${printExpr(func.body, 0)};`;
}
-function printTypeDef(type: TypeDef): string {
+function printTypeDef(type: TypeDef): string {
const fields = type.fields.map(
({ name, type }) => `${ind(1)}${name.name}: ${printType(type)},`
);
@@ -60,7 +61,7 @@ function printTypeDef(type: TypeDef): string {
return `type ${type.name} = ${fieldPart};`;
}
-function printImportDef(def: ImportDef): string {
+function printImportDef(def: ImportDef): string {
const args = def.params
.map(({ name, type }) => `${name}: ${printType(type)}`)
.join(", ");
@@ -71,7 +72,7 @@ function printImportDef(def: ImportDef): string {
)}(${args})${ret};`;
}
-function printMod(mod: ModItem): string {
+function printMod(mod: ModItem): string {
switch (mod.modKind.kind) {
case "inline":
return `mod ${mod.name} (\n${mod.modKind.contents
@@ -82,7 +83,7 @@ function printMod(mod: ModItem): string {
}
}
-function printExpr(expr: Expr, indent: number): string {
+function printExpr(expr: Expr, indent: number): string {
switch (expr.kind) {
case "empty": {
return "";
@@ -192,7 +193,7 @@ function printExpr(expr: Expr, indent: number): string {
}
}
-function printType(type: Type): string {
+function printType(type: Type): string {
switch (type.kind) {
case "ident":
return printIdent(type.value);
@@ -217,8 +218,8 @@ function printRes(res: Resolution): string {
}
}
-function printIdent(ident: Identifier): string {
- const res = ident.res ? printRes(ident.res) : "";
+function printIdent(ident: IdentWithRes): string {
+ const res = "res" in ident ? printRes(ident.res) : "";
return `${ident.name}${res}`;
}
diff --git a/src/resolve.ts b/src/resolve.ts
index 5a38348..7289d04 100644
--- a/src/resolve.ts
+++ b/src/resolve.ts
@@ -1,18 +1,21 @@
import {
Ast,
BUILTINS,
+ Built,
BuiltinName,
- DEFAULT_FOLDER,
Expr,
Folder,
- Identifier,
+ Ident,
Item,
ItemId,
LocalInfo,
ModItem,
Resolution,
+ Resolved,
+ mkDefaultFolder,
superFoldExpr,
superFoldItem,
+ superFoldType,
} from "./ast";
import { CompilerError, spanMerge, todo } from "./error";
import { unwrap } from "./utils";
@@ -20,13 +23,14 @@ import { unwrap } from "./utils";
const BUILTIN_SET = new Set(BUILTINS);
type Context = {
- ast: Ast;
+ ast: Ast;
modContentsCache: Map>;
+ newItemsById: Map>;
};
function resolveModItem(
cx: Context,
- mod: ModItem,
+ mod: ModItem,
modId: ItemId,
name: string
): ItemId | undefined {
@@ -49,18 +53,26 @@ function resolveModItem(
}
}
-export function resolve(ast: Ast): Ast {
- const cx: Context = { ast, modContentsCache: new Map() };
+export function resolve(ast: Ast): Ast {
+ const cx: Context = {
+ ast,
+ modContentsCache: new Map(),
+ newItemsById: new Map(),
+ };
const rootItems = resolveModule(cx, [ast.packageName], ast.rootItems);
- return { ...ast, rootItems };
+ return {
+ itemsById: cx.newItemsById,
+ rootItems,
+ packageName: ast.packageName,
+ };
}
function resolveModule(
cx: Context,
modName: string[],
- contents: Item[]
-): Item[] {
+ contents: Item[]
+): Item[] {
const items = new Map();
contents.forEach((item) => {
@@ -85,7 +97,7 @@ function resolveModule(
}
};
- const resolveIdent = (ident: Identifier): Resolution => {
+ const resolveIdent = (ident: Ident): Resolution => {
const lastIdx = scopes.length - 1;
for (let i = lastIdx; i >= 0; i--) {
const candidate = scopes[i];
@@ -115,11 +127,8 @@ function resolveModule(
const blockLocals: LocalInfo[][] = [];
- const resolver: Folder = {
- ...DEFAULT_FOLDER,
- ast() {
- return cx.ast;
- },
+ const resolver: Folder = {
+ ...mkDefaultFolder(),
itemInner(item) {
const defPath = [...modName, item.node.name];
@@ -178,7 +187,9 @@ function resolveModule(
const prevScopeLength = scopes.length;
blockLocals.push([]);
- const exprs = expr.exprs.map((inner) => this.expr(inner));
+ const exprs = expr.exprs.map>((inner) =>
+ this.expr(inner)
+ );
scopes.length = prevScopeLength;
const locals = blockLocals.pop();
@@ -263,6 +274,10 @@ function resolveModule(
const res = resolveIdent(ident);
return { name: ident.name, span: ident.span, res };
},
+ type(type) {
+ return superFoldType(type, this);
+ },
+ newItemsById: cx.newItemsById,
};
return contents.map((item) => resolver.item(item));
diff --git a/src/typeck.ts b/src/typeck.ts
index 9f9855f..7970f1f 100644
--- a/src/typeck.ts
+++ b/src/typeck.ts
@@ -2,19 +2,21 @@ import {
Ast,
BuiltinName,
COMPARISON_KINDS,
- DEFAULT_FOLDER,
+ mkDefaultFolder,
EQUALITY_KINDS,
Expr,
ExprBinary,
ExprUnary,
foldAst,
Folder,
- Identifier,
+ Ident,
+ IdentWithRes,
ItemId,
LOGICAL_KINDS,
LoopId,
ModItemKind,
Resolution,
+ Resolved,
Ty,
TY_BOOL,
TY_I32,
@@ -25,7 +27,9 @@ import {
TyFn,
tyIsUnit,
Type,
+ Typecked,
TyStruct,
+ Item,
} from "./ast";
import { CompilerError, Span } from "./error";
import { printTy } from "./printer";
@@ -84,8 +88,8 @@ function typeOfBuiltinValue(name: BuiltinName, span: Span): Ty {
// TODO: Cleanup, maybe get the ident switch into this function because typeOfItem is unused.
function lowerAstTyBase(
- type: Type,
- lowerIdentTy: (ident: Identifier) => Ty,
+ type: Type,
+ lowerIdentTy: (ident: IdentWithRes) => Ty,
typeOfItem: (index: number, cause: Span) => Ty
): Ty {
switch (type.kind) {
@@ -112,7 +116,7 @@ function lowerAstTyBase(
}
}
-export function typeck(ast: Ast): Ast {
+export function typeck(ast: Ast): Ast {
const itemTys = new Map();
function typeOfItem(index: ItemId, cause: Span): Ty {
const item = unwrap(ast.itemsById.get(index));
@@ -165,11 +169,11 @@ export function typeck(ast: Ast): Ast {
}
}
- function lowerAstTy(type: Type): Ty {
+ function lowerAstTy(type: Type): Ty {
return lowerAstTyBase(
type,
(ident) => {
- const res = ident.res!;
+ const res = ident.res;
switch (res.kind) {
case "local": {
throw new Error("Item type cannot refer to local variable");
@@ -186,12 +190,9 @@ export function typeck(ast: Ast): Ast {
);
}
- const checker: Folder = {
- ...DEFAULT_FOLDER,
- ast() {
- return ast;
- },
- itemInner(item) {
+ const checker: Folder = {
+ ...mkDefaultFolder(),
+ itemInner(item: Item): Item {
switch (item.kind) {
case "function": {
const fnTy = typeOfItem(item.id, item.span) as TyFn;
@@ -298,7 +299,7 @@ export function typeck(ast: Ast): Ast {
case "mod": {
switch (item.node.modKind.kind) {
case "inline": {
- const modKind: ModItemKind = {
+ const modKind: ModItemKind = {
kind: "inline",
contents: item.node.modKind.contents.map((item) =>
this.item(item)
@@ -323,6 +324,15 @@ export function typeck(ast: Ast): Ast {
}
}
},
+ expr(_expr) {
+ throw new Error("expressions need to be handled in checkBody");
+ },
+ ident(ident) {
+ return ident;
+ },
+ type(_type) {
+ throw new Error("all types should be typechecked manually");
+ },
};
const typecked = foldAst(ast, checker);
@@ -518,10 +528,10 @@ export class InferContext {
}
export function checkBody(
- body: Expr,
+ body: Expr,
fnTy: TyFn,
typeOfItem: (index: number, cause: Span) => Ty
-): Expr {
+): Expr {
const localTys = [...fnTy.params];
const loopState: { hasBreak: boolean; loopId: LoopId }[] = [];
@@ -541,11 +551,11 @@ export function checkBody(
}
}
- function lowerAstTy(type: Type): Ty {
+ function lowerAstTy(type: Type): Ty {
return lowerAstTyBase(
type,
(ident) => {
- const res = ident.res!;
+ const res = ident.res;
switch (res.kind) {
case "local": {
const idx = localTys.length - 1 - res.index;
@@ -562,8 +572,8 @@ export function checkBody(
);
}
- const checker: Folder = {
- ...DEFAULT_FOLDER,
+ const checker: Folder = {
+ ...mkDefaultFolder(),
expr(expr) {
switch (expr.kind) {
case "empty": {
@@ -584,7 +594,7 @@ export function checkBody(
expr.local!.ty = bindingTy;
- const type: Type | undefined = loweredBindingTy && {
+ const type: Type | undefined = loweredBindingTy && {
...expr.type!,
ty: loweredBindingTy,
};
@@ -606,7 +616,7 @@ export function checkBody(
switch (lhs.kind) {
case "ident":
- if (lhs.value.res!.kind !== "local") {
+ if (lhs.value.res.kind !== "local") {
throw new CompilerError("cannot assign to items", expr.span);
}
break;
@@ -664,7 +674,7 @@ export function checkBody(
return { ...expr, ty };
}
case "ident": {
- const ty = typeOf(expr.value.res!, expr.value.span);
+ const ty = typeOf(expr.value.res, expr.value.span);
return { ...expr, ty };
}
@@ -842,12 +852,11 @@ export function checkBody(
};
}
case "structLiteral": {
- const fields = expr.fields.map<[Identifier, Expr]>(([name, expr]) => [
- name,
- this.expr(expr),
- ]);
+ const fields = expr.fields.map<[Ident, Expr]>(
+ ([name, expr]) => [name, this.expr(expr)]
+ );
- const structTy = typeOf(expr.name.res!, expr.name.span);
+ const structTy = typeOf(expr.name.res, expr.name.span);
if (structTy.kind !== "struct") {
throw new CompilerError(
@@ -897,6 +906,15 @@ export function checkBody(
}
}
},
+ itemInner(_item) {
+ throw new Error("cannot deal with items inside body");
+ },
+ ident(ident) {
+ return ident;
+ },
+ type(_type) {
+ throw new Error("all types in the body should be handled elsewhere");
+ },
};
const checked = checker.expr(body);
@@ -912,8 +930,8 @@ export function checkBody(
return resTy;
};
- const resolver: Folder = {
- ...DEFAULT_FOLDER,
+ const resolver: Folder = {
+ ...mkDefaultFolder(),
expr(expr) {
const ty = resolveTy(expr.ty!, expr.span);
@@ -925,6 +943,13 @@ export function checkBody(
return { ...expr, ty };
},
+ type(type) {
+ const ty = resolveTy(type.ty!, type.span);
+ return { ...type, ty };
+ },
+ ident(ident) {
+ return ident;
+ },
};
const resolved = resolver.expr(checked);
@@ -932,7 +957,9 @@ export function checkBody(
return resolved;
}
-function checkBinary(expr: Expr & ExprBinary): Expr {
+function checkBinary(
+ expr: Expr & ExprBinary
+): Expr {
const lhsTy = expr.lhs.ty!;
const rhsTy = expr.rhs.ty!;
@@ -973,7 +1000,9 @@ function checkBinary(expr: Expr & ExprBinary): Expr {
);
}
-function checkUnary(expr: Expr & ExprUnary): Expr {
+function checkUnary(
+ expr: Expr & ExprUnary
+): Expr {
const rhsTy = expr.rhs.ty!;
if (