@forthic/interp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +50 -0
  3. package/dist/cjs/forthic/ForthicError.d.ts +19 -0
  4. package/dist/cjs/forthic/ForthicError.js +39 -0
  5. package/dist/cjs/forthic/ForthicError.js.map +1 -0
  6. package/dist/cjs/forthic/global_module/map_word.d.ts +28 -0
  7. package/dist/cjs/forthic/global_module/map_word.js +157 -0
  8. package/dist/cjs/forthic/global_module/map_word.js.map +1 -0
  9. package/dist/cjs/forthic/global_module.d.ts +167 -0
  10. package/dist/cjs/forthic/global_module.js +2434 -0
  11. package/dist/cjs/forthic/global_module.js.map +1 -0
  12. package/dist/cjs/forthic/interpreter.d.ts +70 -0
  13. package/dist/cjs/forthic/interpreter.js +386 -0
  14. package/dist/cjs/forthic/interpreter.js.map +1 -0
  15. package/dist/cjs/forthic/module.d.ts +101 -0
  16. package/dist/cjs/forthic/module.js +277 -0
  17. package/dist/cjs/forthic/module.js.map +1 -0
  18. package/dist/cjs/forthic/tokenizer.d.ts +77 -0
  19. package/dist/cjs/forthic/tokenizer.js +335 -0
  20. package/dist/cjs/forthic/tokenizer.js.map +1 -0
  21. package/dist/cjs/forthic/utils.d.ts +11 -0
  22. package/dist/cjs/forthic/utils.js +86 -0
  23. package/dist/cjs/forthic/utils.js.map +1 -0
  24. package/dist/cjs/index.d.ts +3 -0
  25. package/dist/cjs/index.js +20 -0
  26. package/dist/cjs/index.js.map +1 -0
  27. package/dist/esm/forthic/ForthicError.d.ts +19 -0
  28. package/dist/esm/forthic/ForthicError.js +39 -0
  29. package/dist/esm/forthic/ForthicError.js.map +1 -0
  30. package/dist/esm/forthic/global_module/map_word.d.ts +28 -0
  31. package/dist/esm/forthic/global_module/map_word.js +157 -0
  32. package/dist/esm/forthic/global_module/map_word.js.map +1 -0
  33. package/dist/esm/forthic/global_module.d.ts +167 -0
  34. package/dist/esm/forthic/global_module.js +2434 -0
  35. package/dist/esm/forthic/global_module.js.map +1 -0
  36. package/dist/esm/forthic/interpreter.d.ts +70 -0
  37. package/dist/esm/forthic/interpreter.js +386 -0
  38. package/dist/esm/forthic/interpreter.js.map +1 -0
  39. package/dist/esm/forthic/module.d.ts +101 -0
  40. package/dist/esm/forthic/module.js +277 -0
  41. package/dist/esm/forthic/module.js.map +1 -0
  42. package/dist/esm/forthic/tokenizer.d.ts +77 -0
  43. package/dist/esm/forthic/tokenizer.js +335 -0
  44. package/dist/esm/forthic/tokenizer.js.map +1 -0
  45. package/dist/esm/forthic/utils.d.ts +11 -0
  46. package/dist/esm/forthic/utils.js +86 -0
  47. package/dist/esm/forthic/utils.js.map +1 -0
  48. package/dist/esm/index.d.ts +3 -0
  49. package/dist/esm/index.js +20 -0
  50. package/dist/esm/index.js.map +1 -0
  51. package/package.json +58 -0
@@ -0,0 +1,2434 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GlobalModule = void 0;
4
+ const module_1 = require("./module");
5
+ const ForthicError_1 = require("./ForthicError");
6
+ const utils_1 = require("./utils");
7
+ const map_word_1 = require("./global_module/map_word");
8
+ const tokenizer_1 = require("./tokenizer");
9
+ const DLE = String.fromCharCode(16); // ASCII char for "Data Link Escape" used as an untypeable quote
10
+ class GlobalModule extends module_1.Module {
11
+ constructor(interp) {
12
+ super("<GLOBAL>", interp);
13
+ this.module_id = `<GLOBAL>-${Math.floor(Math.random() * 1000000)}`;
14
+ // Set default flags
15
+ interp.set_flags(this.module_id, {
16
+ with_key: null,
17
+ push_error: null,
18
+ comparator: null,
19
+ push_rest: null,
20
+ depth: null,
21
+ overwrite: null,
22
+ delay: null,
23
+ interps: 1,
24
+ note_progress: null,
25
+ });
26
+ this.literal_handlers = [
27
+ this.to_bool,
28
+ this.to_float,
29
+ this.to_literal_date,
30
+ this.to_time,
31
+ this.to_int,
32
+ ];
33
+ // --------------------
34
+ // Base words
35
+ this.add_module_word("VARIABLES", this.word_VARIABLES);
36
+ this.add_module_word("!", this.word_bang);
37
+ this.add_module_word("@", this.word_at);
38
+ this.add_module_word("!@", this.word_bang_at);
39
+ this.add_module_word("INTERPRET", this.word_INTERPRET);
40
+ this.add_module_word("EXPORT", this.word_EXPORT);
41
+ this.add_module_word("USE-MODULES", this.word_USE_MODULES);
42
+ this.add_module_word("REC", this.word_REC);
43
+ this.add_module_word("REC@", this.word_REC_at);
44
+ this.add_module_word("|REC@", this.word_pipe_REC_at);
45
+ this.add_module_word("<REC!", this.word_l_REC_bang);
46
+ // ----------------
47
+ // Array/Record words
48
+ this.add_module_word("APPEND", this.word_APPEND);
49
+ this.add_module_word("REVERSE", this.word_REVERSE);
50
+ this.add_module_word("UNIQUE", this.word_UNIQUE);
51
+ this.add_module_word("<DEL", this.word_L_DEL);
52
+ this.add_module_word("RELABEL", this.word_RELABEL);
53
+ this.add_module_word("BY-FIELD", this.word_BY_FIELD);
54
+ this.add_module_word("GROUP-BY-FIELD", this.word_GROUP_BY_FIELD);
55
+ this.add_module_word("GROUP-BY", this.word_GROUP_BY);
56
+ this.add_module_word("GROUPS-OF", this.word_GROUPS_OF);
57
+ this.add_module_word("INDEX", this.word_INDEX);
58
+ this.add_module_word("MAP", this.word_MAP);
59
+ this.add_module_word("FOREACH", this.word_FOREACH);
60
+ this.add_module_word("INVERT-KEYS", this.word_INVERT_KEYS);
61
+ this.add_module_word("ZIP", this.word_ZIP);
62
+ this.add_module_word("ZIP-WITH", this.word_ZIP_WITH);
63
+ this.add_module_word("KEYS", this.word_KEYS);
64
+ this.add_module_word("VALUES", this.word_VALUES);
65
+ this.add_module_word("LENGTH", this.word_LENGTH);
66
+ this.add_module_word("RANGE", this.word_RANGE);
67
+ this.add_module_word("SLICE", this.word_SLICE);
68
+ this.add_module_word("DIFFERENCE", this.word_DIFFERENCE);
69
+ this.add_module_word("INTERSECTION", this.word_INTERSECTION);
70
+ this.add_module_word("UNION", this.word_UNION);
71
+ this.add_module_word("SELECT", this.word_SELECT);
72
+ this.add_module_word("TAKE", this.word_TAKE);
73
+ this.add_module_word("DROP", this.word_DROP);
74
+ this.add_module_word("ROTATE", this.word_ROTATE);
75
+ this.add_module_word("ROTATE-ELEMENT", this.word_ROTATE_ELEMENT);
76
+ this.add_module_word("ARRAY?", this.word_ARRAY_q);
77
+ this.add_module_word("SHUFFLE", this.word_SHUFFLE);
78
+ this.add_module_word("SORT", this.word_SORT);
79
+ this.add_module_word("FIELD-KEY-FUNC", this.word_FIELD_KEY_FUNC);
80
+ this.add_module_word("NTH", this.word_NTH);
81
+ this.add_module_word("LAST", this.word_LAST);
82
+ this.add_module_word("UNPACK", this.word_UNPACK);
83
+ this.add_module_word("FLATTEN", this.word_FLATTEN);
84
+ this.add_module_word("KEY-OF", this.word_KEY_OF);
85
+ this.add_module_word("REDUCE", this.word_REDUCE);
86
+ // --------------------
87
+ // Stack words
88
+ this.add_module_word("POP", this.word_POP);
89
+ this.add_module_word("DUP", this.word_DUP);
90
+ this.add_module_word("SWAP", this.word_SWAP);
91
+ // --------------------
92
+ // String words
93
+ this.add_module_word(">STR", this.word_to_STR);
94
+ this.add_module_word("CONCAT", this.word_CONCAT);
95
+ this.add_module_word("SPLIT", this.word_SPLIT);
96
+ this.add_module_word("JOIN", this.word_JOIN);
97
+ this.add_module_word("/N", this.word_slash_N);
98
+ this.add_module_word("/R", this.word_slash_R);
99
+ this.add_module_word("/T", this.word_slash_T);
100
+ this.add_module_word("LOWERCASE", this.word_LOWERCASE);
101
+ this.add_module_word("UPPERCASE", this.word_UPPERCASE);
102
+ this.add_module_word("ASCII", this.word_ASCII);
103
+ this.add_module_word("STRIP", this.word_STRIP);
104
+ this.add_module_word("REPLACE", this.word_REPLACE);
105
+ this.add_module_word("RE-MATCH", this.word_RE_MATCH);
106
+ this.add_module_word("RE-MATCH-GROUP", this.word_RE_MATCH_GROUP);
107
+ this.add_module_word("RE-MATCH-ALL", this.word_RE_MATCH_ALL);
108
+ // --------------------
109
+ // Misc words
110
+ this.add_module_word("NULL", this.word_NULL);
111
+ this.add_module_word("DEFAULT", this.word_DEFAULT);
112
+ this.add_module_word("*DEFAULT", this.word_star_DEFAULT);
113
+ this.add_module_word("REC-DEFAULTS", this.word_REC_DEFAULTS);
114
+ this.add_module_word("<REPEAT", this.word_l_REPEAT);
115
+ this.add_module_word("IDENTITY", this.word_IDENTITY);
116
+ this.add_module_word(">FIXED", this.word_to_FIXED);
117
+ this.add_module_word(">JSON", this.word_to_JSON);
118
+ this.add_module_word("PRETTIFY", this.word_PRETTIFY);
119
+ this.add_module_word("JSON>", this.word_JSON_to);
120
+ this.add_module_word("LOAD-SCREEN", this.word_LOAD_SCREEN);
121
+ this.add_module_word(".s", this.word_dot_s);
122
+ // --------------------
123
+ // Date/time words
124
+ this.add_module_word("AM", this.word_AM);
125
+ this.add_module_word("PM", this.word_PM);
126
+ this.add_module_word("NOW", this.word_NOW);
127
+ this.add_module_word(">TIME", this.word_to_TIME);
128
+ this.add_module_word("TIME>STR", this.word_TIME_to_STR);
129
+ this.add_module_word(">DATE", this.word_to_DATE);
130
+ this.add_module_word("TODAY", this.word_TODAY);
131
+ this.add_module_word("MONDAY", this.word_MONDAY);
132
+ this.add_module_word("TUESDAY", this.word_TUESDAY);
133
+ this.add_module_word("WEDNESDAY", this.word_WEDNESDAY);
134
+ this.add_module_word("THURSDAY", this.word_THURSDAY);
135
+ this.add_module_word("FRIDAY", this.word_FRIDAY);
136
+ this.add_module_word("SATURDAY", this.word_SATURDAY);
137
+ this.add_module_word("SUNDAY", this.word_SUNDAY);
138
+ this.add_module_word("ADD-DAYS", this.word_ADD_DAYS);
139
+ this.add_module_word("SUBTRACT-DATES", this.word_SUBTRACT_DATES);
140
+ this.add_module_word("DATE>STR", this.word_DATE_to_STR);
141
+ this.add_module_word("DATE>INT", this.word_DATE_to_INT);
142
+ this.add_module_word("DATE-TIME>DATETIME", this.word_DATE_TIME_to_DATETIME);
143
+ this.add_module_word("DATETIME>TIMESTAMP", this.word_DATETIME_to_TIMESTAMP);
144
+ this.add_module_word("TIMESTAMP>DATETIME", this.word_TIMESTAMP_to_DATETIME);
145
+ this.add_module_word("STR>DATETIME", this.word_STR_to_DATETIME);
146
+ this.add_module_word("STR>TIMESTAMP", this.word_STR_to_TIMESTAMP);
147
+ this.add_module_word("DAYS-AGO", this.word_DAYS_AGO);
148
+ // --------------------
149
+ // Math words
150
+ this.add_module_word("+", this.word_plus);
151
+ this.add_module_word("-", this.word_minus);
152
+ this.add_module_word("*", this.word_times);
153
+ this.add_module_word("/", this.word_divide_by);
154
+ this.add_module_word("ADD", this.word_plus);
155
+ this.add_module_word("SUBTRACT", this.word_minus);
156
+ this.add_module_word("MULTIPLY", this.word_times);
157
+ this.add_module_word("DIVIDE", this.word_divide_by);
158
+ this.add_module_word("MOD", this.word_MOD);
159
+ this.add_module_word("MEAN", this.word_MEAN);
160
+ this.add_module_word("MAX", this.word_MAX);
161
+ this.add_module_word("MIN", this.word_MIN);
162
+ this.add_module_word("ROUND", this.word_ROUND);
163
+ this.add_module_word("==", this.word_equal_equal);
164
+ this.add_module_word("!=", this.word_not_equal);
165
+ this.add_module_word(">", this.word_greater_than);
166
+ this.add_module_word(">=", this.word_greater_than_or_equal);
167
+ this.add_module_word("<", this.word_less_than);
168
+ this.add_module_word("<=", this.word_less_than_or_equal);
169
+ this.add_module_word("OR", this.word_OR);
170
+ this.add_module_word("AND", this.word_AND);
171
+ this.add_module_word("NOT", this.word_NOT);
172
+ this.add_module_word("IN", this.word_IN);
173
+ this.add_module_word("ANY", this.word_ANY);
174
+ this.add_module_word("ALL", this.word_ALL);
175
+ this.add_module_word(">BOOL", this.word_to_BOOL);
176
+ this.add_module_word(">INT", this.word_to_INT);
177
+ this.add_module_word(">FLOAT", this.word_to_FLOAT);
178
+ this.add_module_word("BUCKET", this.word_BUCKET);
179
+ this.add_module_word("UNIFORM-RANDOM", this.word_UNIFORM_RANDOM);
180
+ this.add_module_word("RANGE-INDEX", this.word_RANGE_INDEX);
181
+ this.add_module_word("RANGE-BUCKETS", this.word_RANGE_BUCKETS);
182
+ this.add_module_word("INFINITY", this.word_INFINITY);
183
+ // ----------------
184
+ // Flag words
185
+ this.add_module_word("!PUSH-ERROR", this.word_bang_PUSH_ERROR);
186
+ this.add_module_word("!WITH-KEY", this.word_bang_WITH_KEY);
187
+ this.add_module_word("!COMPARATOR", this.word_bang_COMPARATOR);
188
+ this.add_module_word("!PUSH-REST", this.word_bang_PUSH_REST);
189
+ this.add_module_word("!DEPTH", this.word_bang_DEPTH);
190
+ this.add_module_word("!OVERWRITE", this.word_bang_OVERWRITE);
191
+ this.add_module_word("!DELAY", this.word_bang_DELAY);
192
+ this.add_module_word("!INTERPS", this.word_bang_INTERPS);
193
+ this.add_module_word("!NOTE-PROGRESS", this.word_bang_NOTE_PROGRESS);
194
+ // --------------------
195
+ // Misc words (js-specific)
196
+ this.add_module_word("URL-ENCODE", this.word_URL_ENCODE);
197
+ this.add_module_word("URL-DECODE", this.word_URL_DECODE);
198
+ this.add_module_word("QUOTE-CHAR", this.word_QUOTE_CHAR);
199
+ this.add_module_word("QUOTED", this.word_QUOTED);
200
+ this.add_module_word("CONSOLE.LOG", this.word_CONSOLE_LOG);
201
+ // --------------------
202
+ // Profiling words
203
+ this.add_module_word("PROFILE-START", this.word_PROFILE_START);
204
+ this.add_module_word("PROFILE-TIMESTAMP", this.word_PROFILE_TIMESTAMP);
205
+ this.add_module_word("PROFILE-END", this.word_PROFILE_END);
206
+ this.add_module_word("PROFILE-DATA", this.word_PROFILE_DATA);
207
+ }
208
+ find_word(name) {
209
+ let result = super.find_word(name);
210
+ if (!result)
211
+ result = this.find_literal_word(name);
212
+ return result;
213
+ }
214
+ find_literal_word(string) {
215
+ const self = this;
216
+ let value = null;
217
+ for (let i = 0; i < self.literal_handlers.length; i++) {
218
+ value = self.literal_handlers[i](string);
219
+ if (value !== null)
220
+ return new module_1.PushValueWord(string, value);
221
+ }
222
+ return null;
223
+ }
224
+ // =======================
225
+ // Literal handlers
226
+ to_bool(str_val) {
227
+ if (str_val == "TRUE")
228
+ return true;
229
+ else if (str_val == "FALSE")
230
+ return false;
231
+ else
232
+ return null;
233
+ }
234
+ to_int(str_val) {
235
+ const result = parseInt(str_val);
236
+ if (isNaN(result))
237
+ return null;
238
+ return result;
239
+ }
240
+ to_float(str_val) {
241
+ if (str_val.indexOf(".") == -1)
242
+ return null;
243
+ const result = parseFloat(str_val);
244
+ if (isNaN(result))
245
+ return null;
246
+ else
247
+ return result;
248
+ }
249
+ to_literal_date(str_val) {
250
+ const match = str_val.match(/(\d{4})-(\d{2})-(\d{2})/);
251
+ if (!match)
252
+ return null;
253
+ const year = Number(match[1]);
254
+ const month = Number(match[2]);
255
+ const day = Number(match[3]);
256
+ const result = new Date();
257
+ result.setUTCFullYear(year, month - 1, day);
258
+ result.setUTCHours(0, 0, 0, 0);
259
+ return result;
260
+ }
261
+ to_time(str_val) {
262
+ const match = str_val.match(/(\d{1,2}):(\d{2})/);
263
+ if (!match)
264
+ return null;
265
+ const hours = Number(match[1]);
266
+ const minutes = Number(match[2]);
267
+ if (hours > 23)
268
+ return null;
269
+ if (minutes >= 60)
270
+ return null;
271
+ const result = new Date();
272
+ result.setHours(hours, minutes, 0, 0);
273
+ return result;
274
+ }
275
+ // Convenience function to create element word handlers
276
+ make_element_word(element_name) {
277
+ async function Result(interp) {
278
+ interp.stack_push(`${element_name}`);
279
+ await interp.run("Element");
280
+ }
281
+ return Result;
282
+ }
283
+ add_element_word(element_name) {
284
+ this.add_module_word(element_name, this.make_element_word(element_name));
285
+ }
286
+ // =======================
287
+ // Words
288
+ // ( varnames -- )
289
+ word_VARIABLES(interp) {
290
+ const varnames = interp.stack_pop();
291
+ const module = interp.cur_module();
292
+ varnames.forEach((v) => {
293
+ if (v.match(/__.*/)) {
294
+ throw new ForthicError_1.ForthicError("global_module-696", `word_VARIABLES: variable names cannot begin with '__': '${JSON.stringify(v)}'`, "This is a reserved variable naming convention");
295
+ }
296
+ module.add_variable(v);
297
+ });
298
+ }
299
+ // ( value variable -- )
300
+ word_bang(interp) {
301
+ const variable = interp.stack_pop();
302
+ const value = interp.stack_pop();
303
+ variable.value = value;
304
+ }
305
+ // ( variable -- value )
306
+ word_at(interp) {
307
+ const variable = interp.stack_pop();
308
+ interp.stack_push(variable.value);
309
+ }
310
+ // ( value variable -- value )
311
+ word_bang_at(interp) {
312
+ const variable = interp.stack_pop();
313
+ const value = interp.stack_pop();
314
+ variable.value = value;
315
+ interp.stack_push(variable.value);
316
+ }
317
+ // ( string -- )
318
+ async word_INTERPRET(interp) {
319
+ const string = interp.stack_pop();
320
+ const string_location = interp.get_string_location();
321
+ if (string)
322
+ await interp.run(string, string_location);
323
+ }
324
+ // ( names -- )
325
+ word_EXPORT(interp) {
326
+ const names = interp.stack_pop();
327
+ interp.cur_module().add_exportable(names);
328
+ }
329
+ // ( names -- )
330
+ async word_USE_MODULES(interp) {
331
+ const names = interp.stack_pop();
332
+ for (let i = 0; i < names.length; i++) {
333
+ const name = names[i];
334
+ let module_name = name;
335
+ let prefix = name;
336
+ if (name instanceof Array) {
337
+ module_name = name[0];
338
+ prefix = name[1];
339
+ }
340
+ const module = interp.find_module(module_name);
341
+ interp.get_app_module().import_module(prefix, module, interp);
342
+ }
343
+ }
344
+ // ( key_vals -- rec )
345
+ word_REC(interp) {
346
+ let key_vals = interp.stack_pop();
347
+ if (!key_vals)
348
+ key_vals = [];
349
+ const result = {};
350
+ key_vals.forEach((pair) => {
351
+ let key = null;
352
+ let val = null;
353
+ if (pair) {
354
+ if (pair.length >= 1)
355
+ key = pair[0];
356
+ if (pair.length >= 2)
357
+ val = pair[1];
358
+ }
359
+ result[key] = val;
360
+ });
361
+ interp.stack_push(result);
362
+ }
363
+ // ( rec field -- value )
364
+ // ( rec fields -- value )
365
+ word_REC_at(interp) {
366
+ const field = interp.stack_pop();
367
+ const rec = interp.stack_pop();
368
+ if (!rec) {
369
+ interp.stack_push(null);
370
+ return;
371
+ }
372
+ let fields = [field];
373
+ if (field instanceof Array)
374
+ fields = field;
375
+ const result = drill_for_value(rec, fields);
376
+ interp.stack_push(result);
377
+ }
378
+ // ( records field -- values )
379
+ // ( records fields -- values )
380
+ async word_pipe_REC_at(interp) {
381
+ const fields = interp.stack_pop();
382
+ await interp.run(`'${JSON.stringify(fields)} REC@' MAP`);
383
+ }
384
+ // ( rec value field -- rec )
385
+ word_l_REC_bang(interp) {
386
+ const field = interp.stack_pop();
387
+ const value = interp.stack_pop();
388
+ let rec = interp.stack_pop();
389
+ if (!rec)
390
+ rec = {};
391
+ let fields = null;
392
+ if (field instanceof Array)
393
+ fields = field;
394
+ else
395
+ fields = [field];
396
+ function ensure_field(rec, field) {
397
+ let res = rec[field];
398
+ if (res === undefined) {
399
+ res = {};
400
+ rec[field] = res;
401
+ }
402
+ return res;
403
+ }
404
+ let cur_rec = rec;
405
+ // Drill down up until the last value
406
+ for (let i = 0; i < fields.length - 1; i++) {
407
+ cur_rec = ensure_field(cur_rec, fields[i]);
408
+ }
409
+ // Set the value at the right depth within rec
410
+ cur_rec[fields[fields.length - 1]] = value;
411
+ interp.stack_push(rec);
412
+ }
413
+ // ( array item -- array )
414
+ // ( record key/val -- record )
415
+ word_APPEND(interp) {
416
+ const item = interp.stack_pop();
417
+ let result = interp.stack_pop();
418
+ if (!result)
419
+ result = [];
420
+ if (result instanceof Array)
421
+ result.push(item);
422
+ // If not a list, treat as record
423
+ else
424
+ result[item[0]] = item[1];
425
+ interp.stack_push(result);
426
+ }
427
+ // ( array -- array )
428
+ // ( record -- record )
429
+ word_REVERSE(interp) {
430
+ let result = interp.stack_pop();
431
+ if (!result) {
432
+ interp.stack_push(result);
433
+ return;
434
+ }
435
+ if (result instanceof Array)
436
+ result = result.reverse();
437
+ interp.stack_push(result);
438
+ }
439
+ // ( array -- array )
440
+ word_UNIQUE(interp) {
441
+ const container = interp.stack_pop();
442
+ if (!container) {
443
+ interp.stack_push(container);
444
+ return;
445
+ }
446
+ let result = container;
447
+ if (container instanceof Array) {
448
+ result = [...new Set(container)];
449
+ }
450
+ interp.stack_push(result);
451
+ }
452
+ // ( array index -- array )
453
+ // ( record key -- record )
454
+ word_L_DEL(interp) {
455
+ const key = interp.stack_pop();
456
+ const container = interp.stack_pop();
457
+ if (!container) {
458
+ interp.stack_push(container);
459
+ return;
460
+ }
461
+ if (container instanceof Array)
462
+ container.splice(key, 1);
463
+ else
464
+ delete container[key];
465
+ interp.stack_push(container);
466
+ }
467
+ // ( array old_keys new_keys -- array )
468
+ // ( record old_keys new_keys -- record )
469
+ word_RELABEL(interp) {
470
+ const new_keys = interp.stack_pop();
471
+ const old_keys = interp.stack_pop();
472
+ const container = interp.stack_pop();
473
+ if (!container) {
474
+ interp.stack_push(container);
475
+ return;
476
+ }
477
+ if (old_keys.length != new_keys.length)
478
+ throw "RELABEL: old_keys and new_keys must be same length";
479
+ const new_to_old = {};
480
+ for (let i = 0; i < old_keys.length; i++) {
481
+ new_to_old[new_keys[i]] = old_keys[i];
482
+ }
483
+ let result = [];
484
+ if (container instanceof Array) {
485
+ Object.keys(new_to_old)
486
+ .sort()
487
+ .forEach((k) => result.push(container[new_to_old[k]]));
488
+ }
489
+ else {
490
+ result = {};
491
+ Object.keys(new_to_old).forEach((k) => (result[k] = container[new_to_old[k]]));
492
+ }
493
+ interp.stack_push(result);
494
+ }
495
+ // ( array field -- field_to_item )
496
+ // ( record field -- field_to_item )
497
+ word_BY_FIELD(interp) {
498
+ const field = interp.stack_pop();
499
+ let container = interp.stack_pop();
500
+ if (!container)
501
+ container = [];
502
+ let values = null;
503
+ if (container instanceof Array) {
504
+ values = container;
505
+ }
506
+ else {
507
+ values = [];
508
+ Object.keys(container).forEach((k) => {
509
+ values.push(container[k]);
510
+ });
511
+ }
512
+ const result = {};
513
+ values.forEach((v) => {
514
+ if (v)
515
+ result[v[field]] = v;
516
+ });
517
+ interp.stack_push(result);
518
+ }
519
+ // ( array field -- field_to_items )
520
+ // ( record field -- field_to_items )
521
+ word_GROUP_BY_FIELD(interp) {
522
+ const field = interp.stack_pop();
523
+ let container = interp.stack_pop();
524
+ if (!container)
525
+ container = [];
526
+ let values = [];
527
+ if (container instanceof Array)
528
+ values = container;
529
+ else
530
+ values = Object.keys(container).map((k) => container[k]);
531
+ const result = {};
532
+ values.forEach((v) => {
533
+ const field_val = v[field];
534
+ if (field_val instanceof Array) {
535
+ for (const fv of field_val) {
536
+ if (!result[fv])
537
+ result[fv] = [];
538
+ result[fv].push(v);
539
+ }
540
+ }
541
+ else {
542
+ if (!result[field_val])
543
+ result[field_val] = [];
544
+ result[field_val].push(v);
545
+ }
546
+ });
547
+ interp.stack_push(result);
548
+ }
549
+ // ( array forthic -- group_to_items )
550
+ // ( record forthic -- group_to_items )
551
+ async word_GROUP_BY(interp) {
552
+ const forthic = interp.stack_pop();
553
+ const string_location = interp.get_string_location();
554
+ let container = interp.stack_pop();
555
+ const flags = interp.get_flags(this.module_id);
556
+ if (!container)
557
+ container = [];
558
+ let keys, values;
559
+ if (container instanceof Array) {
560
+ keys = [];
561
+ for (let i = 0; i < container.length; i++)
562
+ keys.push(i);
563
+ values = container;
564
+ }
565
+ else {
566
+ keys = Object.keys(container);
567
+ values = keys.map((k) => container[k]);
568
+ }
569
+ const result = {};
570
+ for (let i = 0; i < values.length; i++) {
571
+ const key = keys[i];
572
+ const value = values[i];
573
+ if (flags.with_key)
574
+ interp.stack_push(key);
575
+ interp.stack_push(value);
576
+ await interp.run(forthic, string_location);
577
+ const group = interp.stack_pop();
578
+ if (!result[group])
579
+ result[group] = [];
580
+ result[group].push(value);
581
+ }
582
+ interp.stack_push(result);
583
+ }
584
+ // ( array n -- arrays )
585
+ // ( record n -- records )
586
+ word_GROUPS_OF(interp) {
587
+ const size = interp.stack_pop();
588
+ let container = interp.stack_pop();
589
+ if (size <= 0)
590
+ throw "GROUPS-OF requires group size > 0";
591
+ if (!container)
592
+ container = [];
593
+ function group_items(items, group_size) {
594
+ const num_groups = Math.ceil(items.length / group_size);
595
+ const res = [];
596
+ let remaining = items.slice();
597
+ for (let i = 0; i < num_groups; i++) {
598
+ res.push(remaining.slice(0, group_size));
599
+ remaining = remaining.slice(group_size);
600
+ }
601
+ return res;
602
+ }
603
+ function extract_rec(record, keys) {
604
+ const res = {};
605
+ keys.forEach((k) => (res[k] = record[k]));
606
+ return res;
607
+ }
608
+ let result;
609
+ if (container instanceof Array) {
610
+ result = group_items(container, size);
611
+ }
612
+ else {
613
+ const keys = Object.keys(container);
614
+ const key_groups = group_items(keys, size);
615
+ result = key_groups.map((ks) => extract_rec(container, ks));
616
+ }
617
+ interp.stack_push(result);
618
+ return;
619
+ }
620
+ // ( array forthic -- record )
621
+ async word_INDEX(interp) {
622
+ const forthic = interp.stack_pop();
623
+ const string_location = interp.get_string_location();
624
+ const items = interp.stack_pop();
625
+ if (!items) {
626
+ interp.stack_push(items);
627
+ return;
628
+ }
629
+ const result = {};
630
+ for (let i = 0; i < items.length; i++) {
631
+ const item = items[i];
632
+ interp.stack_push(item);
633
+ await interp.run(forthic, string_location);
634
+ const keys = interp.stack_pop();
635
+ keys.forEach((k) => {
636
+ const lowercased_key = k.toLowerCase();
637
+ if (result[lowercased_key])
638
+ result[lowercased_key].push(item);
639
+ else
640
+ result[lowercased_key] = [item];
641
+ });
642
+ }
643
+ interp.stack_push(result);
644
+ }
645
+ // ( items forthic -- [ ? ] )
646
+ async word_MAP(interp) {
647
+ const forthic = interp.stack_pop();
648
+ const string_location = interp.get_string_location();
649
+ const items = interp.stack_pop();
650
+ const flags = interp.get_flags(this.module_id);
651
+ const map_word = new map_word_1.MapWord(items, forthic, string_location, flags);
652
+ await map_word.execute(interp);
653
+ }
654
+ // ( items word -- ? )
655
+ async word_FOREACH(interp) {
656
+ const forthic = interp.stack_pop();
657
+ const string_location = interp.get_string_location();
658
+ let items = interp.stack_pop();
659
+ const flags = interp.get_flags(this.module_id);
660
+ if (!items)
661
+ items = [];
662
+ const errors = [];
663
+ if (items instanceof Array) {
664
+ for (let i = 0; i < items.length; i++) {
665
+ const item = items[i];
666
+ if (flags.with_key)
667
+ interp.stack_push(i);
668
+ interp.stack_push(item);
669
+ if (flags.push_error)
670
+ errors.push(await execute_returning_error(interp, forthic, string_location));
671
+ else
672
+ await interp.run(forthic, string_location);
673
+ }
674
+ }
675
+ else {
676
+ const keys = Object.keys(items);
677
+ for (let i = 0; i < keys.length; i++) {
678
+ const k = keys[i];
679
+ const item = items[k];
680
+ if (flags.with_key)
681
+ interp.stack_push(k);
682
+ interp.stack_push(item);
683
+ if (flags.push_error)
684
+ errors.push(await execute_returning_error(interp, forthic, string_location));
685
+ else
686
+ await interp.run(forthic, string_location);
687
+ }
688
+ }
689
+ if (flags.push_error)
690
+ interp.stack_push(errors);
691
+ }
692
+ // ( record -- record )
693
+ word_INVERT_KEYS(interp) {
694
+ const record = interp.stack_pop();
695
+ const result = {};
696
+ Object.keys(record).forEach((first_key) => {
697
+ const sub_record = record[first_key];
698
+ Object.keys(sub_record).forEach((second_key) => {
699
+ const value = sub_record[second_key];
700
+ if (!result[second_key])
701
+ result[second_key] = {};
702
+ result[second_key][first_key] = value;
703
+ });
704
+ });
705
+ interp.stack_push(result);
706
+ }
707
+ // ( array1 array2 -- array )
708
+ // ( record1 record2 -- record )
709
+ word_ZIP(interp) {
710
+ let container2 = interp.stack_pop();
711
+ let container1 = interp.stack_pop();
712
+ if (!container1)
713
+ container1 = [];
714
+ if (!container2)
715
+ container2 = [];
716
+ let result;
717
+ if (container2 instanceof Array) {
718
+ result = [];
719
+ for (let i = 0; i < container1.length; i++) {
720
+ let value2 = null;
721
+ if (i < container2.length)
722
+ value2 = container2[i];
723
+ result.push([container1[i], value2]);
724
+ }
725
+ }
726
+ else {
727
+ result = {};
728
+ Object.keys(container1).forEach((k) => {
729
+ const v = container1[k];
730
+ result[k] = [v, container2[k]];
731
+ });
732
+ }
733
+ interp.stack_push(result);
734
+ }
735
+ // ( array1 array2 forthic -- array )
736
+ // ( record1 record2 forthic -- record )
737
+ async word_ZIP_WITH(interp) {
738
+ const forthic = interp.stack_pop();
739
+ const string_location = interp.get_string_location();
740
+ let container2 = interp.stack_pop();
741
+ let container1 = interp.stack_pop();
742
+ if (!container1)
743
+ container1 = [];
744
+ if (!container2)
745
+ container2 = [];
746
+ let result;
747
+ if (container2 instanceof Array) {
748
+ result = [];
749
+ for (let i = 0; i < container1.length; i++) {
750
+ let value2 = null;
751
+ if (i < container2.length)
752
+ value2 = container2[i];
753
+ interp.stack_push(container1[i]);
754
+ interp.stack_push(value2);
755
+ await interp.run(forthic, string_location);
756
+ const res = interp.stack_pop();
757
+ result.push(res);
758
+ }
759
+ }
760
+ else {
761
+ result = {};
762
+ const keys = Object.keys(container1);
763
+ for (let i = 0; i < keys.length; i++) {
764
+ const k = keys[i];
765
+ interp.stack_push(container1[k]);
766
+ interp.stack_push(container2[k]);
767
+ await interp.run(forthic, string_location);
768
+ const res = interp.stack_pop();
769
+ result[k] = res;
770
+ }
771
+ }
772
+ interp.stack_push(result);
773
+ }
774
+ // ( array -- array )
775
+ // ( record -- array )
776
+ word_KEYS(interp) {
777
+ let container = interp.stack_pop();
778
+ if (!container)
779
+ container = [];
780
+ let result;
781
+ if (container instanceof Array) {
782
+ result = [];
783
+ for (let i = 0; i < container.length; i++)
784
+ result.push(i);
785
+ }
786
+ else {
787
+ result = Object.keys(container);
788
+ }
789
+ interp.stack_push(result);
790
+ }
791
+ // ( array -- array )
792
+ // ( record -- array )
793
+ word_VALUES(interp) {
794
+ let container = interp.stack_pop();
795
+ if (!container)
796
+ container = [];
797
+ let result;
798
+ if (container instanceof Array) {
799
+ result = container;
800
+ }
801
+ else {
802
+ result = [];
803
+ Object.keys(container).forEach((k) => result.push(container[k]));
804
+ }
805
+ interp.stack_push(result);
806
+ }
807
+ // ( array -- length )
808
+ // ( record -- length )
809
+ word_LENGTH(interp) {
810
+ let container = interp.stack_pop();
811
+ if (!container)
812
+ container = [];
813
+ let result;
814
+ if (container instanceof Array || typeof container == "string") {
815
+ result = container.length;
816
+ }
817
+ else {
818
+ result = Object.keys(container).length;
819
+ }
820
+ interp.stack_push(result);
821
+ }
822
+ // ( array fstart fend -- indices )
823
+ async word_RANGE(interp) {
824
+ const fend = interp.stack_pop();
825
+ const fend_string_location = interp.get_string_location();
826
+ const fstart = interp.stack_pop();
827
+ const fstart_string_location = interp.get_string_location();
828
+ let array = interp.stack_pop();
829
+ if (!array)
830
+ array = [];
831
+ let start_found = false;
832
+ let end_found = false;
833
+ let start_index = null;
834
+ let end_index = null;
835
+ for (let index = 0; index < array.length; index++) {
836
+ const item = array[index];
837
+ if (!start_found) {
838
+ interp.stack_push(item);
839
+ await interp.run(fstart, fstart_string_location);
840
+ start_found = interp.stack_pop();
841
+ if (start_found) {
842
+ start_index = index;
843
+ }
844
+ }
845
+ if (start_found && !end_found) {
846
+ interp.stack_push(item);
847
+ await interp.run(fend, fend_string_location);
848
+ end_found = interp.stack_pop();
849
+ if (end_found) {
850
+ end_index = index;
851
+ break;
852
+ }
853
+ }
854
+ }
855
+ interp.stack_push([start_index, end_index]);
856
+ }
857
+ // ( array start end -- array )
858
+ // ( record start end -- record )
859
+ word_SLICE(interp) {
860
+ let end = Math.trunc(interp.stack_pop());
861
+ let start = Math.trunc(interp.stack_pop());
862
+ let container = interp.stack_pop();
863
+ if (!container)
864
+ container = [];
865
+ let length;
866
+ if (container instanceof Array) {
867
+ length = container.length;
868
+ }
869
+ else {
870
+ length = Object.keys(container).length;
871
+ }
872
+ function normalize_index(index) {
873
+ let res = index;
874
+ if (index < 0)
875
+ res = index + length;
876
+ return res;
877
+ }
878
+ start = normalize_index(start);
879
+ end = normalize_index(end);
880
+ let step = 1;
881
+ if (start > end)
882
+ step = -1;
883
+ let indexes = [start];
884
+ if (start < 0 || start >= length)
885
+ indexes = [];
886
+ while (start != end) {
887
+ start = start + step;
888
+ if (start < 0 || start >= length)
889
+ indexes.push(null);
890
+ else
891
+ indexes.push(start);
892
+ }
893
+ let result;
894
+ if (container instanceof Array) {
895
+ result = [];
896
+ indexes.forEach((i) => {
897
+ if (i === null)
898
+ result.push(null);
899
+ else
900
+ result.push(container[i]);
901
+ });
902
+ }
903
+ else {
904
+ const keys = Object.keys(container).sort();
905
+ result = {};
906
+ indexes.forEach((i) => {
907
+ if (i !== null) {
908
+ const k = keys[i];
909
+ result[k] = container[k];
910
+ }
911
+ });
912
+ }
913
+ interp.stack_push(result);
914
+ }
915
+ // ( larray rarray -- array )
916
+ // ( lrecord rrecord -- record )
917
+ word_DIFFERENCE(interp) {
918
+ let rcontainer = interp.stack_pop();
919
+ let lcontainer = interp.stack_pop();
920
+ if (!lcontainer)
921
+ lcontainer = [];
922
+ if (!rcontainer)
923
+ rcontainer = [];
924
+ function difference(l, r) {
925
+ const res = [];
926
+ l.forEach((item) => {
927
+ if (r.indexOf(item) < 0)
928
+ res.push(item);
929
+ });
930
+ return res;
931
+ }
932
+ let result;
933
+ if (rcontainer instanceof Array) {
934
+ result = difference(lcontainer, rcontainer);
935
+ }
936
+ else {
937
+ const lkeys = Object.keys(lcontainer);
938
+ const rkeys = Object.keys(rcontainer);
939
+ const diff = difference(lkeys, rkeys);
940
+ result = {};
941
+ diff.forEach((k) => (result[k] = lcontainer[k]));
942
+ }
943
+ interp.stack_push(result);
944
+ }
945
+ // ( larray rarray -- array )
946
+ // ( lrecord rrecord -- record )
947
+ word_INTERSECTION(interp) {
948
+ let rcontainer = interp.stack_pop();
949
+ let lcontainer = interp.stack_pop();
950
+ if (!lcontainer)
951
+ lcontainer = [];
952
+ if (!rcontainer)
953
+ rcontainer = [];
954
+ function intersection(l, r) {
955
+ const res = [];
956
+ l.forEach((item) => {
957
+ if (r.indexOf(item) >= 0)
958
+ res.push(item);
959
+ });
960
+ return res;
961
+ }
962
+ let result;
963
+ if (rcontainer instanceof Array) {
964
+ result = intersection(lcontainer, rcontainer);
965
+ }
966
+ else {
967
+ const lkeys = Object.keys(lcontainer);
968
+ const rkeys = Object.keys(rcontainer);
969
+ const intersect = intersection(lkeys, rkeys);
970
+ result = {};
971
+ intersect.forEach((k) => (result[k] = lcontainer[k]));
972
+ }
973
+ interp.stack_push(result);
974
+ }
975
+ // ( larray rarray -- array )
976
+ // ( lrecord rrecord -- record )
977
+ word_UNION(interp) {
978
+ let rcontainer = interp.stack_pop();
979
+ let lcontainer = interp.stack_pop();
980
+ if (!lcontainer)
981
+ lcontainer = [];
982
+ if (!rcontainer)
983
+ rcontainer = [];
984
+ function union(l, r) {
985
+ const keyset = {};
986
+ l.forEach((item) => {
987
+ keyset[item] = true;
988
+ });
989
+ r.forEach((item) => {
990
+ keyset[item] = true;
991
+ });
992
+ const res = Object.keys(keyset);
993
+ return res;
994
+ }
995
+ let result;
996
+ if (rcontainer instanceof Array) {
997
+ result = union(lcontainer, rcontainer);
998
+ }
999
+ else {
1000
+ const lkeys = Object.keys(lcontainer);
1001
+ const rkeys = Object.keys(rcontainer);
1002
+ const keys = union(lkeys, rkeys);
1003
+ result = {};
1004
+ keys.forEach((k) => {
1005
+ let val = lcontainer[k];
1006
+ if (val === undefined)
1007
+ val = rcontainer[k];
1008
+ result[k] = val;
1009
+ });
1010
+ }
1011
+ interp.stack_push(result);
1012
+ }
1013
+ // ( larray forthic -- array )
1014
+ // ( lrecord forthic -- record )
1015
+ async word_SELECT(interp) {
1016
+ const forthic = interp.stack_pop();
1017
+ const string_location = interp.get_string_location();
1018
+ const container = interp.stack_pop();
1019
+ const flags = interp.get_flags(this.module_id);
1020
+ if (!container) {
1021
+ interp.stack_push(container);
1022
+ return;
1023
+ }
1024
+ let result;
1025
+ if (container instanceof Array) {
1026
+ result = [];
1027
+ for (let i = 0; i < container.length; i++) {
1028
+ const item = container[i];
1029
+ if (flags.with_key)
1030
+ interp.stack_push(i);
1031
+ interp.stack_push(item);
1032
+ await interp.run(forthic, string_location);
1033
+ const should_select = interp.stack_pop();
1034
+ if (should_select)
1035
+ result.push(item);
1036
+ }
1037
+ }
1038
+ else {
1039
+ result = {};
1040
+ const keys = Object.keys(container);
1041
+ for (let i = 0; i < keys.length; i++) {
1042
+ const k = keys[i];
1043
+ const v = container[k];
1044
+ if (flags.with_key)
1045
+ interp.stack_push(k);
1046
+ interp.stack_push(v);
1047
+ await interp.run(forthic, string_location);
1048
+ const should_select = interp.stack_pop();
1049
+ if (should_select)
1050
+ result[k] = v;
1051
+ }
1052
+ }
1053
+ interp.stack_push(result);
1054
+ }
1055
+ // ( array n -- rest taken )
1056
+ // ( record n -- rest taken )
1057
+ word_TAKE(interp) {
1058
+ const n = interp.stack_pop();
1059
+ let container = interp.stack_pop();
1060
+ const flags = interp.get_flags(this.module_id);
1061
+ if (!container)
1062
+ container = [];
1063
+ let rest, taken;
1064
+ if (container instanceof Array) {
1065
+ taken = container.slice(0, n);
1066
+ rest = container.slice(n);
1067
+ }
1068
+ else {
1069
+ const keys = Object.keys(container).sort();
1070
+ const taken_keys = keys.slice(0, n);
1071
+ const rest_keys = keys.slice(n);
1072
+ taken = taken_keys.map((k) => container[k]);
1073
+ rest = rest_keys.map((k) => container[k]);
1074
+ }
1075
+ interp.stack_push(taken);
1076
+ if (flags.push_rest)
1077
+ interp.stack_push(rest);
1078
+ }
1079
+ // ( array n -- array )
1080
+ // ( record n -- record )
1081
+ word_DROP(interp) {
1082
+ const n = interp.stack_pop();
1083
+ let container = interp.stack_pop();
1084
+ if (!container)
1085
+ container = [];
1086
+ let result;
1087
+ if (container instanceof Array) {
1088
+ result = container.slice(n);
1089
+ }
1090
+ else {
1091
+ const keys = Object.keys(container).sort();
1092
+ const rest_keys = keys.slice(n);
1093
+ result = rest_keys.map((k) => container[k]);
1094
+ }
1095
+ interp.stack_push(result);
1096
+ }
1097
+ // ( array -- array )
1098
+ // ( record -- record )
1099
+ word_ROTATE(interp) {
1100
+ const container = interp.stack_pop();
1101
+ let result;
1102
+ if (!container) {
1103
+ result = container;
1104
+ }
1105
+ else if (container instanceof Array) {
1106
+ result = container;
1107
+ if (container.length > 0) {
1108
+ const val = result.pop();
1109
+ result.unshift(val);
1110
+ }
1111
+ }
1112
+ else {
1113
+ result = container;
1114
+ }
1115
+ interp.stack_push(result);
1116
+ }
1117
+ // ( array element -- array )
1118
+ // ( record element -- record )
1119
+ // Moves element to front of array
1120
+ word_ROTATE_ELEMENT(interp) {
1121
+ const element = interp.stack_pop();
1122
+ let container = interp.stack_pop();
1123
+ if (!container)
1124
+ container = [];
1125
+ let result;
1126
+ if (container instanceof Array) {
1127
+ const index = container.indexOf(element);
1128
+ result = container;
1129
+ if (index > 0) {
1130
+ result.splice(index, 1);
1131
+ result.unshift(element);
1132
+ }
1133
+ }
1134
+ else {
1135
+ result = container;
1136
+ }
1137
+ interp.stack_push(result);
1138
+ }
1139
+ // ( val -- bool )
1140
+ word_ARRAY_q(interp) {
1141
+ const val = interp.stack_pop();
1142
+ const result = val instanceof Array;
1143
+ interp.stack_push(result);
1144
+ }
1145
+ // ( array -- array )
1146
+ // ( record -- record )
1147
+ word_SHUFFLE(interp) {
1148
+ let container = interp.stack_pop();
1149
+ if (!container)
1150
+ container = [];
1151
+ let result;
1152
+ if (container instanceof Array) {
1153
+ // See: https://medium.com/@nitinpatel_20236/
1154
+ // how-to-shuffle-correctly-shuffle-an-array-in-javascript-15ea3f84bfb
1155
+ result = container;
1156
+ for (let i = result.length - 1; i > 0; i--) {
1157
+ const j = Math.floor(Math.random() * i);
1158
+ const temp = result[i];
1159
+ result[i] = result[j];
1160
+ result[j] = temp;
1161
+ }
1162
+ }
1163
+ else {
1164
+ result = container;
1165
+ }
1166
+ interp.stack_push(result);
1167
+ }
1168
+ // ( field -- key_func )
1169
+ word_FIELD_KEY_FUNC(interp) {
1170
+ const field = interp.stack_pop();
1171
+ function result(record) {
1172
+ return record[field];
1173
+ }
1174
+ interp.stack_push(result);
1175
+ }
1176
+ // ( array -- array )
1177
+ // ( record -- record )
1178
+ async word_SORT(interp) {
1179
+ const flag_string_position = interp.get_string_location(); // NOTE: If the user specified a comparator flag, we want to get the string position of it
1180
+ let container = interp.stack_pop();
1181
+ const flags = interp.get_flags(this.module_id);
1182
+ const comparator = flags["comparator"];
1183
+ if (!container)
1184
+ container = [];
1185
+ if (!(container instanceof Array)) {
1186
+ interp.stack_push(container);
1187
+ return;
1188
+ }
1189
+ // -----
1190
+ // Default sort
1191
+ function sort_without_comparator() {
1192
+ return container.sort();
1193
+ }
1194
+ // -----
1195
+ // Sort using a forthic string
1196
+ async function sort_with_forthic(forthic) {
1197
+ async function make_aug_array(vals) {
1198
+ const res = [];
1199
+ for (let i = 0; i < vals.length; i++) {
1200
+ const val = vals[i];
1201
+ interp.stack_push(val);
1202
+ await interp.run(forthic, flag_string_position);
1203
+ const aug_val = interp.stack_pop();
1204
+ res.push([val, aug_val]);
1205
+ }
1206
+ return res;
1207
+ }
1208
+ function cmp_items(l, r) {
1209
+ const l_val = l[1];
1210
+ const r_val = r[1];
1211
+ if (l_val < r_val)
1212
+ return -1;
1213
+ else if (l_val > r_val)
1214
+ return 1;
1215
+ else
1216
+ return 0;
1217
+ }
1218
+ function de_aug_array(aug_vals) {
1219
+ const res = aug_vals.map((aug_val) => aug_val[0]);
1220
+ return res;
1221
+ }
1222
+ // Create an augmented array, sort it and then return the underlying values
1223
+ // NOTE: We're doing it this way because sort is synchronous
1224
+ const aug_array = await make_aug_array(container);
1225
+ aug_array.sort(cmp_items);
1226
+ return de_aug_array(aug_array);
1227
+ }
1228
+ // -----
1229
+ // Sort with key func
1230
+ function sort_with_key_func(key_func) {
1231
+ function cmp_items(l, r) {
1232
+ const l_val = key_func(l);
1233
+ const r_val = key_func(r);
1234
+ if (l_val < r_val)
1235
+ return -1;
1236
+ else if (l_val > r_val)
1237
+ return 1;
1238
+ else
1239
+ return 0;
1240
+ }
1241
+ return container.sort(cmp_items);
1242
+ }
1243
+ // Figure out what to do
1244
+ let result;
1245
+ if (typeof comparator == "string") {
1246
+ result = await sort_with_forthic(comparator);
1247
+ }
1248
+ else if (comparator === undefined) {
1249
+ result = sort_without_comparator();
1250
+ }
1251
+ else {
1252
+ result = sort_with_key_func(comparator);
1253
+ }
1254
+ interp.stack_push(result);
1255
+ }
1256
+ // ( array n -- item )
1257
+ // ( record n -- value )
1258
+ word_NTH(interp) {
1259
+ const n = interp.stack_pop();
1260
+ const container = interp.stack_pop();
1261
+ if (n === null || !container) {
1262
+ interp.stack_push(null);
1263
+ return;
1264
+ }
1265
+ let result;
1266
+ if (container instanceof Array) {
1267
+ if (n < 0 || n >= container.length) {
1268
+ interp.stack_push(null);
1269
+ return;
1270
+ }
1271
+ result = container[n];
1272
+ }
1273
+ else {
1274
+ if (n < 0 || n >= Object.keys(container).length) {
1275
+ interp.stack_push(null);
1276
+ return;
1277
+ }
1278
+ const keys = Object.keys(container).sort();
1279
+ const key = keys[n];
1280
+ result = container[key];
1281
+ }
1282
+ interp.stack_push(result);
1283
+ }
1284
+ // ( array -- item )
1285
+ // ( record -- value )
1286
+ word_LAST(interp) {
1287
+ const container = interp.stack_pop();
1288
+ if (!container) {
1289
+ interp.stack_push(null);
1290
+ return;
1291
+ }
1292
+ let result;
1293
+ if (container instanceof Array) {
1294
+ if (container.length == 0)
1295
+ result = null;
1296
+ else
1297
+ result = container[container.length - 1];
1298
+ }
1299
+ else {
1300
+ const keys = Object.keys(container).sort();
1301
+ if (keys.length == 0)
1302
+ result = null;
1303
+ else
1304
+ result = container[keys[keys.length - 1]];
1305
+ }
1306
+ interp.stack_push(result);
1307
+ }
1308
+ // ( array -- a1 a2 .. an )
1309
+ // ( record -- v1 v2 .. vn )
1310
+ word_UNPACK(interp) {
1311
+ let container = interp.stack_pop();
1312
+ if (!container)
1313
+ container = [];
1314
+ if (container instanceof Array) {
1315
+ container.forEach((item) => {
1316
+ interp.stack_push(item);
1317
+ });
1318
+ }
1319
+ else {
1320
+ const keys = Object.keys(container).sort();
1321
+ keys.forEach((k) => {
1322
+ interp.stack_push(container[k]);
1323
+ });
1324
+ }
1325
+ }
1326
+ // ( array -- array )
1327
+ // ( record -- record )
1328
+ word_FLATTEN(interp) {
1329
+ let nested = interp.stack_pop();
1330
+ const flags = interp.get_flags(this.module_id);
1331
+ if (!nested)
1332
+ nested = [];
1333
+ const depth = flags.depth;
1334
+ function is_record(obj) {
1335
+ const keys = Object.keys(obj);
1336
+ return keys.length > 0;
1337
+ }
1338
+ function add_to_record_result(item, key, keys, result) {
1339
+ const new_key = keys.concat([key]).join("\t");
1340
+ result[new_key] = item;
1341
+ }
1342
+ function fully_flatten_record(record, res, keys) {
1343
+ const record_keys = Object.keys(record);
1344
+ for (const k of record_keys) {
1345
+ const item = record[k];
1346
+ if (is_record(item))
1347
+ fully_flatten_record(item, res, keys.concat([k]));
1348
+ else
1349
+ add_to_record_result(item, k, keys, res);
1350
+ }
1351
+ return res;
1352
+ }
1353
+ function flatten_record(record, depth, res, keys) {
1354
+ if (depth === undefined)
1355
+ return fully_flatten_record(record, res, keys);
1356
+ const record_keys = Object.keys(record);
1357
+ for (const k of record_keys) {
1358
+ const item = record[k];
1359
+ if (depth > 0 && is_record(item))
1360
+ flatten_record(item, depth - 1, res, keys.concat([k]));
1361
+ else
1362
+ add_to_record_result(item, k, keys, res);
1363
+ }
1364
+ return res;
1365
+ }
1366
+ let result;
1367
+ if (nested instanceof Array) {
1368
+ result = flatten_array(nested, depth);
1369
+ }
1370
+ else {
1371
+ result = flatten_record(nested, depth, {}, []);
1372
+ }
1373
+ interp.stack_push(result);
1374
+ return;
1375
+ }
1376
+ // ( array item -- index )
1377
+ // ( record item -- key )
1378
+ word_KEY_OF(interp) {
1379
+ const item = interp.stack_pop();
1380
+ let container = interp.stack_pop();
1381
+ if (!container)
1382
+ container = [];
1383
+ let result;
1384
+ if (container instanceof Array) {
1385
+ const index = container.indexOf(item);
1386
+ if (index < 0)
1387
+ result = null;
1388
+ else
1389
+ result = index;
1390
+ }
1391
+ else {
1392
+ result = null;
1393
+ const keys = Object.keys(container);
1394
+ for (let i = 0; i < keys.length; i++) {
1395
+ const k = keys[i];
1396
+ const v = container[k];
1397
+ if (v == item) {
1398
+ result = k;
1399
+ break;
1400
+ }
1401
+ }
1402
+ }
1403
+ interp.stack_push(result);
1404
+ return;
1405
+ }
1406
+ // ( array init forthic -- value )
1407
+ // ( record init forthic -- value )
1408
+ async word_REDUCE(interp) {
1409
+ const forthic = interp.stack_pop();
1410
+ const string_location = interp.get_string_location();
1411
+ const initial = interp.stack_pop();
1412
+ let container = interp.stack_pop();
1413
+ if (!container)
1414
+ container = [];
1415
+ let result;
1416
+ if (container instanceof Array) {
1417
+ interp.stack_push(initial);
1418
+ container.forEach(async (item) => {
1419
+ interp.stack_push(item);
1420
+ await interp.run(forthic, string_location);
1421
+ });
1422
+ result = interp.stack_pop();
1423
+ }
1424
+ else {
1425
+ interp.stack_push(initial);
1426
+ Object.keys(container).forEach(async (k) => {
1427
+ const v = container[k];
1428
+ interp.stack_push(v);
1429
+ await interp.run(forthic, string_location);
1430
+ });
1431
+ result = interp.stack_pop();
1432
+ }
1433
+ interp.stack_push(result);
1434
+ }
1435
+ // ( a -- )
1436
+ word_POP(interp) {
1437
+ interp.stack_pop();
1438
+ }
1439
+ // ( a -- a a )
1440
+ word_DUP(interp) {
1441
+ const a = interp.stack_pop();
1442
+ interp.stack_push(a);
1443
+ interp.stack_push(a);
1444
+ }
1445
+ // ( a b -- b a )
1446
+ word_SWAP(interp) {
1447
+ const b = interp.stack_pop();
1448
+ const a = interp.stack_pop();
1449
+ interp.stack_push(b);
1450
+ interp.stack_push(a);
1451
+ }
1452
+ // ( item -- str )
1453
+ word_to_STR(interp) {
1454
+ const item = interp.stack_pop();
1455
+ interp.stack_push(item.toString());
1456
+ }
1457
+ // ( str1 str2 -- str )
1458
+ // ( array_of_str -- str )
1459
+ word_CONCAT(interp) {
1460
+ const str2 = interp.stack_pop();
1461
+ let array;
1462
+ if (str2 instanceof Array) {
1463
+ array = str2;
1464
+ }
1465
+ else {
1466
+ const str1 = interp.stack_pop();
1467
+ array = [str1, str2];
1468
+ }
1469
+ const result = array.join("");
1470
+ interp.stack_push(result);
1471
+ }
1472
+ // ( string sep -- items )
1473
+ word_SPLIT(interp) {
1474
+ const sep = interp.stack_pop();
1475
+ let string = interp.stack_pop();
1476
+ if (!string)
1477
+ string = "";
1478
+ const result = string.split(sep);
1479
+ interp.stack_push(result);
1480
+ }
1481
+ // ( strings sep -- string )
1482
+ word_JOIN(interp) {
1483
+ const sep = interp.stack_pop();
1484
+ let strings = interp.stack_pop();
1485
+ if (!strings)
1486
+ strings = [];
1487
+ const result = strings.join(sep);
1488
+ interp.stack_push(result);
1489
+ }
1490
+ // ( -- char )
1491
+ word_slash_N(interp) {
1492
+ interp.stack_push("\n");
1493
+ }
1494
+ // ( -- char )
1495
+ word_slash_R(interp) {
1496
+ interp.stack_push("\r");
1497
+ }
1498
+ // ( -- char )
1499
+ word_slash_T(interp) {
1500
+ interp.stack_push("\t");
1501
+ }
1502
+ // ( A -- a )
1503
+ word_LOWERCASE(interp) {
1504
+ const string = interp.stack_pop();
1505
+ let result = "";
1506
+ if (string)
1507
+ result = string.toLowerCase();
1508
+ interp.stack_push(result);
1509
+ }
1510
+ // ( a -- A )
1511
+ word_UPPERCASE(interp) {
1512
+ const string = interp.stack_pop();
1513
+ let result = "";
1514
+ if (string)
1515
+ result = string.toUpperCase();
1516
+ interp.stack_push(result);
1517
+ }
1518
+ // ( string -- string )
1519
+ word_ASCII(interp) {
1520
+ let string = interp.stack_pop();
1521
+ if (!string)
1522
+ string = "";
1523
+ let result = "";
1524
+ for (let i = 0; i < string.length; i++) {
1525
+ const ch = string[i];
1526
+ if (ch.charCodeAt(0) < 256)
1527
+ result += ch;
1528
+ }
1529
+ interp.stack_push(result);
1530
+ }
1531
+ // ( str -- str )
1532
+ word_STRIP(interp) {
1533
+ const string = interp.stack_pop();
1534
+ let result = string;
1535
+ if (result)
1536
+ result = result.trim();
1537
+ interp.stack_push(result);
1538
+ }
1539
+ // ( string text replace -- string )
1540
+ word_REPLACE(interp) {
1541
+ const replace = interp.stack_pop();
1542
+ const text = interp.stack_pop();
1543
+ const string = interp.stack_pop();
1544
+ let result = string;
1545
+ if (string) {
1546
+ const pattern = new RegExp(text, "g");
1547
+ result = string.replace(pattern, replace);
1548
+ }
1549
+ interp.stack_push(result);
1550
+ }
1551
+ // ( string pattern -- match )
1552
+ word_RE_MATCH(interp) {
1553
+ const pattern = interp.stack_pop();
1554
+ const string = interp.stack_pop();
1555
+ const re_pattern = new RegExp(pattern);
1556
+ let result = false;
1557
+ if (string !== null)
1558
+ result = string.match(re_pattern);
1559
+ interp.stack_push(result);
1560
+ }
1561
+ // ( string pattern -- matches )
1562
+ word_RE_MATCH_ALL(interp) {
1563
+ const pattern = interp.stack_pop();
1564
+ const string = interp.stack_pop();
1565
+ const re_pattern = new RegExp(pattern, "g");
1566
+ let matches = [];
1567
+ if (string !== null)
1568
+ matches = string.matchAll(re_pattern);
1569
+ const result = Array.from(matches).map((v) => v[1]);
1570
+ interp.stack_push(result);
1571
+ }
1572
+ // ( match num -- string )
1573
+ word_RE_MATCH_GROUP(interp) {
1574
+ const num = interp.stack_pop();
1575
+ const match = interp.stack_pop();
1576
+ let result = null;
1577
+ if (match)
1578
+ result = match[num];
1579
+ interp.stack_push(result);
1580
+ }
1581
+ // ( time -- time )
1582
+ word_AM(interp) {
1583
+ const time = interp.stack_pop();
1584
+ if (!(time instanceof Date))
1585
+ throw "AM expecting a time";
1586
+ let result = time;
1587
+ if (time.getHours() >= 12) {
1588
+ result = new Date();
1589
+ result.setHours(time.getHours() - 12);
1590
+ result.setMinutes(time.getMinutes());
1591
+ }
1592
+ interp.stack_push(result);
1593
+ }
1594
+ // ( time -- time )
1595
+ word_PM(interp) {
1596
+ const time = interp.stack_pop();
1597
+ if (!(time instanceof Date))
1598
+ throw "PM expecting a time";
1599
+ let result = time;
1600
+ if (time.getHours() < 12) {
1601
+ result = new Date();
1602
+ result.setHours(time.getHours() + 12);
1603
+ result.setMinutes(time.getMinutes());
1604
+ }
1605
+ interp.stack_push(result);
1606
+ }
1607
+ // ( -- time )
1608
+ word_NOW(interp) {
1609
+ const result = new Date();
1610
+ interp.stack_push(result);
1611
+ }
1612
+ // ( item -- time )
1613
+ word_to_TIME(interp) {
1614
+ const item = interp.stack_pop();
1615
+ let result;
1616
+ if (item instanceof Date) {
1617
+ result = item;
1618
+ }
1619
+ else {
1620
+ // NB: We need a date in order for Date.parse to succeed. Also assuming str is a time
1621
+ const date_string = "Jan 1, 2000 " + item;
1622
+ result = new Date(Date.parse(date_string));
1623
+ }
1624
+ interp.stack_push(result);
1625
+ }
1626
+ // ( time -- str )
1627
+ word_TIME_to_STR(interp) {
1628
+ const time = interp.stack_pop();
1629
+ const result = time.getHours() + ":" + time.getMinutes();
1630
+ interp.stack_push(result);
1631
+ }
1632
+ // ( str -- date )
1633
+ word_to_DATE(interp) {
1634
+ const s = interp.stack_pop();
1635
+ const result = (0, utils_1.to_date)(s);
1636
+ interp.stack_push(result);
1637
+ }
1638
+ // ( -- date )
1639
+ word_TODAY(interp) {
1640
+ const result = new Date();
1641
+ result.setHours(0, 0, 0, 0);
1642
+ interp.stack_push(result);
1643
+ }
1644
+ // ( -- date )
1645
+ word_MONDAY(interp) {
1646
+ interp.stack_push(GlobalModule.get_day_this_week(0));
1647
+ }
1648
+ // ( -- date )
1649
+ word_TUESDAY(interp) {
1650
+ interp.stack_push(GlobalModule.get_day_this_week(1));
1651
+ }
1652
+ // ( -- date )
1653
+ word_WEDNESDAY(interp) {
1654
+ interp.stack_push(GlobalModule.get_day_this_week(2));
1655
+ }
1656
+ // ( -- date )
1657
+ word_THURSDAY(interp) {
1658
+ interp.stack_push(GlobalModule.get_day_this_week(3));
1659
+ }
1660
+ // ( -- date )
1661
+ word_FRIDAY(interp) {
1662
+ interp.stack_push(GlobalModule.get_day_this_week(4));
1663
+ }
1664
+ // ( -- date )
1665
+ word_SATURDAY(interp) {
1666
+ interp.stack_push(GlobalModule.get_day_this_week(5));
1667
+ }
1668
+ // ( -- date )
1669
+ word_SUNDAY(interp) {
1670
+ interp.stack_push(GlobalModule.get_day_this_week(6));
1671
+ }
1672
+ static get_day_this_week(day_of_week) {
1673
+ // NOTE: Monday is start of week
1674
+ function normalize_day(day) {
1675
+ return day;
1676
+ }
1677
+ const today = new Date();
1678
+ today.setHours(0, 0, 0, 0);
1679
+ const delta_days = (day_of_week - normalize_day(today.getDay())) % 7;
1680
+ const result = today;
1681
+ result.setDate(result.getDate() + delta_days + 1);
1682
+ return result;
1683
+ }
1684
+ // ( date num-days -- date )
1685
+ word_ADD_DAYS(interp) {
1686
+ const num_days = interp.stack_pop();
1687
+ const date = interp.stack_pop();
1688
+ const result = new Date(date);
1689
+ result.setDate(result.getDate() + num_days);
1690
+ interp.stack_push(result);
1691
+ }
1692
+ // ( l_date r_date -- num_days )
1693
+ word_SUBTRACT_DATES(interp) {
1694
+ const r_date = interp.stack_pop();
1695
+ const l_date = interp.stack_pop();
1696
+ const ms_per_day = 1000 * 60 * 60 * 24;
1697
+ const result = Math.round((l_date.getTime() - r_date.getTime()) / ms_per_day);
1698
+ interp.stack_push(result);
1699
+ }
1700
+ // ( date -- str )
1701
+ word_DATE_to_STR(interp) {
1702
+ const date = interp.stack_pop();
1703
+ const result = (0, utils_1.date_to_string)(date);
1704
+ interp.stack_push(result);
1705
+ }
1706
+ // ( date -- int )
1707
+ // Converts a date like 2023-11-12 to 20231112
1708
+ word_DATE_to_INT(interp) {
1709
+ const date = interp.stack_pop();
1710
+ const result = (0, utils_1.date_to_int)(date);
1711
+ interp.stack_push(result);
1712
+ }
1713
+ // ( date time -- datetime )
1714
+ word_DATE_TIME_to_DATETIME(interp) {
1715
+ const time = interp.stack_pop();
1716
+ const date = interp.stack_pop();
1717
+ const dt_string = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${time.getHours()}:${time.getMinutes()}`;
1718
+ const result = new Date(dt_string);
1719
+ interp.stack_push(result);
1720
+ }
1721
+ // ( datetime -- timestamp )
1722
+ word_DATETIME_to_TIMESTAMP(interp) {
1723
+ const datetime = interp.stack_pop();
1724
+ const result = Math.round(datetime.getTime() / 1000);
1725
+ interp.stack_push(result);
1726
+ }
1727
+ // ( timestamp -- datetime )
1728
+ word_TIMESTAMP_to_DATETIME(interp) {
1729
+ const timestamp = interp.stack_pop();
1730
+ const result = new Date(timestamp * 1000);
1731
+ interp.stack_push(result);
1732
+ }
1733
+ // ( str -- datetime )
1734
+ word_STR_to_DATETIME(interp) {
1735
+ const s = interp.stack_pop();
1736
+ const result = new Date(s);
1737
+ interp.stack_push(result);
1738
+ }
1739
+ // ( str -- timestamp )
1740
+ word_STR_to_TIMESTAMP(interp) {
1741
+ const s = interp.stack_pop();
1742
+ const datetime = new Date(s);
1743
+ const result = Math.round(datetime.getTime() / 1000);
1744
+ interp.stack_push(result);
1745
+ }
1746
+ // ( num_days -- days_ago-str )
1747
+ word_DAYS_AGO(interp) {
1748
+ const num_days = interp.stack_pop();
1749
+ let result;
1750
+ if (num_days == 0) {
1751
+ result = "today";
1752
+ }
1753
+ else if (num_days == 1) {
1754
+ result = "yesterday";
1755
+ }
1756
+ else if (num_days == -1) {
1757
+ result = "tomorrow";
1758
+ }
1759
+ else if (num_days > 0) {
1760
+ result = `${num_days} days ago`;
1761
+ }
1762
+ else {
1763
+ result = `${Math.abs(num_days)} days from now`;
1764
+ }
1765
+ interp.stack_push(result);
1766
+ }
1767
+ // ( a b -- a+b )
1768
+ // ( items -- sum )
1769
+ word_plus(interp) {
1770
+ const items = interp.stack_pop();
1771
+ if (items instanceof Array) {
1772
+ let sum = 0;
1773
+ items.forEach((item) => {
1774
+ sum += item;
1775
+ });
1776
+ interp.stack_push(sum);
1777
+ }
1778
+ else {
1779
+ const b = items;
1780
+ const a = interp.stack_pop();
1781
+ interp.stack_push(a + b);
1782
+ }
1783
+ }
1784
+ // ( a b -- a*b )
1785
+ word_times(interp) {
1786
+ const b = interp.stack_pop();
1787
+ let result = 1;
1788
+ let numbers = [];
1789
+ if (b instanceof Array) {
1790
+ numbers = b;
1791
+ }
1792
+ else {
1793
+ const a = interp.stack_pop();
1794
+ numbers = [a, b];
1795
+ }
1796
+ for (const num of numbers) {
1797
+ if (num === null || num === undefined) {
1798
+ interp.stack_push(null);
1799
+ return;
1800
+ }
1801
+ result = result * num;
1802
+ }
1803
+ interp.stack_push(result);
1804
+ }
1805
+ // ( a b -- a/b )
1806
+ word_divide_by(interp) {
1807
+ const b = interp.stack_pop();
1808
+ const a = interp.stack_pop();
1809
+ interp.stack_push(a / b);
1810
+ }
1811
+ // ( a b -- res )
1812
+ word_MOD(interp) {
1813
+ const b = interp.stack_pop();
1814
+ const a = interp.stack_pop();
1815
+ interp.stack_push(a % b);
1816
+ }
1817
+ // ( numbers -- mean )
1818
+ // ( records -- mean_record)
1819
+ word_MEAN(interp) {
1820
+ const values = interp.stack_pop();
1821
+ if (!values || values.length == 0) {
1822
+ interp.stack_push(0);
1823
+ return;
1824
+ }
1825
+ function compute_number_mean(numbers) {
1826
+ let sum = 0;
1827
+ for (const num of numbers) {
1828
+ sum += num;
1829
+ }
1830
+ const res = sum / numbers.length;
1831
+ return res;
1832
+ }
1833
+ function compute_non_number_mean(objects) {
1834
+ const non_null_objects = objects.filter((obj) => obj !== null || obj !== undefined);
1835
+ const res = {};
1836
+ non_null_objects.forEach((obj) => {
1837
+ const obj_str = (0, utils_1.is_string)(obj) ? obj : JSON.stringify(obj);
1838
+ if (res[obj_str])
1839
+ res[obj_str]++;
1840
+ else
1841
+ res[obj_str] = 1;
1842
+ });
1843
+ Object.entries(res).forEach(([key, value]) => {
1844
+ res[key] = value / non_null_objects.length;
1845
+ });
1846
+ return res;
1847
+ }
1848
+ function compute_object_mean(records) {
1849
+ const res = {};
1850
+ records.forEach((record) => {
1851
+ Object.entries(record).forEach(([key, value]) => {
1852
+ if (res[key])
1853
+ res[key].push(value);
1854
+ else
1855
+ res[key] = [value];
1856
+ });
1857
+ });
1858
+ Object.entries(res).forEach(([key, values]) => {
1859
+ res[key] = compute_mean(values);
1860
+ });
1861
+ return res;
1862
+ }
1863
+ function is_all_numbers(values) {
1864
+ return values.every((val) => typeof val == "number");
1865
+ }
1866
+ function is_all_records(values) {
1867
+ return values.every((val) => (0, utils_1.is_record)(val));
1868
+ }
1869
+ function select_non_null_values(values) {
1870
+ return values.filter((val) => val !== null && val !== undefined);
1871
+ }
1872
+ function compute_mean(values) {
1873
+ let result;
1874
+ if ((0, utils_1.is_array)(values)) {
1875
+ const non_null_values = select_non_null_values(values);
1876
+ if (is_all_numbers(non_null_values)) {
1877
+ result = compute_number_mean(non_null_values);
1878
+ }
1879
+ else if (is_all_records(non_null_values)) {
1880
+ result = compute_object_mean(non_null_values);
1881
+ }
1882
+ else {
1883
+ result = compute_non_number_mean(non_null_values);
1884
+ }
1885
+ }
1886
+ else {
1887
+ result = null;
1888
+ }
1889
+ return result;
1890
+ }
1891
+ const result = compute_mean(values);
1892
+ interp.stack_push(result);
1893
+ }
1894
+ // ( num1 num2 -- num )
1895
+ // ( numbers -- num )
1896
+ word_MAX(interp) {
1897
+ const num2 = interp.stack_pop();
1898
+ let numbers = [];
1899
+ if ((0, utils_1.is_array)(num2)) {
1900
+ numbers = num2;
1901
+ }
1902
+ else {
1903
+ const num1 = interp.stack_pop();
1904
+ numbers = [num1, num2];
1905
+ }
1906
+ interp.stack_push(Math.max(...numbers));
1907
+ }
1908
+ // ( num1 num2 -- num )
1909
+ // ( numbers -- num )
1910
+ word_MIN(interp) {
1911
+ const num2 = interp.stack_pop();
1912
+ let numbers = [];
1913
+ if ((0, utils_1.is_array)(num2)) {
1914
+ numbers = num2;
1915
+ }
1916
+ else {
1917
+ const num1 = interp.stack_pop();
1918
+ numbers = [num1, num2];
1919
+ }
1920
+ interp.stack_push(Math.min(...numbers));
1921
+ }
1922
+ // ( num -- int )
1923
+ word_ROUND(interp) {
1924
+ const num = interp.stack_pop();
1925
+ interp.stack_push(Math.round(num));
1926
+ }
1927
+ // ( l r -- bool )
1928
+ word_equal_equal(interp) {
1929
+ const r = interp.stack_pop();
1930
+ const l = interp.stack_pop();
1931
+ interp.stack_push(l == r);
1932
+ }
1933
+ // ( l r -- bool )
1934
+ word_not_equal(interp) {
1935
+ const r = interp.stack_pop();
1936
+ const l = interp.stack_pop();
1937
+ interp.stack_push(l != r);
1938
+ }
1939
+ // ( l r -- bool )
1940
+ word_greater_than(interp) {
1941
+ const r = interp.stack_pop();
1942
+ const l = interp.stack_pop();
1943
+ if (l === null || r === null) {
1944
+ interp.stack_push(null);
1945
+ return;
1946
+ }
1947
+ interp.stack_push(l > r);
1948
+ }
1949
+ // ( l r -- bool )
1950
+ word_greater_than_or_equal(interp) {
1951
+ const r = interp.stack_pop();
1952
+ const l = interp.stack_pop();
1953
+ if (l === null || r === null) {
1954
+ interp.stack_push(null);
1955
+ return;
1956
+ }
1957
+ interp.stack_push(l >= r);
1958
+ }
1959
+ // ( l r -- bool )
1960
+ word_less_than(interp) {
1961
+ const r = interp.stack_pop();
1962
+ const l = interp.stack_pop();
1963
+ if (l === null || r === null) {
1964
+ interp.stack_push(null);
1965
+ return;
1966
+ }
1967
+ interp.stack_push(l < r);
1968
+ }
1969
+ // ( l r -- bool )
1970
+ word_less_than_or_equal(interp) {
1971
+ const r = interp.stack_pop();
1972
+ const l = interp.stack_pop();
1973
+ if (l === null || r === null) {
1974
+ interp.stack_push(null);
1975
+ return;
1976
+ }
1977
+ interp.stack_push(l <= r);
1978
+ }
1979
+ // ( l r -- bool )
1980
+ // ( items -- bool )
1981
+ word_OR(interp) {
1982
+ const r = interp.stack_pop();
1983
+ let items;
1984
+ if (r instanceof Array) {
1985
+ items = r;
1986
+ }
1987
+ else {
1988
+ const l = interp.stack_pop();
1989
+ items = [l, r];
1990
+ }
1991
+ const result = items.some((item) => item);
1992
+ interp.stack_push(result);
1993
+ }
1994
+ // ( l r -- bool )
1995
+ // ( items -- bool )
1996
+ word_AND(interp) {
1997
+ const r = interp.stack_pop();
1998
+ let items;
1999
+ if (r instanceof Array) {
2000
+ items = r;
2001
+ }
2002
+ else {
2003
+ const l = interp.stack_pop();
2004
+ items = [l, r];
2005
+ }
2006
+ const result = items.every((item) => item);
2007
+ interp.stack_push(result);
2008
+ }
2009
+ // ( bool -- bool )
2010
+ word_NOT(interp) {
2011
+ const value = interp.stack_pop();
2012
+ interp.stack_push(!value);
2013
+ }
2014
+ // ( item items -- bool )
2015
+ word_IN(interp) {
2016
+ let items = interp.stack_pop();
2017
+ const item = interp.stack_pop();
2018
+ if (!items)
2019
+ items = [];
2020
+ const result = items.indexOf(item) >= 0;
2021
+ interp.stack_push(result);
2022
+ }
2023
+ // ( vals required_vals -- bool )
2024
+ word_ANY(interp) {
2025
+ const required_vals = interp.stack_pop();
2026
+ const vals = interp.stack_pop();
2027
+ let result = false;
2028
+ for (let i = 0; i < required_vals.length; i++) {
2029
+ const rv = required_vals[i];
2030
+ if (vals.indexOf(rv) >= 0) {
2031
+ result = true;
2032
+ break;
2033
+ }
2034
+ }
2035
+ // If nothing is required, then all values are true
2036
+ if (required_vals.length == 0)
2037
+ result = true;
2038
+ interp.stack_push(result);
2039
+ }
2040
+ // ( vals required_vals -- bool )
2041
+ word_ALL(interp) {
2042
+ let required_vals = interp.stack_pop();
2043
+ let vals = interp.stack_pop();
2044
+ if (!vals)
2045
+ vals = [];
2046
+ if (!required_vals)
2047
+ required_vals = [];
2048
+ const result = required_vals.every((val) => vals.indexOf(val) >= 0);
2049
+ interp.stack_push(result);
2050
+ }
2051
+ // ( item -- bool )
2052
+ word_to_BOOL(interp) {
2053
+ const item = interp.stack_pop();
2054
+ const result = !!item;
2055
+ interp.stack_push(result);
2056
+ }
2057
+ // ( item -- int )
2058
+ word_to_INT(interp) {
2059
+ const str = interp.stack_pop();
2060
+ const result = parseInt(str);
2061
+ interp.stack_push(result);
2062
+ }
2063
+ // ( item -- int )
2064
+ word_to_FLOAT(interp) {
2065
+ const str = interp.stack_pop();
2066
+ const result = parseFloat(str);
2067
+ interp.stack_push(result);
2068
+ }
2069
+ // ( num buckets -- bucket )
2070
+ // Each bucket has three elements: [low high value]. If num is >= low and < high, it's in the bucket
2071
+ // If a number isn't in any bucket, null is returned
2072
+ word_BUCKET(interp) {
2073
+ const buckets = interp.stack_pop();
2074
+ const num = interp.stack_pop();
2075
+ let result = null;
2076
+ for (let i = 0; i < buckets.length; i++) {
2077
+ const low = buckets[i][0];
2078
+ const high = buckets[i][1];
2079
+ const value = buckets[i][2];
2080
+ if (num >= low && num < high) {
2081
+ result = value;
2082
+ break;
2083
+ }
2084
+ }
2085
+ if (num == undefined)
2086
+ result = "";
2087
+ interp.stack_push(result);
2088
+ }
2089
+ // ( low high -- int )
2090
+ word_UNIFORM_RANDOM(interp) {
2091
+ const high = interp.stack_pop();
2092
+ const low = interp.stack_pop();
2093
+ // From: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
2094
+ function getRandomIntInclusive(min, max) {
2095
+ min = Math.ceil(min);
2096
+ max = Math.floor(max);
2097
+ return Math.floor(Math.random() * (max - min + 1) + min); //The maximum is inclusive and the minimum is inclusive
2098
+ }
2099
+ const result = getRandomIntInclusive(low, high);
2100
+ interp.stack_push(result);
2101
+ }
2102
+ // ( val start_ranges -- index )
2103
+ word_RANGE_INDEX(interp) {
2104
+ const start_ranges = interp.stack_pop();
2105
+ const val = interp.stack_pop();
2106
+ // Cap off the value ranges with infinity
2107
+ start_ranges.push(Infinity);
2108
+ if (val === null || !start_ranges) {
2109
+ interp.stack_push(null);
2110
+ return;
2111
+ }
2112
+ if (val < start_ranges[0]) {
2113
+ interp.stack_push(null);
2114
+ return;
2115
+ }
2116
+ let result = null;
2117
+ for (let i = 0; i < start_ranges.length - 1; i++) {
2118
+ if (val >= start_ranges[i] && val < start_ranges[i + 1]) {
2119
+ result = i;
2120
+ break;
2121
+ }
2122
+ }
2123
+ interp.stack_push(result);
2124
+ }
2125
+ // ( items start_ranges fvalue -- value_by_bucket )
2126
+ async word_RANGE_BUCKETS(interp) {
2127
+ const fvalue = interp.stack_pop();
2128
+ const string_location = interp.get_string_location();
2129
+ const start_ranges = interp.stack_pop();
2130
+ const items = interp.stack_pop();
2131
+ if (!items || !start_ranges) {
2132
+ interp.stack_push({});
2133
+ return;
2134
+ }
2135
+ const result = { BEFORE: [] };
2136
+ for (const range_start of start_ranges) {
2137
+ result[range_start] = [];
2138
+ }
2139
+ for (const item of items) {
2140
+ interp.stack_push(item);
2141
+ await interp.run(fvalue, string_location);
2142
+ const value = interp.stack_pop();
2143
+ let bucket = "BEFORE";
2144
+ for (const range_start of start_ranges) {
2145
+ if (value < range_start) {
2146
+ break;
2147
+ }
2148
+ bucket = range_start;
2149
+ if (value === range_start) {
2150
+ break;
2151
+ }
2152
+ }
2153
+ result[bucket].push(item);
2154
+ }
2155
+ interp.stack_push(result);
2156
+ }
2157
+ // ( -- Infinity )
2158
+ async word_INFINITY(interp) {
2159
+ interp.stack_push(Infinity);
2160
+ }
2161
+ // ( -- )
2162
+ word_bang_PUSH_ERROR(interp) {
2163
+ interp.modify_flags(this.module_id, { push_error: true });
2164
+ }
2165
+ // ( -- )
2166
+ word_bang_WITH_KEY(interp) {
2167
+ interp.modify_flags(this.module_id, { with_key: true });
2168
+ }
2169
+ // (comparator -- )
2170
+ //
2171
+ // `comparator` may be a Forthic string or a Python key function
2172
+ word_bang_COMPARATOR(interp) {
2173
+ const comparator = interp.stack_pop();
2174
+ interp.modify_flags(this.module_id, { comparator });
2175
+ }
2176
+ // ( -- )
2177
+ word_bang_PUSH_REST(interp) {
2178
+ interp.modify_flags(this.module_id, { push_rest: true });
2179
+ }
2180
+ // (depth -- )
2181
+ //
2182
+ // NOTE: `depth` of 0 is the same not having set depth
2183
+ word_bang_DEPTH(interp) {
2184
+ const depth = interp.stack_pop();
2185
+ interp.modify_flags(this.module_id, { depth });
2186
+ }
2187
+ // ( bool -- )
2188
+ word_bang_OVERWRITE(interp) {
2189
+ const overwrite = interp.stack_pop();
2190
+ interp.modify_flags(this.module_id, { overwrite });
2191
+ }
2192
+ // ( delay_ms -- )
2193
+ word_bang_DELAY(interp) {
2194
+ const delay_ms = interp.stack_pop();
2195
+ interp.modify_flags(this.module_id, { delay: delay_ms });
2196
+ }
2197
+ // ( num_interps -- )
2198
+ word_bang_INTERPS(interp) {
2199
+ const num_interps = interp.stack_pop();
2200
+ interp.modify_flags(this.module_id, { interps: num_interps });
2201
+ }
2202
+ // ( label -- )
2203
+ word_bang_NOTE_PROGRESS(interp) {
2204
+ const label = interp.stack_pop();
2205
+ interp.modify_flags(this.module_id, { note_progress: label });
2206
+ }
2207
+ // (str -- encoded)
2208
+ word_URL_ENCODE(interp) {
2209
+ const str = interp.stack_pop();
2210
+ let result = "";
2211
+ if (str)
2212
+ result = encodeURIComponent(str);
2213
+ interp.stack_push(result);
2214
+ }
2215
+ // (urlencoded -- decoded)
2216
+ word_URL_DECODE(interp) {
2217
+ const urlencoded = interp.stack_pop();
2218
+ let result = "";
2219
+ if (urlencoded)
2220
+ result = decodeURIComponent(urlencoded);
2221
+ interp.stack_push(result);
2222
+ }
2223
+ // ( -- char)
2224
+ word_QUOTE_CHAR(interp) {
2225
+ interp.stack_push(DLE);
2226
+ }
2227
+ // ( string -- quoted_string)
2228
+ word_QUOTED(interp) {
2229
+ const string = interp.stack_pop();
2230
+ let clean_string = "";
2231
+ for (let i = 0; i < string.length; i++) {
2232
+ let c = string[i];
2233
+ if (c == DLE)
2234
+ c = " ";
2235
+ clean_string += c;
2236
+ }
2237
+ const result = `${DLE}${clean_string}${DLE}`;
2238
+ interp.stack_push(result);
2239
+ }
2240
+ // ( object -- )
2241
+ word_CONSOLE_LOG(interp) {
2242
+ const object = interp.stack_pop();
2243
+ console.log(object);
2244
+ interp.stack_push(object);
2245
+ }
2246
+ // ( -- )
2247
+ word_PROFILE_START(interp) {
2248
+ interp.start_profiling();
2249
+ }
2250
+ // ( -- )
2251
+ word_PROFILE_END(interp) {
2252
+ interp.stop_profiling();
2253
+ }
2254
+ // ( label -- )
2255
+ word_PROFILE_TIMESTAMP(interp) {
2256
+ const label = interp.stack_pop();
2257
+ interp.add_timestamp(label);
2258
+ }
2259
+ // ( -- )
2260
+ word_PROFILE_DATA(interp) {
2261
+ const histogram = interp.word_histogram();
2262
+ const timestamps = interp.profile_timestamps();
2263
+ const result = {
2264
+ word_counts: [],
2265
+ timestamps: [],
2266
+ };
2267
+ histogram.forEach((val) => {
2268
+ const rec = { word: val["word"], count: val["count"] };
2269
+ result["word_counts"].push(rec);
2270
+ });
2271
+ let prev_time = 0.0;
2272
+ timestamps.forEach((t) => {
2273
+ const rec = {
2274
+ label: t["label"],
2275
+ time_ms: t["time_ms"],
2276
+ delta: t["time_ms"] - prev_time,
2277
+ };
2278
+ prev_time = t["time_ms"];
2279
+ result["timestamps"].push(rec);
2280
+ });
2281
+ interp.stack_push(result);
2282
+ }
2283
+ // ( -- null )
2284
+ word_NULL(interp) {
2285
+ interp.stack_push(null);
2286
+ }
2287
+ // ( value default_value -- value )
2288
+ word_DEFAULT(interp) {
2289
+ const default_value = interp.stack_pop();
2290
+ let value = interp.stack_pop();
2291
+ if (value === undefined || value === null || value === "")
2292
+ value = default_value;
2293
+ interp.stack_push(value);
2294
+ }
2295
+ // ( value default_forthic -- value )
2296
+ async word_star_DEFAULT(interp) {
2297
+ const default_forthic = interp.stack_pop();
2298
+ const string_location = interp.get_string_location();
2299
+ let value = interp.stack_pop();
2300
+ if (value === undefined || value === null || value === "") {
2301
+ await interp.run(default_forthic, string_location);
2302
+ value = interp.stack_pop();
2303
+ }
2304
+ interp.stack_push(value);
2305
+ }
2306
+ // ( Record default_key/vals -- Record )
2307
+ word_REC_DEFAULTS(interp) {
2308
+ const key_vals = interp.stack_pop();
2309
+ const record = interp.stack_pop();
2310
+ key_vals.forEach((key_val) => {
2311
+ const key = key_val[0];
2312
+ const value = record[key];
2313
+ if (value === undefined || value === null || value == "") {
2314
+ record[key] = key_val[1];
2315
+ }
2316
+ });
2317
+ interp.stack_push(record);
2318
+ }
2319
+ // ( item forthic num-times -- ? )
2320
+ async word_l_REPEAT(interp) {
2321
+ const num_times = interp.stack_pop();
2322
+ const forthic = interp.stack_pop();
2323
+ const string_location = interp.get_string_location();
2324
+ for (let i = 0; i < num_times; i++) {
2325
+ // Store item so we can push it back later
2326
+ const item = interp.stack_pop();
2327
+ interp.stack_push(item);
2328
+ await interp.run(forthic, string_location);
2329
+ const res = interp.stack_pop();
2330
+ // Push original item and result
2331
+ interp.stack_push(item);
2332
+ interp.stack_push(res);
2333
+ }
2334
+ }
2335
+ // ( a -- a )
2336
+ async word_IDENTITY(_interp) { }
2337
+ // ( value num_places -- str )
2338
+ word_to_FIXED(interp) {
2339
+ const num_places = interp.stack_pop();
2340
+ const value = interp.stack_pop();
2341
+ let result = value;
2342
+ if (value === undefined || value === null)
2343
+ result = "";
2344
+ else if (!isNaN(value))
2345
+ result = value.toFixed(num_places);
2346
+ interp.stack_push(result);
2347
+ }
2348
+ // ( object -- json )
2349
+ word_to_JSON(interp) {
2350
+ const object = interp.stack_pop();
2351
+ const result = JSON.stringify(object);
2352
+ interp.stack_push(result);
2353
+ }
2354
+ // ( json -- json )
2355
+ word_PRETTIFY(interp) {
2356
+ const json = interp.stack_pop();
2357
+ const result = (0, utils_1.pretty_print)(json);
2358
+ interp.stack_push(result);
2359
+ }
2360
+ // ( json -- object )
2361
+ word_JSON_to(interp) {
2362
+ const str = interp.stack_pop();
2363
+ let result = null;
2364
+ if (str)
2365
+ result = JSON.parse(str);
2366
+ interp.stack_push(result);
2367
+ }
2368
+ // ( name -- ? )
2369
+ async word_LOAD_SCREEN(interp) {
2370
+ const name = interp.stack_pop();
2371
+ const screen_forthic = interp.get_screen_forthic(name);
2372
+ const location = new tokenizer_1.CodeLocation({ screen_name: name });
2373
+ // await interp.run(screen_forthic, location);
2374
+ await interp.run(screen_forthic, location);
2375
+ }
2376
+ // ( -- )
2377
+ word_dot_s(interp) {
2378
+ const stack = interp.stack;
2379
+ console.log(stack);
2380
+ }
2381
+ // ( a b -- a - b )
2382
+ word_minus(interp) {
2383
+ const b = interp.stack_pop();
2384
+ const a = interp.stack_pop();
2385
+ interp.stack_push(a - b);
2386
+ }
2387
+ }
2388
+ exports.GlobalModule = GlobalModule;
2389
+ // Descends into record using an array of fields, returning final value or null
2390
+ function drill_for_value(record, fields) {
2391
+ let result = record;
2392
+ for (let i = 0; i < fields.length; i++) {
2393
+ const f = fields[i];
2394
+ if (result == null)
2395
+ return null;
2396
+ result = result[f];
2397
+ }
2398
+ if (result === undefined)
2399
+ result = null;
2400
+ return result;
2401
+ }
2402
+ async function execute_returning_error(interp, forthic, string_location) {
2403
+ let result = null;
2404
+ try {
2405
+ await interp.run(forthic, string_location);
2406
+ }
2407
+ catch (e) {
2408
+ result = e;
2409
+ }
2410
+ return result;
2411
+ }
2412
+ function fully_flatten_array(items, accum) {
2413
+ for (let i = 0; i < items.length; i++) {
2414
+ const item = items[i];
2415
+ if (item instanceof Array)
2416
+ fully_flatten_array(item, accum);
2417
+ else
2418
+ accum.push(item);
2419
+ }
2420
+ return accum;
2421
+ }
2422
+ function flatten_array(items, depth, accum = []) {
2423
+ if (depth === undefined)
2424
+ return fully_flatten_array(items, accum);
2425
+ for (let i = 0; i < items.length; i++) {
2426
+ const item = items[i];
2427
+ if (depth > 0 && item instanceof Array)
2428
+ flatten_array(item, depth - 1, accum);
2429
+ else
2430
+ accum.push(item);
2431
+ }
2432
+ return accum;
2433
+ }
2434
+ //# sourceMappingURL=global_module.js.map