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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
import { ASTNode, BinaryExpressionNode, FunctionDeclarationNode, NumberNode, VariableDeclarationNode, WhileNode } from "./ast.ts";
// import { PC } from "../pc.ts";
// const pc = new PC();
type Opcode =
'mov' |
'swp' |
'ld' |
'str' |
'add' |
'sub' |
'mul' |
'div' |
'mod' |
'shl' |
'shr' |
'cmp' |
'cmr' |
'and' |
'or' |
'xor' |
'not' |
'push' |
'pop' |
'halt' |
'sys' |
'jmp' |
'jnz' |
'jz' |
'jmr' |
'ret' |
'end';
type Register = 97 | 98 | 99 | 100
interface Instruction {
opcode: Opcode,
args: (Register | number)[]
}
const types: Record<string, number> = {
'int' : 1,
'bool': 1,
'char': 1
}
const A: Register = 97;
const B: Register = 98;
// deno-lint-ignore no-unused-vars
const C: Register = 99;
// deno-lint-ignore no-unused-vars
const D: Register = 100;
export default class Compiler {
vars: Record<string, [number, number]> = {};
functions: Record<string, number> = {};
AST: ASTNode[];
lastAddr: number = 0;
instructions: Instruction[] = [];
constructor (ast: ASTNode[]) {
this.AST = ast
}
compile (node: ASTNode) {
if ((node as VariableDeclarationNode).type == 'VariableDeclaration') {
const varDeclNode = node as VariableDeclarationNode;
if (!types[varDeclNode.vtype]) throw 'unknown type';
const addr = this.vars[varDeclNode.identifier] =
[this.lastAddr, types[varDeclNode.vtype] * varDeclNode.length];
this.lastAddr += types[varDeclNode.vtype] * varDeclNode.length;
if (varDeclNode.value.type != 'Number') throw 'a';
this.instructions.push({
opcode: 'mov',
args: [A, (varDeclNode.value as NumberNode).value]
})
this.instructions.push({
opcode: 'mov',
args: [B, addr[0]]
})
this.instructions.push({
opcode: 'str',
args: [B, A]
})
} else if ((node as FunctionDeclarationNode).type == 'FunctionDeclaration') {
const fnDeclNode = node as FunctionDeclarationNode;
this.functions[fnDeclNode.name] = this.instructions
.map(k => 1 + k.args.length)
.reduce((prev, curr) => {
return prev + curr
}, 0);
for (const node of fnDeclNode.body) {
this.compile(node)
}
} else if ((node as BinaryExpressionNode).type == 'BinaryExpression') {
const binExpNode = node as BinaryExpressionNode;
this.instructions.push({
opcode: 'pop',
args: [A]
})
this.instructions.push({
opcode: 'pop',
args: [B]
})
switch (binExpNode.operator) {
case '+':
this.instructions.push({
opcode: 'add',
args: [A, A, B]
})
break;
default:
throw 'oh no'
}
this.instructions.push({
opcode: 'push',
args: [A]
})
} else if ((node as WhileNode).type == 'While') {
const whileNode = node as WhileNode;
const start = this.instructions
.map(k => 1 + k.args.length)
.reduce((prev, curr) => {
return prev + curr
}, 0);
for (const node of whileNode.branch) {
this.compile(node)
}
this.instructions.push({
opcode: 'pop',
args: [A]
})
this.instructions.push({
opcode: 'mov',
args: [B, 0]
})
this.instructions.push({
opcode: 'cmp',
args: [A, B]
})
this.instructions.push({
opcode: 'mov',
args: [A, start]
})
this.instructions.push({
opcode: 'jnz',
args: [A]
})
} else {
console.error(`!!! UNIMPLEMENTED NODE `, node.type, node)
}
}
}
|