-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathchapter03.js
More file actions
122 lines (108 loc) · 2.76 KB
/
chapter03.js
File metadata and controls
122 lines (108 loc) · 2.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import assert from 'node:assert';
import * as ohm from 'ohm-js';
import {i32, instr, makeTestFn, testExtractedExamples} from './chapter02.js';
const test = makeTestFn(import.meta.url);
const grammarDef = `
Wafer {
Main = Expr
Expr = PrimaryExpr (op PrimaryExpr)*
PrimaryExpr = "(" Expr ")" -- paren
| number
op = "+" | "-" | "*" | "/"
number = digit+
// Examples:
//+ "42", "1", "66 + 99", "1 + 2 - 3", "1 + (2 * 3)", "(((1) / 2))"
//- "abc"
}
`;
test('Extracted examples', () => testExtractedExamples(grammarDef));
const wafer = ohm.grammar(grammarDef);
const semantics = wafer.createSemantics();
semantics.addOperation('jsValue', {
Main(num) {
// To evaluate main, we need to evaluate the number.
return num.jsValue();
},
number(digits) {
// Evaluate the number with JavaScript's built in `parseInt` function.
return parseInt(this.sourceString, 10);
},
});
semantics.addOperation('toWasm', {
Main(expr) {
return [expr.toWasm(), instr.end];
},
Expr(num, iterOps, iterOperands) {
const result = [num.toWasm()];
for (let i = 0; i < iterOps.numChildren; i++) {
const op = iterOps.child(i);
const operand = iterOperands.child(i);
result.push(operand.toWasm(), op.toWasm());
}
return result;
},
PrimaryExpr_paren(_lparen, expr, _rparen) {
return expr.toWasm();
},
op(char) {
const op = char.sourceString;
const instructionByOp = {
'+': instr.i32.add,
'-': instr.i32.sub,
'*': instr.i32.mul,
'/': instr.i32.div_s,
};
if (!Object.hasOwn(instructionByOp, op)) {
throw new Error(`Unhandled operator '${op}'`);
}
return instructionByOp[op];
},
number(digits) {
const num = this.jsValue();
return [instr.i32.const, ...i32(num)];
},
});
instr.i32.add = 0x6a;
instr.i32.sub = 0x6b;
instr.i32.mul = 0x6c;
instr.i32.div_s = 0x6d;
function toWasmFlat(input) {
const matchResult = wafer.match(input);
const bytes = semantics(matchResult).toWasm();
return bytes.flat(Infinity);
}
test('toWasm bytecodes', () => {
assert.deepEqual(toWasmFlat('1'), [instr.i32.const, 1, instr.end]);
assert.deepEqual(
toWasmFlat('1 + 2'),
[
[instr.i32.const, 1],
[instr.i32.const, 2],
instr.i32.add,
instr.end,
].flat(),
);
assert.deepEqual(
toWasmFlat('7 - 3 + 11'),
[
[instr.i32.const, 7],
[instr.i32.const, 3],
instr.i32.sub,
[instr.i32.const, 11],
instr.i32.add,
instr.end,
].flat(),
);
assert.deepEqual(
toWasmFlat('6 / (2 * 1)'),
[
[instr.i32.const, 6],
[instr.i32.const, 2],
[instr.i32.const, 1],
instr.i32.mul,
instr.i32.div_s,
instr.end,
].flat(),
);
});
export * from './chapter02.js';