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.
Files changed (48) hide show
  1. package/README.md +65 -130
  2. package/dist/ast.d.ts +30 -2
  3. package/dist/build.js +1 -1
  4. package/dist/formatter.js +15 -3
  5. package/dist/index.js +104 -0
  6. package/dist/interpreter.js +130 -17
  7. package/dist/lexer.d.ts +41 -37
  8. package/dist/lexer.js +47 -39
  9. package/dist/linter.js +18 -0
  10. package/dist/modules.js +5 -1
  11. package/dist/parser.d.ts +2 -0
  12. package/dist/parser.js +91 -11
  13. package/dist/repl.js +66 -1
  14. package/dist/version.d.ts +1 -1
  15. package/dist/version.js +1 -1
  16. package/package.json +4 -2
  17. package/stdlib/API_DESIGN.md +357 -0
  18. package/stdlib/ASYNC_DESIGN.md +815 -0
  19. package/stdlib/EXAMPLES.md +710 -0
  20. package/stdlib/MODULES.md +854 -0
  21. package/stdlib/README.md +64 -0
  22. package/stdlib/collections.arc +140 -0
  23. package/stdlib/crypto.arc +62 -0
  24. package/stdlib/csv.arc +40 -0
  25. package/stdlib/datetime.arc +75 -0
  26. package/stdlib/embed.arc +42 -0
  27. package/stdlib/env.arc +10 -0
  28. package/stdlib/error.arc +36 -0
  29. package/stdlib/html.arc +30 -0
  30. package/stdlib/http.arc +43 -0
  31. package/stdlib/io.arc +28 -0
  32. package/stdlib/json.arc +204 -0
  33. package/stdlib/llm.arc +193 -0
  34. package/stdlib/log.arc +20 -0
  35. package/stdlib/map.arc +72 -0
  36. package/stdlib/math.arc +133 -0
  37. package/stdlib/net.arc +17 -0
  38. package/stdlib/os.arc +81 -0
  39. package/stdlib/path.arc +11 -0
  40. package/stdlib/prompt.arc +49 -0
  41. package/stdlib/regex.arc +59 -0
  42. package/stdlib/result.arc +50 -0
  43. package/stdlib/store.arc +62 -0
  44. package/stdlib/strings.arc +44 -0
  45. package/stdlib/test.arc +61 -0
  46. package/stdlib/time.arc +19 -0
  47. package/stdlib/toml.arc +10 -0
  48. package/stdlib/yaml.arc +10 -0
@@ -0,0 +1,204 @@
1
+ # Arc Standard Library: json module
2
+ # JSON utilities (pure string manipulation)
3
+ #
4
+ # Note: Arc's lexer treats { inside strings as interpolation.
5
+ # We construct brace characters at runtime from str({}).
6
+
7
+ # Module-level brace chars
8
+ let _LB = slice(str({}), 0, 1)
9
+ let _RB = slice(str({}), 1, 2)
10
+ let _Q = chars("\"")[0]
11
+
12
+ pub fn to_json(value) {
13
+ let t = type_of(value)
14
+ if t == "nil" { "null" }
15
+ el if t == "bool" { if value { "true" } el { "false" } }
16
+ el if t == "int" { str(value) }
17
+ el if t == "float" {
18
+ let s = str(value)
19
+ if s == "Infinity" or s == "-Infinity" or s == "NaN" { "null" }
20
+ el { s }
21
+ }
22
+ el if t == "string" { _quote(value) }
23
+ el if t == "list" {
24
+ "[" ++ join(map(value, v => to_json(v)), ",") ++ "]"
25
+ }
26
+ el if t == "map" {
27
+ let ks = keys(value)
28
+ let pairs = map(ks, k => _quote(k) ++ ":" ++ to_json(value[k]))
29
+ _LB ++ join(pairs, ",") ++ _RB
30
+ }
31
+ el { str(value) }
32
+ }
33
+
34
+ fn _quote(s) {
35
+ let escaped = replace(replace(s, "\\", "\\\\"), "\n", "\\n")
36
+ let escaped = replace(escaped, "\t", "\\t")
37
+ let escaped = replace(escaped, "\r", "\\r")
38
+ let escaped = replace(escaped, "\"", "\\\"")
39
+ _Q ++ escaped ++ _Q
40
+ }
41
+
42
+ pub fn from_json(s) {
43
+ let trimmed = trim(s)
44
+ if len(trimmed) == 0 { nil }
45
+ el {
46
+ let parsed = _parse_value(trimmed)
47
+ parsed.value
48
+ }
49
+ }
50
+
51
+ fn _parse_value(s) {
52
+ let c = slice(s, 0, 1)
53
+ if c == _Q { _parse_string(s) }
54
+ el if c == "[" { _parse_array(s) }
55
+ el if c == _LB { _parse_object(s) }
56
+ el if c == "t" { _mk(true, slice(s, 4, len(s))) }
57
+ el if c == "f" { _mk(false, slice(s, 5, len(s))) }
58
+ el if c == "n" { _mk(nil, slice(s, 4, len(s))) }
59
+ el { _parse_number(s) }
60
+ }
61
+
62
+ fn _mk(v, r) {
63
+ let result = {}
64
+ result["value"] = v
65
+ result["rest"] = r
66
+ result
67
+ }
68
+
69
+ fn _parse_string(s) {
70
+ let mut i = 1
71
+ let mut result = ""
72
+ let mut done = false
73
+ let mut escaped = false
74
+ do {
75
+ let ch = slice(s, i, i + 1)
76
+ if escaped {
77
+ if ch == "n" { result = result ++ "\n" }
78
+ el if ch == "t" { result = result ++ "\t" }
79
+ el if ch == "u" {
80
+ let hex = slice(s, i + 1, i + 5)
81
+ i = i + 4
82
+ result = result ++ __native("json.from_codepoint", hex)
83
+ }
84
+ el { result = result ++ ch }
85
+ escaped = false
86
+ } el if ch == "\\" {
87
+ escaped = true
88
+ } el if ch == _Q {
89
+ done = true
90
+ } el {
91
+ result = result ++ ch
92
+ }
93
+ i = i + 1
94
+ } until done
95
+ _mk(result, trim(slice(s, i, len(s))))
96
+ }
97
+
98
+ fn _parse_number(s) {
99
+ let mut i = 0
100
+ let mut is_float = false
101
+ if slice(s, 0, 1) == "-" { i = 1 }
102
+ do {
103
+ let ch = slice(s, i, i + 1)
104
+ if ch == "." or ch == "e" or ch == "E" { is_float = true }
105
+ i = i + 1
106
+ } until i >= len(s) or not _is_num_char(slice(s, i, i + 1))
107
+ let num_str = slice(s, 0, i)
108
+ let value = if is_float { float(num_str) } el { int(num_str) }
109
+ _mk(value, trim(slice(s, i, len(s))))
110
+ }
111
+
112
+ fn _is_num_char(c) {
113
+ c == "0" or c == "1" or c == "2" or c == "3" or c == "4" or
114
+ c == "5" or c == "6" or c == "7" or c == "8" or c == "9" or
115
+ c == "." or c == "-" or c == "e" or c == "E" or c == "+"
116
+ }
117
+
118
+ fn _parse_array(s) {
119
+ let mut rest = trim(slice(s, 1, len(s)))
120
+ let mut items = []
121
+ if slice(rest, 0, 1) == "]" {
122
+ _mk(items, trim(slice(rest, 1, len(rest))))
123
+ } el {
124
+ let mut done = false
125
+ do {
126
+ let parsed = _parse_value(rest)
127
+ items = push(items, parsed.value)
128
+ rest = trim(parsed.rest)
129
+ let next = slice(rest, 0, 1)
130
+ if next == "," {
131
+ rest = trim(slice(rest, 1, len(rest)))
132
+ } el {
133
+ done = true
134
+ }
135
+ } until done
136
+ rest = trim(slice(rest, 1, len(rest)))
137
+ _mk(items, rest)
138
+ }
139
+ }
140
+
141
+ fn _parse_object(s) {
142
+ let mut rest = trim(slice(s, 1, len(s)))
143
+ let mut obj = {}
144
+ if slice(rest, 0, 1) == _RB {
145
+ _mk(obj, trim(slice(rest, 1, len(rest))))
146
+ } el {
147
+ let mut done = false
148
+ do {
149
+ let key_parsed = _parse_string(rest)
150
+ rest = trim(key_parsed.rest)
151
+ rest = trim(slice(rest, 1, len(rest)))
152
+ let val_parsed = _parse_value(rest)
153
+ obj[key_parsed.value] = val_parsed.value
154
+ rest = trim(val_parsed.rest)
155
+ let next = slice(rest, 0, 1)
156
+ if next == "," {
157
+ rest = trim(slice(rest, 1, len(rest)))
158
+ } el {
159
+ done = true
160
+ }
161
+ } until done
162
+ rest = trim(slice(rest, 1, len(rest)))
163
+ _mk(obj, rest)
164
+ }
165
+ }
166
+
167
+ pub fn pretty(value) {
168
+ _pretty_indent(value, 0)
169
+ }
170
+
171
+ fn _pretty_indent(value, depth) {
172
+ let indent = repeat(" ", depth)
173
+ let inner = repeat(" ", depth + 1)
174
+ let t = type_of(value)
175
+ if t == "map" {
176
+ let ks = keys(value)
177
+ if len(ks) == 0 { _LB ++ _RB }
178
+ el {
179
+ let pairs = map(ks, k => inner ++ _Q ++ k ++ _Q ++ ": " ++ _pretty_indent(value[k], depth + 1))
180
+ _LB ++ "\n" ++ join(pairs, ",\n") ++ "\n" ++ indent ++ _RB
181
+ }
182
+ }
183
+ el if t == "list" {
184
+ if len(value) == 0 { "[]" }
185
+ el {
186
+ let items = map(value, v => inner ++ _pretty_indent(v, depth + 1))
187
+ "[\n" ++ join(items, ",\n") ++ "\n" ++ indent ++ "]"
188
+ }
189
+ }
190
+ el { to_json(value) }
191
+ }
192
+
193
+ pub fn get_path(obj, path) {
194
+ let parts = split(path, ".")
195
+ let mut current = obj
196
+ for part in parts {
197
+ if type_of(current) == "map" {
198
+ current = current[part]
199
+ } el {
200
+ current = nil
201
+ }
202
+ }
203
+ current
204
+ }
package/stdlib/llm.arc ADDED
@@ -0,0 +1,193 @@
1
+ # Arc Standard Library: llm module
2
+ # Multi-provider LLM API integration
3
+ # Makes it natural for AI agents to call other AI agents.
4
+
5
+ use json
6
+ use os
7
+
8
+ # --- Provider registry ---
9
+
10
+ pub fn providers() {
11
+ [
12
+ {name: "openai", base_url: "https://api.openai.com/v1", env_key: "OPENAI_API_KEY"},
13
+ {name: "anthropic", base_url: "https://api.anthropic.com/v1", env_key: "ANTHROPIC_API_KEY"}
14
+ ]
15
+ }
16
+
17
+ fn _provider_config(provider) {
18
+ let all = providers()
19
+ let mut found = nil
20
+ for p in all {
21
+ if p.name == provider { found = p }
22
+ }
23
+ if found == nil { error_new("llm", "Unknown LLM provider: " ++ provider) }
24
+ found
25
+ }
26
+
27
+ fn _api_key(provider) {
28
+ let config = _provider_config(provider)
29
+ let key = os.get_env(config.env_key)
30
+ if key == nil { error_new("llm", "Missing API key: set " ++ config.env_key ++ " environment variable") }
31
+ key
32
+ }
33
+
34
+ # --- OpenAI payload construction ---
35
+
36
+ fn _openai_chat_payload(model, messages, options) {
37
+ let mut payload = {}
38
+ payload["model"] = model
39
+ payload["messages"] = messages
40
+ if options != nil {
41
+ let ks = keys(options)
42
+ for k in ks {
43
+ payload[k] = options[k]
44
+ }
45
+ }
46
+ payload
47
+ }
48
+
49
+ fn _openai_headers(api_key) {
50
+ let mut h = {}
51
+ h["Authorization"] = "Bearer " ++ api_key
52
+ h["Content-Type"] = "application/json"
53
+ h
54
+ }
55
+
56
+ # --- Anthropic payload construction ---
57
+
58
+ fn _anthropic_chat_payload(model, messages, options) {
59
+ # Anthropic expects system message separate from messages
60
+ let mut system_msg = nil
61
+ let mut user_msgs = []
62
+ for m in messages {
63
+ if m.role == "system" {
64
+ system_msg = m.content
65
+ } el {
66
+ user_msgs = push(user_msgs, m)
67
+ }
68
+ }
69
+ let mut payload = {}
70
+ payload["model"] = model
71
+ payload["messages"] = user_msgs
72
+ if system_msg != nil { payload["system"] = system_msg }
73
+ payload["max_tokens"] = if options != nil and options["max_tokens"] != nil { options["max_tokens"] } el { 1024 }
74
+ if options != nil {
75
+ let ks = keys(options)
76
+ for k in ks {
77
+ if k != "max_tokens" { payload[k] = options[k] }
78
+ }
79
+ }
80
+ payload
81
+ }
82
+
83
+ fn _anthropic_headers(api_key) {
84
+ let mut h = {}
85
+ h["x-api-key"] = api_key
86
+ h["anthropic-version"] = "2023-06-01"
87
+ h["Content-Type"] = "application/json"
88
+ h
89
+ }
90
+
91
+ # --- Core API ---
92
+
93
+ # Send a chat completion request
94
+ # provider: "openai" | "anthropic"
95
+ # model: e.g. "gpt-4o", "claude-sonnet-4-20250514"
96
+ # messages: [{role: "user", content: "Hello"}]
97
+ # options: {temperature: 0.7, max_tokens: 1024} (optional)
98
+ pub fn chat(provider, model, messages, options) {
99
+ let key = _api_key(provider)
100
+
101
+ if provider == "openai" {
102
+ let payload = _openai_chat_payload(model, messages, options)
103
+ let body = json.to_json(payload)
104
+ let headers = _openai_headers(key)
105
+ let resp = @POST "https://api.openai.com/v1/chat/completions" {data: body, headers: headers}
106
+ if resp.ok and resp.status == 200 {
107
+ let data = resp.data
108
+ {ok: true, content: data.choices[0].message.content, usage: data.usage, raw: data}
109
+ } el {
110
+ {ok: false, error: resp.data, status: resp.status}
111
+ }
112
+ } el if provider == "anthropic" {
113
+ let payload = _anthropic_chat_payload(model, messages, options)
114
+ let body = json.to_json(payload)
115
+ let headers = _anthropic_headers(key)
116
+ let resp = @POST "https://api.anthropic.com/v1/messages" {data: body, headers: headers}
117
+ if resp.ok and resp.status == 200 {
118
+ let data = resp.data
119
+ let content = if type_of(data.content) == "list" and len(data.content) > 0 {
120
+ data.content[0].text
121
+ } el { nil }
122
+ {ok: true, content: content, usage: data.usage, raw: data}
123
+ } el {
124
+ {ok: false, error: resp.data, status: resp.status}
125
+ }
126
+ } el {
127
+ error_new("llm", "Unsupported provider: " ++ provider)
128
+ }
129
+ }
130
+
131
+ # Simple text completion (wraps chat with a single user message)
132
+ pub fn complete(provider, model, prompt, options) {
133
+ let messages = [{role: "user", content: prompt}]
134
+ chat(provider, model, messages, options)
135
+ }
136
+
137
+ # Streaming chat — calls callback with each chunk
138
+ # Note: true streaming requires SSE parsing; this simulates via full response
139
+ pub fn stream(provider, model, messages, callback) {
140
+ # For now, do a regular call and invoke callback with the full response
141
+ # True SSE streaming would require async I/O support in the runtime
142
+ let result = chat(provider, model, messages, nil)
143
+ if result.ok {
144
+ callback(result.content)
145
+ }
146
+ result
147
+ }
148
+
149
+ # List available models for a provider
150
+ pub fn models(provider) {
151
+ let key = _api_key(provider)
152
+
153
+ if provider == "openai" {
154
+ let headers = _openai_headers(key)
155
+ let resp = @GET "https://api.openai.com/v1/models" {headers: headers}
156
+ if resp.ok and resp.status == 200 {
157
+ map(resp.data.data, m => m.id)
158
+ } el {
159
+ []
160
+ }
161
+ } el if provider == "anthropic" {
162
+ # Anthropic doesn't have a public models endpoint; return known models
163
+ [
164
+ "claude-sonnet-4-20250514",
165
+ "claude-opus-4-20250514",
166
+ "claude-haiku-35-20241022",
167
+ "claude-sonnet-3-5-20241022"
168
+ ]
169
+ } el {
170
+ []
171
+ }
172
+ }
173
+
174
+ # Estimate API cost in USD
175
+ # Pricing approximate as of 2025
176
+ pub fn estimate_cost(model, input_tokens, output_tokens) {
177
+ let pricing = _model_pricing(model)
178
+ let input_cost = (input_tokens / 1000000.0) * pricing.input
179
+ let output_cost = (output_tokens / 1000000.0) * pricing.output
180
+ {input_cost: input_cost, output_cost: output_cost, total: input_cost + output_cost, currency: "USD"}
181
+ }
182
+
183
+ fn _model_pricing(model) {
184
+ # Prices per million tokens (input, output)
185
+ if starts(model, "gpt-4o-mini") { {input: 0.15, output: 0.60} }
186
+ el if starts(model, "gpt-4o") { {input: 2.50, output: 10.00} }
187
+ el if starts(model, "gpt-4-turbo") { {input: 10.00, output: 30.00} }
188
+ el if starts(model, "gpt-3.5") { {input: 0.50, output: 1.50} }
189
+ el if contains(model, "opus") { {input: 15.00, output: 75.00} }
190
+ el if contains(model, "sonnet") { {input: 3.00, output: 15.00} }
191
+ el if contains(model, "haiku") { {input: 0.80, output: 4.00} }
192
+ el { {input: 1.00, output: 3.00} } # fallback estimate
193
+ }
package/stdlib/log.arc ADDED
@@ -0,0 +1,20 @@
1
+ # Arc Standard Library: log module
2
+ # Structured logging with levels and colors
3
+
4
+ pub fn debug(msg) => __native("log.emit", "debug", msg, nil)
5
+ pub fn info(msg) => __native("log.emit", "info", msg, nil)
6
+ pub fn warn(msg) => __native("log.emit", "warn", msg, nil)
7
+ pub fn error(msg) => __native("log.emit", "error", msg, nil)
8
+ pub fn fatal(msg) => __native("log.fatal", msg)
9
+
10
+ pub fn set_level(level) => __native("log.set_level", level)
11
+
12
+ pub fn with(fields) => __native("log.with", fields)
13
+
14
+ pub fn json(level, msg, fields) => __native("log.json", level, msg, fields)
15
+
16
+ # Child logger helpers — call these on the map returned by with()
17
+ pub fn child_debug(logger, msg) => __native("log.emit", "debug", msg, logger)
18
+ pub fn child_info(logger, msg) => __native("log.emit", "info", msg, logger)
19
+ pub fn child_warn(logger, msg) => __native("log.emit", "warn", msg, logger)
20
+ pub fn child_error(logger, msg) => __native("log.emit", "error", msg, logger)
package/stdlib/map.arc ADDED
@@ -0,0 +1,72 @@
1
+ # Arc Standard Library: map module
2
+ # Map utilities
3
+
4
+ pub fn merge(a, b) {
5
+ let mut result = {}
6
+ for k in keys(a) {
7
+ result[k] = a[k]
8
+ }
9
+ for k in keys(b) {
10
+ result[k] = b[k]
11
+ }
12
+ result
13
+ }
14
+
15
+ pub fn map_values(m, f) {
16
+ let mut result = {}
17
+ for k in keys(m) {
18
+ result[k] = f(m[k])
19
+ }
20
+ result
21
+ }
22
+
23
+ pub fn map_keys(m, f) {
24
+ let mut result = {}
25
+ for k in keys(m) {
26
+ let new_key = str(f(k))
27
+ result[new_key] = m[k]
28
+ }
29
+ result
30
+ }
31
+
32
+ pub fn filter_map(m, f) {
33
+ let mut result = {}
34
+ for k in keys(m) {
35
+ if f(m[k]) {
36
+ result[k] = m[k]
37
+ }
38
+ }
39
+ result
40
+ }
41
+
42
+ pub fn from_pairs(list) {
43
+ let mut result = {}
44
+ for pair in list {
45
+ result[pair[0]] = pair[1]
46
+ }
47
+ result
48
+ }
49
+
50
+ pub fn to_pairs(m) {
51
+ map(keys(m), k => [k, m[k]])
52
+ }
53
+
54
+ pub fn pick(m, ks) {
55
+ let mut result = {}
56
+ for k in ks {
57
+ if contains(keys(m), k) {
58
+ result[k] = m[k]
59
+ }
60
+ }
61
+ result
62
+ }
63
+
64
+ pub fn omit(m, ks) {
65
+ let mut result = {}
66
+ for k in keys(m) {
67
+ if not contains(ks, k) {
68
+ result[k] = m[k]
69
+ }
70
+ }
71
+ result
72
+ }
@@ -0,0 +1,133 @@
1
+ # Arc Standard Library: math module
2
+
3
+ # === Constants ===
4
+ pub let PI = 3.141592653589793
5
+ pub let E = 2.718281828459045
6
+ pub let TAU = 6.283185307179586
7
+ # INF and NAN available via native runtime
8
+
9
+ # === Basic ===
10
+ pub fn abs(x) => if x < 0 { 0 - x } el { x }
11
+
12
+ pub fn sign(x) {
13
+ if x > 0 { 1 }
14
+ el if x < 0 { -1 }
15
+ el { 0 }
16
+ }
17
+
18
+ pub fn clamp(x, lo, hi) {
19
+ if x < lo { lo }
20
+ el if x > hi { hi }
21
+ el { x }
22
+ }
23
+
24
+ # === Rounding ===
25
+ pub fn ceil(x) => __native("math.ceil", x)
26
+
27
+ pub fn floor(x) => int(x)
28
+
29
+ pub fn round(x) => int(x + 0.5)
30
+
31
+ # === Powers & Roots ===
32
+ pub fn pow(base, exp) {
33
+ if exp == 0 { 1 }
34
+ el if exp < 0 and base == 0 { nil }
35
+ el if type_of(exp) == "float" { __native("math.pow", base, exp) }
36
+ el if exp < 0 { 1.0 / pow(base, 0 - exp) }
37
+ el {
38
+ let mut result = 1
39
+ let mut b = base
40
+ let mut e = exp
41
+ do {
42
+ if e % 2 == 1 { result = result * b }
43
+ e = int(e / 2)
44
+ b = b * b
45
+ } until e == 0
46
+ result
47
+ }
48
+ }
49
+
50
+ pub fn sqrt(x) {
51
+ if x == 0 { 0.0 }
52
+ el if x < 0 { nil }
53
+ el {
54
+ let mut guess = x / 2.0
55
+ for i in 0..25 {
56
+ guess = (guess + x / guess) / 2.0
57
+ }
58
+ guess
59
+ }
60
+ }
61
+
62
+ pub fn cbrt(x) => __native("math.cbrt", x)
63
+
64
+ pub fn hypot(x, y) => __native("math.hypot", x, y)
65
+
66
+ # === Trigonometry ===
67
+ pub fn sin(x) => __native("math.sin", x)
68
+ pub fn cos(x) => __native("math.cos", x)
69
+ pub fn tan(x) => __native("math.tan", x)
70
+ pub fn asin(x) => __native("math.asin", x)
71
+ pub fn acos(x) => __native("math.acos", x)
72
+ pub fn atan(x) => __native("math.atan", x)
73
+ pub fn atan2(y, x) => __native("math.atan2", y, x)
74
+
75
+ pub fn degrees(rad) => rad * 180.0 / PI
76
+ pub fn radians(deg) => deg * PI / 180.0
77
+
78
+ # === Logarithmic & Exponential ===
79
+ pub fn log(x) => __native("math.log", x)
80
+ pub fn log2(x) => __native("math.log2", x)
81
+ pub fn log10(x) => __native("math.log10", x)
82
+ pub fn exp(x) => __native("math.exp", x)
83
+
84
+ # === Combinatorics ===
85
+ pub fn factorial(n) {
86
+ if n < 0 { nil }
87
+ el if n <= 1 { 1 }
88
+ el {
89
+ let mut result = 1
90
+ for i in 2..n + 1 {
91
+ result = result * i
92
+ }
93
+ if str(result) == "Infinity" { nil }
94
+ el { result }
95
+ }
96
+ }
97
+
98
+ pub fn gcd(a, b) {
99
+ let mut x = abs(a)
100
+ let mut y = abs(b)
101
+ if x == 0 and y == 0 { 0 }
102
+ el {
103
+ do {
104
+ let t = y
105
+ y = x % y
106
+ x = t
107
+ } until y == 0
108
+ x
109
+ }
110
+ }
111
+
112
+ pub fn lcm(a, b) {
113
+ let d = gcd(a, b)
114
+ if d == 0 { 0 }
115
+ el { abs(a / d) * b }
116
+ }
117
+
118
+ # === Aggregation ===
119
+ pub fn sum(lst) {
120
+ let mut total = 0
121
+ for x in lst {
122
+ if type_of(x) == "int" or type_of(x) == "float" {
123
+ total = total + x
124
+ }
125
+ }
126
+ total
127
+ }
128
+
129
+ pub fn product(lst) {
130
+ let mut total = 1
131
+ for x in lst { total = total * x }
132
+ total
133
+ }
package/stdlib/net.arc ADDED
@@ -0,0 +1,17 @@
1
+ # Arc Standard Library: net module
2
+ # Networking utilities
3
+
4
+ # --- URL ---
5
+
6
+ pub fn url_parse(url) => net_url_parse(url)
7
+ pub fn url_encode(text) => net_url_encode(text)
8
+ pub fn url_decode(text) => net_url_decode(text)
9
+
10
+ # --- Query String ---
11
+
12
+ pub fn parse_query(query_string) => net_query_parse(query_string)
13
+ pub fn build_query(params_map) => net_query_stringify(params_map)
14
+
15
+ # --- IP ---
16
+
17
+ pub fn ip_is_valid(addr) => net_ip_is_valid(addr)