This commit is contained in:
nora 2023-07-30 16:54:04 +02:00
parent 50e82066c9
commit 6d2a2fe474
9 changed files with 443 additions and 98 deletions

View file

@ -24,6 +24,7 @@
nodejs-18_x # Node.js 18, plus npm, npx, and corepack nodejs-18_x # Node.js 18, plus npm, npx, and corepack
wasmtime wasmtime
wasm-tools wasm-tools
binaryen
]; ];
}; };
}); });

25
opt.wat Normal file
View file

@ -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")
)

View file

@ -1,4 +1,5 @@
import { Span } from "./error"; import { Span } from "./error";
import { LitIntType } from "./lexer";
export type Ast = { items: Item[]; typeckResults?: TypeckResults }; export type Ast = { items: Item[]; typeckResults?: TypeckResults };
@ -18,6 +19,10 @@ export type ItemKind =
| { | {
kind: "type"; kind: "type";
node: TypeDef; node: TypeDef;
}
| {
kind: "import";
node: ImportDef;
}; };
export type Item = ItemKind & { export type Item = ItemKind & {
@ -50,6 +55,15 @@ export type FieldDef = {
type: Type; type: Type;
}; };
export type ImportDef = {
module: StringLiteral;
func: StringLiteral;
name: string;
params: FunctionArg[];
returnType?: Type;
ty?: TyFn;
};
export type ExprEmpty = { kind: "empty" }; export type ExprEmpty = { kind: "empty" };
export type ExprLet = { export type ExprLet = {
@ -143,14 +157,18 @@ export type Expr = ExprKind & {
ty?: Ty; ty?: Ty;
}; };
export type StringLiteral = {
kind: "str";
value: string;
span: Span;
};
export type Literal = export type Literal =
| { | StringLiteral
kind: "str";
value: string;
}
| { | {
kind: "int"; kind: "int";
value: number; value: number;
type: LitIntType;
}; };
export type BinaryKind = export type BinaryKind =
@ -265,6 +283,13 @@ export const BUILTINS = [
"Bool", "Bool",
"true", "true",
"false", "false",
// Intrinsics:
"__i32_store",
"__i64_store",
"__i32_load",
"__i64_load",
"__string_ptr",
"__string_len",
] as const; ] as const;
export type BuiltinName = (typeof BUILTINS)[number]; export type BuiltinName = (typeof BUILTINS)[number];
@ -399,15 +424,14 @@ export function superFoldItem(item: Item, folder: Folder): Item {
})); }));
return { return {
...item,
kind: "function", kind: "function",
span: item.span,
node: { node: {
name: item.node.name, name: item.node.name,
params: args, params: args,
body: folder.expr(item.node.body), body: folder.expr(item.node.body),
returnType: item.node.returnType && folder.type(item.node.returnType), returnType: item.node.returnType && folder.type(item.node.returnType),
}, },
id: item.id,
}; };
} }
case "type": { case "type": {
@ -417,10 +441,27 @@ export function superFoldItem(item: Item, folder: Folder): Item {
})); }));
return { return {
...item,
kind: "type", kind: "type",
span: item.span,
node: { name: item.node.name, fields }, 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),
},
}; };
} }
} }

View file

@ -10,20 +10,32 @@ import fs from "fs";
import { exec } from "child_process"; import { exec } from "child_process";
const input = ` 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() = ( function coolerPrint(a: String) = (
loop (no(break);); let ptr = __string_ptr(a);
uwu(10); 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 main() = (
coolerPrint("uwu\\n");
function no(a: !): String = a;
function uwu(a: Int) = if a != 0 then (
print("uwu\n");
uwu(a - 1);
); );
`; `;

View file

@ -9,6 +9,7 @@ export type DatalessToken =
| "type" | "type"
| "loop" | "loop"
| "break" | "break"
| "import"
| "(" | "("
| ")" | ")"
| "{" | "{"
@ -36,14 +37,19 @@ export type DatalessToken =
export type TokenIdent = { kind: "identifier"; ident: string }; export type TokenIdent = { kind: "identifier"; ident: string };
export type TokenLitString = {
kind: "lit_string";
value: string;
};
export type LitIntType = "Int" | "I32";
export type TokenLit = export type TokenLit =
| { | TokenLitString
kind: "lit_string";
value: string;
}
| { | {
kind: "lit_int"; kind: "lit_int";
value: number; value: number;
type: LitIntType;
}; };
export type TokenKind = { kind: DatalessToken } | TokenIdent | TokenLit; export type TokenKind = { kind: DatalessToken } | TokenIdent | TokenLit;
@ -80,13 +86,13 @@ export function tokenize(input: string): Token[] {
const next = input[i]; const next = input[i];
const span: Span = { start: i, end: i + 1 }; const span: Span = { start: i, end: i + 1 };
if (next === "/" && input[i + 1] === "/") { if (next === "/" && input[i + 1] === "/") {
while (input[i] !== "\n") { while (input[i] !== "\n") {
i++; i++;
} }
continue; continue;
} }
if (SINGLE_PUNCT.includes(next)) { if (SINGLE_PUNCT.includes(next)) {
tokens.push({ kind: next as DatalessToken, span }); 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)) { } else if (isIdentStart(next)) {
while (isIdentContinue(input[i + 1])) { while (isIdentContinue(input[i + 1])) {
span.end++; span.end++;
@ -267,6 +287,7 @@ const KEYOWRDS: DatalessToken[] = [
"type", "type",
"loop", "loop",
"break", "break",
"import",
]; ];
const KEYWORD_SET = new Set<string>(KEYOWRDS); const KEYWORD_SET = new Set<string>(KEYOWRDS);

View file

@ -3,6 +3,7 @@ import {
Expr, Expr,
ExprBlock, ExprBlock,
FunctionDef, FunctionDef,
ImportDef,
Item, Item,
Resolution, Resolution,
Ty, Ty,
@ -12,8 +13,6 @@ import {
import { encodeUtf8 } from "./utils"; import { encodeUtf8 } from "./utils";
import * as wasm from "./wasm/defs"; import * as wasm from "./wasm/defs";
type StringifiedForMap<T> = string;
const USIZE: wasm.ValType = "i32"; const USIZE: wasm.ValType = "i32";
// POINTERS ARE JUST INTEGERS // POINTERS ARE JUST INTEGERS
const POINTER: wasm.ValType = USIZE; const POINTER: wasm.ValType = USIZE;
@ -28,22 +27,25 @@ type Relocation = {
instr: wasm.Instr & { func: wasm.FuncIdx }; instr: wasm.Instr & { func: wasm.FuncIdx };
} & { res: Resolution }; } & { res: Resolution };
function setMap<K, V>(map: Map<StringifiedForMap<K>, V>, key: K, value: V) { type StringifiedMap<K, V> = { _map: Map<string, V> };
map.set(JSON.stringify(key), value);
function setMap<K, V>(map: StringifiedMap<K, V>, key: K, value: V) {
map._map.set(JSON.stringify(key), value);
} }
function getMap<K, V>( function getMap<K, V>(map: StringifiedMap<K, V>, key: K): V | undefined {
map: Map<StringifiedForMap<K>, V>, return map._map.get(JSON.stringify(key));
key: K
): V | undefined {
return map.get(JSON.stringify(key));
} }
type FuncOrImport =
| { kind: "func"; idx: wasm.FuncIdx }
| { kind: "import"; idx: number };
export type Context = { export type Context = {
mod: wasm.Module; mod: wasm.Module;
funcTypes: Map<StringifiedForMap<wasm.FuncType>, wasm.TypeIdx>; funcTypes: StringifiedMap<wasm.FuncType, wasm.TypeIdx>;
reservedHeapMemoryStart: number; reservedHeapMemoryStart: number;
funcIndices: Map<StringifiedForMap<Resolution>, wasm.FuncIdx>; funcIndices: StringifiedMap<Resolution, FuncOrImport>;
ast: Ast; ast: Ast;
relocations: Relocation[]; relocations: Relocation[];
}; };
@ -117,8 +119,8 @@ export function lower(ast: Ast): wasm.Module {
const cx: Context = { const cx: Context = {
mod, mod,
funcTypes: new Map(), funcTypes: { _map: new Map() },
funcIndices: new Map(), funcIndices: { _map: new Map() },
reservedHeapMemoryStart: 0, reservedHeapMemoryStart: 0,
ast, ast,
relocations: [], relocations: [],
@ -128,6 +130,11 @@ export function lower(ast: Ast): wasm.Module {
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
lowerFunc(cx, item, item.node); 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) => { cx.relocations.forEach((rel) => {
switch (rel.kind) { switch (rel.kind) {
case "funccall": { case "funccall": {
const idx = getMap<Resolution, number>(cx.funcIndices, rel.res); const idx = getMap<Resolution, FuncOrImport>(cx.funcIndices, rel.res);
if (idx === undefined) { if (idx === undefined) {
throw new Error( throw new Error(
`no function found for relocation '${JSON.stringify(rel.res)}'` `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; 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<Resolution, FuncOrImport>(
cx.funcIndices,
{ kind: "item", index: item.id },
{ kind: "import", idx }
);
}
type FuncContext = { type FuncContext = {
cx: Context; cx: Context;
item: Item; item: Item;
@ -198,10 +236,10 @@ function lowerFunc(cx: Context, item: Item, func: FunctionDef) {
const idx = fcx.cx.mod.funcs.length; const idx = fcx.cx.mod.funcs.length;
fcx.cx.mod.funcs.push(wasmFunc); fcx.cx.mod.funcs.push(wasmFunc);
setMap<Resolution, number>( setMap<Resolution, FuncOrImport>(
fcx.cx.funcIndices, fcx.cx.funcIndices,
{ kind: "item", index: fcx.item.id }, { 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) { function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
const ty = expr.ty!; const ty = expr.ty!;
switch (expr.kind) { exprKind: switch (expr.kind) {
case "empty": { case "empty": {
// A ZST, do nothing. // A ZST, do nothing.
return; return;
@ -265,7 +303,14 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
break; break;
case "int": 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;
} }
break; break;
@ -306,49 +351,55 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
lowerExpr(fcx, instrs, expr.lhs); lowerExpr(fcx, instrs, expr.lhs);
lowerExpr(fcx, instrs, expr.rhs); 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"]; let kind: wasm.Instr["kind"];
const valty = lhsTy.kind === "int" ? "i64" : "i32";
switch (expr.binaryKind) { switch (expr.binaryKind) {
case "+": case "+":
kind = "i64.add"; kind = `${valty}.add`;
break; break;
case "-": case "-":
kind = "i64.sub"; kind = `${valty}.sub`;
break; break;
case "*": case "*":
kind = "i64.mul"; kind = `${valty}.mul`;
break; break;
case "/": case "/":
kind = "i64.div_u"; kind = `${valty}.div_u`;
break; break;
case "&": case "&":
kind = "i64.and"; kind = `${valty}.and`;
break; break;
case "|": case "|":
kind = "i64.or"; kind = `${valty}.or`;
break; break;
case "<": case "<":
kind = "i64.lt_u"; kind = `${valty}.lt_u`;
break; break;
case ">": case ">":
kind = "i64.gt_u"; kind = `${valty}.gt_u`;
// errs // errs
break; break;
case "==": case "==":
kind = "i64.eq"; kind = `${valty}.eq`;
break; break;
case "<=": case "<=":
kind = "i64.le_u"; kind = `${valty}.le_u`;
break; break;
case ">=": case ">=":
kind = "i64.ge_u"; kind = `${valty}.ge_u`;
break; break;
case "!=": case "!=":
kind = "i64.ne"; kind = `${valty}.ne`;
break; break;
} }
instrs.push({ kind }); 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"]; let kind: wasm.Instr["kind"];
switch (expr.binaryKind) { switch (expr.binaryKind) {
@ -404,6 +455,50 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
if (expr.lhs.kind !== "ident") { if (expr.lhs.kind !== "ident") {
todo("non constant calls"); 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 }; const callInstr: wasm.Instr = { kind: "call", func: 9999999999 };
fcx.cx.relocations.push({ fcx.cx.relocations.push({
kind: "funccall", kind: "funccall",
@ -676,9 +771,10 @@ function addRt(cx: Context, ast: Ast) {
const printIdx = cx.mod.funcs.length; const printIdx = cx.mod.funcs.length;
cx.mod.funcs.push(print); cx.mod.funcs.push(print);
cx.funcIndices.set( setMap(
JSON.stringify({ kind: "builtin", name: "print" }), cx.funcIndices,
printIdx { kind: "builtin", name: "print" },
{ kind: "func", idx: printIdx }
); );
mod.exports.push({ mod.exports.push({

View file

@ -12,6 +12,7 @@ import {
FunctionArg, FunctionArg,
FunctionDef, FunctionDef,
Identifier, Identifier,
ImportDef,
Item, Item,
LOGICAL_KINDS, LOGICAL_KINDS,
Type, Type,
@ -23,7 +24,13 @@ import {
superFoldExpr, superFoldExpr,
} from "./ast"; } from "./ast";
import { CompilerError, Span, spanMerge } from "./error"; import { CompilerError, Span, spanMerge } from "./error";
import { BaseToken, Token, TokenIdent } from "./lexer"; import {
BaseToken,
Token,
TokenIdent,
TokenLit,
TokenLitString,
} from "./lexer";
type Parser<T> = (t: Token[]) => [Token[], T]; type Parser<T> = (t: Token[]) => [Token[], T];
@ -49,28 +56,8 @@ function parseItem(t: Token[]): [Token[], Item] {
let tok; let tok;
[t, tok] = next(t); [t, tok] = next(t);
if (tok.kind === "function") { if (tok.kind === "function") {
let name; let sig;
[t, name] = expectNext<TokenIdent>(t, "identifier"); [t, sig] = parseFunctionSig(t);
[t] = expectNext(t, "(");
let args: FunctionArg[];
[t, args] = parseCommaSeparatedList(t, ")", (t) => {
let name;
[t, name] = expectNext<TokenIdent & { span: Span }>(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);
}
[t] = expectNext(t, "="); [t] = expectNext(t, "=");
@ -80,9 +67,7 @@ function parseItem(t: Token[]): [Token[], Item] {
[t] = expectNext(t, ";"); [t] = expectNext(t, ";");
const def: FunctionDef = { const def: FunctionDef = {
name: name.ident, ...sig,
params: args,
returnType,
body, body,
}; };
@ -129,11 +114,64 @@ function parseItem(t: Token[]): [Token[], Item] {
}; };
return [t, { kind: "type", node: def, span: name.span, id: 0 }]; return [t, { kind: "type", node: def, span: name.span, id: 0 }];
} else if (tok.kind === "import") {
[t] = expectNext(t, "(");
let module;
[t, module] = expectNext<TokenLitString>(t, "lit_string");
let func;
[t, func] = expectNext<TokenLitString>(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 { } else {
unexpectedToken(tok, "item"); unexpectedToken(tok, "item");
} }
} }
type FunctionSig = {
name: string;
params: FunctionArg[];
returnType?: Type;
};
function parseFunctionSig(t: Token[]): [Token[], FunctionSig] {
let name;
[t, name] = expectNext<TokenIdent>(t, "identifier");
[t] = expectNext(t, "(");
let params: FunctionArg[];
[t, params] = parseCommaSeparatedList(t, ")", (t) => {
let name;
[t, name] = expectNext<TokenIdent & { span: Span }>(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] { function parseExpr(t: Token[]): [Token[], Expr] {
/* /*
EXPR = COMPARISON EXPR = COMPARISON
@ -267,7 +305,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
{ {
kind: "literal", kind: "literal",
span, 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", kind: "literal",
span, 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") { if (tok.kind === "if") {
let cond; let cond;
[t, cond] = parseExpr(t); [t, cond] = parseExpr(t);
[t] = expectNext(t, "then"); [t] = expectNext(t, "then");
let then; let then;
[t, then] = parseExpr(t); [t, then] = parseExpr(t);

View file

@ -3,8 +3,10 @@ import {
Expr, Expr,
FunctionDef, FunctionDef,
Identifier, Identifier,
ImportDef,
Item, Item,
Resolution, Resolution,
StringLiteral,
Ty, Ty,
Type, Type,
TypeDef, TypeDef,
@ -15,6 +17,10 @@ export function printAst(ast: Ast): string {
return ast.items.map(printItem).join("\n"); return ast.items.map(printItem).join("\n");
} }
function printStringLiteral(lit: StringLiteral): string {
return `"${lit.value}"`;
}
function printItem(item: Item): string { function printItem(item: Item): string {
switch (item.kind) { switch (item.kind) {
case "function": { case "function": {
@ -23,6 +29,9 @@ function printItem(item: Item): string {
case "type": { case "type": {
return printTypeDef(item.node); return printTypeDef(item.node);
} }
case "import": {
return printImportDef(item.node);
}
} }
} }
@ -45,6 +54,17 @@ function printTypeDef(type: TypeDef): string {
return `type ${type.name} = ${fieldPart};`; 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 { function printExpr(expr: Expr, indent: number): string {
switch (expr.kind) { switch (expr.kind) {
case "empty": { case "empty": {
@ -84,10 +104,10 @@ function printExpr(expr: Expr, indent: number): string {
case "literal": { case "literal": {
switch (expr.value.kind) { switch (expr.value.kind) {
case "str": { case "str": {
return `"${expr.value.value}"`; return printStringLiteral(expr.value);
} }
case "int": { case "int": {
return `${expr.value.value}`; return `${expr.value.value}_${expr.value.type}`;
} }
} }
} }

View file

@ -1,6 +1,7 @@
import { import {
Ast, Ast,
binaryExprPrecedenceClass, binaryExprPrecedenceClass,
BuiltinName,
COMPARISON_KINDS, COMPARISON_KINDS,
DEFAULT_FOLDER, DEFAULT_FOLDER,
EQUALITY_KINDS, EQUALITY_KINDS,
@ -21,12 +22,17 @@ import {
TY_STRING, TY_STRING,
TY_UNIT, TY_UNIT,
TyFn, TyFn,
tyIsUnit,
Type, Type,
TyStruct, TyStruct,
} from "./ast"; } from "./ast";
import { CompilerError, Span } from "./error"; import { CompilerError, Span } from "./error";
import { printTy } from "./printer"; import { printTy } from "./printer";
function mkTyFn(params: Ty[], returnTy: Ty): Ty {
return { kind: "fn", params, returnTy };
}
function builtinAsTy(name: string, span: Span): Ty { function builtinAsTy(name: string, span: Span): Ty {
switch (name) { switch (name) {
case "String": { 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) { switch (name) {
case "false": case "false":
case "true": case "true":
return TY_BOOL; return TY_BOOL;
case "print": 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: { default: {
throw new CompilerError(`\`${name}\` cannot be used as a value`, span); 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); itemTys.set(index, null);
switch (item.kind) { switch (item.kind) {
case "function": { case "function":
case "import": {
const args = item.node.params.map((arg) => lowerAstTy(arg.type)); const args = item.node.params.map((arg) => lowerAstTy(arg.type));
const returnTy: Ty = item.node.returnType const returnTy: Ty = item.node.returnType
? lowerAstTy(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": { case "type": {
const fieldNames = new Set(); const fieldNames = new Set();
item.node.fields.forEach(({ name }) => { item.node.fields.forEach(({ name }) => {
@ -436,7 +507,17 @@ export function checkBody(
type, type,
(ident) => { (ident) => {
const res = ident.res!; 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 typeOfItem
); );
@ -502,7 +583,14 @@ export function checkBody(
break; break;
} }
case "int": { case "int": {
ty = TY_INT; switch (expr.value.type) {
case "Int":
ty = TY_INT;
break;
case "I32":
ty = TY_I32;
break;
}
break; break;
} }
} }
@ -721,6 +809,9 @@ function checkBinary(expr: Expr & ExprBinary): Expr {
if (lhsTy.kind === "int" && rhsTy.kind === "int") { if (lhsTy.kind === "int" && rhsTy.kind === "int") {
return { ...expr, ty: TY_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 (LOGICAL_KINDS.includes(expr.binaryKind)) {
if (lhsTy.kind === "bool" && rhsTy.kind === "bool") { if (lhsTy.kind === "bool" && rhsTy.kind === "bool") {