arc-lang 0.6.5 → 0.6.7
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/interpreter.js +43 -9
- package/dist/lexer.js +1 -1
- package/dist/repl.js +3 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/stdlib/collections.arc +2 -1
- package/stdlib/json.arc +4 -2
- package/stdlib/regex.arc +3 -3
package/dist/interpreter.js
CHANGED
|
@@ -215,6 +215,12 @@ function resolveAsync(v) {
|
|
|
215
215
|
function makePrelude(env) {
|
|
216
216
|
const fns = {
|
|
217
217
|
print: (...args) => { console.log(args.map(toStr).join(" ")); return null; },
|
|
218
|
+
throw: (msg) => { throw new Error(toStr(msg ?? "error")); },
|
|
219
|
+
arity: (v) => {
|
|
220
|
+
if (v && typeof v === "object" && v.__fn)
|
|
221
|
+
return (v.params || []).length;
|
|
222
|
+
return null;
|
|
223
|
+
},
|
|
218
224
|
len: (v) => {
|
|
219
225
|
if (typeof v === "string")
|
|
220
226
|
return [...v].length; // codepoint count, not UTF-16
|
|
@@ -262,6 +268,25 @@ function makePrelude(env) {
|
|
|
262
268
|
return String(a).localeCompare(String(b));
|
|
263
269
|
});
|
|
264
270
|
},
|
|
271
|
+
sort_by: (list, fn) => {
|
|
272
|
+
if (!Array.isArray(list))
|
|
273
|
+
throw new Error("sort_by expects a list as first argument");
|
|
274
|
+
if (!fn || !fn.__fn)
|
|
275
|
+
throw new Error("sort_by expects a function as second argument");
|
|
276
|
+
const fnVal = fn;
|
|
277
|
+
const paramCount = fnVal.params ? fnVal.params.length : 0;
|
|
278
|
+
if (paramCount !== 1) {
|
|
279
|
+
throw new ArcRuntimeError(`sort_by takes a key function fn(x) that returns a sort key, not a comparator fn(a, b). ` +
|
|
280
|
+
`For example: sort_by(list, fn(x) => x.name) — not sort_by(list, fn(a, b) => a - b)`, { code: ErrorCode.WRONG_ARITY, category: "TypeError" });
|
|
281
|
+
}
|
|
282
|
+
return [...list].sort((a, b) => {
|
|
283
|
+
const ka = callFn(fnVal, [a]);
|
|
284
|
+
const kb = callFn(fnVal, [b]);
|
|
285
|
+
if (typeof ka === "number" && typeof kb === "number")
|
|
286
|
+
return ka - kb;
|
|
287
|
+
return String(ka).localeCompare(String(kb));
|
|
288
|
+
});
|
|
289
|
+
},
|
|
265
290
|
take: (list, n) => Array.isArray(list) ? list.slice(0, n) : null,
|
|
266
291
|
drop: (list, n) => Array.isArray(list) ? list.slice(n) : null,
|
|
267
292
|
find: (list, fn) => {
|
|
@@ -556,21 +581,30 @@ function makePrelude(env) {
|
|
|
556
581
|
}
|
|
557
582
|
},
|
|
558
583
|
error_throw: (code, message) => {
|
|
584
|
+
if (message === undefined || message === null) {
|
|
585
|
+
throw new Error(toStr(code));
|
|
586
|
+
}
|
|
559
587
|
throw new Error(`[${toStr(code)}] ${toStr(message)}`);
|
|
560
588
|
},
|
|
561
589
|
error_retry: (fn, times) => {
|
|
562
590
|
const n = typeof times === "number" ? times : 1;
|
|
563
591
|
let lastErr = null;
|
|
564
592
|
for (let i = 0; i < n; i++) {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
593
|
+
try {
|
|
594
|
+
const result = typeof fn === "function" ? fn() : callFn(fn, []);
|
|
595
|
+
if (result && typeof result === "object" && "__map" in result) {
|
|
596
|
+
const entries = result.entries;
|
|
597
|
+
if (entries.has("kind") && entries.has("message")) {
|
|
598
|
+
lastErr = result;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
571
601
|
}
|
|
602
|
+
return result;
|
|
603
|
+
}
|
|
604
|
+
catch (e) {
|
|
605
|
+
lastErr = e instanceof Error ? e.message : String(e);
|
|
606
|
+
continue;
|
|
572
607
|
}
|
|
573
|
-
return result;
|
|
574
608
|
}
|
|
575
609
|
return lastErr;
|
|
576
610
|
},
|
|
@@ -2548,14 +2582,14 @@ function evalExpr(expr, env) {
|
|
|
2548
2582
|
if (typeof left !== "number" || typeof right !== "number")
|
|
2549
2583
|
throw new ArcRuntimeError(`TypeError: cannot divide non-numbers`, { code: ErrorCode.INVALID_OPERATOR, loc: expr.loc });
|
|
2550
2584
|
if (right === 0)
|
|
2551
|
-
|
|
2585
|
+
throw new ArcRuntimeError("Division by zero", { code: ErrorCode.INVALID_OPERATOR, loc: expr.loc });
|
|
2552
2586
|
return left / right;
|
|
2553
2587
|
}
|
|
2554
2588
|
case "%": {
|
|
2555
2589
|
if (typeof left !== "number" || typeof right !== "number")
|
|
2556
2590
|
throw new ArcRuntimeError(`TypeError: cannot modulo non-numbers`, { code: ErrorCode.INVALID_OPERATOR, loc: expr.loc });
|
|
2557
2591
|
if (right === 0)
|
|
2558
|
-
|
|
2592
|
+
throw new ArcRuntimeError("Division by zero", { code: ErrorCode.INVALID_OPERATOR, loc: expr.loc });
|
|
2559
2593
|
return left % right;
|
|
2560
2594
|
}
|
|
2561
2595
|
case "**": {
|
package/dist/lexer.js
CHANGED
|
@@ -150,7 +150,7 @@ export function lex(source) {
|
|
|
150
150
|
str += advance();
|
|
151
151
|
continue;
|
|
152
152
|
}
|
|
153
|
-
if (peek() === "{") {
|
|
153
|
+
if (peek() === "{" && /[a-zA-Z_]/.test(peek(1))) {
|
|
154
154
|
hasInterp = true;
|
|
155
155
|
if (str.length > 0 || parts.length === 0) {
|
|
156
156
|
parts.push(tok(TokenType.String, str, sl, sc));
|
package/dist/repl.js
CHANGED
|
@@ -5,6 +5,7 @@ import { lex } from "./lexer.js";
|
|
|
5
5
|
import { parse } from "./parser.js";
|
|
6
6
|
import { createEnv, interpretWithEnv, toStr } from "./interpreter.js";
|
|
7
7
|
import { createUseHandler } from "./modules.js";
|
|
8
|
+
import { ARC_VERSION } from "./version.js";
|
|
8
9
|
const GREEN = "\x1b[32m";
|
|
9
10
|
const RED = "\x1b[31m";
|
|
10
11
|
const CYAN = "\x1b[36m";
|
|
@@ -101,7 +102,7 @@ function main() {
|
|
|
101
102
|
output: process.stdout,
|
|
102
103
|
prompt: "arc> ",
|
|
103
104
|
});
|
|
104
|
-
console.log(`${CYAN}Arc REPL
|
|
105
|
+
console.log(`${CYAN}Arc REPL v${ARC_VERSION}${RESET} — Type ${YELLOW}:help${RESET} for commands, ${YELLOW}:builtins${RESET} for functions`);
|
|
105
106
|
rl.prompt();
|
|
106
107
|
let buffer = "";
|
|
107
108
|
let braceDepth = 0;
|
|
@@ -109,7 +110,7 @@ function main() {
|
|
|
109
110
|
const trimmed = line.trim();
|
|
110
111
|
// Handle commands (only when not in multi-line mode)
|
|
111
112
|
if (braceDepth === 0) {
|
|
112
|
-
if (trimmed === ":quit" || trimmed === ":q") {
|
|
113
|
+
if (trimmed === ":quit" || trimmed === ":q" || trimmed === ":exit") {
|
|
113
114
|
console.log("Goodbye!");
|
|
114
115
|
process.exit(0);
|
|
115
116
|
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
package/stdlib/collections.arc
CHANGED
|
@@ -110,7 +110,8 @@ pub fn max_by(list, f) {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
pub fn sort_by(list, f) {
|
|
113
|
-
#
|
|
113
|
+
# sort_by takes a key function fn(x), not a comparator fn(a, b)
|
|
114
|
+
assert(arity(f) == 1, "sort_by takes a key function fn(x) that returns a sort key, not a comparator fn(a, b). Example: sort_by(list, fn(x) => x.name)")
|
|
114
115
|
let mut arr = map(list, x => x)
|
|
115
116
|
for i in 1..len(arr) {
|
|
116
117
|
let mut j = i
|
package/stdlib/json.arc
CHANGED
|
@@ -41,10 +41,12 @@ fn _quote(s) {
|
|
|
41
41
|
|
|
42
42
|
pub fn from_json(s) {
|
|
43
43
|
let trimmed = trim(s)
|
|
44
|
-
if len(trimmed) == 0 {
|
|
44
|
+
if len(trimmed) == 0 { error_throw("Invalid JSON: empty string") }
|
|
45
45
|
el {
|
|
46
46
|
let parsed = _parse_value(trimmed)
|
|
47
|
-
parsed
|
|
47
|
+
if parsed == nil { error_throw("Invalid JSON: " ++ s) }
|
|
48
|
+
el if len(trim(parsed.rest)) > 0 { error_throw("Invalid JSON: unexpected trailing content") }
|
|
49
|
+
el { parsed.value }
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
|
package/stdlib/regex.arc
CHANGED
|
@@ -19,14 +19,14 @@ pub fn test(pattern, text) {
|
|
|
19
19
|
regex_test(re, text)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
# Replaces
|
|
23
|
-
# (For
|
|
22
|
+
# Replaces the first occurrence of `pattern` with `replacement` in `text`
|
|
23
|
+
# (For global replacement, use replace_all)
|
|
24
24
|
pub fn replace(pattern, replacement, text) {
|
|
25
25
|
let re = regex_new(pattern)
|
|
26
26
|
regex_replace(re, replacement, text)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
#
|
|
29
|
+
# Alias for replace — replaces only the first occurrence
|
|
30
30
|
pub fn replace_first(pattern, replacement, text) {
|
|
31
31
|
let re = regex_new(pattern)
|
|
32
32
|
regex_replace(re, replacement, text)
|