darijacode 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +130 -0
- package/README.md +78 -0
- package/dist/cli.js +77 -0
- package/dist/compiler/ast.js +2 -0
- package/dist/compiler/checker/expressions.js +248 -0
- package/dist/compiler/checker/index.js +45 -0
- package/dist/compiler/checker/scope.js +57 -0
- package/dist/compiler/checker/statements.js +255 -0
- package/dist/compiler/checker/types.js +47 -0
- package/dist/compiler/codegen/codegen.js +122 -0
- package/dist/compiler/codegen/ctype.js +23 -0
- package/dist/compiler/codegen/expr.js +63 -0
- package/dist/compiler/codegen/infertype.js +70 -0
- package/dist/compiler/codegen/runtime.js +24 -0
- package/dist/compiler/codegen/scope.js +19 -0
- package/dist/compiler/codegen/stmts.js +90 -0
- package/dist/compiler/codegen/types.js +2 -0
- package/dist/compiler/compiler.js +115 -0
- package/dist/compiler/errors.js +59 -0
- package/dist/compiler/index.js +69 -0
- package/dist/compiler/lexer.js +303 -0
- package/dist/compiler/parser.js +513 -0
- package/dist/compiler/tokens.js +68 -0
- package/dist/utils/showType.js +10 -0
- package/package.json +58 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StatementChecker = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const showType_1 = require("../../utils/showType");
|
|
6
|
+
const types_1 = require("./types");
|
|
7
|
+
const expressions_1 = require("./expressions");
|
|
8
|
+
class StatementChecker {
|
|
9
|
+
functions;
|
|
10
|
+
globalScope;
|
|
11
|
+
typeChecker;
|
|
12
|
+
expressionChecker;
|
|
13
|
+
loopDepth = 0;
|
|
14
|
+
functionStack = [];
|
|
15
|
+
constructor(functions, globalScope) {
|
|
16
|
+
this.functions = functions;
|
|
17
|
+
this.globalScope = globalScope;
|
|
18
|
+
this.typeChecker = new types_1.TypeChecker();
|
|
19
|
+
this.expressionChecker = new expressions_1.ExpressionChecker(functions);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check a statement and validate it semantically.
|
|
23
|
+
*/
|
|
24
|
+
checkStatement(stmt, scope) {
|
|
25
|
+
switch (stmt.type) {
|
|
26
|
+
case "VariableDeclaration":
|
|
27
|
+
return this.checkVariableDeclaration(stmt, scope);
|
|
28
|
+
case "FunctionDeclaration":
|
|
29
|
+
return this.checkFunctionDeclaration(stmt);
|
|
30
|
+
case "ReturnStatement":
|
|
31
|
+
return this.checkReturnStatement(stmt, scope);
|
|
32
|
+
case "IfStatement":
|
|
33
|
+
return this.checkIfStatement(stmt, scope);
|
|
34
|
+
case "WhileStatement":
|
|
35
|
+
return this.checkWhileStatement(stmt, scope);
|
|
36
|
+
case "ForStatement":
|
|
37
|
+
return this.checkForStatement(stmt, scope);
|
|
38
|
+
case "BreakStatement":
|
|
39
|
+
return this.checkBreakStatement(stmt);
|
|
40
|
+
case "ContinueStatement":
|
|
41
|
+
return this.checkContinueStatement(stmt);
|
|
42
|
+
case "BlockStatement":
|
|
43
|
+
return this.checkBlock(stmt, scope);
|
|
44
|
+
case "PrintStatement":
|
|
45
|
+
return this.checkPrintStatement(stmt, scope);
|
|
46
|
+
case "ExpressionStatement":
|
|
47
|
+
return this.checkExpressionStatement(stmt, scope);
|
|
48
|
+
default:
|
|
49
|
+
throw new errors_1.DarijaError({
|
|
50
|
+
code: "DCE1",
|
|
51
|
+
stage: "checker",
|
|
52
|
+
message: `'${stmt.type}' mmd3omach`,
|
|
53
|
+
location: {
|
|
54
|
+
line: stmt.pos.line,
|
|
55
|
+
column: stmt.pos.column,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
checkVariableDeclaration(decl, scope) {
|
|
61
|
+
let type = decl.typeAnnotation
|
|
62
|
+
? this.typeChecker.resolveType(decl.typeAnnotation, decl.pos.line, decl.pos.column)
|
|
63
|
+
: { base: "unknown", d: 0 };
|
|
64
|
+
if (decl.init) {
|
|
65
|
+
const initType = this.expressionChecker.inferType(decl.init, scope);
|
|
66
|
+
if (decl.typeAnnotation &&
|
|
67
|
+
!this.typeChecker.compatible(decl.typeAnnotation, initType)) {
|
|
68
|
+
throw new errors_1.DarijaError({
|
|
69
|
+
code: "DCE15",
|
|
70
|
+
stage: "checker",
|
|
71
|
+
message: `twa93na ${(0, showType_1.showType)(decl.typeAnnotation)}, wlkin l9ina ${(0, showType_1.showType)(initType)}`,
|
|
72
|
+
location: {
|
|
73
|
+
line: decl.pos.line,
|
|
74
|
+
column: decl.pos.column,
|
|
75
|
+
},
|
|
76
|
+
hint: `ach ban link tstkhdm ${(0, showType_1.showType)(decl.typeAnnotation)} fbalst ${(0, showType_1.showType)(initType)}`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (decl.typeAnnotation) {
|
|
80
|
+
type = initType;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (decl.kind === "khli") {
|
|
84
|
+
throw new errors_1.DarijaError({
|
|
85
|
+
code: "DCE16",
|
|
86
|
+
stage: "checker",
|
|
87
|
+
message: `'${decl.name}' howa tabit idan khaso 9ima flwl`,
|
|
88
|
+
location: {
|
|
89
|
+
line: decl.pos.line,
|
|
90
|
+
column: decl.pos.column,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
scope.declare(decl.name, { kind: decl.kind, type }, decl.pos.line, decl.pos.column);
|
|
95
|
+
}
|
|
96
|
+
checkFunctionDeclaration(fn) {
|
|
97
|
+
if (!this.functions.has(fn.name)) {
|
|
98
|
+
this.registerFunction(fn);
|
|
99
|
+
}
|
|
100
|
+
const info = this.functions.get(fn.name);
|
|
101
|
+
const fnScope = this.globalScope.child();
|
|
102
|
+
// Declare parameters in function scope
|
|
103
|
+
for (const param of fn.params) {
|
|
104
|
+
if (param.defaultValue && param.typeAnnotation) {
|
|
105
|
+
const defaultType = this.expressionChecker.inferType(param.defaultValue, fnScope);
|
|
106
|
+
if (!this.typeChecker.compatible(param.typeAnnotation, defaultType)) {
|
|
107
|
+
throw new errors_1.DarijaError({
|
|
108
|
+
code: "DCE15",
|
|
109
|
+
stage: "checker",
|
|
110
|
+
message: `twa93na ${(0, showType_1.showType)(param.typeAnnotation)}, wlkin l9ina ${(0, showType_1.showType)(defaultType)}`,
|
|
111
|
+
location: {
|
|
112
|
+
line: param.pos.line,
|
|
113
|
+
column: param.pos.column,
|
|
114
|
+
},
|
|
115
|
+
hint: `ach ban link tstkhdm ${(0, showType_1.showType)(param.typeAnnotation)} fbalst ${(0, showType_1.showType)(defaultType)}`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
fnScope.declare(param.name, {
|
|
120
|
+
kind: "param",
|
|
121
|
+
type: param.typeAnnotation ?? { base: types_1.UNKNOWN, d: 0 },
|
|
122
|
+
}, param.pos.line, param.pos.column);
|
|
123
|
+
}
|
|
124
|
+
this.functionStack.push(info);
|
|
125
|
+
this.checkBlock(fn.body, fnScope);
|
|
126
|
+
this.functionStack.pop();
|
|
127
|
+
}
|
|
128
|
+
registerFunction(fn) {
|
|
129
|
+
if (this.functions.has(fn.name)) {
|
|
130
|
+
throw new errors_1.DarijaError({
|
|
131
|
+
code: "DCE13",
|
|
132
|
+
stage: "checker",
|
|
133
|
+
message: `dala '${fn.name}' dija m3rfa`,
|
|
134
|
+
location: {
|
|
135
|
+
line: fn.pos.line,
|
|
136
|
+
column: fn.pos.column,
|
|
137
|
+
},
|
|
138
|
+
hint: `stkhdm dalla dyal ${fn.name}() awla dir dalla jdida`,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
this.functions.register(fn.name, {
|
|
142
|
+
params: fn.params,
|
|
143
|
+
returnType: fn.returnType ?? { base: "khawi", d: 0 },
|
|
144
|
+
}, fn.pos.line, fn.pos.column);
|
|
145
|
+
}
|
|
146
|
+
checkReturnStatement(stmt, scope) {
|
|
147
|
+
const fn = this.functionStack[this.functionStack.length - 1];
|
|
148
|
+
if (!fn) {
|
|
149
|
+
throw new errors_1.DarijaError({
|
|
150
|
+
code: "DCE14",
|
|
151
|
+
stage: "checker",
|
|
152
|
+
message: "'raj3' mst5dma bra dyal ddalla",
|
|
153
|
+
location: {
|
|
154
|
+
line: stmt.pos.line,
|
|
155
|
+
column: stmt.pos.column,
|
|
156
|
+
},
|
|
157
|
+
hint: "dirha lldakhl dyal dalla",
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (stmt.argument) {
|
|
161
|
+
const argType = this.expressionChecker.inferType(stmt.argument, scope);
|
|
162
|
+
const tp = { base: fn.returnType.base, d: fn.returnType.d };
|
|
163
|
+
if (fn.returnType && !this.typeChecker.compatible(tp, argType)) {
|
|
164
|
+
throw new errors_1.DarijaError({
|
|
165
|
+
code: "DCE15",
|
|
166
|
+
stage: "checker",
|
|
167
|
+
message: `mknach kanstaw l9ima dyal '${(0, showType_1.showType)(argType)}', kona mtw93in '${(0, showType_1.showType)(fn.returnType)}'`,
|
|
168
|
+
location: {
|
|
169
|
+
line: stmt.pos.line,
|
|
170
|
+
column: stmt.pos.column,
|
|
171
|
+
},
|
|
172
|
+
hint: `ach ban lik traj3 ${(0, showType_1.showType)(fn.returnType)} fbalst ${(0, showType_1.showType)(argType)} ?`,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
checkIfStatement(stmt, scope) {
|
|
178
|
+
this.expressionChecker.inferType(stmt.condition, scope);
|
|
179
|
+
this.checkBlock(stmt.consequent, scope);
|
|
180
|
+
for (const branch of stmt.elseIfs) {
|
|
181
|
+
this.expressionChecker.inferType(branch.condition, scope);
|
|
182
|
+
this.checkBlock(branch.consequent, scope);
|
|
183
|
+
}
|
|
184
|
+
if (stmt.alternate) {
|
|
185
|
+
this.checkBlock(stmt.alternate, scope);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
checkWhileStatement(stmt, scope) {
|
|
189
|
+
this.expressionChecker.inferType(stmt.condition, scope);
|
|
190
|
+
this.loopDepth++;
|
|
191
|
+
this.checkBlock(stmt.body, scope);
|
|
192
|
+
this.loopDepth--;
|
|
193
|
+
}
|
|
194
|
+
checkForStatement(stmt, scope) {
|
|
195
|
+
const forScope = scope.child();
|
|
196
|
+
if (stmt.init) {
|
|
197
|
+
if (stmt.init.type === "VariableDeclaration") {
|
|
198
|
+
this.checkVariableDeclaration(stmt.init, forScope);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
this.expressionChecker.inferType(stmt.init.expression, forScope);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (stmt.condition) {
|
|
205
|
+
this.expressionChecker.inferType(stmt.condition, forScope);
|
|
206
|
+
}
|
|
207
|
+
if (stmt.update) {
|
|
208
|
+
this.expressionChecker.inferType(stmt.update, forScope);
|
|
209
|
+
}
|
|
210
|
+
this.loopDepth++;
|
|
211
|
+
this.checkBlock(stmt.body, forScope);
|
|
212
|
+
this.loopDepth--;
|
|
213
|
+
}
|
|
214
|
+
checkBreakStatement(stmt) {
|
|
215
|
+
if (this.loopDepth === 0) {
|
|
216
|
+
throw new errors_1.DarijaError({
|
|
217
|
+
code: "DCE14",
|
|
218
|
+
stage: "checker",
|
|
219
|
+
message: "'qta3' mst5dma bra dyal dwara",
|
|
220
|
+
location: {
|
|
221
|
+
line: stmt.pos.line,
|
|
222
|
+
column: stmt.pos.column,
|
|
223
|
+
},
|
|
224
|
+
hint: "dir '9ta3' fldakhl dyal chi dwara",
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
checkContinueStatement(stmt) {
|
|
229
|
+
if (this.loopDepth === 0) {
|
|
230
|
+
throw new errors_1.DarijaError({
|
|
231
|
+
code: "DCE14",
|
|
232
|
+
stage: "checker",
|
|
233
|
+
message: "'kml' mst5dma bra by dwara",
|
|
234
|
+
location: {
|
|
235
|
+
line: stmt.pos.line,
|
|
236
|
+
column: stmt.pos.column,
|
|
237
|
+
},
|
|
238
|
+
hint: "dir 'kml' fldakhl dyal chi dwara",
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
checkBlock(block, parentScope) {
|
|
243
|
+
const scope = parentScope.child();
|
|
244
|
+
for (const stmt of block.body) {
|
|
245
|
+
this.checkStatement(stmt, scope);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
checkPrintStatement(stmt, scope) {
|
|
249
|
+
this.expressionChecker.inferType(stmt.argument, scope);
|
|
250
|
+
}
|
|
251
|
+
checkExpressionStatement(stmt, scope) {
|
|
252
|
+
this.expressionChecker.inferType(stmt.expression, scope);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
exports.StatementChecker = StatementChecker;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypeChecker = exports.UNKNOWN = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const showType_1 = require("../../utils/showType");
|
|
6
|
+
exports.UNKNOWN = "unknown";
|
|
7
|
+
class TypeChecker {
|
|
8
|
+
resolveType(type, line, column) {
|
|
9
|
+
if (!["ra9m", "nass", "tona2i", "khawi"].includes(type.base)) {
|
|
10
|
+
throw new errors_1.DarijaError({
|
|
11
|
+
code: "DCE18",
|
|
12
|
+
stage: "checker",
|
|
13
|
+
message: `had naw3 '${(0, showType_1.showType)(type)}' mkaynch awla mmd3omch`,
|
|
14
|
+
location: { line, column },
|
|
15
|
+
hint: "stkhdm chi naw3 dija kayn bhal : 'ra9m', 'nass' etc..",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return { base: type.base, d: type.d };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Check if two types are compatible.
|
|
22
|
+
* Returns true if types match or either is UNKNOWN (dynamic).
|
|
23
|
+
*/
|
|
24
|
+
compatible(declared, actual) {
|
|
25
|
+
if (declared.base === exports.UNKNOWN || actual.base === exports.UNKNOWN)
|
|
26
|
+
return true;
|
|
27
|
+
return (0, showType_1.showType)(declared) === (0, showType_1.showType)(actual);
|
|
28
|
+
}
|
|
29
|
+
isType(type, base, d = 0) {
|
|
30
|
+
return (0, showType_1.isType)(type, base, d);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Assert that actual type matches expected base type.
|
|
34
|
+
* Throws DCE15 on mismatch (unless actual is UNKNOWN).
|
|
35
|
+
*/
|
|
36
|
+
expectType(actual, expected, pos) {
|
|
37
|
+
if (!this.isType(actual, expected) && actual.base !== exports.UNKNOWN) {
|
|
38
|
+
throw new errors_1.DarijaError({
|
|
39
|
+
code: "DCE15",
|
|
40
|
+
stage: "checker",
|
|
41
|
+
message: `twa93na ${expected}, wlkin l9ina ${(0, showType_1.showType)(actual)}`,
|
|
42
|
+
location: { line: pos.line, column: pos.column },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.TypeChecker = TypeChecker;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Codegen = void 0;
|
|
7
|
+
const scope_1 = __importDefault(require("./scope"));
|
|
8
|
+
const stmts_1 = __importDefault(require("./stmts"));
|
|
9
|
+
const expr_1 = __importDefault(require("./expr"));
|
|
10
|
+
const infertype_1 = __importDefault(require("./infertype"));
|
|
11
|
+
const ctype_1 = require("./ctype");
|
|
12
|
+
const runtime_1 = require("./runtime");
|
|
13
|
+
const RUNTIME_PRELUDE = `
|
|
14
|
+
static char* dj_concat(const char* a, const char* b) {
|
|
15
|
+
size_t len = strlen(a) + strlen(b) + 1;
|
|
16
|
+
char* out = (char*)malloc(len);
|
|
17
|
+
strcpy(out, a);
|
|
18
|
+
strcat(out, b);
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
`.trim();
|
|
22
|
+
class Codegen {
|
|
23
|
+
globalScope = new scope_1.default();
|
|
24
|
+
functions = new Map();
|
|
25
|
+
out = [];
|
|
26
|
+
indentLevel = 0;
|
|
27
|
+
generate(program) {
|
|
28
|
+
const functionDecls = program.body.filter((s) => s.type === "FunctionDeclaration");
|
|
29
|
+
const topLevel = program.body.filter((s) => s.type !== "FunctionDeclaration");
|
|
30
|
+
for (const fn of functionDecls)
|
|
31
|
+
this.registerFunction(fn);
|
|
32
|
+
const header = [
|
|
33
|
+
"#include <stdio.h>",
|
|
34
|
+
"#include <stdlib.h>",
|
|
35
|
+
"#include <string.h>",
|
|
36
|
+
"#include <stdbool.h>",
|
|
37
|
+
"#include <math.h>",
|
|
38
|
+
"",
|
|
39
|
+
RUNTIME_PRELUDE,
|
|
40
|
+
"",
|
|
41
|
+
];
|
|
42
|
+
const prototypes = functionDecls.map((fn) => this.genPrototype(fn) + ";");
|
|
43
|
+
const bodies = [];
|
|
44
|
+
for (const fn of functionDecls) {
|
|
45
|
+
bodies.push(this.genFunction(fn));
|
|
46
|
+
bodies.push("");
|
|
47
|
+
}
|
|
48
|
+
const main = ["int main(void) {"];
|
|
49
|
+
this.indentLevel = 1;
|
|
50
|
+
this.out = [];
|
|
51
|
+
for (const stmt of topLevel)
|
|
52
|
+
this.genStatement(stmt, this.globalScope);
|
|
53
|
+
main.push(...this.out);
|
|
54
|
+
main.push(" return 0;", "}");
|
|
55
|
+
this.indentLevel = 0;
|
|
56
|
+
return [...header, ...prototypes, "", ...bodies, ...main].join("\n");
|
|
57
|
+
}
|
|
58
|
+
registerFunction(fn) {
|
|
59
|
+
// uses of as
|
|
60
|
+
const paramTypes = fn.params.map((p) => p.typeAnnotation ?? { base: "nass", d: 0 });
|
|
61
|
+
this.functions.set(fn.name, {
|
|
62
|
+
paramTypes,
|
|
63
|
+
returnType: fn.returnType ?? { base: "khawi", d: 0 }
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
genPrototype(fn) {
|
|
67
|
+
const sig = this.functions.get(fn.name);
|
|
68
|
+
const params = fn.params
|
|
69
|
+
.map((p, i) => `${this.cType(sig.paramTypes[i])} ${p.name}`)
|
|
70
|
+
.join(", ");
|
|
71
|
+
return `${this.cType(sig.returnType)} ${fn.name}(${params || "void"})`;
|
|
72
|
+
}
|
|
73
|
+
genFunction(fn) {
|
|
74
|
+
const sig = this.functions.get(fn.name);
|
|
75
|
+
const fnScope = this.globalScope.child();
|
|
76
|
+
fn.params.forEach((p, i) => fnScope.declare(p.name, sig.paramTypes[i]));
|
|
77
|
+
this.out = [];
|
|
78
|
+
this.indentLevel = 1;
|
|
79
|
+
for (const stmt of fn.body.body)
|
|
80
|
+
this.genStatement(stmt, fnScope);
|
|
81
|
+
const body = this.out;
|
|
82
|
+
this.indentLevel = 0;
|
|
83
|
+
return [`${this.genPrototype(fn)} {`, ...body, "}"].join("\n");
|
|
84
|
+
}
|
|
85
|
+
genStatement(stmt, scope) {
|
|
86
|
+
return (0, stmts_1.default)(this, stmt, scope);
|
|
87
|
+
}
|
|
88
|
+
genNestedBlock(block, parentScope) {
|
|
89
|
+
this.indentLevel++;
|
|
90
|
+
const scope = parentScope.child();
|
|
91
|
+
for (const stmt of block.body)
|
|
92
|
+
this.genStatement(stmt, scope);
|
|
93
|
+
this.indentLevel--;
|
|
94
|
+
}
|
|
95
|
+
genPrint(expr, type, scope) {
|
|
96
|
+
const value = this.genExpression(expr, scope);
|
|
97
|
+
return (0, runtime_1.dj_print)(value, type, expr.pos.line, expr.pos.column);
|
|
98
|
+
}
|
|
99
|
+
genExpression(expr, scope) {
|
|
100
|
+
return (0, expr_1.default)(this, expr, scope);
|
|
101
|
+
}
|
|
102
|
+
// ---------------------------------------------
|
|
103
|
+
// Types
|
|
104
|
+
// ---------------------------------------------
|
|
105
|
+
inferType(expr, scope) {
|
|
106
|
+
return (0, infertype_1.default)(this, expr, scope);
|
|
107
|
+
}
|
|
108
|
+
cType(type) {
|
|
109
|
+
return (0, ctype_1.cType)(this, type);
|
|
110
|
+
}
|
|
111
|
+
emit(line) {
|
|
112
|
+
this.out.push(" ".repeat(this.indentLevel) + line);
|
|
113
|
+
}
|
|
114
|
+
escapeString(value) {
|
|
115
|
+
return value
|
|
116
|
+
.replace(/\\/g, "\\\\")
|
|
117
|
+
.replace(/"/g, '\\"')
|
|
118
|
+
.replace(/\n/g, "\\n")
|
|
119
|
+
.replace(/\t/g, "\\t");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.Codegen = Codegen;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cType = cType;
|
|
4
|
+
function cType(codegen, type) {
|
|
5
|
+
let c;
|
|
6
|
+
switch (type.base) {
|
|
7
|
+
case "ra9m":
|
|
8
|
+
c = "double";
|
|
9
|
+
break;
|
|
10
|
+
case "nass":
|
|
11
|
+
c = "char*";
|
|
12
|
+
break;
|
|
13
|
+
case "tona2i":
|
|
14
|
+
c = "bool";
|
|
15
|
+
break;
|
|
16
|
+
case "khawi":
|
|
17
|
+
c = "void*";
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
c = "void*";
|
|
21
|
+
}
|
|
22
|
+
return type.d !== undefined && type.d > 0 ? (c + "*".repeat(type.d)) : c;
|
|
23
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = genExpression;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const showType_1 = require("../../utils/showType");
|
|
6
|
+
function genExpression(codegen, expr, scope) {
|
|
7
|
+
switch (expr.type) {
|
|
8
|
+
case "NumericLiteral":
|
|
9
|
+
return String(expr.value);
|
|
10
|
+
case "StringLiteral":
|
|
11
|
+
return `"${codegen.escapeString(expr.value)}"`;
|
|
12
|
+
case "BooleanLiteral":
|
|
13
|
+
return expr.value ? "true" : "false";
|
|
14
|
+
case "NullLiteral":
|
|
15
|
+
return "NULL";
|
|
16
|
+
case "Identifier":
|
|
17
|
+
return expr.name;
|
|
18
|
+
case "AssignmentExpression":
|
|
19
|
+
return `${codegen.genExpression(expr.target, scope)} = ${codegen.genExpression(expr.value, scope)}`;
|
|
20
|
+
case "BinaryExpression": {
|
|
21
|
+
const leftType = codegen.inferType(expr.left, scope);
|
|
22
|
+
const rightType = codegen.inferType(expr.right, scope);
|
|
23
|
+
const left = codegen.genExpression(expr.left, scope);
|
|
24
|
+
const right = codegen.genExpression(expr.right, scope);
|
|
25
|
+
if (expr.operator === "+" && ((0, showType_1.isType)(leftType, "nass")) || (0, showType_1.isType)(rightType, "nass")) {
|
|
26
|
+
return `dj_concat(${left}, ${right})`;
|
|
27
|
+
}
|
|
28
|
+
if (expr.operator === "**") {
|
|
29
|
+
return `pow(${left}, ${right})`;
|
|
30
|
+
}
|
|
31
|
+
return `(${left} ${expr.operator} ${right})`;
|
|
32
|
+
}
|
|
33
|
+
case "LogicalExpression": {
|
|
34
|
+
const op = expr.operator === "&&" ? "&&" : "||";
|
|
35
|
+
return `(${codegen.genExpression(expr.left, scope)} ${op} ${codegen.genExpression(expr.right, scope)})`;
|
|
36
|
+
}
|
|
37
|
+
case "UnaryExpression":
|
|
38
|
+
return `(${expr.operator}${codegen.genExpression(expr.argument, scope)})`;
|
|
39
|
+
case "UpdateExpression": {
|
|
40
|
+
const arg = codegen.genExpression(expr.argument, scope);
|
|
41
|
+
return expr.prefix ? `(${expr.operator}${arg})` : `(${arg}${expr.operator})`;
|
|
42
|
+
}
|
|
43
|
+
case "ConditionalExpression":
|
|
44
|
+
return `(${codegen.genExpression(expr.condition, scope)} ? ${codegen.genExpression(expr.consequent, scope)} : ${codegen.genExpression(expr.alternate, scope)})`;
|
|
45
|
+
case "CallExpression": {
|
|
46
|
+
if (expr.callee.type !== "Identifier") {
|
|
47
|
+
throw new errors_1.CodegenError("ghir dawal lli direct lli md3omin", expr.pos.line, expr.pos.column);
|
|
48
|
+
}
|
|
49
|
+
const args = expr.args.map((a) => codegen.genExpression(a, scope)).join(", ");
|
|
50
|
+
return `${expr.callee.name}(${args})`;
|
|
51
|
+
}
|
|
52
|
+
case "MemberExpression": {
|
|
53
|
+
if (!expr.computed) {
|
|
54
|
+
throw new errors_1.CodegenError("lwosol llmajal mmd3omch l7d sa3a", expr.pos.line, expr.pos.column);
|
|
55
|
+
}
|
|
56
|
+
return `${codegen.genExpression(expr.object, scope)}[${codegen.genExpression(expr.property, scope)}]`;
|
|
57
|
+
}
|
|
58
|
+
case "ArrayExpression":
|
|
59
|
+
throw new errors_1.CodegenError("l9yam dyal lmasfofat mmd3ominch bchakl kaaml l7d sa3a", expr.pos.line, expr.pos.column);
|
|
60
|
+
default:
|
|
61
|
+
throw new errors_1.CodegenError(`'${expr.type}' ba9i mmd3omach`, expr.pos.line, expr.pos.column);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = inferType;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const showType_1 = require("../../utils/showType");
|
|
6
|
+
function inferType(codegen, expr, scope) {
|
|
7
|
+
switch (expr.type) {
|
|
8
|
+
case "NumericLiteral":
|
|
9
|
+
return { base: "ra9m" };
|
|
10
|
+
case "StringLiteral":
|
|
11
|
+
return { base: "nass" };
|
|
12
|
+
case "BooleanLiteral":
|
|
13
|
+
return { base: "tona2i" };
|
|
14
|
+
case "NullLiteral":
|
|
15
|
+
return { base: "khawi" };
|
|
16
|
+
case "Identifier": {
|
|
17
|
+
const type = scope.resolve(expr.name);
|
|
18
|
+
if (!type) {
|
|
19
|
+
throw new errors_1.CodegenError(`'${expr.name}' mm3rofach`, expr.pos.line, expr.pos.column);
|
|
20
|
+
}
|
|
21
|
+
return type;
|
|
22
|
+
}
|
|
23
|
+
case "ArrayExpression": {
|
|
24
|
+
if (expr.elements.length === 0)
|
|
25
|
+
return { base: "unknown", d: 1 };
|
|
26
|
+
const t = codegen.inferType(expr.elements[0], scope);
|
|
27
|
+
return { base: t.base, d: t.d ? t.d + 1 : 0 };
|
|
28
|
+
}
|
|
29
|
+
case "AssignmentExpression":
|
|
30
|
+
return codegen.inferType(expr.value, scope);
|
|
31
|
+
case "BinaryExpression": {
|
|
32
|
+
if (["==", "!=", "<", "<=", ">", ">="].includes(expr.operator))
|
|
33
|
+
return { base: "tona2i" };
|
|
34
|
+
if (expr.operator === "+") {
|
|
35
|
+
const leftType = codegen.inferType(expr.left, scope);
|
|
36
|
+
const rightType = codegen.inferType(expr.right, scope);
|
|
37
|
+
if ((0, showType_1.isType)(leftType, "nass") || (0, showType_1.isType)(rightType, "nass")) {
|
|
38
|
+
return { base: "nass", d: 0 };
|
|
39
|
+
}
|
|
40
|
+
return { base: "ra9m", d: 0 };
|
|
41
|
+
}
|
|
42
|
+
return { base: "ra9m" };
|
|
43
|
+
}
|
|
44
|
+
case "LogicalExpression":
|
|
45
|
+
return { base: "tona2i" };
|
|
46
|
+
case "UnaryExpression":
|
|
47
|
+
return expr.operator === "!" ? { base: "tona2i" } : { base: "ra9m" };
|
|
48
|
+
case "UpdateExpression":
|
|
49
|
+
return { base: "ra9m" };
|
|
50
|
+
case "ConditionalExpression": {
|
|
51
|
+
const consequentType = codegen.inferType(expr.consequent, scope);
|
|
52
|
+
const alternateType = codegen.inferType(expr.alternate, scope);
|
|
53
|
+
return consequentType === alternateType ? consequentType : { base: "unknown" };
|
|
54
|
+
}
|
|
55
|
+
case "CallExpression": {
|
|
56
|
+
if (expr.callee.type !== "Identifier")
|
|
57
|
+
return { base: "unknown" };
|
|
58
|
+
return codegen.functions.get(expr.callee.name)?.returnType ?? { base: "unknown", d: 0 };
|
|
59
|
+
}
|
|
60
|
+
case "MemberExpression": {
|
|
61
|
+
const objectType = codegen.inferType(expr.object, scope);
|
|
62
|
+
if (objectType.d && objectType.d > 0) {
|
|
63
|
+
return { base: objectType.base, d: objectType.d - 1 };
|
|
64
|
+
}
|
|
65
|
+
return { base: "unknown" };
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
return { base: "unknown" };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dj_print = dj_print;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
function dj_print(value, type, line, column) {
|
|
6
|
+
if (type.d) {
|
|
7
|
+
if (type.d > 0)
|
|
8
|
+
throw new errors_1.CodegenError("tba3t lmasfofat mazal mamd3omch", line, column);
|
|
9
|
+
}
|
|
10
|
+
switch (type.base) {
|
|
11
|
+
case "nass":
|
|
12
|
+
return `printf("%s\\n", ${value});`;
|
|
13
|
+
case "ra9m":
|
|
14
|
+
return `printf("%g\\n", ${value});`;
|
|
15
|
+
case "tona2i":
|
|
16
|
+
return `printf("%s\\n", ${value} ? "sa7i7" : "ghalat");`;
|
|
17
|
+
case "khawi":
|
|
18
|
+
return `printf("khawi\\n");`;
|
|
19
|
+
case "unknown":
|
|
20
|
+
return `printf("mm3rofch\\n");`;
|
|
21
|
+
default:
|
|
22
|
+
throw new errors_1.CodegenError(`ma9drnach ntab3o had naw3 l7ad sa3a : ${type}`, line, column);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class Scope {
|
|
4
|
+
parent;
|
|
5
|
+
vars = new Map();
|
|
6
|
+
constructor(parent = null) {
|
|
7
|
+
this.parent = parent;
|
|
8
|
+
}
|
|
9
|
+
declare(name, type) {
|
|
10
|
+
this.vars.set(name, type);
|
|
11
|
+
}
|
|
12
|
+
resolve(name) {
|
|
13
|
+
return this.vars.get(name) ?? this.parent?.resolve(name);
|
|
14
|
+
}
|
|
15
|
+
child() {
|
|
16
|
+
return new Scope(this);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.default = Scope;
|