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/stdlib/json.arc
ADDED
|
@@ -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
|
+
}
|
package/stdlib/math.arc
ADDED
|
@@ -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)
|