mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
generate more code
This commit is contained in:
parent
ccd8008731
commit
42bc96dbce
7 changed files with 306 additions and 51 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
||||||
/node_modules
|
/node_modules
|
||||||
/target
|
/target
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
/*-example.wat
|
/*-example.wat
|
||||||
|
/out.wat
|
||||||
|
|
|
||||||
20
src/ast.ts
20
src/ast.ts
|
|
@ -155,8 +155,8 @@ const BINARY_KIND_PREC_CLASS = new Map<BinaryKind, number>([
|
||||||
|
|
||||||
export function binaryExprPrecedenceClass(k: BinaryKind): number {
|
export function binaryExprPrecedenceClass(k: BinaryKind): number {
|
||||||
const cls = BINARY_KIND_PREC_CLASS.get(k);
|
const cls = BINARY_KIND_PREC_CLASS.get(k);
|
||||||
if (!cls) {
|
if (cls === undefined) {
|
||||||
throw new Error(`Invalid binary kind: ${k}`);
|
throw new Error(`Invalid binary kind: '${k}'`);
|
||||||
}
|
}
|
||||||
return cls;
|
return cls;
|
||||||
}
|
}
|
||||||
|
|
@ -209,15 +209,27 @@ export type Resolution =
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
kind: "builtin";
|
kind: "builtin";
|
||||||
name: string;
|
name: BuiltinName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const BUILTINS = [
|
||||||
|
"print",
|
||||||
|
"String",
|
||||||
|
"Int",
|
||||||
|
"Bool",
|
||||||
|
"true",
|
||||||
|
"false",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type BuiltinName = (typeof BUILTINS)[number];
|
||||||
|
|
||||||
export type TyString = {
|
export type TyString = {
|
||||||
kind: "string";
|
kind: "string";
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TyInt = {
|
export type TyInt = {
|
||||||
kind: "int";
|
kind: "int";
|
||||||
|
signed: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TyBool = {
|
export type TyBool = {
|
||||||
|
|
@ -259,7 +271,7 @@ export function tyIsUnit(ty: Ty): ty is TyUnit {
|
||||||
export const TY_UNIT: Ty = { kind: "tuple", elems: [] };
|
export const TY_UNIT: Ty = { kind: "tuple", elems: [] };
|
||||||
export const TY_STRING: Ty = { kind: "string" };
|
export const TY_STRING: Ty = { kind: "string" };
|
||||||
export const TY_BOOL: Ty = { kind: "bool" };
|
export const TY_BOOL: Ty = { kind: "bool" };
|
||||||
export const TY_INT: Ty = { kind: "int" };
|
export const TY_INT: Ty = { kind: "int", signed: false };
|
||||||
|
|
||||||
// folders
|
// folders
|
||||||
|
|
||||||
|
|
|
||||||
33
src/index.ts
33
src/index.ts
|
|
@ -6,13 +6,17 @@ import { printAst } from "./printer";
|
||||||
import { resolve } from "./resolve";
|
import { resolve } from "./resolve";
|
||||||
import { typeck } from "./typeck";
|
import { typeck } from "./typeck";
|
||||||
import { writeModuleWatToString } from "./wasm/wat";
|
import { writeModuleWatToString } from "./wasm/wat";
|
||||||
|
import fs from "fs";
|
||||||
|
import { exec } from "child_process";
|
||||||
|
|
||||||
const input = `
|
const input = `
|
||||||
function main(i: Int): Int = 0;
|
function main(i: Int, j: Int): Bool = false == (i == 0);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
withErrorHandler(input, () => {
|
withErrorHandler(input, () => {
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
const tokens = tokenize(input);
|
const tokens = tokenize(input);
|
||||||
console.log("-----TOKENS------------");
|
console.log("-----TOKENS------------");
|
||||||
console.log(tokens);
|
console.log(tokens);
|
||||||
|
|
@ -34,11 +38,34 @@ function main() {
|
||||||
console.log("-----AST typecked------");
|
console.log("-----AST typecked------");
|
||||||
|
|
||||||
const typecked = typeck(resolved);
|
const typecked = typeck(resolved);
|
||||||
|
|
||||||
console.log("-----wasm--------------");
|
console.log("-----wasm--------------");
|
||||||
const wasmModule = lowerToWasm(typecked);
|
const wasmModule = lowerToWasm(typecked);
|
||||||
|
const moduleStringColor = writeModuleWatToString(wasmModule, true);
|
||||||
const moduleString = writeModuleWatToString(wasmModule);
|
const moduleString = writeModuleWatToString(wasmModule);
|
||||||
console.log(moduleString);
|
|
||||||
|
console.log(moduleStringColor);
|
||||||
|
|
||||||
|
fs.writeFileSync("out.wat", moduleString);
|
||||||
|
|
||||||
|
console.log("--validate wasm-tools--");
|
||||||
|
|
||||||
|
exec("wasm-tools validate out.wat", (error, stdout, stderr) => {
|
||||||
|
if (error && error.code === 1) {
|
||||||
|
console.log(stderr);
|
||||||
|
} else if (error) {
|
||||||
|
console.error(`failed to spawn wasm-tools: ${error}`);
|
||||||
|
} else {
|
||||||
|
if (stderr) {
|
||||||
|
console.log(stderr);
|
||||||
|
}
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`finished in ${Date.now() - start}ms`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
262
src/lower.ts
262
src/lower.ts
|
|
@ -1,4 +1,4 @@
|
||||||
import { Ast, FunctionDef, Item, Ty, TyFn, varUnreachable } from "./ast";
|
import { Ast, Expr, FunctionDef, Item, Ty, TyFn, varUnreachable } from "./ast";
|
||||||
import * as wasm from "./wasm/defs";
|
import * as wasm from "./wasm/defs";
|
||||||
|
|
||||||
type StringifiedForMap<T> = string;
|
type StringifiedForMap<T> = string;
|
||||||
|
|
@ -39,13 +39,7 @@ export function lower(ast: Ast): wasm.Module {
|
||||||
ast.forEach((item) => {
|
ast.forEach((item) => {
|
||||||
switch (item.kind) {
|
switch (item.kind) {
|
||||||
case "function": {
|
case "function": {
|
||||||
const fcx: FuncContext = {
|
lowerFunc(cx, item, item.node);
|
||||||
cx,
|
|
||||||
item,
|
|
||||||
func: item.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
lowerFunc(fcx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -57,6 +51,8 @@ type FuncContext = {
|
||||||
cx: Context;
|
cx: Context;
|
||||||
item: Item;
|
item: Item;
|
||||||
func: FunctionDef;
|
func: FunctionDef;
|
||||||
|
wasm: wasm.Func;
|
||||||
|
varLocations: VarLocation[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type Abi = { params: ArgAbi[]; ret: RetAbi };
|
type Abi = { params: ArgAbi[]; ret: RetAbi };
|
||||||
|
|
@ -64,22 +60,210 @@ type Abi = { params: ArgAbi[]; ret: RetAbi };
|
||||||
type ArgAbi = { kind: "scalar"; type: wasm.ValType } | { kind: "zst" };
|
type ArgAbi = { kind: "scalar"; type: wasm.ValType } | { kind: "zst" };
|
||||||
type RetAbi = { kind: "scalar"; type: wasm.ValType } | { kind: "zst" };
|
type RetAbi = { kind: "scalar"; type: wasm.ValType } | { kind: "zst" };
|
||||||
|
|
||||||
function lowerFunc(fcx: FuncContext) {
|
type VarLocation = { kind: "local"; idx: number } | { kind: "zst" };
|
||||||
const abi = computeAbi(fcx.func.ty!);
|
|
||||||
const wasmType = wasmTypeForAbi(abi);
|
function lowerFunc(cx: Context, item: Item, func: FunctionDef) {
|
||||||
const type = internFuncType(fcx.cx, wasmType);
|
const abi = computeAbi(func.ty!);
|
||||||
|
const { type: wasmType, paramLocations } = wasmTypeForAbi(abi);
|
||||||
|
const type = internFuncType(cx, wasmType);
|
||||||
|
|
||||||
const wasmFunc: wasm.Func = {
|
const wasmFunc: wasm.Func = {
|
||||||
|
_name: func.name,
|
||||||
type,
|
type,
|
||||||
locals: [],
|
locals: [],
|
||||||
body: [],
|
body: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fcx: FuncContext = {
|
||||||
|
cx,
|
||||||
|
item,
|
||||||
|
func,
|
||||||
|
wasm: wasmFunc,
|
||||||
|
varLocations: paramLocations,
|
||||||
|
};
|
||||||
|
|
||||||
|
lowerExpr(fcx, wasmFunc.body, fcx.func.body);
|
||||||
|
|
||||||
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);
|
||||||
fcx.cx.funcIndices.set(fcx.item.id, idx);
|
fcx.cx.funcIndices.set(fcx.item.id, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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) {
|
||||||
|
const ty = expr.ty!;
|
||||||
|
|
||||||
|
switch (expr.kind) {
|
||||||
|
case "empty":
|
||||||
|
// A ZST, do nothing.
|
||||||
|
return;
|
||||||
|
case "let":
|
||||||
|
// Let, that's complicated.
|
||||||
|
todo("let");
|
||||||
|
case "block":
|
||||||
|
if (expr.exprs.length === 1) {
|
||||||
|
lowerExpr(fcx, instrs, expr.exprs[0]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "literal":
|
||||||
|
switch (expr.value.kind) {
|
||||||
|
case "str":
|
||||||
|
todo("strings");
|
||||||
|
case "int":
|
||||||
|
instrs.push({ kind: "i64.const", imm: expr.value.value });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ident":
|
||||||
|
const res = expr.value.res!;
|
||||||
|
switch (res.kind) {
|
||||||
|
case "local": {
|
||||||
|
const location =
|
||||||
|
fcx.varLocations[fcx.varLocations.length - 1 - res.index];
|
||||||
|
loadVariable(instrs, location);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "item":
|
||||||
|
todo("item ident res");
|
||||||
|
case "builtin":
|
||||||
|
switch (res.name) {
|
||||||
|
case "false":
|
||||||
|
instrs.push({ kind: "i32.const", imm: 0 });
|
||||||
|
break;
|
||||||
|
case "true":
|
||||||
|
instrs.push({ kind: "i32.const", imm: 1 });
|
||||||
|
break;
|
||||||
|
case "print":
|
||||||
|
todo("print function");
|
||||||
|
default: {
|
||||||
|
throw new Error(`${res.name}#B is not a value`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "binary":
|
||||||
|
// By evaluating the LHS first, the RHS is on top, which
|
||||||
|
// is correct as it's popped first. Evaluating the LHS first
|
||||||
|
// is correct for the source language too so great, no swapping.
|
||||||
|
lowerExpr(fcx, instrs, expr.lhs);
|
||||||
|
lowerExpr(fcx, instrs, expr.rhs);
|
||||||
|
|
||||||
|
if (expr.lhs.ty!.kind === "int" && expr.rhs.ty!.kind === "int") {
|
||||||
|
let kind: wasm.Instr["kind"];
|
||||||
|
switch (expr.binaryKind) {
|
||||||
|
case "+":
|
||||||
|
kind = "i64.add";
|
||||||
|
break;
|
||||||
|
case "-":
|
||||||
|
kind = "i64.sub";
|
||||||
|
break;
|
||||||
|
case "*":
|
||||||
|
kind = "i64.mul";
|
||||||
|
break;
|
||||||
|
case "/":
|
||||||
|
kind = "i64.div_u";
|
||||||
|
break;
|
||||||
|
case "&":
|
||||||
|
kind = "i64.and";
|
||||||
|
break;
|
||||||
|
case "|":
|
||||||
|
kind = "i64.or";
|
||||||
|
break;
|
||||||
|
case "<":
|
||||||
|
kind = "i64.lt_u";
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
kind = "i64.gt_u";
|
||||||
|
break;
|
||||||
|
case "==":
|
||||||
|
kind = "i64.eq";
|
||||||
|
break;
|
||||||
|
case "<=":
|
||||||
|
kind = "i64.le_u";
|
||||||
|
break;
|
||||||
|
case ">=":
|
||||||
|
kind = "i64.ge_u";
|
||||||
|
break;
|
||||||
|
case "!=":
|
||||||
|
kind = "i64.ne";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instrs.push({ kind });
|
||||||
|
} else if (expr.lhs.ty!.kind === "bool" && expr.rhs.ty!.kind === "bool") {
|
||||||
|
let kind: wasm.Instr["kind"];
|
||||||
|
|
||||||
|
switch (expr.binaryKind) {
|
||||||
|
case "&":
|
||||||
|
kind = "i32.and";
|
||||||
|
break;
|
||||||
|
case "|":
|
||||||
|
kind = "i32.or";
|
||||||
|
break;
|
||||||
|
case "==":
|
||||||
|
kind = "i32.eq";
|
||||||
|
break;
|
||||||
|
case "!=":
|
||||||
|
kind = "i32.ne";
|
||||||
|
break;
|
||||||
|
case "<":
|
||||||
|
case ">":
|
||||||
|
case "<=":
|
||||||
|
case ">=":
|
||||||
|
case "+":
|
||||||
|
case "-":
|
||||||
|
case "*":
|
||||||
|
case "/":
|
||||||
|
throw new Error(`Invalid bool binary expr: ${expr.binaryKind}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
instrs.push({ kind });
|
||||||
|
} else {
|
||||||
|
todo("non int/bool binary expr");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "unary":
|
||||||
|
lowerExpr(fcx, instrs, expr.rhs);
|
||||||
|
switch (expr.unaryKind) {
|
||||||
|
case "!":
|
||||||
|
if (ty.kind === "bool") {
|
||||||
|
// `xor RHS, 1` flips the lowermost bit.
|
||||||
|
instrs.push({ kind: "i64.const", imm: 1 });
|
||||||
|
instrs.push({ kind: "i64.xor" });
|
||||||
|
} else if (ty.kind === "int") {
|
||||||
|
// `xor RHS, -1` flips all bits.
|
||||||
|
todo("Thanks to JS, we cannot represent -1 i64 yet");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "-":
|
||||||
|
todo("negation");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "call":
|
||||||
|
todo("call");
|
||||||
|
case "if":
|
||||||
|
todo("ifs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadVariable(instrs: wasm.Instr[], loc: VarLocation) {
|
||||||
|
switch (loc.kind) {
|
||||||
|
case "local": {
|
||||||
|
instrs.push({ kind: "local.get", imm: loc.idx });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "zst":
|
||||||
|
// Load the ZST:
|
||||||
|
// ...
|
||||||
|
// 🪄 poof, the ZST is on the stack now.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function computeAbi(ty: TyFn): Abi {
|
function computeAbi(ty: TyFn): Abi {
|
||||||
const scalar = (type: wasm.ValType): ArgAbi & RetAbi =>
|
const scalar = (type: wasm.ValType): ArgAbi & RetAbi =>
|
||||||
({ kind: "scalar", type } as const);
|
({ kind: "scalar", type } as const);
|
||||||
|
|
@ -141,18 +325,24 @@ function computeAbi(ty: TyFn): Abi {
|
||||||
return { params, ret };
|
return { params, ret };
|
||||||
}
|
}
|
||||||
|
|
||||||
function wasmTypeForAbi(abi: Abi): wasm.FuncType {
|
function wasmTypeForAbi(abi: Abi): {
|
||||||
const params = abi.params
|
type: wasm.FuncType;
|
||||||
.map((arg) => {
|
paramLocations: VarLocation[];
|
||||||
switch (arg.kind) {
|
} {
|
||||||
case "scalar":
|
const params: wasm.ValType[] = [];
|
||||||
return arg.type;
|
const paramLocations: VarLocation[] = [];
|
||||||
case "zst":
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(exists);
|
|
||||||
|
|
||||||
|
abi.params.forEach((arg) => {
|
||||||
|
switch (arg.kind) {
|
||||||
|
case "scalar":
|
||||||
|
paramLocations.push({ kind: "local", idx: params.length });
|
||||||
|
params.push(arg.type);
|
||||||
|
break;
|
||||||
|
case "zst":
|
||||||
|
paramLocations.push({ kind: "zst" });
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
let returns: wasm.ValType[];
|
let returns: wasm.ValType[];
|
||||||
switch (abi.ret.kind) {
|
switch (abi.ret.kind) {
|
||||||
case "scalar":
|
case "scalar":
|
||||||
|
|
@ -163,7 +353,31 @@ function wasmTypeForAbi(abi: Abi): wasm.FuncType {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { params, returns };
|
return { type: { params, returns }, paramLocations };
|
||||||
|
}
|
||||||
|
|
||||||
|
function wasmTypeForBody(ty: Ty): wasm.ValType | undefined {
|
||||||
|
switch (ty.kind) {
|
||||||
|
case "string":
|
||||||
|
todo("string types");
|
||||||
|
case "int":
|
||||||
|
return "i64";
|
||||||
|
case "bool":
|
||||||
|
return "i32";
|
||||||
|
case "list":
|
||||||
|
todo("list types");
|
||||||
|
case "tuple":
|
||||||
|
if (ty.elems.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
} else if (ty.elems.length === 1) {
|
||||||
|
return wasmTypeForBody(ty.elems[0]);
|
||||||
|
}
|
||||||
|
todo("complex tuples");
|
||||||
|
case "fn":
|
||||||
|
todo("fn types");
|
||||||
|
case "var":
|
||||||
|
varUnreachable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function todo(msg: string): never {
|
function todo(msg: string): never {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Ast,
|
Ast,
|
||||||
|
BUILTINS,
|
||||||
|
BuiltinName,
|
||||||
DEFAULT_FOLDER,
|
DEFAULT_FOLDER,
|
||||||
Folder,
|
Folder,
|
||||||
Identifier,
|
Identifier,
|
||||||
|
|
@ -10,14 +12,7 @@ import {
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
import { CompilerError } from "./error";
|
import { CompilerError } from "./error";
|
||||||
|
|
||||||
const BUILTINS = new Set<string>([
|
const BUILTIN_SET = new Set<string>(BUILTINS);
|
||||||
"print",
|
|
||||||
"String",
|
|
||||||
"Int",
|
|
||||||
"Bool",
|
|
||||||
"true",
|
|
||||||
"false",
|
|
||||||
]);
|
|
||||||
|
|
||||||
export function resolve(ast: Ast): Ast {
|
export function resolve(ast: Ast): Ast {
|
||||||
const items = new Map<string, number>();
|
const items = new Map<string, number>();
|
||||||
|
|
@ -40,7 +35,7 @@ export function resolve(ast: Ast): Ast {
|
||||||
const popped = scopes.pop();
|
const popped = scopes.pop();
|
||||||
if (popped !== expected) {
|
if (popped !== expected) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Scopes corrupted, wanted to pop ${name} but popped ${popped}`
|
`Scopes corrupted, wanted to pop ${expected} but popped ${popped}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -66,8 +61,8 @@ export function resolve(ast: Ast): Ast {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BUILTINS.has(ident.name)) {
|
if (BUILTIN_SET.has(ident.name)) {
|
||||||
return { kind: "builtin", name: ident.name };
|
return { kind: "builtin", name: ident.name as BuiltinName };
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CompilerError(`cannot find ${ident.name}`, ident.span);
|
throw new CompilerError(`cannot find ${ident.name}`, ident.span);
|
||||||
|
|
@ -88,7 +83,9 @@ export function resolve(ast: Ast): Ast {
|
||||||
|
|
||||||
item.node.params.forEach(({ name }) => scopes.push(name));
|
item.node.params.forEach(({ name }) => scopes.push(name));
|
||||||
const body = superFoldExpr(item.node.body, this);
|
const body = superFoldExpr(item.node.body, this);
|
||||||
item.node.params.forEach(({ name }) => popScope(name));
|
const revParams = item.node.params.slice();
|
||||||
|
revParams.reverse();
|
||||||
|
revParams.forEach(({ name }) => popScope(name));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: "function",
|
kind: "function",
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ export function typeck(ast: Ast): Ast {
|
||||||
const returnType = item.node.returnType && {
|
const returnType = item.node.returnType && {
|
||||||
...item.node.returnType,
|
...item.node.returnType,
|
||||||
ty: fnTy.returnTy,
|
ty: fnTy.returnTy,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
kind: "function",
|
kind: "function",
|
||||||
node: {
|
node: {
|
||||||
|
|
@ -506,11 +506,11 @@ function checkBinary(expr: Expr & ExprBinary): Expr {
|
||||||
let lhsTy = expr.lhs.ty!;
|
let lhsTy = expr.lhs.ty!;
|
||||||
let rhsTy = expr.rhs.ty!;
|
let rhsTy = expr.rhs.ty!;
|
||||||
|
|
||||||
if (lhsTy.kind === "int" && rhsTy.kind === "int") {
|
|
||||||
return { ...expr, ty: TY_INT };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (COMPARISON_KINDS.includes(expr.binaryKind)) {
|
if (COMPARISON_KINDS.includes(expr.binaryKind)) {
|
||||||
|
if (lhsTy.kind === "int" && rhsTy.kind === "int") {
|
||||||
|
return { ...expr, ty: TY_BOOL };
|
||||||
|
}
|
||||||
|
|
||||||
if (lhsTy.kind === "string" && rhsTy.kind === "string") {
|
if (lhsTy.kind === "string" && rhsTy.kind === "string") {
|
||||||
return { ...expr, ty: TY_BOOL };
|
return { ...expr, ty: TY_BOOL };
|
||||||
}
|
}
|
||||||
|
|
@ -522,6 +522,10 @@ function checkBinary(expr: Expr & ExprBinary): Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lhsTy.kind === "int" && rhsTy.kind === "int") {
|
||||||
|
return { ...expr, ty: TY_INT };
|
||||||
|
}
|
||||||
|
|
||||||
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") {
|
||||||
return { ...expr, ty: TY_BOOL };
|
return { ...expr, ty: TY_BOOL };
|
||||||
|
|
@ -547,7 +551,7 @@ function checkUnary(expr: Expr & ExprUnary): Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expr.unaryKind === "-" && rhsTy.kind == "int") {
|
if (expr.unaryKind === "-" && rhsTy.kind == "int") {
|
||||||
return { ...expr, ty: rhsTy };
|
// Negating an unsigned integer is a bad idea.
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ class Formatter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function writeModuleWatToString(module: Module, color = true): string {
|
export function writeModuleWatToString(module: Module, color = false): string {
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
const writer = (s: string) => parts.push(s);
|
const writer = (s: string) => parts.push(s);
|
||||||
printModule(module, new Formatter(writer, color));
|
printModule(module, new Formatter(writer, color));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue