From 6d2a2fe474658b806042ce8600cf12dee6aeb11e Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 30 Jul 2023 16:54:04 +0200 Subject: [PATCH] Imports --- flake.nix | 1 + opt.wat | 25 ++++++++ src/ast.ts | 57 ++++++++++++++--- src/index.ts | 34 ++++++---- src/lexer.ts | 35 ++++++++--- src/lower.ts | 168 ++++++++++++++++++++++++++++++++++++++----------- src/parser.ts | 96 +++++++++++++++++++--------- src/printer.ts | 24 ++++++- src/typeck.ts | 101 +++++++++++++++++++++++++++-- 9 files changed, 443 insertions(+), 98 deletions(-) create mode 100644 opt.wat diff --git a/flake.nix b/flake.nix index dc96b38..ec0c14b 100644 --- a/flake.nix +++ b/flake.nix @@ -24,6 +24,7 @@ nodejs-18_x # Node.js 18, plus npm, npx, and corepack wasmtime wasm-tools + binaryen ]; }; }); diff --git a/opt.wat b/opt.wat new file mode 100644 index 0000000..90d212b --- /dev/null +++ b/opt.wat @@ -0,0 +1,25 @@ +(module + (type (;0;) (func (param i32 i32 i32 i32) (result i32))) + (type (;1;) (func)) + (import "wasi_snapshot_preview1" "fd_write" (func (;0;) (type 0))) + (func (;1;) (type 1) + i32.const 1028 + i32.const 0 + i32.store + i32.const 1032 + i32.const 2 + i32.store + i32.const 1 + i32.const 1028 + i32.const 2 + i32.const 1024 + call 0 + drop + ) + (table (;0;) 0 0 funcref) + (memory (;0;) 65536 65536) + (export "memory" (memory 0)) + (export "__indirect_function_table" (table 0)) + (export "_start" (func 1)) + (data (;0;) (i32.const 0) "a\0a") +) \ No newline at end of file diff --git a/src/ast.ts b/src/ast.ts index ed9b352..5510fb9 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,4 +1,5 @@ import { Span } from "./error"; +import { LitIntType } from "./lexer"; export type Ast = { items: Item[]; typeckResults?: TypeckResults }; @@ -18,6 +19,10 @@ export type ItemKind = | { kind: "type"; node: TypeDef; + } + | { + kind: "import"; + node: ImportDef; }; export type Item = ItemKind & { @@ -50,6 +55,15 @@ export type FieldDef = { type: Type; }; +export type ImportDef = { + module: StringLiteral; + func: StringLiteral; + name: string; + params: FunctionArg[]; + returnType?: Type; + ty?: TyFn; +}; + export type ExprEmpty = { kind: "empty" }; export type ExprLet = { @@ -143,14 +157,18 @@ export type Expr = ExprKind & { ty?: Ty; }; +export type StringLiteral = { + kind: "str"; + value: string; + span: Span; +}; + export type Literal = - | { - kind: "str"; - value: string; - } + | StringLiteral | { kind: "int"; value: number; + type: LitIntType; }; export type BinaryKind = @@ -265,6 +283,13 @@ export const BUILTINS = [ "Bool", "true", "false", + // Intrinsics: + "__i32_store", + "__i64_store", + "__i32_load", + "__i64_load", + "__string_ptr", + "__string_len", ] as const; export type BuiltinName = (typeof BUILTINS)[number]; @@ -399,15 +424,14 @@ export function superFoldItem(item: Item, folder: Folder): Item { })); return { + ...item, kind: "function", - span: item.span, node: { name: item.node.name, params: args, body: folder.expr(item.node.body), returnType: item.node.returnType && folder.type(item.node.returnType), }, - id: item.id, }; } case "type": { @@ -417,10 +441,27 @@ export function superFoldItem(item: Item, folder: Folder): Item { })); return { + ...item, kind: "type", - span: item.span, node: { name: item.node.name, fields }, - id: item.id, + }; + } + case "import": { + const args = item.node.params.map(({ name, type, span }) => ({ + name, + type: folder.type(type), + span, + })); + return { + ...item, + kind: "import", + node: { + module: item.node.module, + func: item.node.func, + name: item.node.name, + params: args, + returnType: item.node.returnType && folder.type(item.node.returnType), + }, }; } } diff --git a/src/index.ts b/src/index.ts index 7c6f023..760f0d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,20 +10,32 @@ import fs from "fs"; import { exec } from "child_process"; const input = ` -// import "wasi_snapshot_preview1" "fd_write"(a: I32, b: I32, c: I32, d: I32): I32; +import("wasi_snapshot_preview1" "fd_write") + fd_write(a: I32, b: I32, c: I32, d: I32): I32; -function main() = ( - loop (no(break);); - uwu(10); +function coolerPrint(a: String) = ( + let ptr = __string_ptr(a); + let len = __string_len(a); + + let mem = 1024_I32; + + __i32_store(mem + 4_I32, ptr); + __i32_store(mem + 8_I32, 2_I32); + + fd_write( + // stdout + 1_I32, + // iovec + mem + 4_I32, + // len + len, + // return value + mem, + ); ); -function meow(a: I32): I32 = a; - -function no(a: !): String = a; - -function uwu(a: Int) = if a != 0 then ( - print("uwu\n"); - uwu(a - 1); +function main() = ( + coolerPrint("uwu\\n"); ); `; diff --git a/src/lexer.ts b/src/lexer.ts index 1b8d9f6..07add2d 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -9,6 +9,7 @@ export type DatalessToken = | "type" | "loop" | "break" + | "import" | "(" | ")" | "{" @@ -36,14 +37,19 @@ export type DatalessToken = export type TokenIdent = { kind: "identifier"; ident: string }; +export type TokenLitString = { + kind: "lit_string"; + value: string; +}; + +export type LitIntType = "Int" | "I32"; + export type TokenLit = - | { - kind: "lit_string"; - value: string; - } + | TokenLitString | { kind: "lit_int"; value: number; + type: LitIntType; }; export type TokenKind = { kind: DatalessToken } | TokenIdent | TokenLit; @@ -80,13 +86,13 @@ export function tokenize(input: string): Token[] { const next = input[i]; const span: Span = { start: i, end: i + 1 }; - if (next === "/" && input[i + 1] === "/") { + if (next === "/" && input[i + 1] === "/") { while (input[i] !== "\n") { i++; } continue; - } + } if (SINGLE_PUNCT.includes(next)) { tokens.push({ kind: next as DatalessToken, span }); @@ -207,7 +213,21 @@ export function tokenize(input: string): Token[] { ); } - tokens.push({ kind: "lit_int", value: int, span }); + let type: LitIntType = "Int"; + console.log(input[i + 2]); + if (input[i + 1] === "_" && isIdentStart(input[i + 2])) { + console.log("yes", input.slice(i+2, i+5)); + + if (input.slice(i+2, i+5) === "Int") { + i += 4; + type = "Int"; + } else if (input.slice(i+2, i+5) === "I32") { + i += 4; + type = "I32"; + } + } + + tokens.push({ kind: "lit_int", value: int, span, type }); } else if (isIdentStart(next)) { while (isIdentContinue(input[i + 1])) { span.end++; @@ -267,6 +287,7 @@ const KEYOWRDS: DatalessToken[] = [ "type", "loop", "break", + "import", ]; const KEYWORD_SET = new Set(KEYOWRDS); diff --git a/src/lower.ts b/src/lower.ts index 0d9b0ee..7cc89be 100644 --- a/src/lower.ts +++ b/src/lower.ts @@ -3,6 +3,7 @@ import { Expr, ExprBlock, FunctionDef, + ImportDef, Item, Resolution, Ty, @@ -12,8 +13,6 @@ import { import { encodeUtf8 } from "./utils"; import * as wasm from "./wasm/defs"; -type StringifiedForMap = string; - const USIZE: wasm.ValType = "i32"; // POINTERS ARE JUST INTEGERS const POINTER: wasm.ValType = USIZE; @@ -28,22 +27,25 @@ type Relocation = { instr: wasm.Instr & { func: wasm.FuncIdx }; } & { res: Resolution }; -function setMap(map: Map, V>, key: K, value: V) { - map.set(JSON.stringify(key), value); +type StringifiedMap = { _map: Map }; + +function setMap(map: StringifiedMap, key: K, value: V) { + map._map.set(JSON.stringify(key), value); } -function getMap( - map: Map, V>, - key: K -): V | undefined { - return map.get(JSON.stringify(key)); +function getMap(map: StringifiedMap, key: K): V | undefined { + return map._map.get(JSON.stringify(key)); } +type FuncOrImport = + | { kind: "func"; idx: wasm.FuncIdx } + | { kind: "import"; idx: number }; + export type Context = { mod: wasm.Module; - funcTypes: Map, wasm.TypeIdx>; + funcTypes: StringifiedMap; reservedHeapMemoryStart: number; - funcIndices: Map, wasm.FuncIdx>; + funcIndices: StringifiedMap; ast: Ast; relocations: Relocation[]; }; @@ -117,8 +119,8 @@ export function lower(ast: Ast): wasm.Module { const cx: Context = { mod, - funcTypes: new Map(), - funcIndices: new Map(), + funcTypes: { _map: new Map() }, + funcIndices: { _map: new Map() }, reservedHeapMemoryStart: 0, ast, relocations: [], @@ -128,6 +130,11 @@ export function lower(ast: Ast): wasm.Module { switch (item.kind) { case "function": { lowerFunc(cx, item, item.node); + break; + } + case "import": { + lowerImport(cx, item, item.node); + break; } } }); @@ -145,13 +152,13 @@ export function lower(ast: Ast): wasm.Module { cx.relocations.forEach((rel) => { switch (rel.kind) { case "funccall": { - const idx = getMap(cx.funcIndices, rel.res); + const idx = getMap(cx.funcIndices, rel.res); if (idx === undefined) { throw new Error( `no function found for relocation '${JSON.stringify(rel.res)}'` ); } - rel.instr.func = offset + idx; + rel.instr.func = idx.kind === "func" ? offset + idx.idx : idx.idx; } } }); @@ -160,6 +167,37 @@ export function lower(ast: Ast): wasm.Module { return mod; } +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 + ); + + let idx; + if (existing !== -1) { + idx = existing; + } else { + const abi = computeAbi(def.ty!); + const { type: wasmType } = wasmTypeForAbi(abi); + const type = internFuncType(cx, wasmType); + + idx = cx.mod.imports.length; + cx.mod.imports.push({ + module: def.module.value, + name: def.func.value, + desc: { + kind: "func", + type, + }, + }); + } + + setMap( + cx.funcIndices, + { kind: "item", index: item.id }, + { kind: "import", idx } + ); +} + type FuncContext = { cx: Context; item: Item; @@ -198,10 +236,10 @@ function lowerFunc(cx: Context, item: Item, func: FunctionDef) { const idx = fcx.cx.mod.funcs.length; fcx.cx.mod.funcs.push(wasmFunc); - setMap( + setMap( fcx.cx.funcIndices, { kind: "item", index: fcx.item.id }, - idx + { kind: "func", idx } ); } @@ -213,7 +251,7 @@ Expression lowering. function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) { const ty = expr.ty!; - switch (expr.kind) { + exprKind: switch (expr.kind) { case "empty": { // A ZST, do nothing. return; @@ -265,7 +303,14 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) { break; case "int": - instrs.push({ kind: "i64.const", imm: expr.value.value }); + switch (expr.value.type) { + case "Int": + instrs.push({ kind: "i64.const", imm: expr.value.value }); + break; + case "I32": + instrs.push({ kind: "i32.const", imm: expr.value.value }); + break; + } break; } break; @@ -306,49 +351,55 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) { lowerExpr(fcx, instrs, expr.lhs); lowerExpr(fcx, instrs, expr.rhs); - if (expr.lhs.ty!.kind === "int" && expr.rhs.ty!.kind === "int") { + const lhsTy = expr.lhs.ty!; + const rhsTy = expr.rhs.ty!; + if ( + (lhsTy.kind === "int" && rhsTy.kind === "int") || + (lhsTy.kind === "i32" && rhsTy.kind === "i32") + ) { let kind: wasm.Instr["kind"]; + const valty = lhsTy.kind === "int" ? "i64" : "i32"; switch (expr.binaryKind) { case "+": - kind = "i64.add"; + kind = `${valty}.add`; break; case "-": - kind = "i64.sub"; + kind = `${valty}.sub`; break; case "*": - kind = "i64.mul"; + kind = `${valty}.mul`; break; case "/": - kind = "i64.div_u"; + kind = `${valty}.div_u`; break; case "&": - kind = "i64.and"; + kind = `${valty}.and`; break; case "|": - kind = "i64.or"; + kind = `${valty}.or`; break; case "<": - kind = "i64.lt_u"; + kind = `${valty}.lt_u`; break; case ">": - kind = "i64.gt_u"; + kind = `${valty}.gt_u`; // errs break; case "==": - kind = "i64.eq"; + kind = `${valty}.eq`; break; case "<=": - kind = "i64.le_u"; + kind = `${valty}.le_u`; break; case ">=": - kind = "i64.ge_u"; + kind = `${valty}.ge_u`; break; case "!=": - kind = "i64.ne"; + kind = `${valty}.ne`; break; } instrs.push({ kind }); - } else if (expr.lhs.ty!.kind === "bool" && expr.rhs.ty!.kind === "bool") { + } else if (lhsTy.kind === "bool" && rhsTy.kind === "bool") { let kind: wasm.Instr["kind"]; switch (expr.binaryKind) { @@ -404,6 +455,50 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) { if (expr.lhs.kind !== "ident") { todo("non constant calls"); } + + if (expr.lhs.value.res!.kind === "builtin") { + switch (expr.lhs.value.res!.name) { + case "__i32_load": { + lowerExpr(fcx, instrs, expr.args[0]); + instrs.push({ kind: "i64.load", imm: {} }); + break exprKind; + } + case "__i64_load": { + lowerExpr(fcx, instrs, expr.args[0]); + instrs.push({ kind: "i64.load", imm: {} }); + break exprKind; + } + case "__i32_store": { + lowerExpr(fcx, instrs, expr.args[0]); + lowerExpr(fcx, instrs, expr.args[1]); + instrs.push({ kind: "i32.store", imm: {} }); + break exprKind; + } + case "__i64_store": { + lowerExpr(fcx, instrs, expr.args[0]); + lowerExpr(fcx, instrs, expr.args[1]); + instrs.push({ kind: "i64.store", imm: {} }); + break exprKind; + } + case "__string_ptr": { + lowerExpr(fcx, instrs, expr.args[0]); + // ptr, len + instrs.push({ kind: "drop" }); + // ptr + break exprKind; + } + case "__string_len": { + lowerExpr(fcx, instrs, expr.args[0]); + // ptr, len + instrs.push({ kind: "i32.const", imm: 0 }); + // ptr, len, 0 + instrs.push({ kind: "select" }); + // len + break exprKind; + } + } + } + const callInstr: wasm.Instr = { kind: "call", func: 9999999999 }; fcx.cx.relocations.push({ kind: "funccall", @@ -676,9 +771,10 @@ function addRt(cx: Context, ast: Ast) { const printIdx = cx.mod.funcs.length; cx.mod.funcs.push(print); - cx.funcIndices.set( - JSON.stringify({ kind: "builtin", name: "print" }), - printIdx + setMap( + cx.funcIndices, + { kind: "builtin", name: "print" }, + { kind: "func", idx: printIdx } ); mod.exports.push({ diff --git a/src/parser.ts b/src/parser.ts index 3c6b91d..e32f8d0 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -12,6 +12,7 @@ import { FunctionArg, FunctionDef, Identifier, + ImportDef, Item, LOGICAL_KINDS, Type, @@ -23,7 +24,13 @@ import { superFoldExpr, } from "./ast"; import { CompilerError, Span, spanMerge } from "./error"; -import { BaseToken, Token, TokenIdent } from "./lexer"; +import { + BaseToken, + Token, + TokenIdent, + TokenLit, + TokenLitString, +} from "./lexer"; type Parser = (t: Token[]) => [Token[], T]; @@ -49,28 +56,8 @@ function parseItem(t: Token[]): [Token[], Item] { let tok; [t, tok] = next(t); if (tok.kind === "function") { - let name; - [t, name] = expectNext(t, "identifier"); - - [t] = expectNext(t, "("); - - let args: FunctionArg[]; - [t, args] = parseCommaSeparatedList(t, ")", (t) => { - let name; - [t, name] = expectNext(t, "identifier"); - [t] = expectNext(t, ":"); - let type; - [t, type] = parseType(t); - - return [t, { name: name.ident, type, span: name.span }]; - }); - - let colon; - let returnType = undefined; - [t, colon] = eat(t, ":"); - if (colon) { - [t, returnType] = parseType(t); - } + let sig; + [t, sig] = parseFunctionSig(t); [t] = expectNext(t, "="); @@ -80,9 +67,7 @@ function parseItem(t: Token[]): [Token[], Item] { [t] = expectNext(t, ";"); const def: FunctionDef = { - name: name.ident, - params: args, - returnType, + ...sig, body, }; @@ -129,11 +114,64 @@ function parseItem(t: Token[]): [Token[], Item] { }; return [t, { kind: "type", node: def, span: name.span, id: 0 }]; + } else if (tok.kind === "import") { + [t] = expectNext(t, "("); + let module; + [t, module] = expectNext(t, "lit_string"); + let func; + [t, func] = expectNext(t, "lit_string"); + [t] = expectNext(t, ")"); + + let sig; + [t, sig] = parseFunctionSig(t); + + [t] = expectNext(t, ";"); + + const def: ImportDef = { + module: { kind: "str", value: module.value, span: module.span }, + func: { kind: "str", value: func.value, span: func.span }, + ...sig, + }; + + return [t, { kind: "import", node: def, span: tok.span, id: 0 }]; } else { unexpectedToken(tok, "item"); } } +type FunctionSig = { + name: string; + params: FunctionArg[]; + returnType?: Type; +}; + +function parseFunctionSig(t: Token[]): [Token[], FunctionSig] { + let name; + [t, name] = expectNext(t, "identifier"); + + [t] = expectNext(t, "("); + + let params: FunctionArg[]; + [t, params] = parseCommaSeparatedList(t, ")", (t) => { + let name; + [t, name] = expectNext(t, "identifier"); + [t] = expectNext(t, ":"); + let type; + [t, type] = parseType(t); + + return [t, { name: name.ident, type, span: name.span }]; + }); + + let colon; + let returnType = undefined; + [t, colon] = eat(t, ":"); + if (colon) { + [t, returnType] = parseType(t); + } + + return [t, { name: name.ident, params, returnType }]; +} + function parseExpr(t: Token[]): [Token[], Expr] { /* EXPR = COMPARISON @@ -267,7 +305,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] { { kind: "literal", span, - value: { kind: "str", value: tok.value }, + value: { kind: "str", value: tok.value, span: tok.span }, }, ]; } @@ -278,7 +316,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] { { kind: "literal", span, - value: { kind: "int", value: tok.value }, + value: { kind: "int", value: tok.value, type: tok.type }, }, ]; } @@ -340,7 +378,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] { if (tok.kind === "if") { let cond; [t, cond] = parseExpr(t); - + [t] = expectNext(t, "then"); let then; [t, then] = parseExpr(t); diff --git a/src/printer.ts b/src/printer.ts index 4ecd65c..6256617 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -3,8 +3,10 @@ import { Expr, FunctionDef, Identifier, + ImportDef, Item, Resolution, + StringLiteral, Ty, Type, TypeDef, @@ -15,6 +17,10 @@ export function printAst(ast: Ast): string { return ast.items.map(printItem).join("\n"); } +function printStringLiteral(lit: StringLiteral): string { + return `"${lit.value}"`; +} + function printItem(item: Item): string { switch (item.kind) { case "function": { @@ -23,6 +29,9 @@ function printItem(item: Item): string { case "type": { return printTypeDef(item.node); } + case "import": { + return printImportDef(item.node); + } } } @@ -45,6 +54,17 @@ function printTypeDef(type: TypeDef): string { return `type ${type.name} = ${fieldPart};`; } +function printImportDef(def: ImportDef): string { + const args = def.params + .map(({ name, type }) => `${name}: ${printType(type)}`) + .join(", "); + const ret = def.returnType ? `: ${printType(def.returnType)}` : ""; + + return `import ${printStringLiteral(def.module)} ${printStringLiteral( + def.func + )}(${args})${ret};`; +} + function printExpr(expr: Expr, indent: number): string { switch (expr.kind) { case "empty": { @@ -84,10 +104,10 @@ function printExpr(expr: Expr, indent: number): string { case "literal": { switch (expr.value.kind) { case "str": { - return `"${expr.value.value}"`; + return printStringLiteral(expr.value); } case "int": { - return `${expr.value.value}`; + return `${expr.value.value}_${expr.value.type}`; } } } diff --git a/src/typeck.ts b/src/typeck.ts index f15a558..ac8872f 100644 --- a/src/typeck.ts +++ b/src/typeck.ts @@ -1,6 +1,7 @@ import { Ast, binaryExprPrecedenceClass, + BuiltinName, COMPARISON_KINDS, DEFAULT_FOLDER, EQUALITY_KINDS, @@ -21,12 +22,17 @@ import { TY_STRING, TY_UNIT, TyFn, + tyIsUnit, Type, TyStruct, } from "./ast"; import { CompilerError, Span } from "./error"; import { printTy } from "./printer"; +function mkTyFn(params: Ty[], returnTy: Ty): Ty { + return { kind: "fn", params, returnTy }; +} + function builtinAsTy(name: string, span: Span): Ty { switch (name) { case "String": { @@ -47,13 +53,25 @@ function builtinAsTy(name: string, span: Span): Ty { } } -function typeOfBuiltinValue(name: string, span: Span): Ty { +function typeOfBuiltinValue(name: BuiltinName, span: Span): Ty { switch (name) { case "false": case "true": return TY_BOOL; case "print": - return { kind: "fn", params: [TY_STRING], returnTy: TY_UNIT }; + return mkTyFn([TY_STRING], TY_UNIT); + case "__i32_store": + return mkTyFn([TY_I32, TY_I32], TY_UNIT); + case "__i64_store": + return mkTyFn([TY_I32, TY_INT], TY_UNIT); + case "__i32_load": + return mkTyFn([TY_I32], TY_I32); + case "__i64_load": + return mkTyFn([TY_I32], TY_INT); + case "__string_ptr": + return mkTyFn([TY_STRING], TY_I32); + case "__string_len": + return mkTyFn([TY_STRING], TY_I32); default: { throw new CompilerError(`\`${name}\` cannot be used as a value`, span); } @@ -103,7 +121,8 @@ export function typeck(ast: Ast): Ast { } itemTys.set(index, null); switch (item.kind) { - case "function": { + case "function": + case "import": { const args = item.node.params.map((arg) => lowerAstTy(arg.type)); const returnTy: Ty = item.node.returnType ? lowerAstTy(item.node.returnType) @@ -182,6 +201,58 @@ export function typeck(ast: Ast): Ast { }, }; } + case "import": { + const fnTy = typeOfItem(item.id) as TyFn; + + fnTy.params.forEach((param, i) => { + switch (param.kind) { + case "int": + case "i32": + break; + default: { + throw new CompilerError( + `import parameters must be I32 or Int`, + item.node.params[i].span + ); + } + } + }); + + if (!tyIsUnit(fnTy.returnTy)) { + switch (fnTy.returnTy.kind) { + case "int": + case "i32": + break; + default: { + throw new CompilerError( + `import return must be I32 or Int`, + item.node.returnType!.span + ); + } + } + } + + const returnType = item.node.returnType && { + ...item.node.returnType, + ty: fnTy.returnTy, + }; + + return { + ...item, + kind: "import", + node: { + module: item.node.module, + func: item.node.func, + name: item.node.name, + params: item.node.params.map((arg, i) => ({ + ...arg, + type: { ...arg.type, ty: fnTy.params[i] }, + })), + returnType, + ty: fnTy, + }, + }; + } case "type": { const fieldNames = new Set(); item.node.fields.forEach(({ name }) => { @@ -436,7 +507,17 @@ export function checkBody( type, (ident) => { const res = ident.res!; - return typeOf(res, ident.span); + switch (res.kind) { + case "local": { + const idx = localTys.length - 1 - res.index; + return localTys[idx]; + } + case "item": { + return typeOfItem(res.index); + } + case "builtin": + return builtinAsTy(res.name, ident.span); + } }, typeOfItem ); @@ -502,7 +583,14 @@ export function checkBody( break; } case "int": { - ty = TY_INT; + switch (expr.value.type) { + case "Int": + ty = TY_INT; + break; + case "I32": + ty = TY_I32; + break; + } break; } } @@ -721,6 +809,9 @@ function checkBinary(expr: Expr & ExprBinary): Expr { if (lhsTy.kind === "int" && rhsTy.kind === "int") { return { ...expr, ty: TY_INT }; } + if (lhsTy.kind === "i32" && rhsTy.kind === "i32") { + return { ...expr, ty: TY_I32 }; + } if (LOGICAL_KINDS.includes(expr.binaryKind)) { if (lhsTy.kind === "bool" && rhsTy.kind === "bool") {