bahasa-simpl 1.0.1 → 1.0.3
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/dist/simpl-interpreter.js +567 -448
- package/package.json +1 -1
|
@@ -4,7 +4,12 @@ class SimplError extends Error {
|
|
|
4
4
|
super(message);
|
|
5
5
|
this.line = line;
|
|
6
6
|
}
|
|
7
|
-
}class SimplErrorEksekusi extends SimplError {
|
|
7
|
+
}class SimplErrorEksekusi extends SimplError {
|
|
8
|
+
constructor(message, line, output) {
|
|
9
|
+
super(message, line);
|
|
10
|
+
this.output = output;
|
|
11
|
+
}
|
|
12
|
+
}class SimplErrorStruktur extends SimplError { }class SimplErrorTulisan extends SimplError { }
|
|
8
13
|
|
|
9
14
|
// Environment defines a scope for all data to live in
|
|
10
15
|
class Environment {
|
|
@@ -45,23 +50,23 @@ class Environment {
|
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
const RESERVED_KEYWORDS = [
|
|
48
|
-
"rubah",
|
|
49
|
-
"kalau",
|
|
50
|
-
"namun",
|
|
51
|
-
"slagi",
|
|
52
|
-
"untuk",
|
|
53
|
-
"cetak",
|
|
54
|
-
"henti",
|
|
55
|
-
"lewat",
|
|
56
|
-
"dalam",
|
|
57
|
-
"hasil",
|
|
58
|
-
"kerja",
|
|
59
|
-
"datum",
|
|
60
|
-
"jenis",
|
|
61
|
-
"model",
|
|
62
|
-
"error",
|
|
63
|
-
"tetap",
|
|
64
|
-
"modul",
|
|
53
|
+
"rubah",
|
|
54
|
+
"kalau",
|
|
55
|
+
"namun",
|
|
56
|
+
"slagi",
|
|
57
|
+
"untuk",
|
|
58
|
+
"cetak",
|
|
59
|
+
"henti",
|
|
60
|
+
"lewat",
|
|
61
|
+
"dalam",
|
|
62
|
+
"hasil",
|
|
63
|
+
"kerja",
|
|
64
|
+
"datum",
|
|
65
|
+
"jenis",
|
|
66
|
+
"model",
|
|
67
|
+
"error",
|
|
68
|
+
"tetap",
|
|
69
|
+
"modul",
|
|
65
70
|
];
|
|
66
71
|
|
|
67
72
|
const RUBAH = 0,
|
|
@@ -109,23 +114,23 @@ const RUBAH = 0,
|
|
|
109
114
|
MODULUS = 43;
|
|
110
115
|
|
|
111
116
|
const TOKEN_STRING = [
|
|
112
|
-
"RUBAH",
|
|
113
|
-
"KALAU",
|
|
114
|
-
"NAMUN",
|
|
115
|
-
"SLAGI",
|
|
116
|
-
"UNTUK",
|
|
117
|
-
"CETAK",
|
|
118
|
-
"HENTI",
|
|
119
|
-
"LEWAT",
|
|
120
|
-
"DALAM",
|
|
121
|
-
"HASIL",
|
|
122
|
-
"KERJA",
|
|
123
|
-
"DATUM",
|
|
124
|
-
"JENIS",
|
|
125
|
-
"MODEL",
|
|
126
|
-
"ERROR",
|
|
127
|
-
"TETAP",
|
|
128
|
-
"MODUL",
|
|
117
|
+
"RUBAH",
|
|
118
|
+
"KALAU",
|
|
119
|
+
"NAMUN",
|
|
120
|
+
"SLAGI",
|
|
121
|
+
"UNTUK",
|
|
122
|
+
"CETAK",
|
|
123
|
+
"HENTI",
|
|
124
|
+
"LEWAT",
|
|
125
|
+
"DALAM",
|
|
126
|
+
"HASIL",
|
|
127
|
+
"KERJA",
|
|
128
|
+
"DATUM",
|
|
129
|
+
"JENIS",
|
|
130
|
+
"MODEL",
|
|
131
|
+
"ERROR",
|
|
132
|
+
"TETAP",
|
|
133
|
+
"MODUL",
|
|
129
134
|
"EOF",
|
|
130
135
|
"ID",
|
|
131
136
|
"LITERAL",
|
|
@@ -160,20 +165,23 @@ const RESERVED_NAMES = [
|
|
|
160
165
|
];
|
|
161
166
|
|
|
162
167
|
const petikSymbol = Symbol("petik"),
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
angkaSymbol = Symbol("angka"),
|
|
169
|
+
logisSymbol = Symbol("logis"),
|
|
170
|
+
mesinSymbol = Symbol("mesin"),
|
|
171
|
+
barisSymbol = Symbol("baris"),
|
|
172
|
+
stipeSymbol = Symbol("stipe"),
|
|
173
|
+
modulSymbol = Symbol("modul");
|
|
169
174
|
|
|
170
175
|
class Value {
|
|
171
176
|
constructor(type, data) {
|
|
172
177
|
this.type = type;
|
|
173
178
|
this.data = data;
|
|
179
|
+
this.member = null;
|
|
174
180
|
}
|
|
175
181
|
}
|
|
176
182
|
|
|
183
|
+
const NIHIL = new Value(null, null);
|
|
184
|
+
|
|
177
185
|
class Variable extends Value {
|
|
178
186
|
constructor(type, tetap, data) {
|
|
179
187
|
super(type, data);
|
|
@@ -198,9 +206,9 @@ class Stipe extends Variable {
|
|
|
198
206
|
|
|
199
207
|
let result = null;
|
|
200
208
|
if (left) { // Binary
|
|
201
|
-
result = operatorFunc.data
|
|
209
|
+
result = visitor.callFunc(operatorFunc.data, [right, left]);
|
|
202
210
|
} else { // Unary, safe because valid unary op is just + - ! in the parser
|
|
203
|
-
result = operatorFunc.data
|
|
211
|
+
result = visitor.callFunc(operatorFunc.data, [right]);
|
|
204
212
|
}
|
|
205
213
|
return result;
|
|
206
214
|
}
|
|
@@ -208,184 +216,179 @@ class Stipe extends Variable {
|
|
|
208
216
|
|
|
209
217
|
class PetikTipe extends Stipe {
|
|
210
218
|
constructor() {
|
|
211
|
-
super(petikSymbol,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
219
|
+
super(petikSymbol, new Callable(null, (v, args) => {
|
|
220
|
+
const kePetik = (thing) => {
|
|
221
|
+
if (thing.type === logisSymbol) {
|
|
222
|
+
return thing.data ? "benar" : "salah";
|
|
223
|
+
} else if (thing.type === barisSymbol) {
|
|
224
|
+
return '[' + thing.data.reduce((str, val) => str + ", " + kePetik(val), "").slice(1) + ' ]';
|
|
225
|
+
} else if (thing.type === stipeSymbol) {
|
|
226
|
+
return `Model<${thing.symbol.description}>`;
|
|
227
|
+
} else if (thing.type === mesinSymbol) {
|
|
228
|
+
let underlying = thing.data.returnType?.description;
|
|
229
|
+
return `Mesin<${underlying ? underlying : 'datum'}>`;
|
|
230
|
+
} else if (thing.type === angkaSymbol) {
|
|
231
|
+
return thing.data.toString();
|
|
232
|
+
} else if (thing.type === petikSymbol) {
|
|
233
|
+
return '"' + thing.data + '"';
|
|
234
|
+
} else {
|
|
235
|
+
if (!thing?.type) return `nihil`;
|
|
236
|
+
let type = v.environment.get(thing.type.description);
|
|
237
|
+
if (type?.member.has("kePetik")) {
|
|
238
|
+
v.stack.push(v.line);
|
|
239
|
+
let res = v.callFunc(type.member.get("kePetik").data, [thing]).data;
|
|
240
|
+
v.stack.pop();
|
|
241
|
+
return res;
|
|
216
242
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
} else if (thing.type === stipeSymbol) {
|
|
224
|
-
return `Model:${thing.symbol.description}`;
|
|
225
|
-
} else if (thing.type === mesinSymbol) {
|
|
226
|
-
return `Mesin<>`;
|
|
227
|
-
} else if (thing.type === angkaSymbol || thing.type === petikSymbol) {
|
|
228
|
-
return thing.data !== null ? thing.data.toString() : "nihil";
|
|
229
|
-
} else {
|
|
230
|
-
return `Objek<${thing.symbol.description}>` + thing;
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
return new Value(petikSymbol, kePetik(args[0]));
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
});
|
|
243
|
+
return `${thing.type.description}<>`;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
return new Value(petikSymbol, kePetik(args[0]));
|
|
247
|
+
}, [[null]], petikSymbol, true)
|
|
248
|
+
);
|
|
238
249
|
this.init();
|
|
239
250
|
}
|
|
240
251
|
|
|
241
252
|
init() {
|
|
242
253
|
// BINARY / UNARY OPERATORS
|
|
243
|
-
this.operators.define("PLUS",
|
|
244
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], petikSymbol, (
|
|
245
|
-
this.operators.define("LEBIH",
|
|
246
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (
|
|
247
|
-
this.operators.define("KURANG",
|
|
248
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (
|
|
249
|
-
this.operators.define("SAMA_SAMA",
|
|
250
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (
|
|
254
|
+
this.operators.define("PLUS",
|
|
255
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], petikSymbol, (_, [r, l]) => new Value(petikSymbol, l.data + r.data)));
|
|
256
|
+
this.operators.define("LEBIH",
|
|
257
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data > r.data)));
|
|
258
|
+
this.operators.define("KURANG",
|
|
259
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data < r.data)));
|
|
260
|
+
this.operators.define("SAMA_SAMA",
|
|
261
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data === r.data)));
|
|
251
262
|
this.operators.define("LEBIH_SAMA",
|
|
252
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (
|
|
263
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data >= r.data)));
|
|
253
264
|
this.operators.define("KURANG_SAMA",
|
|
254
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (
|
|
265
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data <= r.data)));
|
|
255
266
|
this.operators.define("SERU_SAMA",
|
|
256
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (
|
|
267
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data !== r.data)));
|
|
257
268
|
this.operators.define("AMPERSAN",
|
|
258
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (
|
|
269
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data && r.data)));
|
|
259
270
|
this.operators.define("PIPA",
|
|
260
|
-
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (
|
|
271
|
+
makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data || r.data)));
|
|
261
272
|
|
|
262
273
|
this.operators.define("SERU_UNARY",
|
|
263
|
-
makeBuiltInFunc([petikSymbol], logisSymbol, (
|
|
274
|
+
makeBuiltInFunc([petikSymbol], logisSymbol, (_, [r]) => new Value(logisSymbol, !Boolean(r.data))));
|
|
264
275
|
|
|
265
276
|
this.member = new Environment();
|
|
266
277
|
|
|
267
|
-
this.member.define("pisah", makeBuiltInFunc([petikSymbol, petikSymbol], barisSymbol, (
|
|
268
|
-
return new Value(barisSymbol, d.data.split(sep.data).map(val=>new Value(petikSymbol, val)));
|
|
278
|
+
this.member.define("pisah", makeBuiltInFunc([petikSymbol, petikSymbol], barisSymbol, (_, [d, sep]) => {
|
|
279
|
+
return new Value(barisSymbol, d.data.split(sep.data).map(val => new Value(petikSymbol, val)));
|
|
269
280
|
}));
|
|
270
|
-
this.member.define("bersih", makeBuiltInFunc([petikSymbol], petikSymbol, (
|
|
281
|
+
this.member.define("bersih", makeBuiltInFunc([petikSymbol], petikSymbol, (_, [d]) => {
|
|
271
282
|
return new Value(petikSymbol, d.data.trim())
|
|
272
283
|
}));
|
|
273
284
|
this.member.define("ganti", makeBuiltInFunc([petikSymbol, petikSymbol, petikSymbol], petikSymbol,
|
|
274
|
-
(
|
|
285
|
+
(_, [d, what, rep]) => new Value(petikSymbol, d.data.replaceAll(what.data, rep.data))
|
|
275
286
|
));
|
|
276
|
-
this.member.define("besar", makeBuiltInFunc([petikSymbol], petikSymbol, (
|
|
287
|
+
this.member.define("besar", makeBuiltInFunc([petikSymbol], petikSymbol, (_, [p]) => {
|
|
277
288
|
return new Value(petikSymbol, p.data.toUpperCase())
|
|
278
289
|
}));
|
|
290
|
+
this.member.define("kecil", makeBuiltInFunc([petikSymbol], petikSymbol, (_, [p]) => {
|
|
291
|
+
return new Value(petikSymbol, p.data.toLowerCase())
|
|
292
|
+
}));
|
|
279
293
|
}
|
|
280
294
|
}
|
|
281
295
|
|
|
282
296
|
class AngkaTipe extends Stipe {
|
|
283
297
|
constructor() {
|
|
284
|
-
super(angkaSymbol, {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
298
|
+
super(angkaSymbol, new Callable(null, (v, args) => {
|
|
299
|
+
if (typeof args[0].data === "number") {
|
|
300
|
+
return new Value(angkaSymbol, args[0].data);
|
|
301
|
+
}
|
|
289
302
|
|
|
290
|
-
|
|
303
|
+
switch (args[0].type) {
|
|
304
|
+
case petikSymbol:
|
|
305
|
+
if (isNaN(Number(args[0].data))) {
|
|
306
|
+
v.error("Nilai dari petik bukanlah sebuah angka, konversi gagal.");
|
|
307
|
+
}
|
|
308
|
+
return new Value(angkaSymbol, Number(args[0].data));
|
|
309
|
+
case angkaSymbol:
|
|
291
310
|
return new Value(angkaSymbol, args[0].data);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
v.error("Nilai dari petik bukanlah sebuah angka, konversi gagal.");
|
|
298
|
-
}
|
|
299
|
-
return new Value(angkaSymbol, Number(args[0].data));
|
|
300
|
-
case angkaSymbol:
|
|
301
|
-
return new Value(angkaSymbol, args[0].data);
|
|
302
|
-
case logisSymbol:
|
|
303
|
-
return new Value(angkaSymbol, Number(args[0].data));
|
|
304
|
-
default:
|
|
305
|
-
v.error(`Tipe yang dapat diterima hanyalah petik, angka, logis, dan jenis. Mendapatkan ${args[0].type.description}.`);
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
311
|
+
case logisSymbol:
|
|
312
|
+
return new Value(angkaSymbol, Number(args[0].data));
|
|
313
|
+
default:
|
|
314
|
+
v.error(`Tipe yang dapat diterima hanyalah petik, angka, logis, dan jenis. Mendapatkan ${args[0].type.description}.`);
|
|
315
|
+
return;
|
|
308
316
|
}
|
|
309
|
-
})
|
|
317
|
+
}, [[null]], angkaSymbol, true)
|
|
318
|
+
);
|
|
310
319
|
this.init();
|
|
311
320
|
}
|
|
312
321
|
|
|
313
322
|
init() {
|
|
314
|
-
this.operators.define("PLUS",
|
|
315
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (
|
|
323
|
+
this.operators.define("PLUS",
|
|
324
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (_, [r, l]) => new Value(angkaSymbol, l.data + r.data)));
|
|
316
325
|
this.operators.define("MINUS",
|
|
317
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (
|
|
318
|
-
this.operators.define("BINTANG",
|
|
319
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (
|
|
326
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (_, [r, l]) => new Value(angkaSymbol, l.data - r.data)));
|
|
327
|
+
this.operators.define("BINTANG",
|
|
328
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (_, [r, l]) => new Value(angkaSymbol, l.data * r.data)));
|
|
320
329
|
this.operators.define("GARIS_MIRING",
|
|
321
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (
|
|
322
|
-
this.operators.define("MODULUS",
|
|
323
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (
|
|
324
|
-
|
|
325
|
-
this.operators.define("LEBIH",
|
|
326
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (
|
|
327
|
-
this.operators.define("KURANG",
|
|
328
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (
|
|
329
|
-
this.operators.define("SAMA_SAMA",
|
|
330
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (
|
|
330
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (_, [r, l]) => new Value(angkaSymbol, l.data / r.data)));
|
|
331
|
+
this.operators.define("MODULUS",
|
|
332
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (_, [r, l]) => new Value(angkaSymbol, l.data % r.data)));
|
|
333
|
+
|
|
334
|
+
this.operators.define("LEBIH",
|
|
335
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data > r.data)));
|
|
336
|
+
this.operators.define("KURANG",
|
|
337
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data < r.data)));
|
|
338
|
+
this.operators.define("SAMA_SAMA",
|
|
339
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data === r.data)));
|
|
331
340
|
this.operators.define("LEBIH_SAMA",
|
|
332
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (
|
|
341
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data >= r.data)));
|
|
333
342
|
this.operators.define("KURANG_SAMA",
|
|
334
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (
|
|
343
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data <= r.data)));
|
|
335
344
|
this.operators.define("SERU_SAMA",
|
|
336
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (
|
|
345
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data !== r.data)));
|
|
337
346
|
this.operators.define("AMPERSAN",
|
|
338
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (
|
|
347
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data && r.data)));
|
|
339
348
|
this.operators.define("PIPA",
|
|
340
|
-
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (
|
|
349
|
+
makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data || r.data)));
|
|
341
350
|
this.operators.define("SERU_UNARY",
|
|
342
|
-
makeBuiltInFunc([angkaSymbol], logisSymbol, (
|
|
351
|
+
makeBuiltInFunc([angkaSymbol], logisSymbol, (_, [r]) => new Value(logisSymbol, !Boolean(r.data))));
|
|
343
352
|
this.operators.define("PLUS_UNARY",
|
|
344
|
-
makeBuiltInFunc([angkaSymbol], angkaSymbol, (
|
|
353
|
+
makeBuiltInFunc([angkaSymbol], angkaSymbol, (_, [r]) => new Value(angkaSymbol, +r.data)));
|
|
345
354
|
this.operators.define("MINUS_UNARY",
|
|
346
|
-
makeBuiltInFunc([angkaSymbol], angkaSymbol, (
|
|
355
|
+
makeBuiltInFunc([angkaSymbol], angkaSymbol, (_, [r]) => new Value(angkaSymbol, -r.data)));
|
|
347
356
|
|
|
348
357
|
this.member = new Environment();
|
|
349
|
-
this.member.define("acak", makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (v, [a,b]) => {
|
|
350
|
-
let width = Math.abs(a.data-b.data);
|
|
351
|
-
let range = Math.random() * width;
|
|
352
|
-
let final = range + Math.min(a.data,b.data);
|
|
353
|
-
return new Value(angkaSymbol, final);
|
|
354
|
-
}));
|
|
355
358
|
|
|
356
|
-
this.member.define("bulat", makeBuiltInFunc([angkaSymbol], angkaSymbol, (
|
|
359
|
+
this.member.define("bulat", makeBuiltInFunc([angkaSymbol], angkaSymbol, (_, [a]) => {
|
|
357
360
|
return new Value(angkaSymbol, Math.round(a.data));
|
|
358
361
|
}));
|
|
362
|
+
this.member.define("bulatAtas", makeBuiltInFunc([angkaSymbol], angkaSymbol, (_, [a]) => {
|
|
363
|
+
return new Value(angkaSymbol, Math.ceil(a.data))
|
|
364
|
+
}));
|
|
365
|
+
this.member.define("bulatBawah", makeBuiltInFunc([angkaSymbol], angkaSymbol, (_, [a]) => {
|
|
366
|
+
return new Value(angkaSymbol, Math.floor(a.data))
|
|
367
|
+
}));
|
|
359
368
|
}
|
|
360
369
|
}
|
|
361
370
|
|
|
362
371
|
class LogisTipe extends Stipe {
|
|
363
372
|
constructor() {
|
|
364
|
-
super(logisSymbol, {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return new Value(logisSymbol, Boolean(args[0].data) || Boolean(args[0].data.member));
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
+
super(logisSymbol, new Callable(null, (_, args) => {
|
|
374
|
+
return new Value(logisSymbol, Boolean(args[0].data) || Boolean(args[0].data.member));
|
|
375
|
+
}, [[null]], logisSymbol, true)
|
|
376
|
+
);
|
|
373
377
|
this.init();
|
|
374
378
|
}
|
|
375
379
|
|
|
376
380
|
init() {
|
|
377
|
-
this.operators.define("SAMA_SAMA",
|
|
378
|
-
makeBuiltInFunc([logisSymbol, logisSymbol], logisSymbol, (
|
|
381
|
+
this.operators.define("SAMA_SAMA",
|
|
382
|
+
makeBuiltInFunc([logisSymbol, logisSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data === r.data)));
|
|
379
383
|
this.operators.define("SERU_SAMA",
|
|
380
|
-
makeBuiltInFunc([logisSymbol, logisSymbol], logisSymbol, (
|
|
381
|
-
|
|
384
|
+
makeBuiltInFunc([logisSymbol, logisSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data !== r.data)));
|
|
382
385
|
this.operators.define("AMPERSAN",
|
|
383
|
-
makeBuiltInFunc([logisSymbol, logisSymbol], logisSymbol, (
|
|
386
|
+
makeBuiltInFunc([logisSymbol, logisSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data && r.data)));
|
|
384
387
|
this.operators.define("PIPA",
|
|
385
|
-
makeBuiltInFunc([logisSymbol, logisSymbol], logisSymbol, (
|
|
388
|
+
makeBuiltInFunc([logisSymbol, logisSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data || r.data)));
|
|
386
389
|
this.operators.define("SERU_UNARY",
|
|
387
|
-
makeBuiltInFunc([logisSymbol], logisSymbol, (
|
|
388
|
-
|
|
390
|
+
makeBuiltInFunc([logisSymbol], logisSymbol, (_, [r]) => new Value(logisSymbol, !Boolean(r.data))));
|
|
391
|
+
|
|
389
392
|
this.member = new Environment();
|
|
390
393
|
}
|
|
391
394
|
}
|
|
@@ -396,43 +399,43 @@ class BarisTipe extends Stipe {
|
|
|
396
399
|
this.init();
|
|
397
400
|
}
|
|
398
401
|
|
|
399
|
-
init
|
|
400
|
-
this.operators.define("PLUS",
|
|
401
|
-
makeBuiltInFunc([barisSymbol, barisSymbol], barisSymbol, (
|
|
402
|
+
init() {
|
|
403
|
+
this.operators.define("PLUS",
|
|
404
|
+
makeBuiltInFunc([barisSymbol, barisSymbol], barisSymbol, (_, [r, l]) =>
|
|
405
|
+
new Value(barisSymbol, Array(...l.data, ...r.data))
|
|
406
|
+
));
|
|
402
407
|
|
|
403
408
|
this.operators.define("MINUS_UNARY",
|
|
404
|
-
makeBuiltInFunc([barisSymbol], barisSymbol, (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
return newBaris;
|
|
408
|
-
}));
|
|
409
|
+
makeBuiltInFunc([barisSymbol], barisSymbol, (_, [r]) =>
|
|
410
|
+
new Value(barisSymbol, r.data.slice(0, r.length))
|
|
411
|
+
));
|
|
409
412
|
|
|
410
413
|
this.operators.define("SAMA_SAMA",
|
|
411
|
-
makeBuiltInFunc([barisSymbol, barisSymbol], logisSymbol, (
|
|
414
|
+
makeBuiltInFunc([barisSymbol, barisSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, ((a, b) => {
|
|
412
415
|
if (a.length !== b.length) return false;
|
|
413
416
|
for (let i = 0; i < a.length; i++) {
|
|
414
417
|
if (a[i].data !== b[i].data) return false;
|
|
415
418
|
}
|
|
416
419
|
return true;
|
|
417
|
-
})(l.data,r.data)))
|
|
420
|
+
})(l.data, r.data)))
|
|
418
421
|
);
|
|
419
422
|
|
|
420
|
-
this.operators.define("SERU_SAMA",
|
|
421
|
-
makeBuiltInFunc([barisSymbol, barisSymbol], logisSymbol, (
|
|
423
|
+
this.operators.define("SERU_SAMA",
|
|
424
|
+
makeBuiltInFunc([barisSymbol, barisSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, ((a, b) => {
|
|
422
425
|
if (a.length !== b.length) return true;
|
|
423
426
|
for (let i = 0; i < a.length; i++) {
|
|
424
427
|
if (a[i].data !== b[i].data) return true;
|
|
425
428
|
}
|
|
426
429
|
return false;
|
|
427
|
-
})(l.data,r.data)))
|
|
430
|
+
})(l.data, r.data)))
|
|
428
431
|
);
|
|
429
432
|
|
|
430
433
|
this.operators.define("AMPERSAN",
|
|
431
|
-
makeBuiltInFunc([barisSymbol, barisSymbol], logisSymbol, (
|
|
434
|
+
makeBuiltInFunc([barisSymbol, barisSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data && r.data)));
|
|
432
435
|
this.operators.define("PIPA",
|
|
433
|
-
makeBuiltInFunc([barisSymbol, barisSymbol], logisSymbol, (
|
|
436
|
+
makeBuiltInFunc([barisSymbol, barisSymbol], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data || r.data)));
|
|
434
437
|
this.operators.define("SERU_UNARY",
|
|
435
|
-
makeBuiltInFunc([barisSymbol], logisSymbol, (
|
|
438
|
+
makeBuiltInFunc([barisSymbol], logisSymbol, (_, [r]) => new Value(logisSymbol, !Boolean(r.data))));
|
|
436
439
|
|
|
437
440
|
this.member = new Environment();
|
|
438
441
|
|
|
@@ -442,7 +445,55 @@ class BarisTipe extends Stipe {
|
|
|
442
445
|
}
|
|
443
446
|
|
|
444
447
|
while (i.data < 0) i.data += b.data.length;
|
|
445
|
-
|
|
448
|
+
b.data = b.data.filter((_, idx) => idx !== i.data);
|
|
449
|
+
return b;
|
|
450
|
+
}));
|
|
451
|
+
|
|
452
|
+
this.member.define("potongan", makeBuiltInFunc([barisSymbol, angkaSymbol, angkaSymbol], barisSymbol, (v, [b, fr, to]) => {
|
|
453
|
+
if (fr.data >= b.data.length || fr.data < 0 || to.data <= fr.data || to.data > b.data.length) {
|
|
454
|
+
v.error(`Indeks tidak valid, ${fr.data}:${to.data}, dengan ukuran baris ${b.data.length}`);
|
|
455
|
+
}
|
|
456
|
+
return new Value(barisSymbol, b.data.slice(fr.data, to.data));
|
|
457
|
+
}));
|
|
458
|
+
this.member.define("tumpuk", makeBuiltInFunc([barisSymbol, null], null, (_, [b, d]) => {
|
|
459
|
+
b.data.push(d);
|
|
460
|
+
return b;
|
|
461
|
+
}));
|
|
462
|
+
this.member.define("tumpah", makeBuiltInFunc([barisSymbol], null, (_, [b]) => {
|
|
463
|
+
b.data.pop();
|
|
464
|
+
return b;
|
|
465
|
+
}));
|
|
466
|
+
this.member.define("masuk", makeBuiltInFunc([barisSymbol, null, angkaSymbol], null, (v, [b, d, idx]) => {
|
|
467
|
+
if (idx.data > b.data.length) {
|
|
468
|
+
v.error(`Indeks tidak valid, ${idx.data}, dengan ukuran baris ${b.data.length}`);
|
|
469
|
+
}
|
|
470
|
+
b.data.splice(idx.data, 0, d);
|
|
471
|
+
return b;
|
|
472
|
+
}));
|
|
473
|
+
this.member.define("petakan", makeBuiltInFunc([barisSymbol, mesinSymbol], barisSymbol, (v, [b, m]) => {
|
|
474
|
+
let newBaris = new Value(barisSymbol, []);
|
|
475
|
+
for (let datum of b.data) {
|
|
476
|
+
let result = v.callFunc(m.data, [datum]);
|
|
477
|
+
newBaris.data.push(result);
|
|
478
|
+
}
|
|
479
|
+
return newBaris;
|
|
480
|
+
}));
|
|
481
|
+
this.member.define("saring", makeBuiltInFunc([barisSymbol, mesinSymbol], barisSymbol, (v, [b, m]) => {
|
|
482
|
+
let newBaris = new Value(barisSymbol, []);
|
|
483
|
+
for (let datum of b.data) {
|
|
484
|
+
let result = v.callFunc(m.data, [datum]);
|
|
485
|
+
if (result.data === true) {
|
|
486
|
+
newBaris.data.push(datum);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return newBaris;
|
|
490
|
+
}));
|
|
491
|
+
this.member.define("reduksi", makeBuiltInFunc([barisSymbol, mesinSymbol, null], null, (v, [b, m, d]) => {
|
|
492
|
+
for (let datum of b.data) {
|
|
493
|
+
let result = v.callFunc(m.data, [d, datum]);
|
|
494
|
+
d = result;
|
|
495
|
+
}
|
|
496
|
+
return d;
|
|
446
497
|
}));
|
|
447
498
|
}
|
|
448
499
|
}
|
|
@@ -457,38 +508,28 @@ class MesinTipe extends Stipe {
|
|
|
457
508
|
let Model$1 = class Model extends Stipe {
|
|
458
509
|
constructor(name, params) {
|
|
459
510
|
let sym = Symbol(name);
|
|
460
|
-
super(sym, {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
}
|
|
465
|
-
// for call error info
|
|
466
|
-
let callLineNum = v.line;
|
|
467
|
-
|
|
468
|
-
let obj = new Value(sym, true);
|
|
469
|
-
obj.member = new Environment();
|
|
470
|
-
|
|
471
|
-
for (let i = 0; i < args.length; i++) {
|
|
472
|
-
let type = params[i][0];
|
|
473
|
-
let symbol = type.accept(v);
|
|
474
|
-
let name = params[i][1].lexeme;
|
|
475
|
-
|
|
476
|
-
let val = new Variable(symbol, type.tetap, args[i].data);
|
|
477
|
-
if (args[i].data === null) ; else if (symbol === null) {
|
|
478
|
-
val.isDatum = true;
|
|
479
|
-
val.type = args[i].type;
|
|
480
|
-
} else if (symbol !== args[i].type) {
|
|
481
|
-
v.line = callLineNum;
|
|
482
|
-
v.error(`Tipe member tidak sama dengan argumen. ${symbol.description} != ${args[i].type.description}`);
|
|
483
|
-
}
|
|
484
|
-
val.member = args[i].member;
|
|
485
|
-
obj.member.define(name, val);
|
|
486
|
-
}
|
|
511
|
+
super(sym, new Callable(null, (v, args) => {
|
|
512
|
+
|
|
513
|
+
let obj = new Value(sym, true);
|
|
514
|
+
obj.member = new Environment();
|
|
487
515
|
|
|
488
|
-
|
|
489
|
-
|
|
516
|
+
for (let i = 0; i < args.length; i++) {
|
|
517
|
+
let type = params[i][0];
|
|
518
|
+
let symbol = type.accept(v);
|
|
519
|
+
let name = params[i][1].lexeme;
|
|
520
|
+
|
|
521
|
+
let val = new Variable(symbol, type.tetap, args[i].data);
|
|
522
|
+
if (args[i].data === null) ; else if (symbol === null) {
|
|
523
|
+
val.isDatum = true;
|
|
524
|
+
val.type = args[i].type;
|
|
525
|
+
}
|
|
526
|
+
val.member = args[i].member;
|
|
527
|
+
obj.member.define(name, val);
|
|
490
528
|
}
|
|
491
|
-
|
|
529
|
+
|
|
530
|
+
return obj;
|
|
531
|
+
}, params.map(_ => [null]), sym, true)
|
|
532
|
+
);
|
|
492
533
|
}
|
|
493
534
|
};
|
|
494
535
|
|
|
@@ -504,115 +545,56 @@ let Jenis$1 = class Jenis extends Stipe {
|
|
|
504
545
|
}
|
|
505
546
|
|
|
506
547
|
init(sym) {
|
|
507
|
-
this.operators.define("SAMA_SAMA",
|
|
508
|
-
makeBuiltInFunc([sym, sym], logisSymbol, (
|
|
548
|
+
this.operators.define("SAMA_SAMA",
|
|
549
|
+
makeBuiltInFunc([sym, sym], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data === r.data)));
|
|
509
550
|
this.operators.define("SERU_SAMA",
|
|
510
|
-
makeBuiltInFunc([sym, sym], logisSymbol, (
|
|
551
|
+
makeBuiltInFunc([sym, sym], logisSymbol, (_, [r, l]) => new Value(logisSymbol, l.data !== r.data)));
|
|
511
552
|
}
|
|
512
553
|
};
|
|
513
554
|
|
|
514
555
|
class Callable {
|
|
515
|
-
constructor(enclosing, block, parameters, returnType) {
|
|
556
|
+
constructor(enclosing, block, parameters, returnType, isBuiltIn = false) {
|
|
516
557
|
this.closure = enclosing;
|
|
517
558
|
this.block = block;
|
|
518
559
|
this.parameters = parameters;
|
|
519
560
|
this.returnType = returnType;
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
// call with arguments (of value)
|
|
523
|
-
callFunc (visitor, args) {
|
|
524
|
-
// this function may go into the interpreter idk
|
|
525
|
-
if (args.length !== this.parameters.length) {
|
|
526
|
-
visitor.error("Jumlah argumen tidak sama dengan parameter mesin:" + ` ${args.length} != ${this.parameters.length}.`);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// for asserting _call_ error.
|
|
530
|
-
let callLineNum = visitor.line;
|
|
531
|
-
|
|
532
|
-
let visitorEnv = visitor.environment;
|
|
533
|
-
let funcEnv = new Environment(this.closure);
|
|
534
|
-
visitor.environment = funcEnv;
|
|
535
|
-
for(let i = 0; i < args.length; i++) {
|
|
536
|
-
let [type, tetap, name] = this.parameters[i];
|
|
537
|
-
let val = new Variable(type, tetap, args[i].data);
|
|
538
|
-
|
|
539
|
-
if (type === null) {
|
|
540
|
-
val.isDatum = true;
|
|
541
|
-
val.type = args[i].type;
|
|
542
|
-
} else if (args[i].data === null) ; else if (args[i].type !== type) {
|
|
543
|
-
visitor.line = callLineNum;
|
|
544
|
-
visitor.error(`Tipe argumen tidak sama dengan parameter. ${args[i].type} != ${type.description}`);
|
|
545
|
-
}
|
|
546
|
-
val.member = args[i].member;
|
|
547
|
-
funcEnv.define(name, val);
|
|
548
|
-
}
|
|
549
|
-
let result = new Value(null, null);
|
|
550
|
-
for(let stmt of this.block.statements) {
|
|
551
|
-
try {
|
|
552
|
-
stmt.accept(visitor);
|
|
553
|
-
} catch (err) {
|
|
554
|
-
if (err instanceof Hasil$1) {
|
|
555
|
-
result = err.value;
|
|
556
|
-
break;
|
|
557
|
-
} else if (err instanceof Henti$1 || err instanceof Lewat$1) {
|
|
558
|
-
visitor.error("Tidak bisa menghentikan atau melewatkan mesin. ");
|
|
559
|
-
} else throw err;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
visitor.environment = visitorEnv;
|
|
563
|
-
return result;
|
|
561
|
+
this.isBuiltIn = isBuiltIn;
|
|
564
562
|
}
|
|
565
563
|
}
|
|
566
564
|
|
|
567
|
-
// this approach is too slow. Will fix later...
|
|
568
|
-
|
|
569
565
|
function makeBuiltInFunc(parameters, returnType, funcBody) {
|
|
570
|
-
let lambda = new Callable(null,
|
|
571
|
-
lambda.callFunc = (v, args) => {
|
|
572
|
-
if (args.length != parameters.length) {
|
|
573
|
-
v.error("Jumlah Argumen tidak sama dengan parameter:" + ` Seharusnya ${parameters.length} dan bukan ${args.length}.`);
|
|
574
|
-
}
|
|
575
|
-
for (let i = 0; i < args.length; i++) {
|
|
576
|
-
if (parameters[i] === null) continue;
|
|
577
|
-
if (args[i].type !== parameters[i]) {
|
|
578
|
-
v.error("Tipe argumen tidak sama dengan tipe parameter." + ` Seharusnya ${parameters[i].type.description} dan bukan ${args[i].description}`);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
return funcBody(v, args);
|
|
582
|
-
};
|
|
566
|
+
let lambda = new Callable(null, funcBody, parameters.map((val) => [val]), returnType, true);
|
|
583
567
|
let variable = new Variable(mesinSymbol, true, lambda);
|
|
584
568
|
return variable;
|
|
585
569
|
}
|
|
586
570
|
|
|
587
571
|
function copier(thing) {
|
|
588
|
-
switch(thing.type) {
|
|
572
|
+
switch (thing.type) {
|
|
589
573
|
case petikSymbol: return new Value(petikSymbol, thing.data);
|
|
590
574
|
case angkaSymbol: return new Value(angkaSymbol, thing.data);
|
|
591
575
|
case logisSymbol: return new Value(logisSymbol, thing.data);
|
|
592
576
|
case mesinSymbol: return new Value(mesinSymbol, thing.data);
|
|
593
|
-
case barisSymbol: return new Value(barisSymbol, thing.data.map((val)=>copier(val)));
|
|
594
|
-
case stipeSymbol: return
|
|
595
|
-
case modulSymbol: return
|
|
577
|
+
case barisSymbol: return new Value(barisSymbol, thing.data.map((val) => copier(val)));
|
|
578
|
+
case stipeSymbol: return NIHIL;
|
|
579
|
+
case modulSymbol: return NIHIL;
|
|
596
580
|
default:
|
|
597
581
|
if (thing.member && thing.member instanceof Environment) {
|
|
598
582
|
let keys = thing.member.memory.keys();
|
|
599
583
|
let valCopy = new Value(thing.type, null);
|
|
600
584
|
let newMember = new Environment();
|
|
601
585
|
for (let i of keys) {
|
|
602
|
-
if (i === "objek") continue;
|
|
603
586
|
newMember.define(i, copier(thing.member.get(i)));
|
|
604
587
|
}
|
|
605
|
-
newMember.define("objek", valCopy);
|
|
606
588
|
valCopy.member = newMember;
|
|
607
589
|
|
|
608
590
|
return valCopy;
|
|
609
591
|
}
|
|
610
|
-
return
|
|
592
|
+
return NIHIL;
|
|
611
593
|
}
|
|
612
594
|
}
|
|
613
595
|
|
|
614
596
|
|
|
615
|
-
const GLOBAL_ENV = (() => {
|
|
597
|
+
const GLOBAL_ENV = (() => {
|
|
616
598
|
let env = new Environment();
|
|
617
599
|
env.define("petik", new PetikTipe());
|
|
618
600
|
env.define("angka", new AngkaTipe());
|
|
@@ -620,29 +602,43 @@ const GLOBAL_ENV = (() => {
|
|
|
620
602
|
env.define("baris", new BarisTipe());
|
|
621
603
|
env.define("mesin", new MesinTipe());
|
|
622
604
|
|
|
623
|
-
env.define("jarak", makeBuiltInFunc([angkaSymbol, angkaSymbol], barisSymbol,
|
|
624
|
-
(
|
|
625
|
-
Array(Math.abs(Math.floor(to.data-from.data)))
|
|
605
|
+
env.define("jarak", makeBuiltInFunc([angkaSymbol, angkaSymbol], barisSymbol,
|
|
606
|
+
(_, [from, to]) => new Value(barisSymbol,
|
|
607
|
+
Array(Math.abs(Math.floor(to.data - from.data)))
|
|
626
608
|
.fill(0)
|
|
627
|
-
.map((
|
|
609
|
+
.map((_, idx) => new Value(angkaSymbol, from.data + ((to.data > from.data) ? 1 : -1) * idx))))
|
|
628
610
|
);
|
|
629
611
|
|
|
630
|
-
env.define("nihil?", makeBuiltInFunc([null], logisSymbol, (
|
|
631
|
-
env.define("ukuran", makeBuiltInFunc([null], angkaSymbol,(
|
|
612
|
+
env.define("nihil?", makeBuiltInFunc([null], logisSymbol, (_, [d]) => new Value(logisSymbol, d.data === null)));
|
|
613
|
+
env.define("ukuran", makeBuiltInFunc([null], angkaSymbol, (_, [d]) => d.type === barisSymbol || d.type === petikSymbol
|
|
632
614
|
? new Value(angkaSymbol, d.data.length)
|
|
633
615
|
: v.error(`Ukuran hanya terdapat untuk tipe petik atau baris. Menemukan tipe ${d.type.description}.`)
|
|
634
616
|
));
|
|
635
617
|
|
|
636
|
-
env.define("salin", makeBuiltInFunc([null], null, (
|
|
637
|
-
env.define("tipe", makeBuiltInFunc([null], petikSymbol, (
|
|
618
|
+
env.define("salin", makeBuiltInFunc([null], null, (_, [d]) => copier(d)));
|
|
619
|
+
env.define("tipe", makeBuiltInFunc([null], petikSymbol, (_, [d]) => (d.type?.description)
|
|
638
620
|
? new Value(petikSymbol, d.type.description)
|
|
639
621
|
: new Value(petikSymbol, "datum")
|
|
640
622
|
));
|
|
623
|
+
|
|
624
|
+
let mtkModul = new Variable(modulSymbol, true, null);
|
|
625
|
+
mtkModul.member = new Environment();
|
|
626
|
+
mtkModul.member.define("acak", makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (_, [a, b]) => {
|
|
627
|
+
let width = Math.abs(a.data - b.data);
|
|
628
|
+
let range = Math.random() * width;
|
|
629
|
+
let final = range + Math.min(a.data, b.data);
|
|
630
|
+
return new Value(angkaSymbol, final);
|
|
631
|
+
}));
|
|
632
|
+
mtkModul.member.define("akar2", makeBuiltInFunc([angkaSymbol], angkaSymbol, (_, [a]) => {
|
|
633
|
+
return new Value(angkaSymbol, Math.sqrt(a.data));
|
|
634
|
+
}));
|
|
635
|
+
|
|
636
|
+
env.define("mtk", mtkModul);
|
|
641
637
|
return env;
|
|
642
638
|
})();
|
|
643
639
|
|
|
644
|
-
let Henti$1 = class Henti {};
|
|
645
|
-
let Lewat$1 = class Lewat {};
|
|
640
|
+
let Henti$1 = class Henti { };
|
|
641
|
+
let Lewat$1 = class Lewat { };
|
|
646
642
|
let Hasil$1 = class Hasil {
|
|
647
643
|
constructor(value) {
|
|
648
644
|
this.value = value;
|
|
@@ -655,7 +651,6 @@ const location = {
|
|
|
655
651
|
UNTUK: 3,
|
|
656
652
|
MESIN: 4,
|
|
657
653
|
};
|
|
658
|
-
const MAX_LOOP_ALLOWED = 1000000;
|
|
659
654
|
const MAX_STACK_SIZE = 500;
|
|
660
655
|
|
|
661
656
|
// Implements all Expressions and Statements Visitor
|
|
@@ -668,6 +663,8 @@ class Interpreter {
|
|
|
668
663
|
this.state = location.GLOBAL;
|
|
669
664
|
this.stack = [];
|
|
670
665
|
this.output = [];
|
|
666
|
+
this.objectStack = null;
|
|
667
|
+
this.exprWillBeCalled = false;
|
|
671
668
|
}
|
|
672
669
|
|
|
673
670
|
// EXPRESSION VISITORS
|
|
@@ -675,15 +672,15 @@ class Interpreter {
|
|
|
675
672
|
visitLiteralExpr(literalExpr) {
|
|
676
673
|
let lit = literalExpr.token;
|
|
677
674
|
this.line = lit.line;
|
|
678
|
-
switch(typeof lit.value) {
|
|
679
|
-
case "string":
|
|
675
|
+
switch (typeof lit.value) {
|
|
676
|
+
case "string":
|
|
680
677
|
return new Value(petikSymbol, lit.value);
|
|
681
|
-
case "number":
|
|
678
|
+
case "number":
|
|
682
679
|
return new Value(angkaSymbol, lit.value);
|
|
683
680
|
case "boolean":
|
|
684
681
|
return new Value(logisSymbol, lit.value);
|
|
685
682
|
default:
|
|
686
|
-
return
|
|
683
|
+
return NIHIL;
|
|
687
684
|
}
|
|
688
685
|
}
|
|
689
686
|
|
|
@@ -691,11 +688,8 @@ class Interpreter {
|
|
|
691
688
|
let value = new Value(barisSymbol, []);
|
|
692
689
|
|
|
693
690
|
for (let expr of arrayExpr.contents) {
|
|
694
|
-
let v = this.
|
|
695
|
-
|
|
696
|
-
let result = new Value(v.type, v.data);
|
|
697
|
-
result.member = v.member;
|
|
698
|
-
value.data.push(new Value(v.type, v.data));
|
|
691
|
+
let v = this.validValue(expr.accept(this));
|
|
692
|
+
value.data.push(v);
|
|
699
693
|
}
|
|
700
694
|
|
|
701
695
|
return value;
|
|
@@ -705,7 +699,7 @@ class Interpreter {
|
|
|
705
699
|
// a real TODO would've been to implement real iterables
|
|
706
700
|
let iterable = indexExpr.iterable.accept(this);
|
|
707
701
|
if (iterable.type === petikSymbol) {
|
|
708
|
-
iterable = new Value(barisSymbol, iterable.data.split("").map(str=>new Value(petikSymbol, str)));
|
|
702
|
+
iterable = new Value(barisSymbol, iterable.data.split("").map(str => new Value(petikSymbol, str)));
|
|
709
703
|
}
|
|
710
704
|
if (iterable.type !== barisSymbol) {
|
|
711
705
|
this.error(`'${iterable.type.description}' bukan baris/petik, tidak bisa di-indeks`);
|
|
@@ -726,11 +720,21 @@ class Interpreter {
|
|
|
726
720
|
|
|
727
721
|
visitLambdaExpr(lambdaExpr) {
|
|
728
722
|
// let type = lambdaExpr.returnValue.accept(this);
|
|
729
|
-
let params = lambdaExpr.params.map(val=>[val[0].accept(this), val[0].tetap, val[1].lexeme]);
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
723
|
+
let params = lambdaExpr.params.map(val => [val[0].accept(this), val[0].tetap, val[1].lexeme]);
|
|
724
|
+
|
|
725
|
+
const check_duplicate_name = (p) => {
|
|
726
|
+
let mapped = new Map();
|
|
727
|
+
for (let name of p) {
|
|
728
|
+
if (mapped.has(name)) return true;
|
|
729
|
+
mapped.set(name, true);
|
|
733
730
|
}
|
|
731
|
+
return false;
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
if (params.length > 1
|
|
735
|
+
&& check_duplicate_name(params.map(p => p[2]))) {
|
|
736
|
+
this.error("Parameter mesin tidak boleh mempunyai nama yang sama.");
|
|
737
|
+
}
|
|
734
738
|
let retType = null;
|
|
735
739
|
if (lambdaExpr.returnType) {
|
|
736
740
|
retType = lambdaExpr.returnType.accept(this);
|
|
@@ -740,29 +744,105 @@ class Interpreter {
|
|
|
740
744
|
}
|
|
741
745
|
|
|
742
746
|
visitCallExpr(callExpr) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
this.error(`Rekursi melebihi batas: Lebih dari ${MAX_STACK_SIZE}`);
|
|
746
|
-
this.state = location.MESIN;
|
|
747
|
+
|
|
748
|
+
this.exprWillBeCalled = true;
|
|
747
749
|
let callable = callExpr.callable.accept(this);
|
|
750
|
+
this.exprWillBeCalled = false;
|
|
748
751
|
|
|
749
752
|
if (callable.type !== mesinSymbol && callable.type !== stipeSymbol) {
|
|
750
753
|
this.error(`Hanya bisa 'memanggil' mesin atau model, malah menemukan ${callable.type.description}. `);
|
|
751
754
|
}
|
|
752
755
|
|
|
753
|
-
if (!callable.data?.
|
|
756
|
+
if (!callable.data?.block) {
|
|
754
757
|
this.error(`Mesin tidak terdefinisi, tidak bisa dipanggil.`);
|
|
755
758
|
}
|
|
756
|
-
|
|
757
|
-
let
|
|
759
|
+
|
|
760
|
+
let args = [];
|
|
761
|
+
|
|
762
|
+
if (this.objectStack) {
|
|
763
|
+
args = [this.objectStack];
|
|
764
|
+
this.objectStack = null;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
args = [...args, ...callExpr.args.map(val => this.validValue(val.accept(this)))];
|
|
768
|
+
|
|
769
|
+
let result = this.callFunc(callable.data, args);
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
callFunc(callable, args) {
|
|
774
|
+
// args.forEach((val,idx)=>{
|
|
775
|
+
// console.log(idx, val);
|
|
776
|
+
// });
|
|
777
|
+
this.stack.push(this.line);
|
|
778
|
+
if (this.stack.length > MAX_STACK_SIZE)
|
|
779
|
+
this.error(`Rekursi melebihi batas: Lebih dari ${MAX_STACK_SIZE}`);
|
|
780
|
+
|
|
781
|
+
let prevState = this.state;
|
|
782
|
+
this.line = this.stack[this.stack.length - 1];
|
|
783
|
+
this.state = location.MESIN;
|
|
784
|
+
|
|
785
|
+
if (args.length !== callable.parameters.length) {
|
|
786
|
+
this.error(`Jumlah argumen tidak sama dengan parameter mesin. Menemukan ${args.length}, harusnya ${callable.parameters.length}.`);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// for asserting _call_ error.
|
|
790
|
+
let callLineNum = this.line;
|
|
791
|
+
|
|
792
|
+
for (let i = 0; i < args.length; i++) {
|
|
793
|
+
let type = callable.parameters[i][0];
|
|
794
|
+
|
|
795
|
+
if (type === null) continue;
|
|
796
|
+
if (args[i].type !== type) {
|
|
797
|
+
this.line = callLineNum;
|
|
798
|
+
this.error(`Tipe argumen tidak sama dengan parameter. Menemukan ${args[i].type.description}, harusnya ${type.description}`);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (callable.isBuiltIn) {
|
|
803
|
+
this.stack.pop();
|
|
804
|
+
this.state = prevState;
|
|
805
|
+
return callable.block(this, args);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
let visitorEnv = this.environment;
|
|
809
|
+
let funcEnv = new Environment(callable.closure);
|
|
810
|
+
this.environment = funcEnv;
|
|
811
|
+
|
|
812
|
+
for (let i = 0; i < args.length; i++) {
|
|
813
|
+
let [type, tetap, name] = callable.parameters[i];
|
|
814
|
+
if (type === null) {
|
|
815
|
+
type = args[i].type;
|
|
816
|
+
}
|
|
817
|
+
let variable = new Variable(type, tetap, args[i].data);
|
|
818
|
+
variable.member = args[i].member;
|
|
819
|
+
|
|
820
|
+
this.environment.define(name, variable);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
let result = NIHIL;
|
|
824
|
+
for (let stmt of callable.block.statements) {
|
|
825
|
+
try {
|
|
826
|
+
stmt.accept(this);
|
|
827
|
+
} catch (err) {
|
|
828
|
+
if (err instanceof Hasil$1) {
|
|
829
|
+
result = err.value;
|
|
830
|
+
break;
|
|
831
|
+
} else if (err instanceof Henti$1 || err instanceof Lewat$1) {
|
|
832
|
+
this.error("Tidak bisa menghentikan atau melewatkan mesin. ");
|
|
833
|
+
} else throw err;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
758
836
|
this.stack.pop();
|
|
759
|
-
|
|
837
|
+
this.state = prevState;
|
|
838
|
+
this.environment = visitorEnv;
|
|
760
839
|
return result;
|
|
761
840
|
}
|
|
762
841
|
|
|
842
|
+
|
|
763
843
|
visitBinaryExpr(binaryExpr) {
|
|
764
|
-
let leftValue = this.
|
|
765
|
-
let rightValue = this.
|
|
844
|
+
let leftValue = this.validValue(binaryExpr.left.accept(this));
|
|
845
|
+
let rightValue = this.validValue(binaryExpr.right.accept(this));
|
|
766
846
|
|
|
767
847
|
this.line = binaryExpr.op.line;
|
|
768
848
|
|
|
@@ -779,7 +859,7 @@ class Interpreter {
|
|
|
779
859
|
}
|
|
780
860
|
|
|
781
861
|
visitUnaryExpr(unaryExpr) {
|
|
782
|
-
let rightValue = this.
|
|
862
|
+
let rightValue = this.validValue(unaryExpr.right.accept(this));
|
|
783
863
|
|
|
784
864
|
this.line = unaryExpr.op.line;
|
|
785
865
|
|
|
@@ -788,33 +868,41 @@ class Interpreter {
|
|
|
788
868
|
this.error(`Tidak bisa mengoperasikan nilai Nihil.`);
|
|
789
869
|
}
|
|
790
870
|
|
|
791
|
-
|
|
871
|
+
let result = this.environment.get(rightValue.type.description) // get Model
|
|
792
872
|
.operate(this, unaryExpr.op.type, rightValue); // dispatch the operation
|
|
793
873
|
|
|
794
874
|
return result;
|
|
795
875
|
}
|
|
796
876
|
|
|
797
877
|
visitGroupingExpr(groupingExpr) {
|
|
798
|
-
return groupingExpr.expr.accept(this);
|
|
878
|
+
return this.validValue(groupingExpr.expr.accept(this));
|
|
799
879
|
}
|
|
800
880
|
|
|
801
881
|
visitIdentifierExpr(identifierExpr) {
|
|
802
|
-
let value =
|
|
882
|
+
let value = this.environment.get(identifierExpr.token.lexeme);
|
|
803
883
|
this.line = identifierExpr.token.line;
|
|
804
884
|
if (!value) this.error(`'${identifierExpr.token.lexeme}' tidak dapat ditemukan.`);
|
|
805
885
|
return value;
|
|
806
886
|
}
|
|
807
887
|
|
|
808
888
|
visitMemberExpr(memberExpr) {
|
|
889
|
+
let willBeCalled = this.exprWillBeCalled;
|
|
890
|
+
this.exprWillBeCalled = false;
|
|
809
891
|
let main = memberExpr.main.accept(this);
|
|
810
892
|
let name = memberExpr.member.token.lexeme;
|
|
811
893
|
this.line = memberExpr.member.token.line;
|
|
812
|
-
if (!main.member) {
|
|
813
|
-
this.
|
|
814
|
-
|
|
815
|
-
|
|
894
|
+
if (!main.member || !main.member.has(name)) {
|
|
895
|
+
const type = this.environment.get(main.type.description);
|
|
896
|
+
if (type.member?.has(name)) {
|
|
897
|
+
if (willBeCalled) {
|
|
898
|
+
this.objectStack = main;
|
|
899
|
+
}
|
|
900
|
+
return type.member.get(name);
|
|
901
|
+
}
|
|
902
|
+
this.error(`nama .${name} tidak ditemukan dalam tipe ${main.type.description}`);
|
|
903
|
+
} else {
|
|
904
|
+
return main.member.get(name);
|
|
816
905
|
}
|
|
817
|
-
return main.member.get(name);
|
|
818
906
|
}
|
|
819
907
|
|
|
820
908
|
// STATEMENT VISITORS
|
|
@@ -822,7 +910,7 @@ class Interpreter {
|
|
|
822
910
|
// this is needed for interpreting generics TODO!
|
|
823
911
|
visitTypeStmt(typeStmt) {
|
|
824
912
|
if (typeStmt.type === null) return null;
|
|
825
|
-
let type =
|
|
913
|
+
let type = typeStmt.type.accept(this);
|
|
826
914
|
if (type.type !== stipeSymbol) this.error(`'${type.type.description}' bukan sebuah Model/Tipe Valid.`);
|
|
827
915
|
return type.symbol;
|
|
828
916
|
}
|
|
@@ -833,18 +921,31 @@ class Interpreter {
|
|
|
833
921
|
if (thing.type === logisSymbol) {
|
|
834
922
|
return thing.data ? "benar" : "salah";
|
|
835
923
|
} else if (thing.type === barisSymbol) {
|
|
836
|
-
return '[' + thing.data.reduce((str, val)=>
|
|
924
|
+
return '[' + thing.data.reduce((str, val) => {
|
|
925
|
+
let item = kePetik(val);
|
|
926
|
+
return str + ", " + (val.type === petikSymbol ? `"${item}"` : item);
|
|
927
|
+
}, "").slice(1) + ' ]';
|
|
837
928
|
} else if (thing.type === stipeSymbol) {
|
|
838
929
|
return `Model<${thing.symbol.description}>`;
|
|
839
930
|
} else if (thing.type === mesinSymbol) {
|
|
840
931
|
let underlying = thing.data.returnType?.description;
|
|
841
|
-
return `Mesin<${underlying? underlying : 'datum'}>`;
|
|
932
|
+
return `Mesin<${underlying ? underlying : 'datum'}>`;
|
|
842
933
|
} else if (thing.type === angkaSymbol) {
|
|
843
934
|
return thing.data.toString();
|
|
844
935
|
} else if (thing.type === petikSymbol) {
|
|
845
|
-
return
|
|
936
|
+
return thing.data;
|
|
846
937
|
} else {
|
|
847
938
|
if (!thing?.type) return `nihil`;
|
|
939
|
+
let type = this.environment.get(thing.type.description);
|
|
940
|
+
if (type?.member?.has("kePetik")) {
|
|
941
|
+
let prevState = this.state;
|
|
942
|
+
this.state = location.MESIN;
|
|
943
|
+
this.stack.push(this.line);
|
|
944
|
+
let res = this.callFunc(type.member.get("kePetik").data, [thing]).data;
|
|
945
|
+
this.stack.pop();
|
|
946
|
+
this.state = prevState;
|
|
947
|
+
return res;
|
|
948
|
+
}
|
|
848
949
|
return `${thing.type.description}<>`;
|
|
849
950
|
}
|
|
850
951
|
};
|
|
@@ -864,7 +965,7 @@ class Interpreter {
|
|
|
864
965
|
|
|
865
966
|
let variable = new Variable(type, datumStmt.type.tetap);
|
|
866
967
|
|
|
867
|
-
let value = this.
|
|
968
|
+
let value = this.validValue(datumStmt.expr.accept(this));
|
|
868
969
|
|
|
869
970
|
if (value.data === null) value.type = variable.type; // if nihil, ok
|
|
870
971
|
|
|
@@ -873,7 +974,7 @@ class Interpreter {
|
|
|
873
974
|
variable.type = value.type;
|
|
874
975
|
} else this.typeCheck(variable, value, `Pada pembuatan variabel '${name}'`);
|
|
875
976
|
|
|
876
|
-
variable.data = value.data;
|
|
977
|
+
variable.data = value.data;
|
|
877
978
|
variable.member = value.member;
|
|
878
979
|
|
|
879
980
|
this.environment.define(name, variable);
|
|
@@ -884,7 +985,7 @@ class Interpreter {
|
|
|
884
985
|
if (variable.tetap) {
|
|
885
986
|
this.error(`Variabel tetap tidak dapat di-rubah.`);
|
|
886
987
|
}
|
|
887
|
-
let value = this.
|
|
988
|
+
let value = this.validValue(rubahStmt.value.accept(this));
|
|
888
989
|
if (value.data === null) value.type = variable.type; // if nihil, ok
|
|
889
990
|
|
|
890
991
|
if (variable.isDatum)
|
|
@@ -912,7 +1013,10 @@ class Interpreter {
|
|
|
912
1013
|
visitKalauStmt(kalauStmt) {
|
|
913
1014
|
// condition may be null for 'namun', accept the thenBlock if it is
|
|
914
1015
|
let condition = kalauStmt.condition?.accept(this);
|
|
915
|
-
if (condition
|
|
1016
|
+
if (condition.type !== logisSymbol) {
|
|
1017
|
+
this.error(`Ekspresi dalam 'kalau' harus bertipe logis, menemukan ${condition.type ? condition.type.description : "nihil"}`);
|
|
1018
|
+
}
|
|
1019
|
+
if (condition === null || condition === undefined || condition.data) {
|
|
916
1020
|
kalauStmt.thenBlock.accept(this);
|
|
917
1021
|
} else {
|
|
918
1022
|
kalauStmt.elseKalau?.accept(this); // kalau may not have namun
|
|
@@ -920,14 +1024,14 @@ class Interpreter {
|
|
|
920
1024
|
}
|
|
921
1025
|
|
|
922
1026
|
visitHentiStmt() {
|
|
923
|
-
if (this.state
|
|
1027
|
+
if (this.state === location.UNTUK || this.state === location.SLAGI)
|
|
924
1028
|
throw new Henti$1(); // throws exception to escape from deep recursion
|
|
925
1029
|
else this.error("Tidak ada pengulangan untuk dihentikan.");
|
|
926
1030
|
}
|
|
927
1031
|
|
|
928
|
-
|
|
1032
|
+
|
|
929
1033
|
visitLewatStmt() {
|
|
930
|
-
if (this.state
|
|
1034
|
+
if (this.state === location.UNTUK || this.state === location.SLAGI)
|
|
931
1035
|
throw new Lewat$1(); // throws exception to escape from deep recursion
|
|
932
1036
|
else this.error("Tidak ada pengulangan untuk dilewatkan.");
|
|
933
1037
|
}
|
|
@@ -940,18 +1044,19 @@ class Interpreter {
|
|
|
940
1044
|
}
|
|
941
1045
|
|
|
942
1046
|
visitSlagiStmt(slagiStmt) {
|
|
943
|
-
const checkTruthy = (v) =>
|
|
1047
|
+
const checkTruthy = (v) => {
|
|
1048
|
+
if (v.type !== logisSymbol) {
|
|
1049
|
+
this.error(`Ekspresi dalam 'slagi' harus bertipe 'logis', menemukan ${v.type ? v.type.description : "nihil" }`);
|
|
1050
|
+
}
|
|
1051
|
+
return v.data;
|
|
1052
|
+
};
|
|
944
1053
|
let lastEnv = this.environment;
|
|
945
|
-
let howManyLoop = 0;
|
|
946
1054
|
while (checkTruthy(slagiStmt.condition.accept(this))) {
|
|
947
|
-
howManyLoop++;
|
|
948
|
-
if (howManyLoop > MAX_LOOP_ALLOWED)
|
|
949
|
-
this.error(`Pengulangan melebihi batas: lebih dari ${MAX_LOOP_ALLOWED}.`);
|
|
950
1055
|
this.environment = new Environment(lastEnv);
|
|
951
1056
|
try {
|
|
952
1057
|
for (let stmt of slagiStmt.block.statements) {
|
|
953
1058
|
this.state = location.SLAGI;
|
|
954
|
-
stmt.accept(this);
|
|
1059
|
+
stmt.accept(this);
|
|
955
1060
|
this.state = location.SLAGI;
|
|
956
1061
|
}
|
|
957
1062
|
} catch (err) {
|
|
@@ -970,10 +1075,10 @@ class Interpreter {
|
|
|
970
1075
|
visitUntukStmt(untukStmt) {
|
|
971
1076
|
let name = this.validName(untukStmt.varName.lexeme, false);
|
|
972
1077
|
let type = untukStmt.varType.accept(this);
|
|
973
|
-
|
|
974
|
-
let iter = untukStmt.iterable.accept(this);
|
|
1078
|
+
|
|
1079
|
+
let iter = this.validValue(untukStmt.iterable.accept(this));
|
|
975
1080
|
if (iter.type === petikSymbol) {
|
|
976
|
-
iter = new Value(barisSymbol, iter.data.split("").map(str=>new Value(petikSymbol, str)));
|
|
1081
|
+
iter = new Value(barisSymbol, iter.data.split("").map(str => new Value(petikSymbol, str)));
|
|
977
1082
|
}
|
|
978
1083
|
if (iter.type !== barisSymbol) {
|
|
979
1084
|
this.error(`Pernyataan 'untuk' harus mengiterasi sebuah baris atau petik, bukan ${iter.type.description}`);
|
|
@@ -993,7 +1098,7 @@ class Interpreter {
|
|
|
993
1098
|
// didn't 'accept' the block, just uses it directly
|
|
994
1099
|
for (let stmt of untukStmt.block.statements) {
|
|
995
1100
|
this.state = location.UNTUK;
|
|
996
|
-
stmt.accept(this);
|
|
1101
|
+
stmt.accept(this);
|
|
997
1102
|
this.state = location.UNTUK;
|
|
998
1103
|
}
|
|
999
1104
|
} catch (err) {
|
|
@@ -1046,8 +1151,7 @@ class Interpreter {
|
|
|
1046
1151
|
}
|
|
1047
1152
|
|
|
1048
1153
|
let lastEnv = this.environment;
|
|
1049
|
-
this.environment = new Environment(this.
|
|
1050
|
-
if (variable) this.environment.define(name, variable);
|
|
1154
|
+
this.environment = new Environment(this.environment);
|
|
1051
1155
|
for (let stmt of modulStmt.statements) {
|
|
1052
1156
|
stmt.accept(this);
|
|
1053
1157
|
}
|
|
@@ -1078,13 +1182,13 @@ class Interpreter {
|
|
|
1078
1182
|
return false;
|
|
1079
1183
|
}
|
|
1080
1184
|
|
|
1081
|
-
|
|
1185
|
+
validValue(v) {
|
|
1082
1186
|
if (v.type !== stipeSymbol) return v;
|
|
1083
1187
|
this.error("stipe tidak dapat menjadi nilai.");
|
|
1084
1188
|
}
|
|
1085
1189
|
|
|
1086
1190
|
validName(n, checkExisted = true) {
|
|
1087
|
-
if (RESERVED_NAMES.some(v=>v===n)) {
|
|
1191
|
+
if (RESERVED_NAMES.some(v => v === n)) {
|
|
1088
1192
|
this.error(`Nama sistem (${n}) tidak boleh didefinisi ulang.`);
|
|
1089
1193
|
} else if (checkExisted && this.environment.has(n)) {
|
|
1090
1194
|
this.error(`Variabel dengan nama '${n}' sudah ada. Tidak bisa didefinisi ulang.`);
|
|
@@ -1093,7 +1197,7 @@ class Interpreter {
|
|
|
1093
1197
|
}
|
|
1094
1198
|
|
|
1095
1199
|
error(message) {
|
|
1096
|
-
throw new SimplErrorEksekusi(`Error Eksekusi => ${message}`, this.line);
|
|
1200
|
+
throw new SimplErrorEksekusi(`Error Eksekusi => ${message}`, this.line, this.output.join('\n'));
|
|
1097
1201
|
}
|
|
1098
1202
|
|
|
1099
1203
|
interpret(tree) {
|
|
@@ -1148,7 +1252,7 @@ class Lexer {
|
|
|
1148
1252
|
}
|
|
1149
1253
|
|
|
1150
1254
|
peek(num = 1) {
|
|
1151
|
-
return this.isAtEnd() ? null : this.text[this.charIndex+num];
|
|
1255
|
+
return this.isAtEnd() ? null : this.text[this.charIndex + num];
|
|
1152
1256
|
}
|
|
1153
1257
|
|
|
1154
1258
|
isAlpha(char) {
|
|
@@ -1185,23 +1289,23 @@ class Lexer {
|
|
|
1185
1289
|
if (token) tokens.push(token);
|
|
1186
1290
|
}
|
|
1187
1291
|
|
|
1188
|
-
tokens.push(new Token(EOF,
|
|
1292
|
+
tokens.push(new Token(EOF, "Akhir dokumen", null, this.lineIndex));
|
|
1189
1293
|
|
|
1190
1294
|
this.tokens = tokens;
|
|
1191
1295
|
return this.tokens;
|
|
1192
1296
|
}
|
|
1193
1297
|
|
|
1194
1298
|
id() {
|
|
1195
|
-
while(!this.isAtEnd() && this.isAlphaNumeric(this.see())) this.advance();
|
|
1196
|
-
|
|
1299
|
+
while (!this.isAtEnd() && this.isAlphaNumeric(this.see())) this.advance();
|
|
1300
|
+
|
|
1197
1301
|
let lexeme = this.parseLexeme();
|
|
1198
1302
|
|
|
1199
|
-
let reservedIndex = RESERVED_KEYWORDS.findIndex((val)=>val===lexeme);
|
|
1303
|
+
let reservedIndex = RESERVED_KEYWORDS.findIndex((val) => val === lexeme);
|
|
1200
1304
|
if (reservedIndex != -1) {
|
|
1201
1305
|
return new Token(reservedIndex, lexeme, null, this.lineIndex);
|
|
1202
1306
|
}
|
|
1203
1307
|
|
|
1204
|
-
let isLogis = ["benar", "salah"].some((val)=>val===lexeme);
|
|
1308
|
+
let isLogis = ["benar", "salah"].some((val) => val === lexeme);
|
|
1205
1309
|
if (isLogis) {
|
|
1206
1310
|
return new Token(LITERAL, lexeme, "benar" === lexeme ? true : false, this.lineIndex);
|
|
1207
1311
|
}
|
|
@@ -1211,7 +1315,7 @@ class Lexer {
|
|
|
1211
1315
|
}
|
|
1212
1316
|
|
|
1213
1317
|
return new Token(
|
|
1214
|
-
ID,
|
|
1318
|
+
ID,
|
|
1215
1319
|
lexeme,
|
|
1216
1320
|
null,
|
|
1217
1321
|
this.lineIndex
|
|
@@ -1220,7 +1324,7 @@ class Lexer {
|
|
|
1220
1324
|
|
|
1221
1325
|
number() {
|
|
1222
1326
|
let isFloat = false;
|
|
1223
|
-
while(!this.isAtEnd() && this.isNumeric(this.see())) {
|
|
1327
|
+
while (!this.isAtEnd() && this.isNumeric(this.see())) {
|
|
1224
1328
|
this.advance();
|
|
1225
1329
|
if (this.see() === '.') {
|
|
1226
1330
|
if (isFloat) break;
|
|
@@ -1234,15 +1338,15 @@ class Lexer {
|
|
|
1234
1338
|
|
|
1235
1339
|
string() {
|
|
1236
1340
|
this.advance();
|
|
1237
|
-
while(!this.isAtEnd() && this.see() !== '"') this.advance();
|
|
1341
|
+
while (!this.isAtEnd() && this.see() !== '"') this.advance();
|
|
1238
1342
|
if (this.isAtEnd()) this.error("Petik tidak tertutup.");
|
|
1239
1343
|
this.advance();
|
|
1240
1344
|
let lexeme = this.parseLexeme();
|
|
1241
|
-
return new Token(LITERAL, lexeme, lexeme.slice(1, lexeme.length-1), this.lineIndex);
|
|
1345
|
+
return new Token(LITERAL, lexeme, lexeme.slice(1, lexeme.length - 1), this.lineIndex);
|
|
1242
1346
|
}
|
|
1243
1347
|
|
|
1244
1348
|
comment() {
|
|
1245
|
-
while(!this.isAtEnd() && this.see() !== '\n') {
|
|
1349
|
+
while (!this.isAtEnd() && this.see() !== '\n') {
|
|
1246
1350
|
this.advance();
|
|
1247
1351
|
this.charStart++;
|
|
1248
1352
|
}
|
|
@@ -1263,10 +1367,10 @@ class Lexer {
|
|
|
1263
1367
|
}
|
|
1264
1368
|
|
|
1265
1369
|
|
|
1266
|
-
if(this.isAlpha(this.see())) return this.id();
|
|
1267
|
-
if(this.isNumeric(this.see())) return this.number();
|
|
1370
|
+
if (this.isAlpha(this.see())) return this.id();
|
|
1371
|
+
if (this.isNumeric(this.see())) return this.number();
|
|
1268
1372
|
|
|
1269
|
-
switch(this.see()) {
|
|
1373
|
+
switch (this.see()) {
|
|
1270
1374
|
case "+": return this.makeToken(PLUS);
|
|
1271
1375
|
case "-": return this.makeToken(MINUS);
|
|
1272
1376
|
case "/": return this.makeToken(SLASH);
|
|
@@ -1282,27 +1386,27 @@ class Lexer {
|
|
|
1282
1386
|
case "|": return this.makeToken(PIPE);
|
|
1283
1387
|
case "&": return this.makeToken(AMPERSAND);
|
|
1284
1388
|
case "%": return this.makeToken(MODULUS);
|
|
1285
|
-
case "!":
|
|
1389
|
+
case "!":
|
|
1286
1390
|
if (this.peek() === "=") {
|
|
1287
1391
|
this.advance();
|
|
1288
1392
|
return this.makeToken(BANG_EQUAL);
|
|
1289
1393
|
}
|
|
1290
1394
|
return this.makeToken(BANG);
|
|
1291
1395
|
|
|
1292
|
-
case ">":
|
|
1396
|
+
case ">":
|
|
1293
1397
|
if (this.peek() === "=") {
|
|
1294
1398
|
this.advance();
|
|
1295
|
-
return this.makeToken(GREATER_EQUAL)
|
|
1399
|
+
return this.makeToken(GREATER_EQUAL)
|
|
1296
1400
|
}
|
|
1297
1401
|
return this.makeToken(GREATER);
|
|
1298
|
-
case "<":
|
|
1402
|
+
case "<":
|
|
1299
1403
|
if (this.peek() === "=") {
|
|
1300
1404
|
this.advance();
|
|
1301
1405
|
return this.makeToken(LESS_EQUAL)
|
|
1302
1406
|
}
|
|
1303
1407
|
return this.makeToken(LESS);
|
|
1304
1408
|
|
|
1305
|
-
case "=":
|
|
1409
|
+
case "=":
|
|
1306
1410
|
if (this.peek() === "=" && this.peek(2) === ">") {
|
|
1307
1411
|
return this.makeToken(EQUAL);
|
|
1308
1412
|
} else if (this.peek() === "=") {
|
|
@@ -1339,8 +1443,8 @@ class ExprBase {
|
|
|
1339
1443
|
}
|
|
1340
1444
|
|
|
1341
1445
|
class Binary extends ExprBase {
|
|
1342
|
-
|
|
1343
|
-
constructor
|
|
1446
|
+
// Expr.ExprBase left, Token op, Expr.ExprBase right
|
|
1447
|
+
constructor(left, op, right) {
|
|
1344
1448
|
super();
|
|
1345
1449
|
this.left = left;
|
|
1346
1450
|
this.op = op;
|
|
@@ -1353,8 +1457,8 @@ class Binary extends ExprBase {
|
|
|
1353
1457
|
}
|
|
1354
1458
|
|
|
1355
1459
|
class Unary extends ExprBase {
|
|
1356
|
-
|
|
1357
|
-
constructor
|
|
1460
|
+
// Token op, Expr.ExprBase right
|
|
1461
|
+
constructor(op, right) {
|
|
1358
1462
|
super();
|
|
1359
1463
|
this.op = op;
|
|
1360
1464
|
this.right = right;
|
|
@@ -1366,8 +1470,8 @@ class Unary extends ExprBase {
|
|
|
1366
1470
|
}
|
|
1367
1471
|
|
|
1368
1472
|
class Literal extends ExprBase {
|
|
1369
|
-
|
|
1370
|
-
constructor
|
|
1473
|
+
// Token token
|
|
1474
|
+
constructor(token) {
|
|
1371
1475
|
super();
|
|
1372
1476
|
this.token = token;
|
|
1373
1477
|
}
|
|
@@ -1378,8 +1482,8 @@ class Literal extends ExprBase {
|
|
|
1378
1482
|
}
|
|
1379
1483
|
|
|
1380
1484
|
class Grouping extends ExprBase {
|
|
1381
|
-
|
|
1382
|
-
constructor
|
|
1485
|
+
// Expr.ExprBase expr
|
|
1486
|
+
constructor(expr) {
|
|
1383
1487
|
super();
|
|
1384
1488
|
this.expr = expr;
|
|
1385
1489
|
}
|
|
@@ -1390,8 +1494,8 @@ class Grouping extends ExprBase {
|
|
|
1390
1494
|
}
|
|
1391
1495
|
|
|
1392
1496
|
class Member extends ExprBase {
|
|
1393
|
-
|
|
1394
|
-
constructor
|
|
1497
|
+
// Expr.ExprBase main, Expr.Identifier member
|
|
1498
|
+
constructor(main, member) {
|
|
1395
1499
|
super();
|
|
1396
1500
|
this.main = main;
|
|
1397
1501
|
this.member = member;
|
|
@@ -1403,8 +1507,8 @@ class Member extends ExprBase {
|
|
|
1403
1507
|
}
|
|
1404
1508
|
|
|
1405
1509
|
class Identifier extends ExprBase {
|
|
1406
|
-
|
|
1407
|
-
constructor
|
|
1510
|
+
// Token token
|
|
1511
|
+
constructor(token) {
|
|
1408
1512
|
super();
|
|
1409
1513
|
this.token = token;
|
|
1410
1514
|
}
|
|
@@ -1415,8 +1519,8 @@ class Identifier extends ExprBase {
|
|
|
1415
1519
|
}
|
|
1416
1520
|
|
|
1417
1521
|
class Lambda extends ExprBase {
|
|
1418
|
-
|
|
1419
|
-
constructor
|
|
1522
|
+
// Array<Stmt.Types-Token> params, Stmt.Type returnType, Stmt.Block block
|
|
1523
|
+
constructor(params, returnType, block) {
|
|
1420
1524
|
super();
|
|
1421
1525
|
this.params = params;
|
|
1422
1526
|
this.returnType = returnType;
|
|
@@ -1429,8 +1533,8 @@ class Lambda extends ExprBase {
|
|
|
1429
1533
|
}
|
|
1430
1534
|
|
|
1431
1535
|
class Call extends ExprBase {
|
|
1432
|
-
|
|
1433
|
-
constructor
|
|
1536
|
+
// Expr.ExprBase callable, Expr.ExprBase args
|
|
1537
|
+
constructor(callable, args) {
|
|
1434
1538
|
super();
|
|
1435
1539
|
this.callable = callable;
|
|
1436
1540
|
this.args = args;
|
|
@@ -1442,8 +1546,8 @@ class Call extends ExprBase {
|
|
|
1442
1546
|
}
|
|
1443
1547
|
|
|
1444
1548
|
let Array$1 = class Array extends ExprBase {
|
|
1445
|
-
|
|
1446
|
-
constructor
|
|
1549
|
+
// Array<Expr.ExprBase> contents
|
|
1550
|
+
constructor(contents) {
|
|
1447
1551
|
super();
|
|
1448
1552
|
this.contents = contents;
|
|
1449
1553
|
}
|
|
@@ -1454,8 +1558,8 @@ let Array$1 = class Array extends ExprBase {
|
|
|
1454
1558
|
};
|
|
1455
1559
|
|
|
1456
1560
|
class Index extends ExprBase {
|
|
1457
|
-
|
|
1458
|
-
constructor
|
|
1561
|
+
// Expr.ExprBase iterable, Expr.ExprBase index
|
|
1562
|
+
constructor(iterable, index) {
|
|
1459
1563
|
super();
|
|
1460
1564
|
this.iterable = iterable;
|
|
1461
1565
|
this.index = index;
|
|
@@ -1473,8 +1577,8 @@ class StmtBase {
|
|
|
1473
1577
|
}
|
|
1474
1578
|
|
|
1475
1579
|
class Cetak extends StmtBase {
|
|
1476
|
-
|
|
1477
|
-
constructor
|
|
1580
|
+
// Expr.ExprBase expr
|
|
1581
|
+
constructor(expr) {
|
|
1478
1582
|
super();
|
|
1479
1583
|
this.expr = expr;
|
|
1480
1584
|
}
|
|
@@ -1485,8 +1589,8 @@ class Cetak extends StmtBase {
|
|
|
1485
1589
|
}
|
|
1486
1590
|
|
|
1487
1591
|
class Datum extends StmtBase {
|
|
1488
|
-
|
|
1489
|
-
constructor
|
|
1592
|
+
// Stmt.Type type, Token name, Expr.ExprBase expr
|
|
1593
|
+
constructor(type, name, expr) {
|
|
1490
1594
|
super();
|
|
1491
1595
|
this.type = type;
|
|
1492
1596
|
this.name = name;
|
|
@@ -1499,8 +1603,8 @@ class Datum extends StmtBase {
|
|
|
1499
1603
|
}
|
|
1500
1604
|
|
|
1501
1605
|
class Type extends StmtBase {
|
|
1502
|
-
|
|
1503
|
-
constructor
|
|
1606
|
+
// Expr.ExprBase type, bool tetap, Array<Stmt.Type> contents
|
|
1607
|
+
constructor(type, tetap, contents) {
|
|
1504
1608
|
super();
|
|
1505
1609
|
this.type = type;
|
|
1506
1610
|
this.tetap = tetap;
|
|
@@ -1513,8 +1617,8 @@ class Type extends StmtBase {
|
|
|
1513
1617
|
}
|
|
1514
1618
|
|
|
1515
1619
|
class Kerja extends StmtBase {
|
|
1516
|
-
|
|
1517
|
-
constructor
|
|
1620
|
+
// Expr.ExprBase expr
|
|
1621
|
+
constructor(expr) {
|
|
1518
1622
|
super();
|
|
1519
1623
|
this.expr = expr;
|
|
1520
1624
|
}
|
|
@@ -1525,8 +1629,8 @@ class Kerja extends StmtBase {
|
|
|
1525
1629
|
}
|
|
1526
1630
|
|
|
1527
1631
|
class Block extends StmtBase {
|
|
1528
|
-
|
|
1529
|
-
constructor
|
|
1632
|
+
// Array<Stmt.StmtBase> statements
|
|
1633
|
+
constructor(statements) {
|
|
1530
1634
|
super();
|
|
1531
1635
|
this.statements = statements;
|
|
1532
1636
|
}
|
|
@@ -1537,8 +1641,8 @@ class Block extends StmtBase {
|
|
|
1537
1641
|
}
|
|
1538
1642
|
|
|
1539
1643
|
class Kalau extends StmtBase {
|
|
1540
|
-
|
|
1541
|
-
constructor
|
|
1644
|
+
// Expr.ExprBase condition, Stmt.Block thenBlock, Stmt.Kalau elseKalau
|
|
1645
|
+
constructor(condition, thenBlock, elseKalau) {
|
|
1542
1646
|
super();
|
|
1543
1647
|
this.condition = condition;
|
|
1544
1648
|
this.thenBlock = thenBlock;
|
|
@@ -1551,8 +1655,8 @@ class Kalau extends StmtBase {
|
|
|
1551
1655
|
}
|
|
1552
1656
|
|
|
1553
1657
|
class Untuk extends StmtBase {
|
|
1554
|
-
|
|
1555
|
-
constructor
|
|
1658
|
+
// Stmt.Type varType, Token varName, Expr.Base iterable, Stmt.Block block
|
|
1659
|
+
constructor(varType, varName, iterable, block) {
|
|
1556
1660
|
super();
|
|
1557
1661
|
this.varType = varType;
|
|
1558
1662
|
this.varName = varName;
|
|
@@ -1566,8 +1670,8 @@ class Untuk extends StmtBase {
|
|
|
1566
1670
|
}
|
|
1567
1671
|
|
|
1568
1672
|
class Slagi extends StmtBase {
|
|
1569
|
-
|
|
1570
|
-
constructor
|
|
1673
|
+
// Expr.ExprBase condition, Stmt.Block block
|
|
1674
|
+
constructor(condition, block) {
|
|
1571
1675
|
super();
|
|
1572
1676
|
this.condition = condition;
|
|
1573
1677
|
this.block = block;
|
|
@@ -1579,7 +1683,7 @@ class Slagi extends StmtBase {
|
|
|
1579
1683
|
}
|
|
1580
1684
|
|
|
1581
1685
|
class Henti extends StmtBase {
|
|
1582
|
-
constructor
|
|
1686
|
+
constructor() {
|
|
1583
1687
|
super();
|
|
1584
1688
|
}
|
|
1585
1689
|
|
|
@@ -1589,7 +1693,7 @@ class Henti extends StmtBase {
|
|
|
1589
1693
|
}
|
|
1590
1694
|
|
|
1591
1695
|
class Lewat extends StmtBase {
|
|
1592
|
-
constructor
|
|
1696
|
+
constructor() {
|
|
1593
1697
|
super();
|
|
1594
1698
|
}
|
|
1595
1699
|
|
|
@@ -1599,8 +1703,8 @@ class Lewat extends StmtBase {
|
|
|
1599
1703
|
}
|
|
1600
1704
|
|
|
1601
1705
|
class Rubah extends StmtBase {
|
|
1602
|
-
|
|
1603
|
-
constructor
|
|
1706
|
+
// Expr.ExprBase variable, Expr.ExprBase value
|
|
1707
|
+
constructor(variable, value) {
|
|
1604
1708
|
super();
|
|
1605
1709
|
this.variable = variable;
|
|
1606
1710
|
this.value = value;
|
|
@@ -1612,8 +1716,8 @@ class Rubah extends StmtBase {
|
|
|
1612
1716
|
}
|
|
1613
1717
|
|
|
1614
1718
|
class Hasil extends StmtBase {
|
|
1615
|
-
|
|
1616
|
-
constructor
|
|
1719
|
+
// Expr.ExprBase expr
|
|
1720
|
+
constructor(expr) {
|
|
1617
1721
|
super();
|
|
1618
1722
|
this.expr = expr;
|
|
1619
1723
|
}
|
|
@@ -1624,8 +1728,8 @@ class Hasil extends StmtBase {
|
|
|
1624
1728
|
}
|
|
1625
1729
|
|
|
1626
1730
|
class Jenis extends StmtBase {
|
|
1627
|
-
|
|
1628
|
-
constructor
|
|
1731
|
+
// Token name, Array<Token> enums
|
|
1732
|
+
constructor(name, enums) {
|
|
1629
1733
|
super();
|
|
1630
1734
|
this.name = name;
|
|
1631
1735
|
this.enums = enums;
|
|
@@ -1637,8 +1741,8 @@ class Jenis extends StmtBase {
|
|
|
1637
1741
|
}
|
|
1638
1742
|
|
|
1639
1743
|
class Model extends StmtBase {
|
|
1640
|
-
|
|
1641
|
-
constructor
|
|
1744
|
+
// Token name, Array<Stmt.Type-Token> contents
|
|
1745
|
+
constructor(name, contents) {
|
|
1642
1746
|
super();
|
|
1643
1747
|
this.name = name;
|
|
1644
1748
|
this.contents = contents;
|
|
@@ -1650,8 +1754,8 @@ class Model extends StmtBase {
|
|
|
1650
1754
|
}
|
|
1651
1755
|
|
|
1652
1756
|
let Simpl$1 = class Simpl extends StmtBase {
|
|
1653
|
-
|
|
1654
|
-
constructor
|
|
1757
|
+
// Array<Stmt.StmtBase> statements
|
|
1758
|
+
constructor(statements) {
|
|
1655
1759
|
super();
|
|
1656
1760
|
this.statements = statements;
|
|
1657
1761
|
}
|
|
@@ -1662,8 +1766,8 @@ let Simpl$1 = class Simpl extends StmtBase {
|
|
|
1662
1766
|
};
|
|
1663
1767
|
|
|
1664
1768
|
class Modul extends StmtBase {
|
|
1665
|
-
|
|
1666
|
-
constructor
|
|
1769
|
+
// Token name, Array<Stmt.StmtBase> statements
|
|
1770
|
+
constructor(name, statements) {
|
|
1667
1771
|
super();
|
|
1668
1772
|
this.name = name;
|
|
1669
1773
|
this.statements = statements;
|
|
@@ -1690,7 +1794,7 @@ class Parser {
|
|
|
1690
1794
|
}
|
|
1691
1795
|
|
|
1692
1796
|
peek() {
|
|
1693
|
-
return this.tokenIndex+1 >= this.tokens.length ? null : this.tokens[this.tokenIndex+1];
|
|
1797
|
+
return this.tokenIndex + 1 >= this.tokens.length ? null : this.tokens[this.tokenIndex + 1];
|
|
1694
1798
|
}
|
|
1695
1799
|
|
|
1696
1800
|
check(type) {
|
|
@@ -1702,7 +1806,7 @@ class Parser {
|
|
|
1702
1806
|
}
|
|
1703
1807
|
|
|
1704
1808
|
match(...args) {
|
|
1705
|
-
if(this.isAtEnd()) return false;
|
|
1809
|
+
if (this.isAtEnd()) return false;
|
|
1706
1810
|
|
|
1707
1811
|
for (let i of args) {
|
|
1708
1812
|
if (this.check(i)) {
|
|
@@ -1723,8 +1827,8 @@ class Parser {
|
|
|
1723
1827
|
}
|
|
1724
1828
|
|
|
1725
1829
|
blockStmt() {
|
|
1726
|
-
let statements= [];
|
|
1727
|
-
while(!this.match(RCURLY)) {
|
|
1830
|
+
let statements = [];
|
|
1831
|
+
while (!this.match(RCURLY)) {
|
|
1728
1832
|
statements.push(this.statement());
|
|
1729
1833
|
}
|
|
1730
1834
|
return new Block(statements);
|
|
@@ -1737,7 +1841,7 @@ class Parser {
|
|
|
1737
1841
|
do {
|
|
1738
1842
|
let expr = this.expression();
|
|
1739
1843
|
args.push(expr);
|
|
1740
|
-
} while(this.match(COMMA))
|
|
1844
|
+
} while (this.match(COMMA))
|
|
1741
1845
|
}
|
|
1742
1846
|
|
|
1743
1847
|
this.eat(RPAREN, "Mengharapkan ')' setelah penggunaan mesin.");
|
|
@@ -1754,9 +1858,9 @@ class Parser {
|
|
|
1754
1858
|
this.eat(ID, "Mengharapkan Nama parameter setelah deklarasi Tipe parameter dalam Lamda.");
|
|
1755
1859
|
let name = this.previous();
|
|
1756
1860
|
params.push([type, name]);
|
|
1757
|
-
|
|
1758
|
-
while(this.match(COMMA)) {
|
|
1759
|
-
if(!this.match(ID, DATUM))
|
|
1861
|
+
|
|
1862
|
+
while (this.match(COMMA)) {
|
|
1863
|
+
if (!this.match(ID, DATUM))
|
|
1760
1864
|
this.error("Mengharapkan Tipe parameter setelah koma dalam Lamda.");
|
|
1761
1865
|
let type = this.typeStmt();
|
|
1762
1866
|
this.eat(ID, "Mengharapkan Nama parameter setelah deklarasi Tipe parameter dalam Lamda.");
|
|
@@ -1801,7 +1905,7 @@ class Parser {
|
|
|
1801
1905
|
arrayIndex(iterable) {
|
|
1802
1906
|
let index = this.expression();
|
|
1803
1907
|
|
|
1804
|
-
this.eat(RSQUARE, "Mengharapkan ']' untuk menutup indeks."
|
|
1908
|
+
this.eat(RSQUARE, "Mengharapkan ']' untuk menutup indeks.");
|
|
1805
1909
|
|
|
1806
1910
|
return new Index(iterable, index);
|
|
1807
1911
|
}
|
|
@@ -1811,7 +1915,7 @@ class Parser {
|
|
|
1811
1915
|
let expr = this.expression();
|
|
1812
1916
|
this.eat(RPAREN, "Mengharapkan ')' untuk mengakhiri ekspresi kurung");
|
|
1813
1917
|
return new Grouping(expr);
|
|
1814
|
-
} else if (this.match(LITERAL)){
|
|
1918
|
+
} else if (this.match(LITERAL)) {
|
|
1815
1919
|
return new Literal(this.previous());
|
|
1816
1920
|
} else if (this.match(ID)) {
|
|
1817
1921
|
return this.identifier();
|
|
@@ -1820,14 +1924,14 @@ class Parser {
|
|
|
1820
1924
|
} else if (this.match(LSQUARE)) {
|
|
1821
1925
|
return this.arrayExpr();
|
|
1822
1926
|
}
|
|
1823
|
-
|
|
1927
|
+
|
|
1824
1928
|
this.error("Mengharapkan Ekspresi valid.");
|
|
1825
1929
|
}
|
|
1826
1930
|
|
|
1827
1931
|
valuable() {
|
|
1828
1932
|
let result = this.primary();
|
|
1829
1933
|
|
|
1830
|
-
while(true) {
|
|
1934
|
+
while (true) {
|
|
1831
1935
|
if (this.match(LPAREN)) {
|
|
1832
1936
|
result = this.functionCallExpr(result);
|
|
1833
1937
|
} else if (this.match(LSQUARE)) {
|
|
@@ -1860,12 +1964,12 @@ class Parser {
|
|
|
1860
1964
|
this.eat(ID, "Mengharapkan Nama member setelah '.'.");
|
|
1861
1965
|
let id = this.identifier();
|
|
1862
1966
|
return new Member(parent, id);
|
|
1863
|
-
}
|
|
1967
|
+
}
|
|
1864
1968
|
|
|
1865
1969
|
factor() {
|
|
1866
1970
|
let expr = this.unary();
|
|
1867
1971
|
|
|
1868
|
-
while(this.match(STAR, SLASH)) {
|
|
1972
|
+
while (this.match(MODULUS, STAR, SLASH)) {
|
|
1869
1973
|
let op = this.previous();
|
|
1870
1974
|
let right = this.unary();
|
|
1871
1975
|
expr = new Binary(expr, op, right);
|
|
@@ -1877,7 +1981,7 @@ class Parser {
|
|
|
1877
1981
|
term() {
|
|
1878
1982
|
let expr = this.factor();
|
|
1879
1983
|
|
|
1880
|
-
while (this.match(
|
|
1984
|
+
while (this.match(PLUS, MINUS)) {
|
|
1881
1985
|
let op = this.previous();
|
|
1882
1986
|
let right = this.factor();
|
|
1883
1987
|
expr = new Binary(expr, op, right);
|
|
@@ -1889,7 +1993,7 @@ class Parser {
|
|
|
1889
1993
|
equality() {
|
|
1890
1994
|
let expr = this.term();
|
|
1891
1995
|
|
|
1892
|
-
|
|
1996
|
+
if (this.match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL, EQUAL_EQUAL, BANG_EQUAL)) {
|
|
1893
1997
|
let op = this.previous();
|
|
1894
1998
|
let right = this.term();
|
|
1895
1999
|
expr = new Binary(expr, op, right);
|
|
@@ -1897,11 +2001,11 @@ class Parser {
|
|
|
1897
2001
|
|
|
1898
2002
|
return expr;
|
|
1899
2003
|
}
|
|
1900
|
-
|
|
2004
|
+
|
|
1901
2005
|
andTerm() {
|
|
1902
2006
|
let expr = this.equality();
|
|
1903
2007
|
|
|
1904
|
-
while(this.match(AMPERSAND)) {
|
|
2008
|
+
while (this.match(AMPERSAND)) {
|
|
1905
2009
|
let op = this.previous();
|
|
1906
2010
|
let right = this.equality();
|
|
1907
2011
|
expr = new Binary(expr, op, right);
|
|
@@ -1913,7 +2017,7 @@ class Parser {
|
|
|
1913
2017
|
orTerm() {
|
|
1914
2018
|
let expr = this.andTerm();
|
|
1915
2019
|
|
|
1916
|
-
while(this.match(PIPE)) {
|
|
2020
|
+
while (this.match(PIPE)) {
|
|
1917
2021
|
let op = this.previous();
|
|
1918
2022
|
let right = this.equality();
|
|
1919
2023
|
expr = new Binary(expr, op, right);
|
|
@@ -2002,7 +2106,7 @@ class Parser {
|
|
|
2002
2106
|
}
|
|
2003
2107
|
|
|
2004
2108
|
untukStmt() {
|
|
2005
|
-
if(!this.match(ID, DATUM))
|
|
2109
|
+
if (!this.match(ID, DATUM))
|
|
2006
2110
|
this.error("Mengharapkan Tipe variabel untuk diinisialisasi setelah 'untuk'.");
|
|
2007
2111
|
let varType = this.typeStmt();
|
|
2008
2112
|
this.eat(ID, "Mengharapkan Nama variabel setelah Tipe dalam pernyataan 'untuk'.");
|
|
@@ -2028,8 +2132,19 @@ class Parser {
|
|
|
2028
2132
|
|
|
2029
2133
|
rubahStmt() {
|
|
2030
2134
|
let id = this.valuable();
|
|
2135
|
+
let shorthand = null;
|
|
2136
|
+
if (this.match(
|
|
2137
|
+
PLUS, MINUS, STAR, SLASH,
|
|
2138
|
+
AMPERSAND, PIPE, MODULUS
|
|
2139
|
+
)) {
|
|
2140
|
+
shorthand = new Binary(id, this.previous(), null);
|
|
2141
|
+
}
|
|
2031
2142
|
this.eat(EQUAL, "Mengharapkan '=' setelah variable yang ingin di-'rubah'.");
|
|
2032
2143
|
let expr = this.expression();
|
|
2144
|
+
if (shorthand) {
|
|
2145
|
+
shorthand.right = expr;
|
|
2146
|
+
expr = shorthand;
|
|
2147
|
+
}
|
|
2033
2148
|
return new Rubah(id, expr);
|
|
2034
2149
|
}
|
|
2035
2150
|
|
|
@@ -2039,13 +2154,13 @@ class Parser {
|
|
|
2039
2154
|
this.eat(LPAREN, "Mengharapkan '(' setelah deklarasi Nama Jenis.");
|
|
2040
2155
|
if (this.check(RPAREN)) {
|
|
2041
2156
|
this.error("Isian Jenis tidak boleh kosong.", false);
|
|
2042
|
-
}
|
|
2157
|
+
}
|
|
2043
2158
|
let enums = [];
|
|
2044
2159
|
do {
|
|
2045
2160
|
this.eat(ID, "Mengharapkan macam jenis dalam 'jenis'.");
|
|
2046
2161
|
let enumb = this.previous();
|
|
2047
2162
|
enums.push(enumb);
|
|
2048
|
-
} while(this.match(COMMA))
|
|
2163
|
+
} while (this.match(COMMA))
|
|
2049
2164
|
|
|
2050
2165
|
this.eat(RPAREN, "Mengharapkan ')' untuk mengakhiri deklarasi 'jenis'.");
|
|
2051
2166
|
|
|
@@ -2060,17 +2175,17 @@ class Parser {
|
|
|
2060
2175
|
if (this.check(RPAREN)) {
|
|
2061
2176
|
this.error("Model tidak boleh tanpa isian.", false);
|
|
2062
2177
|
}
|
|
2063
|
-
|
|
2178
|
+
|
|
2064
2179
|
let contents = [];
|
|
2065
2180
|
do {
|
|
2066
|
-
if(!this.match(ID, DATUM))
|
|
2181
|
+
if (!this.match(ID, DATUM))
|
|
2067
2182
|
this.error("Mengharapkan Tipe member dalam deklarasi 'Model'.");
|
|
2068
2183
|
let type = this.typeStmt();
|
|
2069
2184
|
this.eat(ID, "Mengharapkan Nama member dalam deklarasi 'model'.");
|
|
2070
2185
|
let memberName = this.previous();
|
|
2071
2186
|
|
|
2072
2187
|
contents.push([type, memberName]);
|
|
2073
|
-
} while(this.match(COMMA))
|
|
2188
|
+
} while (this.match(COMMA))
|
|
2074
2189
|
|
|
2075
2190
|
this.eat(RPAREN, "Mengharapkan ')' untuk mengakhiri deklarasi 'model'.");
|
|
2076
2191
|
|
|
@@ -2082,14 +2197,14 @@ class Parser {
|
|
|
2082
2197
|
let token = this.previous();
|
|
2083
2198
|
this.eat(LCURLY, "Mengharapkan '{' setelah Nama modul.");
|
|
2084
2199
|
let statements = [];
|
|
2085
|
-
while(!this.match(RCURLY)) {
|
|
2200
|
+
while (!this.match(RCURLY)) {
|
|
2086
2201
|
statements.push(this.statement());
|
|
2087
2202
|
}
|
|
2088
2203
|
return new Modul(token, statements);
|
|
2089
2204
|
}
|
|
2090
2205
|
|
|
2091
2206
|
statement() {
|
|
2092
|
-
if(this.match(CETAK)) {
|
|
2207
|
+
if (this.match(CETAK)) {
|
|
2093
2208
|
return this.cetakStmt();
|
|
2094
2209
|
} else if (this.match(ID, DATUM)) {
|
|
2095
2210
|
return this.datumStmt();
|
|
@@ -2099,7 +2214,7 @@ class Parser {
|
|
|
2099
2214
|
return this.kalauStmt();
|
|
2100
2215
|
} else if (this.match(LCURLY)) {
|
|
2101
2216
|
return this.blockStmt();
|
|
2102
|
-
} else if (this.match(UNTUK)){
|
|
2217
|
+
} else if (this.match(UNTUK)) {
|
|
2103
2218
|
return this.untukStmt();
|
|
2104
2219
|
} else if (this.match(SLAGI)) {
|
|
2105
2220
|
return this.slagiStmt();
|
|
@@ -2123,14 +2238,14 @@ class Parser {
|
|
|
2123
2238
|
}
|
|
2124
2239
|
|
|
2125
2240
|
error(errmsg, found = true) {
|
|
2126
|
-
throw new SimplErrorStruktur(`Error Struktur => ` + errmsg + ((found) ? ` Menemukan '${this.see().lexeme
|
|
2241
|
+
throw new SimplErrorStruktur(`Error Struktur => ` + errmsg + ((found) ? ` Menemukan '${this.see().lexeme}'.` : ""), this.see().line);
|
|
2127
2242
|
}
|
|
2128
2243
|
|
|
2129
2244
|
parse(tokens) {
|
|
2130
2245
|
this.init();
|
|
2131
2246
|
this.tokens = tokens;
|
|
2132
2247
|
let treeList = [];
|
|
2133
|
-
while(!this.match(EOF)) {
|
|
2248
|
+
while (!this.match(EOF)) {
|
|
2134
2249
|
treeList.push(this.statement());
|
|
2135
2250
|
}
|
|
2136
2251
|
this.tree = new Simpl$1(treeList);
|
|
@@ -2157,13 +2272,17 @@ class Simpl {
|
|
|
2157
2272
|
return output.join("\n");
|
|
2158
2273
|
} catch (err) {
|
|
2159
2274
|
if (err instanceof SimplError) {
|
|
2160
|
-
const
|
|
2161
|
-
|
|
2275
|
+
const errorCode = textLines[err.line - 1];
|
|
2276
|
+
let errorText = (errorCode ? `ERROR! Pada baris ke-${err.line}\n>> ` + errorCode + '\n' : "") + err.message;
|
|
2277
|
+
if (err instanceof SimplErrorEksekusi) {
|
|
2278
|
+
errorText += (err.output ? "\nOutput dari kode:\n" : "") + err.output;
|
|
2279
|
+
}
|
|
2280
|
+
return errorText;
|
|
2162
2281
|
} else {
|
|
2163
2282
|
return `[Pada baris ke-${this.interpreter.line}] Uh Oh, ini error sistem. Mohon laporkan agar diperbaiki. [ ${err} ]`;
|
|
2164
2283
|
}
|
|
2165
2284
|
}
|
|
2166
|
-
|
|
2285
|
+
|
|
2167
2286
|
}
|
|
2168
2287
|
}
|
|
2169
2288
|
|