@forthic/interp 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/forthic/errors.d.ts +1 -0
- package/dist/cjs/forthic/errors.js +6 -0
- package/dist/cjs/forthic/errors.js.map +1 -1
- package/dist/cjs/forthic/global_module.js +53 -8
- package/dist/cjs/forthic/global_module.js.map +1 -1
- package/dist/cjs/forthic/interpreter.d.ts +1 -1
- package/dist/cjs/forthic/interpreter.js +4 -1
- package/dist/cjs/forthic/interpreter.js.map +1 -1
- package/dist/esm/forthic/errors.d.ts +1 -0
- package/dist/esm/forthic/errors.js +20 -31
- package/dist/esm/forthic/errors.js.map +1 -1
- package/dist/esm/forthic/global_module/map_word.js +3 -7
- package/dist/esm/forthic/global_module/map_word.js.map +1 -1
- package/dist/esm/forthic/global_module.js +96 -55
- package/dist/esm/forthic/global_module.js.map +1 -1
- package/dist/esm/forthic/interpreter.d.ts +1 -1
- package/dist/esm/forthic/interpreter.js +57 -60
- package/dist/esm/forthic/interpreter.js.map +1 -1
- package/dist/esm/forthic/module.js +15 -28
- package/dist/esm/forthic/module.js.map +1 -1
- package/dist/esm/forthic/tokenizer.js +10 -20
- package/dist/esm/forthic/tokenizer.js.map +1 -1
- package/dist/esm/forthic/utils.js +16 -28
- package/dist/esm/forthic/utils.js.map +1 -1
- package/dist/esm/index.js +4 -20
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const map_word_1 = require("./global_module/map_word");
|
|
8
|
-
const tokenizer_1 = require("./tokenizer");
|
|
9
|
-
const errors_1 = require("./errors");
|
|
1
|
+
import { Module, PushValueWord } from "./module";
|
|
2
|
+
import { is_array, is_record, is_string, pretty_print, to_date, date_to_string, date_to_int, } from "./utils";
|
|
3
|
+
import { Temporal } from "temporal-polyfill";
|
|
4
|
+
import { MapWord } from "./global_module/map_word";
|
|
5
|
+
import { CodeLocation } from "./tokenizer";
|
|
6
|
+
import { IntentionalStopError, InvalidVariableNameError } from "./errors";
|
|
10
7
|
const DLE = String.fromCharCode(16); // ASCII char for "Data Link Escape" used as an untypeable quote
|
|
11
|
-
class GlobalModule extends
|
|
8
|
+
export class GlobalModule extends Module {
|
|
12
9
|
literal_handlers;
|
|
13
10
|
constructor(interp) {
|
|
14
11
|
super("<GLOBAL>", interp);
|
|
@@ -226,7 +223,7 @@ class GlobalModule extends module_1.Module {
|
|
|
226
223
|
for (let i = 0; i < self.literal_handlers.length; i++) {
|
|
227
224
|
value = self.literal_handlers[i](string);
|
|
228
225
|
if (value !== null)
|
|
229
|
-
return new
|
|
226
|
+
return new PushValueWord(string, value);
|
|
230
227
|
}
|
|
231
228
|
return null;
|
|
232
229
|
}
|
|
@@ -256,16 +253,61 @@ class GlobalModule extends module_1.Module {
|
|
|
256
253
|
return result;
|
|
257
254
|
}
|
|
258
255
|
to_literal_date(str_val) {
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
// --------------------
|
|
257
|
+
// Handle date format strings like "YYYY-MM-DD" or "YYYY-MM-03" or "YYYY-02-03" or "2025-02-03"
|
|
258
|
+
let year;
|
|
259
|
+
let month;
|
|
260
|
+
let day;
|
|
261
|
+
const date_parts = str_val.split("-");
|
|
262
|
+
const zonedDateTime = Temporal.Now.zonedDateTimeISO(this.interp?.get_timezone() ?? "UTC");
|
|
263
|
+
// Case 1: If time is like "YYYY-MM-DD"
|
|
264
|
+
if (str_val.match(/^YYYY-MM-DD$/)) {
|
|
265
|
+
year = zonedDateTime.year;
|
|
266
|
+
month = zonedDateTime.month;
|
|
267
|
+
day = zonedDateTime.day;
|
|
268
|
+
} // Case 2: If time is like "YYYY-MM-03"
|
|
269
|
+
else if (str_val.match(/^YYYY-MM-\d{2}$/)) {
|
|
270
|
+
year = zonedDateTime.year;
|
|
271
|
+
month = zonedDateTime.month;
|
|
272
|
+
day = parseInt(date_parts[2]);
|
|
273
|
+
} // Case 3: If time is like "YYYY-02-03"
|
|
274
|
+
else if (str_val.match(/^YYYY-\d{2}-\d{2}$/)) {
|
|
275
|
+
year = zonedDateTime.year;
|
|
276
|
+
month = parseInt(date_parts[1]);
|
|
277
|
+
day = parseInt(date_parts[2]);
|
|
278
|
+
} // Case 4: If time is like "2025-02-03"
|
|
279
|
+
else if (str_val.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
280
|
+
year = parseInt(date_parts[0]);
|
|
281
|
+
month = parseInt(date_parts[1]);
|
|
282
|
+
day = parseInt(date_parts[2]);
|
|
283
|
+
} // Case 5: If time is like "2025-MM-DD"
|
|
284
|
+
else if (str_val.match(/^\d{4}-MM-DD$/)) {
|
|
285
|
+
year = parseInt(date_parts[0]);
|
|
286
|
+
month = zonedDateTime.month;
|
|
287
|
+
day = zonedDateTime.day;
|
|
288
|
+
} // Case 6: If time is like "2025-02-DD"
|
|
289
|
+
else if (str_val.match(/^\d{4}-\d{2}-DD$/)) {
|
|
290
|
+
year = parseInt(date_parts[0]);
|
|
291
|
+
month = parseInt(date_parts[1]);
|
|
292
|
+
day = zonedDateTime.day;
|
|
293
|
+
} // Case 7: If time is like "2025-MM-03"
|
|
294
|
+
else if (str_val.match(/^\d{4}-MM-\d{2}$/)) {
|
|
295
|
+
year = parseInt(date_parts[0]);
|
|
296
|
+
month = zonedDateTime.month;
|
|
297
|
+
day = parseInt(date_parts[2]);
|
|
298
|
+
} // Case 8: If time is like "YYYY-03-DD"
|
|
299
|
+
else if (str_val.match(/^YYYY-\d{2}-DD$/)) {
|
|
300
|
+
year = zonedDateTime.year;
|
|
301
|
+
month = parseInt(date_parts[1]);
|
|
302
|
+
day = zonedDateTime.day;
|
|
303
|
+
} // Otherwise, return null
|
|
304
|
+
else {
|
|
261
305
|
return null;
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
month,
|
|
268
|
-
day,
|
|
306
|
+
}
|
|
307
|
+
const result = Temporal.PlainDate.from({
|
|
308
|
+
year: year,
|
|
309
|
+
month: month,
|
|
310
|
+
day: day,
|
|
269
311
|
});
|
|
270
312
|
return result;
|
|
271
313
|
}
|
|
@@ -279,7 +321,7 @@ class GlobalModule extends module_1.Module {
|
|
|
279
321
|
return null;
|
|
280
322
|
if (minutes >= 60)
|
|
281
323
|
return null;
|
|
282
|
-
const result =
|
|
324
|
+
const result = Temporal.PlainTime.from({
|
|
283
325
|
hour: hours,
|
|
284
326
|
minute: minutes,
|
|
285
327
|
});
|
|
@@ -304,7 +346,7 @@ class GlobalModule extends module_1.Module {
|
|
|
304
346
|
const module = interp.cur_module();
|
|
305
347
|
varnames.forEach((v) => {
|
|
306
348
|
if (v.match(/__.*/)) {
|
|
307
|
-
throw new
|
|
349
|
+
throw new InvalidVariableNameError(interp.get_top_input_string(), v, "Variable names cannot begin with '__'", interp.get_string_location());
|
|
308
350
|
}
|
|
309
351
|
module.add_variable(v);
|
|
310
352
|
});
|
|
@@ -653,7 +695,7 @@ class GlobalModule extends module_1.Module {
|
|
|
653
695
|
const string_location = interp.get_string_location();
|
|
654
696
|
const items = interp.stack_pop();
|
|
655
697
|
const flags = interp.get_flags(this.module_id);
|
|
656
|
-
const map_word = new
|
|
698
|
+
const map_word = new MapWord(items, forthic, string_location, flags);
|
|
657
699
|
await map_word.execute(interp);
|
|
658
700
|
}
|
|
659
701
|
// ( items word -- ? )
|
|
@@ -1586,11 +1628,11 @@ class GlobalModule extends module_1.Module {
|
|
|
1586
1628
|
// ( time -- time )
|
|
1587
1629
|
word_AM(interp) {
|
|
1588
1630
|
const time = interp.stack_pop();
|
|
1589
|
-
if (!(time instanceof
|
|
1631
|
+
if (!(time instanceof Temporal.PlainTime))
|
|
1590
1632
|
throw "AM expecting a time";
|
|
1591
1633
|
let result = time;
|
|
1592
1634
|
if (time.hour >= 12) {
|
|
1593
|
-
result =
|
|
1635
|
+
result = Temporal.PlainTime.from({
|
|
1594
1636
|
hour: time.hour - 12,
|
|
1595
1637
|
minute: time.minute,
|
|
1596
1638
|
});
|
|
@@ -1600,11 +1642,11 @@ class GlobalModule extends module_1.Module {
|
|
|
1600
1642
|
// ( time -- time )
|
|
1601
1643
|
word_PM(interp) {
|
|
1602
1644
|
const time = interp.stack_pop();
|
|
1603
|
-
if (!(time instanceof
|
|
1645
|
+
if (!(time instanceof Temporal.PlainTime))
|
|
1604
1646
|
throw "PM expecting a time";
|
|
1605
1647
|
let result = time;
|
|
1606
1648
|
if (time.hour < 12) {
|
|
1607
|
-
result =
|
|
1649
|
+
result = Temporal.PlainTime.from({
|
|
1608
1650
|
hour: time.hour + 12,
|
|
1609
1651
|
minute: time.minute,
|
|
1610
1652
|
});
|
|
@@ -1613,24 +1655,24 @@ class GlobalModule extends module_1.Module {
|
|
|
1613
1655
|
}
|
|
1614
1656
|
// ( -- time )
|
|
1615
1657
|
word_NOW(interp) {
|
|
1616
|
-
const result =
|
|
1658
|
+
const result = Temporal.Now.plainDateTimeISO(interp.get_timezone());
|
|
1617
1659
|
interp.stack_push(result);
|
|
1618
1660
|
}
|
|
1619
1661
|
// ( item -- time )
|
|
1620
1662
|
word_to_TIME(interp) {
|
|
1621
1663
|
const item = interp.stack_pop();
|
|
1622
1664
|
let result;
|
|
1623
|
-
if (item instanceof
|
|
1665
|
+
if (item instanceof Temporal.PlainTime) {
|
|
1624
1666
|
result = item;
|
|
1625
1667
|
}
|
|
1626
|
-
else if (item instanceof
|
|
1627
|
-
result =
|
|
1668
|
+
else if (item instanceof Temporal.PlainDateTime) {
|
|
1669
|
+
result = Temporal.PlainTime.from(item);
|
|
1628
1670
|
}
|
|
1629
1671
|
else {
|
|
1630
1672
|
// NB: We need a date in order for Date.parse to succeed. Also assuming str is a time
|
|
1631
1673
|
const date_string = "Jan 1, 2000 " + item;
|
|
1632
1674
|
const date = new Date(Date.parse(date_string));
|
|
1633
|
-
result =
|
|
1675
|
+
result = Temporal.PlainTime.from({
|
|
1634
1676
|
hour: date.getHours(),
|
|
1635
1677
|
minute: date.getMinutes(),
|
|
1636
1678
|
});
|
|
@@ -1646,12 +1688,12 @@ class GlobalModule extends module_1.Module {
|
|
|
1646
1688
|
// ( str -- date )
|
|
1647
1689
|
word_to_DATE(interp) {
|
|
1648
1690
|
const s = interp.stack_pop();
|
|
1649
|
-
const result =
|
|
1691
|
+
const result = to_date(s);
|
|
1650
1692
|
interp.stack_push(result);
|
|
1651
1693
|
}
|
|
1652
1694
|
// ( -- date )
|
|
1653
1695
|
word_TODAY(interp) {
|
|
1654
|
-
const result =
|
|
1696
|
+
const result = Temporal.Now.plainDateISO(interp.get_timezone());
|
|
1655
1697
|
interp.stack_push(result);
|
|
1656
1698
|
}
|
|
1657
1699
|
// ( -- date )
|
|
@@ -1684,7 +1726,7 @@ class GlobalModule extends module_1.Module {
|
|
|
1684
1726
|
}
|
|
1685
1727
|
static get_day_this_week(day_of_week, timezone) {
|
|
1686
1728
|
// Get plain date for day of week
|
|
1687
|
-
const today =
|
|
1729
|
+
const today = Temporal.Now.plainDateISO(timezone);
|
|
1688
1730
|
const delta_days = (day_of_week - today.dayOfWeek) % 7;
|
|
1689
1731
|
const result = today.add({ days: delta_days });
|
|
1690
1732
|
return result;
|
|
@@ -1693,13 +1735,13 @@ class GlobalModule extends module_1.Module {
|
|
|
1693
1735
|
word_ADD_DAYS(interp) {
|
|
1694
1736
|
const num_days = interp.stack_pop();
|
|
1695
1737
|
const date = interp.stack_pop();
|
|
1696
|
-
const result =
|
|
1738
|
+
const result = Temporal.PlainDate.from(date).add({ days: num_days });
|
|
1697
1739
|
interp.stack_push(result);
|
|
1698
1740
|
}
|
|
1699
1741
|
// ( l_date r_date -- num_days )
|
|
1700
1742
|
word_SUBTRACT_DATES(interp) {
|
|
1701
|
-
const r_date =
|
|
1702
|
-
const l_date =
|
|
1743
|
+
const r_date = Temporal.PlainDate.from(interp.stack_pop());
|
|
1744
|
+
const l_date = Temporal.PlainDate.from(interp.stack_pop());
|
|
1703
1745
|
// Subtract the dates and get the difference in days
|
|
1704
1746
|
const result = r_date.until(l_date).total({ unit: "days" });
|
|
1705
1747
|
interp.stack_push(result);
|
|
@@ -1707,21 +1749,21 @@ class GlobalModule extends module_1.Module {
|
|
|
1707
1749
|
// ( date -- str )
|
|
1708
1750
|
word_DATE_to_STR(interp) {
|
|
1709
1751
|
const date = interp.stack_pop();
|
|
1710
|
-
const result =
|
|
1752
|
+
const result = date_to_string(date);
|
|
1711
1753
|
interp.stack_push(result);
|
|
1712
1754
|
}
|
|
1713
1755
|
// ( date -- int )
|
|
1714
1756
|
// Converts a date like 2023-11-12 to 20231112
|
|
1715
1757
|
word_DATE_to_INT(interp) {
|
|
1716
1758
|
const date = interp.stack_pop();
|
|
1717
|
-
const result =
|
|
1759
|
+
const result = date_to_int(date);
|
|
1718
1760
|
interp.stack_push(result);
|
|
1719
1761
|
}
|
|
1720
1762
|
// ( date time -- datetime )
|
|
1721
1763
|
word_DATE_TIME_to_DATETIME(interp) {
|
|
1722
|
-
const time =
|
|
1723
|
-
const date =
|
|
1724
|
-
const result =
|
|
1764
|
+
const time = Temporal.PlainTime.from(interp.stack_pop());
|
|
1765
|
+
const date = Temporal.PlainDate.from(interp.stack_pop());
|
|
1766
|
+
const result = Temporal.PlainDateTime.from({
|
|
1725
1767
|
year: date.year,
|
|
1726
1768
|
month: date.month,
|
|
1727
1769
|
day: date.day,
|
|
@@ -1741,14 +1783,14 @@ class GlobalModule extends module_1.Module {
|
|
|
1741
1783
|
// ( timestamp -- datetime )
|
|
1742
1784
|
word_TIMESTAMP_to_DATETIME(interp) {
|
|
1743
1785
|
const timestamp = interp.stack_pop();
|
|
1744
|
-
const result =
|
|
1786
|
+
const result = Temporal.Instant.fromEpochSeconds(timestamp).toZonedDateTime({ timeZone: interp.get_timezone(), calendar: "iso8601" });
|
|
1745
1787
|
interp.stack_push(result);
|
|
1746
1788
|
}
|
|
1747
1789
|
// ( str -- datetime )
|
|
1748
1790
|
word_STR_to_DATETIME(interp) {
|
|
1749
1791
|
const s = interp.stack_pop();
|
|
1750
1792
|
const date = new Date(s);
|
|
1751
|
-
const result =
|
|
1793
|
+
const result = Temporal.Instant.fromEpochMilliseconds(date.getTime()).toZonedDateTime({ timeZone: interp.get_timezone(), calendar: "iso8601" });
|
|
1752
1794
|
interp.stack_push(result);
|
|
1753
1795
|
}
|
|
1754
1796
|
// ( str -- timestamp )
|
|
@@ -1849,7 +1891,7 @@ class GlobalModule extends module_1.Module {
|
|
|
1849
1891
|
const non_null_objects = objects.filter((obj) => obj !== null || obj !== undefined);
|
|
1850
1892
|
const res = {};
|
|
1851
1893
|
non_null_objects.forEach((obj) => {
|
|
1852
|
-
const obj_str =
|
|
1894
|
+
const obj_str = is_string(obj) ? obj : JSON.stringify(obj);
|
|
1853
1895
|
if (res[obj_str])
|
|
1854
1896
|
res[obj_str]++;
|
|
1855
1897
|
else
|
|
@@ -1879,14 +1921,14 @@ class GlobalModule extends module_1.Module {
|
|
|
1879
1921
|
return values.every((val) => typeof val == "number");
|
|
1880
1922
|
}
|
|
1881
1923
|
function is_all_records(values) {
|
|
1882
|
-
return values.every((val) =>
|
|
1924
|
+
return values.every((val) => is_record(val));
|
|
1883
1925
|
}
|
|
1884
1926
|
function select_non_null_values(values) {
|
|
1885
1927
|
return values.filter((val) => val !== null && val !== undefined);
|
|
1886
1928
|
}
|
|
1887
1929
|
function compute_mean(values) {
|
|
1888
1930
|
let result;
|
|
1889
|
-
if (
|
|
1931
|
+
if (is_array(values)) {
|
|
1890
1932
|
const non_null_values = select_non_null_values(values);
|
|
1891
1933
|
if (is_all_numbers(non_null_values)) {
|
|
1892
1934
|
result = compute_number_mean(non_null_values);
|
|
@@ -1911,7 +1953,7 @@ class GlobalModule extends module_1.Module {
|
|
|
1911
1953
|
word_MAX(interp) {
|
|
1912
1954
|
const num2 = interp.stack_pop();
|
|
1913
1955
|
let numbers = [];
|
|
1914
|
-
if (
|
|
1956
|
+
if (is_array(num2)) {
|
|
1915
1957
|
numbers = num2;
|
|
1916
1958
|
}
|
|
1917
1959
|
else {
|
|
@@ -1925,7 +1967,7 @@ class GlobalModule extends module_1.Module {
|
|
|
1925
1967
|
word_MIN(interp) {
|
|
1926
1968
|
const num2 = interp.stack_pop();
|
|
1927
1969
|
let numbers = [];
|
|
1928
|
-
if (
|
|
1970
|
+
if (is_array(num2)) {
|
|
1929
1971
|
numbers = num2;
|
|
1930
1972
|
}
|
|
1931
1973
|
else {
|
|
@@ -2379,7 +2421,7 @@ class GlobalModule extends module_1.Module {
|
|
|
2379
2421
|
// ( json -- json )
|
|
2380
2422
|
word_PRETTIFY(interp) {
|
|
2381
2423
|
const json = interp.stack_pop();
|
|
2382
|
-
const result =
|
|
2424
|
+
const result = pretty_print(json);
|
|
2383
2425
|
interp.stack_push(result);
|
|
2384
2426
|
}
|
|
2385
2427
|
// ( json -- object )
|
|
@@ -2394,7 +2436,7 @@ class GlobalModule extends module_1.Module {
|
|
|
2394
2436
|
async word_LOAD_SCREEN(interp) {
|
|
2395
2437
|
const name = interp.stack_pop();
|
|
2396
2438
|
const screen_forthic = interp.get_screen_forthic(name);
|
|
2397
|
-
const location = new
|
|
2439
|
+
const location = new CodeLocation({ screen_name: name });
|
|
2398
2440
|
// await interp.run(screen_forthic, location);
|
|
2399
2441
|
await interp.run(screen_forthic, location);
|
|
2400
2442
|
}
|
|
@@ -2407,13 +2449,13 @@ class GlobalModule extends module_1.Module {
|
|
|
2407
2449
|
else {
|
|
2408
2450
|
console.log("<STACK EMPTY>");
|
|
2409
2451
|
}
|
|
2410
|
-
throw new
|
|
2452
|
+
throw new IntentionalStopError(".s");
|
|
2411
2453
|
}
|
|
2412
2454
|
// ( -- )
|
|
2413
2455
|
word_dot_S(interp) {
|
|
2414
2456
|
const stack = interp.get_stack().get_items().reverse();
|
|
2415
2457
|
console.log(JSON.stringify(stack, null, 2));
|
|
2416
|
-
throw new
|
|
2458
|
+
throw new IntentionalStopError(".S");
|
|
2417
2459
|
}
|
|
2418
2460
|
// ( a b -- a - b )
|
|
2419
2461
|
word_minus(interp) {
|
|
@@ -2422,7 +2464,6 @@ class GlobalModule extends module_1.Module {
|
|
|
2422
2464
|
interp.stack_push(a - b);
|
|
2423
2465
|
}
|
|
2424
2466
|
}
|
|
2425
|
-
exports.GlobalModule = GlobalModule;
|
|
2426
2467
|
// Descends into record using an array of fields, returning final value or null
|
|
2427
2468
|
function drill_for_value(record, fields) {
|
|
2428
2469
|
let result = record;
|