arc-lang 0.6.8 → 0.6.10

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/formatter.js CHANGED
@@ -108,7 +108,10 @@ export function format(source, options) {
108
108
  function formatExpr(expr, depth) {
109
109
  switch (expr.kind) {
110
110
  case "IntLiteral": return String(expr.value);
111
- case "FloatLiteral": return String(expr.value);
111
+ case "FloatLiteral": {
112
+ const s = String(expr.value);
113
+ return s.includes('.') ? s : s + '.0';
114
+ }
112
115
  case "BoolLiteral": return expr.value ? "true" : "false";
113
116
  case "NilLiteral": return "nil";
114
117
  case "StringLiteral": {
package/dist/linter.js CHANGED
@@ -222,6 +222,9 @@ export function lint(source, options) {
222
222
  for (const t of expr.targets)
223
223
  analyzeExpr(t, scope);
224
224
  break;
225
+ case "GroupExpr":
226
+ analyzeExpr(expr.expr, scope);
227
+ break;
225
228
  }
226
229
  }
227
230
  function analyzePattern(pat, scope) {
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const ARC_VERSION = "0.6.8";
1
+ export declare const ARC_VERSION = "0.6.9";
2
2
  export declare const ARC_BUILD_DATE: string;
3
3
  export declare const ARC_PLATFORM: string;
4
4
  /** Print version info */
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Arc Version System
2
- export const ARC_VERSION = "0.6.8";
2
+ export const ARC_VERSION = "0.6.9";
3
3
  export const ARC_BUILD_DATE = new Date().toISOString().split("T")[0];
4
4
  export const ARC_PLATFORM = `${process.platform}-${process.arch}`;
5
5
  /** Print version info */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arc-lang",
3
- "version": "0.6.8",
3
+ "version": "0.6.10",
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": {
package/stdlib/crypto.arc CHANGED
@@ -1,62 +1,62 @@
1
- # Arc Standard Library: crypto module
2
- # Hashing, encryption, and cryptographic utilities
3
-
4
- # --- Hashing ---
5
-
6
- pub fn md5(text) => crypto_hash("md5", text)
7
- pub fn sha1(text) => crypto_hash("sha1", text)
8
- pub fn sha256(text) => crypto_hash("sha256", text)
9
- pub fn sha512(text) => crypto_hash("sha512", text)
10
-
11
- # --- HMAC ---
12
-
13
- pub fn hmac_sha256(key, message) => crypto_hmac("sha256", key, message)
14
- pub fn hmac_sha512(key, message) => crypto_hmac("sha512", key, message)
15
-
16
- # --- Random ---
17
-
18
- pub fn random_bytes(n) {
19
- if n < 0 { panic("random_bytes: n must be non-negative") }
20
- el { crypto_random_bytes(n) }
21
- }
22
-
23
- pub fn random_int(min, max) {
24
- if min > max { panic("random_int: min must be <= max") }
25
- el { crypto_random_int(min, max) }
26
- }
27
-
28
- pub fn uuid() => crypto_uuid()
29
-
30
- # --- Base64 ---
31
-
32
- pub fn base64_encode(text) => crypto_encode_base64(text)
33
- pub fn base64_decode(text) => crypto_decode_base64(text)
34
-
35
- # --- Password Hashing ---
36
-
37
- pub fn hash_password(password, salt) {
38
- let input = salt ++ ":" ++ password
39
- let mut hash = sha256(input)
40
- for i in 0..100 {
41
- hash = sha256(hash ++ salt)
42
- }
43
- hash
44
- }
45
-
46
- pub fn verify_password(password, salt, hash) {
47
- let computed = hash_password(password, salt)
48
- computed == hash
49
- }
50
-
51
- # --- Utility ---
52
-
53
- pub fn constant_time_eq(a, b) {
54
- if len(a) != len(b) { false }
55
- el {
56
- let mut result = true
57
- for i in 0..len(a) {
58
- if char_at(a, i) != char_at(b, i) { result = false }
59
- }
60
- result
61
- }
62
- }
1
+ # Arc Standard Library: crypto module
2
+ # Hashing, encryption, and cryptographic utilities
3
+
4
+ # --- Hashing ---
5
+
6
+ pub fn md5(text) => crypto_hash("md5", text)
7
+ pub fn sha1(text) => crypto_hash("sha1", text)
8
+ pub fn sha256(text) => crypto_hash("sha256", text)
9
+ pub fn sha512(text) => crypto_hash("sha512", text)
10
+
11
+ # --- HMAC ---
12
+
13
+ pub fn hmac_sha256(key, message) => crypto_hmac("sha256", key, message)
14
+ pub fn hmac_sha512(key, message) => crypto_hmac("sha512", key, message)
15
+
16
+ # --- Random ---
17
+
18
+ pub fn random_bytes(n) {
19
+ if n < 0 { panic("random_bytes: n must be non-negative") }
20
+ el { crypto_random_bytes(n) }
21
+ }
22
+
23
+ pub fn random_int(min, max) {
24
+ if min > max { panic("random_int: min must be <= max") }
25
+ el { crypto_random_int(min, max) }
26
+ }
27
+
28
+ pub fn uuid() => crypto_uuid()
29
+
30
+ # --- Base64 ---
31
+
32
+ pub fn base64_encode(text) => crypto_encode_base64(text)
33
+ pub fn base64_decode(text) => crypto_decode_base64(text)
34
+
35
+ # --- Password Hashing ---
36
+
37
+ pub fn hash_password(password, salt) {
38
+ let input = salt ++ ":" ++ password
39
+ let mut hash = sha256(input)
40
+ for i in 0..100 {
41
+ hash = sha256(hash ++ salt)
42
+ }
43
+ hash
44
+ }
45
+
46
+ pub fn verify_password(password, salt, hash) {
47
+ let computed = hash_password(password, salt)
48
+ computed == hash
49
+ }
50
+
51
+ # --- Utility ---
52
+
53
+ pub fn constant_time_eq(a, b) {
54
+ if len(a) != len(b) { false }
55
+ el {
56
+ let mut result = true
57
+ for i in 0..len(a) {
58
+ if char_at(a, i) != char_at(b, i) { result = false }
59
+ }
60
+ result
61
+ }
62
+ }
@@ -1,75 +1,75 @@
1
- # Arc Standard Library: datetime module
2
- # Comprehensive date and time utilities
3
-
4
- # Constants
5
- let MS_PER_MINUTE = 60000
6
- let MS_PER_HOUR = 3600000
7
- let MS_PER_DAY = 86400000
8
-
9
- # Returns the current timestamp in milliseconds since Unix epoch
10
- pub fn now() => __builtin_now()
11
-
12
- # Returns today's date as a map {year, month, day}
13
- pub fn today() {
14
- let ts = now()
15
- let days = int(ts / MS_PER_DAY)
16
- __builtin_date_from_ts(ts)
17
- }
18
-
19
- # Parse a date string with the given format to a timestamp
20
- # Format tokens: YYYY, MM, DD, hh, mm, ss
21
- pub fn parse(date_string, format) => __builtin_date_parse(date_string, format)
22
-
23
- # Format a timestamp to a string using the given format
24
- # Format tokens: YYYY, MM, DD, hh, mm, ss
25
- pub fn format(timestamp, format_string) => __builtin_date_format(timestamp, format_string)
26
-
27
- # Add days to a timestamp, returns new timestamp
28
- pub fn add_days(timestamp, days) => timestamp + days * MS_PER_DAY
29
-
30
- # Add hours to a timestamp, returns new timestamp
31
- pub fn add_hours(timestamp, hours) => timestamp + hours * MS_PER_HOUR
32
-
33
- # Add minutes to a timestamp, returns new timestamp
34
- pub fn add_minutes(timestamp, minutes) => timestamp + minutes * MS_PER_MINUTE
35
-
36
- # Difference in days between two timestamps (absolute value)
37
- pub fn diff_days(ts1, ts2) {
38
- let diff = ts1 - ts2
39
- let abs_diff = if diff < 0 { 0 - diff } el { diff }
40
- int(abs_diff / MS_PER_DAY)
41
- }
42
-
43
- # Difference in hours between two timestamps (absolute value)
44
- pub fn diff_hours(ts1, ts2) {
45
- let diff = ts1 - ts2
46
- let abs_diff = if diff < 0 { 0 - diff } el { diff }
47
- int(abs_diff / MS_PER_HOUR)
48
- }
49
-
50
- # Difference in minutes between two timestamps (absolute value)
51
- pub fn diff_minutes(ts1, ts2) {
52
- let diff = ts1 - ts2
53
- let abs_diff = if diff < 0 { 0 - diff } el { diff }
54
- int(abs_diff / MS_PER_MINUTE)
55
- }
56
-
57
- # Returns the day of the week (0 = Sunday, 6 = Saturday)
58
- pub fn day_of_week(timestamp) {
59
- # Jan 1 1970 was a Thursday (4)
60
- let days = int(timestamp / MS_PER_DAY)
61
- let result = ((days + 4) % 7 + 7) % 7
62
- result
63
- }
64
-
65
- # Returns true if ts1 is before ts2
66
- pub fn is_before(ts1, ts2) => ts1 < ts2
67
-
68
- # Returns true if ts1 is after ts2
69
- pub fn is_after(ts1, ts2) => ts1 > ts2
70
-
71
- # Convert a timestamp to an ISO 8601 string
72
- pub fn to_iso(timestamp) => __builtin_date_to_iso(timestamp)
73
-
74
- # Parse an ISO 8601 string to a timestamp
75
- pub fn from_iso(iso_string) => __builtin_date_from_iso(iso_string)
1
+ # Arc Standard Library: datetime module
2
+ # Comprehensive date and time utilities
3
+
4
+ # Constants
5
+ let MS_PER_MINUTE = 60000
6
+ let MS_PER_HOUR = 3600000
7
+ let MS_PER_DAY = 86400000
8
+
9
+ # Returns the current timestamp in milliseconds since Unix epoch
10
+ pub fn now() => __builtin_now()
11
+
12
+ # Returns today's date as a map {year, month, day}
13
+ pub fn today() {
14
+ let ts = now()
15
+ let days = int(ts / MS_PER_DAY)
16
+ __builtin_date_from_ts(ts)
17
+ }
18
+
19
+ # Parse a date string with the given format to a timestamp
20
+ # Format tokens: YYYY, MM, DD, hh, mm, ss
21
+ pub fn parse(date_string, format) => __builtin_date_parse(date_string, format)
22
+
23
+ # Format a timestamp to a string using the given format
24
+ # Format tokens: YYYY, MM, DD, hh, mm, ss
25
+ pub fn format(timestamp, format_string) => __builtin_date_format(timestamp, format_string)
26
+
27
+ # Add days to a timestamp, returns new timestamp
28
+ pub fn add_days(timestamp, days) => timestamp + days * MS_PER_DAY
29
+
30
+ # Add hours to a timestamp, returns new timestamp
31
+ pub fn add_hours(timestamp, hours) => timestamp + hours * MS_PER_HOUR
32
+
33
+ # Add minutes to a timestamp, returns new timestamp
34
+ pub fn add_minutes(timestamp, minutes) => timestamp + minutes * MS_PER_MINUTE
35
+
36
+ # Difference in days between two timestamps (absolute value)
37
+ pub fn diff_days(ts1, ts2) {
38
+ let diff = ts1 - ts2
39
+ let abs_diff = if diff < 0 { 0 - diff } el { diff }
40
+ int(abs_diff / MS_PER_DAY)
41
+ }
42
+
43
+ # Difference in hours between two timestamps (absolute value)
44
+ pub fn diff_hours(ts1, ts2) {
45
+ let diff = ts1 - ts2
46
+ let abs_diff = if diff < 0 { 0 - diff } el { diff }
47
+ int(abs_diff / MS_PER_HOUR)
48
+ }
49
+
50
+ # Difference in minutes between two timestamps (absolute value)
51
+ pub fn diff_minutes(ts1, ts2) {
52
+ let diff = ts1 - ts2
53
+ let abs_diff = if diff < 0 { 0 - diff } el { diff }
54
+ int(abs_diff / MS_PER_MINUTE)
55
+ }
56
+
57
+ # Returns the day of the week (0 = Sunday, 6 = Saturday)
58
+ pub fn day_of_week(timestamp) {
59
+ # Jan 1 1970 was a Thursday (4)
60
+ let days = int(timestamp / MS_PER_DAY)
61
+ let result = ((days + 4) % 7 + 7) % 7
62
+ result
63
+ }
64
+
65
+ # Returns true if ts1 is before ts2
66
+ pub fn is_before(ts1, ts2) => ts1 < ts2
67
+
68
+ # Returns true if ts1 is after ts2
69
+ pub fn is_after(ts1, ts2) => ts1 > ts2
70
+
71
+ # Convert a timestamp to an ISO 8601 string
72
+ pub fn to_iso(timestamp) => __builtin_date_to_iso(timestamp)
73
+
74
+ # Parse an ISO 8601 string to a timestamp
75
+ pub fn from_iso(iso_string) => __builtin_date_from_iso(iso_string)
package/stdlib/error.arc CHANGED
@@ -1,42 +1,42 @@
1
- # Arc Standard Library: error module
2
- # Structured error handling
3
-
4
- # Create a structured error
5
- pub fn error(code, message) => error_new(code, message)
6
-
7
- # Check if a value is an error
8
- pub fn is_error(value) => error_is_error(value)
9
-
10
- # Wrap an error with additional context
11
- pub fn wrap_error(err, context) => error_wrap(err, context)
12
-
13
- # Try executing a function, return Ok/Err result
14
- pub fn try_fn(f) => error_try(f)
15
-
16
- # Execute fn, call handler if result is an error
17
- pub fn try_catch(f, handler) {
18
- let result = f()
19
- if is_error(result) { handler(result) }
20
- el { result }
21
- }
22
-
23
- # Execute fn, always run cleanup
24
- pub fn try_finally(f, cleanup) {
25
- let result = f()
26
- cleanup()
27
- result
28
- }
29
-
30
- # Full try/catch/finally
31
- pub fn try_catch_finally(f, handler, cleanup) {
32
- let result = f()
33
- let handled = if is_error(result) { handler(result) } el { result }
34
- cleanup()
35
- handled
36
- }
37
-
38
- # Throw an error (raises a runtime exception)
39
- pub fn throw(code, message) => error_throw(code, message)
40
-
41
- # Retry a function up to n times, returning the first success or last error
42
- pub fn retry(f, times) => error_retry(f, times)
1
+ # Arc Standard Library: error module
2
+ # Structured error handling
3
+
4
+ # Create a structured error
5
+ pub fn error(code, message) => error_new(code, message)
6
+
7
+ # Check if a value is an error
8
+ pub fn is_error(value) => error_is_error(value)
9
+
10
+ # Wrap an error with additional context
11
+ pub fn wrap_error(err, context) => error_wrap(err, context)
12
+
13
+ # Try executing a function, return Ok/Err result
14
+ pub fn try_fn(f) => error_try(f)
15
+
16
+ # Execute fn, call handler if result is an error
17
+ pub fn try_catch(f, handler) {
18
+ let result = f()
19
+ if is_error(result) { handler(result) }
20
+ el { result }
21
+ }
22
+
23
+ # Execute fn, always run cleanup
24
+ pub fn try_finally(f, cleanup) {
25
+ let result = f()
26
+ cleanup()
27
+ result
28
+ }
29
+
30
+ # Full try/catch/finally
31
+ pub fn try_catch_finally(f, handler, cleanup) {
32
+ let result = f()
33
+ let handled = if is_error(result) { handler(result) } el { result }
34
+ cleanup()
35
+ handled
36
+ }
37
+
38
+ # Throw an error (raises a runtime exception)
39
+ pub fn throw(code, message) => error_throw(code, message)
40
+
41
+ # Retry a function up to n times, returning the first success or last error
42
+ pub fn retry(f, times) => error_retry(f, times)
package/stdlib/llm.arc CHANGED
@@ -1,193 +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
- }
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/math.arc CHANGED
@@ -1,133 +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
- }
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 CHANGED
@@ -1,17 +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)
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)
package/stdlib/regex.arc CHANGED
@@ -21,19 +21,19 @@ pub fn test(pattern, text) {
21
21
 
22
22
  # Replaces the first occurrence of `pattern` with `replacement` in `text`
23
23
  # (For global replacement, use replace_all)
24
- pub fn replace(pattern, replacement, text) {
24
+ pub fn replace(pattern, text, replacement) {
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
- pub fn replace_first(pattern, replacement, text) {
30
+ pub fn replace_first(pattern, text, replacement) {
31
31
  let re = regex_new(pattern)
32
32
  regex_replace(re, replacement, text)
33
33
  }
34
34
 
35
35
  # Replaces all occurrences of `pattern` with `replacement` in `text`
36
- pub fn replace_all(pattern, replacement, text) {
36
+ pub fn replace_all(pattern, text, replacement) {
37
37
  let re = regex_new(pattern)
38
38
  regex_replace_all(re, replacement, text)
39
39
  }
@@ -56,6 +56,12 @@ pub fn capture_all(pattern, text) {
56
56
  regex_captures_all(re, text)
57
57
  }
58
58
 
59
+ # Returns a list of all matches of `pattern` in `text` (alias for find_all)
60
+ pub fn match_all(pattern, text) {
61
+ let re = regex_new(pattern)
62
+ regex_find_all(re, text)
63
+ }
64
+
59
65
  # Escapes all regex special characters in `text`
60
66
  pub fn escape(text) => __native("regex.escape", text)
61
67