Skip to content

Commit 70f6ff4

Browse files
author
jonas.ong
committed
add bool operator support, fix WasmIntTest type
1 parent 13d090d commit 70f6ff4

File tree

4 files changed

+141
-43
lines changed

4 files changed

+141
-43
lines changed

src/wasm-compiler/builderGenerator.ts

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
applyFuncFactory,
77
ARITHMETIC_OP_FX,
88
ARITHMETIC_OP_TAG,
9+
BOOL_NOT_FX,
10+
BOOLISE_FX,
911
COMPARISON_OP_FX,
1012
COMPARISON_OP_TAG,
1113
CURR_ENV,
@@ -30,14 +32,15 @@ import {
3032
SET_PAIR_HEAD_FX,
3133
SET_PAIR_TAIL_FX,
3234
SET_PARAM_FX,
35+
TYPE_TAG,
3336
} from "./constants";
3437
import { f64, global, i32, i64, local, mut, wasm } from "./wasm-util/builder";
35-
import { WasmCall, WasmInstruction, WasmRaw } from "./wasm-util/types";
38+
import { WasmInstruction, WasmNumeric, WasmRaw } from "./wasm-util/types";
3639

3740
const builtInFunctions: {
3841
name: string;
3942
arity: number;
40-
body: WasmInstruction;
43+
body: WasmInstruction | WasmInstruction[];
4144
isVoid: boolean;
4245
}[] = [
4346
{
@@ -97,22 +100,29 @@ const builtInFunctions: {
97100
),
98101
isVoid: true,
99102
},
103+
{
104+
name: "bool",
105+
arity: 1,
106+
body: [
107+
i32.const(TYPE_TAG.BOOL),
108+
wasm
109+
.call(BOOLISE_FX)
110+
.args(wasm.call(GET_LEX_ADDR_FX).args(i32.const(0), i32.const(0))),
111+
],
112+
isVoid: false,
113+
},
100114
];
101115

102116
type Binding = { name: string; tag: "local" | "nonlocal" };
103117

104-
// all expressions compile to a call to a function like makeX, get/setLexAddress, arithOp, etc.
105-
// so expressions return WasmCalls. (every expression results in i32 i64)
106-
// WasmRaw is for function calls
107-
108118
interface BuilderVisitor<S, E> extends StmtNS.Visitor<S>, ExprNS.Visitor<E> {
109119
visit(stmt: StmtNS.Stmt): S;
110120
visit(stmt: ExprNS.Expr): E;
111121
visit(stmt: StmtNS.Stmt | ExprNS.Expr): S | E;
112122
}
113123

114124
export class BuilderGenerator
115-
implements BuilderVisitor<WasmInstruction, WasmCall | WasmRaw>
125+
implements BuilderVisitor<WasmInstruction, WasmNumeric>
116126
{
117127
private strings: [string, number][] = [];
118128
private heapPointer = 0;
@@ -186,8 +196,8 @@ export class BuilderGenerator
186196
}
187197

188198
visit(stmt: StmtNS.Stmt): WasmInstruction;
189-
visit(stmt: ExprNS.Expr): WasmCall | WasmRaw;
190-
visit(stmt: StmtNS.Stmt | ExprNS.Expr): WasmInstruction | WasmCall | WasmRaw {
199+
visit(stmt: ExprNS.Expr): WasmNumeric;
200+
visit(stmt: StmtNS.Stmt | ExprNS.Expr): WasmInstruction | WasmNumeric {
191201
return stmt.accept(this);
192202
}
193203

@@ -203,7 +213,7 @@ export class BuilderGenerator
203213
this.environment[0].push({ name, tag: "local" });
204214
const tag = this.userFunctions.length;
205215
const newBody = [
206-
body,
216+
...(Array.isArray(body) ? body : [body]),
207217
wasm.return(
208218
...(isVoid ? [wasm.call(MAKE_NONE_FX)] : []),
209219
global.set(CURR_ENV, local.get("$return_env"))
@@ -286,11 +296,11 @@ export class BuilderGenerator
286296
return wasm.drop(wasm.drop(expr));
287297
}
288298

289-
visitGroupingExpr(expr: ExprNS.Grouping): WasmCall | WasmRaw {
299+
visitGroupingExpr(expr: ExprNS.Grouping): WasmNumeric {
290300
return this.visit(expr.expression);
291301
}
292302

293-
visitBinaryExpr(expr: ExprNS.Binary): WasmCall {
303+
visitBinaryExpr(expr: ExprNS.Binary): WasmNumeric {
294304
const left = this.visit(expr.left);
295305
const right = this.visit(expr.right);
296306

@@ -305,7 +315,7 @@ export class BuilderGenerator
305315
return wasm.call(ARITHMETIC_OP_FX).args(left, right, i32.const(opTag));
306316
}
307317

308-
visitCompareExpr(expr: ExprNS.Compare): WasmCall {
318+
visitCompareExpr(expr: ExprNS.Compare): WasmNumeric {
309319
const left = this.visit(expr.left);
310320
const right = this.visit(expr.right);
311321

@@ -322,17 +332,35 @@ export class BuilderGenerator
322332
return wasm.call(COMPARISON_OP_FX).args(left, right, i32.const(opTag));
323333
}
324334

325-
visitUnaryExpr(expr: ExprNS.Unary): WasmCall {
335+
visitUnaryExpr(expr: ExprNS.Unary): WasmNumeric {
326336
const right = this.visit(expr.right);
327337

328-
if (expr.operator.type !== TokenType.MINUS) {
329-
throw new Error(`Unsupported unary operator: ${expr.operator.type}`);
330-
}
338+
const type = expr.operator.type;
339+
if (type === TokenType.MINUS) return wasm.call(NEG_FX).args(right);
340+
else if (type === TokenType.NOT) return wasm.call(BOOL_NOT_FX).args(right);
341+
else throw new Error(`Unsupported unary operator: ${type}`);
342+
}
343+
344+
visitBoolOpExpr(expr: ExprNS.BoolOp): WasmNumeric {
345+
const left = this.visit(expr.left);
346+
const right = this.visit(expr.right);
331347

332-
return wasm.call(NEG_FX).args(right);
348+
const type = expr.operator.type;
349+
350+
const boolised = i64.eqz(wasm.call(BOOLISE_FX).args(left));
351+
352+
if (type === TokenType.AND) {
353+
return wasm.raw`(if (result i32 i64) ${boolised} (then ${left}) (else ${right}))`;
354+
} else if (type === TokenType.OR) {
355+
return wasm.raw`(if (result i32 i64) ${boolised} (then ${right}) (else ${left}))`;
356+
} else throw new Error(`Unsupported boolean binary operator: ${type}`);
357+
}
358+
359+
visitNoneExpr(expr: ExprNS.None): WasmNumeric {
360+
return wasm.call(MAKE_NONE_FX);
333361
}
334362

335-
visitBigIntLiteralExpr(expr: ExprNS.BigIntLiteral): WasmCall {
363+
visitBigIntLiteralExpr(expr: ExprNS.BigIntLiteral): WasmNumeric {
336364
const value = BigInt(expr.value);
337365
const min = BigInt("-9223372036854775808"); // -(2^63)
338366
const max = BigInt("9223372036854775807"); // (2^63) - 1
@@ -343,7 +371,7 @@ export class BuilderGenerator
343371
return wasm.call(MAKE_INT_FX).args(i64.const(value));
344372
}
345373

346-
visitLiteralExpr(expr: ExprNS.Literal): WasmCall {
374+
visitLiteralExpr(expr: ExprNS.Literal): WasmNumeric {
347375
if (typeof expr.value === "number")
348376
return wasm.call(MAKE_FLOAT_FX).args(f64.const(expr.value));
349377
else if (typeof expr.value === "boolean")
@@ -363,7 +391,7 @@ export class BuilderGenerator
363391
}
364392
}
365393

366-
visitComplexExpr(expr: ExprNS.Complex): WasmCall {
394+
visitComplexExpr(expr: ExprNS.Complex): WasmNumeric {
367395
return wasm
368396
.call(MAKE_COMPLEX_FX)
369397
.args(f64.const(expr.value.real), f64.const(expr.value.imag));
@@ -378,7 +406,7 @@ export class BuilderGenerator
378406
.args(i32.const(depth), i32.const(index), expression);
379407
}
380408

381-
visitVariableExpr(expr: ExprNS.Variable): WasmCall {
409+
visitVariableExpr(expr: ExprNS.Variable): WasmNumeric {
382410
const [depth, index] = this.getLexAddress(expr.name.lexeme);
383411
return wasm.call(GET_LEX_ADDR_FX).args(i32.const(depth), i32.const(index));
384412
}
@@ -437,8 +465,8 @@ export class BuilderGenerator
437465
// the SET_PARAM function returns the env address after setting the parameter
438466
// so we can chain the calls together
439467
return wasm.raw`
440-
(global.get ${CURR_ENV})
441-
(call ${PRE_APPLY_FX.name} ${callee} (i32.const ${args.length}))
468+
${global.get(CURR_ENV)}
469+
${wasm.call(PRE_APPLY_FX).args(callee, i32.const(args.length))}
442470
443471
${args.map(
444472
(arg, i) =>
@@ -482,21 +510,14 @@ ${args.map(
482510
return wasm.nop();
483511
}
484512

485-
visitNoneExpr(expr: ExprNS.None): WasmCall {
486-
return wasm.call(MAKE_NONE_FX);
487-
}
488-
489513
// UNIMPLEMENTED PYTHON CONSTRUCTS
490-
visitBoolOpExpr(expr: ExprNS.BoolOp): WasmCall {
491-
throw new Error("Method not implemented.");
492-
}
493-
visitTernaryExpr(expr: ExprNS.Ternary): WasmCall {
514+
visitTernaryExpr(expr: ExprNS.Ternary): WasmNumeric {
494515
throw new Error("Method not implemented.");
495516
}
496-
visitLambdaExpr(expr: ExprNS.Lambda): WasmCall {
517+
visitLambdaExpr(expr: ExprNS.Lambda): WasmNumeric {
497518
throw new Error("Method not implemented.");
498519
}
499-
visitMultiLambdaExpr(expr: ExprNS.MultiLambda): WasmCall {
520+
visitMultiLambdaExpr(expr: ExprNS.MultiLambda): WasmNumeric {
500521
throw new Error("Method not implemented.");
501522
}
502523
visitIndentCreation(stmt: StmtNS.Indent): WasmInstruction {

src/wasm-compiler/constants.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { f64, global, i32, i64, local, memory, wasm } from "./wasm-util/builder"
22
import { WasmInstruction } from "./wasm-util/types";
33

44
// tags
5-
const TYPE_TAG = {
5+
export const TYPE_TAG = {
66
INT: 0,
77
FLOAT: 1,
88
COMPLEX: 2,
@@ -25,6 +25,8 @@ export const ERROR_MAP = {
2525
UNBOUND: [7, "Accessing an unbound value."],
2626
HEAD_NOT_PAIR: [8, "Accessing the head of a non-pair value."],
2727
TAIL_NOT_PAIR: [9, "Accessing the tail of a non-pair value."],
28+
BOOL_UNKNOWN_TYPE: [10, "Trying to convert an unknnown runtime type to a bool."],
29+
BOOL_UNKNOWN_OP: [11, "Unknown boolean binary operator."],
2830
} as const;
2931

3032
export const HEAP_PTR = "$_heap_pointer";
@@ -627,6 +629,74 @@ export const COMPARISON_OP_FX = wasm
627629
wasm.unreachable()
628630
);
629631

632+
// bool related functions
633+
634+
export const BOOLISE_FX = wasm
635+
.func("$_boolise")
636+
.params({ $tag: i32, $val: i64 })
637+
.results(i64)
638+
.body(
639+
// None => False
640+
wasm
641+
.if(i32.eq(local.get("$tag"), i32.const(TYPE_TAG.NONE)))
642+
.then(wasm.return(wasm.call(MAKE_BOOL_FX).args(i32.const(0)))),
643+
644+
// bool or int => return bool with value (False if 0)
645+
wasm
646+
.if(
647+
i32.or(i32.eq(local.get("$tag"), i32.const(TYPE_TAG.INT)), i32.eq(local.get("$tag"), i32.const(TYPE_TAG.BOOL)))
648+
)
649+
.then(wasm.return(wasm.call(MAKE_BOOL_FX).args(i32.wrap_i64(local.get("$val"))))),
650+
651+
// float/complex => False if equivalent of 0
652+
wasm
653+
.if(i32.eq(local.get("$tag"), i32.const(TYPE_TAG.FLOAT)))
654+
.then(wasm.return(wasm.call(MAKE_BOOL_FX).args(f64.ne(f64.reinterpret_i64(local.get("$val")), f64.const(0))))),
655+
wasm
656+
.if(i32.eq(local.get("$tag"), i32.const(TYPE_TAG.COMPLEX)))
657+
.then(
658+
wasm.return(
659+
wasm
660+
.call(MAKE_BOOL_FX)
661+
.args(
662+
i32.or(
663+
f64.ne(f64.load(i32.add(i32.wrap_i64(local.get("$val")), i32.const(8))), f64.const(0)),
664+
f64.ne(f64.load(i32.wrap_i64(local.get("$val"))), f64.const(0))
665+
)
666+
)
667+
)
668+
),
669+
670+
// string => False if length is 0
671+
wasm
672+
.if(i32.eq(local.get("$tag"), i32.const(TYPE_TAG.STRING)))
673+
.then(wasm.return(wasm.call(MAKE_BOOL_FX).args(i32.wrap_i64(local.get("$val"))))),
674+
675+
// closure/pair => True
676+
wasm
677+
.if(
678+
i32.or(
679+
i32.eq(local.get("$tag"), i32.const(TYPE_TAG.CLOSURE)),
680+
i32.eq(local.get("$tag"), i32.const(TYPE_TAG.PAIR))
681+
)
682+
)
683+
.then(wasm.return(wasm.call(MAKE_BOOL_FX).args(i32.const(1)))),
684+
685+
wasm.call("$_log_error").args(i32.const(ERROR_MAP.BOOL_UNKNOWN_TYPE[0])),
686+
wasm.unreachable()
687+
);
688+
689+
export const BOOL_NOT_FX = wasm
690+
.func("$_bool_not")
691+
.params({ $tag: i32, $val: i64 })
692+
.results(i32, i64)
693+
.body(
694+
i32.const(TYPE_TAG.BOOL),
695+
i64.extend_i32_u(i64.eqz(wasm.call(BOOLISE_FX).args(local.get("$tag"), local.get("$val"))))
696+
);
697+
698+
export const BOOL_BINARY_OP_TAG = { AND: 0, OR: 1 };
699+
630700
// *3*4 because each variable has a tag and payload = 3 words = 12 bytes; +4 because parentEnv is stored at start of env
631701
// we initialise only local variables to UNBOUND, NOT parameters.
632702
// this is because have already set parameters in the new environment before calling this function.
@@ -781,6 +851,8 @@ export const nativeFunctions = [
781851
ARITHMETIC_OP_FX,
782852
STRING_COMPARE_FX,
783853
COMPARISON_OP_FX,
854+
BOOLISE_FX,
855+
BOOL_NOT_FX,
784856
ALLOC_ENV_FX,
785857
PRE_APPLY_FX,
786858
GET_LEX_ADDR_FX,

src/wasm-compiler/wasm-util/builder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ const binaryOp = <
8787
[K in keyof Op]: [
8888
Op[K],
8989
(
90-
...args: Extract<WasmNumericFor<T>, { op: `${T}.${Op[K]}` }> extends {
90+
...args: Extract<WasmInstruction, { op: `${T}.${Op[K]}` }> extends {
9191
left: infer L;
9292
right: infer R;
9393
}
9494
? [left: L, right: R]
9595
: never
96-
) => Extract<WasmNumericFor<T>, { op: `${T}.${Op[K]}` }>
96+
) => Extract<WasmInstruction, { op: `${T}.${Op[K]}` }>
9797
];
9898
}
9999
);

src/wasm-compiler/wasm-util/types.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,14 @@ export type WasmNumericFor<T extends WasmNumericType> =
199199
| WasmConst<T>
200200
| (T extends WasmFloatNumericType ? WasmUnaryOp<T> : never)
201201
| WasmBinaryOp<T>
202-
| (T extends WasmIntNumericType ? WasmIntTestOp<T> : never)
203-
| (T extends "i32" ? WasmComparisonOp<WasmNumericType> : never)
202+
| (T extends "i32"
203+
? WasmIntTestOp<T> | WasmComparisonOp<WasmNumericType>
204+
: never)
204205
| WasmConversionOp<T>
206+
| WasmRaw
205207

206208
// below are not numeric instructions, but the results of these are numeric
207209
| WasmLoad
208-
| WasmStore
209210
| WasmLocalGet
210211
| WasmGlobalGet
211212
| WasmLocalTee
@@ -216,8 +217,7 @@ export type WasmNumeric =
216217
| WasmNumericFor<"i32">
217218
| WasmNumericFor<"i64">
218219
| WasmNumericFor<"f32">
219-
| WasmNumericFor<"f64">
220-
| WasmRaw;
220+
| WasmNumericFor<"f64">;
221221

222222
// ------------------------ WASM Variable Instructions ----------------------------
223223

@@ -265,7 +265,12 @@ export type WasmMemoryFill = {
265265
numOfBytes: WasmNumericFor<"i32">;
266266
};
267267

268-
type WasmMemory = WasmMemoryCopy | WasmMemoryFill | WasmRaw;
268+
type WasmMemory =
269+
| WasmMemoryCopy
270+
| WasmMemoryFill
271+
| WasmLoad
272+
| WasmStore
273+
| WasmRaw;
269274

270275
// ------------------------ WASM Control Instructions ----------------------------
271276

0 commit comments

Comments
 (0)