arc-lang 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ast.d.ts +30 -2
- package/dist/build.js +1 -1
- package/dist/index.js +104 -0
- package/dist/interpreter.js +129 -16
- package/dist/lexer.d.ts +41 -37
- package/dist/lexer.js +47 -39
- package/dist/modules.js +5 -1
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +72 -11
- package/dist/repl.js +63 -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/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,42 @@ export class Parser {
|
|
|
660
703
|
const body = this.parseBlock();
|
|
661
704
|
return { kind: "AsyncExpr", body, loc };
|
|
662
705
|
}
|
|
706
|
+
// Try/catch expression: try { body } catch e { handler }
|
|
707
|
+
if (t.type === TokenType.Try) {
|
|
708
|
+
this.advance();
|
|
709
|
+
const body = this.parseBlock();
|
|
710
|
+
if (this.at(TokenType.Catch)) {
|
|
711
|
+
this.expect(TokenType.Catch);
|
|
712
|
+
const catchVar = this.expect(TokenType.Ident).value;
|
|
713
|
+
const catchBody = this.parseBlock();
|
|
714
|
+
return { kind: "TryCatchExpr", body, catchVar, catchBody, loc };
|
|
715
|
+
}
|
|
716
|
+
// Plain try expression (wraps in Result)
|
|
717
|
+
return { kind: "TryExpr", expr: body, loc };
|
|
718
|
+
}
|
|
663
719
|
// Await expression: await expr
|
|
664
720
|
if (t.type === TokenType.Await) {
|
|
665
721
|
this.advance();
|
|
666
722
|
const expr = this.parseExpr(8); // high precedence
|
|
667
723
|
return { kind: "AwaitExpr", expr, loc };
|
|
668
724
|
}
|
|
669
|
-
// Fetch expression: fetch [expr1, expr2, ...]
|
|
725
|
+
// Fetch expression: fetch @GET "url" or fetch [expr1, expr2, ...]
|
|
670
726
|
if (t.type === TokenType.Fetch) {
|
|
671
727
|
this.advance();
|
|
672
|
-
this.
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
this.
|
|
728
|
+
if (this.at(TokenType.LBracket)) {
|
|
729
|
+
this.advance();
|
|
730
|
+
const targets = [];
|
|
731
|
+
while (!this.at(TokenType.RBracket)) {
|
|
732
|
+
targets.push(this.parseExpr());
|
|
733
|
+
if (this.at(TokenType.Comma))
|
|
734
|
+
this.advance();
|
|
735
|
+
}
|
|
736
|
+
this.expect(TokenType.RBracket);
|
|
737
|
+
return { kind: "FetchExpr", targets, loc };
|
|
678
738
|
}
|
|
679
|
-
|
|
680
|
-
|
|
739
|
+
// Single target: fetch @GET "url"
|
|
740
|
+
const target = this.parseExpr();
|
|
741
|
+
return { kind: "FetchExpr", targets: [target], loc };
|
|
681
742
|
}
|
|
682
743
|
// Tool call: @GET "url" or @ident(args)
|
|
683
744
|
if (t.type === TokenType.At) {
|
package/dist/repl.js
CHANGED
|
@@ -16,6 +16,8 @@ const useHandler = createUseHandler(process.cwd() + "/repl.arc");
|
|
|
16
16
|
function printHelp() {
|
|
17
17
|
console.log(`${CYAN}Arc REPL Commands:${RESET}`);
|
|
18
18
|
console.log(` ${YELLOW}:help${RESET} Show this help message`);
|
|
19
|
+
console.log(` ${YELLOW}:builtins${RESET} List all built-in functions`);
|
|
20
|
+
console.log(` ${YELLOW}:modules${RESET} List available stdlib modules`);
|
|
19
21
|
console.log(` ${YELLOW}:ast${RESET} Toggle AST display before execution`);
|
|
20
22
|
console.log(` ${YELLOW}:reset${RESET} Clear all variables and state`);
|
|
21
23
|
console.log(` ${YELLOW}:load <file>${RESET} Load and execute an Arc file`);
|
|
@@ -23,6 +25,56 @@ function printHelp() {
|
|
|
23
25
|
console.log(` ${YELLOW}Ctrl+C${RESET} Exit the REPL`);
|
|
24
26
|
console.log();
|
|
25
27
|
console.log(` Multi-line: end a line with { to start a block`);
|
|
28
|
+
console.log(` Comments: # or // (both work)`);
|
|
29
|
+
console.log(` Docs: https://arclang.dev | See CHEATSHEET.md`);
|
|
30
|
+
}
|
|
31
|
+
function printBuiltins() {
|
|
32
|
+
console.log(`${CYAN}Built-in Functions:${RESET}`);
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(`${YELLOW}I/O:${RESET} print(...values)`);
|
|
35
|
+
console.log(`${YELLOW}Type Convert:${RESET} int(v) float(v) str(v) bool(v) type_of(v)`);
|
|
36
|
+
console.log(`${YELLOW}Strings:${RESET} len trim upper lower split join replace`);
|
|
37
|
+
console.log(` contains starts ends repeat chars slice`);
|
|
38
|
+
console.log(` index_of ord chr char_at`);
|
|
39
|
+
console.log(`${YELLOW}Lists:${RESET} map filter reduce fold find any all sort`);
|
|
40
|
+
console.log(` head tail last reverse take drop flat`);
|
|
41
|
+
console.log(` zip enumerate push concat sum range`);
|
|
42
|
+
console.log(`${YELLOW}Maps:${RESET} keys values entries len`);
|
|
43
|
+
console.log(`${YELLOW}Math:${RESET} abs min max round`);
|
|
44
|
+
console.log(`${YELLOW}Other:${RESET} assert time_ms to_string`);
|
|
45
|
+
console.log();
|
|
46
|
+
console.log(`${CYAN}Operators:${RESET} + - * / % ** ++ == != < > <= >= and or not |> .. ?.`);
|
|
47
|
+
console.log(`${CYAN}Syntax:${RESET} "string {expr}" * (string repeat) # // (comments) try/catch`);
|
|
48
|
+
}
|
|
49
|
+
function printModules() {
|
|
50
|
+
console.log(`${CYAN}Standard Library Modules:${RESET} (import with: use <module>)`);
|
|
51
|
+
console.log();
|
|
52
|
+
console.log(` ${YELLOW}math${RESET} sqrt, pow, ceil, floor, clamp, PI, E, sin, cos, log`);
|
|
53
|
+
console.log(` ${YELLOW}strings${RESET} pad_left, pad_right, capitalize, words`);
|
|
54
|
+
console.log(` ${YELLOW}collections${RESET} group_by, chunk, flatten, zip_with, partition, sort_by, unique`);
|
|
55
|
+
console.log(` ${YELLOW}map${RESET} merge, map_values, filter_map, from_pairs, pick, omit`);
|
|
56
|
+
console.log(` ${YELLOW}io${RESET} read_file, write_file, read_lines, exists, append`);
|
|
57
|
+
console.log(` ${YELLOW}http${RESET} get, post, put, delete — real HTTP requests`);
|
|
58
|
+
console.log(` ${YELLOW}json${RESET} to_json, from_json, pretty, get_path`);
|
|
59
|
+
console.log(` ${YELLOW}csv${RESET} parse_csv, to_csv, parse_csv_headers`);
|
|
60
|
+
console.log(` ${YELLOW}regex${RESET} match, find, test, replace, split, capture`);
|
|
61
|
+
console.log(` ${YELLOW}datetime${RESET} now, today, parse, format, add_days, diff_days`);
|
|
62
|
+
console.log(` ${YELLOW}os${RESET} cwd, list_dir, mkdir, exec, platform, env`);
|
|
63
|
+
console.log(` ${YELLOW}env${RESET} get, set, has, all — environment variables`);
|
|
64
|
+
console.log(` ${YELLOW}crypto${RESET} sha256, sha512, hmac_sha256, uuid, random_bytes`);
|
|
65
|
+
console.log(` ${YELLOW}error${RESET} try_catch, try_finally, throw, retry, assert`);
|
|
66
|
+
console.log(` ${YELLOW}result${RESET} ok, err, is_ok, unwrap, map_result, try_fn`);
|
|
67
|
+
console.log(` ${YELLOW}net${RESET} ws_connect, tcp_connect, dns_lookup, base64_encode`);
|
|
68
|
+
console.log(` ${YELLOW}yaml${RESET} parse, stringify`);
|
|
69
|
+
console.log(` ${YELLOW}toml${RESET} parse, stringify`);
|
|
70
|
+
console.log(` ${YELLOW}html${RESET} parse, create_element, to_html`);
|
|
71
|
+
console.log(` ${YELLOW}path${RESET} join, dirname, basename, extname`);
|
|
72
|
+
console.log(` ${YELLOW}log${RESET} info, warn, error, debug`);
|
|
73
|
+
console.log(` ${YELLOW}store${RESET} get, set, delete — persistent key-value storage`);
|
|
74
|
+
console.log(` ${YELLOW}test${RESET} describe, it, expect_eq, run_tests`);
|
|
75
|
+
console.log(` ${YELLOW}prompt${RESET} template, count_tokens, window`);
|
|
76
|
+
console.log(` ${YELLOW}embed${RESET} similarity, cosine, search`);
|
|
77
|
+
console.log(` ${YELLOW}llm${RESET} chat, complete — multi-provider LLM API`);
|
|
26
78
|
}
|
|
27
79
|
function execute(source) {
|
|
28
80
|
try {
|
|
@@ -46,7 +98,7 @@ function main() {
|
|
|
46
98
|
output: process.stdout,
|
|
47
99
|
prompt: "arc> ",
|
|
48
100
|
});
|
|
49
|
-
console.log(`${CYAN}Arc REPL v0.
|
|
101
|
+
console.log(`${CYAN}Arc REPL v0.6.3${RESET} — Type ${YELLOW}:help${RESET} for commands, ${YELLOW}:builtins${RESET} for functions`);
|
|
50
102
|
rl.prompt();
|
|
51
103
|
let buffer = "";
|
|
52
104
|
let braceDepth = 0;
|
|
@@ -63,6 +115,16 @@ function main() {
|
|
|
63
115
|
rl.prompt();
|
|
64
116
|
return;
|
|
65
117
|
}
|
|
118
|
+
if (trimmed === ":builtins" || trimmed === ":b") {
|
|
119
|
+
printBuiltins();
|
|
120
|
+
rl.prompt();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (trimmed === ":modules" || trimmed === ":m") {
|
|
124
|
+
printModules();
|
|
125
|
+
rl.prompt();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
66
128
|
if (trimmed === ":ast") {
|
|
67
129
|
showAst = !showAst;
|
|
68
130
|
console.log(`AST display: ${showAst ? "ON" : "OFF"}`);
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arc-lang",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Arc ⚡ — A programming language designed by AI agents, for AI agents. 27-63% fewer tokens than JavaScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/",
|
|
12
|
+
"stdlib/",
|
|
12
13
|
"README.md",
|
|
13
14
|
"LICENSE"
|
|
14
15
|
],
|
|
15
16
|
"scripts": {
|
|
16
17
|
"build": "tsc",
|
|
17
|
-
"
|
|
18
|
+
"copy-stdlib": "node -e \"const fs=require('fs');const p=require('path');function cp(s,d){fs.mkdirSync(d,{recursive:true});for(const f of fs.readdirSync(s)){const sp=p.join(s,f),dp=p.join(d,f);fs.statSync(sp).isDirectory()?cp(sp,dp):fs.copyFileSync(sp,dp)}}cp(p.join(__dirname,'..','stdlib'),p.join(__dirname,'stdlib'))\"",
|
|
19
|
+
"prepublishOnly": "npm run copy-stdlib && tsc",
|
|
18
20
|
"run": "tsx src/index.ts run",
|
|
19
21
|
"parse": "tsx src/index.ts parse"
|
|
20
22
|
},
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# Arc Standard Library API Design
|
|
2
|
+
|
|
3
|
+
## Design Principles
|
|
4
|
+
|
|
5
|
+
1. **Token-minimal APIs** — Short function names, no unnecessary prefixes
|
|
6
|
+
2. **Convention over configuration** — Sensible defaults, override when needed
|
|
7
|
+
3. **Composable** — All functions work with `|>` pipeline
|
|
8
|
+
4. **Consistent** — Same patterns across all modules
|
|
9
|
+
5. **Zero-import common ops** — `print`, `len`, `map`, `filter` are always available (prelude)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Prelude (Always Available)
|
|
14
|
+
|
|
15
|
+
These functions require no `use` statement:
|
|
16
|
+
|
|
17
|
+
```arc
|
|
18
|
+
# I/O
|
|
19
|
+
print(value) # Print to stdout
|
|
20
|
+
read(path) -> String # Read file contents
|
|
21
|
+
write(path, data) # Write to file
|
|
22
|
+
|
|
23
|
+
# Collections
|
|
24
|
+
len(collection) -> Int
|
|
25
|
+
map(collection, fn) -> [T]
|
|
26
|
+
filter(collection, fn) -> [T]
|
|
27
|
+
reduce(collection, init, fn) -> T
|
|
28
|
+
sort(collection, [key_fn]) -> [T]
|
|
29
|
+
take(collection, n) -> [T]
|
|
30
|
+
drop(collection, n) -> [T]
|
|
31
|
+
find(collection, fn) -> Option<T>
|
|
32
|
+
any(collection, fn) -> Bool
|
|
33
|
+
all(collection, fn) -> Bool
|
|
34
|
+
sum(collection) -> Number
|
|
35
|
+
flat(collection) -> [T]
|
|
36
|
+
zip(a, b) -> [(A, B)]
|
|
37
|
+
enumerate(collection) -> [(Int, T)]
|
|
38
|
+
|
|
39
|
+
# Strings
|
|
40
|
+
trim(s) -> String
|
|
41
|
+
split(s, delim) -> [String]
|
|
42
|
+
join(collection, delim) -> String
|
|
43
|
+
upper(s) -> String
|
|
44
|
+
lower(s) -> String
|
|
45
|
+
replace(s, from, to) -> String
|
|
46
|
+
contains(s, substr) -> Bool
|
|
47
|
+
starts(s, prefix) -> Bool
|
|
48
|
+
ends(s, suffix) -> Bool
|
|
49
|
+
|
|
50
|
+
# Type conversion
|
|
51
|
+
int(value) -> Int
|
|
52
|
+
float(value) -> Float
|
|
53
|
+
str(value) -> String
|
|
54
|
+
bool(value) -> Bool
|
|
55
|
+
|
|
56
|
+
# Math
|
|
57
|
+
min(a, b) -> Number
|
|
58
|
+
max(a, b) -> Number
|
|
59
|
+
abs(x) -> Number
|
|
60
|
+
round(x, [decimals]) -> Number
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Rationale:** These ~40 functions cover ~80% of typical agent operations. No imports needed = fewer tokens per program. Names are short (3-5 chars) and unambiguous.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Module: `std/collections`
|
|
68
|
+
|
|
69
|
+
```arc
|
|
70
|
+
use std/collections: Set, Stack, Queue, deque
|
|
71
|
+
|
|
72
|
+
# Sets
|
|
73
|
+
let s = Set([1, 2, 3])
|
|
74
|
+
s |> add(4) # {1, 2, 3, 4}
|
|
75
|
+
s |> has(2) # true
|
|
76
|
+
s |> union(Set([3, 4, 5])) # {1, 2, 3, 4, 5}
|
|
77
|
+
s |> intersect(Set([2, 3])) # {2, 3}
|
|
78
|
+
s |> diff(Set([2])) # {1, 3}
|
|
79
|
+
|
|
80
|
+
# Stack (LIFO)
|
|
81
|
+
let st = Stack()
|
|
82
|
+
st |> push(1) |> push(2)
|
|
83
|
+
st |> pop # (2, Stack([1]))
|
|
84
|
+
|
|
85
|
+
# Queue (FIFO)
|
|
86
|
+
let q = Queue()
|
|
87
|
+
q |> enq(1) |> enq(2)
|
|
88
|
+
q |> deq # (1, Queue([2]))
|
|
89
|
+
|
|
90
|
+
# Sorted map
|
|
91
|
+
use std/collections: SortedMap
|
|
92
|
+
let sm = SortedMap({a: 1, c: 3, b: 2})
|
|
93
|
+
sm |> keys # ["a", "b", "c"]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Module: `std/math`
|
|
99
|
+
|
|
100
|
+
```arc
|
|
101
|
+
use std/math: pi, e, sin, cos, tan, log, sqrt, pow, floor, ceil, clamp, rand
|
|
102
|
+
|
|
103
|
+
sqrt(16) # 4.0
|
|
104
|
+
clamp(15, 0, 10) # 10
|
|
105
|
+
rand() # 0.0..1.0
|
|
106
|
+
rand(1, 100) # random int 1-100
|
|
107
|
+
sin(pi / 2) # 1.0
|
|
108
|
+
log(e) # 1.0
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Module: `std/http`
|
|
114
|
+
|
|
115
|
+
```arc
|
|
116
|
+
use std/http: serve, request
|
|
117
|
+
|
|
118
|
+
# Server
|
|
119
|
+
serve 3000 {
|
|
120
|
+
GET "/" => fn(req) => "Hello!"
|
|
121
|
+
|
|
122
|
+
GET "/users/:id" => fn(req) {
|
|
123
|
+
@db("SELECT * FROM users WHERE id = {req.params.id}")
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
POST "/users" => fn(req) {
|
|
127
|
+
@db("INSERT INTO users VALUES ({req.body.name}, {req.body.email})")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# Middleware
|
|
131
|
+
before => fn(req) {
|
|
132
|
+
log("{req.method} {req.path}")
|
|
133
|
+
req
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Client (usually via @GET/@POST, but configurable requests available)
|
|
138
|
+
let resp = request({
|
|
139
|
+
url: "https://api.example.com/data",
|
|
140
|
+
method: "GET",
|
|
141
|
+
headers: {Authorization: "Bearer {token}"},
|
|
142
|
+
timeout: 5000
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Comparison — Express.js equivalent:**
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
// JavaScript: ~50 tokens for basic server setup
|
|
150
|
+
const express = require('express');
|
|
151
|
+
const app = express();
|
|
152
|
+
app.use(express.json());
|
|
153
|
+
app.get('/', (req, res) => res.send('Hello!'));
|
|
154
|
+
app.get('/users/:id', async (req, res) => { ... });
|
|
155
|
+
app.post('/users', async (req, res) => { ... });
|
|
156
|
+
app.listen(3000);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```arc
|
|
160
|
+
# Arc: ~20 tokens for same functionality
|
|
161
|
+
serve 3000 {
|
|
162
|
+
GET "/" => fn(req) => "Hello!"
|
|
163
|
+
GET "/users/:id" => fn(req) { ... }
|
|
164
|
+
POST "/users" => fn(req) { ... }
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Module: `std/json`
|
|
171
|
+
|
|
172
|
+
```arc
|
|
173
|
+
use std/json: parse, stringify
|
|
174
|
+
|
|
175
|
+
let data = parse('{"name": "Arc", "version": 1}')
|
|
176
|
+
data.name # "Arc"
|
|
177
|
+
|
|
178
|
+
let text = stringify(data) # '{"name":"Arc","version":1}'
|
|
179
|
+
let pretty = stringify(data, indent: 2)
|
|
180
|
+
|
|
181
|
+
# Usually unnecessary — @ tool calls auto-parse JSON
|
|
182
|
+
let user = @GET "api/users/1" # Already parsed
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Module: `std/io`
|
|
188
|
+
|
|
189
|
+
```arc
|
|
190
|
+
use std/io: readLines, appendFile, exists, mkdir, ls, rm, glob
|
|
191
|
+
|
|
192
|
+
# File operations (read/write are in prelude)
|
|
193
|
+
let lines = readLines("data.txt") # [String]
|
|
194
|
+
appendFile("log.txt", "entry\n")
|
|
195
|
+
let found = exists("config.json") # Bool
|
|
196
|
+
|
|
197
|
+
# Directory operations
|
|
198
|
+
mkdir("output")
|
|
199
|
+
let files = ls("src") # [String]
|
|
200
|
+
let arcs = glob("**/*.arc") # [String]
|
|
201
|
+
|
|
202
|
+
# Stdin
|
|
203
|
+
use std/io: input
|
|
204
|
+
let name = input("What's your name? ")
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Module: `std/async`
|
|
210
|
+
|
|
211
|
+
```arc
|
|
212
|
+
use std/async: spawn, sleep, timeout, channel, select
|
|
213
|
+
|
|
214
|
+
# Spawn concurrent task
|
|
215
|
+
let task = spawn { heavyWork() }
|
|
216
|
+
let result = await task
|
|
217
|
+
|
|
218
|
+
# Sleep
|
|
219
|
+
sleep(1000) # ms
|
|
220
|
+
|
|
221
|
+
# Timeout
|
|
222
|
+
let data = timeout(5000) {
|
|
223
|
+
@GET "slow-api/data"
|
|
224
|
+
} ? defaultData
|
|
225
|
+
|
|
226
|
+
# Channels (CSP-style)
|
|
227
|
+
let (tx, rx) = channel()
|
|
228
|
+
spawn { tx.send("hello") }
|
|
229
|
+
let msg = rx.recv() # "hello"
|
|
230
|
+
|
|
231
|
+
# Select (wait for first)
|
|
232
|
+
match select [rx1, rx2, timeout(1000)] {
|
|
233
|
+
(0, msg) => handle1(msg)
|
|
234
|
+
(1, msg) => handle2(msg)
|
|
235
|
+
(2, _) => handleTimeout()
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Rationale:** CSP-style channels (Go-inspired) are simpler than shared-memory concurrency. `select` provides multiplexing. `timeout` wraps any async operation with a deadline.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Module: `std/csv`
|
|
244
|
+
|
|
245
|
+
```arc
|
|
246
|
+
use std/csv: parseCSV, toCSV
|
|
247
|
+
|
|
248
|
+
let data = read "data.csv" |> parseCSV # [{name: "...", age: "..."}]
|
|
249
|
+
let text = data |> toCSV # Back to CSV string
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Module: `std/time`
|
|
255
|
+
|
|
256
|
+
```arc
|
|
257
|
+
use std/time: now, format, parse, duration
|
|
258
|
+
|
|
259
|
+
let t = now() # Timestamp
|
|
260
|
+
let s = format(t, "YYYY-MM-DD") # "2026-02-16"
|
|
261
|
+
let d = duration(hours: 2, minutes: 30)
|
|
262
|
+
let later = t + d
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Module: `std/regex`
|
|
268
|
+
|
|
269
|
+
```arc
|
|
270
|
+
use std/regex: regex, matchAll, replaceAll
|
|
271
|
+
|
|
272
|
+
let pat = regex("[0-9]+")
|
|
273
|
+
let found = "abc 123 def 456" |> matchAll(pat) # ["123", "456"]
|
|
274
|
+
let cleaned = "abc 123" |> replaceAll(pat, "#") # "abc #"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Module: `std/crypto`
|
|
280
|
+
|
|
281
|
+
```arc
|
|
282
|
+
use std/crypto: hash, hmac, encrypt, decrypt, uuid
|
|
283
|
+
|
|
284
|
+
let h = hash("sha256", "hello")
|
|
285
|
+
let id = uuid() # "550e8400-e29b-..."
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Module: `std/test`
|
|
291
|
+
|
|
292
|
+
```arc
|
|
293
|
+
use std/test: test, assert, assertEq, assertErr
|
|
294
|
+
|
|
295
|
+
test "addition" {
|
|
296
|
+
assertEq(add(2, 3), 5)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
test "division by zero" {
|
|
300
|
+
assertErr(divide(1, 0))
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
test "user validation" {
|
|
304
|
+
let user = User({name: "Arc", email: "arc@test.com", age: 1})
|
|
305
|
+
assert(user.age > 0)
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## API Design Patterns
|
|
312
|
+
|
|
313
|
+
### 1. Functions over methods
|
|
314
|
+
```arc
|
|
315
|
+
# Arc prefers free functions (work with |>)
|
|
316
|
+
len(items) # not items.length
|
|
317
|
+
sort(items) # not items.sort()
|
|
318
|
+
filter(items, fn) # not items.filter(fn)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Rationale:** Free functions compose with `|>`. No method resolution overhead. Agents don't need to remember which type has which method.
|
|
322
|
+
|
|
323
|
+
### 2. Options as last parameter
|
|
324
|
+
```arc
|
|
325
|
+
fn request(url, method = "GET", headers = {}, timeout = 30000)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 3. Result types for fallible operations
|
|
329
|
+
```arc
|
|
330
|
+
fn readFile(path) -> Result<String> # not throw
|
|
331
|
+
fn parse(text) -> Result<Value> # not throw
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 4. Overloading by arity
|
|
335
|
+
```arc
|
|
336
|
+
rand() # 0.0..1.0
|
|
337
|
+
rand(max) # 0..max
|
|
338
|
+
rand(min, max) # min..max
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Token Efficiency Summary
|
|
344
|
+
|
|
345
|
+
| Operation | JS stdlib | Python stdlib | Arc stdlib | vs JS | vs Python |
|
|
346
|
+
|-----------|-----------|---------------|------------|-------|-----------|
|
|
347
|
+
| Read file | `fs.readFileSync(p,'utf8')` | `open(p).read()` | `read p` | -70% | -50% |
|
|
348
|
+
| Parse JSON | `JSON.parse(text)` | `json.loads(text)` | `parse(text)` | -30% | -25% |
|
|
349
|
+
| HTTP GET | `await fetch(url).then(r=>r.json())` | `requests.get(url).json()` | `@GET url` | -75% | -60% |
|
|
350
|
+
| Sort by key | `arr.sort((a,b)=>a.k-b.k)` | `sorted(arr,key=lambda x:x.k)` | `sort(arr,x=>x.k)` | -40% | -30% |
|
|
351
|
+
| Filter | `arr.filter(x=>x.active)` | `[x for x in arr if x.active]` | `filter(arr,x=>x.active)` | -10% | -20% |
|
|
352
|
+
| **Average** | | | | **-45%** | **-37%** |
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
**Last Updated:** 2026-02-16
|
|
357
|
+
**Status:** Draft v0.1
|