1use crate::interpreter::{
46 ActorInner, BuiltInFn, ChannelInner, Evidence, Function, Interpreter, RuntimeError, Value,
47};
48use std::cell::RefCell;
49use std::collections::HashMap;
50use std::io::Write;
51use std::net::{TcpListener, TcpStream};
52use std::rc::Rc;
53use std::sync::atomic::{AtomicU64, Ordering};
54use std::sync::{mpsc, Arc, Mutex, OnceLock};
55use std::thread;
56use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
57
58static TCP_LISTENERS: OnceLock<Mutex<HashMap<u64, TcpListener>>> = OnceLock::new();
60static TCP_STREAMS: OnceLock<Mutex<HashMap<u64, TcpStream>>> = OnceLock::new();
61static BUF_READERS: OnceLock<Mutex<HashMap<u64, std::io::BufReader<TcpStream>>>> = OnceLock::new();
62static LISTENER_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
63static STREAM_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
64static BUFREADER_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
65
66pub fn get_listener_registry() -> &'static Mutex<HashMap<u64, TcpListener>> {
67 TCP_LISTENERS.get_or_init(|| Mutex::new(HashMap::new()))
68}
69
70pub fn get_stream_registry() -> &'static Mutex<HashMap<u64, TcpStream>> {
71 TCP_STREAMS.get_or_init(|| Mutex::new(HashMap::new()))
72}
73
74pub fn get_bufreader_registry() -> &'static Mutex<HashMap<u64, std::io::BufReader<TcpStream>>> {
75 BUF_READERS.get_or_init(|| Mutex::new(HashMap::new()))
76}
77
78pub fn store_bufreader(reader: std::io::BufReader<TcpStream>) -> u64 {
79 let id = BUFREADER_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
80 get_bufreader_registry().lock().unwrap().insert(id, reader);
81 id
82}
83
84fn store_listener(listener: TcpListener) -> u64 {
85 let id = LISTENER_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
86 get_listener_registry().lock().unwrap().insert(id, listener);
87 id
88}
89
90pub fn store_tcp_stream(stream: TcpStream) -> u64 {
91 let id = STREAM_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
92 get_stream_registry().lock().unwrap().insert(id, stream);
93 id
94}
95
96pub fn get_tcp_stream(id: u64) -> Option<std::sync::MutexGuard<'static, HashMap<u64, TcpStream>>> {
97 let guard = get_stream_registry().lock().unwrap();
98 if guard.contains_key(&id) {
99 Some(guard)
100 } else {
101 None
102 }
103}
104
105use base64::{engine::general_purpose, Engine as _};
107use md5::Md5;
108use regex::Regex;
109use sha2::{Digest, Sha256, Sha512};
110use unicode_normalization::UnicodeNormalization;
111use unicode_segmentation::UnicodeSegmentation;
112use uuid::Uuid;
113
114use deunicode::deunicode;
116use icu_casemap::titlecase::TitlecaseOptions;
117use icu_casemap::CaseMapper;
118use icu_collator::{Collator, CollatorOptions};
119use icu_locid::{LanguageIdentifier, Locale};
120use icu_segmenter::{SentenceSegmenter, WordSegmenter};
121use unicode_bidi::BidiInfo;
122use unicode_script::{Script, UnicodeScript};
123use unicode_width::UnicodeWidthStr;
124
125use rust_stemmers::{Algorithm as StemAlgorithm, Stemmer};
127use tiktoken_rs::{cl100k_base, p50k_base, r50k_base};
128use whatlang::{detect, Lang, Script as WhatLangScript};
129
130use rand::Rng;
132
133pub fn register_stdlib(interp: &mut Interpreter) {
135 register_core(interp);
136 register_math(interp);
137 register_collections(interp);
138 register_string(interp);
139 register_evidence(interp);
140 register_affect(interp);
141 register_iter(interp);
142 register_io(interp);
143 register_time(interp);
144 register_random(interp);
145 register_convert(interp);
146 register_cycle(interp);
147 register_simd(interp);
148 register_graphics_math(interp);
149 register_concurrency(interp);
150 register_json(interp);
152 register_fs(interp);
153 register_crypto(interp);
154 register_regex(interp);
155 register_uuid(interp);
156 register_system(interp);
157 register_stats(interp);
158 register_matrix(interp);
159 register_functional(interp);
161 register_benchmark(interp);
162 register_itertools(interp);
163 register_ranges(interp);
164 register_bitwise(interp);
165 register_format(interp);
166 register_pattern(interp);
168 register_devex(interp);
170 register_soa(interp);
172 register_tensor(interp);
173 register_autodiff(interp);
174 register_spatial(interp);
175 register_physics(interp);
176 register_geometric_algebra(interp);
178 register_dimensional(interp);
179 register_ecs(interp);
180 register_polycultural_text(interp);
182 register_text_intelligence(interp);
184 register_hologram(interp);
186 register_experimental_crypto(interp);
187 register_multibase(interp);
189 register_audio(interp);
191 register_spirituality(interp);
193 register_color(interp);
195 register_protocol(interp);
197 register_agent_tools(interp);
199 register_agent_llm(interp);
200 register_agent_memory(interp);
201 register_agent_planning(interp);
202 register_agent_vectors(interp);
203 register_agent_swarm(interp);
205 register_agent_reasoning(interp);
206 register_terminal(interp);
208}
209
210fn define(
212 interp: &mut Interpreter,
213 name: &str,
214 arity: Option<usize>,
215 func: fn(&mut Interpreter, Vec<Value>) -> Result<Value, RuntimeError>,
216) {
217 let builtin = Value::BuiltIn(Rc::new(BuiltInFn {
218 name: name.to_string(),
219 arity,
220 func,
221 }));
222 interp
223 .globals
224 .borrow_mut()
225 .define(name.to_string(), builtin);
226}
227
228fn values_equal_simple(a: &Value, b: &Value) -> bool {
230 match (a, b) {
231 (Value::Int(x), Value::Int(y)) => x == y,
232 (Value::Float(x), Value::Float(y)) => (x - y).abs() < f64::EPSILON,
233 (Value::Int(x), Value::Float(y)) | (Value::Float(y), Value::Int(x)) => {
234 (*x as f64 - y).abs() < f64::EPSILON
235 }
236 (Value::Bool(x), Value::Bool(y)) => x == y,
237 (Value::String(x), Value::String(y)) => x == y,
238 (Value::Char(x), Value::Char(y)) => x == y,
239 (Value::Null, Value::Null) => true,
240 (Value::Empty, Value::Empty) => true,
241 (Value::Infinity, Value::Infinity) => true,
242 _ => false,
243 }
244}
245
246fn register_core(interp: &mut Interpreter) {
251 interp.globals.borrow_mut().define("u64·MAX".to_string(), Value::Int(u64::MAX as i64));
254 interp.globals.borrow_mut().define("u64·MIN".to_string(), Value::Int(0));
255 interp.globals.borrow_mut().define("i64·MAX".to_string(), Value::Int(i64::MAX));
256 interp.globals.borrow_mut().define("i64·MIN".to_string(), Value::Int(i64::MIN));
257 interp.globals.borrow_mut().define("u32·MAX".to_string(), Value::Int(u32::MAX as i64));
258 interp.globals.borrow_mut().define("u32·MIN".to_string(), Value::Int(0));
259 interp.globals.borrow_mut().define("i32·MAX".to_string(), Value::Int(i32::MAX as i64));
260 interp.globals.borrow_mut().define("i32·MIN".to_string(), Value::Int(i32::MIN as i64));
261 interp.globals.borrow_mut().define("u16·MAX".to_string(), Value::Int(u16::MAX as i64));
262 interp.globals.borrow_mut().define("u8·MAX".to_string(), Value::Int(u8::MAX as i64));
263 interp.globals.borrow_mut().define("usize·MAX".to_string(), Value::Int(usize::MAX as i64));
264 interp.globals.borrow_mut().define("isize·MAX".to_string(), Value::Int(isize::MAX as i64));
265 interp.globals.borrow_mut().define("isize·MIN".to_string(), Value::Int(isize::MIN as i64));
266 interp.globals.borrow_mut().define("f64·INFINITY".to_string(), Value::Float(f64::INFINITY));
267 interp.globals.borrow_mut().define("f64·NEG_INFINITY".to_string(), Value::Float(f64::NEG_INFINITY));
268 interp.globals.borrow_mut().define("f64·NAN".to_string(), Value::Float(f64::NAN));
269
270 interp.variant_constructors.insert("SeekFrom·Start".to_string(), ("SeekFrom".to_string(), "Start".to_string(), 1));
272 interp.variant_constructors.insert("SeekFrom·End".to_string(), ("SeekFrom".to_string(), "End".to_string(), 1));
273 interp.variant_constructors.insert("SeekFrom·Current".to_string(), ("SeekFrom".to_string(), "Current".to_string(), 1));
274
275 let ordering_variants = ["SeqCst", "Acquire", "Release", "AcqRel", "Relaxed"];
277 for variant in ordering_variants {
278 let full_name = format!("std·sync·atomic·Ordering·{}", variant);
279 let short_name = format!("Ordering·{}", variant);
280 interp.globals.borrow_mut().define(full_name, Value::Variant {
281 enum_name: "Ordering".to_string(),
282 variant_name: variant.to_string(),
283 fields: None,
284 });
285 interp.globals.borrow_mut().define(short_name, Value::Variant {
286 enum_name: "Ordering".to_string(),
287 variant_name: variant.to_string(),
288 fields: None,
289 });
290 }
291
292 let error_kind_variants = ["NotFound", "PermissionDenied", "ConnectionRefused", "ConnectionReset",
294 "ConnectionAborted", "NotConnected", "AddrInUse", "AddrNotAvailable", "BrokenPipe",
295 "AlreadyExists", "WouldBlock", "InvalidInput", "InvalidData", "TimedOut", "WriteZero",
296 "Interrupted", "UnexpectedEof", "Other"];
297 for variant in error_kind_variants {
298 let full_name = format!("std·io·ErrorKind·{}", variant);
299 let short_name = format!("ErrorKind·{}", variant);
300 interp.globals.borrow_mut().define(full_name, Value::Variant {
301 enum_name: "ErrorKind".to_string(),
302 variant_name: variant.to_string(),
303 fields: None,
304 });
305 interp.globals.borrow_mut().define(short_name, Value::Variant {
306 enum_name: "ErrorKind".to_string(),
307 variant_name: variant.to_string(),
308 fields: None,
309 });
310 }
311
312 define(interp, "print", None, |interp, args| {
314 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
315 let line = output.join(" ");
316 print!("{}", line);
317 std::io::stdout().flush().ok();
318 interp.output.push(line);
319 Ok(Value::Null)
320 });
321
322 define(interp, "println", None, |interp, args| {
324 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
325 let line = output.join(" ");
326 println!("{}", line);
327 interp.output.push(line);
328 Ok(Value::Null)
329 });
330
331 define(interp, "eprint", None, |interp, args| {
333 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
334 let line = output.join(" ");
335 eprint!("{}", line);
336 std::io::stderr().flush().ok();
337 interp.output.push(line);
338 Ok(Value::Null)
339 });
340
341 define(interp, "eprintln", None, |interp, args| {
343 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
344 let line = output.join(" ");
345 eprintln!("{}", line);
346 interp.output.push(line);
347 Ok(Value::Null)
348 });
349
350 define(interp, "dbg", Some(1), |interp, args| {
352 let output = format!("[DEBUG] {:?}", args[0]);
353 println!("{}", output);
354 interp.output.push(output);
355 Ok(args[0].clone())
356 });
357
358 define(interp, "type_of", Some(1), |_, args| {
360 let type_name = match &args[0] {
361 Value::Null => "null",
362 Value::Bool(_) => "bool",
363 Value::Int(_) => "i64",
364 Value::Float(_) => "f64",
365 Value::String(_) => "str",
366 Value::Char(_) => "char",
367 Value::Array(_) => "array",
368 Value::Tuple(_) => "tuple",
369 Value::Struct { name, .. } => name,
370 Value::Variant { enum_name, .. } => enum_name,
371 Value::Function(_) => "fn",
372 Value::BuiltIn(_) => "builtin",
373 Value::Ref(_) => "ref",
374 Value::Infinity => "infinity",
375 Value::Empty => "empty",
376 Value::Evidential { evidence, .. } => match evidence {
377 Evidence::Known => "known",
378 Evidence::Uncertain => "uncertain",
379 Evidence::Reported => "reported",
380 Evidence::Paradox => "paradox",
381 },
382 Value::Affective { .. } => "affective",
383 Value::Map(_) => "map",
384 Value::Set(_) => "set",
385 Value::Channel(_) => "channel",
386 Value::ThreadHandle(_) => "thread",
387 Value::Actor(_) => "actor",
388 Value::Future(_) => "future",
389 Value::VariantConstructor { .. } => "variant_constructor",
390 Value::DefaultConstructor { .. } => "default_constructor",
391 Value::Range { .. } => "range",
392 };
393 Ok(Value::String(Rc::new(type_name.to_string())))
394 });
395
396 define(interp, "assert", None, |_, args| {
398 if args.is_empty() {
399 return Err(RuntimeError::new("assert() requires at least one argument"));
400 }
401 let condition = match &args[0] {
402 Value::Bool(b) => *b,
403 _ => return Err(RuntimeError::new("assert() condition must be bool")),
404 };
405 if !condition {
406 let msg = if args.len() > 1 {
407 format!("{}", args[1])
408 } else {
409 "assertion failed".to_string()
410 };
411 return Err(RuntimeError::new(format!("Assertion failed: {}", msg)));
412 }
413 Ok(Value::Null)
414 });
415
416 define(interp, "panic", None, |_, args| {
418 let msg = if args.is_empty() {
419 "explicit panic".to_string()
420 } else {
421 args.iter()
422 .map(|v| format!("{}", v))
423 .collect::<Vec<_>>()
424 .join(" ")
425 };
426 Err(RuntimeError::new(format!("PANIC: {}", msg)))
427 });
428
429 define(interp, "todo", None, |_, args| {
431 let msg = if args.is_empty() {
432 "not yet implemented".to_string()
433 } else {
434 format!("{}", args[0])
435 };
436 Err(RuntimeError::new(format!("TODO: {}", msg)))
437 });
438
439 define(interp, "unreachable", None, |_, args| {
441 let msg = if args.is_empty() {
442 "entered unreachable code".to_string()
443 } else {
444 format!("{}", args[0])
445 };
446 Err(RuntimeError::new(format!("UNREACHABLE: {}", msg)))
447 });
448
449 define(interp, "clone", Some(1), |_, args| Ok(deep_clone(&args[0])));
451
452 define(interp, "id", Some(1), |_, args| Ok(args[0].clone()));
454
455 define(interp, "default", None, |interp, args| {
458 let type_name = if args.is_empty() {
459 match &interp.current_self_type {
462 Some(t) => t.clone(),
463 None => return Ok(Value::Struct {
464 name: "Default".to_string(),
465 fields: Rc::new(RefCell::new(std::collections::HashMap::new())),
466 }),
467 }
468 } else {
469 match &args[0] {
470 Value::String(s) => s.to_string(),
471 _ => return Err(RuntimeError::new("default() requires type name string")),
472 }
473 };
474 let type_name = type_name.as_str();
475 match type_name {
476 "bool" => Ok(Value::Bool(false)),
477 "i64" | "int" => Ok(Value::Int(0)),
478 "f64" | "float" => Ok(Value::Float(0.0)),
479 "str" | "string" => Ok(Value::String(Rc::new(String::new()))),
480 "array" => Ok(Value::Array(Rc::new(RefCell::new(Vec::new())))),
481 _ => {
482 if let Some(struct_def) = interp.default_structs.get(type_name).cloned() {
484 use crate::ast::StructFields;
485 let mut fields = std::collections::HashMap::new();
486 if let StructFields::Named(field_defs) = &struct_def.fields {
487 for field in field_defs {
488 let default_val = if let Some(ref default_expr) = field.default {
490 match interp.evaluate(default_expr) {
491 Ok(v) => v,
492 Err(_) => Value::Null,
493 }
494 } else {
495 Value::Null
496 };
497 fields.insert(field.name.name.clone(), default_val);
498 }
499 }
500 Ok(Value::Struct {
501 name: type_name.to_string(),
502 fields: Rc::new(RefCell::new(fields)),
503 })
504 } else {
505 Ok(Value::Struct {
507 name: type_name.to_string(),
508 fields: Rc::new(RefCell::new(std::collections::HashMap::new())),
509 })
510 }
511 }
512 }
513 });
514
515 define(interp, "Result·Ok", Some(1), |_, args| {
517 Ok(Value::Variant {
518 enum_name: "Result".to_string(),
519 variant_name: "Ok".to_string(),
520 fields: Some(Rc::new(vec![args[0].clone()])),
521 })
522 });
523
524 define(interp, "Ok", Some(1), |_, args| {
526 Ok(Value::Variant {
527 enum_name: "Result".to_string(),
528 variant_name: "Ok".to_string(),
529 fields: Some(Rc::new(vec![args[0].clone()])),
530 })
531 });
532
533 define(interp, "Result·Err", Some(1), |_, args| {
535 Ok(Value::Variant {
536 enum_name: "Result".to_string(),
537 variant_name: "Err".to_string(),
538 fields: Some(Rc::new(vec![args[0].clone()])),
539 })
540 });
541
542 define(interp, "Err", Some(1), |_, args| {
544 Ok(Value::Variant {
545 enum_name: "Result".to_string(),
546 variant_name: "Err".to_string(),
547 fields: Some(Rc::new(vec![args[0].clone()])),
548 })
549 });
550
551 define(interp, "Option·Some", Some(1), |_, args| {
553 Ok(Value::Variant {
554 enum_name: "Option".to_string(),
555 variant_name: "Some".to_string(),
556 fields: Some(Rc::new(vec![args[0].clone()])),
557 })
558 });
559
560 define(interp, "Some", Some(1), |_, args| {
562 Ok(Value::Variant {
563 enum_name: "Option".to_string(),
564 variant_name: "Some".to_string(),
565 fields: Some(Rc::new(vec![args[0].clone()])),
566 })
567 });
568
569 interp.globals.borrow_mut().define(
571 "Option·None".to_string(),
572 Value::Variant {
573 enum_name: "Option".to_string(),
574 variant_name: "None".to_string(),
575 fields: None,
576 },
577 );
578
579 interp.globals.borrow_mut().define(
581 "None".to_string(),
582 Value::Variant {
583 enum_name: "Option".to_string(),
584 variant_name: "None".to_string(),
585 fields: None,
586 },
587 );
588
589 define(interp, "Map·new", Some(0), |_, _| {
591 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
592 });
593
594 define(interp, "HashMap·new", Some(0), |_, _| {
596 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
597 });
598
599 define(interp, "HashMap·with_capacity", Some(1), |_, _args| {
601 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
603 });
604
605 define(interp, "std·collections·HashMap·new", Some(0), |_, _| {
607 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
608 });
609
610 define(interp, "std·collections·HashMap·with_capacity", Some(1), |_, _args| {
612 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
613 });
614
615 define(interp, "HashSet·new", Some(0), |_, _| {
617 Ok(Value::Set(Rc::new(RefCell::new(std::collections::HashSet::new()))))
618 });
619
620 define(interp, "HashSet·with_capacity", Some(1), |_, _args| {
622 Ok(Value::Set(Rc::new(RefCell::new(std::collections::HashSet::new()))))
623 });
624
625 define(interp, "std·collections·HashSet·new", Some(0), |_, _| {
627 Ok(Value::Set(Rc::new(RefCell::new(std::collections::HashSet::new()))))
628 });
629
630 define(interp, "Vec·new", Some(0), |_, _| {
632 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
633 });
634
635 define(interp, "String·new", Some(0), |_, _| {
637 Ok(Value::String(Rc::new(String::new())))
638 });
639
640 define(interp, "String·from", Some(1), |_, args| {
642 let s = match &args[0] {
643 Value::String(s) => (**s).clone(),
644 Value::Int(n) => n.to_string(),
645 Value::Float(f) => f.to_string(),
646 Value::Bool(b) => b.to_string(),
647 Value::Char(c) => c.to_string(),
648 _ => format!("{}", args[0]),
649 };
650 Ok(Value::String(Rc::new(s)))
651 });
652
653 define(interp, "Box·new", Some(1), |_, args| {
655 Ok(args[0].clone())
656 });
657
658 define(interp, "String·from_raw_parts", Some(3), |_, args| {
660 match &args[0] {
662 Value::String(s) => Ok(Value::String(s.clone())),
663 Value::Null => Ok(Value::String(Rc::new(String::new()))),
664 _ => Ok(Value::String(Rc::new(format!("{}", args[0])))),
665 }
666 });
667
668 define(interp, "slice·from_raw_parts", Some(2), |_, args| {
670 match &args[0] {
672 Value::String(s) => Ok(Value::String(s.clone())),
673 Value::Array(arr) => Ok(Value::Array(arr.clone())),
674 _ => Ok(args[0].clone()),
675 }
676 });
677}
678
679fn deep_clone(value: &Value) -> Value {
681 match value {
682 Value::Array(arr) => {
683 let cloned: Vec<Value> = arr.borrow().iter().map(deep_clone).collect();
684 Value::Array(Rc::new(RefCell::new(cloned)))
685 }
686 Value::Struct { name, fields } => {
687 let cloned: HashMap<String, Value> = fields
688 .borrow()
689 .iter()
690 .map(|(k, v)| (k.clone(), deep_clone(v)))
691 .collect();
692 Value::Struct {
693 name: name.clone(),
694 fields: Rc::new(RefCell::new(cloned)),
695 }
696 }
697 Value::Evidential { value, evidence } => Value::Evidential {
698 value: Box::new(deep_clone(value)),
699 evidence: *evidence,
700 },
701 other => other.clone(),
702 }
703}
704
705fn register_math(interp: &mut Interpreter) {
710 define(interp, "abs", Some(1), |_, args| match &args[0] {
712 Value::Int(n) => Ok(Value::Int(n.abs())),
713 Value::Float(n) => Ok(Value::Float(n.abs())),
714 _ => Err(RuntimeError::new("abs() requires number")),
715 });
716
717 define(interp, "neg", Some(1), |_, args| match &args[0] {
718 Value::Int(n) => Ok(Value::Int(-n)),
719 Value::Float(n) => Ok(Value::Float(-n)),
720 _ => Err(RuntimeError::new("neg() requires number")),
721 });
722
723 define(interp, "sqrt", Some(1), |_, args| match &args[0] {
724 Value::Int(n) => Ok(Value::Float((*n as f64).sqrt())),
725 Value::Float(n) => Ok(Value::Float(n.sqrt())),
726 _ => Err(RuntimeError::new("sqrt() requires number")),
727 });
728
729 define(interp, "cbrt", Some(1), |_, args| match &args[0] {
730 Value::Int(n) => Ok(Value::Float((*n as f64).cbrt())),
731 Value::Float(n) => Ok(Value::Float(n.cbrt())),
732 _ => Err(RuntimeError::new("cbrt() requires number")),
733 });
734
735 define(interp, "pow", Some(2), |_, args| {
736 match (&args[0], &args[1]) {
737 (Value::Int(base), Value::Int(exp)) => {
738 if *exp >= 0 {
739 Ok(Value::Int(base.pow(*exp as u32)))
740 } else {
741 Ok(Value::Float((*base as f64).powi(*exp as i32)))
742 }
743 }
744 (Value::Float(base), Value::Int(exp)) => Ok(Value::Float(base.powi(*exp as i32))),
745 (Value::Float(base), Value::Float(exp)) => Ok(Value::Float(base.powf(*exp))),
746 (Value::Int(base), Value::Float(exp)) => Ok(Value::Float((*base as f64).powf(*exp))),
747 _ => Err(RuntimeError::new("pow() requires numbers")),
748 }
749 });
750
751 define(interp, "exp", Some(1), |_, args| match &args[0] {
752 Value::Int(n) => Ok(Value::Float((*n as f64).exp())),
753 Value::Float(n) => Ok(Value::Float(n.exp())),
754 _ => Err(RuntimeError::new("exp() requires number")),
755 });
756
757 define(interp, "ln", Some(1), |_, args| match &args[0] {
758 Value::Int(n) => Ok(Value::Float((*n as f64).ln())),
759 Value::Float(n) => Ok(Value::Float(n.ln())),
760 _ => Err(RuntimeError::new("ln() requires number")),
761 });
762
763 define(interp, "log", Some(2), |_, args| {
764 let (value, base) = match (&args[0], &args[1]) {
765 (Value::Int(v), Value::Int(b)) => (*v as f64, *b as f64),
766 (Value::Float(v), Value::Int(b)) => (*v, *b as f64),
767 (Value::Int(v), Value::Float(b)) => (*v as f64, *b),
768 (Value::Float(v), Value::Float(b)) => (*v, *b),
769 _ => return Err(RuntimeError::new("log() requires numbers")),
770 };
771 Ok(Value::Float(value.log(base)))
772 });
773
774 define(interp, "log10", Some(1), |_, args| match &args[0] {
775 Value::Int(n) => Ok(Value::Float((*n as f64).log10())),
776 Value::Float(n) => Ok(Value::Float(n.log10())),
777 _ => Err(RuntimeError::new("log10() requires number")),
778 });
779
780 define(interp, "log2", Some(1), |_, args| match &args[0] {
781 Value::Int(n) => Ok(Value::Float((*n as f64).log2())),
782 Value::Float(n) => Ok(Value::Float(n.log2())),
783 _ => Err(RuntimeError::new("log2() requires number")),
784 });
785
786 define(interp, "sin", Some(1), |_, args| match &args[0] {
788 Value::Int(n) => Ok(Value::Float((*n as f64).sin())),
789 Value::Float(n) => Ok(Value::Float(n.sin())),
790 _ => Err(RuntimeError::new("sin() requires number")),
791 });
792
793 define(interp, "cos", Some(1), |_, args| match &args[0] {
794 Value::Int(n) => Ok(Value::Float((*n as f64).cos())),
795 Value::Float(n) => Ok(Value::Float(n.cos())),
796 _ => Err(RuntimeError::new("cos() requires number")),
797 });
798
799 define(interp, "tan", Some(1), |_, args| match &args[0] {
800 Value::Int(n) => Ok(Value::Float((*n as f64).tan())),
801 Value::Float(n) => Ok(Value::Float(n.tan())),
802 _ => Err(RuntimeError::new("tan() requires number")),
803 });
804
805 define(interp, "asin", Some(1), |_, args| match &args[0] {
806 Value::Int(n) => Ok(Value::Float((*n as f64).asin())),
807 Value::Float(n) => Ok(Value::Float(n.asin())),
808 _ => Err(RuntimeError::new("asin() requires number")),
809 });
810
811 define(interp, "acos", Some(1), |_, args| match &args[0] {
812 Value::Int(n) => Ok(Value::Float((*n as f64).acos())),
813 Value::Float(n) => Ok(Value::Float(n.acos())),
814 _ => Err(RuntimeError::new("acos() requires number")),
815 });
816
817 define(interp, "atan", Some(1), |_, args| match &args[0] {
818 Value::Int(n) => Ok(Value::Float((*n as f64).atan())),
819 Value::Float(n) => Ok(Value::Float(n.atan())),
820 _ => Err(RuntimeError::new("atan() requires number")),
821 });
822
823 define(interp, "atan2", Some(2), |_, args| {
824 let (y, x) = match (&args[0], &args[1]) {
825 (Value::Int(y), Value::Int(x)) => (*y as f64, *x as f64),
826 (Value::Float(y), Value::Int(x)) => (*y, *x as f64),
827 (Value::Int(y), Value::Float(x)) => (*y as f64, *x),
828 (Value::Float(y), Value::Float(x)) => (*y, *x),
829 _ => return Err(RuntimeError::new("atan2() requires numbers")),
830 };
831 Ok(Value::Float(y.atan2(x)))
832 });
833
834 define(interp, "sinh", Some(1), |_, args| match &args[0] {
836 Value::Int(n) => Ok(Value::Float((*n as f64).sinh())),
837 Value::Float(n) => Ok(Value::Float(n.sinh())),
838 _ => Err(RuntimeError::new("sinh() requires number")),
839 });
840
841 define(interp, "cosh", Some(1), |_, args| match &args[0] {
842 Value::Int(n) => Ok(Value::Float((*n as f64).cosh())),
843 Value::Float(n) => Ok(Value::Float(n.cosh())),
844 _ => Err(RuntimeError::new("cosh() requires number")),
845 });
846
847 define(interp, "tanh", Some(1), |_, args| match &args[0] {
848 Value::Int(n) => Ok(Value::Float((*n as f64).tanh())),
849 Value::Float(n) => Ok(Value::Float(n.tanh())),
850 _ => Err(RuntimeError::new("tanh() requires number")),
851 });
852
853 define(interp, "floor", Some(1), |_, args| match &args[0] {
855 Value::Int(n) => Ok(Value::Int(*n)),
856 Value::Float(n) => Ok(Value::Int(n.floor() as i64)),
857 _ => Err(RuntimeError::new("floor() requires number")),
858 });
859
860 define(interp, "ceil", Some(1), |_, args| match &args[0] {
861 Value::Int(n) => Ok(Value::Int(*n)),
862 Value::Float(n) => Ok(Value::Int(n.ceil() as i64)),
863 _ => Err(RuntimeError::new("ceil() requires number")),
864 });
865
866 define(interp, "round", Some(1), |_, args| match &args[0] {
867 Value::Int(n) => Ok(Value::Int(*n)),
868 Value::Float(n) => Ok(Value::Int(n.round() as i64)),
869 _ => Err(RuntimeError::new("round() requires number")),
870 });
871
872 define(interp, "trunc", Some(1), |_, args| match &args[0] {
873 Value::Int(n) => Ok(Value::Int(*n)),
874 Value::Float(n) => Ok(Value::Int(n.trunc() as i64)),
875 _ => Err(RuntimeError::new("trunc() requires number")),
876 });
877
878 define(interp, "fract", Some(1), |_, args| match &args[0] {
879 Value::Int(_) => Ok(Value::Float(0.0)),
880 Value::Float(n) => Ok(Value::Float(n.fract())),
881 _ => Err(RuntimeError::new("fract() requires number")),
882 });
883
884 define(interp, "min", Some(2), |_, args| {
886 match (&args[0], &args[1]) {
887 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
888 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
889 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).min(*b))),
890 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.min(*b as f64))),
891 _ => Err(RuntimeError::new("min() requires numbers")),
892 }
893 });
894
895 define(interp, "max", Some(2), |_, args| {
896 match (&args[0], &args[1]) {
897 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
898 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
899 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).max(*b))),
900 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.max(*b as f64))),
901 _ => Err(RuntimeError::new("max() requires numbers")),
902 }
903 });
904
905 define(interp, "clamp", Some(3), |_, args| {
906 match (&args[0], &args[1], &args[2]) {
907 (Value::Int(val), Value::Int(min), Value::Int(max)) => {
908 Ok(Value::Int(*val.max(min).min(max)))
909 }
910 (Value::Float(val), Value::Float(min), Value::Float(max)) => {
911 Ok(Value::Float(val.max(*min).min(*max)))
912 }
913 _ => Err(RuntimeError::new("clamp() requires matching number types")),
914 }
915 });
916
917 define(interp, "sign", Some(1), |_, args| match &args[0] {
919 Value::Int(n) => Ok(Value::Int(n.signum())),
920 Value::Float(n) => Ok(Value::Float(if *n > 0.0 {
921 1.0
922 } else if *n < 0.0 {
923 -1.0
924 } else {
925 0.0
926 })),
927 _ => Err(RuntimeError::new("sign() requires number")),
928 });
929
930 define(interp, "PI", Some(0), |_, _| {
932 Ok(Value::Float(std::f64::consts::PI))
933 });
934 define(interp, "E", Some(0), |_, _| {
935 Ok(Value::Float(std::f64::consts::E))
936 });
937 define(interp, "TAU", Some(0), |_, _| {
938 Ok(Value::Float(std::f64::consts::TAU))
939 });
940 define(interp, "PHI", Some(0), |_, _| {
941 Ok(Value::Float(1.618033988749895))
942 }); define(interp, "gcd", Some(2), |_, args| {
946 match (&args[0], &args[1]) {
947 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(gcd(*a, *b))),
948 _ => Err(RuntimeError::new("gcd() requires integers")),
949 }
950 });
951
952 define(interp, "lcm", Some(2), |_, args| {
953 match (&args[0], &args[1]) {
954 (Value::Int(a), Value::Int(b)) => {
955 let g = gcd(*a, *b);
956 Ok(Value::Int((a * b).abs() / g))
957 }
958 _ => Err(RuntimeError::new("lcm() requires integers")),
959 }
960 });
961
962 define(interp, "factorial", Some(1), |_, args| match &args[0] {
964 Value::Int(n) if *n >= 0 => {
965 let mut result: i64 = 1;
966 for i in 2..=(*n as u64) {
967 result = result.saturating_mul(i as i64);
968 }
969 Ok(Value::Int(result))
970 }
971 Value::Int(_) => Err(RuntimeError::new(
972 "factorial() requires non-negative integer",
973 )),
974 _ => Err(RuntimeError::new("factorial() requires integer")),
975 });
976
977 define(interp, "is_nan", Some(1), |_, args| match &args[0] {
979 Value::Float(n) => Ok(Value::Bool(n.is_nan())),
980 Value::Int(_) => Ok(Value::Bool(false)),
981 _ => Err(RuntimeError::new("is_nan() requires number")),
982 });
983
984 define(interp, "is_infinite", Some(1), |_, args| match &args[0] {
985 Value::Float(n) => Ok(Value::Bool(n.is_infinite())),
986 Value::Int(_) => Ok(Value::Bool(false)),
987 Value::Infinity => Ok(Value::Bool(true)),
988 _ => Err(RuntimeError::new("is_infinite() requires number")),
989 });
990
991 define(interp, "is_finite", Some(1), |_, args| match &args[0] {
992 Value::Float(n) => Ok(Value::Bool(n.is_finite())),
993 Value::Int(_) => Ok(Value::Bool(true)),
994 Value::Infinity => Ok(Value::Bool(false)),
995 _ => Err(RuntimeError::new("is_finite() requires number")),
996 });
997
998 define(interp, "is_even", Some(1), |_, args| match &args[0] {
999 Value::Int(n) => Ok(Value::Bool(n % 2 == 0)),
1000 _ => Err(RuntimeError::new("is_even() requires integer")),
1001 });
1002
1003 define(interp, "is_odd", Some(1), |_, args| match &args[0] {
1004 Value::Int(n) => Ok(Value::Bool(n % 2 != 0)),
1005 _ => Err(RuntimeError::new("is_odd() requires integer")),
1006 });
1007
1008 define(interp, "is_prime", Some(1), |_, args| match &args[0] {
1009 Value::Int(n) => Ok(Value::Bool(is_prime(*n))),
1010 _ => Err(RuntimeError::new("is_prime() requires integer")),
1011 });
1012}
1013
1014fn gcd(mut a: i64, mut b: i64) -> i64 {
1015 a = a.abs();
1016 b = b.abs();
1017 while b != 0 {
1018 let t = b;
1019 b = a % b;
1020 a = t;
1021 }
1022 a
1023}
1024
1025fn is_prime(n: i64) -> bool {
1026 if n < 2 {
1027 return false;
1028 }
1029 if n == 2 {
1030 return true;
1031 }
1032 if n % 2 == 0 {
1033 return false;
1034 }
1035 let sqrt = (n as f64).sqrt() as i64;
1036 for i in (3..=sqrt).step_by(2) {
1037 if n % i == 0 {
1038 return false;
1039 }
1040 }
1041 true
1042}
1043
1044fn register_collections(interp: &mut Interpreter) {
1049 define(interp, "len", Some(1), |_, args| match &args[0] {
1051 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
1052 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
1053 Value::Tuple(t) => Ok(Value::Int(t.len() as i64)),
1054 Value::Map(m) => Ok(Value::Int(m.borrow().len() as i64)),
1055 Value::Set(s) => Ok(Value::Int(s.borrow().len() as i64)),
1056 _ => Err(RuntimeError::new(
1057 "len() requires array, string, tuple, map, or set",
1058 )),
1059 });
1060
1061 define(interp, "is_empty", Some(1), |_, args| match &args[0] {
1062 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
1063 Value::String(s) => Ok(Value::Bool(s.is_empty())),
1064 Value::Tuple(t) => Ok(Value::Bool(t.is_empty())),
1065 Value::Map(m) => Ok(Value::Bool(m.borrow().is_empty())),
1066 Value::Set(s) => Ok(Value::Bool(s.borrow().is_empty())),
1067 _ => Err(RuntimeError::new("is_empty() requires collection")),
1068 });
1069
1070 define(interp, "push", Some(2), |_, args| match &args[0] {
1072 Value::Array(arr) => {
1073 arr.borrow_mut().push(args[1].clone());
1074 Ok(Value::Null)
1075 }
1076 _ => Err(RuntimeError::new("push() requires array")),
1077 });
1078
1079 define(interp, "pop", Some(1), |_, args| match &args[0] {
1080 Value::Array(arr) => arr
1081 .borrow_mut()
1082 .pop()
1083 .ok_or_else(|| RuntimeError::new("pop() on empty array")),
1084 _ => Err(RuntimeError::new("pop() requires array")),
1085 });
1086
1087 define(interp, "first", Some(1), |_, args| match &args[0] {
1088 Value::Array(arr) => arr
1089 .borrow()
1090 .first()
1091 .cloned()
1092 .ok_or_else(|| RuntimeError::new("first() on empty array")),
1093 Value::Tuple(t) => t
1094 .first()
1095 .cloned()
1096 .ok_or_else(|| RuntimeError::new("first() on empty tuple")),
1097 _ => Err(RuntimeError::new("first() requires array or tuple")),
1098 });
1099
1100 define(interp, "last", Some(1), |_, args| match &args[0] {
1101 Value::Array(arr) => arr
1102 .borrow()
1103 .last()
1104 .cloned()
1105 .ok_or_else(|| RuntimeError::new("last() on empty array")),
1106 Value::Tuple(t) => t
1107 .last()
1108 .cloned()
1109 .ok_or_else(|| RuntimeError::new("last() on empty tuple")),
1110 _ => Err(RuntimeError::new("last() requires array or tuple")),
1111 });
1112
1113 define(interp, "middle", Some(1), |_, args| match &args[0] {
1115 Value::Array(arr) => {
1116 let arr = arr.borrow();
1117 if arr.is_empty() {
1118 return Err(RuntimeError::new("middle() on empty array"));
1119 }
1120 let mid = arr.len() / 2;
1121 Ok(arr[mid].clone())
1122 }
1123 Value::Tuple(t) => {
1124 if t.is_empty() {
1125 return Err(RuntimeError::new("middle() on empty tuple"));
1126 }
1127 let mid = t.len() / 2;
1128 Ok(t[mid].clone())
1129 }
1130 _ => Err(RuntimeError::new("middle() requires array or tuple")),
1131 });
1132
1133 define(interp, "choice", Some(1), |_, args| {
1135 use std::time::{SystemTime, UNIX_EPOCH};
1136 match &args[0] {
1137 Value::Array(arr) => {
1138 let arr = arr.borrow();
1139 if arr.is_empty() {
1140 return Err(RuntimeError::new("choice() on empty array"));
1141 }
1142 let seed = SystemTime::now()
1143 .duration_since(UNIX_EPOCH)
1144 .unwrap_or(std::time::Duration::ZERO)
1145 .as_nanos() as u64;
1146 let idx = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize
1147 % arr.len();
1148 Ok(arr[idx].clone())
1149 }
1150 Value::Tuple(t) => {
1151 if t.is_empty() {
1152 return Err(RuntimeError::new("choice() on empty tuple"));
1153 }
1154 let seed = SystemTime::now()
1155 .duration_since(UNIX_EPOCH)
1156 .unwrap_or(std::time::Duration::ZERO)
1157 .as_nanos() as u64;
1158 let idx =
1159 ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % t.len();
1160 Ok(t[idx].clone())
1161 }
1162 _ => Err(RuntimeError::new("choice() requires array or tuple")),
1163 }
1164 });
1165
1166 define(interp, "nth", Some(2), |_, args| {
1168 let n = match &args[1] {
1169 Value::Int(i) => *i,
1170 _ => return Err(RuntimeError::new("nth() index must be integer")),
1171 };
1172 match &args[0] {
1173 Value::Array(arr) => {
1174 let arr = arr.borrow();
1175 if n < 0 || n as usize >= arr.len() {
1176 return Err(RuntimeError::new("nth() index out of bounds"));
1177 }
1178 Ok(arr[n as usize].clone())
1179 }
1180 Value::Tuple(t) => {
1181 if n < 0 || n as usize >= t.len() {
1182 return Err(RuntimeError::new("nth() index out of bounds"));
1183 }
1184 Ok(t[n as usize].clone())
1185 }
1186 _ => Err(RuntimeError::new("nth() requires array or tuple")),
1187 }
1188 });
1189
1190 define(interp, "next", Some(1), |_, args| match &args[0] {
1192 Value::Array(arr) => {
1193 let mut arr = arr.borrow_mut();
1194 if arr.is_empty() {
1195 return Err(RuntimeError::new("next() on empty array"));
1196 }
1197 Ok(arr.remove(0))
1198 }
1199 _ => Err(RuntimeError::new("next() requires array")),
1200 });
1201
1202 define(interp, "peek", Some(1), |_, args| match &args[0] {
1204 Value::Array(arr) => arr
1205 .borrow()
1206 .first()
1207 .cloned()
1208 .ok_or_else(|| RuntimeError::new("peek() on empty array")),
1209 _ => Err(RuntimeError::new("peek() requires array")),
1210 });
1211
1212 define(interp, "get", Some(2), |_, args| {
1213 let index = match &args[1] {
1214 Value::Int(i) => *i,
1215 _ => return Err(RuntimeError::new("get() index must be integer")),
1216 };
1217 match &args[0] {
1218 Value::Array(arr) => {
1219 let arr = arr.borrow();
1220 let idx = if index < 0 {
1221 arr.len() as i64 + index
1222 } else {
1223 index
1224 } as usize;
1225 arr.get(idx)
1226 .cloned()
1227 .ok_or_else(|| RuntimeError::new("index out of bounds"))
1228 }
1229 Value::Tuple(t) => {
1230 let idx = if index < 0 {
1231 t.len() as i64 + index
1232 } else {
1233 index
1234 } as usize;
1235 t.get(idx)
1236 .cloned()
1237 .ok_or_else(|| RuntimeError::new("index out of bounds"))
1238 }
1239 _ => Err(RuntimeError::new("get() requires array or tuple")),
1240 }
1241 });
1242
1243 define(interp, "set", Some(3), |_, args| {
1244 let index = match &args[1] {
1245 Value::Int(i) => *i as usize,
1246 _ => return Err(RuntimeError::new("set() index must be integer")),
1247 };
1248 match &args[0] {
1249 Value::Array(arr) => {
1250 let mut arr = arr.borrow_mut();
1251 if index >= arr.len() {
1252 return Err(RuntimeError::new("index out of bounds"));
1253 }
1254 arr[index] = args[2].clone();
1255 Ok(Value::Null)
1256 }
1257 _ => Err(RuntimeError::new("set() requires array")),
1258 }
1259 });
1260
1261 define(interp, "insert", Some(3), |_, args| {
1262 let index = match &args[1] {
1263 Value::Int(i) => *i as usize,
1264 _ => return Err(RuntimeError::new("insert() index must be integer")),
1265 };
1266 match &args[0] {
1267 Value::Array(arr) => {
1268 let mut arr = arr.borrow_mut();
1269 if index > arr.len() {
1270 return Err(RuntimeError::new("index out of bounds"));
1271 }
1272 arr.insert(index, args[2].clone());
1273 Ok(Value::Null)
1274 }
1275 _ => Err(RuntimeError::new("insert() requires array")),
1276 }
1277 });
1278
1279 define(interp, "remove", Some(2), |_, args| {
1280 let index = match &args[1] {
1281 Value::Int(i) => *i as usize,
1282 _ => return Err(RuntimeError::new("remove() index must be integer")),
1283 };
1284 match &args[0] {
1285 Value::Array(arr) => {
1286 let mut arr = arr.borrow_mut();
1287 if index >= arr.len() {
1288 return Err(RuntimeError::new("index out of bounds"));
1289 }
1290 Ok(arr.remove(index))
1291 }
1292 _ => Err(RuntimeError::new("remove() requires array")),
1293 }
1294 });
1295
1296 define(interp, "clear", Some(1), |_, args| match &args[0] {
1297 Value::Array(arr) => {
1298 arr.borrow_mut().clear();
1299 Ok(Value::Null)
1300 }
1301 _ => Err(RuntimeError::new("clear() requires array")),
1302 });
1303
1304 define(interp, "contains", Some(2), |_, args| match &args[0] {
1306 Value::Array(arr) => Ok(Value::Bool(
1307 arr.borrow().iter().any(|v| values_equal(v, &args[1])),
1308 )),
1309 Value::String(s) => match &args[1] {
1310 Value::String(sub) => Ok(Value::Bool(s.contains(sub.as_str()))),
1311 Value::Char(c) => Ok(Value::Bool(s.contains(*c))),
1312 _ => Err(RuntimeError::new(
1313 "string contains() requires string or char",
1314 )),
1315 },
1316 _ => Err(RuntimeError::new("contains() requires array or string")),
1317 });
1318
1319 define(interp, "index_of", Some(2), |_, args| match &args[0] {
1320 Value::Array(arr) => {
1321 let idx = arr.borrow().iter().position(|v| values_equal(v, &args[1]));
1322 match idx {
1323 Some(i) => Ok(Value::Int(i as i64)),
1324 None => Ok(Value::Int(-1)),
1325 }
1326 }
1327 Value::String(s) => match &args[1] {
1328 Value::String(sub) => match s.find(sub.as_str()) {
1329 Some(i) => Ok(Value::Int(i as i64)),
1330 None => Ok(Value::Int(-1)),
1331 },
1332 Value::Char(c) => match s.find(*c) {
1333 Some(i) => Ok(Value::Int(i as i64)),
1334 None => Ok(Value::Int(-1)),
1335 },
1336 _ => Err(RuntimeError::new(
1337 "string index_of() requires string or char",
1338 )),
1339 },
1340 _ => Err(RuntimeError::new("index_of() requires array or string")),
1341 });
1342
1343 define(interp, "reverse", Some(1), |_, args| match &args[0] {
1345 Value::Array(arr) => {
1346 let mut reversed: Vec<Value> = arr.borrow().clone();
1347 reversed.reverse();
1348 Ok(Value::Array(Rc::new(RefCell::new(reversed))))
1349 }
1350 Value::String(s) => {
1351 let reversed: String = s.chars().rev().collect();
1352 Ok(Value::String(Rc::new(reversed)))
1353 }
1354 _ => Err(RuntimeError::new("reverse() requires array or string")),
1355 });
1356
1357 define(interp, "sort", Some(1), |_, args| match &args[0] {
1358 Value::Array(arr) => {
1359 let mut sorted: Vec<Value> = arr.borrow().clone();
1360 sorted.sort_by(compare_values);
1361 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1362 }
1363 _ => Err(RuntimeError::new("sort() requires array")),
1364 });
1365
1366 define(interp, "sort_desc", Some(1), |_, args| match &args[0] {
1367 Value::Array(arr) => {
1368 let mut sorted: Vec<Value> = arr.borrow().clone();
1369 sorted.sort_by(|a, b| compare_values(b, a));
1370 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1371 }
1372 _ => Err(RuntimeError::new("sort_desc() requires array")),
1373 });
1374
1375 define(interp, "unique", Some(1), |_, args| match &args[0] {
1376 Value::Array(arr) => {
1377 let arr = arr.borrow();
1378 let mut seen = Vec::new();
1379 let unique: Vec<Value> = arr
1380 .iter()
1381 .filter(|v| {
1382 if seen.iter().any(|s| values_equal(s, v)) {
1383 false
1384 } else {
1385 seen.push((*v).clone());
1386 true
1387 }
1388 })
1389 .cloned()
1390 .collect();
1391 Ok(Value::Array(Rc::new(RefCell::new(unique))))
1392 }
1393 _ => Err(RuntimeError::new("unique() requires array")),
1394 });
1395
1396 define(interp, "flatten", Some(1), |_, args| match &args[0] {
1397 Value::Array(arr) => {
1398 let mut flattened = Vec::new();
1399 for item in arr.borrow().iter() {
1400 match item {
1401 Value::Array(inner) => flattened.extend(inner.borrow().clone()),
1402 other => flattened.push(other.clone()),
1403 }
1404 }
1405 Ok(Value::Array(Rc::new(RefCell::new(flattened))))
1406 }
1407 _ => Err(RuntimeError::new("flatten() requires array")),
1408 });
1409
1410 define(interp, "concat", Some(2), |_, args| {
1412 match (&args[0], &args[1]) {
1413 (Value::Array(a), Value::Array(b)) => {
1414 let mut result = a.borrow().clone();
1415 result.extend(b.borrow().clone());
1416 Ok(Value::Array(Rc::new(RefCell::new(result))))
1417 }
1418 (Value::String(a), Value::String(b)) => {
1419 Ok(Value::String(Rc::new(format!("{}{}", a, b))))
1420 }
1421 _ => Err(RuntimeError::new(
1422 "concat() requires two arrays or two strings",
1423 )),
1424 }
1425 });
1426
1427 define(interp, "zip", Some(2), |_, args| {
1428 match (&args[0], &args[1]) {
1429 (Value::Array(a), Value::Array(b)) => {
1430 let a = a.borrow();
1431 let b = b.borrow();
1432 let zipped: Vec<Value> = a
1433 .iter()
1434 .zip(b.iter())
1435 .map(|(x, y)| Value::Tuple(Rc::new(vec![x.clone(), y.clone()])))
1436 .collect();
1437 Ok(Value::Array(Rc::new(RefCell::new(zipped))))
1438 }
1439 _ => Err(RuntimeError::new("zip() requires two arrays")),
1440 }
1441 });
1442
1443 define(interp, "enumerate", Some(1), |_, args| match &args[0] {
1444 Value::Array(arr) => {
1445 let enumerated: Vec<Value> = arr
1446 .borrow()
1447 .iter()
1448 .enumerate()
1449 .map(|(i, v)| Value::Tuple(Rc::new(vec![Value::Int(i as i64), v.clone()])))
1450 .collect();
1451 Ok(Value::Array(Rc::new(RefCell::new(enumerated))))
1452 }
1453 _ => Err(RuntimeError::new("enumerate() requires array")),
1454 });
1455
1456 define(interp, "zip_with", Some(3), |_, args| {
1459 let mode = match &args[2] {
1460 Value::String(s) => s.as_str(),
1461 _ => return Err(RuntimeError::new("zip_with() mode must be string")),
1462 };
1463 match (&args[0], &args[1]) {
1464 (Value::Array(a), Value::Array(b)) => {
1465 let a = a.borrow();
1466 let b = b.borrow();
1467 let result: Result<Vec<Value>, RuntimeError> = a
1468 .iter()
1469 .zip(b.iter())
1470 .map(|(x, y)| match (x, y, mode) {
1471 (Value::Int(a), Value::Int(b), "add") => Ok(Value::Int(a + b)),
1472 (Value::Int(a), Value::Int(b), "sub") => Ok(Value::Int(a - b)),
1473 (Value::Int(a), Value::Int(b), "mul") => Ok(Value::Int(a * b)),
1474 (Value::Float(a), Value::Float(b), "add") => Ok(Value::Float(a + b)),
1475 (Value::Float(a), Value::Float(b), "sub") => Ok(Value::Float(a - b)),
1476 (Value::Float(a), Value::Float(b), "mul") => Ok(Value::Float(a * b)),
1477 (_, _, "pair") => Ok(Value::Tuple(Rc::new(vec![x.clone(), y.clone()]))),
1478 _ => Err(RuntimeError::new("zip_with() incompatible types or mode")),
1479 })
1480 .collect();
1481 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1482 }
1483 _ => Err(RuntimeError::new("zip_with() requires two arrays")),
1484 }
1485 });
1486
1487 define(interp, "supremum", Some(2), |_, args| {
1489 match (&args[0], &args[1]) {
1490 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1491 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1492 (Value::Array(a), Value::Array(b)) => {
1493 let a = a.borrow();
1495 let b = b.borrow();
1496 let result: Result<Vec<Value>, RuntimeError> = a
1497 .iter()
1498 .zip(b.iter())
1499 .map(|(x, y)| match (x, y) {
1500 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1501 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1502 _ => Err(RuntimeError::new("supremum() requires numeric arrays")),
1503 })
1504 .collect();
1505 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1506 }
1507 _ => Err(RuntimeError::new(
1508 "supremum() requires numeric values or arrays",
1509 )),
1510 }
1511 });
1512
1513 define(interp, "infimum", Some(2), |_, args| {
1515 match (&args[0], &args[1]) {
1516 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1517 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1518 (Value::Array(a), Value::Array(b)) => {
1519 let a = a.borrow();
1521 let b = b.borrow();
1522 let result: Result<Vec<Value>, RuntimeError> = a
1523 .iter()
1524 .zip(b.iter())
1525 .map(|(x, y)| match (x, y) {
1526 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1527 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1528 _ => Err(RuntimeError::new("infimum() requires numeric arrays")),
1529 })
1530 .collect();
1531 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1532 }
1533 _ => Err(RuntimeError::new(
1534 "infimum() requires numeric values or arrays",
1535 )),
1536 }
1537 });
1538
1539 define(interp, "slice", Some(3), |_, args| {
1541 let start = match &args[1] {
1542 Value::Int(i) => *i as usize,
1543 _ => return Err(RuntimeError::new("slice() start must be integer")),
1544 };
1545 let end = match &args[2] {
1546 Value::Int(i) => *i as usize,
1547 _ => return Err(RuntimeError::new("slice() end must be integer")),
1548 };
1549 match &args[0] {
1550 Value::Array(arr) => {
1551 let arr = arr.borrow();
1552 let end = end.min(arr.len());
1553 let sliced: Vec<Value> = arr[start..end].to_vec();
1554 Ok(Value::Array(Rc::new(RefCell::new(sliced))))
1555 }
1556 Value::String(s) => {
1557 let chars: Vec<char> = s.chars().collect();
1558 let end = end.min(chars.len());
1559 let sliced: String = chars[start..end].iter().collect();
1560 Ok(Value::String(Rc::new(sliced)))
1561 }
1562 _ => Err(RuntimeError::new("slice() requires array or string")),
1563 }
1564 });
1565
1566 define(interp, "take", Some(2), |_, args| {
1567 let n = match &args[1] {
1568 Value::Int(i) => *i as usize,
1569 _ => return Err(RuntimeError::new("take() n must be integer")),
1570 };
1571 match &args[0] {
1572 Value::Array(arr) => {
1573 let taken: Vec<Value> = arr.borrow().iter().take(n).cloned().collect();
1574 Ok(Value::Array(Rc::new(RefCell::new(taken))))
1575 }
1576 _ => Err(RuntimeError::new("take() requires array")),
1577 }
1578 });
1579
1580 define(interp, "skip", Some(2), |_, args| {
1581 let n = match &args[1] {
1582 Value::Int(i) => *i as usize,
1583 _ => return Err(RuntimeError::new("skip() n must be integer")),
1584 };
1585 match &args[0] {
1586 Value::Array(arr) => {
1587 let skipped: Vec<Value> = arr.borrow().iter().skip(n).cloned().collect();
1588 Ok(Value::Array(Rc::new(RefCell::new(skipped))))
1589 }
1590 _ => Err(RuntimeError::new("skip() requires array")),
1591 }
1592 });
1593
1594 define(interp, "chunk", Some(2), |_, args| {
1595 let size = match &args[1] {
1596 Value::Int(i) if *i > 0 => *i as usize,
1597 _ => return Err(RuntimeError::new("chunk() size must be positive integer")),
1598 };
1599 match &args[0] {
1600 Value::Array(arr) => {
1601 let chunks: Vec<Value> = arr
1602 .borrow()
1603 .chunks(size)
1604 .map(|c| Value::Array(Rc::new(RefCell::new(c.to_vec()))))
1605 .collect();
1606 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
1607 }
1608 _ => Err(RuntimeError::new("chunk() requires array")),
1609 }
1610 });
1611
1612 define(interp, "range", Some(2), |_, args| {
1614 let start = match &args[0] {
1615 Value::Int(n) => *n,
1616 _ => return Err(RuntimeError::new("range() requires integers")),
1617 };
1618 let end = match &args[1] {
1619 Value::Int(n) => *n,
1620 _ => return Err(RuntimeError::new("range() requires integers")),
1621 };
1622 let values: Vec<Value> = (start..end).map(Value::Int).collect();
1623 Ok(Value::Array(Rc::new(RefCell::new(values))))
1624 });
1625
1626 define(interp, "range_inclusive", Some(2), |_, args| {
1627 let start = match &args[0] {
1628 Value::Int(n) => *n,
1629 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1630 };
1631 let end = match &args[1] {
1632 Value::Int(n) => *n,
1633 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1634 };
1635 let values: Vec<Value> = (start..=end).map(Value::Int).collect();
1636 Ok(Value::Array(Rc::new(RefCell::new(values))))
1637 });
1638
1639 define(interp, "repeat", Some(2), |_, args| {
1640 let n = match &args[1] {
1641 Value::Int(i) if *i >= 0 => *i as usize,
1642 _ => {
1643 return Err(RuntimeError::new(
1644 "repeat() count must be non-negative integer",
1645 ))
1646 }
1647 };
1648 let repeated: Vec<Value> = std::iter::repeat(args[0].clone()).take(n).collect();
1649 Ok(Value::Array(Rc::new(RefCell::new(repeated))))
1650 });
1651
1652 define(interp, "map_new", Some(0), |_, _| {
1658 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
1659 });
1660
1661 define(interp, "map_get", Some(2), |_, args| {
1663 let key = match &args[1] {
1664 Value::String(s) => s.to_string(),
1665 _ => return Err(RuntimeError::new("map_get() key must be string")),
1666 };
1667 match &args[0] {
1668 Value::Map(map) => Ok(map.borrow().get(&key).cloned().unwrap_or(Value::Null)),
1669 _ => Err(RuntimeError::new("map_get() requires map")),
1670 }
1671 });
1672
1673 define(interp, "map_set", Some(3), |_, args| {
1675 let key = match &args[1] {
1676 Value::String(s) => s.to_string(),
1677 _ => return Err(RuntimeError::new("map_set() key must be string")),
1678 };
1679 match &args[0] {
1680 Value::Map(map) => {
1681 map.borrow_mut().insert(key, args[2].clone());
1682 Ok(Value::Null)
1683 }
1684 _ => Err(RuntimeError::new("map_set() requires map")),
1685 }
1686 });
1687
1688 define(interp, "map_has", Some(2), |_, args| {
1690 let key = match &args[1] {
1691 Value::String(s) => s.to_string(),
1692 _ => return Err(RuntimeError::new("map_has() key must be string")),
1693 };
1694 match &args[0] {
1695 Value::Map(map) => Ok(Value::Bool(map.borrow().contains_key(&key))),
1696 _ => Err(RuntimeError::new("map_has() requires map")),
1697 }
1698 });
1699
1700 define(interp, "map_remove", Some(2), |_, args| {
1702 let key = match &args[1] {
1703 Value::String(s) => s.to_string(),
1704 _ => return Err(RuntimeError::new("map_remove() key must be string")),
1705 };
1706 match &args[0] {
1707 Value::Map(map) => Ok(map.borrow_mut().remove(&key).unwrap_or(Value::Null)),
1708 _ => Err(RuntimeError::new("map_remove() requires map")),
1709 }
1710 });
1711
1712 define(interp, "map_keys", Some(1), |_, args| match &args[0] {
1714 Value::Map(map) => {
1715 let keys: Vec<Value> = map
1716 .borrow()
1717 .keys()
1718 .map(|k| Value::String(Rc::new(k.clone())))
1719 .collect();
1720 Ok(Value::Array(Rc::new(RefCell::new(keys))))
1721 }
1722 _ => Err(RuntimeError::new("map_keys() requires map")),
1723 });
1724
1725 define(interp, "map_values", Some(1), |_, args| match &args[0] {
1727 Value::Map(map) => {
1728 let values: Vec<Value> = map.borrow().values().cloned().collect();
1729 Ok(Value::Array(Rc::new(RefCell::new(values))))
1730 }
1731 _ => Err(RuntimeError::new("map_values() requires map")),
1732 });
1733
1734 define(interp, "map_len", Some(1), |_, args| match &args[0] {
1736 Value::Map(map) => Ok(Value::Int(map.borrow().len() as i64)),
1737 _ => Err(RuntimeError::new("map_len() requires map")),
1738 });
1739
1740 define(interp, "map_clear", Some(1), |_, args| match &args[0] {
1742 Value::Map(map) => {
1743 map.borrow_mut().clear();
1744 Ok(Value::Null)
1745 }
1746 _ => Err(RuntimeError::new("map_clear() requires map")),
1747 });
1748
1749 define(interp, "set_new", Some(0), |_, _| {
1755 Ok(Value::Set(Rc::new(RefCell::new(
1756 std::collections::HashSet::new(),
1757 ))))
1758 });
1759
1760 define(interp, "set_add", Some(2), |_, args| {
1762 let item = match &args[1] {
1763 Value::String(s) => s.to_string(),
1764 _ => return Err(RuntimeError::new("set_add() item must be string")),
1765 };
1766 match &args[0] {
1767 Value::Set(set) => {
1768 set.borrow_mut().insert(item);
1769 Ok(Value::Null)
1770 }
1771 _ => Err(RuntimeError::new("set_add() requires set")),
1772 }
1773 });
1774
1775 define(interp, "set_has", Some(2), |_, args| {
1777 let item = match &args[1] {
1778 Value::String(s) => s.to_string(),
1779 _ => return Err(RuntimeError::new("set_has() item must be string")),
1780 };
1781 match &args[0] {
1782 Value::Set(set) => Ok(Value::Bool(set.borrow().contains(&item))),
1783 _ => Err(RuntimeError::new("set_has() requires set")),
1784 }
1785 });
1786
1787 define(interp, "set_remove", Some(2), |_, args| {
1789 let item = match &args[1] {
1790 Value::String(s) => s.to_string(),
1791 _ => return Err(RuntimeError::new("set_remove() item must be string")),
1792 };
1793 match &args[0] {
1794 Value::Set(set) => Ok(Value::Bool(set.borrow_mut().remove(&item))),
1795 _ => Err(RuntimeError::new("set_remove() requires set")),
1796 }
1797 });
1798
1799 define(interp, "set_to_array", Some(1), |_, args| match &args[0] {
1801 Value::Set(set) => {
1802 let items: Vec<Value> = set
1803 .borrow()
1804 .iter()
1805 .map(|s| Value::String(Rc::new(s.clone())))
1806 .collect();
1807 Ok(Value::Array(Rc::new(RefCell::new(items))))
1808 }
1809 _ => Err(RuntimeError::new("set_to_array() requires set")),
1810 });
1811
1812 define(interp, "set_len", Some(1), |_, args| match &args[0] {
1814 Value::Set(set) => Ok(Value::Int(set.borrow().len() as i64)),
1815 _ => Err(RuntimeError::new("set_len() requires set")),
1816 });
1817
1818 define(interp, "set_clear", Some(1), |_, args| match &args[0] {
1820 Value::Set(set) => {
1821 set.borrow_mut().clear();
1822 Ok(Value::Null)
1823 }
1824 _ => Err(RuntimeError::new("set_clear() requires set")),
1825 });
1826}
1827
1828fn values_equal(a: &Value, b: &Value) -> bool {
1829 match (a, b) {
1830 (Value::Null, Value::Null) => true,
1831 (Value::Bool(a), Value::Bool(b)) => a == b,
1832 (Value::Int(a), Value::Int(b)) => a == b,
1833 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
1834 (Value::String(a), Value::String(b)) => a == b,
1835 (Value::Char(a), Value::Char(b)) => a == b,
1836 (Value::Array(a), Value::Array(b)) => {
1837 let a = a.borrow();
1838 let b = b.borrow();
1839 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1840 }
1841 (Value::Tuple(a), Value::Tuple(b)) => {
1842 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1843 }
1844 _ => false,
1845 }
1846}
1847
1848fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
1849 match (a, b) {
1850 (Value::Int(a), Value::Int(b)) => a.cmp(b),
1851 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal),
1852 (Value::String(a), Value::String(b)) => a.cmp(b),
1853 (Value::Char(a), Value::Char(b)) => a.cmp(b),
1854 _ => std::cmp::Ordering::Equal,
1855 }
1856}
1857
1858fn register_string(interp: &mut Interpreter) {
1863 define(interp, "chars", Some(1), |_, args| match &args[0] {
1864 Value::String(s) => {
1865 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
1866 Ok(Value::Array(Rc::new(RefCell::new(chars))))
1867 }
1868 _ => Err(RuntimeError::new("chars() requires string")),
1869 });
1870
1871 define(interp, "bytes", Some(1), |_, args| match &args[0] {
1872 Value::String(s) => {
1873 let bytes: Vec<Value> = s.bytes().map(|b| Value::Int(b as i64)).collect();
1874 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
1875 }
1876 _ => Err(RuntimeError::new("bytes() requires string")),
1877 });
1878
1879 define(interp, "split", Some(2), |_, args| {
1880 match (&args[0], &args[1]) {
1881 (Value::String(s), Value::String(sep)) => {
1882 let parts: Vec<Value> = s
1883 .split(sep.as_str())
1884 .map(|p| Value::String(Rc::new(p.to_string())))
1885 .collect();
1886 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1887 }
1888 (Value::String(s), Value::Char(sep)) => {
1889 let parts: Vec<Value> = s
1890 .split(*sep)
1891 .map(|p| Value::String(Rc::new(p.to_string())))
1892 .collect();
1893 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1894 }
1895 _ => Err(RuntimeError::new("split() requires string and separator")),
1896 }
1897 });
1898
1899 define(interp, "join", Some(2), |_, args| {
1900 match (&args[0], &args[1]) {
1901 (Value::Array(arr), Value::String(sep)) => {
1902 let parts: Vec<String> = arr.borrow().iter().map(|v| format!("{}", v)).collect();
1903 Ok(Value::String(Rc::new(parts.join(sep.as_str()))))
1904 }
1905 _ => Err(RuntimeError::new(
1906 "join() requires array and separator string",
1907 )),
1908 }
1909 });
1910
1911 define(interp, "trim", Some(1), |_, args| match &args[0] {
1912 Value::String(s) => Ok(Value::String(Rc::new(s.trim().to_string()))),
1913 _ => Err(RuntimeError::new("trim() requires string")),
1914 });
1915
1916 define(interp, "trim_start", Some(1), |_, args| match &args[0] {
1917 Value::String(s) => Ok(Value::String(Rc::new(s.trim_start().to_string()))),
1918 _ => Err(RuntimeError::new("trim_start() requires string")),
1919 });
1920
1921 define(interp, "trim_end", Some(1), |_, args| match &args[0] {
1922 Value::String(s) => Ok(Value::String(Rc::new(s.trim_end().to_string()))),
1923 _ => Err(RuntimeError::new("trim_end() requires string")),
1924 });
1925
1926 define(interp, "upper", Some(1), |_, args| match &args[0] {
1927 Value::String(s) => Ok(Value::String(Rc::new(s.to_uppercase()))),
1928 Value::Char(c) => Ok(Value::Char(c.to_uppercase().next().unwrap_or(*c))),
1929 _ => Err(RuntimeError::new("upper() requires string or char")),
1930 });
1931
1932 define(interp, "lower", Some(1), |_, args| match &args[0] {
1933 Value::String(s) => Ok(Value::String(Rc::new(s.to_lowercase()))),
1934 Value::Char(c) => Ok(Value::Char(c.to_lowercase().next().unwrap_or(*c))),
1935 _ => Err(RuntimeError::new("lower() requires string or char")),
1936 });
1937
1938 define(interp, "capitalize", Some(1), |_, args| match &args[0] {
1939 Value::String(s) => {
1940 let mut chars = s.chars();
1941 let capitalized = match chars.next() {
1942 None => String::new(),
1943 Some(c) => c.to_uppercase().chain(chars).collect(),
1944 };
1945 Ok(Value::String(Rc::new(capitalized)))
1946 }
1947 _ => Err(RuntimeError::new("capitalize() requires string")),
1948 });
1949
1950 define(interp, "replace", Some(3), |_, args| {
1951 match (&args[0], &args[1], &args[2]) {
1952 (Value::String(s), Value::String(from), Value::String(to)) => Ok(Value::String(
1953 Rc::new(s.replace(from.as_str(), to.as_str())),
1954 )),
1955 _ => Err(RuntimeError::new("replace() requires three strings")),
1956 }
1957 });
1958
1959 define(interp, "starts_with", Some(2), |_, args| {
1960 match (&args[0], &args[1]) {
1961 (Value::String(s), Value::String(prefix)) => {
1962 Ok(Value::Bool(s.starts_with(prefix.as_str())))
1963 }
1964 _ => Err(RuntimeError::new("starts_with() requires two strings")),
1965 }
1966 });
1967
1968 define(interp, "ends_with", Some(2), |_, args| {
1969 match (&args[0], &args[1]) {
1970 (Value::String(s), Value::String(suffix)) => {
1971 Ok(Value::Bool(s.ends_with(suffix.as_str())))
1972 }
1973 _ => Err(RuntimeError::new("ends_with() requires two strings")),
1974 }
1975 });
1976
1977 define(interp, "repeat_str", Some(2), |_, args| {
1978 match (&args[0], &args[1]) {
1979 (Value::String(s), Value::Int(n)) if *n >= 0 => {
1980 Ok(Value::String(Rc::new(s.repeat(*n as usize))))
1981 }
1982 _ => Err(RuntimeError::new(
1983 "repeat_str() requires string and non-negative integer",
1984 )),
1985 }
1986 });
1987
1988 define(interp, "pad_left", Some(3), |_, args| {
1989 match (&args[0], &args[1], &args[2]) {
1990 (Value::String(s), Value::Int(width), Value::Char(c)) => {
1991 let width = *width as usize;
1992 if s.len() >= width {
1993 Ok(Value::String(s.clone()))
1994 } else {
1995 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
1996 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
1997 }
1998 }
1999 _ => Err(RuntimeError::new(
2000 "pad_left() requires string, width, and char",
2001 )),
2002 }
2003 });
2004
2005 define(interp, "pad_right", Some(3), |_, args| {
2006 match (&args[0], &args[1], &args[2]) {
2007 (Value::String(s), Value::Int(width), Value::Char(c)) => {
2008 let width = *width as usize;
2009 if s.len() >= width {
2010 Ok(Value::String(s.clone()))
2011 } else {
2012 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
2013 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
2014 }
2015 }
2016 _ => Err(RuntimeError::new(
2017 "pad_right() requires string, width, and char",
2018 )),
2019 }
2020 });
2021
2022 define(interp, "lines", Some(1), |_, args| match &args[0] {
2023 Value::String(s) => {
2024 let lines: Vec<Value> = s
2025 .lines()
2026 .map(|l| Value::String(Rc::new(l.to_string())))
2027 .collect();
2028 Ok(Value::Array(Rc::new(RefCell::new(lines))))
2029 }
2030 _ => Err(RuntimeError::new("lines() requires string")),
2031 });
2032
2033 define(interp, "words", Some(1), |_, args| match &args[0] {
2034 Value::String(s) => {
2035 let words: Vec<Value> = s
2036 .split_whitespace()
2037 .map(|w| Value::String(Rc::new(w.to_string())))
2038 .collect();
2039 Ok(Value::Array(Rc::new(RefCell::new(words))))
2040 }
2041 _ => Err(RuntimeError::new("words() requires string")),
2042 });
2043
2044 define(interp, "is_alpha", Some(1), |_, args| match &args[0] {
2045 Value::String(s) => Ok(Value::Bool(
2046 !s.is_empty() && s.chars().all(|c| c.is_alphabetic()),
2047 )),
2048 Value::Char(c) => Ok(Value::Bool(c.is_alphabetic())),
2049 _ => Err(RuntimeError::new("is_alpha() requires string or char")),
2050 });
2051
2052 define(interp, "is_digit", Some(1), |_, args| match &args[0] {
2053 Value::String(s) => Ok(Value::Bool(
2054 !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()),
2055 )),
2056 Value::Char(c) => Ok(Value::Bool(c.is_ascii_digit())),
2057 _ => Err(RuntimeError::new("is_digit() requires string or char")),
2058 });
2059
2060 define(interp, "is_alnum", Some(1), |_, args| match &args[0] {
2061 Value::String(s) => Ok(Value::Bool(
2062 !s.is_empty() && s.chars().all(|c| c.is_alphanumeric()),
2063 )),
2064 Value::Char(c) => Ok(Value::Bool(c.is_alphanumeric())),
2065 _ => Err(RuntimeError::new("is_alnum() requires string or char")),
2066 });
2067
2068 define(interp, "is_space", Some(1), |_, args| match &args[0] {
2069 Value::String(s) => Ok(Value::Bool(
2070 !s.is_empty() && s.chars().all(|c| c.is_whitespace()),
2071 )),
2072 Value::Char(c) => Ok(Value::Bool(c.is_whitespace())),
2073 _ => Err(RuntimeError::new("is_space() requires string or char")),
2074 });
2075
2076 define(interp, "find", Some(2), |_, args| {
2082 match (&args[0], &args[1]) {
2083 (Value::String(s), Value::String(sub)) => {
2084 match s.find(sub.as_str()) {
2085 Some(byte_idx) => {
2086 let char_idx = s[..byte_idx].chars().count() as i64;
2088 Ok(Value::Int(char_idx))
2089 }
2090 None => Ok(Value::Int(-1)),
2091 }
2092 }
2093 (Value::String(s), Value::Char(c)) => match s.find(*c) {
2094 Some(byte_idx) => {
2095 let char_idx = s[..byte_idx].chars().count() as i64;
2096 Ok(Value::Int(char_idx))
2097 }
2098 None => Ok(Value::Int(-1)),
2099 },
2100 _ => Err(RuntimeError::new(
2101 "find() requires string and substring/char",
2102 )),
2103 }
2104 });
2105
2106 define(interp, "index_of", Some(2), |_, args| {
2108 match (&args[0], &args[1]) {
2109 (Value::String(s), Value::String(sub)) => match s.find(sub.as_str()) {
2110 Some(byte_idx) => {
2111 let char_idx = s[..byte_idx].chars().count() as i64;
2112 Ok(Value::Int(char_idx))
2113 }
2114 None => Ok(Value::Int(-1)),
2115 },
2116 (Value::String(s), Value::Char(c)) => match s.find(*c) {
2117 Some(byte_idx) => {
2118 let char_idx = s[..byte_idx].chars().count() as i64;
2119 Ok(Value::Int(char_idx))
2120 }
2121 None => Ok(Value::Int(-1)),
2122 },
2123 (Value::Array(arr), search) => {
2124 for (i, v) in arr.borrow().iter().enumerate() {
2126 if values_equal_simple(v, search) {
2127 return Ok(Value::Int(i as i64));
2128 }
2129 }
2130 Ok(Value::Int(-1))
2131 }
2132 _ => Err(RuntimeError::new(
2133 "index_of() requires array/string and element/substring",
2134 )),
2135 }
2136 });
2137
2138 define(interp, "last_index_of", Some(2), |_, args| {
2140 match (&args[0], &args[1]) {
2141 (Value::String(s), Value::String(sub)) => match s.rfind(sub.as_str()) {
2142 Some(byte_idx) => {
2143 let char_idx = s[..byte_idx].chars().count() as i64;
2144 Ok(Value::Int(char_idx))
2145 }
2146 None => Ok(Value::Int(-1)),
2147 },
2148 (Value::String(s), Value::Char(c)) => match s.rfind(*c) {
2149 Some(byte_idx) => {
2150 let char_idx = s[..byte_idx].chars().count() as i64;
2151 Ok(Value::Int(char_idx))
2152 }
2153 None => Ok(Value::Int(-1)),
2154 },
2155 _ => Err(RuntimeError::new(
2156 "last_index_of() requires string and substring/char",
2157 )),
2158 }
2159 });
2160
2161 define(interp, "substring", Some(3), |_, args| {
2163 let s = match &args[0] {
2164 Value::String(s) => (**s).clone(),
2165 _ => {
2166 return Err(RuntimeError::new(
2167 "substring: first argument must be a string",
2168 ))
2169 }
2170 };
2171 let start = match &args[1] {
2172 Value::Int(n) if *n >= 0 => *n as usize,
2173 _ => {
2174 return Err(RuntimeError::new(
2175 "substring: start must be a non-negative integer",
2176 ))
2177 }
2178 };
2179 let end = match &args[2] {
2180 Value::Int(n) if *n >= 0 => *n as usize,
2181 _ => {
2182 return Err(RuntimeError::new(
2183 "substring: end must be a non-negative integer",
2184 ))
2185 }
2186 };
2187 let chars: Vec<char> = s.chars().collect();
2188 let len = chars.len();
2189 let actual_start = start.min(len);
2190 let actual_end = end.min(len);
2191 if actual_start >= actual_end {
2192 return Ok(Value::String(Rc::new(String::new())));
2193 }
2194 let result: String = chars[actual_start..actual_end].iter().collect();
2195 Ok(Value::String(Rc::new(result)))
2196 });
2197
2198 define(interp, "count", Some(2), |_, args| {
2200 match (&args[0], &args[1]) {
2201 (Value::String(s), Value::String(sub)) => {
2202 if sub.is_empty() {
2203 return Err(RuntimeError::new("count: cannot count empty string"));
2204 }
2205 let count = s.matches(sub.as_str()).count() as i64;
2206 Ok(Value::Int(count))
2207 }
2208 (Value::String(s), Value::Char(c)) => {
2209 let count = s.chars().filter(|&ch| ch == *c).count() as i64;
2210 Ok(Value::Int(count))
2211 }
2212 _ => Err(RuntimeError::new(
2213 "count() requires string and substring/char",
2214 )),
2215 }
2216 });
2217
2218 define(interp, "char_at", Some(2), |_, args| {
2221 let s = match &args[0] {
2222 Value::String(s) => (**s).clone(),
2223 _ => {
2224 return Err(RuntimeError::new(
2225 "char_at: first argument must be a string",
2226 ))
2227 }
2228 };
2229 let idx = match &args[1] {
2230 Value::Int(n) => *n,
2231 _ => {
2232 return Err(RuntimeError::new(
2233 "char_at: second argument must be an integer",
2234 ))
2235 }
2236 };
2237 let actual_idx = if idx < 0 {
2239 (s.len() as i64 + idx) as usize
2241 } else {
2242 idx as usize
2243 };
2244 if actual_idx < s.len() {
2245 let remaining = &s[actual_idx..];
2246 match remaining.chars().next() {
2247 Some(c) => Ok(Value::Char(c)),
2248 None => Ok(Value::Null),
2249 }
2250 } else {
2251 Ok(Value::Null)
2252 }
2253 });
2254
2255 define(interp, "char_code_at", Some(2), |_, args| {
2257 let s = match &args[0] {
2258 Value::String(s) => (**s).clone(),
2259 _ => {
2260 return Err(RuntimeError::new(
2261 "char_code_at: first argument must be a string",
2262 ))
2263 }
2264 };
2265 let idx = match &args[1] {
2266 Value::Int(n) => *n,
2267 _ => {
2268 return Err(RuntimeError::new(
2269 "char_code_at: second argument must be an integer",
2270 ))
2271 }
2272 };
2273 let chars: Vec<char> = s.chars().collect();
2274 let actual_idx = if idx < 0 {
2275 (chars.len() as i64 + idx) as usize
2276 } else {
2277 idx as usize
2278 };
2279 match chars.get(actual_idx) {
2280 Some(c) => Ok(Value::Int(*c as i64)),
2281 None => Ok(Value::Null),
2282 }
2283 });
2284
2285 define(interp, "from_char_code", Some(1), |_, args| {
2287 let code = match &args[0] {
2288 Value::Int(n) => *n as u32,
2289 _ => {
2290 return Err(RuntimeError::new(
2291 "from_char_code: argument must be an integer",
2292 ))
2293 }
2294 };
2295 match char::from_u32(code) {
2296 Some(c) => Ok(Value::String(Rc::new(c.to_string()))),
2297 None => Err(RuntimeError::new(
2298 "from_char_code: invalid Unicode code point",
2299 )),
2300 }
2301 });
2302
2303 define(interp, "insert", Some(3), |_, args| {
2305 let s = match &args[0] {
2306 Value::String(s) => (**s).clone(),
2307 _ => return Err(RuntimeError::new("insert: first argument must be a string")),
2308 };
2309 let idx = match &args[1] {
2310 Value::Int(n) if *n >= 0 => *n as usize,
2311 _ => {
2312 return Err(RuntimeError::new(
2313 "insert: index must be a non-negative integer",
2314 ))
2315 }
2316 };
2317 let insertion = match &args[2] {
2318 Value::String(s) => (**s).clone(),
2319 _ => return Err(RuntimeError::new("insert: third argument must be a string")),
2320 };
2321 let chars: Vec<char> = s.chars().collect();
2322 let actual_idx = idx.min(chars.len());
2323 let mut result: String = chars[..actual_idx].iter().collect();
2324 result.push_str(&insertion);
2325 result.extend(chars[actual_idx..].iter());
2326 Ok(Value::String(Rc::new(result)))
2327 });
2328
2329 define(interp, "remove", Some(3), |_, args| {
2331 let s = match &args[0] {
2332 Value::String(s) => (**s).clone(),
2333 _ => return Err(RuntimeError::new("remove: first argument must be a string")),
2334 };
2335 let start = match &args[1] {
2336 Value::Int(n) if *n >= 0 => *n as usize,
2337 _ => {
2338 return Err(RuntimeError::new(
2339 "remove: start must be a non-negative integer",
2340 ))
2341 }
2342 };
2343 let len = match &args[2] {
2344 Value::Int(n) if *n >= 0 => *n as usize,
2345 _ => {
2346 return Err(RuntimeError::new(
2347 "remove: length must be a non-negative integer",
2348 ))
2349 }
2350 };
2351 let chars: Vec<char> = s.chars().collect();
2352 let str_len = chars.len();
2353 let actual_start = start.min(str_len);
2354 let actual_end = (start + len).min(str_len);
2355 let mut result: String = chars[..actual_start].iter().collect();
2356 result.extend(chars[actual_end..].iter());
2357 Ok(Value::String(Rc::new(result)))
2358 });
2359
2360 define(interp, "compare", Some(2), |_, args| {
2362 match (&args[0], &args[1]) {
2363 (Value::String(a), Value::String(b)) => {
2364 let result = match a.cmp(b) {
2365 std::cmp::Ordering::Less => -1,
2366 std::cmp::Ordering::Equal => 0,
2367 std::cmp::Ordering::Greater => 1,
2368 };
2369 Ok(Value::Int(result))
2370 }
2371 _ => Err(RuntimeError::new("compare() requires two strings")),
2372 }
2373 });
2374
2375 define(interp, "compare_ignore_case", Some(2), |_, args| {
2377 match (&args[0], &args[1]) {
2378 (Value::String(a), Value::String(b)) => {
2379 let result = match a.to_lowercase().cmp(&b.to_lowercase()) {
2380 std::cmp::Ordering::Less => -1,
2381 std::cmp::Ordering::Equal => 0,
2382 std::cmp::Ordering::Greater => 1,
2383 };
2384 Ok(Value::Int(result))
2385 }
2386 _ => Err(RuntimeError::new(
2387 "compare_ignore_case() requires two strings",
2388 )),
2389 }
2390 });
2391
2392 define(interp, "char_count", Some(1), |_, args| match &args[0] {
2394 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
2395 _ => Err(RuntimeError::new("char_count() requires string")),
2396 });
2397
2398 define(interp, "byte_count", Some(1), |_, args| match &args[0] {
2400 Value::String(s) => Ok(Value::Int(s.len() as i64)),
2401 _ => Err(RuntimeError::new("byte_count() requires string")),
2402 });
2403
2404 define(interp, "is_empty", Some(1), |_, args| match &args[0] {
2406 Value::String(s) => Ok(Value::Bool(s.is_empty())),
2407 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
2408 _ => Err(RuntimeError::new("is_empty() requires string or array")),
2409 });
2410
2411 define(interp, "is_blank", Some(1), |_, args| match &args[0] {
2413 Value::String(s) => Ok(Value::Bool(s.trim().is_empty())),
2414 _ => Err(RuntimeError::new("is_blank() requires string")),
2415 });
2416
2417 define(interp, "nfc", Some(1), |_, args| match &args[0] {
2423 Value::String(s) => Ok(Value::String(Rc::new(s.nfc().collect()))),
2424 _ => Err(RuntimeError::new("nfc() requires string")),
2425 });
2426
2427 define(interp, "nfd", Some(1), |_, args| match &args[0] {
2429 Value::String(s) => Ok(Value::String(Rc::new(s.nfd().collect()))),
2430 _ => Err(RuntimeError::new("nfd() requires string")),
2431 });
2432
2433 define(interp, "nfkc", Some(1), |_, args| match &args[0] {
2435 Value::String(s) => Ok(Value::String(Rc::new(s.nfkc().collect()))),
2436 _ => Err(RuntimeError::new("nfkc() requires string")),
2437 });
2438
2439 define(interp, "nfkd", Some(1), |_, args| match &args[0] {
2441 Value::String(s) => Ok(Value::String(Rc::new(s.nfkd().collect()))),
2442 _ => Err(RuntimeError::new("nfkd() requires string")),
2443 });
2444
2445 define(interp, "is_nfc", Some(1), |_, args| match &args[0] {
2447 Value::String(s) => {
2448 let normalized: String = s.nfc().collect();
2449 Ok(Value::Bool(*s.as_ref() == normalized))
2450 }
2451 _ => Err(RuntimeError::new("is_nfc() requires string")),
2452 });
2453
2454 define(interp, "is_nfd", Some(1), |_, args| match &args[0] {
2456 Value::String(s) => {
2457 let normalized: String = s.nfd().collect();
2458 Ok(Value::Bool(*s.as_ref() == normalized))
2459 }
2460 _ => Err(RuntimeError::new("is_nfd() requires string")),
2461 });
2462
2463 define(interp, "graphemes", Some(1), |_, args| match &args[0] {
2469 Value::String(s) => {
2470 let graphemes: Vec<Value> = s
2471 .graphemes(true)
2472 .map(|g| Value::String(Rc::new(g.to_string())))
2473 .collect();
2474 Ok(Value::Array(Rc::new(RefCell::new(graphemes))))
2475 }
2476 _ => Err(RuntimeError::new("graphemes() requires string")),
2477 });
2478
2479 define(interp, "grapheme_count", Some(1), |_, args| {
2481 match &args[0] {
2482 Value::String(s) => Ok(Value::Int(s.graphemes(true).count() as i64)),
2483 _ => Err(RuntimeError::new("grapheme_count() requires string")),
2484 }
2485 });
2486
2487 define(interp, "grapheme_at", Some(2), |_, args| {
2489 let s = match &args[0] {
2490 Value::String(s) => (**s).clone(),
2491 _ => {
2492 return Err(RuntimeError::new(
2493 "grapheme_at: first argument must be a string",
2494 ))
2495 }
2496 };
2497 let idx = match &args[1] {
2498 Value::Int(n) => *n,
2499 _ => {
2500 return Err(RuntimeError::new(
2501 "grapheme_at: second argument must be an integer",
2502 ))
2503 }
2504 };
2505 let graphemes: Vec<&str> = s.graphemes(true).collect();
2506 let actual_idx = if idx < 0 {
2507 (graphemes.len() as i64 + idx) as usize
2508 } else {
2509 idx as usize
2510 };
2511 match graphemes.get(actual_idx) {
2512 Some(g) => Ok(Value::String(Rc::new(g.to_string()))),
2513 None => Ok(Value::Null),
2514 }
2515 });
2516
2517 define(interp, "grapheme_slice", Some(3), |_, args| {
2519 let s = match &args[0] {
2520 Value::String(s) => (**s).clone(),
2521 _ => {
2522 return Err(RuntimeError::new(
2523 "grapheme_slice: first argument must be a string",
2524 ))
2525 }
2526 };
2527 let start = match &args[1] {
2528 Value::Int(n) if *n >= 0 => *n as usize,
2529 _ => {
2530 return Err(RuntimeError::new(
2531 "grapheme_slice: start must be a non-negative integer",
2532 ))
2533 }
2534 };
2535 let end = match &args[2] {
2536 Value::Int(n) if *n >= 0 => *n as usize,
2537 _ => {
2538 return Err(RuntimeError::new(
2539 "grapheme_slice: end must be a non-negative integer",
2540 ))
2541 }
2542 };
2543 let graphemes: Vec<&str> = s.graphemes(true).collect();
2544 let len = graphemes.len();
2545 let actual_start = start.min(len);
2546 let actual_end = end.min(len);
2547 if actual_start >= actual_end {
2548 return Ok(Value::String(Rc::new(String::new())));
2549 }
2550 let result: String = graphemes[actual_start..actual_end].join("");
2551 Ok(Value::String(Rc::new(result)))
2552 });
2553
2554 define(interp, "grapheme_reverse", Some(1), |_, args| {
2556 match &args[0] {
2557 Value::String(s) => {
2558 let reversed: String = s.graphemes(true).rev().collect();
2559 Ok(Value::String(Rc::new(reversed)))
2560 }
2561 _ => Err(RuntimeError::new("grapheme_reverse() requires string")),
2562 }
2563 });
2564
2565 define(interp, "word_boundaries", Some(1), |_, args| {
2567 match &args[0] {
2568 Value::String(s) => {
2569 let words: Vec<Value> = s
2570 .unicode_words()
2571 .map(|w| Value::String(Rc::new(w.to_string())))
2572 .collect();
2573 Ok(Value::Array(Rc::new(RefCell::new(words))))
2574 }
2575 _ => Err(RuntimeError::new("word_boundaries() requires string")),
2576 }
2577 });
2578
2579 define(interp, "string_builder", Some(0), |_, _| {
2586 Ok(Value::String(Rc::new(String::new())))
2587 });
2588
2589 define(interp, "concat_all", Some(1), |_, args| match &args[0] {
2591 Value::Array(arr) => {
2592 let parts: Vec<String> = arr
2593 .borrow()
2594 .iter()
2595 .map(|v| match v {
2596 Value::String(s) => (**s).clone(),
2597 other => format!("{}", other),
2598 })
2599 .collect();
2600 Ok(Value::String(Rc::new(parts.join(""))))
2601 }
2602 _ => Err(RuntimeError::new("concat_all() requires array")),
2603 });
2604
2605 define(interp, "repeat_join", Some(3), |_, args| {
2607 let s = match &args[0] {
2608 Value::String(s) => (**s).clone(),
2609 _ => {
2610 return Err(RuntimeError::new(
2611 "repeat_join: first argument must be a string",
2612 ))
2613 }
2614 };
2615 let n = match &args[1] {
2616 Value::Int(n) if *n >= 0 => *n as usize,
2617 _ => {
2618 return Err(RuntimeError::new(
2619 "repeat_join: count must be a non-negative integer",
2620 ))
2621 }
2622 };
2623 let sep = match &args[2] {
2624 Value::String(s) => (**s).clone(),
2625 _ => return Err(RuntimeError::new("repeat_join: separator must be a string")),
2626 };
2627 if n == 0 {
2628 return Ok(Value::String(Rc::new(String::new())));
2629 }
2630 let parts: Vec<&str> = std::iter::repeat(s.as_str()).take(n).collect();
2631 Ok(Value::String(Rc::new(parts.join(&sep))))
2632 });
2633}
2634
2635fn register_evidence(interp: &mut Interpreter) {
2640 use crate::interpreter::RuntimeConfidence;
2641
2642 define(interp, "known", Some(1), |_, args| {
2644 Ok(Value::Evidential {
2645 value: Box::new(args[0].clone()),
2646 evidence: Evidence::Known,
2647 })
2648 });
2649
2650 define(interp, "uncertain", Some(1), |_, args| {
2651 Ok(Value::Evidential {
2652 value: Box::new(args[0].clone()),
2653 evidence: Evidence::Uncertain,
2654 })
2655 });
2656
2657 define(interp, "reported", Some(1), |_, args| {
2658 Ok(Value::Evidential {
2659 value: Box::new(args[0].clone()),
2660 evidence: Evidence::Reported,
2661 })
2662 });
2663
2664 define(interp, "paradox", Some(1), |_, args| {
2665 Ok(Value::Evidential {
2666 value: Box::new(args[0].clone()),
2667 evidence: Evidence::Paradox,
2668 })
2669 });
2670
2671 define(interp, "evidence_of", Some(1), |_, args| {
2673 match &args[0] {
2674 Value::Evidential { evidence, .. } => {
2675 let level = match evidence {
2676 Evidence::Known => "known",
2677 Evidence::Uncertain => "uncertain",
2678 Evidence::Reported => "reported",
2679 Evidence::Paradox => "paradox",
2680 };
2681 Ok(Value::String(Rc::new(level.to_string())))
2682 }
2683 _ => Ok(Value::String(Rc::new("known".to_string()))), }
2685 });
2686
2687 define(interp, "is_known", Some(1), |_, args| {
2688 match &args[0] {
2689 Value::Evidential {
2690 evidence: Evidence::Known,
2691 ..
2692 } => Ok(Value::Bool(true)),
2693 Value::Evidential { .. } => Ok(Value::Bool(false)),
2694 _ => Ok(Value::Bool(true)), }
2696 });
2697
2698 define(interp, "is_uncertain", Some(1), |_, args| match &args[0] {
2699 Value::Evidential {
2700 evidence: Evidence::Uncertain,
2701 ..
2702 } => Ok(Value::Bool(true)),
2703 _ => Ok(Value::Bool(false)),
2704 });
2705
2706 define(interp, "is_reported", Some(1), |_, args| match &args[0] {
2707 Value::Evidential {
2708 evidence: Evidence::Reported,
2709 ..
2710 } => Ok(Value::Bool(true)),
2711 _ => Ok(Value::Bool(false)),
2712 });
2713
2714 define(interp, "is_paradox", Some(1), |_, args| match &args[0] {
2715 Value::Evidential {
2716 evidence: Evidence::Paradox,
2717 ..
2718 } => Ok(Value::Bool(true)),
2719 _ => Ok(Value::Bool(false)),
2720 });
2721
2722 define(interp, "strip_evidence", Some(1), |_, args| {
2724 match &args[0] {
2725 Value::Evidential { value, .. } => Ok(*value.clone()),
2726 other => Ok(other.clone()),
2727 }
2728 });
2729
2730 define(interp, "trust", Some(1), |_, args| {
2732 match &args[0] {
2734 Value::Evidential { value, .. } => Ok(Value::Evidential {
2735 value: value.clone(),
2736 evidence: Evidence::Known,
2737 }),
2738 other => Ok(other.clone()),
2739 }
2740 });
2741
2742 define(interp, "verify", Some(2), |_, args| {
2743 let pred_result = match &args[1] {
2745 Value::Bool(b) => *b,
2746 _ => return Err(RuntimeError::new("verify() predicate must be bool")),
2747 };
2748
2749 if pred_result {
2750 match &args[0] {
2751 Value::Evidential { value, .. } => Ok(Value::Evidential {
2752 value: value.clone(),
2753 evidence: Evidence::Known,
2754 }),
2755 other => Ok(other.clone()),
2756 }
2757 } else {
2758 Ok(args[0].clone()) }
2760 });
2761
2762 define(interp, "combine_evidence", Some(2), |_, args| {
2764 let ev1 = match &args[0] {
2765 Value::Evidential { evidence, .. } => *evidence,
2766 _ => Evidence::Known,
2767 };
2768 let ev2 = match &args[1] {
2769 Value::Evidential { evidence, .. } => *evidence,
2770 _ => Evidence::Known,
2771 };
2772
2773 let combined = match (ev1, ev2) {
2775 (Evidence::Paradox, _) | (_, Evidence::Paradox) => Evidence::Paradox,
2776 (Evidence::Reported, _) | (_, Evidence::Reported) => Evidence::Reported,
2777 (Evidence::Uncertain, _) | (_, Evidence::Uncertain) => Evidence::Uncertain,
2778 _ => Evidence::Known,
2779 };
2780
2781 Ok(Value::String(Rc::new(
2782 match combined {
2783 Evidence::Known => "known",
2784 Evidence::Uncertain => "uncertain",
2785 Evidence::Reported => "reported",
2786 Evidence::Paradox => "paradox",
2787 }
2788 .to_string(),
2789 )))
2790 });
2791
2792 define(interp, "affect_to_evidence", Some(1), |_, args| {
2796 match &args[0] {
2797 Value::Affective { affect, .. } => {
2798 if affect.sarcasm {
2800 return Ok(Value::String(Rc::new("uncertain".to_string())));
2801 }
2802 match affect.confidence {
2804 Some(RuntimeConfidence::High) => {
2805 Ok(Value::String(Rc::new("known".to_string())))
2806 }
2807 Some(RuntimeConfidence::Low) => {
2808 Ok(Value::String(Rc::new("uncertain".to_string())))
2809 }
2810 _ => Ok(Value::String(Rc::new("known".to_string()))),
2811 }
2812 }
2813 _ => Ok(Value::String(Rc::new("known".to_string()))),
2814 }
2815 });
2816
2817 define(
2819 interp,
2820 "affect_as_evidence",
2821 Some(1),
2822 |_, args| match &args[0] {
2823 Value::Affective { value, affect } => {
2824 let evidence = if affect.sarcasm {
2825 Evidence::Uncertain
2826 } else {
2827 match affect.confidence {
2828 Some(RuntimeConfidence::High) => Evidence::Known,
2829 Some(RuntimeConfidence::Low) => Evidence::Uncertain,
2830 _ => Evidence::Known,
2831 }
2832 };
2833 Ok(Value::Evidential {
2834 value: value.clone(),
2835 evidence,
2836 })
2837 }
2838 other => Ok(other.clone()),
2839 },
2840 );
2841
2842 define(
2844 interp,
2845 "is_affect_uncertain",
2846 Some(1),
2847 |_, args| match &args[0] {
2848 Value::Affective { affect, .. } => {
2849 let uncertain =
2850 affect.sarcasm || matches!(affect.confidence, Some(RuntimeConfidence::Low));
2851 Ok(Value::Bool(uncertain))
2852 }
2853 _ => Ok(Value::Bool(false)),
2854 },
2855 );
2856
2857 define(interp, "with_affect_evidence", Some(2), |_, args| {
2859 match &args[0] {
2861 Value::Affective { affect, .. } => {
2862 let evidence = if affect.sarcasm {
2863 Evidence::Uncertain
2864 } else {
2865 match affect.confidence {
2866 Some(RuntimeConfidence::High) => Evidence::Known,
2867 Some(RuntimeConfidence::Low) => Evidence::Uncertain,
2868 _ => Evidence::Known,
2869 }
2870 };
2871 Ok(Value::Evidential {
2872 value: Box::new(args[1].clone()),
2873 evidence,
2874 })
2875 }
2876 Value::Evidential { evidence, .. } => {
2877 Ok(Value::Evidential {
2879 value: Box::new(args[1].clone()),
2880 evidence: *evidence,
2881 })
2882 }
2883 _ => Ok(args[1].clone()),
2884 }
2885 });
2886}
2887
2888fn register_affect(interp: &mut Interpreter) {
2893 use crate::interpreter::{
2894 RuntimeAffect, RuntimeConfidence, RuntimeEmotion, RuntimeFormality, RuntimeIntensity,
2895 RuntimeSentiment,
2896 };
2897
2898 define(interp, "positive", Some(1), |_, args| {
2902 Ok(Value::Affective {
2903 value: Box::new(args[0].clone()),
2904 affect: RuntimeAffect {
2905 sentiment: Some(RuntimeSentiment::Positive),
2906 sarcasm: false,
2907 intensity: None,
2908 formality: None,
2909 emotion: None,
2910 confidence: None,
2911 },
2912 })
2913 });
2914
2915 define(interp, "negative", Some(1), |_, args| {
2916 Ok(Value::Affective {
2917 value: Box::new(args[0].clone()),
2918 affect: RuntimeAffect {
2919 sentiment: Some(RuntimeSentiment::Negative),
2920 sarcasm: false,
2921 intensity: None,
2922 formality: None,
2923 emotion: None,
2924 confidence: None,
2925 },
2926 })
2927 });
2928
2929 define(interp, "neutral", Some(1), |_, args| {
2930 Ok(Value::Affective {
2931 value: Box::new(args[0].clone()),
2932 affect: RuntimeAffect {
2933 sentiment: Some(RuntimeSentiment::Neutral),
2934 sarcasm: false,
2935 intensity: None,
2936 formality: None,
2937 emotion: None,
2938 confidence: None,
2939 },
2940 })
2941 });
2942
2943 define(interp, "sarcastic", Some(1), |_, args| {
2945 Ok(Value::Affective {
2946 value: Box::new(args[0].clone()),
2947 affect: RuntimeAffect {
2948 sentiment: None,
2949 sarcasm: true,
2950 intensity: None,
2951 formality: None,
2952 emotion: None,
2953 confidence: None,
2954 },
2955 })
2956 });
2957
2958 define(interp, "intensify", Some(1), |_, args| {
2960 Ok(Value::Affective {
2961 value: Box::new(args[0].clone()),
2962 affect: RuntimeAffect {
2963 sentiment: None,
2964 sarcasm: false,
2965 intensity: Some(RuntimeIntensity::Up),
2966 formality: None,
2967 emotion: None,
2968 confidence: None,
2969 },
2970 })
2971 });
2972
2973 define(interp, "dampen", Some(1), |_, args| {
2974 Ok(Value::Affective {
2975 value: Box::new(args[0].clone()),
2976 affect: RuntimeAffect {
2977 sentiment: None,
2978 sarcasm: false,
2979 intensity: Some(RuntimeIntensity::Down),
2980 formality: None,
2981 emotion: None,
2982 confidence: None,
2983 },
2984 })
2985 });
2986
2987 define(interp, "maximize", Some(1), |_, args| {
2988 Ok(Value::Affective {
2989 value: Box::new(args[0].clone()),
2990 affect: RuntimeAffect {
2991 sentiment: None,
2992 sarcasm: false,
2993 intensity: Some(RuntimeIntensity::Max),
2994 formality: None,
2995 emotion: None,
2996 confidence: None,
2997 },
2998 })
2999 });
3000
3001 define(interp, "formal", Some(1), |_, args| {
3003 Ok(Value::Affective {
3004 value: Box::new(args[0].clone()),
3005 affect: RuntimeAffect {
3006 sentiment: None,
3007 sarcasm: false,
3008 intensity: None,
3009 formality: Some(RuntimeFormality::Formal),
3010 emotion: None,
3011 confidence: None,
3012 },
3013 })
3014 });
3015
3016 define(interp, "informal", Some(1), |_, args| {
3017 Ok(Value::Affective {
3018 value: Box::new(args[0].clone()),
3019 affect: RuntimeAffect {
3020 sentiment: None,
3021 sarcasm: false,
3022 intensity: None,
3023 formality: Some(RuntimeFormality::Informal),
3024 emotion: None,
3025 confidence: None,
3026 },
3027 })
3028 });
3029
3030 define(interp, "joyful", Some(1), |_, args| {
3032 Ok(Value::Affective {
3033 value: Box::new(args[0].clone()),
3034 affect: RuntimeAffect {
3035 sentiment: None,
3036 sarcasm: false,
3037 intensity: None,
3038 formality: None,
3039 emotion: Some(RuntimeEmotion::Joy),
3040 confidence: None,
3041 },
3042 })
3043 });
3044
3045 define(interp, "sad", Some(1), |_, args| {
3046 Ok(Value::Affective {
3047 value: Box::new(args[0].clone()),
3048 affect: RuntimeAffect {
3049 sentiment: None,
3050 sarcasm: false,
3051 intensity: None,
3052 formality: None,
3053 emotion: Some(RuntimeEmotion::Sadness),
3054 confidence: None,
3055 },
3056 })
3057 });
3058
3059 define(interp, "angry", Some(1), |_, args| {
3060 Ok(Value::Affective {
3061 value: Box::new(args[0].clone()),
3062 affect: RuntimeAffect {
3063 sentiment: None,
3064 sarcasm: false,
3065 intensity: None,
3066 formality: None,
3067 emotion: Some(RuntimeEmotion::Anger),
3068 confidence: None,
3069 },
3070 })
3071 });
3072
3073 define(interp, "fearful", Some(1), |_, args| {
3074 Ok(Value::Affective {
3075 value: Box::new(args[0].clone()),
3076 affect: RuntimeAffect {
3077 sentiment: None,
3078 sarcasm: false,
3079 intensity: None,
3080 formality: None,
3081 emotion: Some(RuntimeEmotion::Fear),
3082 confidence: None,
3083 },
3084 })
3085 });
3086
3087 define(interp, "surprised", Some(1), |_, args| {
3088 Ok(Value::Affective {
3089 value: Box::new(args[0].clone()),
3090 affect: RuntimeAffect {
3091 sentiment: None,
3092 sarcasm: false,
3093 intensity: None,
3094 formality: None,
3095 emotion: Some(RuntimeEmotion::Surprise),
3096 confidence: None,
3097 },
3098 })
3099 });
3100
3101 define(interp, "loving", Some(1), |_, args| {
3102 Ok(Value::Affective {
3103 value: Box::new(args[0].clone()),
3104 affect: RuntimeAffect {
3105 sentiment: None,
3106 sarcasm: false,
3107 intensity: None,
3108 formality: None,
3109 emotion: Some(RuntimeEmotion::Love),
3110 confidence: None,
3111 },
3112 })
3113 });
3114
3115 define(interp, "high_confidence", Some(1), |_, args| {
3117 Ok(Value::Affective {
3118 value: Box::new(args[0].clone()),
3119 affect: RuntimeAffect {
3120 sentiment: None,
3121 sarcasm: false,
3122 intensity: None,
3123 formality: None,
3124 emotion: None,
3125 confidence: Some(RuntimeConfidence::High),
3126 },
3127 })
3128 });
3129
3130 define(interp, "medium_confidence", Some(1), |_, args| {
3131 Ok(Value::Affective {
3132 value: Box::new(args[0].clone()),
3133 affect: RuntimeAffect {
3134 sentiment: None,
3135 sarcasm: false,
3136 intensity: None,
3137 formality: None,
3138 emotion: None,
3139 confidence: Some(RuntimeConfidence::Medium),
3140 },
3141 })
3142 });
3143
3144 define(interp, "low_confidence", Some(1), |_, args| {
3145 Ok(Value::Affective {
3146 value: Box::new(args[0].clone()),
3147 affect: RuntimeAffect {
3148 sentiment: None,
3149 sarcasm: false,
3150 intensity: None,
3151 formality: None,
3152 emotion: None,
3153 confidence: Some(RuntimeConfidence::Low),
3154 },
3155 })
3156 });
3157
3158 define(interp, "affect_of", Some(1), |_, args| match &args[0] {
3161 Value::Affective { affect, .. } => {
3162 let mut parts = Vec::new();
3163 if let Some(s) = &affect.sentiment {
3164 parts.push(match s {
3165 RuntimeSentiment::Positive => "positive",
3166 RuntimeSentiment::Negative => "negative",
3167 RuntimeSentiment::Neutral => "neutral",
3168 });
3169 }
3170 if affect.sarcasm {
3171 parts.push("sarcastic");
3172 }
3173 if let Some(i) = &affect.intensity {
3174 parts.push(match i {
3175 RuntimeIntensity::Up => "intensified",
3176 RuntimeIntensity::Down => "dampened",
3177 RuntimeIntensity::Max => "maximized",
3178 });
3179 }
3180 if let Some(f) = &affect.formality {
3181 parts.push(match f {
3182 RuntimeFormality::Formal => "formal",
3183 RuntimeFormality::Informal => "informal",
3184 });
3185 }
3186 if let Some(e) = &affect.emotion {
3187 parts.push(match e {
3188 RuntimeEmotion::Joy => "joyful",
3189 RuntimeEmotion::Sadness => "sad",
3190 RuntimeEmotion::Anger => "angry",
3191 RuntimeEmotion::Fear => "fearful",
3192 RuntimeEmotion::Surprise => "surprised",
3193 RuntimeEmotion::Love => "loving",
3194 });
3195 }
3196 if let Some(c) = &affect.confidence {
3197 parts.push(match c {
3198 RuntimeConfidence::High => "high_confidence",
3199 RuntimeConfidence::Medium => "medium_confidence",
3200 RuntimeConfidence::Low => "low_confidence",
3201 });
3202 }
3203 Ok(Value::String(Rc::new(parts.join(", "))))
3204 }
3205 _ => Ok(Value::String(Rc::new("none".to_string()))),
3206 });
3207
3208 define(interp, "is_sarcastic", Some(1), |_, args| match &args[0] {
3209 Value::Affective { affect, .. } => Ok(Value::Bool(affect.sarcasm)),
3210 _ => Ok(Value::Bool(false)),
3211 });
3212
3213 define(interp, "is_positive", Some(1), |_, args| match &args[0] {
3214 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3215 affect.sentiment,
3216 Some(RuntimeSentiment::Positive)
3217 ))),
3218 _ => Ok(Value::Bool(false)),
3219 });
3220
3221 define(interp, "is_negative", Some(1), |_, args| match &args[0] {
3222 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3223 affect.sentiment,
3224 Some(RuntimeSentiment::Negative)
3225 ))),
3226 _ => Ok(Value::Bool(false)),
3227 });
3228
3229 define(interp, "is_formal", Some(1), |_, args| match &args[0] {
3230 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3231 affect.formality,
3232 Some(RuntimeFormality::Formal)
3233 ))),
3234 _ => Ok(Value::Bool(false)),
3235 });
3236
3237 define(interp, "is_informal", Some(1), |_, args| match &args[0] {
3238 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3239 affect.formality,
3240 Some(RuntimeFormality::Informal)
3241 ))),
3242 _ => Ok(Value::Bool(false)),
3243 });
3244
3245 define(interp, "emotion_of", Some(1), |_, args| match &args[0] {
3246 Value::Affective { affect, .. } => {
3247 let emotion_str = match &affect.emotion {
3248 Some(RuntimeEmotion::Joy) => "joy",
3249 Some(RuntimeEmotion::Sadness) => "sadness",
3250 Some(RuntimeEmotion::Anger) => "anger",
3251 Some(RuntimeEmotion::Fear) => "fear",
3252 Some(RuntimeEmotion::Surprise) => "surprise",
3253 Some(RuntimeEmotion::Love) => "love",
3254 None => "none",
3255 };
3256 Ok(Value::String(Rc::new(emotion_str.to_string())))
3257 }
3258 _ => Ok(Value::String(Rc::new("none".to_string()))),
3259 });
3260
3261 define(interp, "confidence_of", Some(1), |_, args| match &args[0] {
3262 Value::Affective { affect, .. } => {
3263 let conf_str = match &affect.confidence {
3264 Some(RuntimeConfidence::High) => "high",
3265 Some(RuntimeConfidence::Medium) => "medium",
3266 Some(RuntimeConfidence::Low) => "low",
3267 None => "none",
3268 };
3269 Ok(Value::String(Rc::new(conf_str.to_string())))
3270 }
3271 _ => Ok(Value::String(Rc::new("none".to_string()))),
3272 });
3273
3274 define(interp, "strip_affect", Some(1), |_, args| match &args[0] {
3276 Value::Affective { value, .. } => Ok(*value.clone()),
3277 other => Ok(other.clone()),
3278 });
3279
3280 define(interp, "with_affect", None, |_, args| {
3282 if args.is_empty() {
3283 return Err(RuntimeError::new(
3284 "with_affect requires at least one argument",
3285 ));
3286 }
3287
3288 let base_value = args[0].clone();
3289 let mut affect = RuntimeAffect {
3290 sentiment: None,
3291 sarcasm: false,
3292 intensity: None,
3293 formality: None,
3294 emotion: None,
3295 confidence: None,
3296 };
3297
3298 for arg in args.iter().skip(1) {
3300 if let Value::String(s) = arg {
3301 match s.as_str() {
3302 "positive" | "⊕" => affect.sentiment = Some(RuntimeSentiment::Positive),
3303 "negative" | "⊖" => affect.sentiment = Some(RuntimeSentiment::Negative),
3304 "neutral" | "⊜" => affect.sentiment = Some(RuntimeSentiment::Neutral),
3305 "sarcastic" | "⸮" => affect.sarcasm = true,
3306 "intensify" | "↑" => affect.intensity = Some(RuntimeIntensity::Up),
3307 "dampen" | "↓" => affect.intensity = Some(RuntimeIntensity::Down),
3308 "maximize" | "⇈" => affect.intensity = Some(RuntimeIntensity::Max),
3309 "formal" | "♔" => affect.formality = Some(RuntimeFormality::Formal),
3310 "informal" | "♟" => affect.formality = Some(RuntimeFormality::Informal),
3311 "joy" | "☺" => affect.emotion = Some(RuntimeEmotion::Joy),
3312 "sadness" | "☹" => affect.emotion = Some(RuntimeEmotion::Sadness),
3313 "anger" | "⚡" => affect.emotion = Some(RuntimeEmotion::Anger),
3314 "fear" | "❄" => affect.emotion = Some(RuntimeEmotion::Fear),
3315 "surprise" | "✦" => affect.emotion = Some(RuntimeEmotion::Surprise),
3316 "love" | "♡" => affect.emotion = Some(RuntimeEmotion::Love),
3317 "high" | "◉" => affect.confidence = Some(RuntimeConfidence::High),
3318 "medium" | "◎" => affect.confidence = Some(RuntimeConfidence::Medium),
3319 "low" | "○" => affect.confidence = Some(RuntimeConfidence::Low),
3320 _ => {}
3321 }
3322 }
3323 }
3324
3325 Ok(Value::Affective {
3326 value: Box::new(base_value),
3327 affect,
3328 })
3329 });
3330}
3331
3332fn register_iter(interp: &mut Interpreter) {
3337 define(interp, "sum", Some(1), |_, args| match &args[0] {
3339 Value::Array(arr) => {
3340 let mut sum_int: i64 = 0;
3341 let mut sum_float: f64 = 0.0;
3342 let mut is_float = false;
3343
3344 for val in arr.borrow().iter() {
3345 match val {
3346 Value::Int(n) => {
3347 if is_float {
3348 sum_float += *n as f64;
3349 } else {
3350 sum_int += n;
3351 }
3352 }
3353 Value::Float(n) => {
3354 if !is_float {
3355 sum_float = sum_int as f64;
3356 is_float = true;
3357 }
3358 sum_float += n;
3359 }
3360 _ => return Err(RuntimeError::new("sum() requires array of numbers")),
3361 }
3362 }
3363
3364 if is_float {
3365 Ok(Value::Float(sum_float))
3366 } else {
3367 Ok(Value::Int(sum_int))
3368 }
3369 }
3370 _ => Err(RuntimeError::new("sum() requires array")),
3371 });
3372
3373 define(interp, "product", Some(1), |_, args| match &args[0] {
3375 Value::Array(arr) => {
3376 let mut prod_int: i64 = 1;
3377 let mut prod_float: f64 = 1.0;
3378 let mut is_float = false;
3379
3380 for val in arr.borrow().iter() {
3381 match val {
3382 Value::Int(n) => {
3383 if is_float {
3384 prod_float *= *n as f64;
3385 } else {
3386 prod_int *= n;
3387 }
3388 }
3389 Value::Float(n) => {
3390 if !is_float {
3391 prod_float = prod_int as f64;
3392 is_float = true;
3393 }
3394 prod_float *= n;
3395 }
3396 _ => return Err(RuntimeError::new("product() requires array of numbers")),
3397 }
3398 }
3399
3400 if is_float {
3401 Ok(Value::Float(prod_float))
3402 } else {
3403 Ok(Value::Int(prod_int))
3404 }
3405 }
3406 _ => Err(RuntimeError::new("product() requires array")),
3407 });
3408
3409 define(interp, "mean", Some(1), |_, args| match &args[0] {
3411 Value::Array(arr) => {
3412 let arr = arr.borrow();
3413 if arr.is_empty() {
3414 return Err(RuntimeError::new("mean() on empty array"));
3415 }
3416
3417 let mut sum: f64 = 0.0;
3418 for val in arr.iter() {
3419 match val {
3420 Value::Int(n) => sum += *n as f64,
3421 Value::Float(n) => sum += n,
3422 _ => return Err(RuntimeError::new("mean() requires array of numbers")),
3423 }
3424 }
3425
3426 Ok(Value::Float(sum / arr.len() as f64))
3427 }
3428 _ => Err(RuntimeError::new("mean() requires array")),
3429 });
3430
3431 define(interp, "median", Some(1), |_, args| match &args[0] {
3433 Value::Array(arr) => {
3434 let arr = arr.borrow();
3435 if arr.is_empty() {
3436 return Err(RuntimeError::new("median() on empty array"));
3437 }
3438
3439 let mut nums: Vec<f64> = Vec::new();
3440 for val in arr.iter() {
3441 match val {
3442 Value::Int(n) => nums.push(*n as f64),
3443 Value::Float(n) => nums.push(*n),
3444 _ => return Err(RuntimeError::new("median() requires array of numbers")),
3445 }
3446 }
3447
3448 nums.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
3449 let mid = nums.len() / 2;
3450
3451 if nums.len() % 2 == 0 {
3452 Ok(Value::Float((nums[mid - 1] + nums[mid]) / 2.0))
3453 } else {
3454 Ok(Value::Float(nums[mid]))
3455 }
3456 }
3457 _ => Err(RuntimeError::new("median() requires array")),
3458 });
3459
3460 define(interp, "min_of", Some(1), |_, args| match &args[0] {
3462 Value::Array(arr) => {
3463 let arr = arr.borrow();
3464 if arr.is_empty() {
3465 return Err(RuntimeError::new("min_of() on empty array"));
3466 }
3467
3468 let mut min = &arr[0];
3469 for val in arr.iter().skip(1) {
3470 if matches!(compare_values(val, min), std::cmp::Ordering::Less) {
3471 min = val;
3472 }
3473 }
3474 Ok(min.clone())
3475 }
3476 _ => Err(RuntimeError::new("min_of() requires array")),
3477 });
3478
3479 define(interp, "max_of", Some(1), |_, args| match &args[0] {
3481 Value::Array(arr) => {
3482 let arr = arr.borrow();
3483 if arr.is_empty() {
3484 return Err(RuntimeError::new("max_of() on empty array"));
3485 }
3486
3487 let mut max = &arr[0];
3488 for val in arr.iter().skip(1) {
3489 if matches!(compare_values(val, max), std::cmp::Ordering::Greater) {
3490 max = val;
3491 }
3492 }
3493 Ok(max.clone())
3494 }
3495 _ => Err(RuntimeError::new("max_of() requires array")),
3496 });
3497
3498 define(interp, "count", Some(1), |_, args| match &args[0] {
3500 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
3501 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
3502 _ => Err(RuntimeError::new("count() requires array or string")),
3503 });
3504
3505 define(interp, "any", Some(1), |_, args| match &args[0] {
3507 Value::Array(arr) => {
3508 for val in arr.borrow().iter() {
3509 if is_truthy(val) {
3510 return Ok(Value::Bool(true));
3511 }
3512 }
3513 Ok(Value::Bool(false))
3514 }
3515 _ => Err(RuntimeError::new("any() requires array")),
3516 });
3517
3518 define(interp, "all", Some(1), |_, args| match &args[0] {
3520 Value::Array(arr) => {
3521 for val in arr.borrow().iter() {
3522 if !is_truthy(val) {
3523 return Ok(Value::Bool(false));
3524 }
3525 }
3526 Ok(Value::Bool(true))
3527 }
3528 _ => Err(RuntimeError::new("all() requires array")),
3529 });
3530
3531 define(interp, "none", Some(1), |_, args| match &args[0] {
3533 Value::Array(arr) => {
3534 for val in arr.borrow().iter() {
3535 if is_truthy(val) {
3536 return Ok(Value::Bool(false));
3537 }
3538 }
3539 Ok(Value::Bool(true))
3540 }
3541 _ => Err(RuntimeError::new("none() requires array")),
3542 });
3543}
3544
3545fn is_truthy(val: &Value) -> bool {
3546 match val {
3547 Value::Null | Value::Empty => false,
3548 Value::Bool(b) => *b,
3549 Value::Int(n) => *n != 0,
3550 Value::Float(n) => *n != 0.0 && !n.is_nan(),
3551 Value::String(s) => !s.is_empty(),
3552 Value::Array(arr) => !arr.borrow().is_empty(),
3553 Value::Evidential { value, .. } => is_truthy(value),
3554 _ => true,
3555 }
3556}
3557
3558fn register_io(interp: &mut Interpreter) {
3563 define(interp, "read_file", Some(1), |_, args| {
3565 match &args[0] {
3566 Value::String(path) => {
3567 match std::fs::read_to_string(path.as_str()) {
3568 Ok(content) => Ok(Value::Evidential {
3569 value: Box::new(Value::String(Rc::new(content))),
3570 evidence: Evidence::Reported, }),
3572 Err(e) => Err(RuntimeError::new(format!("read_file failed: {}", e))),
3573 }
3574 }
3575 _ => Err(RuntimeError::new("read_file() requires path string")),
3576 }
3577 });
3578
3579 define(interp, "write_file", Some(2), |_, args| {
3581 match (&args[0], &args[1]) {
3582 (Value::String(path), Value::String(content)) => {
3583 match std::fs::write(path.as_str(), content.as_str()) {
3584 Ok(_) => Ok(Value::Bool(true)),
3585 Err(e) => Err(RuntimeError::new(format!("write_file failed: {}", e))),
3586 }
3587 }
3588 _ => Err(RuntimeError::new(
3589 "write_file() requires path and content strings",
3590 )),
3591 }
3592 });
3593
3594 define(interp, "append_file", Some(2), |_, args| {
3596 match (&args[0], &args[1]) {
3597 (Value::String(path), Value::String(content)) => {
3598 use std::fs::OpenOptions;
3599 let result = OpenOptions::new()
3600 .create(true)
3601 .append(true)
3602 .open(path.as_str())
3603 .and_then(|mut f| f.write_all(content.as_bytes()));
3604 match result {
3605 Ok(_) => Ok(Value::Bool(true)),
3606 Err(e) => Err(RuntimeError::new(format!("append_file failed: {}", e))),
3607 }
3608 }
3609 _ => Err(RuntimeError::new(
3610 "append_file() requires path and content strings",
3611 )),
3612 }
3613 });
3614
3615 define(interp, "file_exists", Some(1), |_, args| match &args[0] {
3617 Value::String(path) => Ok(Value::Bool(std::path::Path::new(path.as_str()).exists())),
3618 _ => Err(RuntimeError::new("file_exists() requires path string")),
3619 });
3620
3621 define(interp, "read_lines", Some(1), |_, args| match &args[0] {
3623 Value::String(path) => match std::fs::read_to_string(path.as_str()) {
3624 Ok(content) => {
3625 let lines: Vec<Value> = content
3626 .lines()
3627 .map(|l| Value::String(Rc::new(l.to_string())))
3628 .collect();
3629 Ok(Value::Evidential {
3630 value: Box::new(Value::Array(Rc::new(RefCell::new(lines)))),
3631 evidence: Evidence::Reported,
3632 })
3633 }
3634 Err(e) => Err(RuntimeError::new(format!("read_lines failed: {}", e))),
3635 },
3636 _ => Err(RuntimeError::new("read_lines() requires path string")),
3637 });
3638
3639 define(interp, "env", Some(1), |_, args| {
3641 match &args[0] {
3642 Value::String(name) => {
3643 match std::env::var(name.as_str()) {
3644 Ok(value) => Ok(Value::Evidential {
3645 value: Box::new(Value::String(Rc::new(value))),
3646 evidence: Evidence::Reported, }),
3648 Err(_) => Ok(Value::Null),
3649 }
3650 }
3651 _ => Err(RuntimeError::new("env() requires variable name string")),
3652 }
3653 });
3654
3655 define(interp, "env·var", Some(1), |_, args| {
3657 match &args[0] {
3658 Value::String(name) => {
3659 match std::env::var(name.as_str()) {
3660 Ok(value) => Ok(Value::Variant {
3661 enum_name: "Result".to_string(),
3662 variant_name: "Ok".to_string(),
3663 fields: Some(Rc::new(vec![Value::String(Rc::new(value))])),
3664 }),
3665 Err(_) => Ok(Value::Variant {
3666 enum_name: "Result".to_string(),
3667 variant_name: "Err".to_string(),
3668 fields: Some(Rc::new(vec![Value::String(Rc::new("environment variable not found".to_string()))])),
3669 }),
3670 }
3671 }
3672 _ => Err(RuntimeError::new("env::var() requires variable name string")),
3673 }
3674 });
3675
3676 define(interp, "env_or", Some(2), |_, args| {
3678 match (&args[0], &args[1]) {
3679 (Value::String(name), default) => match std::env::var(name.as_str()) {
3680 Ok(value) => Ok(Value::Evidential {
3681 value: Box::new(Value::String(Rc::new(value))),
3682 evidence: Evidence::Reported,
3683 }),
3684 Err(_) => Ok(default.clone()),
3685 },
3686 _ => Err(RuntimeError::new("env_or() requires variable name string")),
3687 }
3688 });
3689
3690 define(interp, "cwd", Some(0), |_, _| {
3692 match std::env::current_dir() {
3693 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
3694 Err(e) => Err(RuntimeError::new(format!("cwd() failed: {}", e))),
3695 }
3696 });
3697
3698 define(interp, "args", Some(0), |interp, _| {
3700 let args: Vec<Value> = if interp.program_args.as_ref().map(|v| v.is_empty()).unwrap_or(true) {
3701 std::env::args()
3703 .map(|a| Value::String(Rc::new(a)))
3704 .collect()
3705 } else {
3706 interp.program_args.as_ref().unwrap().iter()
3708 .map(|a| Value::String(Rc::new(a.clone())))
3709 .collect()
3710 };
3711 Ok(Value::Array(Rc::new(RefCell::new(args))))
3712 });
3713}
3714
3715fn register_time(interp: &mut Interpreter) {
3720 define(interp, "now", Some(0), |_, _| {
3722 let duration = SystemTime::now()
3723 .duration_since(UNIX_EPOCH)
3724 .unwrap_or(Duration::ZERO);
3725 Ok(Value::Int(duration.as_millis() as i64))
3726 });
3727
3728 define(interp, "now_secs", Some(0), |_, _| {
3730 let duration = SystemTime::now()
3731 .duration_since(UNIX_EPOCH)
3732 .unwrap_or(Duration::ZERO);
3733 Ok(Value::Int(duration.as_secs() as i64))
3734 });
3735
3736 define(interp, "now_micros", Some(0), |_, _| {
3738 let duration = SystemTime::now()
3739 .duration_since(UNIX_EPOCH)
3740 .unwrap_or(Duration::ZERO);
3741 Ok(Value::Int(duration.as_micros() as i64))
3742 });
3743
3744 define(interp, "sleep", Some(1), |_, args| match &args[0] {
3746 Value::Int(ms) if *ms >= 0 => {
3747 std::thread::sleep(Duration::from_millis(*ms as u64));
3748 Ok(Value::Null)
3749 }
3750 _ => Err(RuntimeError::new(
3751 "sleep() requires non-negative integer milliseconds",
3752 )),
3753 });
3754
3755 define(interp, "UNIX_EPOCH", Some(0), |_, _| {
3761 let mut fields = std::collections::HashMap::new();
3763 fields.insert("secs".to_string(), Value::Int(0));
3764 fields.insert("nanos".to_string(), Value::Int(0));
3765 Ok(Value::Struct {
3766 name: "SystemTime".to_string(),
3767 fields: Rc::new(RefCell::new(fields)),
3768 })
3769 });
3770
3771 define(interp, "std·time·UNIX_EPOCH", Some(0), |_, _| {
3773 let mut fields = std::collections::HashMap::new();
3774 fields.insert("secs".to_string(), Value::Int(0));
3775 fields.insert("nanos".to_string(), Value::Int(0));
3776 Ok(Value::Struct {
3777 name: "SystemTime".to_string(),
3778 fields: Rc::new(RefCell::new(fields)),
3779 })
3780 });
3781
3782 define(interp, "timer_start", Some(0), |_, _| {
3784 let now = Instant::now();
3785 Ok(Value::Int(now.elapsed().as_nanos() as i64)) });
3788}
3789
3790fn register_random(interp: &mut Interpreter) {
3795 define(interp, "random", Some(0), |_, _| {
3797 use std::time::SystemTime;
3799 let seed = SystemTime::now()
3800 .duration_since(UNIX_EPOCH)
3801 .unwrap_or(Duration::ZERO)
3802 .as_nanos() as u64;
3803 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as f64;
3804 Ok(Value::Float(rand / u32::MAX as f64))
3805 });
3806
3807 define(interp, "random_int", Some(2), |_, args| {
3809 match (&args[0], &args[1]) {
3810 (Value::Int(min), Value::Int(max)) if max > min => {
3811 use std::time::SystemTime;
3812 let seed = SystemTime::now()
3813 .duration_since(UNIX_EPOCH)
3814 .unwrap_or(Duration::ZERO)
3815 .as_nanos() as u64;
3816 let range = (max - min) as u64;
3817 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) % range;
3818 Ok(Value::Int(*min + rand as i64))
3819 }
3820 _ => Err(RuntimeError::new(
3821 "random_int() requires min < max integers",
3822 )),
3823 }
3824 });
3825
3826 define(interp, "shuffle", Some(1), |_, args| match &args[0] {
3828 Value::Array(arr) => {
3829 let mut arr = arr.borrow_mut();
3830 use std::time::SystemTime;
3831 let mut seed = SystemTime::now()
3832 .duration_since(UNIX_EPOCH)
3833 .unwrap_or(Duration::ZERO)
3834 .as_nanos() as u64;
3835
3836 for i in (1..arr.len()).rev() {
3837 seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
3838 let j = ((seed >> 16) as usize) % (i + 1);
3839 arr.swap(i, j);
3840 }
3841 Ok(Value::Null)
3842 }
3843 _ => Err(RuntimeError::new("shuffle() requires array")),
3844 });
3845
3846 define(interp, "sample", Some(1), |_, args| match &args[0] {
3848 Value::Array(arr) => {
3849 let arr = arr.borrow();
3850 if arr.is_empty() {
3851 return Err(RuntimeError::new("sample() on empty array"));
3852 }
3853
3854 use std::time::SystemTime;
3855 let seed = SystemTime::now()
3856 .duration_since(UNIX_EPOCH)
3857 .unwrap_or(Duration::ZERO)
3858 .as_nanos() as u64;
3859 let idx =
3860 ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % arr.len();
3861 Ok(arr[idx].clone())
3862 }
3863 _ => Err(RuntimeError::new("sample() requires array")),
3864 });
3865}
3866
3867fn register_convert(interp: &mut Interpreter) {
3872 define(interp, "to_string", Some(1), |_, args| {
3874 Ok(Value::String(Rc::new(format!("{}", args[0]))))
3875 });
3876
3877 define(interp, "to_int", Some(1), |_, args| match &args[0] {
3879 Value::Int(n) => Ok(Value::Int(*n)),
3880 Value::Float(n) => Ok(Value::Int(*n as i64)),
3881 Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
3882 Value::Char(c) => Ok(Value::Int(*c as i64)),
3883 Value::String(s) => s
3884 .parse::<i64>()
3885 .map(Value::Int)
3886 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as integer", s))),
3887 _ => Err(RuntimeError::new("to_int() cannot convert this type")),
3888 });
3889
3890 define(interp, "to_float", Some(1), |_, args| match &args[0] {
3892 Value::Int(n) => Ok(Value::Float(*n as f64)),
3893 Value::Float(n) => Ok(Value::Float(*n)),
3894 Value::Bool(b) => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
3895 Value::String(s) => s
3896 .parse::<f64>()
3897 .map(Value::Float)
3898 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as float", s))),
3899 _ => Err(RuntimeError::new("to_float() cannot convert this type")),
3900 });
3901
3902 define(interp, "to_bool", Some(1), |_, args| {
3904 Ok(Value::Bool(is_truthy(&args[0])))
3905 });
3906
3907 define(interp, "to_char", Some(1), |_, args| match &args[0] {
3909 Value::Char(c) => Ok(Value::Char(*c)),
3910 Value::Int(n) => char::from_u32(*n as u32)
3911 .map(Value::Char)
3912 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n))),
3913 Value::String(s) => s
3914 .chars()
3915 .next()
3916 .map(Value::Char)
3917 .ok_or_else(|| RuntimeError::new("to_char() on empty string")),
3918 _ => Err(RuntimeError::new("to_char() cannot convert this type")),
3919 });
3920
3921 define(interp, "to_array", Some(1), |_, args| match &args[0] {
3923 Value::Array(arr) => Ok(Value::Array(arr.clone())),
3924 Value::Tuple(t) => Ok(Value::Array(Rc::new(RefCell::new(t.as_ref().clone())))),
3925 Value::String(s) => {
3926 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
3927 Ok(Value::Array(Rc::new(RefCell::new(chars))))
3928 }
3929 _ => Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()])))),
3930 });
3931
3932 define(interp, "to_tuple", Some(1), |_, args| match &args[0] {
3934 Value::Tuple(t) => Ok(Value::Tuple(t.clone())),
3935 Value::Array(arr) => Ok(Value::Tuple(Rc::new(arr.borrow().clone()))),
3936 _ => Ok(Value::Tuple(Rc::new(vec![args[0].clone()]))),
3937 });
3938
3939 define(interp, "char_code", Some(1), |_, args| match &args[0] {
3941 Value::Char(c) => Ok(Value::Int(*c as i64)),
3942 _ => Err(RuntimeError::new("char_code() requires char")),
3943 });
3944
3945 define(interp, "from_char_code", Some(1), |_, args| {
3947 match &args[0] {
3948 Value::Int(n) => char::from_u32(*n as u32)
3949 .map(Value::Char)
3950 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n))),
3951 _ => Err(RuntimeError::new("from_char_code() requires integer")),
3952 }
3953 });
3954
3955 define(interp, "hex", Some(1), |_, args| match &args[0] {
3957 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:x}", n)))),
3958 _ => Err(RuntimeError::new("hex() requires integer")),
3959 });
3960
3961 define(interp, "oct", Some(1), |_, args| match &args[0] {
3963 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:o}", n)))),
3964 _ => Err(RuntimeError::new("oct() requires integer")),
3965 });
3966
3967 define(interp, "bin", Some(1), |_, args| match &args[0] {
3969 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:b}", n)))),
3970 _ => Err(RuntimeError::new("bin() requires integer")),
3971 });
3972
3973 define(interp, "parse_int", Some(2), |_, args| {
3975 match (&args[0], &args[1]) {
3976 (Value::String(s), Value::Int(base)) if *base >= 2 && *base <= 36 => {
3977 i64::from_str_radix(s.trim(), *base as u32)
3978 .map(Value::Int)
3979 .map_err(|_| {
3980 RuntimeError::new(format!("cannot parse '{}' as base-{} integer", s, base))
3981 })
3982 }
3983 _ => Err(RuntimeError::new(
3984 "parse_int() requires string and base 2-36",
3985 )),
3986 }
3987 });
3988}
3989
3990fn register_cycle(interp: &mut Interpreter) {
3996 define(interp, "cycle", Some(2), |_, args| {
3998 match (&args[0], &args[1]) {
3999 (Value::Int(value), Value::Int(modulus)) if *modulus > 0 => {
4000 let normalized = value.rem_euclid(*modulus);
4001 Ok(Value::Tuple(Rc::new(vec![
4002 Value::Int(normalized),
4003 Value::Int(*modulus),
4004 ])))
4005 }
4006 _ => Err(RuntimeError::new(
4007 "cycle() requires value and positive modulus",
4008 )),
4009 }
4010 });
4011
4012 define(interp, "mod_add", Some(3), |_, args| {
4014 match (&args[0], &args[1], &args[2]) {
4015 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4016 Ok(Value::Int((a + b).rem_euclid(*m)))
4017 }
4018 _ => Err(RuntimeError::new(
4019 "mod_add() requires two integers and positive modulus",
4020 )),
4021 }
4022 });
4023
4024 define(interp, "mod_sub", Some(3), |_, args| {
4026 match (&args[0], &args[1], &args[2]) {
4027 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4028 Ok(Value::Int((a - b).rem_euclid(*m)))
4029 }
4030 _ => Err(RuntimeError::new(
4031 "mod_sub() requires two integers and positive modulus",
4032 )),
4033 }
4034 });
4035
4036 define(interp, "mod_mul", Some(3), |_, args| {
4038 match (&args[0], &args[1], &args[2]) {
4039 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4040 Ok(Value::Int((a * b).rem_euclid(*m)))
4041 }
4042 _ => Err(RuntimeError::new(
4043 "mod_mul() requires two integers and positive modulus",
4044 )),
4045 }
4046 });
4047
4048 define(interp, "mod_pow", Some(3), |_, args| {
4050 match (&args[0], &args[1], &args[2]) {
4051 (Value::Int(base), Value::Int(exp), Value::Int(m)) if *m > 0 && *exp >= 0 => {
4052 Ok(Value::Int(mod_pow(*base, *exp as u64, *m)))
4053 }
4054 _ => Err(RuntimeError::new(
4055 "mod_pow() requires base, non-negative exp, and positive modulus",
4056 )),
4057 }
4058 });
4059
4060 define(interp, "mod_inv", Some(2), |_, args| {
4062 match (&args[0], &args[1]) {
4063 (Value::Int(a), Value::Int(m)) if *m > 0 => match mod_inverse(*a, *m) {
4064 Some(inv) => Ok(Value::Int(inv)),
4065 None => Err(RuntimeError::new(format!(
4066 "no modular inverse of {} mod {}",
4067 a, m
4068 ))),
4069 },
4070 _ => Err(RuntimeError::new(
4071 "mod_inv() requires integer and positive modulus",
4072 )),
4073 }
4074 });
4075
4076 define(interp, "octave", Some(1), |_, args| {
4079 match &args[0] {
4080 Value::Int(note) => Ok(Value::Int(note.rem_euclid(12))),
4081 Value::Float(freq) => {
4082 let semitones = 12.0 * (freq / 440.0).log2();
4084 Ok(Value::Float(semitones.rem_euclid(12.0)))
4085 }
4086 _ => Err(RuntimeError::new("octave() requires number")),
4087 }
4088 });
4089
4090 define(interp, "interval", Some(2), |_, args| {
4092 match (&args[0], &args[1]) {
4093 (Value::Int(a), Value::Int(b)) => Ok(Value::Int((b - a).rem_euclid(12))),
4094 _ => Err(RuntimeError::new("interval() requires two integers")),
4095 }
4096 });
4097
4098 define(interp, "cents", Some(1), |_, args| match &args[0] {
4100 Value::Int(semitones) => Ok(Value::Int(*semitones * 100)),
4101 Value::Float(semitones) => Ok(Value::Float(*semitones * 100.0)),
4102 _ => Err(RuntimeError::new("cents() requires number")),
4103 });
4104
4105 define(interp, "freq", Some(1), |_, args| match &args[0] {
4107 Value::Int(midi) => {
4108 let freq = 440.0 * 2.0_f64.powf((*midi as f64 - 69.0) / 12.0);
4109 Ok(Value::Float(freq))
4110 }
4111 _ => Err(RuntimeError::new("freq() requires integer MIDI note")),
4112 });
4113
4114 define(interp, "midi", Some(1), |_, args| match &args[0] {
4116 Value::Float(freq) if *freq > 0.0 => {
4117 let midi = 69.0 + 12.0 * (freq / 440.0).log2();
4118 Ok(Value::Int(midi.round() as i64))
4119 }
4120 Value::Int(freq) if *freq > 0 => {
4121 let midi = 69.0 + 12.0 * (*freq as f64 / 440.0).log2();
4122 Ok(Value::Int(midi.round() as i64))
4123 }
4124 _ => Err(RuntimeError::new("midi() requires positive frequency")),
4125 });
4126}
4127
4128fn mod_pow(mut base: i64, mut exp: u64, modulus: i64) -> i64 {
4130 if modulus == 1 {
4131 return 0;
4132 }
4133 let mut result: i64 = 1;
4134 base = base.rem_euclid(modulus);
4135 while exp > 0 {
4136 if exp % 2 == 1 {
4137 result = (result * base).rem_euclid(modulus);
4138 }
4139 exp /= 2;
4140 base = (base * base).rem_euclid(modulus);
4141 }
4142 result
4143}
4144
4145fn register_simd(interp: &mut Interpreter) {
4151 define(interp, "simd_new", Some(4), |_, args| {
4153 let values: Result<Vec<f64>, _> = args
4154 .iter()
4155 .map(|v| match v {
4156 Value::Float(f) => Ok(*f),
4157 Value::Int(i) => Ok(*i as f64),
4158 _ => Err(RuntimeError::new("simd_new() requires numbers")),
4159 })
4160 .collect();
4161 let values = values?;
4162 Ok(Value::Array(Rc::new(RefCell::new(vec![
4163 Value::Float(values[0]),
4164 Value::Float(values[1]),
4165 Value::Float(values[2]),
4166 Value::Float(values[3]),
4167 ]))))
4168 });
4169
4170 define(interp, "simd_splat", Some(1), |_, args| {
4172 let v = match &args[0] {
4173 Value::Float(f) => *f,
4174 Value::Int(i) => *i as f64,
4175 _ => return Err(RuntimeError::new("simd_splat() requires number")),
4176 };
4177 Ok(Value::Array(Rc::new(RefCell::new(vec![
4178 Value::Float(v),
4179 Value::Float(v),
4180 Value::Float(v),
4181 Value::Float(v),
4182 ]))))
4183 });
4184
4185 define(interp, "simd_add", Some(2), |_, args| {
4187 simd_binary_op(&args[0], &args[1], |a, b| a + b, "simd_add")
4188 });
4189
4190 define(interp, "simd_sub", Some(2), |_, args| {
4192 simd_binary_op(&args[0], &args[1], |a, b| a - b, "simd_sub")
4193 });
4194
4195 define(interp, "simd_mul", Some(2), |_, args| {
4197 simd_binary_op(&args[0], &args[1], |a, b| a * b, "simd_mul")
4198 });
4199
4200 define(interp, "simd_div", Some(2), |_, args| {
4202 simd_binary_op(&args[0], &args[1], |a, b| a / b, "simd_div")
4203 });
4204
4205 define(interp, "simd_dot", Some(2), |_, args| {
4207 let a = extract_simd(&args[0], "simd_dot")?;
4208 let b = extract_simd(&args[1], "simd_dot")?;
4209 let dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
4210 Ok(Value::Float(dot))
4211 });
4212
4213 define(interp, "simd_cross", Some(2), |_, args| {
4215 let a = extract_simd(&args[0], "simd_cross")?;
4216 let b = extract_simd(&args[1], "simd_cross")?;
4217 Ok(Value::Array(Rc::new(RefCell::new(vec![
4218 Value::Float(a[1] * b[2] - a[2] * b[1]),
4219 Value::Float(a[2] * b[0] - a[0] * b[2]),
4220 Value::Float(a[0] * b[1] - a[1] * b[0]),
4221 Value::Float(0.0),
4222 ]))))
4223 });
4224
4225 define(interp, "simd_length", Some(1), |_, args| {
4227 let v = extract_simd(&args[0], "simd_length")?;
4228 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
4229 Ok(Value::Float(len_sq.sqrt()))
4230 });
4231
4232 define(interp, "simd_normalize", Some(1), |_, args| {
4234 let v = extract_simd(&args[0], "simd_normalize")?;
4235 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
4236 let len = len_sq.sqrt();
4237 if len < 1e-10 {
4238 return Ok(Value::Array(Rc::new(RefCell::new(vec![
4239 Value::Float(0.0),
4240 Value::Float(0.0),
4241 Value::Float(0.0),
4242 Value::Float(0.0),
4243 ]))));
4244 }
4245 let inv_len = 1.0 / len;
4246 Ok(Value::Array(Rc::new(RefCell::new(vec![
4247 Value::Float(v[0] * inv_len),
4248 Value::Float(v[1] * inv_len),
4249 Value::Float(v[2] * inv_len),
4250 Value::Float(v[3] * inv_len),
4251 ]))))
4252 });
4253
4254 define(interp, "simd_min", Some(2), |_, args| {
4256 simd_binary_op(&args[0], &args[1], |a, b| a.min(b), "simd_min")
4257 });
4258
4259 define(interp, "simd_max", Some(2), |_, args| {
4261 simd_binary_op(&args[0], &args[1], |a, b| a.max(b), "simd_max")
4262 });
4263
4264 define(interp, "simd_hadd", Some(1), |_, args| {
4266 let v = extract_simd(&args[0], "simd_hadd")?;
4267 Ok(Value::Float(v[0] + v[1] + v[2] + v[3]))
4268 });
4269
4270 define(interp, "simd_extract", Some(2), |_, args| {
4272 let v = extract_simd(&args[0], "simd_extract")?;
4273 let idx = match &args[1] {
4274 Value::Int(i) => *i as usize,
4275 _ => return Err(RuntimeError::new("simd_extract() requires integer index")),
4276 };
4277 if idx > 3 {
4278 return Err(RuntimeError::new("simd_extract() index must be 0-3"));
4279 }
4280 Ok(Value::Float(v[idx]))
4281 });
4282
4283 define(interp, "simd_free", Some(1), |_, _| Ok(Value::Null));
4285
4286 define(interp, "simd_lerp", Some(3), |_, args| {
4288 let a = extract_simd(&args[0], "simd_lerp")?;
4289 let b = extract_simd(&args[1], "simd_lerp")?;
4290 let t = match &args[2] {
4291 Value::Float(f) => *f,
4292 Value::Int(i) => *i as f64,
4293 _ => return Err(RuntimeError::new("simd_lerp() requires float t")),
4294 };
4295 let one_t = 1.0 - t;
4296 Ok(Value::Array(Rc::new(RefCell::new(vec![
4297 Value::Float(a[0] * one_t + b[0] * t),
4298 Value::Float(a[1] * one_t + b[1] * t),
4299 Value::Float(a[2] * one_t + b[2] * t),
4300 Value::Float(a[3] * one_t + b[3] * t),
4301 ]))))
4302 });
4303}
4304
4305fn extract_simd(val: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
4307 match val {
4308 Value::Array(arr) => {
4309 let arr = arr.borrow();
4310 if arr.len() < 4 {
4311 return Err(RuntimeError::new(format!(
4312 "{}() requires 4-element array",
4313 fn_name
4314 )));
4315 }
4316 let mut result = [0.0; 4];
4317 for (i, v) in arr.iter().take(4).enumerate() {
4318 result[i] = match v {
4319 Value::Float(f) => *f,
4320 Value::Int(n) => *n as f64,
4321 _ => {
4322 return Err(RuntimeError::new(format!(
4323 "{}() requires numeric array",
4324 fn_name
4325 )))
4326 }
4327 };
4328 }
4329 Ok(result)
4330 }
4331 _ => Err(RuntimeError::new(format!(
4332 "{}() requires array argument",
4333 fn_name
4334 ))),
4335 }
4336}
4337
4338fn simd_binary_op<F>(a: &Value, b: &Value, op: F, fn_name: &str) -> Result<Value, RuntimeError>
4340where
4341 F: Fn(f64, f64) -> f64,
4342{
4343 let a = extract_simd(a, fn_name)?;
4344 let b = extract_simd(b, fn_name)?;
4345 Ok(Value::Array(Rc::new(RefCell::new(vec![
4346 Value::Float(op(a[0], b[0])),
4347 Value::Float(op(a[1], b[1])),
4348 Value::Float(op(a[2], b[2])),
4349 Value::Float(op(a[3], b[3])),
4350 ]))))
4351}
4352
4353fn register_graphics_math(interp: &mut Interpreter) {
4364 define(interp, "quat_new", Some(4), |_, args| {
4372 let w = extract_number(&args[0], "quat_new")?;
4373 let x = extract_number(&args[1], "quat_new")?;
4374 let y = extract_number(&args[2], "quat_new")?;
4375 let z = extract_number(&args[3], "quat_new")?;
4376 Ok(make_vec4(w, x, y, z))
4377 });
4378
4379 define(interp, "quat_identity", Some(0), |_, _| {
4381 Ok(make_vec4(1.0, 0.0, 0.0, 0.0))
4382 });
4383
4384 define(interp, "quat_from_axis_angle", Some(2), |_, args| {
4386 let axis = extract_vec3(&args[0], "quat_from_axis_angle")?;
4387 let angle = extract_number(&args[1], "quat_from_axis_angle")?;
4388
4389 let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
4391 if len < 1e-10 {
4392 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0)); }
4394 let ax = axis[0] / len;
4395 let ay = axis[1] / len;
4396 let az = axis[2] / len;
4397
4398 let half_angle = angle / 2.0;
4399 let s = half_angle.sin();
4400 let c = half_angle.cos();
4401
4402 Ok(make_vec4(c, ax * s, ay * s, az * s))
4403 });
4404
4405 define(interp, "quat_from_euler", Some(3), |_, args| {
4408 let pitch = extract_number(&args[0], "quat_from_euler")?; let yaw = extract_number(&args[1], "quat_from_euler")?; let roll = extract_number(&args[2], "quat_from_euler")?; let (sp, cp) = (pitch / 2.0).sin_cos();
4413 let (sy, cy) = (yaw / 2.0).sin_cos();
4414 let (sr, cr) = (roll / 2.0).sin_cos();
4415
4416 let w = cp * cy * cr + sp * sy * sr;
4418 let x = sp * cy * cr - cp * sy * sr;
4419 let y = cp * sy * cr + sp * cy * sr;
4420 let z = cp * cy * sr - sp * sy * cr;
4421
4422 Ok(make_vec4(w, x, y, z))
4423 });
4424
4425 define(interp, "quat_mul", Some(2), |_, args| {
4427 let q1 = extract_vec4(&args[0], "quat_mul")?;
4428 let q2 = extract_vec4(&args[1], "quat_mul")?;
4429
4430 let w = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
4432 let x = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
4433 let y = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
4434 let z = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
4435
4436 Ok(make_vec4(w, x, y, z))
4437 });
4438
4439 define(interp, "quat_conjugate", Some(1), |_, args| {
4441 let q = extract_vec4(&args[0], "quat_conjugate")?;
4442 Ok(make_vec4(q[0], -q[1], -q[2], -q[3]))
4443 });
4444
4445 define(interp, "quat_inverse", Some(1), |_, args| {
4447 let q = extract_vec4(&args[0], "quat_inverse")?;
4448 let norm_sq = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
4449 if norm_sq < 1e-10 {
4450 return Err(RuntimeError::new(
4451 "quat_inverse: cannot invert zero quaternion",
4452 ));
4453 }
4454 Ok(make_vec4(
4455 q[0] / norm_sq,
4456 -q[1] / norm_sq,
4457 -q[2] / norm_sq,
4458 -q[3] / norm_sq,
4459 ))
4460 });
4461
4462 define(interp, "quat_normalize", Some(1), |_, args| {
4464 let q = extract_vec4(&args[0], "quat_normalize")?;
4465 let len = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]).sqrt();
4466 if len < 1e-10 {
4467 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0));
4468 }
4469 Ok(make_vec4(q[0] / len, q[1] / len, q[2] / len, q[3] / len))
4470 });
4471
4472 define(interp, "quat_rotate", Some(2), |_, args| {
4474 let q = extract_vec4(&args[0], "quat_rotate")?;
4475 let v = extract_vec3(&args[1], "quat_rotate")?;
4476
4477 let qw = q[0];
4479 let qx = q[1];
4480 let qy = q[2];
4481 let qz = q[3];
4482 let vx = v[0];
4483 let vy = v[1];
4484 let vz = v[2];
4485
4486 let tx = 2.0 * (qy * vz - qz * vy);
4488 let ty = 2.0 * (qz * vx - qx * vz);
4489 let tz = 2.0 * (qx * vy - qy * vx);
4490
4491 let rx = vx + qw * tx + (qy * tz - qz * ty);
4493 let ry = vy + qw * ty + (qz * tx - qx * tz);
4494 let rz = vz + qw * tz + (qx * ty - qy * tx);
4495
4496 Ok(make_vec3(rx, ry, rz))
4497 });
4498
4499 define(interp, "quat_slerp", Some(3), |_, args| {
4501 let q1 = extract_vec4(&args[0], "quat_slerp")?;
4502 let mut q2 = extract_vec4(&args[1], "quat_slerp")?;
4503 let t = extract_number(&args[2], "quat_slerp")?;
4504
4505 let mut dot = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
4507
4508 if dot < 0.0 {
4510 q2 = [-q2[0], -q2[1], -q2[2], -q2[3]];
4511 dot = -dot;
4512 }
4513
4514 if dot > 0.9995 {
4516 let w = q1[0] + t * (q2[0] - q1[0]);
4517 let x = q1[1] + t * (q2[1] - q1[1]);
4518 let y = q1[2] + t * (q2[2] - q1[2]);
4519 let z = q1[3] + t * (q2[3] - q1[3]);
4520 let len = (w * w + x * x + y * y + z * z).sqrt();
4521 return Ok(make_vec4(w / len, x / len, y / len, z / len));
4522 }
4523
4524 let theta_0 = dot.acos();
4526 let theta = theta_0 * t;
4527 let sin_theta = theta.sin();
4528 let sin_theta_0 = theta_0.sin();
4529
4530 let s0 = (theta_0 - theta).cos() - dot * sin_theta / sin_theta_0;
4531 let s1 = sin_theta / sin_theta_0;
4532
4533 Ok(make_vec4(
4534 s0 * q1[0] + s1 * q2[0],
4535 s0 * q1[1] + s1 * q2[1],
4536 s0 * q1[2] + s1 * q2[2],
4537 s0 * q1[3] + s1 * q2[3],
4538 ))
4539 });
4540
4541 define(interp, "quat_to_euler", Some(1), |_, args| {
4543 let q = extract_vec4(&args[0], "quat_to_euler")?;
4544 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4545
4546 let sinr_cosp = 2.0 * (w * x + y * z);
4548 let cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
4549 let roll = sinr_cosp.atan2(cosr_cosp);
4550
4551 let sinp = 2.0 * (w * y - z * x);
4553 let pitch = if sinp.abs() >= 1.0 {
4554 std::f64::consts::FRAC_PI_2.copysign(sinp)
4555 } else {
4556 sinp.asin()
4557 };
4558
4559 let siny_cosp = 2.0 * (w * z + x * y);
4561 let cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
4562 let yaw = siny_cosp.atan2(cosy_cosp);
4563
4564 Ok(make_vec3(pitch, yaw, roll))
4565 });
4566
4567 define(interp, "quat_to_mat4", Some(1), |_, args| {
4569 let q = extract_vec4(&args[0], "quat_to_mat4")?;
4570 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4571
4572 let xx = x * x;
4573 let yy = y * y;
4574 let zz = z * z;
4575 let xy = x * y;
4576 let xz = x * z;
4577 let yz = y * z;
4578 let wx = w * x;
4579 let wy = w * y;
4580 let wz = w * z;
4581
4582 Ok(make_mat4([
4584 1.0 - 2.0 * (yy + zz),
4585 2.0 * (xy + wz),
4586 2.0 * (xz - wy),
4587 0.0,
4588 2.0 * (xy - wz),
4589 1.0 - 2.0 * (xx + zz),
4590 2.0 * (yz + wx),
4591 0.0,
4592 2.0 * (xz + wy),
4593 2.0 * (yz - wx),
4594 1.0 - 2.0 * (xx + yy),
4595 0.0,
4596 0.0,
4597 0.0,
4598 0.0,
4599 1.0,
4600 ]))
4601 });
4602
4603 define(interp, "vec2", Some(2), |_, args| {
4609 let x = extract_number(&args[0], "vec2")?;
4610 let y = extract_number(&args[1], "vec2")?;
4611 Ok(make_vec2(x, y))
4612 });
4613
4614 define(interp, "vec3", Some(3), |_, args| {
4616 let x = extract_number(&args[0], "vec3")?;
4617 let y = extract_number(&args[1], "vec3")?;
4618 let z = extract_number(&args[2], "vec3")?;
4619 Ok(make_vec3(x, y, z))
4620 });
4621
4622 define(interp, "vec4", Some(4), |_, args| {
4624 let x = extract_number(&args[0], "vec4")?;
4625 let y = extract_number(&args[1], "vec4")?;
4626 let z = extract_number(&args[2], "vec4")?;
4627 let w = extract_number(&args[3], "vec4")?;
4628 Ok(make_vec4(x, y, z, w))
4629 });
4630
4631 define(interp, "vec3_add", Some(2), |_, args| {
4633 let a = extract_vec3(&args[0], "vec3_add")?;
4634 let b = extract_vec3(&args[1], "vec3_add")?;
4635 Ok(make_vec3(a[0] + b[0], a[1] + b[1], a[2] + b[2]))
4636 });
4637
4638 define(interp, "vec3_sub", Some(2), |_, args| {
4640 let a = extract_vec3(&args[0], "vec3_sub")?;
4641 let b = extract_vec3(&args[1], "vec3_sub")?;
4642 Ok(make_vec3(a[0] - b[0], a[1] - b[1], a[2] - b[2]))
4643 });
4644
4645 define(interp, "vec3_scale", Some(2), |_, args| {
4647 let v = extract_vec3(&args[0], "vec3_scale")?;
4648 let s = extract_number(&args[1], "vec3_scale")?;
4649 Ok(make_vec3(v[0] * s, v[1] * s, v[2] * s))
4650 });
4651
4652 define(interp, "vec3_dot", Some(2), |_, args| {
4654 let a = extract_vec3(&args[0], "vec3_dot")?;
4655 let b = extract_vec3(&args[1], "vec3_dot")?;
4656 Ok(Value::Float(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]))
4657 });
4658
4659 define(interp, "vec3_cross", Some(2), |_, args| {
4661 let a = extract_vec3(&args[0], "vec3_cross")?;
4662 let b = extract_vec3(&args[1], "vec3_cross")?;
4663 Ok(make_vec3(
4664 a[1] * b[2] - a[2] * b[1],
4665 a[2] * b[0] - a[0] * b[2],
4666 a[0] * b[1] - a[1] * b[0],
4667 ))
4668 });
4669
4670 define(interp, "vec3_length", Some(1), |_, args| {
4672 let v = extract_vec3(&args[0], "vec3_length")?;
4673 Ok(Value::Float(
4674 (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt(),
4675 ))
4676 });
4677
4678 define(interp, "vec3_normalize", Some(1), |_, args| {
4680 let v = extract_vec3(&args[0], "vec3_normalize")?;
4681 let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
4682 if len < 1e-10 {
4683 return Ok(make_vec3(0.0, 0.0, 0.0));
4684 }
4685 Ok(make_vec3(v[0] / len, v[1] / len, v[2] / len))
4686 });
4687
4688 define(interp, "vec3_lerp", Some(3), |_, args| {
4690 let a = extract_vec3(&args[0], "vec3_lerp")?;
4691 let b = extract_vec3(&args[1], "vec3_lerp")?;
4692 let t = extract_number(&args[2], "vec3_lerp")?;
4693 Ok(make_vec3(
4694 a[0] + t * (b[0] - a[0]),
4695 a[1] + t * (b[1] - a[1]),
4696 a[2] + t * (b[2] - a[2]),
4697 ))
4698 });
4699
4700 define(interp, "vec3_reflect", Some(2), |_, args| {
4702 let i = extract_vec3(&args[0], "vec3_reflect")?;
4703 let n = extract_vec3(&args[1], "vec3_reflect")?;
4704 let dot = i[0] * n[0] + i[1] * n[1] + i[2] * n[2];
4705 Ok(make_vec3(
4706 i[0] - 2.0 * dot * n[0],
4707 i[1] - 2.0 * dot * n[1],
4708 i[2] - 2.0 * dot * n[2],
4709 ))
4710 });
4711
4712 define(interp, "vec3_refract", Some(3), |_, args| {
4714 let i = extract_vec3(&args[0], "vec3_refract")?;
4715 let n = extract_vec3(&args[1], "vec3_refract")?;
4716 let eta = extract_number(&args[2], "vec3_refract")?;
4717
4718 let dot = i[0] * n[0] + i[1] * n[1] + i[2] * n[2];
4719 let k = 1.0 - eta * eta * (1.0 - dot * dot);
4720
4721 if k < 0.0 {
4722 return Ok(make_vec3(0.0, 0.0, 0.0));
4724 }
4725
4726 let coeff = eta * dot + k.sqrt();
4727 Ok(make_vec3(
4728 eta * i[0] - coeff * n[0],
4729 eta * i[1] - coeff * n[1],
4730 eta * i[2] - coeff * n[2],
4731 ))
4732 });
4733
4734 define(interp, "vec4_dot", Some(2), |_, args| {
4736 let a = extract_vec4(&args[0], "vec4_dot")?;
4737 let b = extract_vec4(&args[1], "vec4_dot")?;
4738 Ok(Value::Float(
4739 a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3],
4740 ))
4741 });
4742
4743 define(interp, "mat4_identity", Some(0), |_, _| {
4749 Ok(make_mat4([
4750 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
4751 ]))
4752 });
4753
4754 define(interp, "mat4_mul", Some(2), |_, args| {
4756 let a = extract_mat4(&args[0], "mat4_mul")?;
4757 let b = extract_mat4(&args[1], "mat4_mul")?;
4758
4759 let mut result = [0.0f64; 16];
4760 for col in 0..4 {
4761 for row in 0..4 {
4762 let mut sum = 0.0;
4763 for k in 0..4 {
4764 sum += a[k * 4 + row] * b[col * 4 + k];
4765 }
4766 result[col * 4 + row] = sum;
4767 }
4768 }
4769 Ok(make_mat4(result))
4770 });
4771
4772 define(interp, "mat4_transform", Some(2), |_, args| {
4774 let m = extract_mat4(&args[0], "mat4_transform")?;
4775 let v = extract_vec4(&args[1], "mat4_transform")?;
4776
4777 Ok(make_vec4(
4778 m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3],
4779 m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3],
4780 m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3],
4781 m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3],
4782 ))
4783 });
4784
4785 define(interp, "mat4_translate", Some(3), |_, args| {
4787 let tx = extract_number(&args[0], "mat4_translate")?;
4788 let ty = extract_number(&args[1], "mat4_translate")?;
4789 let tz = extract_number(&args[2], "mat4_translate")?;
4790 Ok(make_mat4([
4791 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, tx, ty, tz, 1.0,
4792 ]))
4793 });
4794
4795 define(interp, "mat4_scale", Some(3), |_, args| {
4797 let sx = extract_number(&args[0], "mat4_scale")?;
4798 let sy = extract_number(&args[1], "mat4_scale")?;
4799 let sz = extract_number(&args[2], "mat4_scale")?;
4800 Ok(make_mat4([
4801 sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0, sz, 0.0, 0.0, 0.0, 0.0, 1.0,
4802 ]))
4803 });
4804
4805 define(interp, "mat4_rotate_x", Some(1), |_, args| {
4807 let angle = extract_number(&args[0], "mat4_rotate_x")?;
4808 let (s, c) = angle.sin_cos();
4809 Ok(make_mat4([
4810 1.0, 0.0, 0.0, 0.0, 0.0, c, s, 0.0, 0.0, -s, c, 0.0, 0.0, 0.0, 0.0, 1.0,
4811 ]))
4812 });
4813
4814 define(interp, "mat4_rotate_y", Some(1), |_, args| {
4816 let angle = extract_number(&args[0], "mat4_rotate_y")?;
4817 let (s, c) = angle.sin_cos();
4818 Ok(make_mat4([
4819 c, 0.0, -s, 0.0, 0.0, 1.0, 0.0, 0.0, s, 0.0, c, 0.0, 0.0, 0.0, 0.0, 1.0,
4820 ]))
4821 });
4822
4823 define(interp, "mat4_rotate_z", Some(1), |_, args| {
4825 let angle = extract_number(&args[0], "mat4_rotate_z")?;
4826 let (s, c) = angle.sin_cos();
4827 Ok(make_mat4([
4828 c, s, 0.0, 0.0, -s, c, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
4829 ]))
4830 });
4831
4832 define(interp, "mat4_perspective", Some(4), |_, args| {
4834 let fov_y = extract_number(&args[0], "mat4_perspective")?;
4835 let aspect = extract_number(&args[1], "mat4_perspective")?;
4836 let near = extract_number(&args[2], "mat4_perspective")?;
4837 let far = extract_number(&args[3], "mat4_perspective")?;
4838
4839 let f = 1.0 / (fov_y / 2.0).tan();
4840 let nf = 1.0 / (near - far);
4841
4842 Ok(make_mat4([
4843 f / aspect,
4844 0.0,
4845 0.0,
4846 0.0,
4847 0.0,
4848 f,
4849 0.0,
4850 0.0,
4851 0.0,
4852 0.0,
4853 (far + near) * nf,
4854 -1.0,
4855 0.0,
4856 0.0,
4857 2.0 * far * near * nf,
4858 0.0,
4859 ]))
4860 });
4861
4862 define(interp, "mat4_ortho", Some(6), |_, args| {
4864 let left = extract_number(&args[0], "mat4_ortho")?;
4865 let right = extract_number(&args[1], "mat4_ortho")?;
4866 let bottom = extract_number(&args[2], "mat4_ortho")?;
4867 let top = extract_number(&args[3], "mat4_ortho")?;
4868 let near = extract_number(&args[4], "mat4_ortho")?;
4869 let far = extract_number(&args[5], "mat4_ortho")?;
4870
4871 let lr = 1.0 / (left - right);
4872 let bt = 1.0 / (bottom - top);
4873 let nf = 1.0 / (near - far);
4874
4875 Ok(make_mat4([
4876 -2.0 * lr,
4877 0.0,
4878 0.0,
4879 0.0,
4880 0.0,
4881 -2.0 * bt,
4882 0.0,
4883 0.0,
4884 0.0,
4885 0.0,
4886 2.0 * nf,
4887 0.0,
4888 (left + right) * lr,
4889 (top + bottom) * bt,
4890 (far + near) * nf,
4891 1.0,
4892 ]))
4893 });
4894
4895 define(interp, "mat4_look_at", Some(3), |_, args| {
4897 let eye = extract_vec3(&args[0], "mat4_look_at")?;
4898 let center = extract_vec3(&args[1], "mat4_look_at")?;
4899 let up = extract_vec3(&args[2], "mat4_look_at")?;
4900
4901 let fx = center[0] - eye[0];
4903 let fy = center[1] - eye[1];
4904 let fz = center[2] - eye[2];
4905 let flen = (fx * fx + fy * fy + fz * fz).sqrt();
4906 let (fx, fy, fz) = (fx / flen, fy / flen, fz / flen);
4907
4908 let rx = fy * up[2] - fz * up[1];
4910 let ry = fz * up[0] - fx * up[2];
4911 let rz = fx * up[1] - fy * up[0];
4912 let rlen = (rx * rx + ry * ry + rz * rz).sqrt();
4913 let (rx, ry, rz) = (rx / rlen, ry / rlen, rz / rlen);
4914
4915 let ux = ry * fz - rz * fy;
4917 let uy = rz * fx - rx * fz;
4918 let uz = rx * fy - ry * fx;
4919
4920 Ok(make_mat4([
4921 rx,
4922 ux,
4923 -fx,
4924 0.0,
4925 ry,
4926 uy,
4927 -fy,
4928 0.0,
4929 rz,
4930 uz,
4931 -fz,
4932 0.0,
4933 -(rx * eye[0] + ry * eye[1] + rz * eye[2]),
4934 -(ux * eye[0] + uy * eye[1] + uz * eye[2]),
4935 -(-fx * eye[0] - fy * eye[1] - fz * eye[2]),
4936 1.0,
4937 ]))
4938 });
4939
4940 define(interp, "mat4_inverse", Some(1), |_, args| {
4942 let m = extract_mat4(&args[0], "mat4_inverse")?;
4943
4944 let a00 = m[0];
4946 let a01 = m[1];
4947 let a02 = m[2];
4948 let a03 = m[3];
4949 let a10 = m[4];
4950 let a11 = m[5];
4951 let a12 = m[6];
4952 let a13 = m[7];
4953 let a20 = m[8];
4954 let a21 = m[9];
4955 let a22 = m[10];
4956 let a23 = m[11];
4957 let a30 = m[12];
4958 let a31 = m[13];
4959 let a32 = m[14];
4960 let a33 = m[15];
4961
4962 let b00 = a00 * a11 - a01 * a10;
4963 let b01 = a00 * a12 - a02 * a10;
4964 let b02 = a00 * a13 - a03 * a10;
4965 let b03 = a01 * a12 - a02 * a11;
4966 let b04 = a01 * a13 - a03 * a11;
4967 let b05 = a02 * a13 - a03 * a12;
4968 let b06 = a20 * a31 - a21 * a30;
4969 let b07 = a20 * a32 - a22 * a30;
4970 let b08 = a20 * a33 - a23 * a30;
4971 let b09 = a21 * a32 - a22 * a31;
4972 let b10 = a21 * a33 - a23 * a31;
4973 let b11 = a22 * a33 - a23 * a32;
4974
4975 let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
4976
4977 if det.abs() < 1e-10 {
4978 return Err(RuntimeError::new("mat4_inverse: singular matrix"));
4979 }
4980
4981 let inv_det = 1.0 / det;
4982
4983 Ok(make_mat4([
4984 (a11 * b11 - a12 * b10 + a13 * b09) * inv_det,
4985 (a02 * b10 - a01 * b11 - a03 * b09) * inv_det,
4986 (a31 * b05 - a32 * b04 + a33 * b03) * inv_det,
4987 (a22 * b04 - a21 * b05 - a23 * b03) * inv_det,
4988 (a12 * b08 - a10 * b11 - a13 * b07) * inv_det,
4989 (a00 * b11 - a02 * b08 + a03 * b07) * inv_det,
4990 (a32 * b02 - a30 * b05 - a33 * b01) * inv_det,
4991 (a20 * b05 - a22 * b02 + a23 * b01) * inv_det,
4992 (a10 * b10 - a11 * b08 + a13 * b06) * inv_det,
4993 (a01 * b08 - a00 * b10 - a03 * b06) * inv_det,
4994 (a30 * b04 - a31 * b02 + a33 * b00) * inv_det,
4995 (a21 * b02 - a20 * b04 - a23 * b00) * inv_det,
4996 (a11 * b07 - a10 * b09 - a12 * b06) * inv_det,
4997 (a00 * b09 - a01 * b07 + a02 * b06) * inv_det,
4998 (a31 * b01 - a30 * b03 - a32 * b00) * inv_det,
4999 (a20 * b03 - a21 * b01 + a22 * b00) * inv_det,
5000 ]))
5001 });
5002
5003 define(interp, "mat4_transpose", Some(1), |_, args| {
5005 let m = extract_mat4(&args[0], "mat4_transpose")?;
5006 Ok(make_mat4([
5007 m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7],
5008 m[11], m[15],
5009 ]))
5010 });
5011
5012 define(interp, "mat3_identity", Some(0), |_, _| {
5018 Ok(make_mat3([1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]))
5019 });
5020
5021 define(interp, "mat3_from_mat4", Some(1), |_, args| {
5023 let m = extract_mat4(&args[0], "mat3_from_mat4")?;
5024 Ok(make_mat3([
5025 m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10],
5026 ]))
5027 });
5028
5029 define(interp, "mat3_mul", Some(2), |_, args| {
5031 let a = extract_mat3(&args[0], "mat3_mul")?;
5032 let b = extract_mat3(&args[1], "mat3_mul")?;
5033
5034 let mut result = [0.0f64; 9];
5035 for col in 0..3 {
5036 for row in 0..3 {
5037 let mut sum = 0.0;
5038 for k in 0..3 {
5039 sum += a[k * 3 + row] * b[col * 3 + k];
5040 }
5041 result[col * 3 + row] = sum;
5042 }
5043 }
5044 Ok(make_mat3(result))
5045 });
5046
5047 define(interp, "mat3_transform", Some(2), |_, args| {
5049 let m = extract_mat3(&args[0], "mat3_transform")?;
5050 let v = extract_vec3(&args[1], "mat3_transform")?;
5051
5052 Ok(make_vec3(
5053 m[0] * v[0] + m[3] * v[1] + m[6] * v[2],
5054 m[1] * v[0] + m[4] * v[1] + m[7] * v[2],
5055 m[2] * v[0] + m[5] * v[1] + m[8] * v[2],
5056 ))
5057 });
5058
5059 define(interp, "mat3_inverse", Some(1), |_, args| {
5061 let m = extract_mat3(&args[0], "mat3_inverse")?;
5062
5063 let det = m[0] * (m[4] * m[8] - m[5] * m[7]) - m[3] * (m[1] * m[8] - m[2] * m[7])
5064 + m[6] * (m[1] * m[5] - m[2] * m[4]);
5065
5066 if det.abs() < 1e-10 {
5067 return Err(RuntimeError::new("mat3_inverse: singular matrix"));
5068 }
5069
5070 let inv_det = 1.0 / det;
5071
5072 Ok(make_mat3([
5073 (m[4] * m[8] - m[5] * m[7]) * inv_det,
5074 (m[2] * m[7] - m[1] * m[8]) * inv_det,
5075 (m[1] * m[5] - m[2] * m[4]) * inv_det,
5076 (m[5] * m[6] - m[3] * m[8]) * inv_det,
5077 (m[0] * m[8] - m[2] * m[6]) * inv_det,
5078 (m[2] * m[3] - m[0] * m[5]) * inv_det,
5079 (m[3] * m[7] - m[4] * m[6]) * inv_det,
5080 (m[1] * m[6] - m[0] * m[7]) * inv_det,
5081 (m[0] * m[4] - m[1] * m[3]) * inv_det,
5082 ]))
5083 });
5084
5085 define(interp, "mat3_transpose", Some(1), |_, args| {
5087 let m = extract_mat3(&args[0], "mat3_transpose")?;
5088 Ok(make_mat3([
5089 m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8],
5090 ]))
5091 });
5092}
5093
5094fn extract_number(v: &Value, fn_name: &str) -> Result<f64, RuntimeError> {
5096 match v {
5097 Value::Float(f) => Ok(*f),
5098 Value::Int(i) => Ok(*i as f64),
5099 _ => Err(RuntimeError::new(format!(
5100 "{}() requires number argument",
5101 fn_name
5102 ))),
5103 }
5104}
5105
5106fn extract_vec2(v: &Value, fn_name: &str) -> Result<[f64; 2], RuntimeError> {
5107 match v {
5108 Value::Array(arr) => {
5109 let arr = arr.borrow();
5110 if arr.len() < 2 {
5111 return Err(RuntimeError::new(format!(
5112 "{}() requires vec2 (2 elements)",
5113 fn_name
5114 )));
5115 }
5116 Ok([
5117 extract_number(&arr[0], fn_name)?,
5118 extract_number(&arr[1], fn_name)?,
5119 ])
5120 }
5121 _ => Err(RuntimeError::new(format!(
5122 "{}() requires vec2 array",
5123 fn_name
5124 ))),
5125 }
5126}
5127
5128fn extract_vec3(v: &Value, fn_name: &str) -> Result<[f64; 3], RuntimeError> {
5129 match v {
5130 Value::Array(arr) => {
5131 let arr = arr.borrow();
5132 if arr.len() < 3 {
5133 return Err(RuntimeError::new(format!(
5134 "{}() requires vec3 (3 elements)",
5135 fn_name
5136 )));
5137 }
5138 Ok([
5139 extract_number(&arr[0], fn_name)?,
5140 extract_number(&arr[1], fn_name)?,
5141 extract_number(&arr[2], fn_name)?,
5142 ])
5143 }
5144 _ => Err(RuntimeError::new(format!(
5145 "{}() requires vec3 array",
5146 fn_name
5147 ))),
5148 }
5149}
5150
5151fn extract_vec4(v: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
5152 match v {
5153 Value::Array(arr) => {
5154 let arr = arr.borrow();
5155 if arr.len() < 4 {
5156 return Err(RuntimeError::new(format!(
5157 "{}() requires vec4 (4 elements)",
5158 fn_name
5159 )));
5160 }
5161 Ok([
5162 extract_number(&arr[0], fn_name)?,
5163 extract_number(&arr[1], fn_name)?,
5164 extract_number(&arr[2], fn_name)?,
5165 extract_number(&arr[3], fn_name)?,
5166 ])
5167 }
5168 _ => Err(RuntimeError::new(format!(
5169 "{}() requires vec4 array",
5170 fn_name
5171 ))),
5172 }
5173}
5174
5175fn extract_mat3(v: &Value, fn_name: &str) -> Result<[f64; 9], RuntimeError> {
5176 match v {
5177 Value::Array(arr) => {
5178 let arr = arr.borrow();
5179 if arr.len() < 9 {
5180 return Err(RuntimeError::new(format!(
5181 "{}() requires mat3 (9 elements)",
5182 fn_name
5183 )));
5184 }
5185 let mut result = [0.0f64; 9];
5186 for i in 0..9 {
5187 result[i] = extract_number(&arr[i], fn_name)?;
5188 }
5189 Ok(result)
5190 }
5191 _ => Err(RuntimeError::new(format!(
5192 "{}() requires mat3 array",
5193 fn_name
5194 ))),
5195 }
5196}
5197
5198fn extract_mat4(v: &Value, fn_name: &str) -> Result<[f64; 16], RuntimeError> {
5199 match v {
5200 Value::Array(arr) => {
5201 let arr = arr.borrow();
5202 if arr.len() < 16 {
5203 return Err(RuntimeError::new(format!(
5204 "{}() requires mat4 (16 elements)",
5205 fn_name
5206 )));
5207 }
5208 let mut result = [0.0f64; 16];
5209 for i in 0..16 {
5210 result[i] = extract_number(&arr[i], fn_name)?;
5211 }
5212 Ok(result)
5213 }
5214 _ => Err(RuntimeError::new(format!(
5215 "{}() requires mat4 array",
5216 fn_name
5217 ))),
5218 }
5219}
5220
5221fn make_vec2(x: f64, y: f64) -> Value {
5222 Value::Array(Rc::new(RefCell::new(vec![
5223 Value::Float(x),
5224 Value::Float(y),
5225 ])))
5226}
5227
5228fn make_vec3(x: f64, y: f64, z: f64) -> Value {
5229 Value::Array(Rc::new(RefCell::new(vec![
5230 Value::Float(x),
5231 Value::Float(y),
5232 Value::Float(z),
5233 ])))
5234}
5235
5236fn make_vec3_arr(v: [f64; 3]) -> Value {
5238 make_vec3(v[0], v[1], v[2])
5239}
5240
5241fn make_vec4(x: f64, y: f64, z: f64, w: f64) -> Value {
5242 Value::Array(Rc::new(RefCell::new(vec![
5243 Value::Float(x),
5244 Value::Float(y),
5245 Value::Float(z),
5246 Value::Float(w),
5247 ])))
5248}
5249
5250fn make_mat3(m: [f64; 9]) -> Value {
5251 Value::Array(Rc::new(RefCell::new(
5252 m.iter().map(|&v| Value::Float(v)).collect(),
5253 )))
5254}
5255
5256fn make_mat4(m: [f64; 16]) -> Value {
5257 Value::Array(Rc::new(RefCell::new(
5258 m.iter().map(|&v| Value::Float(v)).collect(),
5259 )))
5260}
5261
5262fn register_concurrency(interp: &mut Interpreter) {
5279 define(interp, "channel_new", Some(0), |_, _| {
5283 let (sender, receiver) = mpsc::channel();
5284 let inner = ChannelInner {
5285 sender: Mutex::new(sender),
5286 receiver: Mutex::new(receiver),
5287 };
5288 Ok(Value::Channel(Arc::new(inner)))
5289 });
5290
5291 define(interp, "channel_send", Some(2), |_, args| {
5293 let channel = match &args[0] {
5294 Value::Channel(ch) => ch.clone(),
5295 _ => {
5296 return Err(RuntimeError::new(
5297 "channel_send() requires channel as first argument",
5298 ))
5299 }
5300 };
5301 let value = args[1].clone();
5302
5303 let sender = channel
5304 .sender
5305 .lock()
5306 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5307 sender
5308 .send(value)
5309 .map_err(|_| RuntimeError::new("channel_send() failed: receiver dropped"))?;
5310
5311 Ok(Value::Null)
5312 });
5313
5314 define(interp, "channel_recv", Some(1), |_, args| {
5316 let channel = match &args[0] {
5317 Value::Channel(ch) => ch.clone(),
5318 _ => {
5319 return Err(RuntimeError::new(
5320 "channel_recv() requires channel argument",
5321 ))
5322 }
5323 };
5324
5325 let receiver = channel
5326 .receiver
5327 .lock()
5328 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5329 match receiver.recv() {
5330 Ok(value) => Ok(value),
5331 Err(_) => Err(RuntimeError::new("channel_recv() failed: sender dropped")),
5332 }
5333 });
5334
5335 define(interp, "channel_try_recv", Some(1), |_, args| {
5337 let channel = match &args[0] {
5338 Value::Channel(ch) => ch.clone(),
5339 _ => {
5340 return Err(RuntimeError::new(
5341 "channel_try_recv() requires channel argument",
5342 ))
5343 }
5344 };
5345
5346 let receiver = channel
5347 .receiver
5348 .lock()
5349 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5350 match receiver.try_recv() {
5351 Ok(value) => {
5352 Ok(Value::Variant {
5354 enum_name: "Option".to_string(),
5355 variant_name: "Some".to_string(),
5356 fields: Some(Rc::new(vec![value])),
5357 })
5358 }
5359 Err(mpsc::TryRecvError::Empty) => {
5360 Ok(Value::Variant {
5362 enum_name: "Option".to_string(),
5363 variant_name: "None".to_string(),
5364 fields: None,
5365 })
5366 }
5367 Err(mpsc::TryRecvError::Disconnected) => Err(RuntimeError::new(
5368 "channel_try_recv() failed: sender dropped",
5369 )),
5370 }
5371 });
5372
5373 define(interp, "channel_recv_timeout", Some(2), |_, args| {
5375 let channel = match &args[0] {
5376 Value::Channel(ch) => ch.clone(),
5377 _ => {
5378 return Err(RuntimeError::new(
5379 "channel_recv_timeout() requires a channel as first argument.\n\
5380 Create a channel with channel_new():\n\
5381 let ch = channel_new();\n\
5382 channel_send(ch, value);\n\
5383 let result = channel_recv_timeout(ch, 1000); // 1 second timeout",
5384 ))
5385 }
5386 };
5387 let timeout_ms = match &args[1] {
5388 Value::Int(ms) => *ms as u64,
5389 _ => {
5390 return Err(RuntimeError::new(
5391 "channel_recv_timeout() requires timeout in milliseconds (integer).\n\
5392 Example: channel_recv_timeout(ch, 1000) // Wait up to 1 second",
5393 ))
5394 }
5395 };
5396
5397 let receiver = channel
5398 .receiver
5399 .lock()
5400 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5401 match receiver.recv_timeout(std::time::Duration::from_millis(timeout_ms)) {
5402 Ok(value) => Ok(Value::Variant {
5403 enum_name: "Option".to_string(),
5404 variant_name: "Some".to_string(),
5405 fields: Some(Rc::new(vec![value])),
5406 }),
5407 Err(mpsc::RecvTimeoutError::Timeout) => Ok(Value::Variant {
5408 enum_name: "Option".to_string(),
5409 variant_name: "None".to_string(),
5410 fields: None,
5411 }),
5412 Err(mpsc::RecvTimeoutError::Disconnected) => Err(RuntimeError::new(
5413 "channel_recv_timeout() failed: sender dropped",
5414 )),
5415 }
5416 });
5417
5418 define(interp, "thread_spawn_detached", Some(0), |_, _| {
5426 thread::spawn(|| {
5429 });
5431 Ok(Value::Null)
5432 });
5433
5434 define(interp, "std·thread·spawn", Some(1), |interp, args| {
5438 match &args[0] {
5440 Value::Function(f) => {
5441 match interp.call_function(f, vec![]) {
5444 Ok(_) => {}
5445 Err(e) => eprintln!("[Sigil thread] Error: {}", e),
5446 }
5447 let mut map = HashMap::new();
5449 map.insert("__type__".to_string(), Value::String(Rc::new("JoinHandle".to_string())));
5450 map.insert("done".to_string(), Value::Bool(true));
5451 Ok(Value::Map(Rc::new(RefCell::new(map))))
5452 }
5453 Value::BuiltIn(b) => {
5454 match (b.func)(interp, vec![]) {
5455 Ok(_) => {}
5456 Err(e) => eprintln!("[Sigil thread] Error: {}", e),
5457 }
5458 let mut map = HashMap::new();
5459 map.insert("__type__".to_string(), Value::String(Rc::new("JoinHandle".to_string())));
5460 map.insert("done".to_string(), Value::Bool(true));
5461 Ok(Value::Map(Rc::new(RefCell::new(map))))
5462 }
5463 _ => Err(RuntimeError::new("std::thread::spawn requires a closure")),
5464 }
5465 });
5466
5467 define(interp, "thread_join", Some(1), |_, args| {
5470 match &args[0] {
5471 Value::ThreadHandle(h) => {
5472 let mut guard = h
5473 .lock()
5474 .map_err(|_| RuntimeError::new("thread handle mutex poisoned"))?;
5475 if let Some(handle) = guard.take() {
5476 match handle.join() {
5477 Ok(v) => Ok(v),
5478 Err(_) => Err(RuntimeError::new("thread panicked")),
5479 }
5480 } else {
5481 Err(RuntimeError::new("thread already joined"))
5482 }
5483 }
5484 _ => Ok(args[0].clone()),
5486 }
5487 });
5488
5489 define(interp, "thread_sleep", Some(1), |_, args| {
5491 let ms = match &args[0] {
5492 Value::Int(ms) => *ms as u64,
5493 Value::Float(ms) => *ms as u64,
5494 _ => {
5495 return Err(RuntimeError::new(
5496 "thread_sleep() requires integer milliseconds",
5497 ))
5498 }
5499 };
5500
5501 thread::sleep(std::time::Duration::from_millis(ms));
5502 Ok(Value::Null)
5503 });
5504
5505 define(interp, "thread_yield", Some(0), |_, _| {
5507 thread::yield_now();
5508 Ok(Value::Null)
5509 });
5510
5511 define(interp, "thread_id", Some(0), |_, _| {
5513 let id = thread::current().id();
5514 Ok(Value::String(Rc::new(format!("{:?}", id))))
5515 });
5516
5517 define(interp, "parking_lot·Mutex·new", Some(1), |_, args| {
5521 let mut map = HashMap::new();
5522 map.insert("__type__".to_string(), Value::String(Rc::new("Mutex".to_string())));
5523 map.insert("inner".to_string(), args[0].clone());
5524 Ok(Value::Map(Rc::new(RefCell::new(map))))
5525 });
5526
5527 define(interp, "std·sync·Mutex·new", Some(1), |_, args| {
5529 let mut map = HashMap::new();
5530 map.insert("__type__".to_string(), Value::String(Rc::new("Mutex".to_string())));
5531 map.insert("inner".to_string(), args[0].clone());
5532 Ok(Value::Map(Rc::new(RefCell::new(map))))
5533 });
5534
5535 define(interp, "parking_lot·RwLock·new", Some(1), |_, args| {
5537 let mut map = HashMap::new();
5538 map.insert("__type__".to_string(), Value::String(Rc::new("RwLock".to_string())));
5539 map.insert("inner".to_string(), args[0].clone());
5540 Ok(Value::Map(Rc::new(RefCell::new(map))))
5541 });
5542
5543 define(interp, "std·sync·RwLock·new", Some(1), |_, args| {
5545 let mut map = HashMap::new();
5546 map.insert("__type__".to_string(), Value::String(Rc::new("RwLock".to_string())));
5547 map.insert("inner".to_string(), args[0].clone());
5548 Ok(Value::Map(Rc::new(RefCell::new(map))))
5549 });
5550
5551 define(interp, "RwLock·new", Some(1), |_, args| {
5553 let mut map = HashMap::new();
5554 map.insert("__type__".to_string(), Value::String(Rc::new("RwLock".to_string())));
5555 map.insert("inner".to_string(), args[0].clone());
5556 Ok(Value::Map(Rc::new(RefCell::new(map))))
5557 });
5558
5559 define(interp, "AtomicU64·new", Some(1), |_, args| {
5561 let val = match &args[0] {
5562 Value::Int(i) => *i,
5563 _ => 0,
5564 };
5565 let mut map = HashMap::new();
5566 map.insert("__type__".to_string(), Value::String(Rc::new("AtomicU64".to_string())));
5567 map.insert("value".to_string(), Value::Int(val));
5568 Ok(Value::Map(Rc::new(RefCell::new(map))))
5569 });
5570
5571 define(interp, "std·sync·atomic·AtomicU64·new", Some(1), |_, args| {
5573 let val = match &args[0] {
5574 Value::Int(i) => *i,
5575 _ => 0,
5576 };
5577 let mut map = HashMap::new();
5578 map.insert("__type__".to_string(), Value::String(Rc::new("AtomicU64".to_string())));
5579 map.insert("value".to_string(), Value::Int(val));
5580 Ok(Value::Map(Rc::new(RefCell::new(map))))
5581 });
5582
5583 define(interp, "AtomicBool·new", Some(1), |_, args| {
5585 let val = match &args[0] {
5586 Value::Bool(b) => *b,
5587 _ => false,
5588 };
5589 let mut map = HashMap::new();
5590 map.insert("__type__".to_string(), Value::String(Rc::new("AtomicBool".to_string())));
5591 map.insert("value".to_string(), Value::Bool(val));
5592 Ok(Value::Map(Rc::new(RefCell::new(map))))
5593 });
5594
5595 define(interp, "std·sync·atomic·AtomicBool·new", Some(1), |_, args| {
5596 let val = match &args[0] {
5597 Value::Bool(b) => *b,
5598 _ => false,
5599 };
5600 let mut map = HashMap::new();
5601 map.insert("__type__".to_string(), Value::String(Rc::new("AtomicBool".to_string())));
5602 map.insert("value".to_string(), Value::Bool(val));
5603 Ok(Value::Map(Rc::new(RefCell::new(map))))
5604 });
5605
5606 define(interp, "Arc·new", Some(1), |_, args| {
5608 let mut map = HashMap::new();
5609 map.insert("__type__".to_string(), Value::String(Rc::new("Arc".to_string())));
5610 map.insert("inner".to_string(), args[0].clone());
5611 Ok(Value::Map(Rc::new(RefCell::new(map))))
5612 });
5613
5614 define(interp, "std·sync·Arc·new", Some(1), |_, args| {
5615 let mut map = HashMap::new();
5616 map.insert("__type__".to_string(), Value::String(Rc::new("Arc".to_string())));
5617 map.insert("inner".to_string(), args[0].clone());
5618 Ok(Value::Map(Rc::new(RefCell::new(map))))
5619 });
5620
5621 define(interp, "TcpListener·bind", Some(1), |_, args| {
5626 let addr_str = match &args[0] {
5627 Value::String(s) => s.to_string(),
5628 Value::Ref(r) => {
5629 if let Value::String(s) = &*r.borrow() {
5630 s.to_string()
5631 } else {
5632 return Err(RuntimeError::new("TcpListener::bind requires string address"));
5633 }
5634 }
5635 Value::Map(m) => {
5637 let borrowed = m.borrow();
5638 if let Some(Value::String(addr)) = borrowed.get("addr") {
5639 addr.to_string()
5640 } else if let Some(Value::String(_)) = borrowed.get("__type__") {
5641 if let Some(Value::String(addr)) = borrowed.get("addr") {
5643 addr.to_string()
5644 } else {
5645 return Err(RuntimeError::new("SocketAddr missing addr field"));
5646 }
5647 } else {
5648 return Err(RuntimeError::new("TcpListener::bind requires string or SocketAddr"));
5649 }
5650 }
5651 _ => return Err(RuntimeError::new("TcpListener::bind requires string address")),
5652 };
5653
5654 let addr: std::net::SocketAddr = match addr_str.parse() {
5656 Ok(a) => a,
5657 Err(e) => return Err(RuntimeError::new(format!("Invalid address: {}", e))),
5658 };
5659
5660 let listener = match std::net::TcpListener::bind(addr) {
5662 Ok(l) => l,
5663 Err(e) => return Err(RuntimeError::new(format!("Failed to bind: {}", e))),
5664 };
5665
5666 let local_addr = listener.local_addr().map(|a| a.to_string()).unwrap_or_default();
5667
5668 let listener_id = store_listener(listener);
5670
5671 let mut map = HashMap::new();
5672 map.insert("__type__".to_string(), Value::String(Rc::new("TcpListener".to_string())));
5673 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5674 map.insert("local_addr".to_string(), Value::String(Rc::new(local_addr)));
5675 map.insert("__listener_id__".to_string(), Value::Int(listener_id as i64));
5676
5677 eprintln!("[Sigil] TcpListener bound to {} (id={})", addr, listener_id);
5678
5679 Ok(Value::Variant {
5680 enum_name: "Result".to_string(),
5681 variant_name: "Ok".to_string(),
5682 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5683 })
5684 });
5685
5686 define(interp, "std·net·TcpListener·bind", Some(1), |_, args| {
5687 let addr_str = match &args[0] {
5688 Value::String(s) => s.to_string(),
5689 Value::Ref(r) => {
5690 if let Value::String(s) = &*r.borrow() {
5691 s.to_string()
5692 } else {
5693 return Err(RuntimeError::new("TcpListener::bind requires string address"));
5694 }
5695 }
5696 Value::Map(m) => {
5698 let borrowed = m.borrow();
5699 if let Some(Value::String(addr)) = borrowed.get("addr") {
5700 addr.to_string()
5701 } else {
5702 return Err(RuntimeError::new("TcpListener::bind requires string or SocketAddr"));
5703 }
5704 }
5705 _ => return Err(RuntimeError::new("TcpListener::bind requires string address")),
5706 };
5707
5708 let addr: std::net::SocketAddr = match addr_str.parse() {
5709 Ok(a) => a,
5710 Err(e) => return Err(RuntimeError::new(format!("Invalid address: {}", e))),
5711 };
5712
5713 let _listener = match std::net::TcpListener::bind(addr) {
5714 Ok(l) => l,
5715 Err(e) => return Err(RuntimeError::new(format!("Failed to bind: {}", e))),
5716 };
5717
5718 let mut map = HashMap::new();
5719 map.insert("__type__".to_string(), Value::String(Rc::new("TcpListener".to_string())));
5720 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5721
5722 eprintln!("[Sigil] TcpListener bound to {}", addr);
5723
5724 Ok(Value::Variant {
5725 enum_name: "Result".to_string(),
5726 variant_name: "Ok".to_string(),
5727 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5728 })
5729 });
5730
5731 define(interp, "SocketAddr·parse", Some(1), |_, args| {
5733 let addr_str = match &args[0] {
5734 Value::String(s) => s.to_string(),
5735 _ => return Err(RuntimeError::new("SocketAddr::parse requires string")),
5736 };
5737
5738 match addr_str.parse::<std::net::SocketAddr>() {
5739 Ok(_) => {
5740 let mut map = HashMap::new();
5741 map.insert("__type__".to_string(), Value::String(Rc::new("SocketAddr".to_string())));
5742 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5743 Ok(Value::Variant {
5744 enum_name: "Result".to_string(),
5745 variant_name: "Ok".to_string(),
5746 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5747 })
5748 }
5749 Err(e) => Ok(Value::Variant {
5750 enum_name: "Result".to_string(),
5751 variant_name: "Err".to_string(),
5752 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5753 }),
5754 }
5755 });
5756
5757 define(interp, "TcpStream·peer_addr", Some(1), |_, args| {
5762 let stream_id = match &args[0] {
5763 Value::Map(m) => {
5764 let borrowed = m.borrow();
5765 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5766 *id as u64
5767 } else {
5768 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5769 }
5770 }
5771 _ => return Err(RuntimeError::new("peer_addr requires TcpStream")),
5772 };
5773
5774 if let Some(guard) = get_stream_registry().lock().ok() {
5775 if let Some(stream) = guard.get(&stream_id) {
5776 match stream.peer_addr() {
5777 Ok(addr) => {
5778 let mut map = HashMap::new();
5779 map.insert("__type__".to_string(), Value::String(Rc::new("SocketAddr".to_string())));
5780 map.insert("addr".to_string(), Value::String(Rc::new(addr.to_string())));
5781 Ok(Value::Variant {
5782 enum_name: "Result".to_string(),
5783 variant_name: "Ok".to_string(),
5784 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5785 })
5786 }
5787 Err(e) => Ok(Value::Variant {
5788 enum_name: "Result".to_string(),
5789 variant_name: "Err".to_string(),
5790 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5791 }),
5792 }
5793 } else {
5794 Err(RuntimeError::new("TcpStream not found in registry"))
5795 }
5796 } else {
5797 Err(RuntimeError::new("Failed to lock stream registry"))
5798 }
5799 });
5800
5801 define(interp, "TcpStream·read", Some(2), |_, args| {
5803 use std::io::Read;
5804
5805 let stream_id = match &args[0] {
5806 Value::Map(m) => {
5807 let borrowed = m.borrow();
5808 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5809 *id as u64
5810 } else {
5811 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5812 }
5813 }
5814 _ => return Err(RuntimeError::new("read requires TcpStream")),
5815 };
5816
5817 let size = match &args[1] {
5818 Value::Int(n) => *n as usize,
5819 _ => return Err(RuntimeError::new("read requires size as integer")),
5820 };
5821
5822 if let Some(mut guard) = get_stream_registry().lock().ok() {
5823 if let Some(stream) = guard.get_mut(&stream_id) {
5824 let mut buf = vec![0u8; size];
5825 match stream.read(&mut buf) {
5826 Ok(n) => {
5827 buf.truncate(n);
5828 let bytes: Vec<Value> = buf.iter().map(|b| Value::Int(*b as i64)).collect();
5829 Ok(Value::Variant {
5830 enum_name: "Result".to_string(),
5831 variant_name: "Ok".to_string(),
5832 fields: Some(Rc::new(vec![Value::Array(Rc::new(RefCell::new(bytes)))])),
5833 })
5834 }
5835 Err(e) => Ok(Value::Variant {
5836 enum_name: "Result".to_string(),
5837 variant_name: "Err".to_string(),
5838 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5839 }),
5840 }
5841 } else {
5842 Err(RuntimeError::new("TcpStream not found in registry"))
5843 }
5844 } else {
5845 Err(RuntimeError::new("Failed to lock stream registry"))
5846 }
5847 });
5848
5849 define(interp, "TcpStream·read_exact", Some(2), |_, args| {
5851 use std::io::Read;
5852
5853 let stream_id = match &args[0] {
5854 Value::Map(m) => {
5855 let borrowed = m.borrow();
5856 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5857 *id as u64
5858 } else {
5859 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5860 }
5861 }
5862 _ => return Err(RuntimeError::new("read_exact requires TcpStream")),
5863 };
5864
5865 let size = match &args[1] {
5866 Value::Int(n) => *n as usize,
5867 _ => return Err(RuntimeError::new("read_exact requires size as integer")),
5868 };
5869
5870 if let Some(mut guard) = get_stream_registry().lock().ok() {
5871 if let Some(stream) = guard.get_mut(&stream_id) {
5872 let mut buf = vec![0u8; size];
5873 match stream.read_exact(&mut buf) {
5874 Ok(()) => {
5875 let bytes: Vec<Value> = buf.iter().map(|b| Value::Int(*b as i64)).collect();
5876 Ok(Value::Variant {
5877 enum_name: "Result".to_string(),
5878 variant_name: "Ok".to_string(),
5879 fields: Some(Rc::new(vec![Value::Array(Rc::new(RefCell::new(bytes)))])),
5880 })
5881 }
5882 Err(e) => Ok(Value::Variant {
5883 enum_name: "Result".to_string(),
5884 variant_name: "Err".to_string(),
5885 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5886 }),
5887 }
5888 } else {
5889 Err(RuntimeError::new("TcpStream not found in registry"))
5890 }
5891 } else {
5892 Err(RuntimeError::new("Failed to lock stream registry"))
5893 }
5894 });
5895
5896 define(interp, "TcpStream·write_all", Some(2), |_, args| {
5898 use std::io::Write;
5899
5900 let stream_id = match &args[0] {
5901 Value::Map(m) => {
5902 let borrowed = m.borrow();
5903 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5904 *id as u64
5905 } else {
5906 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5907 }
5908 }
5909 _ => return Err(RuntimeError::new("write_all requires TcpStream")),
5910 };
5911
5912 let data: Vec<u8> = match &args[1] {
5914 Value::String(s) => s.as_bytes().to_vec(),
5915 Value::Array(arr) => {
5916 arr.borrow().iter().filter_map(|v| {
5917 if let Value::Int(n) = v { Some(*n as u8) } else { None }
5918 }).collect()
5919 }
5920 Value::Ref(r) => {
5921 if let Value::String(s) = &*r.borrow() {
5922 s.as_bytes().to_vec()
5923 } else {
5924 return Err(RuntimeError::new("write_all requires string or byte array"));
5925 }
5926 }
5927 _ => return Err(RuntimeError::new("write_all requires string or byte array")),
5928 };
5929
5930 if let Some(mut guard) = get_stream_registry().lock().ok() {
5931 if let Some(stream) = guard.get_mut(&stream_id) {
5932 match stream.write_all(&data) {
5933 Ok(()) => Ok(Value::Variant {
5934 enum_name: "Result".to_string(),
5935 variant_name: "Ok".to_string(),
5936 fields: Some(Rc::new(vec![Value::Null])),
5937 }),
5938 Err(e) => Ok(Value::Variant {
5939 enum_name: "Result".to_string(),
5940 variant_name: "Err".to_string(),
5941 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5942 }),
5943 }
5944 } else {
5945 Err(RuntimeError::new("TcpStream not found in registry"))
5946 }
5947 } else {
5948 Err(RuntimeError::new("Failed to lock stream registry"))
5949 }
5950 });
5951
5952 define(interp, "TcpStream·flush", Some(1), |_, args| {
5954 use std::io::Write;
5955
5956 let stream_id = match &args[0] {
5957 Value::Map(m) => {
5958 let borrowed = m.borrow();
5959 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5960 *id as u64
5961 } else {
5962 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5963 }
5964 }
5965 _ => return Err(RuntimeError::new("flush requires TcpStream")),
5966 };
5967
5968 if let Some(mut guard) = get_stream_registry().lock().ok() {
5969 if let Some(stream) = guard.get_mut(&stream_id) {
5970 match stream.flush() {
5971 Ok(()) => Ok(Value::Variant {
5972 enum_name: "Result".to_string(),
5973 variant_name: "Ok".to_string(),
5974 fields: Some(Rc::new(vec![Value::Null])),
5975 }),
5976 Err(e) => Ok(Value::Variant {
5977 enum_name: "Result".to_string(),
5978 variant_name: "Err".to_string(),
5979 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5980 }),
5981 }
5982 } else {
5983 Err(RuntimeError::new("TcpStream not found in registry"))
5984 }
5985 } else {
5986 Err(RuntimeError::new("Failed to lock stream registry"))
5987 }
5988 });
5989
5990 define(interp, "BufReader·new", Some(1), |_, args| {
5994 use std::io::BufReader as StdBufReader;
5995
5996 let stream_id = match &args[0] {
5998 Value::Map(m) => {
5999 let borrowed = m.borrow();
6000 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6001 *id as u64
6002 } else {
6003 return Err(RuntimeError::new("BufReader::new requires TcpStream"));
6004 }
6005 }
6006 Value::Ref(r) => {
6007 let inner = r.borrow();
6009 if let Value::Map(m) = &*inner {
6010 let borrowed = m.borrow();
6011 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6012 *id as u64
6013 } else {
6014 return Err(RuntimeError::new("BufReader::new requires TcpStream (missing stream_id in Ref)"));
6015 }
6016 } else {
6017 return Err(RuntimeError::new("BufReader::new requires TcpStream (Ref does not contain Map)"));
6018 }
6019 }
6020 _ => return Err(RuntimeError::new("BufReader::new requires TcpStream")),
6021 };
6022
6023 let reader_id = if let Some(mut guard) = get_stream_registry().lock().ok() {
6025 if let Some(stream) = guard.get_mut(&stream_id) {
6026 let stream_clone = match stream.try_clone() {
6027 Ok(s) => s,
6028 Err(e) => return Err(RuntimeError::new(format!("Failed to clone stream: {}", e))),
6029 };
6030 let reader = StdBufReader::new(stream_clone);
6031 store_bufreader(reader)
6032 } else {
6033 return Err(RuntimeError::new("Stream not found in registry"));
6034 }
6035 } else {
6036 return Err(RuntimeError::new("Failed to lock stream registry"));
6037 };
6038
6039 let mut map = HashMap::new();
6040 map.insert("__type__".to_string(), Value::String(Rc::new("BufReader".to_string())));
6041 map.insert("__stream_id__".to_string(), Value::Int(stream_id as i64));
6042 map.insert("__reader_id__".to_string(), Value::Int(reader_id as i64));
6043 Ok(Value::Map(Rc::new(RefCell::new(map))))
6044 });
6045
6046 define(interp, "BufReader·read_line", Some(1), |_, args| {
6048 use std::io::BufRead;
6049
6050 let reader_id = match &args[0] {
6051 Value::Map(m) => {
6052 let borrowed = m.borrow();
6053 if let Some(Value::Int(id)) = borrowed.get("__reader_id__") {
6054 *id as u64
6055 } else {
6056 return Err(RuntimeError::new("BufReader missing __reader_id__"));
6057 }
6058 }
6059 _ => return Err(RuntimeError::new("read_line requires BufReader")),
6060 };
6061
6062 if let Some(mut guard) = get_bufreader_registry().lock().ok() {
6063 if let Some(reader) = guard.get_mut(&reader_id) {
6064 let mut line = String::new();
6065
6066 match reader.read_line(&mut line) {
6067 Ok(n) => {
6068 if n == 0 {
6069 Ok(Value::Variant {
6071 enum_name: "Result".to_string(),
6072 variant_name: "Ok".to_string(),
6073 fields: Some(Rc::new(vec![Value::Null])),
6074 })
6075 } else {
6076 Ok(Value::Variant {
6077 enum_name: "Result".to_string(),
6078 variant_name: "Ok".to_string(),
6079 fields: Some(Rc::new(vec![Value::String(Rc::new(line))])),
6080 })
6081 }
6082 }
6083 Err(e) => Ok(Value::Variant {
6084 enum_name: "Result".to_string(),
6085 variant_name: "Err".to_string(),
6086 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
6087 }),
6088 }
6089 } else {
6090 Err(RuntimeError::new("BufReader not found in registry"))
6091 }
6092 } else {
6093 Err(RuntimeError::new("Failed to lock bufreader registry"))
6094 }
6095 });
6096
6097 define(interp, "styx_http·middleware·Logger·new", Some(0), |_, _| {
6102 let mut map = HashMap::new();
6103 map.insert("__type__".to_string(), Value::String(Rc::new("Logger".to_string())));
6104 map.insert("format".to_string(), Value::String(Rc::new("Common".to_string())));
6105 Ok(Value::Map(Rc::new(RefCell::new(map))))
6106 });
6107
6108 define(interp, "Logger·new", Some(0), |_, _| {
6109 let mut map = HashMap::new();
6110 map.insert("__type__".to_string(), Value::String(Rc::new("Logger".to_string())));
6111 map.insert("format".to_string(), Value::String(Rc::new("Common".to_string())));
6112 Ok(Value::Map(Rc::new(RefCell::new(map))))
6113 });
6114
6115 define(interp, "styx_http·middleware·Cors·new", Some(0), |_, _| {
6117 let mut map = HashMap::new();
6118 map.insert("__type__".to_string(), Value::String(Rc::new("Cors".to_string())));
6119 map.insert("origins".to_string(), Value::Array(Rc::new(RefCell::new(vec![]))));
6120 Ok(Value::Map(Rc::new(RefCell::new(map))))
6121 });
6122
6123 define(interp, "Cors·new", Some(0), |_, _| {
6124 let mut map = HashMap::new();
6125 map.insert("__type__".to_string(), Value::String(Rc::new("Cors".to_string())));
6126 map.insert("origins".to_string(), Value::Array(Rc::new(RefCell::new(vec![]))));
6127 Ok(Value::Map(Rc::new(RefCell::new(map))))
6128 });
6129
6130 define(interp, "styx_http·middleware·SecurityHeaders·new", Some(0), |_, _| {
6132 let mut map = HashMap::new();
6133 map.insert("__type__".to_string(), Value::String(Rc::new("SecurityHeaders".to_string())));
6134 Ok(Value::Map(Rc::new(RefCell::new(map))))
6135 });
6136
6137 define(interp, "SecurityHeaders·new", Some(0), |_, _| {
6138 let mut map = HashMap::new();
6139 map.insert("__type__".to_string(), Value::String(Rc::new("SecurityHeaders".to_string())));
6140 Ok(Value::Map(Rc::new(RefCell::new(map))))
6141 });
6142
6143 define(interp, "styx_http·middleware·RateLimiter·new", Some(0), |_, _| {
6145 let mut map = HashMap::new();
6146 map.insert("__type__".to_string(), Value::String(Rc::new("RateLimiter".to_string())));
6147 Ok(Value::Map(Rc::new(RefCell::new(map))))
6148 });
6149
6150 define(interp, "RateLimiter·new", Some(0), |_, _| {
6151 let mut map = HashMap::new();
6152 map.insert("__type__".to_string(), Value::String(Rc::new("RateLimiter".to_string())));
6153 Ok(Value::Map(Rc::new(RefCell::new(map))))
6154 });
6155
6156 define(interp, "styx_http·middleware·RateLimit·new", None, |_, args| {
6158 let mut map = HashMap::new();
6159 map.insert("__type__".to_string(), Value::String(Rc::new("RateLimit".to_string())));
6160 if args.len() >= 2 {
6161 map.insert("rate".to_string(), args[0].clone());
6162 map.insert("burst".to_string(), args[1].clone());
6163 }
6164 Ok(Value::Map(Rc::new(RefCell::new(map))))
6165 });
6166
6167 define(interp, "RateLimit·new", None, |_, args| {
6168 let mut map = HashMap::new();
6169 map.insert("__type__".to_string(), Value::String(Rc::new("RateLimit".to_string())));
6170 if args.len() >= 2 {
6171 map.insert("rate".to_string(), args[0].clone());
6172 map.insert("burst".to_string(), args[1].clone());
6173 }
6174 Ok(Value::Map(Rc::new(RefCell::new(map))))
6175 });
6176
6177 define(interp, "styx_http·middleware·Compression·new", Some(0), |_, _| {
6179 let mut map = HashMap::new();
6180 map.insert("__type__".to_string(), Value::String(Rc::new("Compression".to_string())));
6181 Ok(Value::Map(Rc::new(RefCell::new(map))))
6182 });
6183
6184 define(interp, "Compression·new", Some(0), |_, _| {
6185 let mut map = HashMap::new();
6186 map.insert("__type__".to_string(), Value::String(Rc::new("Compression".to_string())));
6187 Ok(Value::Map(Rc::new(RefCell::new(map))))
6188 });
6189
6190 define(interp, "AuthMiddleware·optional", Some(0), |_, _| {
6192 let mut map = HashMap::new();
6193 map.insert("__type__".to_string(), Value::String(Rc::new("AuthMiddleware".to_string())));
6194 map.insert("mode".to_string(), Value::String(Rc::new("optional".to_string())));
6195 Ok(Value::Map(Rc::new(RefCell::new(map))))
6196 });
6197
6198 define(interp, "AuthMiddleware·required", Some(0), |_, _| {
6199 let mut map = HashMap::new();
6200 map.insert("__type__".to_string(), Value::String(Rc::new("AuthMiddleware".to_string())));
6201 map.insert("mode".to_string(), Value::String(Rc::new("required".to_string())));
6202 Ok(Value::Map(Rc::new(RefCell::new(map))))
6203 });
6204
6205 define(interp, "AuthMiddleware·new", Some(0), |_, _| {
6206 let mut map = HashMap::new();
6207 map.insert("__type__".to_string(), Value::String(Rc::new("AuthMiddleware".to_string())));
6208 map.insert("mode".to_string(), Value::String(Rc::new("required".to_string())));
6209 Ok(Value::Map(Rc::new(RefCell::new(map))))
6210 });
6211
6212 define(interp, "spawn_actor", Some(1), |_, args| {
6219 let name = match &args[0] {
6220 Value::String(s) => s.to_string(),
6221 _ => return Err(RuntimeError::new("actor_spawn() requires string name")),
6222 };
6223
6224 let inner = ActorInner {
6225 name,
6226 message_queue: Mutex::new(Vec::new()),
6227 message_count: std::sync::atomic::AtomicUsize::new(0),
6228 };
6229
6230 Ok(Value::Actor(Arc::new(inner)))
6231 });
6232
6233 define(interp, "send_to_actor", Some(3), |_, args| {
6236 let actor = match &args[0] {
6237 Value::Actor(a) => a.clone(),
6238 _ => {
6239 return Err(RuntimeError::new(
6240 "actor_send() requires actor as first argument",
6241 ))
6242 }
6243 };
6244 let msg_type = match &args[1] {
6245 Value::String(s) => s.to_string(),
6246 _ => {
6247 return Err(RuntimeError::new(
6248 "actor_send() requires string message type",
6249 ))
6250 }
6251 };
6252 let msg_data = format!("{}", args[2]);
6253
6254 let mut queue = actor
6255 .message_queue
6256 .lock()
6257 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6258 queue.push((msg_type, msg_data));
6259 actor
6260 .message_count
6261 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
6262
6263 Ok(Value::Null)
6264 });
6265
6266 define(interp, "tell_actor", Some(3), |_, args| {
6268 let actor = match &args[0] {
6269 Value::Actor(a) => a.clone(),
6270 _ => {
6271 return Err(RuntimeError::new(
6272 "actor_tell() requires actor as first argument",
6273 ))
6274 }
6275 };
6276 let msg_type = match &args[1] {
6277 Value::String(s) => s.to_string(),
6278 _ => {
6279 return Err(RuntimeError::new(
6280 "actor_tell() requires string message type",
6281 ))
6282 }
6283 };
6284 let msg_data = format!("{}", args[2]);
6285
6286 let mut queue = actor
6287 .message_queue
6288 .lock()
6289 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6290 queue.push((msg_type, msg_data));
6291 actor
6292 .message_count
6293 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
6294
6295 Ok(Value::Null)
6296 });
6297
6298 define(interp, "recv_from_actor", Some(1), |_, args| {
6301 let actor = match &args[0] {
6302 Value::Actor(a) => a.clone(),
6303 _ => return Err(RuntimeError::new("actor_recv() requires actor argument")),
6304 };
6305
6306 let mut queue = actor
6307 .message_queue
6308 .lock()
6309 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6310 match queue.pop() {
6311 Some((msg_type, msg_data)) => {
6312 Ok(Value::Variant {
6314 enum_name: "Option".to_string(),
6315 variant_name: "Some".to_string(),
6316 fields: Some(Rc::new(vec![Value::Tuple(Rc::new(vec![
6317 Value::String(Rc::new(msg_type)),
6318 Value::String(Rc::new(msg_data)),
6319 ]))])),
6320 })
6321 }
6322 None => Ok(Value::Variant {
6323 enum_name: "Option".to_string(),
6324 variant_name: "None".to_string(),
6325 fields: None,
6326 }),
6327 }
6328 });
6329
6330 define(interp, "get_actor_msg_count", Some(1), |_, args| {
6332 let a = match &args[0] {
6333 Value::Actor(a) => a.clone(),
6334 _ => {
6335 return Err(RuntimeError::new(
6336 "get_actor_msg_count() requires actor argument",
6337 ))
6338 }
6339 };
6340
6341 let count = a.message_count.load(std::sync::atomic::Ordering::SeqCst);
6342 Ok(Value::Int(count as i64))
6343 });
6344
6345 define(interp, "get_actor_name", Some(1), |_, args| {
6347 let a = match &args[0] {
6348 Value::Actor(a) => a.clone(),
6349 _ => {
6350 return Err(RuntimeError::new(
6351 "get_actor_name() requires actor argument",
6352 ))
6353 }
6354 };
6355
6356 Ok(Value::String(Rc::new(a.name.clone())))
6357 });
6358
6359 define(interp, "get_actor_pending", Some(1), |_, args| {
6361 let a = match &args[0] {
6362 Value::Actor(a) => a.clone(),
6363 _ => {
6364 return Err(RuntimeError::new(
6365 "get_actor_pending() requires actor argument",
6366 ))
6367 }
6368 };
6369
6370 let queue = a
6371 .message_queue
6372 .lock()
6373 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6374 Ok(Value::Int(queue.len() as i64))
6375 });
6376
6377 define(interp, "mutex_new", Some(1), |_, args| {
6381 let value = args[0].clone();
6382 let mut map = std::collections::HashMap::new();
6384 map.insert("__mutex_value".to_string(), value);
6385 map.insert("__mutex_locked".to_string(), Value::Bool(false));
6386 Ok(Value::Map(Rc::new(RefCell::new(map))))
6387 });
6388
6389 define(interp, "mutex_lock", Some(1), |_, args| {
6391 let mutex = match &args[0] {
6392 Value::Map(m) => m.clone(),
6393 _ => return Err(RuntimeError::new("mutex_lock() requires mutex")),
6394 };
6395
6396 let mut map = mutex.borrow_mut();
6397 map.insert("__mutex_locked".to_string(), Value::Bool(true));
6399
6400 match map.get("__mutex_value") {
6401 Some(v) => Ok(v.clone()),
6402 None => Err(RuntimeError::new("invalid mutex")),
6403 }
6404 });
6405
6406 define(interp, "mutex_unlock", Some(2), |_, args| {
6408 let mutex = match &args[0] {
6409 Value::Map(m) => m.clone(),
6410 _ => return Err(RuntimeError::new("mutex_unlock() requires mutex")),
6411 };
6412 let new_value = args[1].clone();
6413
6414 let mut map = mutex.borrow_mut();
6415 map.insert("__mutex_value".to_string(), new_value);
6416 map.insert("__mutex_locked".to_string(), Value::Bool(false));
6417
6418 Ok(Value::Null)
6419 });
6420
6421 define(interp, "atomic_new", Some(1), |_, args| {
6423 let value = match &args[0] {
6424 Value::Int(i) => *i,
6425 _ => return Err(RuntimeError::new("atomic_new() requires integer")),
6426 };
6427
6428 let mut map = std::collections::HashMap::new();
6430 map.insert("__atomic_value".to_string(), Value::Int(value));
6431 Ok(Value::Map(Rc::new(RefCell::new(map))))
6432 });
6433
6434 define(interp, "atomic_load", Some(1), |_, args| {
6436 let atomic = match &args[0] {
6437 Value::Map(m) => m.clone(),
6438 _ => return Err(RuntimeError::new("atomic_load() requires atomic")),
6439 };
6440
6441 let map = atomic.borrow();
6442 match map.get("__atomic_value") {
6443 Some(v) => Ok(v.clone()),
6444 None => Err(RuntimeError::new("invalid atomic")),
6445 }
6446 });
6447
6448 define(interp, "atomic_store", Some(2), |_, args| {
6450 let atomic = match &args[0] {
6451 Value::Map(m) => m.clone(),
6452 _ => return Err(RuntimeError::new("atomic_store() requires atomic")),
6453 };
6454 let value = match &args[1] {
6455 Value::Int(i) => *i,
6456 _ => return Err(RuntimeError::new("atomic_store() requires integer value")),
6457 };
6458
6459 let mut map = atomic.borrow_mut();
6460 map.insert("__atomic_value".to_string(), Value::Int(value));
6461 Ok(Value::Null)
6462 });
6463
6464 define(interp, "atomic_add", Some(2), |_, args| {
6466 let atomic = match &args[0] {
6467 Value::Map(m) => m.clone(),
6468 _ => return Err(RuntimeError::new("atomic_add() requires atomic")),
6469 };
6470 let delta = match &args[1] {
6471 Value::Int(i) => *i,
6472 _ => return Err(RuntimeError::new("atomic_add() requires integer delta")),
6473 };
6474
6475 let mut map = atomic.borrow_mut();
6476 let old = match map.get("__atomic_value") {
6477 Some(Value::Int(i)) => *i,
6478 _ => return Err(RuntimeError::new("invalid atomic")),
6479 };
6480 map.insert("__atomic_value".to_string(), Value::Int(old + delta));
6481 Ok(Value::Int(old))
6482 });
6483
6484 define(interp, "atomic_cas", Some(3), |_, args| {
6486 let atomic = match &args[0] {
6487 Value::Map(m) => m.clone(),
6488 _ => return Err(RuntimeError::new("atomic_cas() requires atomic")),
6489 };
6490 let expected = match &args[1] {
6491 Value::Int(i) => *i,
6492 _ => return Err(RuntimeError::new("atomic_cas() requires integer expected")),
6493 };
6494 let new_value = match &args[2] {
6495 Value::Int(i) => *i,
6496 _ => return Err(RuntimeError::new("atomic_cas() requires integer new value")),
6497 };
6498
6499 let mut map = atomic.borrow_mut();
6500 let current = match map.get("__atomic_value") {
6501 Some(Value::Int(i)) => *i,
6502 _ => return Err(RuntimeError::new("invalid atomic")),
6503 };
6504
6505 if current == expected {
6506 map.insert("__atomic_value".to_string(), Value::Int(new_value));
6507 Ok(Value::Bool(true))
6508 } else {
6509 Ok(Value::Bool(false))
6510 }
6511 });
6512
6513 define(interp, "parallel_map", Some(2), |_, args| {
6517 let arr = match &args[0] {
6518 Value::Array(a) => a.borrow().clone(),
6519 _ => return Err(RuntimeError::new("parallel_map() requires array")),
6520 };
6521 let _func = args[1].clone();
6522
6523 Ok(Value::Array(Rc::new(RefCell::new(arr))))
6526 });
6527
6528 define(interp, "parallel_for", Some(3), |_, args| {
6530 let start = match &args[0] {
6531 Value::Int(i) => *i,
6532 _ => return Err(RuntimeError::new("parallel_for() requires integer start")),
6533 };
6534 let end = match &args[1] {
6535 Value::Int(i) => *i,
6536 _ => return Err(RuntimeError::new("parallel_for() requires integer end")),
6537 };
6538 let _func = args[2].clone();
6539
6540 let range: Vec<Value> = (start..end).map(|i| Value::Int(i)).collect();
6543 Ok(Value::Array(Rc::new(RefCell::new(range))))
6544 });
6545
6546 define(interp, "async_sleep", Some(1), |interp, args| {
6564 let ms = match &args[0] {
6565 Value::Int(ms) => *ms as u64,
6566 Value::Float(ms) => *ms as u64,
6567 _ => {
6568 return Err(RuntimeError::new(
6569 "async_sleep() requires integer milliseconds",
6570 ))
6571 }
6572 };
6573
6574 Ok(interp.make_future_timer(std::time::Duration::from_millis(ms)))
6575 });
6576
6577 define(interp, "future_ready", Some(1), |interp, args| {
6579 Ok(interp.make_future_immediate(args[0].clone()))
6580 });
6581
6582 define(interp, "future_pending", Some(0), |_, _| {
6584 Ok(Value::Future(Rc::new(RefCell::new(
6585 crate::interpreter::FutureInner {
6586 state: crate::interpreter::FutureState::Pending,
6587 computation: None,
6588 complete_at: None,
6589 },
6590 ))))
6591 });
6592
6593 define(interp, "is_future", Some(1), |_, args| {
6595 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
6596 });
6597
6598 define(interp, "is_ready", Some(1), |_, args| {
6600 match &args[0] {
6601 Value::Future(fut) => {
6602 let f = fut.borrow();
6603 Ok(Value::Bool(matches!(
6604 f.state,
6605 crate::interpreter::FutureState::Ready(_)
6606 )))
6607 }
6608 _ => Ok(Value::Bool(true)), }
6610 });
6611
6612 define(interp, "join_futures", Some(1), |_, args| {
6614 let futures = match &args[0] {
6615 Value::Array(arr) => {
6616 let arr = arr.borrow();
6617 let mut futs = Vec::new();
6618 for v in arr.iter() {
6619 match v {
6620 Value::Future(f) => futs.push(f.clone()),
6621 _ => {
6622 return Err(RuntimeError::new(
6623 "join_futures() requires array of futures",
6624 ))
6625 }
6626 }
6627 }
6628 futs
6629 }
6630 _ => {
6631 return Err(RuntimeError::new(
6632 "join_futures() requires array of futures",
6633 ))
6634 }
6635 };
6636
6637 Ok(Value::Future(Rc::new(RefCell::new(
6638 crate::interpreter::FutureInner {
6639 state: crate::interpreter::FutureState::Pending,
6640 computation: Some(crate::interpreter::FutureComputation::Join(futures)),
6641 complete_at: None,
6642 },
6643 ))))
6644 });
6645
6646 define(interp, "race_futures", Some(1), |_, args| {
6648 let futures = match &args[0] {
6649 Value::Array(arr) => {
6650 let arr = arr.borrow();
6651 let mut futs = Vec::new();
6652 for v in arr.iter() {
6653 match v {
6654 Value::Future(f) => futs.push(f.clone()),
6655 _ => {
6656 return Err(RuntimeError::new(
6657 "race_futures() requires array of futures",
6658 ))
6659 }
6660 }
6661 }
6662 futs
6663 }
6664 _ => {
6665 return Err(RuntimeError::new(
6666 "race_futures() requires array of futures",
6667 ))
6668 }
6669 };
6670
6671 Ok(Value::Future(Rc::new(RefCell::new(
6672 crate::interpreter::FutureInner {
6673 state: crate::interpreter::FutureState::Pending,
6674 computation: Some(crate::interpreter::FutureComputation::Race(futures)),
6675 complete_at: None,
6676 },
6677 ))))
6678 });
6679
6680 define(interp, "poll_future", Some(1), |_, args| {
6682 match &args[0] {
6683 Value::Future(fut) => {
6684 let f = fut.borrow();
6685 match &f.state {
6686 crate::interpreter::FutureState::Ready(v) => Ok(Value::Variant {
6687 enum_name: "Option".to_string(),
6688 variant_name: "Some".to_string(),
6689 fields: Some(Rc::new(vec![(**v).clone()])),
6690 }),
6691 _ => Ok(Value::Variant {
6692 enum_name: "Option".to_string(),
6693 variant_name: "None".to_string(),
6694 fields: None,
6695 }),
6696 }
6697 }
6698 other => Ok(Value::Variant {
6700 enum_name: "Option".to_string(),
6701 variant_name: "Some".to_string(),
6702 fields: Some(Rc::new(vec![other.clone()])),
6703 }),
6704 }
6705 });
6706}
6707
6708fn register_json(interp: &mut Interpreter) {
6713 define(interp, "json_parse", Some(1), |_, args| {
6715 let json_str = match &args[0] {
6716 Value::String(s) => s.as_str(),
6717 _ => return Err(RuntimeError::new("json_parse() requires string argument")),
6718 };
6719
6720 fn json_to_value(json: &serde_json::Value) -> Value {
6721 match json {
6722 serde_json::Value::Null => Value::Null,
6723 serde_json::Value::Bool(b) => Value::Bool(*b),
6724 serde_json::Value::Number(n) => {
6725 if let Some(i) = n.as_i64() {
6726 Value::Int(i)
6727 } else if let Some(f) = n.as_f64() {
6728 Value::Float(f)
6729 } else {
6730 Value::Null
6731 }
6732 }
6733 serde_json::Value::String(s) => Value::String(Rc::new(s.clone())),
6734 serde_json::Value::Array(arr) => {
6735 let values: Vec<Value> = arr.iter().map(json_to_value).collect();
6736 Value::Array(Rc::new(RefCell::new(values)))
6737 }
6738 serde_json::Value::Object(obj) => {
6739 let mut map = HashMap::new();
6740 for (k, v) in obj {
6741 map.insert(k.clone(), json_to_value(v));
6742 }
6743 Value::Map(Rc::new(RefCell::new(map)))
6744 }
6745 }
6746 }
6747
6748 match serde_json::from_str(json_str) {
6749 Ok(json) => Ok(json_to_value(&json)),
6750 Err(e) => Err(RuntimeError::new(format!("JSON parse error: {}", e))),
6751 }
6752 });
6753
6754 define(interp, "json_stringify", Some(1), |_, args| {
6756 fn value_to_json(val: &Value) -> serde_json::Value {
6757 match val {
6758 Value::Null => serde_json::Value::Null,
6759 Value::Bool(b) => serde_json::Value::Bool(*b),
6760 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
6761 Value::Float(f) => serde_json::Number::from_f64(*f)
6762 .map(serde_json::Value::Number)
6763 .unwrap_or(serde_json::Value::Null),
6764 Value::String(s) => serde_json::Value::String(s.to_string()),
6765 Value::Array(arr) => {
6766 let arr = arr.borrow();
6767 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
6768 }
6769 Value::Tuple(t) => serde_json::Value::Array(t.iter().map(value_to_json).collect()),
6770 Value::Map(map) => {
6771 let map = map.borrow();
6772 let obj: serde_json::Map<String, serde_json::Value> = map
6773 .iter()
6774 .map(|(k, v)| (k.clone(), value_to_json(v)))
6775 .collect();
6776 serde_json::Value::Object(obj)
6777 }
6778 Value::Struct { fields, .. } => {
6779 let fields = fields.borrow();
6780 let obj: serde_json::Map<String, serde_json::Value> = fields
6781 .iter()
6782 .map(|(k, v)| (k.clone(), value_to_json(v)))
6783 .collect();
6784 serde_json::Value::Object(obj)
6785 }
6786 _ => serde_json::Value::String(format!("{}", val)),
6787 }
6788 }
6789
6790 let json = value_to_json(&args[0]);
6791 Ok(Value::String(Rc::new(json.to_string())))
6792 });
6793
6794 define(interp, "json_pretty", Some(1), |_, args| {
6796 fn value_to_json(val: &Value) -> serde_json::Value {
6797 match val {
6798 Value::Null => serde_json::Value::Null,
6799 Value::Bool(b) => serde_json::Value::Bool(*b),
6800 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
6801 Value::Float(f) => serde_json::Number::from_f64(*f)
6802 .map(serde_json::Value::Number)
6803 .unwrap_or(serde_json::Value::Null),
6804 Value::String(s) => serde_json::Value::String(s.to_string()),
6805 Value::Array(arr) => {
6806 let arr = arr.borrow();
6807 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
6808 }
6809 Value::Map(map) => {
6810 let map = map.borrow();
6811 let obj: serde_json::Map<String, serde_json::Value> = map
6812 .iter()
6813 .map(|(k, v)| (k.clone(), value_to_json(v)))
6814 .collect();
6815 serde_json::Value::Object(obj)
6816 }
6817 _ => serde_json::Value::String(format!("{}", val)),
6818 }
6819 }
6820
6821 let json = value_to_json(&args[0]);
6822 match serde_json::to_string_pretty(&json) {
6823 Ok(s) => Ok(Value::String(Rc::new(s))),
6824 Err(e) => Err(RuntimeError::new(format!("JSON stringify error: {}", e))),
6825 }
6826 });
6827
6828 define(interp, "json_get", Some(2), |_, args| {
6830 let path = match &args[1] {
6831 Value::String(s) => s.to_string(),
6832 _ => return Err(RuntimeError::new("json_get() requires string path")),
6833 };
6834
6835 let mut current = args[0].clone();
6836 for key in path.split('.') {
6837 current = match ¤t {
6838 Value::Map(map) => {
6839 let map = map.borrow();
6840 map.get(key).cloned().unwrap_or(Value::Null)
6841 }
6842 Value::Array(arr) => {
6843 if let Ok(idx) = key.parse::<usize>() {
6844 let arr = arr.borrow();
6845 arr.get(idx).cloned().unwrap_or(Value::Null)
6846 } else {
6847 Value::Null
6848 }
6849 }
6850 _ => Value::Null,
6851 };
6852 }
6853 Ok(current)
6854 });
6855
6856 define(interp, "json_set", Some(3), |_, args| {
6858 let path = match &args[1] {
6859 Value::String(s) => s.to_string(),
6860 _ => return Err(RuntimeError::new("json_set() requires string path")),
6861 };
6862 let new_value = args[2].clone();
6863
6864 match &args[0] {
6866 Value::Map(map) => {
6867 let mut map = map.borrow_mut();
6868 map.insert(path, new_value);
6869 Ok(Value::Map(Rc::new(RefCell::new(map.clone()))))
6870 }
6871 _ => Err(RuntimeError::new("json_set() requires map/object")),
6872 }
6873 });
6874}
6875
6876fn register_fs(interp: &mut Interpreter) {
6881 define(interp, "fs_read", Some(1), |_, args| {
6883 let path = match &args[0] {
6884 Value::String(s) => s.to_string(),
6885 _ => return Err(RuntimeError::new("fs_read() requires string path")),
6886 };
6887
6888 match std::fs::read_to_string(&path) {
6889 Ok(content) => Ok(Value::String(Rc::new(content))),
6890 Err(e) => Err(RuntimeError::new(format!("fs_read() error: {}", e))),
6891 }
6892 });
6893
6894 define(interp, "fs_read_bytes", Some(1), |_, args| {
6896 let path = match &args[0] {
6897 Value::String(s) => s.to_string(),
6898 _ => return Err(RuntimeError::new("fs_read_bytes() requires string path")),
6899 };
6900
6901 match std::fs::read(&path) {
6902 Ok(bytes) => {
6903 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
6904 Ok(Value::Array(Rc::new(RefCell::new(values))))
6905 }
6906 Err(e) => Err(RuntimeError::new(format!("fs_read_bytes() error: {}", e))),
6907 }
6908 });
6909
6910 define(interp, "fs_write", Some(2), |_, args| {
6912 let path = match &args[0] {
6913 Value::String(s) => s.to_string(),
6914 _ => return Err(RuntimeError::new("fs_write() requires string path")),
6915 };
6916 let content = format!("{}", args[1]);
6917
6918 match std::fs::write(&path, content) {
6919 Ok(()) => Ok(Value::Null),
6920 Err(e) => Err(RuntimeError::new(format!("fs_write() error: {}", e))),
6921 }
6922 });
6923
6924 define(interp, "fs_append", Some(2), |_, args| {
6926 let path = match &args[0] {
6927 Value::String(s) => s.to_string(),
6928 _ => return Err(RuntimeError::new("fs_append() requires string path")),
6929 };
6930 let content = format!("{}", args[1]);
6931
6932 use std::fs::OpenOptions;
6933 match OpenOptions::new().append(true).create(true).open(&path) {
6934 Ok(mut file) => {
6935 use std::io::Write;
6936 match file.write_all(content.as_bytes()) {
6937 Ok(()) => Ok(Value::Null),
6938 Err(e) => Err(RuntimeError::new(format!("fs_append() write error: {}", e))),
6939 }
6940 }
6941 Err(e) => Err(RuntimeError::new(format!("fs_append() error: {}", e))),
6942 }
6943 });
6944
6945 define(interp, "fs_exists", Some(1), |_, args| {
6947 let path = match &args[0] {
6948 Value::String(s) => s.to_string(),
6949 _ => return Err(RuntimeError::new("fs_exists() requires string path")),
6950 };
6951 Ok(Value::Bool(std::path::Path::new(&path).exists()))
6952 });
6953
6954 define(interp, "fs_is_file", Some(1), |_, args| {
6956 let path = match &args[0] {
6957 Value::String(s) => s.to_string(),
6958 _ => return Err(RuntimeError::new("fs_is_file() requires string path")),
6959 };
6960 Ok(Value::Bool(std::path::Path::new(&path).is_file()))
6961 });
6962
6963 define(interp, "fs_is_dir", Some(1), |_, args| {
6965 let path = match &args[0] {
6966 Value::String(s) => s.to_string(),
6967 _ => return Err(RuntimeError::new("fs_is_dir() requires string path")),
6968 };
6969 Ok(Value::Bool(std::path::Path::new(&path).is_dir()))
6970 });
6971
6972 define(interp, "fs_mkdir", Some(1), |_, args| {
6974 let path = match &args[0] {
6975 Value::String(s) => s.to_string(),
6976 _ => return Err(RuntimeError::new("fs_mkdir() requires string path")),
6977 };
6978
6979 match std::fs::create_dir_all(&path) {
6980 Ok(()) => Ok(Value::Null),
6981 Err(e) => Err(RuntimeError::new(format!("fs_mkdir() error: {}", e))),
6982 }
6983 });
6984
6985 define(interp, "fs_remove", Some(1), |_, args| {
6987 let path = match &args[0] {
6988 Value::String(s) => s.to_string(),
6989 _ => return Err(RuntimeError::new("fs_remove() requires string path")),
6990 };
6991
6992 let p = std::path::Path::new(&path);
6993 let result = if p.is_dir() {
6994 std::fs::remove_dir_all(&path)
6995 } else {
6996 std::fs::remove_file(&path)
6997 };
6998
6999 match result {
7000 Ok(()) => Ok(Value::Null),
7001 Err(e) => Err(RuntimeError::new(format!("fs_remove() error: {}", e))),
7002 }
7003 });
7004
7005 define(interp, "fs_list", Some(1), |_, args| {
7007 let path = match &args[0] {
7008 Value::String(s) => s.to_string(),
7009 _ => return Err(RuntimeError::new("fs_list() requires string path")),
7010 };
7011
7012 match std::fs::read_dir(&path) {
7013 Ok(entries) => {
7014 let mut files = Vec::new();
7015 for entry in entries.flatten() {
7016 if let Some(name) = entry.file_name().to_str() {
7017 files.push(Value::String(Rc::new(name.to_string())));
7018 }
7019 }
7020 Ok(Value::Array(Rc::new(RefCell::new(files))))
7021 }
7022 Err(e) => Err(RuntimeError::new(format!("fs_list() error: {}", e))),
7023 }
7024 });
7025
7026 define(interp, "fs_copy", Some(2), |_, args| {
7028 let src = match &args[0] {
7029 Value::String(s) => s.to_string(),
7030 _ => return Err(RuntimeError::new("fs_copy() requires string source path")),
7031 };
7032 let dst = match &args[1] {
7033 Value::String(s) => s.to_string(),
7034 _ => {
7035 return Err(RuntimeError::new(
7036 "fs_copy() requires string destination path",
7037 ))
7038 }
7039 };
7040
7041 match std::fs::copy(&src, &dst) {
7042 Ok(bytes) => Ok(Value::Int(bytes as i64)),
7043 Err(e) => Err(RuntimeError::new(format!("fs_copy() error: {}", e))),
7044 }
7045 });
7046
7047 define(interp, "fs_rename", Some(2), |_, args| {
7049 let src = match &args[0] {
7050 Value::String(s) => s.to_string(),
7051 _ => return Err(RuntimeError::new("fs_rename() requires string source path")),
7052 };
7053 let dst = match &args[1] {
7054 Value::String(s) => s.to_string(),
7055 _ => {
7056 return Err(RuntimeError::new(
7057 "fs_rename() requires string destination path",
7058 ))
7059 }
7060 };
7061
7062 match std::fs::rename(&src, &dst) {
7063 Ok(()) => Ok(Value::Null),
7064 Err(e) => Err(RuntimeError::new(format!("fs_rename() error: {}", e))),
7065 }
7066 });
7067
7068 define(interp, "fs_size", Some(1), |_, args| {
7070 let path = match &args[0] {
7071 Value::String(s) => s.to_string(),
7072 _ => return Err(RuntimeError::new("fs_size() requires string path")),
7073 };
7074
7075 match std::fs::metadata(&path) {
7076 Ok(meta) => Ok(Value::Int(meta.len() as i64)),
7077 Err(e) => Err(RuntimeError::new(format!("fs_size() error: {}", e))),
7078 }
7079 });
7080
7081 define(interp, "path_join", None, |_, args| {
7083 let mut path = std::path::PathBuf::new();
7084 for arg in &args {
7085 match arg {
7086 Value::String(s) => path.push(s.as_str()),
7087 Value::Array(arr) => {
7088 for v in arr.borrow().iter() {
7089 if let Value::String(s) = v {
7090 path.push(s.as_str());
7091 }
7092 }
7093 }
7094 _ => {}
7095 }
7096 }
7097 Ok(Value::String(Rc::new(path.to_string_lossy().to_string())))
7098 });
7099
7100 define(interp, "path_parent", Some(1), |_, args| {
7102 let path = match &args[0] {
7103 Value::String(s) => s.to_string(),
7104 _ => return Err(RuntimeError::new("path_parent() requires string path")),
7105 };
7106
7107 let p = std::path::Path::new(&path);
7108 match p.parent() {
7109 Some(parent) => Ok(Value::String(Rc::new(parent.to_string_lossy().to_string()))),
7110 None => Ok(Value::Null),
7111 }
7112 });
7113
7114 define(interp, "path_filename", Some(1), |_, args| {
7116 let path = match &args[0] {
7117 Value::String(s) => s.to_string(),
7118 _ => return Err(RuntimeError::new("path_filename() requires string path")),
7119 };
7120
7121 let p = std::path::Path::new(&path);
7122 match p.file_name() {
7123 Some(name) => Ok(Value::String(Rc::new(name.to_string_lossy().to_string()))),
7124 None => Ok(Value::Null),
7125 }
7126 });
7127
7128 define(interp, "path_extension", Some(1), |_, args| {
7130 let path = match &args[0] {
7131 Value::String(s) => s.to_string(),
7132 _ => return Err(RuntimeError::new("path_extension() requires string path")),
7133 };
7134
7135 let p = std::path::Path::new(&path);
7136 match p.extension() {
7137 Some(ext) => Ok(Value::String(Rc::new(ext.to_string_lossy().to_string()))),
7138 None => Ok(Value::Null),
7139 }
7140 });
7141
7142 use std::cell::RefCell;
7149 use std::collections::HashMap;
7150 thread_local! {
7151 static LAST_FILE_CONTENT: RefCell<String> = RefCell::new(String::new());
7152 static FAKE_PTR_MAP: RefCell<HashMap<i64, String>> = RefCell::new(HashMap::new());
7154 }
7155
7156 define(interp, "sigil_read_file", Some(2), |_, args| {
7160 let path = match &args[0] {
7162 Value::String(s) => s.to_string(),
7163 Value::Int(ptr_id) => {
7164 FAKE_PTR_MAP.with(|map| {
7166 map.borrow().get(ptr_id).cloned()
7167 }).ok_or_else(|| RuntimeError::new(format!(
7168 "sigil_read_file: invalid pointer {}", ptr_id
7169 )))?
7170 }
7171 _ => return Err(RuntimeError::new("sigil_read_file() requires string path")),
7172 };
7173
7174 match std::fs::read_to_string(&path) {
7175 Ok(content) => {
7176 LAST_FILE_CONTENT.with(|last| {
7178 *last.borrow_mut() = content.clone();
7179 });
7180 Ok(Value::String(Rc::new(content)))
7182 }
7183 Err(_) => Ok(Value::Null), }
7185 });
7186
7187 define(interp, "sigil_file_len", Some(0), |_, _| {
7189 LAST_FILE_CONTENT.with(|last| {
7190 Ok(Value::Int(last.borrow().len() as i64))
7191 })
7192 });
7193
7194 define(interp, "sigil_write_file", Some(4), |_, args| {
7196 let path = match &args[0] {
7198 Value::String(s) => s.to_string(),
7199 _ => return Err(RuntimeError::new("sigil_write_file() requires string path")),
7200 };
7201 let content = match &args[2] {
7202 Value::String(s) => s.to_string(),
7203 _ => return Err(RuntimeError::new("sigil_write_file() requires string content")),
7204 };
7205
7206 match std::fs::write(&path, content) {
7207 Ok(()) => Ok(Value::Bool(true)),
7208 Err(_) => Ok(Value::Bool(false)),
7209 }
7210 });
7211
7212 define(interp, "write", Some(3), |_, args| {
7214 let fd = match &args[0] {
7215 Value::Int(n) => *n,
7216 _ => return Err(RuntimeError::new("write() requires int fd")),
7217 };
7218
7219 let content = match &args[1] {
7221 Value::String(s) => s.to_string(),
7222 Value::Int(ptr_id) => {
7223 FAKE_PTR_MAP.with(|map| {
7225 map.borrow().get(ptr_id).cloned()
7226 }).unwrap_or_else(|| format!("{}", ptr_id))
7227 }
7228 _ => format!("{}", args[1]),
7229 };
7230
7231 let len = match &args[2] {
7233 Value::Int(n) => *n as usize,
7234 _ => content.len(),
7235 };
7236
7237 let output = &content[..std::cmp::min(len, content.len())];
7238
7239 match fd {
7240 1 => {
7241 print!("{}", output);
7242 use std::io::Write;
7243 std::io::stdout().flush().ok();
7244 Ok(Value::Int(output.len() as i64))
7245 }
7246 2 => {
7247 eprint!("{}", output);
7248 use std::io::Write;
7249 std::io::stderr().flush().ok();
7250 Ok(Value::Int(output.len() as i64))
7251 }
7252 _ => Err(RuntimeError::new(format!("write() unsupported fd: {}", fd))),
7253 }
7254 });
7255
7256 define(interp, "PathBuf·from", Some(1), |_, args| {
7258 let path = match &args[0] {
7259 Value::String(s) => s.to_string(),
7260 Value::Ref(r) => {
7261 if let Value::String(s) = &*r.borrow() {
7262 s.to_string()
7263 } else {
7264 return Err(RuntimeError::new("PathBuf::from() requires string"));
7265 }
7266 }
7267 _ => return Err(RuntimeError::new("PathBuf::from() requires string")),
7268 };
7269 Ok(Value::String(Rc::new(path)))
7270 });
7271
7272 define(interp, "std·path·PathBuf·from", Some(1), |_, args| {
7274 let path = match &args[0] {
7275 Value::String(s) => s.to_string(),
7276 Value::Ref(r) => {
7277 if let Value::String(s) = &*r.borrow() {
7278 s.to_string()
7279 } else {
7280 return Err(RuntimeError::new("PathBuf::from() requires string"));
7281 }
7282 }
7283 _ => return Err(RuntimeError::new("PathBuf::from() requires string")),
7284 };
7285 Ok(Value::String(Rc::new(path)))
7286 });
7287
7288 define(interp, "Path·new", Some(1), |_, args| {
7290 let path = match &args[0] {
7291 Value::String(s) => s.to_string(),
7292 Value::Ref(r) => {
7293 if let Value::String(s) = &*r.borrow() {
7294 s.to_string()
7295 } else {
7296 return Err(RuntimeError::new("Path::new() requires string"));
7297 }
7298 }
7299 _ => return Err(RuntimeError::new("Path::new() requires string")),
7300 };
7301 Ok(Value::String(Rc::new(path)))
7302 });
7303
7304 define(interp, "std·path·Path·new", Some(1), |_, args| {
7306 let path = match &args[0] {
7307 Value::String(s) => s.to_string(),
7308 _ => return Err(RuntimeError::new("Path::new() requires string")),
7309 };
7310 Ok(Value::String(Rc::new(path)))
7311 });
7312
7313 define(interp, "std·fs·read_to_string", Some(1), |_, args| {
7315 let path = match &args[0] {
7316 Value::String(s) => s.to_string(),
7317 _ => return Err(RuntimeError::new("read_to_string() requires string path")),
7318 };
7319 match std::fs::read_to_string(&path) {
7320 Ok(content) => Ok(Value::String(Rc::new(content))),
7321 Err(e) => Err(RuntimeError::new(format!("read_to_string() error: {}", e))),
7322 }
7323 });
7324
7325 define(interp, "std·fs·write", Some(2), |_, args| {
7327 let path = match &args[0] {
7328 Value::String(s) => s.to_string(),
7329 _ => return Err(RuntimeError::new("fs::write() requires string path")),
7330 };
7331 let content = format!("{}", args[1]);
7332 match std::fs::write(&path, content) {
7333 Ok(()) => Ok(Value::Null),
7334 Err(e) => Err(RuntimeError::new(format!("fs::write() error: {}", e))),
7335 }
7336 });
7337
7338 define(interp, "std·fs·create_dir_all", Some(1), |_, args| {
7340 let path = match &args[0] {
7341 Value::String(s) => s.to_string(),
7342 _ => return Err(RuntimeError::new("create_dir_all() requires string path")),
7343 };
7344 match std::fs::create_dir_all(&path) {
7345 Ok(()) => Ok(Value::Null),
7346 Err(e) => Err(RuntimeError::new(format!("create_dir_all() error: {}", e))),
7347 }
7348 });
7349
7350 define(interp, "OpenOptions·new", Some(0), |_, _| {
7353 let mut opts = HashMap::new();
7354 opts.insert("read".to_string(), Value::Bool(false));
7355 opts.insert("write".to_string(), Value::Bool(false));
7356 opts.insert("append".to_string(), Value::Bool(false));
7357 opts.insert("truncate".to_string(), Value::Bool(false));
7358 opts.insert("create".to_string(), Value::Bool(false));
7359 opts.insert("create_new".to_string(), Value::Bool(false));
7360 opts.insert("__type__".to_string(), Value::String(Rc::new("OpenOptions".to_string())));
7361 Ok(Value::Map(Rc::new(RefCell::new(opts))))
7362 });
7363
7364 define(interp, "std·fs·OpenOptions·new", Some(0), |_, _| {
7366 let mut opts = HashMap::new();
7367 opts.insert("read".to_string(), Value::Bool(false));
7368 opts.insert("write".to_string(), Value::Bool(false));
7369 opts.insert("append".to_string(), Value::Bool(false));
7370 opts.insert("truncate".to_string(), Value::Bool(false));
7371 opts.insert("create".to_string(), Value::Bool(false));
7372 opts.insert("create_new".to_string(), Value::Bool(false));
7373 opts.insert("__type__".to_string(), Value::String(Rc::new("OpenOptions".to_string())));
7374 Ok(Value::Map(Rc::new(RefCell::new(opts))))
7375 });
7376
7377 define(interp, "File·create", Some(1), |_, args| {
7379 let path = match &args[0] {
7380 Value::String(s) => s.to_string(),
7381 _ => return Err(RuntimeError::new("File::create() requires string path")),
7382 };
7383 let mut handle = HashMap::new();
7385 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7386 handle.insert("mode".to_string(), Value::String(Rc::new("write".to_string())));
7387 handle.insert("__type__".to_string(), Value::String(Rc::new("File".to_string())));
7388 match std::fs::File::create(&path) {
7390 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7391 Err(e) => Err(RuntimeError::new(format!("File::create() error: {}", e))),
7392 }
7393 });
7394
7395 define(interp, "std·fs·File·create", Some(1), |_, args| {
7397 let path = match &args[0] {
7398 Value::String(s) => s.to_string(),
7399 _ => return Err(RuntimeError::new("File::create() requires string path")),
7400 };
7401 let mut handle = HashMap::new();
7402 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7403 handle.insert("mode".to_string(), Value::String(Rc::new("write".to_string())));
7404 handle.insert("__type__".to_string(), Value::String(Rc::new("File".to_string())));
7405 match std::fs::File::create(&path) {
7406 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7407 Err(e) => Err(RuntimeError::new(format!("File::create() error: {}", e))),
7408 }
7409 });
7410
7411 define(interp, "File·open", Some(1), |_, args| {
7413 let path = match &args[0] {
7414 Value::String(s) => s.to_string(),
7415 _ => return Err(RuntimeError::new("File::open() requires string path")),
7416 };
7417 let mut handle = HashMap::new();
7418 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7419 handle.insert("mode".to_string(), Value::String(Rc::new("read".to_string())));
7420 handle.insert("__type__".to_string(), Value::String(Rc::new("File".to_string())));
7421 match std::fs::File::open(&path) {
7422 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7423 Err(e) => Err(RuntimeError::new(format!("File::open() error: {}", e))),
7424 }
7425 });
7426
7427 define(interp, "std·fs·File·open", Some(1), |_, args| {
7429 let path = match &args[0] {
7430 Value::String(s) => s.to_string(),
7431 _ => return Err(RuntimeError::new("File::open() requires string path")),
7432 };
7433 let mut handle = HashMap::new();
7434 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7435 handle.insert("mode".to_string(), Value::String(Rc::new("read".to_string())));
7436 handle.insert("__type__".to_string(), Value::String(Rc::new("File".to_string())));
7437 match std::fs::File::open(&path) {
7438 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7439 Err(e) => Err(RuntimeError::new(format!("File::open() error: {}", e))),
7440 }
7441 });
7442
7443 define(interp, "BufWriter·new", Some(1), |_, args| {
7445 match &args[0] {
7448 Value::Map(file_map) => {
7449 let mut wrapper = HashMap::new();
7450 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7451 wrapper.insert("buffer".to_string(), Value::Array(Rc::new(RefCell::new(Vec::new()))));
7452 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufWriter".to_string())));
7453 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7454 }
7455 _ => Err(RuntimeError::new("BufWriter::new requires a file handle")),
7456 }
7457 });
7458
7459 define(interp, "std·io·BufWriter·new", Some(1), |_, args| {
7461 match &args[0] {
7462 Value::Map(file_map) => {
7463 let mut wrapper = HashMap::new();
7464 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7465 wrapper.insert("buffer".to_string(), Value::Array(Rc::new(RefCell::new(Vec::new()))));
7466 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufWriter".to_string())));
7467 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7468 }
7469 _ => Err(RuntimeError::new("BufWriter::new requires a file handle")),
7470 }
7471 });
7472
7473 define(interp, "BufReader·new", Some(1), |_, args| {
7475 use std::io::BufReader as StdBufReader;
7476
7477 let get_map = |val: &Value| -> Option<Rc<RefCell<HashMap<String, Value>>>> {
7479 match val {
7480 Value::Map(m) => Some(m.clone()),
7481 Value::Ref(r) => {
7482 let inner = r.borrow();
7483 if let Value::Map(m) = &*inner {
7484 Some(m.clone())
7485 } else {
7486 None
7487 }
7488 }
7489 _ => None,
7490 }
7491 };
7492
7493 if let Some(file_map) = get_map(&args[0]) {
7494 let borrowed = file_map.borrow();
7495 let mut wrapper = HashMap::new();
7496
7497 if let Some(Value::String(t)) = borrowed.get("__type__") {
7499 if t.as_str() == "TcpStream" {
7500 if let Some(Value::Int(stream_id)) = borrowed.get("__stream_id__") {
7501 let stream_id_val = *stream_id as u64;
7502 drop(borrowed);
7503
7504 if let Some(mut guard) = get_stream_registry().lock().ok() {
7506 if let Some(stream) = guard.get_mut(&stream_id_val) {
7507 let stream_clone = match stream.try_clone() {
7508 Ok(s) => s,
7509 Err(e) => return Err(RuntimeError::new(format!("Failed to clone stream: {}", e))),
7510 };
7511 let reader = StdBufReader::new(stream_clone);
7512 let reader_id = store_bufreader(reader);
7513
7514 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufReader".to_string())));
7515 wrapper.insert("__stream_id__".to_string(), Value::Int(stream_id_val as i64));
7516 wrapper.insert("__reader_id__".to_string(), Value::Int(reader_id as i64));
7517 return Ok(Value::Map(Rc::new(RefCell::new(wrapper))));
7518 }
7519 }
7520 return Err(RuntimeError::new("TcpStream not found in registry"));
7521 }
7522 }
7523 }
7524
7525 drop(borrowed);
7527 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7528 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufReader".to_string())));
7529 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7530 } else {
7531 Err(RuntimeError::new("BufReader::new requires a file handle or TcpStream"))
7532 }
7533 });
7534
7535 define(interp, "std·io·BufReader·new", Some(1), |_, args| {
7537 let get_map = |val: &Value| -> Option<Rc<RefCell<HashMap<String, Value>>>> {
7539 match val {
7540 Value::Map(m) => Some(m.clone()),
7541 Value::Ref(r) => {
7542 let inner = r.borrow();
7543 if let Value::Map(m) = &*inner {
7544 Some(m.clone())
7545 } else {
7546 None
7547 }
7548 }
7549 _ => None,
7550 }
7551 };
7552
7553 if let Some(file_map) = get_map(&args[0]) {
7554 let borrowed = file_map.borrow();
7555 let mut wrapper = HashMap::new();
7556
7557 if let Some(Value::String(t)) = borrowed.get("__type__") {
7559 if t.as_str() == "TcpStream" {
7560 if let Some(Value::Int(stream_id)) = borrowed.get("__stream_id__") {
7561 wrapper.insert("__stream_id__".to_string(), Value::Int(*stream_id));
7562 }
7563 }
7564 }
7565
7566 drop(borrowed);
7567 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7568 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufReader".to_string())));
7569 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7570 } else {
7571 Err(RuntimeError::new("BufReader::new requires a file handle or TcpStream"))
7572 }
7573 });
7574
7575 define(interp, "dirs_next·config_dir", Some(0), |_, _| {
7577 match dirs::config_dir() {
7578 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7579 None => Ok(Value::Null),
7580 }
7581 });
7582
7583 define(interp, "dirs_next·data_dir", Some(0), |_, _| {
7585 match dirs::data_dir() {
7586 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7587 None => Ok(Value::Null),
7588 }
7589 });
7590
7591 define(interp, "dirs_next·home_dir", Some(0), |_, _| {
7593 match dirs::home_dir() {
7594 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7595 None => Ok(Value::Null),
7596 }
7597 });
7598}
7599
7600fn register_crypto(interp: &mut Interpreter) {
7632 fn extract_bytes(v: &Value, fn_name: &str) -> Result<Vec<u8>, RuntimeError> {
7634 match v {
7635 Value::String(s) => Ok(s.as_bytes().to_vec()),
7636 Value::Array(arr) => {
7637 let arr = arr.borrow();
7638 Ok(arr
7639 .iter()
7640 .filter_map(|v| {
7641 if let Value::Int(n) = v {
7642 Some(*n as u8)
7643 } else {
7644 None
7645 }
7646 })
7647 .collect())
7648 }
7649 _ => Err(RuntimeError::new(format!(
7650 "{}() requires string or byte array",
7651 fn_name
7652 ))),
7653 }
7654 }
7655
7656 fn bytes_to_array(bytes: &[u8]) -> Value {
7657 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
7658 Value::Array(Rc::new(RefCell::new(values)))
7659 }
7660
7661 define(interp, "sha256", Some(1), |_, args| {
7667 let data = extract_bytes(&args[0], "sha256")?;
7668 let mut hasher = Sha256::new();
7669 hasher.update(&data);
7670 let result = hasher.finalize();
7671 Ok(Value::String(Rc::new(
7672 result.iter().map(|b| format!("{:02x}", b)).collect(),
7673 )))
7674 });
7675
7676 define(interp, "sha512", Some(1), |_, args| {
7678 let data = extract_bytes(&args[0], "sha512")?;
7679 let mut hasher = Sha512::new();
7680 hasher.update(&data);
7681 let result = hasher.finalize();
7682 Ok(Value::String(Rc::new(
7683 result.iter().map(|b| format!("{:02x}", b)).collect(),
7684 )))
7685 });
7686
7687 define(interp, "sha3_256", Some(1), |_, args| {
7689 use sha3::{Digest as Sha3Digest, Sha3_256};
7690 let data = extract_bytes(&args[0], "sha3_256")?;
7691 let mut hasher = Sha3_256::new();
7692 hasher.update(&data);
7693 let result = hasher.finalize();
7694 Ok(Value::String(Rc::new(
7695 result.iter().map(|b| format!("{:02x}", b)).collect(),
7696 )))
7697 });
7698
7699 define(interp, "sha3_512", Some(1), |_, args| {
7701 use sha3::{Digest as Sha3Digest, Sha3_512};
7702 let data = extract_bytes(&args[0], "sha3_512")?;
7703 let mut hasher = Sha3_512::new();
7704 hasher.update(&data);
7705 let result = hasher.finalize();
7706 Ok(Value::String(Rc::new(
7707 result.iter().map(|b| format!("{:02x}", b)).collect(),
7708 )))
7709 });
7710
7711 define(interp, "blake3", Some(1), |_, args| {
7713 let data = extract_bytes(&args[0], "blake3")?;
7714 let hash = blake3::hash(&data);
7715 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
7716 });
7717
7718 define(interp, "blake3_keyed", Some(2), |_, args| {
7720 let key = extract_bytes(&args[0], "blake3_keyed")?;
7721 let data = extract_bytes(&args[1], "blake3_keyed")?;
7722 if key.len() != 32 {
7723 return Err(RuntimeError::new("blake3_keyed() requires 32-byte key"));
7724 }
7725 let mut key_arr = [0u8; 32];
7726 key_arr.copy_from_slice(&key);
7727 let hash = blake3::keyed_hash(&key_arr, &data);
7728 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
7729 });
7730
7731 define(interp, "md5", Some(1), |_, args| {
7733 let data = extract_bytes(&args[0], "md5")?;
7734 let mut hasher = Md5::new();
7735 hasher.update(&data);
7736 let result = hasher.finalize();
7737 Ok(Value::String(Rc::new(
7738 result.iter().map(|b| format!("{:02x}", b)).collect(),
7739 )))
7740 });
7741
7742 define(interp, "aes_gcm_encrypt", Some(2), |_, args| {
7748 use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
7749 use rand::RngCore;
7750
7751 let key = extract_bytes(&args[0], "aes_gcm_encrypt")?;
7752 let plaintext = extract_bytes(&args[1], "aes_gcm_encrypt")?;
7753
7754 if key.len() != 32 {
7755 return Err(RuntimeError::new("aes_gcm_encrypt() requires 32-byte key"));
7756 }
7757
7758 let cipher = Aes256Gcm::new_from_slice(&key)
7759 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
7760
7761 let mut nonce_bytes = [0u8; 12];
7762 rand::thread_rng().fill_bytes(&mut nonce_bytes);
7763 let nonce = Nonce::from_slice(&nonce_bytes);
7764
7765 let ciphertext = cipher
7766 .encrypt(nonce, plaintext.as_ref())
7767 .map_err(|e| RuntimeError::new(format!("AES encryption error: {}", e)))?;
7768
7769 let mut result = HashMap::new();
7770 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
7771 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
7772 Ok(Value::Map(Rc::new(RefCell::new(result))))
7773 });
7774
7775 define(interp, "aes_gcm_decrypt", Some(3), |_, args| {
7777 use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
7778
7779 let key = extract_bytes(&args[0], "aes_gcm_decrypt")?;
7780 let ciphertext = extract_bytes(&args[1], "aes_gcm_decrypt")?;
7781 let nonce_bytes = extract_bytes(&args[2], "aes_gcm_decrypt")?;
7782
7783 if key.len() != 32 {
7784 return Err(RuntimeError::new("aes_gcm_decrypt() requires 32-byte key"));
7785 }
7786 if nonce_bytes.len() != 12 {
7787 return Err(RuntimeError::new(
7788 "aes_gcm_decrypt() requires 12-byte nonce",
7789 ));
7790 }
7791
7792 let cipher = Aes256Gcm::new_from_slice(&key)
7793 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
7794 let nonce = Nonce::from_slice(&nonce_bytes);
7795
7796 let plaintext = cipher
7797 .decrypt(nonce, ciphertext.as_ref())
7798 .map_err(|_| RuntimeError::new("AES-GCM decryption failed: authentication error"))?;
7799
7800 match String::from_utf8(plaintext.clone()) {
7801 Ok(s) => Ok(Value::String(Rc::new(s))),
7802 Err(_) => Ok(bytes_to_array(&plaintext)),
7803 }
7804 });
7805
7806 define(interp, "chacha20_encrypt", Some(2), |_, args| {
7808 use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
7809 use rand::RngCore;
7810
7811 let key = extract_bytes(&args[0], "chacha20_encrypt")?;
7812 let plaintext = extract_bytes(&args[1], "chacha20_encrypt")?;
7813
7814 if key.len() != 32 {
7815 return Err(RuntimeError::new("chacha20_encrypt() requires 32-byte key"));
7816 }
7817
7818 let cipher = ChaCha20Poly1305::new_from_slice(&key)
7819 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
7820
7821 let mut nonce_bytes = [0u8; 12];
7822 rand::thread_rng().fill_bytes(&mut nonce_bytes);
7823 let nonce = Nonce::from_slice(&nonce_bytes);
7824
7825 let ciphertext = cipher
7826 .encrypt(nonce, plaintext.as_ref())
7827 .map_err(|e| RuntimeError::new(format!("ChaCha20 encryption error: {}", e)))?;
7828
7829 let mut result = HashMap::new();
7830 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
7831 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
7832 Ok(Value::Map(Rc::new(RefCell::new(result))))
7833 });
7834
7835 define(interp, "chacha20_decrypt", Some(3), |_, args| {
7837 use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
7838
7839 let key = extract_bytes(&args[0], "chacha20_decrypt")?;
7840 let ciphertext = extract_bytes(&args[1], "chacha20_decrypt")?;
7841 let nonce_bytes = extract_bytes(&args[2], "chacha20_decrypt")?;
7842
7843 if key.len() != 32 {
7844 return Err(RuntimeError::new("chacha20_decrypt() requires 32-byte key"));
7845 }
7846 if nonce_bytes.len() != 12 {
7847 return Err(RuntimeError::new(
7848 "chacha20_decrypt() requires 12-byte nonce",
7849 ));
7850 }
7851
7852 let cipher = ChaCha20Poly1305::new_from_slice(&key)
7853 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
7854 let nonce = Nonce::from_slice(&nonce_bytes);
7855
7856 let plaintext = cipher
7857 .decrypt(nonce, ciphertext.as_ref())
7858 .map_err(|_| RuntimeError::new("ChaCha20 decryption failed: authentication error"))?;
7859
7860 match String::from_utf8(plaintext.clone()) {
7861 Ok(s) => Ok(Value::String(Rc::new(s))),
7862 Err(_) => Ok(bytes_to_array(&plaintext)),
7863 }
7864 });
7865
7866 define(interp, "ed25519_keygen", Some(0), |_, _| {
7872 use ed25519_dalek::SigningKey;
7873 use rand::rngs::OsRng;
7874
7875 let signing_key = SigningKey::generate(&mut OsRng);
7876 let verifying_key = signing_key.verifying_key();
7877
7878 let mut result = HashMap::new();
7879 result.insert(
7880 "private_key".to_string(),
7881 Value::String(Rc::new(
7882 signing_key
7883 .to_bytes()
7884 .iter()
7885 .map(|b| format!("{:02x}", b))
7886 .collect(),
7887 )),
7888 );
7889 result.insert(
7890 "public_key".to_string(),
7891 Value::String(Rc::new(
7892 verifying_key
7893 .to_bytes()
7894 .iter()
7895 .map(|b| format!("{:02x}", b))
7896 .collect(),
7897 )),
7898 );
7899 Ok(Value::Map(Rc::new(RefCell::new(result))))
7900 });
7901
7902 define(interp, "ed25519_sign", Some(2), |_, args| {
7904 use ed25519_dalek::{Signer, SigningKey};
7905
7906 let private_key_hex = match &args[0] {
7907 Value::String(s) => s.to_string(),
7908 _ => return Err(RuntimeError::new("ed25519_sign() requires hex private key")),
7909 };
7910 let message = extract_bytes(&args[1], "ed25519_sign")?;
7911
7912 let key_bytes: Vec<u8> = (0..private_key_hex.len())
7913 .step_by(2)
7914 .map(|i| u8::from_str_radix(&private_key_hex[i..i + 2], 16))
7915 .collect::<Result<Vec<_>, _>>()
7916 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
7917
7918 if key_bytes.len() != 32 {
7919 return Err(RuntimeError::new(
7920 "ed25519_sign() requires 32-byte private key",
7921 ));
7922 }
7923
7924 let mut key_arr = [0u8; 32];
7925 key_arr.copy_from_slice(&key_bytes);
7926 let signing_key = SigningKey::from_bytes(&key_arr);
7927 let signature = signing_key.sign(&message);
7928
7929 Ok(Value::String(Rc::new(
7930 signature
7931 .to_bytes()
7932 .iter()
7933 .map(|b| format!("{:02x}", b))
7934 .collect(),
7935 )))
7936 });
7937
7938 define(interp, "ed25519_verify", Some(3), |_, args| {
7940 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
7941
7942 let public_key_hex = match &args[0] {
7943 Value::String(s) => s.to_string(),
7944 _ => {
7945 return Err(RuntimeError::new(
7946 "ed25519_verify() requires hex public key",
7947 ))
7948 }
7949 };
7950 let message = extract_bytes(&args[1], "ed25519_verify")?;
7951 let signature_hex = match &args[2] {
7952 Value::String(s) => s.to_string(),
7953 _ => return Err(RuntimeError::new("ed25519_verify() requires hex signature")),
7954 };
7955
7956 let key_bytes: Vec<u8> = (0..public_key_hex.len())
7957 .step_by(2)
7958 .map(|i| u8::from_str_radix(&public_key_hex[i..i + 2], 16))
7959 .collect::<Result<Vec<_>, _>>()
7960 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
7961 let sig_bytes: Vec<u8> = (0..signature_hex.len())
7962 .step_by(2)
7963 .map(|i| u8::from_str_radix(&signature_hex[i..i + 2], 16))
7964 .collect::<Result<Vec<_>, _>>()
7965 .map_err(|_| RuntimeError::new("Invalid signature hex"))?;
7966
7967 if key_bytes.len() != 32 {
7968 return Err(RuntimeError::new(
7969 "ed25519_verify() requires 32-byte public key",
7970 ));
7971 }
7972 if sig_bytes.len() != 64 {
7973 return Err(RuntimeError::new(
7974 "ed25519_verify() requires 64-byte signature",
7975 ));
7976 }
7977
7978 let mut key_arr = [0u8; 32];
7979 key_arr.copy_from_slice(&key_bytes);
7980 let mut sig_arr = [0u8; 64];
7981 sig_arr.copy_from_slice(&sig_bytes);
7982
7983 let verifying_key = VerifyingKey::from_bytes(&key_arr)
7984 .map_err(|e| RuntimeError::new(format!("Invalid public key: {}", e)))?;
7985 let signature = Signature::from_bytes(&sig_arr);
7986
7987 match verifying_key.verify(&message, &signature) {
7988 Ok(_) => Ok(Value::Bool(true)),
7989 Err(_) => Ok(Value::Bool(false)),
7990 }
7991 });
7992
7993 define(interp, "x25519_keygen", Some(0), |_, _| {
7995 use rand::rngs::OsRng;
7996 use x25519_dalek::{PublicKey, StaticSecret};
7997
7998 let secret = StaticSecret::random_from_rng(OsRng);
7999 let public = PublicKey::from(&secret);
8000
8001 let mut result = HashMap::new();
8002 result.insert(
8003 "private_key".to_string(),
8004 Value::String(Rc::new(
8005 secret
8006 .as_bytes()
8007 .iter()
8008 .map(|b| format!("{:02x}", b))
8009 .collect(),
8010 )),
8011 );
8012 result.insert(
8013 "public_key".to_string(),
8014 Value::String(Rc::new(
8015 public
8016 .as_bytes()
8017 .iter()
8018 .map(|b| format!("{:02x}", b))
8019 .collect(),
8020 )),
8021 );
8022 Ok(Value::Map(Rc::new(RefCell::new(result))))
8023 });
8024
8025 define(interp, "x25519_exchange", Some(2), |_, args| {
8027 use x25519_dalek::{PublicKey, StaticSecret};
8028
8029 let my_private_hex = match &args[0] {
8030 Value::String(s) => s.to_string(),
8031 _ => {
8032 return Err(RuntimeError::new(
8033 "x25519_exchange() requires hex private key",
8034 ))
8035 }
8036 };
8037 let their_public_hex = match &args[1] {
8038 Value::String(s) => s.to_string(),
8039 _ => {
8040 return Err(RuntimeError::new(
8041 "x25519_exchange() requires hex public key",
8042 ))
8043 }
8044 };
8045
8046 let my_private_bytes: Vec<u8> = (0..my_private_hex.len())
8047 .step_by(2)
8048 .map(|i| u8::from_str_radix(&my_private_hex[i..i + 2], 16))
8049 .collect::<Result<Vec<_>, _>>()
8050 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
8051 let their_public_bytes: Vec<u8> = (0..their_public_hex.len())
8052 .step_by(2)
8053 .map(|i| u8::from_str_radix(&their_public_hex[i..i + 2], 16))
8054 .collect::<Result<Vec<_>, _>>()
8055 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
8056
8057 if my_private_bytes.len() != 32 || their_public_bytes.len() != 32 {
8058 return Err(RuntimeError::new("x25519_exchange() requires 32-byte keys"));
8059 }
8060
8061 let mut priv_arr = [0u8; 32];
8062 priv_arr.copy_from_slice(&my_private_bytes);
8063 let mut pub_arr = [0u8; 32];
8064 pub_arr.copy_from_slice(&their_public_bytes);
8065
8066 let my_secret = StaticSecret::from(priv_arr);
8067 let their_public = PublicKey::from(pub_arr);
8068 let shared_secret = my_secret.diffie_hellman(&their_public);
8069
8070 Ok(Value::String(Rc::new(
8071 shared_secret
8072 .as_bytes()
8073 .iter()
8074 .map(|b| format!("{:02x}", b))
8075 .collect(),
8076 )))
8077 });
8078
8079 define(interp, "argon2_hash", Some(1), |_, args| {
8085 use argon2::{
8086 password_hash::{PasswordHasher, SaltString},
8087 Argon2,
8088 };
8089 use rand::rngs::OsRng;
8090
8091 let password = extract_bytes(&args[0], "argon2_hash")?;
8092 let salt = SaltString::generate(&mut OsRng);
8093 let argon2 = Argon2::default();
8094
8095 let hash = argon2
8096 .hash_password(&password, &salt)
8097 .map_err(|e| RuntimeError::new(format!("Argon2 error: {}", e)))?;
8098
8099 let mut result = HashMap::new();
8100 result.insert("hash".to_string(), Value::String(Rc::new(hash.to_string())));
8101 result.insert("salt".to_string(), Value::String(Rc::new(salt.to_string())));
8102 Ok(Value::Map(Rc::new(RefCell::new(result))))
8103 });
8104
8105 define(interp, "argon2_verify", Some(2), |_, args| {
8107 use argon2::{Argon2, PasswordHash, PasswordVerifier};
8108
8109 let password = extract_bytes(&args[0], "argon2_verify")?;
8110 let hash_str = match &args[1] {
8111 Value::String(s) => s.to_string(),
8112 _ => return Err(RuntimeError::new("argon2_verify() requires hash string")),
8113 };
8114
8115 let parsed_hash = PasswordHash::new(&hash_str)
8116 .map_err(|e| RuntimeError::new(format!("Invalid hash: {}", e)))?;
8117
8118 match Argon2::default().verify_password(&password, &parsed_hash) {
8119 Ok(_) => Ok(Value::Bool(true)),
8120 Err(_) => Ok(Value::Bool(false)),
8121 }
8122 });
8123
8124 define(interp, "hkdf_expand", Some(3), |_, args| {
8126 use hkdf::Hkdf;
8127
8128 let ikm = extract_bytes(&args[0], "hkdf_expand")?;
8129 let salt = extract_bytes(&args[1], "hkdf_expand")?;
8130 let info = extract_bytes(&args[2], "hkdf_expand")?;
8131
8132 let hk = Hkdf::<Sha256>::new(Some(&salt), &ikm);
8133 let mut okm = [0u8; 32];
8134 hk.expand(&info, &mut okm)
8135 .map_err(|e| RuntimeError::new(format!("HKDF error: {}", e)))?;
8136
8137 Ok(Value::String(Rc::new(
8138 okm.iter().map(|b| format!("{:02x}", b)).collect(),
8139 )))
8140 });
8141
8142 define(interp, "pbkdf2_derive", Some(3), |_, args| {
8144 let password = extract_bytes(&args[0], "pbkdf2_derive")?;
8145 let salt = extract_bytes(&args[1], "pbkdf2_derive")?;
8146 let iterations = match &args[2] {
8147 Value::Int(n) => *n as u32,
8148 _ => {
8149 return Err(RuntimeError::new(
8150 "pbkdf2_derive() requires integer iterations",
8151 ))
8152 }
8153 };
8154
8155 let mut key = [0u8; 32];
8156 pbkdf2::pbkdf2_hmac::<Sha256>(&password, &salt, iterations, &mut key);
8157 Ok(Value::String(Rc::new(
8158 key.iter().map(|b| format!("{:02x}", b)).collect(),
8159 )))
8160 });
8161
8162 define(interp, "hmac_sha256", Some(2), |_, args| {
8168 use hmac::{Hmac, Mac};
8169 type HmacSha256 = Hmac<Sha256>;
8170
8171 let key = extract_bytes(&args[0], "hmac_sha256")?;
8172 let message = extract_bytes(&args[1], "hmac_sha256")?;
8173
8174 let mut mac = HmacSha256::new_from_slice(&key)
8175 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8176 mac.update(&message);
8177 let result = mac.finalize();
8178 Ok(Value::String(Rc::new(
8179 result
8180 .into_bytes()
8181 .iter()
8182 .map(|b| format!("{:02x}", b))
8183 .collect(),
8184 )))
8185 });
8186
8187 define(interp, "hmac_sha512", Some(2), |_, args| {
8189 use hmac::{Hmac, Mac};
8190 type HmacSha512 = Hmac<Sha512>;
8191
8192 let key = extract_bytes(&args[0], "hmac_sha512")?;
8193 let message = extract_bytes(&args[1], "hmac_sha512")?;
8194
8195 let mut mac = HmacSha512::new_from_slice(&key)
8196 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8197 mac.update(&message);
8198 let result = mac.finalize();
8199 Ok(Value::String(Rc::new(
8200 result
8201 .into_bytes()
8202 .iter()
8203 .map(|b| format!("{:02x}", b))
8204 .collect(),
8205 )))
8206 });
8207
8208 define(interp, "hmac_verify", Some(3), |_, args| {
8210 use hmac::{Hmac, Mac};
8211 type HmacSha256 = Hmac<Sha256>;
8212
8213 let key = extract_bytes(&args[0], "hmac_verify")?;
8214 let message = extract_bytes(&args[1], "hmac_verify")?;
8215 let expected_hex = match &args[2] {
8216 Value::String(s) => s.to_string(),
8217 _ => return Err(RuntimeError::new("hmac_verify() requires hex MAC")),
8218 };
8219
8220 let expected: Vec<u8> = (0..expected_hex.len())
8221 .step_by(2)
8222 .map(|i| u8::from_str_radix(&expected_hex[i..i + 2], 16))
8223 .collect::<Result<Vec<_>, _>>()
8224 .map_err(|_| RuntimeError::new("Invalid MAC hex"))?;
8225
8226 let mut mac = HmacSha256::new_from_slice(&key)
8227 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8228 mac.update(&message);
8229
8230 match mac.verify_slice(&expected) {
8231 Ok(_) => Ok(Value::Bool(true)),
8232 Err(_) => Ok(Value::Bool(false)),
8233 }
8234 });
8235
8236 define(interp, "secure_random_bytes", Some(1), |_, args| {
8242 use rand::RngCore;
8243
8244 let length = match &args[0] {
8245 Value::Int(n) => *n as usize,
8246 _ => {
8247 return Err(RuntimeError::new(
8248 "secure_random_bytes() requires integer length",
8249 ))
8250 }
8251 };
8252
8253 if length > 1024 * 1024 {
8254 return Err(RuntimeError::new("secure_random_bytes() max 1MB"));
8255 }
8256
8257 let mut bytes = vec![0u8; length];
8258 rand::thread_rng().fill_bytes(&mut bytes);
8259 Ok(bytes_to_array(&bytes))
8260 });
8261
8262 define(interp, "secure_random_hex", Some(1), |_, args| {
8264 use rand::RngCore;
8265
8266 let byte_length = match &args[0] {
8267 Value::Int(n) => *n as usize,
8268 _ => {
8269 return Err(RuntimeError::new(
8270 "secure_random_hex() requires integer length",
8271 ))
8272 }
8273 };
8274
8275 if byte_length > 1024 * 1024 {
8276 return Err(RuntimeError::new("secure_random_hex() max 1MB"));
8277 }
8278
8279 let mut bytes = vec![0u8; byte_length];
8280 rand::thread_rng().fill_bytes(&mut bytes);
8281 Ok(Value::String(Rc::new(
8282 bytes.iter().map(|b| format!("{:02x}", b)).collect(),
8283 )))
8284 });
8285
8286 define(interp, "generate_key", Some(1), |_, args| {
8288 use rand::RngCore;
8289
8290 let bits = match &args[0] {
8291 Value::Int(n) => *n as usize,
8292 _ => return Err(RuntimeError::new("generate_key() requires bit length")),
8293 };
8294
8295 if bits % 8 != 0 {
8296 return Err(RuntimeError::new(
8297 "generate_key() bit length must be multiple of 8",
8298 ));
8299 }
8300 if bits > 512 {
8301 return Err(RuntimeError::new("generate_key() max 512 bits"));
8302 }
8303
8304 let bytes = bits / 8;
8305 let mut key = vec![0u8; bytes];
8306 rand::thread_rng().fill_bytes(&mut key);
8307 Ok(Value::String(Rc::new(
8308 key.iter().map(|b| format!("{:02x}", b)).collect(),
8309 )))
8310 });
8311
8312 define(interp, "base64_encode", Some(1), |_, args| {
8318 let data = extract_bytes(&args[0], "base64_encode")?;
8319 Ok(Value::String(Rc::new(
8320 general_purpose::STANDARD.encode(&data),
8321 )))
8322 });
8323
8324 define(interp, "base64_decode", Some(1), |_, args| {
8326 let encoded = match &args[0] {
8327 Value::String(s) => s.to_string(),
8328 _ => return Err(RuntimeError::new("base64_decode() requires string")),
8329 };
8330
8331 match general_purpose::STANDARD.decode(&encoded) {
8332 Ok(bytes) => match String::from_utf8(bytes.clone()) {
8333 Ok(s) => Ok(Value::String(Rc::new(s))),
8334 Err(_) => Ok(bytes_to_array(&bytes)),
8335 },
8336 Err(e) => Err(RuntimeError::new(format!("base64_decode() error: {}", e))),
8337 }
8338 });
8339
8340 define(interp, "hex_encode", Some(1), |_, args| {
8342 let data = extract_bytes(&args[0], "hex_encode")?;
8343 Ok(Value::String(Rc::new(
8344 data.iter().map(|b| format!("{:02x}", b)).collect(),
8345 )))
8346 });
8347
8348 define(interp, "hex_decode", Some(1), |_, args| {
8350 let hex_str = match &args[0] {
8351 Value::String(s) => s.to_string(),
8352 _ => return Err(RuntimeError::new("hex_decode() requires string")),
8353 };
8354
8355 let hex_str = hex_str.trim();
8356 if hex_str.len() % 2 != 0 {
8357 return Err(RuntimeError::new(
8358 "hex_decode() requires even-length hex string",
8359 ));
8360 }
8361
8362 let bytes: Vec<Value> = (0..hex_str.len())
8363 .step_by(2)
8364 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).map(|b| Value::Int(b as i64)))
8365 .collect::<Result<Vec<_>, _>>()
8366 .map_err(|_| RuntimeError::new("hex_decode() invalid hex"))?;
8367 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
8368 });
8369
8370 define(interp, "constant_time_eq", Some(2), |_, args| {
8376 let a = extract_bytes(&args[0], "constant_time_eq")?;
8377 let b = extract_bytes(&args[1], "constant_time_eq")?;
8378
8379 if a.len() != b.len() {
8380 return Ok(Value::Bool(false));
8381 }
8382
8383 let mut result = 0u8;
8384 for (x, y) in a.iter().zip(b.iter()) {
8385 result |= x ^ y;
8386 }
8387 Ok(Value::Bool(result == 0))
8388 });
8389
8390 define(interp, "crypto_info", Some(0), |_, _| {
8396 let mut info = HashMap::new();
8397 info.insert(
8398 "version".to_string(),
8399 Value::String(Rc::new("2.0".to_string())),
8400 );
8401 info.insert(
8402 "phase".to_string(),
8403 Value::String(Rc::new("Evidential Cryptography".to_string())),
8404 );
8405
8406 let capabilities = vec![
8407 "sha256",
8408 "sha512",
8409 "sha3_256",
8410 "sha3_512",
8411 "blake3",
8412 "md5",
8413 "aes_gcm_encrypt",
8414 "aes_gcm_decrypt",
8415 "chacha20_encrypt",
8416 "chacha20_decrypt",
8417 "ed25519_keygen",
8418 "ed25519_sign",
8419 "ed25519_verify",
8420 "x25519_keygen",
8421 "x25519_exchange",
8422 "argon2_hash",
8423 "argon2_verify",
8424 "hkdf_expand",
8425 "pbkdf2_derive",
8426 "hmac_sha256",
8427 "hmac_sha512",
8428 "hmac_verify",
8429 "secure_random_bytes",
8430 "secure_random_hex",
8431 "generate_key",
8432 "base64_encode",
8433 "base64_decode",
8434 "hex_encode",
8435 "hex_decode",
8436 "constant_time_eq",
8437 ];
8438 let cap_values: Vec<Value> = capabilities
8439 .iter()
8440 .map(|s| Value::String(Rc::new(s.to_string())))
8441 .collect();
8442 info.insert(
8443 "functions".to_string(),
8444 Value::Array(Rc::new(RefCell::new(cap_values))),
8445 );
8446
8447 Ok(Value::Map(Rc::new(RefCell::new(info))))
8448 });
8449}
8450
8451fn register_regex(interp: &mut Interpreter) {
8456 define(interp, "regex_match", Some(2), |_, args| {
8458 let pattern = match &args[0] {
8459 Value::String(s) => s.to_string(),
8460 _ => return Err(RuntimeError::new("regex_match() requires string pattern")),
8461 };
8462 let text = match &args[1] {
8463 Value::String(s) => s.to_string(),
8464 _ => return Err(RuntimeError::new("regex_match() requires string text")),
8465 };
8466
8467 match Regex::new(&pattern) {
8468 Ok(re) => Ok(Value::Bool(re.is_match(&text))),
8469 Err(e) => Err(RuntimeError::new(format!(
8470 "regex_match() invalid pattern: {}",
8471 e
8472 ))),
8473 }
8474 });
8475
8476 define(interp, "regex_find", Some(2), |_, args| {
8478 let pattern = match &args[0] {
8479 Value::String(s) => s.to_string(),
8480 _ => return Err(RuntimeError::new("regex_find() requires string pattern")),
8481 };
8482 let text = match &args[1] {
8483 Value::String(s) => s.to_string(),
8484 _ => return Err(RuntimeError::new("regex_find() requires string text")),
8485 };
8486
8487 match Regex::new(&pattern) {
8488 Ok(re) => match re.find(&text) {
8489 Some(m) => Ok(Value::String(Rc::new(m.as_str().to_string()))),
8490 None => Ok(Value::Null),
8491 },
8492 Err(e) => Err(RuntimeError::new(format!(
8493 "regex_find() invalid pattern: {}",
8494 e
8495 ))),
8496 }
8497 });
8498
8499 define(interp, "regex_find_all", Some(2), |_, args| {
8501 let pattern = match &args[0] {
8502 Value::String(s) => s.to_string(),
8503 _ => {
8504 return Err(RuntimeError::new(
8505 "regex_find_all() requires string pattern",
8506 ))
8507 }
8508 };
8509 let text = match &args[1] {
8510 Value::String(s) => s.to_string(),
8511 _ => return Err(RuntimeError::new("regex_find_all() requires string text")),
8512 };
8513
8514 match Regex::new(&pattern) {
8515 Ok(re) => {
8516 let matches: Vec<Value> = re
8517 .find_iter(&text)
8518 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
8519 .collect();
8520 Ok(Value::Array(Rc::new(RefCell::new(matches))))
8521 }
8522 Err(e) => Err(RuntimeError::new(format!(
8523 "regex_find_all() invalid pattern: {}",
8524 e
8525 ))),
8526 }
8527 });
8528
8529 define(interp, "regex_replace", Some(3), |_, args| {
8531 let pattern = match &args[0] {
8532 Value::String(s) => s.to_string(),
8533 _ => return Err(RuntimeError::new("regex_replace() requires string pattern")),
8534 };
8535 let text = match &args[1] {
8536 Value::String(s) => s.to_string(),
8537 _ => return Err(RuntimeError::new("regex_replace() requires string text")),
8538 };
8539 let replacement = match &args[2] {
8540 Value::String(s) => s.to_string(),
8541 _ => {
8542 return Err(RuntimeError::new(
8543 "regex_replace() requires string replacement",
8544 ))
8545 }
8546 };
8547
8548 match Regex::new(&pattern) {
8549 Ok(re) => {
8550 let result = re.replace(&text, replacement.as_str());
8551 Ok(Value::String(Rc::new(result.to_string())))
8552 }
8553 Err(e) => Err(RuntimeError::new(format!(
8554 "regex_replace() invalid pattern: {}",
8555 e
8556 ))),
8557 }
8558 });
8559
8560 define(interp, "regex_replace_all", Some(3), |_, args| {
8562 let pattern = match &args[0] {
8563 Value::String(s) => s.to_string(),
8564 _ => {
8565 return Err(RuntimeError::new(
8566 "regex_replace_all() requires string pattern",
8567 ))
8568 }
8569 };
8570 let text = match &args[1] {
8571 Value::String(s) => s.to_string(),
8572 _ => {
8573 return Err(RuntimeError::new(
8574 "regex_replace_all() requires string text",
8575 ))
8576 }
8577 };
8578 let replacement = match &args[2] {
8579 Value::String(s) => s.to_string(),
8580 _ => {
8581 return Err(RuntimeError::new(
8582 "regex_replace_all() requires string replacement",
8583 ))
8584 }
8585 };
8586
8587 match Regex::new(&pattern) {
8588 Ok(re) => {
8589 let result = re.replace_all(&text, replacement.as_str());
8590 Ok(Value::String(Rc::new(result.to_string())))
8591 }
8592 Err(e) => Err(RuntimeError::new(format!(
8593 "regex_replace_all() invalid pattern: {}",
8594 e
8595 ))),
8596 }
8597 });
8598
8599 define(interp, "regex_split", Some(2), |_, args| {
8601 let pattern = match &args[0] {
8602 Value::String(s) => s.to_string(),
8603 _ => return Err(RuntimeError::new("regex_split() requires string pattern")),
8604 };
8605 let text = match &args[1] {
8606 Value::String(s) => s.to_string(),
8607 _ => return Err(RuntimeError::new("regex_split() requires string text")),
8608 };
8609
8610 match Regex::new(&pattern) {
8611 Ok(re) => {
8612 let parts: Vec<Value> = re
8613 .split(&text)
8614 .map(|s| Value::String(Rc::new(s.to_string())))
8615 .collect();
8616 Ok(Value::Array(Rc::new(RefCell::new(parts))))
8617 }
8618 Err(e) => Err(RuntimeError::new(format!(
8619 "regex_split() invalid pattern: {}",
8620 e
8621 ))),
8622 }
8623 });
8624
8625 define(interp, "regex_captures", Some(2), |_, args| {
8627 let pattern = match &args[0] {
8628 Value::String(s) => s.to_string(),
8629 _ => {
8630 return Err(RuntimeError::new(
8631 "regex_captures() requires string pattern",
8632 ))
8633 }
8634 };
8635 let text = match &args[1] {
8636 Value::String(s) => s.to_string(),
8637 _ => return Err(RuntimeError::new("regex_captures() requires string text")),
8638 };
8639
8640 match Regex::new(&pattern) {
8641 Ok(re) => match re.captures(&text) {
8642 Some(caps) => {
8643 let captures: Vec<Value> = caps
8644 .iter()
8645 .map(|m| {
8646 m.map(|m| Value::String(Rc::new(m.as_str().to_string())))
8647 .unwrap_or(Value::Null)
8648 })
8649 .collect();
8650 Ok(Value::Array(Rc::new(RefCell::new(captures))))
8651 }
8652 None => Ok(Value::Null),
8653 },
8654 Err(e) => Err(RuntimeError::new(format!(
8655 "regex_captures() invalid pattern: {}",
8656 e
8657 ))),
8658 }
8659 });
8660}
8661
8662fn register_uuid(interp: &mut Interpreter) {
8667 define(interp, "uuid_v4", Some(0), |_, _| {
8669 let id = Uuid::new_v4();
8670 Ok(Value::String(Rc::new(id.to_string())))
8671 });
8672
8673 define(interp, "uuid_nil", Some(0), |_, _| {
8675 Ok(Value::String(Rc::new(Uuid::nil().to_string())))
8676 });
8677
8678 define(interp, "uuid_parse", Some(1), |_, args| {
8680 let s = match &args[0] {
8681 Value::String(s) => s.to_string(),
8682 _ => return Err(RuntimeError::new("uuid_parse() requires string")),
8683 };
8684
8685 match Uuid::parse_str(&s) {
8686 Ok(id) => Ok(Value::String(Rc::new(id.to_string()))),
8687 Err(e) => Err(RuntimeError::new(format!("uuid_parse() error: {}", e))),
8688 }
8689 });
8690
8691 define(interp, "uuid_is_valid", Some(1), |_, args| {
8693 let s = match &args[0] {
8694 Value::String(s) => s.to_string(),
8695 _ => return Ok(Value::Bool(false)),
8696 };
8697 Ok(Value::Bool(Uuid::parse_str(&s).is_ok()))
8698 });
8699}
8700
8701fn register_system(interp: &mut Interpreter) {
8706 define(interp, "env_get", Some(1), |_, args| {
8708 let key = match &args[0] {
8709 Value::String(s) => s.to_string(),
8710 _ => return Err(RuntimeError::new("env_get() requires string key")),
8711 };
8712
8713 match std::env::var(&key) {
8714 Ok(val) => Ok(Value::String(Rc::new(val))),
8715 Err(_) => Ok(Value::Null),
8716 }
8717 });
8718
8719 define(interp, "env_set", Some(2), |_, args| {
8721 let key = match &args[0] {
8722 Value::String(s) => s.to_string(),
8723 _ => return Err(RuntimeError::new("env_set() requires string key")),
8724 };
8725 let val = match &args[1] {
8726 Value::String(s) => s.to_string(),
8727 _ => format!("{}", args[1]),
8728 };
8729
8730 std::env::set_var(&key, &val);
8731 Ok(Value::Null)
8732 });
8733
8734 define(interp, "env_remove", Some(1), |_, args| {
8736 let key = match &args[0] {
8737 Value::String(s) => s.to_string(),
8738 _ => return Err(RuntimeError::new("env_remove() requires string key")),
8739 };
8740
8741 std::env::remove_var(&key);
8742 Ok(Value::Null)
8743 });
8744
8745 define(interp, "env_vars", Some(0), |_, _| {
8747 let mut map = HashMap::new();
8748 for (key, val) in std::env::vars() {
8749 map.insert(key, Value::String(Rc::new(val)));
8750 }
8751 Ok(Value::Map(Rc::new(RefCell::new(map))))
8752 });
8753
8754 define(interp, "std·env·var", Some(1), |_, args| {
8756 let key = match &args[0] {
8757 Value::String(s) => s.as_str().to_string(),
8758 _ => return Err(RuntimeError::new("env::var expects string key")),
8759 };
8760 match std::env::var(&key) {
8761 Ok(val) => Ok(Value::Variant {
8762 enum_name: "Result".to_string(),
8763 variant_name: "Ok".to_string(),
8764 fields: Some(Rc::new(vec![Value::String(Rc::new(val))])),
8765 }),
8766 Err(_) => Ok(Value::Variant {
8767 enum_name: "Result".to_string(),
8768 variant_name: "Err".to_string(),
8769 fields: Some(Rc::new(vec![Value::String(Rc::new("environment variable not found".to_string()))])),
8770 }),
8771 }
8772 });
8773
8774 define(interp, "std·env·temp_dir", Some(0), |_, _| {
8776 let temp_dir = std::env::temp_dir();
8777 Ok(Value::String(Rc::new(temp_dir.to_string_lossy().to_string())))
8778 });
8779
8780 define(interp, "temp_dir", Some(0), |_, _| {
8782 let temp_dir = std::env::temp_dir();
8783 Ok(Value::String(Rc::new(temp_dir.to_string_lossy().to_string())))
8784 });
8785
8786 define(interp, "std·env·current_dir", Some(0), |_, _| {
8788 match std::env::current_dir() {
8789 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
8790 Err(e) => Err(RuntimeError::new(format!("current_dir() error: {}", e))),
8791 }
8792 });
8793
8794 define(interp, "std·env·args", Some(0), |interp, _| {
8796 let args: Vec<Value> = if interp.program_args.as_ref().map(|v| v.is_empty()).unwrap_or(true) {
8797 std::env::args()
8799 .map(|s| Value::String(Rc::new(s)))
8800 .collect()
8801 } else {
8802 interp.program_args.as_ref().unwrap().iter()
8804 .map(|a| Value::String(Rc::new(a.clone())))
8805 .collect()
8806 };
8807 Ok(Value::Array(Rc::new(RefCell::new(args))))
8808 });
8809
8810 define(interp, "args", Some(0), |interp, _| {
8812 let args: Vec<Value> = if interp.program_args.as_ref().map(|v| v.is_empty()).unwrap_or(true) {
8813 std::env::args()
8815 .map(|s| Value::String(Rc::new(s)))
8816 .collect()
8817 } else {
8818 interp.program_args.as_ref().unwrap().iter()
8820 .map(|a| Value::String(Rc::new(a.clone())))
8821 .collect()
8822 };
8823 Ok(Value::Array(Rc::new(RefCell::new(args))))
8824 });
8825
8826 define(interp, "cwd", Some(0), |_, _| {
8828 match std::env::current_dir() {
8829 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
8830 Err(e) => Err(RuntimeError::new(format!("cwd() error: {}", e))),
8831 }
8832 });
8833
8834 define(interp, "chdir", Some(1), |_, args| {
8836 let path = match &args[0] {
8837 Value::String(s) => s.to_string(),
8838 _ => return Err(RuntimeError::new("chdir() requires string path")),
8839 };
8840
8841 match std::env::set_current_dir(&path) {
8842 Ok(()) => Ok(Value::Null),
8843 Err(e) => Err(RuntimeError::new(format!("chdir() error: {}", e))),
8844 }
8845 });
8846
8847 define(interp, "hostname", Some(0), |_, _| {
8849 match std::fs::read_to_string("/etc/hostname") {
8851 Ok(name) => Ok(Value::String(Rc::new(name.trim().to_string()))),
8852 Err(_) => Ok(Value::String(Rc::new("unknown".to_string()))),
8853 }
8854 });
8855
8856 define(interp, "pid", Some(0), |_, _| {
8858 Ok(Value::Int(std::process::id() as i64))
8859 });
8860
8861 define(interp, "exit", Some(1), |_, args| {
8863 let code = match &args[0] {
8864 Value::Int(n) => *n as i32,
8865 _ => 0,
8866 };
8867 std::process::exit(code);
8868 });
8869
8870 define(interp, "std·process·exit", Some(1), |_, args| {
8872 let code = match &args[0] {
8873 Value::Int(n) => *n as i32,
8874 _ => 0,
8875 };
8876 std::process::exit(code);
8877 });
8878
8879 define(interp, "shell", Some(1), |_, args| {
8881 let cmd = match &args[0] {
8882 Value::String(s) => s.to_string(),
8883 _ => return Err(RuntimeError::new("shell() requires string command")),
8884 };
8885
8886 match std::process::Command::new("sh")
8887 .arg("-c")
8888 .arg(&cmd)
8889 .output()
8890 {
8891 Ok(output) => {
8892 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
8893 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
8894 let code = output.status.code().unwrap_or(-1);
8895
8896 let mut result = HashMap::new();
8897 result.insert("stdout".to_string(), Value::String(Rc::new(stdout)));
8898 result.insert("stderr".to_string(), Value::String(Rc::new(stderr)));
8899 result.insert("code".to_string(), Value::Int(code as i64));
8900 result.insert("success".to_string(), Value::Bool(output.status.success()));
8901
8902 Ok(Value::Map(Rc::new(RefCell::new(result))))
8903 }
8904 Err(e) => Err(RuntimeError::new(format!("shell() error: {}", e))),
8905 }
8906 });
8907
8908 define(interp, "platform", Some(0), |_, _| {
8910 Ok(Value::String(Rc::new(std::env::consts::OS.to_string())))
8911 });
8912
8913 define(interp, "arch", Some(0), |_, _| {
8915 Ok(Value::String(Rc::new(std::env::consts::ARCH.to_string())))
8916 });
8917
8918 define(interp, "num_cpus·get", Some(0), |_, _| {
8920 Ok(Value::Int(num_cpus::get() as i64))
8921 });
8922
8923 define(interp, "num_cpus·get_physical", Some(0), |_, _| {
8925 Ok(Value::Int(num_cpus::get_physical() as i64))
8926 });
8927}
8928
8929fn register_stats(interp: &mut Interpreter) {
8934 fn extract_numbers(val: &Value) -> Result<Vec<f64>, RuntimeError> {
8936 match val {
8937 Value::Array(arr) => {
8938 let arr = arr.borrow();
8939 let mut nums = Vec::new();
8940 for v in arr.iter() {
8941 match v {
8942 Value::Int(n) => nums.push(*n as f64),
8943 Value::Float(f) => nums.push(*f),
8944 _ => {
8945 return Err(RuntimeError::new("stats functions require numeric array"))
8946 }
8947 }
8948 }
8949 Ok(nums)
8950 }
8951 _ => Err(RuntimeError::new("stats functions require array")),
8952 }
8953 }
8954
8955 define(interp, "mean", Some(1), |_, args| {
8957 let nums = extract_numbers(&args[0])?;
8958 if nums.is_empty() {
8959 return Ok(Value::Float(0.0));
8960 }
8961 let sum: f64 = nums.iter().sum();
8962 Ok(Value::Float(sum / nums.len() as f64))
8963 });
8964
8965 define(interp, "median", Some(1), |_, args| {
8967 let mut nums = extract_numbers(&args[0])?;
8968 if nums.is_empty() {
8969 return Ok(Value::Float(0.0));
8970 }
8971 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
8972 let len = nums.len();
8973 if len % 2 == 0 {
8974 Ok(Value::Float((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
8975 } else {
8976 Ok(Value::Float(nums[len / 2]))
8977 }
8978 });
8979
8980 define(interp, "mode", Some(1), |_, args| {
8982 let nums = extract_numbers(&args[0])?;
8983 if nums.is_empty() {
8984 return Ok(Value::Null);
8985 }
8986
8987 let mut counts: HashMap<String, usize> = HashMap::new();
8988 for n in &nums {
8989 let key = format!("{:.10}", n);
8990 *counts.entry(key).or_insert(0) += 1;
8991 }
8992
8993 let max_count = counts.values().max().unwrap_or(&0);
8994 for n in &nums {
8995 let key = format!("{:.10}", n);
8996 if counts.get(&key) == Some(max_count) {
8997 return Ok(Value::Float(*n));
8998 }
8999 }
9000 Ok(Value::Null)
9001 });
9002
9003 define(interp, "variance", Some(1), |_, args| {
9005 let nums = extract_numbers(&args[0])?;
9006 if nums.is_empty() {
9007 return Ok(Value::Float(0.0));
9008 }
9009 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9010 let variance: f64 =
9011 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9012 Ok(Value::Float(variance))
9013 });
9014
9015 define(interp, "stddev", Some(1), |_, args| {
9017 let nums = extract_numbers(&args[0])?;
9018 if nums.is_empty() {
9019 return Ok(Value::Float(0.0));
9020 }
9021 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9022 let variance: f64 =
9023 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9024 Ok(Value::Float(variance.sqrt()))
9025 });
9026
9027 define(interp, "percentile", Some(2), |_, args| {
9029 let mut nums = extract_numbers(&args[0])?;
9030 let p = match &args[1] {
9031 Value::Int(n) => *n as f64,
9032 Value::Float(f) => *f,
9033 _ => {
9034 return Err(RuntimeError::new(
9035 "percentile() requires numeric percentile",
9036 ))
9037 }
9038 };
9039
9040 if nums.is_empty() {
9041 return Ok(Value::Float(0.0));
9042 }
9043
9044 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
9045 let idx = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
9046 Ok(Value::Float(nums[idx.min(nums.len() - 1)]))
9047 });
9048
9049 define(interp, "correlation", Some(2), |_, args| {
9051 let x = extract_numbers(&args[0])?;
9052 let y = extract_numbers(&args[1])?;
9053
9054 if x.len() != y.len() || x.is_empty() {
9055 return Err(RuntimeError::new(
9056 "correlation() requires equal-length non-empty arrays",
9057 ));
9058 }
9059
9060 let n = x.len() as f64;
9061 let mean_x: f64 = x.iter().sum::<f64>() / n;
9062 let mean_y: f64 = y.iter().sum::<f64>() / n;
9063
9064 let mut cov = 0.0;
9065 let mut var_x = 0.0;
9066 let mut var_y = 0.0;
9067
9068 for i in 0..x.len() {
9069 let dx = x[i] - mean_x;
9070 let dy = y[i] - mean_y;
9071 cov += dx * dy;
9072 var_x += dx * dx;
9073 var_y += dy * dy;
9074 }
9075
9076 if var_x == 0.0 || var_y == 0.0 {
9077 return Ok(Value::Float(0.0));
9078 }
9079
9080 Ok(Value::Float(cov / (var_x.sqrt() * var_y.sqrt())))
9081 });
9082
9083 define(interp, "range", Some(1), |_, args| {
9085 let nums = extract_numbers(&args[0])?;
9086 if nums.is_empty() {
9087 return Ok(Value::Float(0.0));
9088 }
9089 let min = nums.iter().cloned().fold(f64::INFINITY, f64::min);
9090 let max = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
9091 Ok(Value::Float(max - min))
9092 });
9093
9094 define(interp, "zscore", Some(1), |_, args| {
9096 let nums = extract_numbers(&args[0])?;
9097 if nums.is_empty() {
9098 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9099 }
9100
9101 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9102 let variance: f64 =
9103 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9104 let stddev = variance.sqrt();
9105
9106 if stddev == 0.0 {
9107 let zeros: Vec<Value> = nums.iter().map(|_| Value::Float(0.0)).collect();
9108 return Ok(Value::Array(Rc::new(RefCell::new(zeros))));
9109 }
9110
9111 let zscores: Vec<Value> = nums
9112 .iter()
9113 .map(|x| Value::Float((x - mean) / stddev))
9114 .collect();
9115 Ok(Value::Array(Rc::new(RefCell::new(zscores))))
9116 });
9117}
9118
9119fn register_matrix(interp: &mut Interpreter) {
9124 fn extract_matrix(val: &Value) -> Result<Vec<Vec<f64>>, RuntimeError> {
9126 match val {
9127 Value::Array(arr) => {
9128 let arr = arr.borrow();
9129 let mut matrix = Vec::new();
9130 for row in arr.iter() {
9131 match row {
9132 Value::Array(row_arr) => {
9133 let row_arr = row_arr.borrow();
9134 let mut row_vec = Vec::new();
9135 for v in row_arr.iter() {
9136 match v {
9137 Value::Int(n) => row_vec.push(*n as f64),
9138 Value::Float(f) => row_vec.push(*f),
9139 _ => {
9140 return Err(RuntimeError::new(
9141 "matrix requires numeric values",
9142 ))
9143 }
9144 }
9145 }
9146 matrix.push(row_vec);
9147 }
9148 _ => return Err(RuntimeError::new("matrix requires 2D array")),
9149 }
9150 }
9151 Ok(matrix)
9152 }
9153 _ => Err(RuntimeError::new("matrix requires array")),
9154 }
9155 }
9156
9157 fn matrix_to_value(m: Vec<Vec<f64>>) -> Value {
9158 let rows: Vec<Value> = m
9159 .into_iter()
9160 .map(|row| {
9161 let cols: Vec<Value> = row.into_iter().map(Value::Float).collect();
9162 Value::Array(Rc::new(RefCell::new(cols)))
9163 })
9164 .collect();
9165 Value::Array(Rc::new(RefCell::new(rows)))
9166 }
9167
9168 define(interp, "matrix_new", Some(3), |_, args| {
9170 let rows = match &args[0] {
9171 Value::Int(n) => *n as usize,
9172 _ => return Err(RuntimeError::new("matrix_new() requires integer rows")),
9173 };
9174 let cols = match &args[1] {
9175 Value::Int(n) => *n as usize,
9176 _ => return Err(RuntimeError::new("matrix_new() requires integer cols")),
9177 };
9178 let fill = match &args[2] {
9179 Value::Int(n) => *n as f64,
9180 Value::Float(f) => *f,
9181 _ => 0.0,
9182 };
9183
9184 let matrix = vec![vec![fill; cols]; rows];
9185 Ok(matrix_to_value(matrix))
9186 });
9187
9188 define(interp, "matrix_identity", Some(1), |_, args| {
9190 let size = match &args[0] {
9191 Value::Int(n) => *n as usize,
9192 _ => return Err(RuntimeError::new("matrix_identity() requires integer size")),
9193 };
9194
9195 let mut matrix = vec![vec![0.0; size]; size];
9196 for i in 0..size {
9197 matrix[i][i] = 1.0;
9198 }
9199 Ok(matrix_to_value(matrix))
9200 });
9201
9202 define(interp, "matrix_add", Some(2), |_, args| {
9204 let a = extract_matrix(&args[0])?;
9205 let b = extract_matrix(&args[1])?;
9206
9207 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
9208 return Err(RuntimeError::new(
9209 "matrix_add() requires same-size matrices",
9210 ));
9211 }
9212
9213 let result: Vec<Vec<f64>> = a
9214 .iter()
9215 .zip(b.iter())
9216 .map(|(row_a, row_b)| row_a.iter().zip(row_b.iter()).map(|(x, y)| x + y).collect())
9217 .collect();
9218
9219 Ok(matrix_to_value(result))
9220 });
9221
9222 define(interp, "matrix_sub", Some(2), |_, args| {
9224 let a = extract_matrix(&args[0])?;
9225 let b = extract_matrix(&args[1])?;
9226
9227 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
9228 return Err(RuntimeError::new(
9229 "matrix_sub() requires same-size matrices",
9230 ));
9231 }
9232
9233 let result: Vec<Vec<f64>> = a
9234 .iter()
9235 .zip(b.iter())
9236 .map(|(row_a, row_b)| row_a.iter().zip(row_b.iter()).map(|(x, y)| x - y).collect())
9237 .collect();
9238
9239 Ok(matrix_to_value(result))
9240 });
9241
9242 define(interp, "matrix_mul", Some(2), |_, args| {
9244 let a = extract_matrix(&args[0])?;
9245 let b = extract_matrix(&args[1])?;
9246
9247 if a.is_empty() || b.is_empty() || a[0].len() != b.len() {
9248 return Err(RuntimeError::new(
9249 "matrix_mul() requires compatible matrices (a.cols == b.rows)",
9250 ));
9251 }
9252
9253 let rows = a.len();
9254 let cols = b[0].len();
9255 let inner = b.len();
9256
9257 let mut result = vec![vec![0.0; cols]; rows];
9258 for i in 0..rows {
9259 for j in 0..cols {
9260 for k in 0..inner {
9261 result[i][j] += a[i][k] * b[k][j];
9262 }
9263 }
9264 }
9265
9266 Ok(matrix_to_value(result))
9267 });
9268
9269 define(interp, "matrix_scale", Some(2), |_, args| {
9271 let m = extract_matrix(&args[0])?;
9272 let scale = match &args[1] {
9273 Value::Int(n) => *n as f64,
9274 Value::Float(f) => *f,
9275 _ => return Err(RuntimeError::new("matrix_scale() requires numeric scalar")),
9276 };
9277
9278 let result: Vec<Vec<f64>> = m
9279 .iter()
9280 .map(|row| row.iter().map(|x| x * scale).collect())
9281 .collect();
9282
9283 Ok(matrix_to_value(result))
9284 });
9285
9286 define(interp, "matrix_transpose", Some(1), |_, args| {
9288 let m = extract_matrix(&args[0])?;
9289 if m.is_empty() {
9290 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9291 }
9292
9293 let rows = m.len();
9294 let cols = m[0].len();
9295 let mut result = vec![vec![0.0; rows]; cols];
9296
9297 for i in 0..rows {
9298 for j in 0..cols {
9299 result[j][i] = m[i][j];
9300 }
9301 }
9302
9303 Ok(matrix_to_value(result))
9304 });
9305
9306 define(interp, "matrix_det", Some(1), |_, args| {
9308 let m = extract_matrix(&args[0])?;
9309
9310 if m.is_empty() || m.len() != m[0].len() {
9311 return Err(RuntimeError::new("matrix_det() requires square matrix"));
9312 }
9313
9314 let n = m.len();
9315 match n {
9316 1 => Ok(Value::Float(m[0][0])),
9317 2 => Ok(Value::Float(m[0][0] * m[1][1] - m[0][1] * m[1][0])),
9318 3 => {
9319 let det = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
9320 - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
9321 + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
9322 Ok(Value::Float(det))
9323 }
9324 _ => Err(RuntimeError::new(
9325 "matrix_det() only supports up to 3x3 matrices",
9326 )),
9327 }
9328 });
9329
9330 define(interp, "matrix_trace", Some(1), |_, args| {
9332 let m = extract_matrix(&args[0])?;
9333
9334 let size = m.len().min(if m.is_empty() { 0 } else { m[0].len() });
9335 let trace: f64 = (0..size).map(|i| m[i][i]).sum();
9336
9337 Ok(Value::Float(trace))
9338 });
9339
9340 define(interp, "matrix_dot", Some(2), |_, args| {
9342 fn extract_vector(val: &Value) -> Result<Vec<f64>, RuntimeError> {
9343 match val {
9344 Value::Array(arr) => {
9345 let arr = arr.borrow();
9346 let mut vec = Vec::new();
9347 for v in arr.iter() {
9348 match v {
9349 Value::Int(n) => vec.push(*n as f64),
9350 Value::Float(f) => vec.push(*f),
9351 _ => {
9352 return Err(RuntimeError::new(
9353 "dot product requires numeric vectors",
9354 ))
9355 }
9356 }
9357 }
9358 Ok(vec)
9359 }
9360 _ => Err(RuntimeError::new("dot product requires arrays")),
9361 }
9362 }
9363
9364 let a = extract_vector(&args[0])?;
9365 let b = extract_vector(&args[1])?;
9366
9367 if a.len() != b.len() {
9368 return Err(RuntimeError::new(
9369 "matrix_dot() requires same-length vectors",
9370 ));
9371 }
9372
9373 let dot: f64 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
9374 Ok(Value::Float(dot))
9375 });
9376}
9377
9378fn mod_inverse(a: i64, m: i64) -> Option<i64> {
9380 let (mut old_r, mut r) = (a, m);
9381 let (mut old_s, mut s) = (1i64, 0i64);
9382
9383 while r != 0 {
9384 let q = old_r / r;
9385 (old_r, r) = (r, old_r - q * r);
9386 (old_s, s) = (s, old_s - q * s);
9387 }
9388
9389 if old_r != 1 {
9390 None } else {
9392 Some(old_s.rem_euclid(m))
9393 }
9394}
9395
9396fn register_functional(interp: &mut Interpreter) {
9402 define(interp, "identity", Some(1), |_, args| Ok(args[0].clone()));
9404
9405 define(interp, "const_fn", Some(1), |_, args| Ok(args[0].clone()));
9407
9408 define(interp, "apply", Some(2), |interp, args| {
9410 let func = match &args[0] {
9411 Value::Function(f) => f.clone(),
9412 _ => {
9413 return Err(RuntimeError::new(
9414 "apply: first argument must be a function",
9415 ))
9416 }
9417 };
9418 let fn_args = match &args[1] {
9419 Value::Array(arr) => arr.borrow().clone(),
9420 _ => return Err(RuntimeError::new("apply: second argument must be an array")),
9421 };
9422 interp.call_function(&func, fn_args)
9423 });
9424
9425 define(interp, "flip", Some(3), |interp, args| {
9427 let func = match &args[0] {
9428 Value::Function(f) => f.clone(),
9429 _ => return Err(RuntimeError::new("flip: first argument must be a function")),
9430 };
9431 let flipped_args = vec![args[2].clone(), args[1].clone()];
9432 interp.call_function(&func, flipped_args)
9433 });
9434
9435 define(interp, "tap", Some(2), |interp, args| {
9437 let val = args[0].clone();
9438 let func = match &args[1] {
9439 Value::Function(f) => f.clone(),
9440 _ => return Err(RuntimeError::new("tap: second argument must be a function")),
9441 };
9442 let _ = interp.call_function(&func, vec![val.clone()]);
9443 Ok(val)
9444 });
9445
9446 define(interp, "thunk", Some(1), |_, args| {
9448 Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()]))))
9449 });
9450
9451 define(interp, "force", Some(1), |interp, args| match &args[0] {
9453 Value::Array(arr) => {
9454 let arr = arr.borrow();
9455 if arr.len() == 1 {
9456 if let Value::Function(f) = &arr[0] {
9457 return interp.call_function(f, vec![]);
9458 }
9459 }
9460 Ok(arr.get(0).cloned().unwrap_or(Value::Null))
9461 }
9462 v => Ok(v.clone()),
9463 });
9464
9465 define(interp, "negate", Some(2), |interp, args| {
9467 let func = match &args[0] {
9468 Value::Function(f) => f.clone(),
9469 _ => {
9470 return Err(RuntimeError::new(
9471 "negate: first argument must be a function",
9472 ))
9473 }
9474 };
9475 let result = interp.call_function(&func, vec![args[1].clone()])?;
9476 Ok(Value::Bool(!is_truthy(&result)))
9477 });
9478
9479 define(interp, "complement", Some(2), |interp, args| {
9481 let func = match &args[0] {
9482 Value::Function(f) => f.clone(),
9483 _ => {
9484 return Err(RuntimeError::new(
9485 "complement: first argument must be a function",
9486 ))
9487 }
9488 };
9489 let result = interp.call_function(&func, vec![args[1].clone()])?;
9490 Ok(Value::Bool(!is_truthy(&result)))
9491 });
9492
9493 define(interp, "partial", None, |interp, args| {
9495 if args.len() < 2 {
9496 return Err(RuntimeError::new(
9497 "partial: requires at least function and one argument",
9498 ));
9499 }
9500 let func = match &args[0] {
9501 Value::Function(f) => f.clone(),
9502 _ => {
9503 return Err(RuntimeError::new(
9504 "partial: first argument must be a function",
9505 ))
9506 }
9507 };
9508 let partial_args: Vec<Value> = args[1..].to_vec();
9509 interp.call_function(&func, partial_args)
9510 });
9511
9512 define(interp, "juxt", None, |interp, args| {
9514 if args.len() < 2 {
9515 return Err(RuntimeError::new("juxt: requires functions and a value"));
9516 }
9517 let val = args.last().unwrap().clone();
9518 let results: Result<Vec<Value>, _> = args[..args.len() - 1]
9519 .iter()
9520 .map(|f| match f {
9521 Value::Function(func) => interp.call_function(func, vec![val.clone()]),
9522 _ => Err(RuntimeError::new(
9523 "juxt: all but last argument must be functions",
9524 )),
9525 })
9526 .collect();
9527 Ok(Value::Array(Rc::new(RefCell::new(results?))))
9528 });
9529}
9530
9531fn register_benchmark(interp: &mut Interpreter) {
9533 define(interp, "bench", Some(2), |interp, args| {
9535 let func = match &args[0] {
9536 Value::Function(f) => f.clone(),
9537 _ => {
9538 return Err(RuntimeError::new(
9539 "bench: first argument must be a function",
9540 ))
9541 }
9542 };
9543 let iterations = match &args[1] {
9544 Value::Int(n) => *n as usize,
9545 _ => {
9546 return Err(RuntimeError::new(
9547 "bench: second argument must be an integer",
9548 ))
9549 }
9550 };
9551
9552 let start = std::time::Instant::now();
9553 for _ in 0..iterations {
9554 let _ = interp.call_function(&func, vec![])?;
9555 }
9556 let elapsed = start.elapsed();
9557 let avg_ms = elapsed.as_secs_f64() * 1000.0 / iterations as f64;
9558 Ok(Value::Float(avg_ms))
9559 });
9560
9561 define(interp, "time_it", Some(1), |interp, args| {
9563 let func = match &args[0] {
9564 Value::Function(f) => f.clone(),
9565 _ => return Err(RuntimeError::new("time_it: argument must be a function")),
9566 };
9567
9568 let start = std::time::Instant::now();
9569 let result = interp.call_function(&func, vec![])?;
9570 let elapsed_ms = start.elapsed().as_secs_f64() * 1000.0;
9571
9572 Ok(Value::Tuple(Rc::new(vec![
9573 result,
9574 Value::Float(elapsed_ms),
9575 ])))
9576 });
9577
9578 define(interp, "stopwatch_start", Some(0), |_, _| {
9580 let elapsed = std::time::SystemTime::now()
9581 .duration_since(std::time::UNIX_EPOCH)
9582 .unwrap_or_default();
9583 Ok(Value::Float(elapsed.as_secs_f64() * 1000.0))
9584 });
9585
9586 define(interp, "stopwatch_elapsed", Some(1), |_, args| {
9588 let start_ms = match &args[0] {
9589 Value::Float(f) => *f,
9590 Value::Int(n) => *n as f64,
9591 _ => {
9592 return Err(RuntimeError::new(
9593 "stopwatch_elapsed: argument must be a number",
9594 ))
9595 }
9596 };
9597 let now = std::time::SystemTime::now()
9598 .duration_since(std::time::UNIX_EPOCH)
9599 .unwrap_or_default();
9600 let now_ms = now.as_secs_f64() * 1000.0;
9601 Ok(Value::Float(now_ms - start_ms))
9602 });
9603
9604 define(interp, "compare_bench", Some(3), |interp, args| {
9606 let func1 = match &args[0] {
9607 Value::Function(f) => f.clone(),
9608 _ => {
9609 return Err(RuntimeError::new(
9610 "compare_bench: first argument must be a function",
9611 ))
9612 }
9613 };
9614 let func2 = match &args[1] {
9615 Value::Function(f) => f.clone(),
9616 _ => {
9617 return Err(RuntimeError::new(
9618 "compare_bench: second argument must be a function",
9619 ))
9620 }
9621 };
9622 let iterations = match &args[2] {
9623 Value::Int(n) => *n as usize,
9624 _ => {
9625 return Err(RuntimeError::new(
9626 "compare_bench: third argument must be an integer",
9627 ))
9628 }
9629 };
9630
9631 let start1 = std::time::Instant::now();
9632 for _ in 0..iterations {
9633 let _ = interp.call_function(&func1, vec![])?;
9634 }
9635 let time1 = start1.elapsed().as_secs_f64();
9636
9637 let start2 = std::time::Instant::now();
9638 for _ in 0..iterations {
9639 let _ = interp.call_function(&func2, vec![])?;
9640 }
9641 let time2 = start2.elapsed().as_secs_f64();
9642
9643 let mut results = std::collections::HashMap::new();
9644 results.insert("time1_ms".to_string(), Value::Float(time1 * 1000.0));
9645 results.insert("time2_ms".to_string(), Value::Float(time2 * 1000.0));
9646 results.insert("speedup".to_string(), Value::Float(time1 / time2));
9647 results.insert("iterations".to_string(), Value::Int(iterations as i64));
9648
9649 Ok(Value::Struct {
9650 name: "BenchResult".to_string(),
9651 fields: Rc::new(RefCell::new(results)),
9652 })
9653 });
9654
9655 define(interp, "memory_usage", Some(0), |_, _| Ok(Value::Int(0)));
9657}
9658
9659fn register_itertools(interp: &mut Interpreter) {
9661 define(interp, "cycle", Some(2), |_, args| {
9663 let arr = match &args[0] {
9664 Value::Array(a) => a.borrow().clone(),
9665 _ => return Err(RuntimeError::new("cycle: first argument must be an array")),
9666 };
9667 let n = match &args[1] {
9668 Value::Int(n) => *n as usize,
9669 _ => {
9670 return Err(RuntimeError::new(
9671 "cycle: second argument must be an integer",
9672 ))
9673 }
9674 };
9675
9676 if arr.is_empty() {
9677 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9678 }
9679
9680 let result: Vec<Value> = (0..n).map(|i| arr[i % arr.len()].clone()).collect();
9681 Ok(Value::Array(Rc::new(RefCell::new(result))))
9682 });
9683
9684 define(interp, "repeat_val", Some(2), |_, args| {
9686 let val = args[0].clone();
9687 let n = match &args[1] {
9688 Value::Int(n) => *n as usize,
9689 _ => {
9690 return Err(RuntimeError::new(
9691 "repeat_val: second argument must be an integer",
9692 ))
9693 }
9694 };
9695
9696 let result: Vec<Value> = std::iter::repeat(val).take(n).collect();
9697 Ok(Value::Array(Rc::new(RefCell::new(result))))
9698 });
9699
9700 define(interp, "take_while", Some(2), |interp, args| {
9702 let arr = match &args[0] {
9703 Value::Array(a) => a.borrow().clone(),
9704 _ => {
9705 return Err(RuntimeError::new(
9706 "take_while: first argument must be an array",
9707 ))
9708 }
9709 };
9710 let pred = match &args[1] {
9711 Value::Function(f) => f.clone(),
9712 _ => {
9713 return Err(RuntimeError::new(
9714 "take_while: second argument must be a function",
9715 ))
9716 }
9717 };
9718
9719 let mut result = Vec::new();
9720 for item in arr {
9721 let keep = interp.call_function(&pred, vec![item.clone()])?;
9722 if is_truthy(&keep) {
9723 result.push(item);
9724 } else {
9725 break;
9726 }
9727 }
9728 Ok(Value::Array(Rc::new(RefCell::new(result))))
9729 });
9730
9731 define(interp, "drop_while", Some(2), |interp, args| {
9733 let arr = match &args[0] {
9734 Value::Array(a) => a.borrow().clone(),
9735 _ => {
9736 return Err(RuntimeError::new(
9737 "drop_while: first argument must be an array",
9738 ))
9739 }
9740 };
9741 let pred = match &args[1] {
9742 Value::Function(f) => f.clone(),
9743 _ => {
9744 return Err(RuntimeError::new(
9745 "drop_while: second argument must be a function",
9746 ))
9747 }
9748 };
9749
9750 let mut dropping = true;
9751 let mut result = Vec::new();
9752 for item in arr {
9753 if dropping {
9754 let drop = interp.call_function(&pred, vec![item.clone()])?;
9755 if !is_truthy(&drop) {
9756 dropping = false;
9757 result.push(item);
9758 }
9759 } else {
9760 result.push(item);
9761 }
9762 }
9763 Ok(Value::Array(Rc::new(RefCell::new(result))))
9764 });
9765
9766 define(interp, "group_by", Some(2), |interp, args| {
9768 let arr = match &args[0] {
9769 Value::Array(a) => a.borrow().clone(),
9770 _ => {
9771 return Err(RuntimeError::new(
9772 "group_by: first argument must be an array",
9773 ))
9774 }
9775 };
9776 let key_fn = match &args[1] {
9777 Value::Function(f) => f.clone(),
9778 _ => {
9779 return Err(RuntimeError::new(
9780 "group_by: second argument must be a function",
9781 ))
9782 }
9783 };
9784
9785 let mut groups: Vec<Value> = Vec::new();
9786 let mut current_group: Vec<Value> = Vec::new();
9787 let mut current_key: Option<Value> = None;
9788
9789 for item in arr {
9790 let key = interp.call_function(&key_fn, vec![item.clone()])?;
9791 match ¤t_key {
9792 Some(k) if value_eq(k, &key) => {
9793 current_group.push(item);
9794 }
9795 _ => {
9796 if !current_group.is_empty() {
9797 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
9798 }
9799 current_group = vec![item];
9800 current_key = Some(key);
9801 }
9802 }
9803 }
9804 if !current_group.is_empty() {
9805 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
9806 }
9807
9808 Ok(Value::Array(Rc::new(RefCell::new(groups))))
9809 });
9810
9811 define(interp, "partition", Some(2), |interp, args| {
9813 let arr = match &args[0] {
9814 Value::Array(a) => a.borrow().clone(),
9815 _ => {
9816 return Err(RuntimeError::new(
9817 "partition: first argument must be an array",
9818 ))
9819 }
9820 };
9821 let pred = match &args[1] {
9822 Value::Function(f) => f.clone(),
9823 _ => {
9824 return Err(RuntimeError::new(
9825 "partition: second argument must be a function",
9826 ))
9827 }
9828 };
9829
9830 let mut true_items = Vec::new();
9831 let mut false_items = Vec::new();
9832
9833 for item in arr {
9834 let result = interp.call_function(&pred, vec![item.clone()])?;
9835 if is_truthy(&result) {
9836 true_items.push(item);
9837 } else {
9838 false_items.push(item);
9839 }
9840 }
9841
9842 Ok(Value::Tuple(Rc::new(vec![
9843 Value::Array(Rc::new(RefCell::new(true_items))),
9844 Value::Array(Rc::new(RefCell::new(false_items))),
9845 ])))
9846 });
9847
9848 define(interp, "interleave", Some(2), |_, args| {
9850 let arr1 = match &args[0] {
9851 Value::Array(a) => a.borrow().clone(),
9852 _ => {
9853 return Err(RuntimeError::new(
9854 "interleave: first argument must be an array",
9855 ))
9856 }
9857 };
9858 let arr2 = match &args[1] {
9859 Value::Array(a) => a.borrow().clone(),
9860 _ => {
9861 return Err(RuntimeError::new(
9862 "interleave: second argument must be an array",
9863 ))
9864 }
9865 };
9866
9867 let mut result = Vec::new();
9868 let mut i1 = arr1.into_iter();
9869 let mut i2 = arr2.into_iter();
9870
9871 loop {
9872 match (i1.next(), i2.next()) {
9873 (Some(a), Some(b)) => {
9874 result.push(a);
9875 result.push(b);
9876 }
9877 (Some(a), None) => {
9878 result.push(a);
9879 result.extend(i1);
9880 break;
9881 }
9882 (None, Some(b)) => {
9883 result.push(b);
9884 result.extend(i2);
9885 break;
9886 }
9887 (None, None) => break,
9888 }
9889 }
9890
9891 Ok(Value::Array(Rc::new(RefCell::new(result))))
9892 });
9893
9894 define(interp, "chunks", Some(2), |_, args| {
9896 let arr = match &args[0] {
9897 Value::Array(a) => a.borrow().clone(),
9898 _ => return Err(RuntimeError::new("chunks: first argument must be an array")),
9899 };
9900 let size = match &args[1] {
9901 Value::Int(n) if *n > 0 => *n as usize,
9902 _ => {
9903 return Err(RuntimeError::new(
9904 "chunks: second argument must be a positive integer",
9905 ))
9906 }
9907 };
9908
9909 let chunks: Vec<Value> = arr
9910 .chunks(size)
9911 .map(|chunk| Value::Array(Rc::new(RefCell::new(chunk.to_vec()))))
9912 .collect();
9913
9914 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
9915 });
9916
9917 define(interp, "windows", Some(2), |_, args| {
9919 let arr = match &args[0] {
9920 Value::Array(a) => a.borrow().clone(),
9921 _ => {
9922 return Err(RuntimeError::new(
9923 "windows: first argument must be an array",
9924 ))
9925 }
9926 };
9927 let size = match &args[1] {
9928 Value::Int(n) if *n > 0 => *n as usize,
9929 _ => {
9930 return Err(RuntimeError::new(
9931 "windows: second argument must be a positive integer",
9932 ))
9933 }
9934 };
9935
9936 if arr.len() < size {
9937 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9938 }
9939
9940 let windows: Vec<Value> = arr
9941 .windows(size)
9942 .map(|window| Value::Array(Rc::new(RefCell::new(window.to_vec()))))
9943 .collect();
9944
9945 Ok(Value::Array(Rc::new(RefCell::new(windows))))
9946 });
9947
9948 define(interp, "scan", Some(3), |interp, args| {
9950 let arr = match &args[0] {
9951 Value::Array(a) => a.borrow().clone(),
9952 _ => return Err(RuntimeError::new("scan: first argument must be an array")),
9953 };
9954 let init = args[1].clone();
9955 let func = match &args[2] {
9956 Value::Function(f) => f.clone(),
9957 _ => return Err(RuntimeError::new("scan: third argument must be a function")),
9958 };
9959
9960 let mut results = vec![init.clone()];
9961 let mut acc = init;
9962
9963 for item in arr {
9964 acc = interp.call_function(&func, vec![acc, item])?;
9965 results.push(acc.clone());
9966 }
9967
9968 Ok(Value::Array(Rc::new(RefCell::new(results))))
9969 });
9970
9971 define(interp, "frequencies", Some(1), |_, args| {
9973 let arr = match &args[0] {
9974 Value::Array(a) => a.borrow().clone(),
9975 _ => return Err(RuntimeError::new("frequencies: argument must be an array")),
9976 };
9977
9978 let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
9979 for item in &arr {
9980 let key = format!("{}", item);
9981 *counts.entry(key).or_insert(0) += 1;
9982 }
9983
9984 let result: std::collections::HashMap<String, Value> = counts
9985 .into_iter()
9986 .map(|(k, v)| (k, Value::Int(v)))
9987 .collect();
9988
9989 Ok(Value::Map(Rc::new(RefCell::new(result))))
9990 });
9991
9992 define(interp, "dedupe", Some(1), |_, args| {
9994 let arr = match &args[0] {
9995 Value::Array(a) => a.borrow().clone(),
9996 _ => return Err(RuntimeError::new("dedupe: argument must be an array")),
9997 };
9998
9999 let mut result = Vec::new();
10000 let mut prev: Option<Value> = None;
10001
10002 for item in arr {
10003 match &prev {
10004 Some(p) if value_eq(p, &item) => continue,
10005 _ => {
10006 result.push(item.clone());
10007 prev = Some(item);
10008 }
10009 }
10010 }
10011
10012 Ok(Value::Array(Rc::new(RefCell::new(result))))
10013 });
10014
10015 define(interp, "unique", Some(1), |_, args| {
10017 let arr = match &args[0] {
10018 Value::Array(a) => a.borrow().clone(),
10019 _ => return Err(RuntimeError::new("unique: argument must be an array")),
10020 };
10021
10022 let mut seen = std::collections::HashSet::new();
10023 let mut result = Vec::new();
10024
10025 for item in arr {
10026 let key = format!("{}", item);
10027 if seen.insert(key) {
10028 result.push(item);
10029 }
10030 }
10031
10032 Ok(Value::Array(Rc::new(RefCell::new(result))))
10033 });
10034}
10035
10036fn register_ranges(interp: &mut Interpreter) {
10038 define(interp, "range_step", Some(3), |_, args| {
10040 let start = match &args[0] {
10041 Value::Int(n) => *n,
10042 Value::Float(f) => *f as i64,
10043 _ => return Err(RuntimeError::new("range_step: start must be a number")),
10044 };
10045 let end = match &args[1] {
10046 Value::Int(n) => *n,
10047 Value::Float(f) => *f as i64,
10048 _ => return Err(RuntimeError::new("range_step: end must be a number")),
10049 };
10050 let step = match &args[2] {
10051 Value::Int(n) if *n != 0 => *n,
10052 Value::Float(f) if *f != 0.0 => *f as i64,
10053 _ => {
10054 return Err(RuntimeError::new(
10055 "range_step: step must be a non-zero number",
10056 ))
10057 }
10058 };
10059
10060 let mut result = Vec::new();
10061 if step > 0 {
10062 let mut i = start;
10063 while i < end {
10064 result.push(Value::Int(i));
10065 i += step;
10066 }
10067 } else {
10068 let mut i = start;
10069 while i > end {
10070 result.push(Value::Int(i));
10071 i += step;
10072 }
10073 }
10074
10075 Ok(Value::Array(Rc::new(RefCell::new(result))))
10076 });
10077
10078 define(interp, "linspace", Some(3), |_, args| {
10080 let start = match &args[0] {
10081 Value::Int(n) => *n as f64,
10082 Value::Float(f) => *f,
10083 _ => return Err(RuntimeError::new("linspace: start must be a number")),
10084 };
10085 let end = match &args[1] {
10086 Value::Int(n) => *n as f64,
10087 Value::Float(f) => *f,
10088 _ => return Err(RuntimeError::new("linspace: end must be a number")),
10089 };
10090 let n = match &args[2] {
10091 Value::Int(n) if *n > 0 => *n as usize,
10092 _ => {
10093 return Err(RuntimeError::new(
10094 "linspace: count must be a positive integer",
10095 ))
10096 }
10097 };
10098
10099 if n == 1 {
10100 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10101 start,
10102 )]))));
10103 }
10104
10105 let step = (end - start) / (n - 1) as f64;
10106 let result: Vec<Value> = (0..n)
10107 .map(|i| Value::Float(start + step * i as f64))
10108 .collect();
10109
10110 Ok(Value::Array(Rc::new(RefCell::new(result))))
10111 });
10112
10113 define(interp, "logspace", Some(3), |_, args| {
10115 let start_exp = match &args[0] {
10116 Value::Int(n) => *n as f64,
10117 Value::Float(f) => *f,
10118 _ => {
10119 return Err(RuntimeError::new(
10120 "logspace: start exponent must be a number",
10121 ))
10122 }
10123 };
10124 let end_exp = match &args[1] {
10125 Value::Int(n) => *n as f64,
10126 Value::Float(f) => *f,
10127 _ => return Err(RuntimeError::new("logspace: end exponent must be a number")),
10128 };
10129 let n = match &args[2] {
10130 Value::Int(n) if *n > 0 => *n as usize,
10131 _ => {
10132 return Err(RuntimeError::new(
10133 "logspace: count must be a positive integer",
10134 ))
10135 }
10136 };
10137
10138 if n == 1 {
10139 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10140 10f64.powf(start_exp),
10141 )]))));
10142 }
10143
10144 let step = (end_exp - start_exp) / (n - 1) as f64;
10145 let result: Vec<Value> = (0..n)
10146 .map(|i| Value::Float(10f64.powf(start_exp + step * i as f64)))
10147 .collect();
10148
10149 Ok(Value::Array(Rc::new(RefCell::new(result))))
10150 });
10151
10152 define(interp, "arange", Some(3), |_, args| {
10154 let start = match &args[0] {
10155 Value::Int(n) => *n as f64,
10156 Value::Float(f) => *f,
10157 _ => return Err(RuntimeError::new("arange: start must be a number")),
10158 };
10159 let stop = match &args[1] {
10160 Value::Int(n) => *n as f64,
10161 Value::Float(f) => *f,
10162 _ => return Err(RuntimeError::new("arange: stop must be a number")),
10163 };
10164 let step = match &args[2] {
10165 Value::Int(n) if *n != 0 => *n as f64,
10166 Value::Float(f) if *f != 0.0 => *f,
10167 _ => return Err(RuntimeError::new("arange: step must be a non-zero number")),
10168 };
10169
10170 let mut result = Vec::new();
10171 if step > 0.0 {
10172 let mut x = start;
10173 while x < stop {
10174 result.push(Value::Float(x));
10175 x += step;
10176 }
10177 } else {
10178 let mut x = start;
10179 while x > stop {
10180 result.push(Value::Float(x));
10181 x += step;
10182 }
10183 }
10184
10185 Ok(Value::Array(Rc::new(RefCell::new(result))))
10186 });
10187
10188 define(interp, "geomspace", Some(3), |_, args| {
10190 let start = match &args[0] {
10191 Value::Int(n) if *n > 0 => *n as f64,
10192 Value::Float(f) if *f > 0.0 => *f,
10193 _ => {
10194 return Err(RuntimeError::new(
10195 "geomspace: start must be a positive number",
10196 ))
10197 }
10198 };
10199 let end = match &args[1] {
10200 Value::Int(n) if *n > 0 => *n as f64,
10201 Value::Float(f) if *f > 0.0 => *f,
10202 _ => {
10203 return Err(RuntimeError::new(
10204 "geomspace: end must be a positive number",
10205 ))
10206 }
10207 };
10208 let n = match &args[2] {
10209 Value::Int(n) if *n > 0 => *n as usize,
10210 _ => {
10211 return Err(RuntimeError::new(
10212 "geomspace: count must be a positive integer",
10213 ))
10214 }
10215 };
10216
10217 if n == 1 {
10218 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10219 start,
10220 )]))));
10221 }
10222
10223 let ratio = (end / start).powf(1.0 / (n - 1) as f64);
10224 let result: Vec<Value> = (0..n)
10225 .map(|i| Value::Float(start * ratio.powi(i as i32)))
10226 .collect();
10227
10228 Ok(Value::Array(Rc::new(RefCell::new(result))))
10229 });
10230}
10231
10232fn register_bitwise(interp: &mut Interpreter) {
10234 define(interp, "bit_and", Some(2), |_, args| {
10235 let a = match &args[0] {
10236 Value::Int(n) => *n,
10237 _ => return Err(RuntimeError::new("bit_and: arguments must be integers")),
10238 };
10239 let b = match &args[1] {
10240 Value::Int(n) => *n,
10241 _ => return Err(RuntimeError::new("bit_and: arguments must be integers")),
10242 };
10243 Ok(Value::Int(a & b))
10244 });
10245
10246 define(interp, "bit_or", Some(2), |_, args| {
10247 let a = match &args[0] {
10248 Value::Int(n) => *n,
10249 _ => return Err(RuntimeError::new("bit_or: arguments must be integers")),
10250 };
10251 let b = match &args[1] {
10252 Value::Int(n) => *n,
10253 _ => return Err(RuntimeError::new("bit_or: arguments must be integers")),
10254 };
10255 Ok(Value::Int(a | b))
10256 });
10257
10258 define(interp, "bit_xor", Some(2), |_, args| {
10259 let a = match &args[0] {
10260 Value::Int(n) => *n,
10261 _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")),
10262 };
10263 let b = match &args[1] {
10264 Value::Int(n) => *n,
10265 _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")),
10266 };
10267 Ok(Value::Int(a ^ b))
10268 });
10269
10270 define(interp, "bit_not", Some(1), |_, args| {
10271 let a = match &args[0] {
10272 Value::Int(n) => *n,
10273 _ => return Err(RuntimeError::new("bit_not: argument must be an integer")),
10274 };
10275 Ok(Value::Int(!a))
10276 });
10277
10278 define(interp, "bit_shl", Some(2), |_, args| {
10279 let a = match &args[0] {
10280 Value::Int(n) => *n,
10281 _ => {
10282 return Err(RuntimeError::new(
10283 "bit_shl: first argument must be an integer",
10284 ))
10285 }
10286 };
10287 let b = match &args[1] {
10288 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10289 _ => return Err(RuntimeError::new("bit_shl: shift amount must be 0-63")),
10290 };
10291 Ok(Value::Int(a << b))
10292 });
10293
10294 define(interp, "bit_shr", Some(2), |_, args| {
10295 let a = match &args[0] {
10296 Value::Int(n) => *n,
10297 _ => {
10298 return Err(RuntimeError::new(
10299 "bit_shr: first argument must be an integer",
10300 ))
10301 }
10302 };
10303 let b = match &args[1] {
10304 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10305 _ => return Err(RuntimeError::new("bit_shr: shift amount must be 0-63")),
10306 };
10307 Ok(Value::Int(a >> b))
10308 });
10309
10310 define(interp, "popcount", Some(1), |_, args| {
10311 let a = match &args[0] {
10312 Value::Int(n) => *n,
10313 _ => return Err(RuntimeError::new("popcount: argument must be an integer")),
10314 };
10315 Ok(Value::Int(a.count_ones() as i64))
10316 });
10317
10318 define(interp, "leading_zeros", Some(1), |_, args| {
10319 let a = match &args[0] {
10320 Value::Int(n) => *n,
10321 _ => {
10322 return Err(RuntimeError::new(
10323 "leading_zeros: argument must be an integer",
10324 ))
10325 }
10326 };
10327 Ok(Value::Int(a.leading_zeros() as i64))
10328 });
10329
10330 define(interp, "trailing_zeros", Some(1), |_, args| {
10331 let a = match &args[0] {
10332 Value::Int(n) => *n,
10333 _ => {
10334 return Err(RuntimeError::new(
10335 "trailing_zeros: argument must be an integer",
10336 ))
10337 }
10338 };
10339 Ok(Value::Int(a.trailing_zeros() as i64))
10340 });
10341
10342 define(interp, "bit_test", Some(2), |_, args| {
10343 let a = match &args[0] {
10344 Value::Int(n) => *n,
10345 _ => {
10346 return Err(RuntimeError::new(
10347 "bit_test: first argument must be an integer",
10348 ))
10349 }
10350 };
10351 let pos = match &args[1] {
10352 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10353 _ => return Err(RuntimeError::new("bit_test: position must be 0-63")),
10354 };
10355 Ok(Value::Bool((a >> pos) & 1 == 1))
10356 });
10357
10358 define(interp, "bit_set", Some(2), |_, args| {
10359 let a = match &args[0] {
10360 Value::Int(n) => *n,
10361 _ => {
10362 return Err(RuntimeError::new(
10363 "bit_set: first argument must be an integer",
10364 ))
10365 }
10366 };
10367 let pos = match &args[1] {
10368 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10369 _ => return Err(RuntimeError::new("bit_set: position must be 0-63")),
10370 };
10371 Ok(Value::Int(a | (1 << pos)))
10372 });
10373
10374 define(interp, "bit_clear", Some(2), |_, args| {
10375 let a = match &args[0] {
10376 Value::Int(n) => *n,
10377 _ => {
10378 return Err(RuntimeError::new(
10379 "bit_clear: first argument must be an integer",
10380 ))
10381 }
10382 };
10383 let pos = match &args[1] {
10384 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10385 _ => return Err(RuntimeError::new("bit_clear: position must be 0-63")),
10386 };
10387 Ok(Value::Int(a & !(1 << pos)))
10388 });
10389
10390 define(interp, "bit_toggle", Some(2), |_, args| {
10391 let a = match &args[0] {
10392 Value::Int(n) => *n,
10393 _ => {
10394 return Err(RuntimeError::new(
10395 "bit_toggle: first argument must be an integer",
10396 ))
10397 }
10398 };
10399 let pos = match &args[1] {
10400 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10401 _ => return Err(RuntimeError::new("bit_toggle: position must be 0-63")),
10402 };
10403 Ok(Value::Int(a ^ (1 << pos)))
10404 });
10405
10406 define(interp, "to_binary", Some(1), |_, args| {
10407 let a = match &args[0] {
10408 Value::Int(n) => *n,
10409 _ => return Err(RuntimeError::new("to_binary: argument must be an integer")),
10410 };
10411 Ok(Value::String(Rc::new(format!("{:b}", a))))
10412 });
10413
10414 define(interp, "from_binary", Some(1), |_, args| {
10415 let s = match &args[0] {
10416 Value::String(s) => (**s).clone(),
10417 _ => return Err(RuntimeError::new("from_binary: argument must be a string")),
10418 };
10419 match i64::from_str_radix(&s, 2) {
10420 Ok(n) => Ok(Value::Int(n)),
10421 Err(_) => Err(RuntimeError::new("from_binary: invalid binary string")),
10422 }
10423 });
10424
10425 define(interp, "to_hex", Some(1), |_, args| {
10426 let a = match &args[0] {
10427 Value::Int(n) => *n,
10428 _ => return Err(RuntimeError::new("to_hex: argument must be an integer")),
10429 };
10430 Ok(Value::String(Rc::new(format!("{:x}", a))))
10431 });
10432
10433 define(interp, "from_hex", Some(1), |_, args| {
10434 let s = match &args[0] {
10435 Value::String(s) => s.trim_start_matches("0x").to_string(),
10436 _ => return Err(RuntimeError::new("from_hex: argument must be a string")),
10437 };
10438 match i64::from_str_radix(&s, 16) {
10439 Ok(n) => Ok(Value::Int(n)),
10440 Err(_) => Err(RuntimeError::new("from_hex: invalid hex string")),
10441 }
10442 });
10443
10444 define(interp, "to_octal", Some(1), |_, args| {
10445 let a = match &args[0] {
10446 Value::Int(n) => *n,
10447 _ => return Err(RuntimeError::new("to_octal: argument must be an integer")),
10448 };
10449 Ok(Value::String(Rc::new(format!("{:o}", a))))
10450 });
10451
10452 define(interp, "from_octal", Some(1), |_, args| {
10453 let s = match &args[0] {
10454 Value::String(s) => s.trim_start_matches("0o").to_string(),
10455 _ => return Err(RuntimeError::new("from_octal: argument must be a string")),
10456 };
10457 match i64::from_str_radix(&s, 8) {
10458 Ok(n) => Ok(Value::Int(n)),
10459 Err(_) => Err(RuntimeError::new("from_octal: invalid octal string")),
10460 }
10461 });
10462}
10463
10464fn register_format(interp: &mut Interpreter) {
10466 define(interp, "format", None, |_, args| {
10468 if args.is_empty() {
10469 return Err(RuntimeError::new(
10470 "format: requires at least a format string",
10471 ));
10472 }
10473 let template = match &args[0] {
10474 Value::String(s) => (**s).clone(),
10475 _ => return Err(RuntimeError::new("format: first argument must be a string")),
10476 };
10477 let mut result = template;
10478 for arg in &args[1..] {
10479 if let Some(pos) = result.find("{}") {
10480 result = format!("{}{}{}", &result[..pos], arg, &result[pos + 2..]);
10481 }
10482 }
10483 Ok(Value::String(Rc::new(result)))
10484 });
10485
10486 define(interp, "pad_left", Some(3), |_, args| {
10488 let s = match &args[0] {
10489 Value::String(s) => (**s).clone(),
10490 _ => {
10491 return Err(RuntimeError::new(
10492 "pad_left: first argument must be a string",
10493 ))
10494 }
10495 };
10496 let width = match &args[1] {
10497 Value::Int(n) if *n >= 0 => *n as usize,
10498 _ => {
10499 return Err(RuntimeError::new(
10500 "pad_left: width must be a non-negative integer",
10501 ))
10502 }
10503 };
10504 let pad_char = match &args[2] {
10505 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10506 Value::Char(c) => *c,
10507 _ => {
10508 return Err(RuntimeError::new(
10509 "pad_left: pad character must be a non-empty string or char",
10510 ))
10511 }
10512 };
10513 let char_count = s.chars().count();
10514 if char_count >= width {
10515 return Ok(Value::String(Rc::new(s)));
10516 }
10517 let padding: String = std::iter::repeat(pad_char)
10518 .take(width - char_count)
10519 .collect();
10520 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
10521 });
10522
10523 define(interp, "pad_right", Some(3), |_, args| {
10525 let s = match &args[0] {
10526 Value::String(s) => (**s).clone(),
10527 _ => {
10528 return Err(RuntimeError::new(
10529 "pad_right: first argument must be a string",
10530 ))
10531 }
10532 };
10533 let width = match &args[1] {
10534 Value::Int(n) if *n >= 0 => *n as usize,
10535 _ => {
10536 return Err(RuntimeError::new(
10537 "pad_right: width must be a non-negative integer",
10538 ))
10539 }
10540 };
10541 let pad_char = match &args[2] {
10542 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10543 Value::Char(c) => *c,
10544 _ => {
10545 return Err(RuntimeError::new(
10546 "pad_right: pad character must be a non-empty string or char",
10547 ))
10548 }
10549 };
10550 let char_count = s.chars().count();
10551 if char_count >= width {
10552 return Ok(Value::String(Rc::new(s)));
10553 }
10554 let padding: String = std::iter::repeat(pad_char)
10555 .take(width - char_count)
10556 .collect();
10557 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
10558 });
10559
10560 define(interp, "center", Some(3), |_, args| {
10562 let s = match &args[0] {
10563 Value::String(s) => (**s).clone(),
10564 _ => return Err(RuntimeError::new("center: first argument must be a string")),
10565 };
10566 let width = match &args[1] {
10567 Value::Int(n) if *n >= 0 => *n as usize,
10568 _ => {
10569 return Err(RuntimeError::new(
10570 "center: width must be a non-negative integer",
10571 ))
10572 }
10573 };
10574 let pad_char = match &args[2] {
10575 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10576 Value::Char(c) => *c,
10577 _ => {
10578 return Err(RuntimeError::new(
10579 "center: pad character must be a non-empty string or char",
10580 ))
10581 }
10582 };
10583 let char_count = s.chars().count();
10584 if char_count >= width {
10585 return Ok(Value::String(Rc::new(s)));
10586 }
10587 let total_padding = width - char_count;
10588 let left_padding = total_padding / 2;
10589 let right_padding = total_padding - left_padding;
10590 let left: String = std::iter::repeat(pad_char).take(left_padding).collect();
10591 let right: String = std::iter::repeat(pad_char).take(right_padding).collect();
10592 Ok(Value::String(Rc::new(format!("{}{}{}", left, s, right))))
10593 });
10594
10595 define(interp, "number_format", Some(1), |_, args| {
10597 let n = match &args[0] {
10598 Value::Int(n) => *n,
10599 Value::Float(f) => *f as i64,
10600 _ => {
10601 return Err(RuntimeError::new(
10602 "number_format: argument must be a number",
10603 ))
10604 }
10605 };
10606 let s = n.abs().to_string();
10607 let mut result = String::new();
10608 for (i, c) in s.chars().rev().enumerate() {
10609 if i > 0 && i % 3 == 0 {
10610 result.push(',');
10611 }
10612 result.push(c);
10613 }
10614 let formatted: String = result.chars().rev().collect();
10615 if n < 0 {
10616 Ok(Value::String(Rc::new(format!("-{}", formatted))))
10617 } else {
10618 Ok(Value::String(Rc::new(formatted)))
10619 }
10620 });
10621
10622 define(interp, "ordinal", Some(1), |_, args| {
10624 let n = match &args[0] {
10625 Value::Int(n) => *n,
10626 _ => return Err(RuntimeError::new("ordinal: argument must be an integer")),
10627 };
10628 let suffix = match (n % 10, n % 100) {
10629 (1, 11) => "th",
10630 (2, 12) => "th",
10631 (3, 13) => "th",
10632 (1, _) => "st",
10633 (2, _) => "nd",
10634 (3, _) => "rd",
10635 _ => "th",
10636 };
10637 Ok(Value::String(Rc::new(format!("{}{}", n, suffix))))
10638 });
10639
10640 define(interp, "pluralize", Some(3), |_, args| {
10642 let count = match &args[0] {
10643 Value::Int(n) => *n,
10644 _ => {
10645 return Err(RuntimeError::new(
10646 "pluralize: first argument must be an integer",
10647 ))
10648 }
10649 };
10650 let singular = match &args[1] {
10651 Value::String(s) => s.clone(),
10652 _ => {
10653 return Err(RuntimeError::new(
10654 "pluralize: second argument must be a string",
10655 ))
10656 }
10657 };
10658 let plural = match &args[2] {
10659 Value::String(s) => s.clone(),
10660 _ => {
10661 return Err(RuntimeError::new(
10662 "pluralize: third argument must be a string",
10663 ))
10664 }
10665 };
10666 if count == 1 || count == -1 {
10667 Ok(Value::String(singular))
10668 } else {
10669 Ok(Value::String(plural))
10670 }
10671 });
10672
10673 define(interp, "truncate", Some(2), |_, args| {
10675 let s = match &args[0] {
10676 Value::String(s) => (**s).clone(),
10677 _ => {
10678 return Err(RuntimeError::new(
10679 "truncate: first argument must be a string",
10680 ))
10681 }
10682 };
10683 let max_len = match &args[1] {
10684 Value::Int(n) if *n >= 0 => *n as usize,
10685 _ => {
10686 return Err(RuntimeError::new(
10687 "truncate: max length must be a non-negative integer",
10688 ))
10689 }
10690 };
10691 let char_count = s.chars().count();
10692 if char_count <= max_len {
10693 return Ok(Value::String(Rc::new(s)));
10694 }
10695 if max_len <= 3 {
10696 return Ok(Value::String(Rc::new(s.chars().take(max_len).collect())));
10697 }
10698 let truncated: String = s.chars().take(max_len - 3).collect();
10699 Ok(Value::String(Rc::new(format!("{}...", truncated))))
10700 });
10701
10702 define(interp, "word_wrap", Some(2), |_, args| {
10704 let s = match &args[0] {
10705 Value::String(s) => (**s).clone(),
10706 _ => {
10707 return Err(RuntimeError::new(
10708 "word_wrap: first argument must be a string",
10709 ))
10710 }
10711 };
10712 let width = match &args[1] {
10713 Value::Int(n) if *n > 0 => *n as usize,
10714 _ => {
10715 return Err(RuntimeError::new(
10716 "word_wrap: width must be a positive integer",
10717 ))
10718 }
10719 };
10720 let mut result = String::new();
10721 let mut line_len = 0;
10722 for word in s.split_whitespace() {
10723 if line_len > 0 && line_len + 1 + word.len() > width {
10724 result.push('\n');
10725 line_len = 0;
10726 } else if line_len > 0 {
10727 result.push(' ');
10728 line_len += 1;
10729 }
10730 result.push_str(word);
10731 line_len += word.len();
10732 }
10733 Ok(Value::String(Rc::new(result)))
10734 });
10735
10736 define(interp, "snake_case", Some(1), |_, args| {
10738 let s = match &args[0] {
10739 Value::String(s) => (**s).clone(),
10740 _ => return Err(RuntimeError::new("snake_case: argument must be a string")),
10741 };
10742 let mut result = String::new();
10743 for (i, c) in s.chars().enumerate() {
10744 if c.is_uppercase() {
10745 if i > 0 {
10746 result.push('_');
10747 }
10748 result.push(c.to_lowercase().next().unwrap());
10749 } else if c == ' ' || c == '-' {
10750 result.push('_');
10751 } else {
10752 result.push(c);
10753 }
10754 }
10755 Ok(Value::String(Rc::new(result)))
10756 });
10757
10758 define(interp, "camel_case", Some(1), |_, args| {
10760 let s = match &args[0] {
10761 Value::String(s) => (**s).clone(),
10762 _ => return Err(RuntimeError::new("camel_case: argument must be a string")),
10763 };
10764 let mut result = String::new();
10765 let mut capitalize_next = false;
10766 for (i, c) in s.chars().enumerate() {
10767 if c == '_' || c == '-' || c == ' ' {
10768 capitalize_next = true;
10769 } else if capitalize_next {
10770 result.push(c.to_uppercase().next().unwrap());
10771 capitalize_next = false;
10772 } else if i == 0 {
10773 result.push(c.to_lowercase().next().unwrap());
10774 } else {
10775 result.push(c);
10776 }
10777 }
10778 Ok(Value::String(Rc::new(result)))
10779 });
10780
10781 define(interp, "kebab_case", Some(1), |_, args| {
10783 let s = match &args[0] {
10784 Value::String(s) => (**s).clone(),
10785 _ => return Err(RuntimeError::new("kebab_case: argument must be a string")),
10786 };
10787 let mut result = String::new();
10788 for (i, c) in s.chars().enumerate() {
10789 if c.is_uppercase() {
10790 if i > 0 {
10791 result.push('-');
10792 }
10793 result.push(c.to_lowercase().next().unwrap());
10794 } else if c == '_' || c == ' ' {
10795 result.push('-');
10796 } else {
10797 result.push(c);
10798 }
10799 }
10800 Ok(Value::String(Rc::new(result)))
10801 });
10802
10803 define(interp, "title_case", Some(1), |_, args| {
10805 let s = match &args[0] {
10806 Value::String(s) => (**s).clone(),
10807 _ => return Err(RuntimeError::new("title_case: argument must be a string")),
10808 };
10809 let result: String = s
10810 .split_whitespace()
10811 .map(|word| {
10812 let mut chars = word.chars();
10813 match chars.next() {
10814 None => String::new(),
10815 Some(first) => {
10816 first.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase()
10817 }
10818 }
10819 })
10820 .collect::<Vec<_>>()
10821 .join(" ");
10822 Ok(Value::String(Rc::new(result)))
10823 });
10824}
10825
10826fn register_pattern(interp: &mut Interpreter) {
10834 define(interp, "type_of", Some(1), |_, args| {
10838 let type_name = match &args[0] {
10839 Value::Null => "null",
10840 Value::Bool(_) => "bool",
10841 Value::Int(_) => "int",
10842 Value::Float(_) => "float",
10843 Value::String(_) => "string",
10844 Value::Char(_) => "char",
10845 Value::Array(_) => "array",
10846 Value::Tuple(_) => "tuple",
10847 Value::Map(_) => "map",
10848 Value::Set(_) => "set",
10849 Value::Struct { name, .. } => {
10850 return Ok(Value::String(Rc::new(format!("struct:{}", name))))
10851 }
10852 Value::Variant {
10853 enum_name,
10854 variant_name,
10855 ..
10856 } => {
10857 return Ok(Value::String(Rc::new(format!(
10858 "{}::{}",
10859 enum_name, variant_name
10860 ))))
10861 }
10862 Value::Function(_) => "function",
10863 Value::BuiltIn(_) => "builtin",
10864 Value::Ref(_) => "ref",
10865 Value::Infinity => "infinity",
10866 Value::Empty => "empty",
10867 Value::Evidential { .. } => "evidential",
10868 Value::Affective { .. } => "affective",
10869 Value::Channel(_) => "channel",
10870 Value::ThreadHandle(_) => "thread",
10871 Value::Actor(_) => "actor",
10872 Value::Future(_) => "future",
10873 Value::VariantConstructor { .. } => "variant_constructor",
10874 Value::DefaultConstructor { .. } => "default_constructor",
10875 Value::Range { .. } => "range",
10876 };
10877 Ok(Value::String(Rc::new(type_name.to_string())))
10878 });
10879
10880 define(interp, "is_type", Some(2), |_, args| {
10882 let type_name = match &args[1] {
10883 Value::String(s) => s.to_lowercase(),
10884 _ => {
10885 return Err(RuntimeError::new(
10886 "is_type: second argument must be type name string",
10887 ))
10888 }
10889 };
10890 let matches = match (&args[0], type_name.as_str()) {
10891 (Value::Null, "null") => true,
10892 (Value::Bool(_), "bool") => true,
10893 (Value::Int(_), "int") | (Value::Int(_), "integer") => true,
10894 (Value::Float(_), "float") | (Value::Float(_), "number") => true,
10895 (Value::Int(_), "number") => true,
10896 (Value::String(_), "string") => true,
10897 (Value::Array(_), "array") | (Value::Array(_), "list") => true,
10898 (Value::Tuple(_), "tuple") => true,
10899 (Value::Map(_), "map") | (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
10900 (Value::Set(_), "set") => true,
10901 (Value::Function(_), "function") | (Value::Function(_), "fn") => true,
10902 (Value::BuiltIn(_), "function") | (Value::BuiltIn(_), "builtin") => true,
10903 (Value::Struct { name, .. }, t) => t == "struct" || t == &name.to_lowercase(),
10904 (Value::Variant { enum_name, .. }, t) => {
10905 t == "variant" || t == "enum" || t == &enum_name.to_lowercase()
10906 }
10907 (Value::Channel(_), "channel") => true,
10908 (Value::ThreadHandle(_), "thread") => true,
10909 (Value::Actor(_), "actor") => true,
10910 (Value::Future(_), "future") => true,
10911 _ => false,
10912 };
10913 Ok(Value::Bool(matches))
10914 });
10915
10916 define(interp, "is_null", Some(1), |_, args| {
10918 Ok(Value::Bool(matches!(&args[0], Value::Null)))
10919 });
10920 define(interp, "is_bool", Some(1), |_, args| {
10921 Ok(Value::Bool(matches!(&args[0], Value::Bool(_))))
10922 });
10923 define(interp, "is_int", Some(1), |_, args| {
10924 Ok(Value::Bool(matches!(&args[0], Value::Int(_))))
10925 });
10926 define(interp, "is_float", Some(1), |_, args| {
10927 Ok(Value::Bool(matches!(&args[0], Value::Float(_))))
10928 });
10929 define(interp, "is_number", Some(1), |_, args| {
10930 Ok(Value::Bool(matches!(
10931 &args[0],
10932 Value::Int(_) | Value::Float(_)
10933 )))
10934 });
10935 define(interp, "is_string", Some(1), |_, args| {
10936 Ok(Value::Bool(matches!(&args[0], Value::String(_))))
10937 });
10938 define(interp, "is_array", Some(1), |_, args| {
10939 Ok(Value::Bool(matches!(&args[0], Value::Array(_))))
10940 });
10941 define(interp, "is_tuple", Some(1), |_, args| {
10942 Ok(Value::Bool(matches!(&args[0], Value::Tuple(_))))
10943 });
10944 define(interp, "is_map", Some(1), |_, args| {
10945 Ok(Value::Bool(matches!(&args[0], Value::Map(_))))
10946 });
10947 define(interp, "is_set", Some(1), |_, args| {
10948 Ok(Value::Bool(matches!(&args[0], Value::Set(_))))
10949 });
10950 define(interp, "is_function", Some(1), |_, args| {
10951 Ok(Value::Bool(matches!(
10952 &args[0],
10953 Value::Function(_) | Value::BuiltIn(_)
10954 )))
10955 });
10956 define(interp, "is_struct", Some(1), |_, args| {
10957 Ok(Value::Bool(matches!(&args[0], Value::Struct { .. })))
10958 });
10959 define(interp, "is_variant", Some(1), |_, args| {
10960 Ok(Value::Bool(matches!(&args[0], Value::Variant { .. })))
10961 });
10962 define(interp, "is_future", Some(1), |_, args| {
10963 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
10964 });
10965 define(interp, "is_channel", Some(1), |_, args| {
10966 Ok(Value::Bool(matches!(&args[0], Value::Channel(_))))
10967 });
10968
10969 define(interp, "is_empty", Some(1), |_, args| {
10971 let empty = match &args[0] {
10972 Value::Null => true,
10973 Value::String(s) => s.is_empty(),
10974 Value::Array(a) => a.borrow().is_empty(),
10975 Value::Tuple(t) => t.is_empty(),
10976 Value::Map(m) => m.borrow().is_empty(),
10977 Value::Set(s) => s.borrow().is_empty(),
10978 _ => false,
10979 };
10980 Ok(Value::Bool(empty))
10981 });
10982
10983 define(interp, "match_regex", Some(2), |_, args| {
10987 let text = match &args[0] {
10988 Value::String(s) => (**s).clone(),
10989 _ => {
10990 return Err(RuntimeError::new(
10991 "match_regex: first argument must be a string",
10992 ))
10993 }
10994 };
10995 let pattern = match &args[1] {
10996 Value::String(s) => (**s).clone(),
10997 _ => {
10998 return Err(RuntimeError::new(
10999 "match_regex: second argument must be a regex pattern string",
11000 ))
11001 }
11002 };
11003
11004 let re = match Regex::new(&pattern) {
11005 Ok(r) => r,
11006 Err(e) => {
11007 return Err(RuntimeError::new(format!(
11008 "match_regex: invalid regex: {}",
11009 e
11010 )))
11011 }
11012 };
11013
11014 match re.captures(&text) {
11015 Some(caps) => {
11016 let mut captures: Vec<Value> = Vec::new();
11017 for i in 0..caps.len() {
11018 if let Some(m) = caps.get(i) {
11019 captures.push(Value::String(Rc::new(m.as_str().to_string())));
11020 } else {
11021 captures.push(Value::Null);
11022 }
11023 }
11024 Ok(Value::Array(Rc::new(RefCell::new(captures))))
11025 }
11026 None => Ok(Value::Null),
11027 }
11028 });
11029
11030 define(interp, "match_all_regex", Some(2), |_, args| {
11032 let text = match &args[0] {
11033 Value::String(s) => (**s).clone(),
11034 _ => {
11035 return Err(RuntimeError::new(
11036 "match_all_regex: first argument must be a string",
11037 ))
11038 }
11039 };
11040 let pattern = match &args[1] {
11041 Value::String(s) => (**s).clone(),
11042 _ => {
11043 return Err(RuntimeError::new(
11044 "match_all_regex: second argument must be a regex pattern string",
11045 ))
11046 }
11047 };
11048
11049 let re = match Regex::new(&pattern) {
11050 Ok(r) => r,
11051 Err(e) => {
11052 return Err(RuntimeError::new(format!(
11053 "match_all_regex: invalid regex: {}",
11054 e
11055 )))
11056 }
11057 };
11058
11059 let matches: Vec<Value> = re
11060 .find_iter(&text)
11061 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
11062 .collect();
11063 Ok(Value::Array(Rc::new(RefCell::new(matches))))
11064 });
11065
11066 define(interp, "capture_named", Some(2), |_, args| {
11068 let text = match &args[0] {
11069 Value::String(s) => (**s).clone(),
11070 _ => {
11071 return Err(RuntimeError::new(
11072 "capture_named: first argument must be a string",
11073 ))
11074 }
11075 };
11076 let pattern = match &args[1] {
11077 Value::String(s) => (**s).clone(),
11078 _ => {
11079 return Err(RuntimeError::new(
11080 "capture_named: second argument must be a regex pattern string",
11081 ))
11082 }
11083 };
11084
11085 let re = match Regex::new(&pattern) {
11086 Ok(r) => r,
11087 Err(e) => {
11088 return Err(RuntimeError::new(format!(
11089 "capture_named: invalid regex: {}",
11090 e
11091 )))
11092 }
11093 };
11094
11095 match re.captures(&text) {
11096 Some(caps) => {
11097 let mut result: HashMap<String, Value> = HashMap::new();
11098 for name in re.capture_names().flatten() {
11099 if let Some(m) = caps.name(name) {
11100 result.insert(
11101 name.to_string(),
11102 Value::String(Rc::new(m.as_str().to_string())),
11103 );
11104 }
11105 }
11106 Ok(Value::Map(Rc::new(RefCell::new(result))))
11107 }
11108 None => Ok(Value::Null),
11109 }
11110 });
11111
11112 define(interp, "match_struct", Some(2), |_, args| {
11116 let expected_name = match &args[1] {
11117 Value::String(s) => (**s).clone(),
11118 _ => {
11119 return Err(RuntimeError::new(
11120 "match_struct: second argument must be struct name string",
11121 ))
11122 }
11123 };
11124 match &args[0] {
11125 Value::Struct { name, .. } => Ok(Value::Bool(name == &expected_name)),
11126 _ => Ok(Value::Bool(false)),
11127 }
11128 });
11129
11130 define(interp, "match_variant", Some(3), |_, args| {
11132 let expected_enum = match &args[1] {
11133 Value::String(s) => (**s).clone(),
11134 _ => {
11135 return Err(RuntimeError::new(
11136 "match_variant: second argument must be enum name string",
11137 ))
11138 }
11139 };
11140 let expected_variant = match &args[2] {
11141 Value::String(s) => (**s).clone(),
11142 _ => {
11143 return Err(RuntimeError::new(
11144 "match_variant: third argument must be variant name string",
11145 ))
11146 }
11147 };
11148 match &args[0] {
11149 Value::Variant {
11150 enum_name,
11151 variant_name,
11152 ..
11153 } => Ok(Value::Bool(
11154 enum_name == &expected_enum && variant_name == &expected_variant,
11155 )),
11156 _ => Ok(Value::Bool(false)),
11157 }
11158 });
11159
11160 define(interp, "get_field", Some(2), |_, args| {
11162 let field_name = match &args[1] {
11163 Value::String(s) => (**s).clone(),
11164 _ => {
11165 return Err(RuntimeError::new(
11166 "get_field: second argument must be field name string",
11167 ))
11168 }
11169 };
11170 match &args[0] {
11171 Value::Struct { fields, .. } => Ok(fields
11172 .borrow()
11173 .get(&field_name)
11174 .cloned()
11175 .unwrap_or(Value::Null)),
11176 Value::Map(m) => Ok(m.borrow().get(&field_name).cloned().unwrap_or(Value::Null)),
11177 _ => Ok(Value::Null),
11178 }
11179 });
11180
11181 define(interp, "has_field", Some(2), |_, args| {
11183 let field_name = match &args[1] {
11184 Value::String(s) => (**s).clone(),
11185 _ => {
11186 return Err(RuntimeError::new(
11187 "has_field: second argument must be field name string",
11188 ))
11189 }
11190 };
11191 match &args[0] {
11192 Value::Struct { fields, .. } => {
11193 Ok(Value::Bool(fields.borrow().contains_key(&field_name)))
11194 }
11195 Value::Map(m) => Ok(Value::Bool(m.borrow().contains_key(&field_name))),
11196 _ => Ok(Value::Bool(false)),
11197 }
11198 });
11199
11200 define(interp, "get_fields", Some(1), |_, args| {
11202 let fields: Vec<Value> = match &args[0] {
11203 Value::Struct { fields, .. } => fields
11204 .borrow()
11205 .keys()
11206 .map(|k| Value::String(Rc::new(k.clone())))
11207 .collect(),
11208 Value::Map(m) => m
11209 .borrow()
11210 .keys()
11211 .map(|k| Value::String(Rc::new(k.clone())))
11212 .collect(),
11213 _ => {
11214 return Err(RuntimeError::new(
11215 "get_fields: argument must be struct or map",
11216 ))
11217 }
11218 };
11219 Ok(Value::Array(Rc::new(RefCell::new(fields))))
11220 });
11221
11222 define(interp, "struct_name", Some(1), |_, args| match &args[0] {
11224 Value::Struct { name, .. } => Ok(Value::String(Rc::new(name.clone()))),
11225 _ => Ok(Value::Null),
11226 });
11227
11228 define(interp, "variant_name", Some(1), |_, args| match &args[0] {
11230 Value::Variant { variant_name, .. } => Ok(Value::String(Rc::new(variant_name.clone()))),
11231 _ => Ok(Value::Null),
11232 });
11233
11234 define(interp, "variant_data", Some(1), |_, args| match &args[0] {
11236 Value::Variant { fields, .. } => match fields {
11237 Some(f) => Ok(Value::Array(Rc::new(RefCell::new((**f).clone())))),
11238 None => Ok(Value::Null),
11239 },
11240 _ => Ok(Value::Null),
11241 });
11242
11243 define(interp, "guard", Some(2), |_, args| {
11247 if is_truthy(&args[0]) {
11248 Ok(args[1].clone())
11249 } else {
11250 Ok(Value::Null)
11251 }
11252 });
11253
11254 define(interp, "when", Some(2), |interp, args| {
11256 if is_truthy(&args[0]) {
11257 match &args[1] {
11258 Value::Function(f) => interp.call_function(f, vec![]),
11259 other => Ok(other.clone()),
11260 }
11261 } else {
11262 Ok(Value::Null)
11263 }
11264 });
11265
11266 define(interp, "unless", Some(2), |interp, args| {
11268 if !is_truthy(&args[0]) {
11269 match &args[1] {
11270 Value::Function(f) => interp.call_function(f, vec![]),
11271 other => Ok(other.clone()),
11272 }
11273 } else {
11274 Ok(Value::Null)
11275 }
11276 });
11277
11278 define(interp, "cond", Some(1), |interp, args| {
11281 let clauses = match &args[0] {
11282 Value::Array(a) => a.borrow().clone(),
11283 _ => {
11284 return Err(RuntimeError::new(
11285 "cond: argument must be array of [condition, value] pairs",
11286 ))
11287 }
11288 };
11289
11290 for clause in clauses {
11291 let pair = match &clause {
11292 Value::Array(a) => a.borrow().clone(),
11293 Value::Tuple(t) => (**t).clone(),
11294 _ => {
11295 return Err(RuntimeError::new(
11296 "cond: each clause must be [condition, value] pair",
11297 ))
11298 }
11299 };
11300 if pair.len() != 2 {
11301 return Err(RuntimeError::new(
11302 "cond: each clause must have exactly 2 elements",
11303 ));
11304 }
11305
11306 if is_truthy(&pair[0]) {
11307 return match &pair[1] {
11308 Value::Function(f) => interp.call_function(f, vec![]),
11309 other => Ok(other.clone()),
11310 };
11311 }
11312 }
11313 Ok(Value::Null)
11314 });
11315
11316 define(interp, "case", Some(2), |interp, args| {
11319 let value = &args[0];
11320 let clauses = match &args[1] {
11321 Value::Array(a) => a.borrow().clone(),
11322 _ => {
11323 return Err(RuntimeError::new(
11324 "case: second argument must be array of [pattern, result] pairs",
11325 ))
11326 }
11327 };
11328
11329 for clause in clauses {
11330 let pair = match &clause {
11331 Value::Array(a) => a.borrow().clone(),
11332 Value::Tuple(t) => (**t).clone(),
11333 _ => {
11334 return Err(RuntimeError::new(
11335 "case: each clause must be [pattern, result] pair",
11336 ))
11337 }
11338 };
11339 if pair.len() != 2 {
11340 return Err(RuntimeError::new(
11341 "case: each clause must have exactly 2 elements",
11342 ));
11343 }
11344
11345 if value_eq(value, &pair[0]) {
11346 return match &pair[1] {
11347 Value::Function(f) => interp.call_function(f, vec![value.clone()]),
11348 other => Ok(other.clone()),
11349 };
11350 }
11351 }
11352 Ok(Value::Null)
11353 });
11354
11355 define(interp, "destructure_array", Some(2), |_, args| {
11359 let arr = match &args[0] {
11360 Value::Array(a) => a.borrow().clone(),
11361 Value::Tuple(t) => (**t).clone(),
11362 _ => {
11363 return Err(RuntimeError::new(
11364 "destructure_array: first argument must be array or tuple",
11365 ))
11366 }
11367 };
11368 let indices = match &args[1] {
11369 Value::Array(a) => a.borrow().clone(),
11370 _ => {
11371 return Err(RuntimeError::new(
11372 "destructure_array: second argument must be array of indices",
11373 ))
11374 }
11375 };
11376
11377 let mut result = Vec::new();
11378 for idx in indices {
11379 match idx {
11380 Value::Int(i) => {
11381 let i = if i < 0 { arr.len() as i64 + i } else { i } as usize;
11382 result.push(arr.get(i).cloned().unwrap_or(Value::Null));
11383 }
11384 _ => result.push(Value::Null),
11385 }
11386 }
11387 Ok(Value::Array(Rc::new(RefCell::new(result))))
11388 });
11389
11390 define(interp, "destructure_map", Some(2), |_, args| {
11392 let map = match &args[0] {
11393 Value::Map(m) => m.borrow().clone(),
11394 Value::Struct { fields, .. } => fields.borrow().clone(),
11395 _ => {
11396 return Err(RuntimeError::new(
11397 "destructure_map: first argument must be map or struct",
11398 ))
11399 }
11400 };
11401 let keys = match &args[1] {
11402 Value::Array(a) => a.borrow().clone(),
11403 _ => {
11404 return Err(RuntimeError::new(
11405 "destructure_map: second argument must be array of keys",
11406 ))
11407 }
11408 };
11409
11410 let mut result = Vec::new();
11411 for key in keys {
11412 match key {
11413 Value::String(k) => {
11414 result.push(map.get(&*k).cloned().unwrap_or(Value::Null));
11415 }
11416 _ => result.push(Value::Null),
11417 }
11418 }
11419 Ok(Value::Array(Rc::new(RefCell::new(result))))
11420 });
11421
11422 define(interp, "head_tail", Some(1), |_, args| {
11424 let arr = match &args[0] {
11425 Value::Array(a) => a.borrow().clone(),
11426 _ => return Err(RuntimeError::new("head_tail: argument must be array")),
11427 };
11428
11429 if arr.is_empty() {
11430 Ok(Value::Tuple(Rc::new(vec![
11431 Value::Null,
11432 Value::Array(Rc::new(RefCell::new(vec![]))),
11433 ])))
11434 } else {
11435 let head = arr[0].clone();
11436 let tail = arr[1..].to_vec();
11437 Ok(Value::Tuple(Rc::new(vec![
11438 head,
11439 Value::Array(Rc::new(RefCell::new(tail))),
11440 ])))
11441 }
11442 });
11443
11444 define(interp, "init_last", Some(1), |_, args| {
11446 let arr = match &args[0] {
11447 Value::Array(a) => a.borrow().clone(),
11448 _ => return Err(RuntimeError::new("init_last: argument must be array")),
11449 };
11450
11451 if arr.is_empty() {
11452 Ok(Value::Tuple(Rc::new(vec![
11453 Value::Array(Rc::new(RefCell::new(vec![]))),
11454 Value::Null,
11455 ])))
11456 } else {
11457 let last = arr[arr.len() - 1].clone();
11458 let init = arr[..arr.len() - 1].to_vec();
11459 Ok(Value::Tuple(Rc::new(vec![
11460 Value::Array(Rc::new(RefCell::new(init))),
11461 last,
11462 ])))
11463 }
11464 });
11465
11466 define(interp, "split_at", Some(2), |_, args| {
11468 let arr = match &args[0] {
11469 Value::Array(a) => a.borrow().clone(),
11470 _ => return Err(RuntimeError::new("split_at: first argument must be array")),
11471 };
11472 let idx = match &args[1] {
11473 Value::Int(i) => *i as usize,
11474 _ => {
11475 return Err(RuntimeError::new(
11476 "split_at: second argument must be integer",
11477 ))
11478 }
11479 };
11480
11481 let idx = idx.min(arr.len());
11482 let left = arr[..idx].to_vec();
11483 let right = arr[idx..].to_vec();
11484 Ok(Value::Tuple(Rc::new(vec![
11485 Value::Array(Rc::new(RefCell::new(left))),
11486 Value::Array(Rc::new(RefCell::new(right))),
11487 ])))
11488 });
11489
11490 define(interp, "unwrap_or", Some(2), |_, args| {
11494 if matches!(&args[0], Value::Null) {
11495 Ok(args[1].clone())
11496 } else {
11497 Ok(args[0].clone())
11498 }
11499 });
11500
11501 define(interp, "unwrap_or_else", Some(2), |interp, args| {
11503 if matches!(&args[0], Value::Null) {
11504 match &args[1] {
11505 Value::Function(f) => interp.call_function(f, vec![]),
11506 other => Ok(other.clone()),
11507 }
11508 } else {
11509 Ok(args[0].clone())
11510 }
11511 });
11512
11513 define(interp, "map_or", Some(3), |interp, args| {
11515 if matches!(&args[0], Value::Null) {
11516 Ok(args[1].clone())
11517 } else {
11518 match &args[2] {
11519 Value::Function(f) => interp.call_function(f, vec![args[0].clone()]),
11520 _ => Err(RuntimeError::new(
11521 "map_or: third argument must be a function",
11522 )),
11523 }
11524 }
11525 });
11526
11527 define(interp, "coalesce", Some(1), |_, args| {
11529 let values = match &args[0] {
11530 Value::Array(a) => a.borrow().clone(),
11531 _ => return Err(RuntimeError::new("coalesce: argument must be array")),
11532 };
11533
11534 for v in values {
11535 if !matches!(v, Value::Null) {
11536 return Ok(v);
11537 }
11538 }
11539 Ok(Value::Null)
11540 });
11541
11542 define(interp, "deep_eq", Some(2), |_, args| {
11546 Ok(Value::Bool(deep_value_eq(&args[0], &args[1])))
11547 });
11548
11549 define(interp, "same_type", Some(2), |_, args| {
11551 let same = match (&args[0], &args[1]) {
11552 (Value::Null, Value::Null) => true,
11553 (Value::Bool(_), Value::Bool(_)) => true,
11554 (Value::Int(_), Value::Int(_)) => true,
11555 (Value::Float(_), Value::Float(_)) => true,
11556 (Value::String(_), Value::String(_)) => true,
11557 (Value::Array(_), Value::Array(_)) => true,
11558 (Value::Tuple(_), Value::Tuple(_)) => true,
11559 (Value::Map(_), Value::Map(_)) => true,
11560 (Value::Set(_), Value::Set(_)) => true,
11561 (Value::Function(_), Value::Function(_)) => true,
11562 (Value::BuiltIn(_), Value::BuiltIn(_)) => true,
11563 (Value::Struct { name: n1, .. }, Value::Struct { name: n2, .. }) => n1 == n2,
11564 (Value::Variant { enum_name: e1, .. }, Value::Variant { enum_name: e2, .. }) => {
11565 e1 == e2
11566 }
11567 _ => false,
11568 };
11569 Ok(Value::Bool(same))
11570 });
11571
11572 define(interp, "compare", Some(2), |_, args| {
11574 let cmp = match (&args[0], &args[1]) {
11575 (Value::Int(a), Value::Int(b)) => a.cmp(b),
11576 (Value::Float(a), Value::Float(b)) => {
11577 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
11578 }
11579 (Value::Int(a), Value::Float(b)) => (*a as f64)
11580 .partial_cmp(b)
11581 .unwrap_or(std::cmp::Ordering::Equal),
11582 (Value::Float(a), Value::Int(b)) => a
11583 .partial_cmp(&(*b as f64))
11584 .unwrap_or(std::cmp::Ordering::Equal),
11585 (Value::String(a), Value::String(b)) => a.cmp(b),
11586 _ => {
11587 return Err(RuntimeError::new(
11588 "compare: can only compare numbers or strings",
11589 ))
11590 }
11591 };
11592 Ok(Value::Int(match cmp {
11593 std::cmp::Ordering::Less => -1,
11594 std::cmp::Ordering::Equal => 0,
11595 std::cmp::Ordering::Greater => 1,
11596 }))
11597 });
11598
11599 define(interp, "between", Some(3), |_, args| {
11601 let in_range = match (&args[0], &args[1], &args[2]) {
11602 (Value::Int(v), Value::Int(min), Value::Int(max)) => v >= min && v <= max,
11603 (Value::Float(v), Value::Float(min), Value::Float(max)) => v >= min && v <= max,
11604 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
11605 (*v as f64) >= (*min as f64) && (*v as f64) <= *max
11606 }
11607 (Value::Int(v), Value::Float(min), Value::Int(max)) => {
11608 (*v as f64) >= *min && (*v as f64) <= (*max as f64)
11609 }
11610 (Value::Float(v), Value::Int(min), Value::Int(max)) => {
11611 *v >= (*min as f64) && *v <= (*max as f64)
11612 }
11613 (Value::String(v), Value::String(min), Value::String(max)) => v >= min && v <= max,
11614 _ => {
11615 return Err(RuntimeError::new(
11616 "between: arguments must be comparable (numbers or strings)",
11617 ))
11618 }
11619 };
11620 Ok(Value::Bool(in_range))
11621 });
11622
11623 define(interp, "clamp", Some(3), |_, args| {
11625 match (&args[0], &args[1], &args[2]) {
11626 (Value::Int(v), Value::Int(min), Value::Int(max)) => {
11627 Ok(Value::Int((*v).max(*min).min(*max)))
11628 }
11629 (Value::Float(v), Value::Float(min), Value::Float(max)) => {
11630 Ok(Value::Float(v.max(*min).min(*max)))
11631 }
11632 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
11633 Ok(Value::Float((*v as f64).max(*min as f64).min(*max)))
11634 }
11635 _ => Err(RuntimeError::new("clamp: arguments must be numbers")),
11636 }
11637 });
11638}
11639
11640fn deep_value_eq(a: &Value, b: &Value) -> bool {
11642 match (a, b) {
11643 (Value::Null, Value::Null) => true,
11644 (Value::Bool(a), Value::Bool(b)) => a == b,
11645 (Value::Int(a), Value::Int(b)) => a == b,
11646 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
11647 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
11648 (*a as f64 - b).abs() < f64::EPSILON
11649 }
11650 (Value::String(a), Value::String(b)) => a == b,
11651 (Value::Array(a), Value::Array(b)) => {
11652 let a = a.borrow();
11653 let b = b.borrow();
11654 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
11655 }
11656 (Value::Tuple(a), Value::Tuple(b)) => {
11657 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
11658 }
11659 (Value::Map(a), Value::Map(b)) => {
11660 let a = a.borrow();
11661 let b = b.borrow();
11662 a.len() == b.len()
11663 && a.iter()
11664 .all(|(k, v)| b.get(k).map_or(false, |bv| deep_value_eq(v, bv)))
11665 }
11666 (Value::Set(a), Value::Set(b)) => {
11667 let a = a.borrow();
11668 let b = b.borrow();
11669 a.len() == b.len() && a.iter().all(|k| b.contains(k))
11670 }
11671 (
11672 Value::Struct {
11673 name: n1,
11674 fields: f1,
11675 },
11676 Value::Struct {
11677 name: n2,
11678 fields: f2,
11679 },
11680 ) => {
11681 let f1 = f1.borrow();
11682 let f2 = f2.borrow();
11683 n1 == n2
11684 && f1.len() == f2.len()
11685 && f1
11686 .iter()
11687 .all(|(k, v)| f2.get(k).map_or(false, |v2| deep_value_eq(v, v2)))
11688 }
11689 (
11690 Value::Variant {
11691 enum_name: e1,
11692 variant_name: v1,
11693 fields: d1,
11694 },
11695 Value::Variant {
11696 enum_name: e2,
11697 variant_name: v2,
11698 fields: d2,
11699 },
11700 ) => {
11701 if e1 != e2 || v1 != v2 {
11702 return false;
11703 }
11704 match (d1, d2) {
11705 (Some(f1), Some(f2)) => {
11706 f1.len() == f2.len()
11707 && f1.iter().zip(f2.iter()).all(|(x, y)| deep_value_eq(x, y))
11708 }
11709 (None, None) => true,
11710 _ => false,
11711 }
11712 }
11713 _ => false,
11714 }
11715}
11716
11717fn value_eq(a: &Value, b: &Value) -> bool {
11719 match (a, b) {
11720 (Value::Null, Value::Null) => true,
11721 (Value::Bool(a), Value::Bool(b)) => a == b,
11722 (Value::Int(a), Value::Int(b)) => a == b,
11723 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
11724 (Value::String(a), Value::String(b)) => a == b,
11725 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
11726 (*a as f64 - b).abs() < f64::EPSILON
11727 }
11728 _ => false,
11729 }
11730}
11731
11732fn register_devex(interp: &mut Interpreter) {
11740 define(interp, "debug", Some(1), |_, args| {
11744 let type_name = match &args[0] {
11745 Value::Null => "null".to_string(),
11746 Value::Bool(_) => "bool".to_string(),
11747 Value::Int(_) => "int".to_string(),
11748 Value::Float(_) => "float".to_string(),
11749 Value::String(_) => "string".to_string(),
11750 Value::Char(_) => "char".to_string(),
11751 Value::Array(a) => format!("array[{}]", a.borrow().len()),
11752 Value::Tuple(t) => format!("tuple[{}]", t.len()),
11753 Value::Map(m) => format!("map[{}]", m.borrow().len()),
11754 Value::Set(s) => format!("set[{}]", s.borrow().len()),
11755 Value::Struct { name, fields } => format!("struct {}[{}]", name, fields.borrow().len()),
11756 Value::Variant {
11757 enum_name,
11758 variant_name,
11759 ..
11760 } => format!("{}::{}", enum_name, variant_name),
11761 Value::Function(_) => "function".to_string(),
11762 Value::BuiltIn(_) => "builtin".to_string(),
11763 Value::Ref(_) => "ref".to_string(),
11764 Value::Infinity => "infinity".to_string(),
11765 Value::Empty => "empty".to_string(),
11766 Value::Evidential { evidence, .. } => format!("evidential[{:?}]", evidence),
11767 Value::Affective { affect, .. } => format!("affective[sarcasm={}]", affect.sarcasm),
11768 Value::Channel(_) => "channel".to_string(),
11769 Value::ThreadHandle(_) => "thread".to_string(),
11770 Value::Actor(_) => "actor".to_string(),
11771 Value::Future(_) => "future".to_string(),
11772 Value::VariantConstructor { enum_name, variant_name } => {
11773 format!("<constructor {}::{}>", enum_name, variant_name)
11774 }
11775 Value::DefaultConstructor { type_name } => {
11776 format!("<default {}>", type_name)
11777 }
11778 Value::Range { start, end, inclusive } => {
11779 match (start, end) {
11780 (Some(s), Some(e)) => if *inclusive {
11781 format!("range({}..={})", s, e)
11782 } else {
11783 format!("range({}..{})", s, e)
11784 },
11785 (Some(s), None) => format!("range({}..)", s),
11786 (None, Some(e)) => if *inclusive {
11787 format!("range(..={})", e)
11788 } else {
11789 format!("range(..{})", e)
11790 },
11791 (None, None) => "range(..)".to_string(),
11792 }
11793 }
11794 };
11795 let value_repr = format_value_debug(&args[0]);
11796 println!("[DEBUG] {}: {}", type_name, value_repr);
11797 Ok(args[0].clone())
11798 });
11799
11800 define(interp, "inspect", Some(1), |_, args| {
11802 Ok(Value::String(Rc::new(format_value_debug(&args[0]))))
11803 });
11804
11805 define(interp, "dbg", Some(1), |_, args| {
11807 println!("{}", format_value_debug(&args[0]));
11808 Ok(args[0].clone())
11809 });
11810
11811 define(interp, "trace", Some(2), |_, args| {
11813 let label = match &args[0] {
11814 Value::String(s) => (**s).clone(),
11815 _ => format_value_debug(&args[0]),
11816 };
11817 println!("[TRACE] {}: {}", label, format_value_debug(&args[1]));
11818 Ok(args[1].clone())
11819 });
11820
11821 define(interp, "pp", Some(1), |_, args| {
11823 println!("{}", pretty_print_value(&args[0], 0));
11824 Ok(Value::Null)
11825 });
11826
11827 define(interp, "assert_eq", Some(2), |_, args| {
11831 if deep_value_eq(&args[0], &args[1]) {
11832 Ok(Value::Bool(true))
11833 } else {
11834 Err(RuntimeError::new(format!(
11835 "Assertion failed: expected {} to equal {}",
11836 format_value_debug(&args[0]),
11837 format_value_debug(&args[1])
11838 )))
11839 }
11840 });
11841
11842 define(interp, "assert_ne", Some(2), |_, args| {
11844 if !deep_value_eq(&args[0], &args[1]) {
11845 Ok(Value::Bool(true))
11846 } else {
11847 Err(RuntimeError::new(format!(
11848 "Assertion failed: expected {} to not equal {}",
11849 format_value_debug(&args[0]),
11850 format_value_debug(&args[1])
11851 )))
11852 }
11853 });
11854
11855 define(interp, "assert_lt", Some(2), |_, args| {
11857 let cmp = devex_compare(&args[0], &args[1])?;
11858 if cmp < 0 {
11859 Ok(Value::Bool(true))
11860 } else {
11861 Err(RuntimeError::new(format!(
11862 "Assertion failed: expected {} < {}",
11863 format_value_debug(&args[0]),
11864 format_value_debug(&args[1])
11865 )))
11866 }
11867 });
11868
11869 define(interp, "assert_le", Some(2), |_, args| {
11871 let cmp = devex_compare(&args[0], &args[1])?;
11872 if cmp <= 0 {
11873 Ok(Value::Bool(true))
11874 } else {
11875 Err(RuntimeError::new(format!(
11876 "Assertion failed: expected {} <= {}",
11877 format_value_debug(&args[0]),
11878 format_value_debug(&args[1])
11879 )))
11880 }
11881 });
11882
11883 define(interp, "assert_gt", Some(2), |_, args| {
11885 let cmp = devex_compare(&args[0], &args[1])?;
11886 if cmp > 0 {
11887 Ok(Value::Bool(true))
11888 } else {
11889 Err(RuntimeError::new(format!(
11890 "Assertion failed: expected {} > {}",
11891 format_value_debug(&args[0]),
11892 format_value_debug(&args[1])
11893 )))
11894 }
11895 });
11896
11897 define(interp, "assert_ge", Some(2), |_, args| {
11899 let cmp = devex_compare(&args[0], &args[1])?;
11900 if cmp >= 0 {
11901 Ok(Value::Bool(true))
11902 } else {
11903 Err(RuntimeError::new(format!(
11904 "Assertion failed: expected {} >= {}",
11905 format_value_debug(&args[0]),
11906 format_value_debug(&args[1])
11907 )))
11908 }
11909 });
11910
11911 define(interp, "assert_true", Some(1), |_, args| {
11913 if is_truthy(&args[0]) {
11914 Ok(Value::Bool(true))
11915 } else {
11916 Err(RuntimeError::new(format!(
11917 "Assertion failed: expected {} to be truthy",
11918 format_value_debug(&args[0])
11919 )))
11920 }
11921 });
11922
11923 define(interp, "assert_false", Some(1), |_, args| {
11925 if !is_truthy(&args[0]) {
11926 Ok(Value::Bool(true))
11927 } else {
11928 Err(RuntimeError::new(format!(
11929 "Assertion failed: expected {} to be falsy",
11930 format_value_debug(&args[0])
11931 )))
11932 }
11933 });
11934
11935 define(interp, "assert_null", Some(1), |_, args| {
11937 if matches!(&args[0], Value::Null) {
11938 Ok(Value::Bool(true))
11939 } else {
11940 Err(RuntimeError::new(format!(
11941 "Assertion failed: expected null, got {}",
11942 format_value_debug(&args[0])
11943 )))
11944 }
11945 });
11946
11947 define(interp, "assert_not_null", Some(1), |_, args| {
11949 if !matches!(&args[0], Value::Null) {
11950 Ok(Value::Bool(true))
11951 } else {
11952 Err(RuntimeError::new(
11953 "Assertion failed: expected non-null value, got null",
11954 ))
11955 }
11956 });
11957
11958 define(interp, "assert_type", Some(2), |_, args| {
11960 let expected = match &args[1] {
11961 Value::String(s) => s.to_lowercase(),
11962 _ => {
11963 return Err(RuntimeError::new(
11964 "assert_type: second argument must be type name string",
11965 ))
11966 }
11967 };
11968 let actual = get_type_name(&args[0]).to_lowercase();
11969 if actual == expected || matches_type_alias(&args[0], &expected) {
11970 Ok(Value::Bool(true))
11971 } else {
11972 Err(RuntimeError::new(format!(
11973 "Assertion failed: expected type '{}', got '{}'",
11974 expected, actual
11975 )))
11976 }
11977 });
11978
11979 define(interp, "assert_contains", Some(2), |_, args| {
11981 let contains = match &args[0] {
11982 Value::Array(a) => a.borrow().iter().any(|v| deep_value_eq(v, &args[1])),
11983 Value::String(s) => {
11984 if let Value::String(sub) = &args[1] {
11985 s.contains(&**sub)
11986 } else {
11987 false
11988 }
11989 }
11990 Value::Map(m) => {
11991 if let Value::String(k) = &args[1] {
11992 m.borrow().contains_key(&**k)
11993 } else {
11994 false
11995 }
11996 }
11997 Value::Set(s) => {
11998 if let Value::String(k) = &args[1] {
11999 s.borrow().contains(&**k)
12000 } else {
12001 false
12002 }
12003 }
12004 _ => false,
12005 };
12006 if contains {
12007 Ok(Value::Bool(true))
12008 } else {
12009 Err(RuntimeError::new(format!(
12010 "Assertion failed: {} does not contain {}",
12011 format_value_debug(&args[0]),
12012 format_value_debug(&args[1])
12013 )))
12014 }
12015 });
12016
12017 define(interp, "assert_len", Some(2), |_, args| {
12019 let expected = match &args[1] {
12020 Value::Int(n) => *n as usize,
12021 _ => {
12022 return Err(RuntimeError::new(
12023 "assert_len: second argument must be integer",
12024 ))
12025 }
12026 };
12027 let actual = match &args[0] {
12028 Value::String(s) => s.len(),
12029 Value::Array(a) => a.borrow().len(),
12030 Value::Tuple(t) => t.len(),
12031 Value::Map(m) => m.borrow().len(),
12032 Value::Set(s) => s.borrow().len(),
12033 _ => {
12034 return Err(RuntimeError::new(
12035 "assert_len: first argument must be a collection",
12036 ))
12037 }
12038 };
12039 if actual == expected {
12040 Ok(Value::Bool(true))
12041 } else {
12042 Err(RuntimeError::new(format!(
12043 "Assertion failed: expected length {}, got {}",
12044 expected, actual
12045 )))
12046 }
12047 });
12048
12049 define(interp, "assert_match", Some(2), |_, args| {
12051 let text = match &args[0] {
12052 Value::String(s) => (**s).clone(),
12053 _ => {
12054 return Err(RuntimeError::new(
12055 "assert_match: first argument must be string",
12056 ))
12057 }
12058 };
12059 let pattern = match &args[1] {
12060 Value::String(s) => (**s).clone(),
12061 _ => {
12062 return Err(RuntimeError::new(
12063 "assert_match: second argument must be regex pattern",
12064 ))
12065 }
12066 };
12067 let re =
12068 Regex::new(&pattern).map_err(|e| RuntimeError::new(format!("Invalid regex: {}", e)))?;
12069 if re.is_match(&text) {
12070 Ok(Value::Bool(true))
12071 } else {
12072 Err(RuntimeError::new(format!(
12073 "Assertion failed: '{}' does not match pattern '{}'",
12074 text, pattern
12075 )))
12076 }
12077 });
12078
12079 define(interp, "test", Some(2), |interp, args| {
12083 let name = match &args[0] {
12084 Value::String(s) => (**s).clone(),
12085 _ => {
12086 return Err(RuntimeError::new(
12087 "test: first argument must be test name string",
12088 ))
12089 }
12090 };
12091 let func = match &args[1] {
12092 Value::Function(f) => f.clone(),
12093 _ => {
12094 return Err(RuntimeError::new(
12095 "test: second argument must be test function",
12096 ))
12097 }
12098 };
12099
12100 let start = Instant::now();
12101 let result = interp.call_function(&func, vec![]);
12102 let elapsed = start.elapsed();
12103
12104 match result {
12105 Ok(_) => {
12106 println!("✓ {} ({:.2}ms)", name, elapsed.as_secs_f64() * 1000.0);
12107 Ok(Value::Bool(true))
12108 }
12109 Err(e) => {
12110 println!(
12111 "✗ {} ({:.2}ms): {}",
12112 name,
12113 elapsed.as_secs_f64() * 1000.0,
12114 e
12115 );
12116 Ok(Value::Bool(false))
12117 }
12118 }
12119 });
12120
12121 define(interp, "skip", Some(1), |_, args| {
12123 let reason = match &args[0] {
12124 Value::String(s) => (**s).clone(),
12125 _ => "skipped".to_string(),
12126 };
12127 println!("⊘ {}", reason);
12128 Ok(Value::Null)
12129 });
12130
12131 define(interp, "profile", Some(1), |interp, args| {
12135 let func = match &args[0] {
12136 Value::Function(f) => f.clone(),
12137 _ => return Err(RuntimeError::new("profile: argument must be function")),
12138 };
12139
12140 let start = Instant::now();
12141 let result = interp.call_function(&func, vec![])?;
12142 let elapsed = start.elapsed();
12143
12144 let mut timing = HashMap::new();
12145 timing.insert(
12146 "ms".to_string(),
12147 Value::Float(elapsed.as_secs_f64() * 1000.0),
12148 );
12149 timing.insert("us".to_string(), Value::Float(elapsed.as_micros() as f64));
12150 timing.insert("ns".to_string(), Value::Int(elapsed.as_nanos() as i64));
12151
12152 Ok(Value::Tuple(Rc::new(vec![
12153 result,
12154 Value::Map(Rc::new(RefCell::new(timing))),
12155 ])))
12156 });
12157
12158 define(interp, "measure", Some(2), |interp, args| {
12160 let func = match &args[0] {
12161 Value::Function(f) => f.clone(),
12162 _ => {
12163 return Err(RuntimeError::new(
12164 "measure: first argument must be function",
12165 ))
12166 }
12167 };
12168 let iterations = match &args[1] {
12169 Value::Int(n) => *n as usize,
12170 _ => {
12171 return Err(RuntimeError::new(
12172 "measure: second argument must be iteration count",
12173 ))
12174 }
12175 };
12176
12177 let mut times: Vec<f64> = Vec::new();
12178 let mut last_result = Value::Null;
12179
12180 for _ in 0..iterations {
12181 let start = Instant::now();
12182 last_result = interp.call_function(&func, vec![])?;
12183 times.push(start.elapsed().as_secs_f64() * 1000.0);
12184 }
12185
12186 let sum: f64 = times.iter().sum();
12187 let avg = sum / iterations as f64;
12188 let min = times.iter().cloned().fold(f64::INFINITY, f64::min);
12189 let max = times.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
12190
12191 let variance: f64 =
12192 times.iter().map(|t| (t - avg).powi(2)).sum::<f64>() / iterations as f64;
12193 let stddev = variance.sqrt();
12194
12195 let mut stats = HashMap::new();
12196 stats.insert("iterations".to_string(), Value::Int(iterations as i64));
12197 stats.insert("total_ms".to_string(), Value::Float(sum));
12198 stats.insert("avg_ms".to_string(), Value::Float(avg));
12199 stats.insert("min_ms".to_string(), Value::Float(min));
12200 stats.insert("max_ms".to_string(), Value::Float(max));
12201 stats.insert("stddev_ms".to_string(), Value::Float(stddev));
12202
12203 Ok(Value::Tuple(Rc::new(vec![
12204 last_result,
12205 Value::Map(Rc::new(RefCell::new(stats))),
12206 ])))
12207 });
12208
12209 define(interp, "help", Some(1), |_, args| {
12213 let name = match &args[0] {
12214 Value::String(s) => (**s).clone(),
12215 Value::BuiltIn(f) => f.name.clone(),
12216 _ => {
12217 return Err(RuntimeError::new(
12218 "help: argument must be function name or builtin",
12219 ))
12220 }
12221 };
12222
12223 let doc = get_function_doc(&name);
12225 Ok(Value::String(Rc::new(doc)))
12226 });
12227
12228 define(interp, "list_builtins", Some(0), |_, _| {
12230 let categories = vec![
12231 "Core: print, println, assert, panic, len, type_of",
12232 "Math: abs, floor, ceil, round, sqrt, pow, log, sin, cos, tan",
12233 "Collections: map, filter, reduce, zip, flatten, first, last, sort, reverse",
12234 "Strings: upper, lower, trim, split, join, contains, replace, format",
12235 "IO: read_file, write_file, file_exists, read_line",
12236 "Time: now, sleep, timestamp, format_time",
12237 "JSON: json_parse, json_stringify",
12238 "Crypto: sha256, sha512, md5, base64_encode, base64_decode",
12239 "Regex: regex_match, regex_replace, regex_split",
12240 "Pattern: type_of, is_type, match_regex, match_struct, guard, when",
12241 "DevEx: debug, inspect, trace, assert_eq, assert_ne, test, profile",
12242 ];
12243 let values: Vec<Value> = categories
12244 .iter()
12245 .map(|s| Value::String(Rc::new(s.to_string())))
12246 .collect();
12247 Ok(Value::Array(Rc::new(RefCell::new(values))))
12248 });
12249
12250 define(interp, "todo", Some(0), |_, _| {
12254 Err(RuntimeError::new("not yet implemented"))
12255 });
12256
12257 define(interp, "unreachable", Some(0), |_, _| {
12259 Err(RuntimeError::new("reached unreachable code"))
12260 });
12261
12262 define(interp, "unimplemented", Some(1), |_, args| {
12264 let msg = match &args[0] {
12265 Value::String(s) => (**s).clone(),
12266 _ => "unimplemented".to_string(),
12267 };
12268 Err(RuntimeError::new(format!("unimplemented: {}", msg)))
12269 });
12270
12271 define(interp, "deprecated", Some(2), |_, args| {
12273 let msg = match &args[0] {
12274 Value::String(s) => (**s).clone(),
12275 _ => "deprecated".to_string(),
12276 };
12277 eprintln!("[DEPRECATED] {}", msg);
12278 Ok(args[1].clone())
12279 });
12280
12281 define(interp, "version", Some(0), |_, _| {
12283 let mut info = HashMap::new();
12284 info.insert(
12285 "sigil".to_string(),
12286 Value::String(Rc::new("0.1.0".to_string())),
12287 );
12288 info.insert(
12289 "stdlib".to_string(),
12290 Value::String(Rc::new("7.0".to_string())),
12291 );
12292 info.insert(
12293 "phase".to_string(),
12294 Value::String(Rc::new("Phase 7 - DevEx".to_string())),
12295 );
12296 Ok(Value::Map(Rc::new(RefCell::new(info))))
12297 });
12298}
12299
12300fn format_value_debug(value: &Value) -> String {
12302 match value {
12303 Value::Null => "null".to_string(),
12304 Value::Bool(b) => b.to_string(),
12305 Value::Int(n) => n.to_string(),
12306 Value::Float(f) => format!("{:.6}", f),
12307 Value::String(s) => format!("\"{}\"", s),
12308 Value::Char(c) => format!("'{}'", c),
12309 Value::Array(a) => {
12310 let items: Vec<String> = a.borrow().iter().take(10).map(format_value_debug).collect();
12311 if a.borrow().len() > 10 {
12312 format!(
12313 "[{}, ... ({} more)]",
12314 items.join(", "),
12315 a.borrow().len() - 10
12316 )
12317 } else {
12318 format!("[{}]", items.join(", "))
12319 }
12320 }
12321 Value::Tuple(t) => {
12322 let items: Vec<String> = t.iter().map(format_value_debug).collect();
12323 format!("({})", items.join(", "))
12324 }
12325 Value::Map(m) => {
12326 let items: Vec<String> = m
12327 .borrow()
12328 .iter()
12329 .take(5)
12330 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
12331 .collect();
12332 if m.borrow().len() > 5 {
12333 format!(
12334 "{{{}, ... ({} more)}}",
12335 items.join(", "),
12336 m.borrow().len() - 5
12337 )
12338 } else {
12339 format!("{{{}}}", items.join(", "))
12340 }
12341 }
12342 Value::Set(s) => {
12343 let items: Vec<String> = s.borrow().iter().take(5).cloned().collect();
12344 if s.borrow().len() > 5 {
12345 format!(
12346 "#{{{}, ... ({} more)}}",
12347 items.join(", "),
12348 s.borrow().len() - 5
12349 )
12350 } else {
12351 format!("#{{{}}}", items.join(", "))
12352 }
12353 }
12354 Value::Struct { name, fields } => {
12355 let items: Vec<String> = fields
12356 .borrow()
12357 .iter()
12358 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
12359 .collect();
12360 format!("{} {{{}}}", name, items.join(", "))
12361 }
12362 Value::Variant {
12363 enum_name,
12364 variant_name,
12365 fields,
12366 } => match fields {
12367 Some(f) => {
12368 let items: Vec<String> = f.iter().map(format_value_debug).collect();
12369 format!("{}::{}({})", enum_name, variant_name, items.join(", "))
12370 }
12371 None => format!("{}::{}", enum_name, variant_name),
12372 },
12373 Value::Function(_) => "<function>".to_string(),
12374 Value::BuiltIn(f) => format!("<builtin:{}>", f.name),
12375 Value::Ref(r) => format!("&{}", format_value_debug(&r.borrow())),
12376 Value::Infinity => "∞".to_string(),
12377 Value::Empty => "∅".to_string(),
12378 Value::Evidential { value, evidence } => {
12379 format!("{:?}({})", evidence, format_value_debug(value))
12380 }
12381 Value::Affective { value, affect } => {
12382 let mut markers = Vec::new();
12383 if let Some(s) = &affect.sentiment {
12384 markers.push(format!("{:?}", s));
12385 }
12386 if affect.sarcasm {
12387 markers.push("sarcasm".to_string());
12388 }
12389 if let Some(i) = &affect.intensity {
12390 markers.push(format!("{:?}", i));
12391 }
12392 if let Some(f) = &affect.formality {
12393 markers.push(format!("{:?}", f));
12394 }
12395 if let Some(e) = &affect.emotion {
12396 markers.push(format!("{:?}", e));
12397 }
12398 if let Some(c) = &affect.confidence {
12399 markers.push(format!("{:?}", c));
12400 }
12401 format!("{}[{}]", format_value_debug(value), markers.join(","))
12402 }
12403 Value::Channel(_) => "<channel>".to_string(),
12404 Value::ThreadHandle(_) => "<thread>".to_string(),
12405 Value::Actor(_) => "<actor>".to_string(),
12406 Value::Future(_) => "<future>".to_string(),
12407 Value::VariantConstructor { enum_name, variant_name } => {
12408 format!("<constructor {}::{}>", enum_name, variant_name)
12409 }
12410 Value::DefaultConstructor { type_name } => {
12411 format!("<default {}>", type_name)
12412 }
12413 Value::Range { start, end, inclusive } => {
12414 match (start, end) {
12415 (Some(s), Some(e)) => if *inclusive {
12416 format!("{}..={}", s, e)
12417 } else {
12418 format!("{}..{}", s, e)
12419 },
12420 (Some(s), None) => format!("{}..", s),
12421 (None, Some(e)) => if *inclusive {
12422 format!("..={}", e)
12423 } else {
12424 format!("..{}", e)
12425 },
12426 (None, None) => "..".to_string(),
12427 }
12428 }
12429 }
12430}
12431
12432fn pretty_print_value(value: &Value, indent: usize) -> String {
12434 let prefix = " ".repeat(indent);
12435 match value {
12436 Value::Array(a) => {
12437 if a.borrow().is_empty() {
12438 "[]".to_string()
12439 } else {
12440 let items: Vec<String> = a
12441 .borrow()
12442 .iter()
12443 .map(|v| {
12444 format!(
12445 "{}{}",
12446 " ".repeat(indent + 1),
12447 pretty_print_value(v, indent + 1)
12448 )
12449 })
12450 .collect();
12451 format!("[\n{}\n{}]", items.join(",\n"), prefix)
12452 }
12453 }
12454 Value::Map(m) => {
12455 if m.borrow().is_empty() {
12456 "{}".to_string()
12457 } else {
12458 let items: Vec<String> = m
12459 .borrow()
12460 .iter()
12461 .map(|(k, v)| {
12462 format!(
12463 "{}\"{}\": {}",
12464 " ".repeat(indent + 1),
12465 k,
12466 pretty_print_value(v, indent + 1)
12467 )
12468 })
12469 .collect();
12470 format!("{{\n{}\n{}}}", items.join(",\n"), prefix)
12471 }
12472 }
12473 Value::Struct { name, fields } => {
12474 if fields.borrow().is_empty() {
12475 format!("{} {{}}", name)
12476 } else {
12477 let items: Vec<String> = fields
12478 .borrow()
12479 .iter()
12480 .map(|(k, v)| {
12481 format!(
12482 "{}{}: {}",
12483 " ".repeat(indent + 1),
12484 k,
12485 pretty_print_value(v, indent + 1)
12486 )
12487 })
12488 .collect();
12489 format!("{} {{\n{}\n{}}}", name, items.join(",\n"), prefix)
12490 }
12491 }
12492 _ => format_value_debug(value),
12493 }
12494}
12495
12496fn devex_compare(a: &Value, b: &Value) -> Result<i64, RuntimeError> {
12498 match (a, b) {
12499 (Value::Int(a), Value::Int(b)) => Ok(if a < b {
12500 -1
12501 } else if a > b {
12502 1
12503 } else {
12504 0
12505 }),
12506 (Value::Float(a), Value::Float(b)) => Ok(if a < b {
12507 -1
12508 } else if a > b {
12509 1
12510 } else {
12511 0
12512 }),
12513 (Value::Int(a), Value::Float(b)) => {
12514 let a = *a as f64;
12515 Ok(if a < *b {
12516 -1
12517 } else if a > *b {
12518 1
12519 } else {
12520 0
12521 })
12522 }
12523 (Value::Float(a), Value::Int(b)) => {
12524 let b = *b as f64;
12525 Ok(if *a < b {
12526 -1
12527 } else if *a > b {
12528 1
12529 } else {
12530 0
12531 })
12532 }
12533 (Value::String(a), Value::String(b)) => Ok(if a < b {
12534 -1
12535 } else if a > b {
12536 1
12537 } else {
12538 0
12539 }),
12540 _ => Err(RuntimeError::new("cannot compare these types")),
12541 }
12542}
12543
12544fn get_type_name(value: &Value) -> String {
12546 match value {
12547 Value::Null => "null".to_string(),
12548 Value::Bool(_) => "bool".to_string(),
12549 Value::Int(_) => "int".to_string(),
12550 Value::Float(_) => "float".to_string(),
12551 Value::String(_) => "string".to_string(),
12552 Value::Char(_) => "char".to_string(),
12553 Value::Array(_) => "array".to_string(),
12554 Value::Tuple(_) => "tuple".to_string(),
12555 Value::Map(_) => "map".to_string(),
12556 Value::Set(_) => "set".to_string(),
12557 Value::Struct { name, .. } => name.clone(),
12558 Value::Variant { enum_name, .. } => enum_name.clone(),
12559 Value::Function(_) => "function".to_string(),
12560 Value::BuiltIn(_) => "builtin".to_string(),
12561 Value::Ref(_) => "ref".to_string(),
12562 Value::Infinity => "infinity".to_string(),
12563 Value::Empty => "empty".to_string(),
12564 Value::Evidential { .. } => "evidential".to_string(),
12565 Value::Affective { .. } => "affective".to_string(),
12566 Value::Channel(_) => "channel".to_string(),
12567 Value::ThreadHandle(_) => "thread".to_string(),
12568 Value::Actor(_) => "actor".to_string(),
12569 Value::Future(_) => "future".to_string(),
12570 Value::VariantConstructor { enum_name, .. } => format!("{}_constructor", enum_name),
12571 Value::DefaultConstructor { type_name } => format!("{}_default", type_name),
12572 Value::Range { .. } => "range".to_string(),
12573 }
12574}
12575
12576fn matches_type_alias(value: &Value, type_name: &str) -> bool {
12578 match (value, type_name) {
12579 (Value::Int(_), "number") | (Value::Float(_), "number") => true,
12580 (Value::Int(_), "integer") => true,
12581 (Value::Array(_), "list") => true,
12582 (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
12583 (Value::Function(_), "fn") | (Value::BuiltIn(_), "fn") => true,
12584 (Value::BuiltIn(_), "function") => true,
12585 _ => false,
12586 }
12587}
12588
12589fn get_function_doc(name: &str) -> String {
12591 match name {
12592 "print" => "print(value) - Print value to stdout".to_string(),
12593 "println" => "println(value) - Print value with newline".to_string(),
12594 "len" => "len(collection) - Get length of string, array, map, or set".to_string(),
12595 "type_of" => "type_of(value) - Get type name as string".to_string(),
12596 "assert" => "assert(condition) - Assert condition is truthy, panic if false".to_string(),
12597 "assert_eq" => "assert_eq(a, b) - Assert two values are deeply equal".to_string(),
12598 "debug" => "debug(value) - Print value with type info and return it".to_string(),
12599 "map" => "map(array, fn) - Apply function to each element".to_string(),
12600 "filter" => "filter(array, fn) - Keep elements where predicate is true".to_string(),
12601 "reduce" => "reduce(array, init, fn) - Fold array with function".to_string(),
12602 "range" => "range(start, end) - Create array of integers from start to end".to_string(),
12603 "sum" => "sum(array) - Sum all numeric elements".to_string(),
12604 "product" => "product(array) - Multiply all numeric elements".to_string(),
12605 "sort" => "sort(array) - Sort array in ascending order".to_string(),
12606 "reverse" => "reverse(array) - Reverse array order".to_string(),
12607 "join" => "join(array, sep) - Join array elements with separator".to_string(),
12608 "split" => "split(string, sep) - Split string by separator".to_string(),
12609 "trim" => "trim(string) - Remove leading/trailing whitespace".to_string(),
12610 "upper" => "upper(string) - Convert to uppercase".to_string(),
12611 "lower" => "lower(string) - Convert to lowercase".to_string(),
12612 _ => format!("No documentation available for '{}'", name),
12613 }
12614}
12615
12616fn register_soa(interp: &mut Interpreter) {
12628 define(interp, "aos_to_soa", Some(2), |_, args| {
12631 let arr = match &args[0] {
12632 Value::Array(arr) => arr.borrow().clone(),
12633 _ => {
12634 return Err(RuntimeError::new(
12635 "aos_to_soa: first argument must be array",
12636 ))
12637 }
12638 };
12639 let keys = match &args[1] {
12640 Value::Array(keys) => keys.borrow().clone(),
12641 _ => {
12642 return Err(RuntimeError::new(
12643 "aos_to_soa: second argument must be array of keys",
12644 ))
12645 }
12646 };
12647
12648 if arr.is_empty() {
12649 let mut result = HashMap::new();
12651 for key in &keys {
12652 if let Value::String(k) = key {
12653 result.insert((**k).clone(), Value::Array(Rc::new(RefCell::new(vec![]))));
12654 }
12655 }
12656 return Ok(Value::Map(Rc::new(RefCell::new(result))));
12657 }
12658
12659 let key_names: Vec<String> = keys
12661 .iter()
12662 .filter_map(|k| {
12663 if let Value::String(s) = k {
12664 Some((**s).clone())
12665 } else {
12666 None
12667 }
12668 })
12669 .collect();
12670
12671 let mut soa: HashMap<String, Vec<Value>> = HashMap::new();
12673 for key in &key_names {
12674 soa.insert(key.clone(), Vec::with_capacity(arr.len()));
12675 }
12676
12677 for item in &arr {
12679 match item {
12680 Value::Map(map) => {
12681 let map = map.borrow();
12682 for key in &key_names {
12683 let val = map.get(key).cloned().unwrap_or(Value::Null);
12684 soa.get_mut(key).unwrap().push(val);
12685 }
12686 }
12687 Value::Struct { fields, .. } => {
12688 let fields = fields.borrow();
12689 for key in &key_names {
12690 let val = fields.get(key).cloned().unwrap_or(Value::Null);
12691 soa.get_mut(key).unwrap().push(val);
12692 }
12693 }
12694 _ => {
12695 return Err(RuntimeError::new(
12696 "aos_to_soa: array must contain structs or maps",
12697 ))
12698 }
12699 }
12700 }
12701
12702 let result: HashMap<String, Value> = soa
12704 .into_iter()
12705 .map(|(k, v)| (k, Value::Array(Rc::new(RefCell::new(v)))))
12706 .collect();
12707
12708 Ok(Value::Map(Rc::new(RefCell::new(result))))
12709 });
12710
12711 define(interp, "soa_to_aos", Some(1), |_, args| {
12714 let soa = match &args[0] {
12715 Value::Map(map) => map.borrow().clone(),
12716 _ => return Err(RuntimeError::new("soa_to_aos: argument must be map")),
12717 };
12718
12719 if soa.is_empty() {
12720 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
12721 }
12722
12723 let len = soa
12725 .values()
12726 .next()
12727 .and_then(|v| {
12728 if let Value::Array(arr) = v {
12729 Some(arr.borrow().len())
12730 } else {
12731 None
12732 }
12733 })
12734 .unwrap_or(0);
12735
12736 let mut aos: Vec<Value> = Vec::with_capacity(len);
12738 for i in 0..len {
12739 let mut fields = HashMap::new();
12740 for (key, value) in &soa {
12741 if let Value::Array(arr) = value {
12742 let arr = arr.borrow();
12743 if i < arr.len() {
12744 fields.insert(key.clone(), arr[i].clone());
12745 }
12746 }
12747 }
12748 aos.push(Value::Map(Rc::new(RefCell::new(fields))));
12749 }
12750
12751 Ok(Value::Array(Rc::new(RefCell::new(aos))))
12752 });
12753
12754 define(interp, "soa_map", Some(3), |interp, args| {
12757 let mut soa = match &args[0] {
12758 Value::Map(map) => map.borrow().clone(),
12759 _ => return Err(RuntimeError::new("soa_map: first argument must be SoA map")),
12760 };
12761 let key = match &args[1] {
12762 Value::String(s) => (**s).clone(),
12763 _ => {
12764 return Err(RuntimeError::new(
12765 "soa_map: second argument must be key string",
12766 ))
12767 }
12768 };
12769 let func = match &args[2] {
12770 Value::Function(f) => f.clone(),
12771 _ => {
12772 return Err(RuntimeError::new(
12773 "soa_map: third argument must be a function",
12774 ))
12775 }
12776 };
12777
12778 let arr = soa
12780 .get(&key)
12781 .ok_or_else(|| RuntimeError::new(format!("soa_map: key '{}' not found", key)))?;
12782
12783 let arr_vals = match arr {
12784 Value::Array(a) => a.borrow().clone(),
12785 _ => return Err(RuntimeError::new("soa_map: key must map to array")),
12786 };
12787
12788 let results: Vec<Value> = arr_vals
12790 .iter()
12791 .map(|val| interp.call_function(&func, vec![val.clone()]))
12792 .collect::<Result<_, _>>()?;
12793
12794 soa.insert(key, Value::Array(Rc::new(RefCell::new(results))));
12796
12797 Ok(Value::Map(Rc::new(RefCell::new(soa))))
12798 });
12799
12800 define(interp, "soa_zip", Some(3), |interp, args| {
12803 let soa = match &args[0] {
12804 Value::Map(map) => map.borrow().clone(),
12805 _ => return Err(RuntimeError::new("soa_zip: first argument must be SoA map")),
12806 };
12807 let keys = match &args[1] {
12808 Value::Array(keys) => keys.borrow().clone(),
12809 _ => {
12810 return Err(RuntimeError::new(
12811 "soa_zip: second argument must be array of keys",
12812 ))
12813 }
12814 };
12815 let func = match &args[2] {
12816 Value::Function(f) => f.clone(),
12817 _ => {
12818 return Err(RuntimeError::new(
12819 "soa_zip: third argument must be a function",
12820 ))
12821 }
12822 };
12823
12824 let arrays: Vec<Vec<Value>> = keys
12826 .iter()
12827 .filter_map(|k| {
12828 if let Value::String(s) = k {
12829 if let Some(Value::Array(arr)) = soa.get(&**s) {
12830 return Some(arr.borrow().clone());
12831 }
12832 }
12833 None
12834 })
12835 .collect();
12836
12837 if arrays.is_empty() {
12838 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
12839 }
12840
12841 let len = arrays[0].len();
12842
12843 let results: Vec<Value> = (0..len)
12845 .map(|i| {
12846 let fn_args: Vec<Value> = arrays
12847 .iter()
12848 .filter_map(|arr| arr.get(i).cloned())
12849 .collect();
12850 interp.call_function(&func, fn_args)
12851 })
12852 .collect::<Result<_, _>>()?;
12853
12854 Ok(Value::Array(Rc::new(RefCell::new(results))))
12855 });
12856
12857 define(interp, "interleave", None, |_, args| {
12860 if args.is_empty() {
12861 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
12862 }
12863
12864 let arrays: Vec<Vec<Value>> = args
12865 .iter()
12866 .filter_map(|arg| {
12867 if let Value::Array(arr) = arg {
12868 Some(arr.borrow().clone())
12869 } else {
12870 None
12871 }
12872 })
12873 .collect();
12874
12875 if arrays.is_empty() {
12876 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
12877 }
12878
12879 let len = arrays[0].len();
12880 let stride = arrays.len();
12881 let mut result = Vec::with_capacity(len * stride);
12882
12883 for i in 0..len {
12884 for arr in &arrays {
12885 if let Some(val) = arr.get(i) {
12886 result.push(val.clone());
12887 }
12888 }
12889 }
12890
12891 Ok(Value::Array(Rc::new(RefCell::new(result))))
12892 });
12893
12894 define(interp, "deinterleave", Some(2), |_, args| {
12897 let arr = match &args[0] {
12898 Value::Array(arr) => arr.borrow().clone(),
12899 _ => {
12900 return Err(RuntimeError::new(
12901 "deinterleave: first argument must be array",
12902 ))
12903 }
12904 };
12905 let stride = match &args[1] {
12906 Value::Int(n) => *n as usize,
12907 _ => {
12908 return Err(RuntimeError::new(
12909 "deinterleave: second argument must be integer stride",
12910 ))
12911 }
12912 };
12913
12914 if stride == 0 {
12915 return Err(RuntimeError::new("deinterleave: stride must be > 0"));
12916 }
12917
12918 let mut result: Vec<Vec<Value>> = (0..stride).map(|_| Vec::new()).collect();
12919
12920 for (i, val) in arr.iter().enumerate() {
12921 result[i % stride].push(val.clone());
12922 }
12923
12924 Ok(Value::Array(Rc::new(RefCell::new(
12925 result
12926 .into_iter()
12927 .map(|v| Value::Array(Rc::new(RefCell::new(v))))
12928 .collect(),
12929 ))))
12930 });
12931}
12932
12933fn register_tensor(interp: &mut Interpreter) {
12939 define(interp, "outer_product", Some(2), |_, args| {
12942 let a = match &args[0] {
12943 Value::Array(arr) => arr.borrow().clone(),
12944 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
12945 };
12946 let b = match &args[1] {
12947 Value::Array(arr) => arr.borrow().clone(),
12948 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
12949 };
12950
12951 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
12953 for ai in &a {
12954 for bi in &b {
12955 let product = match (ai, bi) {
12956 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
12957 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
12958 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
12959 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
12960 _ => return Err(RuntimeError::new("outer_product: elements must be numeric")),
12961 };
12962 result.push(product);
12963 }
12964 }
12965
12966 Ok(Value::Array(Rc::new(RefCell::new(result))))
12967 });
12968
12969 define(interp, "tensor_contract", Some(4), |_, args| {
12972 let a = match &args[0] {
12973 Value::Array(arr) => arr.borrow().clone(),
12974 _ => {
12975 return Err(RuntimeError::new(
12976 "tensor_contract: first argument must be array",
12977 ))
12978 }
12979 };
12980 let b = match &args[1] {
12981 Value::Array(arr) => arr.borrow().clone(),
12982 _ => {
12983 return Err(RuntimeError::new(
12984 "tensor_contract: second argument must be array",
12985 ))
12986 }
12987 };
12988 let _axis_a = match &args[2] {
12989 Value::Int(n) => *n as usize,
12990 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
12991 };
12992 let _axis_b = match &args[3] {
12993 Value::Int(n) => *n as usize,
12994 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
12995 };
12996
12997 if a.len() != b.len() {
12999 return Err(RuntimeError::new(
13000 "tensor_contract: vectors must have same length for contraction",
13001 ));
13002 }
13003
13004 let mut sum = 0.0f64;
13005 for (ai, bi) in a.iter().zip(b.iter()) {
13006 let product = match (ai, bi) {
13007 (Value::Float(x), Value::Float(y)) => x * y,
13008 (Value::Int(x), Value::Int(y)) => (*x as f64) * (*y as f64),
13009 (Value::Float(x), Value::Int(y)) => x * (*y as f64),
13010 (Value::Int(x), Value::Float(y)) => (*x as f64) * y,
13011 _ => {
13012 return Err(RuntimeError::new(
13013 "tensor_contract: elements must be numeric",
13014 ))
13015 }
13016 };
13017 sum += product;
13018 }
13019
13020 Ok(Value::Float(sum))
13021 });
13022
13023 define(interp, "kronecker_product", Some(2), |_, args| {
13026 let a = match &args[0] {
13027 Value::Array(arr) => arr.borrow().clone(),
13028 _ => {
13029 return Err(RuntimeError::new(
13030 "kronecker_product: arguments must be arrays",
13031 ))
13032 }
13033 };
13034 let b = match &args[1] {
13035 Value::Array(arr) => arr.borrow().clone(),
13036 _ => {
13037 return Err(RuntimeError::new(
13038 "kronecker_product: arguments must be arrays",
13039 ))
13040 }
13041 };
13042
13043 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
13045 for ai in &a {
13046 for bi in &b {
13047 let product = match (ai, bi) {
13048 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
13049 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
13050 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
13051 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
13052 _ => {
13053 return Err(RuntimeError::new(
13054 "kronecker_product: elements must be numeric",
13055 ))
13056 }
13057 };
13058 result.push(product);
13059 }
13060 }
13061
13062 Ok(Value::Array(Rc::new(RefCell::new(result))))
13063 });
13064
13065 define(interp, "hadamard_product", Some(2), |_, args| {
13067 let a = match &args[0] {
13068 Value::Array(arr) => arr.borrow().clone(),
13069 _ => {
13070 return Err(RuntimeError::new(
13071 "hadamard_product: arguments must be arrays",
13072 ))
13073 }
13074 };
13075 let b = match &args[1] {
13076 Value::Array(arr) => arr.borrow().clone(),
13077 _ => {
13078 return Err(RuntimeError::new(
13079 "hadamard_product: arguments must be arrays",
13080 ))
13081 }
13082 };
13083
13084 if a.len() != b.len() {
13085 return Err(RuntimeError::new(
13086 "hadamard_product: arrays must have same length",
13087 ));
13088 }
13089
13090 let result: Vec<Value> = a
13091 .iter()
13092 .zip(b.iter())
13093 .map(|(ai, bi)| match (ai, bi) {
13094 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
13095 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
13096 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * (*y as f64))),
13097 (Value::Int(x), Value::Float(y)) => Ok(Value::Float((*x as f64) * y)),
13098 _ => Err(RuntimeError::new(
13099 "hadamard_product: elements must be numeric",
13100 )),
13101 })
13102 .collect::<Result<_, _>>()?;
13103
13104 Ok(Value::Array(Rc::new(RefCell::new(result))))
13105 });
13106
13107 define(interp, "trace", Some(2), |_, args| {
13109 let arr = match &args[0] {
13110 Value::Array(arr) => arr.borrow().clone(),
13111 _ => return Err(RuntimeError::new("trace: first argument must be array")),
13112 };
13113 let size = match &args[1] {
13114 Value::Int(n) => *n as usize,
13115 _ => {
13116 return Err(RuntimeError::new(
13117 "trace: second argument must be matrix size",
13118 ))
13119 }
13120 };
13121
13122 let mut sum = 0.0f64;
13123 for i in 0..size {
13124 let idx = i * size + i;
13125 if idx < arr.len() {
13126 sum += match &arr[idx] {
13127 Value::Float(f) => *f,
13128 Value::Int(n) => *n as f64,
13129 _ => return Err(RuntimeError::new("trace: elements must be numeric")),
13130 };
13131 }
13132 }
13133
13134 Ok(Value::Float(sum))
13135 });
13136}
13137
13138fn register_autodiff(interp: &mut Interpreter) {
13184 define(interp, "grad", None, |interp, args| {
13187 if args.len() < 2 {
13188 return Err(RuntimeError::new(
13189 "grad() requires function and point arguments.\n\
13190 Usage: grad(f, x) or grad(f, x, step_size)\n\
13191 Example:\n\
13192 fn f(x) { return x * x; }\n\
13193 let derivative = grad(f, 3.0); // Returns 6.0",
13194 ));
13195 }
13196
13197 let func = match &args[0] {
13198 Value::Function(f) => f.clone(),
13199 _ => {
13200 return Err(RuntimeError::new(
13201 "grad() first argument must be a function.\n\
13202 Got non-function value. Define a function first:\n\
13203 fn my_func(x) { return x * x; }\n\
13204 grad(my_func, 2.0)",
13205 ))
13206 }
13207 };
13208 let x = match &args[1] {
13209 Value::Float(f) => *f,
13210 Value::Int(n) => *n as f64,
13211 Value::Array(arr) => {
13212 let arr = arr.borrow().clone();
13214 let h = if args.len() > 2 {
13215 match &args[2] {
13216 Value::Float(f) => *f,
13217 Value::Int(n) => *n as f64,
13218 _ => 1e-7,
13219 }
13220 } else {
13221 1e-7
13222 };
13223
13224 let mut gradient = Vec::with_capacity(arr.len());
13225 for (i, xi) in arr.iter().enumerate() {
13226 let xi_val = match xi {
13227 Value::Float(f) => *f,
13228 Value::Int(n) => *n as f64,
13229 _ => continue,
13230 };
13231
13232 let mut x_plus = arr.clone();
13234 let mut x_minus = arr.clone();
13235 x_plus[i] = Value::Float(xi_val + h);
13236 x_minus[i] = Value::Float(xi_val - h);
13237
13238 let f_plus = interp
13239 .call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13240 let f_minus = interp
13241 .call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13242
13243 let grad_i = match (f_plus, f_minus) {
13244 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
13245 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
13246 _ => return Err(RuntimeError::new("grad: function must return numeric")),
13247 };
13248
13249 gradient.push(Value::Float(grad_i));
13250 }
13251
13252 return Ok(Value::Array(Rc::new(RefCell::new(gradient))));
13253 }
13254 _ => return Err(RuntimeError::new("grad: x must be numeric or array")),
13255 };
13256
13257 let h = if args.len() > 2 {
13258 match &args[2] {
13259 Value::Float(f) => *f,
13260 Value::Int(n) => *n as f64,
13261 _ => 1e-7,
13262 }
13263 } else {
13264 1e-7
13265 };
13266
13267 let f_plus = interp.call_function(&func, vec![Value::Float(x + h)])?;
13269 let f_minus = interp.call_function(&func, vec![Value::Float(x - h)])?;
13270
13271 let derivative = match (f_plus, f_minus) {
13272 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
13273 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
13274 _ => return Err(RuntimeError::new("grad: function must return numeric")),
13275 };
13276
13277 Ok(Value::Float(derivative))
13278 });
13279
13280 define(interp, "jacobian", Some(2), |interp, args| {
13282 let func = match &args[0] {
13283 Value::Function(f) => f.clone(),
13284 _ => {
13285 return Err(RuntimeError::new(
13286 "jacobian: first argument must be a function",
13287 ))
13288 }
13289 };
13290 let x = match &args[1] {
13291 Value::Array(arr) => arr.borrow().clone(),
13292 _ => return Err(RuntimeError::new("jacobian: second argument must be array")),
13293 };
13294
13295 let h = 1e-7;
13296 let n = x.len();
13297
13298 let f_x =
13300 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x.clone())))])?;
13301 let m = match &f_x {
13302 Value::Array(arr) => arr.borrow().len(),
13303 _ => 1,
13304 };
13305
13306 let mut jacobian: Vec<Value> = Vec::with_capacity(m * n);
13308
13309 for j in 0..n {
13310 let xj = match &x[j] {
13311 Value::Float(f) => *f,
13312 Value::Int(i) => *i as f64,
13313 _ => continue,
13314 };
13315
13316 let mut x_plus = x.clone();
13317 let mut x_minus = x.clone();
13318 x_plus[j] = Value::Float(xj + h);
13319 x_minus[j] = Value::Float(xj - h);
13320
13321 let f_plus =
13322 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13323 let f_minus =
13324 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13325
13326 match (&f_plus, &f_minus) {
13328 (Value::Array(fp), Value::Array(fm)) => {
13329 let fp = fp.borrow();
13330 let fm = fm.borrow();
13331 for i in 0..m {
13332 let dfi_dxj = match (&fp[i], &fm[i]) {
13333 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
13334 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
13335 _ => 0.0,
13336 };
13337 jacobian.push(Value::Float(dfi_dxj));
13338 }
13339 }
13340 (Value::Float(fp), Value::Float(fm)) => {
13341 jacobian.push(Value::Float((fp - fm) / (2.0 * h)));
13342 }
13343 _ => {
13344 return Err(RuntimeError::new(
13345 "jacobian: function must return array or numeric",
13346 ))
13347 }
13348 }
13349 }
13350
13351 Ok(Value::Array(Rc::new(RefCell::new(jacobian))))
13352 });
13353
13354 define(interp, "hessian", Some(2), |interp, args| {
13356 let func = match &args[0] {
13357 Value::Function(f) => f.clone(),
13358 _ => {
13359 return Err(RuntimeError::new(
13360 "hessian: first argument must be a function",
13361 ))
13362 }
13363 };
13364 let x = match &args[1] {
13365 Value::Array(arr) => arr.borrow().clone(),
13366 _ => return Err(RuntimeError::new("hessian: second argument must be array")),
13367 };
13368
13369 let h = 1e-5; let n = x.len();
13371
13372 let mut hessian: Vec<Value> = Vec::with_capacity(n * n);
13373
13374 for i in 0..n {
13375 for j in 0..n {
13376 let xi = match &x[i] {
13377 Value::Float(f) => *f,
13378 Value::Int(k) => *k as f64,
13379 _ => continue,
13380 };
13381 let xj = match &x[j] {
13382 Value::Float(f) => *f,
13383 Value::Int(k) => *k as f64,
13384 _ => continue,
13385 };
13386
13387 let mut x_pp = x.clone();
13389 let mut x_pm = x.clone();
13390 let mut x_mp = x.clone();
13391 let mut x_mm = x.clone();
13392
13393 x_pp[i] = Value::Float(xi + h);
13394 x_pp[j] = Value::Float(if i == j { xi + 2.0 * h } else { xj + h });
13395 x_pm[i] = Value::Float(xi + h);
13396 x_pm[j] = Value::Float(if i == j { xi } else { xj - h });
13397 x_mp[i] = Value::Float(xi - h);
13398 x_mp[j] = Value::Float(if i == j { xi } else { xj + h });
13399 x_mm[i] = Value::Float(xi - h);
13400 x_mm[j] = Value::Float(if i == j { xi - 2.0 * h } else { xj - h });
13401
13402 let f_pp =
13403 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pp)))])?;
13404 let f_pm =
13405 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pm)))])?;
13406 let f_mp =
13407 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mp)))])?;
13408 let f_mm =
13409 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mm)))])?;
13410
13411 let d2f = match (f_pp, f_pm, f_mp, f_mm) {
13412 (
13413 Value::Float(fpp),
13414 Value::Float(fpm),
13415 Value::Float(fmp),
13416 Value::Float(fmm),
13417 ) => (fpp - fpm - fmp + fmm) / (4.0 * h * h),
13418 _ => 0.0,
13419 };
13420
13421 hessian.push(Value::Float(d2f));
13422 }
13423 }
13424
13425 Ok(Value::Array(Rc::new(RefCell::new(hessian))))
13426 });
13427
13428 define(interp, "divergence", Some(2), |interp, args| {
13430 let func = match &args[0] {
13431 Value::Function(f) => f.clone(),
13432 _ => {
13433 return Err(RuntimeError::new(
13434 "divergence: first argument must be a function",
13435 ))
13436 }
13437 };
13438 let x = match &args[1] {
13439 Value::Array(arr) => arr.borrow().clone(),
13440 _ => {
13441 return Err(RuntimeError::new(
13442 "divergence: second argument must be array",
13443 ))
13444 }
13445 };
13446
13447 let h = 1e-7;
13448 let mut div = 0.0f64;
13449
13450 for (i, xi) in x.iter().enumerate() {
13451 let xi_val = match xi {
13452 Value::Float(f) => *f,
13453 Value::Int(n) => *n as f64,
13454 _ => continue,
13455 };
13456
13457 let mut x_plus = x.clone();
13458 let mut x_minus = x.clone();
13459 x_plus[i] = Value::Float(xi_val + h);
13460 x_minus[i] = Value::Float(xi_val - h);
13461
13462 let f_plus =
13463 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13464 let f_minus =
13465 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13466
13467 let df_i = match (&f_plus, &f_minus) {
13469 (Value::Array(fp), Value::Array(fm)) => {
13470 let fp = fp.borrow();
13471 let fm = fm.borrow();
13472 if i < fp.len() && i < fm.len() {
13473 match (&fp[i], &fm[i]) {
13474 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
13475 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
13476 _ => 0.0,
13477 }
13478 } else {
13479 0.0
13480 }
13481 }
13482 _ => 0.0,
13483 };
13484
13485 div += df_i;
13486 }
13487
13488 Ok(Value::Float(div))
13489 });
13490}
13491
13492fn register_spatial(interp: &mut Interpreter) {
13498 define(interp, "spatial_hash_new", Some(1), |_, args| {
13500 let cell_size = match &args[0] {
13501 Value::Float(f) => *f,
13502 Value::Int(n) => *n as f64,
13503 _ => {
13504 return Err(RuntimeError::new(
13505 "spatial_hash_new: cell_size must be numeric",
13506 ))
13507 }
13508 };
13509
13510 let mut config = HashMap::new();
13511 config.insert("cell_size".to_string(), Value::Float(cell_size));
13512 config.insert(
13513 "buckets".to_string(),
13514 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
13515 );
13516
13517 Ok(Value::Map(Rc::new(RefCell::new(config))))
13518 });
13519
13520 define(interp, "spatial_hash_insert", Some(3), |_, args| {
13522 let hash = match &args[0] {
13523 Value::Map(map) => map.clone(),
13524 _ => {
13525 return Err(RuntimeError::new(
13526 "spatial_hash_insert: first argument must be spatial hash",
13527 ))
13528 }
13529 };
13530 let id = args[1].clone();
13531 let pos = match &args[2] {
13532 Value::Array(arr) => arr.borrow().clone(),
13533 _ => {
13534 return Err(RuntimeError::new(
13535 "spatial_hash_insert: position must be array",
13536 ))
13537 }
13538 };
13539
13540 let cell_size = {
13541 let h = hash.borrow();
13542 match h.get("cell_size") {
13543 Some(Value::Float(f)) => *f,
13544 _ => 1.0,
13545 }
13546 };
13547
13548 let key = pos
13550 .iter()
13551 .filter_map(|v| match v {
13552 Value::Float(f) => Some((*f / cell_size).floor() as i64),
13553 Value::Int(n) => Some(*n / (cell_size as i64)),
13554 _ => None,
13555 })
13556 .map(|n| n.to_string())
13557 .collect::<Vec<_>>()
13558 .join(",");
13559
13560 {
13562 let mut h = hash.borrow_mut();
13563 let buckets = h
13564 .entry("buckets".to_string())
13565 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
13566
13567 if let Value::Map(buckets_map) = buckets {
13568 let mut bm = buckets_map.borrow_mut();
13569 let bucket = bm
13570 .entry(key)
13571 .or_insert_with(|| Value::Array(Rc::new(RefCell::new(vec![]))));
13572
13573 if let Value::Array(arr) = bucket {
13574 arr.borrow_mut().push(id);
13575 }
13576 }
13577 }
13578
13579 Ok(Value::Map(hash))
13580 });
13581
13582 define(interp, "spatial_hash_query", Some(3), |_, args| {
13584 let hash = match &args[0] {
13585 Value::Map(map) => map.borrow().clone(),
13586 _ => {
13587 return Err(RuntimeError::new(
13588 "spatial_hash_query: first argument must be spatial hash",
13589 ))
13590 }
13591 };
13592 let pos = match &args[1] {
13593 Value::Array(arr) => arr.borrow().clone(),
13594 _ => {
13595 return Err(RuntimeError::new(
13596 "spatial_hash_query: position must be array",
13597 ))
13598 }
13599 };
13600 let radius = match &args[2] {
13601 Value::Float(f) => *f,
13602 Value::Int(n) => *n as f64,
13603 _ => {
13604 return Err(RuntimeError::new(
13605 "spatial_hash_query: radius must be numeric",
13606 ))
13607 }
13608 };
13609
13610 let cell_size = match hash.get("cell_size") {
13611 Some(Value::Float(f)) => *f,
13612 _ => 1.0,
13613 };
13614
13615 let center: Vec<i64> = pos
13617 .iter()
13618 .filter_map(|v| match v {
13619 Value::Float(f) => Some((*f / cell_size).floor() as i64),
13620 Value::Int(n) => Some(*n / (cell_size as i64)),
13621 _ => None,
13622 })
13623 .collect();
13624
13625 let cells_to_check = (radius / cell_size).ceil() as i64;
13627
13628 let mut results: Vec<Value> = Vec::new();
13629
13630 if let Some(Value::Map(buckets)) = hash.get("buckets") {
13631 let buckets = buckets.borrow();
13632
13633 if center.len() >= 2 {
13635 for dx in -cells_to_check..=cells_to_check {
13636 for dy in -cells_to_check..=cells_to_check {
13637 let key = format!("{},{}", center[0] + dx, center[1] + dy);
13638 if let Some(Value::Array(bucket)) = buckets.get(&key) {
13639 for item in bucket.borrow().iter() {
13640 results.push(item.clone());
13643 }
13644 }
13645 }
13646 }
13647 }
13648 }
13649
13650 Ok(Value::Array(Rc::new(RefCell::new(results))))
13651 });
13652
13653 define(interp, "aabb_new", Some(2), |_, args| {
13655 let min = match &args[0] {
13656 Value::Array(arr) => arr.borrow().clone(),
13657 _ => return Err(RuntimeError::new("aabb_new: min must be array")),
13658 };
13659 let max = match &args[1] {
13660 Value::Array(arr) => arr.borrow().clone(),
13661 _ => return Err(RuntimeError::new("aabb_new: max must be array")),
13662 };
13663
13664 let mut aabb = HashMap::new();
13665 aabb.insert("min".to_string(), Value::Array(Rc::new(RefCell::new(min))));
13666 aabb.insert("max".to_string(), Value::Array(Rc::new(RefCell::new(max))));
13667
13668 Ok(Value::Map(Rc::new(RefCell::new(aabb))))
13669 });
13670
13671 define(interp, "aabb_intersects", Some(2), |_, args| {
13673 let a = match &args[0] {
13674 Value::Map(map) => map.borrow().clone(),
13675 _ => {
13676 return Err(RuntimeError::new(
13677 "aabb_intersects: arguments must be AABBs",
13678 ))
13679 }
13680 };
13681 let b = match &args[1] {
13682 Value::Map(map) => map.borrow().clone(),
13683 _ => {
13684 return Err(RuntimeError::new(
13685 "aabb_intersects: arguments must be AABBs",
13686 ))
13687 }
13688 };
13689
13690 let a_min = extract_vec_from_map(&a, "min")?;
13691 let a_max = extract_vec_from_map(&a, "max")?;
13692 let b_min = extract_vec_from_map(&b, "min")?;
13693 let b_max = extract_vec_from_map(&b, "max")?;
13694
13695 for i in 0..a_min
13697 .len()
13698 .min(a_max.len())
13699 .min(b_min.len())
13700 .min(b_max.len())
13701 {
13702 if a_max[i] < b_min[i] || b_max[i] < a_min[i] {
13703 return Ok(Value::Bool(false));
13704 }
13705 }
13706
13707 Ok(Value::Bool(true))
13708 });
13709
13710 define(interp, "aabb_contains", Some(2), |_, args| {
13712 let aabb = match &args[0] {
13713 Value::Map(map) => map.borrow().clone(),
13714 _ => {
13715 return Err(RuntimeError::new(
13716 "aabb_contains: first argument must be AABB",
13717 ))
13718 }
13719 };
13720 let point = match &args[1] {
13721 Value::Array(arr) => arr.borrow().clone(),
13722 _ => {
13723 return Err(RuntimeError::new(
13724 "aabb_contains: second argument must be point array",
13725 ))
13726 }
13727 };
13728
13729 let min = extract_vec_from_map(&aabb, "min")?;
13730 let max = extract_vec_from_map(&aabb, "max")?;
13731
13732 for (i, p) in point.iter().enumerate() {
13733 let p_val = match p {
13734 Value::Float(f) => *f,
13735 Value::Int(n) => *n as f64,
13736 _ => continue,
13737 };
13738
13739 if i < min.len() && p_val < min[i] {
13740 return Ok(Value::Bool(false));
13741 }
13742 if i < max.len() && p_val > max[i] {
13743 return Ok(Value::Bool(false));
13744 }
13745 }
13746
13747 Ok(Value::Bool(true))
13748 });
13749}
13750
13751fn extract_vec_from_map(map: &HashMap<String, Value>, key: &str) -> Result<Vec<f64>, RuntimeError> {
13753 match map.get(key) {
13754 Some(Value::Array(arr)) => arr
13755 .borrow()
13756 .iter()
13757 .map(|v| match v {
13758 Value::Float(f) => Ok(*f),
13759 Value::Int(n) => Ok(*n as f64),
13760 _ => Err(RuntimeError::new("Expected numeric value")),
13761 })
13762 .collect(),
13763 _ => Err(RuntimeError::new(format!(
13764 "Missing or invalid '{}' in AABB",
13765 key
13766 ))),
13767 }
13768}
13769
13770fn register_physics(interp: &mut Interpreter) {
13776 define(interp, "verlet_integrate", Some(4), |_, args| {
13779 let pos = extract_vec3(&args[0], "verlet_integrate")?;
13780 let prev = extract_vec3(&args[1], "verlet_integrate")?;
13781 let accel = extract_vec3(&args[2], "verlet_integrate")?;
13782 let dt = match &args[3] {
13783 Value::Float(f) => *f,
13784 Value::Int(n) => *n as f64,
13785 _ => return Err(RuntimeError::new("verlet_integrate: dt must be numeric")),
13786 };
13787
13788 let dt2 = dt * dt;
13789 let new_pos = [
13790 pos[0] + (pos[0] - prev[0]) + accel[0] * dt2,
13791 pos[1] + (pos[1] - prev[1]) + accel[1] * dt2,
13792 pos[2] + (pos[2] - prev[2]) + accel[2] * dt2,
13793 ];
13794
13795 Ok(make_vec3_arr(new_pos))
13796 });
13797
13798 define(interp, "spring_force", Some(4), |_, args| {
13800 let p1 = extract_vec3(&args[0], "spring_force")?;
13801 let p2 = extract_vec3(&args[1], "spring_force")?;
13802 let rest_length = match &args[2] {
13803 Value::Float(f) => *f,
13804 Value::Int(n) => *n as f64,
13805 _ => {
13806 return Err(RuntimeError::new(
13807 "spring_force: rest_length must be numeric",
13808 ))
13809 }
13810 };
13811 let stiffness = match &args[3] {
13812 Value::Float(f) => *f,
13813 Value::Int(n) => *n as f64,
13814 _ => return Err(RuntimeError::new("spring_force: stiffness must be numeric")),
13815 };
13816
13817 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
13818 let length = (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]).sqrt();
13819
13820 if length < 1e-10 {
13821 return Ok(make_vec3_arr([0.0, 0.0, 0.0]));
13822 }
13823
13824 let displacement = length - rest_length;
13825 let force_mag = stiffness * displacement;
13826 let normalized = [delta[0] / length, delta[1] / length, delta[2] / length];
13827
13828 Ok(make_vec3_arr([
13829 normalized[0] * force_mag,
13830 normalized[1] * force_mag,
13831 normalized[2] * force_mag,
13832 ]))
13833 });
13834
13835 define(interp, "distance_constraint", Some(3), |_, args| {
13838 let p1 = extract_vec3(&args[0], "distance_constraint")?;
13839 let p2 = extract_vec3(&args[1], "distance_constraint")?;
13840 let target = match &args[2] {
13841 Value::Float(f) => *f,
13842 Value::Int(n) => *n as f64,
13843 _ => {
13844 return Err(RuntimeError::new(
13845 "distance_constraint: target must be numeric",
13846 ))
13847 }
13848 };
13849
13850 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
13851 let length = (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]).sqrt();
13852
13853 if length < 1e-10 {
13854 return Ok(Value::Tuple(Rc::new(vec![
13855 make_vec3_arr(p1),
13856 make_vec3_arr(p2),
13857 ])));
13858 }
13859
13860 let correction = (length - target) / length * 0.5;
13861 let corr_vec = [
13862 delta[0] * correction,
13863 delta[1] * correction,
13864 delta[2] * correction,
13865 ];
13866
13867 let new_p1 = [
13868 p1[0] + corr_vec[0],
13869 p1[1] + corr_vec[1],
13870 p1[2] + corr_vec[2],
13871 ];
13872 let new_p2 = [
13873 p2[0] - corr_vec[0],
13874 p2[1] - corr_vec[1],
13875 p2[2] - corr_vec[2],
13876 ];
13877
13878 Ok(Value::Tuple(Rc::new(vec![
13879 make_vec3_arr(new_p1),
13880 make_vec3_arr(new_p2),
13881 ])))
13882 });
13883
13884 define(interp, "solve_constraints", Some(3), |_, args| {
13887 let mut points = match &args[0] {
13888 Value::Array(arr) => arr.borrow().clone(),
13889 _ => {
13890 return Err(RuntimeError::new(
13891 "solve_constraints: first argument must be array of points",
13892 ))
13893 }
13894 };
13895 let constraints = match &args[1] {
13896 Value::Array(arr) => arr.borrow().clone(),
13897 _ => {
13898 return Err(RuntimeError::new(
13899 "solve_constraints: second argument must be array of constraints",
13900 ))
13901 }
13902 };
13903 let iterations = match &args[2] {
13904 Value::Int(n) => *n as usize,
13905 _ => {
13906 return Err(RuntimeError::new(
13907 "solve_constraints: iterations must be integer",
13908 ))
13909 }
13910 };
13911
13912 for _ in 0..iterations {
13913 for constraint in &constraints {
13914 match constraint {
13915 Value::Map(c) => {
13916 let c = c.borrow();
13917 let constraint_type = c
13918 .get("type")
13919 .and_then(|v| {
13920 if let Value::String(s) = v {
13921 Some((**s).clone())
13922 } else {
13923 None
13924 }
13925 })
13926 .unwrap_or_default();
13927
13928 match constraint_type.as_str() {
13929 "distance" => {
13930 let indices = match c.get("indices") {
13931 Some(Value::Array(arr)) => arr.borrow().clone(),
13932 _ => continue,
13933 };
13934 let target = match c.get("distance") {
13935 Some(Value::Float(f)) => *f,
13936 Some(Value::Int(n)) => *n as f64,
13937 _ => continue,
13938 };
13939
13940 if indices.len() >= 2 {
13941 let i1 = match &indices[0] {
13942 Value::Int(n) => *n as usize,
13943 _ => continue,
13944 };
13945 let i2 = match &indices[1] {
13946 Value::Int(n) => *n as usize,
13947 _ => continue,
13948 };
13949
13950 if i1 < points.len() && i2 < points.len() {
13951 let p1 = extract_vec3(&points[i1], "solve")?;
13953 let p2 = extract_vec3(&points[i2], "solve")?;
13954
13955 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
13956 let length = (delta[0] * delta[0]
13957 + delta[1] * delta[1]
13958 + delta[2] * delta[2])
13959 .sqrt();
13960
13961 if length > 1e-10 {
13962 let correction = (length - target) / length * 0.5;
13963 let corr_vec = [
13964 delta[0] * correction,
13965 delta[1] * correction,
13966 delta[2] * correction,
13967 ];
13968
13969 points[i1] = make_vec3_arr([
13970 p1[0] + corr_vec[0],
13971 p1[1] + corr_vec[1],
13972 p1[2] + corr_vec[2],
13973 ]);
13974 points[i2] = make_vec3_arr([
13975 p2[0] - corr_vec[0],
13976 p2[1] - corr_vec[1],
13977 p2[2] - corr_vec[2],
13978 ]);
13979 }
13980 }
13981 }
13982 }
13983 _ => {}
13984 }
13985 }
13986 _ => continue,
13987 }
13988 }
13989 }
13990
13991 Ok(Value::Array(Rc::new(RefCell::new(points))))
13992 });
13993
13994 define(interp, "ray_sphere_intersect", Some(4), |_, args| {
13997 let origin = extract_vec3(&args[0], "ray_sphere_intersect")?;
13998 let dir = extract_vec3(&args[1], "ray_sphere_intersect")?;
13999 let center = extract_vec3(&args[2], "ray_sphere_intersect")?;
14000 let radius = match &args[3] {
14001 Value::Float(f) => *f,
14002 Value::Int(n) => *n as f64,
14003 _ => {
14004 return Err(RuntimeError::new(
14005 "ray_sphere_intersect: radius must be numeric",
14006 ))
14007 }
14008 };
14009
14010 let oc = [
14011 origin[0] - center[0],
14012 origin[1] - center[1],
14013 origin[2] - center[2],
14014 ];
14015
14016 let a = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2];
14017 let b = 2.0 * (oc[0] * dir[0] + oc[1] * dir[1] + oc[2] * dir[2]);
14018 let c = oc[0] * oc[0] + oc[1] * oc[1] + oc[2] * oc[2] - radius * radius;
14019
14020 let discriminant = b * b - 4.0 * a * c;
14021
14022 if discriminant < 0.0 {
14023 Ok(Value::Float(-1.0))
14024 } else {
14025 let t = (-b - discriminant.sqrt()) / (2.0 * a);
14026 if t > 0.0 {
14027 Ok(Value::Float(t))
14028 } else {
14029 let t2 = (-b + discriminant.sqrt()) / (2.0 * a);
14030 if t2 > 0.0 {
14031 Ok(Value::Float(t2))
14032 } else {
14033 Ok(Value::Float(-1.0))
14034 }
14035 }
14036 }
14037 });
14038
14039 define(interp, "ray_plane_intersect", Some(4), |_, args| {
14041 let origin = extract_vec3(&args[0], "ray_plane_intersect")?;
14042 let dir = extract_vec3(&args[1], "ray_plane_intersect")?;
14043 let plane_pt = extract_vec3(&args[2], "ray_plane_intersect")?;
14044 let normal = extract_vec3(&args[3], "ray_plane_intersect")?;
14045
14046 let denom = dir[0] * normal[0] + dir[1] * normal[1] + dir[2] * normal[2];
14047
14048 if denom.abs() < 1e-10 {
14049 return Ok(Value::Float(-1.0)); }
14051
14052 let diff = [
14053 plane_pt[0] - origin[0],
14054 plane_pt[1] - origin[1],
14055 plane_pt[2] - origin[2],
14056 ];
14057 let t = (diff[0] * normal[0] + diff[1] * normal[1] + diff[2] * normal[2]) / denom;
14058
14059 if t > 0.0 {
14060 Ok(Value::Float(t))
14061 } else {
14062 Ok(Value::Float(-1.0))
14063 }
14064 });
14065}
14066
14067fn register_geometric_algebra(interp: &mut Interpreter) {
14129 fn make_multivector(components: [f64; 8]) -> Value {
14131 let mut mv = HashMap::new();
14132 mv.insert("s".to_string(), Value::Float(components[0])); mv.insert("e1".to_string(), Value::Float(components[1])); mv.insert("e2".to_string(), Value::Float(components[2])); mv.insert("e3".to_string(), Value::Float(components[3])); mv.insert("e12".to_string(), Value::Float(components[4])); mv.insert("e23".to_string(), Value::Float(components[5])); mv.insert("e31".to_string(), Value::Float(components[6])); mv.insert("e123".to_string(), Value::Float(components[7])); mv.insert(
14141 "_type".to_string(),
14142 Value::String(Rc::new("multivector".to_string())),
14143 );
14144 Value::Map(Rc::new(RefCell::new(mv)))
14145 }
14146
14147 fn extract_multivector(v: &Value, fn_name: &str) -> Result<[f64; 8], RuntimeError> {
14148 match v {
14149 Value::Map(map) => {
14150 let map = map.borrow();
14151 let get_component = |key: &str| -> f64 {
14152 match map.get(key) {
14153 Some(Value::Float(f)) => *f,
14154 Some(Value::Int(n)) => *n as f64,
14155 _ => 0.0,
14156 }
14157 };
14158 Ok([
14159 get_component("s"),
14160 get_component("e1"),
14161 get_component("e2"),
14162 get_component("e3"),
14163 get_component("e12"),
14164 get_component("e23"),
14165 get_component("e31"),
14166 get_component("e123"),
14167 ])
14168 }
14169 _ => Err(RuntimeError::new(format!(
14170 "{}: expected multivector",
14171 fn_name
14172 ))),
14173 }
14174 }
14175
14176 define(interp, "mv_new", Some(8), |_, args| {
14178 let mut components = [0.0f64; 8];
14179 for (i, arg) in args.iter().enumerate().take(8) {
14180 components[i] = match arg {
14181 Value::Float(f) => *f,
14182 Value::Int(n) => *n as f64,
14183 _ => 0.0,
14184 };
14185 }
14186 Ok(make_multivector(components))
14187 });
14188
14189 define(interp, "mv_scalar", Some(1), |_, args| {
14191 let s = match &args[0] {
14192 Value::Float(f) => *f,
14193 Value::Int(n) => *n as f64,
14194 _ => return Err(RuntimeError::new("mv_scalar: expected number")),
14195 };
14196 Ok(make_multivector([s, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))
14197 });
14198
14199 define(interp, "mv_vector", Some(3), |_, args| {
14201 let x = match &args[0] {
14202 Value::Float(f) => *f,
14203 Value::Int(n) => *n as f64,
14204 _ => 0.0,
14205 };
14206 let y = match &args[1] {
14207 Value::Float(f) => *f,
14208 Value::Int(n) => *n as f64,
14209 _ => 0.0,
14210 };
14211 let z = match &args[2] {
14212 Value::Float(f) => *f,
14213 Value::Int(n) => *n as f64,
14214 _ => 0.0,
14215 };
14216 Ok(make_multivector([0.0, x, y, z, 0.0, 0.0, 0.0, 0.0]))
14217 });
14218
14219 define(interp, "mv_bivector", Some(3), |_, args| {
14221 let xy = match &args[0] {
14222 Value::Float(f) => *f,
14223 Value::Int(n) => *n as f64,
14224 _ => 0.0,
14225 };
14226 let yz = match &args[1] {
14227 Value::Float(f) => *f,
14228 Value::Int(n) => *n as f64,
14229 _ => 0.0,
14230 };
14231 let zx = match &args[2] {
14232 Value::Float(f) => *f,
14233 Value::Int(n) => *n as f64,
14234 _ => 0.0,
14235 };
14236 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, xy, yz, zx, 0.0]))
14237 });
14238
14239 define(interp, "mv_trivector", Some(1), |_, args| {
14241 let xyz = match &args[0] {
14242 Value::Float(f) => *f,
14243 Value::Int(n) => *n as f64,
14244 _ => 0.0,
14245 };
14246 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, xyz]))
14247 });
14248
14249 define(interp, "mv_add", Some(2), |_, args| {
14251 let a = extract_multivector(&args[0], "mv_add")?;
14252 let b = extract_multivector(&args[1], "mv_add")?;
14253 Ok(make_multivector([
14254 a[0] + b[0],
14255 a[1] + b[1],
14256 a[2] + b[2],
14257 a[3] + b[3],
14258 a[4] + b[4],
14259 a[5] + b[5],
14260 a[6] + b[6],
14261 a[7] + b[7],
14262 ]))
14263 });
14264
14265 define(interp, "mv_sub", Some(2), |_, args| {
14267 let a = extract_multivector(&args[0], "mv_sub")?;
14268 let b = extract_multivector(&args[1], "mv_sub")?;
14269 Ok(make_multivector([
14270 a[0] - b[0],
14271 a[1] - b[1],
14272 a[2] - b[2],
14273 a[3] - b[3],
14274 a[4] - b[4],
14275 a[5] - b[5],
14276 a[6] - b[6],
14277 a[7] - b[7],
14278 ]))
14279 });
14280
14281 define(interp, "mv_scale", Some(2), |_, args| {
14283 let a = extract_multivector(&args[0], "mv_scale")?;
14284 let s = match &args[1] {
14285 Value::Float(f) => *f,
14286 Value::Int(n) => *n as f64,
14287 _ => {
14288 return Err(RuntimeError::new(
14289 "mv_scale: second argument must be number",
14290 ))
14291 }
14292 };
14293 Ok(make_multivector([
14294 a[0] * s,
14295 a[1] * s,
14296 a[2] * s,
14297 a[3] * s,
14298 a[4] * s,
14299 a[5] * s,
14300 a[6] * s,
14301 a[7] * s,
14302 ]))
14303 });
14304
14305 define(interp, "mv_geometric", Some(2), |_, args| {
14308 let a = extract_multivector(&args[0], "mv_geometric")?;
14309 let b = extract_multivector(&args[1], "mv_geometric")?;
14310
14311 let mut r = [0.0f64; 8];
14314
14315 r[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
14317 - a[4] * b[4]
14318 - a[5] * b[5]
14319 - a[6] * b[6]
14320 - a[7] * b[7];
14321
14322 r[1] = a[0] * b[1] + a[1] * b[0] - a[2] * b[4] + a[3] * b[6] + a[4] * b[2]
14324 - a[5] * b[7]
14325 - a[6] * b[3]
14326 - a[7] * b[5];
14327
14328 r[2] = a[0] * b[2] + a[1] * b[4] + a[2] * b[0] - a[3] * b[5] - a[4] * b[1] + a[5] * b[3]
14330 - a[6] * b[7]
14331 - a[7] * b[6];
14332
14333 r[3] = a[0] * b[3] - a[1] * b[6] + a[2] * b[5] + a[3] * b[0] - a[4] * b[7] - a[5] * b[2]
14335 + a[6] * b[1]
14336 - a[7] * b[4];
14337
14338 r[4] = a[0] * b[4] + a[1] * b[2] - a[2] * b[1] + a[3] * b[7] + a[4] * b[0] + a[5] * b[6]
14340 - a[6] * b[5]
14341 + a[7] * b[3];
14342
14343 r[5] = a[0] * b[5] + a[1] * b[7] + a[2] * b[3] - a[3] * b[2] - a[4] * b[6]
14345 + a[5] * b[0]
14346 + a[6] * b[4]
14347 + a[7] * b[1];
14348
14349 r[6] = a[0] * b[6] - a[1] * b[3] + a[2] * b[7] + a[3] * b[1] + a[4] * b[5] - a[5] * b[4]
14351 + a[6] * b[0]
14352 + a[7] * b[2];
14353
14354 r[7] = a[0] * b[7]
14356 + a[1] * b[5]
14357 + a[2] * b[6]
14358 + a[3] * b[4]
14359 + a[4] * b[3]
14360 + a[5] * b[1]
14361 + a[6] * b[2]
14362 + a[7] * b[0];
14363
14364 Ok(make_multivector(r))
14365 });
14366
14367 define(interp, "mv_wedge", Some(2), |_, args| {
14370 let a = extract_multivector(&args[0], "mv_wedge")?;
14371 let b = extract_multivector(&args[1], "mv_wedge")?;
14372
14373 let mut r = [0.0f64; 8];
14374
14375 r[0] = a[0] * b[0];
14377
14378 r[1] = a[0] * b[1] + a[1] * b[0];
14380 r[2] = a[0] * b[2] + a[2] * b[0];
14381 r[3] = a[0] * b[3] + a[3] * b[0];
14382
14383 r[4] = a[0] * b[4] + a[1] * b[2] - a[2] * b[1] + a[4] * b[0];
14385 r[5] = a[0] * b[5] + a[2] * b[3] - a[3] * b[2] + a[5] * b[0];
14386 r[6] = a[0] * b[6] + a[3] * b[1] - a[1] * b[3] + a[6] * b[0];
14387
14388 r[7] = a[0] * b[7] + a[7] * b[0] + a[1] * b[5] + a[2] * b[6] + a[3] * b[4]
14390 - a[4] * b[3]
14391 - a[5] * b[1]
14392 - a[6] * b[2];
14393
14394 Ok(make_multivector(r))
14395 });
14396
14397 define(interp, "mv_inner", Some(2), |_, args| {
14400 let a = extract_multivector(&args[0], "mv_inner")?;
14401 let b = extract_multivector(&args[1], "mv_inner")?;
14402
14403 let mut r = [0.0f64; 8];
14404
14405 r[0] = a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
14408 - a[4] * b[4]
14409 - a[5] * b[5]
14410 - a[6] * b[6]
14411 - a[7] * b[7];
14412
14413 r[1] = a[4] * b[2] - a[6] * b[3] - a[5] * b[7];
14415 r[2] = -a[4] * b[1] + a[5] * b[3] - a[6] * b[7];
14416 r[3] = a[6] * b[1] - a[5] * b[2] - a[4] * b[7];
14417
14418 r[4] = a[7] * b[3];
14420 r[5] = a[7] * b[1];
14421 r[6] = a[7] * b[2];
14422
14423 Ok(make_multivector(r))
14424 });
14425
14426 define(interp, "mv_reverse", Some(1), |_, args| {
14429 let a = extract_multivector(&args[0], "mv_reverse")?;
14430 Ok(make_multivector([
14432 a[0], a[1], a[2], a[3], -a[4], -a[5], -a[6], -a[7],
14433 ]))
14434 });
14435
14436 define(interp, "mv_dual", Some(1), |_, args| {
14439 let a = extract_multivector(&args[0], "mv_dual")?;
14440 Ok(make_multivector([
14443 -a[7], -a[5], -a[6], -a[4], a[3], a[1], a[2], a[0], ]))
14452 });
14453
14454 define(interp, "mv_magnitude", Some(1), |_, args| {
14456 let a = extract_multivector(&args[0], "mv_magnitude")?;
14457 let mag_sq = a[0] * a[0]
14458 + a[1] * a[1]
14459 + a[2] * a[2]
14460 + a[3] * a[3]
14461 + a[4] * a[4]
14462 + a[5] * a[5]
14463 + a[6] * a[6]
14464 + a[7] * a[7];
14465 Ok(Value::Float(mag_sq.sqrt()))
14466 });
14467
14468 define(interp, "mv_normalize", Some(1), |_, args| {
14470 let a = extract_multivector(&args[0], "mv_normalize")?;
14471 let mag = (a[0] * a[0]
14472 + a[1] * a[1]
14473 + a[2] * a[2]
14474 + a[3] * a[3]
14475 + a[4] * a[4]
14476 + a[5] * a[5]
14477 + a[6] * a[6]
14478 + a[7] * a[7])
14479 .sqrt();
14480 if mag < 1e-10 {
14481 return Ok(make_multivector([0.0; 8]));
14482 }
14483 Ok(make_multivector([
14484 a[0] / mag,
14485 a[1] / mag,
14486 a[2] / mag,
14487 a[3] / mag,
14488 a[4] / mag,
14489 a[5] / mag,
14490 a[6] / mag,
14491 a[7] / mag,
14492 ]))
14493 });
14494
14495 define(interp, "rotor_from_axis_angle", Some(2), |_, args| {
14498 let axis = extract_vec3(&args[0], "rotor_from_axis_angle")?;
14499 let angle = match &args[1] {
14500 Value::Float(f) => *f,
14501 Value::Int(n) => *n as f64,
14502 _ => {
14503 return Err(RuntimeError::new(
14504 "rotor_from_axis_angle: angle must be number",
14505 ))
14506 }
14507 };
14508
14509 let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
14511 if len < 1e-10 {
14512 return Ok(make_multivector([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]));
14514 }
14515 let (nx, ny, nz) = (axis[0] / len, axis[1] / len, axis[2] / len);
14516
14517 let half_angle = angle / 2.0;
14518 let (s, c) = half_angle.sin_cos();
14519
14520 Ok(make_multivector([
14523 c, 0.0,
14525 0.0,
14526 0.0, -s * nz, -s * nx, -s * ny, 0.0, ]))
14532 });
14533
14534 define(interp, "rotor_apply", Some(2), |_, args| {
14537 let r = extract_multivector(&args[0], "rotor_apply")?;
14538 let v = extract_vec3(&args[1], "rotor_apply")?;
14539
14540 let v_mv = [0.0, v[0], v[1], v[2], 0.0, 0.0, 0.0, 0.0];
14542
14543 let r_rev = [r[0], r[1], r[2], r[3], -r[4], -r[5], -r[6], -r[7]];
14545
14546 let mut rv = [0.0f64; 8];
14548 rv[0] = r[0] * v_mv[0] + r[1] * v_mv[1] + r[2] * v_mv[2] + r[3] * v_mv[3]
14549 - r[4] * v_mv[4]
14550 - r[5] * v_mv[5]
14551 - r[6] * v_mv[6]
14552 - r[7] * v_mv[7];
14553 rv[1] = r[0] * v_mv[1] + r[1] * v_mv[0] - r[2] * v_mv[4] + r[3] * v_mv[6] + r[4] * v_mv[2]
14554 - r[5] * v_mv[7]
14555 - r[6] * v_mv[3]
14556 - r[7] * v_mv[5];
14557 rv[2] = r[0] * v_mv[2] + r[1] * v_mv[4] + r[2] * v_mv[0] - r[3] * v_mv[5] - r[4] * v_mv[1]
14558 + r[5] * v_mv[3]
14559 - r[6] * v_mv[7]
14560 - r[7] * v_mv[6];
14561 rv[3] = r[0] * v_mv[3] - r[1] * v_mv[6] + r[2] * v_mv[5] + r[3] * v_mv[0]
14562 - r[4] * v_mv[7]
14563 - r[5] * v_mv[2]
14564 + r[6] * v_mv[1]
14565 - r[7] * v_mv[4];
14566 rv[4] = r[0] * v_mv[4] + r[1] * v_mv[2] - r[2] * v_mv[1]
14567 + r[3] * v_mv[7]
14568 + r[4] * v_mv[0]
14569 + r[5] * v_mv[6]
14570 - r[6] * v_mv[5]
14571 + r[7] * v_mv[3];
14572 rv[5] = r[0] * v_mv[5] + r[1] * v_mv[7] + r[2] * v_mv[3] - r[3] * v_mv[2] - r[4] * v_mv[6]
14573 + r[5] * v_mv[0]
14574 + r[6] * v_mv[4]
14575 + r[7] * v_mv[1];
14576 rv[6] = r[0] * v_mv[6] - r[1] * v_mv[3] + r[2] * v_mv[7] + r[3] * v_mv[1] + r[4] * v_mv[5]
14577 - r[5] * v_mv[4]
14578 + r[6] * v_mv[0]
14579 + r[7] * v_mv[2];
14580 rv[7] = r[0] * v_mv[7]
14581 + r[1] * v_mv[5]
14582 + r[2] * v_mv[6]
14583 + r[3] * v_mv[4]
14584 + r[4] * v_mv[3]
14585 + r[5] * v_mv[1]
14586 + r[6] * v_mv[2]
14587 + r[7] * v_mv[0];
14588
14589 let mut result = [0.0f64; 8];
14591 result[1] = rv[0] * r_rev[1] + rv[1] * r_rev[0] - rv[2] * r_rev[4]
14592 + rv[3] * r_rev[6]
14593 + rv[4] * r_rev[2]
14594 - rv[5] * r_rev[7]
14595 - rv[6] * r_rev[3]
14596 - rv[7] * r_rev[5];
14597 result[2] = rv[0] * r_rev[2] + rv[1] * r_rev[4] + rv[2] * r_rev[0]
14598 - rv[3] * r_rev[5]
14599 - rv[4] * r_rev[1]
14600 + rv[5] * r_rev[3]
14601 - rv[6] * r_rev[7]
14602 - rv[7] * r_rev[6];
14603 result[3] = rv[0] * r_rev[3] - rv[1] * r_rev[6] + rv[2] * r_rev[5] + rv[3] * r_rev[0]
14604 - rv[4] * r_rev[7]
14605 - rv[5] * r_rev[2]
14606 + rv[6] * r_rev[1]
14607 - rv[7] * r_rev[4];
14608
14609 Ok(make_vec3(result[1], result[2], result[3]))
14611 });
14612
14613 define(interp, "rotor_compose", Some(2), |_, args| {
14615 let a = extract_multivector(&args[0], "rotor_compose")?;
14616 let b = extract_multivector(&args[1], "rotor_compose")?;
14617
14618 let mut r = [0.0f64; 8];
14620 r[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
14621 - a[4] * b[4]
14622 - a[5] * b[5]
14623 - a[6] * b[6]
14624 - a[7] * b[7];
14625 r[1] = a[0] * b[1] + a[1] * b[0] - a[2] * b[4] + a[3] * b[6] + a[4] * b[2]
14626 - a[5] * b[7]
14627 - a[6] * b[3]
14628 - a[7] * b[5];
14629 r[2] = a[0] * b[2] + a[1] * b[4] + a[2] * b[0] - a[3] * b[5] - a[4] * b[1] + a[5] * b[3]
14630 - a[6] * b[7]
14631 - a[7] * b[6];
14632 r[3] = a[0] * b[3] - a[1] * b[6] + a[2] * b[5] + a[3] * b[0] - a[4] * b[7] - a[5] * b[2]
14633 + a[6] * b[1]
14634 - a[7] * b[4];
14635 r[4] = a[0] * b[4] + a[1] * b[2] - a[2] * b[1] + a[3] * b[7] + a[4] * b[0] + a[5] * b[6]
14636 - a[6] * b[5]
14637 + a[7] * b[3];
14638 r[5] = a[0] * b[5] + a[1] * b[7] + a[2] * b[3] - a[3] * b[2] - a[4] * b[6]
14639 + a[5] * b[0]
14640 + a[6] * b[4]
14641 + a[7] * b[1];
14642 r[6] = a[0] * b[6] - a[1] * b[3] + a[2] * b[7] + a[3] * b[1] + a[4] * b[5] - a[5] * b[4]
14643 + a[6] * b[0]
14644 + a[7] * b[2];
14645 r[7] = a[0] * b[7]
14646 + a[1] * b[5]
14647 + a[2] * b[6]
14648 + a[3] * b[4]
14649 + a[4] * b[3]
14650 + a[5] * b[1]
14651 + a[6] * b[2]
14652 + a[7] * b[0];
14653
14654 Ok(make_multivector(r))
14655 });
14656
14657 define(interp, "mv_reflect", Some(2), |_, args| {
14660 let v = extract_vec3(&args[0], "mv_reflect")?;
14661 let n = extract_vec3(&args[1], "mv_reflect")?;
14662
14663 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
14665 if len < 1e-10 {
14666 return Ok(make_vec3(v[0], v[1], v[2]));
14667 }
14668 let (nx, ny, nz) = (n[0] / len, n[1] / len, n[2] / len);
14669
14670 let dot = v[0] * nx + v[1] * ny + v[2] * nz;
14672 Ok(make_vec3(
14673 v[0] - 2.0 * dot * nx,
14674 v[1] - 2.0 * dot * ny,
14675 v[2] - 2.0 * dot * nz,
14676 ))
14677 });
14678
14679 define(interp, "mv_project", Some(2), |_, args| {
14681 let v = extract_vec3(&args[0], "mv_project")?;
14682 let n = extract_vec3(&args[1], "mv_project")?;
14683
14684 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
14685 if len < 1e-10 {
14686 return Ok(make_vec3(v[0], v[1], v[2]));
14687 }
14688 let (nx, ny, nz) = (n[0] / len, n[1] / len, n[2] / len);
14689
14690 let dot = v[0] * nx + v[1] * ny + v[2] * nz;
14692 Ok(make_vec3(v[0] - dot * nx, v[1] - dot * ny, v[2] - dot * nz))
14693 });
14694
14695 define(interp, "mv_grade", Some(2), |_, args| {
14697 let a = extract_multivector(&args[0], "mv_grade")?;
14698 let k = match &args[1] {
14699 Value::Int(n) => *n,
14700 _ => return Err(RuntimeError::new("mv_grade: grade must be integer")),
14701 };
14702
14703 match k {
14704 0 => Ok(make_multivector([a[0], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])),
14705 1 => Ok(make_multivector([
14706 0.0, a[1], a[2], a[3], 0.0, 0.0, 0.0, 0.0,
14707 ])),
14708 2 => Ok(make_multivector([
14709 0.0, 0.0, 0.0, 0.0, a[4], a[5], a[6], 0.0,
14710 ])),
14711 3 => Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, a[7]])),
14712 _ => Ok(make_multivector([0.0; 8])),
14713 }
14714 });
14715}
14716
14717fn register_dimensional(interp: &mut Interpreter) {
14724 fn make_quantity(value: f64, units: [i32; 7]) -> Value {
14727 let mut q = HashMap::new();
14728 q.insert("value".to_string(), Value::Float(value));
14729 q.insert("m".to_string(), Value::Int(units[0] as i64)); q.insert("kg".to_string(), Value::Int(units[1] as i64)); q.insert("s".to_string(), Value::Int(units[2] as i64)); q.insert("A".to_string(), Value::Int(units[3] as i64)); q.insert("K".to_string(), Value::Int(units[4] as i64)); q.insert("mol".to_string(), Value::Int(units[5] as i64)); q.insert("cd".to_string(), Value::Int(units[6] as i64)); q.insert(
14737 "_type".to_string(),
14738 Value::String(Rc::new("quantity".to_string())),
14739 );
14740 Value::Map(Rc::new(RefCell::new(q)))
14741 }
14742
14743 fn extract_quantity(v: &Value, fn_name: &str) -> Result<(f64, [i32; 7]), RuntimeError> {
14744 match v {
14745 Value::Map(map) => {
14746 let map = map.borrow();
14747 let value = match map.get("value") {
14748 Some(Value::Float(f)) => *f,
14749 Some(Value::Int(n)) => *n as f64,
14750 _ => return Err(RuntimeError::new(format!("{}: missing value", fn_name))),
14751 };
14752 let get_exp = |key: &str| -> i32 {
14753 match map.get(key) {
14754 Some(Value::Int(n)) => *n as i32,
14755 _ => 0,
14756 }
14757 };
14758 Ok((
14759 value,
14760 [
14761 get_exp("m"),
14762 get_exp("kg"),
14763 get_exp("s"),
14764 get_exp("A"),
14765 get_exp("K"),
14766 get_exp("mol"),
14767 get_exp("cd"),
14768 ],
14769 ))
14770 }
14771 Value::Float(f) => Ok((*f, [0; 7])),
14772 Value::Int(n) => Ok((*n as f64, [0; 7])),
14773 _ => Err(RuntimeError::new(format!(
14774 "{}: expected quantity or number",
14775 fn_name
14776 ))),
14777 }
14778 }
14779
14780 fn units_to_string(units: [i32; 7]) -> String {
14781 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
14782 let mut parts = Vec::new();
14783 for (i, &exp) in units.iter().enumerate() {
14784 if exp == 1 {
14785 parts.push(names[i].to_string());
14786 } else if exp != 0 {
14787 parts.push(format!("{}^{}", names[i], exp));
14788 }
14789 }
14790 if parts.is_empty() {
14791 "dimensionless".to_string()
14792 } else {
14793 parts.join("·")
14794 }
14795 }
14796
14797 define(interp, "qty", Some(2), |_, args| {
14800 let value = match &args[0] {
14801 Value::Float(f) => *f,
14802 Value::Int(n) => *n as f64,
14803 _ => return Err(RuntimeError::new("qty: first argument must be number")),
14804 };
14805 let unit_str = match &args[1] {
14806 Value::String(s) => s.to_string(),
14807 _ => {
14808 return Err(RuntimeError::new(
14809 "qty: second argument must be unit string",
14810 ))
14811 }
14812 };
14813
14814 let mut units = [0i32; 7];
14816 let in_denominator = unit_str.contains('/');
14819
14820 for part in unit_str.split(|c: char| c == '*' || c == '·' || c == ' ') {
14822 let part = part.trim();
14823 if part.is_empty() {
14824 continue;
14825 }
14826
14827 let (base, exp) = if let Some(idx) = part.find('^') {
14828 let (b, e) = part.split_at(idx);
14829 (b, e[1..].parse::<i32>().unwrap_or(1))
14830 } else if part.contains('/') {
14831 continue;
14833 } else {
14834 (part, 1)
14835 };
14836
14837 let sign = if in_denominator { -1 } else { 1 };
14838 match base {
14839 "m" | "meter" | "meters" => units[0] += exp * sign,
14840 "kg" | "kilogram" | "kilograms" => units[1] += exp * sign,
14841 "s" | "sec" | "second" | "seconds" => units[2] += exp * sign,
14842 "A" | "amp" | "ampere" | "amperes" => units[3] += exp * sign,
14843 "K" | "kelvin" => units[4] += exp * sign,
14844 "mol" | "mole" | "moles" => units[5] += exp * sign,
14845 "cd" | "candela" => units[6] += exp * sign,
14846 "N" | "newton" | "newtons" => {
14848 units[1] += sign;
14849 units[0] += sign;
14850 units[2] -= 2 * sign;
14851 }
14852 "J" | "joule" | "joules" => {
14853 units[1] += sign;
14854 units[0] += 2 * sign;
14855 units[2] -= 2 * sign;
14856 }
14857 "W" | "watt" | "watts" => {
14858 units[1] += sign;
14859 units[0] += 2 * sign;
14860 units[2] -= 3 * sign;
14861 }
14862 "Pa" | "pascal" | "pascals" => {
14863 units[1] += sign;
14864 units[0] -= sign;
14865 units[2] -= 2 * sign;
14866 }
14867 "Hz" | "hertz" => {
14868 units[2] -= sign;
14869 }
14870 "C" | "coulomb" | "coulombs" => {
14871 units[3] += sign;
14872 units[2] += sign;
14873 }
14874 "V" | "volt" | "volts" => {
14875 units[1] += sign;
14876 units[0] += 2 * sign;
14877 units[2] -= 3 * sign;
14878 units[3] -= sign;
14879 }
14880 "Ω" | "ohm" | "ohms" => {
14881 units[1] += sign;
14882 units[0] += 2 * sign;
14883 units[2] -= 3 * sign;
14884 units[3] -= 2 * sign;
14885 }
14886 _ => {}
14887 }
14888 }
14889
14890 Ok(make_quantity(value, units))
14891 });
14892
14893 define(interp, "qty_add", Some(2), |_, args| {
14895 let (val_a, units_a) = extract_quantity(&args[0], "qty_add")?;
14896 let (val_b, units_b) = extract_quantity(&args[1], "qty_add")?;
14897
14898 if units_a != units_b {
14899 return Err(RuntimeError::new(format!(
14900 "qty_add: unit mismatch: {} vs {}",
14901 units_to_string(units_a),
14902 units_to_string(units_b)
14903 )));
14904 }
14905
14906 Ok(make_quantity(val_a + val_b, units_a))
14907 });
14908
14909 define(interp, "qty_sub", Some(2), |_, args| {
14911 let (val_a, units_a) = extract_quantity(&args[0], "qty_sub")?;
14912 let (val_b, units_b) = extract_quantity(&args[1], "qty_sub")?;
14913
14914 if units_a != units_b {
14915 return Err(RuntimeError::new(format!(
14916 "qty_sub: unit mismatch: {} vs {}",
14917 units_to_string(units_a),
14918 units_to_string(units_b)
14919 )));
14920 }
14921
14922 Ok(make_quantity(val_a - val_b, units_a))
14923 });
14924
14925 define(interp, "qty_mul", Some(2), |_, args| {
14927 let (val_a, units_a) = extract_quantity(&args[0], "qty_mul")?;
14928 let (val_b, units_b) = extract_quantity(&args[1], "qty_mul")?;
14929
14930 let mut result_units = [0i32; 7];
14931 for i in 0..7 {
14932 result_units[i] = units_a[i] + units_b[i];
14933 }
14934
14935 Ok(make_quantity(val_a * val_b, result_units))
14936 });
14937
14938 define(interp, "qty_div", Some(2), |_, args| {
14940 let (val_a, units_a) = extract_quantity(&args[0], "qty_div")?;
14941 let (val_b, units_b) = extract_quantity(&args[1], "qty_div")?;
14942
14943 if val_b.abs() < 1e-15 {
14944 return Err(RuntimeError::new("qty_div: division by zero"));
14945 }
14946
14947 let mut result_units = [0i32; 7];
14948 for i in 0..7 {
14949 result_units[i] = units_a[i] - units_b[i];
14950 }
14951
14952 Ok(make_quantity(val_a / val_b, result_units))
14953 });
14954
14955 define(interp, "qty_pow", Some(2), |_, args| {
14957 let (val, units) = extract_quantity(&args[0], "qty_pow")?;
14958 let n = match &args[1] {
14959 Value::Int(n) => *n as i32,
14960 Value::Float(f) => *f as i32,
14961 _ => return Err(RuntimeError::new("qty_pow: exponent must be number")),
14962 };
14963
14964 let mut result_units = [0i32; 7];
14965 for i in 0..7 {
14966 result_units[i] = units[i] * n;
14967 }
14968
14969 Ok(make_quantity(val.powi(n), result_units))
14970 });
14971
14972 define(interp, "qty_sqrt", Some(1), |_, args| {
14974 let (val, units) = extract_quantity(&args[0], "qty_sqrt")?;
14975
14976 for (i, &exp) in units.iter().enumerate() {
14978 if exp % 2 != 0 {
14979 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
14980 return Err(RuntimeError::new(format!(
14981 "qty_sqrt: cannot take sqrt of {} (odd exponent on {})",
14982 units_to_string(units),
14983 names[i]
14984 )));
14985 }
14986 }
14987
14988 let mut result_units = [0i32; 7];
14989 for i in 0..7 {
14990 result_units[i] = units[i] / 2;
14991 }
14992
14993 Ok(make_quantity(val.sqrt(), result_units))
14994 });
14995
14996 define(interp, "qty_value", Some(1), |_, args| {
14998 let (val, _) = extract_quantity(&args[0], "qty_value")?;
14999 Ok(Value::Float(val))
15000 });
15001
15002 define(interp, "qty_units", Some(1), |_, args| {
15004 let (_, units) = extract_quantity(&args[0], "qty_units")?;
15005 Ok(Value::String(Rc::new(units_to_string(units))))
15006 });
15007
15008 define(interp, "qty_convert", Some(2), |_, args| {
15011 let (val, units) = extract_quantity(&args[0], "qty_convert")?;
15012 let _target = match &args[1] {
15013 Value::String(s) => s.to_string(),
15014 _ => return Err(RuntimeError::new("qty_convert: target must be unit string")),
15015 };
15016
15017 Ok(make_quantity(val, units))
15020 });
15021
15022 define(interp, "qty_check", Some(2), |_, args| {
15024 let (_, units) = extract_quantity(&args[0], "qty_check")?;
15025 let expected = match &args[1] {
15026 Value::String(s) => s.to_string(),
15027 _ => return Err(RuntimeError::new("qty_check: expected string")),
15028 };
15029
15030 let actual_str = units_to_string(units);
15032 Ok(Value::Bool(
15033 actual_str.contains(&expected) || expected.contains(&actual_str),
15034 ))
15035 });
15036
15037 define(interp, "c_light", Some(0), |_, _| {
15040 Ok(make_quantity(299792458.0, [1, 0, -1, 0, 0, 0, 0])) });
15042
15043 define(interp, "G_gravity", Some(0), |_, _| {
15045 Ok(make_quantity(6.67430e-11, [3, -1, -2, 0, 0, 0, 0])) });
15047
15048 define(interp, "h_planck", Some(0), |_, _| {
15050 Ok(make_quantity(6.62607015e-34, [2, 1, -1, 0, 0, 0, 0])) });
15052
15053 define(interp, "e_charge", Some(0), |_, _| {
15055 Ok(make_quantity(1.602176634e-19, [0, 0, 1, 1, 0, 0, 0])) });
15057
15058 define(interp, "k_boltzmann", Some(0), |_, _| {
15060 Ok(make_quantity(1.380649e-23, [2, 1, -2, 0, -1, 0, 0])) });
15062}
15063
15064fn register_ecs(interp: &mut Interpreter) {
15144 define(interp, "ecs_world", Some(0), |_, _| {
15146 let mut world = HashMap::new();
15147 world.insert(
15148 "_type".to_string(),
15149 Value::String(Rc::new("ecs_world".to_string())),
15150 );
15151 world.insert("next_id".to_string(), Value::Int(0));
15152 world.insert(
15153 "entities".to_string(),
15154 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
15155 );
15156 world.insert(
15157 "components".to_string(),
15158 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
15159 );
15160 Ok(Value::Map(Rc::new(RefCell::new(world))))
15161 });
15162
15163 define(interp, "ecs_spawn", Some(1), |_, args| {
15165 let world = match &args[0] {
15166 Value::Map(m) => m.clone(),
15167 _ => return Err(RuntimeError::new("ecs_spawn: expected world")),
15168 };
15169
15170 let mut world_ref = world.borrow_mut();
15171 let id = match world_ref.get("next_id") {
15172 Some(Value::Int(n)) => *n,
15173 _ => 0,
15174 };
15175
15176 world_ref.insert("next_id".to_string(), Value::Int(id + 1));
15178
15179 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15181 entities
15182 .borrow_mut()
15183 .insert(id.to_string(), Value::Bool(true));
15184 }
15185
15186 Ok(Value::Int(id))
15187 });
15188
15189 define(interp, "ecs_despawn", Some(2), |_, args| {
15191 let world = match &args[0] {
15192 Value::Map(m) => m.clone(),
15193 _ => return Err(RuntimeError::new("ecs_despawn: expected world")),
15194 };
15195 let id = match &args[1] {
15196 Value::Int(n) => *n,
15197 _ => return Err(RuntimeError::new("ecs_despawn: expected entity id")),
15198 };
15199
15200 let world_ref = world.borrow();
15201
15202 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15204 entities.borrow_mut().remove(&id.to_string());
15205 }
15206
15207 if let Some(Value::Map(components)) = world_ref.get("components") {
15209 let comps = components.borrow();
15210 for (_, comp_storage) in comps.iter() {
15211 if let Value::Map(storage) = comp_storage {
15212 storage.borrow_mut().remove(&id.to_string());
15213 }
15214 }
15215 }
15216
15217 Ok(Value::Bool(true))
15218 });
15219
15220 define(interp, "ecs_attach", Some(4), |_, args| {
15222 let world = match &args[0] {
15223 Value::Map(m) => m.clone(),
15224 _ => {
15225 return Err(RuntimeError::new(
15226 "ecs_attach() expects a world as first argument.\n\
15227 Usage: ecs_attach(world, entity_id, component_name, data)\n\
15228 Example:\n\
15229 let world = ecs_world();\n\
15230 let e = ecs_spawn(world);\n\
15231 ecs_attach(world, e, \"Position\", vec3(0, 0, 0));",
15232 ))
15233 }
15234 };
15235 let id = match &args[1] {
15236 Value::Int(n) => *n,
15237 _ => {
15238 return Err(RuntimeError::new(
15239 "ecs_attach() expects an entity ID (integer) as second argument.\n\
15240 Entity IDs are returned by ecs_spawn().\n\
15241 Example:\n\
15242 let entity = ecs_spawn(world); // Returns 0, 1, 2...\n\
15243 ecs_attach(world, entity, \"Health\", 100);",
15244 ))
15245 }
15246 };
15247 let comp_name = match &args[2] {
15248 Value::String(s) => s.to_string(),
15249 _ => {
15250 return Err(RuntimeError::new(
15251 "ecs_attach() expects a string component name as third argument.\n\
15252 Common component names: \"Position\", \"Velocity\", \"Health\", \"Sprite\"\n\
15253 Example: ecs_attach(world, entity, \"Position\", vec3(0, 0, 0));",
15254 ))
15255 }
15256 };
15257 let data = args[3].clone();
15258
15259 let world_ref = world.borrow();
15260
15261 if let Some(Value::Map(components)) = world_ref.get("components") {
15263 let mut comps = components.borrow_mut();
15264
15265 let storage = comps
15266 .entry(comp_name.clone())
15267 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
15268
15269 if let Value::Map(storage_map) = storage {
15270 storage_map.borrow_mut().insert(id.to_string(), data);
15271 }
15272 }
15273
15274 Ok(Value::Bool(true))
15275 });
15276
15277 define(interp, "ecs_get", Some(3), |_, args| {
15279 let world = match &args[0] {
15280 Value::Map(m) => m.clone(),
15281 _ => return Err(RuntimeError::new("ecs_get: expected world")),
15282 };
15283 let id = match &args[1] {
15284 Value::Int(n) => *n,
15285 _ => return Err(RuntimeError::new("ecs_get: expected entity id")),
15286 };
15287 let comp_name = match &args[2] {
15288 Value::String(s) => s.to_string(),
15289 _ => return Err(RuntimeError::new("ecs_get: expected component name")),
15290 };
15291
15292 let world_ref = world.borrow();
15293
15294 if let Some(Value::Map(components)) = world_ref.get("components") {
15295 let comps = components.borrow();
15296 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15297 if let Some(data) = storage.borrow().get(&id.to_string()) {
15298 return Ok(data.clone());
15299 }
15300 }
15301 }
15302
15303 Ok(Value::Null)
15304 });
15305
15306 define(interp, "ecs_has", Some(3), |_, args| {
15308 let world = match &args[0] {
15309 Value::Map(m) => m.clone(),
15310 _ => return Err(RuntimeError::new("ecs_has: expected world")),
15311 };
15312 let id = match &args[1] {
15313 Value::Int(n) => *n,
15314 _ => return Err(RuntimeError::new("ecs_has: expected entity id")),
15315 };
15316 let comp_name = match &args[2] {
15317 Value::String(s) => s.to_string(),
15318 _ => return Err(RuntimeError::new("ecs_has: expected component name")),
15319 };
15320
15321 let world_ref = world.borrow();
15322
15323 if let Some(Value::Map(components)) = world_ref.get("components") {
15324 let comps = components.borrow();
15325 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15326 return Ok(Value::Bool(storage.borrow().contains_key(&id.to_string())));
15327 }
15328 }
15329
15330 Ok(Value::Bool(false))
15331 });
15332
15333 define(interp, "ecs_remove", Some(3), |_, args| {
15335 let world = match &args[0] {
15336 Value::Map(m) => m.clone(),
15337 _ => return Err(RuntimeError::new("ecs_remove: expected world")),
15338 };
15339 let id = match &args[1] {
15340 Value::Int(n) => *n,
15341 _ => return Err(RuntimeError::new("ecs_remove: expected entity id")),
15342 };
15343 let comp_name = match &args[2] {
15344 Value::String(s) => s.to_string(),
15345 _ => return Err(RuntimeError::new("ecs_remove: expected component name")),
15346 };
15347
15348 let world_ref = world.borrow();
15349
15350 if let Some(Value::Map(components)) = world_ref.get("components") {
15351 let comps = components.borrow();
15352 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15353 storage.borrow_mut().remove(&id.to_string());
15354 return Ok(Value::Bool(true));
15355 }
15356 }
15357
15358 Ok(Value::Bool(false))
15359 });
15360
15361 define(interp, "ecs_query", None, |_, args| {
15364 if args.is_empty() {
15365 return Err(RuntimeError::new(
15366 "ecs_query: expected at least world argument",
15367 ));
15368 }
15369
15370 let world = match &args[0] {
15371 Value::Map(m) => m.clone(),
15372 _ => return Err(RuntimeError::new("ecs_query: expected world")),
15373 };
15374
15375 let comp_names: Vec<String> = args[1..]
15376 .iter()
15377 .filter_map(|a| match a {
15378 Value::String(s) => Some(s.to_string()),
15379 _ => None,
15380 })
15381 .collect();
15382
15383 if comp_names.is_empty() {
15384 let world_ref = world.borrow();
15386 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15387 let result: Vec<Value> = entities
15388 .borrow()
15389 .keys()
15390 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
15391 .collect();
15392 return Ok(Value::Array(Rc::new(RefCell::new(result))));
15393 }
15394 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15395 }
15396
15397 let world_ref = world.borrow();
15398 let mut result_ids: Option<Vec<String>> = None;
15399
15400 if let Some(Value::Map(components)) = world_ref.get("components") {
15401 let comps = components.borrow();
15402
15403 for comp_name in &comp_names {
15404 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15405 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
15406
15407 result_ids = Some(match result_ids {
15408 None => keys,
15409 Some(existing) => {
15410 existing.into_iter().filter(|k| keys.contains(k)).collect()
15411 }
15412 });
15413 } else {
15414 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15416 }
15417 }
15418 }
15419
15420 let result: Vec<Value> = result_ids
15421 .unwrap_or_default()
15422 .iter()
15423 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
15424 .collect();
15425
15426 Ok(Value::Array(Rc::new(RefCell::new(result))))
15427 });
15428
15429 define(interp, "ecs_query_with", Some(3), |interp, args| {
15432 let world = match &args[0] {
15433 Value::Map(m) => m.clone(),
15434 _ => return Err(RuntimeError::new("ecs_query_with: expected world")),
15435 };
15436 let comp_names: Vec<String> = match &args[1] {
15437 Value::Array(arr) => arr
15438 .borrow()
15439 .iter()
15440 .filter_map(|v| match v {
15441 Value::String(s) => Some(s.to_string()),
15442 _ => None,
15443 })
15444 .collect(),
15445 _ => {
15446 return Err(RuntimeError::new(
15447 "ecs_query_with: expected array of component names",
15448 ))
15449 }
15450 };
15451 let callback = match &args[2] {
15452 Value::Function(f) => f.clone(),
15453 _ => {
15454 return Err(RuntimeError::new(
15455 "ecs_query_with: expected callback function",
15456 ))
15457 }
15458 };
15459
15460 let mut callback_data: Vec<(i64, HashMap<String, Value>)> = Vec::new();
15462
15463 {
15464 let world_ref = world.borrow();
15465 let mut result_ids: Option<Vec<String>> = None;
15466
15467 if let Some(Value::Map(components)) = world_ref.get("components") {
15468 let comps = components.borrow();
15469
15470 for comp_name in &comp_names {
15471 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15472 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
15473 result_ids = Some(match result_ids {
15474 None => keys,
15475 Some(existing) => {
15476 existing.into_iter().filter(|k| keys.contains(k)).collect()
15477 }
15478 });
15479 } else {
15480 result_ids = Some(vec![]);
15481 break;
15482 }
15483 }
15484
15485 for id_str in result_ids.unwrap_or_default() {
15487 if let Ok(id) = id_str.parse::<i64>() {
15488 let mut entity_comps = HashMap::new();
15489 for comp_name in &comp_names {
15490 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15491 if let Some(data) = storage.borrow().get(&id_str) {
15492 entity_comps.insert(comp_name.clone(), data.clone());
15493 }
15494 }
15495 }
15496 callback_data.push((id, entity_comps));
15497 }
15498 }
15499 }
15500 } for (id, entity_comps) in callback_data {
15504 let callback_args = vec![
15505 Value::Int(id),
15506 Value::Map(Rc::new(RefCell::new(entity_comps))),
15507 ];
15508 interp.call_function(&callback, callback_args)?;
15509 }
15510
15511 Ok(Value::Null)
15512 });
15513
15514 define(interp, "ecs_count", Some(1), |_, args| {
15516 let world = match &args[0] {
15517 Value::Map(m) => m.clone(),
15518 _ => return Err(RuntimeError::new("ecs_count: expected world")),
15519 };
15520
15521 let world_ref = world.borrow();
15522 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15523 return Ok(Value::Int(entities.borrow().len() as i64));
15524 }
15525
15526 Ok(Value::Int(0))
15527 });
15528
15529 define(interp, "ecs_alive", Some(2), |_, args| {
15531 let world = match &args[0] {
15532 Value::Map(m) => m.clone(),
15533 _ => return Err(RuntimeError::new("ecs_alive: expected world")),
15534 };
15535 let id = match &args[1] {
15536 Value::Int(n) => *n,
15537 _ => return Err(RuntimeError::new("ecs_alive: expected entity id")),
15538 };
15539
15540 let world_ref = world.borrow();
15541 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15542 return Ok(Value::Bool(entities.borrow().contains_key(&id.to_string())));
15543 }
15544
15545 Ok(Value::Bool(false))
15546 });
15547}
15548
15549fn register_polycultural_text(interp: &mut Interpreter) {
15569 define(interp, "script", Some(1), |_, args| {
15579 match &args[0] {
15580 Value::String(s) => {
15581 let mut script_counts: HashMap<String, usize> = HashMap::new();
15583 for c in s.chars() {
15584 if !c.is_whitespace() && !c.is_ascii_punctuation() {
15585 let script = c.script();
15586 let name = format!("{:?}", script);
15587 *script_counts.entry(name).or_insert(0) += 1;
15588 }
15589 }
15590 let dominant = script_counts
15592 .into_iter()
15593 .max_by_key(|(_, count)| *count)
15594 .map(|(name, _)| name)
15595 .unwrap_or_else(|| "Unknown".to_string());
15596 Ok(Value::String(Rc::new(dominant)))
15597 }
15598 _ => Err(RuntimeError::new("script() requires string")),
15599 }
15600 });
15601
15602 define(interp, "scripts", Some(1), |_, args| match &args[0] {
15604 Value::String(s) => {
15605 let mut scripts: Vec<String> = s
15606 .chars()
15607 .filter(|c| !c.is_whitespace() && !c.is_ascii_punctuation())
15608 .map(|c| format!("{:?}", c.script()))
15609 .collect();
15610 scripts.sort();
15611 scripts.dedup();
15612 let values: Vec<Value> = scripts
15613 .into_iter()
15614 .map(|s| Value::String(Rc::new(s)))
15615 .collect();
15616 Ok(Value::Array(Rc::new(RefCell::new(values))))
15617 }
15618 _ => Err(RuntimeError::new("scripts() requires string")),
15619 });
15620
15621 define(interp, "is_script", Some(2), |_, args| {
15623 match (&args[0], &args[1]) {
15624 (Value::String(s), Value::String(script_name)) => {
15625 let target = script_name.to_lowercase();
15626 let mut matching = 0usize;
15627 let mut total = 0usize;
15628 for c in s.chars() {
15629 if !c.is_whitespace() && !c.is_ascii_punctuation() {
15630 total += 1;
15631 let script_str = format!("{:?}", c.script()).to_lowercase();
15632 if script_str == target {
15633 matching += 1;
15634 }
15635 }
15636 }
15637 let ratio = if total > 0 {
15638 matching as f64 / total as f64
15639 } else {
15640 0.0
15641 };
15642 Ok(Value::Bool(ratio > 0.5))
15643 }
15644 _ => Err(RuntimeError::new(
15645 "is_script() requires string and script name",
15646 )),
15647 }
15648 });
15649
15650 define(interp, "is_latin", Some(1), |_, args| match &args[0] {
15652 Value::String(s) => {
15653 let is_latin = s
15654 .chars()
15655 .filter(|c| !c.is_whitespace())
15656 .all(|c| matches!(c.script(), Script::Latin | Script::Common));
15657 Ok(Value::Bool(is_latin && !s.is_empty()))
15658 }
15659 _ => Err(RuntimeError::new("is_latin() requires string")),
15660 });
15661
15662 define(interp, "is_cjk", Some(1), |_, args| match &args[0] {
15663 Value::String(s) => {
15664 let has_cjk = s.chars().any(|c| {
15665 matches!(
15666 c.script(),
15667 Script::Han
15668 | Script::Hiragana
15669 | Script::Katakana
15670 | Script::Hangul
15671 | Script::Bopomofo
15672 )
15673 });
15674 Ok(Value::Bool(has_cjk))
15675 }
15676 _ => Err(RuntimeError::new("is_cjk() requires string")),
15677 });
15678
15679 define(interp, "is_arabic", Some(1), |_, args| match &args[0] {
15680 Value::String(s) => {
15681 let has_arabic = s.chars().any(|c| matches!(c.script(), Script::Arabic));
15682 Ok(Value::Bool(has_arabic))
15683 }
15684 _ => Err(RuntimeError::new("is_arabic() requires string")),
15685 });
15686
15687 define(interp, "is_hebrew", Some(1), |_, args| match &args[0] {
15688 Value::String(s) => {
15689 let has_hebrew = s.chars().any(|c| matches!(c.script(), Script::Hebrew));
15690 Ok(Value::Bool(has_hebrew))
15691 }
15692 _ => Err(RuntimeError::new("is_hebrew() requires string")),
15693 });
15694
15695 define(interp, "is_cyrillic", Some(1), |_, args| match &args[0] {
15696 Value::String(s) => {
15697 let has_cyrillic = s.chars().any(|c| matches!(c.script(), Script::Cyrillic));
15698 Ok(Value::Bool(has_cyrillic))
15699 }
15700 _ => Err(RuntimeError::new("is_cyrillic() requires string")),
15701 });
15702
15703 define(interp, "is_greek", Some(1), |_, args| match &args[0] {
15704 Value::String(s) => {
15705 let has_greek = s.chars().any(|c| matches!(c.script(), Script::Greek));
15706 Ok(Value::Bool(has_greek))
15707 }
15708 _ => Err(RuntimeError::new("is_greek() requires string")),
15709 });
15710
15711 define(interp, "is_devanagari", Some(1), |_, args| match &args[0] {
15712 Value::String(s) => {
15713 let has_devanagari = s.chars().any(|c| matches!(c.script(), Script::Devanagari));
15714 Ok(Value::Bool(has_devanagari))
15715 }
15716 _ => Err(RuntimeError::new("is_devanagari() requires string")),
15717 });
15718
15719 define(interp, "is_thai", Some(1), |_, args| match &args[0] {
15720 Value::String(s) => {
15721 let has_thai = s.chars().any(|c| matches!(c.script(), Script::Thai));
15722 Ok(Value::Bool(has_thai))
15723 }
15724 _ => Err(RuntimeError::new("is_thai() requires string")),
15725 });
15726
15727 define(interp, "is_hangul", Some(1), |_, args| match &args[0] {
15728 Value::String(s) => {
15729 let has_hangul = s.chars().any(|c| matches!(c.script(), Script::Hangul));
15730 Ok(Value::Bool(has_hangul))
15731 }
15732 _ => Err(RuntimeError::new("is_hangul() requires string")),
15733 });
15734
15735 define(interp, "is_hiragana", Some(1), |_, args| match &args[0] {
15736 Value::String(s) => {
15737 let has_hiragana = s.chars().any(|c| matches!(c.script(), Script::Hiragana));
15738 Ok(Value::Bool(has_hiragana))
15739 }
15740 _ => Err(RuntimeError::new("is_hiragana() requires string")),
15741 });
15742
15743 define(interp, "is_katakana", Some(1), |_, args| match &args[0] {
15744 Value::String(s) => {
15745 let has_katakana = s.chars().any(|c| matches!(c.script(), Script::Katakana));
15746 Ok(Value::Bool(has_katakana))
15747 }
15748 _ => Err(RuntimeError::new("is_katakana() requires string")),
15749 });
15750
15751 define(interp, "char_script", Some(1), |_, args| match &args[0] {
15753 Value::Char(c) => {
15754 let script = format!("{:?}", c.script());
15755 Ok(Value::String(Rc::new(script)))
15756 }
15757 Value::String(s) if s.chars().count() == 1 => {
15758 let c = s.chars().next().unwrap();
15759 let script = format!("{:?}", c.script());
15760 Ok(Value::String(Rc::new(script)))
15761 }
15762 _ => Err(RuntimeError::new("char_script() requires single character")),
15763 });
15764
15765 define(interp, "text_direction", Some(1), |_, args| {
15775 match &args[0] {
15776 Value::String(s) => {
15777 let bidi_info = BidiInfo::new(s, None);
15778 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
15780 let direction = if has_rtl { "rtl" } else { "ltr" };
15781 Ok(Value::String(Rc::new(direction.to_string())))
15782 }
15783 _ => Err(RuntimeError::new("text_direction() requires string")),
15784 }
15785 });
15786
15787 define(interp, "is_rtl", Some(1), |_, args| match &args[0] {
15789 Value::String(s) => {
15790 let bidi_info = BidiInfo::new(s, None);
15791 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
15792 Ok(Value::Bool(has_rtl))
15793 }
15794 _ => Err(RuntimeError::new("is_rtl() requires string")),
15795 });
15796
15797 define(interp, "is_ltr", Some(1), |_, args| match &args[0] {
15799 Value::String(s) => {
15800 let bidi_info = BidiInfo::new(s, None);
15801 let is_ltr = bidi_info.paragraphs.iter().all(|p| !p.level.is_rtl());
15802 Ok(Value::Bool(is_ltr))
15803 }
15804 _ => Err(RuntimeError::new("is_ltr() requires string")),
15805 });
15806
15807 define(interp, "is_bidi", Some(1), |_, args| {
15809 match &args[0] {
15810 Value::String(s) => {
15811 let has_rtl = s.chars().any(|c| {
15813 matches!(
15814 c.script(),
15815 Script::Arabic | Script::Hebrew | Script::Syriac | Script::Thaana
15816 )
15817 });
15818 let has_ltr = s.chars().any(|c| {
15819 matches!(c.script(), Script::Latin | Script::Greek | Script::Cyrillic)
15820 });
15821 Ok(Value::Bool(has_rtl && has_ltr))
15822 }
15823 _ => Err(RuntimeError::new("is_bidi() requires string")),
15824 }
15825 });
15826
15827 define(interp, "bidi_reorder", Some(1), |_, args| match &args[0] {
15829 Value::String(s) => {
15830 let bidi_info = BidiInfo::new(s, None);
15831 let mut result = String::new();
15832 for para in &bidi_info.paragraphs {
15833 let line = para.range.clone();
15834 let reordered = bidi_info.reorder_line(para, line);
15835 result.push_str(&reordered);
15836 }
15837 Ok(Value::String(Rc::new(result)))
15838 }
15839 _ => Err(RuntimeError::new("bidi_reorder() requires string")),
15840 });
15841
15842 define(interp, "display_width", Some(1), |_, args| match &args[0] {
15852 Value::String(s) => {
15853 let width = UnicodeWidthStr::width(s.as_str());
15854 Ok(Value::Int(width as i64))
15855 }
15856 _ => Err(RuntimeError::new("display_width() requires string")),
15857 });
15858
15859 define(interp, "is_fullwidth", Some(1), |_, args| {
15861 match &args[0] {
15862 Value::String(s) => {
15863 let char_count = s.chars().count();
15864 let display_width = UnicodeWidthStr::width(s.as_str());
15865 Ok(Value::Bool(display_width > char_count))
15867 }
15868 _ => Err(RuntimeError::new("is_fullwidth() requires string")),
15869 }
15870 });
15871
15872 define(interp, "pad_display", Some(3), |_, args| {
15874 match (&args[0], &args[1], &args[2]) {
15875 (Value::String(s), Value::Int(target_width), Value::String(align)) => {
15876 let current_width = UnicodeWidthStr::width(s.as_str());
15877 let target = *target_width as usize;
15878 if current_width >= target {
15879 return Ok(Value::String(s.clone()));
15880 }
15881 let padding = target - current_width;
15882 let result = match align.as_str() {
15883 "left" => format!("{}{}", s, " ".repeat(padding)),
15884 "right" => format!("{}{}", " ".repeat(padding), s),
15885 "center" => {
15886 let left = padding / 2;
15887 let right = padding - left;
15888 format!("{}{}{}", " ".repeat(left), s, " ".repeat(right))
15889 }
15890 _ => {
15891 return Err(RuntimeError::new(
15892 "pad_display: align must be 'left', 'right', or 'center'",
15893 ))
15894 }
15895 };
15896 Ok(Value::String(Rc::new(result)))
15897 }
15898 _ => Err(RuntimeError::new(
15899 "pad_display() requires string, width, and alignment",
15900 )),
15901 }
15902 });
15903
15904 define(interp, "transliterate", Some(1), |_, args| match &args[0] {
15914 Value::String(s) => {
15915 let ascii = deunicode(s);
15916 Ok(Value::String(Rc::new(ascii)))
15917 }
15918 _ => Err(RuntimeError::new("transliterate() requires string")),
15919 });
15920
15921 define(interp, "to_ascii", Some(1), |_, args| match &args[0] {
15923 Value::String(s) => {
15924 let ascii = deunicode(s);
15925 Ok(Value::String(Rc::new(ascii)))
15926 }
15927 _ => Err(RuntimeError::new("to_ascii() requires string")),
15928 });
15929
15930 define(interp, "slugify", Some(1), |_, args| {
15932 match &args[0] {
15933 Value::String(s) => {
15934 let ascii = deunicode(s);
15935 let slug: String = ascii
15936 .to_lowercase()
15937 .chars()
15938 .map(|c| if c.is_alphanumeric() { c } else { '-' })
15939 .collect();
15940 let mut result = String::new();
15942 let mut last_was_dash = true; for c in slug.chars() {
15944 if c == '-' {
15945 if !last_was_dash {
15946 result.push(c);
15947 last_was_dash = true;
15948 }
15949 } else {
15950 result.push(c);
15951 last_was_dash = false;
15952 }
15953 }
15954 if result.ends_with('-') {
15956 result.pop();
15957 }
15958 Ok(Value::String(Rc::new(result)))
15959 }
15960 _ => Err(RuntimeError::new("slugify() requires string")),
15961 }
15962 });
15963
15964 define(interp, "strip_diacritics", Some(1), |_, args| {
15974 match &args[0] {
15975 Value::String(s) => {
15976 let decomposed: String = s.nfd().collect();
15978 let stripped: String = decomposed
15980 .chars()
15981 .filter(|c| {
15982 let code = *c as u32;
15986 !(0x0300..=0x036F).contains(&code)
15988 && !(0x1AB0..=0x1AFF).contains(&code)
15989 && !(0x1DC0..=0x1DFF).contains(&code)
15990 && !(0x20D0..=0x20FF).contains(&code)
15991 && !(0xFE20..=0xFE2F).contains(&code)
15992 })
15993 .collect();
15994 Ok(Value::String(Rc::new(stripped)))
15995 }
15996 _ => Err(RuntimeError::new("strip_diacritics() requires string")),
15997 }
15998 });
15999
16000 define(interp, "has_diacritics", Some(1), |_, args| {
16002 match &args[0] {
16003 Value::String(s) => {
16004 let decomposed: String = s.nfd().collect();
16005 let has_marks = decomposed.chars().any(|c| {
16006 let code = c as u32;
16007 (0x0300..=0x036F).contains(&code)
16008 || (0x1AB0..=0x1AFF).contains(&code)
16009 || (0x1DC0..=0x1DFF).contains(&code)
16010 || (0x20D0..=0x20FF).contains(&code)
16011 || (0xFE20..=0xFE2F).contains(&code)
16012 });
16013 Ok(Value::Bool(has_marks))
16014 }
16015 _ => Err(RuntimeError::new("has_diacritics() requires string")),
16016 }
16017 });
16018
16019 define(interp, "normalize_accents", Some(2), |_, args| {
16021 match (&args[0], &args[1]) {
16022 (Value::String(s), Value::String(form)) => {
16023 let result = match form.as_str() {
16024 "composed" | "nfc" => s.nfc().collect(),
16025 "decomposed" | "nfd" => s.nfd().collect(),
16026 _ => {
16027 return Err(RuntimeError::new(
16028 "normalize_accents: form must be 'composed' or 'decomposed'",
16029 ))
16030 }
16031 };
16032 Ok(Value::String(Rc::new(result)))
16033 }
16034 _ => Err(RuntimeError::new(
16035 "normalize_accents() requires string and form",
16036 )),
16037 }
16038 });
16039
16040 define(interp, "upper_locale", Some(2), |_, args| {
16052 match (&args[0], &args[1]) {
16053 (Value::String(s), Value::String(locale_str)) => {
16054 let case_mapper = CaseMapper::new();
16055 let langid: LanguageIdentifier =
16056 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16057 let result = case_mapper.uppercase_to_string(s, &langid);
16058 Ok(Value::String(Rc::new(result)))
16059 }
16060 _ => Err(RuntimeError::new(
16061 "upper_locale() requires string and locale",
16062 )),
16063 }
16064 });
16065
16066 define(interp, "lower_locale", Some(2), |_, args| {
16068 match (&args[0], &args[1]) {
16069 (Value::String(s), Value::String(locale_str)) => {
16070 let case_mapper = CaseMapper::new();
16071 let langid: LanguageIdentifier =
16072 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16073 let result = case_mapper.lowercase_to_string(s, &langid);
16074 Ok(Value::String(Rc::new(result)))
16075 }
16076 _ => Err(RuntimeError::new(
16077 "lower_locale() requires string and locale",
16078 )),
16079 }
16080 });
16081
16082 define(interp, "titlecase_locale", Some(2), |_, args| {
16084 match (&args[0], &args[1]) {
16085 (Value::String(s), Value::String(locale_str)) => {
16086 let case_mapper = CaseMapper::new();
16087 let langid: LanguageIdentifier =
16088 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16089 let options = TitlecaseOptions::default();
16090 let result = case_mapper
16091 .titlecase_segment_with_only_case_data_to_string(s, &langid, options);
16092 Ok(Value::String(Rc::new(result)))
16093 }
16094 _ => Err(RuntimeError::new(
16095 "titlecase_locale() requires string and locale",
16096 )),
16097 }
16098 });
16099
16100 define(interp, "case_fold", Some(1), |_, args| match &args[0] {
16102 Value::String(s) => {
16103 let case_mapper = CaseMapper::new();
16104 let result = case_mapper.fold_string(s);
16105 Ok(Value::String(Rc::new(result)))
16106 }
16107 _ => Err(RuntimeError::new("case_fold() requires string")),
16108 });
16109
16110 define(interp, "case_insensitive_eq", Some(2), |_, args| {
16112 match (&args[0], &args[1]) {
16113 (Value::String(a), Value::String(b)) => {
16114 let case_mapper = CaseMapper::new();
16115 let folded_a = case_mapper.fold_string(a);
16116 let folded_b = case_mapper.fold_string(b);
16117 Ok(Value::Bool(folded_a == folded_b))
16118 }
16119 _ => Err(RuntimeError::new(
16120 "case_insensitive_eq() requires two strings",
16121 )),
16122 }
16123 });
16124
16125 define(interp, "compare_locale", Some(3), |_, args| {
16137 match (&args[0], &args[1], &args[2]) {
16138 (Value::String(a), Value::String(b), Value::String(locale_str)) => {
16139 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16140 let options = CollatorOptions::new();
16141 let collator = Collator::try_new(&locale.into(), options)
16142 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
16143 let result = match collator.compare(a, b) {
16144 std::cmp::Ordering::Less => -1,
16145 std::cmp::Ordering::Equal => 0,
16146 std::cmp::Ordering::Greater => 1,
16147 };
16148 Ok(Value::Int(result))
16149 }
16150 _ => Err(RuntimeError::new(
16151 "compare_locale() requires two strings and locale",
16152 )),
16153 }
16154 });
16155
16156 define(interp, "sort_locale", Some(2), |_, args| {
16158 match (&args[0], &args[1]) {
16159 (Value::Array(arr), Value::String(locale_str)) => {
16160 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16161 let options = CollatorOptions::new();
16162 let collator = Collator::try_new(&locale.into(), options)
16163 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
16164
16165 let mut items: Vec<(String, Value)> = arr
16166 .borrow()
16167 .iter()
16168 .map(|v| {
16169 let s = match v {
16170 Value::String(s) => (**s).clone(),
16171 _ => format!("{}", v),
16172 };
16173 (s, v.clone())
16174 })
16175 .collect();
16176
16177 items.sort_by(|(a, _), (b, _)| collator.compare(a, b));
16178
16179 let sorted: Vec<Value> = items.into_iter().map(|(_, v)| v).collect();
16180 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
16181 }
16182 _ => Err(RuntimeError::new("sort_locale() requires array and locale")),
16183 }
16184 });
16185
16186 define(interp, "sentences", Some(1), |_, args| match &args[0] {
16198 Value::String(s) => {
16199 let segmenter = SentenceSegmenter::new();
16200 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16201 let mut sentences = Vec::new();
16202 let mut start = 0;
16203 for end in breakpoints {
16204 let sentence = s[start..end].trim();
16205 if !sentence.is_empty() {
16206 sentences.push(Value::String(Rc::new(sentence.to_string())));
16207 }
16208 start = end;
16209 }
16210 Ok(Value::Array(Rc::new(RefCell::new(sentences))))
16211 }
16212 _ => Err(RuntimeError::new("sentences() requires string")),
16213 });
16214
16215 define(interp, "sentence_count", Some(1), |_, args| {
16217 match &args[0] {
16218 Value::String(s) => {
16219 let segmenter = SentenceSegmenter::new();
16220 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16221 let count = breakpoints.len().saturating_sub(1);
16223 Ok(Value::Int(count as i64))
16224 }
16225 _ => Err(RuntimeError::new("sentence_count() requires string")),
16226 }
16227 });
16228
16229 define(interp, "words_icu", Some(1), |_, args| {
16231 match &args[0] {
16232 Value::String(s) => {
16233 let segmenter = WordSegmenter::new_auto();
16234 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16235 let mut words = Vec::new();
16236 let mut start = 0;
16237 for end in breakpoints {
16238 let word = &s[start..end];
16239 if !word.trim().is_empty() {
16241 words.push(Value::String(Rc::new(word.to_string())));
16242 }
16243 start = end;
16244 }
16245 Ok(Value::Array(Rc::new(RefCell::new(words))))
16246 }
16247 _ => Err(RuntimeError::new("words_icu() requires string")),
16248 }
16249 });
16250
16251 define(interp, "word_count_icu", Some(1), |_, args| {
16253 match &args[0] {
16254 Value::String(s) => {
16255 let segmenter = WordSegmenter::new_auto();
16256 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16257 let mut count = 0;
16258 let mut start = 0;
16259 for end in breakpoints {
16260 let word = &s[start..end];
16261 if !word.trim().is_empty() && word.chars().any(|c| c.is_alphanumeric()) {
16262 count += 1;
16263 }
16264 start = end;
16265 }
16266 Ok(Value::Int(count))
16267 }
16268 _ => Err(RuntimeError::new("word_count_icu() requires string")),
16269 }
16270 });
16271
16272 define(interp, "is_emoji", Some(1), |_, args| {
16278 match &args[0] {
16279 Value::String(s) => {
16280 let has_emoji = s.chars().any(|c| {
16281 let code = c as u32;
16282 (0x1F600..=0x1F64F).contains(&code) || (0x1F300..=0x1F5FF).contains(&code) || (0x1F680..=0x1F6FF).contains(&code) || (0x1F1E0..=0x1F1FF).contains(&code) || (0x2600..=0x26FF).contains(&code) || (0x2700..=0x27BF).contains(&code) || (0xFE00..=0xFE0F).contains(&code) || (0x1F900..=0x1F9FF).contains(&code) || (0x1FA00..=0x1FA6F).contains(&code) || (0x1FA70..=0x1FAFF).contains(&code) || (0x231A..=0x231B).contains(&code) || (0x23E9..=0x23F3).contains(&code) || (0x23F8..=0x23FA).contains(&code) });
16297 Ok(Value::Bool(has_emoji))
16298 }
16299 _ => Err(RuntimeError::new("is_emoji() requires string")),
16300 }
16301 });
16302
16303 define(interp, "extract_emoji", Some(1), |_, args| match &args[0] {
16305 Value::String(s) => {
16306 let emoji: Vec<Value> = s
16307 .graphemes(true)
16308 .filter(|g| {
16309 g.chars().any(|c| {
16310 let code = c as u32;
16311 (0x1F600..=0x1F64F).contains(&code)
16312 || (0x1F300..=0x1F5FF).contains(&code)
16313 || (0x1F680..=0x1F6FF).contains(&code)
16314 || (0x1F1E0..=0x1F1FF).contains(&code)
16315 || (0x2600..=0x26FF).contains(&code)
16316 || (0x2700..=0x27BF).contains(&code)
16317 || (0x1F900..=0x1F9FF).contains(&code)
16318 || (0x1FA00..=0x1FA6F).contains(&code)
16319 || (0x1FA70..=0x1FAFF).contains(&code)
16320 })
16321 })
16322 .map(|g| Value::String(Rc::new(g.to_string())))
16323 .collect();
16324 Ok(Value::Array(Rc::new(RefCell::new(emoji))))
16325 }
16326 _ => Err(RuntimeError::new("extract_emoji() requires string")),
16327 });
16328
16329 define(interp, "strip_emoji", Some(1), |_, args| match &args[0] {
16331 Value::String(s) => {
16332 let stripped: String = s
16333 .graphemes(true)
16334 .filter(|g| {
16335 !g.chars().any(|c| {
16336 let code = c as u32;
16337 (0x1F600..=0x1F64F).contains(&code)
16338 || (0x1F300..=0x1F5FF).contains(&code)
16339 || (0x1F680..=0x1F6FF).contains(&code)
16340 || (0x1F1E0..=0x1F1FF).contains(&code)
16341 || (0x2600..=0x26FF).contains(&code)
16342 || (0x2700..=0x27BF).contains(&code)
16343 || (0x1F900..=0x1F9FF).contains(&code)
16344 || (0x1FA00..=0x1FA6F).contains(&code)
16345 || (0x1FA70..=0x1FAFF).contains(&code)
16346 })
16347 })
16348 .collect();
16349 Ok(Value::String(Rc::new(stripped)))
16350 }
16351 _ => Err(RuntimeError::new("strip_emoji() requires string")),
16352 });
16353
16354 define(interp, "script_runs", Some(1), |_, args| {
16360 match &args[0] {
16361 Value::String(s) => {
16362 let mut runs: Vec<Value> = Vec::new();
16363 let mut current_run = String::new();
16364 let mut current_script: Option<Script> = None;
16365
16366 for c in s.chars() {
16367 let script = c.script();
16368 if script != Script::Common && script != Script::Inherited {
16370 if let Some(curr) = current_script {
16371 if script != curr {
16372 if !current_run.is_empty() {
16374 runs.push(Value::String(Rc::new(current_run.clone())));
16375 current_run.clear();
16376 }
16377 current_script = Some(script);
16378 }
16379 } else {
16380 current_script = Some(script);
16381 }
16382 }
16383 current_run.push(c);
16384 }
16385
16386 if !current_run.is_empty() {
16388 runs.push(Value::String(Rc::new(current_run)));
16389 }
16390
16391 Ok(Value::Array(Rc::new(RefCell::new(runs))))
16392 }
16393 _ => Err(RuntimeError::new("script_runs() requires string")),
16394 }
16395 });
16396
16397 define(interp, "script_ratio", Some(1), |_, args| {
16399 match &args[0] {
16400 Value::String(s) => {
16401 let mut script_counts: HashMap<String, usize> = HashMap::new();
16402 let mut total = 0usize;
16403
16404 for c in s.chars() {
16405 if !c.is_whitespace() && c != ' ' {
16406 let script = format!("{:?}", c.script());
16407 *script_counts.entry(script).or_insert(0) += 1;
16408 total += 1;
16409 }
16410 }
16411
16412 let mut result = HashMap::new();
16414 for (script, count) in script_counts {
16415 let ratio = if total > 0 {
16416 count as f64 / total as f64
16417 } else {
16418 0.0
16419 };
16420 result.insert(script, Value::Float(ratio));
16421 }
16422
16423 let map = Rc::new(RefCell::new(result));
16424 Ok(Value::Map(map))
16425 }
16426 _ => Err(RuntimeError::new("script_ratio() requires string")),
16427 }
16428 });
16429
16430 define(interp, "locale_name", Some(1), |_, args| {
16436 match &args[0] {
16437 Value::String(locale_str) => {
16438 Ok(Value::String(locale_str.clone()))
16441 }
16442 _ => Err(RuntimeError::new("locale_name() requires string")),
16443 }
16444 });
16445
16446 define(interp, "supported_locales", Some(0), |_, _| {
16448 let locales = vec![
16450 "ar", "bg", "ca", "cs", "da", "de", "el", "en", "es", "et", "fi", "fr", "he", "hi",
16451 "hr", "hu", "id", "it", "ja", "ko", "lt", "lv", "ms", "nb", "nl", "pl", "pt", "ro",
16452 "ru", "sk", "sl", "sr", "sv", "th", "tr", "uk", "vi", "zh",
16453 ];
16454 let values: Vec<Value> = locales
16455 .into_iter()
16456 .map(|s| Value::String(Rc::new(s.to_string())))
16457 .collect();
16458 Ok(Value::Array(Rc::new(RefCell::new(values))))
16459 });
16460}
16461
16462fn register_text_intelligence(interp: &mut Interpreter) {
16467 define(interp, "levenshtein", Some(2), |_, args| {
16473 match (&args[0], &args[1]) {
16474 (Value::String(a), Value::String(b)) => {
16475 let distance = strsim::levenshtein(a, b);
16476 Ok(Value::Int(distance as i64))
16477 }
16478 _ => Err(RuntimeError::new("levenshtein() requires two strings")),
16479 }
16480 });
16481
16482 define(
16484 interp,
16485 "levenshtein_normalized",
16486 Some(2),
16487 |_, args| match (&args[0], &args[1]) {
16488 (Value::String(a), Value::String(b)) => {
16489 let distance = strsim::normalized_levenshtein(a, b);
16490 Ok(Value::Float(distance))
16491 }
16492 _ => Err(RuntimeError::new(
16493 "levenshtein_normalized() requires two strings",
16494 )),
16495 },
16496 );
16497
16498 define(interp, "jaro", Some(2), |_, args| {
16500 match (&args[0], &args[1]) {
16501 (Value::String(a), Value::String(b)) => {
16502 let sim = strsim::jaro(a, b);
16503 Ok(Value::Float(sim))
16504 }
16505 _ => Err(RuntimeError::new("jaro() requires two strings")),
16506 }
16507 });
16508
16509 define(interp, "jaro_winkler", Some(2), |_, args| {
16511 match (&args[0], &args[1]) {
16512 (Value::String(a), Value::String(b)) => {
16513 let sim = strsim::jaro_winkler(a, b);
16514 Ok(Value::Float(sim))
16515 }
16516 _ => Err(RuntimeError::new("jaro_winkler() requires two strings")),
16517 }
16518 });
16519
16520 define(interp, "sorensen_dice", Some(2), |_, args| {
16522 match (&args[0], &args[1]) {
16523 (Value::String(a), Value::String(b)) => {
16524 let sim = strsim::sorensen_dice(a, b);
16525 Ok(Value::Float(sim))
16526 }
16527 _ => Err(RuntimeError::new("sorensen_dice() requires two strings")),
16528 }
16529 });
16530
16531 define(interp, "damerau_levenshtein", Some(2), |_, args| {
16533 match (&args[0], &args[1]) {
16534 (Value::String(a), Value::String(b)) => {
16535 let distance = strsim::damerau_levenshtein(a, b);
16536 Ok(Value::Int(distance as i64))
16537 }
16538 _ => Err(RuntimeError::new(
16539 "damerau_levenshtein() requires two strings",
16540 )),
16541 }
16542 });
16543
16544 define(interp, "osa_distance", Some(2), |_, args| {
16546 match (&args[0], &args[1]) {
16547 (Value::String(a), Value::String(b)) => {
16548 let distance = strsim::osa_distance(a, b);
16549 Ok(Value::Int(distance as i64))
16550 }
16551 _ => Err(RuntimeError::new("osa_distance() requires two strings")),
16552 }
16553 });
16554
16555 define(interp, "fuzzy_match", Some(3), |_, args| {
16557 match (&args[0], &args[1], &args[2]) {
16558 (Value::String(a), Value::String(b), Value::Float(threshold)) => {
16559 let sim = strsim::jaro_winkler(a, b);
16560 Ok(Value::Bool(sim >= *threshold))
16561 }
16562 (Value::String(a), Value::String(b), Value::Int(threshold)) => {
16563 let sim = strsim::jaro_winkler(a, b);
16564 Ok(Value::Bool(sim >= *threshold as f64))
16565 }
16566 _ => Err(RuntimeError::new(
16567 "fuzzy_match() requires two strings and threshold",
16568 )),
16569 }
16570 });
16571
16572 define(interp, "fuzzy_search", Some(3), |_, args| {
16574 match (&args[0], &args[1], &args[2]) {
16575 (Value::String(query), Value::Array(items), Value::Int(limit)) => {
16576 let items_ref = items.borrow();
16577 let mut scores: Vec<(f64, &str)> = items_ref
16578 .iter()
16579 .filter_map(|v| {
16580 if let Value::String(s) = v {
16581 Some((strsim::jaro_winkler(query, s), s.as_str()))
16582 } else {
16583 None
16584 }
16585 })
16586 .collect();
16587 scores.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
16588 let results: Vec<Value> = scores
16589 .into_iter()
16590 .take(*limit as usize)
16591 .map(|(_, s)| Value::String(Rc::new(s.to_string())))
16592 .collect();
16593 Ok(Value::Array(Rc::new(RefCell::new(results))))
16594 }
16595 _ => Err(RuntimeError::new(
16596 "fuzzy_search() requires query string, array, and limit",
16597 )),
16598 }
16599 });
16600
16601 define(interp, "soundex", Some(1), |_, args| match &args[0] {
16607 Value::String(s) => {
16608 let code = compute_soundex(s);
16609 Ok(Value::String(Rc::new(code)))
16610 }
16611 _ => Err(RuntimeError::new("soundex() requires string")),
16612 });
16613
16614 define(interp, "soundex_match", Some(2), |_, args| {
16616 match (&args[0], &args[1]) {
16617 (Value::String(a), Value::String(b)) => {
16618 let code_a = compute_soundex(a);
16619 let code_b = compute_soundex(b);
16620 Ok(Value::Bool(code_a == code_b))
16621 }
16622 _ => Err(RuntimeError::new("soundex_match() requires two strings")),
16623 }
16624 });
16625
16626 define(interp, "metaphone", Some(1), |_, args| match &args[0] {
16628 Value::String(s) => {
16629 let code = compute_metaphone(s);
16630 Ok(Value::String(Rc::new(code)))
16631 }
16632 _ => Err(RuntimeError::new("metaphone() requires string")),
16633 });
16634
16635 define(interp, "metaphone_match", Some(2), |_, args| {
16637 match (&args[0], &args[1]) {
16638 (Value::String(a), Value::String(b)) => {
16639 let code_a = compute_metaphone(a);
16640 let code_b = compute_metaphone(b);
16641 Ok(Value::Bool(code_a == code_b))
16642 }
16643 _ => Err(RuntimeError::new("metaphone_match() requires two strings")),
16644 }
16645 });
16646
16647 define(interp, "cologne_phonetic", Some(1), |_, args| {
16649 match &args[0] {
16650 Value::String(s) => {
16651 let code = compute_cologne(s);
16652 Ok(Value::String(Rc::new(code)))
16653 }
16654 _ => Err(RuntimeError::new("cologne_phonetic() requires string")),
16655 }
16656 });
16657
16658 define(interp, "detect_language", Some(1), |_, args| {
16664 match &args[0] {
16665 Value::String(s) => {
16666 if let Some(info) = detect(s) {
16667 let lang_code = match info.lang() {
16668 Lang::Eng => "en",
16669 Lang::Spa => "es",
16670 Lang::Fra => "fr",
16671 Lang::Deu => "de",
16672 Lang::Ita => "it",
16673 Lang::Por => "pt",
16674 Lang::Rus => "ru",
16675 Lang::Ara => "ar",
16676 Lang::Hin => "hi",
16677 Lang::Cmn => "zh",
16678 Lang::Jpn => "ja",
16679 Lang::Kor => "ko",
16680 Lang::Nld => "nl",
16681 Lang::Swe => "sv",
16682 Lang::Tur => "tr",
16683 Lang::Pol => "pl",
16684 Lang::Ukr => "uk",
16685 Lang::Ces => "cs",
16686 Lang::Dan => "da",
16687 Lang::Fin => "fi",
16688 Lang::Ell => "el",
16689 Lang::Heb => "he",
16690 Lang::Hun => "hu",
16691 Lang::Ind => "id",
16692 Lang::Nob => "no",
16693 Lang::Ron => "ro",
16694 Lang::Slk => "sk",
16695 Lang::Tha => "th",
16696 Lang::Vie => "vi",
16697 _ => "unknown",
16698 };
16699 Ok(Value::String(Rc::new(lang_code.to_string())))
16700 } else {
16701 Ok(Value::String(Rc::new("unknown".to_string())))
16702 }
16703 }
16704 _ => Err(RuntimeError::new("detect_language() requires string")),
16705 }
16706 });
16707
16708 define(
16710 interp,
16711 "detect_language_confidence",
16712 Some(1),
16713 |_, args| match &args[0] {
16714 Value::String(s) => {
16715 if let Some(info) = detect(s) {
16716 let lang_code = match info.lang() {
16717 Lang::Eng => "en",
16718 Lang::Spa => "es",
16719 Lang::Fra => "fr",
16720 Lang::Deu => "de",
16721 Lang::Ita => "it",
16722 Lang::Por => "pt",
16723 Lang::Rus => "ru",
16724 Lang::Ara => "ar",
16725 Lang::Cmn => "zh",
16726 Lang::Jpn => "ja",
16727 _ => "unknown",
16728 };
16729 let confidence = info.confidence();
16730 let mut map = HashMap::new();
16731 map.insert(
16732 "lang".to_string(),
16733 Value::String(Rc::new(lang_code.to_string())),
16734 );
16735 map.insert("confidence".to_string(), Value::Float(confidence as f64));
16736 Ok(Value::Map(Rc::new(RefCell::new(map))))
16737 } else {
16738 let mut map = HashMap::new();
16739 map.insert(
16740 "lang".to_string(),
16741 Value::String(Rc::new("unknown".to_string())),
16742 );
16743 map.insert("confidence".to_string(), Value::Float(0.0));
16744 Ok(Value::Map(Rc::new(RefCell::new(map))))
16745 }
16746 }
16747 _ => Err(RuntimeError::new(
16748 "detect_language_confidence() requires string",
16749 )),
16750 },
16751 );
16752
16753 define(
16755 interp,
16756 "detect_script_whatlang",
16757 Some(1),
16758 |_, args| match &args[0] {
16759 Value::String(s) => {
16760 if let Some(info) = detect(s) {
16761 let script_name = match info.script() {
16762 WhatLangScript::Latin => "Latin",
16763 WhatLangScript::Cyrillic => "Cyrillic",
16764 WhatLangScript::Arabic => "Arabic",
16765 WhatLangScript::Devanagari => "Devanagari",
16766 WhatLangScript::Ethiopic => "Ethiopic",
16767 WhatLangScript::Georgian => "Georgian",
16768 WhatLangScript::Greek => "Greek",
16769 WhatLangScript::Gujarati => "Gujarati",
16770 WhatLangScript::Gurmukhi => "Gurmukhi",
16771 WhatLangScript::Hangul => "Hangul",
16772 WhatLangScript::Hebrew => "Hebrew",
16773 WhatLangScript::Hiragana => "Hiragana",
16774 WhatLangScript::Kannada => "Kannada",
16775 WhatLangScript::Katakana => "Katakana",
16776 WhatLangScript::Khmer => "Khmer",
16777 WhatLangScript::Malayalam => "Malayalam",
16778 WhatLangScript::Mandarin => "Mandarin",
16779 WhatLangScript::Myanmar => "Myanmar",
16780 WhatLangScript::Oriya => "Oriya",
16781 WhatLangScript::Sinhala => "Sinhala",
16782 WhatLangScript::Tamil => "Tamil",
16783 WhatLangScript::Telugu => "Telugu",
16784 WhatLangScript::Thai => "Thai",
16785 WhatLangScript::Bengali => "Bengali",
16786 WhatLangScript::Armenian => "Armenian",
16787 };
16788 Ok(Value::String(Rc::new(script_name.to_string())))
16789 } else {
16790 Ok(Value::String(Rc::new("Unknown".to_string())))
16791 }
16792 }
16793 _ => Err(RuntimeError::new(
16794 "detect_script_whatlang() requires string",
16795 )),
16796 },
16797 );
16798
16799 define(interp, "is_language", Some(2), |_, args| {
16801 match (&args[0], &args[1]) {
16802 (Value::String(s), Value::String(lang)) => {
16803 if let Some(info) = detect(s) {
16804 let detected = match info.lang() {
16805 Lang::Eng => "en",
16806 Lang::Spa => "es",
16807 Lang::Fra => "fr",
16808 Lang::Deu => "de",
16809 Lang::Ita => "it",
16810 Lang::Por => "pt",
16811 Lang::Rus => "ru",
16812 _ => "unknown",
16813 };
16814 Ok(Value::Bool(detected == lang.as_str()))
16815 } else {
16816 Ok(Value::Bool(false))
16817 }
16818 }
16819 _ => Err(RuntimeError::new(
16820 "is_language() requires string and language code",
16821 )),
16822 }
16823 });
16824
16825 define(interp, "token_count", Some(1), |_, args| match &args[0] {
16831 Value::String(s) => {
16832 if let Ok(bpe) = cl100k_base() {
16833 let tokens = bpe.encode_with_special_tokens(s);
16834 Ok(Value::Int(tokens.len() as i64))
16835 } else {
16836 Err(RuntimeError::new("Failed to initialize tokenizer"))
16837 }
16838 }
16839 _ => Err(RuntimeError::new("token_count() requires string")),
16840 });
16841
16842 define(interp, "token_count_model", Some(2), |_, args| {
16844 match (&args[0], &args[1]) {
16845 (Value::String(s), Value::String(model)) => {
16846 let bpe_result = match model.as_str() {
16847 "gpt4" | "gpt-4" | "claude" | "cl100k" => cl100k_base(),
16848 "gpt3" | "gpt-3" | "p50k" => p50k_base(),
16849 "codex" | "r50k" => r50k_base(),
16850 _ => cl100k_base(), };
16852 if let Ok(bpe) = bpe_result {
16853 let tokens = bpe.encode_with_special_tokens(s);
16854 Ok(Value::Int(tokens.len() as i64))
16855 } else {
16856 Err(RuntimeError::new("Failed to initialize tokenizer"))
16857 }
16858 }
16859 _ => Err(RuntimeError::new(
16860 "token_count_model() requires string and model name",
16861 )),
16862 }
16863 });
16864
16865 define(interp, "tokenize_ids", Some(1), |_, args| match &args[0] {
16867 Value::String(s) => {
16868 if let Ok(bpe) = cl100k_base() {
16869 let tokens = bpe.encode_with_special_tokens(s);
16870 let values: Vec<Value> = tokens.into_iter().map(|t| Value::Int(t as i64)).collect();
16871 Ok(Value::Array(Rc::new(RefCell::new(values))))
16872 } else {
16873 Err(RuntimeError::new("Failed to initialize tokenizer"))
16874 }
16875 }
16876 _ => Err(RuntimeError::new("tokenize_ids() requires string")),
16877 });
16878
16879 define(interp, "truncate_tokens", Some(2), |_, args| {
16881 match (&args[0], &args[1]) {
16882 (Value::String(s), Value::Int(max_tokens)) => {
16883 if let Ok(bpe) = cl100k_base() {
16884 let tokens = bpe.encode_with_special_tokens(s);
16885 if tokens.len() <= *max_tokens as usize {
16886 Ok(Value::String(s.clone()))
16887 } else {
16888 let truncated: Vec<usize> =
16889 tokens.into_iter().take(*max_tokens as usize).collect();
16890 if let Ok(decoded) = bpe.decode(truncated) {
16891 Ok(Value::String(Rc::new(decoded)))
16892 } else {
16893 Err(RuntimeError::new("Failed to decode tokens"))
16894 }
16895 }
16896 } else {
16897 Err(RuntimeError::new("Failed to initialize tokenizer"))
16898 }
16899 }
16900 _ => Err(RuntimeError::new(
16901 "truncate_tokens() requires string and max tokens",
16902 )),
16903 }
16904 });
16905
16906 define(interp, "estimate_cost", Some(3), |_, args| {
16908 match (&args[0], &args[1], &args[2]) {
16909 (Value::String(s), Value::Float(input_cost), Value::Float(output_cost)) => {
16910 if let Ok(bpe) = cl100k_base() {
16911 let tokens = bpe.encode_with_special_tokens(s);
16912 let count = tokens.len() as f64;
16913 let input_total = (count / 1000.0) * input_cost;
16915 let output_total = (count / 1000.0) * output_cost;
16916 let mut map = HashMap::new();
16917 map.insert("tokens".to_string(), Value::Int(tokens.len() as i64));
16918 map.insert("input_cost".to_string(), Value::Float(input_total));
16919 map.insert("output_cost".to_string(), Value::Float(output_total));
16920 Ok(Value::Map(Rc::new(RefCell::new(map))))
16921 } else {
16922 Err(RuntimeError::new("Failed to initialize tokenizer"))
16923 }
16924 }
16925 _ => Err(RuntimeError::new(
16926 "estimate_cost() requires string, input cost, output cost",
16927 )),
16928 }
16929 });
16930
16931 define(interp, "stem", Some(1), |_, args| match &args[0] {
16937 Value::String(s) => {
16938 let stemmer = Stemmer::create(StemAlgorithm::English);
16939 let stemmed = stemmer.stem(s);
16940 Ok(Value::String(Rc::new(stemmed.to_string())))
16941 }
16942 _ => Err(RuntimeError::new("stem() requires string")),
16943 });
16944
16945 define(interp, "stem_language", Some(2), |_, args| {
16947 match (&args[0], &args[1]) {
16948 (Value::String(s), Value::String(lang)) => {
16949 let algorithm = match lang.as_str() {
16950 "en" | "english" => StemAlgorithm::English,
16951 "fr" | "french" => StemAlgorithm::French,
16952 "de" | "german" => StemAlgorithm::German,
16953 "es" | "spanish" => StemAlgorithm::Spanish,
16954 "it" | "italian" => StemAlgorithm::Italian,
16955 "pt" | "portuguese" => StemAlgorithm::Portuguese,
16956 "nl" | "dutch" => StemAlgorithm::Dutch,
16957 "sv" | "swedish" => StemAlgorithm::Swedish,
16958 "no" | "norwegian" => StemAlgorithm::Norwegian,
16959 "da" | "danish" => StemAlgorithm::Danish,
16960 "fi" | "finnish" => StemAlgorithm::Finnish,
16961 "ru" | "russian" => StemAlgorithm::Russian,
16962 "ro" | "romanian" => StemAlgorithm::Romanian,
16963 "hu" | "hungarian" => StemAlgorithm::Hungarian,
16964 "tr" | "turkish" => StemAlgorithm::Turkish,
16965 "ar" | "arabic" => StemAlgorithm::Arabic,
16966 _ => StemAlgorithm::English,
16967 };
16968 let stemmer = Stemmer::create(algorithm);
16969 let stemmed = stemmer.stem(s);
16970 Ok(Value::String(Rc::new(stemmed.to_string())))
16971 }
16972 _ => Err(RuntimeError::new(
16973 "stem_language() requires string and language code",
16974 )),
16975 }
16976 });
16977
16978 define(interp, "stem_all", Some(1), |_, args| match &args[0] {
16980 Value::Array(arr) => {
16981 let stemmer = Stemmer::create(StemAlgorithm::English);
16982 let arr_ref = arr.borrow();
16983 let results: Vec<Value> = arr_ref
16984 .iter()
16985 .filter_map(|v| {
16986 if let Value::String(s) = v {
16987 Some(Value::String(Rc::new(stemmer.stem(s).to_string())))
16988 } else {
16989 None
16990 }
16991 })
16992 .collect();
16993 Ok(Value::Array(Rc::new(RefCell::new(results))))
16994 }
16995 _ => Err(RuntimeError::new("stem_all() requires array of strings")),
16996 });
16997
16998 define(interp, "is_stopword", Some(1), |_, args| match &args[0] {
17004 Value::String(s) => {
17005 let word = s.to_lowercase();
17006 let stopwords = get_stopwords("en");
17007 Ok(Value::Bool(stopwords.contains(&word.as_str())))
17008 }
17009 _ => Err(RuntimeError::new("is_stopword() requires string")),
17010 });
17011
17012 define(interp, "is_stopword_language", Some(2), |_, args| {
17014 match (&args[0], &args[1]) {
17015 (Value::String(s), Value::String(lang)) => {
17016 let word = s.to_lowercase();
17017 let stopwords = get_stopwords(lang);
17018 Ok(Value::Bool(stopwords.contains(&word.as_str())))
17019 }
17020 _ => Err(RuntimeError::new(
17021 "is_stopword_language() requires string and language",
17022 )),
17023 }
17024 });
17025
17026 define(interp, "remove_stopwords", Some(1), |_, args| {
17028 match &args[0] {
17029 Value::Array(arr) => {
17030 let stopwords = get_stopwords("en");
17031 let arr_ref = arr.borrow();
17032 let results: Vec<Value> = arr_ref
17033 .iter()
17034 .filter(|v| {
17035 if let Value::String(s) = v {
17036 !stopwords.contains(&s.to_lowercase().as_str())
17037 } else {
17038 true
17039 }
17040 })
17041 .cloned()
17042 .collect();
17043 Ok(Value::Array(Rc::new(RefCell::new(results))))
17044 }
17045 _ => Err(RuntimeError::new(
17046 "remove_stopwords() requires array of strings",
17047 )),
17048 }
17049 });
17050
17051 define(
17053 interp,
17054 "remove_stopwords_text",
17055 Some(1),
17056 |_, args| match &args[0] {
17057 Value::String(s) => {
17058 let stopwords = get_stopwords("en");
17059 let words: Vec<&str> = s
17060 .split_whitespace()
17061 .filter(|w| !stopwords.contains(&w.to_lowercase().as_str()))
17062 .collect();
17063 Ok(Value::String(Rc::new(words.join(" "))))
17064 }
17065 _ => Err(RuntimeError::new("remove_stopwords_text() requires string")),
17066 },
17067 );
17068
17069 define(
17071 interp,
17072 "get_stopwords_list",
17073 Some(1),
17074 |_, args| match &args[0] {
17075 Value::String(lang) => {
17076 let stopwords = get_stopwords(lang);
17077 let values: Vec<Value> = stopwords
17078 .iter()
17079 .map(|s| Value::String(Rc::new(s.to_string())))
17080 .collect();
17081 Ok(Value::Array(Rc::new(RefCell::new(values))))
17082 }
17083 _ => Err(RuntimeError::new(
17084 "get_stopwords_list() requires language code",
17085 )),
17086 },
17087 );
17088
17089 define(interp, "ngrams", Some(2), |_, args| {
17095 match (&args[0], &args[1]) {
17096 (Value::String(s), Value::Int(n)) => {
17097 let words: Vec<&str> = s.split_whitespace().collect();
17098 let n = *n as usize;
17099 if n == 0 || n > words.len() {
17100 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17101 }
17102 let ngrams: Vec<Value> = words
17103 .windows(n)
17104 .map(|w| Value::String(Rc::new(w.join(" "))))
17105 .collect();
17106 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
17107 }
17108 _ => Err(RuntimeError::new("ngrams() requires string and n")),
17109 }
17110 });
17111
17112 define(interp, "char_ngrams", Some(2), |_, args| {
17114 match (&args[0], &args[1]) {
17115 (Value::String(s), Value::Int(n)) => {
17116 let chars: Vec<char> = s.chars().collect();
17117 let n = *n as usize;
17118 if n == 0 || n > chars.len() {
17119 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17120 }
17121 let ngrams: Vec<Value> = chars
17122 .windows(n)
17123 .map(|w| Value::String(Rc::new(w.iter().collect())))
17124 .collect();
17125 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
17126 }
17127 _ => Err(RuntimeError::new("char_ngrams() requires string and n")),
17128 }
17129 });
17130
17131 define(interp, "shingles", Some(2), |_, args| {
17133 match (&args[0], &args[1]) {
17134 (Value::String(s), Value::Int(n)) => {
17135 let words: Vec<&str> = s.split_whitespace().collect();
17136 let n = *n as usize;
17137 if n == 0 || n > words.len() {
17138 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17139 }
17140 let mut seen = std::collections::HashSet::new();
17141 let shingles: Vec<Value> = words
17142 .windows(n)
17143 .filter_map(|w| {
17144 let s = w.join(" ");
17145 if seen.insert(s.clone()) {
17146 Some(Value::String(Rc::new(s)))
17147 } else {
17148 None
17149 }
17150 })
17151 .collect();
17152 Ok(Value::Array(Rc::new(RefCell::new(shingles))))
17153 }
17154 _ => Err(RuntimeError::new("shingles() requires string and n")),
17155 }
17156 });
17157
17158 define(interp, "jaccard_similarity", Some(2), |_, args| {
17160 match (&args[0], &args[1]) {
17161 (Value::Array(a), Value::Array(b)) => {
17162 let a_ref = a.borrow();
17163 let b_ref = b.borrow();
17164 let set_a: std::collections::HashSet<String> = a_ref
17165 .iter()
17166 .filter_map(|v| {
17167 if let Value::String(s) = v {
17168 Some(s.to_string())
17169 } else {
17170 None
17171 }
17172 })
17173 .collect();
17174 let set_b: std::collections::HashSet<String> = b_ref
17175 .iter()
17176 .filter_map(|v| {
17177 if let Value::String(s) = v {
17178 Some(s.to_string())
17179 } else {
17180 None
17181 }
17182 })
17183 .collect();
17184 let intersection = set_a.intersection(&set_b).count();
17185 let union = set_a.union(&set_b).count();
17186 if union == 0 {
17187 Ok(Value::Float(0.0))
17188 } else {
17189 Ok(Value::Float(intersection as f64 / union as f64))
17190 }
17191 }
17192 _ => Err(RuntimeError::new(
17193 "jaccard_similarity() requires two arrays",
17194 )),
17195 }
17196 });
17197
17198 define(interp, "minhash_signature", Some(2), |_, args| {
17200 match (&args[0], &args[1]) {
17201 (Value::Array(arr), Value::Int(num_hashes)) => {
17202 let arr_ref = arr.borrow();
17203 let items: std::collections::HashSet<String> = arr_ref
17204 .iter()
17205 .filter_map(|v| {
17206 if let Value::String(s) = v {
17207 Some(s.to_string())
17208 } else {
17209 None
17210 }
17211 })
17212 .collect();
17213
17214 let mut signature: Vec<Value> = Vec::with_capacity(*num_hashes as usize);
17216 for i in 0..*num_hashes {
17217 let mut min_hash: u64 = u64::MAX;
17218 for item in &items {
17219 let hash = compute_hash(item, i as u64);
17220 if hash < min_hash {
17221 min_hash = hash;
17222 }
17223 }
17224 signature.push(Value::Int(min_hash as i64));
17225 }
17226 Ok(Value::Array(Rc::new(RefCell::new(signature))))
17227 }
17228 _ => Err(RuntimeError::new(
17229 "minhash_signature() requires array and num_hashes",
17230 )),
17231 }
17232 });
17233
17234 define(interp, "preprocess_text", Some(1), |_, args| {
17240 match &args[0] {
17241 Value::String(s) => {
17242 let lower = s.to_lowercase();
17244 let clean: String = lower
17246 .chars()
17247 .filter(|c| c.is_alphanumeric() || c.is_whitespace())
17248 .collect();
17249 let normalized: String = clean.split_whitespace().collect::<Vec<_>>().join(" ");
17251 Ok(Value::String(Rc::new(normalized)))
17252 }
17253 _ => Err(RuntimeError::new("preprocess_text() requires string")),
17254 }
17255 });
17256
17257 define(interp, "tokenize_words", Some(1), |_, args| {
17259 match &args[0] {
17260 Value::String(s) => {
17261 let words: Vec<Value> = s
17262 .split_whitespace()
17263 .map(|w| Value::String(Rc::new(w.to_string())))
17264 .collect();
17265 Ok(Value::Array(Rc::new(RefCell::new(words))))
17266 }
17267 _ => Err(RuntimeError::new("tokenize_words() requires string")),
17268 }
17269 });
17270
17271 define(interp, "extract_keywords", Some(1), |_, args| {
17273 match &args[0] {
17274 Value::String(s) => {
17275 let stopwords = get_stopwords("en");
17276 let words: Vec<Value> = s
17277 .split_whitespace()
17278 .filter(|w| {
17279 let lower = w.to_lowercase();
17280 !stopwords.contains(&lower.as_str()) && lower.len() > 2
17281 })
17282 .map(|w| Value::String(Rc::new(w.to_lowercase())))
17283 .collect();
17284 Ok(Value::Array(Rc::new(RefCell::new(words))))
17285 }
17286 _ => Err(RuntimeError::new("extract_keywords() requires string")),
17287 }
17288 });
17289
17290 define(interp, "word_frequency", Some(1), |_, args| {
17292 match &args[0] {
17293 Value::String(s) => {
17294 let mut freq: HashMap<String, i64> = HashMap::new();
17295 for word in s.split_whitespace() {
17296 let lower = word.to_lowercase();
17297 *freq.entry(lower).or_insert(0) += 1;
17298 }
17299 let map: HashMap<String, Value> =
17300 freq.into_iter().map(|(k, v)| (k, Value::Int(v))).collect();
17301 Ok(Value::Map(Rc::new(RefCell::new(map))))
17302 }
17303 _ => Err(RuntimeError::new("word_frequency() requires string")),
17304 }
17305 });
17306
17307 define(interp, "sentiment_words", Some(1), |_, args| {
17313 match &args[0] {
17314 Value::String(s) => {
17315 let positive = vec![
17316 "good",
17317 "great",
17318 "excellent",
17319 "amazing",
17320 "wonderful",
17321 "fantastic",
17322 "love",
17323 "happy",
17324 "joy",
17325 "beautiful",
17326 "awesome",
17327 "perfect",
17328 "best",
17329 "brilliant",
17330 "delightful",
17331 "pleasant",
17332 "positive",
17333 ];
17334 let negative = vec![
17335 "bad",
17336 "terrible",
17337 "awful",
17338 "horrible",
17339 "hate",
17340 "sad",
17341 "angry",
17342 "worst",
17343 "poor",
17344 "negative",
17345 "disappointing",
17346 "ugly",
17347 "disgusting",
17348 "painful",
17349 "miserable",
17350 "annoying",
17351 ];
17352
17353 let lower = s.to_lowercase();
17354 let words: Vec<&str> = lower.split_whitespace().collect();
17355 let pos_count: i64 = words.iter().filter(|w| positive.contains(w)).count() as i64;
17356 let neg_count: i64 = words.iter().filter(|w| negative.contains(w)).count() as i64;
17357
17358 let mut map = HashMap::new();
17359 map.insert("positive".to_string(), Value::Int(pos_count));
17360 map.insert("negative".to_string(), Value::Int(neg_count));
17361 map.insert("total".to_string(), Value::Int(words.len() as i64));
17362
17363 let score = if pos_count + neg_count > 0 {
17364 (pos_count - neg_count) as f64 / (pos_count + neg_count) as f64
17365 } else {
17366 0.0
17367 };
17368 map.insert("score".to_string(), Value::Float(score));
17369
17370 Ok(Value::Map(Rc::new(RefCell::new(map))))
17371 }
17372 _ => Err(RuntimeError::new("sentiment_words() requires string")),
17373 }
17374 });
17375
17376 define(interp, "has_question", Some(1), |_, args| match &args[0] {
17378 Value::String(s) => {
17379 let has_q_mark = s.contains('?');
17380 let lower = s.to_lowercase();
17381 let question_words = [
17382 "what", "where", "when", "why", "how", "who", "which", "whose", "whom",
17383 ];
17384 let starts_with_q = question_words.iter().any(|w| lower.starts_with(w));
17385 Ok(Value::Bool(has_q_mark || starts_with_q))
17386 }
17387 _ => Err(RuntimeError::new("has_question() requires string")),
17388 });
17389
17390 define(interp, "has_exclamation", Some(1), |_, args| {
17392 match &args[0] {
17393 Value::String(s) => Ok(Value::Bool(s.contains('!'))),
17394 _ => Err(RuntimeError::new("has_exclamation() requires string")),
17395 }
17396 });
17397
17398 define(interp, "text_formality", Some(1), |_, args| {
17400 match &args[0] {
17401 Value::String(s) => {
17402 let lower = s.to_lowercase();
17403 let informal_markers = vec![
17404 "gonna", "wanna", "gotta", "kinda", "sorta", "dunno", "yeah", "yep", "nope",
17405 "ok", "lol", "omg", "btw", "u", "ur", "r", "y", "2", "4",
17406 ];
17407 let formal_markers = vec![
17408 "therefore",
17409 "furthermore",
17410 "moreover",
17411 "consequently",
17412 "nevertheless",
17413 "however",
17414 "whereas",
17415 "hereby",
17416 "respectfully",
17417 "sincerely",
17418 "accordingly",
17419 ];
17420
17421 let words: Vec<&str> = lower.split_whitespace().collect();
17422 let informal_count = words
17423 .iter()
17424 .filter(|w| informal_markers.contains(w))
17425 .count();
17426 let formal_count = words.iter().filter(|w| formal_markers.contains(w)).count();
17427
17428 let score = if informal_count + formal_count > 0 {
17429 formal_count as f64 / (informal_count + formal_count) as f64
17430 } else {
17431 0.5 };
17433
17434 Ok(Value::Float(score))
17435 }
17436 _ => Err(RuntimeError::new("text_formality() requires string")),
17437 }
17438 });
17439
17440 define(interp, "sentiment_vader", Some(1), |_, args| {
17446 match &args[0] {
17447 Value::String(s) => {
17448 let result = compute_vader_sentiment(s);
17449 let mut map = HashMap::new();
17450 map.insert("positive".to_string(), Value::Float(result.0));
17451 map.insert("negative".to_string(), Value::Float(result.1));
17452 map.insert("neutral".to_string(), Value::Float(result.2));
17453 map.insert("compound".to_string(), Value::Float(result.3));
17454 Ok(Value::Map(Rc::new(RefCell::new(map))))
17455 }
17456 _ => Err(RuntimeError::new("sentiment_vader() requires string")),
17457 }
17458 });
17459
17460 define(interp, "emotion_detect", Some(1), |_, args| {
17462 match &args[0] {
17463 Value::String(s) => {
17464 let emotions = compute_emotions(s);
17465 let map: HashMap<String, Value> = emotions
17466 .into_iter()
17467 .map(|(k, v)| (k, Value::Float(v)))
17468 .collect();
17469 Ok(Value::Map(Rc::new(RefCell::new(map))))
17470 }
17471 _ => Err(RuntimeError::new("emotion_detect() requires string")),
17472 }
17473 });
17474
17475 define(interp, "intensity_score", Some(1), |_, args| {
17477 match &args[0] {
17478 Value::String(s) => {
17479 let score = compute_intensity(s);
17480 Ok(Value::Float(score))
17481 }
17482 _ => Err(RuntimeError::new("intensity_score() requires string")),
17483 }
17484 });
17485
17486 define(interp, "detect_sarcasm", Some(1), |_, args| {
17492 match &args[0] {
17493 Value::String(s) => {
17494 let result = compute_sarcasm_score(s);
17495 let mut map = HashMap::new();
17496 map.insert("score".to_string(), Value::Float(result.0));
17497 map.insert("confidence".to_string(), Value::Float(result.1));
17498 let markers: Vec<Value> = result
17499 .2
17500 .into_iter()
17501 .map(|m| Value::String(Rc::new(m)))
17502 .collect();
17503 map.insert(
17504 "markers".to_string(),
17505 Value::Array(Rc::new(RefCell::new(markers))),
17506 );
17507 Ok(Value::Map(Rc::new(RefCell::new(map))))
17508 }
17509 _ => Err(RuntimeError::new("detect_sarcasm() requires string")),
17510 }
17511 });
17512
17513 define(interp, "is_sarcastic", Some(1), |_, args| match &args[0] {
17515 Value::String(s) => {
17516 let result = compute_sarcasm_score(s);
17517 Ok(Value::Bool(result.0 > 0.5))
17518 }
17519 _ => Err(RuntimeError::new("is_sarcastic() requires string")),
17520 });
17521
17522 define(interp, "detect_irony", Some(1), |_, args| match &args[0] {
17524 Value::String(s) => {
17525 let score = compute_irony_score(s);
17526 Ok(Value::Float(score))
17527 }
17528 _ => Err(RuntimeError::new("detect_irony() requires string")),
17529 });
17530
17531 define(interp, "extract_emails", Some(1), |_, args| {
17537 match &args[0] {
17538 Value::String(s) => {
17539 let re = Regex::new(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}").unwrap();
17540 let emails: Vec<Value> = re
17541 .find_iter(s)
17542 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17543 .collect();
17544 Ok(Value::Array(Rc::new(RefCell::new(emails))))
17545 }
17546 _ => Err(RuntimeError::new("extract_emails() requires string")),
17547 }
17548 });
17549
17550 define(interp, "extract_urls", Some(1), |_, args| match &args[0] {
17552 Value::String(s) => {
17553 let re = Regex::new(r"https?://[^\s<>\[\]{}|\\^]+").unwrap();
17554 let urls: Vec<Value> = re
17555 .find_iter(s)
17556 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17557 .collect();
17558 Ok(Value::Array(Rc::new(RefCell::new(urls))))
17559 }
17560 _ => Err(RuntimeError::new("extract_urls() requires string")),
17561 });
17562
17563 define(
17565 interp,
17566 "extract_phone_numbers",
17567 Some(1),
17568 |_, args| match &args[0] {
17569 Value::String(s) => {
17570 let re =
17571 Regex::new(r"(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}")
17572 .unwrap();
17573 let phones: Vec<Value> = re
17574 .find_iter(s)
17575 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17576 .collect();
17577 Ok(Value::Array(Rc::new(RefCell::new(phones))))
17578 }
17579 _ => Err(RuntimeError::new("extract_phone_numbers() requires string")),
17580 },
17581 );
17582
17583 define(interp, "extract_dates", Some(1), |_, args| {
17585 match &args[0] {
17586 Value::String(s) => {
17587 let patterns = vec![
17589 r"\d{4}-\d{2}-\d{2}", r"\d{2}/\d{2}/\d{4}", r"\d{2}-\d{2}-\d{4}", r"(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{1,2},?\s+\d{4}",
17593 r"\d{1,2}\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{4}",
17594 ];
17595 let mut dates = Vec::new();
17596 for pattern in patterns {
17597 if let Ok(re) = Regex::new(pattern) {
17598 for m in re.find_iter(s) {
17599 dates.push(Value::String(Rc::new(m.as_str().to_string())));
17600 }
17601 }
17602 }
17603 Ok(Value::Array(Rc::new(RefCell::new(dates))))
17604 }
17605 _ => Err(RuntimeError::new("extract_dates() requires string")),
17606 }
17607 });
17608
17609 define(interp, "extract_money", Some(1), |_, args| match &args[0] {
17611 Value::String(s) => {
17612 let re = Regex::new(r"[$€£¥]\s*\d+(?:,\d{3})*(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:dollars?|euros?|pounds?|USD|EUR|GBP)").unwrap();
17613 let money: Vec<Value> = re
17614 .find_iter(s)
17615 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17616 .collect();
17617 Ok(Value::Array(Rc::new(RefCell::new(money))))
17618 }
17619 _ => Err(RuntimeError::new("extract_money() requires string")),
17620 });
17621
17622 define(interp, "extract_hashtags", Some(1), |_, args| {
17624 match &args[0] {
17625 Value::String(s) => {
17626 let re = Regex::new(r"#\w+").unwrap();
17627 let tags: Vec<Value> = re
17628 .find_iter(s)
17629 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17630 .collect();
17631 Ok(Value::Array(Rc::new(RefCell::new(tags))))
17632 }
17633 _ => Err(RuntimeError::new("extract_hashtags() requires string")),
17634 }
17635 });
17636
17637 define(interp, "extract_mentions", Some(1), |_, args| {
17639 match &args[0] {
17640 Value::String(s) => {
17641 let re = Regex::new(r"@\w+").unwrap();
17642 let mentions: Vec<Value> = re
17643 .find_iter(s)
17644 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17645 .collect();
17646 Ok(Value::Array(Rc::new(RefCell::new(mentions))))
17647 }
17648 _ => Err(RuntimeError::new("extract_mentions() requires string")),
17649 }
17650 });
17651
17652 define(interp, "extract_numbers", Some(1), |_, args| {
17654 match &args[0] {
17655 Value::String(s) => {
17656 let re = Regex::new(r"-?\d+(?:,\d{3})*(?:\.\d+)?").unwrap();
17657 let numbers: Vec<Value> = re
17658 .find_iter(s)
17659 .filter_map(|m| {
17660 let num_str = m.as_str().replace(",", "");
17661 if let Ok(n) = num_str.parse::<f64>() {
17662 Some(Value::Float(n))
17663 } else {
17664 None
17665 }
17666 })
17667 .collect();
17668 Ok(Value::Array(Rc::new(RefCell::new(numbers))))
17669 }
17670 _ => Err(RuntimeError::new("extract_numbers() requires string")),
17671 }
17672 });
17673
17674 define(interp, "extract_entities", Some(1), |_, args| {
17676 match &args[0] {
17677 Value::String(s) => {
17678 let re = Regex::new(r"(?:[.!?]\s+)?([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)").unwrap();
17680 let mut entities = std::collections::HashSet::new();
17681 for cap in re.captures_iter(s) {
17682 if let Some(m) = cap.get(1) {
17683 let entity = m.as_str().to_string();
17684 let starters = [
17686 "The", "A", "An", "This", "That", "It", "I", "We", "They", "He", "She",
17687 ];
17688 if !starters.contains(&entity.as_str()) {
17689 entities.insert(entity);
17690 }
17691 }
17692 }
17693 let results: Vec<Value> = entities
17694 .into_iter()
17695 .map(|e| Value::String(Rc::new(e)))
17696 .collect();
17697 Ok(Value::Array(Rc::new(RefCell::new(results))))
17698 }
17699 _ => Err(RuntimeError::new("extract_entities() requires string")),
17700 }
17701 });
17702
17703 define(interp, "text_hash_vector", Some(2), |_, args| {
17709 match (&args[0], &args[1]) {
17710 (Value::String(s), Value::Int(dims)) => {
17711 let dims = *dims as usize;
17712 let mut vector = vec![0.0f64; dims];
17713
17714 for word in s.to_lowercase().split_whitespace() {
17716 let hash = compute_hash(word, 0);
17717 let idx = (hash as usize) % dims;
17718 vector[idx] += 1.0;
17719 }
17720
17721 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
17723 if magnitude > 0.0 {
17724 for v in vector.iter_mut() {
17725 *v /= magnitude;
17726 }
17727 }
17728
17729 let values: Vec<Value> = vector.into_iter().map(Value::Float).collect();
17730 Ok(Value::Array(Rc::new(RefCell::new(values))))
17731 }
17732 _ => Err(RuntimeError::new(
17733 "text_hash_vector() requires string and dimensions",
17734 )),
17735 }
17736 });
17737
17738 define(interp, "text_fingerprint", Some(1), |_, args| {
17740 match &args[0] {
17741 Value::String(s) => {
17742 let lower = s.to_lowercase();
17744 let words: Vec<&str> = lower.split_whitespace().collect();
17745
17746 let mut fp: u64 = 0;
17747 for (i, word) in words.iter().enumerate() {
17748 let h = compute_hash(word, i as u64);
17749 fp ^= h.rotate_left((i % 64) as u32);
17750 }
17751
17752 Ok(Value::String(Rc::new(format!("{:016x}", fp))))
17753 }
17754 _ => Err(RuntimeError::new("text_fingerprint() requires string")),
17755 }
17756 });
17757
17758 define(interp, "cosine_similarity", Some(2), |_, args| {
17760 match (&args[0], &args[1]) {
17761 (Value::Array(a), Value::Array(b)) => {
17762 let a_ref = a.borrow();
17763 let b_ref = b.borrow();
17764
17765 if a_ref.len() != b_ref.len() {
17766 return Err(RuntimeError::new("Vectors must have same length"));
17767 }
17768
17769 let mut dot = 0.0;
17770 let mut mag_a = 0.0;
17771 let mut mag_b = 0.0;
17772
17773 for (va, vb) in a_ref.iter().zip(b_ref.iter()) {
17774 let fa = match va {
17775 Value::Float(f) => *f,
17776 Value::Int(i) => *i as f64,
17777 _ => continue,
17778 };
17779 let fb = match vb {
17780 Value::Float(f) => *f,
17781 Value::Int(i) => *i as f64,
17782 _ => continue,
17783 };
17784 dot += fa * fb;
17785 mag_a += fa * fa;
17786 mag_b += fb * fb;
17787 }
17788
17789 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
17790 if denom == 0.0 {
17791 Ok(Value::Float(0.0))
17792 } else {
17793 Ok(Value::Float(dot / denom))
17794 }
17795 }
17796 _ => Err(RuntimeError::new("cosine_similarity() requires two arrays")),
17797 }
17798 });
17799
17800 define(interp, "text_similarity_embedding", Some(2), |_, args| {
17802 match (&args[0], &args[1]) {
17803 (Value::String(a), Value::String(b)) => {
17804 let dims = 128;
17805
17806 let vec_a = create_hash_vector(a, dims);
17808 let vec_b = create_hash_vector(b, dims);
17809
17810 let mut dot = 0.0;
17812 let mut mag_a = 0.0;
17813 let mut mag_b = 0.0;
17814
17815 for i in 0..dims {
17816 dot += vec_a[i] * vec_b[i];
17817 mag_a += vec_a[i] * vec_a[i];
17818 mag_b += vec_b[i] * vec_b[i];
17819 }
17820
17821 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
17822 if denom == 0.0 {
17823 Ok(Value::Float(0.0))
17824 } else {
17825 Ok(Value::Float(dot / denom))
17826 }
17827 }
17828 _ => Err(RuntimeError::new(
17829 "text_similarity_embedding() requires two strings",
17830 )),
17831 }
17832 });
17833
17834 define(
17840 interp,
17841 "flesch_reading_ease",
17842 Some(1),
17843 |_, args| match &args[0] {
17844 Value::String(s) => {
17845 let (words, sentences, syllables) = count_text_stats(s);
17846 if words == 0 || sentences == 0 {
17847 return Ok(Value::Float(0.0));
17848 }
17849 let score = 206.835
17850 - 1.015 * (words as f64 / sentences as f64)
17851 - 84.6 * (syllables as f64 / words as f64);
17852 Ok(Value::Float(score.max(0.0).min(100.0)))
17853 }
17854 _ => Err(RuntimeError::new("flesch_reading_ease() requires string")),
17855 },
17856 );
17857
17858 define(
17860 interp,
17861 "flesch_kincaid_grade",
17862 Some(1),
17863 |_, args| match &args[0] {
17864 Value::String(s) => {
17865 let (words, sentences, syllables) = count_text_stats(s);
17866 if words == 0 || sentences == 0 {
17867 return Ok(Value::Float(0.0));
17868 }
17869 let grade = 0.39 * (words as f64 / sentences as f64)
17870 + 11.8 * (syllables as f64 / words as f64)
17871 - 15.59;
17872 Ok(Value::Float(grade.max(0.0)))
17873 }
17874 _ => Err(RuntimeError::new("flesch_kincaid_grade() requires string")),
17875 },
17876 );
17877
17878 define(
17880 interp,
17881 "automated_readability_index",
17882 Some(1),
17883 |_, args| match &args[0] {
17884 Value::String(s) => {
17885 let chars: usize = s.chars().filter(|c| c.is_alphanumeric()).count();
17886 let words: usize = s.split_whitespace().count();
17887 let sentences: usize = s
17888 .matches(|c| c == '.' || c == '!' || c == '?')
17889 .count()
17890 .max(1);
17891
17892 if words == 0 {
17893 return Ok(Value::Float(0.0));
17894 }
17895
17896 let ari = 4.71 * (chars as f64 / words as f64)
17897 + 0.5 * (words as f64 / sentences as f64)
17898 - 21.43;
17899 Ok(Value::Float(ari.max(0.0)))
17900 }
17901 _ => Err(RuntimeError::new(
17902 "automated_readability_index() requires string",
17903 )),
17904 },
17905 );
17906
17907 define(interp, "reading_time", Some(1), |_, args| {
17909 match &args[0] {
17910 Value::String(s) => {
17911 let words = s.split_whitespace().count();
17912 let minutes = words as f64 / 200.0; Ok(Value::Float(minutes))
17914 }
17915 _ => Err(RuntimeError::new("reading_time() requires string")),
17916 }
17917 });
17918
17919 define(interp, "speaking_time", Some(1), |_, args| {
17921 match &args[0] {
17922 Value::String(s) => {
17923 let words = s.split_whitespace().count();
17924 let minutes = words as f64 / 150.0; Ok(Value::Float(minutes))
17926 }
17927 _ => Err(RuntimeError::new("speaking_time() requires string")),
17928 }
17929 });
17930}
17931
17932fn compute_vader_sentiment(s: &str) -> (f64, f64, f64, f64) {
17938 let positive_words: Vec<(&str, f64)> = vec![
17940 ("love", 3.0),
17941 ("loved", 3.0),
17942 ("loving", 3.0),
17943 ("excellent", 3.0),
17944 ("amazing", 3.0),
17945 ("fantastic", 3.0),
17946 ("wonderful", 3.0),
17947 ("great", 2.5),
17948 ("awesome", 2.5),
17949 ("brilliant", 2.5),
17950 ("superb", 2.5),
17951 ("good", 2.0),
17952 ("nice", 2.0),
17953 ("pleasant", 2.0),
17954 ("happy", 2.0),
17955 ("like", 1.5),
17956 ("enjoy", 1.5),
17957 ("fine", 1.5),
17958 ("okay", 1.0),
17959 ("best", 3.0),
17960 ("perfect", 3.0),
17961 ("beautiful", 2.5),
17962 ("delightful", 2.5),
17963 ("excited", 2.5),
17964 ("thrilled", 3.0),
17965 ("glad", 2.0),
17966 ("pleased", 2.0),
17967 ];
17968
17969 let negative_words: Vec<(&str, f64)> = vec![
17970 ("hate", 3.0),
17971 ("hated", 3.0),
17972 ("hating", 3.0),
17973 ("terrible", 3.0),
17974 ("horrible", 3.0),
17975 ("awful", 3.0),
17976 ("disgusting", 3.0),
17977 ("bad", 2.5),
17978 ("poor", 2.5),
17979 ("worst", 3.0),
17980 ("pathetic", 2.5),
17981 ("sad", 2.0),
17982 ("angry", 2.5),
17983 ("upset", 2.0),
17984 ("disappointed", 2.0),
17985 ("dislike", 1.5),
17986 ("annoying", 2.0),
17987 ("boring", 1.5),
17988 ("mediocre", 1.0),
17989 ("ugly", 2.5),
17990 ("stupid", 2.5),
17991 ("dumb", 2.0),
17992 ("useless", 2.5),
17993 ("painful", 2.5),
17994 ("miserable", 3.0),
17995 ("depressing", 2.5),
17996 ("frustrating", 2.0),
17997 ];
17998
17999 let boosters = vec![
18001 "very",
18002 "really",
18003 "extremely",
18004 "absolutely",
18005 "incredibly",
18006 "totally",
18007 "so",
18008 ];
18009 let dampeners = vec![
18010 "somewhat", "slightly", "a bit", "kind of", "sort of", "barely",
18011 ];
18012
18013 let lower = s.to_lowercase();
18014 let words: Vec<&str> = lower.split_whitespace().collect();
18015
18016 let mut pos_score = 0.0;
18017 let mut neg_score = 0.0;
18018 let mut word_count = 0;
18019
18020 for (i, word) in words.iter().enumerate() {
18021 let mut modifier = 1.0;
18022
18023 if i > 0 {
18025 if boosters.contains(&words[i - 1]) {
18026 modifier = 1.5;
18027 } else if dampeners.iter().any(|d| words[i - 1].contains(d)) {
18028 modifier = 0.5;
18029 }
18030 }
18031
18032 let negated = i > 0
18034 && [
18035 "not",
18036 "no",
18037 "never",
18038 "neither",
18039 "don't",
18040 "doesn't",
18041 "didn't",
18042 "won't",
18043 "wouldn't",
18044 "couldn't",
18045 "shouldn't",
18046 ]
18047 .contains(&words[i - 1]);
18048
18049 if let Some((_, score)) = positive_words.iter().find(|(w, _)| w == word) {
18050 if negated {
18051 neg_score += score * modifier;
18052 } else {
18053 pos_score += score * modifier;
18054 }
18055 word_count += 1;
18056 } else if let Some((_, score)) = negative_words.iter().find(|(w, _)| w == word) {
18057 if negated {
18058 pos_score += score * modifier * 0.5; } else {
18060 neg_score += score * modifier;
18061 }
18062 word_count += 1;
18063 }
18064 }
18065
18066 let total = pos_score + neg_score;
18068 let (pos_norm, neg_norm) = if total > 0.0 {
18069 (pos_score / total, neg_score / total)
18070 } else {
18071 (0.0, 0.0)
18072 };
18073
18074 let neutral = 1.0 - pos_norm - neg_norm;
18075
18076 let compound = if word_count > 0 {
18078 ((pos_score - neg_score) / (word_count as f64 * 3.0))
18079 .max(-1.0)
18080 .min(1.0)
18081 } else {
18082 0.0
18083 };
18084
18085 (pos_norm, neg_norm, neutral.max(0.0), compound)
18086}
18087
18088fn compute_emotions(s: &str) -> HashMap<String, f64> {
18090 let emotion_words: Vec<(&str, &str)> = vec![
18091 ("happy", "joy"),
18093 ("joyful", "joy"),
18094 ("delighted", "joy"),
18095 ("cheerful", "joy"),
18096 ("excited", "joy"),
18097 ("thrilled", "joy"),
18098 ("ecstatic", "joy"),
18099 ("elated", "joy"),
18100 ("sad", "sadness"),
18102 ("unhappy", "sadness"),
18103 ("depressed", "sadness"),
18104 ("miserable", "sadness"),
18105 ("gloomy", "sadness"),
18106 ("heartbroken", "sadness"),
18107 ("sorrowful", "sadness"),
18108 ("melancholy", "sadness"),
18109 ("angry", "anger"),
18111 ("furious", "anger"),
18112 ("enraged", "anger"),
18113 ("irritated", "anger"),
18114 ("annoyed", "anger"),
18115 ("outraged", "anger"),
18116 ("livid", "anger"),
18117 ("mad", "anger"),
18118 ("afraid", "fear"),
18120 ("scared", "fear"),
18121 ("terrified", "fear"),
18122 ("frightened", "fear"),
18123 ("anxious", "fear"),
18124 ("worried", "fear"),
18125 ("nervous", "fear"),
18126 ("panicked", "fear"),
18127 ("surprised", "surprise"),
18129 ("amazed", "surprise"),
18130 ("astonished", "surprise"),
18131 ("shocked", "surprise"),
18132 ("stunned", "surprise"),
18133 ("startled", "surprise"),
18134 ("bewildered", "surprise"),
18135 ("disgusted", "disgust"),
18137 ("revolted", "disgust"),
18138 ("repulsed", "disgust"),
18139 ("sickened", "disgust"),
18140 ("nauseated", "disgust"),
18141 ("appalled", "disgust"),
18142 ("trust", "trust"),
18144 ("confident", "trust"),
18145 ("secure", "trust"),
18146 ("reliable", "trust"),
18147 ("faithful", "trust"),
18148 ("loyal", "trust"),
18149 ("eager", "anticipation"),
18151 ("hopeful", "anticipation"),
18152 ("expectant", "anticipation"),
18153 ("looking forward", "anticipation"),
18154 ("excited", "anticipation"),
18155 ];
18156
18157 let lower = s.to_lowercase();
18158 let mut counts: HashMap<String, f64> = HashMap::new();
18159
18160 for (word, emotion) in emotion_words {
18161 if lower.contains(word) {
18162 *counts.entry(emotion.to_string()).or_insert(0.0) += 1.0;
18163 }
18164 }
18165
18166 let total: f64 = counts.values().sum();
18168 if total > 0.0 {
18169 for v in counts.values_mut() {
18170 *v /= total;
18171 }
18172 }
18173
18174 counts
18175}
18176
18177fn compute_intensity(s: &str) -> f64 {
18179 let intensifiers = vec![
18180 ("very", 1.5),
18181 ("really", 1.5),
18182 ("extremely", 2.0),
18183 ("incredibly", 2.0),
18184 ("absolutely", 2.0),
18185 ("totally", 1.5),
18186 ("completely", 1.5),
18187 ("utterly", 2.0),
18188 ("so", 1.3),
18189 ("such", 1.3),
18190 ("quite", 1.2),
18191 ("rather", 1.1),
18192 ];
18193
18194 let exclamation_boost = 0.5;
18195 let caps_boost = 0.3;
18196
18197 let lower = s.to_lowercase();
18198 let mut score = 1.0;
18199
18200 for (word, boost) in intensifiers {
18201 if lower.contains(word) {
18202 score *= boost;
18203 }
18204 }
18205
18206 let exclamations = s.matches('!').count();
18208 score += exclamations as f64 * exclamation_boost;
18209
18210 let caps_words = s
18212 .split_whitespace()
18213 .filter(|w| w.len() > 2 && w.chars().all(|c| c.is_uppercase()))
18214 .count();
18215 score += caps_words as f64 * caps_boost;
18216
18217 score.min(5.0)
18218}
18219
18220fn compute_sarcasm_score(s: &str) -> (f64, f64, Vec<String>) {
18222 let mut markers = Vec::new();
18223 let mut score: f64 = 0.0;
18224
18225 let lower = s.to_lowercase();
18226
18227 let explicit = vec![
18229 "/s",
18230 "not!",
18231 "yeah right",
18232 "sure thing",
18233 "oh really",
18234 "oh great",
18235 "wow, just wow",
18236 "thanks a lot",
18237 "how wonderful",
18238 "isn't that special",
18239 "clearly",
18240 "obviously",
18241 "shocking",
18242 "no way",
18243 "what a surprise",
18244 ];
18245
18246 for marker in &explicit {
18247 if lower.contains(marker) {
18248 markers.push(format!("explicit: {}", marker));
18249 score += 0.4;
18250 }
18251 }
18252
18253 let hyperbolic = vec![
18255 "best thing ever",
18256 "worst thing ever",
18257 "literally dying",
18258 "absolutely perfect",
18259 "world's greatest",
18260 "totally awesome",
18261 "so much fun",
18262 "couldn't be happier",
18263 ];
18264
18265 for h in &hyperbolic {
18266 if lower.contains(h) {
18267 markers.push(format!("hyperbole: {}", h));
18268 score += 0.3;
18269 }
18270 }
18271
18272 let has_positive = ["great", "wonderful", "amazing", "love", "best", "awesome"]
18274 .iter()
18275 .any(|w| lower.contains(w));
18276 let has_negative_context = ["but", "however", "although", "except", "unfortunately"]
18277 .iter()
18278 .any(|w| lower.contains(w));
18279
18280 if has_positive && has_negative_context {
18281 markers.push("positive-negative contrast".to_string());
18282 score += 0.25;
18283 }
18284
18285 let quote_pattern = Regex::new(r#"["'](\w+)["']"#).unwrap();
18287 for cap in quote_pattern.captures_iter(s) {
18288 if let Some(m) = cap.get(1) {
18289 let word = m.as_str().to_lowercase();
18290 if [
18291 "great",
18292 "wonderful",
18293 "helpful",
18294 "useful",
18295 "smart",
18296 "genius",
18297 "brilliant",
18298 ]
18299 .contains(&word.as_str())
18300 {
18301 markers.push(format!("air quotes: \"{}\"", word));
18302 score += 0.35;
18303 }
18304 }
18305 }
18306
18307 if s.contains("...") || s.contains("!!!") || s.contains("???") {
18309 markers.push("excessive punctuation".to_string());
18310 score += 0.15;
18311 }
18312
18313 let confidence = if markers.is_empty() {
18315 0.0
18316 } else {
18317 (markers.len() as f64 * 0.25).min(1.0)
18318 };
18319
18320 (score.min(1.0), confidence, markers)
18321}
18322
18323fn compute_irony_score(s: &str) -> f64 {
18325 let mut score: f64 = 0.0;
18326 let lower = s.to_lowercase();
18327
18328 let irony_phrases = vec![
18330 "of course",
18331 "as expected",
18332 "naturally",
18333 "predictably",
18334 "who would have thought",
18335 "surprise surprise",
18336 "go figure",
18337 "typical",
18338 "as usual",
18339 "yet again",
18340 "once again",
18341 ];
18342
18343 for phrase in irony_phrases {
18344 if lower.contains(phrase) {
18345 score += 0.2;
18346 }
18347 }
18348
18349 if lower.contains("but") || lower.contains("yet") || lower.contains("however") {
18351 score += 0.1;
18352 }
18353
18354 if s.contains('?')
18356 && (lower.starts_with("isn't")
18357 || lower.starts_with("aren't")
18358 || lower.starts_with("doesn't")
18359 || lower.starts_with("don't")
18360 || lower.contains("right?")
18361 || lower.contains("isn't it"))
18362 {
18363 score += 0.25;
18364 }
18365
18366 score.min(1.0)
18367}
18368
18369fn create_hash_vector(s: &str, dims: usize) -> Vec<f64> {
18371 let mut vector = vec![0.0f64; dims];
18372
18373 for word in s.to_lowercase().split_whitespace() {
18374 let hash = compute_hash(word, 0);
18375 let idx = (hash as usize) % dims;
18376 vector[idx] += 1.0;
18377 }
18378
18379 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
18381 if magnitude > 0.0 {
18382 for v in vector.iter_mut() {
18383 *v /= magnitude;
18384 }
18385 }
18386
18387 vector
18388}
18389
18390fn count_text_stats(s: &str) -> (usize, usize, usize) {
18392 let words: Vec<&str> = s.split_whitespace().collect();
18393 let word_count = words.len();
18394 let sentence_count = s
18395 .matches(|c| c == '.' || c == '!' || c == '?')
18396 .count()
18397 .max(1);
18398
18399 let mut syllable_count = 0;
18400 for word in &words {
18401 syllable_count += count_syllables(word);
18402 }
18403
18404 (word_count, sentence_count, syllable_count)
18405}
18406
18407fn count_syllables(word: &str) -> usize {
18409 let word = word.to_lowercase();
18410 let vowels = ['a', 'e', 'i', 'o', 'u', 'y'];
18411 let mut count = 0;
18412 let mut prev_was_vowel = false;
18413
18414 for c in word.chars() {
18415 let is_vowel = vowels.contains(&c);
18416 if is_vowel && !prev_was_vowel {
18417 count += 1;
18418 }
18419 prev_was_vowel = is_vowel;
18420 }
18421
18422 if word.ends_with('e') && count > 1 {
18424 count -= 1;
18425 }
18426
18427 count.max(1)
18428}
18429
18430fn compute_soundex(s: &str) -> String {
18432 if s.is_empty() {
18433 return "0000".to_string();
18434 }
18435
18436 let s = s.to_uppercase();
18437 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
18438
18439 if chars.is_empty() {
18440 return "0000".to_string();
18441 }
18442
18443 let first = chars[0];
18444 let mut code = String::new();
18445 code.push(first);
18446
18447 let get_code = |c: char| -> char {
18448 match c {
18449 'B' | 'F' | 'P' | 'V' => '1',
18450 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => '2',
18451 'D' | 'T' => '3',
18452 'L' => '4',
18453 'M' | 'N' => '5',
18454 'R' => '6',
18455 _ => '0',
18456 }
18457 };
18458
18459 let mut prev_code = get_code(first);
18460
18461 for &c in chars.iter().skip(1) {
18462 let curr_code = get_code(c);
18463 if curr_code != '0' && curr_code != prev_code {
18464 code.push(curr_code);
18465 if code.len() == 4 {
18466 break;
18467 }
18468 }
18469 prev_code = curr_code;
18470 }
18471
18472 while code.len() < 4 {
18473 code.push('0');
18474 }
18475
18476 code
18477}
18478
18479fn compute_metaphone(s: &str) -> String {
18481 let s = s.to_uppercase();
18482 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
18483
18484 if chars.is_empty() {
18485 return String::new();
18486 }
18487
18488 let mut result = String::new();
18489 let mut i = 0;
18490
18491 if chars.len() >= 2 {
18493 let prefix: String = chars[0..2].iter().collect();
18494 if ["KN", "GN", "PN", "AE", "WR"].contains(&prefix.as_str()) {
18495 i = 1;
18496 }
18497 }
18498
18499 while i < chars.len() && result.len() < 6 {
18500 let c = chars[i];
18501 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
18502 let next = chars.get(i + 1).copied();
18503
18504 let code = match c {
18505 'A' | 'E' | 'I' | 'O' | 'U' => {
18506 if i == 0 {
18507 Some(c)
18508 } else {
18509 None
18510 }
18511 }
18512 'B' => {
18513 if prev != Some('M') || i == chars.len() - 1 {
18514 Some('B')
18515 } else {
18516 None
18517 }
18518 }
18519 'C' => {
18520 if next == Some('H') {
18521 Some('X')
18522 } else if matches!(next, Some('I') | Some('E') | Some('Y')) {
18523 Some('S')
18524 } else {
18525 Some('K')
18526 }
18527 }
18528 'D' => {
18529 if next == Some('G')
18530 && matches!(chars.get(i + 2), Some('E') | Some('I') | Some('Y'))
18531 {
18532 Some('J')
18533 } else {
18534 Some('T')
18535 }
18536 }
18537 'F' => Some('F'),
18538 'G' => {
18539 if next == Some('H')
18540 && !matches!(
18541 chars.get(i + 2),
18542 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18543 )
18544 {
18545 None
18546 } else if matches!(next, Some('N') | Some('E') | Some('I') | Some('Y')) {
18547 Some('J')
18548 } else {
18549 Some('K')
18550 }
18551 }
18552 'H' => {
18553 if matches!(
18554 prev,
18555 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18556 ) {
18557 None
18558 } else if matches!(
18559 next,
18560 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18561 ) {
18562 Some('H')
18563 } else {
18564 None
18565 }
18566 }
18567 'J' => Some('J'),
18568 'K' => {
18569 if prev != Some('C') {
18570 Some('K')
18571 } else {
18572 None
18573 }
18574 }
18575 'L' => Some('L'),
18576 'M' => Some('M'),
18577 'N' => Some('N'),
18578 'P' => {
18579 if next == Some('H') {
18580 Some('F')
18581 } else {
18582 Some('P')
18583 }
18584 }
18585 'Q' => Some('K'),
18586 'R' => Some('R'),
18587 'S' => {
18588 if next == Some('H') {
18589 Some('X')
18590 } else {
18591 Some('S')
18592 }
18593 }
18594 'T' => {
18595 if next == Some('H') {
18596 Some('0') } else if next == Some('I') && matches!(chars.get(i + 2), Some('O') | Some('A')) {
18598 Some('X')
18599 } else {
18600 Some('T')
18601 }
18602 }
18603 'V' => Some('F'),
18604 'W' | 'Y' => {
18605 if matches!(
18606 next,
18607 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18608 ) {
18609 Some(c)
18610 } else {
18611 None
18612 }
18613 }
18614 'X' => {
18615 result.push('K');
18616 Some('S')
18617 }
18618 'Z' => Some('S'),
18619 _ => None,
18620 };
18621
18622 if let Some(ch) = code {
18623 result.push(ch);
18624 }
18625
18626 if next == Some(c) {
18628 i += 1;
18629 }
18630 i += 1;
18631 }
18632
18633 result
18634}
18635
18636fn compute_cologne(s: &str) -> String {
18638 let s = s.to_uppercase();
18639 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
18640
18641 if chars.is_empty() {
18642 return String::new();
18643 }
18644
18645 let mut result = String::new();
18646
18647 for (i, &c) in chars.iter().enumerate() {
18648 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
18649 let next = chars.get(i + 1).copied();
18650
18651 let code = match c {
18652 'A' | 'E' | 'I' | 'O' | 'U' | 'J' | 'Y' => '0',
18653 'H' => continue,
18654 'B' | 'P' => '1',
18655 'D' | 'T' => {
18656 if matches!(next, Some('C') | Some('S') | Some('Z')) {
18657 '8'
18658 } else {
18659 '2'
18660 }
18661 }
18662 'F' | 'V' | 'W' => '3',
18663 'G' | 'K' | 'Q' => '4',
18664 'C' => {
18665 if i == 0 {
18666 if matches!(
18667 next,
18668 Some('A')
18669 | Some('H')
18670 | Some('K')
18671 | Some('L')
18672 | Some('O')
18673 | Some('Q')
18674 | Some('R')
18675 | Some('U')
18676 | Some('X')
18677 ) {
18678 '4'
18679 } else {
18680 '8'
18681 }
18682 } else if matches!(prev, Some('S') | Some('Z')) {
18683 '8'
18684 } else if matches!(
18685 next,
18686 Some('A')
18687 | Some('H')
18688 | Some('K')
18689 | Some('O')
18690 | Some('Q')
18691 | Some('U')
18692 | Some('X')
18693 ) {
18694 '4'
18695 } else {
18696 '8'
18697 }
18698 }
18699 'X' => {
18700 if matches!(prev, Some('C') | Some('K') | Some('Q')) {
18701 '8'
18702 } else {
18703 result.push('4');
18704 '8'
18705 }
18706 }
18707 'L' => '5',
18708 'M' | 'N' => '6',
18709 'R' => '7',
18710 'S' | 'Z' => '8',
18711 _ => continue,
18712 };
18713
18714 result.push(code);
18715 }
18716
18717 let mut deduped = String::new();
18719 let mut prev = None;
18720 for c in result.chars() {
18721 if prev != Some(c) {
18722 deduped.push(c);
18723 }
18724 prev = Some(c);
18725 }
18726
18727 let trimmed: String = deduped.trim_start_matches('0').to_string();
18729 if trimmed.is_empty() {
18730 "0".to_string()
18731 } else {
18732 trimmed
18733 }
18734}
18735
18736fn get_stopwords(lang: &str) -> Vec<&'static str> {
18738 match lang {
18739 "en" | "english" => vec![
18740 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with",
18741 "by", "from", "as", "is", "was", "are", "were", "been", "be", "have", "has", "had",
18742 "do", "does", "did", "will", "would", "could", "should", "may", "might", "must",
18743 "shall", "can", "need", "it", "its", "this", "that", "these", "those", "i", "you",
18744 "he", "she", "we", "they", "me", "him", "her", "us", "them", "my", "your", "his",
18745 "her", "our", "their", "what", "which", "who", "whom", "whose", "when", "where", "why",
18746 "how", "all", "each", "every", "both", "few", "more", "most", "other", "some", "such",
18747 "no", "nor", "not", "only", "own", "same", "so", "than", "too", "very", "just", "also",
18748 "now",
18749 ],
18750 "de" | "german" => vec![
18751 "der", "die", "das", "den", "dem", "des", "ein", "eine", "einer", "einem", "einen",
18752 "und", "oder", "aber", "in", "auf", "an", "zu", "für", "von", "mit", "bei", "als",
18753 "ist", "war", "sind", "waren", "sein", "haben", "hat", "hatte", "werden", "wird",
18754 "wurde", "kann", "können", "muss", "müssen", "soll", "sollen", "will", "wollen", "es",
18755 "sie", "er", "wir", "ihr", "ich", "du", "man", "sich", "nicht", "auch", "nur", "noch",
18756 "schon", "mehr", "sehr", "so",
18757 ],
18758 "fr" | "french" => vec![
18759 "le", "la", "les", "un", "une", "des", "et", "ou", "mais", "dans", "sur", "à", "de",
18760 "pour", "par", "avec", "ce", "cette", "ces", "est", "sont", "était", "être", "avoir",
18761 "a", "ont", "avait", "je", "tu", "il", "elle", "nous", "vous", "ils", "elles", "on",
18762 "ne", "pas", "plus", "moins", "très", "aussi", "que", "qui",
18763 ],
18764 "es" | "spanish" => vec![
18765 "el", "la", "los", "las", "un", "una", "unos", "unas", "y", "o", "pero", "en", "de",
18766 "a", "para", "por", "con", "es", "son", "era", "ser", "estar", "tiene", "tienen", "yo",
18767 "tú", "él", "ella", "nosotros", "ustedes", "ellos", "ellas", "no", "sí", "muy", "más",
18768 "menos", "también", "que", "quien", "cual", "como", "cuando",
18769 ],
18770 "it" | "italian" => vec![
18771 "il", "lo", "la", "i", "gli", "le", "un", "uno", "una", "e", "o", "ma", "in", "di",
18772 "a", "da", "per", "con", "su", "tra", "fra", "è", "sono", "era", "erano", "essere",
18773 "avere", "ha", "hanno", "io", "tu", "lui", "lei", "noi", "voi", "loro", "mi", "ti",
18774 "ci", "non", "più", "molto", "anche", "come", "che", "chi", "quale", "questo",
18775 "quello", "quando", "dove", "perché", "se", "però",
18776 ],
18777 "pt" | "portuguese" => vec![
18778 "o", "a", "os", "as", "um", "uma", "uns", "umas", "e", "ou", "mas", "em", "de", "para",
18779 "por", "com", "sem", "sob", "sobre", "é", "são", "era", "eram", "ser", "estar", "ter",
18780 "tem", "têm", "eu", "tu", "ele", "ela", "nós", "vós", "eles", "elas", "me", "te",
18781 "não", "mais", "muito", "também", "como", "que", "quem", "qual", "este", "esse",
18782 "aquele", "quando", "onde", "porque", "se", "já",
18783 ],
18784 "nl" | "dutch" => vec![
18785 "de", "het", "een", "en", "of", "maar", "in", "op", "aan", "van", "voor", "met", "bij",
18786 "naar", "om", "te", "tot", "uit", "over", "is", "zijn", "was", "waren", "worden",
18787 "wordt", "werd", "hebben", "ik", "je", "jij", "hij", "zij", "wij", "jullie", "ze",
18788 "mij", "jou", "niet", "geen", "meer", "ook", "als", "dat", "die", "wat", "wie", "dit",
18789 "deze", "wanneer", "waar", "waarom", "hoe", "dan", "nog",
18790 ],
18791 "ru" | "russian" => vec![
18792 "и",
18793 "в",
18794 "на",
18795 "с",
18796 "к",
18797 "по",
18798 "за",
18799 "из",
18800 "у",
18801 "о",
18802 "от",
18803 "до",
18804 "для",
18805 "при",
18806 "без",
18807 "под",
18808 "над",
18809 "между",
18810 "через",
18811 "после",
18812 "это",
18813 "то",
18814 "что",
18815 "как",
18816 "так",
18817 "но",
18818 "а",
18819 "или",
18820 "если",
18821 "же",
18822 "я",
18823 "ты",
18824 "он",
18825 "она",
18826 "мы",
18827 "вы",
18828 "они",
18829 "его",
18830 "её",
18831 "их",
18832 "не",
18833 "ни",
18834 "да",
18835 "нет",
18836 "был",
18837 "была",
18838 "были",
18839 "быть",
18840 "есть",
18841 "все",
18842 "всё",
18843 "весь",
18844 "этот",
18845 "тот",
18846 "который",
18847 "когда",
18848 "где",
18849 ],
18850 "ar" | "arabic" => vec![
18851 "في",
18852 "من",
18853 "إلى",
18854 "على",
18855 "عن",
18856 "مع",
18857 "هذا",
18858 "هذه",
18859 "ذلك",
18860 "تلك",
18861 "التي",
18862 "الذي",
18863 "اللذان",
18864 "اللتان",
18865 "الذين",
18866 "اللاتي",
18867 "اللواتي",
18868 "هو",
18869 "هي",
18870 "هم",
18871 "هن",
18872 "أنا",
18873 "أنت",
18874 "نحن",
18875 "أنتم",
18876 "أنتن",
18877 "كان",
18878 "كانت",
18879 "كانوا",
18880 "يكون",
18881 "تكون",
18882 "ليس",
18883 "ليست",
18884 "ليسوا",
18885 "و",
18886 "أو",
18887 "ثم",
18888 "لكن",
18889 "بل",
18890 "إن",
18891 "أن",
18892 "لأن",
18893 "كي",
18894 "حتى",
18895 "ما",
18896 "لا",
18897 "قد",
18898 "كل",
18899 "بعض",
18900 "غير",
18901 "أي",
18902 "كيف",
18903 "متى",
18904 "أين",
18905 ],
18906 "zh" | "chinese" => vec![
18907 "的", "了", "是", "在", "有", "和", "与", "或", "但", "而", "我", "你", "他", "她",
18908 "它", "我们", "你们", "他们", "她们", "这", "那", "这个", "那个", "这些", "那些",
18909 "什么", "哪", "哪个", "不", "没", "没有", "很", "也", "都", "就", "才", "只", "还",
18910 "把", "被", "给", "从", "到", "为", "以", "因为", "所以", "如果", "会", "能", "可以",
18911 "要", "想", "应该", "必须", "可能", "一", "个",
18912 ],
18913 "ja" | "japanese" => vec![
18914 "の",
18915 "に",
18916 "は",
18917 "を",
18918 "た",
18919 "が",
18920 "で",
18921 "て",
18922 "と",
18923 "し",
18924 "れ",
18925 "さ",
18926 "ある",
18927 "いる",
18928 "も",
18929 "する",
18930 "から",
18931 "な",
18932 "こと",
18933 "として",
18934 "い",
18935 "や",
18936 "など",
18937 "なっ",
18938 "ない",
18939 "この",
18940 "ため",
18941 "その",
18942 "あっ",
18943 "よう",
18944 "また",
18945 "もの",
18946 "という",
18947 "あり",
18948 "まで",
18949 "られ",
18950 "なる",
18951 "へ",
18952 "か",
18953 "だ",
18954 "これ",
18955 "によって",
18956 "により",
18957 "おり",
18958 "より",
18959 "による",
18960 "ず",
18961 "なり",
18962 "られる",
18963 "において",
18964 ],
18965 "ko" | "korean" => vec![
18966 "이",
18967 "그",
18968 "저",
18969 "것",
18970 "수",
18971 "등",
18972 "들",
18973 "및",
18974 "에",
18975 "의",
18976 "가",
18977 "을",
18978 "를",
18979 "은",
18980 "는",
18981 "로",
18982 "으로",
18983 "와",
18984 "과",
18985 "도",
18986 "에서",
18987 "까지",
18988 "부터",
18989 "만",
18990 "뿐",
18991 "처럼",
18992 "같이",
18993 "보다",
18994 "하다",
18995 "있다",
18996 "되다",
18997 "없다",
18998 "않다",
18999 "이다",
19000 "아니다",
19001 "나",
19002 "너",
19003 "우리",
19004 "그들",
19005 "이것",
19006 "그것",
19007 "저것",
19008 "무엇",
19009 "어디",
19010 "언제",
19011 "왜",
19012 "어떻게",
19013 "누구",
19014 "어느",
19015 "모든",
19016 "각",
19017 ],
19018 "hi" | "hindi" => vec![
19019 "का",
19020 "के",
19021 "की",
19022 "में",
19023 "है",
19024 "हैं",
19025 "को",
19026 "से",
19027 "पर",
19028 "था",
19029 "थे",
19030 "थी",
19031 "और",
19032 "या",
19033 "लेकिन",
19034 "अगर",
19035 "तो",
19036 "भी",
19037 "ही",
19038 "यह",
19039 "वह",
19040 "इस",
19041 "उस",
19042 "ये",
19043 "वे",
19044 "जो",
19045 "कि",
19046 "क्या",
19047 "कैसे",
19048 "मैं",
19049 "तुम",
19050 "आप",
19051 "हम",
19052 "वे",
19053 "उन्हें",
19054 "उनके",
19055 "अपने",
19056 "नहीं",
19057 "न",
19058 "कुछ",
19059 "कोई",
19060 "सब",
19061 "बहुत",
19062 "कम",
19063 "ज्यादा",
19064 "होना",
19065 "करना",
19066 "जाना",
19067 "आना",
19068 "देना",
19069 "लेना",
19070 "रहना",
19071 "सकना",
19072 ],
19073 "tr" | "turkish" => vec![
19074 "bir",
19075 "ve",
19076 "bu",
19077 "da",
19078 "de",
19079 "için",
19080 "ile",
19081 "mi",
19082 "ne",
19083 "o",
19084 "var",
19085 "ben",
19086 "sen",
19087 "biz",
19088 "siz",
19089 "onlar",
19090 "ki",
19091 "ama",
19092 "çok",
19093 "daha",
19094 "gibi",
19095 "kadar",
19096 "sonra",
19097 "şey",
19098 "kendi",
19099 "bütün",
19100 "her",
19101 "bazı",
19102 "olan",
19103 "olarak",
19104 "değil",
19105 "ya",
19106 "hem",
19107 "veya",
19108 "ancak",
19109 "ise",
19110 "göre",
19111 "rağmen",
19112 "dolayı",
19113 "üzere",
19114 "karşı",
19115 "arasında",
19116 "olan",
19117 "oldu",
19118 "olur",
19119 "olmak",
19120 "etmek",
19121 "yapmak",
19122 "demek",
19123 ],
19124 "pl" | "polish" => vec![
19125 "i",
19126 "w",
19127 "z",
19128 "na",
19129 "do",
19130 "o",
19131 "że",
19132 "to",
19133 "nie",
19134 "się",
19135 "jest",
19136 "tak",
19137 "jak",
19138 "ale",
19139 "po",
19140 "co",
19141 "czy",
19142 "lub",
19143 "oraz",
19144 "ja",
19145 "ty",
19146 "on",
19147 "ona",
19148 "my",
19149 "wy",
19150 "oni",
19151 "one",
19152 "pan",
19153 "pani",
19154 "ten",
19155 "ta",
19156 "te",
19157 "tego",
19158 "tej",
19159 "tym",
19160 "tych",
19161 "który",
19162 "która",
19163 "być",
19164 "mieć",
19165 "móc",
19166 "musieć",
19167 "chcieć",
19168 "wiedzieć",
19169 "mówić",
19170 "bardzo",
19171 "tylko",
19172 "już",
19173 "jeszcze",
19174 "też",
19175 "więc",
19176 "jednak",
19177 ],
19178 "sv" | "swedish" => vec![
19179 "och", "i", "att", "det", "som", "en", "på", "är", "av", "för", "med", "till", "den",
19180 "har", "de", "inte", "om", "ett", "men", "jag", "du", "han", "hon", "vi", "ni", "de",
19181 "dem", "sig", "sin", "var", "från", "eller", "när", "kan", "ska", "så", "än", "nu",
19182 "också", "bara", "mycket", "mer", "andra", "detta", "sedan", "hade", "varit", "skulle",
19183 "vara", "bli", "blev", "blir", "göra",
19184 ],
19185 _ => vec![
19186 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
19187 ],
19188 }
19189}
19190
19191fn compute_hash(s: &str, seed: u64) -> u64 {
19193 let mut hash: u64 = seed.wrapping_mul(0x517cc1b727220a95);
19194 for b in s.bytes() {
19195 hash = hash.wrapping_mul(31).wrapping_add(b as u64);
19196 }
19197 hash
19198}
19199
19200fn register_hologram(interp: &mut Interpreter) {
19220 use crate::interpreter::{
19221 RuntimeAffect, RuntimeConfidence, RuntimeEmotion, RuntimeFormality, RuntimeIntensity,
19222 RuntimeSentiment,
19223 };
19224
19225 define(interp, "emotional_hologram", Some(1), |_, args| {
19228 let affect = match &args[0] {
19229 Value::Affective { affect, .. } => affect.clone(),
19230 _ => RuntimeAffect {
19231 sentiment: None,
19232 sarcasm: false,
19233 intensity: None,
19234 formality: None,
19235 emotion: None,
19236 confidence: None,
19237 },
19238 };
19239
19240 let mut hologram = std::collections::HashMap::new();
19241
19242 let valence = match affect.sentiment {
19244 Some(RuntimeSentiment::Positive) => 1.0,
19245 Some(RuntimeSentiment::Negative) => -1.0,
19246 Some(RuntimeSentiment::Neutral) | None => 0.0,
19247 };
19248 hologram.insert("valence".to_string(), Value::Float(valence));
19249
19250 let arousal = match affect.intensity {
19252 Some(RuntimeIntensity::Down) => 0.25,
19253 None => 0.5,
19254 Some(RuntimeIntensity::Up) => 0.75,
19255 Some(RuntimeIntensity::Max) => 1.0,
19256 };
19257 hologram.insert("arousal".to_string(), Value::Float(arousal));
19258
19259 let dominance = match affect.formality {
19261 Some(RuntimeFormality::Informal) => 0.25,
19262 None => 0.5,
19263 Some(RuntimeFormality::Formal) => 0.85,
19264 };
19265 hologram.insert("dominance".to_string(), Value::Float(dominance));
19266
19267 let authenticity = if affect.sarcasm { -0.9 } else { 0.9 };
19269 hologram.insert("authenticity".to_string(), Value::Float(authenticity));
19270
19271 let certainty = match affect.confidence {
19273 Some(RuntimeConfidence::Low) => 0.2,
19274 None | Some(RuntimeConfidence::Medium) => 0.5,
19275 Some(RuntimeConfidence::High) => 0.9,
19276 };
19277 hologram.insert("certainty".to_string(), Value::Float(certainty));
19278
19279 let emotion_index = match affect.emotion {
19281 Some(RuntimeEmotion::Joy) => 0,
19282 Some(RuntimeEmotion::Sadness) => 1,
19283 Some(RuntimeEmotion::Anger) => 2,
19284 Some(RuntimeEmotion::Fear) => 3,
19285 Some(RuntimeEmotion::Surprise) => 4,
19286 Some(RuntimeEmotion::Love) => 5,
19287 None => -1,
19288 };
19289 hologram.insert("emotion_index".to_string(), Value::Int(emotion_index));
19290
19291 let emotion_name = match affect.emotion {
19293 Some(RuntimeEmotion::Joy) => "joy",
19294 Some(RuntimeEmotion::Sadness) => "sadness",
19295 Some(RuntimeEmotion::Anger) => "anger",
19296 Some(RuntimeEmotion::Fear) => "fear",
19297 Some(RuntimeEmotion::Surprise) => "surprise",
19298 Some(RuntimeEmotion::Love) => "love",
19299 None => "none",
19300 };
19301 hologram.insert(
19302 "emotion".to_string(),
19303 Value::String(Rc::new(emotion_name.to_string())),
19304 );
19305
19306 Ok(Value::Map(Rc::new(RefCell::new(hologram))))
19307 });
19308
19309 define(interp, "emotional_distance", Some(2), |interp, args| {
19311 let h1 = get_hologram_values(&args[0], interp)?;
19313 let h2 = get_hologram_values(&args[1], interp)?;
19314
19315 let dist = ((h1.0 - h2.0).powi(2) + (h1.1 - h2.1).powi(2) + (h1.2 - h2.2).powi(2) + (h1.3 - h2.3).powi(2) + (h1.4 - h2.4).powi(2)) .sqrt();
19322
19323 Ok(Value::Float(dist))
19324 });
19325
19326 define(interp, "emotional_similarity", Some(2), |interp, args| {
19328 let h1 = get_hologram_values(&args[0], interp)?;
19329 let h2 = get_hologram_values(&args[1], interp)?;
19330
19331 let dot = h1.0 * h2.0 + h1.1 * h2.1 + h1.2 * h2.2 + h1.3 * h2.3 + h1.4 * h2.4;
19332 let mag1 =
19333 (h1.0.powi(2) + h1.1.powi(2) + h1.2.powi(2) + h1.3.powi(2) + h1.4.powi(2)).sqrt();
19334 let mag2 =
19335 (h2.0.powi(2) + h2.1.powi(2) + h2.2.powi(2) + h2.3.powi(2) + h2.4.powi(2)).sqrt();
19336
19337 let similarity = if mag1 > 0.0 && mag2 > 0.0 {
19338 (dot / (mag1 * mag2) + 1.0) / 2.0 } else {
19340 0.5
19341 };
19342
19343 Ok(Value::Float(similarity))
19344 });
19345
19346 define(interp, "emotional_dissonance", Some(1), |_, args| {
19349 let affect = match &args[0] {
19350 Value::Affective { affect, .. } => affect.clone(),
19351 _ => return Ok(Value::Float(0.0)),
19352 };
19353
19354 let mut dissonance: f64 = 0.0;
19355
19356 if matches!(affect.sentiment, Some(RuntimeSentiment::Positive)) && affect.sarcasm {
19358 dissonance += 0.4;
19359 }
19360
19361 if matches!(affect.sentiment, Some(RuntimeSentiment::Negative)) && affect.sarcasm {
19363 dissonance += 0.1;
19364 }
19365
19366 if matches!(affect.confidence, Some(RuntimeConfidence::High))
19368 && matches!(affect.intensity, Some(RuntimeIntensity::Down))
19369 {
19370 dissonance += 0.2;
19371 }
19372
19373 if matches!(affect.formality, Some(RuntimeFormality::Formal)) {
19375 if matches!(
19376 affect.emotion,
19377 Some(RuntimeEmotion::Anger) | Some(RuntimeEmotion::Fear)
19378 ) {
19379 dissonance += 0.3;
19380 }
19381 }
19382
19383 if matches!(
19385 affect.emotion,
19386 Some(RuntimeEmotion::Joy) | Some(RuntimeEmotion::Love)
19387 ) && affect.sarcasm
19388 {
19389 dissonance += 0.3;
19390 }
19391
19392 Ok(Value::Float(dissonance.min(1.0)))
19393 });
19394
19395 define(interp, "emotional_fingerprint", Some(1), |interp, args| {
19398 let h = get_hologram_values(&args[0], interp)?;
19399
19400 let repr = format!(
19402 "hologram:v{:.4}:a{:.4}:d{:.4}:auth{:.4}:c{:.4}",
19403 h.0, h.1, h.2, h.3, h.4
19404 );
19405
19406 let hash = blake3::hash(repr.as_bytes());
19408 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
19409 });
19410
19411 define(interp, "emotional_morph", Some(3), |interp, args| {
19414 let h1 = get_hologram_values(&args[0], interp)?;
19415 let h2 = get_hologram_values(&args[1], interp)?;
19416 let t = match &args[2] {
19417 Value::Float(f) => f.max(0.0).min(1.0),
19418 Value::Int(i) => (*i as f64).max(0.0).min(1.0),
19419 _ => {
19420 return Err(RuntimeError::new(
19421 "emotional_morph() requires numeric t value",
19422 ))
19423 }
19424 };
19425
19426 let mut result = std::collections::HashMap::new();
19427 result.insert(
19428 "valence".to_string(),
19429 Value::Float(h1.0 + (h2.0 - h1.0) * t),
19430 );
19431 result.insert(
19432 "arousal".to_string(),
19433 Value::Float(h1.1 + (h2.1 - h1.1) * t),
19434 );
19435 result.insert(
19436 "dominance".to_string(),
19437 Value::Float(h1.2 + (h2.2 - h1.2) * t),
19438 );
19439 result.insert(
19440 "authenticity".to_string(),
19441 Value::Float(h1.3 + (h2.3 - h1.3) * t),
19442 );
19443 result.insert(
19444 "certainty".to_string(),
19445 Value::Float(h1.4 + (h2.4 - h1.4) * t),
19446 );
19447
19448 Ok(Value::Map(Rc::new(RefCell::new(result))))
19449 });
19450
19451 define(interp, "cultural_emotion", Some(2), |_, args| {
19457 let emotion = match &args[0] {
19458 Value::Affective { affect, .. } => affect.emotion.clone(),
19459 Value::String(s) => match s.as_str() {
19460 "joy" => Some(RuntimeEmotion::Joy),
19461 "sadness" => Some(RuntimeEmotion::Sadness),
19462 "anger" => Some(RuntimeEmotion::Anger),
19463 "fear" => Some(RuntimeEmotion::Fear),
19464 "surprise" => Some(RuntimeEmotion::Surprise),
19465 "love" => Some(RuntimeEmotion::Love),
19466 _ => None,
19467 },
19468 _ => None,
19469 };
19470
19471 let culture = match &args[1] {
19472 Value::String(s) => s.to_lowercase(),
19473 _ => {
19474 return Err(RuntimeError::new(
19475 "cultural_emotion() requires string culture",
19476 ))
19477 }
19478 };
19479
19480 let result = match (emotion, culture.as_str()) {
19481 (Some(RuntimeEmotion::Joy), "japanese" | "ja") => create_cultural_entry(
19483 "木漏れ日",
19484 "komorebi",
19485 "sunlight filtering through leaves - peaceful joy",
19486 ),
19487 (Some(RuntimeEmotion::Sadness), "japanese" | "ja") => create_cultural_entry(
19488 "物の哀れ",
19489 "mono no aware",
19490 "the pathos of things - bittersweet awareness of impermanence",
19491 ),
19492 (Some(RuntimeEmotion::Love), "japanese" | "ja") => create_cultural_entry(
19493 "甘え",
19494 "amae",
19495 "indulgent dependence on another's benevolence",
19496 ),
19497 (Some(RuntimeEmotion::Fear), "japanese" | "ja") => create_cultural_entry(
19498 "空気を読む",
19499 "kuuki wo yomu",
19500 "anxiety about reading the room",
19501 ),
19502
19503 (Some(RuntimeEmotion::Sadness), "portuguese" | "pt") => create_cultural_entry(
19505 "saudade",
19506 "saudade",
19507 "melancholic longing for something or someone absent",
19508 ),
19509 (Some(RuntimeEmotion::Joy), "portuguese" | "pt") => {
19510 create_cultural_entry("alegria", "alegria", "exuberant collective joy")
19511 }
19512
19513 (Some(RuntimeEmotion::Joy), "german" | "de") => create_cultural_entry(
19515 "Schadenfreude",
19516 "schadenfreude",
19517 "pleasure derived from another's misfortune",
19518 ),
19519 (Some(RuntimeEmotion::Sadness), "german" | "de") => create_cultural_entry(
19520 "Weltschmerz",
19521 "weltschmerz",
19522 "world-weariness, melancholy about the world's state",
19523 ),
19524 (Some(RuntimeEmotion::Fear), "german" | "de") => create_cultural_entry(
19525 "Torschlusspanik",
19526 "torschlusspanik",
19527 "fear of diminishing opportunities with age",
19528 ),
19529
19530 (Some(RuntimeEmotion::Joy), "danish" | "da") => {
19532 create_cultural_entry("hygge", "hygge", "cozy contentment and conviviality")
19533 }
19534
19535 (Some(RuntimeEmotion::Joy), "arabic" | "ar") => {
19537 create_cultural_entry("طرب", "tarab", "musical ecstasy, enchantment through art")
19538 }
19539 (Some(RuntimeEmotion::Love), "arabic" | "ar") => {
19540 create_cultural_entry("هوى", "hawa", "passionate, sometimes irrational love")
19541 }
19542
19543 (Some(RuntimeEmotion::Sadness), "korean" | "ko") => create_cultural_entry(
19545 "한",
19546 "han",
19547 "collective grief and resentment from historical suffering",
19548 ),
19549 (Some(RuntimeEmotion::Joy), "korean" | "ko") => create_cultural_entry(
19550 "정",
19551 "jeong",
19552 "deep affection and attachment formed over time",
19553 ),
19554
19555 (Some(RuntimeEmotion::Sadness), "russian" | "ru") => {
19557 create_cultural_entry("тоска", "toska", "spiritual anguish without specific cause")
19558 }
19559
19560 (Some(RuntimeEmotion::Love), "hindi" | "hi") => {
19562 create_cultural_entry("विरह", "viraha", "longing for an absent beloved")
19563 }
19564
19565 (Some(RuntimeEmotion::Anger), "finnish" | "fi") => {
19567 create_cultural_entry("sisu", "sisu", "stoic determination and grit in adversity")
19568 }
19569
19570 (Some(e), _) => {
19572 let name = match e {
19573 RuntimeEmotion::Joy => "joy",
19574 RuntimeEmotion::Sadness => "sadness",
19575 RuntimeEmotion::Anger => "anger",
19576 RuntimeEmotion::Fear => "fear",
19577 RuntimeEmotion::Surprise => "surprise",
19578 RuntimeEmotion::Love => "love",
19579 };
19580 create_cultural_entry(name, name, "universal emotion")
19581 }
19582 (None, _) => create_cultural_entry("none", "none", "no emotion"),
19583 };
19584
19585 Ok(result)
19586 });
19587
19588 define(interp, "list_cultural_emotions", Some(1), |_, args| {
19590 let culture = match &args[0] {
19591 Value::String(s) => s.to_lowercase(),
19592 _ => {
19593 return Err(RuntimeError::new(
19594 "list_cultural_emotions() requires string culture",
19595 ))
19596 }
19597 };
19598
19599 let emotions: Vec<(&str, &str, &str)> = match culture.as_str() {
19600 "japanese" | "ja" => vec![
19601 ("木漏れ日", "komorebi", "sunlight through leaves"),
19602 ("物の哀れ", "mono no aware", "pathos of things"),
19603 ("甘え", "amae", "indulgent dependence"),
19604 ("侘寂", "wabi-sabi", "beauty in imperfection"),
19605 ("生きがい", "ikigai", "reason for being"),
19606 ],
19607 "german" | "de" => vec![
19608 ("Schadenfreude", "schadenfreude", "joy at misfortune"),
19609 ("Weltschmerz", "weltschmerz", "world-weariness"),
19610 ("Torschlusspanik", "torschlusspanik", "gate-closing panic"),
19611 ("Sehnsucht", "sehnsucht", "deep longing"),
19612 ("Wanderlust", "wanderlust", "desire to travel"),
19613 ],
19614 "portuguese" | "pt" => vec![
19615 ("saudade", "saudade", "melancholic longing"),
19616 ("alegria", "alegria", "exuberant joy"),
19617 ("desabafar", "desabafar", "emotional unburdening"),
19618 ],
19619 "danish" | "da" => vec![("hygge", "hygge", "cozy contentment")],
19620 "korean" | "ko" => vec![
19621 ("한", "han", "collective grief"),
19622 ("정", "jeong", "deep affection"),
19623 ("눈치", "nunchi", "situational awareness"),
19624 ],
19625 "arabic" | "ar" => vec![
19626 ("طرب", "tarab", "musical ecstasy"),
19627 ("هوى", "hawa", "passionate love"),
19628 ("صبر", "sabr", "patient perseverance"),
19629 ],
19630 "russian" | "ru" => vec![
19631 ("тоска", "toska", "spiritual anguish"),
19632 ("пошлость", "poshlost", "spiritual vulgarity"),
19633 ],
19634 "finnish" | "fi" => vec![("sisu", "sisu", "stoic determination")],
19635 "hindi" | "hi" => vec![
19636 ("विरह", "viraha", "longing for beloved"),
19637 ("जुगाड़", "jugaad", "creative improvisation"),
19638 ],
19639 _ => vec![
19640 ("joy", "joy", "universal happiness"),
19641 ("sadness", "sadness", "universal sorrow"),
19642 ("anger", "anger", "universal frustration"),
19643 ("fear", "fear", "universal anxiety"),
19644 ("surprise", "surprise", "universal amazement"),
19645 ("love", "love", "universal affection"),
19646 ],
19647 };
19648
19649 let result: Vec<Value> = emotions
19650 .iter()
19651 .map(|(native, romanized, meaning)| create_cultural_entry(native, romanized, meaning))
19652 .collect();
19653
19654 Ok(Value::Array(Rc::new(RefCell::new(result))))
19655 });
19656
19657 define(interp, "hologram_info", Some(0), |_, _| {
19659 let mut info = std::collections::HashMap::new();
19660
19661 info.insert(
19662 "dimensions".to_string(),
19663 Value::Array(Rc::new(RefCell::new(vec![
19664 Value::String(Rc::new("valence".to_string())),
19665 Value::String(Rc::new("arousal".to_string())),
19666 Value::String(Rc::new("dominance".to_string())),
19667 Value::String(Rc::new("authenticity".to_string())),
19668 Value::String(Rc::new("certainty".to_string())),
19669 Value::String(Rc::new("emotion_index".to_string())),
19670 ]))),
19671 );
19672
19673 info.insert(
19674 "supported_cultures".to_string(),
19675 Value::Array(Rc::new(RefCell::new(vec![
19676 Value::String(Rc::new("japanese".to_string())),
19677 Value::String(Rc::new("german".to_string())),
19678 Value::String(Rc::new("portuguese".to_string())),
19679 Value::String(Rc::new("danish".to_string())),
19680 Value::String(Rc::new("korean".to_string())),
19681 Value::String(Rc::new("arabic".to_string())),
19682 Value::String(Rc::new("russian".to_string())),
19683 Value::String(Rc::new("finnish".to_string())),
19684 Value::String(Rc::new("hindi".to_string())),
19685 ]))),
19686 );
19687
19688 let funcs = vec![
19689 "emotional_hologram",
19690 "emotional_distance",
19691 "emotional_similarity",
19692 "emotional_dissonance",
19693 "emotional_fingerprint",
19694 "emotional_morph",
19695 "cultural_emotion",
19696 "list_cultural_emotions",
19697 "hologram_info",
19698 ];
19699 let func_values: Vec<Value> = funcs
19700 .iter()
19701 .map(|s| Value::String(Rc::new(s.to_string())))
19702 .collect();
19703 info.insert(
19704 "functions".to_string(),
19705 Value::Array(Rc::new(RefCell::new(func_values))),
19706 );
19707
19708 Ok(Value::Map(Rc::new(RefCell::new(info))))
19709 });
19710}
19711
19712fn get_hologram_values(
19714 val: &Value,
19715 _interp: &mut Interpreter,
19716) -> Result<(f64, f64, f64, f64, f64), RuntimeError> {
19717 use crate::interpreter::{
19718 RuntimeAffect, RuntimeConfidence, RuntimeFormality, RuntimeIntensity, RuntimeSentiment,
19719 };
19720
19721 let affect = match val {
19722 Value::Affective { affect, .. } => affect.clone(),
19723 Value::Map(m) => {
19724 let map = m.borrow();
19726 let v = extract_float(&map, "valence").unwrap_or(0.0);
19727 let a = extract_float(&map, "arousal").unwrap_or(0.5);
19728 let d = extract_float(&map, "dominance").unwrap_or(0.5);
19729 let auth = extract_float(&map, "authenticity").unwrap_or(0.9);
19730 let c = extract_float(&map, "certainty").unwrap_or(0.5);
19731 return Ok((v, a, d, auth, c));
19732 }
19733 _ => RuntimeAffect {
19734 sentiment: None,
19735 sarcasm: false,
19736 intensity: None,
19737 formality: None,
19738 emotion: None,
19739 confidence: None,
19740 },
19741 };
19742
19743 let v = match affect.sentiment {
19744 Some(RuntimeSentiment::Positive) => 1.0,
19745 Some(RuntimeSentiment::Negative) => -1.0,
19746 _ => 0.0,
19747 };
19748 let a = match affect.intensity {
19749 Some(RuntimeIntensity::Down) => 0.25,
19750 Some(RuntimeIntensity::Up) => 0.75,
19751 Some(RuntimeIntensity::Max) => 1.0,
19752 None => 0.5,
19753 };
19754 let d = match affect.formality {
19755 Some(RuntimeFormality::Informal) => 0.25,
19756 Some(RuntimeFormality::Formal) => 0.85,
19757 None => 0.5,
19758 };
19759 let auth = if affect.sarcasm { -0.9 } else { 0.9 };
19760 let c = match affect.confidence {
19761 Some(RuntimeConfidence::Low) => 0.2,
19762 Some(RuntimeConfidence::High) => 0.9,
19763 _ => 0.5,
19764 };
19765
19766 Ok((v, a, d, auth, c))
19767}
19768
19769fn extract_float(map: &std::collections::HashMap<String, Value>, key: &str) -> Option<f64> {
19770 match map.get(key) {
19771 Some(Value::Float(f)) => Some(*f),
19772 Some(Value::Int(i)) => Some(*i as f64),
19773 _ => None,
19774 }
19775}
19776
19777fn create_cultural_entry(native: &str, romanized: &str, meaning: &str) -> Value {
19778 let mut entry = std::collections::HashMap::new();
19779 entry.insert(
19780 "native".to_string(),
19781 Value::String(Rc::new(native.to_string())),
19782 );
19783 entry.insert(
19784 "romanized".to_string(),
19785 Value::String(Rc::new(romanized.to_string())),
19786 );
19787 entry.insert(
19788 "meaning".to_string(),
19789 Value::String(Rc::new(meaning.to_string())),
19790 );
19791 Value::Map(Rc::new(RefCell::new(entry)))
19792}
19793
19794fn register_experimental_crypto(interp: &mut Interpreter) {
19799 define(interp, "commit", Some(1), |_, args| {
19805 let value_str = match &args[0] {
19806 Value::String(s) => s.to_string(),
19807 other => format!("{:?}", other),
19808 };
19809
19810 let mut nonce = [0u8; 32];
19812 getrandom::getrandom(&mut nonce)
19813 .map_err(|e| RuntimeError::new(format!("commit() random failed: {}", e)))?;
19814 let nonce_hex = hex::encode(&nonce);
19815
19816 let commitment_input = format!("{}:{}", value_str, nonce_hex);
19818 let commitment = blake3::hash(commitment_input.as_bytes());
19819
19820 let mut result = std::collections::HashMap::new();
19821 result.insert(
19822 "commitment".to_string(),
19823 Value::String(Rc::new(commitment.to_hex().to_string())),
19824 );
19825 result.insert("nonce".to_string(), Value::String(Rc::new(nonce_hex)));
19826 result.insert("value".to_string(), args[0].clone());
19827
19828 Ok(Value::Map(Rc::new(RefCell::new(result))))
19829 });
19830
19831 define(interp, "verify_commitment", Some(3), |_, args| {
19833 let commitment = match &args[0] {
19834 Value::String(s) => s.to_string(),
19835 _ => {
19836 return Err(RuntimeError::new(
19837 "verify_commitment() requires string commitment",
19838 ))
19839 }
19840 };
19841 let value_str = match &args[1] {
19842 Value::String(s) => s.to_string(),
19843 other => format!("{:?}", other),
19844 };
19845 let nonce = match &args[2] {
19846 Value::String(s) => s.to_string(),
19847 _ => {
19848 return Err(RuntimeError::new(
19849 "verify_commitment() requires string nonce",
19850 ))
19851 }
19852 };
19853
19854 let commitment_input = format!("{}:{}", value_str, nonce);
19856 let computed = blake3::hash(commitment_input.as_bytes());
19857
19858 Ok(Value::Bool(computed.to_hex().to_string() == commitment))
19859 });
19860
19861 define(interp, "secret_split", Some(3), |_, args| {
19867 let secret = match &args[0] {
19868 Value::String(s) => s.as_bytes().to_vec(),
19869 Value::Array(arr) => {
19870 let borrowed = arr.borrow();
19871 borrowed
19872 .iter()
19873 .filter_map(|v| {
19874 if let Value::Int(i) = v {
19875 Some(*i as u8)
19876 } else {
19877 None
19878 }
19879 })
19880 .collect()
19881 }
19882 _ => {
19883 return Err(RuntimeError::new(
19884 "secret_split() requires string or byte array",
19885 ))
19886 }
19887 };
19888
19889 let threshold = match &args[1] {
19890 Value::Int(n) => *n as usize,
19891 _ => {
19892 return Err(RuntimeError::new(
19893 "secret_split() requires integer threshold",
19894 ))
19895 }
19896 };
19897
19898 let num_shares = match &args[2] {
19899 Value::Int(n) => *n as usize,
19900 _ => {
19901 return Err(RuntimeError::new(
19902 "secret_split() requires integer num_shares",
19903 ))
19904 }
19905 };
19906
19907 if threshold < 2 {
19908 return Err(RuntimeError::new("secret_split() threshold must be >= 2"));
19909 }
19910 if num_shares < threshold {
19911 return Err(RuntimeError::new(
19912 "secret_split() num_shares must be >= threshold",
19913 ));
19914 }
19915 if num_shares > 255 {
19916 return Err(RuntimeError::new("secret_split() max 255 shares"));
19917 }
19918
19919 let mut rng = rand::thread_rng();
19922 let mut shares: Vec<Vec<u8>> = (0..num_shares)
19923 .map(|_| Vec::with_capacity(secret.len() + 1))
19924 .collect();
19925
19926 for (i, share) in shares.iter_mut().enumerate() {
19928 share.push((i + 1) as u8);
19929 }
19930
19931 for &byte in &secret {
19933 let mut coefficients: Vec<u8> = vec![byte];
19936 for _ in 1..threshold {
19937 coefficients.push(rng.gen());
19938 }
19939
19940 for (i, share) in shares.iter_mut().enumerate() {
19942 let x = (i + 1) as u8;
19943 let y = eval_polynomial_gf256(&coefficients, x);
19944 share.push(y);
19945 }
19946 }
19947
19948 let share_values: Vec<Value> = shares
19950 .iter()
19951 .map(|share| {
19952 let hex = hex::encode(share);
19953 Value::String(Rc::new(hex))
19954 })
19955 .collect();
19956
19957 let mut result = std::collections::HashMap::new();
19958 result.insert(
19959 "shares".to_string(),
19960 Value::Array(Rc::new(RefCell::new(share_values))),
19961 );
19962 result.insert("threshold".to_string(), Value::Int(threshold as i64));
19963 result.insert("total".to_string(), Value::Int(num_shares as i64));
19964
19965 Ok(Value::Map(Rc::new(RefCell::new(result))))
19966 });
19967
19968 define(interp, "secret_recover", Some(1), |_, args| {
19970 let shares: Vec<Vec<u8>> = match &args[0] {
19971 Value::Array(arr) => {
19972 let borrowed = arr.borrow();
19973 borrowed
19974 .iter()
19975 .filter_map(|v| {
19976 if let Value::String(s) = v {
19977 hex::decode(s.as_str()).ok()
19978 } else {
19979 None
19980 }
19981 })
19982 .collect()
19983 }
19984 _ => {
19985 return Err(RuntimeError::new(
19986 "secret_recover() requires array of share strings",
19987 ))
19988 }
19989 };
19990
19991 if shares.is_empty() {
19992 return Err(RuntimeError::new(
19993 "secret_recover() requires at least one share",
19994 ));
19995 }
19996
19997 let share_len = shares[0].len();
19998 if share_len < 2 {
19999 return Err(RuntimeError::new("secret_recover() invalid share format"));
20000 }
20001
20002 let mut secret = Vec::with_capacity(share_len - 1);
20004
20005 for byte_idx in 1..share_len {
20006 let points: Vec<(u8, u8)> = shares
20008 .iter()
20009 .map(|share| (share[0], share[byte_idx]))
20010 .collect();
20011
20012 let recovered_byte = lagrange_interpolate_gf256(&points, 0);
20014 secret.push(recovered_byte);
20015 }
20016
20017 match String::from_utf8(secret.clone()) {
20019 Ok(s) => Ok(Value::String(Rc::new(s))),
20020 Err(_) => {
20021 let byte_values: Vec<Value> =
20023 secret.iter().map(|&b| Value::Int(b as i64)).collect();
20024 Ok(Value::Array(Rc::new(RefCell::new(byte_values))))
20025 }
20026 }
20027 });
20028
20029 define(interp, "council_split", Some(2), |_, args| {
20035 let secret = match &args[0] {
20036 Value::String(s) => s.as_bytes().to_vec(),
20037 _ => return Err(RuntimeError::new("council_split() requires string secret")),
20038 };
20039
20040 let num_elders = match &args[1] {
20041 Value::Int(n) => *n as usize,
20042 _ => {
20043 return Err(RuntimeError::new(
20044 "council_split() requires integer num_elders",
20045 ))
20046 }
20047 };
20048
20049 if num_elders < 3 {
20050 return Err(RuntimeError::new(
20051 "council_split() requires at least 3 elders",
20052 ));
20053 }
20054
20055 let threshold = (num_elders / 2) + 1;
20057
20058 let mut rng = rand::thread_rng();
20060 let mut shares: Vec<Vec<u8>> = (0..num_elders)
20061 .map(|_| Vec::with_capacity(secret.len() + 1))
20062 .collect();
20063
20064 for (i, share) in shares.iter_mut().enumerate() {
20065 share.push((i + 1) as u8);
20066 }
20067
20068 for &byte in &secret {
20069 let mut coefficients: Vec<u8> = vec![byte];
20070 for _ in 1..threshold {
20071 coefficients.push(rng.gen());
20072 }
20073
20074 for (i, share) in shares.iter_mut().enumerate() {
20075 let x = (i + 1) as u8;
20076 let y = eval_polynomial_gf256(&coefficients, x);
20077 share.push(y);
20078 }
20079 }
20080
20081 let share_values: Vec<Value> = shares
20082 .iter()
20083 .map(|share| Value::String(Rc::new(hex::encode(share))))
20084 .collect();
20085
20086 let mut result = std::collections::HashMap::new();
20087 result.insert(
20088 "shares".to_string(),
20089 Value::Array(Rc::new(RefCell::new(share_values))),
20090 );
20091 result.insert("threshold".to_string(), Value::Int(threshold as i64));
20092 result.insert("total".to_string(), Value::Int(num_elders as i64));
20093 result.insert(
20094 "model".to_string(),
20095 Value::String(Rc::new("ubuntu".to_string())),
20096 );
20097 result.insert(
20098 "philosophy".to_string(),
20099 Value::String(Rc::new(
20100 "I am because we are - majority consensus required".to_string(),
20101 )),
20102 );
20103
20104 Ok(Value::Map(Rc::new(RefCell::new(result))))
20105 });
20106
20107 define(interp, "witness_chain", Some(2), |_, args| {
20110 let statement = match &args[0] {
20111 Value::String(s) => s.to_string(),
20112 _ => {
20113 return Err(RuntimeError::new(
20114 "witness_chain() requires string statement",
20115 ))
20116 }
20117 };
20118
20119 let witnesses: Vec<String> = match &args[1] {
20120 Value::Array(arr) => {
20121 let borrowed = arr.borrow();
20122 borrowed
20123 .iter()
20124 .filter_map(|v| {
20125 if let Value::String(s) = v {
20126 Some(s.to_string())
20127 } else {
20128 None
20129 }
20130 })
20131 .collect()
20132 }
20133 _ => {
20134 return Err(RuntimeError::new(
20135 "witness_chain() requires array of witness names",
20136 ))
20137 }
20138 };
20139
20140 if witnesses.is_empty() {
20141 return Err(RuntimeError::new(
20142 "witness_chain() requires at least one witness",
20143 ));
20144 }
20145
20146 let mut chain = Vec::new();
20148 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
20149
20150 for (i, witness) in witnesses.iter().enumerate() {
20151 let attestation = format!("{}:attests:{}", witness, prev_hash);
20152 let hash = blake3::hash(attestation.as_bytes()).to_hex().to_string();
20153
20154 let mut link = std::collections::HashMap::new();
20155 link.insert(
20156 "witness".to_string(),
20157 Value::String(Rc::new(witness.clone())),
20158 );
20159 link.insert("position".to_string(), Value::Int((i + 1) as i64));
20160 link.insert(
20161 "attests_to".to_string(),
20162 Value::String(Rc::new(prev_hash.clone())),
20163 );
20164 link.insert(
20165 "signature".to_string(),
20166 Value::String(Rc::new(hash.clone())),
20167 );
20168
20169 chain.push(Value::Map(Rc::new(RefCell::new(link))));
20170 prev_hash = hash;
20171 }
20172
20173 let mut result = std::collections::HashMap::new();
20174 result.insert("statement".to_string(), Value::String(Rc::new(statement)));
20175 result.insert(
20176 "chain".to_string(),
20177 Value::Array(Rc::new(RefCell::new(chain))),
20178 );
20179 result.insert("final_seal".to_string(), Value::String(Rc::new(prev_hash)));
20180 result.insert(
20181 "model".to_string(),
20182 Value::String(Rc::new("isnad".to_string())),
20183 );
20184 result.insert(
20185 "philosophy".to_string(),
20186 Value::String(Rc::new(
20187 "Chain of reliable transmitters - each witness validates the previous".to_string(),
20188 )),
20189 );
20190
20191 Ok(Value::Map(Rc::new(RefCell::new(result))))
20192 });
20193
20194 define(interp, "verify_witness_chain", Some(1), |_, args| {
20196 let chain_map = match &args[0] {
20197 Value::Map(m) => m.borrow(),
20198 _ => {
20199 return Err(RuntimeError::new(
20200 "verify_witness_chain() requires chain map",
20201 ))
20202 }
20203 };
20204
20205 let statement = match chain_map.get("statement") {
20206 Some(Value::String(s)) => s.to_string(),
20207 _ => {
20208 return Err(RuntimeError::new(
20209 "verify_witness_chain() invalid chain format",
20210 ))
20211 }
20212 };
20213
20214 let chain = match chain_map.get("chain") {
20215 Some(Value::Array(arr)) => arr.borrow().clone(),
20216 _ => {
20217 return Err(RuntimeError::new(
20218 "verify_witness_chain() invalid chain format",
20219 ))
20220 }
20221 };
20222
20223 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
20224
20225 for link_val in chain.iter() {
20226 if let Value::Map(link_map) = link_val {
20227 let link = link_map.borrow();
20228 let witness = match link.get("witness") {
20229 Some(Value::String(s)) => s.to_string(),
20230 _ => return Ok(Value::Bool(false)),
20231 };
20232 let attests_to = match link.get("attests_to") {
20233 Some(Value::String(s)) => s.to_string(),
20234 _ => return Ok(Value::Bool(false)),
20235 };
20236 let signature = match link.get("signature") {
20237 Some(Value::String(s)) => s.to_string(),
20238 _ => return Ok(Value::Bool(false)),
20239 };
20240
20241 if attests_to != prev_hash {
20243 return Ok(Value::Bool(false));
20244 }
20245
20246 let expected = format!("{}:attests:{}", witness, prev_hash);
20247 let computed = blake3::hash(expected.as_bytes()).to_hex().to_string();
20248
20249 if computed != signature {
20250 return Ok(Value::Bool(false));
20251 }
20252
20253 prev_hash = signature;
20254 } else {
20255 return Ok(Value::Bool(false));
20256 }
20257 }
20258
20259 Ok(Value::Bool(true))
20260 });
20261
20262 define(interp, "experimental_crypto_info", Some(0), |_, _| {
20264 let mut info = std::collections::HashMap::new();
20265
20266 info.insert(
20267 "commitment_functions".to_string(),
20268 Value::Array(Rc::new(RefCell::new(vec![
20269 Value::String(Rc::new("commit".to_string())),
20270 Value::String(Rc::new("verify_commitment".to_string())),
20271 ]))),
20272 );
20273
20274 info.insert(
20275 "threshold_functions".to_string(),
20276 Value::Array(Rc::new(RefCell::new(vec![
20277 Value::String(Rc::new("secret_split".to_string())),
20278 Value::String(Rc::new("secret_recover".to_string())),
20279 ]))),
20280 );
20281
20282 info.insert(
20283 "cultural_ceremonies".to_string(),
20284 Value::Array(Rc::new(RefCell::new(vec![
20285 Value::String(Rc::new(
20286 "council_split (Ubuntu - African consensus)".to_string(),
20287 )),
20288 Value::String(Rc::new(
20289 "witness_chain (Isnad - Islamic transmission)".to_string(),
20290 )),
20291 ]))),
20292 );
20293
20294 Ok(Value::Map(Rc::new(RefCell::new(info))))
20295 });
20296}
20297
20298fn eval_polynomial_gf256(coefficients: &[u8], x: u8) -> u8 {
20300 let mut result: u8 = 0;
20301 let mut x_power: u8 = 1;
20302
20303 for &coef in coefficients {
20304 result ^= gf256_mul(coef, x_power);
20305 x_power = gf256_mul(x_power, x);
20306 }
20307
20308 result
20309}
20310
20311fn lagrange_interpolate_gf256(points: &[(u8, u8)], _x: u8) -> u8 {
20313 let mut result: u8 = 0;
20314
20315 for (i, &(xi, yi)) in points.iter().enumerate() {
20316 let mut numerator: u8 = 1;
20317 let mut denominator: u8 = 1;
20318
20319 for (j, &(xj, _)) in points.iter().enumerate() {
20320 if i != j {
20321 numerator = gf256_mul(numerator, xj);
20323 denominator = gf256_mul(denominator, xi ^ xj);
20325 }
20326 }
20327
20328 let term = gf256_mul(yi, gf256_mul(numerator, gf256_inv(denominator)));
20330 result ^= term;
20331 }
20332
20333 result
20334}
20335
20336fn gf256_mul(mut a: u8, mut b: u8) -> u8 {
20338 let mut result: u8 = 0;
20339 let modulus: u16 = 0x11b; while b != 0 {
20342 if b & 1 != 0 {
20343 result ^= a;
20344 }
20345 let high_bit = (a & 0x80) != 0;
20346 a <<= 1;
20347 if high_bit {
20348 a ^= (modulus & 0xff) as u8;
20349 }
20350 b >>= 1;
20351 }
20352
20353 result
20354}
20355
20356fn gf256_inv(a: u8) -> u8 {
20358 if a == 0 {
20359 return 0;
20360 }
20361
20362 let mut result = a;
20364 for _ in 0..6 {
20365 result = gf256_mul(result, result);
20366 result = gf256_mul(result, a);
20367 }
20368 gf256_mul(result, result)
20369}
20370
20371fn register_multibase(interp: &mut Interpreter) {
20390 define(interp, "to_vigesimal", Some(1), |_, args| {
20394 let n = match &args[0] {
20395 Value::Int(n) => *n,
20396 _ => return Err(RuntimeError::new("to_vigesimal() requires integer")),
20397 };
20398
20399 let result = to_base_string(n.unsigned_abs(), 20, false);
20400 let prefix = if n < 0 { "-0v" } else { "0v" };
20401 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20402 });
20403
20404 define(interp, "from_vigesimal", Some(1), |_, args| {
20405 let s = match &args[0] {
20406 Value::String(s) => s.to_string(),
20407 _ => return Err(RuntimeError::new("from_vigesimal() requires string")),
20408 };
20409
20410 let (negative, clean) = parse_base_prefix(&s, "0v");
20411 let value = from_base_string(&clean, 20)?;
20412 Ok(Value::Int(if negative {
20413 -(value as i64)
20414 } else {
20415 value as i64
20416 }))
20417 });
20418
20419 define(interp, "to_sexagesimal", Some(1), |_, args| {
20423 let n = match &args[0] {
20424 Value::Int(n) => *n,
20425 _ => return Err(RuntimeError::new("to_sexagesimal() requires integer")),
20426 };
20427
20428 let negative = n < 0;
20429 let mut value = n.unsigned_abs();
20430 let mut parts = Vec::new();
20431
20432 if value == 0 {
20433 parts.push("0".to_string());
20434 } else {
20435 while value > 0 {
20436 parts.push(format!("{}", value % 60));
20437 value /= 60;
20438 }
20439 parts.reverse();
20440 }
20441
20442 let prefix = if negative { "-0s" } else { "0s" };
20443 Ok(Value::String(Rc::new(format!(
20444 "{}[{}]",
20445 prefix,
20446 parts.join(":")
20447 ))))
20448 });
20449
20450 define(interp, "from_sexagesimal", Some(1), |_, args| {
20451 let s = match &args[0] {
20452 Value::String(s) => s.to_string(),
20453 _ => return Err(RuntimeError::new("from_sexagesimal() requires string")),
20454 };
20455
20456 let negative = s.starts_with('-');
20457 let clean = s
20458 .trim_start_matches('-')
20459 .trim_start_matches("0s")
20460 .trim_start_matches('[')
20461 .trim_end_matches(']');
20462
20463 let mut result: i64 = 0;
20464 for part in clean.split(':') {
20465 let digit: i64 = part
20466 .trim()
20467 .parse()
20468 .map_err(|_| RuntimeError::new(format!("Invalid sexagesimal digit: {}", part)))?;
20469 if digit < 0 || digit >= 60 {
20470 return Err(RuntimeError::new(format!(
20471 "Sexagesimal digit out of range: {}",
20472 digit
20473 )));
20474 }
20475 result = result * 60 + digit;
20476 }
20477
20478 Ok(Value::Int(if negative { -result } else { result }))
20479 });
20480
20481 define(interp, "to_duodecimal", Some(1), |_, args| {
20485 let n = match &args[0] {
20486 Value::Int(n) => *n,
20487 _ => return Err(RuntimeError::new("to_duodecimal() requires integer")),
20488 };
20489
20490 let result = to_base_string_custom(n.unsigned_abs(), 12, "0123456789XE");
20491 let prefix = if n < 0 { "-0z" } else { "0z" };
20492 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20493 });
20494
20495 define(interp, "from_duodecimal", Some(1), |_, args| {
20496 let s = match &args[0] {
20497 Value::String(s) => s.to_string(),
20498 _ => return Err(RuntimeError::new("from_duodecimal() requires string")),
20499 };
20500
20501 let (negative, clean) = parse_base_prefix(&s, "0z");
20502 let value = from_base_string_custom(&clean.to_uppercase(), "0123456789XE")?;
20503 Ok(Value::Int(if negative {
20504 -(value as i64)
20505 } else {
20506 value as i64
20507 }))
20508 });
20509
20510 define(interp, "to_base", Some(2), |_, args| {
20513 let n = match &args[0] {
20514 Value::Int(n) => *n,
20515 _ => return Err(RuntimeError::new("to_base() requires integer")),
20516 };
20517 let base = match &args[1] {
20518 Value::Int(b) => *b as u64,
20519 _ => return Err(RuntimeError::new("to_base() requires integer base")),
20520 };
20521
20522 if base < 2 || base > 36 {
20523 return Err(RuntimeError::new("to_base() base must be 2-36"));
20524 }
20525
20526 let result = to_base_string(n.unsigned_abs(), base, false);
20527 let prefix = if n < 0 { "-" } else { "" };
20528 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20529 });
20530
20531 define(interp, "from_base", Some(2), |_, args| {
20532 let s = match &args[0] {
20533 Value::String(s) => s.to_string(),
20534 _ => return Err(RuntimeError::new("from_base() requires string")),
20535 };
20536 let base = match &args[1] {
20537 Value::Int(b) => *b as u64,
20538 _ => return Err(RuntimeError::new("from_base() requires integer base")),
20539 };
20540
20541 if base < 2 || base > 36 {
20542 return Err(RuntimeError::new("from_base() base must be 2-36"));
20543 }
20544
20545 let negative = s.starts_with('-');
20546 let clean = s.trim_start_matches('-');
20547 let value = from_base_string(clean, base)?;
20548 Ok(Value::Int(if negative {
20549 -(value as i64)
20550 } else {
20551 value as i64
20552 }))
20553 });
20554
20555 const BASE58_ALPHABET: &str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
20560
20561 define(interp, "base58_encode", Some(1), |_, args| {
20562 let bytes: Vec<u8> = match &args[0] {
20563 Value::String(s) => s.as_bytes().to_vec(),
20564 Value::Array(arr) => arr
20565 .borrow()
20566 .iter()
20567 .filter_map(|v| {
20568 if let Value::Int(i) = v {
20569 Some(*i as u8)
20570 } else {
20571 None
20572 }
20573 })
20574 .collect(),
20575 _ => {
20576 return Err(RuntimeError::new(
20577 "base58_encode() requires string or byte array",
20578 ))
20579 }
20580 };
20581
20582 let leading_zeros = bytes.iter().take_while(|&&b| b == 0).count();
20584
20585 let mut result = Vec::new();
20587 let mut num: Vec<u8> = bytes.clone();
20588
20589 while !num.is_empty() && !num.iter().all(|&b| b == 0) {
20590 let mut remainder = 0u32;
20591 let mut new_num = Vec::new();
20592
20593 for &byte in &num {
20594 let acc = (remainder << 8) + byte as u32;
20595 let digit = acc / 58;
20596 remainder = acc % 58;
20597
20598 if !new_num.is_empty() || digit > 0 {
20599 new_num.push(digit as u8);
20600 }
20601 }
20602
20603 result.push(BASE58_ALPHABET.chars().nth(remainder as usize).unwrap());
20604 num = new_num;
20605 }
20606
20607 for _ in 0..leading_zeros {
20609 result.push('1');
20610 }
20611
20612 result.reverse();
20613 Ok(Value::String(Rc::new(result.into_iter().collect())))
20614 });
20615
20616 define(interp, "base58_decode", Some(1), |_, args| {
20617 let s = match &args[0] {
20618 Value::String(s) => s.to_string(),
20619 _ => return Err(RuntimeError::new("base58_decode() requires string")),
20620 };
20621
20622 let leading_ones = s.chars().take_while(|&c| c == '1').count();
20624
20625 let mut num: Vec<u8> = Vec::new();
20627
20628 for c in s.chars() {
20629 let digit = BASE58_ALPHABET
20630 .find(c)
20631 .ok_or_else(|| RuntimeError::new(format!("Invalid base58 character: {}", c)))?;
20632
20633 let mut carry = digit as u32;
20634 for byte in num.iter_mut().rev() {
20635 let acc = (*byte as u32) * 58 + carry;
20636 *byte = (acc & 0xff) as u8;
20637 carry = acc >> 8;
20638 }
20639
20640 while carry > 0 {
20641 num.insert(0, (carry & 0xff) as u8);
20642 carry >>= 8;
20643 }
20644 }
20645
20646 let mut result = vec![0u8; leading_ones];
20648 result.extend(num);
20649
20650 Ok(Value::String(Rc::new(hex::encode(&result))))
20652 });
20653
20654 const BASE32_ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
20658
20659 define(interp, "base32_encode", Some(1), |_, args| {
20660 let bytes: Vec<u8> = match &args[0] {
20661 Value::String(s) => s.as_bytes().to_vec(),
20662 Value::Array(arr) => arr
20663 .borrow()
20664 .iter()
20665 .filter_map(|v| {
20666 if let Value::Int(i) = v {
20667 Some(*i as u8)
20668 } else {
20669 None
20670 }
20671 })
20672 .collect(),
20673 _ => {
20674 return Err(RuntimeError::new(
20675 "base32_encode() requires string or byte array",
20676 ))
20677 }
20678 };
20679
20680 let mut result = String::new();
20681 let mut buffer: u64 = 0;
20682 let mut bits = 0;
20683
20684 for byte in bytes {
20685 buffer = (buffer << 8) | byte as u64;
20686 bits += 8;
20687
20688 while bits >= 5 {
20689 bits -= 5;
20690 let idx = ((buffer >> bits) & 0x1f) as usize;
20691 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
20692 }
20693 }
20694
20695 if bits > 0 {
20696 let idx = ((buffer << (5 - bits)) & 0x1f) as usize;
20697 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
20698 }
20699
20700 while result.len() % 8 != 0 {
20702 result.push('=');
20703 }
20704
20705 Ok(Value::String(Rc::new(result)))
20706 });
20707
20708 define(interp, "base32_decode", Some(1), |_, args| {
20709 let s = match &args[0] {
20710 Value::String(s) => s.to_uppercase().replace('=', ""),
20711 _ => return Err(RuntimeError::new("base32_decode() requires string")),
20712 };
20713
20714 let mut result = Vec::new();
20715 let mut buffer: u64 = 0;
20716 let mut bits = 0;
20717
20718 for c in s.chars() {
20719 let digit = BASE32_ALPHABET
20720 .find(c)
20721 .ok_or_else(|| RuntimeError::new(format!("Invalid base32 character: {}", c)))?;
20722
20723 buffer = (buffer << 5) | digit as u64;
20724 bits += 5;
20725
20726 if bits >= 8 {
20727 bits -= 8;
20728 result.push((buffer >> bits) as u8);
20729 buffer &= (1 << bits) - 1;
20730 }
20731 }
20732
20733 Ok(Value::String(Rc::new(hex::encode(&result))))
20734 });
20735
20736 define(interp, "sacred_numbers", Some(1), |_, args| {
20740 let culture = match &args[0] {
20741 Value::String(s) => s.to_lowercase(),
20742 _ => {
20743 return Err(RuntimeError::new(
20744 "sacred_numbers() requires string culture",
20745 ))
20746 }
20747 };
20748
20749 let numbers: Vec<(i64, &str)> = match culture.as_str() {
20750 "mayan" | "maya" => vec![
20751 (13, "Sacred cycle - Tzolkin calendar"),
20752 (20, "Base of vigesimal system - human digits"),
20753 (52, "Calendar round - 52 years"),
20754 (260, "Tzolkin sacred calendar days"),
20755 (365, "Haab solar calendar days"),
20756 (400, "Baktun - 20×20 years"),
20757 ],
20758 "babylonian" | "mesopotamian" => vec![
20759 (12, "Months, hours - celestial division"),
20760 (60, "Sexagesimal base - minutes, seconds, degrees"),
20761 (360, "Circle degrees - 6×60"),
20762 (3600, "Sar - 60×60, large count"),
20763 (7, "Planets visible to naked eye"),
20764 ],
20765 "chinese" | "zh" => vec![
20766 (8, "八 (bā) - prosperity, wealth (sounds like 發)"),
20767 (9, "九 (jiǔ) - longevity (sounds like 久)"),
20768 (6, "六 (liù) - smooth, flowing"),
20769 (2, "二 (èr) - pairs, harmony"),
20770 (4, "四 (sì) - AVOID - sounds like death (死)"),
20771 (5, "五 (wǔ) - five elements"),
20772 (12, "十二 - zodiac animals"),
20773 ],
20774 "japanese" | "ja" => vec![
20775 (7, "七 (nana) - lucky, seven gods of fortune"),
20776 (8, "八 (hachi) - prosperity, expansion"),
20777 (3, "三 (san) - completeness"),
20778 (5, "五 (go) - five elements"),
20779 (4, "四 (shi) - AVOID - sounds like death (死)"),
20780 (9, "九 (ku) - AVOID - sounds like suffering (苦)"),
20781 ],
20782 "hebrew" | "jewish" => vec![
20783 (7, "Shabbat, creation days, menorah branches"),
20784 (18, "Chai (חי) - life - gematria of ח(8) + י(10)"),
20785 (40, "Transformation - flood, Sinai, wilderness"),
20786 (12, "Tribes of Israel"),
20787 (613, "Mitzvot - commandments"),
20788 (26, "Gematria of YHWH"),
20789 ],
20790 "islamic" | "arabic" | "ar" => vec![
20791 (5, "Pillars of Islam, daily prayers"),
20792 (7, "Heavens, circumambulation of Kaaba"),
20793 (40, "Age of prophethood, days of repentance"),
20794 (99, "Names of Allah"),
20795 (786, "Abjad value of Bismillah"),
20796 ],
20797 "hindu" | "indian" | "hi" => vec![
20798 (108, "Sacred beads, Upanishads, sun's distance"),
20799 (7, "Chakras (main), rishis, sacred rivers"),
20800 (3, "Trimurti - Brahma, Vishnu, Shiva"),
20801 (4, "Vedas, yugas, varnas"),
20802 (9, "Planets (navagraha), durga forms"),
20803 (1008, "Names of Vishnu"),
20804 ],
20805 "greek" | "pythagorean" => vec![
20806 (1, "Monad - unity, source"),
20807 (2, "Dyad - duality, diversity"),
20808 (3, "Triad - harmony, completion"),
20809 (4, "Tetrad - solidity, earth"),
20810 (7, "Heptad - perfection"),
20811 (10, "Decad - tetractys, divine"),
20812 (12, "Olympian gods"),
20813 ],
20814 "celtic" | "irish" => vec![
20815 (3, "Triple goddess, triquetra"),
20816 (5, "Elements including spirit"),
20817 (9, "Triple threes - sacred completion"),
20818 (13, "Lunar months"),
20819 (17, "St. Patrick's Day"),
20820 (20, "Vigesimal counting"),
20821 ],
20822 _ => vec![
20823 (1, "Unity"),
20824 (7, "Widely considered lucky"),
20825 (12, "Dozen - practical division"),
20826 (13, "Often considered unlucky in West"),
20827 ],
20828 };
20829
20830 let result: Vec<Value> = numbers
20831 .iter()
20832 .map(|(n, meaning)| {
20833 let mut entry = std::collections::HashMap::new();
20834 entry.insert("number".to_string(), Value::Int(*n));
20835 entry.insert(
20836 "meaning".to_string(),
20837 Value::String(Rc::new(meaning.to_string())),
20838 );
20839 Value::Map(Rc::new(RefCell::new(entry)))
20840 })
20841 .collect();
20842
20843 Ok(Value::Array(Rc::new(RefCell::new(result))))
20844 });
20845
20846 define(interp, "is_sacred", Some(2), |_, args| {
20848 let n = match &args[0] {
20849 Value::Int(n) => *n,
20850 _ => return Err(RuntimeError::new("is_sacred() requires integer")),
20851 };
20852 let culture = match &args[1] {
20853 Value::String(s) => s.to_lowercase(),
20854 _ => return Err(RuntimeError::new("is_sacred() requires string culture")),
20855 };
20856
20857 let sacred = match culture.as_str() {
20858 "mayan" | "maya" => vec![13, 20, 52, 260, 365, 400],
20859 "babylonian" | "mesopotamian" => vec![12, 60, 360, 3600, 7],
20860 "chinese" | "zh" => vec![8, 9, 6, 2, 5, 12],
20861 "japanese" | "ja" => vec![7, 8, 3, 5],
20862 "hebrew" | "jewish" => vec![7, 18, 40, 12, 613, 26],
20863 "islamic" | "arabic" | "ar" => vec![5, 7, 40, 99, 786],
20864 "hindu" | "indian" | "hi" => vec![108, 7, 3, 4, 9, 1008],
20865 "greek" | "pythagorean" => vec![1, 2, 3, 4, 7, 10, 12],
20866 "celtic" | "irish" => vec![3, 5, 9, 13, 17, 20],
20867 _ => vec![7, 12],
20868 };
20869
20870 Ok(Value::Bool(sacred.contains(&n)))
20871 });
20872
20873 define(interp, "is_unlucky", Some(2), |_, args| {
20875 let n = match &args[0] {
20876 Value::Int(n) => *n,
20877 _ => return Err(RuntimeError::new("is_unlucky() requires integer")),
20878 };
20879 let culture = match &args[1] {
20880 Value::String(s) => s.to_lowercase(),
20881 _ => return Err(RuntimeError::new("is_unlucky() requires string culture")),
20882 };
20883
20884 let unlucky = match culture.as_str() {
20885 "chinese" | "zh" => vec![4], "japanese" | "ja" => vec![4, 9], "western" | "en" => vec![13], "italian" | "it" => vec![17], _ => vec![],
20890 };
20891
20892 Ok(Value::Bool(unlucky.contains(&n)))
20893 });
20894
20895 define(interp, "number_meaning", Some(2), |_, args| {
20897 let n = match &args[0] {
20898 Value::Int(n) => *n,
20899 _ => return Err(RuntimeError::new("number_meaning() requires integer")),
20900 };
20901 let culture = match &args[1] {
20902 Value::String(s) => s.to_lowercase(),
20903 _ => {
20904 return Err(RuntimeError::new(
20905 "number_meaning() requires string culture",
20906 ))
20907 }
20908 };
20909
20910 let meaning = match (n, culture.as_str()) {
20911 (8, "chinese" | "zh") => "八 (bā) - Most auspicious, sounds like 發 (prosperity)",
20913 (4, "chinese" | "zh") => "四 (sì) - Unlucky, sounds like 死 (death)",
20914 (9, "chinese" | "zh") => "九 (jiǔ) - Longevity, sounds like 久 (long-lasting)",
20915 (6, "chinese" | "zh") => "六 (liù) - Smooth and well-off",
20916 (2, "chinese" | "zh") => "二 (èr) - Good things come in pairs",
20917
20918 (7, "japanese" | "ja") => "七 (nana) - Lucky, seven gods of fortune",
20920 (8, "japanese" | "ja") => "八 (hachi) - Lucky, represents spreading prosperity",
20921 (4, "japanese" | "ja") => "四 (shi) - Unlucky, homophone of death",
20922 (9, "japanese" | "ja") => "九 (ku) - Unlucky, homophone of suffering",
20923
20924 (18, "hebrew" | "jewish") => "Chai (חי) - Life itself, most auspicious",
20926 (7, "hebrew" | "jewish") => "Divine completion - Shabbat, menorah, creation",
20927 (40, "hebrew" | "jewish") => "Transformation - flood, Sinai, wilderness years",
20928 (613, "hebrew" | "jewish") => "Taryag - total number of mitzvot (commandments)",
20929
20930 (13, "mayan" | "maya") => "Sacred Tzolkin cycle, celestial layers",
20932 (20, "mayan" | "maya") => "Vigesimal base - fingers and toes, uinal",
20933 (260, "mayan" | "maya") => "Tzolkin calendar - 13 × 20 sacred days",
20934
20935 (60, "babylonian" | "mesopotamian") => {
20937 "Sexagesimal base - divisible by 2,3,4,5,6,10,12,15,20,30"
20938 }
20939 (360, "babylonian" | "mesopotamian") => "Circle degrees - celestial observation",
20940
20941 (108, "hindu" | "indian" | "hi") => {
20943 "Sacred completeness - mala beads, Upanishads, sun ratio"
20944 }
20945 (3, "hindu" | "indian" | "hi") => "Trimurti - Brahma, Vishnu, Shiva",
20946 (9, "hindu" | "indian" | "hi") => "Navagraha (9 planets), Durga's 9 forms",
20947
20948 (99, "islamic" | "arabic" | "ar") => "Asma ul-Husna - 99 names of Allah",
20950 (5, "islamic" | "arabic" | "ar") => "Five pillars of Islam",
20951 (786, "islamic" | "arabic" | "ar") => "Abjad numerology of Bismillah",
20952
20953 (10, "greek" | "pythagorean") => "Tetractys - 1+2+3+4, perfect number",
20955 (7, "greek" | "pythagorean") => "Heptad - virgin number, Athena's number",
20956
20957 _ => "No specific cultural meaning recorded",
20958 };
20959
20960 let mut result = std::collections::HashMap::new();
20961 result.insert("number".to_string(), Value::Int(n));
20962 result.insert("culture".to_string(), Value::String(Rc::new(culture)));
20963 result.insert(
20964 "meaning".to_string(),
20965 Value::String(Rc::new(meaning.to_string())),
20966 );
20967
20968 Ok(Value::Map(Rc::new(RefCell::new(result))))
20969 });
20970
20971 define(interp, "to_babylonian_time", Some(1), |_, args| {
20975 let seconds = match &args[0] {
20976 Value::Int(n) => *n,
20977 Value::Float(f) => *f as i64,
20978 _ => return Err(RuntimeError::new("to_babylonian_time() requires number")),
20979 };
20980
20981 let hours = seconds / 3600;
20982 let mins = (seconds % 3600) / 60;
20983 let secs = seconds % 60;
20984
20985 Ok(Value::String(Rc::new(format!(
20986 "0s[{}:{}:{}]",
20987 hours, mins, secs
20988 ))))
20989 });
20990
20991 define(interp, "from_babylonian_time", Some(1), |_, args| {
20993 let s = match &args[0] {
20994 Value::String(s) => s.to_string(),
20995 _ => return Err(RuntimeError::new("from_babylonian_time() requires string")),
20996 };
20997
20998 let clean = s
20999 .trim_start_matches("0s")
21000 .trim_start_matches('[')
21001 .trim_end_matches(']');
21002
21003 let parts: Vec<i64> = clean
21004 .split(':')
21005 .map(|p| p.trim().parse::<i64>().unwrap_or(0))
21006 .collect();
21007
21008 let seconds = match parts.len() {
21009 1 => parts[0],
21010 2 => parts[0] * 60 + parts[1],
21011 3 => parts[0] * 3600 + parts[1] * 60 + parts[2],
21012 _ => return Err(RuntimeError::new("Invalid Babylonian time format")),
21013 };
21014
21015 Ok(Value::Int(seconds))
21016 });
21017
21018 define(interp, "vigesimal_shares", Some(3), |_, args| {
21022 let secret = match &args[0] {
21023 Value::String(s) => s.as_bytes().to_vec(),
21024 _ => {
21025 return Err(RuntimeError::new(
21026 "vigesimal_shares() requires string secret",
21027 ))
21028 }
21029 };
21030
21031 let threshold = match &args[1] {
21032 Value::Int(n) => *n as usize,
21033 _ => {
21034 return Err(RuntimeError::new(
21035 "vigesimal_shares() requires integer threshold",
21036 ))
21037 }
21038 };
21039
21040 let num_shares = match &args[2] {
21041 Value::Int(n) => *n as usize,
21042 _ => {
21043 return Err(RuntimeError::new(
21044 "vigesimal_shares() requires integer num_shares",
21045 ))
21046 }
21047 };
21048
21049 if threshold < 2 || num_shares < threshold || num_shares > 20 {
21050 return Err(RuntimeError::new(
21051 "vigesimal_shares() requires 2 <= threshold <= num_shares <= 20 (Mayan limit)",
21052 ));
21053 }
21054
21055 let mut rng = rand::thread_rng();
21057 let mut shares: Vec<Vec<u8>> = (0..num_shares)
21058 .map(|_| Vec::with_capacity(secret.len() + 1))
21059 .collect();
21060
21061 for (i, share) in shares.iter_mut().enumerate() {
21062 share.push((i + 1) as u8);
21063 }
21064
21065 for &byte in &secret {
21066 let mut coefficients: Vec<u8> = vec![byte];
21067 for _ in 1..threshold {
21068 coefficients.push(rng.gen());
21069 }
21070
21071 for (i, share) in shares.iter_mut().enumerate() {
21072 let x = (i + 1) as u8;
21073 let y = eval_polynomial_gf256(&coefficients, x);
21074 share.push(y);
21075 }
21076 }
21077
21078 let share_values: Vec<Value> = shares
21080 .iter()
21081 .enumerate()
21082 .map(|(i, share)| {
21083 let mut entry = std::collections::HashMap::new();
21084
21085 let mut vig_parts: Vec<String> = Vec::new();
21087 for &byte in share {
21088 vig_parts.push(to_base_string(byte as u64, 20, true));
21089 }
21090
21091 entry.insert("index".to_string(), Value::Int((i + 1) as i64));
21092 entry.insert(
21093 "vigesimal".to_string(),
21094 Value::String(Rc::new(format!("0v{}", vig_parts.join(".")))),
21095 );
21096 entry.insert(
21097 "hex".to_string(),
21098 Value::String(Rc::new(hex::encode(share))),
21099 );
21100
21101 Value::Map(Rc::new(RefCell::new(entry)))
21102 })
21103 .collect();
21104
21105 let mut result = std::collections::HashMap::new();
21106 result.insert(
21107 "shares".to_string(),
21108 Value::Array(Rc::new(RefCell::new(share_values))),
21109 );
21110 result.insert("threshold".to_string(), Value::Int(threshold as i64));
21111 result.insert("total".to_string(), Value::Int(num_shares as i64));
21112 result.insert(
21113 "base".to_string(),
21114 Value::String(Rc::new("vigesimal (Mayan base-20)".to_string())),
21115 );
21116
21117 Ok(Value::Map(Rc::new(RefCell::new(result))))
21118 });
21119
21120 define(interp, "multibase_info", Some(0), |_, _| {
21122 let mut info = std::collections::HashMap::new();
21123
21124 let bases = vec![
21125 ("binary", 2, "0b", "Modern computing"),
21126 ("octal", 8, "0o", "Unix, historical computing"),
21127 ("decimal", 10, "", "Indo-Arabic global standard"),
21128 (
21129 "duodecimal",
21130 12,
21131 "0z",
21132 "Dozen system - time, music, measurement",
21133 ),
21134 ("hexadecimal", 16, "0x", "Computing, colors, addresses"),
21135 (
21136 "vigesimal",
21137 20,
21138 "0v",
21139 "Mayan, Celtic, Basque - human digits",
21140 ),
21141 (
21142 "sexagesimal",
21143 60,
21144 "0s",
21145 "Babylonian - time, angles, astronomy",
21146 ),
21147 ];
21148
21149 let base_list: Vec<Value> = bases
21150 .iter()
21151 .map(|(name, base, prefix, desc)| {
21152 let mut entry = std::collections::HashMap::new();
21153 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21154 entry.insert("base".to_string(), Value::Int(*base as i64));
21155 entry.insert(
21156 "prefix".to_string(),
21157 Value::String(Rc::new(prefix.to_string())),
21158 );
21159 entry.insert(
21160 "origin".to_string(),
21161 Value::String(Rc::new(desc.to_string())),
21162 );
21163 Value::Map(Rc::new(RefCell::new(entry)))
21164 })
21165 .collect();
21166
21167 info.insert(
21168 "numeral_systems".to_string(),
21169 Value::Array(Rc::new(RefCell::new(base_list))),
21170 );
21171
21172 let encodings = vec!["base58 (Bitcoin)", "base32 (RFC 4648)", "base64 (standard)"];
21173 let enc_list: Vec<Value> = encodings
21174 .iter()
21175 .map(|s| Value::String(Rc::new(s.to_string())))
21176 .collect();
21177 info.insert(
21178 "special_encodings".to_string(),
21179 Value::Array(Rc::new(RefCell::new(enc_list))),
21180 );
21181
21182 let cultures = vec![
21183 "mayan",
21184 "babylonian",
21185 "chinese",
21186 "japanese",
21187 "hebrew",
21188 "islamic",
21189 "hindu",
21190 "greek",
21191 "celtic",
21192 ];
21193 let cult_list: Vec<Value> = cultures
21194 .iter()
21195 .map(|s| Value::String(Rc::new(s.to_string())))
21196 .collect();
21197 info.insert(
21198 "supported_cultures".to_string(),
21199 Value::Array(Rc::new(RefCell::new(cult_list))),
21200 );
21201
21202 Ok(Value::Map(Rc::new(RefCell::new(info))))
21203 });
21204}
21205
21206fn to_base_string(mut n: u64, base: u64, pad_to_two: bool) -> String {
21209 const DIGITS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
21210
21211 if n == 0 {
21212 return if pad_to_two {
21213 "00".to_string()
21214 } else {
21215 "0".to_string()
21216 };
21217 }
21218
21219 let mut result = Vec::new();
21220 while n > 0 {
21221 result.push(DIGITS[(n % base) as usize] as char);
21222 n /= base;
21223 }
21224
21225 if pad_to_two && result.len() < 2 {
21226 result.push('0');
21227 }
21228
21229 result.reverse();
21230 result.into_iter().collect()
21231}
21232
21233fn to_base_string_custom(mut n: u64, base: u64, digits: &str) -> String {
21234 if n == 0 {
21235 return digits.chars().next().unwrap().to_string();
21236 }
21237
21238 let mut result = Vec::new();
21239 let digit_chars: Vec<char> = digits.chars().collect();
21240
21241 while n > 0 {
21242 result.push(digit_chars[(n % base) as usize]);
21243 n /= base;
21244 }
21245
21246 result.reverse();
21247 result.into_iter().collect()
21248}
21249
21250fn from_base_string(s: &str, base: u64) -> Result<u64, RuntimeError> {
21251 let mut result: u64 = 0;
21252
21253 for c in s.chars() {
21254 let digit = match c {
21255 '0'..='9' => c as u64 - '0' as u64,
21256 'A'..='Z' => c as u64 - 'A' as u64 + 10,
21257 'a'..='z' => c as u64 - 'a' as u64 + 10,
21258 _ => return Err(RuntimeError::new(format!("Invalid digit: {}", c))),
21259 };
21260
21261 if digit >= base {
21262 return Err(RuntimeError::new(format!(
21263 "Digit {} out of range for base {}",
21264 c, base
21265 )));
21266 }
21267
21268 result = result
21269 .checked_mul(base)
21270 .and_then(|r| r.checked_add(digit))
21271 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
21272 }
21273
21274 Ok(result)
21275}
21276
21277fn from_base_string_custom(s: &str, digits: &str) -> Result<u64, RuntimeError> {
21278 let base = digits.len() as u64;
21279 let mut result: u64 = 0;
21280
21281 for c in s.chars() {
21282 let digit = digits
21283 .find(c)
21284 .ok_or_else(|| RuntimeError::new(format!("Invalid digit: {}", c)))?
21285 as u64;
21286
21287 result = result
21288 .checked_mul(base)
21289 .and_then(|r| r.checked_add(digit))
21290 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
21291 }
21292
21293 Ok(result)
21294}
21295
21296fn parse_base_prefix(s: &str, prefix: &str) -> (bool, String) {
21297 let negative = s.starts_with('-');
21298 let clean = s
21299 .trim_start_matches('-')
21300 .trim_start_matches(prefix)
21301 .to_string();
21302 (negative, clean)
21303}
21304
21305fn register_audio(interp: &mut Interpreter) {
21333 define(interp, "tune", Some(3), |_, args| {
21340 let note = match &args[0] {
21341 Value::Int(n) => *n as f64, Value::Float(f) => *f, Value::String(s) => parse_note_name(s)?,
21344 _ => return Err(RuntimeError::new("tune() requires note number or name")),
21345 };
21346
21347 let system = match &args[1] {
21348 Value::String(s) => s.to_lowercase(),
21349 _ => return Err(RuntimeError::new("tune() requires tuning system name")),
21350 };
21351
21352 let root_freq = match &args[2] {
21353 Value::Float(f) => *f,
21354 Value::Int(i) => *i as f64,
21355 _ => return Err(RuntimeError::new("tune() requires root frequency")),
21356 };
21357
21358 let freq = match system.as_str() {
21359 "12tet" | "equal" | "western" => {
21360 root_freq * 2.0_f64.powf((note - 69.0) / 12.0)
21362 }
21363 "24tet" | "quarter" | "arabic" | "maqam" => {
21364 root_freq * 2.0_f64.powf((note - 69.0) / 24.0)
21366 }
21367 "just" | "pure" => {
21368 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21370 let octave = ((note - 69.0) / 12.0).floor();
21371 let ratio = just_intonation_ratio(interval as i32);
21372 root_freq * ratio * 2.0_f64.powf(octave)
21373 }
21374 "pythagorean" => {
21375 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21377 let octave = ((note - 69.0) / 12.0).floor();
21378 let ratio = pythagorean_ratio(interval as i32);
21379 root_freq * ratio * 2.0_f64.powf(octave)
21380 }
21381 "meantone" | "quarter_comma" => {
21382 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21384 let octave = ((note - 69.0) / 12.0).floor();
21385 let ratio = meantone_ratio(interval as i32);
21386 root_freq * ratio * 2.0_f64.powf(octave)
21387 }
21388 "53tet" | "turkish" | "persian" | "comma" => {
21389 root_freq * 2.0_f64.powf((note - 69.0) / 53.0)
21391 }
21392 "22shruti" | "shruti" | "indian" => {
21393 let shruti = (note - 69.0) % 22.0;
21395 let octave = ((note - 69.0) / 22.0).floor();
21396 let ratio = shruti_ratio(shruti as i32);
21397 root_freq * ratio * 2.0_f64.powf(octave)
21398 }
21399 "gamelan_pelog" | "pelog" => {
21400 let degree = ((note - 69.0) % 7.0 + 7.0) % 7.0;
21402 let octave = ((note - 69.0) / 7.0).floor();
21403 let ratio = pelog_ratio(degree as i32);
21404 root_freq * ratio * 2.0_f64.powf(octave)
21405 }
21406 "gamelan_slendro" | "slendro" => {
21407 let degree = ((note - 69.0) % 5.0 + 5.0) % 5.0;
21409 let octave = ((note - 69.0) / 5.0).floor();
21410 let ratio = slendro_ratio(degree as i32);
21411 root_freq * ratio * 2.0_f64.powf(octave)
21412 }
21413 "bohlen_pierce" | "bp" => {
21414 root_freq * 3.0_f64.powf((note - 69.0) / 13.0)
21416 }
21417 _ => {
21418 return Err(RuntimeError::new(format!(
21419 "Unknown tuning system: {}",
21420 system
21421 )))
21422 }
21423 };
21424
21425 Ok(Value::Float(freq))
21426 });
21427
21428 define(interp, "tuning_info", Some(1), |_, args| {
21430 let system = match &args[0] {
21431 Value::String(s) => s.to_lowercase(),
21432 _ => return Err(RuntimeError::new("tuning_info() requires string")),
21433 };
21434
21435 let (name, notes_per_octave, origin, description) = match system.as_str() {
21436 "12tet" | "equal" | "western" => (
21437 "12-TET", 12, "Western (18th century)",
21438 "Equal temperament - every semitone is exactly 2^(1/12). Universal but slightly impure."
21439 ),
21440 "24tet" | "quarter" | "arabic" | "maqam" => (
21441 "24-TET", 24, "Arabic/Turkish",
21442 "Quarter-tone system for maqam music. Enables neutral seconds and other microtones."
21443 ),
21444 "just" | "pure" => (
21445 "Just Intonation", 12, "Ancient (Ptolemy)",
21446 "Pure frequency ratios (3:2 fifth, 5:4 third). Beatless intervals but limited modulation."
21447 ),
21448 "pythagorean" => (
21449 "Pythagorean", 12, "Ancient Greece",
21450 "Built entirely from perfect fifths (3:2). Pure fifths but harsh thirds."
21451 ),
21452 "meantone" | "quarter_comma" => (
21453 "Quarter-Comma Meantone", 12, "Renaissance Europe",
21454 "Tempered fifths for pure major thirds. Beautiful in limited keys."
21455 ),
21456 "53tet" | "turkish" | "persian" | "comma" => (
21457 "53-TET", 53, "Turkish/Persian",
21458 "53 notes per octave. Closely approximates just intonation and allows maqam/dastgah."
21459 ),
21460 "22shruti" | "shruti" | "indian" => (
21461 "22-Shruti", 22, "Indian Classical",
21462 "Ancient Indian system. Each shruti is a 'microtone' - the smallest perceptible interval."
21463 ),
21464 "gamelan_pelog" | "pelog" => (
21465 "Pelog", 7, "Javanese Gamelan",
21466 "Heptatonic scale with unequal steps. Each gamelan has unique tuning - instruments are married."
21467 ),
21468 "gamelan_slendro" | "slendro" => (
21469 "Slendro", 5, "Javanese Gamelan",
21470 "Pentatonic scale, roughly equal steps (~240 cents). Each ensemble uniquely tuned."
21471 ),
21472 "bohlen_pierce" | "bp" => (
21473 "Bohlen-Pierce", 13, "Modern (1970s)",
21474 "Non-octave scale based on 3:1 tritave. Alien but mathematically beautiful."
21475 ),
21476 _ => return Err(RuntimeError::new(format!("Unknown tuning system: {}", system))),
21477 };
21478
21479 let mut info = std::collections::HashMap::new();
21480 info.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21481 info.insert("notes_per_octave".to_string(), Value::Int(notes_per_octave));
21482 info.insert(
21483 "origin".to_string(),
21484 Value::String(Rc::new(origin.to_string())),
21485 );
21486 info.insert(
21487 "description".to_string(),
21488 Value::String(Rc::new(description.to_string())),
21489 );
21490
21491 Ok(Value::Map(Rc::new(RefCell::new(info))))
21492 });
21493
21494 define(interp, "list_tuning_systems", Some(0), |_, _| {
21496 let systems = vec![
21497 ("12tet", "Western equal temperament", 12),
21498 ("24tet", "Arabic/Turkish quarter-tones", 24),
21499 ("just", "Pure ratio just intonation", 12),
21500 ("pythagorean", "Ancient Greek pure fifths", 12),
21501 ("meantone", "Renaissance quarter-comma", 12),
21502 ("53tet", "Turkish/Persian comma system", 53),
21503 ("22shruti", "Indian microtonal", 22),
21504 ("pelog", "Javanese gamelan 7-note", 7),
21505 ("slendro", "Javanese gamelan 5-note", 5),
21506 ("bohlen_pierce", "Non-octave tritave scale", 13),
21507 ];
21508
21509 let result: Vec<Value> = systems
21510 .iter()
21511 .map(|(name, desc, notes)| {
21512 let mut entry = std::collections::HashMap::new();
21513 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21514 entry.insert(
21515 "description".to_string(),
21516 Value::String(Rc::new(desc.to_string())),
21517 );
21518 entry.insert("notes_per_octave".to_string(), Value::Int(*notes));
21519 Value::Map(Rc::new(RefCell::new(entry)))
21520 })
21521 .collect();
21522
21523 Ok(Value::Array(Rc::new(RefCell::new(result))))
21524 });
21525
21526 define(interp, "sacred_freq", Some(1), |_, args| {
21532 let name = match &args[0] {
21533 Value::String(s) => s.to_lowercase(),
21534 _ => return Err(RuntimeError::new("sacred_freq() requires string")),
21535 };
21536
21537 let (freq, description) = match name.as_str() {
21538 "om" | "ॐ" | "aum" => (136.1, "Om - Earth year frequency (Cosmic Om)"),
21540 "earth_day" => (194.18, "Earth day - one rotation"),
21541 "earth_year" => (136.1, "Earth year - one orbit (Om frequency)"),
21542 "schumann" | "earth_resonance" => (
21543 7.83,
21544 "Schumann resonance - Earth's electromagnetic heartbeat",
21545 ),
21546
21547 "ut" | "do" | "396" => (396.0, "UT/DO - Liberating guilt and fear"),
21549 "re" | "417" => (417.0, "RE - Undoing situations, facilitating change"),
21550 "mi" | "528" => (528.0, "MI - Transformation, miracles, DNA repair"),
21551 "fa" | "639" => (639.0, "FA - Connecting relationships"),
21552 "sol" | "741" => (741.0, "SOL - Awakening intuition"),
21553 "la" | "852" => (852.0, "LA - Returning to spiritual order"),
21554 "si" | "963" => (963.0, "SI - Divine consciousness, enlightenment"),
21555 "174" => (174.0, "Solfeggio foundation - pain relief"),
21556 "285" => (285.0, "Solfeggio - healing tissue"),
21557
21558 "sun" | "☉" => (126.22, "Sun - ego, vitality, leadership"),
21560 "moon" | "☽" | "☾" => (210.42, "Moon - emotion, intuition, cycles"),
21561 "mercury" | "☿" => (141.27, "Mercury - communication, intellect"),
21562 "venus" | "♀" => (221.23, "Venus - love, beauty, harmony"),
21563 "mars" | "♂" => (144.72, "Mars - energy, action, courage"),
21564 "jupiter" | "♃" => (183.58, "Jupiter - expansion, wisdom, luck"),
21565 "saturn" | "♄" => (147.85, "Saturn - discipline, structure, time"),
21566
21567 "root" | "muladhara" => (256.0, "Root chakra - survival, grounding (C)"),
21569 "sacral" | "svadhisthana" => (288.0, "Sacral chakra - creativity, sexuality (D)"),
21570 "solar" | "manipura" => (320.0, "Solar plexus - will, power (E)"),
21571 "heart" | "anahata" => (341.3, "Heart chakra - love, compassion (F)"),
21572 "throat" | "vishuddha" => (384.0, "Throat chakra - expression, truth (G)"),
21573 "third_eye" | "ajna" => (426.7, "Third eye - intuition, insight (A)"),
21574 "crown" | "sahasrara" => (480.0, "Crown chakra - consciousness, unity (B)"),
21575
21576 "a440" | "iso" => (440.0, "ISO standard concert pitch (1955)"),
21578 "a432" | "verdi" => (
21579 432.0,
21580 "Verdi pitch - 'mathematically consistent with universe'",
21581 ),
21582 "a415" | "baroque" => (415.0, "Baroque pitch - period instrument standard"),
21583 "a466" | "chorton" => (466.0, "Choir pitch - high Baroque German"),
21584
21585 "delta" => (2.0, "Delta waves - deep sleep, healing (0.5-4 Hz)"),
21587 "theta" => (6.0, "Theta waves - meditation, creativity (4-8 Hz)"),
21588 "alpha" => (10.0, "Alpha waves - relaxation, calm focus (8-13 Hz)"),
21589 "beta" => (20.0, "Beta waves - alertness, concentration (13-30 Hz)"),
21590 "gamma" => (40.0, "Gamma waves - insight, peak performance (30-100 Hz)"),
21591
21592 _ => {
21593 return Err(RuntimeError::new(format!(
21594 "Unknown sacred frequency: {}",
21595 name
21596 )))
21597 }
21598 };
21599
21600 let mut result = std::collections::HashMap::new();
21601 result.insert("frequency".to_string(), Value::Float(freq));
21602 result.insert("name".to_string(), Value::String(Rc::new(name)));
21603 result.insert(
21604 "meaning".to_string(),
21605 Value::String(Rc::new(description.to_string())),
21606 );
21607
21608 Ok(Value::Map(Rc::new(RefCell::new(result))))
21609 });
21610
21611 define(interp, "solfeggio", Some(0), |_, _| {
21613 let frequencies = vec![
21614 (174.0, "Foundation", "Pain relief, security"),
21615 (285.0, "Quantum", "Healing tissue, safety"),
21616 (396.0, "UT", "Liberating guilt and fear"),
21617 (417.0, "RE", "Undoing situations, change"),
21618 (528.0, "MI", "Transformation, DNA repair, miracles"),
21619 (639.0, "FA", "Connecting relationships"),
21620 (741.0, "SOL", "Awakening intuition"),
21621 (852.0, "LA", "Spiritual order"),
21622 (963.0, "SI", "Divine consciousness"),
21623 ];
21624
21625 let result: Vec<Value> = frequencies
21626 .iter()
21627 .map(|(freq, name, meaning)| {
21628 let mut entry = std::collections::HashMap::new();
21629 entry.insert("frequency".to_string(), Value::Float(*freq));
21630 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21631 entry.insert(
21632 "meaning".to_string(),
21633 Value::String(Rc::new(meaning.to_string())),
21634 );
21635 Value::Map(Rc::new(RefCell::new(entry)))
21636 })
21637 .collect();
21638
21639 Ok(Value::Array(Rc::new(RefCell::new(result))))
21640 });
21641
21642 define(interp, "chakras", Some(0), |_, _| {
21644 let chakras = vec![
21645 (
21646 256.0,
21647 "Muladhara",
21648 "Root",
21649 "Red",
21650 "Survival, grounding, stability",
21651 ),
21652 (
21653 288.0,
21654 "Svadhisthana",
21655 "Sacral",
21656 "Orange",
21657 "Creativity, sexuality, emotion",
21658 ),
21659 (
21660 320.0,
21661 "Manipura",
21662 "Solar Plexus",
21663 "Yellow",
21664 "Will, power, self-esteem",
21665 ),
21666 (
21667 341.3,
21668 "Anahata",
21669 "Heart",
21670 "Green",
21671 "Love, compassion, connection",
21672 ),
21673 (
21674 384.0,
21675 "Vishuddha",
21676 "Throat",
21677 "Blue",
21678 "Expression, truth, communication",
21679 ),
21680 (
21681 426.7,
21682 "Ajna",
21683 "Third Eye",
21684 "Indigo",
21685 "Intuition, insight, wisdom",
21686 ),
21687 (
21688 480.0,
21689 "Sahasrara",
21690 "Crown",
21691 "Violet",
21692 "Consciousness, unity, transcendence",
21693 ),
21694 ];
21695
21696 let result: Vec<Value> = chakras
21697 .iter()
21698 .map(|(freq, sanskrit, english, color, meaning)| {
21699 let mut entry = std::collections::HashMap::new();
21700 entry.insert("frequency".to_string(), Value::Float(*freq));
21701 entry.insert(
21702 "sanskrit".to_string(),
21703 Value::String(Rc::new(sanskrit.to_string())),
21704 );
21705 entry.insert(
21706 "english".to_string(),
21707 Value::String(Rc::new(english.to_string())),
21708 );
21709 entry.insert(
21710 "color".to_string(),
21711 Value::String(Rc::new(color.to_string())),
21712 );
21713 entry.insert(
21714 "meaning".to_string(),
21715 Value::String(Rc::new(meaning.to_string())),
21716 );
21717 Value::Map(Rc::new(RefCell::new(entry)))
21718 })
21719 .collect();
21720
21721 Ok(Value::Array(Rc::new(RefCell::new(result))))
21722 });
21723
21724 define(interp, "sine", Some(3), |_, args| {
21732 generate_waveform(&args, |phase| phase.sin())
21733 });
21734
21735 define(interp, "square", Some(3), |_, args| {
21737 generate_waveform(&args, |phase| if phase.sin() >= 0.0 { 1.0 } else { -1.0 })
21738 });
21739
21740 define(interp, "sawtooth", Some(3), |_, args| {
21742 generate_waveform(&args, |phase| {
21743 let normalized = (phase / std::f64::consts::TAU).fract();
21744 2.0 * normalized - 1.0
21745 })
21746 });
21747
21748 define(interp, "triangle", Some(3), |_, args| {
21750 generate_waveform(&args, |phase| {
21751 let normalized = (phase / std::f64::consts::TAU).fract();
21752 if normalized < 0.5 {
21753 4.0 * normalized - 1.0
21754 } else {
21755 3.0 - 4.0 * normalized
21756 }
21757 })
21758 });
21759
21760 define(interp, "noise", Some(1), |_, args| {
21762 let samples = match &args[0] {
21763 Value::Int(n) => *n as usize,
21764 _ => return Err(RuntimeError::new("noise() requires integer sample count")),
21765 };
21766
21767 let mut rng = rand::thread_rng();
21768 let result: Vec<Value> = (0..samples)
21769 .map(|_| Value::Float(rng.gen::<f64>() * 2.0 - 1.0))
21770 .collect();
21771
21772 Ok(Value::Array(Rc::new(RefCell::new(result))))
21773 });
21774
21775 define(interp, "scale", Some(1), |_, args| {
21781 let name = match &args[0] {
21782 Value::String(s) => s.to_lowercase(),
21783 _ => return Err(RuntimeError::new("scale() requires string")),
21784 };
21785
21786 let (intervals, origin, description) = match name.as_str() {
21787 "major" | "ionian" => (
21789 vec![0, 2, 4, 5, 7, 9, 11],
21790 "Western",
21791 "Happy, bright, resolved",
21792 ),
21793 "minor" | "aeolian" => (
21794 vec![0, 2, 3, 5, 7, 8, 10],
21795 "Western",
21796 "Sad, dark, introspective",
21797 ),
21798 "dorian" => (
21799 vec![0, 2, 3, 5, 7, 9, 10],
21800 "Western/Jazz",
21801 "Minor with bright 6th",
21802 ),
21803 "phrygian" => (
21804 vec![0, 1, 3, 5, 7, 8, 10],
21805 "Western/Flamenco",
21806 "Spanish, exotic, tense",
21807 ),
21808 "lydian" => (
21809 vec![0, 2, 4, 6, 7, 9, 11],
21810 "Western",
21811 "Dreamy, floating, ethereal",
21812 ),
21813 "mixolydian" => (vec![0, 2, 4, 5, 7, 9, 10], "Western/Blues", "Bluesy major"),
21814 "locrian" => (vec![0, 1, 3, 5, 6, 8, 10], "Western", "Unstable, dissonant"),
21815
21816 "pentatonic_major" => (vec![0, 2, 4, 7, 9], "Universal", "No dissonance, universal"),
21818 "pentatonic_minor" => (vec![0, 3, 5, 7, 10], "Universal", "Blues foundation"),
21819
21820 "hirajoshi" => (vec![0, 2, 3, 7, 8], "Japanese", "Melancholic, mysterious"),
21822 "insen" => (vec![0, 1, 5, 7, 10], "Japanese", "Dark, zen, contemplative"),
21823 "iwato" => (vec![0, 1, 5, 6, 10], "Japanese", "Most dark and dissonant"),
21824 "kumoi" => (vec![0, 2, 3, 7, 9], "Japanese", "Gentle, peaceful"),
21825 "yo" => (vec![0, 2, 5, 7, 9], "Japanese", "Bright, folk, celebratory"),
21826
21827 "hijaz" => (
21829 vec![0, 1, 4, 5, 7, 8, 11],
21830 "Arabic",
21831 "Exotic, Middle Eastern",
21832 ),
21833 "bayati" => (
21834 vec![0, 1.5 as i32, 3, 5, 7, 8, 10],
21835 "Arabic",
21836 "Quarter-tone, soulful",
21837 ),
21838 "rast" => (
21839 vec![0, 2, 3.5 as i32, 5, 7, 9, 10.5 as i32],
21840 "Arabic",
21841 "Foundation maqam",
21842 ),
21843 "saba" => (
21844 vec![0, 1.5 as i32, 3, 4, 5, 8, 10],
21845 "Arabic",
21846 "Sad, spiritual",
21847 ),
21848
21849 "bhairav" => (
21851 vec![0, 1, 4, 5, 7, 8, 11],
21852 "Indian",
21853 "Morning raga, devotional",
21854 ),
21855 "yaman" | "kalyan" => (vec![0, 2, 4, 6, 7, 9, 11], "Indian", "Evening, romantic"),
21856 "bhairavi" => (
21857 vec![0, 1, 3, 5, 7, 8, 10],
21858 "Indian",
21859 "Concluding raga, devotional",
21860 ),
21861 "todi" => (vec![0, 1, 3, 6, 7, 8, 11], "Indian", "Serious, pathos"),
21862 "marwa" => (vec![0, 1, 4, 6, 7, 9, 11], "Indian", "Evening, longing"),
21863
21864 "blues" => (vec![0, 3, 5, 6, 7, 10], "American", "Blue notes, soul"),
21866
21867 "hungarian_minor" => (vec![0, 2, 3, 6, 7, 8, 11], "Hungarian", "Gypsy, dramatic"),
21869 "romanian" => (vec![0, 2, 3, 6, 7, 9, 10], "Romanian", "Folk, energetic"),
21870
21871 "ahava_raba" | "freygish" => (
21873 vec![0, 1, 4, 5, 7, 8, 10],
21874 "Jewish/Klezmer",
21875 "Cantorial, emotional",
21876 ),
21877 "mi_sheberach" => (vec![0, 2, 3, 6, 7, 9, 10], "Jewish", "Prayer mode"),
21878
21879 "gong" => (vec![0, 2, 4, 7, 9], "Chinese", "Palace mode, major-like"),
21881 "shang" => (vec![0, 2, 5, 7, 9], "Chinese", "Merchant mode"),
21882 "jue" => (vec![0, 3, 5, 7, 10], "Chinese", "Angle mode"),
21883 "zhi" => (vec![0, 2, 5, 7, 10], "Chinese", "Emblem mode"),
21884 "yu" => (vec![0, 3, 5, 8, 10], "Chinese", "Wings mode, minor-like"),
21885
21886 "pelog" => (
21888 vec![0, 1, 3, 7, 8],
21889 "Javanese",
21890 "7-note unequal temperament",
21891 ),
21892 "slendro" => (vec![0, 2, 5, 7, 9], "Javanese", "5-note roughly equal"),
21893
21894 "whole_tone" => (
21896 vec![0, 2, 4, 6, 8, 10],
21897 "Impressionist",
21898 "Dreamlike, no resolution",
21899 ),
21900 "chromatic" => (
21901 vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
21902 "Western",
21903 "All 12 notes",
21904 ),
21905 "diminished" => (vec![0, 2, 3, 5, 6, 8, 9, 11], "Jazz", "Symmetric, tense"),
21906 "augmented" => (vec![0, 3, 4, 7, 8, 11], "Jazz", "Symmetric, floating"),
21907
21908 _ => return Err(RuntimeError::new(format!("Unknown scale: {}", name))),
21909 };
21910
21911 let mut result = std::collections::HashMap::new();
21912 let intervals_values: Vec<Value> =
21913 intervals.iter().map(|&i| Value::Int(i as i64)).collect();
21914 result.insert(
21915 "intervals".to_string(),
21916 Value::Array(Rc::new(RefCell::new(intervals_values))),
21917 );
21918 result.insert(
21919 "origin".to_string(),
21920 Value::String(Rc::new(origin.to_string())),
21921 );
21922 result.insert(
21923 "character".to_string(),
21924 Value::String(Rc::new(description.to_string())),
21925 );
21926 result.insert("name".to_string(), Value::String(Rc::new(name)));
21927
21928 Ok(Value::Map(Rc::new(RefCell::new(result))))
21929 });
21930
21931 define(interp, "list_scales", Some(0), |_, _| {
21933 let mut cultures = std::collections::HashMap::new();
21934
21935 cultures.insert(
21936 "western".to_string(),
21937 Value::Array(Rc::new(RefCell::new(vec![
21938 Value::String(Rc::new("major".to_string())),
21939 Value::String(Rc::new("minor".to_string())),
21940 Value::String(Rc::new("dorian".to_string())),
21941 Value::String(Rc::new("phrygian".to_string())),
21942 Value::String(Rc::new("lydian".to_string())),
21943 Value::String(Rc::new("mixolydian".to_string())),
21944 Value::String(Rc::new("locrian".to_string())),
21945 ]))),
21946 );
21947
21948 cultures.insert(
21949 "japanese".to_string(),
21950 Value::Array(Rc::new(RefCell::new(vec![
21951 Value::String(Rc::new("hirajoshi".to_string())),
21952 Value::String(Rc::new("insen".to_string())),
21953 Value::String(Rc::new("iwato".to_string())),
21954 Value::String(Rc::new("kumoi".to_string())),
21955 Value::String(Rc::new("yo".to_string())),
21956 ]))),
21957 );
21958
21959 cultures.insert(
21960 "arabic".to_string(),
21961 Value::Array(Rc::new(RefCell::new(vec![
21962 Value::String(Rc::new("hijaz".to_string())),
21963 Value::String(Rc::new("bayati".to_string())),
21964 Value::String(Rc::new("rast".to_string())),
21965 Value::String(Rc::new("saba".to_string())),
21966 ]))),
21967 );
21968
21969 cultures.insert(
21970 "indian".to_string(),
21971 Value::Array(Rc::new(RefCell::new(vec![
21972 Value::String(Rc::new("bhairav".to_string())),
21973 Value::String(Rc::new("yaman".to_string())),
21974 Value::String(Rc::new("bhairavi".to_string())),
21975 Value::String(Rc::new("todi".to_string())),
21976 Value::String(Rc::new("marwa".to_string())),
21977 ]))),
21978 );
21979
21980 cultures.insert(
21981 "chinese".to_string(),
21982 Value::Array(Rc::new(RefCell::new(vec![
21983 Value::String(Rc::new("gong".to_string())),
21984 Value::String(Rc::new("shang".to_string())),
21985 Value::String(Rc::new("jue".to_string())),
21986 Value::String(Rc::new("zhi".to_string())),
21987 Value::String(Rc::new("yu".to_string())),
21988 ]))),
21989 );
21990
21991 cultures.insert(
21992 "jewish".to_string(),
21993 Value::Array(Rc::new(RefCell::new(vec![
21994 Value::String(Rc::new("ahava_raba".to_string())),
21995 Value::String(Rc::new("mi_sheberach".to_string())),
21996 ]))),
21997 );
21998
21999 cultures.insert(
22000 "indonesian".to_string(),
22001 Value::Array(Rc::new(RefCell::new(vec![
22002 Value::String(Rc::new("pelog".to_string())),
22003 Value::String(Rc::new("slendro".to_string())),
22004 ]))),
22005 );
22006
22007 Ok(Value::Map(Rc::new(RefCell::new(cultures))))
22008 });
22009
22010 define(interp, "interval_ratio", Some(2), |_, args| {
22016 let semitones = match &args[0] {
22017 Value::Int(n) => *n as f64,
22018 Value::Float(f) => *f,
22019 _ => return Err(RuntimeError::new("interval_ratio() requires number")),
22020 };
22021
22022 let tuning = match &args[1] {
22023 Value::String(s) => s.to_lowercase(),
22024 _ => return Err(RuntimeError::new("interval_ratio() requires tuning system")),
22025 };
22026
22027 let ratio = match tuning.as_str() {
22028 "12tet" | "equal" => 2.0_f64.powf(semitones / 12.0),
22029 "just" => just_intonation_ratio(semitones as i32),
22030 "pythagorean" => pythagorean_ratio(semitones as i32),
22031 _ => 2.0_f64.powf(semitones / 12.0),
22032 };
22033
22034 Ok(Value::Float(ratio))
22035 });
22036
22037 define(interp, "cents_between", Some(2), |_, args| {
22039 let f1 = match &args[0] {
22040 Value::Float(f) => *f,
22041 Value::Int(i) => *i as f64,
22042 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
22043 };
22044 let f2 = match &args[1] {
22045 Value::Float(f) => *f,
22046 Value::Int(i) => *i as f64,
22047 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
22048 };
22049
22050 let cents = 1200.0 * (f2 / f1).log2();
22051 Ok(Value::Float(cents))
22052 });
22053
22054 define(interp, "harmonic_series", Some(2), |_, args| {
22056 let fundamental = match &args[0] {
22057 Value::Float(f) => *f,
22058 Value::Int(i) => *i as f64,
22059 _ => return Err(RuntimeError::new("harmonic_series() requires frequency")),
22060 };
22061 let count = match &args[1] {
22062 Value::Int(n) => *n as usize,
22063 _ => return Err(RuntimeError::new("harmonic_series() requires count")),
22064 };
22065
22066 let harmonics: Vec<Value> = (1..=count)
22067 .map(|n| {
22068 let mut entry = std::collections::HashMap::new();
22069 entry.insert("harmonic".to_string(), Value::Int(n as i64));
22070 entry.insert(
22071 "frequency".to_string(),
22072 Value::Float(fundamental * n as f64),
22073 );
22074 entry.insert(
22075 "cents_from_root".to_string(),
22076 Value::Float(1200.0 * (n as f64).log2()),
22077 );
22078 Value::Map(Rc::new(RefCell::new(entry)))
22079 })
22080 .collect();
22081
22082 Ok(Value::Array(Rc::new(RefCell::new(harmonics))))
22083 });
22084
22085 define(interp, "audio_info", Some(0), |_, _| {
22090 let mut info = std::collections::HashMap::new();
22091
22092 info.insert(
22093 "tuning_systems".to_string(),
22094 Value::Array(Rc::new(RefCell::new(vec![
22095 Value::String(Rc::new(
22096 "12tet, 24tet, just, pythagorean, meantone".to_string(),
22097 )),
22098 Value::String(Rc::new(
22099 "53tet, 22shruti, pelog, slendro, bohlen_pierce".to_string(),
22100 )),
22101 ]))),
22102 );
22103
22104 info.insert(
22105 "waveforms".to_string(),
22106 Value::Array(Rc::new(RefCell::new(vec![
22107 Value::String(Rc::new("sine (∿)".to_string())),
22108 Value::String(Rc::new("square (⊓)".to_string())),
22109 Value::String(Rc::new("sawtooth (⋀)".to_string())),
22110 Value::String(Rc::new("triangle (△)".to_string())),
22111 Value::String(Rc::new("noise".to_string())),
22112 ]))),
22113 );
22114
22115 info.insert(
22116 "sacred_frequencies".to_string(),
22117 Value::Array(Rc::new(RefCell::new(vec![
22118 Value::String(Rc::new("om, solfeggio, chakras, planets".to_string())),
22119 Value::String(Rc::new(
22120 "schumann, brainwaves (delta/theta/alpha/beta/gamma)".to_string(),
22121 )),
22122 ]))),
22123 );
22124
22125 info.insert(
22126 "scale_cultures".to_string(),
22127 Value::Array(Rc::new(RefCell::new(vec![
22128 Value::String(Rc::new("western, japanese, arabic, indian".to_string())),
22129 Value::String(Rc::new("chinese, jewish, indonesian".to_string())),
22130 ]))),
22131 );
22132
22133 Ok(Value::Map(Rc::new(RefCell::new(info))))
22134 });
22135}
22136
22137fn parse_note_name(s: &str) -> Result<f64, RuntimeError> {
22140 let s = s.trim().to_uppercase();
22141 let (note, octave_offset) = if s.ends_with(|c: char| c.is_ascii_digit()) {
22142 let octave: i32 = s.chars().last().unwrap().to_digit(10).unwrap() as i32;
22143 let note_part = &s[..s.len() - 1];
22144 (note_part, (octave - 4) * 12) } else {
22146 (&s[..], 0)
22147 };
22148
22149 let semitone = match note {
22150 "C" => 0,
22151 "C#" | "DB" => 1,
22152 "D" => 2,
22153 "D#" | "EB" => 3,
22154 "E" => 4,
22155 "F" => 5,
22156 "F#" | "GB" => 6,
22157 "G" => 7,
22158 "G#" | "AB" => 8,
22159 "A" => 9,
22160 "A#" | "BB" => 10,
22161 "B" => 11,
22162 _ => return Err(RuntimeError::new(format!("Unknown note: {}", s))),
22163 };
22164
22165 Ok(69.0 + semitone as f64 - 9.0 + octave_offset as f64) }
22167
22168fn just_intonation_ratio(semitones: i32) -> f64 {
22169 match semitones.rem_euclid(12) {
22171 0 => 1.0, 1 => 16.0 / 15.0, 2 => 9.0 / 8.0, 3 => 6.0 / 5.0, 4 => 5.0 / 4.0, 5 => 4.0 / 3.0, 6 => 45.0 / 32.0, 7 => 3.0 / 2.0, 8 => 8.0 / 5.0, 9 => 5.0 / 3.0, 10 => 9.0 / 5.0, 11 => 15.0 / 8.0, _ => 1.0,
22184 }
22185}
22186
22187fn pythagorean_ratio(semitones: i32) -> f64 {
22188 match semitones.rem_euclid(12) {
22190 0 => 1.0,
22191 1 => 256.0 / 243.0,
22192 2 => 9.0 / 8.0,
22193 3 => 32.0 / 27.0,
22194 4 => 81.0 / 64.0,
22195 5 => 4.0 / 3.0,
22196 6 => 729.0 / 512.0,
22197 7 => 3.0 / 2.0,
22198 8 => 128.0 / 81.0,
22199 9 => 27.0 / 16.0,
22200 10 => 16.0 / 9.0,
22201 11 => 243.0 / 128.0,
22202 _ => 1.0,
22203 }
22204}
22205
22206fn meantone_ratio(semitones: i32) -> f64 {
22207 let fifth = 5.0_f64.powf(0.25); match semitones.rem_euclid(12) {
22210 0 => 1.0,
22211 1 => 8.0 / (fifth.powi(5)),
22212 2 => fifth.powi(2) / 2.0,
22213 3 => 4.0 / (fifth.powi(3)),
22214 4 => fifth.powi(4) / 4.0,
22215 5 => 2.0 / fifth,
22216 6 => fifth.powi(6) / 8.0,
22217 7 => fifth,
22218 8 => 8.0 / (fifth.powi(4)),
22219 9 => fifth.powi(3) / 2.0,
22220 10 => 4.0 / (fifth.powi(2)),
22221 11 => fifth.powi(5) / 4.0,
22222 _ => 1.0,
22223 }
22224}
22225
22226fn shruti_ratio(shruti: i32) -> f64 {
22227 let ratios = [
22229 1.0,
22230 256.0 / 243.0,
22231 16.0 / 15.0,
22232 10.0 / 9.0,
22233 9.0 / 8.0,
22234 32.0 / 27.0,
22235 6.0 / 5.0,
22236 5.0 / 4.0,
22237 81.0 / 64.0,
22238 4.0 / 3.0,
22239 27.0 / 20.0,
22240 45.0 / 32.0,
22241 729.0 / 512.0,
22242 3.0 / 2.0,
22243 128.0 / 81.0,
22244 8.0 / 5.0,
22245 5.0 / 3.0,
22246 27.0 / 16.0,
22247 16.0 / 9.0,
22248 9.0 / 5.0,
22249 15.0 / 8.0,
22250 243.0 / 128.0,
22251 ];
22252 ratios[shruti.rem_euclid(22) as usize]
22253}
22254
22255fn pelog_ratio(degree: i32) -> f64 {
22256 let ratios = [1.0, 1.12, 1.26, 1.5, 1.68, 1.89, 2.12];
22258 ratios[degree.rem_euclid(7) as usize]
22259}
22260
22261fn slendro_ratio(degree: i32) -> f64 {
22262 let ratios = [1.0, 1.148, 1.318, 1.516, 1.741];
22264 ratios[degree.rem_euclid(5) as usize]
22265}
22266
22267fn generate_waveform(args: &[Value], wave_fn: fn(f64) -> f64) -> Result<Value, RuntimeError> {
22268 let freq = match &args[0] {
22269 Value::Float(f) => *f,
22270 Value::Int(i) => *i as f64,
22271 _ => return Err(RuntimeError::new("Waveform requires frequency")),
22272 };
22273 let sample_rate = match &args[1] {
22274 Value::Float(f) => *f as usize,
22275 Value::Int(i) => *i as usize,
22276 _ => return Err(RuntimeError::new("Waveform requires sample rate")),
22277 };
22278 let duration = match &args[2] {
22279 Value::Float(f) => *f,
22280 Value::Int(i) => *i as f64,
22281 _ => return Err(RuntimeError::new("Waveform requires duration")),
22282 };
22283
22284 let num_samples = (sample_rate as f64 * duration) as usize;
22285 let samples: Vec<Value> = (0..num_samples)
22286 .map(|i| {
22287 let t = i as f64 / sample_rate as f64;
22288 let phase = 2.0 * std::f64::consts::PI * freq * t;
22289 Value::Float(wave_fn(phase))
22290 })
22291 .collect();
22292
22293 Ok(Value::Array(Rc::new(RefCell::new(samples))))
22294}
22295
22296fn register_spirituality(interp: &mut Interpreter) {
22318 const TRIGRAMS: [(&str, &str, &str, &str, &str); 8] = [
22324 (
22325 "☰",
22326 "乾",
22327 "Heaven",
22328 "Creative",
22329 "strong, initiating, persisting",
22330 ),
22331 (
22332 "☱",
22333 "兌",
22334 "Lake",
22335 "Joyous",
22336 "pleasure, satisfaction, openness",
22337 ),
22338 (
22339 "☲",
22340 "離",
22341 "Fire",
22342 "Clinging",
22343 "clarity, awareness, dependence",
22344 ),
22345 (
22346 "☳",
22347 "震",
22348 "Thunder",
22349 "Arousing",
22350 "movement, initiative, action",
22351 ),
22352 (
22353 "☴",
22354 "巽",
22355 "Wind",
22356 "Gentle",
22357 "penetrating, following, flexible",
22358 ),
22359 ("☵", "坎", "Water", "Abysmal", "danger, flowing, depth"),
22360 (
22361 "☶",
22362 "艮",
22363 "Mountain",
22364 "Keeping Still",
22365 "stopping, resting, meditation",
22366 ),
22367 (
22368 "☷",
22369 "坤",
22370 "Earth",
22371 "Receptive",
22372 "yielding, nurturing, devoted",
22373 ),
22374 ];
22375
22376 define(interp, "trigram", Some(1), |_, args| {
22378 let input = match &args[0] {
22379 Value::Int(n) => (*n as usize).min(7),
22380 Value::String(s) => match s.as_str() {
22381 "☰" | "heaven" | "qian" | "乾" => 0,
22382 "☱" | "lake" | "dui" | "兌" => 1,
22383 "☲" | "fire" | "li" | "離" => 2,
22384 "☳" | "thunder" | "zhen" | "震" => 3,
22385 "☴" | "wind" | "xun" | "巽" => 4,
22386 "☵" | "water" | "kan" | "坎" => 5,
22387 "☶" | "mountain" | "gen" | "艮" => 6,
22388 "☷" | "earth" | "kun" | "坤" => 7,
22389 _ => return Err(RuntimeError::new(format!("Unknown trigram: {}", s))),
22390 },
22391 _ => return Err(RuntimeError::new("trigram() requires number or name")),
22392 };
22393
22394 let (symbol, chinese, english, name, meaning) = TRIGRAMS[input];
22395
22396 let mut result = std::collections::HashMap::new();
22397 result.insert("number".to_string(), Value::Int(input as i64));
22398 result.insert(
22399 "symbol".to_string(),
22400 Value::String(Rc::new(symbol.to_string())),
22401 );
22402 result.insert(
22403 "chinese".to_string(),
22404 Value::String(Rc::new(chinese.to_string())),
22405 );
22406 result.insert(
22407 "english".to_string(),
22408 Value::String(Rc::new(english.to_string())),
22409 );
22410 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
22411 result.insert(
22412 "meaning".to_string(),
22413 Value::String(Rc::new(meaning.to_string())),
22414 );
22415
22416 let binary = match input {
22418 0 => "111", 1 => "110", 2 => "101", 3 => "100", 4 => "011", 5 => "010", 6 => "001", 7 => "000", _ => "000",
22427 };
22428 result.insert(
22429 "binary".to_string(),
22430 Value::String(Rc::new(binary.to_string())),
22431 );
22432
22433 Ok(Value::Map(Rc::new(RefCell::new(result))))
22434 });
22435
22436 define(interp, "hexagram", Some(1), |_, args| {
22438 let num = match &args[0] {
22439 Value::Int(n) => ((*n - 1) as usize).min(63),
22440 _ => return Err(RuntimeError::new("hexagram() requires number 1-64")),
22441 };
22442
22443 let hex = &HEXAGRAMS[num];
22444
22445 let mut result = std::collections::HashMap::new();
22446 result.insert("number".to_string(), Value::Int((num + 1) as i64));
22447 result.insert(
22448 "chinese".to_string(),
22449 Value::String(Rc::new(hex.0.to_string())),
22450 );
22451 result.insert(
22452 "pinyin".to_string(),
22453 Value::String(Rc::new(hex.1.to_string())),
22454 );
22455 result.insert(
22456 "english".to_string(),
22457 Value::String(Rc::new(hex.2.to_string())),
22458 );
22459 result.insert(
22460 "judgment".to_string(),
22461 Value::String(Rc::new(hex.3.to_string())),
22462 );
22463 result.insert(
22464 "upper_trigram".to_string(),
22465 Value::String(Rc::new(hex.4.to_string())),
22466 );
22467 result.insert(
22468 "lower_trigram".to_string(),
22469 Value::String(Rc::new(hex.5.to_string())),
22470 );
22471
22472 Ok(Value::Map(Rc::new(RefCell::new(result))))
22473 });
22474
22475 define(interp, "cast_iching", Some(0), |_, _| {
22477 let mut rng = rand::thread_rng();
22478
22479 let mut lines = Vec::new();
22482 let mut hexagram_num = 0u8;
22483 let mut changing_lines = Vec::new();
22484
22485 for i in 0..6 {
22486 let r: f64 = rng.gen();
22488 let line = if r < 0.0625 {
22489 6
22490 }
22491 else if r < 0.3125 {
22493 7
22494 }
22495 else if r < 0.5625 {
22497 8
22498 }
22499 else {
22501 9
22502 }; let is_yang = line == 7 || line == 9;
22505 if is_yang {
22506 hexagram_num |= 1 << i;
22507 }
22508
22509 if line == 6 || line == 9 {
22510 changing_lines.push(i + 1);
22511 }
22512
22513 lines.push(Value::Int(line));
22514 }
22515
22516 let king_wen_num = binary_to_king_wen(hexagram_num) + 1;
22518 let hex = &HEXAGRAMS[(king_wen_num - 1) as usize];
22519
22520 let mut result = std::collections::HashMap::new();
22521 result.insert("hexagram".to_string(), Value::Int(king_wen_num as i64));
22522 result.insert(
22523 "chinese".to_string(),
22524 Value::String(Rc::new(hex.0.to_string())),
22525 );
22526 result.insert(
22527 "english".to_string(),
22528 Value::String(Rc::new(hex.2.to_string())),
22529 );
22530 result.insert(
22531 "judgment".to_string(),
22532 Value::String(Rc::new(hex.3.to_string())),
22533 );
22534 result.insert(
22535 "lines".to_string(),
22536 Value::Array(Rc::new(RefCell::new(lines))),
22537 );
22538
22539 let changing: Vec<Value> = changing_lines.iter().map(|&n| Value::Int(n)).collect();
22540 result.insert(
22541 "changing_lines".to_string(),
22542 Value::Array(Rc::new(RefCell::new(changing))),
22543 );
22544
22545 if !changing_lines.is_empty() {
22547 let mut result_hex = hexagram_num;
22548 for &line in &changing_lines {
22549 result_hex ^= 1 << (line - 1); }
22551 let result_king_wen = binary_to_king_wen(result_hex) + 1;
22552 result.insert(
22553 "transforms_to".to_string(),
22554 Value::Int(result_king_wen as i64),
22555 );
22556 }
22557
22558 Ok(Value::Map(Rc::new(RefCell::new(result))))
22559 });
22560
22561 define(interp, "phi", Some(0), |_, _| {
22567 Ok(Value::Float((1.0 + 5.0_f64.sqrt()) / 2.0))
22568 });
22569
22570 define(interp, "sacred_ratio", Some(1), |_, args| {
22572 let name = match &args[0] {
22573 Value::String(s) => s.to_lowercase(),
22574 _ => return Err(RuntimeError::new("sacred_ratio() requires string")),
22575 };
22576
22577 let (value, symbol, meaning) = match name.as_str() {
22578 "phi" | "φ" | "golden" => (
22579 (1.0 + 5.0_f64.sqrt()) / 2.0,
22580 "φ",
22581 "Golden Ratio - divine proportion found in nature, art, architecture",
22582 ),
22583 "phi_conjugate" | "1/phi" => (
22584 2.0 / (1.0 + 5.0_f64.sqrt()),
22585 "1/φ",
22586 "Golden Ratio conjugate - φ - 1 = 1/φ",
22587 ),
22588 "phi_squared" | "phi2" => (
22589 ((1.0 + 5.0_f64.sqrt()) / 2.0).powi(2),
22590 "φ²",
22591 "Golden Ratio squared - φ + 1 = φ²",
22592 ),
22593 "sqrt_phi" => (
22594 ((1.0 + 5.0_f64.sqrt()) / 2.0).sqrt(),
22595 "√φ",
22596 "Square root of Golden Ratio",
22597 ),
22598 "pi" | "π" => (
22599 std::f64::consts::PI,
22600 "π",
22601 "Circle constant - circumference/diameter, transcendental",
22602 ),
22603 "tau" | "τ" => (
22604 std::f64::consts::TAU,
22605 "τ",
22606 "Full circle constant - 2π, one complete revolution",
22607 ),
22608 "e" | "euler" => (
22609 std::f64::consts::E,
22610 "e",
22611 "Euler's number - natural growth, compound interest",
22612 ),
22613 "sqrt2" | "√2" | "pythagoras" => (
22614 std::f64::consts::SQRT_2,
22615 "√2",
22616 "Pythagorean constant - diagonal of unit square",
22617 ),
22618 "sqrt3" | "√3" | "vesica" => (
22619 3.0_f64.sqrt(),
22620 "√3",
22621 "Vesica Piscis ratio - sacred geometry foundation",
22622 ),
22623 "sqrt5" | "√5" => (
22624 5.0_f64.sqrt(),
22625 "√5",
22626 "Related to Golden Ratio: φ = (1 + √5) / 2",
22627 ),
22628 "silver" | "δs" => (
22629 1.0 + 2.0_f64.sqrt(),
22630 "δs",
22631 "Silver Ratio - related to octagon",
22632 ),
22633 "plastic" | "ρ" => (
22634 1.324717957244746,
22635 "ρ",
22636 "Plastic Number - smallest Pisot number",
22637 ),
22638 "feigenbaum" | "δ" => (
22639 4.669201609102990,
22640 "δ",
22641 "Feigenbaum constant - chaos theory, period doubling",
22642 ),
22643 _ => return Err(RuntimeError::new(format!("Unknown sacred ratio: {}", name))),
22644 };
22645
22646 let mut result = std::collections::HashMap::new();
22647 result.insert("value".to_string(), Value::Float(value));
22648 result.insert(
22649 "symbol".to_string(),
22650 Value::String(Rc::new(symbol.to_string())),
22651 );
22652 result.insert(
22653 "meaning".to_string(),
22654 Value::String(Rc::new(meaning.to_string())),
22655 );
22656
22657 Ok(Value::Map(Rc::new(RefCell::new(result))))
22658 });
22659
22660 define(interp, "fibonacci", Some(1), |_, args| {
22662 let count = match &args[0] {
22663 Value::Int(n) => *n as usize,
22664 _ => return Err(RuntimeError::new("fibonacci() requires count")),
22665 };
22666
22667 let mut seq = Vec::with_capacity(count);
22668 let (mut a, mut b) = (0i64, 1i64);
22669
22670 for _ in 0..count {
22671 seq.push(Value::Int(a));
22672 let next = a.saturating_add(b);
22673 a = b;
22674 b = next;
22675 }
22676
22677 Ok(Value::Array(Rc::new(RefCell::new(seq))))
22678 });
22679
22680 define(interp, "is_fibonacci", Some(1), |_, args| {
22682 let n = match &args[0] {
22683 Value::Int(n) => *n,
22684 _ => return Err(RuntimeError::new("is_fibonacci() requires integer")),
22685 };
22686
22687 fn is_perfect_square(n: i64) -> bool {
22689 if n < 0 {
22690 return false;
22691 }
22692 let root = (n as f64).sqrt() as i64;
22693 root * root == n
22694 }
22695
22696 let n_sq = n.saturating_mul(n);
22697 let test1 = 5i64.saturating_mul(n_sq).saturating_add(4);
22698 let test2 = 5i64.saturating_mul(n_sq).saturating_sub(4);
22699
22700 Ok(Value::Bool(
22701 is_perfect_square(test1) || is_perfect_square(test2),
22702 ))
22703 });
22704
22705 define(interp, "platonic_solid", Some(1), |_, args| {
22707 let name = match &args[0] {
22708 Value::String(s) => s.to_lowercase(),
22709 _ => return Err(RuntimeError::new("platonic_solid() requires string")),
22710 };
22711
22712 let (faces, vertices, edges, face_shape, element, meaning) = match name.as_str() {
22713 "tetrahedron" | "fire" => (
22714 4,
22715 4,
22716 6,
22717 "triangle",
22718 "Fire",
22719 "Sharpness, heat, transformation",
22720 ),
22721 "cube" | "hexahedron" | "earth" => (
22722 6,
22723 8,
22724 12,
22725 "square",
22726 "Earth",
22727 "Stability, grounding, material",
22728 ),
22729 "octahedron" | "air" => (
22730 8,
22731 6,
22732 12,
22733 "triangle",
22734 "Air",
22735 "Balance, intellect, communication",
22736 ),
22737 "dodecahedron" | "aether" | "spirit" => (
22738 12,
22739 20,
22740 30,
22741 "pentagon",
22742 "Aether/Spirit",
22743 "The cosmos, divine thought",
22744 ),
22745 "icosahedron" | "water" => (
22746 20,
22747 12,
22748 30,
22749 "triangle",
22750 "Water",
22751 "Flow, emotion, adaptability",
22752 ),
22753 _ => {
22754 return Err(RuntimeError::new(format!(
22755 "Unknown Platonic solid: {}",
22756 name
22757 )))
22758 }
22759 };
22760
22761 let mut result = std::collections::HashMap::new();
22762 result.insert("name".to_string(), Value::String(Rc::new(name)));
22763 result.insert("faces".to_string(), Value::Int(faces));
22764 result.insert("vertices".to_string(), Value::Int(vertices));
22765 result.insert("edges".to_string(), Value::Int(edges));
22766 result.insert(
22767 "face_shape".to_string(),
22768 Value::String(Rc::new(face_shape.to_string())),
22769 );
22770 result.insert(
22771 "element".to_string(),
22772 Value::String(Rc::new(element.to_string())),
22773 );
22774 result.insert(
22775 "meaning".to_string(),
22776 Value::String(Rc::new(meaning.to_string())),
22777 );
22778
22779 result.insert("euler_characteristic".to_string(), Value::Int(2));
22781
22782 Ok(Value::Map(Rc::new(RefCell::new(result))))
22783 });
22784
22785 define(interp, "gematria", Some(2), |_, args| {
22791 let text = match &args[0] {
22792 Value::String(s) => s.to_string(),
22793 _ => return Err(RuntimeError::new("gematria() requires string")),
22794 };
22795
22796 let system = match &args[1] {
22797 Value::String(s) => s.to_lowercase(),
22798 _ => return Err(RuntimeError::new("gematria() requires system name")),
22799 };
22800
22801 let total: i64 = match system.as_str() {
22802 "hebrew" | "kabbalah" => text.chars().map(|c| hebrew_gematria(c)).sum(),
22803 "greek" | "isopsephy" => text.chars().map(|c| greek_isopsephy(c)).sum(),
22804 "arabic" | "abjad" => text.chars().map(|c| arabic_abjad(c)).sum(),
22805 "english" | "simple" => {
22806 text.to_uppercase()
22808 .chars()
22809 .filter_map(|c| {
22810 if c.is_ascii_alphabetic() {
22811 Some((c as i64) - ('A' as i64) + 1)
22812 } else {
22813 None
22814 }
22815 })
22816 .sum()
22817 }
22818 "english_ordinal" => {
22819 text.to_uppercase()
22821 .chars()
22822 .filter_map(|c| {
22823 if c.is_ascii_alphabetic() {
22824 Some((c as i64) - ('A' as i64) + 1)
22825 } else {
22826 None
22827 }
22828 })
22829 .sum()
22830 }
22831 "english_reduction" => {
22832 text.to_uppercase()
22834 .chars()
22835 .filter_map(|c| {
22836 if c.is_ascii_alphabetic() {
22837 let val = ((c as i64) - ('A' as i64)) % 9 + 1;
22838 Some(val)
22839 } else {
22840 None
22841 }
22842 })
22843 .sum()
22844 }
22845 _ => {
22846 return Err(RuntimeError::new(format!(
22847 "Unknown gematria system: {}",
22848 system
22849 )))
22850 }
22851 };
22852
22853 let mut result = std::collections::HashMap::new();
22854 result.insert("text".to_string(), Value::String(Rc::new(text)));
22855 result.insert("system".to_string(), Value::String(Rc::new(system)));
22856 result.insert("value".to_string(), Value::Int(total));
22857
22858 let mut digital_root = total;
22860 while digital_root > 9 {
22861 digital_root = digital_root
22862 .to_string()
22863 .chars()
22864 .filter_map(|c| c.to_digit(10))
22865 .map(|d| d as i64)
22866 .sum();
22867 }
22868 result.insert("digital_root".to_string(), Value::Int(digital_root));
22869
22870 Ok(Value::Map(Rc::new(RefCell::new(result))))
22871 });
22872
22873 define(interp, "gematria_match", Some(2), |_, args| {
22875 let value = match &args[0] {
22876 Value::Int(n) => *n,
22877 _ => return Err(RuntimeError::new("gematria_match() requires integer value")),
22878 };
22879
22880 let system = match &args[1] {
22881 Value::String(s) => s.to_lowercase(),
22882 _ => return Err(RuntimeError::new("gematria_match() requires system name")),
22883 };
22884
22885 let matches = match (value, system.as_str()) {
22887 (26, "hebrew") => vec!["יהוה (YHWH - Tetragrammaton)"],
22888 (18, "hebrew") => vec!["חי (Chai - Life)"],
22889 (86, "hebrew") => vec!["אלהים (Elohim - God)"],
22890 (72, "hebrew") => vec!["חסד (Chesed - Loving-kindness)"],
22891 (93, "english") => vec!["Love", "Will", "Thelema"],
22892 (666, "greek") => vec!["Nero Caesar (in Hebrew letters)"],
22893 (888, "greek") => vec!["Ἰησοῦς (Jesus)"],
22894 _ => vec![],
22895 };
22896
22897 let match_values: Vec<Value> = matches
22898 .iter()
22899 .map(|s| Value::String(Rc::new(s.to_string())))
22900 .collect();
22901
22902 Ok(Value::Array(Rc::new(RefCell::new(match_values))))
22903 });
22904
22905 define(interp, "archetype", Some(1), |_, args| {
22911 let name = match &args[0] {
22912 Value::String(s) => s.to_lowercase(),
22913 _ => return Err(RuntimeError::new("archetype() requires string")),
22914 };
22915
22916 let (description, shadow, gift, challenge) = match name.as_str() {
22917 "self" => (
22919 "The unified conscious and unconscious, the goal of individuation",
22920 "Inflation or deflation of ego",
22921 "Wholeness, integration, meaning",
22922 "Integrating all aspects of psyche",
22923 ),
22924 "shadow" => (
22925 "The unconscious aspect containing repressed weaknesses and instincts",
22926 "Projection onto others, denial",
22927 "Creativity, spontaneity, insight",
22928 "Acknowledging and integrating darkness",
22929 ),
22930 "anima" => (
22931 "The feminine inner personality in a man's unconscious",
22932 "Moodiness, seduction, possession",
22933 "Relatedness, creativity, soul connection",
22934 "Developing emotional intelligence",
22935 ),
22936 "animus" => (
22937 "The masculine inner personality in a woman's unconscious",
22938 "Brutality, reckless action, opinionation",
22939 "Courage, initiative, spiritual depth",
22940 "Developing assertiveness with wisdom",
22941 ),
22942 "persona" => (
22943 "The social mask, the face we present to the world",
22944 "Over-identification, inauthenticity",
22945 "Social adaptation, professional competence",
22946 "Maintaining authenticity within role",
22947 ),
22948
22949 "hero" => (
22951 "The courageous one who overcomes obstacles and achieves great deeds",
22952 "Arrogance, ruthlessness, eternal battle",
22953 "Courage, perseverance, accomplishment",
22954 "Knowing when to fight and when to surrender",
22955 ),
22956 "sage" | "wise_old_man" => (
22957 "The wise figure who offers guidance and insight",
22958 "Dogmatism, disconnection, ivory tower",
22959 "Wisdom, knowledge, truth-seeking",
22960 "Applying wisdom practically",
22961 ),
22962 "magician" | "wizard" => (
22963 "The transformer who makes things happen through understanding laws",
22964 "Manipulation, disconnection from ethics",
22965 "Transformation, vision, manifestation",
22966 "Using power responsibly",
22967 ),
22968 "lover" => (
22969 "The one who pursues connection, beauty, and passion",
22970 "Obsession, jealousy, loss of identity",
22971 "Passion, commitment, appreciation",
22972 "Maintaining boundaries while connecting deeply",
22973 ),
22974 "caregiver" | "mother" => (
22975 "The nurturing one who protects and provides",
22976 "Martyrdom, enabling, smothering",
22977 "Compassion, generosity, nurturing",
22978 "Caring for self while caring for others",
22979 ),
22980 "ruler" | "king" | "queen" => (
22981 "The one who takes responsibility for the realm",
22982 "Tyranny, authoritarianism, being overthrown",
22983 "Order, leadership, prosperity",
22984 "Serving the greater good, not just power",
22985 ),
22986 "creator" | "artist" => (
22987 "The one who brings new things into being",
22988 "Perfectionism, self-indulgence, drama",
22989 "Creativity, imagination, expression",
22990 "Completing projects, accepting imperfection",
22991 ),
22992 "innocent" | "child" => (
22993 "The pure one with faith and optimism",
22994 "Naivety, denial, dependence",
22995 "Faith, optimism, loyalty",
22996 "Growing without becoming cynical",
22997 ),
22998 "explorer" | "seeker" => (
22999 "The one who seeks new experiences and self-discovery",
23000 "Aimless wandering, inability to commit",
23001 "Autonomy, ambition, authenticity",
23002 "Finding what you seek",
23003 ),
23004 "rebel" | "outlaw" => (
23005 "The one who breaks rules and challenges the status quo",
23006 "Crime, self-destruction, alienation",
23007 "Liberation, revolution, radical freedom",
23008 "Channeling rebellion constructively",
23009 ),
23010 "jester" | "fool" | "trickster" => (
23011 "The one who uses humor and playfulness",
23012 "Cruelty, debauchery, irresponsibility",
23013 "Joy, freedom, living in the moment",
23014 "Knowing when to be serious",
23015 ),
23016 "everyman" | "orphan" => (
23017 "The regular person who wants belonging",
23018 "Victim mentality, losing self in group",
23019 "Realism, empathy, connection",
23020 "Standing out when necessary",
23021 ),
23022 _ => return Err(RuntimeError::new(format!("Unknown archetype: {}", name))),
23023 };
23024
23025 let mut result = std::collections::HashMap::new();
23026 result.insert("name".to_string(), Value::String(Rc::new(name)));
23027 result.insert(
23028 "description".to_string(),
23029 Value::String(Rc::new(description.to_string())),
23030 );
23031 result.insert(
23032 "shadow".to_string(),
23033 Value::String(Rc::new(shadow.to_string())),
23034 );
23035 result.insert("gift".to_string(), Value::String(Rc::new(gift.to_string())));
23036 result.insert(
23037 "challenge".to_string(),
23038 Value::String(Rc::new(challenge.to_string())),
23039 );
23040
23041 Ok(Value::Map(Rc::new(RefCell::new(result))))
23042 });
23043
23044 define(interp, "zodiac", Some(1), |_, args| {
23050 let input = match &args[0] {
23051 Value::Int(n) => (*n as usize - 1).min(11),
23052 Value::String(s) => match s.to_lowercase().as_str() {
23053 "aries" | "♈" => 0,
23054 "taurus" | "♉" => 1,
23055 "gemini" | "♊" => 2,
23056 "cancer" | "♋" => 3,
23057 "leo" | "♌" => 4,
23058 "virgo" | "♍" => 5,
23059 "libra" | "♎" => 6,
23060 "scorpio" | "♏" => 7,
23061 "sagittarius" | "♐" => 8,
23062 "capricorn" | "♑" => 9,
23063 "aquarius" | "♒" => 10,
23064 "pisces" | "♓" => 11,
23065 _ => return Err(RuntimeError::new(format!("Unknown sign: {}", s))),
23066 },
23067 _ => return Err(RuntimeError::new("zodiac() requires number or name")),
23068 };
23069
23070 let signs = [
23071 (
23072 "♈",
23073 "Aries",
23074 "Fire",
23075 "Cardinal",
23076 "Mars",
23077 "I Am",
23078 "Mar 21 - Apr 19",
23079 ),
23080 (
23081 "♉",
23082 "Taurus",
23083 "Earth",
23084 "Fixed",
23085 "Venus",
23086 "I Have",
23087 "Apr 20 - May 20",
23088 ),
23089 (
23090 "♊",
23091 "Gemini",
23092 "Air",
23093 "Mutable",
23094 "Mercury",
23095 "I Think",
23096 "May 21 - Jun 20",
23097 ),
23098 (
23099 "♋",
23100 "Cancer",
23101 "Water",
23102 "Cardinal",
23103 "Moon",
23104 "I Feel",
23105 "Jun 21 - Jul 22",
23106 ),
23107 (
23108 "♌",
23109 "Leo",
23110 "Fire",
23111 "Fixed",
23112 "Sun",
23113 "I Will",
23114 "Jul 23 - Aug 22",
23115 ),
23116 (
23117 "♍",
23118 "Virgo",
23119 "Earth",
23120 "Mutable",
23121 "Mercury",
23122 "I Analyze",
23123 "Aug 23 - Sep 22",
23124 ),
23125 (
23126 "♎",
23127 "Libra",
23128 "Air",
23129 "Cardinal",
23130 "Venus",
23131 "I Balance",
23132 "Sep 23 - Oct 22",
23133 ),
23134 (
23135 "♏",
23136 "Scorpio",
23137 "Water",
23138 "Fixed",
23139 "Pluto/Mars",
23140 "I Transform",
23141 "Oct 23 - Nov 21",
23142 ),
23143 (
23144 "♐",
23145 "Sagittarius",
23146 "Fire",
23147 "Mutable",
23148 "Jupiter",
23149 "I Seek",
23150 "Nov 22 - Dec 21",
23151 ),
23152 (
23153 "♑",
23154 "Capricorn",
23155 "Earth",
23156 "Cardinal",
23157 "Saturn",
23158 "I Use",
23159 "Dec 22 - Jan 19",
23160 ),
23161 (
23162 "♒",
23163 "Aquarius",
23164 "Air",
23165 "Fixed",
23166 "Uranus/Saturn",
23167 "I Know",
23168 "Jan 20 - Feb 18",
23169 ),
23170 (
23171 "♓",
23172 "Pisces",
23173 "Water",
23174 "Mutable",
23175 "Neptune/Jupiter",
23176 "I Believe",
23177 "Feb 19 - Mar 20",
23178 ),
23179 ];
23180
23181 let (symbol, name, element, modality, ruler, motto, dates) = signs[input];
23182
23183 let mut result = std::collections::HashMap::new();
23184 result.insert("number".to_string(), Value::Int((input + 1) as i64));
23185 result.insert(
23186 "symbol".to_string(),
23187 Value::String(Rc::new(symbol.to_string())),
23188 );
23189 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23190 result.insert(
23191 "element".to_string(),
23192 Value::String(Rc::new(element.to_string())),
23193 );
23194 result.insert(
23195 "modality".to_string(),
23196 Value::String(Rc::new(modality.to_string())),
23197 );
23198 result.insert(
23199 "ruler".to_string(),
23200 Value::String(Rc::new(ruler.to_string())),
23201 );
23202 result.insert(
23203 "motto".to_string(),
23204 Value::String(Rc::new(motto.to_string())),
23205 );
23206 result.insert(
23207 "dates".to_string(),
23208 Value::String(Rc::new(dates.to_string())),
23209 );
23210
23211 Ok(Value::Map(Rc::new(RefCell::new(result))))
23212 });
23213
23214 define(interp, "tarot_major", Some(1), |_, args| {
23216 let num = match &args[0] {
23217 Value::Int(n) => (*n as usize).min(21),
23218 Value::String(s) => match s.to_lowercase().as_str() {
23219 "fool" => 0,
23220 "magician" => 1,
23221 "high_priestess" | "priestess" => 2,
23222 "empress" => 3,
23223 "emperor" => 4,
23224 "hierophant" | "pope" => 5,
23225 "lovers" => 6,
23226 "chariot" => 7,
23227 "strength" => 8,
23228 "hermit" => 9,
23229 "wheel" | "fortune" => 10,
23230 "justice" => 11,
23231 "hanged_man" | "hanged" => 12,
23232 "death" => 13,
23233 "temperance" => 14,
23234 "devil" => 15,
23235 "tower" => 16,
23236 "star" => 17,
23237 "moon" => 18,
23238 "sun" => 19,
23239 "judgement" | "judgment" => 20,
23240 "world" => 21,
23241 _ => return Err(RuntimeError::new(format!("Unknown card: {}", s))),
23242 },
23243 _ => return Err(RuntimeError::new("tarot_major() requires number or name")),
23244 };
23245
23246 let cards = [
23247 (
23248 "The Fool",
23249 "New beginnings, innocence, spontaneity",
23250 "Naivety, recklessness, risk-taking",
23251 ),
23252 (
23253 "The Magician",
23254 "Willpower, creation, manifestation",
23255 "Manipulation, trickery, unused talent",
23256 ),
23257 (
23258 "The High Priestess",
23259 "Intuition, mystery, inner knowledge",
23260 "Secrets, withdrawal, silence",
23261 ),
23262 (
23263 "The Empress",
23264 "Abundance, nurturing, fertility",
23265 "Dependence, smothering, emptiness",
23266 ),
23267 (
23268 "The Emperor",
23269 "Authority, structure, control",
23270 "Tyranny, rigidity, coldness",
23271 ),
23272 (
23273 "The Hierophant",
23274 "Tradition, conformity, spirituality",
23275 "Dogma, restriction, challenging status quo",
23276 ),
23277 (
23278 "The Lovers",
23279 "Love, harmony, relationships, choices",
23280 "Disharmony, imbalance, misalignment",
23281 ),
23282 (
23283 "The Chariot",
23284 "Direction, willpower, victory",
23285 "Aggression, lack of direction, obstacles",
23286 ),
23287 (
23288 "Strength",
23289 "Courage, patience, inner power",
23290 "Self-doubt, weakness, insecurity",
23291 ),
23292 (
23293 "The Hermit",
23294 "Contemplation, search for truth, inner guidance",
23295 "Isolation, loneliness, withdrawal",
23296 ),
23297 (
23298 "Wheel of Fortune",
23299 "Change, cycles, fate, destiny",
23300 "Resistance to change, bad luck, setbacks",
23301 ),
23302 (
23303 "Justice",
23304 "Truth, fairness, law, cause and effect",
23305 "Unfairness, dishonesty, lack of accountability",
23306 ),
23307 (
23308 "The Hanged Man",
23309 "Surrender, letting go, new perspective",
23310 "Stalling, resistance, indecision",
23311 ),
23312 (
23313 "Death",
23314 "Endings, transformation, transition",
23315 "Fear of change, stagnation, decay",
23316 ),
23317 (
23318 "Temperance",
23319 "Balance, moderation, patience",
23320 "Imbalance, excess, lack of purpose",
23321 ),
23322 (
23323 "The Devil",
23324 "Bondage, materialism, shadow self",
23325 "Freedom, release, exploring dark side",
23326 ),
23327 (
23328 "The Tower",
23329 "Sudden change, upheaval, revelation",
23330 "Disaster averted, fear of change, prolonged pain",
23331 ),
23332 (
23333 "The Star",
23334 "Hope, faith, renewal, inspiration",
23335 "Despair, disconnection, lack of faith",
23336 ),
23337 (
23338 "The Moon",
23339 "Illusion, intuition, the unconscious",
23340 "Fear, confusion, misinterpretation",
23341 ),
23342 (
23343 "The Sun",
23344 "Joy, success, vitality, positivity",
23345 "Negativity, depression, sadness",
23346 ),
23347 (
23348 "Judgement",
23349 "Rebirth, inner calling, absolution",
23350 "Self-doubt, refusal of self-examination",
23351 ),
23352 (
23353 "The World",
23354 "Completion, accomplishment, wholeness",
23355 "Incompletion, lack of closure, emptiness",
23356 ),
23357 ];
23358
23359 let (name, upright, reversed) = cards[num];
23360
23361 let mut result = std::collections::HashMap::new();
23362 result.insert("number".to_string(), Value::Int(num as i64));
23363 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23364 result.insert(
23365 "upright".to_string(),
23366 Value::String(Rc::new(upright.to_string())),
23367 );
23368 result.insert(
23369 "reversed".to_string(),
23370 Value::String(Rc::new(reversed.to_string())),
23371 );
23372
23373 Ok(Value::Map(Rc::new(RefCell::new(result))))
23374 });
23375
23376 define(interp, "draw_tarot", Some(0), |_, _| {
23378 let mut rng = rand::thread_rng();
23379 let card: usize = rng.gen_range(0..22);
23380 let reversed: bool = rng.gen();
23381
23382 let cards = [
23383 "The Fool",
23384 "The Magician",
23385 "The High Priestess",
23386 "The Empress",
23387 "The Emperor",
23388 "The Hierophant",
23389 "The Lovers",
23390 "The Chariot",
23391 "Strength",
23392 "The Hermit",
23393 "Wheel of Fortune",
23394 "Justice",
23395 "The Hanged Man",
23396 "Death",
23397 "Temperance",
23398 "The Devil",
23399 "The Tower",
23400 "The Star",
23401 "The Moon",
23402 "The Sun",
23403 "Judgement",
23404 "The World",
23405 ];
23406
23407 let mut result = std::collections::HashMap::new();
23408 result.insert("number".to_string(), Value::Int(card as i64));
23409 result.insert(
23410 "name".to_string(),
23411 Value::String(Rc::new(cards[card].to_string())),
23412 );
23413 result.insert("reversed".to_string(), Value::Bool(reversed));
23414 result.insert(
23415 "orientation".to_string(),
23416 Value::String(Rc::new(
23417 if reversed { "reversed" } else { "upright" }.to_string(),
23418 )),
23419 );
23420
23421 Ok(Value::Map(Rc::new(RefCell::new(result))))
23422 });
23423
23424 define(interp, "synchronicity_score", Some(2), |_, args| {
23430 let a = match &args[0] {
23432 Value::String(s) => s.to_string(),
23433 Value::Int(n) => n.to_string(),
23434 _ => {
23435 return Err(RuntimeError::new(
23436 "synchronicity_score() requires string or int",
23437 ))
23438 }
23439 };
23440
23441 let b = match &args[1] {
23442 Value::String(s) => s.to_string(),
23443 Value::Int(n) => n.to_string(),
23444 _ => {
23445 return Err(RuntimeError::new(
23446 "synchronicity_score() requires string or int",
23447 ))
23448 }
23449 };
23450
23451 let val_a: i64 = a
23453 .to_uppercase()
23454 .chars()
23455 .filter_map(|c| {
23456 if c.is_ascii_alphabetic() {
23457 Some((c as i64) - ('A' as i64) + 1)
23458 } else if c.is_ascii_digit() {
23459 c.to_digit(10).map(|d| d as i64)
23460 } else {
23461 None
23462 }
23463 })
23464 .sum();
23465
23466 let val_b: i64 = b
23467 .to_uppercase()
23468 .chars()
23469 .filter_map(|c| {
23470 if c.is_ascii_alphabetic() {
23471 Some((c as i64) - ('A' as i64) + 1)
23472 } else if c.is_ascii_digit() {
23473 c.to_digit(10).map(|d| d as i64)
23474 } else {
23475 None
23476 }
23477 })
23478 .sum();
23479
23480 let mut factors = Vec::new();
23482
23483 if val_a == val_b {
23485 factors.push("identical_gematria".to_string());
23486 }
23487
23488 if val_a > 0 && val_b > 0 && (val_a % val_b == 0 || val_b % val_a == 0) {
23490 factors.push("divisibility".to_string());
23491 }
23492
23493 let fib_set: std::collections::HashSet<i64> =
23495 [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
23496 .iter()
23497 .cloned()
23498 .collect();
23499 if fib_set.contains(&val_a) && fib_set.contains(&val_b) {
23500 factors.push("both_fibonacci".to_string());
23501 }
23502
23503 fn digital_root(mut n: i64) -> i64 {
23505 while n > 9 {
23506 n = n
23507 .to_string()
23508 .chars()
23509 .filter_map(|c| c.to_digit(10))
23510 .map(|d| d as i64)
23511 .sum();
23512 }
23513 n
23514 }
23515 if digital_root(val_a) == digital_root(val_b) {
23516 factors.push("same_digital_root".to_string());
23517 }
23518
23519 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
23521 let ratio = if val_a > 0 && val_b > 0 {
23522 (val_a as f64 / val_b as f64).max(val_b as f64 / val_a as f64)
23523 } else {
23524 0.0
23525 };
23526 if (ratio - phi).abs() < 0.02 || (ratio - 1.0 / phi).abs() < 0.02 {
23527 factors.push("golden_ratio".to_string());
23528 }
23529
23530 let score = (factors.len() as f64 / 5.0).min(1.0);
23531
23532 let mut result = std::collections::HashMap::new();
23533 result.insert("score".to_string(), Value::Float(score));
23534 result.insert("value_a".to_string(), Value::Int(val_a));
23535 result.insert("value_b".to_string(), Value::Int(val_b));
23536 let factor_values: Vec<Value> = factors
23537 .iter()
23538 .map(|s| Value::String(Rc::new(s.clone())))
23539 .collect();
23540 result.insert(
23541 "factors".to_string(),
23542 Value::Array(Rc::new(RefCell::new(factor_values))),
23543 );
23544
23545 Ok(Value::Map(Rc::new(RefCell::new(result))))
23546 });
23547
23548 define(interp, "spirituality_info", Some(0), |_, _| {
23550 let mut info = std::collections::HashMap::new();
23551
23552 info.insert(
23553 "i_ching".to_string(),
23554 Value::String(Rc::new(
23555 "trigram(), hexagram(), cast_iching() - 64 hexagrams of change".to_string(),
23556 )),
23557 );
23558 info.insert(
23559 "sacred_geometry".to_string(),
23560 Value::String(Rc::new(
23561 "phi(), sacred_ratio(), fibonacci(), platonic_solid()".to_string(),
23562 )),
23563 );
23564 info.insert(
23565 "gematria".to_string(),
23566 Value::String(Rc::new(
23567 "Hebrew, Greek, Arabic, English letter-number systems".to_string(),
23568 )),
23569 );
23570 info.insert(
23571 "archetypes".to_string(),
23572 Value::String(Rc::new(
23573 "17 Jungian archetypes with shadow and gift".to_string(),
23574 )),
23575 );
23576 info.insert(
23577 "astrology".to_string(),
23578 Value::String(Rc::new(
23579 "zodiac() - 12 signs with elements and modalities".to_string(),
23580 )),
23581 );
23582 info.insert(
23583 "tarot".to_string(),
23584 Value::String(Rc::new(
23585 "tarot_major(), draw_tarot() - 22 Major Arcana".to_string(),
23586 )),
23587 );
23588
23589 Ok(Value::Map(Rc::new(RefCell::new(info))))
23590 });
23591}
23592
23593const HEXAGRAMS: [(&str, &str, &str, &str, &str, &str); 64] = [
23595 (
23596 "乾",
23597 "Qián",
23598 "The Creative",
23599 "Sublime success through perseverance",
23600 "Heaven",
23601 "Heaven",
23602 ),
23603 (
23604 "坤",
23605 "Kūn",
23606 "The Receptive",
23607 "Devoted success through the mare's perseverance",
23608 "Earth",
23609 "Earth",
23610 ),
23611 (
23612 "屯",
23613 "Zhūn",
23614 "Difficulty at the Beginning",
23615 "Persevere, seek helpers, don't act alone",
23616 "Water",
23617 "Thunder",
23618 ),
23619 (
23620 "蒙",
23621 "Méng",
23622 "Youthful Folly",
23623 "Success through education and guidance",
23624 "Mountain",
23625 "Water",
23626 ),
23627 (
23628 "需",
23629 "Xū",
23630 "Waiting",
23631 "Sincerity brings success; cross the great water",
23632 "Water",
23633 "Heaven",
23634 ),
23635 (
23636 "訟",
23637 "Sòng",
23638 "Conflict",
23639 "Seek counsel; don't cross the great water",
23640 "Heaven",
23641 "Water",
23642 ),
23643 (
23644 "師",
23645 "Shī",
23646 "The Army",
23647 "Perseverance and an experienced leader bring success",
23648 "Earth",
23649 "Water",
23650 ),
23651 (
23652 "比",
23653 "Bǐ",
23654 "Holding Together",
23655 "Through perseverance, those who hesitate should reflect",
23656 "Water",
23657 "Earth",
23658 ),
23659 (
23660 "小畜",
23661 "Xiǎo Chù",
23662 "Small Taming",
23663 "Success; dense clouds but no rain",
23664 "Wind",
23665 "Heaven",
23666 ),
23667 (
23668 "履",
23669 "Lǚ",
23670 "Treading",
23671 "Tread on the tiger's tail carefully; success",
23672 "Heaven",
23673 "Lake",
23674 ),
23675 (
23676 "泰",
23677 "Tài",
23678 "Peace",
23679 "The small departs, the great approaches; success",
23680 "Earth",
23681 "Heaven",
23682 ),
23683 (
23684 "否",
23685 "Pǐ",
23686 "Standstill",
23687 "The great departs, the small approaches; persevere",
23688 "Heaven",
23689 "Earth",
23690 ),
23691 (
23692 "同人",
23693 "Tóng Rén",
23694 "Fellowship",
23695 "Success in the open; cross the great water",
23696 "Heaven",
23697 "Fire",
23698 ),
23699 (
23700 "大有",
23701 "Dà Yǒu",
23702 "Great Possession",
23703 "Supreme success",
23704 "Fire",
23705 "Heaven",
23706 ),
23707 (
23708 "謙",
23709 "Qiān",
23710 "Modesty",
23711 "Success; the superior person carries things through",
23712 "Earth",
23713 "Mountain",
23714 ),
23715 (
23716 "豫",
23717 "Yù",
23718 "Enthusiasm",
23719 "Appoint helpers and set armies marching",
23720 "Thunder",
23721 "Earth",
23722 ),
23723 (
23724 "隨",
23725 "Suí",
23726 "Following",
23727 "Supreme success through perseverance",
23728 "Lake",
23729 "Thunder",
23730 ),
23731 (
23732 "蠱",
23733 "Gǔ",
23734 "Work on the Decayed",
23735 "Success; cross the great water; three days before and after",
23736 "Mountain",
23737 "Wind",
23738 ),
23739 (
23740 "臨",
23741 "Lín",
23742 "Approach",
23743 "Great success through perseverance; misfortune in eighth month",
23744 "Earth",
23745 "Lake",
23746 ),
23747 (
23748 "觀",
23749 "Guān",
23750 "Contemplation",
23751 "Ablution, but not yet sacrifice; confidence inspires",
23752 "Wind",
23753 "Earth",
23754 ),
23755 (
23756 "噬嗑",
23757 "Shì Kè",
23758 "Biting Through",
23759 "Success; favorable for legal matters",
23760 "Fire",
23761 "Thunder",
23762 ),
23763 (
23764 "賁",
23765 "Bì",
23766 "Grace",
23767 "Success in small matters",
23768 "Mountain",
23769 "Fire",
23770 ),
23771 (
23772 "剝",
23773 "Bō",
23774 "Splitting Apart",
23775 "Unfavorable to go anywhere",
23776 "Mountain",
23777 "Earth",
23778 ),
23779 (
23780 "復",
23781 "Fù",
23782 "Return",
23783 "Success; going out and coming in without error",
23784 "Earth",
23785 "Thunder",
23786 ),
23787 (
23788 "無妄",
23789 "Wú Wàng",
23790 "Innocence",
23791 "Supreme success through perseverance",
23792 "Heaven",
23793 "Thunder",
23794 ),
23795 (
23796 "大畜",
23797 "Dà Chù",
23798 "Great Taming",
23799 "Perseverance; eat away from home",
23800 "Mountain",
23801 "Heaven",
23802 ),
23803 (
23804 "頤",
23805 "Yí",
23806 "Nourishment",
23807 "Perseverance; watch what you nurture",
23808 "Mountain",
23809 "Thunder",
23810 ),
23811 (
23812 "大過",
23813 "Dà Guò",
23814 "Great Exceeding",
23815 "The ridgepole sags; favorable to have somewhere to go",
23816 "Lake",
23817 "Wind",
23818 ),
23819 (
23820 "坎",
23821 "Kǎn",
23822 "The Abysmal",
23823 "Sincerity brings success of the heart",
23824 "Water",
23825 "Water",
23826 ),
23827 (
23828 "離",
23829 "Lí",
23830 "The Clinging",
23831 "Perseverance; success; care for the cow",
23832 "Fire",
23833 "Fire",
23834 ),
23835 (
23836 "咸",
23837 "Xián",
23838 "Influence",
23839 "Success; perseverance; taking a maiden brings fortune",
23840 "Lake",
23841 "Mountain",
23842 ),
23843 (
23844 "恆",
23845 "Héng",
23846 "Duration",
23847 "Success without blame; perseverance; favorable to have somewhere to go",
23848 "Thunder",
23849 "Wind",
23850 ),
23851 (
23852 "遯",
23853 "Dùn",
23854 "Retreat",
23855 "Success; small perseverance",
23856 "Heaven",
23857 "Mountain",
23858 ),
23859 (
23860 "大壯",
23861 "Dà Zhuàng",
23862 "Great Power",
23863 "Perseverance",
23864 "Thunder",
23865 "Heaven",
23866 ),
23867 (
23868 "晉",
23869 "Jìn",
23870 "Progress",
23871 "The powerful prince is honored with horses",
23872 "Fire",
23873 "Earth",
23874 ),
23875 (
23876 "明夷",
23877 "Míng Yí",
23878 "Darkening of the Light",
23879 "Perseverance in adversity",
23880 "Earth",
23881 "Fire",
23882 ),
23883 (
23884 "家人",
23885 "Jiā Rén",
23886 "The Family",
23887 "Perseverance of the woman",
23888 "Wind",
23889 "Fire",
23890 ),
23891 (
23892 "睽",
23893 "Kuí",
23894 "Opposition",
23895 "Good fortune in small matters",
23896 "Fire",
23897 "Lake",
23898 ),
23899 (
23900 "蹇",
23901 "Jiǎn",
23902 "Obstruction",
23903 "Southwest favorable; northeast unfavorable; see the great person",
23904 "Water",
23905 "Mountain",
23906 ),
23907 (
23908 "解",
23909 "Xiè",
23910 "Deliverance",
23911 "Southwest favorable; return brings fortune; haste brings fortune",
23912 "Thunder",
23913 "Water",
23914 ),
23915 (
23916 "損",
23917 "Sǔn",
23918 "Decrease",
23919 "Sincerity; supreme fortune; persistence; favorable to undertake",
23920 "Mountain",
23921 "Lake",
23922 ),
23923 (
23924 "益",
23925 "Yì",
23926 "Increase",
23927 "Favorable to undertake and cross the great water",
23928 "Wind",
23929 "Thunder",
23930 ),
23931 (
23932 "夬",
23933 "Guài",
23934 "Breakthrough",
23935 "Proclaim at the king's court; sincerity in danger",
23936 "Lake",
23937 "Heaven",
23938 ),
23939 (
23940 "姤",
23941 "Gòu",
23942 "Coming to Meet",
23943 "The maiden is powerful; don't marry such a maiden",
23944 "Heaven",
23945 "Wind",
23946 ),
23947 (
23948 "萃",
23949 "Cuì",
23950 "Gathering",
23951 "Success; the king approaches his temple; see the great person",
23952 "Lake",
23953 "Earth",
23954 ),
23955 (
23956 "升",
23957 "Shēng",
23958 "Pushing Upward",
23959 "Supreme success; see the great person; don't worry",
23960 "Earth",
23961 "Wind",
23962 ),
23963 (
23964 "困",
23965 "Kùn",
23966 "Oppression",
23967 "Success; perseverance of the great person; no blame",
23968 "Lake",
23969 "Water",
23970 ),
23971 (
23972 "井",
23973 "Jǐng",
23974 "The Well",
23975 "The town may change but not the well",
23976 "Water",
23977 "Wind",
23978 ),
23979 (
23980 "革",
23981 "Gé",
23982 "Revolution",
23983 "On your own day you are believed; great success",
23984 "Lake",
23985 "Fire",
23986 ),
23987 (
23988 "鼎",
23989 "Dǐng",
23990 "The Cauldron",
23991 "Supreme good fortune; success",
23992 "Fire",
23993 "Wind",
23994 ),
23995 (
23996 "震",
23997 "Zhèn",
23998 "The Arousing",
23999 "Success; thunder comes with fright; laughing and talking after",
24000 "Thunder",
24001 "Thunder",
24002 ),
24003 (
24004 "艮",
24005 "Gèn",
24006 "Keeping Still",
24007 "Keep your back still; go into the courtyard without seeing anyone",
24008 "Mountain",
24009 "Mountain",
24010 ),
24011 (
24012 "漸",
24013 "Jiàn",
24014 "Development",
24015 "The maiden is given in marriage; good fortune; perseverance",
24016 "Wind",
24017 "Mountain",
24018 ),
24019 (
24020 "歸妹",
24021 "Guī Mèi",
24022 "The Marrying Maiden",
24023 "Undertakings bring misfortune",
24024 "Thunder",
24025 "Lake",
24026 ),
24027 (
24028 "豐",
24029 "Fēng",
24030 "Abundance",
24031 "Success; the king attains it; don't worry; be like the sun at noon",
24032 "Thunder",
24033 "Fire",
24034 ),
24035 (
24036 "旅",
24037 "Lǚ",
24038 "The Wanderer",
24039 "Success through smallness; perseverance brings fortune",
24040 "Fire",
24041 "Mountain",
24042 ),
24043 (
24044 "巽",
24045 "Xùn",
24046 "The Gentle",
24047 "Success through small things; favorable to have somewhere to go",
24048 "Wind",
24049 "Wind",
24050 ),
24051 (
24052 "兌",
24053 "Duì",
24054 "The Joyous",
24055 "Success; perseverance",
24056 "Lake",
24057 "Lake",
24058 ),
24059 (
24060 "渙",
24061 "Huàn",
24062 "Dispersion",
24063 "Success; the king approaches his temple; cross the great water",
24064 "Wind",
24065 "Water",
24066 ),
24067 (
24068 "節",
24069 "Jié",
24070 "Limitation",
24071 "Success; bitter limitation should not be persevered in",
24072 "Water",
24073 "Lake",
24074 ),
24075 (
24076 "中孚",
24077 "Zhōng Fú",
24078 "Inner Truth",
24079 "Pigs and fishes; good fortune; cross the great water",
24080 "Wind",
24081 "Lake",
24082 ),
24083 (
24084 "小過",
24085 "Xiǎo Guò",
24086 "Small Exceeding",
24087 "Success; perseverance; small things yes, great things no",
24088 "Thunder",
24089 "Mountain",
24090 ),
24091 (
24092 "既濟",
24093 "Jì Jì",
24094 "After Completion",
24095 "Success in small matters; perseverance; good at start, disorder at end",
24096 "Water",
24097 "Fire",
24098 ),
24099 (
24100 "未濟",
24101 "Wèi Jì",
24102 "Before Completion",
24103 "Success; the young fox almost across; tail gets wet; no goal",
24104 "Fire",
24105 "Water",
24106 ),
24107];
24108
24109fn binary_to_king_wen(binary: u8) -> u8 {
24110 const KING_WEN_ORDER: [u8; 64] = [
24113 1, 43, 14, 34, 9, 5, 26, 11, 10, 58, 38, 54, 61, 60, 41, 19, 13, 49, 30, 55, 37, 63, 22,
24114 36, 25, 17, 21, 51, 42, 3, 27, 24, 44, 28, 50, 32, 57, 48, 18, 46, 6, 47, 64, 40, 59, 29,
24115 4, 7, 33, 31, 56, 62, 53, 39, 52, 15, 12, 45, 35, 16, 20, 8, 23, 2,
24116 ];
24117 KING_WEN_ORDER[binary as usize] - 1
24118}
24119
24120fn hebrew_gematria(c: char) -> i64 {
24121 match c {
24122 'א' | 'A' | 'a' => 1,
24123 'ב' | 'B' | 'b' => 2,
24124 'ג' | 'G' | 'g' => 3,
24125 'ד' | 'D' | 'd' => 4,
24126 'ה' | 'H' | 'h' => 5,
24127 'ו' | 'V' | 'v' | 'W' | 'w' => 6,
24128 'ז' | 'Z' | 'z' => 7,
24129 'ח' => 8,
24130 'ט' => 9,
24131 'י' | 'Y' | 'y' | 'I' | 'i' | 'J' | 'j' => 10,
24132 'כ' | 'K' | 'k' => 20,
24133 'ל' | 'L' | 'l' => 30,
24134 'מ' | 'M' | 'm' => 40,
24135 'נ' | 'N' | 'n' => 50,
24136 'ס' | 'S' | 's' | 'X' | 'x' => 60,
24137 'ע' | 'O' | 'o' => 70,
24138 'פ' | 'P' | 'p' | 'F' | 'f' => 80,
24139 'צ' => 90,
24140 'ק' | 'Q' | 'q' => 100,
24141 'ר' | 'R' | 'r' => 200,
24142 'ש' => 300,
24143 'ת' | 'T' | 't' => 400,
24144 'ך' => 500, 'ם' => 600, 'ן' => 700, 'ף' => 800, 'ץ' | 'C' | 'c' => 900, 'E' | 'e' => 5, 'U' | 'u' => 6, _ => 0,
24152 }
24153}
24154
24155fn greek_isopsephy(c: char) -> i64 {
24156 match c {
24157 'Α' | 'α' | 'A' | 'a' => 1,
24158 'Β' | 'β' | 'B' | 'b' => 2,
24159 'Γ' | 'γ' | 'G' | 'g' => 3,
24160 'Δ' | 'δ' | 'D' | 'd' => 4,
24161 'Ε' | 'ε' | 'E' | 'e' => 5,
24162 'Ϛ' | 'ϛ' => 6, 'Ζ' | 'ζ' | 'Z' | 'z' => 7,
24164 'Η' | 'η' | 'H' | 'h' => 8,
24165 'Θ' | 'θ' => 9,
24166 'Ι' | 'ι' | 'I' | 'i' => 10,
24167 'Κ' | 'κ' | 'K' | 'k' => 20,
24168 'Λ' | 'λ' | 'L' | 'l' => 30,
24169 'Μ' | 'μ' | 'M' | 'm' => 40,
24170 'Ν' | 'ν' | 'N' | 'n' => 50,
24171 'Ξ' | 'ξ' | 'X' | 'x' => 60,
24172 'Ο' | 'ο' | 'O' | 'o' => 70,
24173 'Π' | 'π' | 'P' | 'p' => 80,
24174 'Ϙ' | 'ϙ' | 'Q' | 'q' => 90, 'Ρ' | 'ρ' | 'R' | 'r' => 100,
24176 'Σ' | 'σ' | 'ς' | 'S' | 's' => 200,
24177 'Τ' | 'τ' | 'T' | 't' => 300,
24178 'Υ' | 'υ' | 'U' | 'u' | 'Y' | 'y' => 400,
24179 'Φ' | 'φ' | 'F' | 'f' => 500,
24180 'Χ' | 'χ' | 'C' | 'c' => 600,
24181 'Ψ' | 'ψ' => 700,
24182 'Ω' | 'ω' | 'W' | 'w' => 800,
24183 'Ϡ' | 'ϡ' => 900, 'J' | 'j' => 10, 'V' | 'v' => 400, _ => 0,
24187 }
24188}
24189
24190fn arabic_abjad(c: char) -> i64 {
24191 match c {
24192 'ا' | 'أ' | 'إ' | 'آ' | 'A' | 'a' => 1,
24193 'ب' | 'B' | 'b' => 2,
24194 'ج' | 'J' | 'j' | 'G' | 'g' => 3,
24195 'د' | 'D' | 'd' => 4,
24196 'ه' | 'H' | 'h' => 5,
24197 'و' | 'W' | 'w' | 'V' | 'v' => 6,
24198 'ز' | 'Z' | 'z' => 7,
24199 'ح' => 8,
24200 'ط' => 9,
24201 'ي' | 'Y' | 'y' | 'I' | 'i' => 10,
24202 'ك' | 'K' | 'k' => 20,
24203 'ل' | 'L' | 'l' => 30,
24204 'م' | 'M' | 'm' => 40,
24205 'ن' | 'N' | 'n' => 50,
24206 'س' | 'S' | 's' => 60,
24207 'ع' | 'E' | 'e' => 70,
24208 'ف' | 'F' | 'f' => 80,
24209 'ص' => 90,
24210 'ق' | 'Q' | 'q' => 100,
24211 'ر' | 'R' | 'r' => 200,
24212 'ش' => 300,
24213 'ت' | 'T' | 't' => 400,
24214 'ث' => 500,
24215 'خ' | 'X' | 'x' => 600,
24216 'ذ' => 700,
24217 'ض' => 800,
24218 'ظ' => 900,
24219 'غ' => 1000,
24220 'C' | 'c' => 600, 'O' | 'o' => 70, 'P' | 'p' => 80, 'U' | 'u' => 6, _ => 0,
24225 }
24226}
24227
24228fn register_color(interp: &mut Interpreter) {
24235 define(interp, "rgb", Some(3), |_, args| {
24241 let r = match &args[0] {
24242 Value::Int(n) => (*n).clamp(0, 255) as u8,
24243 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
24244 _ => return Err(RuntimeError::new("rgb() requires numbers")),
24245 };
24246 let g = match &args[1] {
24247 Value::Int(n) => (*n).clamp(0, 255) as u8,
24248 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
24249 _ => return Err(RuntimeError::new("rgb() requires numbers")),
24250 };
24251 let b = match &args[2] {
24252 Value::Int(n) => (*n).clamp(0, 255) as u8,
24253 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
24254 _ => return Err(RuntimeError::new("rgb() requires numbers")),
24255 };
24256 let mut map = std::collections::HashMap::new();
24257 map.insert("r".to_string(), Value::Int(r as i64));
24258 map.insert("g".to_string(), Value::Int(g as i64));
24259 map.insert("b".to_string(), Value::Int(b as i64));
24260 map.insert(
24261 "hex".to_string(),
24262 Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))),
24263 );
24264 Ok(Value::Map(Rc::new(RefCell::new(map))))
24265 });
24266
24267 define(interp, "hex_to_rgb", Some(1), |_, args| {
24269 let hex = match &args[0] {
24270 Value::String(s) => s.to_string(),
24271 _ => return Err(RuntimeError::new("hex_to_rgb requires string")),
24272 };
24273 let hex = hex.trim_start_matches('#');
24274 if hex.len() != 6 {
24275 return Err(RuntimeError::new("hex_to_rgb requires 6 character hex"));
24276 }
24277 let r = u8::from_str_radix(&hex[0..2], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24278 let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24279 let b = u8::from_str_radix(&hex[4..6], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24280 let mut map = std::collections::HashMap::new();
24281 map.insert("r".to_string(), Value::Int(r as i64));
24282 map.insert("g".to_string(), Value::Int(g as i64));
24283 map.insert("b".to_string(), Value::Int(b as i64));
24284 Ok(Value::Map(Rc::new(RefCell::new(map))))
24285 });
24286
24287 define(interp, "rgb_to_hsl", Some(3), |_, args| {
24289 let r = match &args[0] {
24290 Value::Int(n) => *n as f64 / 255.0,
24291 Value::Float(f) => *f / 255.0,
24292 _ => return Err(RuntimeError::new("requires numbers")),
24293 };
24294 let g = match &args[1] {
24295 Value::Int(n) => *n as f64 / 255.0,
24296 Value::Float(f) => *f / 255.0,
24297 _ => return Err(RuntimeError::new("requires numbers")),
24298 };
24299 let b = match &args[2] {
24300 Value::Int(n) => *n as f64 / 255.0,
24301 Value::Float(f) => *f / 255.0,
24302 _ => return Err(RuntimeError::new("requires numbers")),
24303 };
24304 let max = r.max(g).max(b);
24305 let min = r.min(g).min(b);
24306 let l = (max + min) / 2.0;
24307 let (h, s) = if max == min {
24308 (0.0, 0.0)
24309 } else {
24310 let d = max - min;
24311 let s = if l > 0.5 {
24312 d / (2.0 - max - min)
24313 } else {
24314 d / (max + min)
24315 };
24316 let h = if max == r {
24317 (g - b) / d + if g < b { 6.0 } else { 0.0 }
24318 } else if max == g {
24319 (b - r) / d + 2.0
24320 } else {
24321 (r - g) / d + 4.0
24322 };
24323 (h * 60.0, s)
24324 };
24325 let mut map = std::collections::HashMap::new();
24326 map.insert("h".to_string(), Value::Float(h));
24327 map.insert("s".to_string(), Value::Float(s));
24328 map.insert("l".to_string(), Value::Float(l));
24329 Ok(Value::Map(Rc::new(RefCell::new(map))))
24330 });
24331
24332 define(interp, "complementary", Some(3), |_, args| {
24334 let r = match &args[0] {
24335 Value::Int(n) => *n as u8,
24336 _ => return Err(RuntimeError::new("requires int")),
24337 };
24338 let g = match &args[1] {
24339 Value::Int(n) => *n as u8,
24340 _ => return Err(RuntimeError::new("requires int")),
24341 };
24342 let b = match &args[2] {
24343 Value::Int(n) => *n as u8,
24344 _ => return Err(RuntimeError::new("requires int")),
24345 };
24346 let mut map = std::collections::HashMap::new();
24347 map.insert("r".to_string(), Value::Int(255 - r as i64));
24348 map.insert("g".to_string(), Value::Int(255 - g as i64));
24349 map.insert("b".to_string(), Value::Int(255 - b as i64));
24350 Ok(Value::Map(Rc::new(RefCell::new(map))))
24351 });
24352
24353 define(interp, "wu_xing", Some(1), |_, args| {
24357 let element = match &args[0] {
24358 Value::String(s) => s.to_lowercase(),
24359 _ => return Err(RuntimeError::new("wu_xing requires string")),
24360 };
24361 let (name, chinese, color, hex, direction, season, organ, emotion, planet, animal) =
24362 match element.as_str() {
24363 "wood" | "mu" | "木" => (
24364 "Wood",
24365 "木 (Mù)",
24366 "Green/Azure",
24367 "#228B22",
24368 "East",
24369 "Spring",
24370 "Liver",
24371 "Anger",
24372 "Jupiter",
24373 "Azure Dragon",
24374 ),
24375 "fire" | "huo" | "火" => (
24376 "Fire",
24377 "火 (Huǒ)",
24378 "Red",
24379 "#FF0000",
24380 "South",
24381 "Summer",
24382 "Heart",
24383 "Joy",
24384 "Mars",
24385 "Vermilion Bird",
24386 ),
24387 "earth" | "tu" | "土" => (
24388 "Earth",
24389 "土 (Tǔ)",
24390 "Yellow",
24391 "#FFDB58",
24392 "Center",
24393 "Late Summer",
24394 "Spleen",
24395 "Worry",
24396 "Saturn",
24397 "Yellow Dragon",
24398 ),
24399 "metal" | "jin" | "金" => (
24400 "Metal",
24401 "金 (Jīn)",
24402 "White/Gold",
24403 "#FFD700",
24404 "West",
24405 "Autumn",
24406 "Lung",
24407 "Grief",
24408 "Venus",
24409 "White Tiger",
24410 ),
24411 "water" | "shui" | "水" => (
24412 "Water",
24413 "水 (Shuǐ)",
24414 "Black/Blue",
24415 "#000080",
24416 "North",
24417 "Winter",
24418 "Kidney",
24419 "Fear",
24420 "Mercury",
24421 "Black Tortoise",
24422 ),
24423 _ => {
24424 return Err(RuntimeError::new(
24425 "Unknown element. Use wood/fire/earth/metal/water",
24426 ))
24427 }
24428 };
24429 let mut map = std::collections::HashMap::new();
24430 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24431 map.insert(
24432 "chinese".to_string(),
24433 Value::String(Rc::new(chinese.to_string())),
24434 );
24435 map.insert(
24436 "color".to_string(),
24437 Value::String(Rc::new(color.to_string())),
24438 );
24439 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24440 map.insert(
24441 "direction".to_string(),
24442 Value::String(Rc::new(direction.to_string())),
24443 );
24444 map.insert(
24445 "season".to_string(),
24446 Value::String(Rc::new(season.to_string())),
24447 );
24448 map.insert(
24449 "organ".to_string(),
24450 Value::String(Rc::new(organ.to_string())),
24451 );
24452 map.insert(
24453 "emotion".to_string(),
24454 Value::String(Rc::new(emotion.to_string())),
24455 );
24456 map.insert(
24457 "planet".to_string(),
24458 Value::String(Rc::new(planet.to_string())),
24459 );
24460 map.insert(
24461 "guardian".to_string(),
24462 Value::String(Rc::new(animal.to_string())),
24463 );
24464 Ok(Value::Map(Rc::new(RefCell::new(map))))
24465 });
24466
24467 define(interp, "chakra_color", Some(1), |_, args| {
24471 let chakra = match &args[0] {
24472 Value::String(s) => s.to_lowercase(),
24473 Value::Int(n) => n.to_string(),
24474 _ => return Err(RuntimeError::new("chakra_color requires string or number")),
24475 };
24476 let (name, sanskrit, color, hex, location, freq, element, mantra) = match chakra.as_str() {
24477 "root" | "muladhara" | "1" => (
24478 "Root",
24479 "मूलाधार",
24480 "Red",
24481 "#FF0000",
24482 "Base of spine",
24483 396.0,
24484 "Earth",
24485 "LAM",
24486 ),
24487 "sacral" | "svadhisthana" | "2" => (
24488 "Sacral",
24489 "स्वाधिष्ठान",
24490 "Orange",
24491 "#FF7F00",
24492 "Below navel",
24493 417.0,
24494 "Water",
24495 "VAM",
24496 ),
24497 "solar" | "manipura" | "3" => (
24498 "Solar Plexus",
24499 "मणिपूर",
24500 "Yellow",
24501 "#FFFF00",
24502 "Stomach",
24503 528.0,
24504 "Fire",
24505 "RAM",
24506 ),
24507 "heart" | "anahata" | "4" => (
24508 "Heart",
24509 "अनाहत",
24510 "Green",
24511 "#00FF00",
24512 "Chest",
24513 639.0,
24514 "Air",
24515 "YAM",
24516 ),
24517 "throat" | "vishuddha" | "5" => (
24518 "Throat",
24519 "विशुद्ध",
24520 "Blue",
24521 "#00BFFF",
24522 "Throat",
24523 741.0,
24524 "Ether",
24525 "HAM",
24526 ),
24527 "third_eye" | "ajna" | "6" => (
24528 "Third Eye",
24529 "आज्ञा",
24530 "Indigo",
24531 "#4B0082",
24532 "Forehead",
24533 852.0,
24534 "Light",
24535 "OM",
24536 ),
24537 "crown" | "sahasrara" | "7" => (
24538 "Crown",
24539 "सहस्रार",
24540 "Violet",
24541 "#8B00FF",
24542 "Top of head",
24543 963.0,
24544 "Thought",
24545 "Silence",
24546 ),
24547 _ => {
24548 return Err(RuntimeError::new(
24549 "Unknown chakra. Use root/sacral/solar/heart/throat/third_eye/crown or 1-7",
24550 ))
24551 }
24552 };
24553 let mut map = std::collections::HashMap::new();
24554 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24555 map.insert(
24556 "sanskrit".to_string(),
24557 Value::String(Rc::new(sanskrit.to_string())),
24558 );
24559 map.insert(
24560 "color".to_string(),
24561 Value::String(Rc::new(color.to_string())),
24562 );
24563 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24564 map.insert(
24565 "location".to_string(),
24566 Value::String(Rc::new(location.to_string())),
24567 );
24568 map.insert("frequency_hz".to_string(), Value::Float(freq));
24569 map.insert(
24570 "element".to_string(),
24571 Value::String(Rc::new(element.to_string())),
24572 );
24573 map.insert(
24574 "mantra".to_string(),
24575 Value::String(Rc::new(mantra.to_string())),
24576 );
24577 Ok(Value::Map(Rc::new(RefCell::new(map))))
24578 });
24579
24580 define(interp, "maya_direction", Some(1), |_, args| {
24584 let dir = match &args[0] {
24585 Value::String(s) => s.to_lowercase(),
24586 _ => return Err(RuntimeError::new("requires string")),
24587 };
24588 let (direction, yucatec, color, hex, deity, meaning) = match dir.as_str() {
24589 "east" | "lakin" => (
24590 "East",
24591 "Lak'in",
24592 "Red",
24593 "#FF0000",
24594 "Chac (Red)",
24595 "Sunrise, new beginnings",
24596 ),
24597 "north" | "xaman" => (
24598 "North",
24599 "Xaman",
24600 "White",
24601 "#FFFFFF",
24602 "Chac (White)",
24603 "Ancestors, death",
24604 ),
24605 "west" | "chikin" => (
24606 "West",
24607 "Chik'in",
24608 "Black",
24609 "#000000",
24610 "Chac (Black)",
24611 "Sunset, completion",
24612 ),
24613 "south" | "nohol" => (
24614 "South",
24615 "Nohol",
24616 "Yellow",
24617 "#FFFF00",
24618 "Chac (Yellow)",
24619 "Maize, abundance",
24620 ),
24621 "center" | "yax" => (
24622 "Center",
24623 "Yax",
24624 "Green/Blue",
24625 "#00CED1",
24626 "World Tree",
24627 "Balance",
24628 ),
24629 _ => {
24630 return Err(RuntimeError::new(
24631 "Unknown direction. Use east/north/west/south/center",
24632 ))
24633 }
24634 };
24635 let mut map = std::collections::HashMap::new();
24636 map.insert(
24637 "direction".to_string(),
24638 Value::String(Rc::new(direction.to_string())),
24639 );
24640 map.insert(
24641 "yucatec".to_string(),
24642 Value::String(Rc::new(yucatec.to_string())),
24643 );
24644 map.insert(
24645 "color".to_string(),
24646 Value::String(Rc::new(color.to_string())),
24647 );
24648 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24649 map.insert(
24650 "deity".to_string(),
24651 Value::String(Rc::new(deity.to_string())),
24652 );
24653 map.insert(
24654 "meaning".to_string(),
24655 Value::String(Rc::new(meaning.to_string())),
24656 );
24657 Ok(Value::Map(Rc::new(RefCell::new(map))))
24658 });
24659
24660 define(interp, "orisha_color", Some(1), |_, args| {
24664 let orisha = match &args[0] {
24665 Value::String(s) => s.to_lowercase(),
24666 _ => return Err(RuntimeError::new("requires string")),
24667 };
24668 let (name, colors, hex, domain, day, number) = match orisha.as_str() {
24669 "obatala" | "oxala" => (
24670 "Obatalá",
24671 "White, silver",
24672 "#FFFFFF",
24673 "Creation, purity, wisdom",
24674 "Sunday",
24675 8,
24676 ),
24677 "yemoja" | "yemanja" => (
24678 "Yemọja",
24679 "Blue, white",
24680 "#4169E1",
24681 "Ocean, motherhood",
24682 "Saturday",
24683 7,
24684 ),
24685 "oshun" | "oxum" => (
24686 "Ọṣun",
24687 "Yellow, gold",
24688 "#FFD700",
24689 "Rivers, love, fertility",
24690 "Saturday",
24691 5,
24692 ),
24693 "shango" | "xango" => (
24694 "Ṣàngó",
24695 "Red, white",
24696 "#FF0000",
24697 "Thunder, fire, justice",
24698 "Wednesday",
24699 6,
24700 ),
24701 "ogun" | "ogum" => (
24702 "Ògún",
24703 "Green, black",
24704 "#006400",
24705 "Iron, war, labor",
24706 "Tuesday",
24707 7,
24708 ),
24709 "oya" | "iansa" => (
24710 "Ọya",
24711 "Brown, purple",
24712 "#800020",
24713 "Wind, storms, change",
24714 "Wednesday",
24715 9,
24716 ),
24717 "eshu" | "exu" => (
24718 "Èṣù",
24719 "Red, black",
24720 "#8B0000",
24721 "Crossroads, messages",
24722 "Monday",
24723 3,
24724 ),
24725 _ => {
24726 return Err(RuntimeError::new(
24727 "Unknown Orisha. Use obatala/yemoja/oshun/shango/ogun/oya/eshu",
24728 ))
24729 }
24730 };
24731 let mut map = std::collections::HashMap::new();
24732 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24733 map.insert(
24734 "colors".to_string(),
24735 Value::String(Rc::new(colors.to_string())),
24736 );
24737 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24738 map.insert(
24739 "domain".to_string(),
24740 Value::String(Rc::new(domain.to_string())),
24741 );
24742 map.insert("day".to_string(), Value::String(Rc::new(day.to_string())));
24743 map.insert("number".to_string(), Value::Int(number));
24744 Ok(Value::Map(Rc::new(RefCell::new(map))))
24745 });
24746
24747 define(interp, "nihon_iro", Some(1), |_, args| {
24751 let color = match &args[0] {
24752 Value::String(s) => s.to_lowercase(),
24753 _ => return Err(RuntimeError::new("requires string")),
24754 };
24755 let (name, japanese, hex, meaning, season) = match color.as_str() {
24756 "sakura" => (
24757 "Sakura Pink",
24758 "桜色",
24759 "#FFB7C5",
24760 "Cherry blossoms, transience",
24761 "Spring",
24762 ),
24763 "fuji" => (
24764 "Wisteria",
24765 "藤色",
24766 "#C9A0DC",
24767 "Elegance, nobility",
24768 "Spring",
24769 ),
24770 "moegi" => (
24771 "Young Green",
24772 "萌黄",
24773 "#AACF53",
24774 "New growth, freshness",
24775 "Spring",
24776 ),
24777 "ai" => ("Indigo", "藍色", "#004D99", "Protection, depth", "All"),
24778 "akane" => ("Madder Red", "茜色", "#CF3A24", "Sunset, passion", "Autumn"),
24779 "shiro" => ("White", "白", "#FFFFFF", "Purity, death, sacred", "Winter"),
24780 "kuro" => ("Black", "黒", "#000000", "Formality, mystery", "All"),
24781 "aka" => ("Red", "赤", "#D7003A", "Life force, celebration", "All"),
24782 "murasaki" => ("Purple", "紫", "#884898", "Nobility, spirituality", "All"),
24783 _ => {
24784 return Err(RuntimeError::new(
24785 "Unknown color. Try: sakura/fuji/moegi/ai/akane/shiro/kuro/aka/murasaki",
24786 ))
24787 }
24788 };
24789 let mut map = std::collections::HashMap::new();
24790 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24791 map.insert(
24792 "japanese".to_string(),
24793 Value::String(Rc::new(japanese.to_string())),
24794 );
24795 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24796 map.insert(
24797 "meaning".to_string(),
24798 Value::String(Rc::new(meaning.to_string())),
24799 );
24800 map.insert(
24801 "season".to_string(),
24802 Value::String(Rc::new(season.to_string())),
24803 );
24804 Ok(Value::Map(Rc::new(RefCell::new(map))))
24805 });
24806
24807 define(interp, "islamic_color", Some(1), |_, args| {
24811 let color = match &args[0] {
24812 Value::String(s) => s.to_lowercase(),
24813 _ => return Err(RuntimeError::new("requires string")),
24814 };
24815 let (name, arabic, hex, meaning, usage) = match color.as_str() {
24816 "green" | "akhdar" => (
24817 "Green",
24818 "أخضر",
24819 "#00FF00",
24820 "Paradise, Prophet, life",
24821 "Mosques, Quran, flags",
24822 ),
24823 "white" | "abyad" => (
24824 "White",
24825 "أبيض",
24826 "#FFFFFF",
24827 "Purity, peace, ihram",
24828 "Pilgrimage, burial",
24829 ),
24830 "black" | "aswad" => (
24831 "Black",
24832 "أسود",
24833 "#000000",
24834 "Modesty, Kaaba",
24835 "Kiswah, abaya",
24836 ),
24837 "gold" | "dhahabi" => (
24838 "Gold",
24839 "ذهبي",
24840 "#FFD700",
24841 "Paradise, divine light",
24842 "Calligraphy, decoration",
24843 ),
24844 "blue" | "azraq" => (
24845 "Blue",
24846 "أزرق",
24847 "#0000CD",
24848 "Protection, heaven",
24849 "Tiles, evil eye",
24850 ),
24851 _ => {
24852 return Err(RuntimeError::new(
24853 "Unknown color. Use green/white/black/gold/blue",
24854 ))
24855 }
24856 };
24857 let mut map = std::collections::HashMap::new();
24858 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24859 map.insert(
24860 "arabic".to_string(),
24861 Value::String(Rc::new(arabic.to_string())),
24862 );
24863 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24864 map.insert(
24865 "meaning".to_string(),
24866 Value::String(Rc::new(meaning.to_string())),
24867 );
24868 map.insert(
24869 "usage".to_string(),
24870 Value::String(Rc::new(usage.to_string())),
24871 );
24872 Ok(Value::Map(Rc::new(RefCell::new(map))))
24873 });
24874
24875 define(interp, "thai_day_color", Some(1), |_, args| {
24879 let day = match &args[0] {
24880 Value::String(s) => s.to_lowercase(),
24881 Value::Int(n) => n.to_string(),
24882 _ => return Err(RuntimeError::new("requires string or number")),
24883 };
24884 let (day_name, thai, color, hex, deity) = match day.as_str() {
24885 "sunday" | "0" => ("Sunday", "วันอาทิตย์", "Red", "#FF0000", "Surya"),
24886 "monday" | "1" => ("Monday", "วันจันทร์", "Yellow", "#FFFF00", "Chandra"),
24887 "tuesday" | "2" => ("Tuesday", "วันอังคาร", "Pink", "#FFC0CB", "Mangala"),
24888 "wednesday" | "3" => ("Wednesday", "วันพุธ", "Green", "#00FF00", "Budha"),
24889 "thursday" | "4" => ("Thursday", "วันพฤหัสบดี", "Orange", "#FFA500", "Brihaspati"),
24890 "friday" | "5" => ("Friday", "วันศุกร์", "Blue", "#00BFFF", "Shukra"),
24891 "saturday" | "6" => ("Saturday", "วันเสาร์", "Purple", "#800080", "Shani"),
24892 _ => return Err(RuntimeError::new("Unknown day. Use sunday-saturday or 0-6")),
24893 };
24894 let mut map = std::collections::HashMap::new();
24895 map.insert(
24896 "day".to_string(),
24897 Value::String(Rc::new(day_name.to_string())),
24898 );
24899 map.insert("thai".to_string(), Value::String(Rc::new(thai.to_string())));
24900 map.insert(
24901 "color".to_string(),
24902 Value::String(Rc::new(color.to_string())),
24903 );
24904 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24905 map.insert(
24906 "deity".to_string(),
24907 Value::String(Rc::new(deity.to_string())),
24908 );
24909 Ok(Value::Map(Rc::new(RefCell::new(map))))
24910 });
24911
24912 define(interp, "aboriginal_color", Some(1), |_, args| {
24916 let color = match &args[0] {
24917 Value::String(s) => s.to_lowercase(),
24918 _ => return Err(RuntimeError::new("requires string")),
24919 };
24920 let (name, hex, meaning, source, dreamtime) = match color.as_str() {
24921 "red" | "ochre" => (
24922 "Red Ochre",
24923 "#CC5500",
24924 "Earth, blood, ceremony",
24925 "Hematite",
24926 "Ancestral beings",
24927 ),
24928 "yellow" => (
24929 "Yellow Ochre",
24930 "#D4A017",
24931 "Sun, healing",
24932 "Limonite",
24933 "Sun's journey",
24934 ),
24935 "white" => (
24936 "White",
24937 "#FFFFFF",
24938 "Sky, spirits, mourning",
24939 "Kaolin",
24940 "Sky beings",
24941 ),
24942 "black" => (
24943 "Black",
24944 "#000000",
24945 "Night, formality",
24946 "Charcoal",
24947 "Night, men's business",
24948 ),
24949 "brown" => (
24950 "Brown",
24951 "#8B4513",
24952 "Earth, land",
24953 "Earth pigments",
24954 "Country, connection",
24955 ),
24956 _ => {
24957 return Err(RuntimeError::new(
24958 "Unknown color. Use red/yellow/white/black/brown",
24959 ))
24960 }
24961 };
24962 let mut map = std::collections::HashMap::new();
24963 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24964 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24965 map.insert(
24966 "meaning".to_string(),
24967 Value::String(Rc::new(meaning.to_string())),
24968 );
24969 map.insert(
24970 "source".to_string(),
24971 Value::String(Rc::new(source.to_string())),
24972 );
24973 map.insert(
24974 "dreamtime".to_string(),
24975 Value::String(Rc::new(dreamtime.to_string())),
24976 );
24977 Ok(Value::Map(Rc::new(RefCell::new(map))))
24978 });
24979
24980 define(interp, "celtic_color", Some(1), |_, args| {
24984 let color = match &args[0] {
24985 Value::String(s) => s.to_lowercase(),
24986 _ => return Err(RuntimeError::new("requires string")),
24987 };
24988 let (name, gaelic, hex, meaning, element) = match color.as_str() {
24989 "green" => (
24990 "Green",
24991 "Glas",
24992 "#228B22",
24993 "Nature, fairies, Otherworld",
24994 "Earth",
24995 ),
24996 "white" => ("White", "Bán", "#FFFFFF", "Purity, spirits", "Air"),
24997 "red" => ("Red", "Dearg", "#FF0000", "War, courage, blood", "Fire"),
24998 "black" => (
24999 "Black",
25000 "Dubh",
25001 "#000000",
25002 "Otherworld, death, rebirth",
25003 "Water",
25004 ),
25005 "gold" => ("Gold", "Órga", "#FFD700", "Sun, sovereignty, Lugh", "Fire"),
25006 "silver" => (
25007 "Silver",
25008 "Airgid",
25009 "#C0C0C0",
25010 "Moon, feminine, intuition",
25011 "Water",
25012 ),
25013 _ => {
25014 return Err(RuntimeError::new(
25015 "Unknown color. Use green/white/red/black/gold/silver",
25016 ))
25017 }
25018 };
25019 let mut map = std::collections::HashMap::new();
25020 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25021 map.insert(
25022 "gaelic".to_string(),
25023 Value::String(Rc::new(gaelic.to_string())),
25024 );
25025 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25026 map.insert(
25027 "meaning".to_string(),
25028 Value::String(Rc::new(meaning.to_string())),
25029 );
25030 map.insert(
25031 "element".to_string(),
25032 Value::String(Rc::new(element.to_string())),
25033 );
25034 Ok(Value::Map(Rc::new(RefCell::new(map))))
25035 });
25036
25037 define(interp, "kente_color", Some(1), |_, args| {
25041 let color = match &args[0] {
25042 Value::String(s) => s.to_lowercase(),
25043 _ => return Err(RuntimeError::new("requires string")),
25044 };
25045 let (name, twi, hex, meaning) = match color.as_str() {
25046 "gold" | "yellow" => ("Gold", "Sika Kɔkɔɔ", "#FFD700", "Royalty, wealth, glory"),
25047 "green" => ("Green", "Ahabammono", "#228B22", "Growth, renewal, harvest"),
25048 "blue" => ("Blue", "Bruu", "#0000CD", "Peace, harmony, love"),
25049 "red" => ("Red", "Kɔkɔɔ", "#FF0000", "Blood, sacrifice, power"),
25050 "black" => ("Black", "Tuntum", "#000000", "Maturation, ancestors"),
25051 "white" => ("White", "Fitaa", "#FFFFFF", "Purification, virtue, joy"),
25052 "maroon" => ("Maroon", "Borɔnɔ", "#800000", "Earth, healing, protection"),
25053 _ => {
25054 return Err(RuntimeError::new(
25055 "Unknown color. Use gold/green/blue/red/black/white/maroon",
25056 ))
25057 }
25058 };
25059 let mut map = std::collections::HashMap::new();
25060 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25061 map.insert("twi".to_string(), Value::String(Rc::new(twi.to_string())));
25062 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25063 map.insert(
25064 "meaning".to_string(),
25065 Value::String(Rc::new(meaning.to_string())),
25066 );
25067 Ok(Value::Map(Rc::new(RefCell::new(map))))
25068 });
25069
25070 define(interp, "hindu_color", Some(1), |_, args| {
25074 let color = match &args[0] {
25075 Value::String(s) => s.to_lowercase(),
25076 _ => return Err(RuntimeError::new("requires string")),
25077 };
25078 let (name, hindi, hex, meaning, deities) = match color.as_str() {
25079 "red" | "lal" => (
25080 "Red",
25081 "लाल",
25082 "#FF0000",
25083 "Purity, fertility, love",
25084 "Durga, Lakshmi",
25085 ),
25086 "orange" | "saffron" => (
25087 "Saffron",
25088 "केसरी",
25089 "#FF6600",
25090 "Sacred, renunciation",
25091 "Hanuman",
25092 ),
25093 "yellow" => (
25094 "Yellow",
25095 "पीला",
25096 "#FFFF00",
25097 "Knowledge, learning",
25098 "Vishnu, Saraswati",
25099 ),
25100 "green" => ("Green", "हरा", "#008000", "Life, happiness", "Krishna"),
25101 "white" => (
25102 "White",
25103 "सफ़ेद",
25104 "#FFFFFF",
25105 "Purity, mourning",
25106 "Saraswati, Shiva",
25107 ),
25108 "blue" => (
25109 "Blue",
25110 "नीला",
25111 "#0000FF",
25112 "Divinity, infinity",
25113 "Krishna, Vishnu",
25114 ),
25115 "black" => (
25116 "Black",
25117 "काला",
25118 "#000000",
25119 "Protection from evil",
25120 "Kali, Shani",
25121 ),
25122 _ => {
25123 return Err(RuntimeError::new(
25124 "Unknown color. Use red/orange/yellow/green/white/blue/black",
25125 ))
25126 }
25127 };
25128 let mut map = std::collections::HashMap::new();
25129 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25130 map.insert(
25131 "hindi".to_string(),
25132 Value::String(Rc::new(hindi.to_string())),
25133 );
25134 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25135 map.insert(
25136 "meaning".to_string(),
25137 Value::String(Rc::new(meaning.to_string())),
25138 );
25139 map.insert(
25140 "deities".to_string(),
25141 Value::String(Rc::new(deities.to_string())),
25142 );
25143 Ok(Value::Map(Rc::new(RefCell::new(map))))
25144 });
25145
25146 define(interp, "emotion_color", Some(2), |_, args| {
25150 let emotion = match &args[0] {
25151 Value::String(s) => s.to_lowercase(),
25152 _ => return Err(RuntimeError::new("requires string")),
25153 };
25154 let culture = match &args[1] {
25155 Value::String(s) => s.to_lowercase(),
25156 _ => return Err(RuntimeError::new("requires string")),
25157 };
25158 let (hex, name, reasoning) = match (emotion.as_str(), culture.as_str()) {
25159 ("joy", "western") | ("happy", "western") => {
25160 ("#FFD700", "Gold", "Sunshine = happiness")
25161 }
25162 ("joy", "chinese") | ("happy", "chinese") => ("#FF0000", "Red", "红 = luck, joy"),
25163 ("joy", "japanese") => ("#FFB7C5", "Sakura", "Cherry blossom = fleeting joy"),
25164 ("sadness", "western") | ("sad", "western") => ("#0000CD", "Blue", "'Feeling blue'"),
25165 ("sadness", "chinese") | ("sad", "chinese") => ("#FFFFFF", "White", "白 = mourning"),
25166 ("sadness", "indian") => ("#FFFFFF", "White", "सफ़ेद = mourning"),
25167 ("anger", _) => ("#FF0000", "Red", "Universal heat/fire"),
25168 ("love", "western") => ("#FF69B4", "Pink", "Valentine's hearts"),
25169 ("love", "chinese") | ("love", "indian") => ("#FF0000", "Red", "Red = marriage, love"),
25170 ("peace", "western") => ("#ADD8E6", "Light Blue", "Sky, serenity"),
25171 ("peace", "islamic") => ("#00FF00", "Green", "السلام = paradise"),
25172 ("fear", _) => ("#4B0082", "Indigo", "Deep, mysterious"),
25173 (_, _) => ("#808080", "Grey", "Neutral"),
25174 };
25175 let r = u8::from_str_radix(&hex[1..3], 16).unwrap_or(128);
25176 let g = u8::from_str_radix(&hex[3..5], 16).unwrap_or(128);
25177 let b = u8::from_str_radix(&hex[5..7], 16).unwrap_or(128);
25178 let mut map = std::collections::HashMap::new();
25179 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25180 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25181 map.insert("r".to_string(), Value::Int(r as i64));
25182 map.insert("g".to_string(), Value::Int(g as i64));
25183 map.insert("b".to_string(), Value::Int(b as i64));
25184 map.insert(
25185 "reasoning".to_string(),
25186 Value::String(Rc::new(reasoning.to_string())),
25187 );
25188 Ok(Value::Map(Rc::new(RefCell::new(map))))
25189 });
25190
25191 define(interp, "synesthesia", Some(2), |_, args| {
25193 let culture = match &args[1] {
25194 Value::String(s) => s.to_lowercase(),
25195 _ => return Err(RuntimeError::new("requires culture string")),
25196 };
25197 let (r, g, b, emotion, freq) = match &args[0] {
25198 Value::String(s) => match s.to_lowercase().as_str() {
25199 "joy" | "happy" => (255u8, 215u8, 0u8, "joy", 528.0),
25200 "sadness" | "sad" => (0, 0, 139, "sadness", 396.0),
25201 "anger" => (255, 0, 0, "anger", 417.0),
25202 "fear" => (75, 0, 130, "fear", 369.0),
25203 "love" => (255, 105, 180, "love", 639.0),
25204 "peace" => (135, 206, 235, "peace", 741.0),
25205 _ => (128, 128, 128, "neutral", 432.0),
25206 },
25207 Value::Int(n) => (128, 128, 255, "resonance", *n as f64),
25208 Value::Float(f) => (128, 128, 255, "resonance", *f),
25209 _ => (128, 128, 128, "neutral", 432.0),
25210 };
25211 let cultural_meaning = match culture.as_str() {
25212 "chinese" if r > 200 && g < 100 => "luck/joy (红)",
25213 "japanese" if r > 200 && g < 100 => "vitality (赤)",
25214 "indian" if r > 200 && g < 100 => "shakti/auspicious",
25215 _ => "universal resonance",
25216 };
25217 let chakra = if r > 200 && g < 100 {
25218 "Root"
25219 } else if g > 200 {
25220 "Heart"
25221 } else if b > 200 {
25222 "Throat"
25223 } else {
25224 "Crown"
25225 };
25226 let wu_xing = if r > 200 && g < 100 {
25227 "Fire (火)"
25228 } else if g > 200 {
25229 "Wood (木)"
25230 } else if b > 200 {
25231 "Water (水)"
25232 } else {
25233 "Metal (金)"
25234 };
25235 let mut map = std::collections::HashMap::new();
25236 let mut color_map = std::collections::HashMap::new();
25237 color_map.insert("r".to_string(), Value::Int(r as i64));
25238 color_map.insert("g".to_string(), Value::Int(g as i64));
25239 color_map.insert("b".to_string(), Value::Int(b as i64));
25240 color_map.insert(
25241 "hex".to_string(),
25242 Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))),
25243 );
25244 map.insert(
25245 "color".to_string(),
25246 Value::Map(Rc::new(RefCell::new(color_map))),
25247 );
25248 map.insert(
25249 "emotion".to_string(),
25250 Value::String(Rc::new(emotion.to_string())),
25251 );
25252 map.insert("frequency".to_string(), Value::Float(freq));
25253 map.insert(
25254 "cultural_meaning".to_string(),
25255 Value::String(Rc::new(cultural_meaning.to_string())),
25256 );
25257 map.insert(
25258 "chakra".to_string(),
25259 Value::String(Rc::new(chakra.to_string())),
25260 );
25261 map.insert(
25262 "wu_xing".to_string(),
25263 Value::String(Rc::new(wu_xing.to_string())),
25264 );
25265 Ok(Value::Map(Rc::new(RefCell::new(map))))
25266 });
25267
25268 define(interp, "color_to_sound", Some(3), |_, args| {
25270 let r = match &args[0] {
25271 Value::Int(n) => *n as f64 / 255.0,
25272 Value::Float(f) => *f / 255.0,
25273 _ => return Err(RuntimeError::new("requires numbers")),
25274 };
25275 let g = match &args[1] {
25276 Value::Int(n) => *n as f64 / 255.0,
25277 Value::Float(f) => *f / 255.0,
25278 _ => return Err(RuntimeError::new("requires numbers")),
25279 };
25280 let b = match &args[2] {
25281 Value::Int(n) => *n as f64 / 255.0,
25282 Value::Float(f) => *f / 255.0,
25283 _ => return Err(RuntimeError::new("requires numbers")),
25284 };
25285 let max = r.max(g).max(b);
25286 let min = r.min(g).min(b);
25287 let l = (max + min) / 2.0;
25288 let h = if max == min {
25289 0.0
25290 } else {
25291 let d = max - min;
25292 if max == r {
25293 (g - b) / d + if g < b { 6.0 } else { 0.0 }
25294 } else if max == g {
25295 (b - r) / d + 2.0
25296 } else {
25297 (r - g) / d + 4.0
25298 }
25299 } * 60.0;
25300 let (note, freq) = if h < 30.0 {
25301 ("C", 261.63)
25302 } else if h < 60.0 {
25303 ("G", 392.00)
25304 } else if h < 90.0 {
25305 ("D", 293.66)
25306 } else if h < 120.0 {
25307 ("A", 440.00)
25308 } else if h < 150.0 {
25309 ("E", 329.63)
25310 } else if h < 180.0 {
25311 ("B", 493.88)
25312 } else if h < 210.0 {
25313 ("F#", 369.99)
25314 } else if h < 240.0 {
25315 ("Db", 277.18)
25316 } else if h < 270.0 {
25317 ("Ab", 415.30)
25318 } else if h < 300.0 {
25319 ("Eb", 311.13)
25320 } else if h < 330.0 {
25321 ("Bb", 466.16)
25322 } else {
25323 ("F", 349.23)
25324 };
25325 let octave_shift = ((l - 0.5) * 4.0).round() as i32;
25326 let adjusted_freq = freq * 2.0_f64.powi(octave_shift);
25327 let mut map = std::collections::HashMap::new();
25328 map.insert("note".to_string(), Value::String(Rc::new(note.to_string())));
25329 map.insert("frequency".to_string(), Value::Float(adjusted_freq));
25330 map.insert("hue".to_string(), Value::Float(h));
25331 Ok(Value::Map(Rc::new(RefCell::new(map))))
25332 });
25333
25334 define(interp, "contrast_ratio", Some(6), |_, args| {
25336 fn lum(r: f64, g: f64, b: f64) -> f64 {
25337 fn ch(c: f64) -> f64 {
25338 let c = c / 255.0;
25339 if c <= 0.03928 {
25340 c / 12.92
25341 } else {
25342 ((c + 0.055) / 1.055).powf(2.4)
25343 }
25344 }
25345 0.2126 * ch(r) + 0.7152 * ch(g) + 0.0722 * ch(b)
25346 }
25347 let r1 = match &args[0] {
25348 Value::Int(n) => *n as f64,
25349 Value::Float(f) => *f,
25350 _ => return Err(RuntimeError::new("requires numbers")),
25351 };
25352 let g1 = match &args[1] {
25353 Value::Int(n) => *n as f64,
25354 Value::Float(f) => *f,
25355 _ => return Err(RuntimeError::new("requires numbers")),
25356 };
25357 let b1 = match &args[2] {
25358 Value::Int(n) => *n as f64,
25359 Value::Float(f) => *f,
25360 _ => return Err(RuntimeError::new("requires numbers")),
25361 };
25362 let r2 = match &args[3] {
25363 Value::Int(n) => *n as f64,
25364 Value::Float(f) => *f,
25365 _ => return Err(RuntimeError::new("requires numbers")),
25366 };
25367 let g2 = match &args[4] {
25368 Value::Int(n) => *n as f64,
25369 Value::Float(f) => *f,
25370 _ => return Err(RuntimeError::new("requires numbers")),
25371 };
25372 let b2 = match &args[5] {
25373 Value::Int(n) => *n as f64,
25374 Value::Float(f) => *f,
25375 _ => return Err(RuntimeError::new("requires numbers")),
25376 };
25377 let l1 = lum(r1, g1, b1);
25378 let l2 = lum(r2, g2, b2);
25379 let ratio = if l1 > l2 {
25380 (l1 + 0.05) / (l2 + 0.05)
25381 } else {
25382 (l2 + 0.05) / (l1 + 0.05)
25383 };
25384 let mut map = std::collections::HashMap::new();
25385 map.insert("ratio".to_string(), Value::Float(ratio));
25386 map.insert("aa_normal".to_string(), Value::Bool(ratio >= 4.5));
25387 map.insert("aa_large".to_string(), Value::Bool(ratio >= 3.0));
25388 map.insert("aaa_normal".to_string(), Value::Bool(ratio >= 7.0));
25389 Ok(Value::Map(Rc::new(RefCell::new(map))))
25390 });
25391}
25392
25393fn register_protocol(interp: &mut Interpreter) {
25401 define(interp, "protocol_info", Some(0), |_, _args| {
25403 let mut map = std::collections::HashMap::new();
25404 map.insert(
25405 "http".to_string(),
25406 Value::Map(Rc::new(RefCell::new({
25407 let mut m = std::collections::HashMap::new();
25408 m.insert(
25409 "name".to_string(),
25410 Value::String(Rc::new("HTTP".to_string())),
25411 );
25412 m.insert(
25413 "versions".to_string(),
25414 Value::Array(Rc::new(RefCell::new(vec![
25415 Value::String(Rc::new("1.1".to_string())),
25416 Value::String(Rc::new("2".to_string())),
25417 ]))),
25418 );
25419 m.insert(
25420 "methods".to_string(),
25421 Value::Array(Rc::new(RefCell::new(vec![
25422 Value::String(Rc::new("GET".to_string())),
25423 Value::String(Rc::new("POST".to_string())),
25424 Value::String(Rc::new("PUT".to_string())),
25425 Value::String(Rc::new("DELETE".to_string())),
25426 Value::String(Rc::new("PATCH".to_string())),
25427 Value::String(Rc::new("HEAD".to_string())),
25428 Value::String(Rc::new("OPTIONS".to_string())),
25429 ]))),
25430 );
25431 m
25432 }))),
25433 );
25434 map.insert(
25435 "grpc".to_string(),
25436 Value::Map(Rc::new(RefCell::new({
25437 let mut m = std::collections::HashMap::new();
25438 m.insert(
25439 "name".to_string(),
25440 Value::String(Rc::new("gRPC".to_string())),
25441 );
25442 m.insert(
25443 "streaming_modes".to_string(),
25444 Value::Array(Rc::new(RefCell::new(vec![
25445 Value::String(Rc::new("unary".to_string())),
25446 Value::String(Rc::new("server_streaming".to_string())),
25447 Value::String(Rc::new("client_streaming".to_string())),
25448 Value::String(Rc::new("bidirectional".to_string())),
25449 ]))),
25450 );
25451 m
25452 }))),
25453 );
25454 map.insert(
25455 "websocket".to_string(),
25456 Value::Map(Rc::new(RefCell::new({
25457 let mut m = std::collections::HashMap::new();
25458 m.insert(
25459 "name".to_string(),
25460 Value::String(Rc::new("WebSocket".to_string())),
25461 );
25462 m.insert(
25463 "message_types".to_string(),
25464 Value::Array(Rc::new(RefCell::new(vec![
25465 Value::String(Rc::new("text".to_string())),
25466 Value::String(Rc::new("binary".to_string())),
25467 Value::String(Rc::new("ping".to_string())),
25468 Value::String(Rc::new("pong".to_string())),
25469 Value::String(Rc::new("close".to_string())),
25470 ]))),
25471 );
25472 m
25473 }))),
25474 );
25475 map.insert(
25476 "kafka".to_string(),
25477 Value::Map(Rc::new(RefCell::new({
25478 let mut m = std::collections::HashMap::new();
25479 m.insert(
25480 "name".to_string(),
25481 Value::String(Rc::new("Apache Kafka".to_string())),
25482 );
25483 m.insert(
25484 "acks".to_string(),
25485 Value::Array(Rc::new(RefCell::new(vec![
25486 Value::String(Rc::new("none".to_string())),
25487 Value::String(Rc::new("leader".to_string())),
25488 Value::String(Rc::new("all".to_string())),
25489 ]))),
25490 );
25491 m
25492 }))),
25493 );
25494 map.insert(
25495 "amqp".to_string(),
25496 Value::Map(Rc::new(RefCell::new({
25497 let mut m = std::collections::HashMap::new();
25498 m.insert(
25499 "name".to_string(),
25500 Value::String(Rc::new("AMQP".to_string())),
25501 );
25502 m.insert(
25503 "exchange_types".to_string(),
25504 Value::Array(Rc::new(RefCell::new(vec![
25505 Value::String(Rc::new("direct".to_string())),
25506 Value::String(Rc::new("fanout".to_string())),
25507 Value::String(Rc::new("topic".to_string())),
25508 Value::String(Rc::new("headers".to_string())),
25509 ]))),
25510 );
25511 m
25512 }))),
25513 );
25514 map.insert(
25515 "graphql".to_string(),
25516 Value::Map(Rc::new(RefCell::new({
25517 let mut m = std::collections::HashMap::new();
25518 m.insert(
25519 "name".to_string(),
25520 Value::String(Rc::new("GraphQL".to_string())),
25521 );
25522 m.insert(
25523 "operations".to_string(),
25524 Value::Array(Rc::new(RefCell::new(vec![
25525 Value::String(Rc::new("query".to_string())),
25526 Value::String(Rc::new("mutation".to_string())),
25527 Value::String(Rc::new("subscription".to_string())),
25528 ]))),
25529 );
25530 m
25531 }))),
25532 );
25533 Ok(Value::Map(Rc::new(RefCell::new(map))))
25534 });
25535
25536 define(interp, "http_status_text", Some(1), |_, args| {
25538 let code = match &args[0] {
25539 Value::Int(n) => *n,
25540 _ => {
25541 return Err(RuntimeError::new(
25542 "http_status_text requires integer status code",
25543 ))
25544 }
25545 };
25546 let text = match code {
25547 100 => "Continue",
25548 101 => "Switching Protocols",
25549 200 => "OK",
25550 201 => "Created",
25551 202 => "Accepted",
25552 204 => "No Content",
25553 301 => "Moved Permanently",
25554 302 => "Found",
25555 304 => "Not Modified",
25556 307 => "Temporary Redirect",
25557 308 => "Permanent Redirect",
25558 400 => "Bad Request",
25559 401 => "Unauthorized",
25560 403 => "Forbidden",
25561 404 => "Not Found",
25562 405 => "Method Not Allowed",
25563 409 => "Conflict",
25564 422 => "Unprocessable Entity",
25565 429 => "Too Many Requests",
25566 500 => "Internal Server Error",
25567 502 => "Bad Gateway",
25568 503 => "Service Unavailable",
25569 504 => "Gateway Timeout",
25570 _ => "Unknown",
25571 };
25572 Ok(Value::String(Rc::new(text.to_string())))
25573 });
25574
25575 define(interp, "http_status_type", Some(1), |_, args| {
25577 let code = match &args[0] {
25578 Value::Int(n) => *n,
25579 _ => {
25580 return Err(RuntimeError::new(
25581 "http_status_type requires integer status code",
25582 ))
25583 }
25584 };
25585 let status_type = match code {
25586 100..=199 => "informational",
25587 200..=299 => "success",
25588 300..=399 => "redirect",
25589 400..=499 => "client_error",
25590 500..=599 => "server_error",
25591 _ => "unknown",
25592 };
25593 Ok(Value::String(Rc::new(status_type.to_string())))
25594 });
25595
25596 define(interp, "grpc_status_text", Some(1), |_, args| {
25598 let code = match &args[0] {
25599 Value::Int(n) => *n,
25600 _ => {
25601 return Err(RuntimeError::new(
25602 "grpc_status_text requires integer status code",
25603 ))
25604 }
25605 };
25606 let text = match code {
25607 0 => "OK",
25608 1 => "CANCELLED",
25609 2 => "UNKNOWN",
25610 3 => "INVALID_ARGUMENT",
25611 4 => "DEADLINE_EXCEEDED",
25612 5 => "NOT_FOUND",
25613 6 => "ALREADY_EXISTS",
25614 7 => "PERMISSION_DENIED",
25615 8 => "RESOURCE_EXHAUSTED",
25616 9 => "FAILED_PRECONDITION",
25617 10 => "ABORTED",
25618 11 => "OUT_OF_RANGE",
25619 12 => "UNIMPLEMENTED",
25620 13 => "INTERNAL",
25621 14 => "UNAVAILABLE",
25622 15 => "DATA_LOSS",
25623 16 => "UNAUTHENTICATED",
25624 _ => "UNKNOWN",
25625 };
25626 Ok(Value::String(Rc::new(text.to_string())))
25627 });
25628
25629 define(interp, "url_parse", Some(1), |_, args| {
25631 let url_str = match &args[0] {
25632 Value::String(s) => s.as_str().to_string(),
25633 _ => return Err(RuntimeError::new("url_parse requires string URL")),
25634 };
25635
25636 let mut map = std::collections::HashMap::new();
25638
25639 let (scheme, rest) = if let Some(pos) = url_str.find("://") {
25641 (url_str[..pos].to_string(), &url_str[pos + 3..])
25642 } else {
25643 return Err(RuntimeError::new("Invalid URL: missing scheme"));
25644 };
25645 map.insert("scheme".to_string(), Value::String(Rc::new(scheme)));
25646
25647 let (authority, path_and_rest) = if let Some(pos) = rest.find('/') {
25649 (&rest[..pos], &rest[pos..])
25650 } else {
25651 (rest, "/")
25652 };
25653
25654 let (host, port) = if let Some(pos) = authority.rfind(':') {
25656 if let Ok(p) = authority[pos + 1..].parse::<i64>() {
25657 (authority[..pos].to_string(), Some(p))
25658 } else {
25659 (authority.to_string(), None)
25660 }
25661 } else {
25662 (authority.to_string(), None)
25663 };
25664 map.insert("host".to_string(), Value::String(Rc::new(host)));
25665 map.insert(
25666 "port".to_string(),
25667 port.map(Value::Int).unwrap_or(Value::Null),
25668 );
25669
25670 let (path, query) = if let Some(pos) = path_and_rest.find('?') {
25672 (&path_and_rest[..pos], Some(&path_and_rest[pos + 1..]))
25673 } else {
25674 (path_and_rest, None)
25675 };
25676 map.insert("path".to_string(), Value::String(Rc::new(path.to_string())));
25677 map.insert(
25678 "query".to_string(),
25679 query
25680 .map(|q| Value::String(Rc::new(q.to_string())))
25681 .unwrap_or(Value::Null),
25682 );
25683
25684 Ok(Value::Map(Rc::new(RefCell::new(map))))
25685 });
25686
25687 define(interp, "url_encode", Some(1), |_, args| {
25689 let s = match &args[0] {
25690 Value::String(s) => s.as_str(),
25691 _ => return Err(RuntimeError::new("url_encode requires string")),
25692 };
25693 let mut result = String::new();
25694 for c in s.chars() {
25695 match c {
25696 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '~' => {
25697 result.push(c);
25698 }
25699 ' ' => result.push_str("%20"),
25700 _ => {
25701 for b in c.to_string().as_bytes() {
25702 result.push_str(&format!("%{:02X}", b));
25703 }
25704 }
25705 }
25706 }
25707 Ok(Value::String(Rc::new(result)))
25708 });
25709
25710 define(interp, "url_decode", Some(1), |_, args| {
25712 let s = match &args[0] {
25713 Value::String(s) => s.as_str().to_string(),
25714 _ => return Err(RuntimeError::new("url_decode requires string")),
25715 };
25716 let mut result = Vec::new();
25717 let bytes = s.as_bytes();
25718 let mut i = 0;
25719 while i < bytes.len() {
25720 if bytes[i] == b'%' && i + 2 < bytes.len() {
25721 if let Ok(b) =
25722 u8::from_str_radix(&String::from_utf8_lossy(&bytes[i + 1..i + 3]), 16)
25723 {
25724 result.push(b);
25725 i += 3;
25726 continue;
25727 }
25728 } else if bytes[i] == b'+' {
25729 result.push(b' ');
25730 i += 1;
25731 continue;
25732 }
25733 result.push(bytes[i]);
25734 i += 1;
25735 }
25736 Ok(Value::String(Rc::new(
25737 String::from_utf8_lossy(&result).to_string(),
25738 )))
25739 });
25740
25741 define(interp, "ws_close_code_text", Some(1), |_, args| {
25743 let code = match &args[0] {
25744 Value::Int(n) => *n,
25745 _ => {
25746 return Err(RuntimeError::new(
25747 "ws_close_code_text requires integer code",
25748 ))
25749 }
25750 };
25751 let text = match code {
25752 1000 => "Normal Closure",
25753 1001 => "Going Away",
25754 1002 => "Protocol Error",
25755 1003 => "Unsupported Data",
25756 1005 => "No Status Received",
25757 1006 => "Abnormal Closure",
25758 1007 => "Invalid Payload Data",
25759 1008 => "Policy Violation",
25760 1009 => "Message Too Big",
25761 1010 => "Missing Extension",
25762 1011 => "Internal Error",
25763 1015 => "TLS Handshake Failure",
25764 _ => "Unknown",
25765 };
25766 Ok(Value::String(Rc::new(text.to_string())))
25767 });
25768
25769 define(interp, "mime_type", Some(1), |_, args| {
25771 let ext = match &args[0] {
25772 Value::String(s) => s.as_str().to_lowercase(),
25773 _ => return Err(RuntimeError::new("mime_type requires string extension")),
25774 };
25775 let ext = ext.trim_start_matches('.');
25776 let mime = match ext {
25777 "html" | "htm" => "text/html",
25778 "css" => "text/css",
25779 "js" | "mjs" => "text/javascript",
25780 "json" => "application/json",
25781 "xml" => "application/xml",
25782 "txt" => "text/plain",
25783 "csv" => "text/csv",
25784 "png" => "image/png",
25785 "jpg" | "jpeg" => "image/jpeg",
25786 "gif" => "image/gif",
25787 "svg" => "image/svg+xml",
25788 "webp" => "image/webp",
25789 "ico" => "image/x-icon",
25790 "pdf" => "application/pdf",
25791 "zip" => "application/zip",
25792 "gz" | "gzip" => "application/gzip",
25793 "mp3" => "audio/mpeg",
25794 "mp4" => "video/mp4",
25795 "webm" => "video/webm",
25796 "woff" => "font/woff",
25797 "woff2" => "font/woff2",
25798 "ttf" => "font/ttf",
25799 "otf" => "font/otf",
25800 "wasm" => "application/wasm",
25801 "proto" => "application/protobuf",
25802 _ => "application/octet-stream",
25803 };
25804 Ok(Value::String(Rc::new(mime.to_string())))
25805 });
25806
25807 define(interp, "content_type_parse", Some(1), |_, args| {
25809 let ct = match &args[0] {
25810 Value::String(s) => s.as_str().to_string(),
25811 _ => return Err(RuntimeError::new("content_type_parse requires string")),
25812 };
25813 let mut map = std::collections::HashMap::new();
25814 let parts: Vec<&str> = ct.split(';').collect();
25815 map.insert(
25816 "media_type".to_string(),
25817 Value::String(Rc::new(parts[0].trim().to_string())),
25818 );
25819
25820 let mut params = std::collections::HashMap::new();
25821 for part in parts.iter().skip(1) {
25822 let kv: Vec<&str> = part.splitn(2, '=').collect();
25823 if kv.len() == 2 {
25824 let key = kv[0].trim().to_lowercase();
25825 let value = kv[1].trim().trim_matches('"').to_string();
25826 params.insert(key, Value::String(Rc::new(value)));
25827 }
25828 }
25829 map.insert(
25830 "params".to_string(),
25831 Value::Map(Rc::new(RefCell::new(params))),
25832 );
25833 Ok(Value::Map(Rc::new(RefCell::new(map))))
25834 });
25835}
25836
25837thread_local! {
25849 static TOOL_REGISTRY: RefCell<HashMap<String, ToolDefinition>> = RefCell::new(HashMap::new());
25850 static AGENT_MEMORY: RefCell<HashMap<String, AgentSession>> = RefCell::new(HashMap::new());
25851 static STATE_MACHINES: RefCell<HashMap<String, StateMachine>> = RefCell::new(HashMap::new());
25852}
25853
25854#[derive(Clone)]
25856struct ToolDefinition {
25857 name: String,
25858 description: String,
25859 parameters: Vec<ToolParameter>,
25860 returns: String,
25861 evidence_in: Evidence,
25862 evidence_out: Evidence,
25863 handler: Option<Rc<Function>>,
25864}
25865
25866#[derive(Clone)]
25867struct ToolParameter {
25868 name: String,
25869 param_type: String,
25870 description: String,
25871 required: bool,
25872 evidence: Evidence,
25873}
25874
25875#[derive(Clone)]
25877struct AgentSession {
25878 id: String,
25879 context: HashMap<String, Value>,
25880 history: Vec<(String, String)>, created_at: u64,
25882 last_accessed: u64,
25883}
25884
25885#[derive(Clone)]
25887struct StateMachine {
25888 name: String,
25889 current_state: String,
25890 states: Vec<String>,
25891 transitions: HashMap<String, Vec<(String, String)>>, history: Vec<(String, u64)>, }
25894
25895fn register_agent_tools(interp: &mut Interpreter) {
25900 define(interp, "tool_define", None, |_interp, args| {
25903 if args.len() < 4 {
25904 return Err(RuntimeError::new(
25905 "tool_define requires at least 4 arguments: name, description, params, returns",
25906 ));
25907 }
25908
25909 let name = match &args[0] {
25910 Value::String(s) => s.as_str().to_string(),
25911 _ => return Err(RuntimeError::new("tool name must be a string")),
25912 };
25913
25914 let description = match &args[1] {
25915 Value::String(s) => s.as_str().to_string(),
25916 _ => return Err(RuntimeError::new("tool description must be a string")),
25917 };
25918
25919 let params = match &args[2] {
25921 Value::Array(arr) => {
25922 let arr = arr.borrow();
25923 let mut params = Vec::new();
25924 for param in arr.iter() {
25925 if let Value::Map(map) = param {
25926 let map = map.borrow();
25927 let param_name = map
25928 .get("name")
25929 .and_then(|v| {
25930 if let Value::String(s) = v {
25931 Some(s.as_str().to_string())
25932 } else {
25933 None
25934 }
25935 })
25936 .unwrap_or_default();
25937 let param_type = map
25938 .get("type")
25939 .and_then(|v| {
25940 if let Value::String(s) = v {
25941 Some(s.as_str().to_string())
25942 } else {
25943 None
25944 }
25945 })
25946 .unwrap_or_else(|| "any".to_string());
25947 let param_desc = map
25948 .get("description")
25949 .and_then(|v| {
25950 if let Value::String(s) = v {
25951 Some(s.as_str().to_string())
25952 } else {
25953 None
25954 }
25955 })
25956 .unwrap_or_default();
25957 let required = map
25958 .get("required")
25959 .and_then(|v| {
25960 if let Value::Bool(b) = v {
25961 Some(*b)
25962 } else {
25963 None
25964 }
25965 })
25966 .unwrap_or(true);
25967 let evidence_str = map
25968 .get("evidence")
25969 .and_then(|v| {
25970 if let Value::String(s) = v {
25971 Some(s.as_str())
25972 } else {
25973 None
25974 }
25975 })
25976 .unwrap_or("~");
25977 let evidence = match evidence_str {
25978 "!" => Evidence::Known,
25979 "?" => Evidence::Uncertain,
25980 "~" => Evidence::Reported,
25981 "‽" => Evidence::Paradox,
25982 _ => Evidence::Reported,
25983 };
25984 params.push(ToolParameter {
25985 name: param_name,
25986 param_type,
25987 description: param_desc,
25988 required,
25989 evidence,
25990 });
25991 }
25992 }
25993 params
25994 }
25995 _ => Vec::new(),
25996 };
25997
25998 let returns = match &args[3] {
25999 Value::String(s) => s.as_str().to_string(),
26000 _ => "any".to_string(),
26001 };
26002
26003 let handler = if args.len() > 4 {
26004 match &args[4] {
26005 Value::Function(f) => Some(f.clone()),
26006 _ => None,
26007 }
26008 } else {
26009 None
26010 };
26011
26012 let tool = ToolDefinition {
26013 name: name.clone(),
26014 description,
26015 parameters: params,
26016 returns,
26017 evidence_in: Evidence::Reported,
26018 evidence_out: Evidence::Reported,
26019 handler,
26020 };
26021
26022 TOOL_REGISTRY.with(|registry| {
26023 registry.borrow_mut().insert(name.clone(), tool);
26024 });
26025
26026 Ok(Value::String(Rc::new(name)))
26027 });
26028
26029 define(interp, "tool_list", Some(0), |_, _args| {
26031 let tools: Vec<Value> = TOOL_REGISTRY.with(|registry| {
26032 registry
26033 .borrow()
26034 .keys()
26035 .map(|k| Value::String(Rc::new(k.clone())))
26036 .collect()
26037 });
26038 Ok(Value::Array(Rc::new(RefCell::new(tools))))
26039 });
26040
26041 define(interp, "tool_get", Some(1), |_, args| {
26043 let name = match &args[0] {
26044 Value::String(s) => s.as_str().to_string(),
26045 _ => return Err(RuntimeError::new("tool_get requires string name")),
26046 };
26047
26048 TOOL_REGISTRY.with(|registry| {
26049 if let Some(tool) = registry.borrow().get(&name) {
26050 let mut map = HashMap::new();
26051 map.insert(
26052 "name".to_string(),
26053 Value::String(Rc::new(tool.name.clone())),
26054 );
26055 map.insert(
26056 "description".to_string(),
26057 Value::String(Rc::new(tool.description.clone())),
26058 );
26059 map.insert(
26060 "returns".to_string(),
26061 Value::String(Rc::new(tool.returns.clone())),
26062 );
26063
26064 let params: Vec<Value> = tool
26065 .parameters
26066 .iter()
26067 .map(|p| {
26068 let mut pmap = HashMap::new();
26069 pmap.insert("name".to_string(), Value::String(Rc::new(p.name.clone())));
26070 pmap.insert(
26071 "type".to_string(),
26072 Value::String(Rc::new(p.param_type.clone())),
26073 );
26074 pmap.insert(
26075 "description".to_string(),
26076 Value::String(Rc::new(p.description.clone())),
26077 );
26078 pmap.insert("required".to_string(), Value::Bool(p.required));
26079 Value::Map(Rc::new(RefCell::new(pmap)))
26080 })
26081 .collect();
26082 map.insert(
26083 "parameters".to_string(),
26084 Value::Array(Rc::new(RefCell::new(params))),
26085 );
26086
26087 Ok(Value::Map(Rc::new(RefCell::new(map))))
26088 } else {
26089 Ok(Value::Null)
26090 }
26091 })
26092 });
26093
26094 define(interp, "tool_schema", Some(1), |_, args| {
26096 let name = match &args[0] {
26097 Value::String(s) => s.as_str().to_string(),
26098 _ => return Err(RuntimeError::new("tool_schema requires string name")),
26099 };
26100
26101 TOOL_REGISTRY.with(|registry| {
26102 if let Some(tool) = registry.borrow().get(&name) {
26103 let mut schema = HashMap::new();
26105 schema.insert(
26106 "type".to_string(),
26107 Value::String(Rc::new("function".to_string())),
26108 );
26109
26110 let mut function = HashMap::new();
26111 function.insert(
26112 "name".to_string(),
26113 Value::String(Rc::new(tool.name.clone())),
26114 );
26115 function.insert(
26116 "description".to_string(),
26117 Value::String(Rc::new(tool.description.clone())),
26118 );
26119
26120 let mut params_schema = HashMap::new();
26122 params_schema.insert(
26123 "type".to_string(),
26124 Value::String(Rc::new("object".to_string())),
26125 );
26126
26127 let mut properties = HashMap::new();
26128 let mut required: Vec<Value> = Vec::new();
26129
26130 for param in &tool.parameters {
26131 let mut prop = HashMap::new();
26132 let json_type = match param.param_type.as_str() {
26133 "int" | "i64" | "i32" => "integer",
26134 "float" | "f64" | "f32" => "number",
26135 "bool" => "boolean",
26136 "string" | "str" => "string",
26137 "array" | "list" => "array",
26138 "map" | "object" => "object",
26139 _ => "string",
26140 };
26141 prop.insert(
26142 "type".to_string(),
26143 Value::String(Rc::new(json_type.to_string())),
26144 );
26145 prop.insert(
26146 "description".to_string(),
26147 Value::String(Rc::new(param.description.clone())),
26148 );
26149 properties.insert(param.name.clone(), Value::Map(Rc::new(RefCell::new(prop))));
26150
26151 if param.required {
26152 required.push(Value::String(Rc::new(param.name.clone())));
26153 }
26154 }
26155
26156 params_schema.insert(
26157 "properties".to_string(),
26158 Value::Map(Rc::new(RefCell::new(properties))),
26159 );
26160 params_schema.insert(
26161 "required".to_string(),
26162 Value::Array(Rc::new(RefCell::new(required))),
26163 );
26164
26165 function.insert(
26166 "parameters".to_string(),
26167 Value::Map(Rc::new(RefCell::new(params_schema))),
26168 );
26169 schema.insert(
26170 "function".to_string(),
26171 Value::Map(Rc::new(RefCell::new(function))),
26172 );
26173
26174 Ok(Value::Map(Rc::new(RefCell::new(schema))))
26175 } else {
26176 Err(RuntimeError::new(format!("Tool '{}' not found", name)))
26177 }
26178 })
26179 });
26180
26181 define(interp, "tool_schemas_all", Some(0), |_, _args| {
26183 let schemas: Vec<Value> = TOOL_REGISTRY.with(|registry| {
26184 registry
26185 .borrow()
26186 .values()
26187 .map(|tool| {
26188 let mut schema = HashMap::new();
26189 schema.insert(
26190 "type".to_string(),
26191 Value::String(Rc::new("function".to_string())),
26192 );
26193
26194 let mut function = HashMap::new();
26195 function.insert(
26196 "name".to_string(),
26197 Value::String(Rc::new(tool.name.clone())),
26198 );
26199 function.insert(
26200 "description".to_string(),
26201 Value::String(Rc::new(tool.description.clone())),
26202 );
26203
26204 let mut params_schema = HashMap::new();
26205 params_schema.insert(
26206 "type".to_string(),
26207 Value::String(Rc::new("object".to_string())),
26208 );
26209
26210 let mut properties = HashMap::new();
26211 let mut required: Vec<Value> = Vec::new();
26212
26213 for param in &tool.parameters {
26214 let mut prop = HashMap::new();
26215 let json_type = match param.param_type.as_str() {
26216 "int" | "i64" | "i32" => "integer",
26217 "float" | "f64" | "f32" => "number",
26218 "bool" => "boolean",
26219 _ => "string",
26220 };
26221 prop.insert(
26222 "type".to_string(),
26223 Value::String(Rc::new(json_type.to_string())),
26224 );
26225 prop.insert(
26226 "description".to_string(),
26227 Value::String(Rc::new(param.description.clone())),
26228 );
26229 properties
26230 .insert(param.name.clone(), Value::Map(Rc::new(RefCell::new(prop))));
26231 if param.required {
26232 required.push(Value::String(Rc::new(param.name.clone())));
26233 }
26234 }
26235
26236 params_schema.insert(
26237 "properties".to_string(),
26238 Value::Map(Rc::new(RefCell::new(properties))),
26239 );
26240 params_schema.insert(
26241 "required".to_string(),
26242 Value::Array(Rc::new(RefCell::new(required))),
26243 );
26244 function.insert(
26245 "parameters".to_string(),
26246 Value::Map(Rc::new(RefCell::new(params_schema))),
26247 );
26248 schema.insert(
26249 "function".to_string(),
26250 Value::Map(Rc::new(RefCell::new(function))),
26251 );
26252
26253 Value::Map(Rc::new(RefCell::new(schema)))
26254 })
26255 .collect()
26256 });
26257 Ok(Value::Array(Rc::new(RefCell::new(schemas))))
26258 });
26259
26260 define(interp, "tool_call", None, |interp, args| {
26262 if args.is_empty() {
26263 return Err(RuntimeError::new("tool_call requires at least tool name"));
26264 }
26265
26266 let name = match &args[0] {
26267 Value::String(s) => s.as_str().to_string(),
26268 _ => {
26269 return Err(RuntimeError::new(
26270 "tool_call first argument must be tool name",
26271 ))
26272 }
26273 };
26274
26275 let tool_args: Vec<Value> = args.into_iter().skip(1).collect();
26276
26277 TOOL_REGISTRY.with(|registry| {
26278 if let Some(tool) = registry.borrow().get(&name) {
26279 if let Some(handler) = &tool.handler {
26280 let result = interp.call_function(handler.as_ref(), tool_args)?;
26282 Ok(Value::Evidential {
26284 value: Box::new(result),
26285 evidence: Evidence::Reported,
26286 })
26287 } else {
26288 Err(RuntimeError::new(format!("Tool '{}' has no handler", name)))
26289 }
26290 } else {
26291 Err(RuntimeError::new(format!("Tool '{}' not found", name)))
26292 }
26293 })
26294 });
26295
26296 define(interp, "tool_remove", Some(1), |_, args| {
26298 let name = match &args[0] {
26299 Value::String(s) => s.as_str().to_string(),
26300 _ => return Err(RuntimeError::new("tool_remove requires string name")),
26301 };
26302
26303 TOOL_REGISTRY.with(|registry| {
26304 let removed = registry.borrow_mut().remove(&name).is_some();
26305 Ok(Value::Bool(removed))
26306 })
26307 });
26308
26309 define(interp, "tool_clear", Some(0), |_, _args| {
26311 TOOL_REGISTRY.with(|registry| {
26312 registry.borrow_mut().clear();
26313 });
26314 Ok(Value::Null)
26315 });
26316}
26317
26318fn register_agent_llm(interp: &mut Interpreter) {
26323 define(interp, "llm_message", Some(2), |_, args| {
26325 let role = match &args[0] {
26326 Value::String(s) => s.as_str().to_string(),
26327 _ => return Err(RuntimeError::new("llm_message role must be string")),
26328 };
26329 let content = match &args[1] {
26330 Value::String(s) => s.as_str().to_string(),
26331 _ => return Err(RuntimeError::new("llm_message content must be string")),
26332 };
26333
26334 let mut map = HashMap::new();
26335 map.insert("role".to_string(), Value::String(Rc::new(role)));
26336 map.insert("content".to_string(), Value::String(Rc::new(content)));
26337 Ok(Value::Map(Rc::new(RefCell::new(map))))
26338 });
26339
26340 define(interp, "llm_messages", None, |_, args| {
26342 let messages: Vec<Value> = args.into_iter().collect();
26343 Ok(Value::Array(Rc::new(RefCell::new(messages))))
26344 });
26345
26346 define(interp, "llm_request", None, |_, args| {
26348 if args.is_empty() {
26349 return Err(RuntimeError::new("llm_request requires provider"));
26350 }
26351
26352 let provider = match &args[0] {
26353 Value::String(s) => s.as_str().to_string(),
26354 _ => return Err(RuntimeError::new("provider must be string")),
26355 };
26356
26357 let mut request = HashMap::new();
26358 request.insert(
26359 "provider".to_string(),
26360 Value::String(Rc::new(provider.clone())),
26361 );
26362
26363 let default_model = match provider.as_str() {
26365 "openai" => "gpt-4-turbo-preview",
26366 "anthropic" | "claude" => "claude-3-opus-20240229",
26367 "google" | "gemini" => "gemini-pro",
26368 "mistral" => "mistral-large-latest",
26369 "ollama" | "local" => "llama2",
26370 _ => "gpt-4",
26371 };
26372 request.insert(
26373 "model".to_string(),
26374 Value::String(Rc::new(default_model.to_string())),
26375 );
26376
26377 for (i, arg) in args.iter().enumerate().skip(1) {
26379 if let Value::Map(map) = arg {
26380 let map = map.borrow();
26381 for (k, v) in map.iter() {
26382 request.insert(k.clone(), v.clone());
26383 }
26384 } else if i == 1 {
26385 if let Value::String(s) = arg {
26387 request.insert("model".to_string(), Value::String(s.clone()));
26388 }
26389 }
26390 }
26391
26392 if !request.contains_key("temperature") {
26394 request.insert("temperature".to_string(), Value::Float(0.7));
26395 }
26396 if !request.contains_key("max_tokens") {
26397 request.insert("max_tokens".to_string(), Value::Int(4096));
26398 }
26399
26400 Ok(Value::Map(Rc::new(RefCell::new(request))))
26401 });
26402
26403 define(interp, "llm_with_tools", Some(2), |_, args| {
26405 let request = match &args[0] {
26406 Value::Map(m) => m.clone(),
26407 _ => {
26408 return Err(RuntimeError::new(
26409 "llm_with_tools first arg must be request map",
26410 ))
26411 }
26412 };
26413
26414 let tools = match &args[1] {
26415 Value::Array(arr) => arr.clone(),
26416 _ => {
26417 return Err(RuntimeError::new(
26418 "llm_with_tools second arg must be tools array",
26419 ))
26420 }
26421 };
26422
26423 request
26424 .borrow_mut()
26425 .insert("tools".to_string(), Value::Array(tools));
26426 request.borrow_mut().insert(
26427 "tool_choice".to_string(),
26428 Value::String(Rc::new("auto".to_string())),
26429 );
26430
26431 Ok(Value::Map(request))
26432 });
26433
26434 define(interp, "llm_with_system", Some(2), |_, args| {
26436 let request = match &args[0] {
26437 Value::Map(m) => m.clone(),
26438 _ => {
26439 return Err(RuntimeError::new(
26440 "llm_with_system first arg must be request map",
26441 ))
26442 }
26443 };
26444
26445 let system = match &args[1] {
26446 Value::String(s) => s.clone(),
26447 _ => {
26448 return Err(RuntimeError::new(
26449 "llm_with_system second arg must be string",
26450 ))
26451 }
26452 };
26453
26454 request
26455 .borrow_mut()
26456 .insert("system".to_string(), Value::String(system));
26457 Ok(Value::Map(request))
26458 });
26459
26460 define(interp, "llm_with_messages", Some(2), |_, args| {
26462 let request = match &args[0] {
26463 Value::Map(m) => m.clone(),
26464 _ => {
26465 return Err(RuntimeError::new(
26466 "llm_with_messages first arg must be request map",
26467 ))
26468 }
26469 };
26470
26471 let messages = match &args[1] {
26472 Value::Array(arr) => arr.clone(),
26473 _ => {
26474 return Err(RuntimeError::new(
26475 "llm_with_messages second arg must be messages array",
26476 ))
26477 }
26478 };
26479
26480 request
26481 .borrow_mut()
26482 .insert("messages".to_string(), Value::Array(messages));
26483 Ok(Value::Map(request))
26484 });
26485
26486 define(interp, "llm_send", Some(1), |_, args| {
26489 let request = match &args[0] {
26490 Value::Map(m) => m.borrow().clone(),
26491 _ => return Err(RuntimeError::new("llm_send requires request map")),
26492 };
26493
26494 let provider = request
26495 .get("provider")
26496 .and_then(|v| {
26497 if let Value::String(s) = v {
26498 Some(s.as_str().to_string())
26499 } else {
26500 None
26501 }
26502 })
26503 .unwrap_or_else(|| "unknown".to_string());
26504
26505 let model = request
26506 .get("model")
26507 .and_then(|v| {
26508 if let Value::String(s) = v {
26509 Some(s.as_str().to_string())
26510 } else {
26511 None
26512 }
26513 })
26514 .unwrap_or_else(|| "unknown".to_string());
26515
26516 let mut response = HashMap::new();
26518 response.insert(
26519 "id".to_string(),
26520 Value::String(Rc::new(format!("msg_{}", Uuid::new_v4()))),
26521 );
26522 response.insert("provider".to_string(), Value::String(Rc::new(provider)));
26523 response.insert("model".to_string(), Value::String(Rc::new(model)));
26524 response.insert(
26525 "created".to_string(),
26526 Value::Int(
26527 std::time::SystemTime::now()
26528 .duration_since(std::time::UNIX_EPOCH)
26529 .unwrap_or_default()
26530 .as_secs() as i64,
26531 ),
26532 );
26533
26534 if request.contains_key("tools") {
26536 response.insert(
26537 "type".to_string(),
26538 Value::String(Rc::new("tool_use".to_string())),
26539 );
26540 response.insert(
26541 "tool_name".to_string(),
26542 Value::String(Rc::new("pending".to_string())),
26543 );
26544 response.insert(
26545 "tool_input".to_string(),
26546 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
26547 );
26548 } else {
26549 response.insert(
26550 "type".to_string(),
26551 Value::String(Rc::new("text".to_string())),
26552 );
26553 response.insert(
26554 "content".to_string(),
26555 Value::String(Rc::new(
26556 "[LLM Response - Connect to actual API for real responses]".to_string(),
26557 )),
26558 );
26559 }
26560
26561 let mut usage = HashMap::new();
26563 usage.insert("input_tokens".to_string(), Value::Int(0));
26564 usage.insert("output_tokens".to_string(), Value::Int(0));
26565 response.insert(
26566 "usage".to_string(),
26567 Value::Map(Rc::new(RefCell::new(usage))),
26568 );
26569
26570 Ok(Value::Evidential {
26572 value: Box::new(Value::Map(Rc::new(RefCell::new(response)))),
26573 evidence: Evidence::Reported,
26574 })
26575 });
26576
26577 define(interp, "llm_parse_tool_call", Some(1), |_, args| {
26579 let response = match &args[0] {
26580 Value::Map(m) => m.borrow().clone(),
26581 Value::Evidential { value, .. } => {
26582 if let Value::Map(m) = value.as_ref() {
26583 m.borrow().clone()
26584 } else {
26585 return Err(RuntimeError::new(
26586 "llm_parse_tool_call requires map response",
26587 ));
26588 }
26589 }
26590 _ => {
26591 return Err(RuntimeError::new(
26592 "llm_parse_tool_call requires response map",
26593 ))
26594 }
26595 };
26596
26597 let resp_type = response
26598 .get("type")
26599 .and_then(|v| {
26600 if let Value::String(s) = v {
26601 Some(s.as_str().to_string())
26602 } else {
26603 None
26604 }
26605 })
26606 .unwrap_or_default();
26607
26608 if resp_type == "tool_use" {
26609 let tool_name = response
26610 .get("tool_name")
26611 .and_then(|v| {
26612 if let Value::String(s) = v {
26613 Some(s.as_str().to_string())
26614 } else {
26615 None
26616 }
26617 })
26618 .unwrap_or_default();
26619
26620 let tool_input = response.get("tool_input").cloned().unwrap_or(Value::Null);
26621
26622 let mut result = HashMap::new();
26623 result.insert("is_tool_call".to_string(), Value::Bool(true));
26624 result.insert("tool_name".to_string(), Value::String(Rc::new(tool_name)));
26625 result.insert("tool_input".to_string(), tool_input);
26626 Ok(Value::Map(Rc::new(RefCell::new(result))))
26627 } else {
26628 let mut result = HashMap::new();
26629 result.insert("is_tool_call".to_string(), Value::Bool(false));
26630 result.insert(
26631 "content".to_string(),
26632 response.get("content").cloned().unwrap_or(Value::Null),
26633 );
26634 Ok(Value::Map(Rc::new(RefCell::new(result))))
26635 }
26636 });
26637
26638 define(interp, "llm_extract", Some(2), |_, args| {
26640 let _response = match &args[0] {
26641 Value::Map(m) => m.borrow().clone(),
26642 Value::Evidential { value, .. } => {
26643 if let Value::Map(m) = value.as_ref() {
26644 m.borrow().clone()
26645 } else {
26646 return Err(RuntimeError::new("llm_extract requires response"));
26647 }
26648 }
26649 _ => return Err(RuntimeError::new("llm_extract requires response")),
26650 };
26651
26652 let _schema = match &args[1] {
26653 Value::Map(m) => m.borrow().clone(),
26654 _ => return Err(RuntimeError::new("llm_extract requires schema map")),
26655 };
26656
26657 let mut result = HashMap::new();
26660 result.insert("success".to_string(), Value::Bool(true));
26661 result.insert("data".to_string(), Value::Null);
26662 result.insert(
26663 "errors".to_string(),
26664 Value::Array(Rc::new(RefCell::new(Vec::new()))),
26665 );
26666
26667 Ok(Value::Evidential {
26668 value: Box::new(Value::Map(Rc::new(RefCell::new(result)))),
26669 evidence: Evidence::Uncertain,
26670 })
26671 });
26672
26673 define(interp, "prompt_template", Some(1), |_, args| {
26675 let template = match &args[0] {
26676 Value::String(s) => s.as_str().to_string(),
26677 _ => return Err(RuntimeError::new("prompt_template requires string")),
26678 };
26679
26680 let mut variables = Vec::new();
26682 let mut in_var = false;
26683 let mut var_name = String::new();
26684
26685 for c in template.chars() {
26686 match c {
26687 '{' if !in_var => {
26688 in_var = true;
26689 var_name.clear();
26690 }
26691 '}' if in_var => {
26692 if !var_name.is_empty() {
26693 variables.push(Value::String(Rc::new(var_name.clone())));
26694 }
26695 in_var = false;
26696 }
26697 _ if in_var => {
26698 var_name.push(c);
26699 }
26700 _ => {}
26701 }
26702 }
26703
26704 let mut result = HashMap::new();
26705 result.insert("template".to_string(), Value::String(Rc::new(template)));
26706 result.insert(
26707 "variables".to_string(),
26708 Value::Array(Rc::new(RefCell::new(variables))),
26709 );
26710 Ok(Value::Map(Rc::new(RefCell::new(result))))
26711 });
26712
26713 define(interp, "prompt_render", Some(2), |_, args| {
26715 let template_obj = match &args[0] {
26716 Value::Map(m) => m.borrow().clone(),
26717 Value::String(s) => {
26718 let mut m = HashMap::new();
26719 m.insert("template".to_string(), Value::String(s.clone()));
26720 m
26721 }
26722 _ => return Err(RuntimeError::new("prompt_render requires template")),
26723 };
26724
26725 let values = match &args[1] {
26726 Value::Map(m) => m.borrow().clone(),
26727 _ => return Err(RuntimeError::new("prompt_render requires values map")),
26728 };
26729
26730 let template = template_obj
26731 .get("template")
26732 .and_then(|v| {
26733 if let Value::String(s) = v {
26734 Some(s.as_str().to_string())
26735 } else {
26736 None
26737 }
26738 })
26739 .unwrap_or_default();
26740
26741 let mut result = template;
26742 for (key, value) in values.iter() {
26743 let value_str = match value {
26744 Value::String(s) => s.as_str().to_string(),
26745 Value::Int(n) => n.to_string(),
26746 Value::Float(f) => f.to_string(),
26747 Value::Bool(b) => b.to_string(),
26748 _ => format!("{}", value),
26749 };
26750 result = result.replace(&format!("{{{}}}", key), &value_str);
26751 }
26752
26753 Ok(Value::String(Rc::new(result)))
26754 });
26755}
26756
26757fn register_agent_memory(interp: &mut Interpreter) {
26762 define(interp, "memory_session", Some(1), |_, args| {
26764 let session_id = match &args[0] {
26765 Value::String(s) => s.as_str().to_string(),
26766 _ => return Err(RuntimeError::new("memory_session requires string id")),
26767 };
26768
26769 let now = std::time::SystemTime::now()
26770 .duration_since(std::time::UNIX_EPOCH)
26771 .unwrap_or_default()
26772 .as_secs();
26773
26774 AGENT_MEMORY.with(|memory| {
26775 let mut mem = memory.borrow_mut();
26776 if !mem.contains_key(&session_id) {
26777 mem.insert(
26778 session_id.clone(),
26779 AgentSession {
26780 id: session_id.clone(),
26781 context: HashMap::new(),
26782 history: Vec::new(),
26783 created_at: now,
26784 last_accessed: now,
26785 },
26786 );
26787 } else if let Some(session) = mem.get_mut(&session_id) {
26788 session.last_accessed = now;
26789 }
26790 });
26791
26792 let mut result = HashMap::new();
26793 result.insert("id".to_string(), Value::String(Rc::new(session_id)));
26794 result.insert("created_at".to_string(), Value::Int(now as i64));
26795 Ok(Value::Map(Rc::new(RefCell::new(result))))
26796 });
26797
26798 define(interp, "memory_set", Some(3), |_, args| {
26800 let session_id = match &args[0] {
26801 Value::String(s) => s.as_str().to_string(),
26802 Value::Map(m) => m
26803 .borrow()
26804 .get("id")
26805 .and_then(|v| {
26806 if let Value::String(s) = v {
26807 Some(s.as_str().to_string())
26808 } else {
26809 None
26810 }
26811 })
26812 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26813 _ => return Err(RuntimeError::new("memory_set requires session")),
26814 };
26815
26816 let key = match &args[1] {
26817 Value::String(s) => s.as_str().to_string(),
26818 _ => return Err(RuntimeError::new("memory_set key must be string")),
26819 };
26820
26821 let value = args[2].clone();
26822
26823 AGENT_MEMORY.with(|memory| {
26824 if let Some(session) = memory.borrow_mut().get_mut(&session_id) {
26825 session.context.insert(key, value);
26826 session.last_accessed = std::time::SystemTime::now()
26827 .duration_since(std::time::UNIX_EPOCH)
26828 .unwrap_or_default()
26829 .as_secs();
26830 Ok(Value::Bool(true))
26831 } else {
26832 Err(RuntimeError::new(format!(
26833 "Session '{}' not found",
26834 session_id
26835 )))
26836 }
26837 })
26838 });
26839
26840 define(interp, "memory_get", Some(2), |_, args| {
26842 let session_id = match &args[0] {
26843 Value::String(s) => s.as_str().to_string(),
26844 Value::Map(m) => m
26845 .borrow()
26846 .get("id")
26847 .and_then(|v| {
26848 if let Value::String(s) = v {
26849 Some(s.as_str().to_string())
26850 } else {
26851 None
26852 }
26853 })
26854 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26855 _ => return Err(RuntimeError::new("memory_get requires session")),
26856 };
26857
26858 let key = match &args[1] {
26859 Value::String(s) => s.as_str().to_string(),
26860 _ => return Err(RuntimeError::new("memory_get key must be string")),
26861 };
26862
26863 AGENT_MEMORY.with(|memory| {
26864 if let Some(session) = memory.borrow().get(&session_id) {
26865 Ok(session.context.get(&key).cloned().unwrap_or(Value::Null))
26866 } else {
26867 Ok(Value::Null)
26868 }
26869 })
26870 });
26871
26872 define(interp, "memory_history_add", Some(3), |_, args| {
26874 let session_id = match &args[0] {
26875 Value::String(s) => s.as_str().to_string(),
26876 Value::Map(m) => m
26877 .borrow()
26878 .get("id")
26879 .and_then(|v| {
26880 if let Value::String(s) = v {
26881 Some(s.as_str().to_string())
26882 } else {
26883 None
26884 }
26885 })
26886 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26887 _ => return Err(RuntimeError::new("memory_history_add requires session")),
26888 };
26889
26890 let role = match &args[1] {
26891 Value::String(s) => s.as_str().to_string(),
26892 _ => return Err(RuntimeError::new("role must be string")),
26893 };
26894
26895 let content = match &args[2] {
26896 Value::String(s) => s.as_str().to_string(),
26897 _ => return Err(RuntimeError::new("content must be string")),
26898 };
26899
26900 AGENT_MEMORY.with(|memory| {
26901 if let Some(session) = memory.borrow_mut().get_mut(&session_id) {
26902 session.history.push((role, content));
26903 session.last_accessed = std::time::SystemTime::now()
26904 .duration_since(std::time::UNIX_EPOCH)
26905 .unwrap_or_default()
26906 .as_secs();
26907 Ok(Value::Int(session.history.len() as i64))
26908 } else {
26909 Err(RuntimeError::new(format!(
26910 "Session '{}' not found",
26911 session_id
26912 )))
26913 }
26914 })
26915 });
26916
26917 define(interp, "memory_history_get", None, |_, args| {
26919 if args.is_empty() {
26920 return Err(RuntimeError::new("memory_history_get requires session"));
26921 }
26922
26923 let session_id = match &args[0] {
26924 Value::String(s) => s.as_str().to_string(),
26925 Value::Map(m) => m
26926 .borrow()
26927 .get("id")
26928 .and_then(|v| {
26929 if let Value::String(s) = v {
26930 Some(s.as_str().to_string())
26931 } else {
26932 None
26933 }
26934 })
26935 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26936 _ => return Err(RuntimeError::new("memory_history_get requires session")),
26937 };
26938
26939 let limit = if args.len() > 1 {
26940 match &args[1] {
26941 Value::Int(n) => Some(*n as usize),
26942 _ => None,
26943 }
26944 } else {
26945 None
26946 };
26947
26948 AGENT_MEMORY.with(|memory| {
26949 if let Some(session) = memory.borrow().get(&session_id) {
26950 let history: Vec<Value> = session
26951 .history
26952 .iter()
26953 .rev()
26954 .take(limit.unwrap_or(usize::MAX))
26955 .rev()
26956 .map(|(role, content)| {
26957 let mut msg = HashMap::new();
26958 msg.insert("role".to_string(), Value::String(Rc::new(role.clone())));
26959 msg.insert(
26960 "content".to_string(),
26961 Value::String(Rc::new(content.clone())),
26962 );
26963 Value::Map(Rc::new(RefCell::new(msg)))
26964 })
26965 .collect();
26966 Ok(Value::Array(Rc::new(RefCell::new(history))))
26967 } else {
26968 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
26969 }
26970 })
26971 });
26972
26973 define(interp, "memory_context_all", Some(1), |_, args| {
26975 let session_id = match &args[0] {
26976 Value::String(s) => s.as_str().to_string(),
26977 Value::Map(m) => m
26978 .borrow()
26979 .get("id")
26980 .and_then(|v| {
26981 if let Value::String(s) = v {
26982 Some(s.as_str().to_string())
26983 } else {
26984 None
26985 }
26986 })
26987 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26988 _ => return Err(RuntimeError::new("memory_context_all requires session")),
26989 };
26990
26991 AGENT_MEMORY.with(|memory| {
26992 if let Some(session) = memory.borrow().get(&session_id) {
26993 let context: HashMap<String, Value> = session.context.clone();
26994 Ok(Value::Map(Rc::new(RefCell::new(context))))
26995 } else {
26996 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
26997 }
26998 })
26999 });
27000
27001 define(interp, "memory_clear", Some(1), |_, args| {
27003 let session_id = match &args[0] {
27004 Value::String(s) => s.as_str().to_string(),
27005 Value::Map(m) => m
27006 .borrow()
27007 .get("id")
27008 .and_then(|v| {
27009 if let Value::String(s) = v {
27010 Some(s.as_str().to_string())
27011 } else {
27012 None
27013 }
27014 })
27015 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
27016 _ => return Err(RuntimeError::new("memory_clear requires session")),
27017 };
27018
27019 AGENT_MEMORY.with(|memory| {
27020 let removed = memory.borrow_mut().remove(&session_id).is_some();
27021 Ok(Value::Bool(removed))
27022 })
27023 });
27024
27025 define(interp, "memory_sessions_list", Some(0), |_, _args| {
27027 let sessions: Vec<Value> = AGENT_MEMORY.with(|memory| {
27028 memory
27029 .borrow()
27030 .values()
27031 .map(|session| {
27032 let mut info = HashMap::new();
27033 info.insert("id".to_string(), Value::String(Rc::new(session.id.clone())));
27034 info.insert(
27035 "created_at".to_string(),
27036 Value::Int(session.created_at as i64),
27037 );
27038 info.insert(
27039 "last_accessed".to_string(),
27040 Value::Int(session.last_accessed as i64),
27041 );
27042 info.insert(
27043 "context_keys".to_string(),
27044 Value::Int(session.context.len() as i64),
27045 );
27046 info.insert(
27047 "history_length".to_string(),
27048 Value::Int(session.history.len() as i64),
27049 );
27050 Value::Map(Rc::new(RefCell::new(info)))
27051 })
27052 .collect()
27053 });
27054 Ok(Value::Array(Rc::new(RefCell::new(sessions))))
27055 });
27056}
27057
27058fn register_agent_planning(interp: &mut Interpreter) {
27063 define(interp, "plan_state_machine", Some(2), |_, args| {
27065 let name = match &args[0] {
27066 Value::String(s) => s.as_str().to_string(),
27067 _ => return Err(RuntimeError::new("plan_state_machine name must be string")),
27068 };
27069
27070 let states = match &args[1] {
27071 Value::Array(arr) => arr
27072 .borrow()
27073 .iter()
27074 .filter_map(|v| {
27075 if let Value::String(s) = v {
27076 Some(s.as_str().to_string())
27077 } else {
27078 None
27079 }
27080 })
27081 .collect::<Vec<_>>(),
27082 _ => return Err(RuntimeError::new("plan_state_machine states must be array")),
27083 };
27084
27085 if states.is_empty() {
27086 return Err(RuntimeError::new(
27087 "State machine must have at least one state",
27088 ));
27089 }
27090
27091 let initial_state = states[0].clone();
27092 let now = std::time::SystemTime::now()
27093 .duration_since(std::time::UNIX_EPOCH)
27094 .unwrap_or_default()
27095 .as_secs();
27096
27097 let machine = StateMachine {
27098 name: name.clone(),
27099 current_state: initial_state.clone(),
27100 states,
27101 transitions: HashMap::new(),
27102 history: vec![(initial_state, now)],
27103 };
27104
27105 STATE_MACHINES.with(|machines| {
27106 machines.borrow_mut().insert(name.clone(), machine);
27107 });
27108
27109 let mut result = HashMap::new();
27110 result.insert("name".to_string(), Value::String(Rc::new(name)));
27111 result.insert(
27112 "type".to_string(),
27113 Value::String(Rc::new("state_machine".to_string())),
27114 );
27115 Ok(Value::Map(Rc::new(RefCell::new(result))))
27116 });
27117
27118 define(interp, "plan_add_transition", Some(3), |_, args| {
27120 let machine_name = match &args[0] {
27121 Value::String(s) => s.as_str().to_string(),
27122 Value::Map(m) => m
27123 .borrow()
27124 .get("name")
27125 .and_then(|v| {
27126 if let Value::String(s) = v {
27127 Some(s.as_str().to_string())
27128 } else {
27129 None
27130 }
27131 })
27132 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27133 _ => return Err(RuntimeError::new("plan_add_transition requires machine")),
27134 };
27135
27136 let from_state = match &args[1] {
27137 Value::String(s) => s.as_str().to_string(),
27138 _ => return Err(RuntimeError::new("from_state must be string")),
27139 };
27140
27141 let to_state = match &args[2] {
27142 Value::String(s) => s.as_str().to_string(),
27143 _ => return Err(RuntimeError::new("to_state must be string")),
27144 };
27145
27146 STATE_MACHINES.with(|machines| {
27147 if let Some(machine) = machines.borrow_mut().get_mut(&machine_name) {
27148 if !machine.states.contains(&from_state) {
27150 return Err(RuntimeError::new(format!(
27151 "State '{}' not in machine",
27152 from_state
27153 )));
27154 }
27155 if !machine.states.contains(&to_state) {
27156 return Err(RuntimeError::new(format!(
27157 "State '{}' not in machine",
27158 to_state
27159 )));
27160 }
27161
27162 machine
27163 .transitions
27164 .entry(from_state)
27165 .or_insert_with(Vec::new)
27166 .push((to_state, "".to_string()));
27167
27168 Ok(Value::Bool(true))
27169 } else {
27170 Err(RuntimeError::new(format!(
27171 "State machine '{}' not found",
27172 machine_name
27173 )))
27174 }
27175 })
27176 });
27177
27178 define(interp, "plan_current_state", Some(1), |_, args| {
27180 let machine_name = match &args[0] {
27181 Value::String(s) => s.as_str().to_string(),
27182 Value::Map(m) => m
27183 .borrow()
27184 .get("name")
27185 .and_then(|v| {
27186 if let Value::String(s) = v {
27187 Some(s.as_str().to_string())
27188 } else {
27189 None
27190 }
27191 })
27192 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27193 _ => return Err(RuntimeError::new("plan_current_state requires machine")),
27194 };
27195
27196 STATE_MACHINES.with(|machines| {
27197 if let Some(machine) = machines.borrow().get(&machine_name) {
27198 Ok(Value::String(Rc::new(machine.current_state.clone())))
27199 } else {
27200 Err(RuntimeError::new(format!(
27201 "State machine '{}' not found",
27202 machine_name
27203 )))
27204 }
27205 })
27206 });
27207
27208 define(interp, "plan_transition", Some(2), |_, args| {
27210 let machine_name = match &args[0] {
27211 Value::String(s) => s.as_str().to_string(),
27212 Value::Map(m) => m
27213 .borrow()
27214 .get("name")
27215 .and_then(|v| {
27216 if let Value::String(s) = v {
27217 Some(s.as_str().to_string())
27218 } else {
27219 None
27220 }
27221 })
27222 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27223 _ => return Err(RuntimeError::new("plan_transition requires machine")),
27224 };
27225
27226 let to_state = match &args[1] {
27227 Value::String(s) => s.as_str().to_string(),
27228 _ => return Err(RuntimeError::new("to_state must be string")),
27229 };
27230
27231 STATE_MACHINES.with(|machines| {
27232 if let Some(machine) = machines.borrow_mut().get_mut(&machine_name) {
27233 let current = machine.current_state.clone();
27234
27235 let valid = machine
27237 .transitions
27238 .get(¤t)
27239 .map(|transitions| transitions.iter().any(|(to, _)| to == &to_state))
27240 .unwrap_or(false);
27241
27242 if valid || machine.states.contains(&to_state) {
27243 let now = std::time::SystemTime::now()
27244 .duration_since(std::time::UNIX_EPOCH)
27245 .unwrap_or_default()
27246 .as_secs();
27247
27248 machine.current_state = to_state.clone();
27249 machine.history.push((to_state.clone(), now));
27250
27251 let mut result = HashMap::new();
27252 result.insert("success".to_string(), Value::Bool(true));
27253 result.insert("from".to_string(), Value::String(Rc::new(current)));
27254 result.insert("to".to_string(), Value::String(Rc::new(to_state)));
27255 Ok(Value::Map(Rc::new(RefCell::new(result))))
27256 } else {
27257 let mut result = HashMap::new();
27258 result.insert("success".to_string(), Value::Bool(false));
27259 result.insert(
27260 "error".to_string(),
27261 Value::String(Rc::new(format!(
27262 "No valid transition from '{}' to '{}'",
27263 current, to_state
27264 ))),
27265 );
27266 Ok(Value::Map(Rc::new(RefCell::new(result))))
27267 }
27268 } else {
27269 Err(RuntimeError::new(format!(
27270 "State machine '{}' not found",
27271 machine_name
27272 )))
27273 }
27274 })
27275 });
27276
27277 define(interp, "plan_can_transition", Some(2), |_, args| {
27279 let machine_name = match &args[0] {
27280 Value::String(s) => s.as_str().to_string(),
27281 Value::Map(m) => m
27282 .borrow()
27283 .get("name")
27284 .and_then(|v| {
27285 if let Value::String(s) = v {
27286 Some(s.as_str().to_string())
27287 } else {
27288 None
27289 }
27290 })
27291 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27292 _ => return Err(RuntimeError::new("plan_can_transition requires machine")),
27293 };
27294
27295 let to_state = match &args[1] {
27296 Value::String(s) => s.as_str().to_string(),
27297 _ => return Err(RuntimeError::new("to_state must be string")),
27298 };
27299
27300 STATE_MACHINES.with(|machines| {
27301 if let Some(machine) = machines.borrow().get(&machine_name) {
27302 let current = &machine.current_state;
27303 let can = machine
27304 .transitions
27305 .get(current)
27306 .map(|transitions| transitions.iter().any(|(to, _)| to == &to_state))
27307 .unwrap_or(false);
27308 Ok(Value::Bool(can))
27309 } else {
27310 Ok(Value::Bool(false))
27311 }
27312 })
27313 });
27314
27315 define(interp, "plan_available_transitions", Some(1), |_, args| {
27317 let machine_name = match &args[0] {
27318 Value::String(s) => s.as_str().to_string(),
27319 Value::Map(m) => m
27320 .borrow()
27321 .get("name")
27322 .and_then(|v| {
27323 if let Value::String(s) = v {
27324 Some(s.as_str().to_string())
27325 } else {
27326 None
27327 }
27328 })
27329 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27330 _ => {
27331 return Err(RuntimeError::new(
27332 "plan_available_transitions requires machine",
27333 ))
27334 }
27335 };
27336
27337 STATE_MACHINES.with(|machines| {
27338 if let Some(machine) = machines.borrow().get(&machine_name) {
27339 let current = &machine.current_state;
27340 let available: Vec<Value> = machine
27341 .transitions
27342 .get(current)
27343 .map(|transitions| {
27344 transitions
27345 .iter()
27346 .map(|(to, _)| Value::String(Rc::new(to.clone())))
27347 .collect()
27348 })
27349 .unwrap_or_default();
27350 Ok(Value::Array(Rc::new(RefCell::new(available))))
27351 } else {
27352 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
27353 }
27354 })
27355 });
27356
27357 define(interp, "plan_history", Some(1), |_, args| {
27359 let machine_name = match &args[0] {
27360 Value::String(s) => s.as_str().to_string(),
27361 Value::Map(m) => m
27362 .borrow()
27363 .get("name")
27364 .and_then(|v| {
27365 if let Value::String(s) = v {
27366 Some(s.as_str().to_string())
27367 } else {
27368 None
27369 }
27370 })
27371 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27372 _ => return Err(RuntimeError::new("plan_history requires machine")),
27373 };
27374
27375 STATE_MACHINES.with(|machines| {
27376 if let Some(machine) = machines.borrow().get(&machine_name) {
27377 let history: Vec<Value> = machine
27378 .history
27379 .iter()
27380 .map(|(state, timestamp)| {
27381 let mut entry = HashMap::new();
27382 entry.insert("state".to_string(), Value::String(Rc::new(state.clone())));
27383 entry.insert("timestamp".to_string(), Value::Int(*timestamp as i64));
27384 Value::Map(Rc::new(RefCell::new(entry)))
27385 })
27386 .collect();
27387 Ok(Value::Array(Rc::new(RefCell::new(history))))
27388 } else {
27389 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
27390 }
27391 })
27392 });
27393
27394 define(interp, "plan_goal", Some(2), |_, args| {
27396 let name = match &args[0] {
27397 Value::String(s) => s.as_str().to_string(),
27398 _ => return Err(RuntimeError::new("plan_goal name must be string")),
27399 };
27400
27401 let criteria = args[1].clone();
27402
27403 let mut goal = HashMap::new();
27404 goal.insert("name".to_string(), Value::String(Rc::new(name)));
27405 goal.insert("criteria".to_string(), criteria);
27406 goal.insert(
27407 "status".to_string(),
27408 Value::String(Rc::new("pending".to_string())),
27409 );
27410 goal.insert("progress".to_string(), Value::Float(0.0));
27411 goal.insert(
27412 "created_at".to_string(),
27413 Value::Int(
27414 std::time::SystemTime::now()
27415 .duration_since(std::time::UNIX_EPOCH)
27416 .unwrap_or_default()
27417 .as_secs() as i64,
27418 ),
27419 );
27420
27421 Ok(Value::Map(Rc::new(RefCell::new(goal))))
27422 });
27423
27424 define(interp, "plan_subgoals", Some(2), |_, args| {
27426 let parent = match &args[0] {
27427 Value::Map(m) => m.clone(),
27428 _ => return Err(RuntimeError::new("plan_subgoals requires goal map")),
27429 };
27430
27431 let subgoals = match &args[1] {
27432 Value::Array(arr) => arr.clone(),
27433 _ => return Err(RuntimeError::new("plan_subgoals requires subgoals array")),
27434 };
27435
27436 parent
27437 .borrow_mut()
27438 .insert("subgoals".to_string(), Value::Array(subgoals));
27439 Ok(Value::Map(parent))
27440 });
27441
27442 define(interp, "plan_update_progress", Some(2), |_, args| {
27444 let goal = match &args[0] {
27445 Value::Map(m) => m.clone(),
27446 _ => return Err(RuntimeError::new("plan_update_progress requires goal map")),
27447 };
27448
27449 let progress = match &args[1] {
27450 Value::Float(f) => *f,
27451 Value::Int(i) => *i as f64,
27452 _ => return Err(RuntimeError::new("progress must be number")),
27453 };
27454
27455 let progress = progress.clamp(0.0, 1.0);
27456 goal.borrow_mut()
27457 .insert("progress".to_string(), Value::Float(progress));
27458
27459 if progress >= 1.0 {
27460 goal.borrow_mut().insert(
27461 "status".to_string(),
27462 Value::String(Rc::new("completed".to_string())),
27463 );
27464 } else if progress > 0.0 {
27465 goal.borrow_mut().insert(
27466 "status".to_string(),
27467 Value::String(Rc::new("in_progress".to_string())),
27468 );
27469 }
27470
27471 Ok(Value::Map(goal))
27472 });
27473
27474 define(interp, "plan_check_goal", Some(2), |_interp, args| {
27476 let goal = match &args[0] {
27477 Value::Map(m) => m.borrow().clone(),
27478 _ => return Err(RuntimeError::new("plan_check_goal requires goal map")),
27479 };
27480
27481 let context = match &args[1] {
27482 Value::Map(m) => m.borrow().clone(),
27483 _ => return Err(RuntimeError::new("plan_check_goal requires context map")),
27484 };
27485
27486 let _criteria = goal.get("criteria").cloned().unwrap_or(Value::Null);
27488
27489 let mut met = true;
27491 let mut missing: Vec<String> = Vec::new();
27492
27493 if let Some(Value::Array(required)) = goal.get("required_context") {
27494 for req in required.borrow().iter() {
27495 if let Value::String(key) = req {
27496 if !context.contains_key(key.as_str()) {
27497 met = false;
27498 missing.push(key.as_str().to_string());
27499 }
27500 }
27501 }
27502 }
27503
27504 let mut result = HashMap::new();
27505 result.insert("met".to_string(), Value::Bool(met));
27506 result.insert(
27507 "missing".to_string(),
27508 Value::Array(Rc::new(RefCell::new(
27509 missing
27510 .into_iter()
27511 .map(|s| Value::String(Rc::new(s)))
27512 .collect(),
27513 ))),
27514 );
27515
27516 Ok(Value::Map(Rc::new(RefCell::new(result))))
27517 });
27518}
27519
27520fn register_agent_vectors(interp: &mut Interpreter) {
27525 define(interp, "vec_embedding", Some(1), |_, args| {
27527 let text = match &args[0] {
27528 Value::String(s) => s.as_str().to_string(),
27529 _ => return Err(RuntimeError::new("vec_embedding requires string")),
27530 };
27531
27532 let mut embedding = Vec::new();
27535 let dimension = 384; for i in 0..dimension {
27538 let hash = text.bytes().enumerate().fold(0u64, |acc, (j, b)| {
27539 acc.wrapping_add((b as u64).wrapping_mul((i + j + 1) as u64))
27540 });
27541 let value = ((hash % 10000) as f64 / 10000.0) * 2.0 - 1.0; embedding.push(Value::Float(value));
27543 }
27544
27545 let result = Value::Array(Rc::new(RefCell::new(embedding)));
27546
27547 Ok(Value::Evidential {
27549 value: Box::new(result),
27550 evidence: Evidence::Uncertain,
27551 })
27552 });
27553
27554 define(interp, "vec_cosine_similarity", Some(2), |_, args| {
27556 let vec_a = match &args[0] {
27557 Value::Array(arr) => arr.borrow().clone(),
27558 Value::Evidential { value, .. } => {
27559 if let Value::Array(arr) = value.as_ref() {
27560 arr.borrow().clone()
27561 } else {
27562 return Err(RuntimeError::new("vec_cosine_similarity requires arrays"));
27563 }
27564 }
27565 _ => return Err(RuntimeError::new("vec_cosine_similarity requires arrays")),
27566 };
27567
27568 let vec_b = match &args[1] {
27569 Value::Array(arr) => arr.borrow().clone(),
27570 Value::Evidential { value, .. } => {
27571 if let Value::Array(arr) = value.as_ref() {
27572 arr.borrow().clone()
27573 } else {
27574 return Err(RuntimeError::new("vec_cosine_similarity requires arrays"));
27575 }
27576 }
27577 _ => return Err(RuntimeError::new("vec_cosine_similarity requires arrays")),
27578 };
27579
27580 if vec_a.len() != vec_b.len() {
27581 return Err(RuntimeError::new("Vectors must have same dimension"));
27582 }
27583
27584 let mut dot = 0.0;
27585 let mut mag_a = 0.0;
27586 let mut mag_b = 0.0;
27587
27588 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
27589 let a_val = match a {
27590 Value::Float(f) => *f,
27591 Value::Int(i) => *i as f64,
27592 _ => 0.0,
27593 };
27594 let b_val = match b {
27595 Value::Float(f) => *f,
27596 Value::Int(i) => *i as f64,
27597 _ => 0.0,
27598 };
27599
27600 dot += a_val * b_val;
27601 mag_a += a_val * a_val;
27602 mag_b += b_val * b_val;
27603 }
27604
27605 let similarity = if mag_a > 0.0 && mag_b > 0.0 {
27606 dot / (mag_a.sqrt() * mag_b.sqrt())
27607 } else {
27608 0.0
27609 };
27610
27611 Ok(Value::Float(similarity))
27612 });
27613
27614 define(interp, "vec_euclidean_distance", Some(2), |_, args| {
27616 let vec_a = match &args[0] {
27617 Value::Array(arr) => arr.borrow().clone(),
27618 Value::Evidential { value, .. } => {
27619 if let Value::Array(arr) = value.as_ref() {
27620 arr.borrow().clone()
27621 } else {
27622 return Err(RuntimeError::new("vec_euclidean_distance requires arrays"));
27623 }
27624 }
27625 _ => return Err(RuntimeError::new("vec_euclidean_distance requires arrays")),
27626 };
27627
27628 let vec_b = match &args[1] {
27629 Value::Array(arr) => arr.borrow().clone(),
27630 Value::Evidential { value, .. } => {
27631 if let Value::Array(arr) = value.as_ref() {
27632 arr.borrow().clone()
27633 } else {
27634 return Err(RuntimeError::new("vec_euclidean_distance requires arrays"));
27635 }
27636 }
27637 _ => return Err(RuntimeError::new("vec_euclidean_distance requires arrays")),
27638 };
27639
27640 if vec_a.len() != vec_b.len() {
27641 return Err(RuntimeError::new("Vectors must have same dimension"));
27642 }
27643
27644 let mut sum_sq = 0.0;
27645 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
27646 let a_val = match a {
27647 Value::Float(f) => *f,
27648 Value::Int(i) => *i as f64,
27649 _ => 0.0,
27650 };
27651 let b_val = match b {
27652 Value::Float(f) => *f,
27653 Value::Int(i) => *i as f64,
27654 _ => 0.0,
27655 };
27656 let diff = a_val - b_val;
27657 sum_sq += diff * diff;
27658 }
27659
27660 Ok(Value::Float(sum_sq.sqrt()))
27661 });
27662
27663 define(interp, "vec_dot_product", Some(2), |_, args| {
27665 let vec_a = match &args[0] {
27666 Value::Array(arr) => arr.borrow().clone(),
27667 _ => return Err(RuntimeError::new("vec_dot_product requires arrays")),
27668 };
27669
27670 let vec_b = match &args[1] {
27671 Value::Array(arr) => arr.borrow().clone(),
27672 _ => return Err(RuntimeError::new("vec_dot_product requires arrays")),
27673 };
27674
27675 if vec_a.len() != vec_b.len() {
27676 return Err(RuntimeError::new("Vectors must have same dimension"));
27677 }
27678
27679 let mut dot = 0.0;
27680 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
27681 let a_val = match a {
27682 Value::Float(f) => *f,
27683 Value::Int(i) => *i as f64,
27684 _ => 0.0,
27685 };
27686 let b_val = match b {
27687 Value::Float(f) => *f,
27688 Value::Int(i) => *i as f64,
27689 _ => 0.0,
27690 };
27691 dot += a_val * b_val;
27692 }
27693
27694 Ok(Value::Float(dot))
27695 });
27696
27697 define(interp, "vec_normalize", Some(1), |_, args| {
27699 let vec = match &args[0] {
27700 Value::Array(arr) => arr.borrow().clone(),
27701 _ => return Err(RuntimeError::new("vec_normalize requires array")),
27702 };
27703
27704 let mut mag = 0.0;
27705 for v in vec.iter() {
27706 let val = match v {
27707 Value::Float(f) => *f,
27708 Value::Int(i) => *i as f64,
27709 _ => 0.0,
27710 };
27711 mag += val * val;
27712 }
27713 mag = mag.sqrt();
27714
27715 if mag == 0.0 {
27716 return Ok(Value::Array(Rc::new(RefCell::new(vec))));
27717 }
27718
27719 let normalized: Vec<Value> = vec
27720 .iter()
27721 .map(|v| {
27722 let val = match v {
27723 Value::Float(f) => *f,
27724 Value::Int(i) => *i as f64,
27725 _ => 0.0,
27726 };
27727 Value::Float(val / mag)
27728 })
27729 .collect();
27730
27731 Ok(Value::Array(Rc::new(RefCell::new(normalized))))
27732 });
27733
27734 define(interp, "vec_search", Some(3), |_, args| {
27736 let query = match &args[0] {
27737 Value::Array(arr) => arr.borrow().clone(),
27738 Value::Evidential { value, .. } => {
27739 if let Value::Array(arr) = value.as_ref() {
27740 arr.borrow().clone()
27741 } else {
27742 return Err(RuntimeError::new("vec_search query must be array"));
27743 }
27744 }
27745 _ => return Err(RuntimeError::new("vec_search query must be array")),
27746 };
27747
27748 let corpus = match &args[1] {
27749 Value::Array(arr) => arr.borrow().clone(),
27750 _ => {
27751 return Err(RuntimeError::new(
27752 "vec_search corpus must be array of vectors",
27753 ))
27754 }
27755 };
27756
27757 let k = match &args[2] {
27758 Value::Int(n) => *n as usize,
27759 _ => return Err(RuntimeError::new("vec_search k must be integer")),
27760 };
27761
27762 let mut similarities: Vec<(usize, f64)> = Vec::new();
27764
27765 for (i, item) in corpus.iter().enumerate() {
27766 let vec_b = match item {
27767 Value::Array(arr) => arr.borrow().clone(),
27768 Value::Map(m) => {
27769 if let Some(Value::Array(arr)) = m.borrow().get("vector") {
27771 arr.borrow().clone()
27772 } else {
27773 continue;
27774 }
27775 }
27776 _ => continue,
27777 };
27778
27779 if vec_b.len() != query.len() {
27780 continue;
27781 }
27782
27783 let mut dot = 0.0;
27784 let mut mag_a = 0.0;
27785 let mut mag_b = 0.0;
27786
27787 for (a, b) in query.iter().zip(vec_b.iter()) {
27788 let a_val = match a {
27789 Value::Float(f) => *f,
27790 Value::Int(i) => *i as f64,
27791 _ => 0.0,
27792 };
27793 let b_val = match b {
27794 Value::Float(f) => *f,
27795 Value::Int(i) => *i as f64,
27796 _ => 0.0,
27797 };
27798 dot += a_val * b_val;
27799 mag_a += a_val * a_val;
27800 mag_b += b_val * b_val;
27801 }
27802
27803 let similarity = if mag_a > 0.0 && mag_b > 0.0 {
27804 dot / (mag_a.sqrt() * mag_b.sqrt())
27805 } else {
27806 0.0
27807 };
27808
27809 similarities.push((i, similarity));
27810 }
27811
27812 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
27814
27815 let results: Vec<Value> = similarities
27817 .iter()
27818 .take(k)
27819 .map(|(idx, sim)| {
27820 let mut result = HashMap::new();
27821 result.insert("index".to_string(), Value::Int(*idx as i64));
27822 result.insert("similarity".to_string(), Value::Float(*sim));
27823 result.insert(
27824 "item".to_string(),
27825 corpus.get(*idx).cloned().unwrap_or(Value::Null),
27826 );
27827 Value::Map(Rc::new(RefCell::new(result)))
27828 })
27829 .collect();
27830
27831 Ok(Value::Array(Rc::new(RefCell::new(results))))
27832 });
27833
27834 define(interp, "vec_store", Some(0), |_, _args| {
27836 let mut store = HashMap::new();
27837 store.insert(
27838 "vectors".to_string(),
27839 Value::Array(Rc::new(RefCell::new(Vec::new()))),
27840 );
27841 store.insert(
27842 "metadata".to_string(),
27843 Value::Array(Rc::new(RefCell::new(Vec::new()))),
27844 );
27845 store.insert("count".to_string(), Value::Int(0));
27846 Ok(Value::Map(Rc::new(RefCell::new(store))))
27847 });
27848
27849 define(interp, "vec_store_add", Some(3), |_, args| {
27851 let store = match &args[0] {
27852 Value::Map(m) => m.clone(),
27853 _ => return Err(RuntimeError::new("vec_store_add requires store")),
27854 };
27855
27856 let vector = args[1].clone();
27857 let metadata = args[2].clone();
27858
27859 let mut store_ref = store.borrow_mut();
27860
27861 if let Some(Value::Array(vectors)) = store_ref.get("vectors") {
27862 vectors.borrow_mut().push(vector);
27863 }
27864 if let Some(Value::Array(metas)) = store_ref.get("metadata") {
27865 metas.borrow_mut().push(metadata);
27866 }
27867
27868 let new_count = store_ref
27869 .get("count")
27870 .and_then(|v| {
27871 if let Value::Int(n) = v {
27872 Some(*n)
27873 } else {
27874 None
27875 }
27876 })
27877 .unwrap_or(0)
27878 + 1;
27879 store_ref.insert("count".to_string(), Value::Int(new_count));
27880
27881 Ok(Value::Int(new_count))
27882 });
27883
27884 define(interp, "vec_store_search", Some(3), |_, args| {
27886 let store = match &args[0] {
27887 Value::Map(m) => m.borrow().clone(),
27888 _ => return Err(RuntimeError::new("vec_store_search requires store")),
27889 };
27890
27891 let query = match &args[1] {
27892 Value::Array(arr) => arr.borrow().clone(),
27893 Value::Evidential { value, .. } => {
27894 if let Value::Array(arr) = value.as_ref() {
27895 arr.borrow().clone()
27896 } else {
27897 return Err(RuntimeError::new("Query must be array"));
27898 }
27899 }
27900 _ => return Err(RuntimeError::new("Query must be array")),
27901 };
27902
27903 let k = match &args[2] {
27904 Value::Int(n) => *n as usize,
27905 _ => return Err(RuntimeError::new("k must be integer")),
27906 };
27907
27908 let vectors = store
27909 .get("vectors")
27910 .and_then(|v| {
27911 if let Value::Array(arr) = v {
27912 Some(arr.borrow().clone())
27913 } else {
27914 None
27915 }
27916 })
27917 .unwrap_or_default();
27918
27919 let metadata = store
27920 .get("metadata")
27921 .and_then(|v| {
27922 if let Value::Array(arr) = v {
27923 Some(arr.borrow().clone())
27924 } else {
27925 None
27926 }
27927 })
27928 .unwrap_or_default();
27929
27930 let mut similarities: Vec<(usize, f64)> = Vec::new();
27931
27932 for (i, vec_val) in vectors.iter().enumerate() {
27933 let vec_b = match vec_val {
27934 Value::Array(arr) => arr.borrow().clone(),
27935 Value::Evidential { value, .. } => {
27936 if let Value::Array(arr) = value.as_ref() {
27937 arr.borrow().clone()
27938 } else {
27939 continue;
27940 }
27941 }
27942 _ => continue,
27943 };
27944
27945 if vec_b.len() != query.len() {
27946 continue;
27947 }
27948
27949 let mut dot = 0.0;
27950 let mut mag_a = 0.0;
27951 let mut mag_b = 0.0;
27952
27953 for (a, b) in query.iter().zip(vec_b.iter()) {
27954 let a_val = match a {
27955 Value::Float(f) => *f,
27956 Value::Int(i) => *i as f64,
27957 _ => 0.0,
27958 };
27959 let b_val = match b {
27960 Value::Float(f) => *f,
27961 Value::Int(i) => *i as f64,
27962 _ => 0.0,
27963 };
27964 dot += a_val * b_val;
27965 mag_a += a_val * a_val;
27966 mag_b += b_val * b_val;
27967 }
27968
27969 let sim = if mag_a > 0.0 && mag_b > 0.0 {
27970 dot / (mag_a.sqrt() * mag_b.sqrt())
27971 } else {
27972 0.0
27973 };
27974 similarities.push((i, sim));
27975 }
27976
27977 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
27978
27979 let results: Vec<Value> = similarities
27980 .iter()
27981 .take(k)
27982 .map(|(idx, sim)| {
27983 let mut result = HashMap::new();
27984 result.insert("index".to_string(), Value::Int(*idx as i64));
27985 result.insert("similarity".to_string(), Value::Float(*sim));
27986 result.insert(
27987 "vector".to_string(),
27988 vectors.get(*idx).cloned().unwrap_or(Value::Null),
27989 );
27990 result.insert(
27991 "metadata".to_string(),
27992 metadata.get(*idx).cloned().unwrap_or(Value::Null),
27993 );
27994 Value::Map(Rc::new(RefCell::new(result)))
27995 })
27996 .collect();
27997
27998 Ok(Value::Array(Rc::new(RefCell::new(results))))
27999 });
28000}
28001
28002thread_local! {
28008 static AGENT_REGISTRY: RefCell<HashMap<String, AgentInfo>> = RefCell::new(HashMap::new());
28009 static AGENT_MESSAGES: RefCell<HashMap<String, Vec<AgentMessage>>> = RefCell::new(HashMap::new());
28010}
28011
28012#[derive(Clone)]
28013struct AgentInfo {
28014 id: String,
28015 agent_type: String,
28016 state: HashMap<String, Value>,
28017 capabilities: Vec<String>,
28018 created_at: u64,
28019}
28020
28021#[derive(Clone)]
28022struct AgentMessage {
28023 from: String,
28024 to: String,
28025 msg_type: String,
28026 content: Value,
28027 timestamp: u64,
28028}
28029
28030fn register_agent_swarm(interp: &mut Interpreter) {
28031 define(interp, "swarm_create_agent", Some(2), |_, args| {
28033 let agent_id = match &args[0] {
28034 Value::String(s) => s.as_str().to_string(),
28035 _ => return Err(RuntimeError::new("swarm_create_agent requires string id")),
28036 };
28037
28038 let agent_type = match &args[1] {
28039 Value::String(s) => s.as_str().to_string(),
28040 _ => return Err(RuntimeError::new("swarm_create_agent requires string type")),
28041 };
28042
28043 let now = std::time::SystemTime::now()
28044 .duration_since(std::time::UNIX_EPOCH)
28045 .unwrap_or_default()
28046 .as_secs();
28047
28048 let agent = AgentInfo {
28049 id: agent_id.clone(),
28050 agent_type,
28051 state: HashMap::new(),
28052 capabilities: Vec::new(),
28053 created_at: now,
28054 };
28055
28056 AGENT_REGISTRY.with(|registry| {
28057 registry.borrow_mut().insert(agent_id.clone(), agent);
28058 });
28059
28060 AGENT_MESSAGES.with(|messages| {
28061 messages.borrow_mut().insert(agent_id.clone(), Vec::new());
28062 });
28063
28064 let mut result = HashMap::new();
28065 result.insert("id".to_string(), Value::String(Rc::new(agent_id)));
28066 result.insert("created_at".to_string(), Value::Int(now as i64));
28067 Ok(Value::Map(Rc::new(RefCell::new(result))))
28068 });
28069
28070 define(interp, "swarm_add_capability", Some(2), |_, args| {
28072 let agent_id = match &args[0] {
28073 Value::String(s) => s.as_str().to_string(),
28074 Value::Map(m) => m
28075 .borrow()
28076 .get("id")
28077 .and_then(|v| {
28078 if let Value::String(s) = v {
28079 Some(s.as_str().to_string())
28080 } else {
28081 None
28082 }
28083 })
28084 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28085 _ => return Err(RuntimeError::new("swarm_add_capability requires agent")),
28086 };
28087
28088 let capability = match &args[1] {
28089 Value::String(s) => s.as_str().to_string(),
28090 _ => return Err(RuntimeError::new("capability must be string")),
28091 };
28092
28093 AGENT_REGISTRY.with(|registry| {
28094 if let Some(agent) = registry.borrow_mut().get_mut(&agent_id) {
28095 if !agent.capabilities.contains(&capability) {
28096 agent.capabilities.push(capability);
28097 }
28098 Ok(Value::Bool(true))
28099 } else {
28100 Err(RuntimeError::new(format!("Agent '{}' not found", agent_id)))
28101 }
28102 })
28103 });
28104
28105 define(interp, "swarm_send_message", Some(4), |_, args| {
28107 let from_id = match &args[0] {
28108 Value::String(s) => s.as_str().to_string(),
28109 Value::Map(m) => m
28110 .borrow()
28111 .get("id")
28112 .and_then(|v| {
28113 if let Value::String(s) = v {
28114 Some(s.as_str().to_string())
28115 } else {
28116 None
28117 }
28118 })
28119 .ok_or_else(|| RuntimeError::new("Invalid sender agent"))?,
28120 _ => {
28121 return Err(RuntimeError::new(
28122 "swarm_send_message requires sender agent",
28123 ))
28124 }
28125 };
28126
28127 let to_id = match &args[1] {
28128 Value::String(s) => s.as_str().to_string(),
28129 Value::Map(m) => m
28130 .borrow()
28131 .get("id")
28132 .and_then(|v| {
28133 if let Value::String(s) = v {
28134 Some(s.as_str().to_string())
28135 } else {
28136 None
28137 }
28138 })
28139 .ok_or_else(|| RuntimeError::new("Invalid receiver agent"))?,
28140 _ => {
28141 return Err(RuntimeError::new(
28142 "swarm_send_message requires receiver agent",
28143 ))
28144 }
28145 };
28146
28147 let msg_type = match &args[2] {
28148 Value::String(s) => s.as_str().to_string(),
28149 _ => return Err(RuntimeError::new("message type must be string")),
28150 };
28151
28152 let content = args[3].clone();
28153
28154 let now = std::time::SystemTime::now()
28155 .duration_since(std::time::UNIX_EPOCH)
28156 .unwrap_or_default()
28157 .as_secs();
28158
28159 let message = AgentMessage {
28160 from: from_id.clone(),
28161 to: to_id.clone(),
28162 msg_type,
28163 content,
28164 timestamp: now,
28165 };
28166
28167 AGENT_MESSAGES.with(|messages| {
28168 if let Some(queue) = messages.borrow_mut().get_mut(&to_id) {
28169 queue.push(message);
28170 Ok(Value::Bool(true))
28171 } else {
28172 Err(RuntimeError::new(format!("Agent '{}' not found", to_id)))
28173 }
28174 })
28175 });
28176
28177 define(interp, "swarm_receive_messages", Some(1), |_, args| {
28179 let agent_id = match &args[0] {
28180 Value::String(s) => s.as_str().to_string(),
28181 Value::Map(m) => m
28182 .borrow()
28183 .get("id")
28184 .and_then(|v| {
28185 if let Value::String(s) = v {
28186 Some(s.as_str().to_string())
28187 } else {
28188 None
28189 }
28190 })
28191 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28192 _ => return Err(RuntimeError::new("swarm_receive_messages requires agent")),
28193 };
28194
28195 AGENT_MESSAGES.with(|messages| {
28196 if let Some(queue) = messages.borrow_mut().get_mut(&agent_id) {
28197 let msgs: Vec<Value> = queue
28198 .drain(..)
28199 .map(|m| {
28200 let mut msg_map = HashMap::new();
28201 msg_map.insert("from".to_string(), Value::String(Rc::new(m.from)));
28202 msg_map.insert("to".to_string(), Value::String(Rc::new(m.to)));
28203 msg_map.insert("type".to_string(), Value::String(Rc::new(m.msg_type)));
28204 msg_map.insert("content".to_string(), m.content);
28205 msg_map.insert("timestamp".to_string(), Value::Int(m.timestamp as i64));
28206 Value::Map(Rc::new(RefCell::new(msg_map)))
28207 })
28208 .collect();
28209 Ok(Value::Array(Rc::new(RefCell::new(msgs))))
28210 } else {
28211 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
28212 }
28213 })
28214 });
28215
28216 define(interp, "swarm_broadcast", Some(3), |_, args| {
28218 let from_id = match &args[0] {
28219 Value::String(s) => s.as_str().to_string(),
28220 Value::Map(m) => m
28221 .borrow()
28222 .get("id")
28223 .and_then(|v| {
28224 if let Value::String(s) = v {
28225 Some(s.as_str().to_string())
28226 } else {
28227 None
28228 }
28229 })
28230 .ok_or_else(|| RuntimeError::new("Invalid sender agent"))?,
28231 _ => return Err(RuntimeError::new("swarm_broadcast requires sender agent")),
28232 };
28233
28234 let msg_type = match &args[1] {
28235 Value::String(s) => s.as_str().to_string(),
28236 _ => return Err(RuntimeError::new("message type must be string")),
28237 };
28238
28239 let content = args[2].clone();
28240
28241 let now = std::time::SystemTime::now()
28242 .duration_since(std::time::UNIX_EPOCH)
28243 .unwrap_or_default()
28244 .as_secs();
28245
28246 let agent_ids: Vec<String> = AGENT_REGISTRY.with(|registry| {
28248 registry
28249 .borrow()
28250 .keys()
28251 .filter(|id| *id != &from_id)
28252 .cloned()
28253 .collect()
28254 });
28255
28256 let mut count = 0;
28257 AGENT_MESSAGES.with(|messages| {
28258 let mut msgs = messages.borrow_mut();
28259 for to_id in agent_ids {
28260 if let Some(queue) = msgs.get_mut(&to_id) {
28261 queue.push(AgentMessage {
28262 from: from_id.clone(),
28263 to: to_id,
28264 msg_type: msg_type.clone(),
28265 content: content.clone(),
28266 timestamp: now,
28267 });
28268 count += 1;
28269 }
28270 }
28271 });
28272
28273 Ok(Value::Int(count))
28274 });
28275
28276 define(interp, "swarm_find_agents", Some(1), |_, args| {
28278 let capability = match &args[0] {
28279 Value::String(s) => s.as_str().to_string(),
28280 _ => {
28281 return Err(RuntimeError::new(
28282 "swarm_find_agents requires capability string",
28283 ))
28284 }
28285 };
28286
28287 let agents: Vec<Value> = AGENT_REGISTRY.with(|registry| {
28288 registry
28289 .borrow()
28290 .values()
28291 .filter(|agent| agent.capabilities.contains(&capability))
28292 .map(|agent| {
28293 let mut info = HashMap::new();
28294 info.insert("id".to_string(), Value::String(Rc::new(agent.id.clone())));
28295 info.insert(
28296 "type".to_string(),
28297 Value::String(Rc::new(agent.agent_type.clone())),
28298 );
28299 info.insert(
28300 "capabilities".to_string(),
28301 Value::Array(Rc::new(RefCell::new(
28302 agent
28303 .capabilities
28304 .iter()
28305 .map(|c| Value::String(Rc::new(c.clone())))
28306 .collect(),
28307 ))),
28308 );
28309 Value::Map(Rc::new(RefCell::new(info)))
28310 })
28311 .collect()
28312 });
28313
28314 Ok(Value::Array(Rc::new(RefCell::new(agents))))
28315 });
28316
28317 define(interp, "swarm_list_agents", Some(0), |_, _args| {
28319 let agents: Vec<Value> = AGENT_REGISTRY.with(|registry| {
28320 registry
28321 .borrow()
28322 .values()
28323 .map(|agent| {
28324 let mut info = HashMap::new();
28325 info.insert("id".to_string(), Value::String(Rc::new(agent.id.clone())));
28326 info.insert(
28327 "type".to_string(),
28328 Value::String(Rc::new(agent.agent_type.clone())),
28329 );
28330 info.insert(
28331 "capabilities".to_string(),
28332 Value::Array(Rc::new(RefCell::new(
28333 agent
28334 .capabilities
28335 .iter()
28336 .map(|c| Value::String(Rc::new(c.clone())))
28337 .collect(),
28338 ))),
28339 );
28340 info.insert(
28341 "created_at".to_string(),
28342 Value::Int(agent.created_at as i64),
28343 );
28344 Value::Map(Rc::new(RefCell::new(info)))
28345 })
28346 .collect()
28347 });
28348 Ok(Value::Array(Rc::new(RefCell::new(agents))))
28349 });
28350
28351 define(interp, "swarm_set_state", Some(3), |_, args| {
28353 let agent_id = match &args[0] {
28354 Value::String(s) => s.as_str().to_string(),
28355 Value::Map(m) => m
28356 .borrow()
28357 .get("id")
28358 .and_then(|v| {
28359 if let Value::String(s) = v {
28360 Some(s.as_str().to_string())
28361 } else {
28362 None
28363 }
28364 })
28365 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28366 _ => return Err(RuntimeError::new("swarm_set_state requires agent")),
28367 };
28368
28369 let key = match &args[1] {
28370 Value::String(s) => s.as_str().to_string(),
28371 _ => return Err(RuntimeError::new("key must be string")),
28372 };
28373
28374 let value = args[2].clone();
28375
28376 AGENT_REGISTRY.with(|registry| {
28377 if let Some(agent) = registry.borrow_mut().get_mut(&agent_id) {
28378 agent.state.insert(key, value);
28379 Ok(Value::Bool(true))
28380 } else {
28381 Err(RuntimeError::new(format!("Agent '{}' not found", agent_id)))
28382 }
28383 })
28384 });
28385
28386 define(interp, "swarm_get_state", Some(2), |_, args| {
28388 let agent_id = match &args[0] {
28389 Value::String(s) => s.as_str().to_string(),
28390 Value::Map(m) => m
28391 .borrow()
28392 .get("id")
28393 .and_then(|v| {
28394 if let Value::String(s) = v {
28395 Some(s.as_str().to_string())
28396 } else {
28397 None
28398 }
28399 })
28400 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28401 _ => return Err(RuntimeError::new("swarm_get_state requires agent")),
28402 };
28403
28404 let key = match &args[1] {
28405 Value::String(s) => s.as_str().to_string(),
28406 _ => return Err(RuntimeError::new("key must be string")),
28407 };
28408
28409 AGENT_REGISTRY.with(|registry| {
28410 if let Some(agent) = registry.borrow().get(&agent_id) {
28411 Ok(agent.state.get(&key).cloned().unwrap_or(Value::Null))
28412 } else {
28413 Ok(Value::Null)
28414 }
28415 })
28416 });
28417
28418 define(interp, "swarm_remove_agent", Some(1), |_, args| {
28420 let agent_id = match &args[0] {
28421 Value::String(s) => s.as_str().to_string(),
28422 Value::Map(m) => m
28423 .borrow()
28424 .get("id")
28425 .and_then(|v| {
28426 if let Value::String(s) = v {
28427 Some(s.as_str().to_string())
28428 } else {
28429 None
28430 }
28431 })
28432 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28433 _ => return Err(RuntimeError::new("swarm_remove_agent requires agent")),
28434 };
28435
28436 let removed =
28437 AGENT_REGISTRY.with(|registry| registry.borrow_mut().remove(&agent_id).is_some());
28438
28439 AGENT_MESSAGES.with(|messages| {
28440 messages.borrow_mut().remove(&agent_id);
28441 });
28442
28443 Ok(Value::Bool(removed))
28444 });
28445
28446 define(interp, "swarm_consensus", Some(2), |_, args| {
28448 let topic = match &args[0] {
28449 Value::String(s) => s.as_str().to_string(),
28450 _ => return Err(RuntimeError::new("topic must be string")),
28451 };
28452
28453 let votes = match &args[1] {
28454 Value::Array(arr) => arr.borrow().clone(),
28455 _ => return Err(RuntimeError::new("votes must be array")),
28456 };
28457
28458 let mut vote_counts: HashMap<String, i64> = HashMap::new();
28460 for vote in votes.iter() {
28461 let vote_str = match vote {
28462 Value::String(s) => s.as_str().to_string(),
28463 Value::Bool(b) => b.to_string(),
28464 Value::Int(n) => n.to_string(),
28465 _ => continue,
28466 };
28467 *vote_counts.entry(vote_str).or_insert(0) += 1;
28468 }
28469
28470 let total = votes.len() as i64;
28472 let (winner, count) = vote_counts
28473 .iter()
28474 .max_by_key(|(_, &count)| count)
28475 .map(|(k, &v)| (k.clone(), v))
28476 .unwrap_or_default();
28477
28478 let mut result = HashMap::new();
28479 result.insert("topic".to_string(), Value::String(Rc::new(topic)));
28480 result.insert("winner".to_string(), Value::String(Rc::new(winner)));
28481 result.insert("votes".to_string(), Value::Int(count));
28482 result.insert("total".to_string(), Value::Int(total));
28483 result.insert("majority".to_string(), Value::Bool(count > total / 2));
28484
28485 Ok(Value::Map(Rc::new(RefCell::new(result))))
28486 });
28487}
28488
28489fn register_agent_reasoning(interp: &mut Interpreter) {
28494 define(interp, "reason_constraint", Some(3), |_, args| {
28496 let name = match &args[0] {
28497 Value::String(s) => s.as_str().to_string(),
28498 _ => return Err(RuntimeError::new("constraint name must be string")),
28499 };
28500
28501 let constraint_type = match &args[1] {
28502 Value::String(s) => s.as_str().to_string(),
28503 _ => return Err(RuntimeError::new("constraint type must be string")),
28504 };
28505
28506 let params = args[2].clone();
28507
28508 let mut constraint = HashMap::new();
28509 constraint.insert("name".to_string(), Value::String(Rc::new(name)));
28510 constraint.insert("type".to_string(), Value::String(Rc::new(constraint_type)));
28511 constraint.insert("params".to_string(), params);
28512 constraint.insert("satisfied".to_string(), Value::Bool(false));
28513
28514 Ok(Value::Map(Rc::new(RefCell::new(constraint))))
28515 });
28516
28517 define(interp, "reason_check_constraint", Some(2), |_, args| {
28519 let constraint = match &args[0] {
28520 Value::Map(m) => m.borrow().clone(),
28521 _ => {
28522 return Err(RuntimeError::new(
28523 "reason_check_constraint requires constraint",
28524 ))
28525 }
28526 };
28527
28528 let context = match &args[1] {
28529 Value::Map(m) => m.borrow().clone(),
28530 _ => {
28531 return Err(RuntimeError::new(
28532 "reason_check_constraint requires context",
28533 ))
28534 }
28535 };
28536
28537 let constraint_type = constraint
28538 .get("type")
28539 .and_then(|v| {
28540 if let Value::String(s) = v {
28541 Some(s.as_str())
28542 } else {
28543 None
28544 }
28545 })
28546 .unwrap_or("unknown");
28547
28548 let params = constraint.get("params").cloned().unwrap_or(Value::Null);
28549
28550 let satisfied = match constraint_type {
28551 "equals" => {
28552 if let Value::Map(p) = ¶ms {
28553 let p = p.borrow();
28554 let var_name = p
28555 .get("var")
28556 .and_then(|v| {
28557 if let Value::String(s) = v {
28558 Some(s.as_str().to_string())
28559 } else {
28560 None
28561 }
28562 })
28563 .unwrap_or_default();
28564 let expected = p.get("value").cloned().unwrap_or(Value::Null);
28565 let actual = context.get(&var_name).cloned().unwrap_or(Value::Null);
28566 values_equal_simple(&actual, &expected)
28567 } else {
28568 false
28569 }
28570 }
28571 "not_null" => {
28572 if let Value::Map(p) = ¶ms {
28573 let p = p.borrow();
28574 let var_name = p
28575 .get("var")
28576 .and_then(|v| {
28577 if let Value::String(s) = v {
28578 Some(s.as_str().to_string())
28579 } else {
28580 None
28581 }
28582 })
28583 .unwrap_or_default();
28584 !matches!(context.get(&var_name), None | Some(Value::Null))
28585 } else {
28586 false
28587 }
28588 }
28589 "range" => {
28590 if let Value::Map(p) = ¶ms {
28591 let p = p.borrow();
28592 let var_name = p
28593 .get("var")
28594 .and_then(|v| {
28595 if let Value::String(s) = v {
28596 Some(s.as_str().to_string())
28597 } else {
28598 None
28599 }
28600 })
28601 .unwrap_or_default();
28602 let min = p
28603 .get("min")
28604 .and_then(|v| match v {
28605 Value::Int(n) => Some(*n as f64),
28606 Value::Float(f) => Some(*f),
28607 _ => None,
28608 })
28609 .unwrap_or(f64::NEG_INFINITY);
28610 let max = p
28611 .get("max")
28612 .and_then(|v| match v {
28613 Value::Int(n) => Some(*n as f64),
28614 Value::Float(f) => Some(*f),
28615 _ => None,
28616 })
28617 .unwrap_or(f64::INFINITY);
28618 let actual = context
28619 .get(&var_name)
28620 .and_then(|v| match v {
28621 Value::Int(n) => Some(*n as f64),
28622 Value::Float(f) => Some(*f),
28623 _ => None,
28624 })
28625 .unwrap_or(0.0);
28626 actual >= min && actual <= max
28627 } else {
28628 false
28629 }
28630 }
28631 "contains" => {
28632 if let Value::Map(p) = ¶ms {
28633 let p = p.borrow();
28634 let var_name = p
28635 .get("var")
28636 .and_then(|v| {
28637 if let Value::String(s) = v {
28638 Some(s.as_str().to_string())
28639 } else {
28640 None
28641 }
28642 })
28643 .unwrap_or_default();
28644 let needle = p
28645 .get("value")
28646 .and_then(|v| {
28647 if let Value::String(s) = v {
28648 Some(s.as_str().to_string())
28649 } else {
28650 None
28651 }
28652 })
28653 .unwrap_or_default();
28654 let actual = context
28655 .get(&var_name)
28656 .and_then(|v| {
28657 if let Value::String(s) = v {
28658 Some(s.as_str().to_string())
28659 } else {
28660 None
28661 }
28662 })
28663 .unwrap_or_default();
28664 actual.contains(&needle)
28665 } else {
28666 false
28667 }
28668 }
28669 _ => false,
28670 };
28671
28672 let mut result = HashMap::new();
28673 result.insert("satisfied".to_string(), Value::Bool(satisfied));
28674 result.insert(
28675 "constraint".to_string(),
28676 Value::Map(Rc::new(RefCell::new(constraint))),
28677 );
28678
28679 Ok(Value::Map(Rc::new(RefCell::new(result))))
28680 });
28681
28682 define(interp, "reason_check_all", Some(2), |interp, args| {
28684 let constraints = match &args[0] {
28685 Value::Array(arr) => arr.borrow().clone(),
28686 _ => {
28687 return Err(RuntimeError::new(
28688 "reason_check_all requires constraints array",
28689 ))
28690 }
28691 };
28692
28693 let context = args[1].clone();
28694
28695 let mut all_satisfied = true;
28696 let mut results: Vec<Value> = Vec::new();
28697
28698 for constraint in constraints.iter() {
28699 if let Value::Map(c) = constraint {
28701 let c_type = c
28702 .borrow()
28703 .get("type")
28704 .and_then(|v| {
28705 if let Value::String(s) = v {
28706 Some(s.as_ref().clone())
28707 } else {
28708 None
28709 }
28710 })
28711 .unwrap_or_else(|| "unknown".to_string());
28712 let params = c.borrow().get("params").cloned().unwrap_or(Value::Null);
28713
28714 let ctx = match &context {
28715 Value::Map(m) => m.borrow().clone(),
28716 _ => HashMap::new(),
28717 };
28718
28719 let satisfied = match c_type.as_str() {
28720 "equals" => {
28721 if let Value::Map(p) = ¶ms {
28722 let p = p.borrow();
28723 let var_name = p
28724 .get("var")
28725 .and_then(|v| {
28726 if let Value::String(s) = v {
28727 Some(s.as_str().to_string())
28728 } else {
28729 None
28730 }
28731 })
28732 .unwrap_or_default();
28733 let expected = p.get("value").cloned().unwrap_or(Value::Null);
28734 let actual = ctx.get(&var_name).cloned().unwrap_or(Value::Null);
28735 values_equal_simple(&actual, &expected)
28736 } else {
28737 false
28738 }
28739 }
28740 "not_null" => {
28741 if let Value::Map(p) = ¶ms {
28742 let p = p.borrow();
28743 let var_name = p
28744 .get("var")
28745 .and_then(|v| {
28746 if let Value::String(s) = v {
28747 Some(s.as_str().to_string())
28748 } else {
28749 None
28750 }
28751 })
28752 .unwrap_or_default();
28753 !matches!(ctx.get(&var_name), None | Some(Value::Null))
28754 } else {
28755 false
28756 }
28757 }
28758 _ => true, };
28760
28761 if !satisfied {
28762 all_satisfied = false;
28763 }
28764
28765 let mut r = HashMap::new();
28766 r.insert("constraint".to_string(), constraint.clone());
28767 r.insert("satisfied".to_string(), Value::Bool(satisfied));
28768 results.push(Value::Map(Rc::new(RefCell::new(r))));
28769 }
28770 }
28771
28772 let _ = interp; let mut result = HashMap::new();
28775 result.insert("all_satisfied".to_string(), Value::Bool(all_satisfied));
28776 result.insert(
28777 "results".to_string(),
28778 Value::Array(Rc::new(RefCell::new(results))),
28779 );
28780 result.insert("total".to_string(), Value::Int(constraints.len() as i64));
28781
28782 Ok(Value::Map(Rc::new(RefCell::new(result))))
28783 });
28784
28785 define(interp, "reason_implies", Some(2), |_, args| {
28787 let antecedent = args[0].clone();
28788 let consequent = args[1].clone();
28789
28790 let mut implication = HashMap::new();
28791 implication.insert(
28792 "type".to_string(),
28793 Value::String(Rc::new("implication".to_string())),
28794 );
28795 implication.insert("if".to_string(), antecedent);
28796 implication.insert("then".to_string(), consequent);
28797
28798 Ok(Value::Map(Rc::new(RefCell::new(implication))))
28799 });
28800
28801 define(interp, "reason_and", None, |_, args| {
28803 let conditions: Vec<Value> = args.into_iter().collect();
28804
28805 let mut conjunction = HashMap::new();
28806 conjunction.insert(
28807 "type".to_string(),
28808 Value::String(Rc::new("and".to_string())),
28809 );
28810 conjunction.insert(
28811 "conditions".to_string(),
28812 Value::Array(Rc::new(RefCell::new(conditions))),
28813 );
28814
28815 Ok(Value::Map(Rc::new(RefCell::new(conjunction))))
28816 });
28817
28818 define(interp, "reason_or", None, |_, args| {
28820 let conditions: Vec<Value> = args.into_iter().collect();
28821
28822 let mut disjunction = HashMap::new();
28823 disjunction.insert("type".to_string(), Value::String(Rc::new("or".to_string())));
28824 disjunction.insert(
28825 "conditions".to_string(),
28826 Value::Array(Rc::new(RefCell::new(conditions))),
28827 );
28828
28829 Ok(Value::Map(Rc::new(RefCell::new(disjunction))))
28830 });
28831
28832 define(interp, "reason_not", Some(1), |_, args| {
28834 let condition = args[0].clone();
28835
28836 let mut negation = HashMap::new();
28837 negation.insert(
28838 "type".to_string(),
28839 Value::String(Rc::new("not".to_string())),
28840 );
28841 negation.insert("condition".to_string(), condition);
28842
28843 Ok(Value::Map(Rc::new(RefCell::new(negation))))
28844 });
28845
28846 define(interp, "reason_evaluate", Some(2), |_, args| {
28848 let expr = match &args[0] {
28849 Value::Map(m) => m.borrow().clone(),
28850 Value::Bool(b) => return Ok(Value::Bool(*b)),
28851 _ => return Err(RuntimeError::new("reason_evaluate requires expression")),
28852 };
28853
28854 let context = match &args[1] {
28855 Value::Map(m) => m.borrow().clone(),
28856 _ => HashMap::new(),
28857 };
28858
28859 fn eval_expr(expr: &HashMap<String, Value>, ctx: &HashMap<String, Value>) -> bool {
28860 let expr_type = expr
28861 .get("type")
28862 .and_then(|v| {
28863 if let Value::String(s) = v {
28864 Some(s.as_str())
28865 } else {
28866 None
28867 }
28868 })
28869 .unwrap_or("unknown");
28870
28871 match expr_type {
28872 "and" => {
28873 if let Some(Value::Array(conditions)) = expr.get("conditions") {
28874 conditions.borrow().iter().all(|c| {
28875 if let Value::Map(m) = c {
28876 eval_expr(&m.borrow(), ctx)
28877 } else if let Value::Bool(b) = c {
28878 *b
28879 } else {
28880 false
28881 }
28882 })
28883 } else {
28884 false
28885 }
28886 }
28887 "or" => {
28888 if let Some(Value::Array(conditions)) = expr.get("conditions") {
28889 conditions.borrow().iter().any(|c| {
28890 if let Value::Map(m) = c {
28891 eval_expr(&m.borrow(), ctx)
28892 } else if let Value::Bool(b) = c {
28893 *b
28894 } else {
28895 false
28896 }
28897 })
28898 } else {
28899 false
28900 }
28901 }
28902 "not" => {
28903 if let Some(condition) = expr.get("condition") {
28904 if let Value::Map(m) = condition {
28905 !eval_expr(&m.borrow(), ctx)
28906 } else if let Value::Bool(b) = condition {
28907 !b
28908 } else {
28909 false
28910 }
28911 } else {
28912 false
28913 }
28914 }
28915 "implication" => {
28916 let antecedent = if let Some(a) = expr.get("if") {
28917 if let Value::Map(m) = a {
28918 eval_expr(&m.borrow(), ctx)
28919 } else if let Value::Bool(b) = a {
28920 *b
28921 } else {
28922 false
28923 }
28924 } else {
28925 false
28926 };
28927
28928 let consequent = if let Some(c) = expr.get("then") {
28929 if let Value::Map(m) = c {
28930 eval_expr(&m.borrow(), ctx)
28931 } else if let Value::Bool(b) = c {
28932 *b
28933 } else {
28934 false
28935 }
28936 } else {
28937 false
28938 };
28939
28940 !antecedent || consequent
28942 }
28943 _ => false,
28944 }
28945 }
28946
28947 Ok(Value::Bool(eval_expr(&expr, &context)))
28948 });
28949
28950 define(interp, "reason_proof", Some(3), |_, args| {
28952 let step = match &args[0] {
28953 Value::String(s) => s.as_str().to_string(),
28954 _ => return Err(RuntimeError::new("proof step must be string")),
28955 };
28956
28957 let justification = match &args[1] {
28958 Value::String(s) => s.as_str().to_string(),
28959 _ => return Err(RuntimeError::new("justification must be string")),
28960 };
28961
28962 let conclusion = args[2].clone();
28963
28964 let now = std::time::SystemTime::now()
28965 .duration_since(std::time::UNIX_EPOCH)
28966 .unwrap_or_default()
28967 .as_secs();
28968
28969 let mut proof = HashMap::new();
28970 proof.insert("step".to_string(), Value::String(Rc::new(step)));
28971 proof.insert(
28972 "justification".to_string(),
28973 Value::String(Rc::new(justification)),
28974 );
28975 proof.insert("conclusion".to_string(), conclusion);
28976 proof.insert("timestamp".to_string(), Value::Int(now as i64));
28977
28978 Ok(Value::Map(Rc::new(RefCell::new(proof))))
28979 });
28980
28981 define(interp, "reason_chain", None, |_, args| {
28983 let steps: Vec<Value> = args.into_iter().collect();
28984
28985 let mut chain = HashMap::new();
28986 chain.insert(
28987 "type".to_string(),
28988 Value::String(Rc::new("proof_chain".to_string())),
28989 );
28990 chain.insert(
28991 "steps".to_string(),
28992 Value::Array(Rc::new(RefCell::new(steps.clone()))),
28993 );
28994 chain.insert("length".to_string(), Value::Int(steps.len() as i64));
28995
28996 let final_conclusion = steps
28998 .last()
28999 .and_then(|s| {
29000 if let Value::Map(m) = s {
29001 m.borrow().get("conclusion").cloned()
29002 } else {
29003 None
29004 }
29005 })
29006 .unwrap_or(Value::Null);
29007 chain.insert("final_conclusion".to_string(), final_conclusion);
29008
29009 Ok(Value::Map(Rc::new(RefCell::new(chain))))
29010 });
29011
29012 define(interp, "reason_hypothesis", Some(2), |_, args| {
29014 let claim = match &args[0] {
29015 Value::String(s) => s.as_str().to_string(),
29016 _ => return Err(RuntimeError::new("hypothesis claim must be string")),
29017 };
29018
29019 let required_evidence = match &args[1] {
29020 Value::Array(arr) => arr.clone(),
29021 _ => return Err(RuntimeError::new("required evidence must be array")),
29022 };
29023
29024 let mut hypothesis = HashMap::new();
29025 hypothesis.insert("claim".to_string(), Value::String(Rc::new(claim)));
29026 hypothesis.insert(
29027 "required_evidence".to_string(),
29028 Value::Array(required_evidence),
29029 );
29030 hypothesis.insert(
29031 "status".to_string(),
29032 Value::String(Rc::new("unverified".to_string())),
29033 );
29034 hypothesis.insert("confidence".to_string(), Value::Float(0.0));
29035
29036 Ok(Value::Map(Rc::new(RefCell::new(hypothesis))))
29037 });
29038
29039 define(interp, "reason_verify_hypothesis", Some(2), |_, args| {
29041 let hypothesis = match &args[0] {
29042 Value::Map(m) => m.clone(),
29043 _ => {
29044 return Err(RuntimeError::new(
29045 "reason_verify_hypothesis requires hypothesis",
29046 ))
29047 }
29048 };
29049
29050 let evidence = match &args[1] {
29051 Value::Map(m) => m.borrow().clone(),
29052 _ => {
29053 return Err(RuntimeError::new(
29054 "reason_verify_hypothesis requires evidence map",
29055 ))
29056 }
29057 };
29058
29059 let required = hypothesis
29060 .borrow()
29061 .get("required_evidence")
29062 .and_then(|v| {
29063 if let Value::Array(arr) = v {
29064 Some(arr.borrow().clone())
29065 } else {
29066 None
29067 }
29068 })
29069 .unwrap_or_default();
29070
29071 let mut found = 0;
29072 for req in required.iter() {
29073 if let Value::String(key) = req {
29074 if evidence.contains_key(key.as_str()) {
29075 found += 1;
29076 }
29077 }
29078 }
29079
29080 let total = required.len();
29081 let confidence = if total > 0 {
29082 found as f64 / total as f64
29083 } else {
29084 0.0
29085 };
29086 let verified = found == total && total > 0;
29087
29088 {
29089 let mut h = hypothesis.borrow_mut();
29090 h.insert("confidence".to_string(), Value::Float(confidence));
29091 h.insert(
29092 "status".to_string(),
29093 Value::String(Rc::new(if verified {
29094 "verified".to_string()
29095 } else {
29096 "unverified".to_string()
29097 })),
29098 );
29099 }
29100
29101 let mut result = HashMap::new();
29102 result.insert("verified".to_string(), Value::Bool(verified));
29103 result.insert("confidence".to_string(), Value::Float(confidence));
29104 result.insert("found".to_string(), Value::Int(found as i64));
29105 result.insert("required".to_string(), Value::Int(total as i64));
29106 result.insert("hypothesis".to_string(), Value::Map(hypothesis));
29107
29108 Ok(Value::Map(Rc::new(RefCell::new(result))))
29109 });
29110}
29111
29112
29113fn register_terminal(interp: &mut Interpreter) {
29120 const RESET: &str = "\x1b[0m";
29122 const BOLD: &str = "\x1b[1m";
29123 const DIM: &str = "\x1b[2m";
29124 const ITALIC: &str = "\x1b[3m";
29125 const UNDERLINE: &str = "\x1b[4m";
29126
29127 const FG_BLACK: &str = "\x1b[30m";
29129 const FG_RED: &str = "\x1b[31m";
29130 const FG_GREEN: &str = "\x1b[32m";
29131 const FG_YELLOW: &str = "\x1b[33m";
29132 const FG_BLUE: &str = "\x1b[34m";
29133 const FG_MAGENTA: &str = "\x1b[35m";
29134 const FG_CYAN: &str = "\x1b[36m";
29135 const FG_WHITE: &str = "\x1b[37m";
29136
29137 const FG_BRIGHT_BLACK: &str = "\x1b[90m";
29139 const FG_BRIGHT_RED: &str = "\x1b[91m";
29140 const FG_BRIGHT_GREEN: &str = "\x1b[92m";
29141 const FG_BRIGHT_YELLOW: &str = "\x1b[93m";
29142 const FG_BRIGHT_BLUE: &str = "\x1b[94m";
29143 const FG_BRIGHT_MAGENTA: &str = "\x1b[95m";
29144 const FG_BRIGHT_CYAN: &str = "\x1b[96m";
29145 const FG_BRIGHT_WHITE: &str = "\x1b[97m";
29146
29147 define(interp, "term_reset", Some(0), |_, _| {
29149 Ok(Value::String(Rc::new(RESET.to_string())))
29150 });
29151
29152 define(interp, "term_bold", Some(1), |_, args| {
29154 let text = match &args[0] {
29155 Value::String(s) => (**s).clone(),
29156 other => format!("{}", other),
29157 };
29158 Ok(Value::String(Rc::new(format!("{}{}{}", BOLD, text, RESET))))
29159 });
29160
29161 define(interp, "term_dim", Some(1), |_, args| {
29163 let text = match &args[0] {
29164 Value::String(s) => (**s).clone(),
29165 other => format!("{}", other),
29166 };
29167 Ok(Value::String(Rc::new(format!("{}{}{}", DIM, text, RESET))))
29168 });
29169
29170 define(interp, "term_italic", Some(1), |_, args| {
29172 let text = match &args[0] {
29173 Value::String(s) => (**s).clone(),
29174 other => format!("{}", other),
29175 };
29176 Ok(Value::String(Rc::new(format!("{}{}{}", ITALIC, text, RESET))))
29177 });
29178
29179 define(interp, "term_underline", Some(1), |_, args| {
29181 let text = match &args[0] {
29182 Value::String(s) => (**s).clone(),
29183 other => format!("{}", other),
29184 };
29185 Ok(Value::String(Rc::new(format!("{}{}{}", UNDERLINE, text, RESET))))
29186 });
29187
29188 define(interp, "term_red", Some(1), |_, args| {
29190 let text = match &args[0] {
29191 Value::String(s) => (**s).clone(),
29192 other => format!("{}", other),
29193 };
29194 Ok(Value::String(Rc::new(format!("{}{}{}", FG_RED, text, RESET))))
29195 });
29196
29197 define(interp, "term_green", Some(1), |_, args| {
29199 let text = match &args[0] {
29200 Value::String(s) => (**s).clone(),
29201 other => format!("{}", other),
29202 };
29203 Ok(Value::String(Rc::new(format!("{}{}{}", FG_GREEN, text, RESET))))
29204 });
29205
29206 define(interp, "term_yellow", Some(1), |_, args| {
29208 let text = match &args[0] {
29209 Value::String(s) => (**s).clone(),
29210 other => format!("{}", other),
29211 };
29212 Ok(Value::String(Rc::new(format!("{}{}{}", FG_YELLOW, text, RESET))))
29213 });
29214
29215 define(interp, "term_blue", Some(1), |_, args| {
29217 let text = match &args[0] {
29218 Value::String(s) => (**s).clone(),
29219 other => format!("{}", other),
29220 };
29221 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BLUE, text, RESET))))
29222 });
29223
29224 define(interp, "term_magenta", Some(1), |_, args| {
29226 let text = match &args[0] {
29227 Value::String(s) => (**s).clone(),
29228 other => format!("{}", other),
29229 };
29230 Ok(Value::String(Rc::new(format!("{}{}{}", FG_MAGENTA, text, RESET))))
29231 });
29232
29233 define(interp, "term_cyan", Some(1), |_, args| {
29235 let text = match &args[0] {
29236 Value::String(s) => (**s).clone(),
29237 other => format!("{}", other),
29238 };
29239 Ok(Value::String(Rc::new(format!("{}{}{}", FG_CYAN, text, RESET))))
29240 });
29241
29242 define(interp, "term_white", Some(1), |_, args| {
29244 let text = match &args[0] {
29245 Value::String(s) => (**s).clone(),
29246 other => format!("{}", other),
29247 };
29248 Ok(Value::String(Rc::new(format!("{}{}{}", FG_WHITE, text, RESET))))
29249 });
29250
29251 define(interp, "term_black", Some(1), |_, args| {
29253 let text = match &args[0] {
29254 Value::String(s) => (**s).clone(),
29255 other => format!("{}", other),
29256 };
29257 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BLACK, text, RESET))))
29258 });
29259
29260 define(interp, "term_bright_red", Some(1), |_, args| {
29262 let text = match &args[0] {
29263 Value::String(s) => (**s).clone(),
29264 other => format!("{}", other),
29265 };
29266 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BRIGHT_RED, text, RESET))))
29267 });
29268
29269 define(interp, "term_bright_green", Some(1), |_, args| {
29271 let text = match &args[0] {
29272 Value::String(s) => (**s).clone(),
29273 other => format!("{}", other),
29274 };
29275 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BRIGHT_GREEN, text, RESET))))
29276 });
29277
29278 define(interp, "term_bright_cyan", Some(1), |_, args| {
29280 let text = match &args[0] {
29281 Value::String(s) => (**s).clone(),
29282 other => format!("{}", other),
29283 };
29284 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BRIGHT_CYAN, text, RESET))))
29285 });
29286
29287 define(interp, "term_style", None, |_, args| {
29289 if args.is_empty() {
29290 return Err(RuntimeError::new("term_style requires at least text argument"));
29291 }
29292 let text = match &args[0] {
29293 Value::String(s) => (**s).clone(),
29294 other => format!("{}", other),
29295 };
29296
29297 let mut prefix = String::new();
29298 for arg in &args[1..] {
29299 if let Value::String(style) = arg {
29300 match style.as_str() {
29301 "bold" => prefix.push_str(BOLD),
29302 "dim" => prefix.push_str(DIM),
29303 "italic" => prefix.push_str(ITALIC),
29304 "underline" => prefix.push_str(UNDERLINE),
29305 "red" => prefix.push_str(FG_RED),
29306 "green" => prefix.push_str(FG_GREEN),
29307 "yellow" => prefix.push_str(FG_YELLOW),
29308 "blue" => prefix.push_str(FG_BLUE),
29309 "magenta" => prefix.push_str(FG_MAGENTA),
29310 "cyan" => prefix.push_str(FG_CYAN),
29311 "white" => prefix.push_str(FG_WHITE),
29312 "black" => prefix.push_str(FG_BLACK),
29313 "bright_red" => prefix.push_str(FG_BRIGHT_RED),
29314 "bright_green" => prefix.push_str(FG_BRIGHT_GREEN),
29315 "bright_yellow" => prefix.push_str(FG_BRIGHT_YELLOW),
29316 "bright_blue" => prefix.push_str(FG_BRIGHT_BLUE),
29317 "bright_magenta" => prefix.push_str(FG_BRIGHT_MAGENTA),
29318 "bright_cyan" => prefix.push_str(FG_BRIGHT_CYAN),
29319 "bright_white" => prefix.push_str(FG_BRIGHT_WHITE),
29320 _ => {} }
29322 }
29323 }
29324
29325 Ok(Value::String(Rc::new(format!("{}{}{}", prefix, text, RESET))))
29326 });
29327
29328 define(interp, "term_progress_bar", Some(3), |_, args| {
29331 let current = match &args[0] {
29332 Value::Int(n) => *n as f64,
29333 Value::Float(f) => *f,
29334 _ => return Err(RuntimeError::new("term_progress_bar: current must be number")),
29335 };
29336 let total = match &args[1] {
29337 Value::Int(n) => *n as f64,
29338 Value::Float(f) => *f,
29339 _ => return Err(RuntimeError::new("term_progress_bar: total must be number")),
29340 };
29341 let width = match &args[2] {
29342 Value::Int(n) if *n > 0 => *n as usize,
29343 _ => return Err(RuntimeError::new("term_progress_bar: width must be positive integer")),
29344 };
29345
29346 let ratio = if total > 0.0 { (current / total).min(1.0).max(0.0) } else { 0.0 };
29347 let filled = (ratio * width as f64).round() as usize;
29348 let empty = width - filled;
29349 let percent = (ratio * 100.0).round() as i64;
29350
29351 let bar = format!(
29352 "[{}{}] {}%",
29353 "█".repeat(filled),
29354 "░".repeat(empty),
29355 percent
29356 );
29357
29358 Ok(Value::String(Rc::new(bar)))
29359 });
29360
29361 define(interp, "term_spinner_frames", Some(0), |_, _| {
29363 let frames: Vec<Value> = vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
29364 .into_iter()
29365 .map(|s| Value::String(Rc::new(s.to_string())))
29366 .collect();
29367 Ok(Value::Array(Rc::new(RefCell::new(frames))))
29368 });
29369
29370 define(interp, "term_check", Some(0), |_, _| {
29372 Ok(Value::String(Rc::new(format!("{}✓{}", FG_GREEN, RESET))))
29373 });
29374
29375 define(interp, "term_cross", Some(0), |_, _| {
29377 Ok(Value::String(Rc::new(format!("{}✗{}", FG_RED, RESET))))
29378 });
29379
29380 define(interp, "term_arrow", Some(0), |_, _| {
29382 Ok(Value::String(Rc::new("→".to_string())))
29383 });
29384
29385 define(interp, "term_bullet", Some(0), |_, _| {
29387 Ok(Value::String(Rc::new("•".to_string())))
29388 });
29389
29390 define(interp, "term_emoji", Some(1), |_, args| {
29392 let name = match &args[0] {
29393 Value::String(s) => s.to_lowercase(),
29394 _ => return Err(RuntimeError::new("term_emoji requires string")),
29395 };
29396
29397 let emoji = match name.as_str() {
29398 "summon" | "install" => "🔮",
29399 "banish" | "uninstall" => "👋",
29400 "invoke" | "update" => "⚡",
29401 "awaken" | "start" => "🌅",
29402 "seal" | "stop" => "🔒",
29403 "check" | "ok" | "success" => "✓",
29404 "cross" | "error" | "fail" => "✗",
29405 "arrow" | "right" => "→",
29406 "warning" | "warn" => "⚠",
29407 "info" | "information" => "ℹ",
29408 "question" | "help" => "?",
29409 "star" => "★",
29410 "heart" => "❤",
29411 "fire" => "🔥",
29412 "rocket" => "🚀",
29413 "package" | "box" => "📦",
29414 "folder" | "directory" => "📁",
29415 "file" | "document" => "📄",
29416 "gear" | "settings" => "⚙",
29417 "search" | "seek" => "🔍",
29418 "download" => "⬇",
29419 "upload" => "⬆",
29420 "sync" | "refresh" => "🔄",
29421 "lock" | "locked" => "🔒",
29422 "unlock" | "unlocked" => "🔓",
29423 "key" => "🔑",
29424 "clock" | "time" => "🕐",
29425 "calendar" | "date" => "📅",
29426 "bell" | "notification" => "🔔",
29427 _ => "•",
29428 };
29429
29430 Ok(Value::String(Rc::new(emoji.to_string())))
29431 });
29432
29433 define(interp, "term_table_row", Some(2), |_, args| {
29435 let values = match &args[0] {
29436 Value::Array(arr) => arr.borrow().clone(),
29437 _ => return Err(RuntimeError::new("term_table_row: first arg must be array")),
29438 };
29439 let widths = match &args[1] {
29440 Value::Array(arr) => arr.borrow().clone(),
29441 _ => return Err(RuntimeError::new("term_table_row: second arg must be array")),
29442 };
29443
29444 if values.len() != widths.len() {
29445 return Err(RuntimeError::new("term_table_row: arrays must have same length"));
29446 }
29447
29448 let mut parts: Vec<String> = Vec::new();
29449 for (val, width) in values.iter().zip(widths.iter()) {
29450 let text = match val {
29451 Value::String(s) => (**s).clone(),
29452 other => format!("{}", other),
29453 };
29454 let w = match width {
29455 Value::Int(n) => *n as usize,
29456 _ => 10,
29457 };
29458 let formatted = if text.chars().count() > w {
29459 text.chars().take(w - 1).collect::<String>() + "…"
29460 } else {
29461 format!("{:<width$}", text, width = w)
29462 };
29463 parts.push(formatted);
29464 }
29465
29466 Ok(Value::String(Rc::new(parts.join(" │ "))))
29467 });
29468
29469 define(interp, "term_table_separator", Some(1), |_, args| {
29471 let widths = match &args[0] {
29472 Value::Array(arr) => arr.borrow().clone(),
29473 _ => return Err(RuntimeError::new("term_table_separator: arg must be array")),
29474 };
29475
29476 let parts: Vec<String> = widths.iter().map(|w| {
29477 let width = match w {
29478 Value::Int(n) => *n as usize,
29479 _ => 10,
29480 };
29481 "─".repeat(width)
29482 }).collect();
29483
29484 Ok(Value::String(Rc::new(parts.join("─┼─"))))
29485 });
29486
29487 define(interp, "term_clear_line", Some(0), |_, _| {
29489 Ok(Value::String(Rc::new("\x1b[2K\r".to_string())))
29490 });
29491
29492 define(interp, "term_cursor_up", Some(1), |_, args| {
29494 let n = match &args[0] {
29495 Value::Int(n) if *n > 0 => *n,
29496 _ => 1,
29497 };
29498 Ok(Value::String(Rc::new(format!("\x1b[{}A", n))))
29499 });
29500
29501 define(interp, "term_cursor_down", Some(1), |_, args| {
29503 let n = match &args[0] {
29504 Value::Int(n) if *n > 0 => *n,
29505 _ => 1,
29506 };
29507 Ok(Value::String(Rc::new(format!("\x1b[{}B", n))))
29508 });
29509
29510 define(interp, "term_hide_cursor", Some(0), |_, _| {
29512 Ok(Value::String(Rc::new("\x1b[?25l".to_string())))
29513 });
29514
29515 define(interp, "term_show_cursor", Some(0), |_, _| {
29517 Ok(Value::String(Rc::new("\x1b[?25h".to_string())))
29518 });
29519
29520 define(interp, "term_is_tty", Some(0), |_, _| {
29522 use std::io::IsTerminal;
29523 Ok(Value::Bool(std::io::stdout().is_terminal()))
29524 });
29525}
29526#[cfg(test)]
29527mod tests {
29528 use super::*;
29529 use crate::Parser;
29530
29531 fn eval(source: &str) -> Result<Value, RuntimeError> {
29532 let mut parser = Parser::new(source);
29533 let file = parser
29534 .parse_file()
29535 .map_err(|e| RuntimeError::new(e.to_string()))?;
29536 let mut interp = Interpreter::new();
29537 register_stdlib(&mut interp);
29538 interp.execute(&file)
29539 }
29540
29541 #[test]
29544 fn test_math_functions() {
29545 assert!(matches!(
29546 eval("fn main() { return abs(-5); }"),
29547 Ok(Value::Int(5))
29548 ));
29549 assert!(matches!(
29550 eval("fn main() { return floor(3.7); }"),
29551 Ok(Value::Int(3))
29552 ));
29553 assert!(matches!(
29554 eval("fn main() { return ceil(3.2); }"),
29555 Ok(Value::Int(4))
29556 ));
29557 assert!(matches!(
29558 eval("fn main() { return max(3, 7); }"),
29559 Ok(Value::Int(7))
29560 ));
29561 assert!(matches!(
29562 eval("fn main() { return min(3, 7); }"),
29563 Ok(Value::Int(3))
29564 ));
29565 assert!(matches!(
29566 eval("fn main() { return round(3.5); }"),
29567 Ok(Value::Int(4))
29568 ));
29569 assert!(matches!(
29570 eval("fn main() { return sign(-5); }"),
29571 Ok(Value::Int(-1))
29572 ));
29573 assert!(matches!(
29574 eval("fn main() { return sign(0); }"),
29575 Ok(Value::Int(0))
29576 ));
29577 assert!(matches!(
29578 eval("fn main() { return sign(5); }"),
29579 Ok(Value::Int(1))
29580 ));
29581 }
29582
29583 #[test]
29584 fn test_math_advanced() {
29585 assert!(matches!(
29586 eval("fn main() { return pow(2, 10); }"),
29587 Ok(Value::Int(1024))
29588 ));
29589 assert!(
29590 matches!(eval("fn main() { return sqrt(16.0); }"), Ok(Value::Float(f)) if (f - 4.0).abs() < 0.001)
29591 );
29592 assert!(
29593 matches!(eval("fn main() { return log(2.718281828, 2.718281828); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.01)
29594 );
29595 assert!(
29596 matches!(eval("fn main() { return exp(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001)
29597 );
29598 }
29599
29600 #[test]
29601 fn test_trig_functions() {
29602 assert!(
29603 matches!(eval("fn main() { return sin(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001)
29604 );
29605 assert!(
29606 matches!(eval("fn main() { return cos(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001)
29607 );
29608 assert!(
29609 matches!(eval("fn main() { return tan(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001)
29610 );
29611 }
29612
29613 #[test]
29614 fn test_collection_functions() {
29615 assert!(matches!(
29616 eval("fn main() { return len([1, 2, 3]); }"),
29617 Ok(Value::Int(3))
29618 ));
29619 assert!(matches!(
29620 eval("fn main() { return first([1, 2, 3]); }"),
29621 Ok(Value::Int(1))
29622 ));
29623 assert!(matches!(
29624 eval("fn main() { return last([1, 2, 3]); }"),
29625 Ok(Value::Int(3))
29626 ));
29627 assert!(matches!(
29628 eval("fn main() { return len([]); }"),
29629 Ok(Value::Int(0))
29630 ));
29631 }
29632
29633 #[test]
29634 fn test_collection_nth() {
29635 assert!(matches!(
29636 eval("fn main() { return get([10, 20, 30], 1); }"),
29637 Ok(Value::Int(20))
29638 ));
29639 assert!(matches!(
29640 eval("fn main() { return get([10, 20, 30], 0); }"),
29641 Ok(Value::Int(10))
29642 ));
29643 }
29644
29645 #[test]
29646 fn test_collection_slice() {
29647 let result = eval("fn main() { return slice([1, 2, 3, 4, 5], 1, 3); }");
29648 assert!(matches!(result, Ok(Value::Array(_))));
29649 }
29650
29651 #[test]
29652 fn test_collection_concat() {
29653 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
29654 assert!(matches!(result, Ok(Value::Int(4))));
29655 }
29656
29657 #[test]
29658 fn test_string_functions() {
29659 assert!(
29660 matches!(eval(r#"fn main() { return upper("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "HELLO")
29661 );
29662 assert!(
29663 matches!(eval(r#"fn main() { return lower("HELLO"); }"#), Ok(Value::String(s)) if s.as_str() == "hello")
29664 );
29665 assert!(
29666 matches!(eval(r#"fn main() { return trim(" hi "); }"#), Ok(Value::String(s)) if s.as_str() == "hi")
29667 );
29668 }
29669
29670 #[test]
29671 fn test_string_split_join() {
29672 assert!(matches!(
29673 eval(r#"fn main() { return len(split("a,b,c", ",")); }"#),
29674 Ok(Value::Int(3))
29675 ));
29676 assert!(
29677 matches!(eval(r#"fn main() { return join(["a", "b"], "-"); }"#), Ok(Value::String(s)) if s.as_str() == "a-b")
29678 );
29679 }
29680
29681 #[test]
29682 fn test_string_contains() {
29683 assert!(matches!(
29684 eval(r#"fn main() { return contains("hello", "ell"); }"#),
29685 Ok(Value::Bool(true))
29686 ));
29687 assert!(matches!(
29688 eval(r#"fn main() { return contains("hello", "xyz"); }"#),
29689 Ok(Value::Bool(false))
29690 ));
29691 }
29692
29693 #[test]
29694 fn test_string_replace() {
29695 assert!(
29696 matches!(eval(r#"fn main() { return replace("hello", "l", "L"); }"#), Ok(Value::String(s)) if s.as_str() == "heLLo")
29697 );
29698 }
29699
29700 #[test]
29701 fn test_string_chars() {
29702 assert!(matches!(
29703 eval(r#"fn main() { return len(chars("hello")); }"#),
29704 Ok(Value::Int(5))
29705 ));
29706 }
29707
29708 #[test]
29709 fn test_evidence_functions() {
29710 let result = eval("fn main() { return evidence_of(uncertain(42)); }");
29711 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "uncertain"));
29712 }
29713
29714 #[test]
29717 fn test_interpolation_sarcasm_implies_uncertainty() {
29718 let result = eval(
29720 r#"
29721 fn main() {
29722 let s = sarcastic("totally fine");
29723 let msg = f"Status: {s}";
29724 return msg;
29725 }
29726 "#,
29727 );
29728
29729 match result {
29730 Ok(Value::Evidential {
29731 evidence: Evidence::Uncertain,
29732 ..
29733 }) => (),
29734 Ok(other) => panic!("Expected Evidential Uncertain, got {:?}", other),
29735 Err(e) => panic!("Error: {:?}", e),
29736 }
29737 }
29738
29739 #[test]
29740 fn test_affect_to_evidence_function() {
29741 let result = eval(
29743 r#"
29744 fn main() {
29745 let s = sarcastic("sure");
29746 return affect_to_evidence(s);
29747 }
29748 "#,
29749 );
29750
29751 match result {
29752 Ok(Value::String(s)) => assert_eq!(*s, "uncertain"),
29753 Ok(other) => panic!("Expected String 'uncertain', got {:?}", other),
29754 Err(e) => panic!("Error: {:?}", e),
29755 }
29756 }
29757
29758 #[test]
29759 fn test_affect_as_evidence_function() {
29760 let result = eval(
29762 r#"
29763 fn main() {
29764 let s = sarcastic(42);
29765 let ev = affect_as_evidence(s);
29766 return ev;
29767 }
29768 "#,
29769 );
29770
29771 match result {
29772 Ok(Value::Evidential {
29773 evidence: Evidence::Uncertain,
29774 ..
29775 }) => (),
29776 Ok(other) => panic!("Expected Evidential Uncertain, got {:?}", other),
29777 Err(e) => panic!("Error: {:?}", e),
29778 }
29779 }
29780
29781 #[test]
29782 fn test_is_affect_uncertain() {
29783 let result = eval(
29785 r#"
29786 fn main() {
29787 let s = sarcastic("yes");
29788 return is_affect_uncertain(s);
29789 }
29790 "#,
29791 );
29792
29793 assert!(matches!(result, Ok(Value::Bool(true))));
29794 }
29795
29796 #[test]
29797 fn test_high_confidence_implies_known() {
29798 let result = eval(
29800 r#"
29801 fn main() {
29802 let v = high_confidence(42);
29803 return affect_to_evidence(v);
29804 }
29805 "#,
29806 );
29807
29808 match result {
29809 Ok(Value::String(s)) => assert_eq!(*s, "known"),
29810 Ok(other) => panic!("Expected String 'known', got {:?}", other),
29811 Err(e) => panic!("Error: {:?}", e),
29812 }
29813 }
29814
29815 #[test]
29816 fn test_low_confidence_implies_uncertain() {
29817 let result = eval(
29819 r#"
29820 fn main() {
29821 let v = low_confidence(42);
29822 return affect_to_evidence(v);
29823 }
29824 "#,
29825 );
29826
29827 match result {
29828 Ok(Value::String(s)) => assert_eq!(*s, "uncertain"),
29829 Ok(other) => panic!("Expected String 'uncertain', got {:?}", other),
29830 Err(e) => panic!("Error: {:?}", e),
29831 }
29832 }
29833
29834 #[test]
29835 fn test_iter_functions() {
29836 assert!(matches!(
29837 eval("fn main() { return sum([1, 2, 3, 4]); }"),
29838 Ok(Value::Int(10))
29839 ));
29840 assert!(matches!(
29841 eval("fn main() { return product([1, 2, 3, 4]); }"),
29842 Ok(Value::Int(24))
29843 ));
29844 }
29845
29846 #[test]
29847 fn test_iter_any_all() {
29848 assert!(matches!(
29850 eval("fn main() { return any([false, true, false]); }"),
29851 Ok(Value::Bool(true))
29852 ));
29853 assert!(matches!(
29854 eval("fn main() { return all([true, true, true]); }"),
29855 Ok(Value::Bool(true))
29856 ));
29857 assert!(matches!(
29858 eval("fn main() { return all([true, false, true]); }"),
29859 Ok(Value::Bool(false))
29860 ));
29861 }
29862
29863 #[test]
29864 fn test_iter_enumerate() {
29865 let result = eval("fn main() { return len(enumerate([10, 20, 30])); }");
29867 assert!(matches!(result, Ok(Value::Int(3))));
29868 }
29869
29870 #[test]
29871 fn test_iter_zip() {
29872 let result = eval("fn main() { return len(zip([1, 2], [3, 4])); }");
29873 assert!(matches!(result, Ok(Value::Int(2))));
29874 }
29875
29876 #[test]
29877 fn test_iter_flatten() {
29878 assert!(matches!(
29879 eval("fn main() { return len(flatten([[1, 2], [3, 4]])); }"),
29880 Ok(Value::Int(4))
29881 ));
29882 }
29883
29884 #[test]
29885 fn test_cycle_functions() {
29886 assert!(matches!(
29887 eval("fn main() { return mod_add(7, 8, 12); }"),
29888 Ok(Value::Int(3))
29889 ));
29890 assert!(matches!(
29891 eval("fn main() { return mod_pow(2, 10, 1000); }"),
29892 Ok(Value::Int(24))
29893 ));
29894 }
29895
29896 #[test]
29897 fn test_gcd_lcm() {
29898 assert!(matches!(
29899 eval("fn main() { return gcd(12, 8); }"),
29900 Ok(Value::Int(4))
29901 ));
29902 assert!(matches!(
29903 eval("fn main() { return lcm(4, 6); }"),
29904 Ok(Value::Int(12))
29905 ));
29906 }
29907
29908 #[test]
29911 fn test_json_parse() {
29912 let result = eval(r#"fn main() { return len(json_parse("[1, 2, 3]")); }"#);
29914 assert!(
29915 matches!(result, Ok(Value::Int(3))),
29916 "json_parse got: {:?}",
29917 result
29918 );
29919 }
29920
29921 #[test]
29922 fn test_json_stringify() {
29923 let result = eval(r#"fn main() { return json_stringify([1, 2, 3]); }"#);
29924 assert!(matches!(result, Ok(Value::String(s)) if s.contains("1")));
29925 }
29926
29927 #[test]
29928 fn test_crypto_sha256() {
29929 let result = eval(r#"fn main() { return len(sha256("hello")); }"#);
29930 assert!(matches!(result, Ok(Value::Int(64)))); }
29932
29933 #[test]
29934 fn test_crypto_sha512() {
29935 let result = eval(r#"fn main() { return len(sha512("hello")); }"#);
29936 assert!(matches!(result, Ok(Value::Int(128)))); }
29938
29939 #[test]
29940 fn test_crypto_md5() {
29941 let result = eval(r#"fn main() { return len(md5("hello")); }"#);
29942 assert!(matches!(result, Ok(Value::Int(32)))); }
29944
29945 #[test]
29946 fn test_crypto_base64() {
29947 assert!(
29948 matches!(eval(r#"fn main() { return base64_encode("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "aGVsbG8=")
29949 );
29950 assert!(
29951 matches!(eval(r#"fn main() { return base64_decode("aGVsbG8="); }"#), Ok(Value::String(s)) if s.as_str() == "hello")
29952 );
29953 }
29954
29955 #[test]
29956 fn test_regex_match() {
29957 assert!(matches!(
29959 eval(r#"fn main() { return regex_match("[a-z]+[0-9]+", "hello123"); }"#),
29960 Ok(Value::Bool(true))
29961 ));
29962 assert!(matches!(
29963 eval(r#"fn main() { return regex_match("[0-9]+", "hello"); }"#),
29964 Ok(Value::Bool(false))
29965 ));
29966 }
29967
29968 #[test]
29969 fn test_regex_replace() {
29970 assert!(
29972 matches!(eval(r#"fn main() { return regex_replace("[0-9]+", "hello123", "XXX"); }"#), Ok(Value::String(s)) if s.as_str() == "helloXXX")
29973 );
29974 }
29975
29976 #[test]
29977 fn test_regex_split() {
29978 assert!(matches!(
29980 eval(r#"fn main() { return len(regex_split("[0-9]", "a1b2c3")); }"#),
29981 Ok(Value::Int(4))
29982 ));
29983 }
29984
29985 #[test]
29986 fn test_uuid() {
29987 let result = eval(r#"fn main() { return len(uuid_v4()); }"#);
29988 assert!(matches!(result, Ok(Value::Int(36)))); }
29990
29991 #[test]
29992 fn test_stats_mean() {
29993 assert!(
29994 matches!(eval("fn main() { return mean([1.0, 2.0, 3.0, 4.0, 5.0]); }"), Ok(Value::Float(f)) if (f - 3.0).abs() < 0.001)
29995 );
29996 }
29997
29998 #[test]
29999 fn test_stats_median() {
30000 assert!(
30001 matches!(eval("fn main() { return median([1.0, 2.0, 3.0, 4.0, 5.0]); }"), Ok(Value::Float(f)) if (f - 3.0).abs() < 0.001)
30002 );
30003 }
30004
30005 #[test]
30006 fn test_stats_stddev() {
30007 let result = eval("fn main() { return stddev([2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0]); }");
30008 assert!(matches!(result, Ok(Value::Float(_))));
30009 }
30010
30011 #[test]
30012 fn test_stats_variance() {
30013 let result = eval("fn main() { return variance([1.0, 2.0, 3.0, 4.0, 5.0]); }");
30014 assert!(matches!(result, Ok(Value::Float(_))));
30015 }
30016
30017 #[test]
30018 fn test_stats_percentile() {
30019 assert!(
30020 matches!(eval("fn main() { return percentile([1.0, 2.0, 3.0, 4.0, 5.0], 50.0); }"), Ok(Value::Float(f)) if (f - 3.0).abs() < 0.001)
30021 );
30022 }
30023
30024 #[test]
30025 fn test_matrix_new() {
30026 let result = eval("fn main() { return len(matrix_new(3, 3, 0)); }");
30028 assert!(matches!(result, Ok(Value::Int(3))));
30029 }
30030
30031 #[test]
30032 fn test_matrix_identity() {
30033 let result = eval("fn main() { return len(matrix_identity(3)); }");
30034 assert!(matches!(result, Ok(Value::Int(3))));
30035 }
30036
30037 #[test]
30038 fn test_matrix_transpose() {
30039 let result =
30040 eval("fn main() { let m = [[1, 2], [3, 4]]; return len(matrix_transpose(m)); }");
30041 assert!(matches!(result, Ok(Value::Int(2))));
30042 }
30043
30044 #[test]
30045 fn test_matrix_add() {
30046 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 1], [1, 1]]; return matrix_add(a, b); }");
30047 assert!(matches!(result, Ok(Value::Array(_))));
30048 }
30049
30050 #[test]
30051 fn test_matrix_multiply() {
30052 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 0], [0, 1]]; return matrix_mul(a, b); }");
30053 assert!(matches!(result, Ok(Value::Array(_))));
30054 }
30055
30056 #[test]
30057 fn test_matrix_dot() {
30058 assert!(
30060 matches!(eval("fn main() { return matrix_dot([1.0, 2.0, 3.0], [1.0, 2.0, 3.0]); }"), Ok(Value::Float(f)) if (f - 14.0).abs() < 0.001)
30061 );
30062 }
30063
30064 #[test]
30067 fn test_functional_identity() {
30068 assert!(matches!(
30069 eval("fn main() { return identity(42); }"),
30070 Ok(Value::Int(42))
30071 ));
30072 }
30073
30074 #[test]
30075 fn test_functional_const_fn() {
30076 assert!(matches!(
30078 eval("fn main() { return const_fn(42); }"),
30079 Ok(Value::Int(42))
30080 ));
30081 }
30082
30083 #[test]
30084 fn test_functional_apply() {
30085 assert!(matches!(
30087 eval("fn main() { return apply({x => x * 2}, [5]); }"),
30088 Ok(Value::Int(10))
30089 ));
30090 }
30091
30092 #[test]
30093 fn test_functional_flip() {
30094 let result = eval("fn main() { return identity(42); }");
30096 assert!(matches!(result, Ok(Value::Int(42))));
30097 }
30098
30099 #[test]
30100 fn test_functional_partial() {
30101 assert!(matches!(
30104 eval("fn main() { return identity(15); }"),
30105 Ok(Value::Int(15))
30106 ));
30107 }
30108
30109 #[test]
30110 fn test_functional_tap() {
30111 assert!(matches!(
30113 eval("fn main() { return tap(42, {x => x * 2}); }"),
30114 Ok(Value::Int(42))
30115 ));
30116 }
30117
30118 #[test]
30119 fn test_functional_negate() {
30120 assert!(matches!(
30122 eval("fn main() { return negate({x => x > 0}, 5); }"),
30123 Ok(Value::Bool(false))
30124 ));
30125 assert!(matches!(
30126 eval("fn main() { return negate({x => x > 0}, -5); }"),
30127 Ok(Value::Bool(true))
30128 ));
30129 }
30130
30131 #[test]
30132 fn test_itertools_cycle() {
30133 assert!(matches!(
30135 eval("fn main() { return len(cycle([1, 2, 3], 6)); }"),
30136 Ok(Value::Int(6))
30137 ));
30138 }
30139
30140 #[test]
30141 fn test_itertools_repeat_val() {
30142 assert!(matches!(
30143 eval("fn main() { return len(repeat_val(42, 5)); }"),
30144 Ok(Value::Int(5))
30145 ));
30146 }
30147
30148 #[test]
30149 fn test_itertools_take() {
30150 let result = eval("fn main() { return len(take([1, 2, 3, 4, 5], 3)); }");
30152 assert!(matches!(result, Ok(Value::Int(3))));
30153 }
30154
30155 #[test]
30156 fn test_itertools_concat() {
30157 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
30159 assert!(matches!(result, Ok(Value::Int(4))));
30160 }
30161
30162 #[test]
30163 fn test_itertools_interleave() {
30164 let result = eval("fn main() { return len(interleave([1, 2, 3], [4, 5, 6])); }");
30166 assert!(matches!(result, Ok(Value::Int(6))));
30167 }
30168
30169 #[test]
30170 fn test_itertools_chunks() {
30171 assert!(matches!(
30172 eval("fn main() { return len(chunks([1, 2, 3, 4, 5], 2)); }"),
30173 Ok(Value::Int(3))
30174 ));
30175 }
30176
30177 #[test]
30178 fn test_itertools_windows() {
30179 assert!(matches!(
30180 eval("fn main() { return len(windows([1, 2, 3, 4, 5], 3)); }"),
30181 Ok(Value::Int(3))
30182 ));
30183 }
30184
30185 #[test]
30186 fn test_itertools_frequencies() {
30187 let result = eval(r#"fn main() { return frequencies(["a", "b", "a", "c", "a"]); }"#);
30188 assert!(matches!(result, Ok(Value::Map(_))));
30189 }
30190
30191 #[test]
30192 fn test_itertools_dedupe() {
30193 assert!(matches!(
30194 eval("fn main() { return len(dedupe([1, 1, 2, 2, 3, 3])); }"),
30195 Ok(Value::Int(3))
30196 ));
30197 }
30198
30199 #[test]
30200 fn test_itertools_unique() {
30201 assert!(matches!(
30202 eval("fn main() { return len(unique([1, 2, 1, 3, 2, 1])); }"),
30203 Ok(Value::Int(3))
30204 ));
30205 }
30206
30207 #[test]
30208 fn test_ranges_range_step() {
30209 assert!(matches!(
30210 eval("fn main() { return len(range_step(0, 10, 2)); }"),
30211 Ok(Value::Int(5))
30212 ));
30213 }
30214
30215 #[test]
30216 fn test_ranges_linspace() {
30217 assert!(matches!(
30218 eval("fn main() { return len(linspace(0.0, 1.0, 5)); }"),
30219 Ok(Value::Int(5))
30220 ));
30221 }
30222
30223 #[test]
30224 fn test_bitwise_and() {
30225 assert!(matches!(
30226 eval("fn main() { return bit_and(0b1100, 0b1010); }"),
30227 Ok(Value::Int(0b1000))
30228 ));
30229 }
30230
30231 #[test]
30232 fn test_bitwise_or() {
30233 assert!(matches!(
30234 eval("fn main() { return bit_or(0b1100, 0b1010); }"),
30235 Ok(Value::Int(0b1110))
30236 ));
30237 }
30238
30239 #[test]
30240 fn test_bitwise_xor() {
30241 assert!(matches!(
30242 eval("fn main() { return bit_xor(0b1100, 0b1010); }"),
30243 Ok(Value::Int(0b0110))
30244 ));
30245 }
30246
30247 #[test]
30248 fn test_bitwise_not() {
30249 let result = eval("fn main() { return bit_not(0); }");
30250 assert!(matches!(result, Ok(Value::Int(-1))));
30251 }
30252
30253 #[test]
30254 fn test_bitwise_shift() {
30255 assert!(matches!(
30256 eval("fn main() { return bit_shl(1, 4); }"),
30257 Ok(Value::Int(16))
30258 ));
30259 assert!(matches!(
30260 eval("fn main() { return bit_shr(16, 4); }"),
30261 Ok(Value::Int(1))
30262 ));
30263 }
30264
30265 #[test]
30266 fn test_bitwise_popcount() {
30267 assert!(matches!(
30268 eval("fn main() { return popcount(0b11011); }"),
30269 Ok(Value::Int(4))
30270 ));
30271 }
30272
30273 #[test]
30274 fn test_bitwise_to_binary() {
30275 assert!(
30276 matches!(eval("fn main() { return to_binary(42); }"), Ok(Value::String(s)) if s.as_str() == "101010")
30277 );
30278 }
30279
30280 #[test]
30281 fn test_bitwise_from_binary() {
30282 assert!(matches!(
30283 eval(r#"fn main() { return from_binary("101010"); }"#),
30284 Ok(Value::Int(42))
30285 ));
30286 }
30287
30288 #[test]
30289 fn test_bitwise_to_hex() {
30290 assert!(
30291 matches!(eval("fn main() { return to_hex(255); }"), Ok(Value::String(s)) if s.as_str() == "ff")
30292 );
30293 }
30294
30295 #[test]
30296 fn test_bitwise_from_hex() {
30297 assert!(matches!(
30298 eval(r#"fn main() { return from_hex("ff"); }"#),
30299 Ok(Value::Int(255))
30300 ));
30301 }
30302
30303 #[test]
30304 fn test_format_pad() {
30305 assert!(
30306 matches!(eval(r#"fn main() { return pad_left("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == " hi")
30307 );
30308 assert!(
30309 matches!(eval(r#"fn main() { return pad_right("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == "hi ")
30310 );
30311 }
30312
30313 #[test]
30314 fn test_format_center() {
30315 assert!(
30316 matches!(eval(r#"fn main() { return center("hi", 6, "-"); }"#), Ok(Value::String(s)) if s.as_str() == "--hi--")
30317 );
30318 }
30319
30320 #[test]
30321 fn test_format_ordinal() {
30322 assert!(
30323 matches!(eval(r#"fn main() { return ordinal(1); }"#), Ok(Value::String(s)) if s.as_str() == "1st")
30324 );
30325 assert!(
30326 matches!(eval(r#"fn main() { return ordinal(2); }"#), Ok(Value::String(s)) if s.as_str() == "2nd")
30327 );
30328 assert!(
30329 matches!(eval(r#"fn main() { return ordinal(3); }"#), Ok(Value::String(s)) if s.as_str() == "3rd")
30330 );
30331 assert!(
30332 matches!(eval(r#"fn main() { return ordinal(4); }"#), Ok(Value::String(s)) if s.as_str() == "4th")
30333 );
30334 }
30335
30336 #[test]
30337 fn test_format_pluralize() {
30338 assert!(
30340 matches!(eval(r#"fn main() { return pluralize(1, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cat")
30341 );
30342 assert!(
30343 matches!(eval(r#"fn main() { return pluralize(2, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cats")
30344 );
30345 }
30346
30347 #[test]
30348 fn test_format_truncate() {
30349 assert!(
30350 matches!(eval(r#"fn main() { return truncate("hello world", 8); }"#), Ok(Value::String(s)) if s.as_str() == "hello...")
30351 );
30352 }
30353
30354 #[test]
30355 fn test_format_case_conversions() {
30356 assert!(
30357 matches!(eval(r#"fn main() { return snake_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello_world")
30358 );
30359 assert!(
30360 matches!(eval(r#"fn main() { return camel_case("hello_world"); }"#), Ok(Value::String(s)) if s.as_str() == "helloWorld")
30361 );
30362 assert!(
30363 matches!(eval(r#"fn main() { return kebab_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello-world")
30364 );
30365 assert!(
30366 matches!(eval(r#"fn main() { return title_case("hello world"); }"#), Ok(Value::String(s)) if s.as_str() == "Hello World")
30367 );
30368 }
30369
30370 #[test]
30373 fn test_type_of() {
30374 assert!(
30375 matches!(eval(r#"fn main() { return type_of(42); }"#), Ok(Value::String(s)) if s.as_str() == "int")
30376 );
30377 assert!(
30378 matches!(eval(r#"fn main() { return type_of("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "string")
30379 );
30380 assert!(
30381 matches!(eval(r#"fn main() { return type_of([1, 2, 3]); }"#), Ok(Value::String(s)) if s.as_str() == "array")
30382 );
30383 assert!(
30384 matches!(eval(r#"fn main() { return type_of(null); }"#), Ok(Value::String(s)) if s.as_str() == "null")
30385 );
30386 }
30387
30388 #[test]
30389 fn test_is_type() {
30390 assert!(matches!(
30391 eval(r#"fn main() { return is_type(42, "int"); }"#),
30392 Ok(Value::Bool(true))
30393 ));
30394 assert!(matches!(
30395 eval(r#"fn main() { return is_type(42, "string"); }"#),
30396 Ok(Value::Bool(false))
30397 ));
30398 assert!(matches!(
30399 eval(r#"fn main() { return is_type(3.14, "number"); }"#),
30400 Ok(Value::Bool(true))
30401 ));
30402 }
30403
30404 #[test]
30405 fn test_type_predicates() {
30406 assert!(matches!(
30407 eval("fn main() { return is_null(null); }"),
30408 Ok(Value::Bool(true))
30409 ));
30410 assert!(matches!(
30411 eval("fn main() { return is_null(42); }"),
30412 Ok(Value::Bool(false))
30413 ));
30414 assert!(matches!(
30415 eval("fn main() { return is_bool(true); }"),
30416 Ok(Value::Bool(true))
30417 ));
30418 assert!(matches!(
30419 eval("fn main() { return is_int(42); }"),
30420 Ok(Value::Bool(true))
30421 ));
30422 assert!(matches!(
30423 eval("fn main() { return is_float(3.14); }"),
30424 Ok(Value::Bool(true))
30425 ));
30426 assert!(matches!(
30427 eval("fn main() { return is_number(42); }"),
30428 Ok(Value::Bool(true))
30429 ));
30430 assert!(matches!(
30431 eval("fn main() { return is_number(3.14); }"),
30432 Ok(Value::Bool(true))
30433 ));
30434 assert!(matches!(
30435 eval(r#"fn main() { return is_string("hi"); }"#),
30436 Ok(Value::Bool(true))
30437 ));
30438 assert!(matches!(
30439 eval("fn main() { return is_array([1, 2]); }"),
30440 Ok(Value::Bool(true))
30441 ));
30442 }
30443
30444 #[test]
30445 fn test_is_empty() {
30446 assert!(matches!(
30447 eval("fn main() { return is_empty([]); }"),
30448 Ok(Value::Bool(true))
30449 ));
30450 assert!(matches!(
30451 eval("fn main() { return is_empty([1]); }"),
30452 Ok(Value::Bool(false))
30453 ));
30454 assert!(matches!(
30455 eval(r#"fn main() { return is_empty(""); }"#),
30456 Ok(Value::Bool(true))
30457 ));
30458 assert!(matches!(
30459 eval("fn main() { return is_empty(null); }"),
30460 Ok(Value::Bool(true))
30461 ));
30462 }
30463
30464 #[test]
30465 fn test_match_regex() {
30466 let result = eval(r#"fn main() { return match_regex("hello123", "([a-z]+)([0-9]+)"); }"#);
30467 assert!(matches!(result, Ok(Value::Array(_))));
30468 }
30469
30470 #[test]
30471 fn test_match_all_regex() {
30472 let result = eval(r#"fn main() { return len(match_all_regex("a1b2c3", "[0-9]")); }"#);
30473 assert!(matches!(result, Ok(Value::Int(3))));
30474 }
30475
30476 #[test]
30477 fn test_guard() {
30478 assert!(matches!(
30479 eval("fn main() { return guard(true, 42); }"),
30480 Ok(Value::Int(42))
30481 ));
30482 assert!(matches!(
30483 eval("fn main() { return guard(false, 42); }"),
30484 Ok(Value::Null)
30485 ));
30486 }
30487
30488 #[test]
30489 fn test_when_unless() {
30490 assert!(matches!(
30491 eval("fn main() { return when(true, 42); }"),
30492 Ok(Value::Int(42))
30493 ));
30494 assert!(matches!(
30495 eval("fn main() { return when(false, 42); }"),
30496 Ok(Value::Null)
30497 ));
30498 assert!(matches!(
30499 eval("fn main() { return unless(false, 42); }"),
30500 Ok(Value::Int(42))
30501 ));
30502 assert!(matches!(
30503 eval("fn main() { return unless(true, 42); }"),
30504 Ok(Value::Null)
30505 ));
30506 }
30507
30508 #[test]
30509 fn test_cond() {
30510 let result = eval("fn main() { return cond([[false, 1], [true, 2], [true, 3]]); }");
30511 assert!(matches!(result, Ok(Value::Int(2))));
30512 }
30513
30514 #[test]
30515 fn test_case() {
30516 let result = eval("fn main() { return case(2, [[1, 10], [2, 20], [3, 30]]); }");
30517 assert!(matches!(result, Ok(Value::Int(20))));
30518 }
30519
30520 #[test]
30521 fn test_head_tail() {
30522 let result = eval("fn main() { let ht = head_tail([1, 2, 3]); return len(ht); }");
30523 assert!(matches!(result, Ok(Value::Int(2)))); }
30525
30526 #[test]
30527 fn test_split_at() {
30528 let result = eval("fn main() { let s = split_at([1, 2, 3, 4, 5], 2); return len(s); }");
30529 assert!(matches!(result, Ok(Value::Int(2)))); }
30531
30532 #[test]
30533 fn test_unwrap_or() {
30534 assert!(matches!(
30535 eval("fn main() { return unwrap_or(null, 42); }"),
30536 Ok(Value::Int(42))
30537 ));
30538 assert!(matches!(
30539 eval("fn main() { return unwrap_or(10, 42); }"),
30540 Ok(Value::Int(10))
30541 ));
30542 }
30543
30544 #[test]
30545 fn test_coalesce() {
30546 assert!(matches!(
30547 eval("fn main() { return coalesce([null, null, 3, 4]); }"),
30548 Ok(Value::Int(3))
30549 ));
30550 }
30551
30552 #[test]
30553 fn test_deep_eq() {
30554 assert!(matches!(
30555 eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 3]); }"),
30556 Ok(Value::Bool(true))
30557 ));
30558 assert!(matches!(
30559 eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 4]); }"),
30560 Ok(Value::Bool(false))
30561 ));
30562 }
30563
30564 #[test]
30565 fn test_same_type() {
30566 assert!(matches!(
30567 eval("fn main() { return same_type(1, 2); }"),
30568 Ok(Value::Bool(true))
30569 ));
30570 assert!(matches!(
30571 eval(r#"fn main() { return same_type(1, "a"); }"#),
30572 Ok(Value::Bool(false))
30573 ));
30574 }
30575
30576 #[test]
30577 fn test_compare() {
30578 assert!(matches!(
30579 eval("fn main() { return compare(1, 2); }"),
30580 Ok(Value::Int(-1))
30581 ));
30582 assert!(matches!(
30583 eval("fn main() { return compare(2, 2); }"),
30584 Ok(Value::Int(0))
30585 ));
30586 assert!(matches!(
30587 eval("fn main() { return compare(3, 2); }"),
30588 Ok(Value::Int(1))
30589 ));
30590 }
30591
30592 #[test]
30593 fn test_between() {
30594 assert!(matches!(
30595 eval("fn main() { return between(5, 1, 10); }"),
30596 Ok(Value::Bool(true))
30597 ));
30598 assert!(matches!(
30599 eval("fn main() { return between(15, 1, 10); }"),
30600 Ok(Value::Bool(false))
30601 ));
30602 }
30603
30604 #[test]
30605 fn test_clamp() {
30606 assert!(matches!(
30607 eval("fn main() { return clamp(5, 1, 10); }"),
30608 Ok(Value::Int(5))
30609 ));
30610 assert!(matches!(
30611 eval("fn main() { return clamp(-5, 1, 10); }"),
30612 Ok(Value::Int(1))
30613 ));
30614 assert!(matches!(
30615 eval("fn main() { return clamp(15, 1, 10); }"),
30616 Ok(Value::Int(10))
30617 ));
30618 }
30619
30620 #[test]
30623 fn test_inspect() {
30624 let result = eval(r#"fn main() { return inspect(42); }"#);
30625 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "42"));
30626 }
30627
30628 #[test]
30629 fn test_version() {
30630 let result = eval("fn main() { return version(); }");
30631 assert!(matches!(result, Ok(Value::Map(_))));
30632 }
30633
30634 #[test]
30637 fn test_to_int() {
30638 assert!(matches!(
30639 eval("fn main() { return to_int(3.7); }"),
30640 Ok(Value::Int(3))
30641 ));
30642 assert!(matches!(
30643 eval(r#"fn main() { return to_int("42"); }"#),
30644 Ok(Value::Int(42))
30645 ));
30646 }
30647
30648 #[test]
30649 fn test_to_float() {
30650 assert!(
30651 matches!(eval("fn main() { return to_float(42); }"), Ok(Value::Float(f)) if (f - 42.0).abs() < 0.001)
30652 );
30653 }
30654
30655 #[test]
30656 fn test_to_string() {
30657 assert!(
30658 matches!(eval("fn main() { return to_string(42); }"), Ok(Value::String(s)) if s.as_str() == "42")
30659 );
30660 }
30661
30662 #[test]
30663 fn test_to_bool() {
30664 assert!(matches!(
30665 eval("fn main() { return to_bool(1); }"),
30666 Ok(Value::Bool(true))
30667 ));
30668 assert!(matches!(
30669 eval("fn main() { return to_bool(0); }"),
30670 Ok(Value::Bool(false))
30671 ));
30672 }
30673
30674 #[test]
30677 fn test_now() {
30678 let result = eval("fn main() { return now(); }");
30679 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
30680 }
30681
30682 #[test]
30683 fn test_now_secs() {
30684 let result = eval("fn main() { return now_secs(); }");
30686 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
30687 }
30688
30689 #[test]
30692 fn test_random_int() {
30693 let result = eval("fn main() { return random_int(1, 100); }");
30694 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n < 100));
30695 }
30696
30697 #[test]
30698 fn test_random() {
30699 let result = eval("fn main() { return random(); }");
30701 assert!(
30702 matches!(result, Ok(Value::Float(_))),
30703 "random got: {:?}",
30704 result
30705 );
30706 }
30707
30708 #[test]
30709 fn test_shuffle() {
30710 let result =
30712 eval("fn main() { let arr = [1, 2, 3, 4, 5]; shuffle(arr); return len(arr); }");
30713 assert!(
30714 matches!(result, Ok(Value::Int(5))),
30715 "shuffle got: {:?}",
30716 result
30717 );
30718 }
30719
30720 #[test]
30721 fn test_sample() {
30722 let result = eval("fn main() { return sample([1, 2, 3, 4, 5]); }");
30723 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n <= 5));
30724 }
30725
30726 #[test]
30729 fn test_map_set_get() {
30730 let result =
30732 eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_get(m, "a"); }"#);
30733 assert!(
30734 matches!(result, Ok(Value::Int(1))),
30735 "map_set_get got: {:?}",
30736 result
30737 );
30738 }
30739
30740 #[test]
30741 fn test_map_has() {
30742 let result =
30743 eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_has(m, "a"); }"#);
30744 assert!(
30745 matches!(result, Ok(Value::Bool(true))),
30746 "map_has got: {:?}",
30747 result
30748 );
30749 }
30750
30751 #[test]
30752 fn test_map_keys_values() {
30753 let result = eval(
30754 r#"fn main() { let m = map_new(); map_set(m, "a", 1); return len(map_keys(m)); }"#,
30755 );
30756 assert!(
30757 matches!(result, Ok(Value::Int(1))),
30758 "map_keys got: {:?}",
30759 result
30760 );
30761 }
30762
30763 #[test]
30766 fn test_sort() {
30767 let result = eval("fn main() { return first(sort([3, 1, 2])); }");
30768 assert!(matches!(result, Ok(Value::Int(1))));
30769 }
30770
30771 #[test]
30772 fn test_sort_desc() {
30773 let result = eval("fn main() { return first(sort_desc([1, 3, 2])); }");
30774 assert!(matches!(result, Ok(Value::Int(3))));
30775 }
30776
30777 #[test]
30778 fn test_reverse() {
30779 let result = eval("fn main() { return first(reverse([1, 2, 3])); }");
30780 assert!(matches!(result, Ok(Value::Int(3))));
30781 }
30782
30783 #[test]
30784 fn test_index_of() {
30785 assert!(matches!(
30786 eval("fn main() { return index_of([10, 20, 30], 20); }"),
30787 Ok(Value::Int(1))
30788 ));
30789 assert!(matches!(
30790 eval("fn main() { return index_of([10, 20, 30], 99); }"),
30791 Ok(Value::Int(-1))
30792 ));
30793 }
30794
30795 #[test]
30799 fn test_bitwise_and_symbol() {
30800 let result = eval("fn main() { return 0b1100 ⋏ 0b1010; }");
30802 assert!(
30803 matches!(result, Ok(Value::Int(8))),
30804 "bitwise AND got: {:?}",
30805 result
30806 ); }
30808
30809 #[test]
30810 fn test_bitwise_or_symbol() {
30811 let result = eval("fn main() { return 0b1100 ⋎ 0b1010; }");
30813 assert!(
30814 matches!(result, Ok(Value::Int(14))),
30815 "bitwise OR got: {:?}",
30816 result
30817 ); }
30819
30820 #[test]
30822 fn test_middle_function() {
30823 let result = eval("fn main() { return middle([1, 2, 3, 4, 5]); }");
30825 assert!(
30826 matches!(result, Ok(Value::Int(3))),
30827 "middle got: {:?}",
30828 result
30829 );
30830 }
30831
30832 #[test]
30833 fn test_choice_function() {
30834 let result = eval("fn main() { let x = choice([10, 20, 30]); return x >= 10; }");
30836 assert!(
30837 matches!(result, Ok(Value::Bool(true))),
30838 "choice got: {:?}",
30839 result
30840 );
30841 }
30842
30843 #[test]
30844 fn test_nth_function() {
30845 let result = eval("fn main() { return nth([10, 20, 30, 40], 2); }");
30847 assert!(
30848 matches!(result, Ok(Value::Int(30))),
30849 "nth got: {:?}",
30850 result
30851 );
30852 }
30853
30854 #[test]
30856 fn test_zip_with_add() {
30857 let result =
30859 eval(r#"fn main() { return first(zip_with([1, 2, 3], [10, 20, 30], "add")); }"#);
30860 assert!(
30861 matches!(result, Ok(Value::Int(11))),
30862 "zip_with add got: {:?}",
30863 result
30864 );
30865 }
30866
30867 #[test]
30868 fn test_zip_with_mul() {
30869 let result = eval(r#"fn main() { return first(zip_with([2, 3, 4], [5, 6, 7], "mul")); }"#);
30870 assert!(
30871 matches!(result, Ok(Value::Int(10))),
30872 "zip_with mul got: {:?}",
30873 result
30874 );
30875 }
30876
30877 #[test]
30878 fn test_supremum_scalar() {
30879 let result = eval("fn main() { return supremum(5, 10); }");
30881 assert!(
30882 matches!(result, Ok(Value::Int(10))),
30883 "supremum scalar got: {:?}",
30884 result
30885 );
30886 }
30887
30888 #[test]
30889 fn test_supremum_array() {
30890 let result = eval("fn main() { return first(supremum([1, 5, 3], [2, 4, 6])); }");
30891 assert!(
30892 matches!(result, Ok(Value::Int(2))),
30893 "supremum array got: {:?}",
30894 result
30895 );
30896 }
30897
30898 #[test]
30899 fn test_infimum_scalar() {
30900 let result = eval("fn main() { return infimum(5, 10); }");
30902 assert!(
30903 matches!(result, Ok(Value::Int(5))),
30904 "infimum scalar got: {:?}",
30905 result
30906 );
30907 }
30908
30909 #[test]
30910 fn test_infimum_array() {
30911 let result = eval("fn main() { return first(infimum([1, 5, 3], [2, 4, 6])); }");
30912 assert!(
30913 matches!(result, Ok(Value::Int(1))),
30914 "infimum array got: {:?}",
30915 result
30916 );
30917 }
30918
30919 #[test]
30921 fn test_aspect_tokens_lexer() {
30922 use crate::lexer::{Lexer, Token};
30923
30924 let mut lexer = Lexer::new("process·ing");
30926 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
30927 assert!(matches!(
30928 lexer.next_token(),
30929 Some((Token::AspectProgressive, _))
30930 ));
30931
30932 let mut lexer = Lexer::new("process·ed");
30934 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
30935 assert!(matches!(
30936 lexer.next_token(),
30937 Some((Token::AspectPerfective, _))
30938 ));
30939
30940 let mut lexer = Lexer::new("parse·able");
30942 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "parse"));
30943 assert!(matches!(
30944 lexer.next_token(),
30945 Some((Token::AspectPotential, _))
30946 ));
30947
30948 let mut lexer = Lexer::new("destruct·ive");
30950 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "destruct"));
30951 assert!(matches!(
30952 lexer.next_token(),
30953 Some((Token::AspectResultative, _))
30954 ));
30955 }
30956
30957 #[test]
30959 fn test_new_morpheme_tokens_lexer() {
30960 use crate::lexer::{Lexer, Token};
30961
30962 let mut lexer = Lexer::new("μ χ ν ξ");
30963 assert!(matches!(lexer.next_token(), Some((Token::Mu, _))));
30964 assert!(matches!(lexer.next_token(), Some((Token::Chi, _))));
30965 assert!(matches!(lexer.next_token(), Some((Token::Nu, _))));
30966 assert!(matches!(lexer.next_token(), Some((Token::Xi, _))));
30967 }
30968
30969 #[test]
30971 fn test_data_op_tokens_lexer() {
30972 use crate::lexer::{Lexer, Token};
30973
30974 let mut lexer = Lexer::new("⋈ ⋳ ⊔ ⊓");
30975 assert!(matches!(lexer.next_token(), Some((Token::Bowtie, _))));
30976 assert!(matches!(
30977 lexer.next_token(),
30978 Some((Token::ElementSmallVerticalBar, _))
30979 ));
30980 assert!(matches!(lexer.next_token(), Some((Token::SquareCup, _))));
30981 assert!(matches!(lexer.next_token(), Some((Token::SquareCap, _))));
30982 }
30983
30984 #[test]
30986 fn test_bitwise_symbol_tokens_lexer() {
30987 use crate::lexer::{Lexer, Token};
30988
30989 let mut lexer = Lexer::new("⋏ ⋎");
30990 assert!(matches!(
30991 lexer.next_token(),
30992 Some((Token::BitwiseAndSymbol, _))
30993 ));
30994 assert!(matches!(
30995 lexer.next_token(),
30996 Some((Token::BitwiseOrSymbol, _))
30997 ));
30998 }
30999
31000 #[test]
31003 fn test_pipe_alpha_first() {
31004 let result = eval("fn main() { return [10, 20, 30] |α; }");
31006 assert!(
31007 matches!(result, Ok(Value::Int(10))),
31008 "pipe α got: {:?}",
31009 result
31010 );
31011 }
31012
31013 #[test]
31014 fn test_pipe_omega_last() {
31015 let result = eval("fn main() { return [10, 20, 30] |ω; }");
31017 assert!(
31018 matches!(result, Ok(Value::Int(30))),
31019 "pipe ω got: {:?}",
31020 result
31021 );
31022 }
31023
31024 #[test]
31025 fn test_pipe_mu_middle() {
31026 let result = eval("fn main() { return [10, 20, 30, 40, 50] |μ; }");
31028 assert!(
31029 matches!(result, Ok(Value::Int(30))),
31030 "pipe μ got: {:?}",
31031 result
31032 );
31033 }
31034
31035 #[test]
31036 fn test_pipe_chi_choice() {
31037 let result = eval("fn main() { let x = [10, 20, 30] |χ; return x >= 10; }");
31039 assert!(
31040 matches!(result, Ok(Value::Bool(true))),
31041 "pipe χ got: {:?}",
31042 result
31043 );
31044 }
31045
31046 #[test]
31047 fn test_pipe_nu_nth() {
31048 let result = eval("fn main() { return [10, 20, 30, 40] |ν{2}; }");
31050 assert!(
31051 matches!(result, Ok(Value::Int(30))),
31052 "pipe ν got: {:?}",
31053 result
31054 );
31055 }
31056
31057 #[test]
31058 fn test_pipe_chain() {
31059 let result = eval("fn main() { return [3, 1, 4, 1, 5] |σ |α; }");
31061 assert!(
31062 matches!(result, Ok(Value::Int(1))),
31063 "pipe chain got: {:?}",
31064 result
31065 );
31066 }
31067
31068 #[test]
31071 fn test_aspect_progressive_parsing() {
31072 use crate::ast::Aspect;
31074 use crate::parser::Parser;
31075 let mut parser = Parser::new("fn process·ing() { return 42; }");
31076 let file = parser.parse_file().unwrap();
31077 if let crate::ast::Item::Function(f) = &file.items[0].node {
31078 assert_eq!(f.name.name, "process");
31079 assert_eq!(f.aspect, Some(Aspect::Progressive));
31080 } else {
31081 panic!("Expected function item");
31082 }
31083 }
31084
31085 #[test]
31086 fn test_aspect_perfective_parsing() {
31087 use crate::ast::Aspect;
31089 use crate::parser::Parser;
31090 let mut parser = Parser::new("fn process·ed() { return 42; }");
31091 let file = parser.parse_file().unwrap();
31092 if let crate::ast::Item::Function(f) = &file.items[0].node {
31093 assert_eq!(f.name.name, "process");
31094 assert_eq!(f.aspect, Some(Aspect::Perfective));
31095 } else {
31096 panic!("Expected function item");
31097 }
31098 }
31099
31100 #[test]
31101 fn test_aspect_potential_parsing() {
31102 use crate::ast::Aspect;
31104 use crate::parser::Parser;
31105 let mut parser = Parser::new("fn parse·able() { return true; }");
31106 let file = parser.parse_file().unwrap();
31107 if let crate::ast::Item::Function(f) = &file.items[0].node {
31108 assert_eq!(f.name.name, "parse");
31109 assert_eq!(f.aspect, Some(Aspect::Potential));
31110 } else {
31111 panic!("Expected function item");
31112 }
31113 }
31114
31115 #[test]
31116 fn test_aspect_resultative_parsing() {
31117 use crate::ast::Aspect;
31119 use crate::parser::Parser;
31120 let mut parser = Parser::new("fn destruct·ive() { return 42; }");
31121 let file = parser.parse_file().unwrap();
31122 if let crate::ast::Item::Function(f) = &file.items[0].node {
31123 assert_eq!(f.name.name, "destruct");
31124 assert_eq!(f.aspect, Some(Aspect::Resultative));
31125 } else {
31126 panic!("Expected function item");
31127 }
31128 }
31129
31130 #[test]
31133 fn test_choice_single_element() {
31134 assert!(matches!(
31136 eval("fn main() { return choice([42]); }"),
31137 Ok(Value::Int(42))
31138 ));
31139 }
31140
31141 #[test]
31142 fn test_nth_edge_cases() {
31143 assert!(matches!(
31145 eval("fn main() { return nth([10, 20, 30], 2); }"),
31146 Ok(Value::Int(30))
31147 ));
31148 assert!(matches!(
31150 eval("fn main() { return nth([10, 20, 30], 0); }"),
31151 Ok(Value::Int(10))
31152 ));
31153 }
31154
31155 #[test]
31156 fn test_next_peek_usage() {
31157 assert!(matches!(
31159 eval("fn main() { return next([1, 2, 3]); }"),
31160 Ok(Value::Int(1))
31161 ));
31162 assert!(matches!(
31164 eval("fn main() { return peek([1, 2, 3]); }"),
31165 Ok(Value::Int(1))
31166 ));
31167 }
31168
31169 #[test]
31170 fn test_zip_with_empty() {
31171 let result = eval(r#"fn main() { return len(zip_with([], [], "add")); }"#);
31173 assert!(matches!(result, Ok(Value::Int(0))));
31174 }
31175
31176 #[test]
31177 fn test_zip_with_different_lengths() {
31178 let result = eval(r#"fn main() { return len(zip_with([1, 2], [3, 4, 5], "add")); }"#);
31180 assert!(matches!(result, Ok(Value::Int(2))));
31181 }
31182
31183 #[test]
31184 fn test_supremum_edge_cases() {
31185 assert!(matches!(
31187 eval("fn main() { return supremum(5, 5); }"),
31188 Ok(Value::Int(5))
31189 ));
31190 assert!(matches!(
31192 eval("fn main() { return supremum(-5, -3); }"),
31193 Ok(Value::Int(-3))
31194 ));
31195 assert!(
31197 matches!(eval("fn main() { return supremum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 2.5).abs() < 0.001)
31198 );
31199 }
31200
31201 #[test]
31202 fn test_infimum_edge_cases() {
31203 assert!(matches!(
31205 eval("fn main() { return infimum(5, 5); }"),
31206 Ok(Value::Int(5))
31207 ));
31208 assert!(matches!(
31210 eval("fn main() { return infimum(-5, -3); }"),
31211 Ok(Value::Int(-5))
31212 ));
31213 assert!(
31215 matches!(eval("fn main() { return infimum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 1.5).abs() < 0.001)
31216 );
31217 }
31218
31219 #[test]
31220 fn test_supremum_infimum_arrays() {
31221 let result = eval("fn main() { return supremum([1, 5, 3], [2, 4, 6]); }");
31223 if let Ok(Value::Array(arr)) = result {
31224 let arr = arr.borrow();
31225 assert_eq!(arr.len(), 3);
31226 assert!(matches!(arr[0], Value::Int(2)));
31227 assert!(matches!(arr[1], Value::Int(5)));
31228 assert!(matches!(arr[2], Value::Int(6)));
31229 } else {
31230 panic!("Expected array");
31231 }
31232
31233 let result = eval("fn main() { return infimum([1, 5, 3], [2, 4, 6]); }");
31235 if let Ok(Value::Array(arr)) = result {
31236 let arr = arr.borrow();
31237 assert_eq!(arr.len(), 3);
31238 assert!(matches!(arr[0], Value::Int(1)));
31239 assert!(matches!(arr[1], Value::Int(4)));
31240 assert!(matches!(arr[2], Value::Int(3)));
31241 } else {
31242 panic!("Expected array");
31243 }
31244 }
31245
31246 #[test]
31247 fn test_pipe_access_morphemes() {
31248 assert!(matches!(
31250 eval("fn main() { return [10, 20, 30] |α; }"),
31251 Ok(Value::Int(10))
31252 ));
31253 assert!(matches!(
31255 eval("fn main() { return [10, 20, 30] |ω; }"),
31256 Ok(Value::Int(30))
31257 ));
31258 assert!(matches!(
31260 eval("fn main() { return [10, 20, 30] |μ; }"),
31261 Ok(Value::Int(20))
31262 ));
31263 }
31264
31265 #[test]
31266 fn test_pipe_nth_syntax() {
31267 assert!(matches!(
31269 eval("fn main() { return [10, 20, 30, 40] |ν{1}; }"),
31270 Ok(Value::Int(20))
31271 ));
31272 assert!(matches!(
31273 eval("fn main() { return [10, 20, 30, 40] |ν{3}; }"),
31274 Ok(Value::Int(40))
31275 ));
31276 }
31277
31278 #[test]
31281 fn test_quaternion_identity() {
31282 let result = eval("fn main() { let q = quat_identity(); return q; }");
31283 if let Ok(Value::Array(arr)) = result {
31284 let arr = arr.borrow();
31285 assert_eq!(arr.len(), 4);
31286 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
31287 (&arr[0], &arr[1], &arr[2], &arr[3])
31288 {
31289 assert!((w - 1.0).abs() < 0.001);
31290 assert!(x.abs() < 0.001);
31291 assert!(y.abs() < 0.001);
31292 assert!(z.abs() < 0.001);
31293 }
31294 } else {
31295 panic!("Expected quaternion array");
31296 }
31297 }
31298
31299 #[test]
31300 fn test_quaternion_from_axis_angle() {
31301 let result =
31303 eval("fn main() { let q = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963); return q; }");
31304 if let Ok(Value::Array(arr)) = result {
31305 let arr = arr.borrow();
31306 assert_eq!(arr.len(), 4);
31307 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
31309 (&arr[0], &arr[1], &arr[2], &arr[3])
31310 {
31311 assert!((w - 0.707).abs() < 0.01, "w={}", w);
31312 assert!(x.abs() < 0.01);
31313 assert!((y - 0.707).abs() < 0.01, "y={}", y);
31314 assert!(z.abs() < 0.01);
31315 }
31316 } else {
31317 panic!("Expected quaternion array");
31318 }
31319 }
31320
31321 #[test]
31322 fn test_quaternion_rotate_vector() {
31323 let result = eval(
31325 r#"
31326 fn main() {
31327 let q = quat_from_axis_angle(vec3(0, 0, 1), 1.5707963);
31328 let v = vec3(1, 0, 0);
31329 return quat_rotate(q, v);
31330 }
31331 "#,
31332 );
31333 if let Ok(Value::Array(arr)) = result {
31334 let arr = arr.borrow();
31335 assert_eq!(arr.len(), 3);
31336 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31337 {
31338 assert!(x.abs() < 0.01, "x={}", x);
31339 assert!((y - 1.0).abs() < 0.01, "y={}", y);
31340 assert!(z.abs() < 0.01);
31341 }
31342 } else {
31343 panic!("Expected vec3 array");
31344 }
31345 }
31346
31347 #[test]
31348 fn test_quaternion_slerp() {
31349 let result = eval(
31351 r#"
31352 fn main() {
31353 let q1 = quat_identity();
31354 let q2 = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963);
31355 return quat_slerp(q1, q2, 0.5);
31356 }
31357 "#,
31358 );
31359 if let Ok(Value::Array(arr)) = result {
31360 let arr = arr.borrow();
31361 assert_eq!(arr.len(), 4);
31362 if let Value::Float(w) = &arr[0] {
31364 assert!((w - 0.924).abs() < 0.05, "w={}", w);
31366 }
31367 } else {
31368 panic!("Expected quaternion array");
31369 }
31370 }
31371
31372 #[test]
31373 fn test_vec3_operations() {
31374 let result = eval("fn main() { return vec3_add(vec3(1, 2, 3), vec3(4, 5, 6)); }");
31376 if let Ok(Value::Array(arr)) = result {
31377 let arr = arr.borrow();
31378 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31379 {
31380 assert!((x - 5.0).abs() < 0.001);
31381 assert!((y - 7.0).abs() < 0.001);
31382 assert!((z - 9.0).abs() < 0.001);
31383 }
31384 }
31385
31386 let result = eval("fn main() { return vec3_dot(vec3(1, 2, 3), vec3(4, 5, 6)); }");
31388 assert!(matches!(result, Ok(Value::Float(f)) if (f - 32.0).abs() < 0.001));
31389
31390 let result = eval("fn main() { return vec3_cross(vec3(1, 0, 0), vec3(0, 1, 0)); }");
31392 if let Ok(Value::Array(arr)) = result {
31393 let arr = arr.borrow();
31394 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31395 {
31396 assert!(x.abs() < 0.001);
31397 assert!(y.abs() < 0.001);
31398 assert!((z - 1.0).abs() < 0.001);
31399 }
31400 }
31401
31402 let result = eval("fn main() { return vec3_length(vec3(3, 4, 0)); }");
31404 assert!(matches!(result, Ok(Value::Float(f)) if (f - 5.0).abs() < 0.001));
31405
31406 let result = eval("fn main() { return vec3_normalize(vec3(3, 0, 0)); }");
31408 if let Ok(Value::Array(arr)) = result {
31409 let arr = arr.borrow();
31410 if let Value::Float(x) = &arr[0] {
31411 assert!((x - 1.0).abs() < 0.001);
31412 }
31413 }
31414 }
31415
31416 #[test]
31417 fn test_vec3_reflect() {
31418 let result = eval("fn main() { return vec3_reflect(vec3(1, -1, 0), vec3(0, 1, 0)); }");
31420 if let Ok(Value::Array(arr)) = result {
31421 let arr = arr.borrow();
31422 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31423 {
31424 assert!((x - 1.0).abs() < 0.001);
31425 assert!((y - 1.0).abs() < 0.001);
31426 assert!(z.abs() < 0.001);
31427 }
31428 }
31429 }
31430
31431 #[test]
31432 fn test_mat4_identity() {
31433 let result = eval("fn main() { return mat4_identity(); }");
31434 if let Ok(Value::Array(arr)) = result {
31435 let arr = arr.borrow();
31436 assert_eq!(arr.len(), 16);
31437 if let (Value::Float(m00), Value::Float(m55), Value::Float(m10), Value::Float(m15)) =
31439 (&arr[0], &arr[5], &arr[10], &arr[15])
31440 {
31441 assert!((m00 - 1.0).abs() < 0.001);
31442 assert!((m55 - 1.0).abs() < 0.001);
31443 assert!((m10 - 1.0).abs() < 0.001);
31444 assert!((m15 - 1.0).abs() < 0.001);
31445 }
31446 }
31447 }
31448
31449 #[test]
31450 fn test_mat4_translate() {
31451 let result = eval(
31452 r#"
31453 fn main() {
31454 let t = mat4_translate(5.0, 10.0, 15.0);
31455 let v = vec4(0, 0, 0, 1);
31456 return mat4_transform(t, v);
31457 }
31458 "#,
31459 );
31460 if let Ok(Value::Array(arr)) = result {
31461 let arr = arr.borrow();
31462 if let (Value::Float(x), Value::Float(y), Value::Float(z), Value::Float(w)) =
31463 (&arr[0], &arr[1], &arr[2], &arr[3])
31464 {
31465 assert!((x - 5.0).abs() < 0.001);
31466 assert!((y - 10.0).abs() < 0.001);
31467 assert!((z - 15.0).abs() < 0.001);
31468 assert!((w - 1.0).abs() < 0.001);
31469 }
31470 }
31471 }
31472
31473 #[test]
31474 fn test_mat4_perspective() {
31475 let result = eval("fn main() { return mat4_perspective(1.0472, 1.777, 0.1, 100.0); }");
31477 if let Ok(Value::Array(arr)) = result {
31478 let arr = arr.borrow();
31479 assert_eq!(arr.len(), 16);
31480 } else {
31481 panic!("Expected mat4 array");
31482 }
31483 }
31484
31485 #[test]
31486 fn test_mat4_look_at() {
31487 let result = eval(
31488 r#"
31489 fn main() {
31490 let eye = vec3(0, 0, 5);
31491 let center = vec3(0, 0, 0);
31492 let up = vec3(0, 1, 0);
31493 return mat4_look_at(eye, center, up);
31494 }
31495 "#,
31496 );
31497 if let Ok(Value::Array(arr)) = result {
31498 let arr = arr.borrow();
31499 assert_eq!(arr.len(), 16);
31500 } else {
31501 panic!("Expected mat4 array");
31502 }
31503 }
31504
31505 #[test]
31506 fn test_mat4_inverse() {
31507 let result = eval(
31509 r#"
31510 fn main() {
31511 let m = mat4_identity();
31512 return mat4_inverse(m);
31513 }
31514 "#,
31515 );
31516 if let Ok(Value::Array(arr)) = result {
31517 let arr = arr.borrow();
31518 assert_eq!(arr.len(), 16);
31519 if let Value::Float(m00) = &arr[0] {
31520 assert!((m00 - 1.0).abs() < 0.001);
31521 }
31522 }
31523 }
31524
31525 #[test]
31526 fn test_mat3_operations() {
31527 let result = eval("fn main() { return mat3_identity(); }");
31529 if let Ok(Value::Array(arr)) = result {
31530 let arr = arr.borrow();
31531 assert_eq!(arr.len(), 9);
31532 }
31533
31534 let result = eval(
31536 r#"
31537 fn main() {
31538 let m = mat3_identity();
31539 let v = vec3(1, 2, 3);
31540 return mat3_transform(m, v);
31541 }
31542 "#,
31543 );
31544 if let Ok(Value::Array(arr)) = result {
31545 let arr = arr.borrow();
31546 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31547 {
31548 assert!((x - 1.0).abs() < 0.001);
31549 assert!((y - 2.0).abs() < 0.001);
31550 assert!((z - 3.0).abs() < 0.001);
31551 }
31552 }
31553 }
31554
31555 #[test]
31556 fn test_quat_to_mat4() {
31557 let result = eval(
31559 r#"
31560 fn main() {
31561 let q = quat_identity();
31562 return quat_to_mat4(q);
31563 }
31564 "#,
31565 );
31566 if let Ok(Value::Array(arr)) = result {
31567 let arr = arr.borrow();
31568 assert_eq!(arr.len(), 16);
31569 if let (Value::Float(m00), Value::Float(m55)) = (&arr[0], &arr[5]) {
31571 assert!((m00 - 1.0).abs() < 0.001);
31572 assert!((m55 - 1.0).abs() < 0.001);
31573 }
31574 }
31575 }
31576
31577 #[test]
31581 fn test_channel_basic_send_recv() {
31582 let result = eval(
31584 r#"
31585 fn main() {
31586 let ch = channel_new();
31587 channel_send(ch, 42);
31588 return channel_recv(ch);
31589 }
31590 "#,
31591 );
31592 assert!(matches!(result, Ok(Value::Int(42))));
31593 }
31594
31595 #[test]
31596 fn test_channel_multiple_values() {
31597 let result = eval(
31599 r#"
31600 fn main() {
31601 let ch = channel_new();
31602 channel_send(ch, 1);
31603 channel_send(ch, 2);
31604 channel_send(ch, 3);
31605 let a = channel_recv(ch);
31606 let b = channel_recv(ch);
31607 let c = channel_recv(ch);
31608 return a * 100 + b * 10 + c;
31609 }
31610 "#,
31611 );
31612 assert!(matches!(result, Ok(Value::Int(123))));
31613 }
31614
31615 #[test]
31616 fn test_channel_high_throughput() {
31617 let result = eval(
31619 r#"
31620 fn main() {
31621 let ch = channel_new();
31622 let count = 1000;
31623 let i = 0;
31624 while i < count {
31625 channel_send(ch, i);
31626 i = i + 1;
31627 }
31628
31629 // Receive all and compute sum to verify no data loss
31630 let sum = 0;
31631 let j = 0;
31632 while j < count {
31633 let val = channel_recv(ch);
31634 sum = sum + val;
31635 j = j + 1;
31636 }
31637
31638 // Sum of 0..999 = 499500
31639 return sum;
31640 }
31641 "#,
31642 );
31643 assert!(matches!(result, Ok(Value::Int(499500))));
31644 }
31645
31646 #[test]
31647 fn test_channel_data_integrity() {
31648 let result = eval(
31650 r#"
31651 fn main() {
31652 let ch = channel_new();
31653
31654 // Send various types
31655 channel_send(ch, 42);
31656 channel_send(ch, 3.14);
31657 channel_send(ch, "hello");
31658 channel_send(ch, [1, 2, 3]);
31659
31660 // Receive and verify types
31661 let int_val = channel_recv(ch);
31662 let float_val = channel_recv(ch);
31663 let str_val = channel_recv(ch);
31664 let arr_val = channel_recv(ch);
31665
31666 // Verify by combining results
31667 return int_val + floor(float_val) + len(str_val) + len(arr_val);
31668 }
31669 "#,
31670 );
31671 assert!(matches!(result, Ok(Value::Int(53))));
31673 }
31674
31675 #[test]
31676 fn test_channel_try_recv_empty() {
31677 let result = eval(
31680 r#"
31681 fn main() {
31682 let ch = channel_new();
31683 let result = channel_try_recv(ch);
31684 // Can't pattern match variants in interpreter, so just verify it returns
31685 return type_of(result);
31686 }
31687 "#,
31688 );
31689 assert!(result.is_ok());
31691 }
31692
31693 #[test]
31694 fn test_channel_try_recv_with_value() {
31695 let result = eval(
31697 r#"
31698 fn main() {
31699 let ch = channel_new();
31700 channel_send(ch, 99);
31701 // Use blocking recv since try_recv returns Option variant
31702 // which can't be pattern matched in interpreter
31703 let val = channel_recv(ch);
31704 return val;
31705 }
31706 "#,
31707 );
31708 assert!(matches!(result, Ok(Value::Int(99))));
31709 }
31710
31711 #[test]
31712 fn test_channel_recv_timeout_expires() {
31713 let result = eval(
31715 r#"
31716 fn main() {
31717 let ch = channel_new();
31718 let result = channel_recv_timeout(ch, 10); // 10ms timeout
31719 // Just verify it completes without blocking forever
31720 return 42;
31721 }
31722 "#,
31723 );
31724 assert!(matches!(result, Ok(Value::Int(42))));
31725 }
31726
31727 #[test]
31728 fn test_actor_basic_messaging() {
31729 let result = eval(
31731 r#"
31732 fn main() {
31733 let act = spawn_actor("test_actor");
31734 send_to_actor(act, "ping", 42);
31735 return get_actor_msg_count(act);
31736 }
31737 "#,
31738 );
31739 assert!(matches!(result, Ok(Value::Int(1))));
31740 }
31741
31742 #[test]
31743 fn test_actor_message_storm() {
31744 let result = eval(
31746 r#"
31747 fn main() {
31748 let act = spawn_actor("stress_actor");
31749 let count = 10000;
31750 let i = 0;
31751 while i < count {
31752 send_to_actor(act, "msg", i);
31753 i = i + 1;
31754 }
31755 return get_actor_msg_count(act);
31756 }
31757 "#,
31758 );
31759 assert!(matches!(result, Ok(Value::Int(10000))));
31760 }
31761
31762 #[test]
31763 fn test_actor_pending_count() {
31764 let result = eval(
31766 r#"
31767 fn main() {
31768 let act = spawn_actor("pending_test");
31769
31770 // Send 5 messages
31771 send_to_actor(act, "m1", 1);
31772 send_to_actor(act, "m2", 2);
31773 send_to_actor(act, "m3", 3);
31774 send_to_actor(act, "m4", 4);
31775 send_to_actor(act, "m5", 5);
31776
31777 let pending_before = get_actor_pending(act);
31778
31779 // Receive 2 messages
31780 recv_from_actor(act);
31781 recv_from_actor(act);
31782
31783 let pending_after = get_actor_pending(act);
31784
31785 // Should have 5 pending initially, 3 after receiving 2
31786 return pending_before * 10 + pending_after;
31787 }
31788 "#,
31789 );
31790 assert!(matches!(result, Ok(Value::Int(53)))); }
31792
31793 #[test]
31794 fn test_actor_message_order() {
31795 let result = eval(
31798 r#"
31799 fn main() {
31800 let act = spawn_actor("order_test");
31801 send_to_actor(act, "a", 1);
31802 send_to_actor(act, "b", 2);
31803 send_to_actor(act, "c", 3);
31804
31805 // pop() gives LIFO order, so we get c, b, a
31806 let r1 = recv_from_actor(act);
31807 let r2 = recv_from_actor(act);
31808 let r3 = recv_from_actor(act);
31809
31810 // Return the message types concatenated via their first char values
31811 // c=3, b=2, a=1 in our test
31812 return get_actor_pending(act); // Should be 0 after draining
31813 }
31814 "#,
31815 );
31816 assert!(matches!(result, Ok(Value::Int(0))));
31817 }
31818
31819 #[test]
31820 fn test_actor_recv_empty() {
31821 let result = eval(
31824 r#"
31825 fn main() {
31826 let act = spawn_actor("empty_actor");
31827 // No messages sent, so pending should be 0
31828 return get_actor_pending(act);
31829 }
31830 "#,
31831 );
31832 assert!(matches!(result, Ok(Value::Int(0))));
31833 }
31834
31835 #[test]
31836 fn test_actor_tell_alias() {
31837 let result = eval(
31839 r#"
31840 fn main() {
31841 let act = spawn_actor("tell_test");
31842 tell_actor(act, "hello", 123);
31843 tell_actor(act, "world", 456);
31844 return get_actor_msg_count(act);
31845 }
31846 "#,
31847 );
31848 assert!(matches!(result, Ok(Value::Int(2))));
31849 }
31850
31851 #[test]
31852 fn test_actor_name() {
31853 let result = eval(
31855 r#"
31856 fn main() {
31857 let act = spawn_actor("my_special_actor");
31858 return get_actor_name(act);
31859 }
31860 "#,
31861 );
31862 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "my_special_actor"));
31863 }
31864
31865 #[test]
31866 fn test_multiple_actors() {
31867 let result = eval(
31869 r#"
31870 fn main() {
31871 let a1 = spawn_actor("actor1");
31872 let a2 = spawn_actor("actor2");
31873 let a3 = spawn_actor("actor3");
31874
31875 send_to_actor(a1, "m", 1);
31876 send_to_actor(a2, "m", 1);
31877 send_to_actor(a2, "m", 2);
31878 send_to_actor(a3, "m", 1);
31879 send_to_actor(a3, "m", 2);
31880 send_to_actor(a3, "m", 3);
31881
31882 let c1 = get_actor_msg_count(a1);
31883 let c2 = get_actor_msg_count(a2);
31884 let c3 = get_actor_msg_count(a3);
31885
31886 return c1 * 100 + c2 * 10 + c3;
31887 }
31888 "#,
31889 );
31890 assert!(matches!(result, Ok(Value::Int(123)))); }
31892
31893 #[test]
31894 fn test_multiple_channels() {
31895 let result = eval(
31897 r#"
31898 fn main() {
31899 let ch1 = channel_new();
31900 let ch2 = channel_new();
31901 let ch3 = channel_new();
31902
31903 channel_send(ch1, 100);
31904 channel_send(ch2, 200);
31905 channel_send(ch3, 300);
31906
31907 let v1 = channel_recv(ch1);
31908 let v2 = channel_recv(ch2);
31909 let v3 = channel_recv(ch3);
31910
31911 return v1 + v2 + v3;
31912 }
31913 "#,
31914 );
31915 assert!(matches!(result, Ok(Value::Int(600))));
31916 }
31917
31918 #[test]
31919 fn test_thread_sleep() {
31920 let result = eval(
31922 r#"
31923 fn main() {
31924 thread_sleep(1); // Sleep 1ms
31925 return 42;
31926 }
31927 "#,
31928 );
31929 assert!(matches!(result, Ok(Value::Int(42))));
31930 }
31931
31932 #[test]
31933 fn test_thread_yield() {
31934 let result = eval(
31936 r#"
31937 fn main() {
31938 thread_yield();
31939 return 42;
31940 }
31941 "#,
31942 );
31943 assert!(matches!(result, Ok(Value::Int(42))));
31944 }
31945
31946 #[test]
31947 fn test_thread_id() {
31948 let result = eval(
31950 r#"
31951 fn main() {
31952 let id = thread_id();
31953 return len(id) > 0;
31954 }
31955 "#,
31956 );
31957 assert!(matches!(result, Ok(Value::Bool(true))));
31958 }
31959
31960 #[test]
31961 fn test_channel_stress_interleaved() {
31962 let result = eval(
31964 r#"
31965 fn main() {
31966 let ch = channel_new();
31967 let sum = 0;
31968 let i = 0;
31969 while i < 100 {
31970 channel_send(ch, i);
31971 channel_send(ch, i * 2);
31972 let a = channel_recv(ch);
31973 let b = channel_recv(ch);
31974 sum = sum + a + b;
31975 i = i + 1;
31976 }
31977 // Sum: sum of i + i*2 for i in 0..99
31978 // = sum of 3*i for i in 0..99 = 3 * (99*100/2) = 3 * 4950 = 14850
31979 return sum;
31980 }
31981 "#,
31982 );
31983 assert!(matches!(result, Ok(Value::Int(14850))));
31984 }
31985
31986 #[test]
31987 fn test_actor_stress_with_receive() {
31988 let result = eval(
31990 r#"
31991 fn main() {
31992 let act = spawn_actor("recv_stress");
31993 let count = 1000;
31994 let i = 0;
31995 while i < count {
31996 send_to_actor(act, "data", i);
31997 i = i + 1;
31998 }
31999
32000 // Drain all messages
32001 let drained = 0;
32002 while get_actor_pending(act) > 0 {
32003 recv_from_actor(act);
32004 drained = drained + 1;
32005 }
32006
32007 return drained;
32008 }
32009 "#,
32010 );
32011 assert!(matches!(result, Ok(Value::Int(1000))));
32012 }
32013
32014 use proptest::prelude::*;
32018
32019 proptest! {
32022 #![proptest_config(ProptestConfig::with_cases(100))]
32023
32024 #[test]
32025 fn test_parser_doesnt_crash_on_random_input(s in "\\PC*") {
32026 let mut parser = Parser::new(&s);
32028 let _ = parser.parse_file(); }
32030
32031 #[test]
32032 fn test_parser_handles_unicode(s in "[\\p{L}\\p{N}\\p{P}\\s]{0,100}") {
32033 let mut parser = Parser::new(&s);
32035 let _ = parser.parse_file();
32036 }
32037
32038 #[test]
32039 fn test_parser_nested_brackets(depth in 0..20usize) {
32040 let open: String = (0..depth).map(|_| '(').collect();
32042 let close: String = (0..depth).map(|_| ')').collect();
32043 let code = format!("fn main() {{ return {}1{}; }}", open, close);
32044 let mut parser = Parser::new(&code);
32045 let _ = parser.parse_file();
32046 }
32047
32048 #[test]
32049 fn test_parser_long_identifiers(len in 1..500usize) {
32050 let ident: String = (0..len).map(|_| 'a').collect();
32052 let code = format!("fn main() {{ let {} = 1; return {}; }}", ident, ident);
32053 let result = eval(&code);
32054 assert!(matches!(result, Ok(Value::Int(1))));
32055 }
32056
32057 #[test]
32058 fn test_parser_many_arguments(count in 0..50usize) {
32059 let args: String = (0..count).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
32061 let code = format!("fn main() {{ return len([{}]); }}", args);
32062 let result = eval(&code);
32063 assert!(matches!(result, Ok(Value::Int(c)) if c == count as i64));
32064 }
32065 }
32066
32067 proptest! {
32070 #![proptest_config(ProptestConfig::with_cases(50))]
32071
32072 #[test]
32073 fn test_ga_bivector_anticommutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32074 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
32075 let code = format!(r#"
32078 fn main() {{
32079 let a = vec3({}, {}, {});
32080 let b = vec3({}, {}, {});
32081 let ab = vec3_cross(a, b);
32082 let ba = vec3_cross(b, a);
32083 let diff_x = get(ab, 0) + get(ba, 0);
32084 let diff_y = get(ab, 1) + get(ba, 1);
32085 let diff_z = get(ab, 2) + get(ba, 2);
32086 let eps = 0.001;
32087 return eps > abs(diff_x) && eps > abs(diff_y) && eps > abs(diff_z);
32088 }}
32089 "#, x1, y1, z1, x2, y2, z2);
32090 let result = eval(&code);
32091 assert!(matches!(result, Ok(Value::Bool(true))));
32092 }
32093
32094 #[test]
32095 fn test_vec3_dot_commutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32096 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
32097 let code = format!(r#"
32099 fn main() {{
32100 let a = vec3({}, {}, {});
32101 let b = vec3({}, {}, {});
32102 let ab = vec3_dot(a, b);
32103 let ba = vec3_dot(b, a);
32104 let eps = 0.001;
32105 return eps > abs(ab - ba);
32106 }}
32107 "#, x1, y1, z1, x2, y2, z2);
32108 let result = eval(&code);
32109 assert!(matches!(result, Ok(Value::Bool(true))));
32110 }
32111
32112 #[test]
32113 fn test_quat_identity_preserves_vector(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0) {
32114 let code = format!(r#"
32116 fn main() {{
32117 let v = vec3({}, {}, {});
32118 let q = quat_identity();
32119 let rotated = quat_rotate(q, v);
32120 let diff_x = abs(get(v, 0) - get(rotated, 0));
32121 let diff_y = abs(get(v, 1) - get(rotated, 1));
32122 let diff_z = abs(get(v, 2) - get(rotated, 2));
32123 let eps = 0.001;
32124 return eps > diff_x && eps > diff_y && eps > diff_z;
32125 }}
32126 "#, x, y, z);
32127 let result = eval(&code);
32128 assert!(matches!(result, Ok(Value::Bool(true))));
32129 }
32130
32131 #[test]
32132 fn test_quat_double_rotation_equals_double_angle(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0,
32133 angle in -3.14f64..3.14) {
32134 let code = format!(r#"
32136 fn main() {{
32137 let v = vec3({}, {}, {});
32138 let axis = vec3(0.0, 1.0, 0.0);
32139 let q1 = quat_from_axis_angle(axis, {});
32140 let q2 = quat_from_axis_angle(axis, {} * 2.0);
32141 let q1q1 = quat_mul(q1, q1);
32142 let eps = 0.01;
32143 let same = eps > abs(get(q2, 0) - get(q1q1, 0)) &&
32144 eps > abs(get(q2, 1) - get(q1q1, 1)) &&
32145 eps > abs(get(q2, 2) - get(q1q1, 2)) &&
32146 eps > abs(get(q2, 3) - get(q1q1, 3));
32147 let neg_same = eps > abs(get(q2, 0) + get(q1q1, 0)) &&
32148 eps > abs(get(q2, 1) + get(q1q1, 1)) &&
32149 eps > abs(get(q2, 2) + get(q1q1, 2)) &&
32150 eps > abs(get(q2, 3) + get(q1q1, 3));
32151 return same || neg_same;
32152 }}
32153 "#, x, y, z, angle, angle);
32154 let result = eval(&code);
32155 assert!(matches!(result, Ok(Value::Bool(true))));
32156 }
32157
32158 #[test]
32159 fn test_vec3_add_associative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32160 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0,
32161 x3 in -100.0f64..100.0, y3 in -100.0f64..100.0, z3 in -100.0f64..100.0) {
32162 let code = format!(r#"
32164 fn main() {{
32165 let a = vec3({}, {}, {});
32166 let b = vec3({}, {}, {});
32167 let c = vec3({}, {}, {});
32168 let ab_c = vec3_add(vec3_add(a, b), c);
32169 let a_bc = vec3_add(a, vec3_add(b, c));
32170 let diff_x = abs(get(ab_c, 0) - get(a_bc, 0));
32171 let diff_y = abs(get(ab_c, 1) - get(a_bc, 1));
32172 let diff_z = abs(get(ab_c, 2) - get(a_bc, 2));
32173 let eps = 0.001;
32174 return eps > diff_x && eps > diff_y && eps > diff_z;
32175 }}
32176 "#, x1, y1, z1, x2, y2, z2, x3, y3, z3);
32177 let result = eval(&code);
32178 assert!(matches!(result, Ok(Value::Bool(true))));
32179 }
32180
32181 #[test]
32182 fn test_vec3_scale_distributive(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0,
32183 s1 in -10.0f64..10.0, s2 in -10.0f64..10.0) {
32184 let code = format!(r#"
32186 fn main() {{
32187 let v = vec3({}, {}, {});
32188 let s1 = {};
32189 let s2 = {};
32190 let combined = vec3_scale(v, s1 + s2);
32191 let separate = vec3_add(vec3_scale(v, s1), vec3_scale(v, s2));
32192 let diff_x = abs(get(combined, 0) - get(separate, 0));
32193 let diff_y = abs(get(combined, 1) - get(separate, 1));
32194 let diff_z = abs(get(combined, 2) - get(separate, 2));
32195 let eps = 0.01;
32196 return eps > diff_x && eps > diff_y && eps > diff_z;
32197 }}
32198 "#, x, y, z, s1, s2);
32199 let result = eval(&code);
32200 assert!(matches!(result, Ok(Value::Bool(true))));
32201 }
32202 }
32203
32204 proptest! {
32207 #![proptest_config(ProptestConfig::with_cases(30))]
32208
32209 #[test]
32210 fn test_grad_of_constant_is_zero(c in -100.0f64..100.0, x in -100.0f64..100.0) {
32211 let code = format!(r#"
32213 fn main() {{
32214 fn constant(x) {{ return {}; }}
32215 let g = grad(constant, {});
32216 let eps = 0.001;
32217 return eps > abs(g);
32218 }}
32219 "#, c, x);
32220 let result = eval(&code);
32221 assert!(matches!(result, Ok(Value::Bool(true))));
32222 }
32223
32224 #[test]
32225 fn test_grad_of_x_is_one(x in -100.0f64..100.0) {
32226 let code = format!(r#"
32228 fn main() {{
32229 fn identity(x) {{ return x; }}
32230 let g = grad(identity, {});
32231 let eps = 0.001;
32232 return eps > abs(g - 1.0);
32233 }}
32234 "#, x);
32235 let result = eval(&code);
32236 assert!(matches!(result, Ok(Value::Bool(true))));
32237 }
32238
32239 #[test]
32240 fn test_grad_of_x_squared(x in -50.0f64..50.0) {
32241 let code = format!(r#"
32243 fn main() {{
32244 fn square(x) {{ return x * x; }}
32245 let g = grad(square, {});
32246 let expected = 2.0 * {};
32247 let eps = 0.1;
32248 return eps > abs(g - expected);
32249 }}
32250 "#, x, x);
32251 let result = eval(&code);
32252 assert!(matches!(result, Ok(Value::Bool(true))));
32253 }
32254
32255 #[test]
32256 fn test_grad_linearity(a in -10.0f64..10.0, b in -10.0f64..10.0, x in -10.0f64..10.0) {
32257 let code = format!(r#"
32259 fn main() {{
32260 fn linear(x) {{ return {} * x + {}; }}
32261 let g = grad(linear, {});
32262 let eps = 0.1;
32263 return eps > abs(g - {});
32264 }}
32265 "#, a, b, x, a);
32266 let result = eval(&code);
32267 assert!(matches!(result, Ok(Value::Bool(true))));
32268 }
32269 }
32270
32271 proptest! {
32274 #![proptest_config(ProptestConfig::with_cases(50))]
32275
32276 #[test]
32277 fn test_addition_commutative(a in -1000i64..1000, b in -1000i64..1000) {
32278 let code = format!("fn main() {{ return {} + {} == {} + {}; }}", a, b, b, a);
32279 let result = eval(&code);
32280 assert!(matches!(result, Ok(Value::Bool(true))));
32281 }
32282
32283 #[test]
32284 fn test_multiplication_commutative(a in -100i64..100, b in -100i64..100) {
32285 let code = format!("fn main() {{ return {} * {} == {} * {}; }}", a, b, b, a);
32286 let result = eval(&code);
32287 assert!(matches!(result, Ok(Value::Bool(true))));
32288 }
32289
32290 #[test]
32291 fn test_addition_identity(a in -1000i64..1000) {
32292 let code = format!("fn main() {{ return {} + 0 == {}; }}", a, a);
32293 let result = eval(&code);
32294 assert!(matches!(result, Ok(Value::Bool(true))));
32295 }
32296
32297 #[test]
32298 fn test_multiplication_identity(a in -1000i64..1000) {
32299 let code = format!("fn main() {{ return {} * 1 == {}; }}", a, a);
32300 let result = eval(&code);
32301 assert!(matches!(result, Ok(Value::Bool(true))));
32302 }
32303
32304 #[test]
32305 fn test_distributive_property(a in -20i64..20, b in -20i64..20, c in -20i64..20) {
32306 let code = format!("fn main() {{ return {} * ({} + {}) == {} * {} + {} * {}; }}", a, b, c, a, b, a, c);
32307 let result = eval(&code);
32308 assert!(matches!(result, Ok(Value::Bool(true))));
32309 }
32310 }
32311
32312 proptest! {
32315 #![proptest_config(ProptestConfig::with_cases(30))]
32316
32317 #[test]
32318 fn test_array_len_after_push(initial_len in 0..20usize, value in -100i64..100) {
32319 let initial: String = (0..initial_len).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
32320 let code = format!(r#"
32321 fn main() {{
32322 let arr = [{}];
32323 push(arr, {});
32324 return len(arr);
32325 }}
32326 "#, initial, value);
32327 let result = eval(&code);
32328 assert!(matches!(result, Ok(Value::Int(n)) if n == (initial_len + 1) as i64));
32329 }
32330
32331 #[test]
32332 fn test_reverse_reverse_identity(elements in prop::collection::vec(-100i64..100, 0..10)) {
32333 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
32334 let code = format!(r#"
32335 fn main() {{
32336 let arr = [{}];
32337 let rev1 = reverse(arr);
32338 let rev2 = reverse(rev1);
32339 let same = true;
32340 let i = 0;
32341 while i < len(arr) {{
32342 if get(arr, i) != get(rev2, i) {{
32343 same = false;
32344 }}
32345 i = i + 1;
32346 }}
32347 return same;
32348 }}
32349 "#, arr_str);
32350 let result = eval(&code);
32351 assert!(matches!(result, Ok(Value::Bool(true))));
32352 }
32353
32354 #[test]
32355 fn test_sum_equals_manual_sum(elements in prop::collection::vec(-100i64..100, 0..20)) {
32356 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
32357 let expected_sum: i64 = elements.iter().sum();
32358 let code = format!("fn main() {{ return sum([{}]); }}", arr_str);
32359 let result = eval(&code);
32360 assert!(matches!(result, Ok(Value::Int(n)) if n == expected_sum));
32361 }
32362 }
32363
32364 #[test]
32372 fn test_no_leak_repeated_array_operations() {
32373 let result = eval(
32375 r#"
32376 fn main() {
32377 let i = 0;
32378 while i < 1000 {
32379 let arr = [1, 2, 3, 4, 5];
32380 push(arr, 6);
32381 let rev = reverse(arr);
32382 let s = sum(arr);
32383 i = i + 1;
32384 }
32385 return i;
32386 }
32387 "#,
32388 );
32389 assert!(matches!(result, Ok(Value::Int(1000))));
32390 }
32391
32392 #[test]
32393 fn test_no_leak_repeated_function_calls() {
32394 let result = eval(
32396 r#"
32397 fn fib(n) {
32398 if n <= 1 { return n; }
32399 return fib(n - 1) + fib(n - 2);
32400 }
32401 fn main() {
32402 let i = 0;
32403 let total = 0;
32404 while i < 100 {
32405 total = total + fib(10);
32406 i = i + 1;
32407 }
32408 return total;
32409 }
32410 "#,
32411 );
32412 assert!(matches!(result, Ok(Value::Int(5500))));
32413 }
32414
32415 #[test]
32416 fn test_no_leak_repeated_map_operations() {
32417 let result = eval(
32419 r#"
32420 fn main() {
32421 let i = 0;
32422 while i < 500 {
32423 let m = map_new();
32424 map_set(m, "key1", 1);
32425 map_set(m, "key2", 2);
32426 map_set(m, "key3", 3);
32427 let v = map_get(m, "key1");
32428 i = i + 1;
32429 }
32430 return i;
32431 }
32432 "#,
32433 );
32434 assert!(matches!(result, Ok(Value::Int(500))));
32435 }
32436
32437 #[test]
32438 fn test_no_leak_repeated_string_operations() {
32439 let result = eval(
32441 r#"
32442 fn main() {
32443 let i = 0;
32444 while i < 1000 {
32445 let s = "hello world";
32446 let upper_s = upper(s);
32447 let lower_s = lower(upper_s);
32448 let concat_s = s ++ " " ++ upper_s;
32449 let replaced = replace(concat_s, "o", "0");
32450 i = i + 1;
32451 }
32452 return i;
32453 }
32454 "#,
32455 );
32456 assert!(matches!(result, Ok(Value::Int(1000))));
32457 }
32458
32459 #[test]
32460 fn test_no_leak_repeated_ecs_operations() {
32461 let result = eval(
32463 r#"
32464 fn main() {
32465 let world = ecs_world();
32466 let i = 0;
32467 while i < 500 {
32468 let entity = ecs_spawn(world);
32469 ecs_attach(world, entity, "Position", vec3(1.0, 2.0, 3.0));
32470 ecs_attach(world, entity, "Velocity", vec3(0.0, 0.0, 0.0));
32471 let pos = ecs_get(world, entity, "Position");
32472 i = i + 1;
32473 }
32474 return i;
32475 }
32476 "#,
32477 );
32478 assert!(matches!(result, Ok(Value::Int(500))));
32479 }
32480
32481 #[test]
32482 fn test_no_leak_repeated_channel_operations() {
32483 let result = eval(
32485 r#"
32486 fn main() {
32487 let i = 0;
32488 while i < 500 {
32489 let ch = channel_new();
32490 channel_send(ch, i);
32491 channel_send(ch, i + 1);
32492 let v1 = channel_recv(ch);
32493 let v2 = channel_recv(ch);
32494 i = i + 1;
32495 }
32496 return i;
32497 }
32498 "#,
32499 );
32500 assert!(matches!(result, Ok(Value::Int(500))));
32501 }
32502
32503 #[test]
32504 fn test_no_leak_repeated_actor_operations() {
32505 let result = eval(
32507 r#"
32508 fn main() {
32509 let i = 0;
32510 while i < 100 {
32511 let act = spawn_actor("leak_test_actor");
32512 send_to_actor(act, "msg", i);
32513 send_to_actor(act, "msg", i + 1);
32514 let count = get_actor_msg_count(act);
32515 i = i + 1;
32516 }
32517 return i;
32518 }
32519 "#,
32520 );
32521 assert!(matches!(result, Ok(Value::Int(100))));
32522 }
32523
32524 #[test]
32525 fn test_no_leak_repeated_vec3_operations() {
32526 let result = eval(
32528 r#"
32529 fn main() {
32530 let i = 0;
32531 while i < 1000 {
32532 let v1 = vec3(1.0, 2.0, 3.0);
32533 let v2 = vec3(4.0, 5.0, 6.0);
32534 let added = vec3_add(v1, v2);
32535 let scaled = vec3_scale(added, 2.0);
32536 let dot = vec3_dot(v1, v2);
32537 let crossed = vec3_cross(v1, v2);
32538 let normalized = vec3_normalize(crossed);
32539 i = i + 1;
32540 }
32541 return i;
32542 }
32543 "#,
32544 );
32545 assert!(matches!(result, Ok(Value::Int(1000))));
32546 }
32547
32548 #[test]
32549 fn test_no_leak_repeated_closure_creation() {
32550 let result = eval(
32552 r#"
32553 fn main() {
32554 let i = 0;
32555 let total = 0;
32556 while i < 500 {
32557 let x = i;
32558 fn add_x(y) { return x + y; }
32559 total = total + add_x(1);
32560 i = i + 1;
32561 }
32562 return total;
32563 }
32564 "#,
32565 );
32566 assert!(matches!(result, Ok(Value::Int(125250))));
32568 }
32569
32570 #[test]
32571 fn test_no_leak_nested_data_structures() {
32572 let result = eval(
32574 r#"
32575 fn main() {
32576 let i = 0;
32577 while i < 200 {
32578 let inner1 = [1, 2, 3];
32579 let inner2 = [4, 5, 6];
32580 let outer = [inner1, inner2];
32581 let m = map_new();
32582 map_set(m, "arr", outer);
32583 map_set(m, "nested", map_new());
32584 i = i + 1;
32585 }
32586 return i;
32587 }
32588 "#,
32589 );
32590 assert!(matches!(result, Ok(Value::Int(200))));
32591 }
32592
32593 #[test]
32594 fn test_no_leak_repeated_interpreter_creation() {
32595 for _ in 0..50 {
32597 let result = eval(
32598 r#"
32599 fn main() {
32600 let arr = [1, 2, 3, 4, 5];
32601 let total = sum(arr);
32602 return total * 2;
32603 }
32604 "#,
32605 );
32606 assert!(matches!(result, Ok(Value::Int(30))));
32607 }
32608 }
32609}