atmos/os_lib/aura/builtins/
modules_str_task.rs1use super::*;
4
5pub(crate) fn builtin_load(env: &mut Env, args: &[AST]) -> Result<Value, String> {
7 let vals = eval_args(env, args)?;
8 if vals.is_empty() {
9 return Err(String::from("USAGE: load(\"path.aura\")"));
10 }
11 let raw_path = unquote(&vals[0]);
12 let path = resolve_path(env, &raw_path)?;
13
14 let fs = crate::kernel::fs::get_fs();
15 let (_, bytes) = fs
16 .read_file(&path)
17 .ok_or_else(|| alloc::format!("load: file not found: {}", path))?;
18 let src = core::str::from_utf8(&bytes)
19 .map_err(|_| alloc::format!("load: invalid UTF-8 in {}", path))?;
20
21 let tokens = super::super::lexer::tokenize(src);
22 let asts = super::super::parser::parse(&tokens)
23 .map_err(|e| alloc::format!("load: parse error in {}: {}", path, e))?;
24
25 let mut last = Value::Nil;
26 for ast in &asts {
27 last = eval(env, ast)?;
28 }
29 Ok(last)
30}
31
32pub(crate) fn builtin_require(env: &mut Env, args: &[AST]) -> Result<Value, String> {
35 let vals = eval_args(env, args)?;
36 if vals.is_empty() {
37 return Err(String::from("USAGE: require(\"path.aura\")"));
38 }
39 let raw_path = unquote(&vals[0]);
40 let path = resolve_path(env, &raw_path)?;
41
42 let already = match env.get("__loaded__") {
44 Some(Value::List(list)) => list.iter().any(|v| {
45 if let Value::Str(s) = v {
46 s == &path
47 } else {
48 false
49 }
50 }),
51 _ => false,
52 };
53 if already {
54 return Ok(Value::Nil);
55 }
56
57 let fs = crate::kernel::fs::get_fs();
59 let (_, bytes) = fs
60 .read_file(&path)
61 .ok_or_else(|| alloc::format!("require: file not found: {}", path))?;
62 let src = core::str::from_utf8(&bytes)
63 .map_err(|_| alloc::format!("require: invalid UTF-8 in {}", path))?;
64
65 let tokens = super::super::lexer::tokenize(src);
66 let asts = super::super::parser::parse(&tokens)
67 .map_err(|e| alloc::format!("require: parse error in {}: {}", path, e))?;
68
69 let mut last = Value::Nil;
70 for ast in &asts {
71 last = eval(env, ast)?;
72 }
73
74 let mut loaded = match env.get("__loaded__") {
76 Some(Value::List(list)) => list,
77 _ => Vec::new(),
78 };
79 loaded.push(Value::Str(path));
80 env.set(String::from("__loaded__"), Value::List(loaded));
81
82 Ok(last)
83}
84
85pub(crate) fn builtin_str_split(env: &mut Env, args: &[AST]) -> Result<Value, String> {
90 let vals = eval_args(env, args)?;
91 if vals.len() != 2 {
92 return Err(String::from(
93 "str.split requires exactly 2 arguments: (str.split target delimiter)",
94 ));
95 }
96 let target = match &vals[0] {
97 Value::Str(s) => s,
98 _ => return Err(String::from("str.split first argument must be a string")),
99 };
100 let delim = match &vals[1] {
101 Value::Str(s) => s,
102 _ => return Err(String::from("str.split second argument must be a string")),
103 };
104
105 let parts: Vec<Value> = target
106 .split(delim)
107 .map(|s| Value::Str(String::from(s)))
108 .collect();
109 Ok(Value::List(parts))
110}
111
112pub(crate) fn builtin_str_join(env: &mut Env, args: &[AST]) -> Result<Value, String> {
113 let vals = eval_args(env, args)?;
114 if vals.len() != 2 {
115 return Err(String::from(
116 "str.join requires exactly 2 arguments: (str.join list delimiter)",
117 ));
118 }
119 let list = match &vals[0] {
120 Value::List(l) => l,
121 _ => return Err(String::from("str.join first argument must be a list")),
122 };
123 let delim = match &vals[1] {
124 Value::Str(s) => s,
125 _ => return Err(String::from("str.join second argument must be a string")),
126 };
127
128 let mut parts = Vec::new();
129 for val in list {
130 match val {
131 Value::Str(s) => parts.push(s.clone()),
132 _ => {
133 parts.push(val.to_string());
134 }
135 }
136 }
137 Ok(Value::Str(parts.join(delim)))
138}
139
140pub(crate) fn builtin_str_substring(env: &mut Env, args: &[AST]) -> Result<Value, String> {
141 let vals = eval_args(env, args)?;
142 if vals.len() != 2 && vals.len() != 3 {
143 return Err(String::from(
144 "str.substring requires 2 or 3 arguments: (str.substring target start [end])",
145 ));
146 }
147 let target = match &vals[0] {
148 Value::Str(s) => s,
149 _ => {
150 return Err(String::from(
151 "str.substring first argument must be a string",
152 ))
153 }
154 };
155 let start = match &vals[1] {
156 Value::Num(n) => n.to_i64().unwrap_or(0) as usize,
157 _ => {
158 return Err(String::from(
159 "str.substring second argument must be a number",
160 ))
161 }
162 };
163
164 let chars: Vec<char> = target.chars().collect();
165 let end = if vals.len() == 3 {
166 match &vals[2] {
167 Value::Num(n) => n.to_i64().unwrap_or(chars.len() as i64) as usize,
168 _ => {
169 return Err(String::from(
170 "str.substring third argument must be a number",
171 ))
172 }
173 }
174 } else {
175 chars.len()
176 };
177
178 if start > chars.len() || start > end {
179 return Ok(Value::Str(String::new()));
180 }
181 let end = end.min(chars.len());
182 let sub: String = chars[start..end].iter().collect();
183 Ok(Value::Str(sub))
184}
185
186pub(crate) fn builtin_str_length(env: &mut Env, args: &[AST]) -> Result<Value, String> {
187 let vals = eval_args(env, args)?;
188 if vals.len() != 1 {
189 return Err(String::from(
190 "str.length requires exactly 1 argument: (str.length target)",
191 ));
192 }
193 let target = match &vals[0] {
194 Value::Str(s) => s,
195 _ => return Err(String::from("str.length argument must be a string")),
196 };
197
198 let len = target.chars().count();
199 Ok(Value::Num(BigInt::from_i64(len as i64)))
200}
201
202pub(crate) fn builtin_str_replace(env: &mut Env, args: &[AST]) -> Result<Value, String> {
203 let vals = eval_args(env, args)?;
204 if vals.len() != 3 {
205 return Err(String::from(
206 "str.replace requires exactly 3 arguments: (str.replace target from to)",
207 ));
208 }
209 let target = match &vals[0] {
210 Value::Str(s) => s,
211 _ => return Err(String::from("str.replace first argument must be a string")),
212 };
213 let from = match &vals[1] {
214 Value::Str(s) => s,
215 _ => return Err(String::from("str.replace second argument must be a string")),
216 };
217 let to = match &vals[2] {
218 Value::Str(s) => s,
219 _ => return Err(String::from("str.replace third argument must be a string")),
220 };
221
222 Ok(Value::Str(target.replace(from, to)))
223}
224
225pub(crate) struct ParallelArgs {
230 lambda: Value,
231 env: Env,
232}
233
234pub(crate) fn parallel_entry(arg: usize) {
235 let args = unsafe { Box::from_raw(arg as *mut ParallelArgs) };
236 if let Value::Lambda(arg_names, body_ast, closure_env) = args.lambda {
237 if arg_names.is_empty() {
238 let mut local_env = Env::with_parent(*closure_env);
239 let _ = eval(&mut local_env, &body_ast);
240 }
241 }
242}
243
244pub(crate) fn builtin_task_parallel(env: &mut Env, args: &[AST]) -> Result<Value, String> {
245 let vals = eval_args(env, args)?;
246 if vals.len() != 1 {
247 return Err(String::from(
248 "task.parallel requires exactly 1 argument: (task.parallel lambda)",
249 ));
250 }
251 let lambda = vals[0].clone();
252 if !matches!(lambda, Value::Lambda(_, _, _)) {
253 return Err(String::from(
254 "task.parallel argument must be a lambda function",
255 ));
256 }
257
258 let thread_args = Box::new(ParallelArgs {
259 lambda,
260 env: env.clone(),
261 });
262 let arg_ptr = Box::into_raw(thread_args) as usize;
263
264 let pid = crate::kernel::scheduler::spawn_with_arg(
265 parallel_entry,
266 arg_ptr,
267 crate::kernel::scheduler::Priority::Normal,
268 "aura_parallel",
269 );
270
271 if pid == 0 {
272 return Err(String::from("Failed to spawn parallel thread"));
273 }
274
275 Ok(Value::Nil)
276}
277
278pub(crate) struct ForkJoinTask {
279 lambda: Value,
280 env: Env,
281 state: Arc<ForkJoinState>,
282}
283
284pub(crate) struct ForkJoinState {
285 done: AtomicBool,
286 result: Mutex<Result<Value, String>>,
287}
288
289pub(crate) fn fork_join_entry(arg: usize) {
290 let task = unsafe { Box::from_raw(arg as *mut ForkJoinTask) };
291 let res = if let Value::Lambda(arg_names, body_ast, closure_env) = &task.lambda {
292 if !arg_names.is_empty() {
293 Err(String::from("fork_join lambda must take 0 arguments"))
294 } else {
295 let mut local_env = Env::with_parent(*closure_env.clone());
296 eval(&mut local_env, body_ast).and_then(|v| force_eval(&mut local_env, v))
297 }
298 } else {
299 Err(String::from("fork_join argument must be a lambda"))
300 };
301
302 *task.state.result.lock() = res;
303 task.state.done.store(true, Ordering::Release);
304}
305
306pub(crate) fn builtin_task_fork_join(env: &mut Env, args: &[AST]) -> Result<Value, String> {
307 let vals = eval_args(env, args)?;
308 if vals.len() != 1 {
309 return Err(String::from(
310 "task.fork_join requires exactly 1 argument: (task.fork_join lambda_list)",
311 ));
312 }
313 let list = match &vals[0] {
314 Value::List(l) => l,
315 _ => {
316 return Err(String::from(
317 "task.fork_join argument must be a list of lambdas",
318 ))
319 }
320 };
321
322 let mut states = Vec::with_capacity(list.len());
323
324 for lambda in list {
325 if !matches!(lambda, Value::Lambda(_, _, _)) {
326 return Err(String::from(
327 "task.fork_join list elements must be lambda functions",
328 ));
329 }
330 let state = Arc::new(ForkJoinState {
331 done: AtomicBool::new(false),
332 result: Mutex::new(Ok(Value::Nil)),
333 });
334 states.push(state.clone());
335
336 let fork_task = Box::new(ForkJoinTask {
337 lambda: lambda.clone(),
338 env: env.clone(),
339 state,
340 });
341 let arg_ptr = Box::into_raw(fork_task) as usize;
342
343 let pid = crate::kernel::scheduler::spawn_with_arg(
344 fork_join_entry,
345 arg_ptr,
346 crate::kernel::scheduler::Priority::Normal,
347 "aura_forkjoin",
348 );
349 if pid == 0 {
350 return Err(String::from("Failed to spawn fork_join thread"));
351 }
352 }
353
354 loop {
356 let mut all_done = true;
357 for state in &states {
358 if !state.done.load(Ordering::Acquire) {
359 all_done = false;
360 break;
361 }
362 }
363 if all_done {
364 break;
365 }
366 crate::kernel::scheduler::yield_now();
367 }
368
369 let mut results = Vec::with_capacity(states.len());
371 for state in states {
372 let res = state.result.lock().clone()?;
373 results.push(res);
374 }
375
376 Ok(Value::List(results))
377}
378
379pub(crate) struct QueueState {
380 queue: Mutex<VecDeque<Value>>,
381 closed: AtomicBool,
382}
383
384pub(crate) static QUEUES: Mutex<Option<alloc::collections::BTreeMap<usize, Arc<QueueState>>>> =
385 Mutex::new(None);
386pub(crate) static NEXT_QUEUE_ID: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(1);
387
388pub(crate) fn get_queue_id(val: &Value) -> Result<usize, String> {
389 if let Value::Dict(d) = val {
390 if let (Some(Value::Str(t)), Some(Value::Num(id))) = (d.get("__type"), d.get("id")) {
391 if t == "queue" {
392 if let Some(id_usize) = id.to_i64().map(|x| x as usize) {
393 return Ok(id_usize);
394 }
395 }
396 }
397 }
398 Err(String::from("Argument is not a valid queue dictionary"))
399}
400
401pub(crate) fn builtin_task_queue(_env: &mut Env, _args: &[AST]) -> Result<Value, String> {
402 let mut q_map = QUEUES.lock();
403 let id = NEXT_QUEUE_ID.fetch_add(1, Ordering::SeqCst);
404 let state = Arc::new(QueueState {
405 queue: Mutex::new(VecDeque::new()),
406 closed: AtomicBool::new(false),
407 });
408 q_map
409 .get_or_insert_with(alloc::collections::BTreeMap::new)
410 .insert(id, state);
411
412 let mut dict = alloc::collections::BTreeMap::new();
413 dict.insert(String::from("__type"), Value::Str(String::from("queue")));
414 dict.insert(String::from("id"), Value::Num(BigInt::from_i64(id as i64)));
415 Ok(Value::Dict(dict))
416}
417
418pub(crate) fn builtin_task_send(env: &mut Env, args: &[AST]) -> Result<Value, String> {
419 let vals = eval_args(env, args)?;
420 if vals.len() != 2 {
421 return Err(String::from(
422 "task.send requires exactly 2 arguments: (task.send queue value)",
423 ));
424 }
425 let q_id = get_queue_id(&vals[0])?;
426 let val = vals[1].clone();
427
428 let q_map = QUEUES.lock();
429 if let Some(map) = q_map.as_ref() {
430 if let Some(q_state) = map.get(&q_id) {
431 if q_state.closed.load(Ordering::Relaxed) {
432 return Err(String::from("Cannot send to a closed queue"));
433 }
434 q_state.queue.lock().push_back(val);
435 return Ok(Value::Nil);
436 }
437 }
438 Err(String::from("Invalid queue object"))
439}
440
441pub(crate) fn builtin_task_recv(env: &mut Env, args: &[AST]) -> Result<Value, String> {
442 let vals = eval_args(env, args)?;
443 if vals.len() != 1 {
444 return Err(String::from(
445 "task.recv requires exactly 1 argument: (task.recv queue)",
446 ));
447 }
448 let q_id = get_queue_id(&vals[0])?;
449
450 let q_state = {
451 let q_map = QUEUES.lock();
452 if let Some(map) = q_map.as_ref() {
453 if let Some(q_state) = map.get(&q_id) {
454 q_state.clone()
455 } else {
456 return Err(String::from("Invalid queue object"));
457 }
458 } else {
459 return Err(String::from("Invalid queue object"));
460 }
461 };
462
463 loop {
464 if q_state.closed.load(Ordering::Relaxed) {
465 let mut lock = q_state.queue.lock();
466 if let Some(val) = lock.pop_front() {
467 return Ok(val);
468 }
469 return Ok(Value::Nil);
470 }
471
472 {
473 let mut lock = q_state.queue.lock();
474 if let Some(val) = lock.pop_front() {
475 return Ok(val);
476 }
477 }
478 crate::kernel::scheduler::yield_now();
479 }
480}
481
482pub(crate) fn builtin_task_close(env: &mut Env, args: &[AST]) -> Result<Value, String> {
483 let vals = eval_args(env, args)?;
484 if vals.len() != 1 {
485 return Err(String::from(
486 "task.close requires exactly 1 argument: (task.close queue)",
487 ));
488 }
489 let q_id = get_queue_id(&vals[0])?;
490
491 let mut q_map = QUEUES.lock();
492 if let Some(map) = q_map.as_mut() {
493 if let Some(q_state) = map.remove(&q_id) {
494 q_state.closed.store(true, Ordering::SeqCst);
495 return Ok(Value::Nil);
496 }
497 }
498 Err(String::from("Invalid queue object"))
499}
500
501pub(crate) static TEMPLATE_GUARD: Mutex<Option<Value>> = Mutex::new(None);
506pub(crate) static TEMPLATE_POST_GUARD: Mutex<Option<Value>> = Mutex::new(None);
507
508pub(crate) struct FormatSpec {
509 pub(crate) alternate: bool, pub(crate) zero_pad: bool, pub(crate) width: usize, pub(crate) comma: bool, pub(crate) precision: Option<usize>, pub(crate) fmt_type: Option<char>, }
516