arc-lang 0.6.2 → 0.6.4
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/README.md +65 -130
- package/dist/ast.d.ts +30 -2
- package/dist/build.js +1 -1
- package/dist/formatter.js +15 -3
- package/dist/index.js +104 -0
- package/dist/interpreter.js +130 -17
- package/dist/lexer.d.ts +41 -37
- package/dist/lexer.js +47 -39
- package/dist/linter.js +18 -0
- package/dist/modules.js +5 -1
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +91 -11
- package/dist/repl.js +66 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -2
- package/stdlib/API_DESIGN.md +357 -0
- package/stdlib/ASYNC_DESIGN.md +815 -0
- package/stdlib/EXAMPLES.md +710 -0
- package/stdlib/MODULES.md +854 -0
- package/stdlib/README.md +64 -0
- package/stdlib/collections.arc +140 -0
- package/stdlib/crypto.arc +62 -0
- package/stdlib/csv.arc +40 -0
- package/stdlib/datetime.arc +75 -0
- package/stdlib/embed.arc +42 -0
- package/stdlib/env.arc +10 -0
- package/stdlib/error.arc +36 -0
- package/stdlib/html.arc +30 -0
- package/stdlib/http.arc +43 -0
- package/stdlib/io.arc +28 -0
- package/stdlib/json.arc +204 -0
- package/stdlib/llm.arc +193 -0
- package/stdlib/log.arc +20 -0
- package/stdlib/map.arc +72 -0
- package/stdlib/math.arc +133 -0
- package/stdlib/net.arc +17 -0
- package/stdlib/os.arc +81 -0
- package/stdlib/path.arc +11 -0
- package/stdlib/prompt.arc +49 -0
- package/stdlib/regex.arc +59 -0
- package/stdlib/result.arc +50 -0
- package/stdlib/store.arc +62 -0
- package/stdlib/strings.arc +44 -0
- package/stdlib/test.arc +61 -0
- package/stdlib/time.arc +19 -0
- package/stdlib/toml.arc +10 -0
- package/stdlib/yaml.arc +10 -0
package/dist/interpreter.js
CHANGED
|
@@ -126,7 +126,7 @@ function syncFetch(method, url, body) {
|
|
|
126
126
|
const bodyJson = bodyStr != null ? JSON.stringify(bodyStr) : "null";
|
|
127
127
|
// Pass config via env to avoid shell escaping issues
|
|
128
128
|
const fetchConfig = JSON.stringify({ method, url, body: bodyStr, headers: customHeaders });
|
|
129
|
-
const script = `const c=JSON.parse(process.env.ARC_FETCH);(async()=>{const o={method:c.method,headers:{...c.headers}};if(c.body!==null){o.body=c.body;if(!o.headers["Content-Type"])o.headers["Content-Type"]="application/json";}try{const r=await fetch(c.url,o);const t=await r.text();let d;try{d=JSON.parse(t)}catch{d=t}console.log(JSON.stringify({ok:
|
|
129
|
+
const script = `const c=JSON.parse(process.env.ARC_FETCH);(async()=>{const o={method:c.method,headers:{...c.headers}};if(c.body!==null){o.body=c.body;if(!o.headers["Content-Type"])o.headers["Content-Type"]="application/json";}try{const r=await fetch(c.url,o);const t=await r.text();let d;try{d=JSON.parse(t)}catch{d=t}console.log(JSON.stringify({ok:r.status>=200&&r.status<300,status:r.status,data:d}))}catch(e){console.log(JSON.stringify({ok:false,status:0,data:e.message}))}})()`;
|
|
130
130
|
try {
|
|
131
131
|
const raw = execSync(`node -e "${script.replace(/"/g, '\\"')}"`, {
|
|
132
132
|
timeout: 30000,
|
|
@@ -182,7 +182,7 @@ function makePrelude(env) {
|
|
|
182
182
|
print: (...args) => { console.log(args.map(toStr).join(" ")); return null; },
|
|
183
183
|
len: (v) => {
|
|
184
184
|
if (typeof v === "string")
|
|
185
|
-
return v.length;
|
|
185
|
+
return [...v].length; // codepoint count, not UTF-16
|
|
186
186
|
if (Array.isArray(v))
|
|
187
187
|
return v.length;
|
|
188
188
|
if (v && typeof v === "object" && "__map" in v)
|
|
@@ -276,8 +276,28 @@ function makePrelude(env) {
|
|
|
276
276
|
},
|
|
277
277
|
starts: (s, pre) => typeof s === "string" ? s.startsWith(pre) : false,
|
|
278
278
|
ends: (s, suf) => typeof s === "string" ? s.endsWith(suf) : false,
|
|
279
|
-
int: (v) =>
|
|
280
|
-
|
|
279
|
+
int: (v) => {
|
|
280
|
+
if (typeof v === "number")
|
|
281
|
+
return Math.floor(v);
|
|
282
|
+
if (typeof v === "string") {
|
|
283
|
+
const n = parseInt(v);
|
|
284
|
+
if (isNaN(n))
|
|
285
|
+
throw new ArcRuntimeError(`ValueError: cannot convert '${v}' to int`, { code: ErrorCode.TYPE_MISMATCH });
|
|
286
|
+
return n;
|
|
287
|
+
}
|
|
288
|
+
return 0;
|
|
289
|
+
},
|
|
290
|
+
float: (v) => {
|
|
291
|
+
if (typeof v === "number")
|
|
292
|
+
return v;
|
|
293
|
+
if (typeof v === "string") {
|
|
294
|
+
const n = parseFloat(v);
|
|
295
|
+
if (isNaN(n))
|
|
296
|
+
throw new ArcRuntimeError(`ValueError: cannot convert '${v}' to float`, { code: ErrorCode.TYPE_MISMATCH });
|
|
297
|
+
return n;
|
|
298
|
+
}
|
|
299
|
+
return 0;
|
|
300
|
+
},
|
|
281
301
|
str: (v) => toStr(v),
|
|
282
302
|
bool: (v) => isTruthy(v),
|
|
283
303
|
min: (...args) => {
|
|
@@ -2361,6 +2381,11 @@ class ReturnSignal {
|
|
|
2361
2381
|
this.value = value;
|
|
2362
2382
|
}
|
|
2363
2383
|
}
|
|
2384
|
+
// Break/Continue signals for loops
|
|
2385
|
+
class BreakSignal {
|
|
2386
|
+
}
|
|
2387
|
+
class ContinueSignal {
|
|
2388
|
+
}
|
|
2364
2389
|
// Evaluate expression in tail position — returns TCOSignal for self-recursive tail calls
|
|
2365
2390
|
function evalExprTCO(expr, env, fnName) {
|
|
2366
2391
|
// Only handle tail-position expressions specially
|
|
@@ -2469,20 +2494,14 @@ function evalExpr(expr, env) {
|
|
|
2469
2494
|
if (typeof left !== "number" || typeof right !== "number")
|
|
2470
2495
|
throw new ArcRuntimeError(`TypeError: cannot divide non-numbers`, { code: ErrorCode.INVALID_OPERATOR, loc: expr.loc });
|
|
2471
2496
|
if (right === 0)
|
|
2472
|
-
|
|
2473
|
-
code: ErrorCode.DIVISION_BY_ZERO, loc: expr.loc,
|
|
2474
|
-
suggestion: "Check that the divisor is not zero before dividing.",
|
|
2475
|
-
});
|
|
2497
|
+
return left === 0 ? null : (left > 0 ? Infinity : -Infinity);
|
|
2476
2498
|
return left / right;
|
|
2477
2499
|
}
|
|
2478
2500
|
case "%": {
|
|
2479
2501
|
if (typeof left !== "number" || typeof right !== "number")
|
|
2480
2502
|
throw new ArcRuntimeError(`TypeError: cannot modulo non-numbers`, { code: ErrorCode.INVALID_OPERATOR, loc: expr.loc });
|
|
2481
2503
|
if (right === 0)
|
|
2482
|
-
|
|
2483
|
-
code: ErrorCode.DIVISION_BY_ZERO, loc: expr.loc,
|
|
2484
|
-
suggestion: "Check that the divisor is not zero before dividing.",
|
|
2485
|
-
});
|
|
2504
|
+
return null;
|
|
2486
2505
|
return left % right;
|
|
2487
2506
|
}
|
|
2488
2507
|
case "**": {
|
|
@@ -2576,8 +2595,24 @@ function evalExpr(expr, env) {
|
|
|
2576
2595
|
if (obj && typeof obj === "object" && "__map" in obj) {
|
|
2577
2596
|
return obj.entries.get(expr.property) ?? null;
|
|
2578
2597
|
}
|
|
2579
|
-
|
|
2580
|
-
|
|
2598
|
+
// Teaching error messages for common method-style access
|
|
2599
|
+
const prop = expr.property;
|
|
2600
|
+
const methodBuiltins = {
|
|
2601
|
+
length: "len(value)", repeat: "repeat(str, n)", trim: "trim(str)",
|
|
2602
|
+
split: "split(str, sep)", join: "join(list, sep)", replace: "replace(str, old, new)",
|
|
2603
|
+
includes: "contains(value, sub)", startsWith: "starts(str, prefix)", endsWith: "ends(str, suffix)",
|
|
2604
|
+
toUpperCase: "upper(str)", toLowerCase: "lower(str)", indexOf: "index_of(str, sub)",
|
|
2605
|
+
slice: "slice(value, start, end)", map: "map(list, fn)", filter: "filter(list, fn)",
|
|
2606
|
+
reduce: "reduce(list, fn, init)", find: "find(list, fn)", sort: "sort(list)",
|
|
2607
|
+
reverse: "reverse(list)", push: "push(list, item)", concat: "concat(a, b)",
|
|
2608
|
+
forEach: "for item in list { ... }", charAt: "char_at(str, i)",
|
|
2609
|
+
toString: "str(value)", match: "use regex; regex.find(str, pattern)",
|
|
2610
|
+
};
|
|
2611
|
+
const suggestion = methodBuiltins[prop]
|
|
2612
|
+
? `Arc uses free functions, not methods. Try: ${methodBuiltins[prop]}`
|
|
2613
|
+
: undefined;
|
|
2614
|
+
throw new ArcRuntimeError(`Cannot access property '${prop}' on ${toStr(obj)}`, {
|
|
2615
|
+
code: ErrorCode.PROPERTY_ACCESS, loc: expr.loc, suggestion,
|
|
2581
2616
|
});
|
|
2582
2617
|
}
|
|
2583
2618
|
case "OptionalMemberExpr": {
|
|
@@ -2606,6 +2641,23 @@ function evalExpr(expr, env) {
|
|
|
2606
2641
|
// Not a Result type — just return the value
|
|
2607
2642
|
return val;
|
|
2608
2643
|
}
|
|
2644
|
+
case "TryCatchExpr": {
|
|
2645
|
+
try {
|
|
2646
|
+
return evalExpr(expr.body, env);
|
|
2647
|
+
}
|
|
2648
|
+
catch (e) {
|
|
2649
|
+
if (e instanceof ReturnSignal)
|
|
2650
|
+
throw e;
|
|
2651
|
+
if (e instanceof BreakSignal)
|
|
2652
|
+
throw e;
|
|
2653
|
+
if (e instanceof ContinueSignal)
|
|
2654
|
+
throw e;
|
|
2655
|
+
const catchEnv = new Env(env);
|
|
2656
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
2657
|
+
catchEnv.set(expr.catchVar, errMsg);
|
|
2658
|
+
return evalExpr(expr.catchBody, catchEnv);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2609
2661
|
case "IndexExpr": {
|
|
2610
2662
|
const obj = evalExpr(expr.object, env);
|
|
2611
2663
|
const idx = evalExpr(expr.index, env);
|
|
@@ -2786,10 +2838,11 @@ function evalExpr(expr, env) {
|
|
|
2786
2838
|
return resolveAsync(val);
|
|
2787
2839
|
}
|
|
2788
2840
|
case "FetchExpr": {
|
|
2789
|
-
|
|
2841
|
+
const results = expr.targets.map(t => {
|
|
2790
2842
|
const val = evalExpr(t, env);
|
|
2791
2843
|
return resolveAsync(val);
|
|
2792
2844
|
});
|
|
2845
|
+
return results.length === 1 ? results[0] : results;
|
|
2793
2846
|
}
|
|
2794
2847
|
case "BlockExpr": {
|
|
2795
2848
|
const blockEnv = new Env(env);
|
|
@@ -2894,14 +2947,72 @@ function evalStmt(stmt, env) {
|
|
|
2894
2947
|
loopEnv.set(n, m.get(n) ?? null);
|
|
2895
2948
|
}
|
|
2896
2949
|
}
|
|
2897
|
-
|
|
2950
|
+
try {
|
|
2951
|
+
result = evalExpr(stmt.body, loopEnv);
|
|
2952
|
+
}
|
|
2953
|
+
catch (e) {
|
|
2954
|
+
if (e instanceof BreakSignal)
|
|
2955
|
+
break;
|
|
2956
|
+
if (e instanceof ContinueSignal)
|
|
2957
|
+
continue;
|
|
2958
|
+
throw e;
|
|
2959
|
+
}
|
|
2898
2960
|
}
|
|
2899
2961
|
return result;
|
|
2900
2962
|
}
|
|
2963
|
+
case "WhileStmt": {
|
|
2964
|
+
let result = null;
|
|
2965
|
+
while (isTruthy(evalExpr(stmt.condition, env))) {
|
|
2966
|
+
try {
|
|
2967
|
+
result = evalExpr(stmt.body, env);
|
|
2968
|
+
}
|
|
2969
|
+
catch (e) {
|
|
2970
|
+
if (e instanceof BreakSignal)
|
|
2971
|
+
break;
|
|
2972
|
+
if (e instanceof ContinueSignal)
|
|
2973
|
+
continue;
|
|
2974
|
+
throw e;
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
return result;
|
|
2978
|
+
}
|
|
2979
|
+
case "TryCatchStmt": {
|
|
2980
|
+
try {
|
|
2981
|
+
return evalExpr(stmt.body, env);
|
|
2982
|
+
}
|
|
2983
|
+
catch (e) {
|
|
2984
|
+
if (e instanceof ReturnSignal)
|
|
2985
|
+
throw e; // don't catch return/break/continue
|
|
2986
|
+
if (e instanceof BreakSignal)
|
|
2987
|
+
throw e;
|
|
2988
|
+
if (e instanceof ContinueSignal)
|
|
2989
|
+
throw e;
|
|
2990
|
+
const catchEnv = new Env(env);
|
|
2991
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
2992
|
+
catchEnv.set(stmt.catchVar, errMsg);
|
|
2993
|
+
return evalExpr(stmt.catchBody, catchEnv);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2901
2996
|
case "DoStmt": {
|
|
2902
2997
|
let result = null;
|
|
2903
2998
|
do {
|
|
2904
|
-
|
|
2999
|
+
try {
|
|
3000
|
+
result = evalExpr(stmt.body, env);
|
|
3001
|
+
}
|
|
3002
|
+
catch (e) {
|
|
3003
|
+
if (e instanceof BreakSignal) {
|
|
3004
|
+
break;
|
|
3005
|
+
}
|
|
3006
|
+
if (e instanceof ContinueSignal) {
|
|
3007
|
+
const cond = evalExpr(stmt.condition, env);
|
|
3008
|
+
if (stmt.isWhile && !isTruthy(cond))
|
|
3009
|
+
break;
|
|
3010
|
+
if (!stmt.isWhile && isTruthy(cond))
|
|
3011
|
+
break;
|
|
3012
|
+
continue;
|
|
3013
|
+
}
|
|
3014
|
+
throw e;
|
|
3015
|
+
}
|
|
2905
3016
|
const cond = evalExpr(stmt.condition, env);
|
|
2906
3017
|
if (stmt.isWhile && !isTruthy(cond))
|
|
2907
3018
|
break;
|
|
@@ -2910,6 +3021,8 @@ function evalStmt(stmt, env) {
|
|
|
2910
3021
|
} while (true);
|
|
2911
3022
|
return result;
|
|
2912
3023
|
}
|
|
3024
|
+
case "BreakStmt": throw new BreakSignal();
|
|
3025
|
+
case "ContinueStmt": throw new ContinueSignal();
|
|
2913
3026
|
case "AssignStmt": {
|
|
2914
3027
|
const value = evalExpr(stmt.value, env);
|
|
2915
3028
|
env.assign(stmt.target, value);
|
package/dist/lexer.d.ts
CHANGED
|
@@ -34,43 +34,47 @@ export declare enum TokenType {
|
|
|
34
34
|
Where = 32,
|
|
35
35
|
Matching = 33,
|
|
36
36
|
Fetch = 34,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
37
|
+
Break = 35,
|
|
38
|
+
Continue = 36,
|
|
39
|
+
Try = 37,
|
|
40
|
+
Catch = 38,
|
|
41
|
+
Plus = 39,
|
|
42
|
+
Minus = 40,
|
|
43
|
+
Star = 41,
|
|
44
|
+
Slash = 42,
|
|
45
|
+
Percent = 43,
|
|
46
|
+
Power = 44,
|
|
47
|
+
Eq = 45,
|
|
48
|
+
Neq = 46,
|
|
49
|
+
Lt = 47,
|
|
50
|
+
Gt = 48,
|
|
51
|
+
Lte = 49,
|
|
52
|
+
Gte = 50,
|
|
53
|
+
Pipe = 51,
|
|
54
|
+
Bar = 52,
|
|
55
|
+
FatArrow = 53,
|
|
56
|
+
Arrow = 54,
|
|
57
|
+
Question = 55,
|
|
58
|
+
QuestionDot = 56,
|
|
59
|
+
Range = 57,
|
|
60
|
+
Concat = 58,
|
|
61
|
+
At = 59,
|
|
62
|
+
Hash = 60,
|
|
63
|
+
DotDotDot = 61,
|
|
64
|
+
Assign = 62,
|
|
65
|
+
LParen = 63,
|
|
66
|
+
RParen = 64,
|
|
67
|
+
LBrace = 65,
|
|
68
|
+
RBrace = 66,
|
|
69
|
+
LBracket = 67,
|
|
70
|
+
RBracket = 68,
|
|
71
|
+
Comma = 69,
|
|
72
|
+
Colon = 70,
|
|
73
|
+
Dot = 71,
|
|
74
|
+
Semicolon = 72,
|
|
75
|
+
Newline = 73,
|
|
76
|
+
Regex = 74,
|
|
77
|
+
EOF = 75
|
|
74
78
|
}
|
|
75
79
|
export interface Token {
|
|
76
80
|
type: TokenType;
|
package/dist/lexer.js
CHANGED
|
@@ -38,47 +38,51 @@ export var TokenType;
|
|
|
38
38
|
TokenType[TokenType["Where"] = 32] = "Where";
|
|
39
39
|
TokenType[TokenType["Matching"] = 33] = "Matching";
|
|
40
40
|
TokenType[TokenType["Fetch"] = 34] = "Fetch";
|
|
41
|
+
TokenType[TokenType["Break"] = 35] = "Break";
|
|
42
|
+
TokenType[TokenType["Continue"] = 36] = "Continue";
|
|
43
|
+
TokenType[TokenType["Try"] = 37] = "Try";
|
|
44
|
+
TokenType[TokenType["Catch"] = 38] = "Catch";
|
|
41
45
|
// Operators
|
|
42
|
-
TokenType[TokenType["Plus"] =
|
|
43
|
-
TokenType[TokenType["Minus"] =
|
|
44
|
-
TokenType[TokenType["Star"] =
|
|
45
|
-
TokenType[TokenType["Slash"] =
|
|
46
|
-
TokenType[TokenType["Percent"] =
|
|
47
|
-
TokenType[TokenType["Power"] =
|
|
48
|
-
TokenType[TokenType["Eq"] =
|
|
49
|
-
TokenType[TokenType["Neq"] =
|
|
50
|
-
TokenType[TokenType["Lt"] =
|
|
51
|
-
TokenType[TokenType["Gt"] =
|
|
52
|
-
TokenType[TokenType["Lte"] =
|
|
53
|
-
TokenType[TokenType["Gte"] =
|
|
54
|
-
TokenType[TokenType["Pipe"] =
|
|
55
|
-
TokenType[TokenType["Bar"] =
|
|
56
|
-
TokenType[TokenType["FatArrow"] =
|
|
57
|
-
TokenType[TokenType["Arrow"] =
|
|
58
|
-
TokenType[TokenType["Question"] =
|
|
59
|
-
TokenType[TokenType["QuestionDot"] =
|
|
60
|
-
TokenType[TokenType["Range"] =
|
|
61
|
-
TokenType[TokenType["Concat"] =
|
|
62
|
-
TokenType[TokenType["At"] =
|
|
63
|
-
TokenType[TokenType["Hash"] =
|
|
64
|
-
TokenType[TokenType["DotDotDot"] =
|
|
65
|
-
TokenType[TokenType["Assign"] =
|
|
46
|
+
TokenType[TokenType["Plus"] = 39] = "Plus";
|
|
47
|
+
TokenType[TokenType["Minus"] = 40] = "Minus";
|
|
48
|
+
TokenType[TokenType["Star"] = 41] = "Star";
|
|
49
|
+
TokenType[TokenType["Slash"] = 42] = "Slash";
|
|
50
|
+
TokenType[TokenType["Percent"] = 43] = "Percent";
|
|
51
|
+
TokenType[TokenType["Power"] = 44] = "Power";
|
|
52
|
+
TokenType[TokenType["Eq"] = 45] = "Eq";
|
|
53
|
+
TokenType[TokenType["Neq"] = 46] = "Neq";
|
|
54
|
+
TokenType[TokenType["Lt"] = 47] = "Lt";
|
|
55
|
+
TokenType[TokenType["Gt"] = 48] = "Gt";
|
|
56
|
+
TokenType[TokenType["Lte"] = 49] = "Lte";
|
|
57
|
+
TokenType[TokenType["Gte"] = 50] = "Gte";
|
|
58
|
+
TokenType[TokenType["Pipe"] = 51] = "Pipe";
|
|
59
|
+
TokenType[TokenType["Bar"] = 52] = "Bar";
|
|
60
|
+
TokenType[TokenType["FatArrow"] = 53] = "FatArrow";
|
|
61
|
+
TokenType[TokenType["Arrow"] = 54] = "Arrow";
|
|
62
|
+
TokenType[TokenType["Question"] = 55] = "Question";
|
|
63
|
+
TokenType[TokenType["QuestionDot"] = 56] = "QuestionDot";
|
|
64
|
+
TokenType[TokenType["Range"] = 57] = "Range";
|
|
65
|
+
TokenType[TokenType["Concat"] = 58] = "Concat";
|
|
66
|
+
TokenType[TokenType["At"] = 59] = "At";
|
|
67
|
+
TokenType[TokenType["Hash"] = 60] = "Hash";
|
|
68
|
+
TokenType[TokenType["DotDotDot"] = 61] = "DotDotDot";
|
|
69
|
+
TokenType[TokenType["Assign"] = 62] = "Assign";
|
|
66
70
|
// Delimiters
|
|
67
|
-
TokenType[TokenType["LParen"] =
|
|
68
|
-
TokenType[TokenType["RParen"] =
|
|
69
|
-
TokenType[TokenType["LBrace"] =
|
|
70
|
-
TokenType[TokenType["RBrace"] =
|
|
71
|
-
TokenType[TokenType["LBracket"] =
|
|
72
|
-
TokenType[TokenType["RBracket"] =
|
|
73
|
-
TokenType[TokenType["Comma"] =
|
|
74
|
-
TokenType[TokenType["Colon"] =
|
|
75
|
-
TokenType[TokenType["Dot"] =
|
|
76
|
-
TokenType[TokenType["Semicolon"] =
|
|
77
|
-
TokenType[TokenType["Newline"] =
|
|
71
|
+
TokenType[TokenType["LParen"] = 63] = "LParen";
|
|
72
|
+
TokenType[TokenType["RParen"] = 64] = "RParen";
|
|
73
|
+
TokenType[TokenType["LBrace"] = 65] = "LBrace";
|
|
74
|
+
TokenType[TokenType["RBrace"] = 66] = "RBrace";
|
|
75
|
+
TokenType[TokenType["LBracket"] = 67] = "LBracket";
|
|
76
|
+
TokenType[TokenType["RBracket"] = 68] = "RBracket";
|
|
77
|
+
TokenType[TokenType["Comma"] = 69] = "Comma";
|
|
78
|
+
TokenType[TokenType["Colon"] = 70] = "Colon";
|
|
79
|
+
TokenType[TokenType["Dot"] = 71] = "Dot";
|
|
80
|
+
TokenType[TokenType["Semicolon"] = 72] = "Semicolon";
|
|
81
|
+
TokenType[TokenType["Newline"] = 73] = "Newline";
|
|
78
82
|
// Regex
|
|
79
|
-
TokenType[TokenType["Regex"] =
|
|
83
|
+
TokenType[TokenType["Regex"] = 74] = "Regex";
|
|
80
84
|
// Special
|
|
81
|
-
TokenType[TokenType["EOF"] =
|
|
85
|
+
TokenType[TokenType["EOF"] = 75] = "EOF";
|
|
82
86
|
})(TokenType || (TokenType = {}));
|
|
83
87
|
const KEYWORDS = {
|
|
84
88
|
fn: TokenType.Fn, let: TokenType.Let, mut: TokenType.Mut, type: TokenType.Type,
|
|
@@ -89,6 +93,10 @@ const KEYWORDS = {
|
|
|
89
93
|
true: TokenType.True, false: TokenType.False, nil: TokenType.NilKw,
|
|
90
94
|
and: TokenType.And, or: TokenType.Or, not: TokenType.Not,
|
|
91
95
|
where: TokenType.Where, matching: TokenType.Matching, fetch: TokenType.Fetch,
|
|
96
|
+
break: TokenType.Break, continue: TokenType.Continue,
|
|
97
|
+
return: TokenType.Ret, // alias: Arc uses 'ret' but accept 'return' too
|
|
98
|
+
else: TokenType.El, // alias: Arc uses 'el' but accept 'else' too
|
|
99
|
+
try: TokenType.Try, catch: TokenType.Catch,
|
|
92
100
|
};
|
|
93
101
|
export function lex(source) {
|
|
94
102
|
const tokens = [];
|
|
@@ -124,8 +132,8 @@ export function lex(source) {
|
|
|
124
132
|
tokens.push(tok(TokenType.Newline, "\\n", sl, sc));
|
|
125
133
|
continue;
|
|
126
134
|
}
|
|
127
|
-
// Comments: # to end of line
|
|
128
|
-
if (ch === "#") {
|
|
135
|
+
// Comments: # or // to end of line
|
|
136
|
+
if (ch === "#" || (ch === "/" && peek(1) === "/")) {
|
|
129
137
|
while (i < source.length && peek() !== "\n")
|
|
130
138
|
advance();
|
|
131
139
|
continue;
|
package/dist/linter.js
CHANGED
|
@@ -343,6 +343,24 @@ export function lint(source, options) {
|
|
|
343
343
|
}
|
|
344
344
|
break;
|
|
345
345
|
}
|
|
346
|
+
case "WhileStmt":
|
|
347
|
+
analyzeExpr(stmt.condition, scope);
|
|
348
|
+
analyzeExpr(stmt.body, scope);
|
|
349
|
+
if (stmt.body.kind === "BlockExpr" && stmt.body.stmts.length === 0) {
|
|
350
|
+
warn("empty-block", "While loop has an empty body", stmt.loc);
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
353
|
+
case "BreakStmt":
|
|
354
|
+
case "ContinueStmt":
|
|
355
|
+
break;
|
|
356
|
+
case "TryCatchStmt":
|
|
357
|
+
analyzeExpr(stmt.body, scope);
|
|
358
|
+
analyzeExpr(stmt.catchBody, scope);
|
|
359
|
+
break;
|
|
360
|
+
case "RetStmt":
|
|
361
|
+
if (stmt.value)
|
|
362
|
+
analyzeExpr(stmt.value, scope);
|
|
363
|
+
break;
|
|
346
364
|
case "DoStmt":
|
|
347
365
|
analyzeExpr(stmt.body, scope);
|
|
348
366
|
analyzeExpr(stmt.condition, scope);
|
package/dist/modules.js
CHANGED
|
@@ -35,10 +35,14 @@ export function resolveModule(path, basePath) {
|
|
|
35
35
|
const relPath = resolve(dirname(basePath), modulePath);
|
|
36
36
|
if (existsSync(relPath))
|
|
37
37
|
return relPath;
|
|
38
|
-
// 3. Check compiler's sibling stdlib/
|
|
38
|
+
// 3. Check compiler's sibling stdlib/ (monorepo layout)
|
|
39
39
|
const compilerStdlib = resolve(__dirname2, "..", "..", "stdlib", modulePath);
|
|
40
40
|
if (existsSync(compilerStdlib))
|
|
41
41
|
return compilerStdlib;
|
|
42
|
+
// 4. Check stdlib bundled inside the npm package (npm install -g layout)
|
|
43
|
+
const bundledStdlib = resolve(__dirname2, "..", "stdlib", modulePath);
|
|
44
|
+
if (existsSync(bundledStdlib))
|
|
45
|
+
return bundledStdlib;
|
|
42
46
|
throw new Error(`Module not found: ${path.join("/")} (searched from ${basePath})`);
|
|
43
47
|
}
|
|
44
48
|
/**
|
package/dist/parser.d.ts
CHANGED
package/dist/parser.js
CHANGED
|
@@ -52,7 +52,18 @@ export class Parser {
|
|
|
52
52
|
case TokenType.Fn: return this.parseFn(false);
|
|
53
53
|
case TokenType.Async: return this.parseAsync();
|
|
54
54
|
case TokenType.For: return this.parseFor();
|
|
55
|
+
case TokenType.While: return this.parseWhile();
|
|
55
56
|
case TokenType.Do: return this.parseDo();
|
|
57
|
+
case TokenType.Break: {
|
|
58
|
+
const loc = this.loc();
|
|
59
|
+
this.advance();
|
|
60
|
+
return { kind: "BreakStmt", loc };
|
|
61
|
+
}
|
|
62
|
+
case TokenType.Continue: {
|
|
63
|
+
const loc = this.loc();
|
|
64
|
+
this.advance();
|
|
65
|
+
return { kind: "ContinueStmt", loc };
|
|
66
|
+
}
|
|
56
67
|
case TokenType.Use: return this.parseUse();
|
|
57
68
|
case TokenType.Type: return this.parseType();
|
|
58
69
|
case TokenType.Ret: return this.parseRet();
|
|
@@ -241,6 +252,22 @@ export class Parser {
|
|
|
241
252
|
const body = this.parseBlock();
|
|
242
253
|
return { kind: "ForStmt", variable, iterable, body, loc };
|
|
243
254
|
}
|
|
255
|
+
parseWhile() {
|
|
256
|
+
const loc = this.loc();
|
|
257
|
+
this.expect(TokenType.While);
|
|
258
|
+
const condition = this.parseExpr(0);
|
|
259
|
+
const body = this.parseBlock();
|
|
260
|
+
return { kind: "WhileStmt", condition, body, loc };
|
|
261
|
+
}
|
|
262
|
+
parseTryCatch() {
|
|
263
|
+
const loc = this.loc();
|
|
264
|
+
this.expect(TokenType.Try);
|
|
265
|
+
const body = this.parseBlock();
|
|
266
|
+
this.expect(TokenType.Catch);
|
|
267
|
+
const catchVar = this.expect(TokenType.Ident).value;
|
|
268
|
+
const catchBody = this.parseBlock();
|
|
269
|
+
return { kind: "TryCatchStmt", body, catchVar, catchBody, loc };
|
|
270
|
+
}
|
|
244
271
|
parseDo() {
|
|
245
272
|
const loc = this.loc();
|
|
246
273
|
this.expect(TokenType.Do);
|
|
@@ -473,14 +500,30 @@ export class Parser {
|
|
|
473
500
|
// Postfix: member access
|
|
474
501
|
if (t.type === TokenType.Dot) {
|
|
475
502
|
this.advance();
|
|
476
|
-
|
|
503
|
+
// Allow keywords (like 'match') as property names after dot
|
|
504
|
+
const propToken = this.peek();
|
|
505
|
+
let prop;
|
|
506
|
+
if (propToken.type === TokenType.Ident || propToken.type === TokenType.Match || propToken.type === TokenType.Fn || propToken.type === TokenType.Let || propToken.type === TokenType.If || propToken.type === TokenType.For || propToken.type === TokenType.In || propToken.type === TokenType.Do || propToken.type === TokenType.While || propToken.type === TokenType.Until || propToken.type === TokenType.Use || propToken.type === TokenType.Pub || propToken.type === TokenType.Type || propToken.type === TokenType.Ret || propToken.type === TokenType.Where || propToken.type === TokenType.Matching || propToken.type === TokenType.Fetch || propToken.type === TokenType.Async || propToken.type === TokenType.Await || propToken.type === TokenType.Try || propToken.type === TokenType.Catch) {
|
|
507
|
+
prop = this.advance().value;
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
prop = this.expect(TokenType.Ident).value;
|
|
511
|
+
}
|
|
477
512
|
left = { kind: "MemberExpr", object: left, property: prop, loc: left.loc };
|
|
478
513
|
continue;
|
|
479
514
|
}
|
|
480
515
|
// Postfix: optional chaining ?.
|
|
481
516
|
if (t.type === TokenType.QuestionDot) {
|
|
482
517
|
this.advance();
|
|
483
|
-
|
|
518
|
+
// Allow keywords as property names after ?.
|
|
519
|
+
const propToken = this.peek();
|
|
520
|
+
let prop;
|
|
521
|
+
if (propToken.type === TokenType.Ident || propToken.type === TokenType.Match || propToken.type === TokenType.Fn || propToken.type === TokenType.Let || propToken.type === TokenType.If || propToken.type === TokenType.For || propToken.type === TokenType.In || propToken.type === TokenType.Do || propToken.type === TokenType.While || propToken.type === TokenType.Until || propToken.type === TokenType.Use || propToken.type === TokenType.Pub || propToken.type === TokenType.Type || propToken.type === TokenType.Ret || propToken.type === TokenType.Where || propToken.type === TokenType.Matching || propToken.type === TokenType.Fetch || propToken.type === TokenType.Async || propToken.type === TokenType.Await || propToken.type === TokenType.Try || propToken.type === TokenType.Catch) {
|
|
522
|
+
prop = this.advance().value;
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
prop = this.expect(TokenType.Ident).value;
|
|
526
|
+
}
|
|
484
527
|
left = { kind: "OptionalMemberExpr", object: left, property: prop, loc: left.loc };
|
|
485
528
|
continue;
|
|
486
529
|
}
|
|
@@ -660,24 +703,61 @@ export class Parser {
|
|
|
660
703
|
const body = this.parseBlock();
|
|
661
704
|
return { kind: "AsyncExpr", body, loc };
|
|
662
705
|
}
|
|
706
|
+
// Anonymous function expression: fn(params) { body } or fn(params) => expr
|
|
707
|
+
if (t.type === TokenType.Fn) {
|
|
708
|
+
this.advance();
|
|
709
|
+
this.expect(TokenType.LParen);
|
|
710
|
+
const params = [];
|
|
711
|
+
while (!this.at(TokenType.RParen)) {
|
|
712
|
+
params.push(this.expect(TokenType.Ident).value);
|
|
713
|
+
if (this.at(TokenType.Comma))
|
|
714
|
+
this.advance();
|
|
715
|
+
}
|
|
716
|
+
this.expect(TokenType.RParen);
|
|
717
|
+
if (this.at(TokenType.FatArrow)) {
|
|
718
|
+
this.advance();
|
|
719
|
+
const body = this.parseExpr();
|
|
720
|
+
return { kind: "LambdaExpr", params, body, loc };
|
|
721
|
+
}
|
|
722
|
+
const body = this.parseBlock();
|
|
723
|
+
return { kind: "LambdaExpr", params, body, loc };
|
|
724
|
+
}
|
|
725
|
+
// Try/catch expression: try { body } catch e { handler }
|
|
726
|
+
if (t.type === TokenType.Try) {
|
|
727
|
+
this.advance();
|
|
728
|
+
const body = this.parseBlock();
|
|
729
|
+
if (this.at(TokenType.Catch)) {
|
|
730
|
+
this.expect(TokenType.Catch);
|
|
731
|
+
const catchVar = this.expect(TokenType.Ident).value;
|
|
732
|
+
const catchBody = this.parseBlock();
|
|
733
|
+
return { kind: "TryCatchExpr", body, catchVar, catchBody, loc };
|
|
734
|
+
}
|
|
735
|
+
// Plain try expression (wraps in Result)
|
|
736
|
+
return { kind: "TryExpr", expr: body, loc };
|
|
737
|
+
}
|
|
663
738
|
// Await expression: await expr
|
|
664
739
|
if (t.type === TokenType.Await) {
|
|
665
740
|
this.advance();
|
|
666
741
|
const expr = this.parseExpr(8); // high precedence
|
|
667
742
|
return { kind: "AwaitExpr", expr, loc };
|
|
668
743
|
}
|
|
669
|
-
// Fetch expression: fetch [expr1, expr2, ...]
|
|
744
|
+
// Fetch expression: fetch @GET "url" or fetch [expr1, expr2, ...]
|
|
670
745
|
if (t.type === TokenType.Fetch) {
|
|
671
746
|
this.advance();
|
|
672
|
-
this.
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
this.
|
|
747
|
+
if (this.at(TokenType.LBracket)) {
|
|
748
|
+
this.advance();
|
|
749
|
+
const targets = [];
|
|
750
|
+
while (!this.at(TokenType.RBracket)) {
|
|
751
|
+
targets.push(this.parseExpr());
|
|
752
|
+
if (this.at(TokenType.Comma))
|
|
753
|
+
this.advance();
|
|
754
|
+
}
|
|
755
|
+
this.expect(TokenType.RBracket);
|
|
756
|
+
return { kind: "FetchExpr", targets, loc };
|
|
678
757
|
}
|
|
679
|
-
|
|
680
|
-
|
|
758
|
+
// Single target: fetch @GET "url"
|
|
759
|
+
const target = this.parseExpr();
|
|
760
|
+
return { kind: "FetchExpr", targets: [target], loc };
|
|
681
761
|
}
|
|
682
762
|
// Tool call: @GET "url" or @ident(args)
|
|
683
763
|
if (t.type === TokenType.At) {
|