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.
@@ -4,7 +4,12 @@ class SimplError extends Error {
4
4
  super(message);
5
5
  this.line = line;
6
6
  }
7
- }class SimplErrorEksekusi extends SimplError {}class SimplErrorStruktur extends SimplError {}class SimplErrorTulisan 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
- angkaSymbol = Symbol("angka"),
164
- logisSymbol = Symbol("logis"),
165
- mesinSymbol = Symbol("mesin"),
166
- barisSymbol = Symbol("baris"),
167
- stipeSymbol = Symbol("stipe"),
168
- modulSymbol = Symbol("modul");
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.callFunc(visitor, [right, left]);
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.callFunc(visitor, [right]);
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
- callFunc: (v,args) => {
214
- if (args.length !== 1) {
215
- v.error(`Jumlah argumen tidak sama dengan parameter mesin: ${args.length} != 1.`);
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
- const kePetik = (thing) => {
219
- if (thing.type === logisSymbol) {
220
- return thing.data ? "benar" : "salah";
221
- } else if (thing.type === barisSymbol) {
222
- return '[' + thing.data.reduce((str, val)=>str+" "+kePetik(val)+",", "") + ' ]'
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, (v, [r, l]) => new Value(petikSymbol, l.data+r.data)));
245
- this.operators.define("LEBIH",
246
- makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (v, [r, l]) => new Value(logisSymbol, l.data>r.data)));
247
- this.operators.define("KURANG",
248
- makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (v, [r, l]) => new Value(logisSymbol, l.data<r.data)));
249
- this.operators.define("SAMA_SAMA",
250
- makeBuiltInFunc([petikSymbol, petikSymbol], logisSymbol, (v, [r, l]) => new Value(logisSymbol, l.data===r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data>=r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data<=r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data!==r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data&&r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data||r.data)));
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, (v, [r]) => new Value(logisSymbol, !Boolean(r.data))));
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, (v, [d, sep])=>{
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, (v, [d])=> {
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
- (v, [d, what, rep]) => new Value(petikSymbol, d.data.replaceAll(what.data, rep.data))
285
+ (_, [d, what, rep]) => new Value(petikSymbol, d.data.replaceAll(what.data, rep.data))
275
286
  ));
276
- this.member.define("besar", makeBuiltInFunc([petikSymbol], petikSymbol, (v, [p]) => {
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
- callFunc: (v, args) => {
286
- if (args.length !== 1) {
287
- v.error(`Jumlah argumen tidak sama dengan parameter mesin: ${args.length} != 1.`);
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
- if (typeof args[0].data === "number") {
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
- switch (args[0].type) {
295
- case petikSymbol:
296
- if (isNaN(Number(args[0].data))) {
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, (v, [r, l]) => new Value(angkaSymbol, l.data+r.data)));
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, (v, [r, l]) => new Value(angkaSymbol, l.data-r.data)));
318
- this.operators.define("BINTANG",
319
- makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (v, [r, l]) => new Value(angkaSymbol, l.data*r.data)));
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, (v, [r, l]) => new Value(angkaSymbol, l.data/r.data)));
322
- this.operators.define("MODULUS",
323
- makeBuiltInFunc([angkaSymbol, angkaSymbol], angkaSymbol, (v, [r, l]) => new Value(angkaSymbol, l.data%r.data)));
324
-
325
- this.operators.define("LEBIH",
326
- makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (v, [r, l]) => new Value(logisSymbol, l.data>r.data)));
327
- this.operators.define("KURANG",
328
- makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (v, [r, l]) => new Value(logisSymbol, l.data<r.data)));
329
- this.operators.define("SAMA_SAMA",
330
- makeBuiltInFunc([angkaSymbol, angkaSymbol], logisSymbol, (v, [r, l]) => new Value(logisSymbol, l.data===r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data>=r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data<=r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data!==r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data&&r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data||r.data)));
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, (v, [r]) => new Value(logisSymbol, !Boolean(r.data))));
351
+ makeBuiltInFunc([angkaSymbol], logisSymbol, (_, [r]) => new Value(logisSymbol, !Boolean(r.data))));
343
352
  this.operators.define("PLUS_UNARY",
344
- makeBuiltInFunc([angkaSymbol], angkaSymbol, (v, [r]) => new Value(angkaSymbol, +r.data)));
353
+ makeBuiltInFunc([angkaSymbol], angkaSymbol, (_, [r]) => new Value(angkaSymbol, +r.data)));
345
354
  this.operators.define("MINUS_UNARY",
346
- makeBuiltInFunc([angkaSymbol], angkaSymbol, (v, [r]) => new Value(angkaSymbol, -r.data)));
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, (v, [a]) => {
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
- callFunc: (v, args) => {
366
- if (args.length !== 1) {
367
- v.error(`Jumlah argumen tidak sama dengan parameter mesin: ${args.length} != 1.`);
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, (v, [r, l]) => new Value(logisSymbol, l.data===r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data!==r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data&&r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data||r.data)));
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, (v, [r]) => new Value(logisSymbol, !Boolean(r.data))));
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, (v, [r,l]) => new Value(barisSymbol, Array(...l.data, ...r.data))));
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, (v, [r]) => {
405
- let newBaris = copier(r);
406
- newBaris.data.pop();
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, (v, [r, l]) => new Value(logisSymbol, ((a,b)=>{
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, (v, [r, l]) => new Value(logisSymbol, ((a,b)=>{
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, (v, [r, l]) => new Value(logisSymbol, l.data&&r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data||r.data)));
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, (v, [r]) => new Value(logisSymbol, !Boolean(r.data))));
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
- return new Value(barisSymbol, b.data.filter((val, idx)=>idx!==i.data));
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
- callFunc: (v, args) => {
462
- if (args.length !== params.length) {
463
- v.error("Jumlah argumen tidak sama dengan parameter mesin:" + ` ${args.length} != ${params.length}.`);
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
- obj.member.define("objek", obj);
489
- return obj;
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, (v, [r, l]) => new Value(logisSymbol, l.data===r.data)));
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, (v, [r, l]) => new Value(logisSymbol, l.data!==r.data)));
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, null, parameters.map((val)=>[val]), returnType);
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 new Value(null, null);
595
- case modulSymbol: return new Value(null, null);
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 new Value(null, null);
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
- (v, [from, to])=>new Value(barisSymbol,
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((val, idx)=>new Value(angkaSymbol, from.data+((to.data>from.data)?1:-1)*idx))))
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, (v, [d]) => new Value(logisSymbol, d.data === null)));
631
- env.define("ukuran", makeBuiltInFunc([null], angkaSymbol,(v, [d]) => d.type === barisSymbol || d.type === petikSymbol
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, (v, [d]) => copier(d)));
637
- env.define("tipe", makeBuiltInFunc([null], petikSymbol, (v, [d]) => (d.type?.description)
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 new Value(null, null);
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.validvalue(expr.accept(this));
695
- // value.data.push(v);
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
- if (params.length > 1
731
- && params.some(param=>params.reduce((count, anotherParam)=>param[2]===anotherParam[2] ? count+1:count, 0) > 1 ? true : false)) {
732
- this.error("Parameter mesin tidak boleh mempunyai nama yang sama.");
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
- this.stack.push(this.line);
744
- if (this.stack.length > MAX_STACK_SIZE)
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?.callFunc) {
756
+ if (!callable.data?.block) {
754
757
  this.error(`Mesin tidak terdefinisi, tidak bisa dipanggil.`);
755
758
  }
756
- this.line = this.stack[this.stack.length-1];
757
- let result = callable.data.callFunc(this, callExpr.args.map(val=>this.validvalue(val.accept(this))));
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
- if (this.stack.length == 0) this.state = location.GLOBAL;
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.validvalue(binaryExpr.left.accept(this));
765
- let rightValue = this.validvalue(binaryExpr.right.accept(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.validvalue(unaryExpr.right.accept(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
- let result = this.environment.get(rightValue.type.description) // get Model
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 = this.environment.get(identifierExpr.token.lexeme);
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.error(`Nilai tidak mempunyai atribut sama sekali.`);
814
- } else if (!main.member.has(name)) {
815
- this.error(`atribut .${name} tidak dapat ditemukan.`);
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 = typeStmt.type.accept(this);
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)=>str+", "+kePetik(val), "").slice(1) + ' ]';
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 '"' + thing.data + '"';
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.validvalue(datumStmt.expr.accept(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.validvalue(rubahStmt.value.accept(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 === null || condition === undefined || condition.data || condition.member) {
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 !== location.GLOBAL)
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 !== location.GLOBAL)
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) => v.data || v.member;
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.globalEnvironment);
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
- validvalue(v) {
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, null, null, this.lineIndex));
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
- // Expr.ExprBase left, Token op, Expr.ExprBase right
1343
- constructor (left, op, right) {
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
- // Token op, Expr.ExprBase right
1357
- constructor (op, right) {
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
- // Token token
1370
- constructor (token) {
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
- // Expr.ExprBase expr
1382
- constructor (expr) {
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
- // Expr.ExprBase main, Expr.Identifier member
1394
- constructor (main, member) {
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
- // Token token
1407
- constructor (token) {
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
- // Array<Stmt.Types-Token> params, Stmt.Type returnType, Stmt.Block block
1419
- constructor (params, returnType, block) {
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
- // Expr.ExprBase callable, Expr.ExprBase args
1433
- constructor (callable, args) {
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
- // Array<Expr.ExprBase> contents
1446
- constructor (contents) {
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
- // Expr.ExprBase iterable, Expr.ExprBase index
1458
- constructor (iterable, index) {
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
- // Expr.ExprBase expr
1477
- constructor (expr) {
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
- // Stmt.Type type, Token name, Expr.ExprBase expr
1489
- constructor (type, name, expr) {
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
- // Expr.ExprBase type, bool tetap, Array<Stmt.Type> contents
1503
- constructor (type, tetap, contents) {
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
- // Expr.ExprBase expr
1517
- constructor (expr) {
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
- // Array<Stmt.StmtBase> statements
1529
- constructor (statements) {
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
- // Expr.ExprBase condition, Stmt.Block thenBlock, Stmt.Kalau elseKalau
1541
- constructor (condition, thenBlock, elseKalau) {
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
- // Stmt.Type varType, Token varName, Expr.Base iterable, Stmt.Block block
1555
- constructor (varType, varName, iterable, block) {
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
- // Expr.ExprBase condition, Stmt.Block block
1570
- constructor (condition, block) {
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
- // Expr.ExprBase variable, Expr.ExprBase value
1603
- constructor (variable, value) {
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
- // Expr.ExprBase expr
1616
- constructor (expr) {
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
- // Token name, Array<Token> enums
1628
- constructor (name, enums) {
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
- // Token name, Array<Stmt.Type-Token> contents
1641
- constructor (name, contents) {
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
- // Array<Stmt.StmtBase> statements
1654
- constructor (statements) {
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
- // Token name, Array<Stmt.StmtBase> statements
1666
- constructor (name, statements) {
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(MODULUS, PLUS, MINUS)) {
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
- while(this.match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL, EQUAL_EQUAL, BANG_EQUAL)) {
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 }'.` : ""), this.see().line);
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 errorText = textLines[err.line-1];
2161
- return (errorText ? `ERROR! Pada baris ke-${err.line}\n>> ` + errorText + '\n': "") + err.message;
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