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 Value::RefCellValue(_) => "refcell",
393 Value::TraitObject { trait_name, .. } => return Ok(Value::String(Rc::new(format!("dyn {}", trait_name)))),
394 };
395 Ok(Value::String(Rc::new(type_name.to_string())))
396 });
397
398 define(interp, "assert", None, |_, args| {
400 if args.is_empty() {
401 return Err(RuntimeError::new("assert() requires at least one argument"));
402 }
403 let condition = match &args[0] {
404 Value::Bool(b) => *b,
405 _ => return Err(RuntimeError::new("assert() condition must be bool")),
406 };
407 if !condition {
408 let msg = if args.len() > 1 {
409 format!("{}", args[1])
410 } else {
411 "assertion failed".to_string()
412 };
413 return Err(RuntimeError::new(format!("Assertion failed: {}", msg)));
414 }
415 Ok(Value::Null)
416 });
417
418 define(interp, "panic", None, |_, args| {
420 let msg = if args.is_empty() {
421 "explicit panic".to_string()
422 } else {
423 args.iter()
424 .map(|v| format!("{}", v))
425 .collect::<Vec<_>>()
426 .join(" ")
427 };
428 Err(RuntimeError::new(format!("PANIC: {}", msg)))
429 });
430
431 define(interp, "todo", None, |_, args| {
433 let msg = if args.is_empty() {
434 "not yet implemented".to_string()
435 } else {
436 format!("{}", args[0])
437 };
438 Err(RuntimeError::new(format!("TODO: {}", msg)))
439 });
440
441 define(interp, "unreachable", None, |_, args| {
443 let msg = if args.is_empty() {
444 "entered unreachable code".to_string()
445 } else {
446 format!("{}", args[0])
447 };
448 Err(RuntimeError::new(format!("UNREACHABLE: {}", msg)))
449 });
450
451 define(interp, "clone", Some(1), |_, args| Ok(deep_clone(&args[0])));
453
454 define(interp, "id", Some(1), |_, args| Ok(args[0].clone()));
456
457 define(interp, "default", None, |interp, args| {
460 let type_name = if args.is_empty() {
461 match &interp.current_self_type {
464 Some(t) => t.clone(),
465 None => return Ok(Value::Struct {
466 name: "Default".to_string(),
467 fields: Rc::new(RefCell::new(std::collections::HashMap::new())),
468 }),
469 }
470 } else {
471 match &args[0] {
472 Value::String(s) => s.to_string(),
473 _ => return Err(RuntimeError::new("default() requires type name string")),
474 }
475 };
476 let type_name = type_name.as_str();
477 match type_name {
478 "bool" => Ok(Value::Bool(false)),
479 "i64" | "int" => Ok(Value::Int(0)),
480 "f64" | "float" => Ok(Value::Float(0.0)),
481 "str" | "string" => Ok(Value::String(Rc::new(String::new()))),
482 "array" => Ok(Value::Array(Rc::new(RefCell::new(Vec::new())))),
483 _ => {
484 if let Some(struct_def) = interp.default_structs.get(type_name).cloned() {
486 use crate::ast::StructFields;
487 let mut fields = std::collections::HashMap::new();
488 if let StructFields::Named(field_defs) = &struct_def.fields {
489 for field in field_defs {
490 let default_val = if let Some(ref default_expr) = field.default {
492 match interp.evaluate(default_expr) {
493 Ok(v) => v,
494 Err(_) => Value::Null,
495 }
496 } else {
497 Value::Null
498 };
499 fields.insert(field.name.name.clone(), default_val);
500 }
501 }
502 Ok(Value::Struct {
503 name: type_name.to_string(),
504 fields: Rc::new(RefCell::new(fields)),
505 })
506 } else {
507 Ok(Value::Struct {
509 name: type_name.to_string(),
510 fields: Rc::new(RefCell::new(std::collections::HashMap::new())),
511 })
512 }
513 }
514 }
515 });
516
517 define(interp, "Result·Ok", Some(1), |_, args| {
519 Ok(Value::Variant {
520 enum_name: "Result".to_string(),
521 variant_name: "Ok".to_string(),
522 fields: Some(Rc::new(vec![args[0].clone()])),
523 })
524 });
525
526 define(interp, "Ok", Some(1), |_, args| {
528 Ok(Value::Variant {
529 enum_name: "Result".to_string(),
530 variant_name: "Ok".to_string(),
531 fields: Some(Rc::new(vec![args[0].clone()])),
532 })
533 });
534
535 define(interp, "Result·Err", Some(1), |_, args| {
537 Ok(Value::Variant {
538 enum_name: "Result".to_string(),
539 variant_name: "Err".to_string(),
540 fields: Some(Rc::new(vec![args[0].clone()])),
541 })
542 });
543
544 define(interp, "Err", Some(1), |_, args| {
546 Ok(Value::Variant {
547 enum_name: "Result".to_string(),
548 variant_name: "Err".to_string(),
549 fields: Some(Rc::new(vec![args[0].clone()])),
550 })
551 });
552
553 define(interp, "Option·Some", Some(1), |_, args| {
555 Ok(Value::Variant {
556 enum_name: "Option".to_string(),
557 variant_name: "Some".to_string(),
558 fields: Some(Rc::new(vec![args[0].clone()])),
559 })
560 });
561
562 define(interp, "Some", Some(1), |_, args| {
564 Ok(Value::Variant {
565 enum_name: "Option".to_string(),
566 variant_name: "Some".to_string(),
567 fields: Some(Rc::new(vec![args[0].clone()])),
568 })
569 });
570
571 interp.globals.borrow_mut().define(
573 "Option·None".to_string(),
574 Value::Variant {
575 enum_name: "Option".to_string(),
576 variant_name: "None".to_string(),
577 fields: None,
578 },
579 );
580
581 interp.globals.borrow_mut().define(
583 "None".to_string(),
584 Value::Variant {
585 enum_name: "Option".to_string(),
586 variant_name: "None".to_string(),
587 fields: None,
588 },
589 );
590
591 define(interp, "Map·new", Some(0), |_, _| {
593 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
594 });
595
596 define(interp, "HashMap·new", Some(0), |_, _| {
598 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
599 });
600
601 define(interp, "HashMap·with_capacity", Some(1), |_, _args| {
603 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
605 });
606
607 define(interp, "std·collections·HashMap·new", Some(0), |_, _| {
609 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
610 });
611
612 define(interp, "std·collections·HashMap·with_capacity", Some(1), |_, _args| {
614 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
615 });
616
617 define(interp, "HashSet·new", Some(0), |_, _| {
619 Ok(Value::Set(Rc::new(RefCell::new(std::collections::HashSet::new()))))
620 });
621
622 define(interp, "HashSet·with_capacity", Some(1), |_, _args| {
624 Ok(Value::Set(Rc::new(RefCell::new(std::collections::HashSet::new()))))
625 });
626
627 define(interp, "std·collections·HashSet·new", Some(0), |_, _| {
629 Ok(Value::Set(Rc::new(RefCell::new(std::collections::HashSet::new()))))
630 });
631
632 define(interp, "Vec·new", Some(0), |_, _| {
634 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
635 });
636
637 define(interp, "String·new", Some(0), |_, _| {
639 Ok(Value::String(Rc::new(String::new())))
640 });
641
642 define(interp, "String·from", Some(1), |_, args| {
644 let s = match &args[0] {
645 Value::String(s) => (**s).clone(),
646 Value::Int(n) => n.to_string(),
647 Value::Float(f) => f.to_string(),
648 Value::Bool(b) => b.to_string(),
649 Value::Char(c) => c.to_string(),
650 _ => format!("{}", args[0]),
651 };
652 Ok(Value::String(Rc::new(s)))
653 });
654
655 define(interp, "Box·new", Some(1), |_, args| {
657 Ok(args[0].clone())
658 });
659
660 define(interp, "String·from_raw_parts", Some(3), |_, args| {
662 match &args[0] {
664 Value::String(s) => Ok(Value::String(s.clone())),
665 Value::Null => Ok(Value::String(Rc::new(String::new()))),
666 _ => Ok(Value::String(Rc::new(format!("{}", args[0])))),
667 }
668 });
669
670 define(interp, "slice·from_raw_parts", Some(2), |_, args| {
672 match &args[0] {
674 Value::String(s) => Ok(Value::String(s.clone())),
675 Value::Array(arr) => Ok(Value::Array(arr.clone())),
676 _ => Ok(args[0].clone()),
677 }
678 });
679}
680
681fn deep_clone(value: &Value) -> Value {
683 match value {
684 Value::Array(arr) => {
685 let cloned: Vec<Value> = arr.borrow().iter().map(deep_clone).collect();
686 Value::Array(Rc::new(RefCell::new(cloned)))
687 }
688 Value::Struct { name, fields } => {
689 let cloned: HashMap<String, Value> = fields
690 .borrow()
691 .iter()
692 .map(|(k, v)| (k.clone(), deep_clone(v)))
693 .collect();
694 Value::Struct {
695 name: name.clone(),
696 fields: Rc::new(RefCell::new(cloned)),
697 }
698 }
699 Value::Evidential { value, evidence } => Value::Evidential {
700 value: Box::new(deep_clone(value)),
701 evidence: *evidence,
702 },
703 other => other.clone(),
704 }
705}
706
707fn register_math(interp: &mut Interpreter) {
712 define(interp, "abs", Some(1), |_, args| match &args[0] {
714 Value::Int(n) => Ok(Value::Int(n.abs())),
715 Value::Float(n) => Ok(Value::Float(n.abs())),
716 _ => Err(RuntimeError::new("abs() requires number")),
717 });
718
719 define(interp, "neg", Some(1), |_, args| match &args[0] {
720 Value::Int(n) => Ok(Value::Int(-n)),
721 Value::Float(n) => Ok(Value::Float(-n)),
722 _ => Err(RuntimeError::new("neg() requires number")),
723 });
724
725 define(interp, "sqrt", Some(1), |_, args| match &args[0] {
726 Value::Int(n) => Ok(Value::Float((*n as f64).sqrt())),
727 Value::Float(n) => Ok(Value::Float(n.sqrt())),
728 _ => Err(RuntimeError::new("sqrt() requires number")),
729 });
730
731 define(interp, "cbrt", Some(1), |_, args| match &args[0] {
732 Value::Int(n) => Ok(Value::Float((*n as f64).cbrt())),
733 Value::Float(n) => Ok(Value::Float(n.cbrt())),
734 _ => Err(RuntimeError::new("cbrt() requires number")),
735 });
736
737 define(interp, "pow", Some(2), |_, args| {
738 match (&args[0], &args[1]) {
739 (Value::Int(base), Value::Int(exp)) => {
740 if *exp >= 0 {
741 Ok(Value::Int(base.pow(*exp as u32)))
742 } else {
743 Ok(Value::Float((*base as f64).powi(*exp as i32)))
744 }
745 }
746 (Value::Float(base), Value::Int(exp)) => Ok(Value::Float(base.powi(*exp as i32))),
747 (Value::Float(base), Value::Float(exp)) => Ok(Value::Float(base.powf(*exp))),
748 (Value::Int(base), Value::Float(exp)) => Ok(Value::Float((*base as f64).powf(*exp))),
749 _ => Err(RuntimeError::new("pow() requires numbers")),
750 }
751 });
752
753 define(interp, "exp", Some(1), |_, args| match &args[0] {
754 Value::Int(n) => Ok(Value::Float((*n as f64).exp())),
755 Value::Float(n) => Ok(Value::Float(n.exp())),
756 _ => Err(RuntimeError::new("exp() requires number")),
757 });
758
759 define(interp, "ln", Some(1), |_, args| match &args[0] {
760 Value::Int(n) => Ok(Value::Float((*n as f64).ln())),
761 Value::Float(n) => Ok(Value::Float(n.ln())),
762 _ => Err(RuntimeError::new("ln() requires number")),
763 });
764
765 define(interp, "log", Some(2), |_, args| {
766 let (value, base) = match (&args[0], &args[1]) {
767 (Value::Int(v), Value::Int(b)) => (*v as f64, *b as f64),
768 (Value::Float(v), Value::Int(b)) => (*v, *b as f64),
769 (Value::Int(v), Value::Float(b)) => (*v as f64, *b),
770 (Value::Float(v), Value::Float(b)) => (*v, *b),
771 _ => return Err(RuntimeError::new("log() requires numbers")),
772 };
773 Ok(Value::Float(value.log(base)))
774 });
775
776 define(interp, "log10", Some(1), |_, args| match &args[0] {
777 Value::Int(n) => Ok(Value::Float((*n as f64).log10())),
778 Value::Float(n) => Ok(Value::Float(n.log10())),
779 _ => Err(RuntimeError::new("log10() requires number")),
780 });
781
782 define(interp, "log2", Some(1), |_, args| match &args[0] {
783 Value::Int(n) => Ok(Value::Float((*n as f64).log2())),
784 Value::Float(n) => Ok(Value::Float(n.log2())),
785 _ => Err(RuntimeError::new("log2() requires number")),
786 });
787
788 define(interp, "sin", Some(1), |_, args| match &args[0] {
790 Value::Int(n) => Ok(Value::Float((*n as f64).sin())),
791 Value::Float(n) => Ok(Value::Float(n.sin())),
792 _ => Err(RuntimeError::new("sin() requires number")),
793 });
794
795 define(interp, "cos", Some(1), |_, args| match &args[0] {
796 Value::Int(n) => Ok(Value::Float((*n as f64).cos())),
797 Value::Float(n) => Ok(Value::Float(n.cos())),
798 _ => Err(RuntimeError::new("cos() requires number")),
799 });
800
801 define(interp, "tan", Some(1), |_, args| match &args[0] {
802 Value::Int(n) => Ok(Value::Float((*n as f64).tan())),
803 Value::Float(n) => Ok(Value::Float(n.tan())),
804 _ => Err(RuntimeError::new("tan() requires number")),
805 });
806
807 define(interp, "asin", Some(1), |_, args| match &args[0] {
808 Value::Int(n) => Ok(Value::Float((*n as f64).asin())),
809 Value::Float(n) => Ok(Value::Float(n.asin())),
810 _ => Err(RuntimeError::new("asin() requires number")),
811 });
812
813 define(interp, "acos", Some(1), |_, args| match &args[0] {
814 Value::Int(n) => Ok(Value::Float((*n as f64).acos())),
815 Value::Float(n) => Ok(Value::Float(n.acos())),
816 _ => Err(RuntimeError::new("acos() requires number")),
817 });
818
819 define(interp, "atan", Some(1), |_, args| match &args[0] {
820 Value::Int(n) => Ok(Value::Float((*n as f64).atan())),
821 Value::Float(n) => Ok(Value::Float(n.atan())),
822 _ => Err(RuntimeError::new("atan() requires number")),
823 });
824
825 define(interp, "atan2", Some(2), |_, args| {
826 let (y, x) = match (&args[0], &args[1]) {
827 (Value::Int(y), Value::Int(x)) => (*y as f64, *x as f64),
828 (Value::Float(y), Value::Int(x)) => (*y, *x as f64),
829 (Value::Int(y), Value::Float(x)) => (*y as f64, *x),
830 (Value::Float(y), Value::Float(x)) => (*y, *x),
831 _ => return Err(RuntimeError::new("atan2() requires numbers")),
832 };
833 Ok(Value::Float(y.atan2(x)))
834 });
835
836 define(interp, "sinh", Some(1), |_, args| match &args[0] {
838 Value::Int(n) => Ok(Value::Float((*n as f64).sinh())),
839 Value::Float(n) => Ok(Value::Float(n.sinh())),
840 _ => Err(RuntimeError::new("sinh() requires number")),
841 });
842
843 define(interp, "cosh", Some(1), |_, args| match &args[0] {
844 Value::Int(n) => Ok(Value::Float((*n as f64).cosh())),
845 Value::Float(n) => Ok(Value::Float(n.cosh())),
846 _ => Err(RuntimeError::new("cosh() requires number")),
847 });
848
849 define(interp, "tanh", Some(1), |_, args| match &args[0] {
850 Value::Int(n) => Ok(Value::Float((*n as f64).tanh())),
851 Value::Float(n) => Ok(Value::Float(n.tanh())),
852 _ => Err(RuntimeError::new("tanh() requires number")),
853 });
854
855 define(interp, "floor", Some(1), |_, args| match &args[0] {
857 Value::Int(n) => Ok(Value::Int(*n)),
858 Value::Float(n) => Ok(Value::Int(n.floor() as i64)),
859 _ => Err(RuntimeError::new("floor() requires number")),
860 });
861
862 define(interp, "ceil", Some(1), |_, args| match &args[0] {
863 Value::Int(n) => Ok(Value::Int(*n)),
864 Value::Float(n) => Ok(Value::Int(n.ceil() as i64)),
865 _ => Err(RuntimeError::new("ceil() requires number")),
866 });
867
868 define(interp, "round", Some(1), |_, args| match &args[0] {
869 Value::Int(n) => Ok(Value::Int(*n)),
870 Value::Float(n) => Ok(Value::Int(n.round() as i64)),
871 _ => Err(RuntimeError::new("round() requires number")),
872 });
873
874 define(interp, "trunc", Some(1), |_, args| match &args[0] {
875 Value::Int(n) => Ok(Value::Int(*n)),
876 Value::Float(n) => Ok(Value::Int(n.trunc() as i64)),
877 _ => Err(RuntimeError::new("trunc() requires number")),
878 });
879
880 define(interp, "fract", Some(1), |_, args| match &args[0] {
881 Value::Int(_) => Ok(Value::Float(0.0)),
882 Value::Float(n) => Ok(Value::Float(n.fract())),
883 _ => Err(RuntimeError::new("fract() requires number")),
884 });
885
886 define(interp, "min", Some(2), |_, args| {
888 match (&args[0], &args[1]) {
889 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
890 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
891 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).min(*b))),
892 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.min(*b as f64))),
893 _ => Err(RuntimeError::new("min() requires numbers")),
894 }
895 });
896
897 define(interp, "max", Some(2), |_, args| {
898 match (&args[0], &args[1]) {
899 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
900 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
901 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).max(*b))),
902 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.max(*b as f64))),
903 _ => Err(RuntimeError::new("max() requires numbers")),
904 }
905 });
906
907 define(interp, "clamp", Some(3), |_, args| {
908 match (&args[0], &args[1], &args[2]) {
909 (Value::Int(val), Value::Int(min), Value::Int(max)) => {
910 Ok(Value::Int(*val.max(min).min(max)))
911 }
912 (Value::Float(val), Value::Float(min), Value::Float(max)) => {
913 Ok(Value::Float(val.max(*min).min(*max)))
914 }
915 _ => Err(RuntimeError::new("clamp() requires matching number types")),
916 }
917 });
918
919 define(interp, "sign", Some(1), |_, args| match &args[0] {
921 Value::Int(n) => Ok(Value::Int(n.signum())),
922 Value::Float(n) => Ok(Value::Float(if *n > 0.0 {
923 1.0
924 } else if *n < 0.0 {
925 -1.0
926 } else {
927 0.0
928 })),
929 _ => Err(RuntimeError::new("sign() requires number")),
930 });
931
932 define(interp, "PI", Some(0), |_, _| {
934 Ok(Value::Float(std::f64::consts::PI))
935 });
936 define(interp, "E", Some(0), |_, _| {
937 Ok(Value::Float(std::f64::consts::E))
938 });
939 define(interp, "TAU", Some(0), |_, _| {
940 Ok(Value::Float(std::f64::consts::TAU))
941 });
942 define(interp, "PHI", Some(0), |_, _| {
943 Ok(Value::Float(1.618033988749895))
944 }); define(interp, "gcd", Some(2), |_, args| {
948 match (&args[0], &args[1]) {
949 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(gcd(*a, *b))),
950 _ => Err(RuntimeError::new("gcd() requires integers")),
951 }
952 });
953
954 define(interp, "lcm", Some(2), |_, args| {
955 match (&args[0], &args[1]) {
956 (Value::Int(a), Value::Int(b)) => {
957 let g = gcd(*a, *b);
958 Ok(Value::Int((a * b).abs() / g))
959 }
960 _ => Err(RuntimeError::new("lcm() requires integers")),
961 }
962 });
963
964 define(interp, "factorial", Some(1), |_, args| match &args[0] {
966 Value::Int(n) if *n >= 0 => {
967 let mut result: i64 = 1;
968 for i in 2..=(*n as u64) {
969 result = result.saturating_mul(i as i64);
970 }
971 Ok(Value::Int(result))
972 }
973 Value::Int(_) => Err(RuntimeError::new(
974 "factorial() requires non-negative integer",
975 )),
976 _ => Err(RuntimeError::new("factorial() requires integer")),
977 });
978
979 define(interp, "is_nan", Some(1), |_, args| match &args[0] {
981 Value::Float(n) => Ok(Value::Bool(n.is_nan())),
982 Value::Int(_) => Ok(Value::Bool(false)),
983 _ => Err(RuntimeError::new("is_nan() requires number")),
984 });
985
986 define(interp, "is_infinite", Some(1), |_, args| match &args[0] {
987 Value::Float(n) => Ok(Value::Bool(n.is_infinite())),
988 Value::Int(_) => Ok(Value::Bool(false)),
989 Value::Infinity => Ok(Value::Bool(true)),
990 _ => Err(RuntimeError::new("is_infinite() requires number")),
991 });
992
993 define(interp, "is_finite", Some(1), |_, args| match &args[0] {
994 Value::Float(n) => Ok(Value::Bool(n.is_finite())),
995 Value::Int(_) => Ok(Value::Bool(true)),
996 Value::Infinity => Ok(Value::Bool(false)),
997 _ => Err(RuntimeError::new("is_finite() requires number")),
998 });
999
1000 define(interp, "is_even", Some(1), |_, args| match &args[0] {
1001 Value::Int(n) => Ok(Value::Bool(n % 2 == 0)),
1002 _ => Err(RuntimeError::new("is_even() requires integer")),
1003 });
1004
1005 define(interp, "is_odd", Some(1), |_, args| match &args[0] {
1006 Value::Int(n) => Ok(Value::Bool(n % 2 != 0)),
1007 _ => Err(RuntimeError::new("is_odd() requires integer")),
1008 });
1009
1010 define(interp, "is_prime", Some(1), |_, args| match &args[0] {
1011 Value::Int(n) => Ok(Value::Bool(is_prime(*n))),
1012 _ => Err(RuntimeError::new("is_prime() requires integer")),
1013 });
1014}
1015
1016fn gcd(mut a: i64, mut b: i64) -> i64 {
1017 a = a.abs();
1018 b = b.abs();
1019 while b != 0 {
1020 let t = b;
1021 b = a % b;
1022 a = t;
1023 }
1024 a
1025}
1026
1027fn is_prime(n: i64) -> bool {
1028 if n < 2 {
1029 return false;
1030 }
1031 if n == 2 {
1032 return true;
1033 }
1034 if n % 2 == 0 {
1035 return false;
1036 }
1037 let sqrt = (n as f64).sqrt() as i64;
1038 for i in (3..=sqrt).step_by(2) {
1039 if n % i == 0 {
1040 return false;
1041 }
1042 }
1043 true
1044}
1045
1046fn register_collections(interp: &mut Interpreter) {
1051 define(interp, "len", Some(1), |_, args| match &args[0] {
1053 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
1054 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
1055 Value::Tuple(t) => Ok(Value::Int(t.len() as i64)),
1056 Value::Map(m) => Ok(Value::Int(m.borrow().len() as i64)),
1057 Value::Set(s) => Ok(Value::Int(s.borrow().len() as i64)),
1058 _ => Err(RuntimeError::new(
1059 "len() requires array, string, tuple, map, or set",
1060 )),
1061 });
1062
1063 define(interp, "is_empty", Some(1), |_, args| match &args[0] {
1064 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
1065 Value::String(s) => Ok(Value::Bool(s.is_empty())),
1066 Value::Tuple(t) => Ok(Value::Bool(t.is_empty())),
1067 Value::Map(m) => Ok(Value::Bool(m.borrow().is_empty())),
1068 Value::Set(s) => Ok(Value::Bool(s.borrow().is_empty())),
1069 _ => Err(RuntimeError::new("is_empty() requires collection")),
1070 });
1071
1072 define(interp, "push", Some(2), |_, args| match &args[0] {
1074 Value::Array(arr) => {
1075 arr.borrow_mut().push(args[1].clone());
1076 Ok(Value::Null)
1077 }
1078 _ => Err(RuntimeError::new("push() requires array")),
1079 });
1080
1081 define(interp, "pop", Some(1), |_, args| match &args[0] {
1082 Value::Array(arr) => arr
1083 .borrow_mut()
1084 .pop()
1085 .ok_or_else(|| RuntimeError::new("pop() on empty array")),
1086 _ => Err(RuntimeError::new("pop() requires array")),
1087 });
1088
1089 define(interp, "first", Some(1), |_, args| match &args[0] {
1090 Value::Array(arr) => arr
1091 .borrow()
1092 .first()
1093 .cloned()
1094 .ok_or_else(|| RuntimeError::new("first() on empty array")),
1095 Value::Tuple(t) => t
1096 .first()
1097 .cloned()
1098 .ok_or_else(|| RuntimeError::new("first() on empty tuple")),
1099 _ => Err(RuntimeError::new("first() requires array or tuple")),
1100 });
1101
1102 define(interp, "last", Some(1), |_, args| match &args[0] {
1103 Value::Array(arr) => arr
1104 .borrow()
1105 .last()
1106 .cloned()
1107 .ok_or_else(|| RuntimeError::new("last() on empty array")),
1108 Value::Tuple(t) => t
1109 .last()
1110 .cloned()
1111 .ok_or_else(|| RuntimeError::new("last() on empty tuple")),
1112 _ => Err(RuntimeError::new("last() requires array or tuple")),
1113 });
1114
1115 define(interp, "middle", Some(1), |_, args| match &args[0] {
1117 Value::Array(arr) => {
1118 let arr = arr.borrow();
1119 if arr.is_empty() {
1120 return Err(RuntimeError::new("middle() on empty array"));
1121 }
1122 let mid = arr.len() / 2;
1123 Ok(arr[mid].clone())
1124 }
1125 Value::Tuple(t) => {
1126 if t.is_empty() {
1127 return Err(RuntimeError::new("middle() on empty tuple"));
1128 }
1129 let mid = t.len() / 2;
1130 Ok(t[mid].clone())
1131 }
1132 _ => Err(RuntimeError::new("middle() requires array or tuple")),
1133 });
1134
1135 define(interp, "choice", Some(1), |_, args| {
1137 use std::time::{SystemTime, UNIX_EPOCH};
1138 match &args[0] {
1139 Value::Array(arr) => {
1140 let arr = arr.borrow();
1141 if arr.is_empty() {
1142 return Err(RuntimeError::new("choice() on empty array"));
1143 }
1144 let seed = SystemTime::now()
1145 .duration_since(UNIX_EPOCH)
1146 .unwrap_or(std::time::Duration::ZERO)
1147 .as_nanos() as u64;
1148 let idx = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize
1149 % arr.len();
1150 Ok(arr[idx].clone())
1151 }
1152 Value::Tuple(t) => {
1153 if t.is_empty() {
1154 return Err(RuntimeError::new("choice() on empty tuple"));
1155 }
1156 let seed = SystemTime::now()
1157 .duration_since(UNIX_EPOCH)
1158 .unwrap_or(std::time::Duration::ZERO)
1159 .as_nanos() as u64;
1160 let idx =
1161 ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % t.len();
1162 Ok(t[idx].clone())
1163 }
1164 _ => Err(RuntimeError::new("choice() requires array or tuple")),
1165 }
1166 });
1167
1168 define(interp, "nth", Some(2), |_, args| {
1170 let n = match &args[1] {
1171 Value::Int(i) => *i,
1172 _ => return Err(RuntimeError::new("nth() index must be integer")),
1173 };
1174 match &args[0] {
1175 Value::Array(arr) => {
1176 let arr = arr.borrow();
1177 if n < 0 || n as usize >= arr.len() {
1178 return Err(RuntimeError::new("nth() index out of bounds"));
1179 }
1180 Ok(arr[n as usize].clone())
1181 }
1182 Value::Tuple(t) => {
1183 if n < 0 || n as usize >= t.len() {
1184 return Err(RuntimeError::new("nth() index out of bounds"));
1185 }
1186 Ok(t[n as usize].clone())
1187 }
1188 _ => Err(RuntimeError::new("nth() requires array or tuple")),
1189 }
1190 });
1191
1192 define(interp, "next", Some(1), |_, args| match &args[0] {
1194 Value::Array(arr) => {
1195 let mut arr = arr.borrow_mut();
1196 if arr.is_empty() {
1197 return Err(RuntimeError::new("next() on empty array"));
1198 }
1199 Ok(arr.remove(0))
1200 }
1201 _ => Err(RuntimeError::new("next() requires array")),
1202 });
1203
1204 define(interp, "peek", Some(1), |_, args| match &args[0] {
1206 Value::Array(arr) => arr
1207 .borrow()
1208 .first()
1209 .cloned()
1210 .ok_or_else(|| RuntimeError::new("peek() on empty array")),
1211 _ => Err(RuntimeError::new("peek() requires array")),
1212 });
1213
1214 define(interp, "get", Some(2), |_, args| {
1215 let index = match &args[1] {
1216 Value::Int(i) => *i,
1217 _ => return Err(RuntimeError::new("get() index must be integer")),
1218 };
1219 match &args[0] {
1220 Value::Array(arr) => {
1221 let arr = arr.borrow();
1222 let idx = if index < 0 {
1223 arr.len() as i64 + index
1224 } else {
1225 index
1226 } as usize;
1227 arr.get(idx)
1228 .cloned()
1229 .ok_or_else(|| RuntimeError::new("index out of bounds"))
1230 }
1231 Value::Tuple(t) => {
1232 let idx = if index < 0 {
1233 t.len() as i64 + index
1234 } else {
1235 index
1236 } as usize;
1237 t.get(idx)
1238 .cloned()
1239 .ok_or_else(|| RuntimeError::new("index out of bounds"))
1240 }
1241 _ => Err(RuntimeError::new("get() requires array or tuple")),
1242 }
1243 });
1244
1245 define(interp, "set", Some(3), |_, args| {
1246 let index = match &args[1] {
1247 Value::Int(i) => *i as usize,
1248 _ => return Err(RuntimeError::new("set() index must be integer")),
1249 };
1250 match &args[0] {
1251 Value::Array(arr) => {
1252 let mut arr = arr.borrow_mut();
1253 if index >= arr.len() {
1254 return Err(RuntimeError::new("index out of bounds"));
1255 }
1256 arr[index] = args[2].clone();
1257 Ok(Value::Null)
1258 }
1259 _ => Err(RuntimeError::new("set() requires array")),
1260 }
1261 });
1262
1263 define(interp, "insert", Some(3), |_, args| {
1264 let index = match &args[1] {
1265 Value::Int(i) => *i as usize,
1266 _ => return Err(RuntimeError::new("insert() index must be integer")),
1267 };
1268 match &args[0] {
1269 Value::Array(arr) => {
1270 let mut arr = arr.borrow_mut();
1271 if index > arr.len() {
1272 return Err(RuntimeError::new("index out of bounds"));
1273 }
1274 arr.insert(index, args[2].clone());
1275 Ok(Value::Null)
1276 }
1277 _ => Err(RuntimeError::new("insert() requires array")),
1278 }
1279 });
1280
1281 define(interp, "remove", Some(2), |_, args| {
1282 let index = match &args[1] {
1283 Value::Int(i) => *i as usize,
1284 _ => return Err(RuntimeError::new("remove() index must be integer")),
1285 };
1286 match &args[0] {
1287 Value::Array(arr) => {
1288 let mut arr = arr.borrow_mut();
1289 if index >= arr.len() {
1290 return Err(RuntimeError::new("index out of bounds"));
1291 }
1292 Ok(arr.remove(index))
1293 }
1294 _ => Err(RuntimeError::new("remove() requires array")),
1295 }
1296 });
1297
1298 define(interp, "clear", Some(1), |_, args| match &args[0] {
1299 Value::Array(arr) => {
1300 arr.borrow_mut().clear();
1301 Ok(Value::Null)
1302 }
1303 _ => Err(RuntimeError::new("clear() requires array")),
1304 });
1305
1306 define(interp, "contains", Some(2), |_, args| match &args[0] {
1308 Value::Array(arr) => Ok(Value::Bool(
1309 arr.borrow().iter().any(|v| values_equal(v, &args[1])),
1310 )),
1311 Value::String(s) => match &args[1] {
1312 Value::String(sub) => Ok(Value::Bool(s.contains(sub.as_str()))),
1313 Value::Char(c) => Ok(Value::Bool(s.contains(*c))),
1314 _ => Err(RuntimeError::new(
1315 "string contains() requires string or char",
1316 )),
1317 },
1318 _ => Err(RuntimeError::new("contains() requires array or string")),
1319 });
1320
1321 define(interp, "index_of", Some(2), |_, args| match &args[0] {
1322 Value::Array(arr) => {
1323 let idx = arr.borrow().iter().position(|v| values_equal(v, &args[1]));
1324 match idx {
1325 Some(i) => Ok(Value::Int(i as i64)),
1326 None => Ok(Value::Int(-1)),
1327 }
1328 }
1329 Value::String(s) => match &args[1] {
1330 Value::String(sub) => match s.find(sub.as_str()) {
1331 Some(i) => Ok(Value::Int(i as i64)),
1332 None => Ok(Value::Int(-1)),
1333 },
1334 Value::Char(c) => match s.find(*c) {
1335 Some(i) => Ok(Value::Int(i as i64)),
1336 None => Ok(Value::Int(-1)),
1337 },
1338 _ => Err(RuntimeError::new(
1339 "string index_of() requires string or char",
1340 )),
1341 },
1342 _ => Err(RuntimeError::new("index_of() requires array or string")),
1343 });
1344
1345 define(interp, "reverse", Some(1), |_, args| match &args[0] {
1347 Value::Array(arr) => {
1348 let mut reversed: Vec<Value> = arr.borrow().clone();
1349 reversed.reverse();
1350 Ok(Value::Array(Rc::new(RefCell::new(reversed))))
1351 }
1352 Value::String(s) => {
1353 let reversed: String = s.chars().rev().collect();
1354 Ok(Value::String(Rc::new(reversed)))
1355 }
1356 _ => Err(RuntimeError::new("reverse() requires array or string")),
1357 });
1358
1359 define(interp, "sort", Some(1), |_, args| match &args[0] {
1360 Value::Array(arr) => {
1361 let mut sorted: Vec<Value> = arr.borrow().clone();
1362 sorted.sort_by(compare_values);
1363 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1364 }
1365 _ => Err(RuntimeError::new("sort() requires array")),
1366 });
1367
1368 define(interp, "sort_desc", Some(1), |_, args| match &args[0] {
1369 Value::Array(arr) => {
1370 let mut sorted: Vec<Value> = arr.borrow().clone();
1371 sorted.sort_by(|a, b| compare_values(b, a));
1372 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1373 }
1374 _ => Err(RuntimeError::new("sort_desc() requires array")),
1375 });
1376
1377 define(interp, "unique", Some(1), |_, args| match &args[0] {
1378 Value::Array(arr) => {
1379 let arr = arr.borrow();
1380 let mut seen = Vec::new();
1381 let unique: Vec<Value> = arr
1382 .iter()
1383 .filter(|v| {
1384 if seen.iter().any(|s| values_equal(s, v)) {
1385 false
1386 } else {
1387 seen.push((*v).clone());
1388 true
1389 }
1390 })
1391 .cloned()
1392 .collect();
1393 Ok(Value::Array(Rc::new(RefCell::new(unique))))
1394 }
1395 _ => Err(RuntimeError::new("unique() requires array")),
1396 });
1397
1398 define(interp, "flatten", Some(1), |_, args| match &args[0] {
1399 Value::Array(arr) => {
1400 let mut flattened = Vec::new();
1401 for item in arr.borrow().iter() {
1402 match item {
1403 Value::Array(inner) => flattened.extend(inner.borrow().clone()),
1404 other => flattened.push(other.clone()),
1405 }
1406 }
1407 Ok(Value::Array(Rc::new(RefCell::new(flattened))))
1408 }
1409 _ => Err(RuntimeError::new("flatten() requires array")),
1410 });
1411
1412 define(interp, "concat", Some(2), |_, args| {
1414 match (&args[0], &args[1]) {
1415 (Value::Array(a), Value::Array(b)) => {
1416 let mut result = a.borrow().clone();
1417 result.extend(b.borrow().clone());
1418 Ok(Value::Array(Rc::new(RefCell::new(result))))
1419 }
1420 (Value::String(a), Value::String(b)) => {
1421 Ok(Value::String(Rc::new(format!("{}{}", a, b))))
1422 }
1423 _ => Err(RuntimeError::new(
1424 "concat() requires two arrays or two strings",
1425 )),
1426 }
1427 });
1428
1429 define(interp, "zip", Some(2), |_, args| {
1430 match (&args[0], &args[1]) {
1431 (Value::Array(a), Value::Array(b)) => {
1432 let a = a.borrow();
1433 let b = b.borrow();
1434 let zipped: Vec<Value> = a
1435 .iter()
1436 .zip(b.iter())
1437 .map(|(x, y)| Value::Tuple(Rc::new(vec![x.clone(), y.clone()])))
1438 .collect();
1439 Ok(Value::Array(Rc::new(RefCell::new(zipped))))
1440 }
1441 _ => Err(RuntimeError::new("zip() requires two arrays")),
1442 }
1443 });
1444
1445 define(interp, "enumerate", Some(1), |_, args| match &args[0] {
1446 Value::Array(arr) => {
1447 let enumerated: Vec<Value> = arr
1448 .borrow()
1449 .iter()
1450 .enumerate()
1451 .map(|(i, v)| Value::Tuple(Rc::new(vec![Value::Int(i as i64), v.clone()])))
1452 .collect();
1453 Ok(Value::Array(Rc::new(RefCell::new(enumerated))))
1454 }
1455 _ => Err(RuntimeError::new("enumerate() requires array")),
1456 });
1457
1458 define(interp, "zip_with", Some(3), |_, args| {
1461 let mode = match &args[2] {
1462 Value::String(s) => s.as_str(),
1463 _ => return Err(RuntimeError::new("zip_with() mode must be string")),
1464 };
1465 match (&args[0], &args[1]) {
1466 (Value::Array(a), Value::Array(b)) => {
1467 let a = a.borrow();
1468 let b = b.borrow();
1469 let result: Result<Vec<Value>, RuntimeError> = a
1470 .iter()
1471 .zip(b.iter())
1472 .map(|(x, y)| match (x, y, mode) {
1473 (Value::Int(a), Value::Int(b), "add") => Ok(Value::Int(a + b)),
1474 (Value::Int(a), Value::Int(b), "sub") => Ok(Value::Int(a - b)),
1475 (Value::Int(a), Value::Int(b), "mul") => Ok(Value::Int(a * b)),
1476 (Value::Float(a), Value::Float(b), "add") => Ok(Value::Float(a + b)),
1477 (Value::Float(a), Value::Float(b), "sub") => Ok(Value::Float(a - b)),
1478 (Value::Float(a), Value::Float(b), "mul") => Ok(Value::Float(a * b)),
1479 (_, _, "pair") => Ok(Value::Tuple(Rc::new(vec![x.clone(), y.clone()]))),
1480 _ => Err(RuntimeError::new("zip_with() incompatible types or mode")),
1481 })
1482 .collect();
1483 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1484 }
1485 _ => Err(RuntimeError::new("zip_with() requires two arrays")),
1486 }
1487 });
1488
1489 define(interp, "supremum", Some(2), |_, args| {
1491 match (&args[0], &args[1]) {
1492 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1493 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1494 (Value::Array(a), Value::Array(b)) => {
1495 let a = a.borrow();
1497 let b = b.borrow();
1498 let result: Result<Vec<Value>, RuntimeError> = a
1499 .iter()
1500 .zip(b.iter())
1501 .map(|(x, y)| match (x, y) {
1502 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1503 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1504 _ => Err(RuntimeError::new("supremum() requires numeric arrays")),
1505 })
1506 .collect();
1507 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1508 }
1509 _ => Err(RuntimeError::new(
1510 "supremum() requires numeric values or arrays",
1511 )),
1512 }
1513 });
1514
1515 define(interp, "infimum", Some(2), |_, args| {
1517 match (&args[0], &args[1]) {
1518 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1519 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1520 (Value::Array(a), Value::Array(b)) => {
1521 let a = a.borrow();
1523 let b = b.borrow();
1524 let result: Result<Vec<Value>, RuntimeError> = a
1525 .iter()
1526 .zip(b.iter())
1527 .map(|(x, y)| match (x, y) {
1528 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1529 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1530 _ => Err(RuntimeError::new("infimum() requires numeric arrays")),
1531 })
1532 .collect();
1533 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1534 }
1535 _ => Err(RuntimeError::new(
1536 "infimum() requires numeric values or arrays",
1537 )),
1538 }
1539 });
1540
1541 define(interp, "slice", Some(3), |_, args| {
1543 let start = match &args[1] {
1544 Value::Int(i) => *i as usize,
1545 _ => return Err(RuntimeError::new("slice() start must be integer")),
1546 };
1547 let end = match &args[2] {
1548 Value::Int(i) => *i as usize,
1549 _ => return Err(RuntimeError::new("slice() end must be integer")),
1550 };
1551 match &args[0] {
1552 Value::Array(arr) => {
1553 let arr = arr.borrow();
1554 let end = end.min(arr.len());
1555 let sliced: Vec<Value> = arr[start..end].to_vec();
1556 Ok(Value::Array(Rc::new(RefCell::new(sliced))))
1557 }
1558 Value::String(s) => {
1559 let chars: Vec<char> = s.chars().collect();
1560 let end = end.min(chars.len());
1561 let sliced: String = chars[start..end].iter().collect();
1562 Ok(Value::String(Rc::new(sliced)))
1563 }
1564 _ => Err(RuntimeError::new("slice() requires array or string")),
1565 }
1566 });
1567
1568 define(interp, "take", Some(2), |_, args| {
1569 let n = match &args[1] {
1570 Value::Int(i) => *i as usize,
1571 _ => return Err(RuntimeError::new("take() n must be integer")),
1572 };
1573 match &args[0] {
1574 Value::Array(arr) => {
1575 let taken: Vec<Value> = arr.borrow().iter().take(n).cloned().collect();
1576 Ok(Value::Array(Rc::new(RefCell::new(taken))))
1577 }
1578 _ => Err(RuntimeError::new("take() requires array")),
1579 }
1580 });
1581
1582 define(interp, "skip", Some(2), |_, args| {
1583 let n = match &args[1] {
1584 Value::Int(i) => *i as usize,
1585 _ => return Err(RuntimeError::new("skip() n must be integer")),
1586 };
1587 match &args[0] {
1588 Value::Array(arr) => {
1589 let skipped: Vec<Value> = arr.borrow().iter().skip(n).cloned().collect();
1590 Ok(Value::Array(Rc::new(RefCell::new(skipped))))
1591 }
1592 _ => Err(RuntimeError::new("skip() requires array")),
1593 }
1594 });
1595
1596 define(interp, "chunk", Some(2), |_, args| {
1597 let size = match &args[1] {
1598 Value::Int(i) if *i > 0 => *i as usize,
1599 _ => return Err(RuntimeError::new("chunk() size must be positive integer")),
1600 };
1601 match &args[0] {
1602 Value::Array(arr) => {
1603 let chunks: Vec<Value> = arr
1604 .borrow()
1605 .chunks(size)
1606 .map(|c| Value::Array(Rc::new(RefCell::new(c.to_vec()))))
1607 .collect();
1608 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
1609 }
1610 _ => Err(RuntimeError::new("chunk() requires array")),
1611 }
1612 });
1613
1614 define(interp, "range", Some(2), |_, args| {
1616 let start = match &args[0] {
1617 Value::Int(n) => *n,
1618 _ => return Err(RuntimeError::new("range() requires integers")),
1619 };
1620 let end = match &args[1] {
1621 Value::Int(n) => *n,
1622 _ => return Err(RuntimeError::new("range() requires integers")),
1623 };
1624 let values: Vec<Value> = (start..end).map(Value::Int).collect();
1625 Ok(Value::Array(Rc::new(RefCell::new(values))))
1626 });
1627
1628 define(interp, "range_inclusive", Some(2), |_, args| {
1629 let start = match &args[0] {
1630 Value::Int(n) => *n,
1631 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1632 };
1633 let end = match &args[1] {
1634 Value::Int(n) => *n,
1635 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1636 };
1637 let values: Vec<Value> = (start..=end).map(Value::Int).collect();
1638 Ok(Value::Array(Rc::new(RefCell::new(values))))
1639 });
1640
1641 define(interp, "repeat", Some(2), |_, args| {
1642 let n = match &args[1] {
1643 Value::Int(i) if *i >= 0 => *i as usize,
1644 _ => {
1645 return Err(RuntimeError::new(
1646 "repeat() count must be non-negative integer",
1647 ))
1648 }
1649 };
1650 let repeated: Vec<Value> = std::iter::repeat(args[0].clone()).take(n).collect();
1651 Ok(Value::Array(Rc::new(RefCell::new(repeated))))
1652 });
1653
1654 define(interp, "map_new", Some(0), |_, _| {
1660 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
1661 });
1662
1663 define(interp, "map_get", Some(2), |_, args| {
1665 let key = match &args[1] {
1666 Value::String(s) => s.to_string(),
1667 _ => return Err(RuntimeError::new("map_get() key must be string")),
1668 };
1669 match &args[0] {
1670 Value::Map(map) => Ok(map.borrow().get(&key).cloned().unwrap_or(Value::Null)),
1671 _ => Err(RuntimeError::new("map_get() requires map")),
1672 }
1673 });
1674
1675 define(interp, "map_set", Some(3), |_, args| {
1677 let key = match &args[1] {
1678 Value::String(s) => s.to_string(),
1679 _ => return Err(RuntimeError::new("map_set() key must be string")),
1680 };
1681 match &args[0] {
1682 Value::Map(map) => {
1683 map.borrow_mut().insert(key, args[2].clone());
1684 Ok(Value::Null)
1685 }
1686 _ => Err(RuntimeError::new("map_set() requires map")),
1687 }
1688 });
1689
1690 define(interp, "map_has", Some(2), |_, args| {
1692 let key = match &args[1] {
1693 Value::String(s) => s.to_string(),
1694 _ => return Err(RuntimeError::new("map_has() key must be string")),
1695 };
1696 match &args[0] {
1697 Value::Map(map) => Ok(Value::Bool(map.borrow().contains_key(&key))),
1698 _ => Err(RuntimeError::new("map_has() requires map")),
1699 }
1700 });
1701
1702 define(interp, "map_remove", Some(2), |_, args| {
1704 let key = match &args[1] {
1705 Value::String(s) => s.to_string(),
1706 _ => return Err(RuntimeError::new("map_remove() key must be string")),
1707 };
1708 match &args[0] {
1709 Value::Map(map) => Ok(map.borrow_mut().remove(&key).unwrap_or(Value::Null)),
1710 _ => Err(RuntimeError::new("map_remove() requires map")),
1711 }
1712 });
1713
1714 define(interp, "map_keys", Some(1), |_, args| match &args[0] {
1716 Value::Map(map) => {
1717 let keys: Vec<Value> = map
1718 .borrow()
1719 .keys()
1720 .map(|k| Value::String(Rc::new(k.clone())))
1721 .collect();
1722 Ok(Value::Array(Rc::new(RefCell::new(keys))))
1723 }
1724 _ => Err(RuntimeError::new("map_keys() requires map")),
1725 });
1726
1727 define(interp, "map_values", Some(1), |_, args| match &args[0] {
1729 Value::Map(map) => {
1730 let values: Vec<Value> = map.borrow().values().cloned().collect();
1731 Ok(Value::Array(Rc::new(RefCell::new(values))))
1732 }
1733 _ => Err(RuntimeError::new("map_values() requires map")),
1734 });
1735
1736 define(interp, "map_len", Some(1), |_, args| match &args[0] {
1738 Value::Map(map) => Ok(Value::Int(map.borrow().len() as i64)),
1739 _ => Err(RuntimeError::new("map_len() requires map")),
1740 });
1741
1742 define(interp, "map_clear", Some(1), |_, args| match &args[0] {
1744 Value::Map(map) => {
1745 map.borrow_mut().clear();
1746 Ok(Value::Null)
1747 }
1748 _ => Err(RuntimeError::new("map_clear() requires map")),
1749 });
1750
1751 define(interp, "set_new", Some(0), |_, _| {
1757 Ok(Value::Set(Rc::new(RefCell::new(
1758 std::collections::HashSet::new(),
1759 ))))
1760 });
1761
1762 define(interp, "set_add", Some(2), |_, args| {
1764 let item = match &args[1] {
1765 Value::String(s) => s.to_string(),
1766 _ => return Err(RuntimeError::new("set_add() item must be string")),
1767 };
1768 match &args[0] {
1769 Value::Set(set) => {
1770 set.borrow_mut().insert(item);
1771 Ok(Value::Null)
1772 }
1773 _ => Err(RuntimeError::new("set_add() requires set")),
1774 }
1775 });
1776
1777 define(interp, "set_has", Some(2), |_, args| {
1779 let item = match &args[1] {
1780 Value::String(s) => s.to_string(),
1781 _ => return Err(RuntimeError::new("set_has() item must be string")),
1782 };
1783 match &args[0] {
1784 Value::Set(set) => Ok(Value::Bool(set.borrow().contains(&item))),
1785 _ => Err(RuntimeError::new("set_has() requires set")),
1786 }
1787 });
1788
1789 define(interp, "set_remove", Some(2), |_, args| {
1791 let item = match &args[1] {
1792 Value::String(s) => s.to_string(),
1793 _ => return Err(RuntimeError::new("set_remove() item must be string")),
1794 };
1795 match &args[0] {
1796 Value::Set(set) => Ok(Value::Bool(set.borrow_mut().remove(&item))),
1797 _ => Err(RuntimeError::new("set_remove() requires set")),
1798 }
1799 });
1800
1801 define(interp, "set_to_array", Some(1), |_, args| match &args[0] {
1803 Value::Set(set) => {
1804 let items: Vec<Value> = set
1805 .borrow()
1806 .iter()
1807 .map(|s| Value::String(Rc::new(s.clone())))
1808 .collect();
1809 Ok(Value::Array(Rc::new(RefCell::new(items))))
1810 }
1811 _ => Err(RuntimeError::new("set_to_array() requires set")),
1812 });
1813
1814 define(interp, "set_len", Some(1), |_, args| match &args[0] {
1816 Value::Set(set) => Ok(Value::Int(set.borrow().len() as i64)),
1817 _ => Err(RuntimeError::new("set_len() requires set")),
1818 });
1819
1820 define(interp, "set_clear", Some(1), |_, args| match &args[0] {
1822 Value::Set(set) => {
1823 set.borrow_mut().clear();
1824 Ok(Value::Null)
1825 }
1826 _ => Err(RuntimeError::new("set_clear() requires set")),
1827 });
1828}
1829
1830fn values_equal(a: &Value, b: &Value) -> bool {
1831 match (a, b) {
1832 (Value::Null, Value::Null) => true,
1833 (Value::Bool(a), Value::Bool(b)) => a == b,
1834 (Value::Int(a), Value::Int(b)) => a == b,
1835 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
1836 (Value::String(a), Value::String(b)) => a == b,
1837 (Value::Char(a), Value::Char(b)) => a == b,
1838 (Value::Array(a), Value::Array(b)) => {
1839 let a = a.borrow();
1840 let b = b.borrow();
1841 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1842 }
1843 (Value::Tuple(a), Value::Tuple(b)) => {
1844 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1845 }
1846 _ => false,
1847 }
1848}
1849
1850fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
1851 match (a, b) {
1852 (Value::Int(a), Value::Int(b)) => a.cmp(b),
1853 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal),
1854 (Value::String(a), Value::String(b)) => a.cmp(b),
1855 (Value::Char(a), Value::Char(b)) => a.cmp(b),
1856 _ => std::cmp::Ordering::Equal,
1857 }
1858}
1859
1860fn register_string(interp: &mut Interpreter) {
1865 define(interp, "chars", Some(1), |_, args| match &args[0] {
1866 Value::String(s) => {
1867 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
1868 Ok(Value::Array(Rc::new(RefCell::new(chars))))
1869 }
1870 _ => Err(RuntimeError::new("chars() requires string")),
1871 });
1872
1873 define(interp, "bytes", Some(1), |_, args| match &args[0] {
1874 Value::String(s) => {
1875 let bytes: Vec<Value> = s.bytes().map(|b| Value::Int(b as i64)).collect();
1876 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
1877 }
1878 _ => Err(RuntimeError::new("bytes() requires string")),
1879 });
1880
1881 define(interp, "split", Some(2), |_, args| {
1882 match (&args[0], &args[1]) {
1883 (Value::String(s), Value::String(sep)) => {
1884 let parts: Vec<Value> = s
1885 .split(sep.as_str())
1886 .map(|p| Value::String(Rc::new(p.to_string())))
1887 .collect();
1888 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1889 }
1890 (Value::String(s), Value::Char(sep)) => {
1891 let parts: Vec<Value> = s
1892 .split(*sep)
1893 .map(|p| Value::String(Rc::new(p.to_string())))
1894 .collect();
1895 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1896 }
1897 _ => Err(RuntimeError::new("split() requires string and separator")),
1898 }
1899 });
1900
1901 define(interp, "join", Some(2), |_, args| {
1902 match (&args[0], &args[1]) {
1903 (Value::Array(arr), Value::String(sep)) => {
1904 let parts: Vec<String> = arr.borrow().iter().map(|v| format!("{}", v)).collect();
1905 Ok(Value::String(Rc::new(parts.join(sep.as_str()))))
1906 }
1907 _ => Err(RuntimeError::new(
1908 "join() requires array and separator string",
1909 )),
1910 }
1911 });
1912
1913 define(interp, "trim", Some(1), |_, args| match &args[0] {
1914 Value::String(s) => Ok(Value::String(Rc::new(s.trim().to_string()))),
1915 _ => Err(RuntimeError::new("trim() requires string")),
1916 });
1917
1918 define(interp, "trim_start", Some(1), |_, args| match &args[0] {
1919 Value::String(s) => Ok(Value::String(Rc::new(s.trim_start().to_string()))),
1920 _ => Err(RuntimeError::new("trim_start() requires string")),
1921 });
1922
1923 define(interp, "trim_end", Some(1), |_, args| match &args[0] {
1924 Value::String(s) => Ok(Value::String(Rc::new(s.trim_end().to_string()))),
1925 _ => Err(RuntimeError::new("trim_end() requires string")),
1926 });
1927
1928 define(interp, "upper", Some(1), |_, args| match &args[0] {
1929 Value::String(s) => Ok(Value::String(Rc::new(s.to_uppercase()))),
1930 Value::Char(c) => Ok(Value::Char(c.to_uppercase().next().unwrap_or(*c))),
1931 _ => Err(RuntimeError::new("upper() requires string or char")),
1932 });
1933
1934 define(interp, "lower", Some(1), |_, args| match &args[0] {
1935 Value::String(s) => Ok(Value::String(Rc::new(s.to_lowercase()))),
1936 Value::Char(c) => Ok(Value::Char(c.to_lowercase().next().unwrap_or(*c))),
1937 _ => Err(RuntimeError::new("lower() requires string or char")),
1938 });
1939
1940 define(interp, "capitalize", Some(1), |_, args| match &args[0] {
1941 Value::String(s) => {
1942 let mut chars = s.chars();
1943 let capitalized = match chars.next() {
1944 None => String::new(),
1945 Some(c) => c.to_uppercase().chain(chars).collect(),
1946 };
1947 Ok(Value::String(Rc::new(capitalized)))
1948 }
1949 _ => Err(RuntimeError::new("capitalize() requires string")),
1950 });
1951
1952 define(interp, "replace", Some(3), |_, args| {
1953 match (&args[0], &args[1], &args[2]) {
1954 (Value::String(s), Value::String(from), Value::String(to)) => Ok(Value::String(
1955 Rc::new(s.replace(from.as_str(), to.as_str())),
1956 )),
1957 _ => Err(RuntimeError::new("replace() requires three strings")),
1958 }
1959 });
1960
1961 define(interp, "starts_with", Some(2), |_, args| {
1962 match (&args[0], &args[1]) {
1963 (Value::String(s), Value::String(prefix)) => {
1964 Ok(Value::Bool(s.starts_with(prefix.as_str())))
1965 }
1966 _ => Err(RuntimeError::new("starts_with() requires two strings")),
1967 }
1968 });
1969
1970 define(interp, "ends_with", Some(2), |_, args| {
1971 match (&args[0], &args[1]) {
1972 (Value::String(s), Value::String(suffix)) => {
1973 Ok(Value::Bool(s.ends_with(suffix.as_str())))
1974 }
1975 _ => Err(RuntimeError::new("ends_with() requires two strings")),
1976 }
1977 });
1978
1979 define(interp, "repeat_str", Some(2), |_, args| {
1980 match (&args[0], &args[1]) {
1981 (Value::String(s), Value::Int(n)) if *n >= 0 => {
1982 Ok(Value::String(Rc::new(s.repeat(*n as usize))))
1983 }
1984 _ => Err(RuntimeError::new(
1985 "repeat_str() requires string and non-negative integer",
1986 )),
1987 }
1988 });
1989
1990 define(interp, "pad_left", Some(3), |_, args| {
1991 match (&args[0], &args[1], &args[2]) {
1992 (Value::String(s), Value::Int(width), Value::Char(c)) => {
1993 let width = *width as usize;
1994 if s.len() >= width {
1995 Ok(Value::String(s.clone()))
1996 } else {
1997 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
1998 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
1999 }
2000 }
2001 _ => Err(RuntimeError::new(
2002 "pad_left() requires string, width, and char",
2003 )),
2004 }
2005 });
2006
2007 define(interp, "pad_right", Some(3), |_, args| {
2008 match (&args[0], &args[1], &args[2]) {
2009 (Value::String(s), Value::Int(width), Value::Char(c)) => {
2010 let width = *width as usize;
2011 if s.len() >= width {
2012 Ok(Value::String(s.clone()))
2013 } else {
2014 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
2015 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
2016 }
2017 }
2018 _ => Err(RuntimeError::new(
2019 "pad_right() requires string, width, and char",
2020 )),
2021 }
2022 });
2023
2024 define(interp, "lines", Some(1), |_, args| match &args[0] {
2025 Value::String(s) => {
2026 let lines: Vec<Value> = s
2027 .lines()
2028 .map(|l| Value::String(Rc::new(l.to_string())))
2029 .collect();
2030 Ok(Value::Array(Rc::new(RefCell::new(lines))))
2031 }
2032 _ => Err(RuntimeError::new("lines() requires string")),
2033 });
2034
2035 define(interp, "words", Some(1), |_, args| match &args[0] {
2036 Value::String(s) => {
2037 let words: Vec<Value> = s
2038 .split_whitespace()
2039 .map(|w| Value::String(Rc::new(w.to_string())))
2040 .collect();
2041 Ok(Value::Array(Rc::new(RefCell::new(words))))
2042 }
2043 _ => Err(RuntimeError::new("words() requires string")),
2044 });
2045
2046 define(interp, "is_alpha", Some(1), |_, args| match &args[0] {
2047 Value::String(s) => Ok(Value::Bool(
2048 !s.is_empty() && s.chars().all(|c| c.is_alphabetic()),
2049 )),
2050 Value::Char(c) => Ok(Value::Bool(c.is_alphabetic())),
2051 _ => Err(RuntimeError::new("is_alpha() requires string or char")),
2052 });
2053
2054 define(interp, "is_digit", Some(1), |_, args| match &args[0] {
2055 Value::String(s) => Ok(Value::Bool(
2056 !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()),
2057 )),
2058 Value::Char(c) => Ok(Value::Bool(c.is_ascii_digit())),
2059 _ => Err(RuntimeError::new("is_digit() requires string or char")),
2060 });
2061
2062 define(interp, "is_alnum", Some(1), |_, args| match &args[0] {
2063 Value::String(s) => Ok(Value::Bool(
2064 !s.is_empty() && s.chars().all(|c| c.is_alphanumeric()),
2065 )),
2066 Value::Char(c) => Ok(Value::Bool(c.is_alphanumeric())),
2067 _ => Err(RuntimeError::new("is_alnum() requires string or char")),
2068 });
2069
2070 define(interp, "is_space", Some(1), |_, args| match &args[0] {
2071 Value::String(s) => Ok(Value::Bool(
2072 !s.is_empty() && s.chars().all(|c| c.is_whitespace()),
2073 )),
2074 Value::Char(c) => Ok(Value::Bool(c.is_whitespace())),
2075 _ => Err(RuntimeError::new("is_space() requires string or char")),
2076 });
2077
2078 define(interp, "find", Some(2), |_, args| {
2084 match (&args[0], &args[1]) {
2085 (Value::String(s), Value::String(sub)) => {
2086 match s.find(sub.as_str()) {
2087 Some(byte_idx) => {
2088 let char_idx = s[..byte_idx].chars().count() as i64;
2090 Ok(Value::Int(char_idx))
2091 }
2092 None => Ok(Value::Int(-1)),
2093 }
2094 }
2095 (Value::String(s), Value::Char(c)) => match s.find(*c) {
2096 Some(byte_idx) => {
2097 let char_idx = s[..byte_idx].chars().count() as i64;
2098 Ok(Value::Int(char_idx))
2099 }
2100 None => Ok(Value::Int(-1)),
2101 },
2102 _ => Err(RuntimeError::new(
2103 "find() requires string and substring/char",
2104 )),
2105 }
2106 });
2107
2108 define(interp, "index_of", Some(2), |_, args| {
2110 match (&args[0], &args[1]) {
2111 (Value::String(s), Value::String(sub)) => match s.find(sub.as_str()) {
2112 Some(byte_idx) => {
2113 let char_idx = s[..byte_idx].chars().count() as i64;
2114 Ok(Value::Int(char_idx))
2115 }
2116 None => Ok(Value::Int(-1)),
2117 },
2118 (Value::String(s), Value::Char(c)) => match s.find(*c) {
2119 Some(byte_idx) => {
2120 let char_idx = s[..byte_idx].chars().count() as i64;
2121 Ok(Value::Int(char_idx))
2122 }
2123 None => Ok(Value::Int(-1)),
2124 },
2125 (Value::Array(arr), search) => {
2126 for (i, v) in arr.borrow().iter().enumerate() {
2128 if values_equal_simple(v, search) {
2129 return Ok(Value::Int(i as i64));
2130 }
2131 }
2132 Ok(Value::Int(-1))
2133 }
2134 _ => Err(RuntimeError::new(
2135 "index_of() requires array/string and element/substring",
2136 )),
2137 }
2138 });
2139
2140 define(interp, "last_index_of", Some(2), |_, args| {
2142 match (&args[0], &args[1]) {
2143 (Value::String(s), Value::String(sub)) => match s.rfind(sub.as_str()) {
2144 Some(byte_idx) => {
2145 let char_idx = s[..byte_idx].chars().count() as i64;
2146 Ok(Value::Int(char_idx))
2147 }
2148 None => Ok(Value::Int(-1)),
2149 },
2150 (Value::String(s), Value::Char(c)) => match s.rfind(*c) {
2151 Some(byte_idx) => {
2152 let char_idx = s[..byte_idx].chars().count() as i64;
2153 Ok(Value::Int(char_idx))
2154 }
2155 None => Ok(Value::Int(-1)),
2156 },
2157 _ => Err(RuntimeError::new(
2158 "last_index_of() requires string and substring/char",
2159 )),
2160 }
2161 });
2162
2163 define(interp, "substring", Some(3), |_, args| {
2165 let s = match &args[0] {
2166 Value::String(s) => (**s).clone(),
2167 _ => {
2168 return Err(RuntimeError::new(
2169 "substring: first argument must be a string",
2170 ))
2171 }
2172 };
2173 let start = match &args[1] {
2174 Value::Int(n) if *n >= 0 => *n as usize,
2175 _ => {
2176 return Err(RuntimeError::new(
2177 "substring: start must be a non-negative integer",
2178 ))
2179 }
2180 };
2181 let end = match &args[2] {
2182 Value::Int(n) if *n >= 0 => *n as usize,
2183 _ => {
2184 return Err(RuntimeError::new(
2185 "substring: end must be a non-negative integer",
2186 ))
2187 }
2188 };
2189 let chars: Vec<char> = s.chars().collect();
2190 let len = chars.len();
2191 let actual_start = start.min(len);
2192 let actual_end = end.min(len);
2193 if actual_start >= actual_end {
2194 return Ok(Value::String(Rc::new(String::new())));
2195 }
2196 let result: String = chars[actual_start..actual_end].iter().collect();
2197 Ok(Value::String(Rc::new(result)))
2198 });
2199
2200 define(interp, "count", Some(2), |_, args| {
2202 match (&args[0], &args[1]) {
2203 (Value::String(s), Value::String(sub)) => {
2204 if sub.is_empty() {
2205 return Err(RuntimeError::new("count: cannot count empty string"));
2206 }
2207 let count = s.matches(sub.as_str()).count() as i64;
2208 Ok(Value::Int(count))
2209 }
2210 (Value::String(s), Value::Char(c)) => {
2211 let count = s.chars().filter(|&ch| ch == *c).count() as i64;
2212 Ok(Value::Int(count))
2213 }
2214 _ => Err(RuntimeError::new(
2215 "count() requires string and substring/char",
2216 )),
2217 }
2218 });
2219
2220 define(interp, "char_at", Some(2), |_, args| {
2223 let s = match &args[0] {
2224 Value::String(s) => (**s).clone(),
2225 _ => {
2226 return Err(RuntimeError::new(
2227 "char_at: first argument must be a string",
2228 ))
2229 }
2230 };
2231 let idx = match &args[1] {
2232 Value::Int(n) => *n,
2233 _ => {
2234 return Err(RuntimeError::new(
2235 "char_at: second argument must be an integer",
2236 ))
2237 }
2238 };
2239 let actual_idx = if idx < 0 {
2241 (s.len() as i64 + idx) as usize
2243 } else {
2244 idx as usize
2245 };
2246 if actual_idx < s.len() {
2247 let remaining = &s[actual_idx..];
2248 match remaining.chars().next() {
2249 Some(c) => Ok(Value::Char(c)),
2250 None => Ok(Value::Null),
2251 }
2252 } else {
2253 Ok(Value::Null)
2254 }
2255 });
2256
2257 define(interp, "char_code_at", Some(2), |_, args| {
2259 let s = match &args[0] {
2260 Value::String(s) => (**s).clone(),
2261 _ => {
2262 return Err(RuntimeError::new(
2263 "char_code_at: first argument must be a string",
2264 ))
2265 }
2266 };
2267 let idx = match &args[1] {
2268 Value::Int(n) => *n,
2269 _ => {
2270 return Err(RuntimeError::new(
2271 "char_code_at: second argument must be an integer",
2272 ))
2273 }
2274 };
2275 let chars: Vec<char> = s.chars().collect();
2276 let actual_idx = if idx < 0 {
2277 (chars.len() as i64 + idx) as usize
2278 } else {
2279 idx as usize
2280 };
2281 match chars.get(actual_idx) {
2282 Some(c) => Ok(Value::Int(*c as i64)),
2283 None => Ok(Value::Null),
2284 }
2285 });
2286
2287 define(interp, "from_char_code", Some(1), |_, args| {
2289 let code = match &args[0] {
2290 Value::Int(n) => *n as u32,
2291 _ => {
2292 return Err(RuntimeError::new(
2293 "from_char_code: argument must be an integer",
2294 ))
2295 }
2296 };
2297 match char::from_u32(code) {
2298 Some(c) => Ok(Value::String(Rc::new(c.to_string()))),
2299 None => Err(RuntimeError::new(
2300 "from_char_code: invalid Unicode code point",
2301 )),
2302 }
2303 });
2304
2305 define(interp, "insert", Some(3), |_, args| {
2307 let s = match &args[0] {
2308 Value::String(s) => (**s).clone(),
2309 _ => return Err(RuntimeError::new("insert: first argument must be a string")),
2310 };
2311 let idx = match &args[1] {
2312 Value::Int(n) if *n >= 0 => *n as usize,
2313 _ => {
2314 return Err(RuntimeError::new(
2315 "insert: index must be a non-negative integer",
2316 ))
2317 }
2318 };
2319 let insertion = match &args[2] {
2320 Value::String(s) => (**s).clone(),
2321 _ => return Err(RuntimeError::new("insert: third argument must be a string")),
2322 };
2323 let chars: Vec<char> = s.chars().collect();
2324 let actual_idx = idx.min(chars.len());
2325 let mut result: String = chars[..actual_idx].iter().collect();
2326 result.push_str(&insertion);
2327 result.extend(chars[actual_idx..].iter());
2328 Ok(Value::String(Rc::new(result)))
2329 });
2330
2331 define(interp, "remove", Some(3), |_, args| {
2333 let s = match &args[0] {
2334 Value::String(s) => (**s).clone(),
2335 _ => return Err(RuntimeError::new("remove: first argument must be a string")),
2336 };
2337 let start = match &args[1] {
2338 Value::Int(n) if *n >= 0 => *n as usize,
2339 _ => {
2340 return Err(RuntimeError::new(
2341 "remove: start must be a non-negative integer",
2342 ))
2343 }
2344 };
2345 let len = match &args[2] {
2346 Value::Int(n) if *n >= 0 => *n as usize,
2347 _ => {
2348 return Err(RuntimeError::new(
2349 "remove: length must be a non-negative integer",
2350 ))
2351 }
2352 };
2353 let chars: Vec<char> = s.chars().collect();
2354 let str_len = chars.len();
2355 let actual_start = start.min(str_len);
2356 let actual_end = (start + len).min(str_len);
2357 let mut result: String = chars[..actual_start].iter().collect();
2358 result.extend(chars[actual_end..].iter());
2359 Ok(Value::String(Rc::new(result)))
2360 });
2361
2362 define(interp, "compare", Some(2), |_, args| {
2364 match (&args[0], &args[1]) {
2365 (Value::String(a), Value::String(b)) => {
2366 let result = match a.cmp(b) {
2367 std::cmp::Ordering::Less => -1,
2368 std::cmp::Ordering::Equal => 0,
2369 std::cmp::Ordering::Greater => 1,
2370 };
2371 Ok(Value::Int(result))
2372 }
2373 _ => Err(RuntimeError::new("compare() requires two strings")),
2374 }
2375 });
2376
2377 define(interp, "compare_ignore_case", Some(2), |_, args| {
2379 match (&args[0], &args[1]) {
2380 (Value::String(a), Value::String(b)) => {
2381 let result = match a.to_lowercase().cmp(&b.to_lowercase()) {
2382 std::cmp::Ordering::Less => -1,
2383 std::cmp::Ordering::Equal => 0,
2384 std::cmp::Ordering::Greater => 1,
2385 };
2386 Ok(Value::Int(result))
2387 }
2388 _ => Err(RuntimeError::new(
2389 "compare_ignore_case() requires two strings",
2390 )),
2391 }
2392 });
2393
2394 define(interp, "char_count", Some(1), |_, args| match &args[0] {
2396 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
2397 _ => Err(RuntimeError::new("char_count() requires string")),
2398 });
2399
2400 define(interp, "byte_count", Some(1), |_, args| match &args[0] {
2402 Value::String(s) => Ok(Value::Int(s.len() as i64)),
2403 _ => Err(RuntimeError::new("byte_count() requires string")),
2404 });
2405
2406 define(interp, "is_empty", Some(1), |_, args| match &args[0] {
2408 Value::String(s) => Ok(Value::Bool(s.is_empty())),
2409 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
2410 _ => Err(RuntimeError::new("is_empty() requires string or array")),
2411 });
2412
2413 define(interp, "is_blank", Some(1), |_, args| match &args[0] {
2415 Value::String(s) => Ok(Value::Bool(s.trim().is_empty())),
2416 _ => Err(RuntimeError::new("is_blank() requires string")),
2417 });
2418
2419 define(interp, "nfc", Some(1), |_, args| match &args[0] {
2425 Value::String(s) => Ok(Value::String(Rc::new(s.nfc().collect()))),
2426 _ => Err(RuntimeError::new("nfc() requires string")),
2427 });
2428
2429 define(interp, "nfd", Some(1), |_, args| match &args[0] {
2431 Value::String(s) => Ok(Value::String(Rc::new(s.nfd().collect()))),
2432 _ => Err(RuntimeError::new("nfd() requires string")),
2433 });
2434
2435 define(interp, "nfkc", Some(1), |_, args| match &args[0] {
2437 Value::String(s) => Ok(Value::String(Rc::new(s.nfkc().collect()))),
2438 _ => Err(RuntimeError::new("nfkc() requires string")),
2439 });
2440
2441 define(interp, "nfkd", Some(1), |_, args| match &args[0] {
2443 Value::String(s) => Ok(Value::String(Rc::new(s.nfkd().collect()))),
2444 _ => Err(RuntimeError::new("nfkd() requires string")),
2445 });
2446
2447 define(interp, "is_nfc", Some(1), |_, args| match &args[0] {
2449 Value::String(s) => {
2450 let normalized: String = s.nfc().collect();
2451 Ok(Value::Bool(*s.as_ref() == normalized))
2452 }
2453 _ => Err(RuntimeError::new("is_nfc() requires string")),
2454 });
2455
2456 define(interp, "is_nfd", Some(1), |_, args| match &args[0] {
2458 Value::String(s) => {
2459 let normalized: String = s.nfd().collect();
2460 Ok(Value::Bool(*s.as_ref() == normalized))
2461 }
2462 _ => Err(RuntimeError::new("is_nfd() requires string")),
2463 });
2464
2465 define(interp, "graphemes", Some(1), |_, args| match &args[0] {
2471 Value::String(s) => {
2472 let graphemes: Vec<Value> = s
2473 .graphemes(true)
2474 .map(|g| Value::String(Rc::new(g.to_string())))
2475 .collect();
2476 Ok(Value::Array(Rc::new(RefCell::new(graphemes))))
2477 }
2478 _ => Err(RuntimeError::new("graphemes() requires string")),
2479 });
2480
2481 define(interp, "grapheme_count", Some(1), |_, args| {
2483 match &args[0] {
2484 Value::String(s) => Ok(Value::Int(s.graphemes(true).count() as i64)),
2485 _ => Err(RuntimeError::new("grapheme_count() requires string")),
2486 }
2487 });
2488
2489 define(interp, "grapheme_at", Some(2), |_, args| {
2491 let s = match &args[0] {
2492 Value::String(s) => (**s).clone(),
2493 _ => {
2494 return Err(RuntimeError::new(
2495 "grapheme_at: first argument must be a string",
2496 ))
2497 }
2498 };
2499 let idx = match &args[1] {
2500 Value::Int(n) => *n,
2501 _ => {
2502 return Err(RuntimeError::new(
2503 "grapheme_at: second argument must be an integer",
2504 ))
2505 }
2506 };
2507 let graphemes: Vec<&str> = s.graphemes(true).collect();
2508 let actual_idx = if idx < 0 {
2509 (graphemes.len() as i64 + idx) as usize
2510 } else {
2511 idx as usize
2512 };
2513 match graphemes.get(actual_idx) {
2514 Some(g) => Ok(Value::String(Rc::new(g.to_string()))),
2515 None => Ok(Value::Null),
2516 }
2517 });
2518
2519 define(interp, "grapheme_slice", Some(3), |_, args| {
2521 let s = match &args[0] {
2522 Value::String(s) => (**s).clone(),
2523 _ => {
2524 return Err(RuntimeError::new(
2525 "grapheme_slice: first argument must be a string",
2526 ))
2527 }
2528 };
2529 let start = match &args[1] {
2530 Value::Int(n) if *n >= 0 => *n as usize,
2531 _ => {
2532 return Err(RuntimeError::new(
2533 "grapheme_slice: start must be a non-negative integer",
2534 ))
2535 }
2536 };
2537 let end = match &args[2] {
2538 Value::Int(n) if *n >= 0 => *n as usize,
2539 _ => {
2540 return Err(RuntimeError::new(
2541 "grapheme_slice: end must be a non-negative integer",
2542 ))
2543 }
2544 };
2545 let graphemes: Vec<&str> = s.graphemes(true).collect();
2546 let len = graphemes.len();
2547 let actual_start = start.min(len);
2548 let actual_end = end.min(len);
2549 if actual_start >= actual_end {
2550 return Ok(Value::String(Rc::new(String::new())));
2551 }
2552 let result: String = graphemes[actual_start..actual_end].join("");
2553 Ok(Value::String(Rc::new(result)))
2554 });
2555
2556 define(interp, "grapheme_reverse", Some(1), |_, args| {
2558 match &args[0] {
2559 Value::String(s) => {
2560 let reversed: String = s.graphemes(true).rev().collect();
2561 Ok(Value::String(Rc::new(reversed)))
2562 }
2563 _ => Err(RuntimeError::new("grapheme_reverse() requires string")),
2564 }
2565 });
2566
2567 define(interp, "word_boundaries", Some(1), |_, args| {
2569 match &args[0] {
2570 Value::String(s) => {
2571 let words: Vec<Value> = s
2572 .unicode_words()
2573 .map(|w| Value::String(Rc::new(w.to_string())))
2574 .collect();
2575 Ok(Value::Array(Rc::new(RefCell::new(words))))
2576 }
2577 _ => Err(RuntimeError::new("word_boundaries() requires string")),
2578 }
2579 });
2580
2581 define(interp, "string_builder", Some(0), |_, _| {
2588 Ok(Value::String(Rc::new(String::new())))
2589 });
2590
2591 define(interp, "concat_all", Some(1), |_, args| match &args[0] {
2593 Value::Array(arr) => {
2594 let parts: Vec<String> = arr
2595 .borrow()
2596 .iter()
2597 .map(|v| match v {
2598 Value::String(s) => (**s).clone(),
2599 other => format!("{}", other),
2600 })
2601 .collect();
2602 Ok(Value::String(Rc::new(parts.join(""))))
2603 }
2604 _ => Err(RuntimeError::new("concat_all() requires array")),
2605 });
2606
2607 define(interp, "repeat_join", Some(3), |_, args| {
2609 let s = match &args[0] {
2610 Value::String(s) => (**s).clone(),
2611 _ => {
2612 return Err(RuntimeError::new(
2613 "repeat_join: first argument must be a string",
2614 ))
2615 }
2616 };
2617 let n = match &args[1] {
2618 Value::Int(n) if *n >= 0 => *n as usize,
2619 _ => {
2620 return Err(RuntimeError::new(
2621 "repeat_join: count must be a non-negative integer",
2622 ))
2623 }
2624 };
2625 let sep = match &args[2] {
2626 Value::String(s) => (**s).clone(),
2627 _ => return Err(RuntimeError::new("repeat_join: separator must be a string")),
2628 };
2629 if n == 0 {
2630 return Ok(Value::String(Rc::new(String::new())));
2631 }
2632 let parts: Vec<&str> = std::iter::repeat(s.as_str()).take(n).collect();
2633 Ok(Value::String(Rc::new(parts.join(&sep))))
2634 });
2635}
2636
2637fn register_evidence(interp: &mut Interpreter) {
2642 use crate::interpreter::RuntimeConfidence;
2643
2644 define(interp, "known", Some(1), |_, args| {
2646 Ok(Value::Evidential {
2647 value: Box::new(args[0].clone()),
2648 evidence: Evidence::Known,
2649 })
2650 });
2651
2652 define(interp, "uncertain", Some(1), |_, args| {
2653 Ok(Value::Evidential {
2654 value: Box::new(args[0].clone()),
2655 evidence: Evidence::Uncertain,
2656 })
2657 });
2658
2659 define(interp, "reported", Some(1), |_, args| {
2660 Ok(Value::Evidential {
2661 value: Box::new(args[0].clone()),
2662 evidence: Evidence::Reported,
2663 })
2664 });
2665
2666 define(interp, "paradox", Some(1), |_, args| {
2667 Ok(Value::Evidential {
2668 value: Box::new(args[0].clone()),
2669 evidence: Evidence::Paradox,
2670 })
2671 });
2672
2673 define(interp, "evidence_of", Some(1), |_, args| {
2675 match &args[0] {
2676 Value::Evidential { evidence, .. } => {
2677 let level = match evidence {
2678 Evidence::Known => "known",
2679 Evidence::Uncertain => "uncertain",
2680 Evidence::Reported => "reported",
2681 Evidence::Paradox => "paradox",
2682 };
2683 Ok(Value::String(Rc::new(level.to_string())))
2684 }
2685 _ => Ok(Value::String(Rc::new("known".to_string()))), }
2687 });
2688
2689 define(interp, "is_known", Some(1), |_, args| {
2690 match &args[0] {
2691 Value::Evidential {
2692 evidence: Evidence::Known,
2693 ..
2694 } => Ok(Value::Bool(true)),
2695 Value::Evidential { .. } => Ok(Value::Bool(false)),
2696 _ => Ok(Value::Bool(true)), }
2698 });
2699
2700 define(interp, "is_uncertain", Some(1), |_, args| match &args[0] {
2701 Value::Evidential {
2702 evidence: Evidence::Uncertain,
2703 ..
2704 } => Ok(Value::Bool(true)),
2705 _ => Ok(Value::Bool(false)),
2706 });
2707
2708 define(interp, "is_reported", Some(1), |_, args| match &args[0] {
2709 Value::Evidential {
2710 evidence: Evidence::Reported,
2711 ..
2712 } => Ok(Value::Bool(true)),
2713 _ => Ok(Value::Bool(false)),
2714 });
2715
2716 define(interp, "is_paradox", Some(1), |_, args| match &args[0] {
2717 Value::Evidential {
2718 evidence: Evidence::Paradox,
2719 ..
2720 } => Ok(Value::Bool(true)),
2721 _ => Ok(Value::Bool(false)),
2722 });
2723
2724 define(interp, "strip_evidence", Some(1), |_, args| {
2726 match &args[0] {
2727 Value::Evidential { value, .. } => Ok(*value.clone()),
2728 other => Ok(other.clone()),
2729 }
2730 });
2731
2732 define(interp, "trust", Some(1), |_, args| {
2734 match &args[0] {
2736 Value::Evidential { value, .. } => Ok(Value::Evidential {
2737 value: value.clone(),
2738 evidence: Evidence::Known,
2739 }),
2740 other => Ok(other.clone()),
2741 }
2742 });
2743
2744 define(interp, "verify", Some(2), |_, args| {
2745 let pred_result = match &args[1] {
2747 Value::Bool(b) => *b,
2748 _ => return Err(RuntimeError::new("verify() predicate must be bool")),
2749 };
2750
2751 if pred_result {
2752 match &args[0] {
2753 Value::Evidential { value, .. } => Ok(Value::Evidential {
2754 value: value.clone(),
2755 evidence: Evidence::Known,
2756 }),
2757 other => Ok(other.clone()),
2758 }
2759 } else {
2760 Ok(args[0].clone()) }
2762 });
2763
2764 define(interp, "combine_evidence", Some(2), |_, args| {
2766 let ev1 = match &args[0] {
2767 Value::Evidential { evidence, .. } => *evidence,
2768 _ => Evidence::Known,
2769 };
2770 let ev2 = match &args[1] {
2771 Value::Evidential { evidence, .. } => *evidence,
2772 _ => Evidence::Known,
2773 };
2774
2775 let combined = match (ev1, ev2) {
2777 (Evidence::Paradox, _) | (_, Evidence::Paradox) => Evidence::Paradox,
2778 (Evidence::Reported, _) | (_, Evidence::Reported) => Evidence::Reported,
2779 (Evidence::Uncertain, _) | (_, Evidence::Uncertain) => Evidence::Uncertain,
2780 _ => Evidence::Known,
2781 };
2782
2783 Ok(Value::String(Rc::new(
2784 match combined {
2785 Evidence::Known => "known",
2786 Evidence::Uncertain => "uncertain",
2787 Evidence::Reported => "reported",
2788 Evidence::Paradox => "paradox",
2789 }
2790 .to_string(),
2791 )))
2792 });
2793
2794 define(interp, "affect_to_evidence", Some(1), |_, args| {
2798 match &args[0] {
2799 Value::Affective { affect, .. } => {
2800 if affect.sarcasm {
2802 return Ok(Value::String(Rc::new("uncertain".to_string())));
2803 }
2804 match affect.confidence {
2806 Some(RuntimeConfidence::High) => {
2807 Ok(Value::String(Rc::new("known".to_string())))
2808 }
2809 Some(RuntimeConfidence::Low) => {
2810 Ok(Value::String(Rc::new("uncertain".to_string())))
2811 }
2812 _ => Ok(Value::String(Rc::new("known".to_string()))),
2813 }
2814 }
2815 _ => Ok(Value::String(Rc::new("known".to_string()))),
2816 }
2817 });
2818
2819 define(
2821 interp,
2822 "affect_as_evidence",
2823 Some(1),
2824 |_, args| match &args[0] {
2825 Value::Affective { value, affect } => {
2826 let evidence = if affect.sarcasm {
2827 Evidence::Uncertain
2828 } else {
2829 match affect.confidence {
2830 Some(RuntimeConfidence::High) => Evidence::Known,
2831 Some(RuntimeConfidence::Low) => Evidence::Uncertain,
2832 _ => Evidence::Known,
2833 }
2834 };
2835 Ok(Value::Evidential {
2836 value: value.clone(),
2837 evidence,
2838 })
2839 }
2840 other => Ok(other.clone()),
2841 },
2842 );
2843
2844 define(
2846 interp,
2847 "is_affect_uncertain",
2848 Some(1),
2849 |_, args| match &args[0] {
2850 Value::Affective { affect, .. } => {
2851 let uncertain =
2852 affect.sarcasm || matches!(affect.confidence, Some(RuntimeConfidence::Low));
2853 Ok(Value::Bool(uncertain))
2854 }
2855 _ => Ok(Value::Bool(false)),
2856 },
2857 );
2858
2859 define(interp, "with_affect_evidence", Some(2), |_, args| {
2861 match &args[0] {
2863 Value::Affective { affect, .. } => {
2864 let evidence = if affect.sarcasm {
2865 Evidence::Uncertain
2866 } else {
2867 match affect.confidence {
2868 Some(RuntimeConfidence::High) => Evidence::Known,
2869 Some(RuntimeConfidence::Low) => Evidence::Uncertain,
2870 _ => Evidence::Known,
2871 }
2872 };
2873 Ok(Value::Evidential {
2874 value: Box::new(args[1].clone()),
2875 evidence,
2876 })
2877 }
2878 Value::Evidential { evidence, .. } => {
2879 Ok(Value::Evidential {
2881 value: Box::new(args[1].clone()),
2882 evidence: *evidence,
2883 })
2884 }
2885 _ => Ok(args[1].clone()),
2886 }
2887 });
2888}
2889
2890fn register_affect(interp: &mut Interpreter) {
2895 use crate::interpreter::{
2896 RuntimeAffect, RuntimeConfidence, RuntimeEmotion, RuntimeFormality, RuntimeIntensity,
2897 RuntimeSentiment,
2898 };
2899
2900 define(interp, "positive", Some(1), |_, args| {
2904 Ok(Value::Affective {
2905 value: Box::new(args[0].clone()),
2906 affect: RuntimeAffect {
2907 sentiment: Some(RuntimeSentiment::Positive),
2908 sarcasm: false,
2909 intensity: None,
2910 formality: None,
2911 emotion: None,
2912 confidence: None,
2913 },
2914 })
2915 });
2916
2917 define(interp, "negative", Some(1), |_, args| {
2918 Ok(Value::Affective {
2919 value: Box::new(args[0].clone()),
2920 affect: RuntimeAffect {
2921 sentiment: Some(RuntimeSentiment::Negative),
2922 sarcasm: false,
2923 intensity: None,
2924 formality: None,
2925 emotion: None,
2926 confidence: None,
2927 },
2928 })
2929 });
2930
2931 define(interp, "neutral", Some(1), |_, args| {
2932 Ok(Value::Affective {
2933 value: Box::new(args[0].clone()),
2934 affect: RuntimeAffect {
2935 sentiment: Some(RuntimeSentiment::Neutral),
2936 sarcasm: false,
2937 intensity: None,
2938 formality: None,
2939 emotion: None,
2940 confidence: None,
2941 },
2942 })
2943 });
2944
2945 define(interp, "sarcastic", Some(1), |_, args| {
2947 Ok(Value::Affective {
2948 value: Box::new(args[0].clone()),
2949 affect: RuntimeAffect {
2950 sentiment: None,
2951 sarcasm: true,
2952 intensity: None,
2953 formality: None,
2954 emotion: None,
2955 confidence: None,
2956 },
2957 })
2958 });
2959
2960 define(interp, "intensify", Some(1), |_, args| {
2962 Ok(Value::Affective {
2963 value: Box::new(args[0].clone()),
2964 affect: RuntimeAffect {
2965 sentiment: None,
2966 sarcasm: false,
2967 intensity: Some(RuntimeIntensity::Up),
2968 formality: None,
2969 emotion: None,
2970 confidence: None,
2971 },
2972 })
2973 });
2974
2975 define(interp, "dampen", Some(1), |_, args| {
2976 Ok(Value::Affective {
2977 value: Box::new(args[0].clone()),
2978 affect: RuntimeAffect {
2979 sentiment: None,
2980 sarcasm: false,
2981 intensity: Some(RuntimeIntensity::Down),
2982 formality: None,
2983 emotion: None,
2984 confidence: None,
2985 },
2986 })
2987 });
2988
2989 define(interp, "maximize", Some(1), |_, args| {
2990 Ok(Value::Affective {
2991 value: Box::new(args[0].clone()),
2992 affect: RuntimeAffect {
2993 sentiment: None,
2994 sarcasm: false,
2995 intensity: Some(RuntimeIntensity::Max),
2996 formality: None,
2997 emotion: None,
2998 confidence: None,
2999 },
3000 })
3001 });
3002
3003 define(interp, "formal", Some(1), |_, args| {
3005 Ok(Value::Affective {
3006 value: Box::new(args[0].clone()),
3007 affect: RuntimeAffect {
3008 sentiment: None,
3009 sarcasm: false,
3010 intensity: None,
3011 formality: Some(RuntimeFormality::Formal),
3012 emotion: None,
3013 confidence: None,
3014 },
3015 })
3016 });
3017
3018 define(interp, "informal", Some(1), |_, args| {
3019 Ok(Value::Affective {
3020 value: Box::new(args[0].clone()),
3021 affect: RuntimeAffect {
3022 sentiment: None,
3023 sarcasm: false,
3024 intensity: None,
3025 formality: Some(RuntimeFormality::Informal),
3026 emotion: None,
3027 confidence: None,
3028 },
3029 })
3030 });
3031
3032 define(interp, "joyful", Some(1), |_, args| {
3034 Ok(Value::Affective {
3035 value: Box::new(args[0].clone()),
3036 affect: RuntimeAffect {
3037 sentiment: None,
3038 sarcasm: false,
3039 intensity: None,
3040 formality: None,
3041 emotion: Some(RuntimeEmotion::Joy),
3042 confidence: None,
3043 },
3044 })
3045 });
3046
3047 define(interp, "sad", Some(1), |_, args| {
3048 Ok(Value::Affective {
3049 value: Box::new(args[0].clone()),
3050 affect: RuntimeAffect {
3051 sentiment: None,
3052 sarcasm: false,
3053 intensity: None,
3054 formality: None,
3055 emotion: Some(RuntimeEmotion::Sadness),
3056 confidence: None,
3057 },
3058 })
3059 });
3060
3061 define(interp, "angry", Some(1), |_, args| {
3062 Ok(Value::Affective {
3063 value: Box::new(args[0].clone()),
3064 affect: RuntimeAffect {
3065 sentiment: None,
3066 sarcasm: false,
3067 intensity: None,
3068 formality: None,
3069 emotion: Some(RuntimeEmotion::Anger),
3070 confidence: None,
3071 },
3072 })
3073 });
3074
3075 define(interp, "fearful", Some(1), |_, args| {
3076 Ok(Value::Affective {
3077 value: Box::new(args[0].clone()),
3078 affect: RuntimeAffect {
3079 sentiment: None,
3080 sarcasm: false,
3081 intensity: None,
3082 formality: None,
3083 emotion: Some(RuntimeEmotion::Fear),
3084 confidence: None,
3085 },
3086 })
3087 });
3088
3089 define(interp, "surprised", Some(1), |_, args| {
3090 Ok(Value::Affective {
3091 value: Box::new(args[0].clone()),
3092 affect: RuntimeAffect {
3093 sentiment: None,
3094 sarcasm: false,
3095 intensity: None,
3096 formality: None,
3097 emotion: Some(RuntimeEmotion::Surprise),
3098 confidence: None,
3099 },
3100 })
3101 });
3102
3103 define(interp, "loving", Some(1), |_, args| {
3104 Ok(Value::Affective {
3105 value: Box::new(args[0].clone()),
3106 affect: RuntimeAffect {
3107 sentiment: None,
3108 sarcasm: false,
3109 intensity: None,
3110 formality: None,
3111 emotion: Some(RuntimeEmotion::Love),
3112 confidence: None,
3113 },
3114 })
3115 });
3116
3117 define(interp, "high_confidence", Some(1), |_, args| {
3119 Ok(Value::Affective {
3120 value: Box::new(args[0].clone()),
3121 affect: RuntimeAffect {
3122 sentiment: None,
3123 sarcasm: false,
3124 intensity: None,
3125 formality: None,
3126 emotion: None,
3127 confidence: Some(RuntimeConfidence::High),
3128 },
3129 })
3130 });
3131
3132 define(interp, "medium_confidence", Some(1), |_, args| {
3133 Ok(Value::Affective {
3134 value: Box::new(args[0].clone()),
3135 affect: RuntimeAffect {
3136 sentiment: None,
3137 sarcasm: false,
3138 intensity: None,
3139 formality: None,
3140 emotion: None,
3141 confidence: Some(RuntimeConfidence::Medium),
3142 },
3143 })
3144 });
3145
3146 define(interp, "low_confidence", Some(1), |_, args| {
3147 Ok(Value::Affective {
3148 value: Box::new(args[0].clone()),
3149 affect: RuntimeAffect {
3150 sentiment: None,
3151 sarcasm: false,
3152 intensity: None,
3153 formality: None,
3154 emotion: None,
3155 confidence: Some(RuntimeConfidence::Low),
3156 },
3157 })
3158 });
3159
3160 define(interp, "affect_of", Some(1), |_, args| match &args[0] {
3163 Value::Affective { affect, .. } => {
3164 let mut parts = Vec::new();
3165 if let Some(s) = &affect.sentiment {
3166 parts.push(match s {
3167 RuntimeSentiment::Positive => "positive",
3168 RuntimeSentiment::Negative => "negative",
3169 RuntimeSentiment::Neutral => "neutral",
3170 });
3171 }
3172 if affect.sarcasm {
3173 parts.push("sarcastic");
3174 }
3175 if let Some(i) = &affect.intensity {
3176 parts.push(match i {
3177 RuntimeIntensity::Up => "intensified",
3178 RuntimeIntensity::Down => "dampened",
3179 RuntimeIntensity::Max => "maximized",
3180 });
3181 }
3182 if let Some(f) = &affect.formality {
3183 parts.push(match f {
3184 RuntimeFormality::Formal => "formal",
3185 RuntimeFormality::Informal => "informal",
3186 });
3187 }
3188 if let Some(e) = &affect.emotion {
3189 parts.push(match e {
3190 RuntimeEmotion::Joy => "joyful",
3191 RuntimeEmotion::Sadness => "sad",
3192 RuntimeEmotion::Anger => "angry",
3193 RuntimeEmotion::Fear => "fearful",
3194 RuntimeEmotion::Surprise => "surprised",
3195 RuntimeEmotion::Love => "loving",
3196 });
3197 }
3198 if let Some(c) = &affect.confidence {
3199 parts.push(match c {
3200 RuntimeConfidence::High => "high_confidence",
3201 RuntimeConfidence::Medium => "medium_confidence",
3202 RuntimeConfidence::Low => "low_confidence",
3203 });
3204 }
3205 Ok(Value::String(Rc::new(parts.join(", "))))
3206 }
3207 _ => Ok(Value::String(Rc::new("none".to_string()))),
3208 });
3209
3210 define(interp, "is_sarcastic", Some(1), |_, args| match &args[0] {
3211 Value::Affective { affect, .. } => Ok(Value::Bool(affect.sarcasm)),
3212 _ => Ok(Value::Bool(false)),
3213 });
3214
3215 define(interp, "is_positive", Some(1), |_, args| match &args[0] {
3216 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3217 affect.sentiment,
3218 Some(RuntimeSentiment::Positive)
3219 ))),
3220 _ => Ok(Value::Bool(false)),
3221 });
3222
3223 define(interp, "is_negative", Some(1), |_, args| match &args[0] {
3224 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3225 affect.sentiment,
3226 Some(RuntimeSentiment::Negative)
3227 ))),
3228 _ => Ok(Value::Bool(false)),
3229 });
3230
3231 define(interp, "is_formal", Some(1), |_, args| match &args[0] {
3232 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3233 affect.formality,
3234 Some(RuntimeFormality::Formal)
3235 ))),
3236 _ => Ok(Value::Bool(false)),
3237 });
3238
3239 define(interp, "is_informal", Some(1), |_, args| match &args[0] {
3240 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3241 affect.formality,
3242 Some(RuntimeFormality::Informal)
3243 ))),
3244 _ => Ok(Value::Bool(false)),
3245 });
3246
3247 define(interp, "emotion_of", Some(1), |_, args| match &args[0] {
3248 Value::Affective { affect, .. } => {
3249 let emotion_str = match &affect.emotion {
3250 Some(RuntimeEmotion::Joy) => "joy",
3251 Some(RuntimeEmotion::Sadness) => "sadness",
3252 Some(RuntimeEmotion::Anger) => "anger",
3253 Some(RuntimeEmotion::Fear) => "fear",
3254 Some(RuntimeEmotion::Surprise) => "surprise",
3255 Some(RuntimeEmotion::Love) => "love",
3256 None => "none",
3257 };
3258 Ok(Value::String(Rc::new(emotion_str.to_string())))
3259 }
3260 _ => Ok(Value::String(Rc::new("none".to_string()))),
3261 });
3262
3263 define(interp, "confidence_of", Some(1), |_, args| match &args[0] {
3264 Value::Affective { affect, .. } => {
3265 let conf_str = match &affect.confidence {
3266 Some(RuntimeConfidence::High) => "high",
3267 Some(RuntimeConfidence::Medium) => "medium",
3268 Some(RuntimeConfidence::Low) => "low",
3269 None => "none",
3270 };
3271 Ok(Value::String(Rc::new(conf_str.to_string())))
3272 }
3273 _ => Ok(Value::String(Rc::new("none".to_string()))),
3274 });
3275
3276 define(interp, "strip_affect", Some(1), |_, args| match &args[0] {
3278 Value::Affective { value, .. } => Ok(*value.clone()),
3279 other => Ok(other.clone()),
3280 });
3281
3282 define(interp, "with_affect", None, |_, args| {
3284 if args.is_empty() {
3285 return Err(RuntimeError::new(
3286 "with_affect requires at least one argument",
3287 ));
3288 }
3289
3290 let base_value = args[0].clone();
3291 let mut affect = RuntimeAffect {
3292 sentiment: None,
3293 sarcasm: false,
3294 intensity: None,
3295 formality: None,
3296 emotion: None,
3297 confidence: None,
3298 };
3299
3300 for arg in args.iter().skip(1) {
3302 if let Value::String(s) = arg {
3303 match s.as_str() {
3304 "positive" | "⊕" => affect.sentiment = Some(RuntimeSentiment::Positive),
3305 "negative" | "⊖" => affect.sentiment = Some(RuntimeSentiment::Negative),
3306 "neutral" | "⊜" => affect.sentiment = Some(RuntimeSentiment::Neutral),
3307 "sarcastic" | "⸮" => affect.sarcasm = true,
3308 "intensify" | "↑" => affect.intensity = Some(RuntimeIntensity::Up),
3309 "dampen" | "↓" => affect.intensity = Some(RuntimeIntensity::Down),
3310 "maximize" | "⇈" => affect.intensity = Some(RuntimeIntensity::Max),
3311 "formal" | "♔" => affect.formality = Some(RuntimeFormality::Formal),
3312 "informal" | "♟" => affect.formality = Some(RuntimeFormality::Informal),
3313 "joy" | "☺" => affect.emotion = Some(RuntimeEmotion::Joy),
3314 "sadness" | "☹" => affect.emotion = Some(RuntimeEmotion::Sadness),
3315 "anger" | "⚡" => affect.emotion = Some(RuntimeEmotion::Anger),
3316 "fear" | "❄" => affect.emotion = Some(RuntimeEmotion::Fear),
3317 "surprise" | "✦" => affect.emotion = Some(RuntimeEmotion::Surprise),
3318 "love" | "♡" => affect.emotion = Some(RuntimeEmotion::Love),
3319 "high" | "◉" => affect.confidence = Some(RuntimeConfidence::High),
3320 "medium" | "◎" => affect.confidence = Some(RuntimeConfidence::Medium),
3321 "low" | "○" => affect.confidence = Some(RuntimeConfidence::Low),
3322 _ => {}
3323 }
3324 }
3325 }
3326
3327 Ok(Value::Affective {
3328 value: Box::new(base_value),
3329 affect,
3330 })
3331 });
3332}
3333
3334fn register_iter(interp: &mut Interpreter) {
3339 define(interp, "sum", Some(1), |_, args| match &args[0] {
3341 Value::Array(arr) => {
3342 let mut sum_int: i64 = 0;
3343 let mut sum_float: f64 = 0.0;
3344 let mut is_float = false;
3345
3346 for val in arr.borrow().iter() {
3347 match val {
3348 Value::Int(n) => {
3349 if is_float {
3350 sum_float += *n as f64;
3351 } else {
3352 sum_int += n;
3353 }
3354 }
3355 Value::Float(n) => {
3356 if !is_float {
3357 sum_float = sum_int as f64;
3358 is_float = true;
3359 }
3360 sum_float += n;
3361 }
3362 _ => return Err(RuntimeError::new("sum() requires array of numbers")),
3363 }
3364 }
3365
3366 if is_float {
3367 Ok(Value::Float(sum_float))
3368 } else {
3369 Ok(Value::Int(sum_int))
3370 }
3371 }
3372 _ => Err(RuntimeError::new("sum() requires array")),
3373 });
3374
3375 define(interp, "product", Some(1), |_, args| match &args[0] {
3377 Value::Array(arr) => {
3378 let mut prod_int: i64 = 1;
3379 let mut prod_float: f64 = 1.0;
3380 let mut is_float = false;
3381
3382 for val in arr.borrow().iter() {
3383 match val {
3384 Value::Int(n) => {
3385 if is_float {
3386 prod_float *= *n as f64;
3387 } else {
3388 prod_int *= n;
3389 }
3390 }
3391 Value::Float(n) => {
3392 if !is_float {
3393 prod_float = prod_int as f64;
3394 is_float = true;
3395 }
3396 prod_float *= n;
3397 }
3398 _ => return Err(RuntimeError::new("product() requires array of numbers")),
3399 }
3400 }
3401
3402 if is_float {
3403 Ok(Value::Float(prod_float))
3404 } else {
3405 Ok(Value::Int(prod_int))
3406 }
3407 }
3408 _ => Err(RuntimeError::new("product() requires array")),
3409 });
3410
3411 define(interp, "mean", Some(1), |_, args| match &args[0] {
3413 Value::Array(arr) => {
3414 let arr = arr.borrow();
3415 if arr.is_empty() {
3416 return Err(RuntimeError::new("mean() on empty array"));
3417 }
3418
3419 let mut sum: f64 = 0.0;
3420 for val in arr.iter() {
3421 match val {
3422 Value::Int(n) => sum += *n as f64,
3423 Value::Float(n) => sum += n,
3424 _ => return Err(RuntimeError::new("mean() requires array of numbers")),
3425 }
3426 }
3427
3428 Ok(Value::Float(sum / arr.len() as f64))
3429 }
3430 _ => Err(RuntimeError::new("mean() requires array")),
3431 });
3432
3433 define(interp, "median", Some(1), |_, args| match &args[0] {
3435 Value::Array(arr) => {
3436 let arr = arr.borrow();
3437 if arr.is_empty() {
3438 return Err(RuntimeError::new("median() on empty array"));
3439 }
3440
3441 let mut nums: Vec<f64> = Vec::new();
3442 for val in arr.iter() {
3443 match val {
3444 Value::Int(n) => nums.push(*n as f64),
3445 Value::Float(n) => nums.push(*n),
3446 _ => return Err(RuntimeError::new("median() requires array of numbers")),
3447 }
3448 }
3449
3450 nums.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
3451 let mid = nums.len() / 2;
3452
3453 if nums.len() % 2 == 0 {
3454 Ok(Value::Float((nums[mid - 1] + nums[mid]) / 2.0))
3455 } else {
3456 Ok(Value::Float(nums[mid]))
3457 }
3458 }
3459 _ => Err(RuntimeError::new("median() requires array")),
3460 });
3461
3462 define(interp, "min_of", Some(1), |_, args| match &args[0] {
3464 Value::Array(arr) => {
3465 let arr = arr.borrow();
3466 if arr.is_empty() {
3467 return Err(RuntimeError::new("min_of() on empty array"));
3468 }
3469
3470 let mut min = &arr[0];
3471 for val in arr.iter().skip(1) {
3472 if matches!(compare_values(val, min), std::cmp::Ordering::Less) {
3473 min = val;
3474 }
3475 }
3476 Ok(min.clone())
3477 }
3478 _ => Err(RuntimeError::new("min_of() requires array")),
3479 });
3480
3481 define(interp, "max_of", Some(1), |_, args| match &args[0] {
3483 Value::Array(arr) => {
3484 let arr = arr.borrow();
3485 if arr.is_empty() {
3486 return Err(RuntimeError::new("max_of() on empty array"));
3487 }
3488
3489 let mut max = &arr[0];
3490 for val in arr.iter().skip(1) {
3491 if matches!(compare_values(val, max), std::cmp::Ordering::Greater) {
3492 max = val;
3493 }
3494 }
3495 Ok(max.clone())
3496 }
3497 _ => Err(RuntimeError::new("max_of() requires array")),
3498 });
3499
3500 define(interp, "count", Some(1), |_, args| match &args[0] {
3502 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
3503 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
3504 _ => Err(RuntimeError::new("count() requires array or string")),
3505 });
3506
3507 define(interp, "any", Some(1), |_, args| match &args[0] {
3509 Value::Array(arr) => {
3510 for val in arr.borrow().iter() {
3511 if is_truthy(val) {
3512 return Ok(Value::Bool(true));
3513 }
3514 }
3515 Ok(Value::Bool(false))
3516 }
3517 _ => Err(RuntimeError::new("any() requires array")),
3518 });
3519
3520 define(interp, "all", Some(1), |_, args| match &args[0] {
3522 Value::Array(arr) => {
3523 for val in arr.borrow().iter() {
3524 if !is_truthy(val) {
3525 return Ok(Value::Bool(false));
3526 }
3527 }
3528 Ok(Value::Bool(true))
3529 }
3530 _ => Err(RuntimeError::new("all() requires array")),
3531 });
3532
3533 define(interp, "none", Some(1), |_, args| match &args[0] {
3535 Value::Array(arr) => {
3536 for val in arr.borrow().iter() {
3537 if is_truthy(val) {
3538 return Ok(Value::Bool(false));
3539 }
3540 }
3541 Ok(Value::Bool(true))
3542 }
3543 _ => Err(RuntimeError::new("none() requires array")),
3544 });
3545}
3546
3547fn is_truthy(val: &Value) -> bool {
3548 match val {
3549 Value::Null | Value::Empty => false,
3550 Value::Bool(b) => *b,
3551 Value::Int(n) => *n != 0,
3552 Value::Float(n) => *n != 0.0 && !n.is_nan(),
3553 Value::String(s) => !s.is_empty(),
3554 Value::Array(arr) => !arr.borrow().is_empty(),
3555 Value::Evidential { value, .. } => is_truthy(value),
3556 _ => true,
3557 }
3558}
3559
3560fn register_io(interp: &mut Interpreter) {
3565 define(interp, "read_file", Some(1), |_, args| {
3567 match &args[0] {
3568 Value::String(path) => {
3569 match std::fs::read_to_string(path.as_str()) {
3570 Ok(content) => Ok(Value::Evidential {
3571 value: Box::new(Value::String(Rc::new(content))),
3572 evidence: Evidence::Reported, }),
3574 Err(e) => Err(RuntimeError::new(format!("read_file failed: {}", e))),
3575 }
3576 }
3577 _ => Err(RuntimeError::new("read_file() requires path string")),
3578 }
3579 });
3580
3581 define(interp, "write_file", Some(2), |_, args| {
3583 match (&args[0], &args[1]) {
3584 (Value::String(path), Value::String(content)) => {
3585 match std::fs::write(path.as_str(), content.as_str()) {
3586 Ok(_) => Ok(Value::Bool(true)),
3587 Err(e) => Err(RuntimeError::new(format!("write_file failed: {}", e))),
3588 }
3589 }
3590 _ => Err(RuntimeError::new(
3591 "write_file() requires path and content strings",
3592 )),
3593 }
3594 });
3595
3596 define(interp, "append_file", Some(2), |_, args| {
3598 match (&args[0], &args[1]) {
3599 (Value::String(path), Value::String(content)) => {
3600 use std::fs::OpenOptions;
3601 let result = OpenOptions::new()
3602 .create(true)
3603 .append(true)
3604 .open(path.as_str())
3605 .and_then(|mut f| f.write_all(content.as_bytes()));
3606 match result {
3607 Ok(_) => Ok(Value::Bool(true)),
3608 Err(e) => Err(RuntimeError::new(format!("append_file failed: {}", e))),
3609 }
3610 }
3611 _ => Err(RuntimeError::new(
3612 "append_file() requires path and content strings",
3613 )),
3614 }
3615 });
3616
3617 define(interp, "file_exists", Some(1), |_, args| match &args[0] {
3619 Value::String(path) => Ok(Value::Bool(std::path::Path::new(path.as_str()).exists())),
3620 _ => Err(RuntimeError::new("file_exists() requires path string")),
3621 });
3622
3623 define(interp, "read_lines", Some(1), |_, args| match &args[0] {
3625 Value::String(path) => match std::fs::read_to_string(path.as_str()) {
3626 Ok(content) => {
3627 let lines: Vec<Value> = content
3628 .lines()
3629 .map(|l| Value::String(Rc::new(l.to_string())))
3630 .collect();
3631 Ok(Value::Evidential {
3632 value: Box::new(Value::Array(Rc::new(RefCell::new(lines)))),
3633 evidence: Evidence::Reported,
3634 })
3635 }
3636 Err(e) => Err(RuntimeError::new(format!("read_lines failed: {}", e))),
3637 },
3638 _ => Err(RuntimeError::new("read_lines() requires path string")),
3639 });
3640
3641 define(interp, "env", Some(1), |_, args| {
3643 match &args[0] {
3644 Value::String(name) => {
3645 match std::env::var(name.as_str()) {
3646 Ok(value) => Ok(Value::Evidential {
3647 value: Box::new(Value::String(Rc::new(value))),
3648 evidence: Evidence::Reported, }),
3650 Err(_) => Ok(Value::Null),
3651 }
3652 }
3653 _ => Err(RuntimeError::new("env() requires variable name string")),
3654 }
3655 });
3656
3657 define(interp, "env·var", Some(1), |_, args| {
3659 match &args[0] {
3660 Value::String(name) => {
3661 match std::env::var(name.as_str()) {
3662 Ok(value) => Ok(Value::Variant {
3663 enum_name: "Result".to_string(),
3664 variant_name: "Ok".to_string(),
3665 fields: Some(Rc::new(vec![Value::String(Rc::new(value))])),
3666 }),
3667 Err(_) => Ok(Value::Variant {
3668 enum_name: "Result".to_string(),
3669 variant_name: "Err".to_string(),
3670 fields: Some(Rc::new(vec![Value::String(Rc::new("environment variable not found".to_string()))])),
3671 }),
3672 }
3673 }
3674 _ => Err(RuntimeError::new("env::var() requires variable name string")),
3675 }
3676 });
3677
3678 define(interp, "env_or", Some(2), |_, args| {
3680 match (&args[0], &args[1]) {
3681 (Value::String(name), default) => match std::env::var(name.as_str()) {
3682 Ok(value) => Ok(Value::Evidential {
3683 value: Box::new(Value::String(Rc::new(value))),
3684 evidence: Evidence::Reported,
3685 }),
3686 Err(_) => Ok(default.clone()),
3687 },
3688 _ => Err(RuntimeError::new("env_or() requires variable name string")),
3689 }
3690 });
3691
3692 define(interp, "cwd", Some(0), |_, _| {
3694 match std::env::current_dir() {
3695 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
3696 Err(e) => Err(RuntimeError::new(format!("cwd() failed: {}", e))),
3697 }
3698 });
3699
3700 define(interp, "args", Some(0), |interp, _| {
3702 let args: Vec<Value> = if interp.program_args.as_ref().map(|v| v.is_empty()).unwrap_or(true) {
3703 std::env::args()
3705 .map(|a| Value::String(Rc::new(a)))
3706 .collect()
3707 } else {
3708 interp.program_args.as_ref().unwrap().iter()
3710 .map(|a| Value::String(Rc::new(a.clone())))
3711 .collect()
3712 };
3713 Ok(Value::Array(Rc::new(RefCell::new(args))))
3714 });
3715}
3716
3717fn register_time(interp: &mut Interpreter) {
3722 define(interp, "now", Some(0), |_, _| {
3724 let duration = SystemTime::now()
3725 .duration_since(UNIX_EPOCH)
3726 .unwrap_or(Duration::ZERO);
3727 Ok(Value::Int(duration.as_millis() as i64))
3728 });
3729
3730 define(interp, "now_secs", Some(0), |_, _| {
3732 let duration = SystemTime::now()
3733 .duration_since(UNIX_EPOCH)
3734 .unwrap_or(Duration::ZERO);
3735 Ok(Value::Int(duration.as_secs() as i64))
3736 });
3737
3738 define(interp, "now_micros", Some(0), |_, _| {
3740 let duration = SystemTime::now()
3741 .duration_since(UNIX_EPOCH)
3742 .unwrap_or(Duration::ZERO);
3743 Ok(Value::Int(duration.as_micros() as i64))
3744 });
3745
3746 define(interp, "sleep", Some(1), |_, args| match &args[0] {
3748 Value::Int(ms) if *ms >= 0 => {
3749 std::thread::sleep(Duration::from_millis(*ms as u64));
3750 Ok(Value::Null)
3751 }
3752 _ => Err(RuntimeError::new(
3753 "sleep() requires non-negative integer milliseconds",
3754 )),
3755 });
3756
3757 define(interp, "UNIX_EPOCH", Some(0), |_, _| {
3763 let mut fields = std::collections::HashMap::new();
3765 fields.insert("secs".to_string(), Value::Int(0));
3766 fields.insert("nanos".to_string(), Value::Int(0));
3767 Ok(Value::Struct {
3768 name: "SystemTime".to_string(),
3769 fields: Rc::new(RefCell::new(fields)),
3770 })
3771 });
3772
3773 define(interp, "std·time·UNIX_EPOCH", Some(0), |_, _| {
3775 let mut fields = std::collections::HashMap::new();
3776 fields.insert("secs".to_string(), Value::Int(0));
3777 fields.insert("nanos".to_string(), Value::Int(0));
3778 Ok(Value::Struct {
3779 name: "SystemTime".to_string(),
3780 fields: Rc::new(RefCell::new(fields)),
3781 })
3782 });
3783
3784 define(interp, "timer_start", Some(0), |_, _| {
3786 let now = Instant::now();
3787 Ok(Value::Int(now.elapsed().as_nanos() as i64)) });
3790}
3791
3792fn register_random(interp: &mut Interpreter) {
3797 define(interp, "random", Some(0), |_, _| {
3799 use std::time::SystemTime;
3801 let seed = SystemTime::now()
3802 .duration_since(UNIX_EPOCH)
3803 .unwrap_or(Duration::ZERO)
3804 .as_nanos() as u64;
3805 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as f64;
3806 Ok(Value::Float(rand / u32::MAX as f64))
3807 });
3808
3809 define(interp, "random_int", Some(2), |_, args| {
3811 match (&args[0], &args[1]) {
3812 (Value::Int(min), Value::Int(max)) if max > min => {
3813 use std::time::SystemTime;
3814 let seed = SystemTime::now()
3815 .duration_since(UNIX_EPOCH)
3816 .unwrap_or(Duration::ZERO)
3817 .as_nanos() as u64;
3818 let range = (max - min) as u64;
3819 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) % range;
3820 Ok(Value::Int(*min + rand as i64))
3821 }
3822 _ => Err(RuntimeError::new(
3823 "random_int() requires min < max integers",
3824 )),
3825 }
3826 });
3827
3828 define(interp, "shuffle", Some(1), |_, args| match &args[0] {
3830 Value::Array(arr) => {
3831 let mut arr = arr.borrow_mut();
3832 use std::time::SystemTime;
3833 let mut seed = SystemTime::now()
3834 .duration_since(UNIX_EPOCH)
3835 .unwrap_or(Duration::ZERO)
3836 .as_nanos() as u64;
3837
3838 for i in (1..arr.len()).rev() {
3839 seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
3840 let j = ((seed >> 16) as usize) % (i + 1);
3841 arr.swap(i, j);
3842 }
3843 Ok(Value::Null)
3844 }
3845 _ => Err(RuntimeError::new("shuffle() requires array")),
3846 });
3847
3848 define(interp, "sample", Some(1), |_, args| match &args[0] {
3850 Value::Array(arr) => {
3851 let arr = arr.borrow();
3852 if arr.is_empty() {
3853 return Err(RuntimeError::new("sample() on empty array"));
3854 }
3855
3856 use std::time::SystemTime;
3857 let seed = SystemTime::now()
3858 .duration_since(UNIX_EPOCH)
3859 .unwrap_or(Duration::ZERO)
3860 .as_nanos() as u64;
3861 let idx =
3862 ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % arr.len();
3863 Ok(arr[idx].clone())
3864 }
3865 _ => Err(RuntimeError::new("sample() requires array")),
3866 });
3867}
3868
3869fn register_convert(interp: &mut Interpreter) {
3874 define(interp, "to_string", Some(1), |_, args| {
3876 Ok(Value::String(Rc::new(format!("{}", args[0]))))
3877 });
3878
3879 define(interp, "to_int", Some(1), |_, args| match &args[0] {
3881 Value::Int(n) => Ok(Value::Int(*n)),
3882 Value::Float(n) => Ok(Value::Int(*n as i64)),
3883 Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
3884 Value::Char(c) => Ok(Value::Int(*c as i64)),
3885 Value::String(s) => s
3886 .parse::<i64>()
3887 .map(Value::Int)
3888 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as integer", s))),
3889 _ => Err(RuntimeError::new("to_int() cannot convert this type")),
3890 });
3891
3892 define(interp, "to_float", Some(1), |_, args| match &args[0] {
3894 Value::Int(n) => Ok(Value::Float(*n as f64)),
3895 Value::Float(n) => Ok(Value::Float(*n)),
3896 Value::Bool(b) => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
3897 Value::String(s) => s
3898 .parse::<f64>()
3899 .map(Value::Float)
3900 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as float", s))),
3901 _ => Err(RuntimeError::new("to_float() cannot convert this type")),
3902 });
3903
3904 define(interp, "to_bool", Some(1), |_, args| {
3906 Ok(Value::Bool(is_truthy(&args[0])))
3907 });
3908
3909 define(interp, "to_char", Some(1), |_, args| match &args[0] {
3911 Value::Char(c) => Ok(Value::Char(*c)),
3912 Value::Int(n) => char::from_u32(*n as u32)
3913 .map(Value::Char)
3914 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n))),
3915 Value::String(s) => s
3916 .chars()
3917 .next()
3918 .map(Value::Char)
3919 .ok_or_else(|| RuntimeError::new("to_char() on empty string")),
3920 _ => Err(RuntimeError::new("to_char() cannot convert this type")),
3921 });
3922
3923 define(interp, "to_array", Some(1), |_, args| match &args[0] {
3925 Value::Array(arr) => Ok(Value::Array(arr.clone())),
3926 Value::Tuple(t) => Ok(Value::Array(Rc::new(RefCell::new(t.as_ref().clone())))),
3927 Value::String(s) => {
3928 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
3929 Ok(Value::Array(Rc::new(RefCell::new(chars))))
3930 }
3931 _ => Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()])))),
3932 });
3933
3934 define(interp, "to_tuple", Some(1), |_, args| match &args[0] {
3936 Value::Tuple(t) => Ok(Value::Tuple(t.clone())),
3937 Value::Array(arr) => Ok(Value::Tuple(Rc::new(arr.borrow().clone()))),
3938 _ => Ok(Value::Tuple(Rc::new(vec![args[0].clone()]))),
3939 });
3940
3941 define(interp, "char_code", Some(1), |_, args| match &args[0] {
3943 Value::Char(c) => Ok(Value::Int(*c as i64)),
3944 _ => Err(RuntimeError::new("char_code() requires char")),
3945 });
3946
3947 define(interp, "from_char_code", Some(1), |_, args| {
3949 match &args[0] {
3950 Value::Int(n) => char::from_u32(*n as u32)
3951 .map(Value::Char)
3952 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n))),
3953 _ => Err(RuntimeError::new("from_char_code() requires integer")),
3954 }
3955 });
3956
3957 define(interp, "hex", Some(1), |_, args| match &args[0] {
3959 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:x}", n)))),
3960 _ => Err(RuntimeError::new("hex() requires integer")),
3961 });
3962
3963 define(interp, "oct", Some(1), |_, args| match &args[0] {
3965 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:o}", n)))),
3966 _ => Err(RuntimeError::new("oct() requires integer")),
3967 });
3968
3969 define(interp, "bin", Some(1), |_, args| match &args[0] {
3971 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:b}", n)))),
3972 _ => Err(RuntimeError::new("bin() requires integer")),
3973 });
3974
3975 define(interp, "parse_int", Some(2), |_, args| {
3977 match (&args[0], &args[1]) {
3978 (Value::String(s), Value::Int(base)) if *base >= 2 && *base <= 36 => {
3979 i64::from_str_radix(s.trim(), *base as u32)
3980 .map(Value::Int)
3981 .map_err(|_| {
3982 RuntimeError::new(format!("cannot parse '{}' as base-{} integer", s, base))
3983 })
3984 }
3985 _ => Err(RuntimeError::new(
3986 "parse_int() requires string and base 2-36",
3987 )),
3988 }
3989 });
3990}
3991
3992fn register_cycle(interp: &mut Interpreter) {
3998 define(interp, "cycle", Some(2), |_, args| {
4000 match (&args[0], &args[1]) {
4001 (Value::Int(value), Value::Int(modulus)) if *modulus > 0 => {
4002 let normalized = value.rem_euclid(*modulus);
4003 Ok(Value::Tuple(Rc::new(vec![
4004 Value::Int(normalized),
4005 Value::Int(*modulus),
4006 ])))
4007 }
4008 _ => Err(RuntimeError::new(
4009 "cycle() requires value and positive modulus",
4010 )),
4011 }
4012 });
4013
4014 define(interp, "mod_add", Some(3), |_, args| {
4016 match (&args[0], &args[1], &args[2]) {
4017 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4018 Ok(Value::Int((a + b).rem_euclid(*m)))
4019 }
4020 _ => Err(RuntimeError::new(
4021 "mod_add() requires two integers and positive modulus",
4022 )),
4023 }
4024 });
4025
4026 define(interp, "mod_sub", Some(3), |_, args| {
4028 match (&args[0], &args[1], &args[2]) {
4029 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4030 Ok(Value::Int((a - b).rem_euclid(*m)))
4031 }
4032 _ => Err(RuntimeError::new(
4033 "mod_sub() requires two integers and positive modulus",
4034 )),
4035 }
4036 });
4037
4038 define(interp, "mod_mul", Some(3), |_, args| {
4040 match (&args[0], &args[1], &args[2]) {
4041 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4042 Ok(Value::Int((a * b).rem_euclid(*m)))
4043 }
4044 _ => Err(RuntimeError::new(
4045 "mod_mul() requires two integers and positive modulus",
4046 )),
4047 }
4048 });
4049
4050 define(interp, "mod_pow", Some(3), |_, args| {
4052 match (&args[0], &args[1], &args[2]) {
4053 (Value::Int(base), Value::Int(exp), Value::Int(m)) if *m > 0 && *exp >= 0 => {
4054 Ok(Value::Int(mod_pow(*base, *exp as u64, *m)))
4055 }
4056 _ => Err(RuntimeError::new(
4057 "mod_pow() requires base, non-negative exp, and positive modulus",
4058 )),
4059 }
4060 });
4061
4062 define(interp, "mod_inv", Some(2), |_, args| {
4064 match (&args[0], &args[1]) {
4065 (Value::Int(a), Value::Int(m)) if *m > 0 => match mod_inverse(*a, *m) {
4066 Some(inv) => Ok(Value::Int(inv)),
4067 None => Err(RuntimeError::new(format!(
4068 "no modular inverse of {} mod {}",
4069 a, m
4070 ))),
4071 },
4072 _ => Err(RuntimeError::new(
4073 "mod_inv() requires integer and positive modulus",
4074 )),
4075 }
4076 });
4077
4078 define(interp, "octave", Some(1), |_, args| {
4081 match &args[0] {
4082 Value::Int(note) => Ok(Value::Int(note.rem_euclid(12))),
4083 Value::Float(freq) => {
4084 let semitones = 12.0 * (freq / 440.0).log2();
4086 Ok(Value::Float(semitones.rem_euclid(12.0)))
4087 }
4088 _ => Err(RuntimeError::new("octave() requires number")),
4089 }
4090 });
4091
4092 define(interp, "interval", Some(2), |_, args| {
4094 match (&args[0], &args[1]) {
4095 (Value::Int(a), Value::Int(b)) => Ok(Value::Int((b - a).rem_euclid(12))),
4096 _ => Err(RuntimeError::new("interval() requires two integers")),
4097 }
4098 });
4099
4100 define(interp, "cents", Some(1), |_, args| match &args[0] {
4102 Value::Int(semitones) => Ok(Value::Int(*semitones * 100)),
4103 Value::Float(semitones) => Ok(Value::Float(*semitones * 100.0)),
4104 _ => Err(RuntimeError::new("cents() requires number")),
4105 });
4106
4107 define(interp, "freq", Some(1), |_, args| match &args[0] {
4109 Value::Int(midi) => {
4110 let freq = 440.0 * 2.0_f64.powf((*midi as f64 - 69.0) / 12.0);
4111 Ok(Value::Float(freq))
4112 }
4113 _ => Err(RuntimeError::new("freq() requires integer MIDI note")),
4114 });
4115
4116 define(interp, "midi", Some(1), |_, args| match &args[0] {
4118 Value::Float(freq) if *freq > 0.0 => {
4119 let midi = 69.0 + 12.0 * (freq / 440.0).log2();
4120 Ok(Value::Int(midi.round() as i64))
4121 }
4122 Value::Int(freq) if *freq > 0 => {
4123 let midi = 69.0 + 12.0 * (*freq as f64 / 440.0).log2();
4124 Ok(Value::Int(midi.round() as i64))
4125 }
4126 _ => Err(RuntimeError::new("midi() requires positive frequency")),
4127 });
4128}
4129
4130fn mod_pow(mut base: i64, mut exp: u64, modulus: i64) -> i64 {
4132 if modulus == 1 {
4133 return 0;
4134 }
4135 let mut result: i64 = 1;
4136 base = base.rem_euclid(modulus);
4137 while exp > 0 {
4138 if exp % 2 == 1 {
4139 result = (result * base).rem_euclid(modulus);
4140 }
4141 exp /= 2;
4142 base = (base * base).rem_euclid(modulus);
4143 }
4144 result
4145}
4146
4147fn register_simd(interp: &mut Interpreter) {
4153 define(interp, "simd_new", Some(4), |_, args| {
4155 let values: Result<Vec<f64>, _> = args
4156 .iter()
4157 .map(|v| match v {
4158 Value::Float(f) => Ok(*f),
4159 Value::Int(i) => Ok(*i as f64),
4160 _ => Err(RuntimeError::new("simd_new() requires numbers")),
4161 })
4162 .collect();
4163 let values = values?;
4164 Ok(Value::Array(Rc::new(RefCell::new(vec![
4165 Value::Float(values[0]),
4166 Value::Float(values[1]),
4167 Value::Float(values[2]),
4168 Value::Float(values[3]),
4169 ]))))
4170 });
4171
4172 define(interp, "simd_splat", Some(1), |_, args| {
4174 let v = match &args[0] {
4175 Value::Float(f) => *f,
4176 Value::Int(i) => *i as f64,
4177 _ => return Err(RuntimeError::new("simd_splat() requires number")),
4178 };
4179 Ok(Value::Array(Rc::new(RefCell::new(vec![
4180 Value::Float(v),
4181 Value::Float(v),
4182 Value::Float(v),
4183 Value::Float(v),
4184 ]))))
4185 });
4186
4187 define(interp, "simd_add", Some(2), |_, args| {
4189 simd_binary_op(&args[0], &args[1], |a, b| a + b, "simd_add")
4190 });
4191
4192 define(interp, "simd_sub", Some(2), |_, args| {
4194 simd_binary_op(&args[0], &args[1], |a, b| a - b, "simd_sub")
4195 });
4196
4197 define(interp, "simd_mul", Some(2), |_, args| {
4199 simd_binary_op(&args[0], &args[1], |a, b| a * b, "simd_mul")
4200 });
4201
4202 define(interp, "simd_div", Some(2), |_, args| {
4204 simd_binary_op(&args[0], &args[1], |a, b| a / b, "simd_div")
4205 });
4206
4207 define(interp, "simd_dot", Some(2), |_, args| {
4209 let a = extract_simd(&args[0], "simd_dot")?;
4210 let b = extract_simd(&args[1], "simd_dot")?;
4211 let dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
4212 Ok(Value::Float(dot))
4213 });
4214
4215 define(interp, "simd_cross", Some(2), |_, args| {
4217 let a = extract_simd(&args[0], "simd_cross")?;
4218 let b = extract_simd(&args[1], "simd_cross")?;
4219 Ok(Value::Array(Rc::new(RefCell::new(vec![
4220 Value::Float(a[1] * b[2] - a[2] * b[1]),
4221 Value::Float(a[2] * b[0] - a[0] * b[2]),
4222 Value::Float(a[0] * b[1] - a[1] * b[0]),
4223 Value::Float(0.0),
4224 ]))))
4225 });
4226
4227 define(interp, "simd_length", Some(1), |_, args| {
4229 let v = extract_simd(&args[0], "simd_length")?;
4230 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
4231 Ok(Value::Float(len_sq.sqrt()))
4232 });
4233
4234 define(interp, "simd_normalize", Some(1), |_, args| {
4236 let v = extract_simd(&args[0], "simd_normalize")?;
4237 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
4238 let len = len_sq.sqrt();
4239 if len < 1e-10 {
4240 return Ok(Value::Array(Rc::new(RefCell::new(vec![
4241 Value::Float(0.0),
4242 Value::Float(0.0),
4243 Value::Float(0.0),
4244 Value::Float(0.0),
4245 ]))));
4246 }
4247 let inv_len = 1.0 / len;
4248 Ok(Value::Array(Rc::new(RefCell::new(vec![
4249 Value::Float(v[0] * inv_len),
4250 Value::Float(v[1] * inv_len),
4251 Value::Float(v[2] * inv_len),
4252 Value::Float(v[3] * inv_len),
4253 ]))))
4254 });
4255
4256 define(interp, "simd_min", Some(2), |_, args| {
4258 simd_binary_op(&args[0], &args[1], |a, b| a.min(b), "simd_min")
4259 });
4260
4261 define(interp, "simd_max", Some(2), |_, args| {
4263 simd_binary_op(&args[0], &args[1], |a, b| a.max(b), "simd_max")
4264 });
4265
4266 define(interp, "simd_hadd", Some(1), |_, args| {
4268 let v = extract_simd(&args[0], "simd_hadd")?;
4269 Ok(Value::Float(v[0] + v[1] + v[2] + v[3]))
4270 });
4271
4272 define(interp, "simd_extract", Some(2), |_, args| {
4274 let v = extract_simd(&args[0], "simd_extract")?;
4275 let idx = match &args[1] {
4276 Value::Int(i) => *i as usize,
4277 _ => return Err(RuntimeError::new("simd_extract() requires integer index")),
4278 };
4279 if idx > 3 {
4280 return Err(RuntimeError::new("simd_extract() index must be 0-3"));
4281 }
4282 Ok(Value::Float(v[idx]))
4283 });
4284
4285 define(interp, "simd_free", Some(1), |_, _| Ok(Value::Null));
4287
4288 define(interp, "simd_lerp", Some(3), |_, args| {
4290 let a = extract_simd(&args[0], "simd_lerp")?;
4291 let b = extract_simd(&args[1], "simd_lerp")?;
4292 let t = match &args[2] {
4293 Value::Float(f) => *f,
4294 Value::Int(i) => *i as f64,
4295 _ => return Err(RuntimeError::new("simd_lerp() requires float t")),
4296 };
4297 let one_t = 1.0 - t;
4298 Ok(Value::Array(Rc::new(RefCell::new(vec![
4299 Value::Float(a[0] * one_t + b[0] * t),
4300 Value::Float(a[1] * one_t + b[1] * t),
4301 Value::Float(a[2] * one_t + b[2] * t),
4302 Value::Float(a[3] * one_t + b[3] * t),
4303 ]))))
4304 });
4305}
4306
4307fn extract_simd(val: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
4309 match val {
4310 Value::Array(arr) => {
4311 let arr = arr.borrow();
4312 if arr.len() < 4 {
4313 return Err(RuntimeError::new(format!(
4314 "{}() requires 4-element array",
4315 fn_name
4316 )));
4317 }
4318 let mut result = [0.0; 4];
4319 for (i, v) in arr.iter().take(4).enumerate() {
4320 result[i] = match v {
4321 Value::Float(f) => *f,
4322 Value::Int(n) => *n as f64,
4323 _ => {
4324 return Err(RuntimeError::new(format!(
4325 "{}() requires numeric array",
4326 fn_name
4327 )))
4328 }
4329 };
4330 }
4331 Ok(result)
4332 }
4333 _ => Err(RuntimeError::new(format!(
4334 "{}() requires array argument",
4335 fn_name
4336 ))),
4337 }
4338}
4339
4340fn simd_binary_op<F>(a: &Value, b: &Value, op: F, fn_name: &str) -> Result<Value, RuntimeError>
4342where
4343 F: Fn(f64, f64) -> f64,
4344{
4345 let a = extract_simd(a, fn_name)?;
4346 let b = extract_simd(b, fn_name)?;
4347 Ok(Value::Array(Rc::new(RefCell::new(vec![
4348 Value::Float(op(a[0], b[0])),
4349 Value::Float(op(a[1], b[1])),
4350 Value::Float(op(a[2], b[2])),
4351 Value::Float(op(a[3], b[3])),
4352 ]))))
4353}
4354
4355fn register_graphics_math(interp: &mut Interpreter) {
4366 define(interp, "quat_new", Some(4), |_, args| {
4374 let w = extract_number(&args[0], "quat_new")?;
4375 let x = extract_number(&args[1], "quat_new")?;
4376 let y = extract_number(&args[2], "quat_new")?;
4377 let z = extract_number(&args[3], "quat_new")?;
4378 Ok(make_vec4(w, x, y, z))
4379 });
4380
4381 define(interp, "quat_identity", Some(0), |_, _| {
4383 Ok(make_vec4(1.0, 0.0, 0.0, 0.0))
4384 });
4385
4386 define(interp, "quat_from_axis_angle", Some(2), |_, args| {
4388 let axis = extract_vec3(&args[0], "quat_from_axis_angle")?;
4389 let angle = extract_number(&args[1], "quat_from_axis_angle")?;
4390
4391 let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
4393 if len < 1e-10 {
4394 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0)); }
4396 let ax = axis[0] / len;
4397 let ay = axis[1] / len;
4398 let az = axis[2] / len;
4399
4400 let half_angle = angle / 2.0;
4401 let s = half_angle.sin();
4402 let c = half_angle.cos();
4403
4404 Ok(make_vec4(c, ax * s, ay * s, az * s))
4405 });
4406
4407 define(interp, "quat_from_euler", Some(3), |_, args| {
4410 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();
4415 let (sy, cy) = (yaw / 2.0).sin_cos();
4416 let (sr, cr) = (roll / 2.0).sin_cos();
4417
4418 let w = cp * cy * cr + sp * sy * sr;
4420 let x = sp * cy * cr - cp * sy * sr;
4421 let y = cp * sy * cr + sp * cy * sr;
4422 let z = cp * cy * sr - sp * sy * cr;
4423
4424 Ok(make_vec4(w, x, y, z))
4425 });
4426
4427 define(interp, "quat_mul", Some(2), |_, args| {
4429 let q1 = extract_vec4(&args[0], "quat_mul")?;
4430 let q2 = extract_vec4(&args[1], "quat_mul")?;
4431
4432 let w = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
4434 let x = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
4435 let y = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
4436 let z = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
4437
4438 Ok(make_vec4(w, x, y, z))
4439 });
4440
4441 define(interp, "quat_conjugate", Some(1), |_, args| {
4443 let q = extract_vec4(&args[0], "quat_conjugate")?;
4444 Ok(make_vec4(q[0], -q[1], -q[2], -q[3]))
4445 });
4446
4447 define(interp, "quat_inverse", Some(1), |_, args| {
4449 let q = extract_vec4(&args[0], "quat_inverse")?;
4450 let norm_sq = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
4451 if norm_sq < 1e-10 {
4452 return Err(RuntimeError::new(
4453 "quat_inverse: cannot invert zero quaternion",
4454 ));
4455 }
4456 Ok(make_vec4(
4457 q[0] / norm_sq,
4458 -q[1] / norm_sq,
4459 -q[2] / norm_sq,
4460 -q[3] / norm_sq,
4461 ))
4462 });
4463
4464 define(interp, "quat_normalize", Some(1), |_, args| {
4466 let q = extract_vec4(&args[0], "quat_normalize")?;
4467 let len = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]).sqrt();
4468 if len < 1e-10 {
4469 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0));
4470 }
4471 Ok(make_vec4(q[0] / len, q[1] / len, q[2] / len, q[3] / len))
4472 });
4473
4474 define(interp, "quat_rotate", Some(2), |_, args| {
4476 let q = extract_vec4(&args[0], "quat_rotate")?;
4477 let v = extract_vec3(&args[1], "quat_rotate")?;
4478
4479 let qw = q[0];
4481 let qx = q[1];
4482 let qy = q[2];
4483 let qz = q[3];
4484 let vx = v[0];
4485 let vy = v[1];
4486 let vz = v[2];
4487
4488 let tx = 2.0 * (qy * vz - qz * vy);
4490 let ty = 2.0 * (qz * vx - qx * vz);
4491 let tz = 2.0 * (qx * vy - qy * vx);
4492
4493 let rx = vx + qw * tx + (qy * tz - qz * ty);
4495 let ry = vy + qw * ty + (qz * tx - qx * tz);
4496 let rz = vz + qw * tz + (qx * ty - qy * tx);
4497
4498 Ok(make_vec3(rx, ry, rz))
4499 });
4500
4501 define(interp, "quat_slerp", Some(3), |_, args| {
4503 let q1 = extract_vec4(&args[0], "quat_slerp")?;
4504 let mut q2 = extract_vec4(&args[1], "quat_slerp")?;
4505 let t = extract_number(&args[2], "quat_slerp")?;
4506
4507 let mut dot = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
4509
4510 if dot < 0.0 {
4512 q2 = [-q2[0], -q2[1], -q2[2], -q2[3]];
4513 dot = -dot;
4514 }
4515
4516 if dot > 0.9995 {
4518 let w = q1[0] + t * (q2[0] - q1[0]);
4519 let x = q1[1] + t * (q2[1] - q1[1]);
4520 let y = q1[2] + t * (q2[2] - q1[2]);
4521 let z = q1[3] + t * (q2[3] - q1[3]);
4522 let len = (w * w + x * x + y * y + z * z).sqrt();
4523 return Ok(make_vec4(w / len, x / len, y / len, z / len));
4524 }
4525
4526 let theta_0 = dot.acos();
4528 let theta = theta_0 * t;
4529 let sin_theta = theta.sin();
4530 let sin_theta_0 = theta_0.sin();
4531
4532 let s0 = (theta_0 - theta).cos() - dot * sin_theta / sin_theta_0;
4533 let s1 = sin_theta / sin_theta_0;
4534
4535 Ok(make_vec4(
4536 s0 * q1[0] + s1 * q2[0],
4537 s0 * q1[1] + s1 * q2[1],
4538 s0 * q1[2] + s1 * q2[2],
4539 s0 * q1[3] + s1 * q2[3],
4540 ))
4541 });
4542
4543 define(interp, "quat_to_euler", Some(1), |_, args| {
4545 let q = extract_vec4(&args[0], "quat_to_euler")?;
4546 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4547
4548 let sinr_cosp = 2.0 * (w * x + y * z);
4550 let cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
4551 let roll = sinr_cosp.atan2(cosr_cosp);
4552
4553 let sinp = 2.0 * (w * y - z * x);
4555 let pitch = if sinp.abs() >= 1.0 {
4556 std::f64::consts::FRAC_PI_2.copysign(sinp)
4557 } else {
4558 sinp.asin()
4559 };
4560
4561 let siny_cosp = 2.0 * (w * z + x * y);
4563 let cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
4564 let yaw = siny_cosp.atan2(cosy_cosp);
4565
4566 Ok(make_vec3(pitch, yaw, roll))
4567 });
4568
4569 define(interp, "quat_to_mat4", Some(1), |_, args| {
4571 let q = extract_vec4(&args[0], "quat_to_mat4")?;
4572 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4573
4574 let xx = x * x;
4575 let yy = y * y;
4576 let zz = z * z;
4577 let xy = x * y;
4578 let xz = x * z;
4579 let yz = y * z;
4580 let wx = w * x;
4581 let wy = w * y;
4582 let wz = w * z;
4583
4584 Ok(make_mat4([
4586 1.0 - 2.0 * (yy + zz),
4587 2.0 * (xy + wz),
4588 2.0 * (xz - wy),
4589 0.0,
4590 2.0 * (xy - wz),
4591 1.0 - 2.0 * (xx + zz),
4592 2.0 * (yz + wx),
4593 0.0,
4594 2.0 * (xz + wy),
4595 2.0 * (yz - wx),
4596 1.0 - 2.0 * (xx + yy),
4597 0.0,
4598 0.0,
4599 0.0,
4600 0.0,
4601 1.0,
4602 ]))
4603 });
4604
4605 define(interp, "vec2", Some(2), |_, args| {
4611 let x = extract_number(&args[0], "vec2")?;
4612 let y = extract_number(&args[1], "vec2")?;
4613 Ok(make_vec2(x, y))
4614 });
4615
4616 define(interp, "vec3", Some(3), |_, args| {
4618 let x = extract_number(&args[0], "vec3")?;
4619 let y = extract_number(&args[1], "vec3")?;
4620 let z = extract_number(&args[2], "vec3")?;
4621 Ok(make_vec3(x, y, z))
4622 });
4623
4624 define(interp, "vec4", Some(4), |_, args| {
4626 let x = extract_number(&args[0], "vec4")?;
4627 let y = extract_number(&args[1], "vec4")?;
4628 let z = extract_number(&args[2], "vec4")?;
4629 let w = extract_number(&args[3], "vec4")?;
4630 Ok(make_vec4(x, y, z, w))
4631 });
4632
4633 define(interp, "vec3_add", Some(2), |_, args| {
4635 let a = extract_vec3(&args[0], "vec3_add")?;
4636 let b = extract_vec3(&args[1], "vec3_add")?;
4637 Ok(make_vec3(a[0] + b[0], a[1] + b[1], a[2] + b[2]))
4638 });
4639
4640 define(interp, "vec3_sub", Some(2), |_, args| {
4642 let a = extract_vec3(&args[0], "vec3_sub")?;
4643 let b = extract_vec3(&args[1], "vec3_sub")?;
4644 Ok(make_vec3(a[0] - b[0], a[1] - b[1], a[2] - b[2]))
4645 });
4646
4647 define(interp, "vec3_scale", Some(2), |_, args| {
4649 let v = extract_vec3(&args[0], "vec3_scale")?;
4650 let s = extract_number(&args[1], "vec3_scale")?;
4651 Ok(make_vec3(v[0] * s, v[1] * s, v[2] * s))
4652 });
4653
4654 define(interp, "vec3_dot", Some(2), |_, args| {
4656 let a = extract_vec3(&args[0], "vec3_dot")?;
4657 let b = extract_vec3(&args[1], "vec3_dot")?;
4658 Ok(Value::Float(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]))
4659 });
4660
4661 define(interp, "vec3_cross", Some(2), |_, args| {
4663 let a = extract_vec3(&args[0], "vec3_cross")?;
4664 let b = extract_vec3(&args[1], "vec3_cross")?;
4665 Ok(make_vec3(
4666 a[1] * b[2] - a[2] * b[1],
4667 a[2] * b[0] - a[0] * b[2],
4668 a[0] * b[1] - a[1] * b[0],
4669 ))
4670 });
4671
4672 define(interp, "vec3_length", Some(1), |_, args| {
4674 let v = extract_vec3(&args[0], "vec3_length")?;
4675 Ok(Value::Float(
4676 (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt(),
4677 ))
4678 });
4679
4680 define(interp, "vec3_normalize", Some(1), |_, args| {
4682 let v = extract_vec3(&args[0], "vec3_normalize")?;
4683 let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
4684 if len < 1e-10 {
4685 return Ok(make_vec3(0.0, 0.0, 0.0));
4686 }
4687 Ok(make_vec3(v[0] / len, v[1] / len, v[2] / len))
4688 });
4689
4690 define(interp, "vec3_lerp", Some(3), |_, args| {
4692 let a = extract_vec3(&args[0], "vec3_lerp")?;
4693 let b = extract_vec3(&args[1], "vec3_lerp")?;
4694 let t = extract_number(&args[2], "vec3_lerp")?;
4695 Ok(make_vec3(
4696 a[0] + t * (b[0] - a[0]),
4697 a[1] + t * (b[1] - a[1]),
4698 a[2] + t * (b[2] - a[2]),
4699 ))
4700 });
4701
4702 define(interp, "vec3_reflect", Some(2), |_, args| {
4704 let i = extract_vec3(&args[0], "vec3_reflect")?;
4705 let n = extract_vec3(&args[1], "vec3_reflect")?;
4706 let dot = i[0] * n[0] + i[1] * n[1] + i[2] * n[2];
4707 Ok(make_vec3(
4708 i[0] - 2.0 * dot * n[0],
4709 i[1] - 2.0 * dot * n[1],
4710 i[2] - 2.0 * dot * n[2],
4711 ))
4712 });
4713
4714 define(interp, "vec3_refract", Some(3), |_, args| {
4716 let i = extract_vec3(&args[0], "vec3_refract")?;
4717 let n = extract_vec3(&args[1], "vec3_refract")?;
4718 let eta = extract_number(&args[2], "vec3_refract")?;
4719
4720 let dot = i[0] * n[0] + i[1] * n[1] + i[2] * n[2];
4721 let k = 1.0 - eta * eta * (1.0 - dot * dot);
4722
4723 if k < 0.0 {
4724 return Ok(make_vec3(0.0, 0.0, 0.0));
4726 }
4727
4728 let coeff = eta * dot + k.sqrt();
4729 Ok(make_vec3(
4730 eta * i[0] - coeff * n[0],
4731 eta * i[1] - coeff * n[1],
4732 eta * i[2] - coeff * n[2],
4733 ))
4734 });
4735
4736 define(interp, "vec4_dot", Some(2), |_, args| {
4738 let a = extract_vec4(&args[0], "vec4_dot")?;
4739 let b = extract_vec4(&args[1], "vec4_dot")?;
4740 Ok(Value::Float(
4741 a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3],
4742 ))
4743 });
4744
4745 define(interp, "mat4_identity", Some(0), |_, _| {
4751 Ok(make_mat4([
4752 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,
4753 ]))
4754 });
4755
4756 define(interp, "mat4_mul", Some(2), |_, args| {
4758 let a = extract_mat4(&args[0], "mat4_mul")?;
4759 let b = extract_mat4(&args[1], "mat4_mul")?;
4760
4761 let mut result = [0.0f64; 16];
4762 for col in 0..4 {
4763 for row in 0..4 {
4764 let mut sum = 0.0;
4765 for k in 0..4 {
4766 sum += a[k * 4 + row] * b[col * 4 + k];
4767 }
4768 result[col * 4 + row] = sum;
4769 }
4770 }
4771 Ok(make_mat4(result))
4772 });
4773
4774 define(interp, "mat4_transform", Some(2), |_, args| {
4776 let m = extract_mat4(&args[0], "mat4_transform")?;
4777 let v = extract_vec4(&args[1], "mat4_transform")?;
4778
4779 Ok(make_vec4(
4780 m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3],
4781 m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3],
4782 m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3],
4783 m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3],
4784 ))
4785 });
4786
4787 define(interp, "mat4_translate", Some(3), |_, args| {
4789 let tx = extract_number(&args[0], "mat4_translate")?;
4790 let ty = extract_number(&args[1], "mat4_translate")?;
4791 let tz = extract_number(&args[2], "mat4_translate")?;
4792 Ok(make_mat4([
4793 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,
4794 ]))
4795 });
4796
4797 define(interp, "mat4_scale", Some(3), |_, args| {
4799 let sx = extract_number(&args[0], "mat4_scale")?;
4800 let sy = extract_number(&args[1], "mat4_scale")?;
4801 let sz = extract_number(&args[2], "mat4_scale")?;
4802 Ok(make_mat4([
4803 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,
4804 ]))
4805 });
4806
4807 define(interp, "mat4_rotate_x", Some(1), |_, args| {
4809 let angle = extract_number(&args[0], "mat4_rotate_x")?;
4810 let (s, c) = angle.sin_cos();
4811 Ok(make_mat4([
4812 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,
4813 ]))
4814 });
4815
4816 define(interp, "mat4_rotate_y", Some(1), |_, args| {
4818 let angle = extract_number(&args[0], "mat4_rotate_y")?;
4819 let (s, c) = angle.sin_cos();
4820 Ok(make_mat4([
4821 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,
4822 ]))
4823 });
4824
4825 define(interp, "mat4_rotate_z", Some(1), |_, args| {
4827 let angle = extract_number(&args[0], "mat4_rotate_z")?;
4828 let (s, c) = angle.sin_cos();
4829 Ok(make_mat4([
4830 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,
4831 ]))
4832 });
4833
4834 define(interp, "mat4_perspective", Some(4), |_, args| {
4836 let fov_y = extract_number(&args[0], "mat4_perspective")?;
4837 let aspect = extract_number(&args[1], "mat4_perspective")?;
4838 let near = extract_number(&args[2], "mat4_perspective")?;
4839 let far = extract_number(&args[3], "mat4_perspective")?;
4840
4841 let f = 1.0 / (fov_y / 2.0).tan();
4842 let nf = 1.0 / (near - far);
4843
4844 Ok(make_mat4([
4845 f / aspect,
4846 0.0,
4847 0.0,
4848 0.0,
4849 0.0,
4850 f,
4851 0.0,
4852 0.0,
4853 0.0,
4854 0.0,
4855 (far + near) * nf,
4856 -1.0,
4857 0.0,
4858 0.0,
4859 2.0 * far * near * nf,
4860 0.0,
4861 ]))
4862 });
4863
4864 define(interp, "mat4_ortho", Some(6), |_, args| {
4866 let left = extract_number(&args[0], "mat4_ortho")?;
4867 let right = extract_number(&args[1], "mat4_ortho")?;
4868 let bottom = extract_number(&args[2], "mat4_ortho")?;
4869 let top = extract_number(&args[3], "mat4_ortho")?;
4870 let near = extract_number(&args[4], "mat4_ortho")?;
4871 let far = extract_number(&args[5], "mat4_ortho")?;
4872
4873 let lr = 1.0 / (left - right);
4874 let bt = 1.0 / (bottom - top);
4875 let nf = 1.0 / (near - far);
4876
4877 Ok(make_mat4([
4878 -2.0 * lr,
4879 0.0,
4880 0.0,
4881 0.0,
4882 0.0,
4883 -2.0 * bt,
4884 0.0,
4885 0.0,
4886 0.0,
4887 0.0,
4888 2.0 * nf,
4889 0.0,
4890 (left + right) * lr,
4891 (top + bottom) * bt,
4892 (far + near) * nf,
4893 1.0,
4894 ]))
4895 });
4896
4897 define(interp, "mat4_look_at", Some(3), |_, args| {
4899 let eye = extract_vec3(&args[0], "mat4_look_at")?;
4900 let center = extract_vec3(&args[1], "mat4_look_at")?;
4901 let up = extract_vec3(&args[2], "mat4_look_at")?;
4902
4903 let fx = center[0] - eye[0];
4905 let fy = center[1] - eye[1];
4906 let fz = center[2] - eye[2];
4907 let flen = (fx * fx + fy * fy + fz * fz).sqrt();
4908 let (fx, fy, fz) = (fx / flen, fy / flen, fz / flen);
4909
4910 let rx = fy * up[2] - fz * up[1];
4912 let ry = fz * up[0] - fx * up[2];
4913 let rz = fx * up[1] - fy * up[0];
4914 let rlen = (rx * rx + ry * ry + rz * rz).sqrt();
4915 let (rx, ry, rz) = (rx / rlen, ry / rlen, rz / rlen);
4916
4917 let ux = ry * fz - rz * fy;
4919 let uy = rz * fx - rx * fz;
4920 let uz = rx * fy - ry * fx;
4921
4922 Ok(make_mat4([
4923 rx,
4924 ux,
4925 -fx,
4926 0.0,
4927 ry,
4928 uy,
4929 -fy,
4930 0.0,
4931 rz,
4932 uz,
4933 -fz,
4934 0.0,
4935 -(rx * eye[0] + ry * eye[1] + rz * eye[2]),
4936 -(ux * eye[0] + uy * eye[1] + uz * eye[2]),
4937 -(-fx * eye[0] - fy * eye[1] - fz * eye[2]),
4938 1.0,
4939 ]))
4940 });
4941
4942 define(interp, "mat4_inverse", Some(1), |_, args| {
4944 let m = extract_mat4(&args[0], "mat4_inverse")?;
4945
4946 let a00 = m[0];
4948 let a01 = m[1];
4949 let a02 = m[2];
4950 let a03 = m[3];
4951 let a10 = m[4];
4952 let a11 = m[5];
4953 let a12 = m[6];
4954 let a13 = m[7];
4955 let a20 = m[8];
4956 let a21 = m[9];
4957 let a22 = m[10];
4958 let a23 = m[11];
4959 let a30 = m[12];
4960 let a31 = m[13];
4961 let a32 = m[14];
4962 let a33 = m[15];
4963
4964 let b00 = a00 * a11 - a01 * a10;
4965 let b01 = a00 * a12 - a02 * a10;
4966 let b02 = a00 * a13 - a03 * a10;
4967 let b03 = a01 * a12 - a02 * a11;
4968 let b04 = a01 * a13 - a03 * a11;
4969 let b05 = a02 * a13 - a03 * a12;
4970 let b06 = a20 * a31 - a21 * a30;
4971 let b07 = a20 * a32 - a22 * a30;
4972 let b08 = a20 * a33 - a23 * a30;
4973 let b09 = a21 * a32 - a22 * a31;
4974 let b10 = a21 * a33 - a23 * a31;
4975 let b11 = a22 * a33 - a23 * a32;
4976
4977 let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
4978
4979 if det.abs() < 1e-10 {
4980 return Err(RuntimeError::new("mat4_inverse: singular matrix"));
4981 }
4982
4983 let inv_det = 1.0 / det;
4984
4985 Ok(make_mat4([
4986 (a11 * b11 - a12 * b10 + a13 * b09) * inv_det,
4987 (a02 * b10 - a01 * b11 - a03 * b09) * inv_det,
4988 (a31 * b05 - a32 * b04 + a33 * b03) * inv_det,
4989 (a22 * b04 - a21 * b05 - a23 * b03) * inv_det,
4990 (a12 * b08 - a10 * b11 - a13 * b07) * inv_det,
4991 (a00 * b11 - a02 * b08 + a03 * b07) * inv_det,
4992 (a32 * b02 - a30 * b05 - a33 * b01) * inv_det,
4993 (a20 * b05 - a22 * b02 + a23 * b01) * inv_det,
4994 (a10 * b10 - a11 * b08 + a13 * b06) * inv_det,
4995 (a01 * b08 - a00 * b10 - a03 * b06) * inv_det,
4996 (a30 * b04 - a31 * b02 + a33 * b00) * inv_det,
4997 (a21 * b02 - a20 * b04 - a23 * b00) * inv_det,
4998 (a11 * b07 - a10 * b09 - a12 * b06) * inv_det,
4999 (a00 * b09 - a01 * b07 + a02 * b06) * inv_det,
5000 (a31 * b01 - a30 * b03 - a32 * b00) * inv_det,
5001 (a20 * b03 - a21 * b01 + a22 * b00) * inv_det,
5002 ]))
5003 });
5004
5005 define(interp, "mat4_transpose", Some(1), |_, args| {
5007 let m = extract_mat4(&args[0], "mat4_transpose")?;
5008 Ok(make_mat4([
5009 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],
5010 m[11], m[15],
5011 ]))
5012 });
5013
5014 define(interp, "mat3_identity", Some(0), |_, _| {
5020 Ok(make_mat3([1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]))
5021 });
5022
5023 define(interp, "mat3_from_mat4", Some(1), |_, args| {
5025 let m = extract_mat4(&args[0], "mat3_from_mat4")?;
5026 Ok(make_mat3([
5027 m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10],
5028 ]))
5029 });
5030
5031 define(interp, "mat3_mul", Some(2), |_, args| {
5033 let a = extract_mat3(&args[0], "mat3_mul")?;
5034 let b = extract_mat3(&args[1], "mat3_mul")?;
5035
5036 let mut result = [0.0f64; 9];
5037 for col in 0..3 {
5038 for row in 0..3 {
5039 let mut sum = 0.0;
5040 for k in 0..3 {
5041 sum += a[k * 3 + row] * b[col * 3 + k];
5042 }
5043 result[col * 3 + row] = sum;
5044 }
5045 }
5046 Ok(make_mat3(result))
5047 });
5048
5049 define(interp, "mat3_transform", Some(2), |_, args| {
5051 let m = extract_mat3(&args[0], "mat3_transform")?;
5052 let v = extract_vec3(&args[1], "mat3_transform")?;
5053
5054 Ok(make_vec3(
5055 m[0] * v[0] + m[3] * v[1] + m[6] * v[2],
5056 m[1] * v[0] + m[4] * v[1] + m[7] * v[2],
5057 m[2] * v[0] + m[5] * v[1] + m[8] * v[2],
5058 ))
5059 });
5060
5061 define(interp, "mat3_inverse", Some(1), |_, args| {
5063 let m = extract_mat3(&args[0], "mat3_inverse")?;
5064
5065 let det = m[0] * (m[4] * m[8] - m[5] * m[7]) - m[3] * (m[1] * m[8] - m[2] * m[7])
5066 + m[6] * (m[1] * m[5] - m[2] * m[4]);
5067
5068 if det.abs() < 1e-10 {
5069 return Err(RuntimeError::new("mat3_inverse: singular matrix"));
5070 }
5071
5072 let inv_det = 1.0 / det;
5073
5074 Ok(make_mat3([
5075 (m[4] * m[8] - m[5] * m[7]) * inv_det,
5076 (m[2] * m[7] - m[1] * m[8]) * inv_det,
5077 (m[1] * m[5] - m[2] * m[4]) * inv_det,
5078 (m[5] * m[6] - m[3] * m[8]) * inv_det,
5079 (m[0] * m[8] - m[2] * m[6]) * inv_det,
5080 (m[2] * m[3] - m[0] * m[5]) * inv_det,
5081 (m[3] * m[7] - m[4] * m[6]) * inv_det,
5082 (m[1] * m[6] - m[0] * m[7]) * inv_det,
5083 (m[0] * m[4] - m[1] * m[3]) * inv_det,
5084 ]))
5085 });
5086
5087 define(interp, "mat3_transpose", Some(1), |_, args| {
5089 let m = extract_mat3(&args[0], "mat3_transpose")?;
5090 Ok(make_mat3([
5091 m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8],
5092 ]))
5093 });
5094}
5095
5096fn extract_number(v: &Value, fn_name: &str) -> Result<f64, RuntimeError> {
5098 match v {
5099 Value::Float(f) => Ok(*f),
5100 Value::Int(i) => Ok(*i as f64),
5101 _ => Err(RuntimeError::new(format!(
5102 "{}() requires number argument",
5103 fn_name
5104 ))),
5105 }
5106}
5107
5108fn extract_vec2(v: &Value, fn_name: &str) -> Result<[f64; 2], RuntimeError> {
5109 match v {
5110 Value::Array(arr) => {
5111 let arr = arr.borrow();
5112 if arr.len() < 2 {
5113 return Err(RuntimeError::new(format!(
5114 "{}() requires vec2 (2 elements)",
5115 fn_name
5116 )));
5117 }
5118 Ok([
5119 extract_number(&arr[0], fn_name)?,
5120 extract_number(&arr[1], fn_name)?,
5121 ])
5122 }
5123 _ => Err(RuntimeError::new(format!(
5124 "{}() requires vec2 array",
5125 fn_name
5126 ))),
5127 }
5128}
5129
5130fn extract_vec3(v: &Value, fn_name: &str) -> Result<[f64; 3], RuntimeError> {
5131 match v {
5132 Value::Array(arr) => {
5133 let arr = arr.borrow();
5134 if arr.len() < 3 {
5135 return Err(RuntimeError::new(format!(
5136 "{}() requires vec3 (3 elements)",
5137 fn_name
5138 )));
5139 }
5140 Ok([
5141 extract_number(&arr[0], fn_name)?,
5142 extract_number(&arr[1], fn_name)?,
5143 extract_number(&arr[2], fn_name)?,
5144 ])
5145 }
5146 _ => Err(RuntimeError::new(format!(
5147 "{}() requires vec3 array",
5148 fn_name
5149 ))),
5150 }
5151}
5152
5153fn extract_vec4(v: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
5154 match v {
5155 Value::Array(arr) => {
5156 let arr = arr.borrow();
5157 if arr.len() < 4 {
5158 return Err(RuntimeError::new(format!(
5159 "{}() requires vec4 (4 elements)",
5160 fn_name
5161 )));
5162 }
5163 Ok([
5164 extract_number(&arr[0], fn_name)?,
5165 extract_number(&arr[1], fn_name)?,
5166 extract_number(&arr[2], fn_name)?,
5167 extract_number(&arr[3], fn_name)?,
5168 ])
5169 }
5170 _ => Err(RuntimeError::new(format!(
5171 "{}() requires vec4 array",
5172 fn_name
5173 ))),
5174 }
5175}
5176
5177fn extract_mat3(v: &Value, fn_name: &str) -> Result<[f64; 9], RuntimeError> {
5178 match v {
5179 Value::Array(arr) => {
5180 let arr = arr.borrow();
5181 if arr.len() < 9 {
5182 return Err(RuntimeError::new(format!(
5183 "{}() requires mat3 (9 elements)",
5184 fn_name
5185 )));
5186 }
5187 let mut result = [0.0f64; 9];
5188 for i in 0..9 {
5189 result[i] = extract_number(&arr[i], fn_name)?;
5190 }
5191 Ok(result)
5192 }
5193 _ => Err(RuntimeError::new(format!(
5194 "{}() requires mat3 array",
5195 fn_name
5196 ))),
5197 }
5198}
5199
5200fn extract_mat4(v: &Value, fn_name: &str) -> Result<[f64; 16], RuntimeError> {
5201 match v {
5202 Value::Array(arr) => {
5203 let arr = arr.borrow();
5204 if arr.len() < 16 {
5205 return Err(RuntimeError::new(format!(
5206 "{}() requires mat4 (16 elements)",
5207 fn_name
5208 )));
5209 }
5210 let mut result = [0.0f64; 16];
5211 for i in 0..16 {
5212 result[i] = extract_number(&arr[i], fn_name)?;
5213 }
5214 Ok(result)
5215 }
5216 _ => Err(RuntimeError::new(format!(
5217 "{}() requires mat4 array",
5218 fn_name
5219 ))),
5220 }
5221}
5222
5223fn make_vec2(x: f64, y: f64) -> Value {
5224 Value::Array(Rc::new(RefCell::new(vec![
5225 Value::Float(x),
5226 Value::Float(y),
5227 ])))
5228}
5229
5230fn make_vec3(x: f64, y: f64, z: f64) -> Value {
5231 Value::Array(Rc::new(RefCell::new(vec![
5232 Value::Float(x),
5233 Value::Float(y),
5234 Value::Float(z),
5235 ])))
5236}
5237
5238fn make_vec3_arr(v: [f64; 3]) -> Value {
5240 make_vec3(v[0], v[1], v[2])
5241}
5242
5243fn make_vec4(x: f64, y: f64, z: f64, w: f64) -> Value {
5244 Value::Array(Rc::new(RefCell::new(vec![
5245 Value::Float(x),
5246 Value::Float(y),
5247 Value::Float(z),
5248 Value::Float(w),
5249 ])))
5250}
5251
5252fn make_mat3(m: [f64; 9]) -> Value {
5253 Value::Array(Rc::new(RefCell::new(
5254 m.iter().map(|&v| Value::Float(v)).collect(),
5255 )))
5256}
5257
5258fn make_mat4(m: [f64; 16]) -> Value {
5259 Value::Array(Rc::new(RefCell::new(
5260 m.iter().map(|&v| Value::Float(v)).collect(),
5261 )))
5262}
5263
5264fn register_concurrency(interp: &mut Interpreter) {
5281 define(interp, "channel_new", Some(0), |_, _| {
5285 let (sender, receiver) = mpsc::channel();
5286 let inner = ChannelInner {
5287 sender: Mutex::new(sender),
5288 receiver: Mutex::new(receiver),
5289 };
5290 Ok(Value::Channel(Arc::new(inner)))
5291 });
5292
5293 define(interp, "channel_send", Some(2), |_, args| {
5295 let channel = match &args[0] {
5296 Value::Channel(ch) => ch.clone(),
5297 _ => {
5298 return Err(RuntimeError::new(
5299 "channel_send() requires channel as first argument",
5300 ))
5301 }
5302 };
5303 let value = args[1].clone();
5304
5305 let sender = channel
5306 .sender
5307 .lock()
5308 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5309 sender
5310 .send(value)
5311 .map_err(|_| RuntimeError::new("channel_send() failed: receiver dropped"))?;
5312
5313 Ok(Value::Null)
5314 });
5315
5316 define(interp, "channel_recv", Some(1), |_, args| {
5318 let channel = match &args[0] {
5319 Value::Channel(ch) => ch.clone(),
5320 _ => {
5321 return Err(RuntimeError::new(
5322 "channel_recv() requires channel argument",
5323 ))
5324 }
5325 };
5326
5327 let receiver = channel
5328 .receiver
5329 .lock()
5330 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5331 match receiver.recv() {
5332 Ok(value) => Ok(value),
5333 Err(_) => Err(RuntimeError::new("channel_recv() failed: sender dropped")),
5334 }
5335 });
5336
5337 define(interp, "channel_try_recv", Some(1), |_, args| {
5339 let channel = match &args[0] {
5340 Value::Channel(ch) => ch.clone(),
5341 _ => {
5342 return Err(RuntimeError::new(
5343 "channel_try_recv() requires channel argument",
5344 ))
5345 }
5346 };
5347
5348 let receiver = channel
5349 .receiver
5350 .lock()
5351 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5352 match receiver.try_recv() {
5353 Ok(value) => {
5354 Ok(Value::Variant {
5356 enum_name: "Option".to_string(),
5357 variant_name: "Some".to_string(),
5358 fields: Some(Rc::new(vec![value])),
5359 })
5360 }
5361 Err(mpsc::TryRecvError::Empty) => {
5362 Ok(Value::Variant {
5364 enum_name: "Option".to_string(),
5365 variant_name: "None".to_string(),
5366 fields: None,
5367 })
5368 }
5369 Err(mpsc::TryRecvError::Disconnected) => Err(RuntimeError::new(
5370 "channel_try_recv() failed: sender dropped",
5371 )),
5372 }
5373 });
5374
5375 define(interp, "channel_recv_timeout", Some(2), |_, args| {
5377 let channel = match &args[0] {
5378 Value::Channel(ch) => ch.clone(),
5379 _ => {
5380 return Err(RuntimeError::new(
5381 "channel_recv_timeout() requires a channel as first argument.\n\
5382 Create a channel with channel_new():\n\
5383 let ch = channel_new();\n\
5384 channel_send(ch, value);\n\
5385 let result = channel_recv_timeout(ch, 1000); // 1 second timeout",
5386 ))
5387 }
5388 };
5389 let timeout_ms = match &args[1] {
5390 Value::Int(ms) => *ms as u64,
5391 _ => {
5392 return Err(RuntimeError::new(
5393 "channel_recv_timeout() requires timeout in milliseconds (integer).\n\
5394 Example: channel_recv_timeout(ch, 1000) // Wait up to 1 second",
5395 ))
5396 }
5397 };
5398
5399 let receiver = channel
5400 .receiver
5401 .lock()
5402 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5403 match receiver.recv_timeout(std::time::Duration::from_millis(timeout_ms)) {
5404 Ok(value) => Ok(Value::Variant {
5405 enum_name: "Option".to_string(),
5406 variant_name: "Some".to_string(),
5407 fields: Some(Rc::new(vec![value])),
5408 }),
5409 Err(mpsc::RecvTimeoutError::Timeout) => Ok(Value::Variant {
5410 enum_name: "Option".to_string(),
5411 variant_name: "None".to_string(),
5412 fields: None,
5413 }),
5414 Err(mpsc::RecvTimeoutError::Disconnected) => Err(RuntimeError::new(
5415 "channel_recv_timeout() failed: sender dropped",
5416 )),
5417 }
5418 });
5419
5420 define(interp, "thread_spawn_detached", Some(0), |_, _| {
5428 thread::spawn(|| {
5431 });
5433 Ok(Value::Null)
5434 });
5435
5436 define(interp, "std·thread·spawn", Some(1), |interp, args| {
5440 match &args[0] {
5442 Value::Function(f) => {
5443 match interp.call_function(f, vec![]) {
5446 Ok(_) => {}
5447 Err(e) => eprintln!("[Sigil thread] Error: {}", e),
5448 }
5449 let mut map = HashMap::new();
5451 map.insert("__type__".to_string(), Value::String(Rc::new("JoinHandle".to_string())));
5452 map.insert("done".to_string(), Value::Bool(true));
5453 Ok(Value::Map(Rc::new(RefCell::new(map))))
5454 }
5455 Value::BuiltIn(b) => {
5456 match (b.func)(interp, vec![]) {
5457 Ok(_) => {}
5458 Err(e) => eprintln!("[Sigil thread] Error: {}", e),
5459 }
5460 let mut map = HashMap::new();
5461 map.insert("__type__".to_string(), Value::String(Rc::new("JoinHandle".to_string())));
5462 map.insert("done".to_string(), Value::Bool(true));
5463 Ok(Value::Map(Rc::new(RefCell::new(map))))
5464 }
5465 _ => Err(RuntimeError::new("std::thread::spawn requires a closure")),
5466 }
5467 });
5468
5469 define(interp, "thread_join", Some(1), |_, args| {
5472 match &args[0] {
5473 Value::ThreadHandle(h) => {
5474 let mut guard = h
5475 .lock()
5476 .map_err(|_| RuntimeError::new("thread handle mutex poisoned"))?;
5477 if let Some(handle) = guard.take() {
5478 match handle.join() {
5479 Ok(v) => Ok(v),
5480 Err(_) => Err(RuntimeError::new("thread panicked")),
5481 }
5482 } else {
5483 Err(RuntimeError::new("thread already joined"))
5484 }
5485 }
5486 _ => Ok(args[0].clone()),
5488 }
5489 });
5490
5491 define(interp, "thread_sleep", Some(1), |_, args| {
5493 let ms = match &args[0] {
5494 Value::Int(ms) => *ms as u64,
5495 Value::Float(ms) => *ms as u64,
5496 _ => {
5497 return Err(RuntimeError::new(
5498 "thread_sleep() requires integer milliseconds",
5499 ))
5500 }
5501 };
5502
5503 thread::sleep(std::time::Duration::from_millis(ms));
5504 Ok(Value::Null)
5505 });
5506
5507 define(interp, "thread_yield", Some(0), |_, _| {
5509 thread::yield_now();
5510 Ok(Value::Null)
5511 });
5512
5513 define(interp, "thread_id", Some(0), |_, _| {
5515 let id = thread::current().id();
5516 Ok(Value::String(Rc::new(format!("{:?}", id))))
5517 });
5518
5519 define(interp, "parking_lot·Mutex·new", Some(1), |_, args| {
5523 let mut map = HashMap::new();
5524 map.insert("__type__".to_string(), Value::String(Rc::new("Mutex".to_string())));
5525 map.insert("inner".to_string(), args[0].clone());
5526 Ok(Value::Map(Rc::new(RefCell::new(map))))
5527 });
5528
5529 define(interp, "std·sync·Mutex·new", Some(1), |_, args| {
5531 let mut map = HashMap::new();
5532 map.insert("__type__".to_string(), Value::String(Rc::new("Mutex".to_string())));
5533 map.insert("inner".to_string(), args[0].clone());
5534 Ok(Value::Map(Rc::new(RefCell::new(map))))
5535 });
5536
5537 define(interp, "parking_lot·RwLock·new", Some(1), |_, args| {
5539 let mut map = HashMap::new();
5540 map.insert("__type__".to_string(), Value::String(Rc::new("RwLock".to_string())));
5541 map.insert("inner".to_string(), args[0].clone());
5542 Ok(Value::Map(Rc::new(RefCell::new(map))))
5543 });
5544
5545 define(interp, "std·sync·RwLock·new", Some(1), |_, args| {
5547 let mut map = HashMap::new();
5548 map.insert("__type__".to_string(), Value::String(Rc::new("RwLock".to_string())));
5549 map.insert("inner".to_string(), args[0].clone());
5550 Ok(Value::Map(Rc::new(RefCell::new(map))))
5551 });
5552
5553 define(interp, "RwLock·new", Some(1), |_, args| {
5555 let mut map = HashMap::new();
5556 map.insert("__type__".to_string(), Value::String(Rc::new("RwLock".to_string())));
5557 map.insert("inner".to_string(), args[0].clone());
5558 Ok(Value::Map(Rc::new(RefCell::new(map))))
5559 });
5560
5561 define(interp, "AtomicU64·new", Some(1), |_, args| {
5563 let val = match &args[0] {
5564 Value::Int(i) => *i,
5565 _ => 0,
5566 };
5567 let mut map = HashMap::new();
5568 map.insert("__type__".to_string(), Value::String(Rc::new("AtomicU64".to_string())));
5569 map.insert("value".to_string(), Value::Int(val));
5570 Ok(Value::Map(Rc::new(RefCell::new(map))))
5571 });
5572
5573 define(interp, "std·sync·atomic·AtomicU64·new", Some(1), |_, args| {
5575 let val = match &args[0] {
5576 Value::Int(i) => *i,
5577 _ => 0,
5578 };
5579 let mut map = HashMap::new();
5580 map.insert("__type__".to_string(), Value::String(Rc::new("AtomicU64".to_string())));
5581 map.insert("value".to_string(), Value::Int(val));
5582 Ok(Value::Map(Rc::new(RefCell::new(map))))
5583 });
5584
5585 define(interp, "AtomicBool·new", Some(1), |_, args| {
5587 let val = match &args[0] {
5588 Value::Bool(b) => *b,
5589 _ => false,
5590 };
5591 let mut map = HashMap::new();
5592 map.insert("__type__".to_string(), Value::String(Rc::new("AtomicBool".to_string())));
5593 map.insert("value".to_string(), Value::Bool(val));
5594 Ok(Value::Map(Rc::new(RefCell::new(map))))
5595 });
5596
5597 define(interp, "std·sync·atomic·AtomicBool·new", Some(1), |_, args| {
5598 let val = match &args[0] {
5599 Value::Bool(b) => *b,
5600 _ => false,
5601 };
5602 let mut map = HashMap::new();
5603 map.insert("__type__".to_string(), Value::String(Rc::new("AtomicBool".to_string())));
5604 map.insert("value".to_string(), Value::Bool(val));
5605 Ok(Value::Map(Rc::new(RefCell::new(map))))
5606 });
5607
5608 define(interp, "Arc·new", Some(1), |_, args| {
5610 let mut map = HashMap::new();
5611 map.insert("__type__".to_string(), Value::String(Rc::new("Arc".to_string())));
5612 map.insert("inner".to_string(), args[0].clone());
5613 Ok(Value::Map(Rc::new(RefCell::new(map))))
5614 });
5615
5616 define(interp, "std·sync·Arc·new", Some(1), |_, args| {
5617 let mut map = HashMap::new();
5618 map.insert("__type__".to_string(), Value::String(Rc::new("Arc".to_string())));
5619 map.insert("inner".to_string(), args[0].clone());
5620 Ok(Value::Map(Rc::new(RefCell::new(map))))
5621 });
5622
5623 define(interp, "TcpListener·bind", Some(1), |_, args| {
5628 let addr_str = match &args[0] {
5629 Value::String(s) => s.to_string(),
5630 Value::Ref(r) => {
5631 if let Value::String(s) = &*r.borrow() {
5632 s.to_string()
5633 } else {
5634 return Err(RuntimeError::new("TcpListener::bind requires string address"));
5635 }
5636 }
5637 Value::Map(m) => {
5639 let borrowed = m.borrow();
5640 if let Some(Value::String(addr)) = borrowed.get("addr") {
5641 addr.to_string()
5642 } else if let Some(Value::String(_)) = borrowed.get("__type__") {
5643 if let Some(Value::String(addr)) = borrowed.get("addr") {
5645 addr.to_string()
5646 } else {
5647 return Err(RuntimeError::new("SocketAddr missing addr field"));
5648 }
5649 } else {
5650 return Err(RuntimeError::new("TcpListener::bind requires string or SocketAddr"));
5651 }
5652 }
5653 _ => return Err(RuntimeError::new("TcpListener::bind requires string address")),
5654 };
5655
5656 let addr: std::net::SocketAddr = match addr_str.parse() {
5658 Ok(a) => a,
5659 Err(e) => return Err(RuntimeError::new(format!("Invalid address: {}", e))),
5660 };
5661
5662 let listener = match std::net::TcpListener::bind(addr) {
5664 Ok(l) => l,
5665 Err(e) => return Err(RuntimeError::new(format!("Failed to bind: {}", e))),
5666 };
5667
5668 let local_addr = listener.local_addr().map(|a| a.to_string()).unwrap_or_default();
5669
5670 let listener_id = store_listener(listener);
5672
5673 let mut map = HashMap::new();
5674 map.insert("__type__".to_string(), Value::String(Rc::new("TcpListener".to_string())));
5675 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5676 map.insert("local_addr".to_string(), Value::String(Rc::new(local_addr)));
5677 map.insert("__listener_id__".to_string(), Value::Int(listener_id as i64));
5678
5679 eprintln!("[Sigil] TcpListener bound to {} (id={})", addr, listener_id);
5680
5681 Ok(Value::Variant {
5682 enum_name: "Result".to_string(),
5683 variant_name: "Ok".to_string(),
5684 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5685 })
5686 });
5687
5688 define(interp, "std·net·TcpListener·bind", Some(1), |_, args| {
5689 let addr_str = match &args[0] {
5690 Value::String(s) => s.to_string(),
5691 Value::Ref(r) => {
5692 if let Value::String(s) = &*r.borrow() {
5693 s.to_string()
5694 } else {
5695 return Err(RuntimeError::new("TcpListener::bind requires string address"));
5696 }
5697 }
5698 Value::Map(m) => {
5700 let borrowed = m.borrow();
5701 if let Some(Value::String(addr)) = borrowed.get("addr") {
5702 addr.to_string()
5703 } else {
5704 return Err(RuntimeError::new("TcpListener::bind requires string or SocketAddr"));
5705 }
5706 }
5707 _ => return Err(RuntimeError::new("TcpListener::bind requires string address")),
5708 };
5709
5710 let addr: std::net::SocketAddr = match addr_str.parse() {
5711 Ok(a) => a,
5712 Err(e) => return Err(RuntimeError::new(format!("Invalid address: {}", e))),
5713 };
5714
5715 let _listener = match std::net::TcpListener::bind(addr) {
5716 Ok(l) => l,
5717 Err(e) => return Err(RuntimeError::new(format!("Failed to bind: {}", e))),
5718 };
5719
5720 let mut map = HashMap::new();
5721 map.insert("__type__".to_string(), Value::String(Rc::new("TcpListener".to_string())));
5722 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5723
5724 eprintln!("[Sigil] TcpListener bound to {}", addr);
5725
5726 Ok(Value::Variant {
5727 enum_name: "Result".to_string(),
5728 variant_name: "Ok".to_string(),
5729 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5730 })
5731 });
5732
5733 define(interp, "SocketAddr·parse", Some(1), |_, args| {
5735 let addr_str = match &args[0] {
5736 Value::String(s) => s.to_string(),
5737 _ => return Err(RuntimeError::new("SocketAddr::parse requires string")),
5738 };
5739
5740 match addr_str.parse::<std::net::SocketAddr>() {
5741 Ok(_) => {
5742 let mut map = HashMap::new();
5743 map.insert("__type__".to_string(), Value::String(Rc::new("SocketAddr".to_string())));
5744 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5745 Ok(Value::Variant {
5746 enum_name: "Result".to_string(),
5747 variant_name: "Ok".to_string(),
5748 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5749 })
5750 }
5751 Err(e) => Ok(Value::Variant {
5752 enum_name: "Result".to_string(),
5753 variant_name: "Err".to_string(),
5754 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5755 }),
5756 }
5757 });
5758
5759 define(interp, "TcpStream·peer_addr", Some(1), |_, args| {
5764 let stream_id = match &args[0] {
5765 Value::Map(m) => {
5766 let borrowed = m.borrow();
5767 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5768 *id as u64
5769 } else {
5770 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5771 }
5772 }
5773 _ => return Err(RuntimeError::new("peer_addr requires TcpStream")),
5774 };
5775
5776 if let Some(guard) = get_stream_registry().lock().ok() {
5777 if let Some(stream) = guard.get(&stream_id) {
5778 match stream.peer_addr() {
5779 Ok(addr) => {
5780 let mut map = HashMap::new();
5781 map.insert("__type__".to_string(), Value::String(Rc::new("SocketAddr".to_string())));
5782 map.insert("addr".to_string(), Value::String(Rc::new(addr.to_string())));
5783 Ok(Value::Variant {
5784 enum_name: "Result".to_string(),
5785 variant_name: "Ok".to_string(),
5786 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5787 })
5788 }
5789 Err(e) => Ok(Value::Variant {
5790 enum_name: "Result".to_string(),
5791 variant_name: "Err".to_string(),
5792 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5793 }),
5794 }
5795 } else {
5796 Err(RuntimeError::new("TcpStream not found in registry"))
5797 }
5798 } else {
5799 Err(RuntimeError::new("Failed to lock stream registry"))
5800 }
5801 });
5802
5803 define(interp, "TcpStream·read", Some(2), |_, args| {
5805 use std::io::Read;
5806
5807 let stream_id = match &args[0] {
5808 Value::Map(m) => {
5809 let borrowed = m.borrow();
5810 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5811 *id as u64
5812 } else {
5813 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5814 }
5815 }
5816 _ => return Err(RuntimeError::new("read requires TcpStream")),
5817 };
5818
5819 let size = match &args[1] {
5820 Value::Int(n) => *n as usize,
5821 _ => return Err(RuntimeError::new("read requires size as integer")),
5822 };
5823
5824 if let Some(mut guard) = get_stream_registry().lock().ok() {
5825 if let Some(stream) = guard.get_mut(&stream_id) {
5826 let mut buf = vec![0u8; size];
5827 match stream.read(&mut buf) {
5828 Ok(n) => {
5829 buf.truncate(n);
5830 let bytes: Vec<Value> = buf.iter().map(|b| Value::Int(*b as i64)).collect();
5831 Ok(Value::Variant {
5832 enum_name: "Result".to_string(),
5833 variant_name: "Ok".to_string(),
5834 fields: Some(Rc::new(vec![Value::Array(Rc::new(RefCell::new(bytes)))])),
5835 })
5836 }
5837 Err(e) => Ok(Value::Variant {
5838 enum_name: "Result".to_string(),
5839 variant_name: "Err".to_string(),
5840 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5841 }),
5842 }
5843 } else {
5844 Err(RuntimeError::new("TcpStream not found in registry"))
5845 }
5846 } else {
5847 Err(RuntimeError::new("Failed to lock stream registry"))
5848 }
5849 });
5850
5851 define(interp, "TcpStream·read_exact", Some(2), |_, args| {
5853 use std::io::Read;
5854
5855 let stream_id = match &args[0] {
5856 Value::Map(m) => {
5857 let borrowed = m.borrow();
5858 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5859 *id as u64
5860 } else {
5861 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5862 }
5863 }
5864 _ => return Err(RuntimeError::new("read_exact requires TcpStream")),
5865 };
5866
5867 let size = match &args[1] {
5868 Value::Int(n) => *n as usize,
5869 _ => return Err(RuntimeError::new("read_exact requires size as integer")),
5870 };
5871
5872 if let Some(mut guard) = get_stream_registry().lock().ok() {
5873 if let Some(stream) = guard.get_mut(&stream_id) {
5874 let mut buf = vec![0u8; size];
5875 match stream.read_exact(&mut buf) {
5876 Ok(()) => {
5877 let bytes: Vec<Value> = buf.iter().map(|b| Value::Int(*b as i64)).collect();
5878 Ok(Value::Variant {
5879 enum_name: "Result".to_string(),
5880 variant_name: "Ok".to_string(),
5881 fields: Some(Rc::new(vec![Value::Array(Rc::new(RefCell::new(bytes)))])),
5882 })
5883 }
5884 Err(e) => Ok(Value::Variant {
5885 enum_name: "Result".to_string(),
5886 variant_name: "Err".to_string(),
5887 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5888 }),
5889 }
5890 } else {
5891 Err(RuntimeError::new("TcpStream not found in registry"))
5892 }
5893 } else {
5894 Err(RuntimeError::new("Failed to lock stream registry"))
5895 }
5896 });
5897
5898 define(interp, "TcpStream·write_all", Some(2), |_, args| {
5900 use std::io::Write;
5901
5902 let stream_id = match &args[0] {
5903 Value::Map(m) => {
5904 let borrowed = m.borrow();
5905 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5906 *id as u64
5907 } else {
5908 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5909 }
5910 }
5911 _ => return Err(RuntimeError::new("write_all requires TcpStream")),
5912 };
5913
5914 let data: Vec<u8> = match &args[1] {
5916 Value::String(s) => s.as_bytes().to_vec(),
5917 Value::Array(arr) => {
5918 arr.borrow().iter().filter_map(|v| {
5919 if let Value::Int(n) = v { Some(*n as u8) } else { None }
5920 }).collect()
5921 }
5922 Value::Ref(r) => {
5923 if let Value::String(s) = &*r.borrow() {
5924 s.as_bytes().to_vec()
5925 } else {
5926 return Err(RuntimeError::new("write_all requires string or byte array"));
5927 }
5928 }
5929 _ => return Err(RuntimeError::new("write_all requires string or byte array")),
5930 };
5931
5932 if let Some(mut guard) = get_stream_registry().lock().ok() {
5933 if let Some(stream) = guard.get_mut(&stream_id) {
5934 match stream.write_all(&data) {
5935 Ok(()) => Ok(Value::Variant {
5936 enum_name: "Result".to_string(),
5937 variant_name: "Ok".to_string(),
5938 fields: Some(Rc::new(vec![Value::Null])),
5939 }),
5940 Err(e) => Ok(Value::Variant {
5941 enum_name: "Result".to_string(),
5942 variant_name: "Err".to_string(),
5943 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5944 }),
5945 }
5946 } else {
5947 Err(RuntimeError::new("TcpStream not found in registry"))
5948 }
5949 } else {
5950 Err(RuntimeError::new("Failed to lock stream registry"))
5951 }
5952 });
5953
5954 define(interp, "TcpStream·flush", Some(1), |_, args| {
5956 use std::io::Write;
5957
5958 let stream_id = match &args[0] {
5959 Value::Map(m) => {
5960 let borrowed = m.borrow();
5961 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5962 *id as u64
5963 } else {
5964 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5965 }
5966 }
5967 _ => return Err(RuntimeError::new("flush requires TcpStream")),
5968 };
5969
5970 if let Some(mut guard) = get_stream_registry().lock().ok() {
5971 if let Some(stream) = guard.get_mut(&stream_id) {
5972 match stream.flush() {
5973 Ok(()) => Ok(Value::Variant {
5974 enum_name: "Result".to_string(),
5975 variant_name: "Ok".to_string(),
5976 fields: Some(Rc::new(vec![Value::Null])),
5977 }),
5978 Err(e) => Ok(Value::Variant {
5979 enum_name: "Result".to_string(),
5980 variant_name: "Err".to_string(),
5981 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5982 }),
5983 }
5984 } else {
5985 Err(RuntimeError::new("TcpStream not found in registry"))
5986 }
5987 } else {
5988 Err(RuntimeError::new("Failed to lock stream registry"))
5989 }
5990 });
5991
5992 define(interp, "BufReader·new", Some(1), |_, args| {
5996 use std::io::BufReader as StdBufReader;
5997
5998 let stream_id = match &args[0] {
6000 Value::Map(m) => {
6001 let borrowed = m.borrow();
6002 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6003 *id as u64
6004 } else {
6005 return Err(RuntimeError::new("BufReader::new requires TcpStream"));
6006 }
6007 }
6008 Value::Ref(r) => {
6009 let inner = r.borrow();
6011 if let Value::Map(m) = &*inner {
6012 let borrowed = m.borrow();
6013 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6014 *id as u64
6015 } else {
6016 return Err(RuntimeError::new("BufReader::new requires TcpStream (missing stream_id in Ref)"));
6017 }
6018 } else {
6019 return Err(RuntimeError::new("BufReader::new requires TcpStream (Ref does not contain Map)"));
6020 }
6021 }
6022 _ => return Err(RuntimeError::new("BufReader::new requires TcpStream")),
6023 };
6024
6025 let reader_id = if let Some(mut guard) = get_stream_registry().lock().ok() {
6027 if let Some(stream) = guard.get_mut(&stream_id) {
6028 let stream_clone = match stream.try_clone() {
6029 Ok(s) => s,
6030 Err(e) => return Err(RuntimeError::new(format!("Failed to clone stream: {}", e))),
6031 };
6032 let reader = StdBufReader::new(stream_clone);
6033 store_bufreader(reader)
6034 } else {
6035 return Err(RuntimeError::new("Stream not found in registry"));
6036 }
6037 } else {
6038 return Err(RuntimeError::new("Failed to lock stream registry"));
6039 };
6040
6041 let mut map = HashMap::new();
6042 map.insert("__type__".to_string(), Value::String(Rc::new("BufReader".to_string())));
6043 map.insert("__stream_id__".to_string(), Value::Int(stream_id as i64));
6044 map.insert("__reader_id__".to_string(), Value::Int(reader_id as i64));
6045 Ok(Value::Map(Rc::new(RefCell::new(map))))
6046 });
6047
6048 define(interp, "BufReader·read_line", Some(1), |_, args| {
6050 use std::io::BufRead;
6051
6052 let reader_id = match &args[0] {
6053 Value::Map(m) => {
6054 let borrowed = m.borrow();
6055 if let Some(Value::Int(id)) = borrowed.get("__reader_id__") {
6056 *id as u64
6057 } else {
6058 return Err(RuntimeError::new("BufReader missing __reader_id__"));
6059 }
6060 }
6061 _ => return Err(RuntimeError::new("read_line requires BufReader")),
6062 };
6063
6064 if let Some(mut guard) = get_bufreader_registry().lock().ok() {
6065 if let Some(reader) = guard.get_mut(&reader_id) {
6066 let mut line = String::new();
6067
6068 match reader.read_line(&mut line) {
6069 Ok(n) => {
6070 if n == 0 {
6071 Ok(Value::Variant {
6073 enum_name: "Result".to_string(),
6074 variant_name: "Ok".to_string(),
6075 fields: Some(Rc::new(vec![Value::Null])),
6076 })
6077 } else {
6078 Ok(Value::Variant {
6079 enum_name: "Result".to_string(),
6080 variant_name: "Ok".to_string(),
6081 fields: Some(Rc::new(vec![Value::String(Rc::new(line))])),
6082 })
6083 }
6084 }
6085 Err(e) => Ok(Value::Variant {
6086 enum_name: "Result".to_string(),
6087 variant_name: "Err".to_string(),
6088 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
6089 }),
6090 }
6091 } else {
6092 Err(RuntimeError::new("BufReader not found in registry"))
6093 }
6094 } else {
6095 Err(RuntimeError::new("Failed to lock bufreader registry"))
6096 }
6097 });
6098
6099 define(interp, "styx_http·middleware·Logger·new", Some(0), |_, _| {
6104 let mut map = HashMap::new();
6105 map.insert("__type__".to_string(), Value::String(Rc::new("Logger".to_string())));
6106 map.insert("format".to_string(), Value::String(Rc::new("Common".to_string())));
6107 Ok(Value::Map(Rc::new(RefCell::new(map))))
6108 });
6109
6110 define(interp, "Logger·new", Some(0), |_, _| {
6111 let mut map = HashMap::new();
6112 map.insert("__type__".to_string(), Value::String(Rc::new("Logger".to_string())));
6113 map.insert("format".to_string(), Value::String(Rc::new("Common".to_string())));
6114 Ok(Value::Map(Rc::new(RefCell::new(map))))
6115 });
6116
6117 define(interp, "styx_http·middleware·Cors·new", Some(0), |_, _| {
6119 let mut map = HashMap::new();
6120 map.insert("__type__".to_string(), Value::String(Rc::new("Cors".to_string())));
6121 map.insert("origins".to_string(), Value::Array(Rc::new(RefCell::new(vec![]))));
6122 Ok(Value::Map(Rc::new(RefCell::new(map))))
6123 });
6124
6125 define(interp, "Cors·new", Some(0), |_, _| {
6126 let mut map = HashMap::new();
6127 map.insert("__type__".to_string(), Value::String(Rc::new("Cors".to_string())));
6128 map.insert("origins".to_string(), Value::Array(Rc::new(RefCell::new(vec![]))));
6129 Ok(Value::Map(Rc::new(RefCell::new(map))))
6130 });
6131
6132 define(interp, "styx_http·middleware·SecurityHeaders·new", Some(0), |_, _| {
6134 let mut map = HashMap::new();
6135 map.insert("__type__".to_string(), Value::String(Rc::new("SecurityHeaders".to_string())));
6136 Ok(Value::Map(Rc::new(RefCell::new(map))))
6137 });
6138
6139 define(interp, "SecurityHeaders·new", Some(0), |_, _| {
6140 let mut map = HashMap::new();
6141 map.insert("__type__".to_string(), Value::String(Rc::new("SecurityHeaders".to_string())));
6142 Ok(Value::Map(Rc::new(RefCell::new(map))))
6143 });
6144
6145 define(interp, "styx_http·middleware·RateLimiter·new", Some(0), |_, _| {
6147 let mut map = HashMap::new();
6148 map.insert("__type__".to_string(), Value::String(Rc::new("RateLimiter".to_string())));
6149 Ok(Value::Map(Rc::new(RefCell::new(map))))
6150 });
6151
6152 define(interp, "RateLimiter·new", Some(0), |_, _| {
6153 let mut map = HashMap::new();
6154 map.insert("__type__".to_string(), Value::String(Rc::new("RateLimiter".to_string())));
6155 Ok(Value::Map(Rc::new(RefCell::new(map))))
6156 });
6157
6158 define(interp, "styx_http·middleware·RateLimit·new", None, |_, args| {
6160 let mut map = HashMap::new();
6161 map.insert("__type__".to_string(), Value::String(Rc::new("RateLimit".to_string())));
6162 if args.len() >= 2 {
6163 map.insert("rate".to_string(), args[0].clone());
6164 map.insert("burst".to_string(), args[1].clone());
6165 }
6166 Ok(Value::Map(Rc::new(RefCell::new(map))))
6167 });
6168
6169 define(interp, "RateLimit·new", None, |_, args| {
6170 let mut map = HashMap::new();
6171 map.insert("__type__".to_string(), Value::String(Rc::new("RateLimit".to_string())));
6172 if args.len() >= 2 {
6173 map.insert("rate".to_string(), args[0].clone());
6174 map.insert("burst".to_string(), args[1].clone());
6175 }
6176 Ok(Value::Map(Rc::new(RefCell::new(map))))
6177 });
6178
6179 define(interp, "styx_http·middleware·Compression·new", Some(0), |_, _| {
6181 let mut map = HashMap::new();
6182 map.insert("__type__".to_string(), Value::String(Rc::new("Compression".to_string())));
6183 Ok(Value::Map(Rc::new(RefCell::new(map))))
6184 });
6185
6186 define(interp, "Compression·new", Some(0), |_, _| {
6187 let mut map = HashMap::new();
6188 map.insert("__type__".to_string(), Value::String(Rc::new("Compression".to_string())));
6189 Ok(Value::Map(Rc::new(RefCell::new(map))))
6190 });
6191
6192 define(interp, "AuthMiddleware·optional", Some(0), |_, _| {
6194 let mut map = HashMap::new();
6195 map.insert("__type__".to_string(), Value::String(Rc::new("AuthMiddleware".to_string())));
6196 map.insert("mode".to_string(), Value::String(Rc::new("optional".to_string())));
6197 Ok(Value::Map(Rc::new(RefCell::new(map))))
6198 });
6199
6200 define(interp, "AuthMiddleware·required", Some(0), |_, _| {
6201 let mut map = HashMap::new();
6202 map.insert("__type__".to_string(), Value::String(Rc::new("AuthMiddleware".to_string())));
6203 map.insert("mode".to_string(), Value::String(Rc::new("required".to_string())));
6204 Ok(Value::Map(Rc::new(RefCell::new(map))))
6205 });
6206
6207 define(interp, "AuthMiddleware·new", Some(0), |_, _| {
6208 let mut map = HashMap::new();
6209 map.insert("__type__".to_string(), Value::String(Rc::new("AuthMiddleware".to_string())));
6210 map.insert("mode".to_string(), Value::String(Rc::new("required".to_string())));
6211 Ok(Value::Map(Rc::new(RefCell::new(map))))
6212 });
6213
6214 define(interp, "spawn_actor", Some(1), |_, args| {
6221 let name = match &args[0] {
6222 Value::String(s) => s.to_string(),
6223 _ => return Err(RuntimeError::new("actor_spawn() requires string name")),
6224 };
6225
6226 let inner = ActorInner {
6227 name,
6228 message_queue: Mutex::new(Vec::new()),
6229 message_count: std::sync::atomic::AtomicUsize::new(0),
6230 };
6231
6232 Ok(Value::Actor(Arc::new(inner)))
6233 });
6234
6235 define(interp, "send_to_actor", Some(3), |_, args| {
6238 let actor = match &args[0] {
6239 Value::Actor(a) => a.clone(),
6240 _ => {
6241 return Err(RuntimeError::new(
6242 "actor_send() requires actor as first argument",
6243 ))
6244 }
6245 };
6246 let msg_type = match &args[1] {
6247 Value::String(s) => s.to_string(),
6248 _ => {
6249 return Err(RuntimeError::new(
6250 "actor_send() requires string message type",
6251 ))
6252 }
6253 };
6254 let msg_data = format!("{}", args[2]);
6255
6256 let mut queue = actor
6257 .message_queue
6258 .lock()
6259 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6260 queue.push((msg_type, msg_data));
6261 actor
6262 .message_count
6263 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
6264
6265 Ok(Value::Null)
6266 });
6267
6268 define(interp, "tell_actor", Some(3), |_, args| {
6270 let actor = match &args[0] {
6271 Value::Actor(a) => a.clone(),
6272 _ => {
6273 return Err(RuntimeError::new(
6274 "actor_tell() requires actor as first argument",
6275 ))
6276 }
6277 };
6278 let msg_type = match &args[1] {
6279 Value::String(s) => s.to_string(),
6280 _ => {
6281 return Err(RuntimeError::new(
6282 "actor_tell() requires string message type",
6283 ))
6284 }
6285 };
6286 let msg_data = format!("{}", args[2]);
6287
6288 let mut queue = actor
6289 .message_queue
6290 .lock()
6291 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6292 queue.push((msg_type, msg_data));
6293 actor
6294 .message_count
6295 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
6296
6297 Ok(Value::Null)
6298 });
6299
6300 define(interp, "recv_from_actor", Some(1), |_, args| {
6303 let actor = match &args[0] {
6304 Value::Actor(a) => a.clone(),
6305 _ => return Err(RuntimeError::new("actor_recv() requires actor argument")),
6306 };
6307
6308 let mut queue = actor
6309 .message_queue
6310 .lock()
6311 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6312 match queue.pop() {
6313 Some((msg_type, msg_data)) => {
6314 Ok(Value::Variant {
6316 enum_name: "Option".to_string(),
6317 variant_name: "Some".to_string(),
6318 fields: Some(Rc::new(vec![Value::Tuple(Rc::new(vec![
6319 Value::String(Rc::new(msg_type)),
6320 Value::String(Rc::new(msg_data)),
6321 ]))])),
6322 })
6323 }
6324 None => Ok(Value::Variant {
6325 enum_name: "Option".to_string(),
6326 variant_name: "None".to_string(),
6327 fields: None,
6328 }),
6329 }
6330 });
6331
6332 define(interp, "get_actor_msg_count", Some(1), |_, args| {
6334 let a = match &args[0] {
6335 Value::Actor(a) => a.clone(),
6336 _ => {
6337 return Err(RuntimeError::new(
6338 "get_actor_msg_count() requires actor argument",
6339 ))
6340 }
6341 };
6342
6343 let count = a.message_count.load(std::sync::atomic::Ordering::SeqCst);
6344 Ok(Value::Int(count as i64))
6345 });
6346
6347 define(interp, "get_actor_name", Some(1), |_, args| {
6349 let a = match &args[0] {
6350 Value::Actor(a) => a.clone(),
6351 _ => {
6352 return Err(RuntimeError::new(
6353 "get_actor_name() requires actor argument",
6354 ))
6355 }
6356 };
6357
6358 Ok(Value::String(Rc::new(a.name.clone())))
6359 });
6360
6361 define(interp, "get_actor_pending", Some(1), |_, args| {
6363 let a = match &args[0] {
6364 Value::Actor(a) => a.clone(),
6365 _ => {
6366 return Err(RuntimeError::new(
6367 "get_actor_pending() requires actor argument",
6368 ))
6369 }
6370 };
6371
6372 let queue = a
6373 .message_queue
6374 .lock()
6375 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6376 Ok(Value::Int(queue.len() as i64))
6377 });
6378
6379 define(interp, "mutex_new", Some(1), |_, args| {
6383 let value = args[0].clone();
6384 let mut map = std::collections::HashMap::new();
6386 map.insert("__mutex_value".to_string(), value);
6387 map.insert("__mutex_locked".to_string(), Value::Bool(false));
6388 Ok(Value::Map(Rc::new(RefCell::new(map))))
6389 });
6390
6391 define(interp, "mutex_lock", Some(1), |_, args| {
6393 let mutex = match &args[0] {
6394 Value::Map(m) => m.clone(),
6395 _ => return Err(RuntimeError::new("mutex_lock() requires mutex")),
6396 };
6397
6398 let mut map = mutex.borrow_mut();
6399 map.insert("__mutex_locked".to_string(), Value::Bool(true));
6401
6402 match map.get("__mutex_value") {
6403 Some(v) => Ok(v.clone()),
6404 None => Err(RuntimeError::new("invalid mutex")),
6405 }
6406 });
6407
6408 define(interp, "mutex_unlock", Some(2), |_, args| {
6410 let mutex = match &args[0] {
6411 Value::Map(m) => m.clone(),
6412 _ => return Err(RuntimeError::new("mutex_unlock() requires mutex")),
6413 };
6414 let new_value = args[1].clone();
6415
6416 let mut map = mutex.borrow_mut();
6417 map.insert("__mutex_value".to_string(), new_value);
6418 map.insert("__mutex_locked".to_string(), Value::Bool(false));
6419
6420 Ok(Value::Null)
6421 });
6422
6423 define(interp, "atomic_new", Some(1), |_, args| {
6425 let value = match &args[0] {
6426 Value::Int(i) => *i,
6427 _ => return Err(RuntimeError::new("atomic_new() requires integer")),
6428 };
6429
6430 let mut map = std::collections::HashMap::new();
6432 map.insert("__atomic_value".to_string(), Value::Int(value));
6433 Ok(Value::Map(Rc::new(RefCell::new(map))))
6434 });
6435
6436 define(interp, "atomic_load", Some(1), |_, args| {
6438 let atomic = match &args[0] {
6439 Value::Map(m) => m.clone(),
6440 _ => return Err(RuntimeError::new("atomic_load() requires atomic")),
6441 };
6442
6443 let map = atomic.borrow();
6444 match map.get("__atomic_value") {
6445 Some(v) => Ok(v.clone()),
6446 None => Err(RuntimeError::new("invalid atomic")),
6447 }
6448 });
6449
6450 define(interp, "atomic_store", Some(2), |_, args| {
6452 let atomic = match &args[0] {
6453 Value::Map(m) => m.clone(),
6454 _ => return Err(RuntimeError::new("atomic_store() requires atomic")),
6455 };
6456 let value = match &args[1] {
6457 Value::Int(i) => *i,
6458 _ => return Err(RuntimeError::new("atomic_store() requires integer value")),
6459 };
6460
6461 let mut map = atomic.borrow_mut();
6462 map.insert("__atomic_value".to_string(), Value::Int(value));
6463 Ok(Value::Null)
6464 });
6465
6466 define(interp, "atomic_add", Some(2), |_, args| {
6468 let atomic = match &args[0] {
6469 Value::Map(m) => m.clone(),
6470 _ => return Err(RuntimeError::new("atomic_add() requires atomic")),
6471 };
6472 let delta = match &args[1] {
6473 Value::Int(i) => *i,
6474 _ => return Err(RuntimeError::new("atomic_add() requires integer delta")),
6475 };
6476
6477 let mut map = atomic.borrow_mut();
6478 let old = match map.get("__atomic_value") {
6479 Some(Value::Int(i)) => *i,
6480 _ => return Err(RuntimeError::new("invalid atomic")),
6481 };
6482 map.insert("__atomic_value".to_string(), Value::Int(old + delta));
6483 Ok(Value::Int(old))
6484 });
6485
6486 define(interp, "atomic_cas", Some(3), |_, args| {
6488 let atomic = match &args[0] {
6489 Value::Map(m) => m.clone(),
6490 _ => return Err(RuntimeError::new("atomic_cas() requires atomic")),
6491 };
6492 let expected = match &args[1] {
6493 Value::Int(i) => *i,
6494 _ => return Err(RuntimeError::new("atomic_cas() requires integer expected")),
6495 };
6496 let new_value = match &args[2] {
6497 Value::Int(i) => *i,
6498 _ => return Err(RuntimeError::new("atomic_cas() requires integer new value")),
6499 };
6500
6501 let mut map = atomic.borrow_mut();
6502 let current = match map.get("__atomic_value") {
6503 Some(Value::Int(i)) => *i,
6504 _ => return Err(RuntimeError::new("invalid atomic")),
6505 };
6506
6507 if current == expected {
6508 map.insert("__atomic_value".to_string(), Value::Int(new_value));
6509 Ok(Value::Bool(true))
6510 } else {
6511 Ok(Value::Bool(false))
6512 }
6513 });
6514
6515 define(interp, "parallel_map", Some(2), |_, args| {
6519 let arr = match &args[0] {
6520 Value::Array(a) => a.borrow().clone(),
6521 _ => return Err(RuntimeError::new("parallel_map() requires array")),
6522 };
6523 let _func = args[1].clone();
6524
6525 Ok(Value::Array(Rc::new(RefCell::new(arr))))
6528 });
6529
6530 define(interp, "parallel_for", Some(3), |_, args| {
6532 let start = match &args[0] {
6533 Value::Int(i) => *i,
6534 _ => return Err(RuntimeError::new("parallel_for() requires integer start")),
6535 };
6536 let end = match &args[1] {
6537 Value::Int(i) => *i,
6538 _ => return Err(RuntimeError::new("parallel_for() requires integer end")),
6539 };
6540 let _func = args[2].clone();
6541
6542 let range: Vec<Value> = (start..end).map(|i| Value::Int(i)).collect();
6545 Ok(Value::Array(Rc::new(RefCell::new(range))))
6546 });
6547
6548 define(interp, "async_sleep", Some(1), |interp, args| {
6566 let ms = match &args[0] {
6567 Value::Int(ms) => *ms as u64,
6568 Value::Float(ms) => *ms as u64,
6569 _ => {
6570 return Err(RuntimeError::new(
6571 "async_sleep() requires integer milliseconds",
6572 ))
6573 }
6574 };
6575
6576 Ok(interp.make_future_timer(std::time::Duration::from_millis(ms)))
6577 });
6578
6579 define(interp, "future_ready", Some(1), |interp, args| {
6581 Ok(interp.make_future_immediate(args[0].clone()))
6582 });
6583
6584 define(interp, "future_pending", Some(0), |_, _| {
6586 Ok(Value::Future(Rc::new(RefCell::new(
6587 crate::interpreter::FutureInner {
6588 state: crate::interpreter::FutureState::Pending,
6589 computation: None,
6590 complete_at: None,
6591 },
6592 ))))
6593 });
6594
6595 define(interp, "is_future", Some(1), |_, args| {
6597 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
6598 });
6599
6600 define(interp, "is_ready", Some(1), |_, args| {
6602 match &args[0] {
6603 Value::Future(fut) => {
6604 let f = fut.borrow();
6605 Ok(Value::Bool(matches!(
6606 f.state,
6607 crate::interpreter::FutureState::Ready(_)
6608 )))
6609 }
6610 _ => Ok(Value::Bool(true)), }
6612 });
6613
6614 define(interp, "join_futures", Some(1), |_, args| {
6616 let futures = match &args[0] {
6617 Value::Array(arr) => {
6618 let arr = arr.borrow();
6619 let mut futs = Vec::new();
6620 for v in arr.iter() {
6621 match v {
6622 Value::Future(f) => futs.push(f.clone()),
6623 _ => {
6624 return Err(RuntimeError::new(
6625 "join_futures() requires array of futures",
6626 ))
6627 }
6628 }
6629 }
6630 futs
6631 }
6632 _ => {
6633 return Err(RuntimeError::new(
6634 "join_futures() requires array of futures",
6635 ))
6636 }
6637 };
6638
6639 Ok(Value::Future(Rc::new(RefCell::new(
6640 crate::interpreter::FutureInner {
6641 state: crate::interpreter::FutureState::Pending,
6642 computation: Some(crate::interpreter::FutureComputation::Join(futures)),
6643 complete_at: None,
6644 },
6645 ))))
6646 });
6647
6648 define(interp, "race_futures", Some(1), |_, args| {
6650 let futures = match &args[0] {
6651 Value::Array(arr) => {
6652 let arr = arr.borrow();
6653 let mut futs = Vec::new();
6654 for v in arr.iter() {
6655 match v {
6656 Value::Future(f) => futs.push(f.clone()),
6657 _ => {
6658 return Err(RuntimeError::new(
6659 "race_futures() requires array of futures",
6660 ))
6661 }
6662 }
6663 }
6664 futs
6665 }
6666 _ => {
6667 return Err(RuntimeError::new(
6668 "race_futures() requires array of futures",
6669 ))
6670 }
6671 };
6672
6673 Ok(Value::Future(Rc::new(RefCell::new(
6674 crate::interpreter::FutureInner {
6675 state: crate::interpreter::FutureState::Pending,
6676 computation: Some(crate::interpreter::FutureComputation::Race(futures)),
6677 complete_at: None,
6678 },
6679 ))))
6680 });
6681
6682 define(interp, "poll_future", Some(1), |_, args| {
6684 match &args[0] {
6685 Value::Future(fut) => {
6686 let f = fut.borrow();
6687 match &f.state {
6688 crate::interpreter::FutureState::Ready(v) => Ok(Value::Variant {
6689 enum_name: "Option".to_string(),
6690 variant_name: "Some".to_string(),
6691 fields: Some(Rc::new(vec![(**v).clone()])),
6692 }),
6693 _ => Ok(Value::Variant {
6694 enum_name: "Option".to_string(),
6695 variant_name: "None".to_string(),
6696 fields: None,
6697 }),
6698 }
6699 }
6700 other => Ok(Value::Variant {
6702 enum_name: "Option".to_string(),
6703 variant_name: "Some".to_string(),
6704 fields: Some(Rc::new(vec![other.clone()])),
6705 }),
6706 }
6707 });
6708}
6709
6710fn register_json(interp: &mut Interpreter) {
6715 define(interp, "json_parse", Some(1), |_, args| {
6717 let json_str = match &args[0] {
6718 Value::String(s) => s.as_str(),
6719 _ => return Err(RuntimeError::new("json_parse() requires string argument")),
6720 };
6721
6722 fn json_to_value(json: &serde_json::Value) -> Value {
6723 match json {
6724 serde_json::Value::Null => Value::Null,
6725 serde_json::Value::Bool(b) => Value::Bool(*b),
6726 serde_json::Value::Number(n) => {
6727 if let Some(i) = n.as_i64() {
6728 Value::Int(i)
6729 } else if let Some(f) = n.as_f64() {
6730 Value::Float(f)
6731 } else {
6732 Value::Null
6733 }
6734 }
6735 serde_json::Value::String(s) => Value::String(Rc::new(s.clone())),
6736 serde_json::Value::Array(arr) => {
6737 let values: Vec<Value> = arr.iter().map(json_to_value).collect();
6738 Value::Array(Rc::new(RefCell::new(values)))
6739 }
6740 serde_json::Value::Object(obj) => {
6741 let mut map = HashMap::new();
6742 for (k, v) in obj {
6743 map.insert(k.clone(), json_to_value(v));
6744 }
6745 Value::Map(Rc::new(RefCell::new(map)))
6746 }
6747 }
6748 }
6749
6750 match serde_json::from_str(json_str) {
6751 Ok(json) => Ok(json_to_value(&json)),
6752 Err(e) => Err(RuntimeError::new(format!("JSON parse error: {}", e))),
6753 }
6754 });
6755
6756 define(interp, "json_stringify", Some(1), |_, args| {
6758 fn value_to_json(val: &Value) -> serde_json::Value {
6759 match val {
6760 Value::Null => serde_json::Value::Null,
6761 Value::Bool(b) => serde_json::Value::Bool(*b),
6762 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
6763 Value::Float(f) => serde_json::Number::from_f64(*f)
6764 .map(serde_json::Value::Number)
6765 .unwrap_or(serde_json::Value::Null),
6766 Value::String(s) => serde_json::Value::String(s.to_string()),
6767 Value::Array(arr) => {
6768 let arr = arr.borrow();
6769 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
6770 }
6771 Value::Tuple(t) => serde_json::Value::Array(t.iter().map(value_to_json).collect()),
6772 Value::Map(map) => {
6773 let map = map.borrow();
6774 let obj: serde_json::Map<String, serde_json::Value> = map
6775 .iter()
6776 .map(|(k, v)| (k.clone(), value_to_json(v)))
6777 .collect();
6778 serde_json::Value::Object(obj)
6779 }
6780 Value::Struct { fields, .. } => {
6781 let fields = fields.borrow();
6782 let obj: serde_json::Map<String, serde_json::Value> = fields
6783 .iter()
6784 .map(|(k, v)| (k.clone(), value_to_json(v)))
6785 .collect();
6786 serde_json::Value::Object(obj)
6787 }
6788 _ => serde_json::Value::String(format!("{}", val)),
6789 }
6790 }
6791
6792 let json = value_to_json(&args[0]);
6793 Ok(Value::String(Rc::new(json.to_string())))
6794 });
6795
6796 define(interp, "json_pretty", Some(1), |_, args| {
6798 fn value_to_json(val: &Value) -> serde_json::Value {
6799 match val {
6800 Value::Null => serde_json::Value::Null,
6801 Value::Bool(b) => serde_json::Value::Bool(*b),
6802 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
6803 Value::Float(f) => serde_json::Number::from_f64(*f)
6804 .map(serde_json::Value::Number)
6805 .unwrap_or(serde_json::Value::Null),
6806 Value::String(s) => serde_json::Value::String(s.to_string()),
6807 Value::Array(arr) => {
6808 let arr = arr.borrow();
6809 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
6810 }
6811 Value::Map(map) => {
6812 let map = map.borrow();
6813 let obj: serde_json::Map<String, serde_json::Value> = map
6814 .iter()
6815 .map(|(k, v)| (k.clone(), value_to_json(v)))
6816 .collect();
6817 serde_json::Value::Object(obj)
6818 }
6819 _ => serde_json::Value::String(format!("{}", val)),
6820 }
6821 }
6822
6823 let json = value_to_json(&args[0]);
6824 match serde_json::to_string_pretty(&json) {
6825 Ok(s) => Ok(Value::String(Rc::new(s))),
6826 Err(e) => Err(RuntimeError::new(format!("JSON stringify error: {}", e))),
6827 }
6828 });
6829
6830 define(interp, "json_get", Some(2), |_, args| {
6832 let path = match &args[1] {
6833 Value::String(s) => s.to_string(),
6834 _ => return Err(RuntimeError::new("json_get() requires string path")),
6835 };
6836
6837 let mut current = args[0].clone();
6838 for key in path.split('.') {
6839 current = match ¤t {
6840 Value::Map(map) => {
6841 let map = map.borrow();
6842 map.get(key).cloned().unwrap_or(Value::Null)
6843 }
6844 Value::Array(arr) => {
6845 if let Ok(idx) = key.parse::<usize>() {
6846 let arr = arr.borrow();
6847 arr.get(idx).cloned().unwrap_or(Value::Null)
6848 } else {
6849 Value::Null
6850 }
6851 }
6852 _ => Value::Null,
6853 };
6854 }
6855 Ok(current)
6856 });
6857
6858 define(interp, "json_set", Some(3), |_, args| {
6860 let path = match &args[1] {
6861 Value::String(s) => s.to_string(),
6862 _ => return Err(RuntimeError::new("json_set() requires string path")),
6863 };
6864 let new_value = args[2].clone();
6865
6866 match &args[0] {
6868 Value::Map(map) => {
6869 let mut map = map.borrow_mut();
6870 map.insert(path, new_value);
6871 Ok(Value::Map(Rc::new(RefCell::new(map.clone()))))
6872 }
6873 _ => Err(RuntimeError::new("json_set() requires map/object")),
6874 }
6875 });
6876}
6877
6878fn register_fs(interp: &mut Interpreter) {
6883 define(interp, "fs_read", Some(1), |_, args| {
6885 let path = match &args[0] {
6886 Value::String(s) => s.to_string(),
6887 _ => return Err(RuntimeError::new("fs_read() requires string path")),
6888 };
6889
6890 match std::fs::read_to_string(&path) {
6891 Ok(content) => Ok(Value::String(Rc::new(content))),
6892 Err(e) => Err(RuntimeError::new(format!("fs_read() error: {}", e))),
6893 }
6894 });
6895
6896 define(interp, "fs_read_bytes", Some(1), |_, args| {
6898 let path = match &args[0] {
6899 Value::String(s) => s.to_string(),
6900 _ => return Err(RuntimeError::new("fs_read_bytes() requires string path")),
6901 };
6902
6903 match std::fs::read(&path) {
6904 Ok(bytes) => {
6905 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
6906 Ok(Value::Array(Rc::new(RefCell::new(values))))
6907 }
6908 Err(e) => Err(RuntimeError::new(format!("fs_read_bytes() error: {}", e))),
6909 }
6910 });
6911
6912 define(interp, "fs_write", Some(2), |_, args| {
6914 let path = match &args[0] {
6915 Value::String(s) => s.to_string(),
6916 _ => return Err(RuntimeError::new("fs_write() requires string path")),
6917 };
6918 let content = format!("{}", args[1]);
6919
6920 match std::fs::write(&path, content) {
6921 Ok(()) => Ok(Value::Null),
6922 Err(e) => Err(RuntimeError::new(format!("fs_write() error: {}", e))),
6923 }
6924 });
6925
6926 define(interp, "fs_append", Some(2), |_, args| {
6928 let path = match &args[0] {
6929 Value::String(s) => s.to_string(),
6930 _ => return Err(RuntimeError::new("fs_append() requires string path")),
6931 };
6932 let content = format!("{}", args[1]);
6933
6934 use std::fs::OpenOptions;
6935 match OpenOptions::new().append(true).create(true).open(&path) {
6936 Ok(mut file) => {
6937 use std::io::Write;
6938 match file.write_all(content.as_bytes()) {
6939 Ok(()) => Ok(Value::Null),
6940 Err(e) => Err(RuntimeError::new(format!("fs_append() write error: {}", e))),
6941 }
6942 }
6943 Err(e) => Err(RuntimeError::new(format!("fs_append() error: {}", e))),
6944 }
6945 });
6946
6947 define(interp, "fs_exists", Some(1), |_, args| {
6949 let path = match &args[0] {
6950 Value::String(s) => s.to_string(),
6951 _ => return Err(RuntimeError::new("fs_exists() requires string path")),
6952 };
6953 Ok(Value::Bool(std::path::Path::new(&path).exists()))
6954 });
6955
6956 define(interp, "fs_is_file", Some(1), |_, args| {
6958 let path = match &args[0] {
6959 Value::String(s) => s.to_string(),
6960 _ => return Err(RuntimeError::new("fs_is_file() requires string path")),
6961 };
6962 Ok(Value::Bool(std::path::Path::new(&path).is_file()))
6963 });
6964
6965 define(interp, "fs_is_dir", Some(1), |_, args| {
6967 let path = match &args[0] {
6968 Value::String(s) => s.to_string(),
6969 _ => return Err(RuntimeError::new("fs_is_dir() requires string path")),
6970 };
6971 Ok(Value::Bool(std::path::Path::new(&path).is_dir()))
6972 });
6973
6974 define(interp, "fs_mkdir", Some(1), |_, args| {
6976 let path = match &args[0] {
6977 Value::String(s) => s.to_string(),
6978 _ => return Err(RuntimeError::new("fs_mkdir() requires string path")),
6979 };
6980
6981 match std::fs::create_dir_all(&path) {
6982 Ok(()) => Ok(Value::Null),
6983 Err(e) => Err(RuntimeError::new(format!("fs_mkdir() error: {}", e))),
6984 }
6985 });
6986
6987 define(interp, "fs_remove", Some(1), |_, args| {
6989 let path = match &args[0] {
6990 Value::String(s) => s.to_string(),
6991 _ => return Err(RuntimeError::new("fs_remove() requires string path")),
6992 };
6993
6994 let p = std::path::Path::new(&path);
6995 let result = if p.is_dir() {
6996 std::fs::remove_dir_all(&path)
6997 } else {
6998 std::fs::remove_file(&path)
6999 };
7000
7001 match result {
7002 Ok(()) => Ok(Value::Null),
7003 Err(e) => Err(RuntimeError::new(format!("fs_remove() error: {}", e))),
7004 }
7005 });
7006
7007 define(interp, "fs_list", Some(1), |_, args| {
7009 let path = match &args[0] {
7010 Value::String(s) => s.to_string(),
7011 _ => return Err(RuntimeError::new("fs_list() requires string path")),
7012 };
7013
7014 match std::fs::read_dir(&path) {
7015 Ok(entries) => {
7016 let mut files = Vec::new();
7017 for entry in entries.flatten() {
7018 if let Some(name) = entry.file_name().to_str() {
7019 files.push(Value::String(Rc::new(name.to_string())));
7020 }
7021 }
7022 Ok(Value::Array(Rc::new(RefCell::new(files))))
7023 }
7024 Err(e) => Err(RuntimeError::new(format!("fs_list() error: {}", e))),
7025 }
7026 });
7027
7028 define(interp, "fs_copy", Some(2), |_, args| {
7030 let src = match &args[0] {
7031 Value::String(s) => s.to_string(),
7032 _ => return Err(RuntimeError::new("fs_copy() requires string source path")),
7033 };
7034 let dst = match &args[1] {
7035 Value::String(s) => s.to_string(),
7036 _ => {
7037 return Err(RuntimeError::new(
7038 "fs_copy() requires string destination path",
7039 ))
7040 }
7041 };
7042
7043 match std::fs::copy(&src, &dst) {
7044 Ok(bytes) => Ok(Value::Int(bytes as i64)),
7045 Err(e) => Err(RuntimeError::new(format!("fs_copy() error: {}", e))),
7046 }
7047 });
7048
7049 define(interp, "fs_rename", Some(2), |_, args| {
7051 let src = match &args[0] {
7052 Value::String(s) => s.to_string(),
7053 _ => return Err(RuntimeError::new("fs_rename() requires string source path")),
7054 };
7055 let dst = match &args[1] {
7056 Value::String(s) => s.to_string(),
7057 _ => {
7058 return Err(RuntimeError::new(
7059 "fs_rename() requires string destination path",
7060 ))
7061 }
7062 };
7063
7064 match std::fs::rename(&src, &dst) {
7065 Ok(()) => Ok(Value::Null),
7066 Err(e) => Err(RuntimeError::new(format!("fs_rename() error: {}", e))),
7067 }
7068 });
7069
7070 define(interp, "fs_size", Some(1), |_, args| {
7072 let path = match &args[0] {
7073 Value::String(s) => s.to_string(),
7074 _ => return Err(RuntimeError::new("fs_size() requires string path")),
7075 };
7076
7077 match std::fs::metadata(&path) {
7078 Ok(meta) => Ok(Value::Int(meta.len() as i64)),
7079 Err(e) => Err(RuntimeError::new(format!("fs_size() error: {}", e))),
7080 }
7081 });
7082
7083 define(interp, "path_join", None, |_, args| {
7085 let mut path = std::path::PathBuf::new();
7086 for arg in &args {
7087 match arg {
7088 Value::String(s) => path.push(s.as_str()),
7089 Value::Array(arr) => {
7090 for v in arr.borrow().iter() {
7091 if let Value::String(s) = v {
7092 path.push(s.as_str());
7093 }
7094 }
7095 }
7096 _ => {}
7097 }
7098 }
7099 Ok(Value::String(Rc::new(path.to_string_lossy().to_string())))
7100 });
7101
7102 define(interp, "path_parent", Some(1), |_, args| {
7104 let path = match &args[0] {
7105 Value::String(s) => s.to_string(),
7106 _ => return Err(RuntimeError::new("path_parent() requires string path")),
7107 };
7108
7109 let p = std::path::Path::new(&path);
7110 match p.parent() {
7111 Some(parent) => Ok(Value::String(Rc::new(parent.to_string_lossy().to_string()))),
7112 None => Ok(Value::Null),
7113 }
7114 });
7115
7116 define(interp, "path_filename", Some(1), |_, args| {
7118 let path = match &args[0] {
7119 Value::String(s) => s.to_string(),
7120 _ => return Err(RuntimeError::new("path_filename() requires string path")),
7121 };
7122
7123 let p = std::path::Path::new(&path);
7124 match p.file_name() {
7125 Some(name) => Ok(Value::String(Rc::new(name.to_string_lossy().to_string()))),
7126 None => Ok(Value::Null),
7127 }
7128 });
7129
7130 define(interp, "path_extension", Some(1), |_, args| {
7132 let path = match &args[0] {
7133 Value::String(s) => s.to_string(),
7134 _ => return Err(RuntimeError::new("path_extension() requires string path")),
7135 };
7136
7137 let p = std::path::Path::new(&path);
7138 match p.extension() {
7139 Some(ext) => Ok(Value::String(Rc::new(ext.to_string_lossy().to_string()))),
7140 None => Ok(Value::Null),
7141 }
7142 });
7143
7144 use std::cell::RefCell;
7151 use std::collections::HashMap;
7152 thread_local! {
7153 static LAST_FILE_CONTENT: RefCell<String> = RefCell::new(String::new());
7154 static FAKE_PTR_MAP: RefCell<HashMap<i64, String>> = RefCell::new(HashMap::new());
7156 }
7157
7158 define(interp, "sigil_read_file", Some(2), |_, args| {
7162 let path = match &args[0] {
7164 Value::String(s) => s.to_string(),
7165 Value::Int(ptr_id) => {
7166 FAKE_PTR_MAP.with(|map| {
7168 map.borrow().get(ptr_id).cloned()
7169 }).ok_or_else(|| RuntimeError::new(format!(
7170 "sigil_read_file: invalid pointer {}", ptr_id
7171 )))?
7172 }
7173 _ => return Err(RuntimeError::new("sigil_read_file() requires string path")),
7174 };
7175
7176 match std::fs::read_to_string(&path) {
7177 Ok(content) => {
7178 LAST_FILE_CONTENT.with(|last| {
7180 *last.borrow_mut() = content.clone();
7181 });
7182 Ok(Value::String(Rc::new(content)))
7184 }
7185 Err(_) => Ok(Value::Null), }
7187 });
7188
7189 define(interp, "sigil_file_len", Some(0), |_, _| {
7191 LAST_FILE_CONTENT.with(|last| {
7192 Ok(Value::Int(last.borrow().len() as i64))
7193 })
7194 });
7195
7196 define(interp, "sigil_write_file", Some(4), |_, args| {
7198 let path = match &args[0] {
7200 Value::String(s) => s.to_string(),
7201 _ => return Err(RuntimeError::new("sigil_write_file() requires string path")),
7202 };
7203 let content = match &args[2] {
7204 Value::String(s) => s.to_string(),
7205 _ => return Err(RuntimeError::new("sigil_write_file() requires string content")),
7206 };
7207
7208 match std::fs::write(&path, content) {
7209 Ok(()) => Ok(Value::Bool(true)),
7210 Err(_) => Ok(Value::Bool(false)),
7211 }
7212 });
7213
7214 define(interp, "write", Some(3), |_, args| {
7216 let fd = match &args[0] {
7217 Value::Int(n) => *n,
7218 _ => return Err(RuntimeError::new("write() requires int fd")),
7219 };
7220
7221 let content = match &args[1] {
7223 Value::String(s) => s.to_string(),
7224 Value::Int(ptr_id) => {
7225 FAKE_PTR_MAP.with(|map| {
7227 map.borrow().get(ptr_id).cloned()
7228 }).unwrap_or_else(|| format!("{}", ptr_id))
7229 }
7230 _ => format!("{}", args[1]),
7231 };
7232
7233 let len = match &args[2] {
7235 Value::Int(n) => *n as usize,
7236 _ => content.len(),
7237 };
7238
7239 let output = &content[..std::cmp::min(len, content.len())];
7240
7241 match fd {
7242 1 => {
7243 print!("{}", output);
7244 use std::io::Write;
7245 std::io::stdout().flush().ok();
7246 Ok(Value::Int(output.len() as i64))
7247 }
7248 2 => {
7249 eprint!("{}", output);
7250 use std::io::Write;
7251 std::io::stderr().flush().ok();
7252 Ok(Value::Int(output.len() as i64))
7253 }
7254 _ => Err(RuntimeError::new(format!("write() unsupported fd: {}", fd))),
7255 }
7256 });
7257
7258 define(interp, "PathBuf·from", Some(1), |_, args| {
7260 let path = match &args[0] {
7261 Value::String(s) => s.to_string(),
7262 Value::Ref(r) => {
7263 if let Value::String(s) = &*r.borrow() {
7264 s.to_string()
7265 } else {
7266 return Err(RuntimeError::new("PathBuf::from() requires string"));
7267 }
7268 }
7269 _ => return Err(RuntimeError::new("PathBuf::from() requires string")),
7270 };
7271 Ok(Value::String(Rc::new(path)))
7272 });
7273
7274 define(interp, "std·path·PathBuf·from", Some(1), |_, args| {
7276 let path = match &args[0] {
7277 Value::String(s) => s.to_string(),
7278 Value::Ref(r) => {
7279 if let Value::String(s) = &*r.borrow() {
7280 s.to_string()
7281 } else {
7282 return Err(RuntimeError::new("PathBuf::from() requires string"));
7283 }
7284 }
7285 _ => return Err(RuntimeError::new("PathBuf::from() requires string")),
7286 };
7287 Ok(Value::String(Rc::new(path)))
7288 });
7289
7290 define(interp, "Path·new", Some(1), |_, args| {
7292 let path = match &args[0] {
7293 Value::String(s) => s.to_string(),
7294 Value::Ref(r) => {
7295 if let Value::String(s) = &*r.borrow() {
7296 s.to_string()
7297 } else {
7298 return Err(RuntimeError::new("Path::new() requires string"));
7299 }
7300 }
7301 _ => return Err(RuntimeError::new("Path::new() requires string")),
7302 };
7303 Ok(Value::String(Rc::new(path)))
7304 });
7305
7306 define(interp, "std·path·Path·new", Some(1), |_, args| {
7308 let path = match &args[0] {
7309 Value::String(s) => s.to_string(),
7310 _ => return Err(RuntimeError::new("Path::new() requires string")),
7311 };
7312 Ok(Value::String(Rc::new(path)))
7313 });
7314
7315 define(interp, "std·fs·read_to_string", Some(1), |_, args| {
7317 let path = match &args[0] {
7318 Value::String(s) => s.to_string(),
7319 _ => return Err(RuntimeError::new("read_to_string() requires string path")),
7320 };
7321 match std::fs::read_to_string(&path) {
7322 Ok(content) => Ok(Value::String(Rc::new(content))),
7323 Err(e) => Err(RuntimeError::new(format!("read_to_string() error: {}", e))),
7324 }
7325 });
7326
7327 define(interp, "std·fs·write", Some(2), |_, args| {
7329 let path = match &args[0] {
7330 Value::String(s) => s.to_string(),
7331 _ => return Err(RuntimeError::new("fs::write() requires string path")),
7332 };
7333 let content = format!("{}", args[1]);
7334 match std::fs::write(&path, content) {
7335 Ok(()) => Ok(Value::Null),
7336 Err(e) => Err(RuntimeError::new(format!("fs::write() error: {}", e))),
7337 }
7338 });
7339
7340 define(interp, "std·fs·create_dir_all", Some(1), |_, args| {
7342 let path = match &args[0] {
7343 Value::String(s) => s.to_string(),
7344 _ => return Err(RuntimeError::new("create_dir_all() requires string path")),
7345 };
7346 match std::fs::create_dir_all(&path) {
7347 Ok(()) => Ok(Value::Null),
7348 Err(e) => Err(RuntimeError::new(format!("create_dir_all() error: {}", e))),
7349 }
7350 });
7351
7352 define(interp, "OpenOptions·new", Some(0), |_, _| {
7355 let mut opts = HashMap::new();
7356 opts.insert("read".to_string(), Value::Bool(false));
7357 opts.insert("write".to_string(), Value::Bool(false));
7358 opts.insert("append".to_string(), Value::Bool(false));
7359 opts.insert("truncate".to_string(), Value::Bool(false));
7360 opts.insert("create".to_string(), Value::Bool(false));
7361 opts.insert("create_new".to_string(), Value::Bool(false));
7362 opts.insert("__type__".to_string(), Value::String(Rc::new("OpenOptions".to_string())));
7363 Ok(Value::Map(Rc::new(RefCell::new(opts))))
7364 });
7365
7366 define(interp, "std·fs·OpenOptions·new", Some(0), |_, _| {
7368 let mut opts = HashMap::new();
7369 opts.insert("read".to_string(), Value::Bool(false));
7370 opts.insert("write".to_string(), Value::Bool(false));
7371 opts.insert("append".to_string(), Value::Bool(false));
7372 opts.insert("truncate".to_string(), Value::Bool(false));
7373 opts.insert("create".to_string(), Value::Bool(false));
7374 opts.insert("create_new".to_string(), Value::Bool(false));
7375 opts.insert("__type__".to_string(), Value::String(Rc::new("OpenOptions".to_string())));
7376 Ok(Value::Map(Rc::new(RefCell::new(opts))))
7377 });
7378
7379 define(interp, "File·create", Some(1), |_, args| {
7381 let path = match &args[0] {
7382 Value::String(s) => s.to_string(),
7383 _ => return Err(RuntimeError::new("File::create() requires string path")),
7384 };
7385 let mut handle = HashMap::new();
7387 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7388 handle.insert("mode".to_string(), Value::String(Rc::new("write".to_string())));
7389 handle.insert("__type__".to_string(), Value::String(Rc::new("File".to_string())));
7390 match std::fs::File::create(&path) {
7392 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7393 Err(e) => Err(RuntimeError::new(format!("File::create() error: {}", e))),
7394 }
7395 });
7396
7397 define(interp, "std·fs·File·create", Some(1), |_, args| {
7399 let path = match &args[0] {
7400 Value::String(s) => s.to_string(),
7401 _ => return Err(RuntimeError::new("File::create() requires string path")),
7402 };
7403 let mut handle = HashMap::new();
7404 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7405 handle.insert("mode".to_string(), Value::String(Rc::new("write".to_string())));
7406 handle.insert("__type__".to_string(), Value::String(Rc::new("File".to_string())));
7407 match std::fs::File::create(&path) {
7408 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7409 Err(e) => Err(RuntimeError::new(format!("File::create() error: {}", e))),
7410 }
7411 });
7412
7413 define(interp, "File·open", Some(1), |_, args| {
7415 let path = match &args[0] {
7416 Value::String(s) => s.to_string(),
7417 _ => return Err(RuntimeError::new("File::open() requires string path")),
7418 };
7419 let mut handle = HashMap::new();
7420 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7421 handle.insert("mode".to_string(), Value::String(Rc::new("read".to_string())));
7422 handle.insert("__type__".to_string(), Value::String(Rc::new("File".to_string())));
7423 match std::fs::File::open(&path) {
7424 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7425 Err(e) => Err(RuntimeError::new(format!("File::open() error: {}", e))),
7426 }
7427 });
7428
7429 define(interp, "std·fs·File·open", Some(1), |_, args| {
7431 let path = match &args[0] {
7432 Value::String(s) => s.to_string(),
7433 _ => return Err(RuntimeError::new("File::open() requires string path")),
7434 };
7435 let mut handle = HashMap::new();
7436 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7437 handle.insert("mode".to_string(), Value::String(Rc::new("read".to_string())));
7438 handle.insert("__type__".to_string(), Value::String(Rc::new("File".to_string())));
7439 match std::fs::File::open(&path) {
7440 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7441 Err(e) => Err(RuntimeError::new(format!("File::open() error: {}", e))),
7442 }
7443 });
7444
7445 define(interp, "BufWriter·new", Some(1), |_, args| {
7447 match &args[0] {
7450 Value::Map(file_map) => {
7451 let mut wrapper = HashMap::new();
7452 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7453 wrapper.insert("buffer".to_string(), Value::Array(Rc::new(RefCell::new(Vec::new()))));
7454 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufWriter".to_string())));
7455 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7456 }
7457 _ => Err(RuntimeError::new("BufWriter::new requires a file handle")),
7458 }
7459 });
7460
7461 define(interp, "std·io·BufWriter·new", Some(1), |_, args| {
7463 match &args[0] {
7464 Value::Map(file_map) => {
7465 let mut wrapper = HashMap::new();
7466 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7467 wrapper.insert("buffer".to_string(), Value::Array(Rc::new(RefCell::new(Vec::new()))));
7468 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufWriter".to_string())));
7469 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7470 }
7471 _ => Err(RuntimeError::new("BufWriter::new requires a file handle")),
7472 }
7473 });
7474
7475 define(interp, "BufReader·new", Some(1), |_, args| {
7477 use std::io::BufReader as StdBufReader;
7478
7479 let get_map = |val: &Value| -> Option<Rc<RefCell<HashMap<String, Value>>>> {
7481 match val {
7482 Value::Map(m) => Some(m.clone()),
7483 Value::Ref(r) => {
7484 let inner = r.borrow();
7485 if let Value::Map(m) = &*inner {
7486 Some(m.clone())
7487 } else {
7488 None
7489 }
7490 }
7491 _ => None,
7492 }
7493 };
7494
7495 if let Some(file_map) = get_map(&args[0]) {
7496 let borrowed = file_map.borrow();
7497 let mut wrapper = HashMap::new();
7498
7499 if let Some(Value::String(t)) = borrowed.get("__type__") {
7501 if t.as_str() == "TcpStream" {
7502 if let Some(Value::Int(stream_id)) = borrowed.get("__stream_id__") {
7503 let stream_id_val = *stream_id as u64;
7504 drop(borrowed);
7505
7506 if let Some(mut guard) = get_stream_registry().lock().ok() {
7508 if let Some(stream) = guard.get_mut(&stream_id_val) {
7509 let stream_clone = match stream.try_clone() {
7510 Ok(s) => s,
7511 Err(e) => return Err(RuntimeError::new(format!("Failed to clone stream: {}", e))),
7512 };
7513 let reader = StdBufReader::new(stream_clone);
7514 let reader_id = store_bufreader(reader);
7515
7516 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufReader".to_string())));
7517 wrapper.insert("__stream_id__".to_string(), Value::Int(stream_id_val as i64));
7518 wrapper.insert("__reader_id__".to_string(), Value::Int(reader_id as i64));
7519 return Ok(Value::Map(Rc::new(RefCell::new(wrapper))));
7520 }
7521 }
7522 return Err(RuntimeError::new("TcpStream not found in registry"));
7523 }
7524 }
7525 }
7526
7527 drop(borrowed);
7529 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7530 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufReader".to_string())));
7531 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7532 } else {
7533 Err(RuntimeError::new("BufReader::new requires a file handle or TcpStream"))
7534 }
7535 });
7536
7537 define(interp, "std·io·BufReader·new", Some(1), |_, args| {
7539 let get_map = |val: &Value| -> Option<Rc<RefCell<HashMap<String, Value>>>> {
7541 match val {
7542 Value::Map(m) => Some(m.clone()),
7543 Value::Ref(r) => {
7544 let inner = r.borrow();
7545 if let Value::Map(m) = &*inner {
7546 Some(m.clone())
7547 } else {
7548 None
7549 }
7550 }
7551 _ => None,
7552 }
7553 };
7554
7555 if let Some(file_map) = get_map(&args[0]) {
7556 let borrowed = file_map.borrow();
7557 let mut wrapper = HashMap::new();
7558
7559 if let Some(Value::String(t)) = borrowed.get("__type__") {
7561 if t.as_str() == "TcpStream" {
7562 if let Some(Value::Int(stream_id)) = borrowed.get("__stream_id__") {
7563 wrapper.insert("__stream_id__".to_string(), Value::Int(*stream_id));
7564 }
7565 }
7566 }
7567
7568 drop(borrowed);
7569 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7570 wrapper.insert("__type__".to_string(), Value::String(Rc::new("BufReader".to_string())));
7571 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7572 } else {
7573 Err(RuntimeError::new("BufReader::new requires a file handle or TcpStream"))
7574 }
7575 });
7576
7577 define(interp, "dirs_next·config_dir", Some(0), |_, _| {
7579 match dirs::config_dir() {
7580 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7581 None => Ok(Value::Null),
7582 }
7583 });
7584
7585 define(interp, "dirs_next·data_dir", Some(0), |_, _| {
7587 match dirs::data_dir() {
7588 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7589 None => Ok(Value::Null),
7590 }
7591 });
7592
7593 define(interp, "dirs_next·home_dir", Some(0), |_, _| {
7595 match dirs::home_dir() {
7596 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7597 None => Ok(Value::Null),
7598 }
7599 });
7600}
7601
7602fn register_crypto(interp: &mut Interpreter) {
7634 fn extract_bytes(v: &Value, fn_name: &str) -> Result<Vec<u8>, RuntimeError> {
7636 match v {
7637 Value::String(s) => Ok(s.as_bytes().to_vec()),
7638 Value::Array(arr) => {
7639 let arr = arr.borrow();
7640 Ok(arr
7641 .iter()
7642 .filter_map(|v| {
7643 if let Value::Int(n) = v {
7644 Some(*n as u8)
7645 } else {
7646 None
7647 }
7648 })
7649 .collect())
7650 }
7651 _ => Err(RuntimeError::new(format!(
7652 "{}() requires string or byte array",
7653 fn_name
7654 ))),
7655 }
7656 }
7657
7658 fn bytes_to_array(bytes: &[u8]) -> Value {
7659 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
7660 Value::Array(Rc::new(RefCell::new(values)))
7661 }
7662
7663 define(interp, "sha256", Some(1), |_, args| {
7669 let data = extract_bytes(&args[0], "sha256")?;
7670 let mut hasher = Sha256::new();
7671 hasher.update(&data);
7672 let result = hasher.finalize();
7673 Ok(Value::String(Rc::new(
7674 result.iter().map(|b| format!("{:02x}", b)).collect(),
7675 )))
7676 });
7677
7678 define(interp, "sha512", Some(1), |_, args| {
7680 let data = extract_bytes(&args[0], "sha512")?;
7681 let mut hasher = Sha512::new();
7682 hasher.update(&data);
7683 let result = hasher.finalize();
7684 Ok(Value::String(Rc::new(
7685 result.iter().map(|b| format!("{:02x}", b)).collect(),
7686 )))
7687 });
7688
7689 define(interp, "sha3_256", Some(1), |_, args| {
7691 use sha3::{Digest as Sha3Digest, Sha3_256};
7692 let data = extract_bytes(&args[0], "sha3_256")?;
7693 let mut hasher = Sha3_256::new();
7694 hasher.update(&data);
7695 let result = hasher.finalize();
7696 Ok(Value::String(Rc::new(
7697 result.iter().map(|b| format!("{:02x}", b)).collect(),
7698 )))
7699 });
7700
7701 define(interp, "sha3_512", Some(1), |_, args| {
7703 use sha3::{Digest as Sha3Digest, Sha3_512};
7704 let data = extract_bytes(&args[0], "sha3_512")?;
7705 let mut hasher = Sha3_512::new();
7706 hasher.update(&data);
7707 let result = hasher.finalize();
7708 Ok(Value::String(Rc::new(
7709 result.iter().map(|b| format!("{:02x}", b)).collect(),
7710 )))
7711 });
7712
7713 define(interp, "blake3", Some(1), |_, args| {
7715 let data = extract_bytes(&args[0], "blake3")?;
7716 let hash = blake3::hash(&data);
7717 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
7718 });
7719
7720 define(interp, "blake3_keyed", Some(2), |_, args| {
7722 let key = extract_bytes(&args[0], "blake3_keyed")?;
7723 let data = extract_bytes(&args[1], "blake3_keyed")?;
7724 if key.len() != 32 {
7725 return Err(RuntimeError::new("blake3_keyed() requires 32-byte key"));
7726 }
7727 let mut key_arr = [0u8; 32];
7728 key_arr.copy_from_slice(&key);
7729 let hash = blake3::keyed_hash(&key_arr, &data);
7730 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
7731 });
7732
7733 define(interp, "md5", Some(1), |_, args| {
7735 let data = extract_bytes(&args[0], "md5")?;
7736 let mut hasher = Md5::new();
7737 hasher.update(&data);
7738 let result = hasher.finalize();
7739 Ok(Value::String(Rc::new(
7740 result.iter().map(|b| format!("{:02x}", b)).collect(),
7741 )))
7742 });
7743
7744 define(interp, "aes_gcm_encrypt", Some(2), |_, args| {
7750 use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
7751 use rand::RngCore;
7752
7753 let key = extract_bytes(&args[0], "aes_gcm_encrypt")?;
7754 let plaintext = extract_bytes(&args[1], "aes_gcm_encrypt")?;
7755
7756 if key.len() != 32 {
7757 return Err(RuntimeError::new("aes_gcm_encrypt() requires 32-byte key"));
7758 }
7759
7760 let cipher = Aes256Gcm::new_from_slice(&key)
7761 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
7762
7763 let mut nonce_bytes = [0u8; 12];
7764 rand::thread_rng().fill_bytes(&mut nonce_bytes);
7765 let nonce = Nonce::from_slice(&nonce_bytes);
7766
7767 let ciphertext = cipher
7768 .encrypt(nonce, plaintext.as_ref())
7769 .map_err(|e| RuntimeError::new(format!("AES encryption error: {}", e)))?;
7770
7771 let mut result = HashMap::new();
7772 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
7773 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
7774 Ok(Value::Map(Rc::new(RefCell::new(result))))
7775 });
7776
7777 define(interp, "aes_gcm_decrypt", Some(3), |_, args| {
7779 use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
7780
7781 let key = extract_bytes(&args[0], "aes_gcm_decrypt")?;
7782 let ciphertext = extract_bytes(&args[1], "aes_gcm_decrypt")?;
7783 let nonce_bytes = extract_bytes(&args[2], "aes_gcm_decrypt")?;
7784
7785 if key.len() != 32 {
7786 return Err(RuntimeError::new("aes_gcm_decrypt() requires 32-byte key"));
7787 }
7788 if nonce_bytes.len() != 12 {
7789 return Err(RuntimeError::new(
7790 "aes_gcm_decrypt() requires 12-byte nonce",
7791 ));
7792 }
7793
7794 let cipher = Aes256Gcm::new_from_slice(&key)
7795 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
7796 let nonce = Nonce::from_slice(&nonce_bytes);
7797
7798 let plaintext = cipher
7799 .decrypt(nonce, ciphertext.as_ref())
7800 .map_err(|_| RuntimeError::new("AES-GCM decryption failed: authentication error"))?;
7801
7802 match String::from_utf8(plaintext.clone()) {
7803 Ok(s) => Ok(Value::String(Rc::new(s))),
7804 Err(_) => Ok(bytes_to_array(&plaintext)),
7805 }
7806 });
7807
7808 define(interp, "chacha20_encrypt", Some(2), |_, args| {
7810 use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
7811 use rand::RngCore;
7812
7813 let key = extract_bytes(&args[0], "chacha20_encrypt")?;
7814 let plaintext = extract_bytes(&args[1], "chacha20_encrypt")?;
7815
7816 if key.len() != 32 {
7817 return Err(RuntimeError::new("chacha20_encrypt() requires 32-byte key"));
7818 }
7819
7820 let cipher = ChaCha20Poly1305::new_from_slice(&key)
7821 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
7822
7823 let mut nonce_bytes = [0u8; 12];
7824 rand::thread_rng().fill_bytes(&mut nonce_bytes);
7825 let nonce = Nonce::from_slice(&nonce_bytes);
7826
7827 let ciphertext = cipher
7828 .encrypt(nonce, plaintext.as_ref())
7829 .map_err(|e| RuntimeError::new(format!("ChaCha20 encryption error: {}", e)))?;
7830
7831 let mut result = HashMap::new();
7832 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
7833 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
7834 Ok(Value::Map(Rc::new(RefCell::new(result))))
7835 });
7836
7837 define(interp, "chacha20_decrypt", Some(3), |_, args| {
7839 use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
7840
7841 let key = extract_bytes(&args[0], "chacha20_decrypt")?;
7842 let ciphertext = extract_bytes(&args[1], "chacha20_decrypt")?;
7843 let nonce_bytes = extract_bytes(&args[2], "chacha20_decrypt")?;
7844
7845 if key.len() != 32 {
7846 return Err(RuntimeError::new("chacha20_decrypt() requires 32-byte key"));
7847 }
7848 if nonce_bytes.len() != 12 {
7849 return Err(RuntimeError::new(
7850 "chacha20_decrypt() requires 12-byte nonce",
7851 ));
7852 }
7853
7854 let cipher = ChaCha20Poly1305::new_from_slice(&key)
7855 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
7856 let nonce = Nonce::from_slice(&nonce_bytes);
7857
7858 let plaintext = cipher
7859 .decrypt(nonce, ciphertext.as_ref())
7860 .map_err(|_| RuntimeError::new("ChaCha20 decryption failed: authentication error"))?;
7861
7862 match String::from_utf8(plaintext.clone()) {
7863 Ok(s) => Ok(Value::String(Rc::new(s))),
7864 Err(_) => Ok(bytes_to_array(&plaintext)),
7865 }
7866 });
7867
7868 define(interp, "ed25519_keygen", Some(0), |_, _| {
7874 use ed25519_dalek::SigningKey;
7875 use rand::rngs::OsRng;
7876
7877 let signing_key = SigningKey::generate(&mut OsRng);
7878 let verifying_key = signing_key.verifying_key();
7879
7880 let mut result = HashMap::new();
7881 result.insert(
7882 "private_key".to_string(),
7883 Value::String(Rc::new(
7884 signing_key
7885 .to_bytes()
7886 .iter()
7887 .map(|b| format!("{:02x}", b))
7888 .collect(),
7889 )),
7890 );
7891 result.insert(
7892 "public_key".to_string(),
7893 Value::String(Rc::new(
7894 verifying_key
7895 .to_bytes()
7896 .iter()
7897 .map(|b| format!("{:02x}", b))
7898 .collect(),
7899 )),
7900 );
7901 Ok(Value::Map(Rc::new(RefCell::new(result))))
7902 });
7903
7904 define(interp, "ed25519_sign", Some(2), |_, args| {
7906 use ed25519_dalek::{Signer, SigningKey};
7907
7908 let private_key_hex = match &args[0] {
7909 Value::String(s) => s.to_string(),
7910 _ => return Err(RuntimeError::new("ed25519_sign() requires hex private key")),
7911 };
7912 let message = extract_bytes(&args[1], "ed25519_sign")?;
7913
7914 let key_bytes: Vec<u8> = (0..private_key_hex.len())
7915 .step_by(2)
7916 .map(|i| u8::from_str_radix(&private_key_hex[i..i + 2], 16))
7917 .collect::<Result<Vec<_>, _>>()
7918 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
7919
7920 if key_bytes.len() != 32 {
7921 return Err(RuntimeError::new(
7922 "ed25519_sign() requires 32-byte private key",
7923 ));
7924 }
7925
7926 let mut key_arr = [0u8; 32];
7927 key_arr.copy_from_slice(&key_bytes);
7928 let signing_key = SigningKey::from_bytes(&key_arr);
7929 let signature = signing_key.sign(&message);
7930
7931 Ok(Value::String(Rc::new(
7932 signature
7933 .to_bytes()
7934 .iter()
7935 .map(|b| format!("{:02x}", b))
7936 .collect(),
7937 )))
7938 });
7939
7940 define(interp, "ed25519_verify", Some(3), |_, args| {
7942 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
7943
7944 let public_key_hex = match &args[0] {
7945 Value::String(s) => s.to_string(),
7946 _ => {
7947 return Err(RuntimeError::new(
7948 "ed25519_verify() requires hex public key",
7949 ))
7950 }
7951 };
7952 let message = extract_bytes(&args[1], "ed25519_verify")?;
7953 let signature_hex = match &args[2] {
7954 Value::String(s) => s.to_string(),
7955 _ => return Err(RuntimeError::new("ed25519_verify() requires hex signature")),
7956 };
7957
7958 let key_bytes: Vec<u8> = (0..public_key_hex.len())
7959 .step_by(2)
7960 .map(|i| u8::from_str_radix(&public_key_hex[i..i + 2], 16))
7961 .collect::<Result<Vec<_>, _>>()
7962 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
7963 let sig_bytes: Vec<u8> = (0..signature_hex.len())
7964 .step_by(2)
7965 .map(|i| u8::from_str_radix(&signature_hex[i..i + 2], 16))
7966 .collect::<Result<Vec<_>, _>>()
7967 .map_err(|_| RuntimeError::new("Invalid signature hex"))?;
7968
7969 if key_bytes.len() != 32 {
7970 return Err(RuntimeError::new(
7971 "ed25519_verify() requires 32-byte public key",
7972 ));
7973 }
7974 if sig_bytes.len() != 64 {
7975 return Err(RuntimeError::new(
7976 "ed25519_verify() requires 64-byte signature",
7977 ));
7978 }
7979
7980 let mut key_arr = [0u8; 32];
7981 key_arr.copy_from_slice(&key_bytes);
7982 let mut sig_arr = [0u8; 64];
7983 sig_arr.copy_from_slice(&sig_bytes);
7984
7985 let verifying_key = VerifyingKey::from_bytes(&key_arr)
7986 .map_err(|e| RuntimeError::new(format!("Invalid public key: {}", e)))?;
7987 let signature = Signature::from_bytes(&sig_arr);
7988
7989 match verifying_key.verify(&message, &signature) {
7990 Ok(_) => Ok(Value::Bool(true)),
7991 Err(_) => Ok(Value::Bool(false)),
7992 }
7993 });
7994
7995 define(interp, "x25519_keygen", Some(0), |_, _| {
7997 use rand::rngs::OsRng;
7998 use x25519_dalek::{PublicKey, StaticSecret};
7999
8000 let secret = StaticSecret::random_from_rng(OsRng);
8001 let public = PublicKey::from(&secret);
8002
8003 let mut result = HashMap::new();
8004 result.insert(
8005 "private_key".to_string(),
8006 Value::String(Rc::new(
8007 secret
8008 .as_bytes()
8009 .iter()
8010 .map(|b| format!("{:02x}", b))
8011 .collect(),
8012 )),
8013 );
8014 result.insert(
8015 "public_key".to_string(),
8016 Value::String(Rc::new(
8017 public
8018 .as_bytes()
8019 .iter()
8020 .map(|b| format!("{:02x}", b))
8021 .collect(),
8022 )),
8023 );
8024 Ok(Value::Map(Rc::new(RefCell::new(result))))
8025 });
8026
8027 define(interp, "x25519_exchange", Some(2), |_, args| {
8029 use x25519_dalek::{PublicKey, StaticSecret};
8030
8031 let my_private_hex = match &args[0] {
8032 Value::String(s) => s.to_string(),
8033 _ => {
8034 return Err(RuntimeError::new(
8035 "x25519_exchange() requires hex private key",
8036 ))
8037 }
8038 };
8039 let their_public_hex = match &args[1] {
8040 Value::String(s) => s.to_string(),
8041 _ => {
8042 return Err(RuntimeError::new(
8043 "x25519_exchange() requires hex public key",
8044 ))
8045 }
8046 };
8047
8048 let my_private_bytes: Vec<u8> = (0..my_private_hex.len())
8049 .step_by(2)
8050 .map(|i| u8::from_str_radix(&my_private_hex[i..i + 2], 16))
8051 .collect::<Result<Vec<_>, _>>()
8052 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
8053 let their_public_bytes: Vec<u8> = (0..their_public_hex.len())
8054 .step_by(2)
8055 .map(|i| u8::from_str_radix(&their_public_hex[i..i + 2], 16))
8056 .collect::<Result<Vec<_>, _>>()
8057 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
8058
8059 if my_private_bytes.len() != 32 || their_public_bytes.len() != 32 {
8060 return Err(RuntimeError::new("x25519_exchange() requires 32-byte keys"));
8061 }
8062
8063 let mut priv_arr = [0u8; 32];
8064 priv_arr.copy_from_slice(&my_private_bytes);
8065 let mut pub_arr = [0u8; 32];
8066 pub_arr.copy_from_slice(&their_public_bytes);
8067
8068 let my_secret = StaticSecret::from(priv_arr);
8069 let their_public = PublicKey::from(pub_arr);
8070 let shared_secret = my_secret.diffie_hellman(&their_public);
8071
8072 Ok(Value::String(Rc::new(
8073 shared_secret
8074 .as_bytes()
8075 .iter()
8076 .map(|b| format!("{:02x}", b))
8077 .collect(),
8078 )))
8079 });
8080
8081 define(interp, "argon2_hash", Some(1), |_, args| {
8087 use argon2::{
8088 password_hash::{PasswordHasher, SaltString},
8089 Argon2,
8090 };
8091 use rand::rngs::OsRng;
8092
8093 let password = extract_bytes(&args[0], "argon2_hash")?;
8094 let salt = SaltString::generate(&mut OsRng);
8095 let argon2 = Argon2::default();
8096
8097 let hash = argon2
8098 .hash_password(&password, &salt)
8099 .map_err(|e| RuntimeError::new(format!("Argon2 error: {}", e)))?;
8100
8101 let mut result = HashMap::new();
8102 result.insert("hash".to_string(), Value::String(Rc::new(hash.to_string())));
8103 result.insert("salt".to_string(), Value::String(Rc::new(salt.to_string())));
8104 Ok(Value::Map(Rc::new(RefCell::new(result))))
8105 });
8106
8107 define(interp, "argon2_verify", Some(2), |_, args| {
8109 use argon2::{Argon2, PasswordHash, PasswordVerifier};
8110
8111 let password = extract_bytes(&args[0], "argon2_verify")?;
8112 let hash_str = match &args[1] {
8113 Value::String(s) => s.to_string(),
8114 _ => return Err(RuntimeError::new("argon2_verify() requires hash string")),
8115 };
8116
8117 let parsed_hash = PasswordHash::new(&hash_str)
8118 .map_err(|e| RuntimeError::new(format!("Invalid hash: {}", e)))?;
8119
8120 match Argon2::default().verify_password(&password, &parsed_hash) {
8121 Ok(_) => Ok(Value::Bool(true)),
8122 Err(_) => Ok(Value::Bool(false)),
8123 }
8124 });
8125
8126 define(interp, "hkdf_expand", Some(3), |_, args| {
8128 use hkdf::Hkdf;
8129
8130 let ikm = extract_bytes(&args[0], "hkdf_expand")?;
8131 let salt = extract_bytes(&args[1], "hkdf_expand")?;
8132 let info = extract_bytes(&args[2], "hkdf_expand")?;
8133
8134 let hk = Hkdf::<Sha256>::new(Some(&salt), &ikm);
8135 let mut okm = [0u8; 32];
8136 hk.expand(&info, &mut okm)
8137 .map_err(|e| RuntimeError::new(format!("HKDF error: {}", e)))?;
8138
8139 Ok(Value::String(Rc::new(
8140 okm.iter().map(|b| format!("{:02x}", b)).collect(),
8141 )))
8142 });
8143
8144 define(interp, "pbkdf2_derive", Some(3), |_, args| {
8146 let password = extract_bytes(&args[0], "pbkdf2_derive")?;
8147 let salt = extract_bytes(&args[1], "pbkdf2_derive")?;
8148 let iterations = match &args[2] {
8149 Value::Int(n) => *n as u32,
8150 _ => {
8151 return Err(RuntimeError::new(
8152 "pbkdf2_derive() requires integer iterations",
8153 ))
8154 }
8155 };
8156
8157 let mut key = [0u8; 32];
8158 pbkdf2::pbkdf2_hmac::<Sha256>(&password, &salt, iterations, &mut key);
8159 Ok(Value::String(Rc::new(
8160 key.iter().map(|b| format!("{:02x}", b)).collect(),
8161 )))
8162 });
8163
8164 define(interp, "hmac_sha256", Some(2), |_, args| {
8170 use hmac::{Hmac, Mac};
8171 type HmacSha256 = Hmac<Sha256>;
8172
8173 let key = extract_bytes(&args[0], "hmac_sha256")?;
8174 let message = extract_bytes(&args[1], "hmac_sha256")?;
8175
8176 let mut mac = HmacSha256::new_from_slice(&key)
8177 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8178 mac.update(&message);
8179 let result = mac.finalize();
8180 Ok(Value::String(Rc::new(
8181 result
8182 .into_bytes()
8183 .iter()
8184 .map(|b| format!("{:02x}", b))
8185 .collect(),
8186 )))
8187 });
8188
8189 define(interp, "hmac_sha512", Some(2), |_, args| {
8191 use hmac::{Hmac, Mac};
8192 type HmacSha512 = Hmac<Sha512>;
8193
8194 let key = extract_bytes(&args[0], "hmac_sha512")?;
8195 let message = extract_bytes(&args[1], "hmac_sha512")?;
8196
8197 let mut mac = HmacSha512::new_from_slice(&key)
8198 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8199 mac.update(&message);
8200 let result = mac.finalize();
8201 Ok(Value::String(Rc::new(
8202 result
8203 .into_bytes()
8204 .iter()
8205 .map(|b| format!("{:02x}", b))
8206 .collect(),
8207 )))
8208 });
8209
8210 define(interp, "hmac_verify", Some(3), |_, args| {
8212 use hmac::{Hmac, Mac};
8213 type HmacSha256 = Hmac<Sha256>;
8214
8215 let key = extract_bytes(&args[0], "hmac_verify")?;
8216 let message = extract_bytes(&args[1], "hmac_verify")?;
8217 let expected_hex = match &args[2] {
8218 Value::String(s) => s.to_string(),
8219 _ => return Err(RuntimeError::new("hmac_verify() requires hex MAC")),
8220 };
8221
8222 let expected: Vec<u8> = (0..expected_hex.len())
8223 .step_by(2)
8224 .map(|i| u8::from_str_radix(&expected_hex[i..i + 2], 16))
8225 .collect::<Result<Vec<_>, _>>()
8226 .map_err(|_| RuntimeError::new("Invalid MAC hex"))?;
8227
8228 let mut mac = HmacSha256::new_from_slice(&key)
8229 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8230 mac.update(&message);
8231
8232 match mac.verify_slice(&expected) {
8233 Ok(_) => Ok(Value::Bool(true)),
8234 Err(_) => Ok(Value::Bool(false)),
8235 }
8236 });
8237
8238 define(interp, "secure_random_bytes", Some(1), |_, args| {
8244 use rand::RngCore;
8245
8246 let length = match &args[0] {
8247 Value::Int(n) => *n as usize,
8248 _ => {
8249 return Err(RuntimeError::new(
8250 "secure_random_bytes() requires integer length",
8251 ))
8252 }
8253 };
8254
8255 if length > 1024 * 1024 {
8256 return Err(RuntimeError::new("secure_random_bytes() max 1MB"));
8257 }
8258
8259 let mut bytes = vec![0u8; length];
8260 rand::thread_rng().fill_bytes(&mut bytes);
8261 Ok(bytes_to_array(&bytes))
8262 });
8263
8264 define(interp, "secure_random_hex", Some(1), |_, args| {
8266 use rand::RngCore;
8267
8268 let byte_length = match &args[0] {
8269 Value::Int(n) => *n as usize,
8270 _ => {
8271 return Err(RuntimeError::new(
8272 "secure_random_hex() requires integer length",
8273 ))
8274 }
8275 };
8276
8277 if byte_length > 1024 * 1024 {
8278 return Err(RuntimeError::new("secure_random_hex() max 1MB"));
8279 }
8280
8281 let mut bytes = vec![0u8; byte_length];
8282 rand::thread_rng().fill_bytes(&mut bytes);
8283 Ok(Value::String(Rc::new(
8284 bytes.iter().map(|b| format!("{:02x}", b)).collect(),
8285 )))
8286 });
8287
8288 define(interp, "generate_key", Some(1), |_, args| {
8290 use rand::RngCore;
8291
8292 let bits = match &args[0] {
8293 Value::Int(n) => *n as usize,
8294 _ => return Err(RuntimeError::new("generate_key() requires bit length")),
8295 };
8296
8297 if bits % 8 != 0 {
8298 return Err(RuntimeError::new(
8299 "generate_key() bit length must be multiple of 8",
8300 ));
8301 }
8302 if bits > 512 {
8303 return Err(RuntimeError::new("generate_key() max 512 bits"));
8304 }
8305
8306 let bytes = bits / 8;
8307 let mut key = vec![0u8; bytes];
8308 rand::thread_rng().fill_bytes(&mut key);
8309 Ok(Value::String(Rc::new(
8310 key.iter().map(|b| format!("{:02x}", b)).collect(),
8311 )))
8312 });
8313
8314 define(interp, "base64_encode", Some(1), |_, args| {
8320 let data = extract_bytes(&args[0], "base64_encode")?;
8321 Ok(Value::String(Rc::new(
8322 general_purpose::STANDARD.encode(&data),
8323 )))
8324 });
8325
8326 define(interp, "base64_decode", Some(1), |_, args| {
8328 let encoded = match &args[0] {
8329 Value::String(s) => s.to_string(),
8330 _ => return Err(RuntimeError::new("base64_decode() requires string")),
8331 };
8332
8333 match general_purpose::STANDARD.decode(&encoded) {
8334 Ok(bytes) => match String::from_utf8(bytes.clone()) {
8335 Ok(s) => Ok(Value::String(Rc::new(s))),
8336 Err(_) => Ok(bytes_to_array(&bytes)),
8337 },
8338 Err(e) => Err(RuntimeError::new(format!("base64_decode() error: {}", e))),
8339 }
8340 });
8341
8342 define(interp, "hex_encode", Some(1), |_, args| {
8344 let data = extract_bytes(&args[0], "hex_encode")?;
8345 Ok(Value::String(Rc::new(
8346 data.iter().map(|b| format!("{:02x}", b)).collect(),
8347 )))
8348 });
8349
8350 define(interp, "hex_decode", Some(1), |_, args| {
8352 let hex_str = match &args[0] {
8353 Value::String(s) => s.to_string(),
8354 _ => return Err(RuntimeError::new("hex_decode() requires string")),
8355 };
8356
8357 let hex_str = hex_str.trim();
8358 if hex_str.len() % 2 != 0 {
8359 return Err(RuntimeError::new(
8360 "hex_decode() requires even-length hex string",
8361 ));
8362 }
8363
8364 let bytes: Vec<Value> = (0..hex_str.len())
8365 .step_by(2)
8366 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).map(|b| Value::Int(b as i64)))
8367 .collect::<Result<Vec<_>, _>>()
8368 .map_err(|_| RuntimeError::new("hex_decode() invalid hex"))?;
8369 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
8370 });
8371
8372 define(interp, "constant_time_eq", Some(2), |_, args| {
8378 let a = extract_bytes(&args[0], "constant_time_eq")?;
8379 let b = extract_bytes(&args[1], "constant_time_eq")?;
8380
8381 if a.len() != b.len() {
8382 return Ok(Value::Bool(false));
8383 }
8384
8385 let mut result = 0u8;
8386 for (x, y) in a.iter().zip(b.iter()) {
8387 result |= x ^ y;
8388 }
8389 Ok(Value::Bool(result == 0))
8390 });
8391
8392 define(interp, "crypto_info", Some(0), |_, _| {
8398 let mut info = HashMap::new();
8399 info.insert(
8400 "version".to_string(),
8401 Value::String(Rc::new("2.0".to_string())),
8402 );
8403 info.insert(
8404 "phase".to_string(),
8405 Value::String(Rc::new("Evidential Cryptography".to_string())),
8406 );
8407
8408 let capabilities = vec![
8409 "sha256",
8410 "sha512",
8411 "sha3_256",
8412 "sha3_512",
8413 "blake3",
8414 "md5",
8415 "aes_gcm_encrypt",
8416 "aes_gcm_decrypt",
8417 "chacha20_encrypt",
8418 "chacha20_decrypt",
8419 "ed25519_keygen",
8420 "ed25519_sign",
8421 "ed25519_verify",
8422 "x25519_keygen",
8423 "x25519_exchange",
8424 "argon2_hash",
8425 "argon2_verify",
8426 "hkdf_expand",
8427 "pbkdf2_derive",
8428 "hmac_sha256",
8429 "hmac_sha512",
8430 "hmac_verify",
8431 "secure_random_bytes",
8432 "secure_random_hex",
8433 "generate_key",
8434 "base64_encode",
8435 "base64_decode",
8436 "hex_encode",
8437 "hex_decode",
8438 "constant_time_eq",
8439 ];
8440 let cap_values: Vec<Value> = capabilities
8441 .iter()
8442 .map(|s| Value::String(Rc::new(s.to_string())))
8443 .collect();
8444 info.insert(
8445 "functions".to_string(),
8446 Value::Array(Rc::new(RefCell::new(cap_values))),
8447 );
8448
8449 Ok(Value::Map(Rc::new(RefCell::new(info))))
8450 });
8451}
8452
8453fn register_regex(interp: &mut Interpreter) {
8458 define(interp, "regex_match", Some(2), |_, args| {
8460 let pattern = match &args[0] {
8461 Value::String(s) => s.to_string(),
8462 _ => return Err(RuntimeError::new("regex_match() requires string pattern")),
8463 };
8464 let text = match &args[1] {
8465 Value::String(s) => s.to_string(),
8466 _ => return Err(RuntimeError::new("regex_match() requires string text")),
8467 };
8468
8469 match Regex::new(&pattern) {
8470 Ok(re) => Ok(Value::Bool(re.is_match(&text))),
8471 Err(e) => Err(RuntimeError::new(format!(
8472 "regex_match() invalid pattern: {}",
8473 e
8474 ))),
8475 }
8476 });
8477
8478 define(interp, "regex_find", Some(2), |_, args| {
8480 let pattern = match &args[0] {
8481 Value::String(s) => s.to_string(),
8482 _ => return Err(RuntimeError::new("regex_find() requires string pattern")),
8483 };
8484 let text = match &args[1] {
8485 Value::String(s) => s.to_string(),
8486 _ => return Err(RuntimeError::new("regex_find() requires string text")),
8487 };
8488
8489 match Regex::new(&pattern) {
8490 Ok(re) => match re.find(&text) {
8491 Some(m) => Ok(Value::String(Rc::new(m.as_str().to_string()))),
8492 None => Ok(Value::Null),
8493 },
8494 Err(e) => Err(RuntimeError::new(format!(
8495 "regex_find() invalid pattern: {}",
8496 e
8497 ))),
8498 }
8499 });
8500
8501 define(interp, "regex_find_all", Some(2), |_, args| {
8503 let pattern = match &args[0] {
8504 Value::String(s) => s.to_string(),
8505 _ => {
8506 return Err(RuntimeError::new(
8507 "regex_find_all() requires string pattern",
8508 ))
8509 }
8510 };
8511 let text = match &args[1] {
8512 Value::String(s) => s.to_string(),
8513 _ => return Err(RuntimeError::new("regex_find_all() requires string text")),
8514 };
8515
8516 match Regex::new(&pattern) {
8517 Ok(re) => {
8518 let matches: Vec<Value> = re
8519 .find_iter(&text)
8520 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
8521 .collect();
8522 Ok(Value::Array(Rc::new(RefCell::new(matches))))
8523 }
8524 Err(e) => Err(RuntimeError::new(format!(
8525 "regex_find_all() invalid pattern: {}",
8526 e
8527 ))),
8528 }
8529 });
8530
8531 define(interp, "regex_replace", Some(3), |_, args| {
8533 let pattern = match &args[0] {
8534 Value::String(s) => s.to_string(),
8535 _ => return Err(RuntimeError::new("regex_replace() requires string pattern")),
8536 };
8537 let text = match &args[1] {
8538 Value::String(s) => s.to_string(),
8539 _ => return Err(RuntimeError::new("regex_replace() requires string text")),
8540 };
8541 let replacement = match &args[2] {
8542 Value::String(s) => s.to_string(),
8543 _ => {
8544 return Err(RuntimeError::new(
8545 "regex_replace() requires string replacement",
8546 ))
8547 }
8548 };
8549
8550 match Regex::new(&pattern) {
8551 Ok(re) => {
8552 let result = re.replace(&text, replacement.as_str());
8553 Ok(Value::String(Rc::new(result.to_string())))
8554 }
8555 Err(e) => Err(RuntimeError::new(format!(
8556 "regex_replace() invalid pattern: {}",
8557 e
8558 ))),
8559 }
8560 });
8561
8562 define(interp, "regex_replace_all", Some(3), |_, args| {
8564 let pattern = match &args[0] {
8565 Value::String(s) => s.to_string(),
8566 _ => {
8567 return Err(RuntimeError::new(
8568 "regex_replace_all() requires string pattern",
8569 ))
8570 }
8571 };
8572 let text = match &args[1] {
8573 Value::String(s) => s.to_string(),
8574 _ => {
8575 return Err(RuntimeError::new(
8576 "regex_replace_all() requires string text",
8577 ))
8578 }
8579 };
8580 let replacement = match &args[2] {
8581 Value::String(s) => s.to_string(),
8582 _ => {
8583 return Err(RuntimeError::new(
8584 "regex_replace_all() requires string replacement",
8585 ))
8586 }
8587 };
8588
8589 match Regex::new(&pattern) {
8590 Ok(re) => {
8591 let result = re.replace_all(&text, replacement.as_str());
8592 Ok(Value::String(Rc::new(result.to_string())))
8593 }
8594 Err(e) => Err(RuntimeError::new(format!(
8595 "regex_replace_all() invalid pattern: {}",
8596 e
8597 ))),
8598 }
8599 });
8600
8601 define(interp, "regex_split", Some(2), |_, args| {
8603 let pattern = match &args[0] {
8604 Value::String(s) => s.to_string(),
8605 _ => return Err(RuntimeError::new("regex_split() requires string pattern")),
8606 };
8607 let text = match &args[1] {
8608 Value::String(s) => s.to_string(),
8609 _ => return Err(RuntimeError::new("regex_split() requires string text")),
8610 };
8611
8612 match Regex::new(&pattern) {
8613 Ok(re) => {
8614 let parts: Vec<Value> = re
8615 .split(&text)
8616 .map(|s| Value::String(Rc::new(s.to_string())))
8617 .collect();
8618 Ok(Value::Array(Rc::new(RefCell::new(parts))))
8619 }
8620 Err(e) => Err(RuntimeError::new(format!(
8621 "regex_split() invalid pattern: {}",
8622 e
8623 ))),
8624 }
8625 });
8626
8627 define(interp, "regex_captures", Some(2), |_, args| {
8629 let pattern = match &args[0] {
8630 Value::String(s) => s.to_string(),
8631 _ => {
8632 return Err(RuntimeError::new(
8633 "regex_captures() requires string pattern",
8634 ))
8635 }
8636 };
8637 let text = match &args[1] {
8638 Value::String(s) => s.to_string(),
8639 _ => return Err(RuntimeError::new("regex_captures() requires string text")),
8640 };
8641
8642 match Regex::new(&pattern) {
8643 Ok(re) => match re.captures(&text) {
8644 Some(caps) => {
8645 let captures: Vec<Value> = caps
8646 .iter()
8647 .map(|m| {
8648 m.map(|m| Value::String(Rc::new(m.as_str().to_string())))
8649 .unwrap_or(Value::Null)
8650 })
8651 .collect();
8652 Ok(Value::Array(Rc::new(RefCell::new(captures))))
8653 }
8654 None => Ok(Value::Null),
8655 },
8656 Err(e) => Err(RuntimeError::new(format!(
8657 "regex_captures() invalid pattern: {}",
8658 e
8659 ))),
8660 }
8661 });
8662}
8663
8664fn register_uuid(interp: &mut Interpreter) {
8669 define(interp, "uuid_v4", Some(0), |_, _| {
8671 let id = Uuid::new_v4();
8672 Ok(Value::String(Rc::new(id.to_string())))
8673 });
8674
8675 define(interp, "uuid_nil", Some(0), |_, _| {
8677 Ok(Value::String(Rc::new(Uuid::nil().to_string())))
8678 });
8679
8680 define(interp, "uuid_parse", Some(1), |_, args| {
8682 let s = match &args[0] {
8683 Value::String(s) => s.to_string(),
8684 _ => return Err(RuntimeError::new("uuid_parse() requires string")),
8685 };
8686
8687 match Uuid::parse_str(&s) {
8688 Ok(id) => Ok(Value::String(Rc::new(id.to_string()))),
8689 Err(e) => Err(RuntimeError::new(format!("uuid_parse() error: {}", e))),
8690 }
8691 });
8692
8693 define(interp, "uuid_is_valid", Some(1), |_, args| {
8695 let s = match &args[0] {
8696 Value::String(s) => s.to_string(),
8697 _ => return Ok(Value::Bool(false)),
8698 };
8699 Ok(Value::Bool(Uuid::parse_str(&s).is_ok()))
8700 });
8701}
8702
8703fn register_system(interp: &mut Interpreter) {
8708 define(interp, "env_get", Some(1), |_, args| {
8710 let key = match &args[0] {
8711 Value::String(s) => s.to_string(),
8712 _ => return Err(RuntimeError::new("env_get() requires string key")),
8713 };
8714
8715 match std::env::var(&key) {
8716 Ok(val) => Ok(Value::String(Rc::new(val))),
8717 Err(_) => Ok(Value::Null),
8718 }
8719 });
8720
8721 define(interp, "env_set", Some(2), |_, args| {
8723 let key = match &args[0] {
8724 Value::String(s) => s.to_string(),
8725 _ => return Err(RuntimeError::new("env_set() requires string key")),
8726 };
8727 let val = match &args[1] {
8728 Value::String(s) => s.to_string(),
8729 _ => format!("{}", args[1]),
8730 };
8731
8732 std::env::set_var(&key, &val);
8733 Ok(Value::Null)
8734 });
8735
8736 define(interp, "env_remove", Some(1), |_, args| {
8738 let key = match &args[0] {
8739 Value::String(s) => s.to_string(),
8740 _ => return Err(RuntimeError::new("env_remove() requires string key")),
8741 };
8742
8743 std::env::remove_var(&key);
8744 Ok(Value::Null)
8745 });
8746
8747 define(interp, "env_vars", Some(0), |_, _| {
8749 let mut map = HashMap::new();
8750 for (key, val) in std::env::vars() {
8751 map.insert(key, Value::String(Rc::new(val)));
8752 }
8753 Ok(Value::Map(Rc::new(RefCell::new(map))))
8754 });
8755
8756 define(interp, "std·env·var", Some(1), |_, args| {
8758 let key = match &args[0] {
8759 Value::String(s) => s.as_str().to_string(),
8760 _ => return Err(RuntimeError::new("env::var expects string key")),
8761 };
8762 match std::env::var(&key) {
8763 Ok(val) => Ok(Value::Variant {
8764 enum_name: "Result".to_string(),
8765 variant_name: "Ok".to_string(),
8766 fields: Some(Rc::new(vec![Value::String(Rc::new(val))])),
8767 }),
8768 Err(_) => Ok(Value::Variant {
8769 enum_name: "Result".to_string(),
8770 variant_name: "Err".to_string(),
8771 fields: Some(Rc::new(vec![Value::String(Rc::new("environment variable not found".to_string()))])),
8772 }),
8773 }
8774 });
8775
8776 define(interp, "std·env·temp_dir", Some(0), |_, _| {
8778 let temp_dir = std::env::temp_dir();
8779 Ok(Value::String(Rc::new(temp_dir.to_string_lossy().to_string())))
8780 });
8781
8782 define(interp, "temp_dir", Some(0), |_, _| {
8784 let temp_dir = std::env::temp_dir();
8785 Ok(Value::String(Rc::new(temp_dir.to_string_lossy().to_string())))
8786 });
8787
8788 define(interp, "std·env·current_dir", Some(0), |_, _| {
8790 match std::env::current_dir() {
8791 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
8792 Err(e) => Err(RuntimeError::new(format!("current_dir() error: {}", e))),
8793 }
8794 });
8795
8796 define(interp, "std·env·args", Some(0), |interp, _| {
8798 let args: Vec<Value> = if interp.program_args.as_ref().map(|v| v.is_empty()).unwrap_or(true) {
8799 std::env::args()
8801 .map(|s| Value::String(Rc::new(s)))
8802 .collect()
8803 } else {
8804 interp.program_args.as_ref().unwrap().iter()
8806 .map(|a| Value::String(Rc::new(a.clone())))
8807 .collect()
8808 };
8809 Ok(Value::Array(Rc::new(RefCell::new(args))))
8810 });
8811
8812 define(interp, "args", Some(0), |interp, _| {
8814 let args: Vec<Value> = if interp.program_args.as_ref().map(|v| v.is_empty()).unwrap_or(true) {
8815 std::env::args()
8817 .map(|s| Value::String(Rc::new(s)))
8818 .collect()
8819 } else {
8820 interp.program_args.as_ref().unwrap().iter()
8822 .map(|a| Value::String(Rc::new(a.clone())))
8823 .collect()
8824 };
8825 Ok(Value::Array(Rc::new(RefCell::new(args))))
8826 });
8827
8828 define(interp, "cwd", Some(0), |_, _| {
8830 match std::env::current_dir() {
8831 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
8832 Err(e) => Err(RuntimeError::new(format!("cwd() error: {}", e))),
8833 }
8834 });
8835
8836 define(interp, "chdir", Some(1), |_, args| {
8838 let path = match &args[0] {
8839 Value::String(s) => s.to_string(),
8840 _ => return Err(RuntimeError::new("chdir() requires string path")),
8841 };
8842
8843 match std::env::set_current_dir(&path) {
8844 Ok(()) => Ok(Value::Null),
8845 Err(e) => Err(RuntimeError::new(format!("chdir() error: {}", e))),
8846 }
8847 });
8848
8849 define(interp, "hostname", Some(0), |_, _| {
8851 match std::fs::read_to_string("/etc/hostname") {
8853 Ok(name) => Ok(Value::String(Rc::new(name.trim().to_string()))),
8854 Err(_) => Ok(Value::String(Rc::new("unknown".to_string()))),
8855 }
8856 });
8857
8858 define(interp, "pid", Some(0), |_, _| {
8860 Ok(Value::Int(std::process::id() as i64))
8861 });
8862
8863 define(interp, "exit", Some(1), |_, args| {
8865 let code = match &args[0] {
8866 Value::Int(n) => *n as i32,
8867 _ => 0,
8868 };
8869 std::process::exit(code);
8870 });
8871
8872 define(interp, "std·process·exit", Some(1), |_, args| {
8874 let code = match &args[0] {
8875 Value::Int(n) => *n as i32,
8876 _ => 0,
8877 };
8878 std::process::exit(code);
8879 });
8880
8881 define(interp, "shell", Some(1), |_, args| {
8883 let cmd = match &args[0] {
8884 Value::String(s) => s.to_string(),
8885 _ => return Err(RuntimeError::new("shell() requires string command")),
8886 };
8887
8888 match std::process::Command::new("sh")
8889 .arg("-c")
8890 .arg(&cmd)
8891 .output()
8892 {
8893 Ok(output) => {
8894 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
8895 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
8896 let code = output.status.code().unwrap_or(-1);
8897
8898 let mut result = HashMap::new();
8899 result.insert("stdout".to_string(), Value::String(Rc::new(stdout)));
8900 result.insert("stderr".to_string(), Value::String(Rc::new(stderr)));
8901 result.insert("code".to_string(), Value::Int(code as i64));
8902 result.insert("success".to_string(), Value::Bool(output.status.success()));
8903
8904 Ok(Value::Map(Rc::new(RefCell::new(result))))
8905 }
8906 Err(e) => Err(RuntimeError::new(format!("shell() error: {}", e))),
8907 }
8908 });
8909
8910 define(interp, "platform", Some(0), |_, _| {
8912 Ok(Value::String(Rc::new(std::env::consts::OS.to_string())))
8913 });
8914
8915 define(interp, "arch", Some(0), |_, _| {
8917 Ok(Value::String(Rc::new(std::env::consts::ARCH.to_string())))
8918 });
8919
8920 define(interp, "num_cpus·get", Some(0), |_, _| {
8922 Ok(Value::Int(num_cpus::get() as i64))
8923 });
8924
8925 define(interp, "num_cpus·get_physical", Some(0), |_, _| {
8927 Ok(Value::Int(num_cpus::get_physical() as i64))
8928 });
8929}
8930
8931fn register_stats(interp: &mut Interpreter) {
8936 fn extract_numbers(val: &Value) -> Result<Vec<f64>, RuntimeError> {
8938 match val {
8939 Value::Array(arr) => {
8940 let arr = arr.borrow();
8941 let mut nums = Vec::new();
8942 for v in arr.iter() {
8943 match v {
8944 Value::Int(n) => nums.push(*n as f64),
8945 Value::Float(f) => nums.push(*f),
8946 _ => {
8947 return Err(RuntimeError::new("stats functions require numeric array"))
8948 }
8949 }
8950 }
8951 Ok(nums)
8952 }
8953 _ => Err(RuntimeError::new("stats functions require array")),
8954 }
8955 }
8956
8957 define(interp, "mean", Some(1), |_, args| {
8959 let nums = extract_numbers(&args[0])?;
8960 if nums.is_empty() {
8961 return Ok(Value::Float(0.0));
8962 }
8963 let sum: f64 = nums.iter().sum();
8964 Ok(Value::Float(sum / nums.len() as f64))
8965 });
8966
8967 define(interp, "median", Some(1), |_, args| {
8969 let mut nums = extract_numbers(&args[0])?;
8970 if nums.is_empty() {
8971 return Ok(Value::Float(0.0));
8972 }
8973 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
8974 let len = nums.len();
8975 if len % 2 == 0 {
8976 Ok(Value::Float((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
8977 } else {
8978 Ok(Value::Float(nums[len / 2]))
8979 }
8980 });
8981
8982 define(interp, "mode", Some(1), |_, args| {
8984 let nums = extract_numbers(&args[0])?;
8985 if nums.is_empty() {
8986 return Ok(Value::Null);
8987 }
8988
8989 let mut counts: HashMap<String, usize> = HashMap::new();
8990 for n in &nums {
8991 let key = format!("{:.10}", n);
8992 *counts.entry(key).or_insert(0) += 1;
8993 }
8994
8995 let max_count = counts.values().max().unwrap_or(&0);
8996 for n in &nums {
8997 let key = format!("{:.10}", n);
8998 if counts.get(&key) == Some(max_count) {
8999 return Ok(Value::Float(*n));
9000 }
9001 }
9002 Ok(Value::Null)
9003 });
9004
9005 define(interp, "variance", Some(1), |_, args| {
9007 let nums = extract_numbers(&args[0])?;
9008 if nums.is_empty() {
9009 return Ok(Value::Float(0.0));
9010 }
9011 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9012 let variance: f64 =
9013 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9014 Ok(Value::Float(variance))
9015 });
9016
9017 define(interp, "stddev", Some(1), |_, args| {
9019 let nums = extract_numbers(&args[0])?;
9020 if nums.is_empty() {
9021 return Ok(Value::Float(0.0));
9022 }
9023 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9024 let variance: f64 =
9025 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9026 Ok(Value::Float(variance.sqrt()))
9027 });
9028
9029 define(interp, "percentile", Some(2), |_, args| {
9031 let mut nums = extract_numbers(&args[0])?;
9032 let p = match &args[1] {
9033 Value::Int(n) => *n as f64,
9034 Value::Float(f) => *f,
9035 _ => {
9036 return Err(RuntimeError::new(
9037 "percentile() requires numeric percentile",
9038 ))
9039 }
9040 };
9041
9042 if nums.is_empty() {
9043 return Ok(Value::Float(0.0));
9044 }
9045
9046 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
9047 let idx = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
9048 Ok(Value::Float(nums[idx.min(nums.len() - 1)]))
9049 });
9050
9051 define(interp, "correlation", Some(2), |_, args| {
9053 let x = extract_numbers(&args[0])?;
9054 let y = extract_numbers(&args[1])?;
9055
9056 if x.len() != y.len() || x.is_empty() {
9057 return Err(RuntimeError::new(
9058 "correlation() requires equal-length non-empty arrays",
9059 ));
9060 }
9061
9062 let n = x.len() as f64;
9063 let mean_x: f64 = x.iter().sum::<f64>() / n;
9064 let mean_y: f64 = y.iter().sum::<f64>() / n;
9065
9066 let mut cov = 0.0;
9067 let mut var_x = 0.0;
9068 let mut var_y = 0.0;
9069
9070 for i in 0..x.len() {
9071 let dx = x[i] - mean_x;
9072 let dy = y[i] - mean_y;
9073 cov += dx * dy;
9074 var_x += dx * dx;
9075 var_y += dy * dy;
9076 }
9077
9078 if var_x == 0.0 || var_y == 0.0 {
9079 return Ok(Value::Float(0.0));
9080 }
9081
9082 Ok(Value::Float(cov / (var_x.sqrt() * var_y.sqrt())))
9083 });
9084
9085 define(interp, "range", Some(1), |_, args| {
9087 let nums = extract_numbers(&args[0])?;
9088 if nums.is_empty() {
9089 return Ok(Value::Float(0.0));
9090 }
9091 let min = nums.iter().cloned().fold(f64::INFINITY, f64::min);
9092 let max = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
9093 Ok(Value::Float(max - min))
9094 });
9095
9096 define(interp, "zscore", Some(1), |_, args| {
9098 let nums = extract_numbers(&args[0])?;
9099 if nums.is_empty() {
9100 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9101 }
9102
9103 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9104 let variance: f64 =
9105 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9106 let stddev = variance.sqrt();
9107
9108 if stddev == 0.0 {
9109 let zeros: Vec<Value> = nums.iter().map(|_| Value::Float(0.0)).collect();
9110 return Ok(Value::Array(Rc::new(RefCell::new(zeros))));
9111 }
9112
9113 let zscores: Vec<Value> = nums
9114 .iter()
9115 .map(|x| Value::Float((x - mean) / stddev))
9116 .collect();
9117 Ok(Value::Array(Rc::new(RefCell::new(zscores))))
9118 });
9119}
9120
9121fn register_matrix(interp: &mut Interpreter) {
9126 fn extract_matrix(val: &Value) -> Result<Vec<Vec<f64>>, RuntimeError> {
9128 match val {
9129 Value::Array(arr) => {
9130 let arr = arr.borrow();
9131 let mut matrix = Vec::new();
9132 for row in arr.iter() {
9133 match row {
9134 Value::Array(row_arr) => {
9135 let row_arr = row_arr.borrow();
9136 let mut row_vec = Vec::new();
9137 for v in row_arr.iter() {
9138 match v {
9139 Value::Int(n) => row_vec.push(*n as f64),
9140 Value::Float(f) => row_vec.push(*f),
9141 _ => {
9142 return Err(RuntimeError::new(
9143 "matrix requires numeric values",
9144 ))
9145 }
9146 }
9147 }
9148 matrix.push(row_vec);
9149 }
9150 _ => return Err(RuntimeError::new("matrix requires 2D array")),
9151 }
9152 }
9153 Ok(matrix)
9154 }
9155 _ => Err(RuntimeError::new("matrix requires array")),
9156 }
9157 }
9158
9159 fn matrix_to_value(m: Vec<Vec<f64>>) -> Value {
9160 let rows: Vec<Value> = m
9161 .into_iter()
9162 .map(|row| {
9163 let cols: Vec<Value> = row.into_iter().map(Value::Float).collect();
9164 Value::Array(Rc::new(RefCell::new(cols)))
9165 })
9166 .collect();
9167 Value::Array(Rc::new(RefCell::new(rows)))
9168 }
9169
9170 define(interp, "matrix_new", Some(3), |_, args| {
9172 let rows = match &args[0] {
9173 Value::Int(n) => *n as usize,
9174 _ => return Err(RuntimeError::new("matrix_new() requires integer rows")),
9175 };
9176 let cols = match &args[1] {
9177 Value::Int(n) => *n as usize,
9178 _ => return Err(RuntimeError::new("matrix_new() requires integer cols")),
9179 };
9180 let fill = match &args[2] {
9181 Value::Int(n) => *n as f64,
9182 Value::Float(f) => *f,
9183 _ => 0.0,
9184 };
9185
9186 let matrix = vec![vec![fill; cols]; rows];
9187 Ok(matrix_to_value(matrix))
9188 });
9189
9190 define(interp, "matrix_identity", Some(1), |_, args| {
9192 let size = match &args[0] {
9193 Value::Int(n) => *n as usize,
9194 _ => return Err(RuntimeError::new("matrix_identity() requires integer size")),
9195 };
9196
9197 let mut matrix = vec![vec![0.0; size]; size];
9198 for i in 0..size {
9199 matrix[i][i] = 1.0;
9200 }
9201 Ok(matrix_to_value(matrix))
9202 });
9203
9204 define(interp, "matrix_add", Some(2), |_, args| {
9206 let a = extract_matrix(&args[0])?;
9207 let b = extract_matrix(&args[1])?;
9208
9209 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
9210 return Err(RuntimeError::new(
9211 "matrix_add() requires same-size matrices",
9212 ));
9213 }
9214
9215 let result: Vec<Vec<f64>> = a
9216 .iter()
9217 .zip(b.iter())
9218 .map(|(row_a, row_b)| row_a.iter().zip(row_b.iter()).map(|(x, y)| x + y).collect())
9219 .collect();
9220
9221 Ok(matrix_to_value(result))
9222 });
9223
9224 define(interp, "matrix_sub", Some(2), |_, args| {
9226 let a = extract_matrix(&args[0])?;
9227 let b = extract_matrix(&args[1])?;
9228
9229 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
9230 return Err(RuntimeError::new(
9231 "matrix_sub() requires same-size matrices",
9232 ));
9233 }
9234
9235 let result: Vec<Vec<f64>> = a
9236 .iter()
9237 .zip(b.iter())
9238 .map(|(row_a, row_b)| row_a.iter().zip(row_b.iter()).map(|(x, y)| x - y).collect())
9239 .collect();
9240
9241 Ok(matrix_to_value(result))
9242 });
9243
9244 define(interp, "matrix_mul", Some(2), |_, args| {
9246 let a = extract_matrix(&args[0])?;
9247 let b = extract_matrix(&args[1])?;
9248
9249 if a.is_empty() || b.is_empty() || a[0].len() != b.len() {
9250 return Err(RuntimeError::new(
9251 "matrix_mul() requires compatible matrices (a.cols == b.rows)",
9252 ));
9253 }
9254
9255 let rows = a.len();
9256 let cols = b[0].len();
9257 let inner = b.len();
9258
9259 let mut result = vec![vec![0.0; cols]; rows];
9260 for i in 0..rows {
9261 for j in 0..cols {
9262 for k in 0..inner {
9263 result[i][j] += a[i][k] * b[k][j];
9264 }
9265 }
9266 }
9267
9268 Ok(matrix_to_value(result))
9269 });
9270
9271 define(interp, "matrix_scale", Some(2), |_, args| {
9273 let m = extract_matrix(&args[0])?;
9274 let scale = match &args[1] {
9275 Value::Int(n) => *n as f64,
9276 Value::Float(f) => *f,
9277 _ => return Err(RuntimeError::new("matrix_scale() requires numeric scalar")),
9278 };
9279
9280 let result: Vec<Vec<f64>> = m
9281 .iter()
9282 .map(|row| row.iter().map(|x| x * scale).collect())
9283 .collect();
9284
9285 Ok(matrix_to_value(result))
9286 });
9287
9288 define(interp, "matrix_transpose", Some(1), |_, args| {
9290 let m = extract_matrix(&args[0])?;
9291 if m.is_empty() {
9292 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9293 }
9294
9295 let rows = m.len();
9296 let cols = m[0].len();
9297 let mut result = vec![vec![0.0; rows]; cols];
9298
9299 for i in 0..rows {
9300 for j in 0..cols {
9301 result[j][i] = m[i][j];
9302 }
9303 }
9304
9305 Ok(matrix_to_value(result))
9306 });
9307
9308 define(interp, "matrix_det", Some(1), |_, args| {
9310 let m = extract_matrix(&args[0])?;
9311
9312 if m.is_empty() || m.len() != m[0].len() {
9313 return Err(RuntimeError::new("matrix_det() requires square matrix"));
9314 }
9315
9316 let n = m.len();
9317 match n {
9318 1 => Ok(Value::Float(m[0][0])),
9319 2 => Ok(Value::Float(m[0][0] * m[1][1] - m[0][1] * m[1][0])),
9320 3 => {
9321 let det = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
9322 - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
9323 + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
9324 Ok(Value::Float(det))
9325 }
9326 _ => Err(RuntimeError::new(
9327 "matrix_det() only supports up to 3x3 matrices",
9328 )),
9329 }
9330 });
9331
9332 define(interp, "matrix_trace", Some(1), |_, args| {
9334 let m = extract_matrix(&args[0])?;
9335
9336 let size = m.len().min(if m.is_empty() { 0 } else { m[0].len() });
9337 let trace: f64 = (0..size).map(|i| m[i][i]).sum();
9338
9339 Ok(Value::Float(trace))
9340 });
9341
9342 define(interp, "matrix_dot", Some(2), |_, args| {
9344 fn extract_vector(val: &Value) -> Result<Vec<f64>, RuntimeError> {
9345 match val {
9346 Value::Array(arr) => {
9347 let arr = arr.borrow();
9348 let mut vec = Vec::new();
9349 for v in arr.iter() {
9350 match v {
9351 Value::Int(n) => vec.push(*n as f64),
9352 Value::Float(f) => vec.push(*f),
9353 _ => {
9354 return Err(RuntimeError::new(
9355 "dot product requires numeric vectors",
9356 ))
9357 }
9358 }
9359 }
9360 Ok(vec)
9361 }
9362 _ => Err(RuntimeError::new("dot product requires arrays")),
9363 }
9364 }
9365
9366 let a = extract_vector(&args[0])?;
9367 let b = extract_vector(&args[1])?;
9368
9369 if a.len() != b.len() {
9370 return Err(RuntimeError::new(
9371 "matrix_dot() requires same-length vectors",
9372 ));
9373 }
9374
9375 let dot: f64 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
9376 Ok(Value::Float(dot))
9377 });
9378}
9379
9380fn mod_inverse(a: i64, m: i64) -> Option<i64> {
9382 let (mut old_r, mut r) = (a, m);
9383 let (mut old_s, mut s) = (1i64, 0i64);
9384
9385 while r != 0 {
9386 let q = old_r / r;
9387 (old_r, r) = (r, old_r - q * r);
9388 (old_s, s) = (s, old_s - q * s);
9389 }
9390
9391 if old_r != 1 {
9392 None } else {
9394 Some(old_s.rem_euclid(m))
9395 }
9396}
9397
9398fn register_functional(interp: &mut Interpreter) {
9404 define(interp, "identity", Some(1), |_, args| Ok(args[0].clone()));
9406
9407 define(interp, "const_fn", Some(1), |_, args| Ok(args[0].clone()));
9409
9410 define(interp, "apply", Some(2), |interp, args| {
9412 let func = match &args[0] {
9413 Value::Function(f) => f.clone(),
9414 _ => {
9415 return Err(RuntimeError::new(
9416 "apply: first argument must be a function",
9417 ))
9418 }
9419 };
9420 let fn_args = match &args[1] {
9421 Value::Array(arr) => arr.borrow().clone(),
9422 _ => return Err(RuntimeError::new("apply: second argument must be an array")),
9423 };
9424 interp.call_function(&func, fn_args)
9425 });
9426
9427 define(interp, "flip", Some(3), |interp, args| {
9429 let func = match &args[0] {
9430 Value::Function(f) => f.clone(),
9431 _ => return Err(RuntimeError::new("flip: first argument must be a function")),
9432 };
9433 let flipped_args = vec![args[2].clone(), args[1].clone()];
9434 interp.call_function(&func, flipped_args)
9435 });
9436
9437 define(interp, "tap", Some(2), |interp, args| {
9439 let val = args[0].clone();
9440 let func = match &args[1] {
9441 Value::Function(f) => f.clone(),
9442 _ => return Err(RuntimeError::new("tap: second argument must be a function")),
9443 };
9444 let _ = interp.call_function(&func, vec![val.clone()]);
9445 Ok(val)
9446 });
9447
9448 define(interp, "thunk", Some(1), |_, args| {
9450 Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()]))))
9451 });
9452
9453 define(interp, "force", Some(1), |interp, args| match &args[0] {
9455 Value::Array(arr) => {
9456 let arr = arr.borrow();
9457 if arr.len() == 1 {
9458 if let Value::Function(f) = &arr[0] {
9459 return interp.call_function(f, vec![]);
9460 }
9461 }
9462 Ok(arr.get(0).cloned().unwrap_or(Value::Null))
9463 }
9464 v => Ok(v.clone()),
9465 });
9466
9467 define(interp, "negate", Some(2), |interp, args| {
9469 let func = match &args[0] {
9470 Value::Function(f) => f.clone(),
9471 _ => {
9472 return Err(RuntimeError::new(
9473 "negate: first argument must be a function",
9474 ))
9475 }
9476 };
9477 let result = interp.call_function(&func, vec![args[1].clone()])?;
9478 Ok(Value::Bool(!is_truthy(&result)))
9479 });
9480
9481 define(interp, "complement", Some(2), |interp, args| {
9483 let func = match &args[0] {
9484 Value::Function(f) => f.clone(),
9485 _ => {
9486 return Err(RuntimeError::new(
9487 "complement: first argument must be a function",
9488 ))
9489 }
9490 };
9491 let result = interp.call_function(&func, vec![args[1].clone()])?;
9492 Ok(Value::Bool(!is_truthy(&result)))
9493 });
9494
9495 define(interp, "partial", None, |interp, args| {
9497 if args.len() < 2 {
9498 return Err(RuntimeError::new(
9499 "partial: requires at least function and one argument",
9500 ));
9501 }
9502 let func = match &args[0] {
9503 Value::Function(f) => f.clone(),
9504 _ => {
9505 return Err(RuntimeError::new(
9506 "partial: first argument must be a function",
9507 ))
9508 }
9509 };
9510 let partial_args: Vec<Value> = args[1..].to_vec();
9511 interp.call_function(&func, partial_args)
9512 });
9513
9514 define(interp, "juxt", None, |interp, args| {
9516 if args.len() < 2 {
9517 return Err(RuntimeError::new("juxt: requires functions and a value"));
9518 }
9519 let val = args.last().unwrap().clone();
9520 let results: Result<Vec<Value>, _> = args[..args.len() - 1]
9521 .iter()
9522 .map(|f| match f {
9523 Value::Function(func) => interp.call_function(func, vec![val.clone()]),
9524 _ => Err(RuntimeError::new(
9525 "juxt: all but last argument must be functions",
9526 )),
9527 })
9528 .collect();
9529 Ok(Value::Array(Rc::new(RefCell::new(results?))))
9530 });
9531}
9532
9533fn register_benchmark(interp: &mut Interpreter) {
9535 define(interp, "bench", Some(2), |interp, args| {
9537 let func = match &args[0] {
9538 Value::Function(f) => f.clone(),
9539 _ => {
9540 return Err(RuntimeError::new(
9541 "bench: first argument must be a function",
9542 ))
9543 }
9544 };
9545 let iterations = match &args[1] {
9546 Value::Int(n) => *n as usize,
9547 _ => {
9548 return Err(RuntimeError::new(
9549 "bench: second argument must be an integer",
9550 ))
9551 }
9552 };
9553
9554 let start = std::time::Instant::now();
9555 for _ in 0..iterations {
9556 let _ = interp.call_function(&func, vec![])?;
9557 }
9558 let elapsed = start.elapsed();
9559 let avg_ms = elapsed.as_secs_f64() * 1000.0 / iterations as f64;
9560 Ok(Value::Float(avg_ms))
9561 });
9562
9563 define(interp, "time_it", Some(1), |interp, args| {
9565 let func = match &args[0] {
9566 Value::Function(f) => f.clone(),
9567 _ => return Err(RuntimeError::new("time_it: argument must be a function")),
9568 };
9569
9570 let start = std::time::Instant::now();
9571 let result = interp.call_function(&func, vec![])?;
9572 let elapsed_ms = start.elapsed().as_secs_f64() * 1000.0;
9573
9574 Ok(Value::Tuple(Rc::new(vec![
9575 result,
9576 Value::Float(elapsed_ms),
9577 ])))
9578 });
9579
9580 define(interp, "stopwatch_start", Some(0), |_, _| {
9582 let elapsed = std::time::SystemTime::now()
9583 .duration_since(std::time::UNIX_EPOCH)
9584 .unwrap_or_default();
9585 Ok(Value::Float(elapsed.as_secs_f64() * 1000.0))
9586 });
9587
9588 define(interp, "stopwatch_elapsed", Some(1), |_, args| {
9590 let start_ms = match &args[0] {
9591 Value::Float(f) => *f,
9592 Value::Int(n) => *n as f64,
9593 _ => {
9594 return Err(RuntimeError::new(
9595 "stopwatch_elapsed: argument must be a number",
9596 ))
9597 }
9598 };
9599 let now = std::time::SystemTime::now()
9600 .duration_since(std::time::UNIX_EPOCH)
9601 .unwrap_or_default();
9602 let now_ms = now.as_secs_f64() * 1000.0;
9603 Ok(Value::Float(now_ms - start_ms))
9604 });
9605
9606 define(interp, "compare_bench", Some(3), |interp, args| {
9608 let func1 = match &args[0] {
9609 Value::Function(f) => f.clone(),
9610 _ => {
9611 return Err(RuntimeError::new(
9612 "compare_bench: first argument must be a function",
9613 ))
9614 }
9615 };
9616 let func2 = match &args[1] {
9617 Value::Function(f) => f.clone(),
9618 _ => {
9619 return Err(RuntimeError::new(
9620 "compare_bench: second argument must be a function",
9621 ))
9622 }
9623 };
9624 let iterations = match &args[2] {
9625 Value::Int(n) => *n as usize,
9626 _ => {
9627 return Err(RuntimeError::new(
9628 "compare_bench: third argument must be an integer",
9629 ))
9630 }
9631 };
9632
9633 let start1 = std::time::Instant::now();
9634 for _ in 0..iterations {
9635 let _ = interp.call_function(&func1, vec![])?;
9636 }
9637 let time1 = start1.elapsed().as_secs_f64();
9638
9639 let start2 = std::time::Instant::now();
9640 for _ in 0..iterations {
9641 let _ = interp.call_function(&func2, vec![])?;
9642 }
9643 let time2 = start2.elapsed().as_secs_f64();
9644
9645 let mut results = std::collections::HashMap::new();
9646 results.insert("time1_ms".to_string(), Value::Float(time1 * 1000.0));
9647 results.insert("time2_ms".to_string(), Value::Float(time2 * 1000.0));
9648 results.insert("speedup".to_string(), Value::Float(time1 / time2));
9649 results.insert("iterations".to_string(), Value::Int(iterations as i64));
9650
9651 Ok(Value::Struct {
9652 name: "BenchResult".to_string(),
9653 fields: Rc::new(RefCell::new(results)),
9654 })
9655 });
9656
9657 define(interp, "memory_usage", Some(0), |_, _| Ok(Value::Int(0)));
9659}
9660
9661fn register_itertools(interp: &mut Interpreter) {
9663 define(interp, "cycle", Some(2), |_, args| {
9665 let arr = match &args[0] {
9666 Value::Array(a) => a.borrow().clone(),
9667 _ => return Err(RuntimeError::new("cycle: first argument must be an array")),
9668 };
9669 let n = match &args[1] {
9670 Value::Int(n) => *n as usize,
9671 _ => {
9672 return Err(RuntimeError::new(
9673 "cycle: second argument must be an integer",
9674 ))
9675 }
9676 };
9677
9678 if arr.is_empty() {
9679 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9680 }
9681
9682 let result: Vec<Value> = (0..n).map(|i| arr[i % arr.len()].clone()).collect();
9683 Ok(Value::Array(Rc::new(RefCell::new(result))))
9684 });
9685
9686 define(interp, "repeat_val", Some(2), |_, args| {
9688 let val = args[0].clone();
9689 let n = match &args[1] {
9690 Value::Int(n) => *n as usize,
9691 _ => {
9692 return Err(RuntimeError::new(
9693 "repeat_val: second argument must be an integer",
9694 ))
9695 }
9696 };
9697
9698 let result: Vec<Value> = std::iter::repeat(val).take(n).collect();
9699 Ok(Value::Array(Rc::new(RefCell::new(result))))
9700 });
9701
9702 define(interp, "take_while", Some(2), |interp, args| {
9704 let arr = match &args[0] {
9705 Value::Array(a) => a.borrow().clone(),
9706 _ => {
9707 return Err(RuntimeError::new(
9708 "take_while: first argument must be an array",
9709 ))
9710 }
9711 };
9712 let pred = match &args[1] {
9713 Value::Function(f) => f.clone(),
9714 _ => {
9715 return Err(RuntimeError::new(
9716 "take_while: second argument must be a function",
9717 ))
9718 }
9719 };
9720
9721 let mut result = Vec::new();
9722 for item in arr {
9723 let keep = interp.call_function(&pred, vec![item.clone()])?;
9724 if is_truthy(&keep) {
9725 result.push(item);
9726 } else {
9727 break;
9728 }
9729 }
9730 Ok(Value::Array(Rc::new(RefCell::new(result))))
9731 });
9732
9733 define(interp, "drop_while", Some(2), |interp, args| {
9735 let arr = match &args[0] {
9736 Value::Array(a) => a.borrow().clone(),
9737 _ => {
9738 return Err(RuntimeError::new(
9739 "drop_while: first argument must be an array",
9740 ))
9741 }
9742 };
9743 let pred = match &args[1] {
9744 Value::Function(f) => f.clone(),
9745 _ => {
9746 return Err(RuntimeError::new(
9747 "drop_while: second argument must be a function",
9748 ))
9749 }
9750 };
9751
9752 let mut dropping = true;
9753 let mut result = Vec::new();
9754 for item in arr {
9755 if dropping {
9756 let drop = interp.call_function(&pred, vec![item.clone()])?;
9757 if !is_truthy(&drop) {
9758 dropping = false;
9759 result.push(item);
9760 }
9761 } else {
9762 result.push(item);
9763 }
9764 }
9765 Ok(Value::Array(Rc::new(RefCell::new(result))))
9766 });
9767
9768 define(interp, "group_by", Some(2), |interp, args| {
9770 let arr = match &args[0] {
9771 Value::Array(a) => a.borrow().clone(),
9772 _ => {
9773 return Err(RuntimeError::new(
9774 "group_by: first argument must be an array",
9775 ))
9776 }
9777 };
9778 let key_fn = match &args[1] {
9779 Value::Function(f) => f.clone(),
9780 _ => {
9781 return Err(RuntimeError::new(
9782 "group_by: second argument must be a function",
9783 ))
9784 }
9785 };
9786
9787 let mut groups: Vec<Value> = Vec::new();
9788 let mut current_group: Vec<Value> = Vec::new();
9789 let mut current_key: Option<Value> = None;
9790
9791 for item in arr {
9792 let key = interp.call_function(&key_fn, vec![item.clone()])?;
9793 match ¤t_key {
9794 Some(k) if value_eq(k, &key) => {
9795 current_group.push(item);
9796 }
9797 _ => {
9798 if !current_group.is_empty() {
9799 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
9800 }
9801 current_group = vec![item];
9802 current_key = Some(key);
9803 }
9804 }
9805 }
9806 if !current_group.is_empty() {
9807 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
9808 }
9809
9810 Ok(Value::Array(Rc::new(RefCell::new(groups))))
9811 });
9812
9813 define(interp, "partition", Some(2), |interp, args| {
9815 let arr = match &args[0] {
9816 Value::Array(a) => a.borrow().clone(),
9817 _ => {
9818 return Err(RuntimeError::new(
9819 "partition: first argument must be an array",
9820 ))
9821 }
9822 };
9823 let pred = match &args[1] {
9824 Value::Function(f) => f.clone(),
9825 _ => {
9826 return Err(RuntimeError::new(
9827 "partition: second argument must be a function",
9828 ))
9829 }
9830 };
9831
9832 let mut true_items = Vec::new();
9833 let mut false_items = Vec::new();
9834
9835 for item in arr {
9836 let result = interp.call_function(&pred, vec![item.clone()])?;
9837 if is_truthy(&result) {
9838 true_items.push(item);
9839 } else {
9840 false_items.push(item);
9841 }
9842 }
9843
9844 Ok(Value::Tuple(Rc::new(vec![
9845 Value::Array(Rc::new(RefCell::new(true_items))),
9846 Value::Array(Rc::new(RefCell::new(false_items))),
9847 ])))
9848 });
9849
9850 define(interp, "interleave", Some(2), |_, args| {
9852 let arr1 = match &args[0] {
9853 Value::Array(a) => a.borrow().clone(),
9854 _ => {
9855 return Err(RuntimeError::new(
9856 "interleave: first argument must be an array",
9857 ))
9858 }
9859 };
9860 let arr2 = match &args[1] {
9861 Value::Array(a) => a.borrow().clone(),
9862 _ => {
9863 return Err(RuntimeError::new(
9864 "interleave: second argument must be an array",
9865 ))
9866 }
9867 };
9868
9869 let mut result = Vec::new();
9870 let mut i1 = arr1.into_iter();
9871 let mut i2 = arr2.into_iter();
9872
9873 loop {
9874 match (i1.next(), i2.next()) {
9875 (Some(a), Some(b)) => {
9876 result.push(a);
9877 result.push(b);
9878 }
9879 (Some(a), None) => {
9880 result.push(a);
9881 result.extend(i1);
9882 break;
9883 }
9884 (None, Some(b)) => {
9885 result.push(b);
9886 result.extend(i2);
9887 break;
9888 }
9889 (None, None) => break,
9890 }
9891 }
9892
9893 Ok(Value::Array(Rc::new(RefCell::new(result))))
9894 });
9895
9896 define(interp, "chunks", Some(2), |_, args| {
9898 let arr = match &args[0] {
9899 Value::Array(a) => a.borrow().clone(),
9900 _ => return Err(RuntimeError::new("chunks: first argument must be an array")),
9901 };
9902 let size = match &args[1] {
9903 Value::Int(n) if *n > 0 => *n as usize,
9904 _ => {
9905 return Err(RuntimeError::new(
9906 "chunks: second argument must be a positive integer",
9907 ))
9908 }
9909 };
9910
9911 let chunks: Vec<Value> = arr
9912 .chunks(size)
9913 .map(|chunk| Value::Array(Rc::new(RefCell::new(chunk.to_vec()))))
9914 .collect();
9915
9916 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
9917 });
9918
9919 define(interp, "windows", Some(2), |_, args| {
9921 let arr = match &args[0] {
9922 Value::Array(a) => a.borrow().clone(),
9923 _ => {
9924 return Err(RuntimeError::new(
9925 "windows: first argument must be an array",
9926 ))
9927 }
9928 };
9929 let size = match &args[1] {
9930 Value::Int(n) if *n > 0 => *n as usize,
9931 _ => {
9932 return Err(RuntimeError::new(
9933 "windows: second argument must be a positive integer",
9934 ))
9935 }
9936 };
9937
9938 if arr.len() < size {
9939 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9940 }
9941
9942 let windows: Vec<Value> = arr
9943 .windows(size)
9944 .map(|window| Value::Array(Rc::new(RefCell::new(window.to_vec()))))
9945 .collect();
9946
9947 Ok(Value::Array(Rc::new(RefCell::new(windows))))
9948 });
9949
9950 define(interp, "scan", Some(3), |interp, args| {
9952 let arr = match &args[0] {
9953 Value::Array(a) => a.borrow().clone(),
9954 _ => return Err(RuntimeError::new("scan: first argument must be an array")),
9955 };
9956 let init = args[1].clone();
9957 let func = match &args[2] {
9958 Value::Function(f) => f.clone(),
9959 _ => return Err(RuntimeError::new("scan: third argument must be a function")),
9960 };
9961
9962 let mut results = vec![init.clone()];
9963 let mut acc = init;
9964
9965 for item in arr {
9966 acc = interp.call_function(&func, vec![acc, item])?;
9967 results.push(acc.clone());
9968 }
9969
9970 Ok(Value::Array(Rc::new(RefCell::new(results))))
9971 });
9972
9973 define(interp, "frequencies", Some(1), |_, args| {
9975 let arr = match &args[0] {
9976 Value::Array(a) => a.borrow().clone(),
9977 _ => return Err(RuntimeError::new("frequencies: argument must be an array")),
9978 };
9979
9980 let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
9981 for item in &arr {
9982 let key = format!("{}", item);
9983 *counts.entry(key).or_insert(0) += 1;
9984 }
9985
9986 let result: std::collections::HashMap<String, Value> = counts
9987 .into_iter()
9988 .map(|(k, v)| (k, Value::Int(v)))
9989 .collect();
9990
9991 Ok(Value::Map(Rc::new(RefCell::new(result))))
9992 });
9993
9994 define(interp, "dedupe", Some(1), |_, args| {
9996 let arr = match &args[0] {
9997 Value::Array(a) => a.borrow().clone(),
9998 _ => return Err(RuntimeError::new("dedupe: argument must be an array")),
9999 };
10000
10001 let mut result = Vec::new();
10002 let mut prev: Option<Value> = None;
10003
10004 for item in arr {
10005 match &prev {
10006 Some(p) if value_eq(p, &item) => continue,
10007 _ => {
10008 result.push(item.clone());
10009 prev = Some(item);
10010 }
10011 }
10012 }
10013
10014 Ok(Value::Array(Rc::new(RefCell::new(result))))
10015 });
10016
10017 define(interp, "unique", Some(1), |_, args| {
10019 let arr = match &args[0] {
10020 Value::Array(a) => a.borrow().clone(),
10021 _ => return Err(RuntimeError::new("unique: argument must be an array")),
10022 };
10023
10024 let mut seen = std::collections::HashSet::new();
10025 let mut result = Vec::new();
10026
10027 for item in arr {
10028 let key = format!("{}", item);
10029 if seen.insert(key) {
10030 result.push(item);
10031 }
10032 }
10033
10034 Ok(Value::Array(Rc::new(RefCell::new(result))))
10035 });
10036}
10037
10038fn register_ranges(interp: &mut Interpreter) {
10040 define(interp, "range_step", Some(3), |_, args| {
10042 let start = match &args[0] {
10043 Value::Int(n) => *n,
10044 Value::Float(f) => *f as i64,
10045 _ => return Err(RuntimeError::new("range_step: start must be a number")),
10046 };
10047 let end = match &args[1] {
10048 Value::Int(n) => *n,
10049 Value::Float(f) => *f as i64,
10050 _ => return Err(RuntimeError::new("range_step: end must be a number")),
10051 };
10052 let step = match &args[2] {
10053 Value::Int(n) if *n != 0 => *n,
10054 Value::Float(f) if *f != 0.0 => *f as i64,
10055 _ => {
10056 return Err(RuntimeError::new(
10057 "range_step: step must be a non-zero number",
10058 ))
10059 }
10060 };
10061
10062 let mut result = Vec::new();
10063 if step > 0 {
10064 let mut i = start;
10065 while i < end {
10066 result.push(Value::Int(i));
10067 i += step;
10068 }
10069 } else {
10070 let mut i = start;
10071 while i > end {
10072 result.push(Value::Int(i));
10073 i += step;
10074 }
10075 }
10076
10077 Ok(Value::Array(Rc::new(RefCell::new(result))))
10078 });
10079
10080 define(interp, "linspace", Some(3), |_, args| {
10082 let start = match &args[0] {
10083 Value::Int(n) => *n as f64,
10084 Value::Float(f) => *f,
10085 _ => return Err(RuntimeError::new("linspace: start must be a number")),
10086 };
10087 let end = match &args[1] {
10088 Value::Int(n) => *n as f64,
10089 Value::Float(f) => *f,
10090 _ => return Err(RuntimeError::new("linspace: end must be a number")),
10091 };
10092 let n = match &args[2] {
10093 Value::Int(n) if *n > 0 => *n as usize,
10094 _ => {
10095 return Err(RuntimeError::new(
10096 "linspace: count must be a positive integer",
10097 ))
10098 }
10099 };
10100
10101 if n == 1 {
10102 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10103 start,
10104 )]))));
10105 }
10106
10107 let step = (end - start) / (n - 1) as f64;
10108 let result: Vec<Value> = (0..n)
10109 .map(|i| Value::Float(start + step * i as f64))
10110 .collect();
10111
10112 Ok(Value::Array(Rc::new(RefCell::new(result))))
10113 });
10114
10115 define(interp, "logspace", Some(3), |_, args| {
10117 let start_exp = match &args[0] {
10118 Value::Int(n) => *n as f64,
10119 Value::Float(f) => *f,
10120 _ => {
10121 return Err(RuntimeError::new(
10122 "logspace: start exponent must be a number",
10123 ))
10124 }
10125 };
10126 let end_exp = match &args[1] {
10127 Value::Int(n) => *n as f64,
10128 Value::Float(f) => *f,
10129 _ => return Err(RuntimeError::new("logspace: end exponent must be a number")),
10130 };
10131 let n = match &args[2] {
10132 Value::Int(n) if *n > 0 => *n as usize,
10133 _ => {
10134 return Err(RuntimeError::new(
10135 "logspace: count must be a positive integer",
10136 ))
10137 }
10138 };
10139
10140 if n == 1 {
10141 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10142 10f64.powf(start_exp),
10143 )]))));
10144 }
10145
10146 let step = (end_exp - start_exp) / (n - 1) as f64;
10147 let result: Vec<Value> = (0..n)
10148 .map(|i| Value::Float(10f64.powf(start_exp + step * i as f64)))
10149 .collect();
10150
10151 Ok(Value::Array(Rc::new(RefCell::new(result))))
10152 });
10153
10154 define(interp, "arange", Some(3), |_, args| {
10156 let start = match &args[0] {
10157 Value::Int(n) => *n as f64,
10158 Value::Float(f) => *f,
10159 _ => return Err(RuntimeError::new("arange: start must be a number")),
10160 };
10161 let stop = match &args[1] {
10162 Value::Int(n) => *n as f64,
10163 Value::Float(f) => *f,
10164 _ => return Err(RuntimeError::new("arange: stop must be a number")),
10165 };
10166 let step = match &args[2] {
10167 Value::Int(n) if *n != 0 => *n as f64,
10168 Value::Float(f) if *f != 0.0 => *f,
10169 _ => return Err(RuntimeError::new("arange: step must be a non-zero number")),
10170 };
10171
10172 let mut result = Vec::new();
10173 if step > 0.0 {
10174 let mut x = start;
10175 while x < stop {
10176 result.push(Value::Float(x));
10177 x += step;
10178 }
10179 } else {
10180 let mut x = start;
10181 while x > stop {
10182 result.push(Value::Float(x));
10183 x += step;
10184 }
10185 }
10186
10187 Ok(Value::Array(Rc::new(RefCell::new(result))))
10188 });
10189
10190 define(interp, "geomspace", Some(3), |_, args| {
10192 let start = match &args[0] {
10193 Value::Int(n) if *n > 0 => *n as f64,
10194 Value::Float(f) if *f > 0.0 => *f,
10195 _ => {
10196 return Err(RuntimeError::new(
10197 "geomspace: start must be a positive number",
10198 ))
10199 }
10200 };
10201 let end = match &args[1] {
10202 Value::Int(n) if *n > 0 => *n as f64,
10203 Value::Float(f) if *f > 0.0 => *f,
10204 _ => {
10205 return Err(RuntimeError::new(
10206 "geomspace: end must be a positive number",
10207 ))
10208 }
10209 };
10210 let n = match &args[2] {
10211 Value::Int(n) if *n > 0 => *n as usize,
10212 _ => {
10213 return Err(RuntimeError::new(
10214 "geomspace: count must be a positive integer",
10215 ))
10216 }
10217 };
10218
10219 if n == 1 {
10220 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10221 start,
10222 )]))));
10223 }
10224
10225 let ratio = (end / start).powf(1.0 / (n - 1) as f64);
10226 let result: Vec<Value> = (0..n)
10227 .map(|i| Value::Float(start * ratio.powi(i as i32)))
10228 .collect();
10229
10230 Ok(Value::Array(Rc::new(RefCell::new(result))))
10231 });
10232}
10233
10234fn register_bitwise(interp: &mut Interpreter) {
10236 define(interp, "bit_and", Some(2), |_, args| {
10237 let a = match &args[0] {
10238 Value::Int(n) => *n,
10239 _ => return Err(RuntimeError::new("bit_and: arguments must be integers")),
10240 };
10241 let b = match &args[1] {
10242 Value::Int(n) => *n,
10243 _ => return Err(RuntimeError::new("bit_and: arguments must be integers")),
10244 };
10245 Ok(Value::Int(a & b))
10246 });
10247
10248 define(interp, "bit_or", Some(2), |_, args| {
10249 let a = match &args[0] {
10250 Value::Int(n) => *n,
10251 _ => return Err(RuntimeError::new("bit_or: arguments must be integers")),
10252 };
10253 let b = match &args[1] {
10254 Value::Int(n) => *n,
10255 _ => return Err(RuntimeError::new("bit_or: arguments must be integers")),
10256 };
10257 Ok(Value::Int(a | b))
10258 });
10259
10260 define(interp, "bit_xor", Some(2), |_, args| {
10261 let a = match &args[0] {
10262 Value::Int(n) => *n,
10263 _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")),
10264 };
10265 let b = match &args[1] {
10266 Value::Int(n) => *n,
10267 _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")),
10268 };
10269 Ok(Value::Int(a ^ b))
10270 });
10271
10272 define(interp, "bit_not", Some(1), |_, args| {
10273 let a = match &args[0] {
10274 Value::Int(n) => *n,
10275 _ => return Err(RuntimeError::new("bit_not: argument must be an integer")),
10276 };
10277 Ok(Value::Int(!a))
10278 });
10279
10280 define(interp, "bit_shl", Some(2), |_, args| {
10281 let a = match &args[0] {
10282 Value::Int(n) => *n,
10283 _ => {
10284 return Err(RuntimeError::new(
10285 "bit_shl: first argument must be an integer",
10286 ))
10287 }
10288 };
10289 let b = match &args[1] {
10290 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10291 _ => return Err(RuntimeError::new("bit_shl: shift amount must be 0-63")),
10292 };
10293 Ok(Value::Int(a << b))
10294 });
10295
10296 define(interp, "bit_shr", Some(2), |_, args| {
10297 let a = match &args[0] {
10298 Value::Int(n) => *n,
10299 _ => {
10300 return Err(RuntimeError::new(
10301 "bit_shr: first argument must be an integer",
10302 ))
10303 }
10304 };
10305 let b = match &args[1] {
10306 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10307 _ => return Err(RuntimeError::new("bit_shr: shift amount must be 0-63")),
10308 };
10309 Ok(Value::Int(a >> b))
10310 });
10311
10312 define(interp, "popcount", Some(1), |_, args| {
10313 let a = match &args[0] {
10314 Value::Int(n) => *n,
10315 _ => return Err(RuntimeError::new("popcount: argument must be an integer")),
10316 };
10317 Ok(Value::Int(a.count_ones() as i64))
10318 });
10319
10320 define(interp, "leading_zeros", Some(1), |_, args| {
10321 let a = match &args[0] {
10322 Value::Int(n) => *n,
10323 _ => {
10324 return Err(RuntimeError::new(
10325 "leading_zeros: argument must be an integer",
10326 ))
10327 }
10328 };
10329 Ok(Value::Int(a.leading_zeros() as i64))
10330 });
10331
10332 define(interp, "trailing_zeros", Some(1), |_, args| {
10333 let a = match &args[0] {
10334 Value::Int(n) => *n,
10335 _ => {
10336 return Err(RuntimeError::new(
10337 "trailing_zeros: argument must be an integer",
10338 ))
10339 }
10340 };
10341 Ok(Value::Int(a.trailing_zeros() as i64))
10342 });
10343
10344 define(interp, "bit_test", Some(2), |_, args| {
10345 let a = match &args[0] {
10346 Value::Int(n) => *n,
10347 _ => {
10348 return Err(RuntimeError::new(
10349 "bit_test: first argument must be an integer",
10350 ))
10351 }
10352 };
10353 let pos = match &args[1] {
10354 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10355 _ => return Err(RuntimeError::new("bit_test: position must be 0-63")),
10356 };
10357 Ok(Value::Bool((a >> pos) & 1 == 1))
10358 });
10359
10360 define(interp, "bit_set", Some(2), |_, args| {
10361 let a = match &args[0] {
10362 Value::Int(n) => *n,
10363 _ => {
10364 return Err(RuntimeError::new(
10365 "bit_set: first argument must be an integer",
10366 ))
10367 }
10368 };
10369 let pos = match &args[1] {
10370 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10371 _ => return Err(RuntimeError::new("bit_set: position must be 0-63")),
10372 };
10373 Ok(Value::Int(a | (1 << pos)))
10374 });
10375
10376 define(interp, "bit_clear", Some(2), |_, args| {
10377 let a = match &args[0] {
10378 Value::Int(n) => *n,
10379 _ => {
10380 return Err(RuntimeError::new(
10381 "bit_clear: first argument must be an integer",
10382 ))
10383 }
10384 };
10385 let pos = match &args[1] {
10386 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10387 _ => return Err(RuntimeError::new("bit_clear: position must be 0-63")),
10388 };
10389 Ok(Value::Int(a & !(1 << pos)))
10390 });
10391
10392 define(interp, "bit_toggle", Some(2), |_, args| {
10393 let a = match &args[0] {
10394 Value::Int(n) => *n,
10395 _ => {
10396 return Err(RuntimeError::new(
10397 "bit_toggle: first argument must be an integer",
10398 ))
10399 }
10400 };
10401 let pos = match &args[1] {
10402 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10403 _ => return Err(RuntimeError::new("bit_toggle: position must be 0-63")),
10404 };
10405 Ok(Value::Int(a ^ (1 << pos)))
10406 });
10407
10408 define(interp, "to_binary", Some(1), |_, args| {
10409 let a = match &args[0] {
10410 Value::Int(n) => *n,
10411 _ => return Err(RuntimeError::new("to_binary: argument must be an integer")),
10412 };
10413 Ok(Value::String(Rc::new(format!("{:b}", a))))
10414 });
10415
10416 define(interp, "from_binary", Some(1), |_, args| {
10417 let s = match &args[0] {
10418 Value::String(s) => (**s).clone(),
10419 _ => return Err(RuntimeError::new("from_binary: argument must be a string")),
10420 };
10421 match i64::from_str_radix(&s, 2) {
10422 Ok(n) => Ok(Value::Int(n)),
10423 Err(_) => Err(RuntimeError::new("from_binary: invalid binary string")),
10424 }
10425 });
10426
10427 define(interp, "to_hex", Some(1), |_, args| {
10428 let a = match &args[0] {
10429 Value::Int(n) => *n,
10430 _ => return Err(RuntimeError::new("to_hex: argument must be an integer")),
10431 };
10432 Ok(Value::String(Rc::new(format!("{:x}", a))))
10433 });
10434
10435 define(interp, "from_hex", Some(1), |_, args| {
10436 let s = match &args[0] {
10437 Value::String(s) => s.trim_start_matches("0x").to_string(),
10438 _ => return Err(RuntimeError::new("from_hex: argument must be a string")),
10439 };
10440 match i64::from_str_radix(&s, 16) {
10441 Ok(n) => Ok(Value::Int(n)),
10442 Err(_) => Err(RuntimeError::new("from_hex: invalid hex string")),
10443 }
10444 });
10445
10446 define(interp, "to_octal", Some(1), |_, args| {
10447 let a = match &args[0] {
10448 Value::Int(n) => *n,
10449 _ => return Err(RuntimeError::new("to_octal: argument must be an integer")),
10450 };
10451 Ok(Value::String(Rc::new(format!("{:o}", a))))
10452 });
10453
10454 define(interp, "from_octal", Some(1), |_, args| {
10455 let s = match &args[0] {
10456 Value::String(s) => s.trim_start_matches("0o").to_string(),
10457 _ => return Err(RuntimeError::new("from_octal: argument must be a string")),
10458 };
10459 match i64::from_str_radix(&s, 8) {
10460 Ok(n) => Ok(Value::Int(n)),
10461 Err(_) => Err(RuntimeError::new("from_octal: invalid octal string")),
10462 }
10463 });
10464}
10465
10466fn register_format(interp: &mut Interpreter) {
10468 define(interp, "format", None, |_, args| {
10470 if args.is_empty() {
10471 return Err(RuntimeError::new(
10472 "format: requires at least a format string",
10473 ));
10474 }
10475 let template = match &args[0] {
10476 Value::String(s) => (**s).clone(),
10477 _ => return Err(RuntimeError::new("format: first argument must be a string")),
10478 };
10479 let mut result = template;
10480 for arg in &args[1..] {
10481 if let Some(pos) = result.find("{}") {
10482 result = format!("{}{}{}", &result[..pos], arg, &result[pos + 2..]);
10483 }
10484 }
10485 Ok(Value::String(Rc::new(result)))
10486 });
10487
10488 define(interp, "pad_left", Some(3), |_, args| {
10490 let s = match &args[0] {
10491 Value::String(s) => (**s).clone(),
10492 _ => {
10493 return Err(RuntimeError::new(
10494 "pad_left: first argument must be a string",
10495 ))
10496 }
10497 };
10498 let width = match &args[1] {
10499 Value::Int(n) if *n >= 0 => *n as usize,
10500 _ => {
10501 return Err(RuntimeError::new(
10502 "pad_left: width must be a non-negative integer",
10503 ))
10504 }
10505 };
10506 let pad_char = match &args[2] {
10507 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10508 Value::Char(c) => *c,
10509 _ => {
10510 return Err(RuntimeError::new(
10511 "pad_left: pad character must be a non-empty string or char",
10512 ))
10513 }
10514 };
10515 let char_count = s.chars().count();
10516 if char_count >= width {
10517 return Ok(Value::String(Rc::new(s)));
10518 }
10519 let padding: String = std::iter::repeat(pad_char)
10520 .take(width - char_count)
10521 .collect();
10522 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
10523 });
10524
10525 define(interp, "pad_right", Some(3), |_, args| {
10527 let s = match &args[0] {
10528 Value::String(s) => (**s).clone(),
10529 _ => {
10530 return Err(RuntimeError::new(
10531 "pad_right: first argument must be a string",
10532 ))
10533 }
10534 };
10535 let width = match &args[1] {
10536 Value::Int(n) if *n >= 0 => *n as usize,
10537 _ => {
10538 return Err(RuntimeError::new(
10539 "pad_right: width must be a non-negative integer",
10540 ))
10541 }
10542 };
10543 let pad_char = match &args[2] {
10544 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10545 Value::Char(c) => *c,
10546 _ => {
10547 return Err(RuntimeError::new(
10548 "pad_right: pad character must be a non-empty string or char",
10549 ))
10550 }
10551 };
10552 let char_count = s.chars().count();
10553 if char_count >= width {
10554 return Ok(Value::String(Rc::new(s)));
10555 }
10556 let padding: String = std::iter::repeat(pad_char)
10557 .take(width - char_count)
10558 .collect();
10559 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
10560 });
10561
10562 define(interp, "center", Some(3), |_, args| {
10564 let s = match &args[0] {
10565 Value::String(s) => (**s).clone(),
10566 _ => return Err(RuntimeError::new("center: first argument must be a string")),
10567 };
10568 let width = match &args[1] {
10569 Value::Int(n) if *n >= 0 => *n as usize,
10570 _ => {
10571 return Err(RuntimeError::new(
10572 "center: width must be a non-negative integer",
10573 ))
10574 }
10575 };
10576 let pad_char = match &args[2] {
10577 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10578 Value::Char(c) => *c,
10579 _ => {
10580 return Err(RuntimeError::new(
10581 "center: pad character must be a non-empty string or char",
10582 ))
10583 }
10584 };
10585 let char_count = s.chars().count();
10586 if char_count >= width {
10587 return Ok(Value::String(Rc::new(s)));
10588 }
10589 let total_padding = width - char_count;
10590 let left_padding = total_padding / 2;
10591 let right_padding = total_padding - left_padding;
10592 let left: String = std::iter::repeat(pad_char).take(left_padding).collect();
10593 let right: String = std::iter::repeat(pad_char).take(right_padding).collect();
10594 Ok(Value::String(Rc::new(format!("{}{}{}", left, s, right))))
10595 });
10596
10597 define(interp, "number_format", Some(1), |_, args| {
10599 let n = match &args[0] {
10600 Value::Int(n) => *n,
10601 Value::Float(f) => *f as i64,
10602 _ => {
10603 return Err(RuntimeError::new(
10604 "number_format: argument must be a number",
10605 ))
10606 }
10607 };
10608 let s = n.abs().to_string();
10609 let mut result = String::new();
10610 for (i, c) in s.chars().rev().enumerate() {
10611 if i > 0 && i % 3 == 0 {
10612 result.push(',');
10613 }
10614 result.push(c);
10615 }
10616 let formatted: String = result.chars().rev().collect();
10617 if n < 0 {
10618 Ok(Value::String(Rc::new(format!("-{}", formatted))))
10619 } else {
10620 Ok(Value::String(Rc::new(formatted)))
10621 }
10622 });
10623
10624 define(interp, "ordinal", Some(1), |_, args| {
10626 let n = match &args[0] {
10627 Value::Int(n) => *n,
10628 _ => return Err(RuntimeError::new("ordinal: argument must be an integer")),
10629 };
10630 let suffix = match (n % 10, n % 100) {
10631 (1, 11) => "th",
10632 (2, 12) => "th",
10633 (3, 13) => "th",
10634 (1, _) => "st",
10635 (2, _) => "nd",
10636 (3, _) => "rd",
10637 _ => "th",
10638 };
10639 Ok(Value::String(Rc::new(format!("{}{}", n, suffix))))
10640 });
10641
10642 define(interp, "pluralize", Some(3), |_, args| {
10644 let count = match &args[0] {
10645 Value::Int(n) => *n,
10646 _ => {
10647 return Err(RuntimeError::new(
10648 "pluralize: first argument must be an integer",
10649 ))
10650 }
10651 };
10652 let singular = match &args[1] {
10653 Value::String(s) => s.clone(),
10654 _ => {
10655 return Err(RuntimeError::new(
10656 "pluralize: second argument must be a string",
10657 ))
10658 }
10659 };
10660 let plural = match &args[2] {
10661 Value::String(s) => s.clone(),
10662 _ => {
10663 return Err(RuntimeError::new(
10664 "pluralize: third argument must be a string",
10665 ))
10666 }
10667 };
10668 if count == 1 || count == -1 {
10669 Ok(Value::String(singular))
10670 } else {
10671 Ok(Value::String(plural))
10672 }
10673 });
10674
10675 define(interp, "truncate", Some(2), |_, args| {
10677 let s = match &args[0] {
10678 Value::String(s) => (**s).clone(),
10679 _ => {
10680 return Err(RuntimeError::new(
10681 "truncate: first argument must be a string",
10682 ))
10683 }
10684 };
10685 let max_len = match &args[1] {
10686 Value::Int(n) if *n >= 0 => *n as usize,
10687 _ => {
10688 return Err(RuntimeError::new(
10689 "truncate: max length must be a non-negative integer",
10690 ))
10691 }
10692 };
10693 let char_count = s.chars().count();
10694 if char_count <= max_len {
10695 return Ok(Value::String(Rc::new(s)));
10696 }
10697 if max_len <= 3 {
10698 return Ok(Value::String(Rc::new(s.chars().take(max_len).collect())));
10699 }
10700 let truncated: String = s.chars().take(max_len - 3).collect();
10701 Ok(Value::String(Rc::new(format!("{}...", truncated))))
10702 });
10703
10704 define(interp, "word_wrap", Some(2), |_, args| {
10706 let s = match &args[0] {
10707 Value::String(s) => (**s).clone(),
10708 _ => {
10709 return Err(RuntimeError::new(
10710 "word_wrap: first argument must be a string",
10711 ))
10712 }
10713 };
10714 let width = match &args[1] {
10715 Value::Int(n) if *n > 0 => *n as usize,
10716 _ => {
10717 return Err(RuntimeError::new(
10718 "word_wrap: width must be a positive integer",
10719 ))
10720 }
10721 };
10722 let mut result = String::new();
10723 let mut line_len = 0;
10724 for word in s.split_whitespace() {
10725 if line_len > 0 && line_len + 1 + word.len() > width {
10726 result.push('\n');
10727 line_len = 0;
10728 } else if line_len > 0 {
10729 result.push(' ');
10730 line_len += 1;
10731 }
10732 result.push_str(word);
10733 line_len += word.len();
10734 }
10735 Ok(Value::String(Rc::new(result)))
10736 });
10737
10738 define(interp, "snake_case", Some(1), |_, args| {
10740 let s = match &args[0] {
10741 Value::String(s) => (**s).clone(),
10742 _ => return Err(RuntimeError::new("snake_case: argument must be a string")),
10743 };
10744 let mut result = String::new();
10745 for (i, c) in s.chars().enumerate() {
10746 if c.is_uppercase() {
10747 if i > 0 {
10748 result.push('_');
10749 }
10750 result.push(c.to_lowercase().next().unwrap());
10751 } else if c == ' ' || c == '-' {
10752 result.push('_');
10753 } else {
10754 result.push(c);
10755 }
10756 }
10757 Ok(Value::String(Rc::new(result)))
10758 });
10759
10760 define(interp, "camel_case", Some(1), |_, args| {
10762 let s = match &args[0] {
10763 Value::String(s) => (**s).clone(),
10764 _ => return Err(RuntimeError::new("camel_case: argument must be a string")),
10765 };
10766 let mut result = String::new();
10767 let mut capitalize_next = false;
10768 for (i, c) in s.chars().enumerate() {
10769 if c == '_' || c == '-' || c == ' ' {
10770 capitalize_next = true;
10771 } else if capitalize_next {
10772 result.push(c.to_uppercase().next().unwrap());
10773 capitalize_next = false;
10774 } else if i == 0 {
10775 result.push(c.to_lowercase().next().unwrap());
10776 } else {
10777 result.push(c);
10778 }
10779 }
10780 Ok(Value::String(Rc::new(result)))
10781 });
10782
10783 define(interp, "kebab_case", Some(1), |_, args| {
10785 let s = match &args[0] {
10786 Value::String(s) => (**s).clone(),
10787 _ => return Err(RuntimeError::new("kebab_case: argument must be a string")),
10788 };
10789 let mut result = String::new();
10790 for (i, c) in s.chars().enumerate() {
10791 if c.is_uppercase() {
10792 if i > 0 {
10793 result.push('-');
10794 }
10795 result.push(c.to_lowercase().next().unwrap());
10796 } else if c == '_' || c == ' ' {
10797 result.push('-');
10798 } else {
10799 result.push(c);
10800 }
10801 }
10802 Ok(Value::String(Rc::new(result)))
10803 });
10804
10805 define(interp, "title_case", Some(1), |_, args| {
10807 let s = match &args[0] {
10808 Value::String(s) => (**s).clone(),
10809 _ => return Err(RuntimeError::new("title_case: argument must be a string")),
10810 };
10811 let result: String = s
10812 .split_whitespace()
10813 .map(|word| {
10814 let mut chars = word.chars();
10815 match chars.next() {
10816 None => String::new(),
10817 Some(first) => {
10818 first.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase()
10819 }
10820 }
10821 })
10822 .collect::<Vec<_>>()
10823 .join(" ");
10824 Ok(Value::String(Rc::new(result)))
10825 });
10826}
10827
10828fn register_pattern(interp: &mut Interpreter) {
10836 define(interp, "type_of", Some(1), |_, args| {
10840 let type_name = match &args[0] {
10841 Value::Null => "null",
10842 Value::Bool(_) => "bool",
10843 Value::Int(_) => "int",
10844 Value::Float(_) => "float",
10845 Value::String(_) => "string",
10846 Value::Char(_) => "char",
10847 Value::Array(_) => "array",
10848 Value::Tuple(_) => "tuple",
10849 Value::Map(_) => "map",
10850 Value::Set(_) => "set",
10851 Value::Struct { name, .. } => {
10852 return Ok(Value::String(Rc::new(format!("struct:{}", name))))
10853 }
10854 Value::Variant {
10855 enum_name,
10856 variant_name,
10857 ..
10858 } => {
10859 return Ok(Value::String(Rc::new(format!(
10860 "{}::{}",
10861 enum_name, variant_name
10862 ))))
10863 }
10864 Value::Function(_) => "function",
10865 Value::BuiltIn(_) => "builtin",
10866 Value::Ref(_) => "ref",
10867 Value::Infinity => "infinity",
10868 Value::Empty => "empty",
10869 Value::Evidential { .. } => "evidential",
10870 Value::Affective { .. } => "affective",
10871 Value::Channel(_) => "channel",
10872 Value::ThreadHandle(_) => "thread",
10873 Value::Actor(_) => "actor",
10874 Value::Future(_) => "future",
10875 Value::VariantConstructor { .. } => "variant_constructor",
10876 Value::DefaultConstructor { .. } => "default_constructor",
10877 Value::Range { .. } => "range",
10878 Value::RefCellValue(_) => "refcell",
10879 Value::TraitObject { trait_name, .. } => return Ok(Value::String(Rc::new(format!("dyn {}", trait_name)))),
10880 };
10881 Ok(Value::String(Rc::new(type_name.to_string())))
10882 });
10883
10884 define(interp, "is_type", Some(2), |_, args| {
10886 let type_name = match &args[1] {
10887 Value::String(s) => s.to_lowercase(),
10888 _ => {
10889 return Err(RuntimeError::new(
10890 "is_type: second argument must be type name string",
10891 ))
10892 }
10893 };
10894 let matches = match (&args[0], type_name.as_str()) {
10895 (Value::Null, "null") => true,
10896 (Value::Bool(_), "bool") => true,
10897 (Value::Int(_), "int") | (Value::Int(_), "integer") => true,
10898 (Value::Float(_), "float") | (Value::Float(_), "number") => true,
10899 (Value::Int(_), "number") => true,
10900 (Value::String(_), "string") => true,
10901 (Value::Array(_), "array") | (Value::Array(_), "list") => true,
10902 (Value::Tuple(_), "tuple") => true,
10903 (Value::Map(_), "map") | (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
10904 (Value::Set(_), "set") => true,
10905 (Value::Function(_), "function") | (Value::Function(_), "fn") => true,
10906 (Value::BuiltIn(_), "function") | (Value::BuiltIn(_), "builtin") => true,
10907 (Value::Struct { name, .. }, t) => t == "struct" || t == &name.to_lowercase(),
10908 (Value::Variant { enum_name, .. }, t) => {
10909 t == "variant" || t == "enum" || t == &enum_name.to_lowercase()
10910 }
10911 (Value::Channel(_), "channel") => true,
10912 (Value::ThreadHandle(_), "thread") => true,
10913 (Value::Actor(_), "actor") => true,
10914 (Value::Future(_), "future") => true,
10915 _ => false,
10916 };
10917 Ok(Value::Bool(matches))
10918 });
10919
10920 define(interp, "is_null", Some(1), |_, args| {
10922 Ok(Value::Bool(matches!(&args[0], Value::Null)))
10923 });
10924 define(interp, "is_bool", Some(1), |_, args| {
10925 Ok(Value::Bool(matches!(&args[0], Value::Bool(_))))
10926 });
10927 define(interp, "is_int", Some(1), |_, args| {
10928 Ok(Value::Bool(matches!(&args[0], Value::Int(_))))
10929 });
10930 define(interp, "is_float", Some(1), |_, args| {
10931 Ok(Value::Bool(matches!(&args[0], Value::Float(_))))
10932 });
10933 define(interp, "is_number", Some(1), |_, args| {
10934 Ok(Value::Bool(matches!(
10935 &args[0],
10936 Value::Int(_) | Value::Float(_)
10937 )))
10938 });
10939 define(interp, "is_string", Some(1), |_, args| {
10940 Ok(Value::Bool(matches!(&args[0], Value::String(_))))
10941 });
10942 define(interp, "is_array", Some(1), |_, args| {
10943 Ok(Value::Bool(matches!(&args[0], Value::Array(_))))
10944 });
10945 define(interp, "is_tuple", Some(1), |_, args| {
10946 Ok(Value::Bool(matches!(&args[0], Value::Tuple(_))))
10947 });
10948 define(interp, "is_map", Some(1), |_, args| {
10949 Ok(Value::Bool(matches!(&args[0], Value::Map(_))))
10950 });
10951 define(interp, "is_set", Some(1), |_, args| {
10952 Ok(Value::Bool(matches!(&args[0], Value::Set(_))))
10953 });
10954 define(interp, "is_function", Some(1), |_, args| {
10955 Ok(Value::Bool(matches!(
10956 &args[0],
10957 Value::Function(_) | Value::BuiltIn(_)
10958 )))
10959 });
10960 define(interp, "is_struct", Some(1), |_, args| {
10961 Ok(Value::Bool(matches!(&args[0], Value::Struct { .. })))
10962 });
10963 define(interp, "is_variant", Some(1), |_, args| {
10964 Ok(Value::Bool(matches!(&args[0], Value::Variant { .. })))
10965 });
10966 define(interp, "is_future", Some(1), |_, args| {
10967 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
10968 });
10969 define(interp, "is_channel", Some(1), |_, args| {
10970 Ok(Value::Bool(matches!(&args[0], Value::Channel(_))))
10971 });
10972
10973 define(interp, "is_empty", Some(1), |_, args| {
10975 let empty = match &args[0] {
10976 Value::Null => true,
10977 Value::String(s) => s.is_empty(),
10978 Value::Array(a) => a.borrow().is_empty(),
10979 Value::Tuple(t) => t.is_empty(),
10980 Value::Map(m) => m.borrow().is_empty(),
10981 Value::Set(s) => s.borrow().is_empty(),
10982 _ => false,
10983 };
10984 Ok(Value::Bool(empty))
10985 });
10986
10987 define(interp, "match_regex", Some(2), |_, args| {
10991 let text = match &args[0] {
10992 Value::String(s) => (**s).clone(),
10993 _ => {
10994 return Err(RuntimeError::new(
10995 "match_regex: first argument must be a string",
10996 ))
10997 }
10998 };
10999 let pattern = match &args[1] {
11000 Value::String(s) => (**s).clone(),
11001 _ => {
11002 return Err(RuntimeError::new(
11003 "match_regex: second argument must be a regex pattern string",
11004 ))
11005 }
11006 };
11007
11008 let re = match Regex::new(&pattern) {
11009 Ok(r) => r,
11010 Err(e) => {
11011 return Err(RuntimeError::new(format!(
11012 "match_regex: invalid regex: {}",
11013 e
11014 )))
11015 }
11016 };
11017
11018 match re.captures(&text) {
11019 Some(caps) => {
11020 let mut captures: Vec<Value> = Vec::new();
11021 for i in 0..caps.len() {
11022 if let Some(m) = caps.get(i) {
11023 captures.push(Value::String(Rc::new(m.as_str().to_string())));
11024 } else {
11025 captures.push(Value::Null);
11026 }
11027 }
11028 Ok(Value::Array(Rc::new(RefCell::new(captures))))
11029 }
11030 None => Ok(Value::Null),
11031 }
11032 });
11033
11034 define(interp, "match_all_regex", Some(2), |_, args| {
11036 let text = match &args[0] {
11037 Value::String(s) => (**s).clone(),
11038 _ => {
11039 return Err(RuntimeError::new(
11040 "match_all_regex: first argument must be a string",
11041 ))
11042 }
11043 };
11044 let pattern = match &args[1] {
11045 Value::String(s) => (**s).clone(),
11046 _ => {
11047 return Err(RuntimeError::new(
11048 "match_all_regex: second argument must be a regex pattern string",
11049 ))
11050 }
11051 };
11052
11053 let re = match Regex::new(&pattern) {
11054 Ok(r) => r,
11055 Err(e) => {
11056 return Err(RuntimeError::new(format!(
11057 "match_all_regex: invalid regex: {}",
11058 e
11059 )))
11060 }
11061 };
11062
11063 let matches: Vec<Value> = re
11064 .find_iter(&text)
11065 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
11066 .collect();
11067 Ok(Value::Array(Rc::new(RefCell::new(matches))))
11068 });
11069
11070 define(interp, "capture_named", Some(2), |_, args| {
11072 let text = match &args[0] {
11073 Value::String(s) => (**s).clone(),
11074 _ => {
11075 return Err(RuntimeError::new(
11076 "capture_named: first argument must be a string",
11077 ))
11078 }
11079 };
11080 let pattern = match &args[1] {
11081 Value::String(s) => (**s).clone(),
11082 _ => {
11083 return Err(RuntimeError::new(
11084 "capture_named: second argument must be a regex pattern string",
11085 ))
11086 }
11087 };
11088
11089 let re = match Regex::new(&pattern) {
11090 Ok(r) => r,
11091 Err(e) => {
11092 return Err(RuntimeError::new(format!(
11093 "capture_named: invalid regex: {}",
11094 e
11095 )))
11096 }
11097 };
11098
11099 match re.captures(&text) {
11100 Some(caps) => {
11101 let mut result: HashMap<String, Value> = HashMap::new();
11102 for name in re.capture_names().flatten() {
11103 if let Some(m) = caps.name(name) {
11104 result.insert(
11105 name.to_string(),
11106 Value::String(Rc::new(m.as_str().to_string())),
11107 );
11108 }
11109 }
11110 Ok(Value::Map(Rc::new(RefCell::new(result))))
11111 }
11112 None => Ok(Value::Null),
11113 }
11114 });
11115
11116 define(interp, "match_struct", Some(2), |_, args| {
11120 let expected_name = match &args[1] {
11121 Value::String(s) => (**s).clone(),
11122 _ => {
11123 return Err(RuntimeError::new(
11124 "match_struct: second argument must be struct name string",
11125 ))
11126 }
11127 };
11128 match &args[0] {
11129 Value::Struct { name, .. } => Ok(Value::Bool(name == &expected_name)),
11130 _ => Ok(Value::Bool(false)),
11131 }
11132 });
11133
11134 define(interp, "match_variant", Some(3), |_, args| {
11136 let expected_enum = match &args[1] {
11137 Value::String(s) => (**s).clone(),
11138 _ => {
11139 return Err(RuntimeError::new(
11140 "match_variant: second argument must be enum name string",
11141 ))
11142 }
11143 };
11144 let expected_variant = match &args[2] {
11145 Value::String(s) => (**s).clone(),
11146 _ => {
11147 return Err(RuntimeError::new(
11148 "match_variant: third argument must be variant name string",
11149 ))
11150 }
11151 };
11152 match &args[0] {
11153 Value::Variant {
11154 enum_name,
11155 variant_name,
11156 ..
11157 } => Ok(Value::Bool(
11158 enum_name == &expected_enum && variant_name == &expected_variant,
11159 )),
11160 _ => Ok(Value::Bool(false)),
11161 }
11162 });
11163
11164 define(interp, "get_field", Some(2), |_, args| {
11166 let field_name = match &args[1] {
11167 Value::String(s) => (**s).clone(),
11168 _ => {
11169 return Err(RuntimeError::new(
11170 "get_field: second argument must be field name string",
11171 ))
11172 }
11173 };
11174 match &args[0] {
11175 Value::Struct { fields, .. } => Ok(fields
11176 .borrow()
11177 .get(&field_name)
11178 .cloned()
11179 .unwrap_or(Value::Null)),
11180 Value::Map(m) => Ok(m.borrow().get(&field_name).cloned().unwrap_or(Value::Null)),
11181 _ => Ok(Value::Null),
11182 }
11183 });
11184
11185 define(interp, "has_field", Some(2), |_, args| {
11187 let field_name = match &args[1] {
11188 Value::String(s) => (**s).clone(),
11189 _ => {
11190 return Err(RuntimeError::new(
11191 "has_field: second argument must be field name string",
11192 ))
11193 }
11194 };
11195 match &args[0] {
11196 Value::Struct { fields, .. } => {
11197 Ok(Value::Bool(fields.borrow().contains_key(&field_name)))
11198 }
11199 Value::Map(m) => Ok(Value::Bool(m.borrow().contains_key(&field_name))),
11200 _ => Ok(Value::Bool(false)),
11201 }
11202 });
11203
11204 define(interp, "get_fields", Some(1), |_, args| {
11206 let fields: Vec<Value> = match &args[0] {
11207 Value::Struct { fields, .. } => fields
11208 .borrow()
11209 .keys()
11210 .map(|k| Value::String(Rc::new(k.clone())))
11211 .collect(),
11212 Value::Map(m) => m
11213 .borrow()
11214 .keys()
11215 .map(|k| Value::String(Rc::new(k.clone())))
11216 .collect(),
11217 _ => {
11218 return Err(RuntimeError::new(
11219 "get_fields: argument must be struct or map",
11220 ))
11221 }
11222 };
11223 Ok(Value::Array(Rc::new(RefCell::new(fields))))
11224 });
11225
11226 define(interp, "struct_name", Some(1), |_, args| match &args[0] {
11228 Value::Struct { name, .. } => Ok(Value::String(Rc::new(name.clone()))),
11229 _ => Ok(Value::Null),
11230 });
11231
11232 define(interp, "variant_name", Some(1), |_, args| match &args[0] {
11234 Value::Variant { variant_name, .. } => Ok(Value::String(Rc::new(variant_name.clone()))),
11235 _ => Ok(Value::Null),
11236 });
11237
11238 define(interp, "variant_data", Some(1), |_, args| match &args[0] {
11240 Value::Variant { fields, .. } => match fields {
11241 Some(f) => Ok(Value::Array(Rc::new(RefCell::new((**f).clone())))),
11242 None => Ok(Value::Null),
11243 },
11244 _ => Ok(Value::Null),
11245 });
11246
11247 define(interp, "guard", Some(2), |_, args| {
11251 if is_truthy(&args[0]) {
11252 Ok(args[1].clone())
11253 } else {
11254 Ok(Value::Null)
11255 }
11256 });
11257
11258 define(interp, "when", Some(2), |interp, args| {
11260 if is_truthy(&args[0]) {
11261 match &args[1] {
11262 Value::Function(f) => interp.call_function(f, vec![]),
11263 other => Ok(other.clone()),
11264 }
11265 } else {
11266 Ok(Value::Null)
11267 }
11268 });
11269
11270 define(interp, "unless", Some(2), |interp, args| {
11272 if !is_truthy(&args[0]) {
11273 match &args[1] {
11274 Value::Function(f) => interp.call_function(f, vec![]),
11275 other => Ok(other.clone()),
11276 }
11277 } else {
11278 Ok(Value::Null)
11279 }
11280 });
11281
11282 define(interp, "cond", Some(1), |interp, args| {
11285 let clauses = match &args[0] {
11286 Value::Array(a) => a.borrow().clone(),
11287 _ => {
11288 return Err(RuntimeError::new(
11289 "cond: argument must be array of [condition, value] pairs",
11290 ))
11291 }
11292 };
11293
11294 for clause in clauses {
11295 let pair = match &clause {
11296 Value::Array(a) => a.borrow().clone(),
11297 Value::Tuple(t) => (**t).clone(),
11298 _ => {
11299 return Err(RuntimeError::new(
11300 "cond: each clause must be [condition, value] pair",
11301 ))
11302 }
11303 };
11304 if pair.len() != 2 {
11305 return Err(RuntimeError::new(
11306 "cond: each clause must have exactly 2 elements",
11307 ));
11308 }
11309
11310 if is_truthy(&pair[0]) {
11311 return match &pair[1] {
11312 Value::Function(f) => interp.call_function(f, vec![]),
11313 other => Ok(other.clone()),
11314 };
11315 }
11316 }
11317 Ok(Value::Null)
11318 });
11319
11320 define(interp, "case", Some(2), |interp, args| {
11323 let value = &args[0];
11324 let clauses = match &args[1] {
11325 Value::Array(a) => a.borrow().clone(),
11326 _ => {
11327 return Err(RuntimeError::new(
11328 "case: second argument must be array of [pattern, result] pairs",
11329 ))
11330 }
11331 };
11332
11333 for clause in clauses {
11334 let pair = match &clause {
11335 Value::Array(a) => a.borrow().clone(),
11336 Value::Tuple(t) => (**t).clone(),
11337 _ => {
11338 return Err(RuntimeError::new(
11339 "case: each clause must be [pattern, result] pair",
11340 ))
11341 }
11342 };
11343 if pair.len() != 2 {
11344 return Err(RuntimeError::new(
11345 "case: each clause must have exactly 2 elements",
11346 ));
11347 }
11348
11349 if value_eq(value, &pair[0]) {
11350 return match &pair[1] {
11351 Value::Function(f) => interp.call_function(f, vec![value.clone()]),
11352 other => Ok(other.clone()),
11353 };
11354 }
11355 }
11356 Ok(Value::Null)
11357 });
11358
11359 define(interp, "destructure_array", Some(2), |_, args| {
11363 let arr = match &args[0] {
11364 Value::Array(a) => a.borrow().clone(),
11365 Value::Tuple(t) => (**t).clone(),
11366 _ => {
11367 return Err(RuntimeError::new(
11368 "destructure_array: first argument must be array or tuple",
11369 ))
11370 }
11371 };
11372 let indices = match &args[1] {
11373 Value::Array(a) => a.borrow().clone(),
11374 _ => {
11375 return Err(RuntimeError::new(
11376 "destructure_array: second argument must be array of indices",
11377 ))
11378 }
11379 };
11380
11381 let mut result = Vec::new();
11382 for idx in indices {
11383 match idx {
11384 Value::Int(i) => {
11385 let i = if i < 0 { arr.len() as i64 + i } else { i } as usize;
11386 result.push(arr.get(i).cloned().unwrap_or(Value::Null));
11387 }
11388 _ => result.push(Value::Null),
11389 }
11390 }
11391 Ok(Value::Array(Rc::new(RefCell::new(result))))
11392 });
11393
11394 define(interp, "destructure_map", Some(2), |_, args| {
11396 let map = match &args[0] {
11397 Value::Map(m) => m.borrow().clone(),
11398 Value::Struct { fields, .. } => fields.borrow().clone(),
11399 _ => {
11400 return Err(RuntimeError::new(
11401 "destructure_map: first argument must be map or struct",
11402 ))
11403 }
11404 };
11405 let keys = match &args[1] {
11406 Value::Array(a) => a.borrow().clone(),
11407 _ => {
11408 return Err(RuntimeError::new(
11409 "destructure_map: second argument must be array of keys",
11410 ))
11411 }
11412 };
11413
11414 let mut result = Vec::new();
11415 for key in keys {
11416 match key {
11417 Value::String(k) => {
11418 result.push(map.get(&*k).cloned().unwrap_or(Value::Null));
11419 }
11420 _ => result.push(Value::Null),
11421 }
11422 }
11423 Ok(Value::Array(Rc::new(RefCell::new(result))))
11424 });
11425
11426 define(interp, "head_tail", Some(1), |_, args| {
11428 let arr = match &args[0] {
11429 Value::Array(a) => a.borrow().clone(),
11430 _ => return Err(RuntimeError::new("head_tail: argument must be array")),
11431 };
11432
11433 if arr.is_empty() {
11434 Ok(Value::Tuple(Rc::new(vec![
11435 Value::Null,
11436 Value::Array(Rc::new(RefCell::new(vec![]))),
11437 ])))
11438 } else {
11439 let head = arr[0].clone();
11440 let tail = arr[1..].to_vec();
11441 Ok(Value::Tuple(Rc::new(vec![
11442 head,
11443 Value::Array(Rc::new(RefCell::new(tail))),
11444 ])))
11445 }
11446 });
11447
11448 define(interp, "init_last", Some(1), |_, args| {
11450 let arr = match &args[0] {
11451 Value::Array(a) => a.borrow().clone(),
11452 _ => return Err(RuntimeError::new("init_last: argument must be array")),
11453 };
11454
11455 if arr.is_empty() {
11456 Ok(Value::Tuple(Rc::new(vec![
11457 Value::Array(Rc::new(RefCell::new(vec![]))),
11458 Value::Null,
11459 ])))
11460 } else {
11461 let last = arr[arr.len() - 1].clone();
11462 let init = arr[..arr.len() - 1].to_vec();
11463 Ok(Value::Tuple(Rc::new(vec![
11464 Value::Array(Rc::new(RefCell::new(init))),
11465 last,
11466 ])))
11467 }
11468 });
11469
11470 define(interp, "split_at", Some(2), |_, args| {
11472 let arr = match &args[0] {
11473 Value::Array(a) => a.borrow().clone(),
11474 _ => return Err(RuntimeError::new("split_at: first argument must be array")),
11475 };
11476 let idx = match &args[1] {
11477 Value::Int(i) => *i as usize,
11478 _ => {
11479 return Err(RuntimeError::new(
11480 "split_at: second argument must be integer",
11481 ))
11482 }
11483 };
11484
11485 let idx = idx.min(arr.len());
11486 let left = arr[..idx].to_vec();
11487 let right = arr[idx..].to_vec();
11488 Ok(Value::Tuple(Rc::new(vec![
11489 Value::Array(Rc::new(RefCell::new(left))),
11490 Value::Array(Rc::new(RefCell::new(right))),
11491 ])))
11492 });
11493
11494 define(interp, "unwrap_or", Some(2), |_, args| {
11498 if matches!(&args[0], Value::Null) {
11499 Ok(args[1].clone())
11500 } else {
11501 Ok(args[0].clone())
11502 }
11503 });
11504
11505 define(interp, "unwrap_or_else", Some(2), |interp, args| {
11507 if matches!(&args[0], Value::Null) {
11508 match &args[1] {
11509 Value::Function(f) => interp.call_function(f, vec![]),
11510 other => Ok(other.clone()),
11511 }
11512 } else {
11513 Ok(args[0].clone())
11514 }
11515 });
11516
11517 define(interp, "map_or", Some(3), |interp, args| {
11519 if matches!(&args[0], Value::Null) {
11520 Ok(args[1].clone())
11521 } else {
11522 match &args[2] {
11523 Value::Function(f) => interp.call_function(f, vec![args[0].clone()]),
11524 _ => Err(RuntimeError::new(
11525 "map_or: third argument must be a function",
11526 )),
11527 }
11528 }
11529 });
11530
11531 define(interp, "coalesce", Some(1), |_, args| {
11533 let values = match &args[0] {
11534 Value::Array(a) => a.borrow().clone(),
11535 _ => return Err(RuntimeError::new("coalesce: argument must be array")),
11536 };
11537
11538 for v in values {
11539 if !matches!(v, Value::Null) {
11540 return Ok(v);
11541 }
11542 }
11543 Ok(Value::Null)
11544 });
11545
11546 define(interp, "deep_eq", Some(2), |_, args| {
11550 Ok(Value::Bool(deep_value_eq(&args[0], &args[1])))
11551 });
11552
11553 define(interp, "same_type", Some(2), |_, args| {
11555 let same = match (&args[0], &args[1]) {
11556 (Value::Null, Value::Null) => true,
11557 (Value::Bool(_), Value::Bool(_)) => true,
11558 (Value::Int(_), Value::Int(_)) => true,
11559 (Value::Float(_), Value::Float(_)) => true,
11560 (Value::String(_), Value::String(_)) => true,
11561 (Value::Array(_), Value::Array(_)) => true,
11562 (Value::Tuple(_), Value::Tuple(_)) => true,
11563 (Value::Map(_), Value::Map(_)) => true,
11564 (Value::Set(_), Value::Set(_)) => true,
11565 (Value::Function(_), Value::Function(_)) => true,
11566 (Value::BuiltIn(_), Value::BuiltIn(_)) => true,
11567 (Value::Struct { name: n1, .. }, Value::Struct { name: n2, .. }) => n1 == n2,
11568 (Value::Variant { enum_name: e1, .. }, Value::Variant { enum_name: e2, .. }) => {
11569 e1 == e2
11570 }
11571 _ => false,
11572 };
11573 Ok(Value::Bool(same))
11574 });
11575
11576 define(interp, "compare", Some(2), |_, args| {
11578 let cmp = match (&args[0], &args[1]) {
11579 (Value::Int(a), Value::Int(b)) => a.cmp(b),
11580 (Value::Float(a), Value::Float(b)) => {
11581 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
11582 }
11583 (Value::Int(a), Value::Float(b)) => (*a as f64)
11584 .partial_cmp(b)
11585 .unwrap_or(std::cmp::Ordering::Equal),
11586 (Value::Float(a), Value::Int(b)) => a
11587 .partial_cmp(&(*b as f64))
11588 .unwrap_or(std::cmp::Ordering::Equal),
11589 (Value::String(a), Value::String(b)) => a.cmp(b),
11590 _ => {
11591 return Err(RuntimeError::new(
11592 "compare: can only compare numbers or strings",
11593 ))
11594 }
11595 };
11596 Ok(Value::Int(match cmp {
11597 std::cmp::Ordering::Less => -1,
11598 std::cmp::Ordering::Equal => 0,
11599 std::cmp::Ordering::Greater => 1,
11600 }))
11601 });
11602
11603 define(interp, "between", Some(3), |_, args| {
11605 let in_range = match (&args[0], &args[1], &args[2]) {
11606 (Value::Int(v), Value::Int(min), Value::Int(max)) => v >= min && v <= max,
11607 (Value::Float(v), Value::Float(min), Value::Float(max)) => v >= min && v <= max,
11608 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
11609 (*v as f64) >= (*min as f64) && (*v as f64) <= *max
11610 }
11611 (Value::Int(v), Value::Float(min), Value::Int(max)) => {
11612 (*v as f64) >= *min && (*v as f64) <= (*max as f64)
11613 }
11614 (Value::Float(v), Value::Int(min), Value::Int(max)) => {
11615 *v >= (*min as f64) && *v <= (*max as f64)
11616 }
11617 (Value::String(v), Value::String(min), Value::String(max)) => v >= min && v <= max,
11618 _ => {
11619 return Err(RuntimeError::new(
11620 "between: arguments must be comparable (numbers or strings)",
11621 ))
11622 }
11623 };
11624 Ok(Value::Bool(in_range))
11625 });
11626
11627 define(interp, "clamp", Some(3), |_, args| {
11629 match (&args[0], &args[1], &args[2]) {
11630 (Value::Int(v), Value::Int(min), Value::Int(max)) => {
11631 Ok(Value::Int((*v).max(*min).min(*max)))
11632 }
11633 (Value::Float(v), Value::Float(min), Value::Float(max)) => {
11634 Ok(Value::Float(v.max(*min).min(*max)))
11635 }
11636 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
11637 Ok(Value::Float((*v as f64).max(*min as f64).min(*max)))
11638 }
11639 _ => Err(RuntimeError::new("clamp: arguments must be numbers")),
11640 }
11641 });
11642}
11643
11644fn deep_value_eq(a: &Value, b: &Value) -> bool {
11646 match (a, b) {
11647 (Value::Null, Value::Null) => true,
11648 (Value::Bool(a), Value::Bool(b)) => a == b,
11649 (Value::Int(a), Value::Int(b)) => a == b,
11650 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
11651 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
11652 (*a as f64 - b).abs() < f64::EPSILON
11653 }
11654 (Value::String(a), Value::String(b)) => a == b,
11655 (Value::Array(a), Value::Array(b)) => {
11656 let a = a.borrow();
11657 let b = b.borrow();
11658 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
11659 }
11660 (Value::Tuple(a), Value::Tuple(b)) => {
11661 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
11662 }
11663 (Value::Map(a), Value::Map(b)) => {
11664 let a = a.borrow();
11665 let b = b.borrow();
11666 a.len() == b.len()
11667 && a.iter()
11668 .all(|(k, v)| b.get(k).map_or(false, |bv| deep_value_eq(v, bv)))
11669 }
11670 (Value::Set(a), Value::Set(b)) => {
11671 let a = a.borrow();
11672 let b = b.borrow();
11673 a.len() == b.len() && a.iter().all(|k| b.contains(k))
11674 }
11675 (
11676 Value::Struct {
11677 name: n1,
11678 fields: f1,
11679 },
11680 Value::Struct {
11681 name: n2,
11682 fields: f2,
11683 },
11684 ) => {
11685 let f1 = f1.borrow();
11686 let f2 = f2.borrow();
11687 n1 == n2
11688 && f1.len() == f2.len()
11689 && f1
11690 .iter()
11691 .all(|(k, v)| f2.get(k).map_or(false, |v2| deep_value_eq(v, v2)))
11692 }
11693 (
11694 Value::Variant {
11695 enum_name: e1,
11696 variant_name: v1,
11697 fields: d1,
11698 },
11699 Value::Variant {
11700 enum_name: e2,
11701 variant_name: v2,
11702 fields: d2,
11703 },
11704 ) => {
11705 if e1 != e2 || v1 != v2 {
11706 return false;
11707 }
11708 match (d1, d2) {
11709 (Some(f1), Some(f2)) => {
11710 f1.len() == f2.len()
11711 && f1.iter().zip(f2.iter()).all(|(x, y)| deep_value_eq(x, y))
11712 }
11713 (None, None) => true,
11714 _ => false,
11715 }
11716 }
11717 _ => false,
11718 }
11719}
11720
11721fn value_eq(a: &Value, b: &Value) -> bool {
11723 match (a, b) {
11724 (Value::Null, Value::Null) => true,
11725 (Value::Bool(a), Value::Bool(b)) => a == b,
11726 (Value::Int(a), Value::Int(b)) => a == b,
11727 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
11728 (Value::String(a), Value::String(b)) => a == b,
11729 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
11730 (*a as f64 - b).abs() < f64::EPSILON
11731 }
11732 _ => false,
11733 }
11734}
11735
11736fn register_devex(interp: &mut Interpreter) {
11744 define(interp, "debug", Some(1), |_, args| {
11748 let type_name = match &args[0] {
11749 Value::Null => "null".to_string(),
11750 Value::Bool(_) => "bool".to_string(),
11751 Value::Int(_) => "int".to_string(),
11752 Value::Float(_) => "float".to_string(),
11753 Value::String(_) => "string".to_string(),
11754 Value::Char(_) => "char".to_string(),
11755 Value::Array(a) => format!("array[{}]", a.borrow().len()),
11756 Value::Tuple(t) => format!("tuple[{}]", t.len()),
11757 Value::Map(m) => format!("map[{}]", m.borrow().len()),
11758 Value::Set(s) => format!("set[{}]", s.borrow().len()),
11759 Value::Struct { name, fields } => format!("struct {}[{}]", name, fields.borrow().len()),
11760 Value::Variant {
11761 enum_name,
11762 variant_name,
11763 ..
11764 } => format!("{}::{}", enum_name, variant_name),
11765 Value::Function(_) => "function".to_string(),
11766 Value::BuiltIn(_) => "builtin".to_string(),
11767 Value::Ref(_) => "ref".to_string(),
11768 Value::Infinity => "infinity".to_string(),
11769 Value::Empty => "empty".to_string(),
11770 Value::Evidential { evidence, .. } => format!("evidential[{:?}]", evidence),
11771 Value::Affective { affect, .. } => format!("affective[sarcasm={}]", affect.sarcasm),
11772 Value::Channel(_) => "channel".to_string(),
11773 Value::ThreadHandle(_) => "thread".to_string(),
11774 Value::Actor(_) => "actor".to_string(),
11775 Value::Future(_) => "future".to_string(),
11776 Value::VariantConstructor { enum_name, variant_name } => {
11777 format!("<constructor {}::{}>", enum_name, variant_name)
11778 }
11779 Value::DefaultConstructor { type_name } => {
11780 format!("<default {}>", type_name)
11781 }
11782 Value::Range { start, end, inclusive } => {
11783 match (start, end) {
11784 (Some(s), Some(e)) => if *inclusive {
11785 format!("range({}..={})", s, e)
11786 } else {
11787 format!("range({}..{})", s, e)
11788 },
11789 (Some(s), None) => format!("range({}..)", s),
11790 (None, Some(e)) => if *inclusive {
11791 format!("range(..={})", e)
11792 } else {
11793 format!("range(..{})", e)
11794 },
11795 (None, None) => "range(..)".to_string(),
11796 }
11797 }
11798 Value::RefCellValue(rc) => format!("refcell({:?})", rc.value.borrow()),
11799 Value::TraitObject { trait_name, concrete_type, .. } => format!("dyn {}({})", trait_name, concrete_type),
11800 };
11801 let value_repr = format_value_debug(&args[0]);
11802 println!("[DEBUG] {}: {}", type_name, value_repr);
11803 Ok(args[0].clone())
11804 });
11805
11806 define(interp, "inspect", Some(1), |_, args| {
11808 Ok(Value::String(Rc::new(format_value_debug(&args[0]))))
11809 });
11810
11811 define(interp, "dbg", Some(1), |_, args| {
11813 println!("{}", format_value_debug(&args[0]));
11814 Ok(args[0].clone())
11815 });
11816
11817 define(interp, "trace", Some(2), |_, args| {
11819 let label = match &args[0] {
11820 Value::String(s) => (**s).clone(),
11821 _ => format_value_debug(&args[0]),
11822 };
11823 println!("[TRACE] {}: {}", label, format_value_debug(&args[1]));
11824 Ok(args[1].clone())
11825 });
11826
11827 define(interp, "pp", Some(1), |_, args| {
11829 println!("{}", pretty_print_value(&args[0], 0));
11830 Ok(Value::Null)
11831 });
11832
11833 define(interp, "assert_eq", Some(2), |_, args| {
11837 if deep_value_eq(&args[0], &args[1]) {
11838 Ok(Value::Bool(true))
11839 } else {
11840 Err(RuntimeError::new(format!(
11841 "Assertion failed: expected {} to equal {}",
11842 format_value_debug(&args[0]),
11843 format_value_debug(&args[1])
11844 )))
11845 }
11846 });
11847
11848 define(interp, "assert_ne", Some(2), |_, args| {
11850 if !deep_value_eq(&args[0], &args[1]) {
11851 Ok(Value::Bool(true))
11852 } else {
11853 Err(RuntimeError::new(format!(
11854 "Assertion failed: expected {} to not equal {}",
11855 format_value_debug(&args[0]),
11856 format_value_debug(&args[1])
11857 )))
11858 }
11859 });
11860
11861 define(interp, "assert_lt", Some(2), |_, args| {
11863 let cmp = devex_compare(&args[0], &args[1])?;
11864 if cmp < 0 {
11865 Ok(Value::Bool(true))
11866 } else {
11867 Err(RuntimeError::new(format!(
11868 "Assertion failed: expected {} < {}",
11869 format_value_debug(&args[0]),
11870 format_value_debug(&args[1])
11871 )))
11872 }
11873 });
11874
11875 define(interp, "assert_le", Some(2), |_, args| {
11877 let cmp = devex_compare(&args[0], &args[1])?;
11878 if cmp <= 0 {
11879 Ok(Value::Bool(true))
11880 } else {
11881 Err(RuntimeError::new(format!(
11882 "Assertion failed: expected {} <= {}",
11883 format_value_debug(&args[0]),
11884 format_value_debug(&args[1])
11885 )))
11886 }
11887 });
11888
11889 define(interp, "assert_gt", Some(2), |_, args| {
11891 let cmp = devex_compare(&args[0], &args[1])?;
11892 if cmp > 0 {
11893 Ok(Value::Bool(true))
11894 } else {
11895 Err(RuntimeError::new(format!(
11896 "Assertion failed: expected {} > {}",
11897 format_value_debug(&args[0]),
11898 format_value_debug(&args[1])
11899 )))
11900 }
11901 });
11902
11903 define(interp, "assert_ge", Some(2), |_, args| {
11905 let cmp = devex_compare(&args[0], &args[1])?;
11906 if cmp >= 0 {
11907 Ok(Value::Bool(true))
11908 } else {
11909 Err(RuntimeError::new(format!(
11910 "Assertion failed: expected {} >= {}",
11911 format_value_debug(&args[0]),
11912 format_value_debug(&args[1])
11913 )))
11914 }
11915 });
11916
11917 define(interp, "assert_true", Some(1), |_, args| {
11919 if is_truthy(&args[0]) {
11920 Ok(Value::Bool(true))
11921 } else {
11922 Err(RuntimeError::new(format!(
11923 "Assertion failed: expected {} to be truthy",
11924 format_value_debug(&args[0])
11925 )))
11926 }
11927 });
11928
11929 define(interp, "assert_false", Some(1), |_, args| {
11931 if !is_truthy(&args[0]) {
11932 Ok(Value::Bool(true))
11933 } else {
11934 Err(RuntimeError::new(format!(
11935 "Assertion failed: expected {} to be falsy",
11936 format_value_debug(&args[0])
11937 )))
11938 }
11939 });
11940
11941 define(interp, "assert_null", Some(1), |_, args| {
11943 if matches!(&args[0], Value::Null) {
11944 Ok(Value::Bool(true))
11945 } else {
11946 Err(RuntimeError::new(format!(
11947 "Assertion failed: expected null, got {}",
11948 format_value_debug(&args[0])
11949 )))
11950 }
11951 });
11952
11953 define(interp, "assert_not_null", Some(1), |_, args| {
11955 if !matches!(&args[0], Value::Null) {
11956 Ok(Value::Bool(true))
11957 } else {
11958 Err(RuntimeError::new(
11959 "Assertion failed: expected non-null value, got null",
11960 ))
11961 }
11962 });
11963
11964 define(interp, "assert_type", Some(2), |_, args| {
11966 let expected = match &args[1] {
11967 Value::String(s) => s.to_lowercase(),
11968 _ => {
11969 return Err(RuntimeError::new(
11970 "assert_type: second argument must be type name string",
11971 ))
11972 }
11973 };
11974 let actual = get_type_name(&args[0]).to_lowercase();
11975 if actual == expected || matches_type_alias(&args[0], &expected) {
11976 Ok(Value::Bool(true))
11977 } else {
11978 Err(RuntimeError::new(format!(
11979 "Assertion failed: expected type '{}', got '{}'",
11980 expected, actual
11981 )))
11982 }
11983 });
11984
11985 define(interp, "assert_contains", Some(2), |_, args| {
11987 let contains = match &args[0] {
11988 Value::Array(a) => a.borrow().iter().any(|v| deep_value_eq(v, &args[1])),
11989 Value::String(s) => {
11990 if let Value::String(sub) = &args[1] {
11991 s.contains(&**sub)
11992 } else {
11993 false
11994 }
11995 }
11996 Value::Map(m) => {
11997 if let Value::String(k) = &args[1] {
11998 m.borrow().contains_key(&**k)
11999 } else {
12000 false
12001 }
12002 }
12003 Value::Set(s) => {
12004 if let Value::String(k) = &args[1] {
12005 s.borrow().contains(&**k)
12006 } else {
12007 false
12008 }
12009 }
12010 _ => false,
12011 };
12012 if contains {
12013 Ok(Value::Bool(true))
12014 } else {
12015 Err(RuntimeError::new(format!(
12016 "Assertion failed: {} does not contain {}",
12017 format_value_debug(&args[0]),
12018 format_value_debug(&args[1])
12019 )))
12020 }
12021 });
12022
12023 define(interp, "assert_len", Some(2), |_, args| {
12025 let expected = match &args[1] {
12026 Value::Int(n) => *n as usize,
12027 _ => {
12028 return Err(RuntimeError::new(
12029 "assert_len: second argument must be integer",
12030 ))
12031 }
12032 };
12033 let actual = match &args[0] {
12034 Value::String(s) => s.len(),
12035 Value::Array(a) => a.borrow().len(),
12036 Value::Tuple(t) => t.len(),
12037 Value::Map(m) => m.borrow().len(),
12038 Value::Set(s) => s.borrow().len(),
12039 _ => {
12040 return Err(RuntimeError::new(
12041 "assert_len: first argument must be a collection",
12042 ))
12043 }
12044 };
12045 if actual == expected {
12046 Ok(Value::Bool(true))
12047 } else {
12048 Err(RuntimeError::new(format!(
12049 "Assertion failed: expected length {}, got {}",
12050 expected, actual
12051 )))
12052 }
12053 });
12054
12055 define(interp, "assert_match", Some(2), |_, args| {
12057 let text = match &args[0] {
12058 Value::String(s) => (**s).clone(),
12059 _ => {
12060 return Err(RuntimeError::new(
12061 "assert_match: first argument must be string",
12062 ))
12063 }
12064 };
12065 let pattern = match &args[1] {
12066 Value::String(s) => (**s).clone(),
12067 _ => {
12068 return Err(RuntimeError::new(
12069 "assert_match: second argument must be regex pattern",
12070 ))
12071 }
12072 };
12073 let re =
12074 Regex::new(&pattern).map_err(|e| RuntimeError::new(format!("Invalid regex: {}", e)))?;
12075 if re.is_match(&text) {
12076 Ok(Value::Bool(true))
12077 } else {
12078 Err(RuntimeError::new(format!(
12079 "Assertion failed: '{}' does not match pattern '{}'",
12080 text, pattern
12081 )))
12082 }
12083 });
12084
12085 define(interp, "test", Some(2), |interp, args| {
12089 let name = match &args[0] {
12090 Value::String(s) => (**s).clone(),
12091 _ => {
12092 return Err(RuntimeError::new(
12093 "test: first argument must be test name string",
12094 ))
12095 }
12096 };
12097 let func = match &args[1] {
12098 Value::Function(f) => f.clone(),
12099 _ => {
12100 return Err(RuntimeError::new(
12101 "test: second argument must be test function",
12102 ))
12103 }
12104 };
12105
12106 let start = Instant::now();
12107 let result = interp.call_function(&func, vec![]);
12108 let elapsed = start.elapsed();
12109
12110 match result {
12111 Ok(_) => {
12112 println!("✓ {} ({:.2}ms)", name, elapsed.as_secs_f64() * 1000.0);
12113 Ok(Value::Bool(true))
12114 }
12115 Err(e) => {
12116 println!(
12117 "✗ {} ({:.2}ms): {}",
12118 name,
12119 elapsed.as_secs_f64() * 1000.0,
12120 e
12121 );
12122 Ok(Value::Bool(false))
12123 }
12124 }
12125 });
12126
12127 define(interp, "skip", Some(1), |_, args| {
12129 let reason = match &args[0] {
12130 Value::String(s) => (**s).clone(),
12131 _ => "skipped".to_string(),
12132 };
12133 println!("⊘ {}", reason);
12134 Ok(Value::Null)
12135 });
12136
12137 define(interp, "profile", Some(1), |interp, args| {
12141 let func = match &args[0] {
12142 Value::Function(f) => f.clone(),
12143 _ => return Err(RuntimeError::new("profile: argument must be function")),
12144 };
12145
12146 let start = Instant::now();
12147 let result = interp.call_function(&func, vec![])?;
12148 let elapsed = start.elapsed();
12149
12150 let mut timing = HashMap::new();
12151 timing.insert(
12152 "ms".to_string(),
12153 Value::Float(elapsed.as_secs_f64() * 1000.0),
12154 );
12155 timing.insert("us".to_string(), Value::Float(elapsed.as_micros() as f64));
12156 timing.insert("ns".to_string(), Value::Int(elapsed.as_nanos() as i64));
12157
12158 Ok(Value::Tuple(Rc::new(vec![
12159 result,
12160 Value::Map(Rc::new(RefCell::new(timing))),
12161 ])))
12162 });
12163
12164 define(interp, "measure", Some(2), |interp, args| {
12166 let func = match &args[0] {
12167 Value::Function(f) => f.clone(),
12168 _ => {
12169 return Err(RuntimeError::new(
12170 "measure: first argument must be function",
12171 ))
12172 }
12173 };
12174 let iterations = match &args[1] {
12175 Value::Int(n) => *n as usize,
12176 _ => {
12177 return Err(RuntimeError::new(
12178 "measure: second argument must be iteration count",
12179 ))
12180 }
12181 };
12182
12183 let mut times: Vec<f64> = Vec::new();
12184 let mut last_result = Value::Null;
12185
12186 for _ in 0..iterations {
12187 let start = Instant::now();
12188 last_result = interp.call_function(&func, vec![])?;
12189 times.push(start.elapsed().as_secs_f64() * 1000.0);
12190 }
12191
12192 let sum: f64 = times.iter().sum();
12193 let avg = sum / iterations as f64;
12194 let min = times.iter().cloned().fold(f64::INFINITY, f64::min);
12195 let max = times.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
12196
12197 let variance: f64 =
12198 times.iter().map(|t| (t - avg).powi(2)).sum::<f64>() / iterations as f64;
12199 let stddev = variance.sqrt();
12200
12201 let mut stats = HashMap::new();
12202 stats.insert("iterations".to_string(), Value::Int(iterations as i64));
12203 stats.insert("total_ms".to_string(), Value::Float(sum));
12204 stats.insert("avg_ms".to_string(), Value::Float(avg));
12205 stats.insert("min_ms".to_string(), Value::Float(min));
12206 stats.insert("max_ms".to_string(), Value::Float(max));
12207 stats.insert("stddev_ms".to_string(), Value::Float(stddev));
12208
12209 Ok(Value::Tuple(Rc::new(vec![
12210 last_result,
12211 Value::Map(Rc::new(RefCell::new(stats))),
12212 ])))
12213 });
12214
12215 define(interp, "help", Some(1), |_, args| {
12219 let name = match &args[0] {
12220 Value::String(s) => (**s).clone(),
12221 Value::BuiltIn(f) => f.name.clone(),
12222 _ => {
12223 return Err(RuntimeError::new(
12224 "help: argument must be function name or builtin",
12225 ))
12226 }
12227 };
12228
12229 let doc = get_function_doc(&name);
12231 Ok(Value::String(Rc::new(doc)))
12232 });
12233
12234 define(interp, "list_builtins", Some(0), |_, _| {
12236 let categories = vec![
12237 "Core: print, println, assert, panic, len, type_of",
12238 "Math: abs, floor, ceil, round, sqrt, pow, log, sin, cos, tan",
12239 "Collections: map, filter, reduce, zip, flatten, first, last, sort, reverse",
12240 "Strings: upper, lower, trim, split, join, contains, replace, format",
12241 "IO: read_file, write_file, file_exists, read_line",
12242 "Time: now, sleep, timestamp, format_time",
12243 "JSON: json_parse, json_stringify",
12244 "Crypto: sha256, sha512, md5, base64_encode, base64_decode",
12245 "Regex: regex_match, regex_replace, regex_split",
12246 "Pattern: type_of, is_type, match_regex, match_struct, guard, when",
12247 "DevEx: debug, inspect, trace, assert_eq, assert_ne, test, profile",
12248 ];
12249 let values: Vec<Value> = categories
12250 .iter()
12251 .map(|s| Value::String(Rc::new(s.to_string())))
12252 .collect();
12253 Ok(Value::Array(Rc::new(RefCell::new(values))))
12254 });
12255
12256 define(interp, "todo", Some(0), |_, _| {
12260 Err(RuntimeError::new("not yet implemented"))
12261 });
12262
12263 define(interp, "unreachable", Some(0), |_, _| {
12265 Err(RuntimeError::new("reached unreachable code"))
12266 });
12267
12268 define(interp, "unimplemented", Some(1), |_, args| {
12270 let msg = match &args[0] {
12271 Value::String(s) => (**s).clone(),
12272 _ => "unimplemented".to_string(),
12273 };
12274 Err(RuntimeError::new(format!("unimplemented: {}", msg)))
12275 });
12276
12277 define(interp, "deprecated", Some(2), |_, args| {
12279 let msg = match &args[0] {
12280 Value::String(s) => (**s).clone(),
12281 _ => "deprecated".to_string(),
12282 };
12283 eprintln!("[DEPRECATED] {}", msg);
12284 Ok(args[1].clone())
12285 });
12286
12287 define(interp, "version", Some(0), |_, _| {
12289 let mut info = HashMap::new();
12290 info.insert(
12291 "sigil".to_string(),
12292 Value::String(Rc::new("0.1.0".to_string())),
12293 );
12294 info.insert(
12295 "stdlib".to_string(),
12296 Value::String(Rc::new("7.0".to_string())),
12297 );
12298 info.insert(
12299 "phase".to_string(),
12300 Value::String(Rc::new("Phase 7 - DevEx".to_string())),
12301 );
12302 Ok(Value::Map(Rc::new(RefCell::new(info))))
12303 });
12304}
12305
12306fn format_value_debug(value: &Value) -> String {
12308 match value {
12309 Value::Null => "null".to_string(),
12310 Value::Bool(b) => b.to_string(),
12311 Value::Int(n) => n.to_string(),
12312 Value::Float(f) => format!("{:.6}", f),
12313 Value::String(s) => format!("\"{}\"", s),
12314 Value::Char(c) => format!("'{}'", c),
12315 Value::Array(a) => {
12316 let items: Vec<String> = a.borrow().iter().take(10).map(format_value_debug).collect();
12317 if a.borrow().len() > 10 {
12318 format!(
12319 "[{}, ... ({} more)]",
12320 items.join(", "),
12321 a.borrow().len() - 10
12322 )
12323 } else {
12324 format!("[{}]", items.join(", "))
12325 }
12326 }
12327 Value::Tuple(t) => {
12328 let items: Vec<String> = t.iter().map(format_value_debug).collect();
12329 format!("({})", items.join(", "))
12330 }
12331 Value::Map(m) => {
12332 let items: Vec<String> = m
12333 .borrow()
12334 .iter()
12335 .take(5)
12336 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
12337 .collect();
12338 if m.borrow().len() > 5 {
12339 format!(
12340 "{{{}, ... ({} more)}}",
12341 items.join(", "),
12342 m.borrow().len() - 5
12343 )
12344 } else {
12345 format!("{{{}}}", items.join(", "))
12346 }
12347 }
12348 Value::Set(s) => {
12349 let items: Vec<String> = s.borrow().iter().take(5).cloned().collect();
12350 if s.borrow().len() > 5 {
12351 format!(
12352 "#{{{}, ... ({} more)}}",
12353 items.join(", "),
12354 s.borrow().len() - 5
12355 )
12356 } else {
12357 format!("#{{{}}}", items.join(", "))
12358 }
12359 }
12360 Value::Struct { name, fields } => {
12361 let items: Vec<String> = fields
12362 .borrow()
12363 .iter()
12364 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
12365 .collect();
12366 format!("{} {{{}}}", name, items.join(", "))
12367 }
12368 Value::Variant {
12369 enum_name,
12370 variant_name,
12371 fields,
12372 } => match fields {
12373 Some(f) => {
12374 let items: Vec<String> = f.iter().map(format_value_debug).collect();
12375 format!("{}::{}({})", enum_name, variant_name, items.join(", "))
12376 }
12377 None => format!("{}::{}", enum_name, variant_name),
12378 },
12379 Value::Function(_) => "<function>".to_string(),
12380 Value::BuiltIn(f) => format!("<builtin:{}>", f.name),
12381 Value::Ref(r) => format!("&{}", format_value_debug(&r.borrow())),
12382 Value::Infinity => "∞".to_string(),
12383 Value::Empty => "∅".to_string(),
12384 Value::Evidential { value, evidence } => {
12385 format!("{:?}({})", evidence, format_value_debug(value))
12386 }
12387 Value::Affective { value, affect } => {
12388 let mut markers = Vec::new();
12389 if let Some(s) = &affect.sentiment {
12390 markers.push(format!("{:?}", s));
12391 }
12392 if affect.sarcasm {
12393 markers.push("sarcasm".to_string());
12394 }
12395 if let Some(i) = &affect.intensity {
12396 markers.push(format!("{:?}", i));
12397 }
12398 if let Some(f) = &affect.formality {
12399 markers.push(format!("{:?}", f));
12400 }
12401 if let Some(e) = &affect.emotion {
12402 markers.push(format!("{:?}", e));
12403 }
12404 if let Some(c) = &affect.confidence {
12405 markers.push(format!("{:?}", c));
12406 }
12407 format!("{}[{}]", format_value_debug(value), markers.join(","))
12408 }
12409 Value::Channel(_) => "<channel>".to_string(),
12410 Value::ThreadHandle(_) => "<thread>".to_string(),
12411 Value::Actor(_) => "<actor>".to_string(),
12412 Value::Future(_) => "<future>".to_string(),
12413 Value::VariantConstructor { enum_name, variant_name } => {
12414 format!("<constructor {}::{}>", enum_name, variant_name)
12415 }
12416 Value::DefaultConstructor { type_name } => {
12417 format!("<default {}>", type_name)
12418 }
12419 Value::Range { start, end, inclusive } => {
12420 match (start, end) {
12421 (Some(s), Some(e)) => if *inclusive {
12422 format!("{}..={}", s, e)
12423 } else {
12424 format!("{}..{}", s, e)
12425 },
12426 (Some(s), None) => format!("{}..", s),
12427 (None, Some(e)) => if *inclusive {
12428 format!("..={}", e)
12429 } else {
12430 format!("..{}", e)
12431 },
12432 (None, None) => "..".to_string(),
12433 }
12434 }
12435 Value::RefCellValue(rc) => format!("RefCell({})", format_value_debug(&rc.value.borrow())),
12436 Value::TraitObject { value, trait_name, concrete_type } => format!("dyn {}({}: {})", trait_name, concrete_type, format_value_debug(value)),
12437 }
12438}
12439
12440fn pretty_print_value(value: &Value, indent: usize) -> String {
12442 let prefix = " ".repeat(indent);
12443 match value {
12444 Value::Array(a) => {
12445 if a.borrow().is_empty() {
12446 "[]".to_string()
12447 } else {
12448 let items: Vec<String> = a
12449 .borrow()
12450 .iter()
12451 .map(|v| {
12452 format!(
12453 "{}{}",
12454 " ".repeat(indent + 1),
12455 pretty_print_value(v, indent + 1)
12456 )
12457 })
12458 .collect();
12459 format!("[\n{}\n{}]", items.join(",\n"), prefix)
12460 }
12461 }
12462 Value::Map(m) => {
12463 if m.borrow().is_empty() {
12464 "{}".to_string()
12465 } else {
12466 let items: Vec<String> = m
12467 .borrow()
12468 .iter()
12469 .map(|(k, v)| {
12470 format!(
12471 "{}\"{}\": {}",
12472 " ".repeat(indent + 1),
12473 k,
12474 pretty_print_value(v, indent + 1)
12475 )
12476 })
12477 .collect();
12478 format!("{{\n{}\n{}}}", items.join(",\n"), prefix)
12479 }
12480 }
12481 Value::Struct { name, fields } => {
12482 if fields.borrow().is_empty() {
12483 format!("{} {{}}", name)
12484 } else {
12485 let items: Vec<String> = fields
12486 .borrow()
12487 .iter()
12488 .map(|(k, v)| {
12489 format!(
12490 "{}{}: {}",
12491 " ".repeat(indent + 1),
12492 k,
12493 pretty_print_value(v, indent + 1)
12494 )
12495 })
12496 .collect();
12497 format!("{} {{\n{}\n{}}}", name, items.join(",\n"), prefix)
12498 }
12499 }
12500 _ => format_value_debug(value),
12501 }
12502}
12503
12504fn devex_compare(a: &Value, b: &Value) -> Result<i64, RuntimeError> {
12506 match (a, b) {
12507 (Value::Int(a), Value::Int(b)) => Ok(if a < b {
12508 -1
12509 } else if a > b {
12510 1
12511 } else {
12512 0
12513 }),
12514 (Value::Float(a), Value::Float(b)) => Ok(if a < b {
12515 -1
12516 } else if a > b {
12517 1
12518 } else {
12519 0
12520 }),
12521 (Value::Int(a), Value::Float(b)) => {
12522 let a = *a as f64;
12523 Ok(if a < *b {
12524 -1
12525 } else if a > *b {
12526 1
12527 } else {
12528 0
12529 })
12530 }
12531 (Value::Float(a), Value::Int(b)) => {
12532 let b = *b as f64;
12533 Ok(if *a < b {
12534 -1
12535 } else if *a > b {
12536 1
12537 } else {
12538 0
12539 })
12540 }
12541 (Value::String(a), Value::String(b)) => Ok(if a < b {
12542 -1
12543 } else if a > b {
12544 1
12545 } else {
12546 0
12547 }),
12548 _ => Err(RuntimeError::new("cannot compare these types")),
12549 }
12550}
12551
12552fn get_type_name(value: &Value) -> String {
12554 match value {
12555 Value::Null => "null".to_string(),
12556 Value::Bool(_) => "bool".to_string(),
12557 Value::Int(_) => "int".to_string(),
12558 Value::Float(_) => "float".to_string(),
12559 Value::String(_) => "string".to_string(),
12560 Value::Char(_) => "char".to_string(),
12561 Value::Array(_) => "array".to_string(),
12562 Value::Tuple(_) => "tuple".to_string(),
12563 Value::Map(_) => "map".to_string(),
12564 Value::Set(_) => "set".to_string(),
12565 Value::Struct { name, .. } => name.clone(),
12566 Value::Variant { enum_name, .. } => enum_name.clone(),
12567 Value::Function(_) => "function".to_string(),
12568 Value::BuiltIn(_) => "builtin".to_string(),
12569 Value::Ref(_) => "ref".to_string(),
12570 Value::Infinity => "infinity".to_string(),
12571 Value::Empty => "empty".to_string(),
12572 Value::Evidential { .. } => "evidential".to_string(),
12573 Value::Affective { .. } => "affective".to_string(),
12574 Value::Channel(_) => "channel".to_string(),
12575 Value::ThreadHandle(_) => "thread".to_string(),
12576 Value::Actor(_) => "actor".to_string(),
12577 Value::Future(_) => "future".to_string(),
12578 Value::VariantConstructor { enum_name, .. } => format!("{}_constructor", enum_name),
12579 Value::DefaultConstructor { type_name } => format!("{}_default", type_name),
12580 Value::Range { .. } => "range".to_string(),
12581 Value::RefCellValue(_) => "refcell".to_string(),
12582 Value::TraitObject { trait_name, .. } => format!("dyn {}", trait_name),
12583 }
12584}
12585
12586fn matches_type_alias(value: &Value, type_name: &str) -> bool {
12588 match (value, type_name) {
12589 (Value::Int(_), "number") | (Value::Float(_), "number") => true,
12590 (Value::Int(_), "integer") => true,
12591 (Value::Array(_), "list") => true,
12592 (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
12593 (Value::Function(_), "fn") | (Value::BuiltIn(_), "fn") => true,
12594 (Value::BuiltIn(_), "function") => true,
12595 _ => false,
12596 }
12597}
12598
12599fn get_function_doc(name: &str) -> String {
12601 match name {
12602 "print" => "print(value) - Print value to stdout".to_string(),
12603 "println" => "println(value) - Print value with newline".to_string(),
12604 "len" => "len(collection) - Get length of string, array, map, or set".to_string(),
12605 "type_of" => "type_of(value) - Get type name as string".to_string(),
12606 "assert" => "assert(condition) - Assert condition is truthy, panic if false".to_string(),
12607 "assert_eq" => "assert_eq(a, b) - Assert two values are deeply equal".to_string(),
12608 "debug" => "debug(value) - Print value with type info and return it".to_string(),
12609 "map" => "map(array, fn) - Apply function to each element".to_string(),
12610 "filter" => "filter(array, fn) - Keep elements where predicate is true".to_string(),
12611 "reduce" => "reduce(array, init, fn) - Fold array with function".to_string(),
12612 "range" => "range(start, end) - Create array of integers from start to end".to_string(),
12613 "sum" => "sum(array) - Sum all numeric elements".to_string(),
12614 "product" => "product(array) - Multiply all numeric elements".to_string(),
12615 "sort" => "sort(array) - Sort array in ascending order".to_string(),
12616 "reverse" => "reverse(array) - Reverse array order".to_string(),
12617 "join" => "join(array, sep) - Join array elements with separator".to_string(),
12618 "split" => "split(string, sep) - Split string by separator".to_string(),
12619 "trim" => "trim(string) - Remove leading/trailing whitespace".to_string(),
12620 "upper" => "upper(string) - Convert to uppercase".to_string(),
12621 "lower" => "lower(string) - Convert to lowercase".to_string(),
12622 _ => format!("No documentation available for '{}'", name),
12623 }
12624}
12625
12626fn register_soa(interp: &mut Interpreter) {
12638 define(interp, "aos_to_soa", Some(2), |_, args| {
12641 let arr = match &args[0] {
12642 Value::Array(arr) => arr.borrow().clone(),
12643 _ => {
12644 return Err(RuntimeError::new(
12645 "aos_to_soa: first argument must be array",
12646 ))
12647 }
12648 };
12649 let keys = match &args[1] {
12650 Value::Array(keys) => keys.borrow().clone(),
12651 _ => {
12652 return Err(RuntimeError::new(
12653 "aos_to_soa: second argument must be array of keys",
12654 ))
12655 }
12656 };
12657
12658 if arr.is_empty() {
12659 let mut result = HashMap::new();
12661 for key in &keys {
12662 if let Value::String(k) = key {
12663 result.insert((**k).clone(), Value::Array(Rc::new(RefCell::new(vec![]))));
12664 }
12665 }
12666 return Ok(Value::Map(Rc::new(RefCell::new(result))));
12667 }
12668
12669 let key_names: Vec<String> = keys
12671 .iter()
12672 .filter_map(|k| {
12673 if let Value::String(s) = k {
12674 Some((**s).clone())
12675 } else {
12676 None
12677 }
12678 })
12679 .collect();
12680
12681 let mut soa: HashMap<String, Vec<Value>> = HashMap::new();
12683 for key in &key_names {
12684 soa.insert(key.clone(), Vec::with_capacity(arr.len()));
12685 }
12686
12687 for item in &arr {
12689 match item {
12690 Value::Map(map) => {
12691 let map = map.borrow();
12692 for key in &key_names {
12693 let val = map.get(key).cloned().unwrap_or(Value::Null);
12694 soa.get_mut(key).unwrap().push(val);
12695 }
12696 }
12697 Value::Struct { fields, .. } => {
12698 let fields = fields.borrow();
12699 for key in &key_names {
12700 let val = fields.get(key).cloned().unwrap_or(Value::Null);
12701 soa.get_mut(key).unwrap().push(val);
12702 }
12703 }
12704 _ => {
12705 return Err(RuntimeError::new(
12706 "aos_to_soa: array must contain structs or maps",
12707 ))
12708 }
12709 }
12710 }
12711
12712 let result: HashMap<String, Value> = soa
12714 .into_iter()
12715 .map(|(k, v)| (k, Value::Array(Rc::new(RefCell::new(v)))))
12716 .collect();
12717
12718 Ok(Value::Map(Rc::new(RefCell::new(result))))
12719 });
12720
12721 define(interp, "soa_to_aos", Some(1), |_, args| {
12724 let soa = match &args[0] {
12725 Value::Map(map) => map.borrow().clone(),
12726 _ => return Err(RuntimeError::new("soa_to_aos: argument must be map")),
12727 };
12728
12729 if soa.is_empty() {
12730 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
12731 }
12732
12733 let len = soa
12735 .values()
12736 .next()
12737 .and_then(|v| {
12738 if let Value::Array(arr) = v {
12739 Some(arr.borrow().len())
12740 } else {
12741 None
12742 }
12743 })
12744 .unwrap_or(0);
12745
12746 let mut aos: Vec<Value> = Vec::with_capacity(len);
12748 for i in 0..len {
12749 let mut fields = HashMap::new();
12750 for (key, value) in &soa {
12751 if let Value::Array(arr) = value {
12752 let arr = arr.borrow();
12753 if i < arr.len() {
12754 fields.insert(key.clone(), arr[i].clone());
12755 }
12756 }
12757 }
12758 aos.push(Value::Map(Rc::new(RefCell::new(fields))));
12759 }
12760
12761 Ok(Value::Array(Rc::new(RefCell::new(aos))))
12762 });
12763
12764 define(interp, "soa_map", Some(3), |interp, args| {
12767 let mut soa = match &args[0] {
12768 Value::Map(map) => map.borrow().clone(),
12769 _ => return Err(RuntimeError::new("soa_map: first argument must be SoA map")),
12770 };
12771 let key = match &args[1] {
12772 Value::String(s) => (**s).clone(),
12773 _ => {
12774 return Err(RuntimeError::new(
12775 "soa_map: second argument must be key string",
12776 ))
12777 }
12778 };
12779 let func = match &args[2] {
12780 Value::Function(f) => f.clone(),
12781 _ => {
12782 return Err(RuntimeError::new(
12783 "soa_map: third argument must be a function",
12784 ))
12785 }
12786 };
12787
12788 let arr = soa
12790 .get(&key)
12791 .ok_or_else(|| RuntimeError::new(format!("soa_map: key '{}' not found", key)))?;
12792
12793 let arr_vals = match arr {
12794 Value::Array(a) => a.borrow().clone(),
12795 _ => return Err(RuntimeError::new("soa_map: key must map to array")),
12796 };
12797
12798 let results: Vec<Value> = arr_vals
12800 .iter()
12801 .map(|val| interp.call_function(&func, vec![val.clone()]))
12802 .collect::<Result<_, _>>()?;
12803
12804 soa.insert(key, Value::Array(Rc::new(RefCell::new(results))));
12806
12807 Ok(Value::Map(Rc::new(RefCell::new(soa))))
12808 });
12809
12810 define(interp, "soa_zip", Some(3), |interp, args| {
12813 let soa = match &args[0] {
12814 Value::Map(map) => map.borrow().clone(),
12815 _ => return Err(RuntimeError::new("soa_zip: first argument must be SoA map")),
12816 };
12817 let keys = match &args[1] {
12818 Value::Array(keys) => keys.borrow().clone(),
12819 _ => {
12820 return Err(RuntimeError::new(
12821 "soa_zip: second argument must be array of keys",
12822 ))
12823 }
12824 };
12825 let func = match &args[2] {
12826 Value::Function(f) => f.clone(),
12827 _ => {
12828 return Err(RuntimeError::new(
12829 "soa_zip: third argument must be a function",
12830 ))
12831 }
12832 };
12833
12834 let arrays: Vec<Vec<Value>> = keys
12836 .iter()
12837 .filter_map(|k| {
12838 if let Value::String(s) = k {
12839 if let Some(Value::Array(arr)) = soa.get(&**s) {
12840 return Some(arr.borrow().clone());
12841 }
12842 }
12843 None
12844 })
12845 .collect();
12846
12847 if arrays.is_empty() {
12848 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
12849 }
12850
12851 let len = arrays[0].len();
12852
12853 let results: Vec<Value> = (0..len)
12855 .map(|i| {
12856 let fn_args: Vec<Value> = arrays
12857 .iter()
12858 .filter_map(|arr| arr.get(i).cloned())
12859 .collect();
12860 interp.call_function(&func, fn_args)
12861 })
12862 .collect::<Result<_, _>>()?;
12863
12864 Ok(Value::Array(Rc::new(RefCell::new(results))))
12865 });
12866
12867 define(interp, "interleave", None, |_, args| {
12870 if args.is_empty() {
12871 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
12872 }
12873
12874 let arrays: Vec<Vec<Value>> = args
12875 .iter()
12876 .filter_map(|arg| {
12877 if let Value::Array(arr) = arg {
12878 Some(arr.borrow().clone())
12879 } else {
12880 None
12881 }
12882 })
12883 .collect();
12884
12885 if arrays.is_empty() {
12886 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
12887 }
12888
12889 let len = arrays[0].len();
12890 let stride = arrays.len();
12891 let mut result = Vec::with_capacity(len * stride);
12892
12893 for i in 0..len {
12894 for arr in &arrays {
12895 if let Some(val) = arr.get(i) {
12896 result.push(val.clone());
12897 }
12898 }
12899 }
12900
12901 Ok(Value::Array(Rc::new(RefCell::new(result))))
12902 });
12903
12904 define(interp, "deinterleave", Some(2), |_, args| {
12907 let arr = match &args[0] {
12908 Value::Array(arr) => arr.borrow().clone(),
12909 _ => {
12910 return Err(RuntimeError::new(
12911 "deinterleave: first argument must be array",
12912 ))
12913 }
12914 };
12915 let stride = match &args[1] {
12916 Value::Int(n) => *n as usize,
12917 _ => {
12918 return Err(RuntimeError::new(
12919 "deinterleave: second argument must be integer stride",
12920 ))
12921 }
12922 };
12923
12924 if stride == 0 {
12925 return Err(RuntimeError::new("deinterleave: stride must be > 0"));
12926 }
12927
12928 let mut result: Vec<Vec<Value>> = (0..stride).map(|_| Vec::new()).collect();
12929
12930 for (i, val) in arr.iter().enumerate() {
12931 result[i % stride].push(val.clone());
12932 }
12933
12934 Ok(Value::Array(Rc::new(RefCell::new(
12935 result
12936 .into_iter()
12937 .map(|v| Value::Array(Rc::new(RefCell::new(v))))
12938 .collect(),
12939 ))))
12940 });
12941}
12942
12943fn register_tensor(interp: &mut Interpreter) {
12949 define(interp, "outer_product", Some(2), |_, args| {
12952 let a = match &args[0] {
12953 Value::Array(arr) => arr.borrow().clone(),
12954 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
12955 };
12956 let b = match &args[1] {
12957 Value::Array(arr) => arr.borrow().clone(),
12958 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
12959 };
12960
12961 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
12963 for ai in &a {
12964 for bi in &b {
12965 let product = match (ai, bi) {
12966 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
12967 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
12968 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
12969 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
12970 _ => return Err(RuntimeError::new("outer_product: elements must be numeric")),
12971 };
12972 result.push(product);
12973 }
12974 }
12975
12976 Ok(Value::Array(Rc::new(RefCell::new(result))))
12977 });
12978
12979 define(interp, "tensor_contract", Some(4), |_, args| {
12982 let a = match &args[0] {
12983 Value::Array(arr) => arr.borrow().clone(),
12984 _ => {
12985 return Err(RuntimeError::new(
12986 "tensor_contract: first argument must be array",
12987 ))
12988 }
12989 };
12990 let b = match &args[1] {
12991 Value::Array(arr) => arr.borrow().clone(),
12992 _ => {
12993 return Err(RuntimeError::new(
12994 "tensor_contract: second argument must be array",
12995 ))
12996 }
12997 };
12998 let _axis_a = match &args[2] {
12999 Value::Int(n) => *n as usize,
13000 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
13001 };
13002 let _axis_b = match &args[3] {
13003 Value::Int(n) => *n as usize,
13004 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
13005 };
13006
13007 if a.len() != b.len() {
13009 return Err(RuntimeError::new(
13010 "tensor_contract: vectors must have same length for contraction",
13011 ));
13012 }
13013
13014 let mut sum = 0.0f64;
13015 for (ai, bi) in a.iter().zip(b.iter()) {
13016 let product = match (ai, bi) {
13017 (Value::Float(x), Value::Float(y)) => x * y,
13018 (Value::Int(x), Value::Int(y)) => (*x as f64) * (*y as f64),
13019 (Value::Float(x), Value::Int(y)) => x * (*y as f64),
13020 (Value::Int(x), Value::Float(y)) => (*x as f64) * y,
13021 _ => {
13022 return Err(RuntimeError::new(
13023 "tensor_contract: elements must be numeric",
13024 ))
13025 }
13026 };
13027 sum += product;
13028 }
13029
13030 Ok(Value::Float(sum))
13031 });
13032
13033 define(interp, "kronecker_product", Some(2), |_, args| {
13036 let a = match &args[0] {
13037 Value::Array(arr) => arr.borrow().clone(),
13038 _ => {
13039 return Err(RuntimeError::new(
13040 "kronecker_product: arguments must be arrays",
13041 ))
13042 }
13043 };
13044 let b = match &args[1] {
13045 Value::Array(arr) => arr.borrow().clone(),
13046 _ => {
13047 return Err(RuntimeError::new(
13048 "kronecker_product: arguments must be arrays",
13049 ))
13050 }
13051 };
13052
13053 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
13055 for ai in &a {
13056 for bi in &b {
13057 let product = match (ai, bi) {
13058 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
13059 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
13060 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
13061 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
13062 _ => {
13063 return Err(RuntimeError::new(
13064 "kronecker_product: elements must be numeric",
13065 ))
13066 }
13067 };
13068 result.push(product);
13069 }
13070 }
13071
13072 Ok(Value::Array(Rc::new(RefCell::new(result))))
13073 });
13074
13075 define(interp, "hadamard_product", Some(2), |_, args| {
13077 let a = match &args[0] {
13078 Value::Array(arr) => arr.borrow().clone(),
13079 _ => {
13080 return Err(RuntimeError::new(
13081 "hadamard_product: arguments must be arrays",
13082 ))
13083 }
13084 };
13085 let b = match &args[1] {
13086 Value::Array(arr) => arr.borrow().clone(),
13087 _ => {
13088 return Err(RuntimeError::new(
13089 "hadamard_product: arguments must be arrays",
13090 ))
13091 }
13092 };
13093
13094 if a.len() != b.len() {
13095 return Err(RuntimeError::new(
13096 "hadamard_product: arrays must have same length",
13097 ));
13098 }
13099
13100 let result: Vec<Value> = a
13101 .iter()
13102 .zip(b.iter())
13103 .map(|(ai, bi)| match (ai, bi) {
13104 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
13105 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
13106 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * (*y as f64))),
13107 (Value::Int(x), Value::Float(y)) => Ok(Value::Float((*x as f64) * y)),
13108 _ => Err(RuntimeError::new(
13109 "hadamard_product: elements must be numeric",
13110 )),
13111 })
13112 .collect::<Result<_, _>>()?;
13113
13114 Ok(Value::Array(Rc::new(RefCell::new(result))))
13115 });
13116
13117 define(interp, "trace", Some(2), |_, args| {
13119 let arr = match &args[0] {
13120 Value::Array(arr) => arr.borrow().clone(),
13121 _ => return Err(RuntimeError::new("trace: first argument must be array")),
13122 };
13123 let size = match &args[1] {
13124 Value::Int(n) => *n as usize,
13125 _ => {
13126 return Err(RuntimeError::new(
13127 "trace: second argument must be matrix size",
13128 ))
13129 }
13130 };
13131
13132 let mut sum = 0.0f64;
13133 for i in 0..size {
13134 let idx = i * size + i;
13135 if idx < arr.len() {
13136 sum += match &arr[idx] {
13137 Value::Float(f) => *f,
13138 Value::Int(n) => *n as f64,
13139 _ => return Err(RuntimeError::new("trace: elements must be numeric")),
13140 };
13141 }
13142 }
13143
13144 Ok(Value::Float(sum))
13145 });
13146}
13147
13148fn register_autodiff(interp: &mut Interpreter) {
13194 define(interp, "grad", None, |interp, args| {
13197 if args.len() < 2 {
13198 return Err(RuntimeError::new(
13199 "grad() requires function and point arguments.\n\
13200 Usage: grad(f, x) or grad(f, x, step_size)\n\
13201 Example:\n\
13202 fn f(x) { return x * x; }\n\
13203 let derivative = grad(f, 3.0); // Returns 6.0",
13204 ));
13205 }
13206
13207 let func = match &args[0] {
13208 Value::Function(f) => f.clone(),
13209 _ => {
13210 return Err(RuntimeError::new(
13211 "grad() first argument must be a function.\n\
13212 Got non-function value. Define a function first:\n\
13213 fn my_func(x) { return x * x; }\n\
13214 grad(my_func, 2.0)",
13215 ))
13216 }
13217 };
13218 let x = match &args[1] {
13219 Value::Float(f) => *f,
13220 Value::Int(n) => *n as f64,
13221 Value::Array(arr) => {
13222 let arr = arr.borrow().clone();
13224 let h = if args.len() > 2 {
13225 match &args[2] {
13226 Value::Float(f) => *f,
13227 Value::Int(n) => *n as f64,
13228 _ => 1e-7,
13229 }
13230 } else {
13231 1e-7
13232 };
13233
13234 let mut gradient = Vec::with_capacity(arr.len());
13235 for (i, xi) in arr.iter().enumerate() {
13236 let xi_val = match xi {
13237 Value::Float(f) => *f,
13238 Value::Int(n) => *n as f64,
13239 _ => continue,
13240 };
13241
13242 let mut x_plus = arr.clone();
13244 let mut x_minus = arr.clone();
13245 x_plus[i] = Value::Float(xi_val + h);
13246 x_minus[i] = Value::Float(xi_val - h);
13247
13248 let f_plus = interp
13249 .call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13250 let f_minus = interp
13251 .call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13252
13253 let grad_i = match (f_plus, f_minus) {
13254 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
13255 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
13256 _ => return Err(RuntimeError::new("grad: function must return numeric")),
13257 };
13258
13259 gradient.push(Value::Float(grad_i));
13260 }
13261
13262 return Ok(Value::Array(Rc::new(RefCell::new(gradient))));
13263 }
13264 _ => return Err(RuntimeError::new("grad: x must be numeric or array")),
13265 };
13266
13267 let h = if args.len() > 2 {
13268 match &args[2] {
13269 Value::Float(f) => *f,
13270 Value::Int(n) => *n as f64,
13271 _ => 1e-7,
13272 }
13273 } else {
13274 1e-7
13275 };
13276
13277 let f_plus = interp.call_function(&func, vec![Value::Float(x + h)])?;
13279 let f_minus = interp.call_function(&func, vec![Value::Float(x - h)])?;
13280
13281 let derivative = match (f_plus, f_minus) {
13282 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
13283 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
13284 _ => return Err(RuntimeError::new("grad: function must return numeric")),
13285 };
13286
13287 Ok(Value::Float(derivative))
13288 });
13289
13290 define(interp, "jacobian", Some(2), |interp, args| {
13292 let func = match &args[0] {
13293 Value::Function(f) => f.clone(),
13294 _ => {
13295 return Err(RuntimeError::new(
13296 "jacobian: first argument must be a function",
13297 ))
13298 }
13299 };
13300 let x = match &args[1] {
13301 Value::Array(arr) => arr.borrow().clone(),
13302 _ => return Err(RuntimeError::new("jacobian: second argument must be array")),
13303 };
13304
13305 let h = 1e-7;
13306 let n = x.len();
13307
13308 let f_x =
13310 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x.clone())))])?;
13311 let m = match &f_x {
13312 Value::Array(arr) => arr.borrow().len(),
13313 _ => 1,
13314 };
13315
13316 let mut jacobian: Vec<Value> = Vec::with_capacity(m * n);
13318
13319 for j in 0..n {
13320 let xj = match &x[j] {
13321 Value::Float(f) => *f,
13322 Value::Int(i) => *i as f64,
13323 _ => continue,
13324 };
13325
13326 let mut x_plus = x.clone();
13327 let mut x_minus = x.clone();
13328 x_plus[j] = Value::Float(xj + h);
13329 x_minus[j] = Value::Float(xj - h);
13330
13331 let f_plus =
13332 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13333 let f_minus =
13334 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13335
13336 match (&f_plus, &f_minus) {
13338 (Value::Array(fp), Value::Array(fm)) => {
13339 let fp = fp.borrow();
13340 let fm = fm.borrow();
13341 for i in 0..m {
13342 let dfi_dxj = match (&fp[i], &fm[i]) {
13343 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
13344 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
13345 _ => 0.0,
13346 };
13347 jacobian.push(Value::Float(dfi_dxj));
13348 }
13349 }
13350 (Value::Float(fp), Value::Float(fm)) => {
13351 jacobian.push(Value::Float((fp - fm) / (2.0 * h)));
13352 }
13353 _ => {
13354 return Err(RuntimeError::new(
13355 "jacobian: function must return array or numeric",
13356 ))
13357 }
13358 }
13359 }
13360
13361 Ok(Value::Array(Rc::new(RefCell::new(jacobian))))
13362 });
13363
13364 define(interp, "hessian", Some(2), |interp, args| {
13366 let func = match &args[0] {
13367 Value::Function(f) => f.clone(),
13368 _ => {
13369 return Err(RuntimeError::new(
13370 "hessian: first argument must be a function",
13371 ))
13372 }
13373 };
13374 let x = match &args[1] {
13375 Value::Array(arr) => arr.borrow().clone(),
13376 _ => return Err(RuntimeError::new("hessian: second argument must be array")),
13377 };
13378
13379 let h = 1e-5; let n = x.len();
13381
13382 let mut hessian: Vec<Value> = Vec::with_capacity(n * n);
13383
13384 for i in 0..n {
13385 for j in 0..n {
13386 let xi = match &x[i] {
13387 Value::Float(f) => *f,
13388 Value::Int(k) => *k as f64,
13389 _ => continue,
13390 };
13391 let xj = match &x[j] {
13392 Value::Float(f) => *f,
13393 Value::Int(k) => *k as f64,
13394 _ => continue,
13395 };
13396
13397 let mut x_pp = x.clone();
13399 let mut x_pm = x.clone();
13400 let mut x_mp = x.clone();
13401 let mut x_mm = x.clone();
13402
13403 x_pp[i] = Value::Float(xi + h);
13404 x_pp[j] = Value::Float(if i == j { xi + 2.0 * h } else { xj + h });
13405 x_pm[i] = Value::Float(xi + h);
13406 x_pm[j] = Value::Float(if i == j { xi } else { xj - h });
13407 x_mp[i] = Value::Float(xi - h);
13408 x_mp[j] = Value::Float(if i == j { xi } else { xj + h });
13409 x_mm[i] = Value::Float(xi - h);
13410 x_mm[j] = Value::Float(if i == j { xi - 2.0 * h } else { xj - h });
13411
13412 let f_pp =
13413 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pp)))])?;
13414 let f_pm =
13415 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pm)))])?;
13416 let f_mp =
13417 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mp)))])?;
13418 let f_mm =
13419 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mm)))])?;
13420
13421 let d2f = match (f_pp, f_pm, f_mp, f_mm) {
13422 (
13423 Value::Float(fpp),
13424 Value::Float(fpm),
13425 Value::Float(fmp),
13426 Value::Float(fmm),
13427 ) => (fpp - fpm - fmp + fmm) / (4.0 * h * h),
13428 _ => 0.0,
13429 };
13430
13431 hessian.push(Value::Float(d2f));
13432 }
13433 }
13434
13435 Ok(Value::Array(Rc::new(RefCell::new(hessian))))
13436 });
13437
13438 define(interp, "divergence", Some(2), |interp, args| {
13440 let func = match &args[0] {
13441 Value::Function(f) => f.clone(),
13442 _ => {
13443 return Err(RuntimeError::new(
13444 "divergence: first argument must be a function",
13445 ))
13446 }
13447 };
13448 let x = match &args[1] {
13449 Value::Array(arr) => arr.borrow().clone(),
13450 _ => {
13451 return Err(RuntimeError::new(
13452 "divergence: second argument must be array",
13453 ))
13454 }
13455 };
13456
13457 let h = 1e-7;
13458 let mut div = 0.0f64;
13459
13460 for (i, xi) in x.iter().enumerate() {
13461 let xi_val = match xi {
13462 Value::Float(f) => *f,
13463 Value::Int(n) => *n as f64,
13464 _ => continue,
13465 };
13466
13467 let mut x_plus = x.clone();
13468 let mut x_minus = x.clone();
13469 x_plus[i] = Value::Float(xi_val + h);
13470 x_minus[i] = Value::Float(xi_val - h);
13471
13472 let f_plus =
13473 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13474 let f_minus =
13475 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13476
13477 let df_i = match (&f_plus, &f_minus) {
13479 (Value::Array(fp), Value::Array(fm)) => {
13480 let fp = fp.borrow();
13481 let fm = fm.borrow();
13482 if i < fp.len() && i < fm.len() {
13483 match (&fp[i], &fm[i]) {
13484 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
13485 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
13486 _ => 0.0,
13487 }
13488 } else {
13489 0.0
13490 }
13491 }
13492 _ => 0.0,
13493 };
13494
13495 div += df_i;
13496 }
13497
13498 Ok(Value::Float(div))
13499 });
13500}
13501
13502fn register_spatial(interp: &mut Interpreter) {
13508 define(interp, "spatial_hash_new", Some(1), |_, args| {
13510 let cell_size = match &args[0] {
13511 Value::Float(f) => *f,
13512 Value::Int(n) => *n as f64,
13513 _ => {
13514 return Err(RuntimeError::new(
13515 "spatial_hash_new: cell_size must be numeric",
13516 ))
13517 }
13518 };
13519
13520 let mut config = HashMap::new();
13521 config.insert("cell_size".to_string(), Value::Float(cell_size));
13522 config.insert(
13523 "buckets".to_string(),
13524 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
13525 );
13526
13527 Ok(Value::Map(Rc::new(RefCell::new(config))))
13528 });
13529
13530 define(interp, "spatial_hash_insert", Some(3), |_, args| {
13532 let hash = match &args[0] {
13533 Value::Map(map) => map.clone(),
13534 _ => {
13535 return Err(RuntimeError::new(
13536 "spatial_hash_insert: first argument must be spatial hash",
13537 ))
13538 }
13539 };
13540 let id = args[1].clone();
13541 let pos = match &args[2] {
13542 Value::Array(arr) => arr.borrow().clone(),
13543 _ => {
13544 return Err(RuntimeError::new(
13545 "spatial_hash_insert: position must be array",
13546 ))
13547 }
13548 };
13549
13550 let cell_size = {
13551 let h = hash.borrow();
13552 match h.get("cell_size") {
13553 Some(Value::Float(f)) => *f,
13554 _ => 1.0,
13555 }
13556 };
13557
13558 let key = pos
13560 .iter()
13561 .filter_map(|v| match v {
13562 Value::Float(f) => Some((*f / cell_size).floor() as i64),
13563 Value::Int(n) => Some(*n / (cell_size as i64)),
13564 _ => None,
13565 })
13566 .map(|n| n.to_string())
13567 .collect::<Vec<_>>()
13568 .join(",");
13569
13570 {
13572 let mut h = hash.borrow_mut();
13573 let buckets = h
13574 .entry("buckets".to_string())
13575 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
13576
13577 if let Value::Map(buckets_map) = buckets {
13578 let mut bm = buckets_map.borrow_mut();
13579 let bucket = bm
13580 .entry(key)
13581 .or_insert_with(|| Value::Array(Rc::new(RefCell::new(vec![]))));
13582
13583 if let Value::Array(arr) = bucket {
13584 arr.borrow_mut().push(id);
13585 }
13586 }
13587 }
13588
13589 Ok(Value::Map(hash))
13590 });
13591
13592 define(interp, "spatial_hash_query", Some(3), |_, args| {
13594 let hash = match &args[0] {
13595 Value::Map(map) => map.borrow().clone(),
13596 _ => {
13597 return Err(RuntimeError::new(
13598 "spatial_hash_query: first argument must be spatial hash",
13599 ))
13600 }
13601 };
13602 let pos = match &args[1] {
13603 Value::Array(arr) => arr.borrow().clone(),
13604 _ => {
13605 return Err(RuntimeError::new(
13606 "spatial_hash_query: position must be array",
13607 ))
13608 }
13609 };
13610 let radius = match &args[2] {
13611 Value::Float(f) => *f,
13612 Value::Int(n) => *n as f64,
13613 _ => {
13614 return Err(RuntimeError::new(
13615 "spatial_hash_query: radius must be numeric",
13616 ))
13617 }
13618 };
13619
13620 let cell_size = match hash.get("cell_size") {
13621 Some(Value::Float(f)) => *f,
13622 _ => 1.0,
13623 };
13624
13625 let center: Vec<i64> = pos
13627 .iter()
13628 .filter_map(|v| match v {
13629 Value::Float(f) => Some((*f / cell_size).floor() as i64),
13630 Value::Int(n) => Some(*n / (cell_size as i64)),
13631 _ => None,
13632 })
13633 .collect();
13634
13635 let cells_to_check = (radius / cell_size).ceil() as i64;
13637
13638 let mut results: Vec<Value> = Vec::new();
13639
13640 if let Some(Value::Map(buckets)) = hash.get("buckets") {
13641 let buckets = buckets.borrow();
13642
13643 if center.len() >= 2 {
13645 for dx in -cells_to_check..=cells_to_check {
13646 for dy in -cells_to_check..=cells_to_check {
13647 let key = format!("{},{}", center[0] + dx, center[1] + dy);
13648 if let Some(Value::Array(bucket)) = buckets.get(&key) {
13649 for item in bucket.borrow().iter() {
13650 results.push(item.clone());
13653 }
13654 }
13655 }
13656 }
13657 }
13658 }
13659
13660 Ok(Value::Array(Rc::new(RefCell::new(results))))
13661 });
13662
13663 define(interp, "aabb_new", Some(2), |_, args| {
13665 let min = match &args[0] {
13666 Value::Array(arr) => arr.borrow().clone(),
13667 _ => return Err(RuntimeError::new("aabb_new: min must be array")),
13668 };
13669 let max = match &args[1] {
13670 Value::Array(arr) => arr.borrow().clone(),
13671 _ => return Err(RuntimeError::new("aabb_new: max must be array")),
13672 };
13673
13674 let mut aabb = HashMap::new();
13675 aabb.insert("min".to_string(), Value::Array(Rc::new(RefCell::new(min))));
13676 aabb.insert("max".to_string(), Value::Array(Rc::new(RefCell::new(max))));
13677
13678 Ok(Value::Map(Rc::new(RefCell::new(aabb))))
13679 });
13680
13681 define(interp, "aabb_intersects", Some(2), |_, args| {
13683 let a = match &args[0] {
13684 Value::Map(map) => map.borrow().clone(),
13685 _ => {
13686 return Err(RuntimeError::new(
13687 "aabb_intersects: arguments must be AABBs",
13688 ))
13689 }
13690 };
13691 let b = match &args[1] {
13692 Value::Map(map) => map.borrow().clone(),
13693 _ => {
13694 return Err(RuntimeError::new(
13695 "aabb_intersects: arguments must be AABBs",
13696 ))
13697 }
13698 };
13699
13700 let a_min = extract_vec_from_map(&a, "min")?;
13701 let a_max = extract_vec_from_map(&a, "max")?;
13702 let b_min = extract_vec_from_map(&b, "min")?;
13703 let b_max = extract_vec_from_map(&b, "max")?;
13704
13705 for i in 0..a_min
13707 .len()
13708 .min(a_max.len())
13709 .min(b_min.len())
13710 .min(b_max.len())
13711 {
13712 if a_max[i] < b_min[i] || b_max[i] < a_min[i] {
13713 return Ok(Value::Bool(false));
13714 }
13715 }
13716
13717 Ok(Value::Bool(true))
13718 });
13719
13720 define(interp, "aabb_contains", Some(2), |_, args| {
13722 let aabb = match &args[0] {
13723 Value::Map(map) => map.borrow().clone(),
13724 _ => {
13725 return Err(RuntimeError::new(
13726 "aabb_contains: first argument must be AABB",
13727 ))
13728 }
13729 };
13730 let point = match &args[1] {
13731 Value::Array(arr) => arr.borrow().clone(),
13732 _ => {
13733 return Err(RuntimeError::new(
13734 "aabb_contains: second argument must be point array",
13735 ))
13736 }
13737 };
13738
13739 let min = extract_vec_from_map(&aabb, "min")?;
13740 let max = extract_vec_from_map(&aabb, "max")?;
13741
13742 for (i, p) in point.iter().enumerate() {
13743 let p_val = match p {
13744 Value::Float(f) => *f,
13745 Value::Int(n) => *n as f64,
13746 _ => continue,
13747 };
13748
13749 if i < min.len() && p_val < min[i] {
13750 return Ok(Value::Bool(false));
13751 }
13752 if i < max.len() && p_val > max[i] {
13753 return Ok(Value::Bool(false));
13754 }
13755 }
13756
13757 Ok(Value::Bool(true))
13758 });
13759}
13760
13761fn extract_vec_from_map(map: &HashMap<String, Value>, key: &str) -> Result<Vec<f64>, RuntimeError> {
13763 match map.get(key) {
13764 Some(Value::Array(arr)) => arr
13765 .borrow()
13766 .iter()
13767 .map(|v| match v {
13768 Value::Float(f) => Ok(*f),
13769 Value::Int(n) => Ok(*n as f64),
13770 _ => Err(RuntimeError::new("Expected numeric value")),
13771 })
13772 .collect(),
13773 _ => Err(RuntimeError::new(format!(
13774 "Missing or invalid '{}' in AABB",
13775 key
13776 ))),
13777 }
13778}
13779
13780fn register_physics(interp: &mut Interpreter) {
13786 define(interp, "verlet_integrate", Some(4), |_, args| {
13789 let pos = extract_vec3(&args[0], "verlet_integrate")?;
13790 let prev = extract_vec3(&args[1], "verlet_integrate")?;
13791 let accel = extract_vec3(&args[2], "verlet_integrate")?;
13792 let dt = match &args[3] {
13793 Value::Float(f) => *f,
13794 Value::Int(n) => *n as f64,
13795 _ => return Err(RuntimeError::new("verlet_integrate: dt must be numeric")),
13796 };
13797
13798 let dt2 = dt * dt;
13799 let new_pos = [
13800 pos[0] + (pos[0] - prev[0]) + accel[0] * dt2,
13801 pos[1] + (pos[1] - prev[1]) + accel[1] * dt2,
13802 pos[2] + (pos[2] - prev[2]) + accel[2] * dt2,
13803 ];
13804
13805 Ok(make_vec3_arr(new_pos))
13806 });
13807
13808 define(interp, "spring_force", Some(4), |_, args| {
13810 let p1 = extract_vec3(&args[0], "spring_force")?;
13811 let p2 = extract_vec3(&args[1], "spring_force")?;
13812 let rest_length = match &args[2] {
13813 Value::Float(f) => *f,
13814 Value::Int(n) => *n as f64,
13815 _ => {
13816 return Err(RuntimeError::new(
13817 "spring_force: rest_length must be numeric",
13818 ))
13819 }
13820 };
13821 let stiffness = match &args[3] {
13822 Value::Float(f) => *f,
13823 Value::Int(n) => *n as f64,
13824 _ => return Err(RuntimeError::new("spring_force: stiffness must be numeric")),
13825 };
13826
13827 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
13828 let length = (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]).sqrt();
13829
13830 if length < 1e-10 {
13831 return Ok(make_vec3_arr([0.0, 0.0, 0.0]));
13832 }
13833
13834 let displacement = length - rest_length;
13835 let force_mag = stiffness * displacement;
13836 let normalized = [delta[0] / length, delta[1] / length, delta[2] / length];
13837
13838 Ok(make_vec3_arr([
13839 normalized[0] * force_mag,
13840 normalized[1] * force_mag,
13841 normalized[2] * force_mag,
13842 ]))
13843 });
13844
13845 define(interp, "distance_constraint", Some(3), |_, args| {
13848 let p1 = extract_vec3(&args[0], "distance_constraint")?;
13849 let p2 = extract_vec3(&args[1], "distance_constraint")?;
13850 let target = match &args[2] {
13851 Value::Float(f) => *f,
13852 Value::Int(n) => *n as f64,
13853 _ => {
13854 return Err(RuntimeError::new(
13855 "distance_constraint: target must be numeric",
13856 ))
13857 }
13858 };
13859
13860 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
13861 let length = (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]).sqrt();
13862
13863 if length < 1e-10 {
13864 return Ok(Value::Tuple(Rc::new(vec![
13865 make_vec3_arr(p1),
13866 make_vec3_arr(p2),
13867 ])));
13868 }
13869
13870 let correction = (length - target) / length * 0.5;
13871 let corr_vec = [
13872 delta[0] * correction,
13873 delta[1] * correction,
13874 delta[2] * correction,
13875 ];
13876
13877 let new_p1 = [
13878 p1[0] + corr_vec[0],
13879 p1[1] + corr_vec[1],
13880 p1[2] + corr_vec[2],
13881 ];
13882 let new_p2 = [
13883 p2[0] - corr_vec[0],
13884 p2[1] - corr_vec[1],
13885 p2[2] - corr_vec[2],
13886 ];
13887
13888 Ok(Value::Tuple(Rc::new(vec![
13889 make_vec3_arr(new_p1),
13890 make_vec3_arr(new_p2),
13891 ])))
13892 });
13893
13894 define(interp, "solve_constraints", Some(3), |_, args| {
13897 let mut points = match &args[0] {
13898 Value::Array(arr) => arr.borrow().clone(),
13899 _ => {
13900 return Err(RuntimeError::new(
13901 "solve_constraints: first argument must be array of points",
13902 ))
13903 }
13904 };
13905 let constraints = match &args[1] {
13906 Value::Array(arr) => arr.borrow().clone(),
13907 _ => {
13908 return Err(RuntimeError::new(
13909 "solve_constraints: second argument must be array of constraints",
13910 ))
13911 }
13912 };
13913 let iterations = match &args[2] {
13914 Value::Int(n) => *n as usize,
13915 _ => {
13916 return Err(RuntimeError::new(
13917 "solve_constraints: iterations must be integer",
13918 ))
13919 }
13920 };
13921
13922 for _ in 0..iterations {
13923 for constraint in &constraints {
13924 match constraint {
13925 Value::Map(c) => {
13926 let c = c.borrow();
13927 let constraint_type = c
13928 .get("type")
13929 .and_then(|v| {
13930 if let Value::String(s) = v {
13931 Some((**s).clone())
13932 } else {
13933 None
13934 }
13935 })
13936 .unwrap_or_default();
13937
13938 match constraint_type.as_str() {
13939 "distance" => {
13940 let indices = match c.get("indices") {
13941 Some(Value::Array(arr)) => arr.borrow().clone(),
13942 _ => continue,
13943 };
13944 let target = match c.get("distance") {
13945 Some(Value::Float(f)) => *f,
13946 Some(Value::Int(n)) => *n as f64,
13947 _ => continue,
13948 };
13949
13950 if indices.len() >= 2 {
13951 let i1 = match &indices[0] {
13952 Value::Int(n) => *n as usize,
13953 _ => continue,
13954 };
13955 let i2 = match &indices[1] {
13956 Value::Int(n) => *n as usize,
13957 _ => continue,
13958 };
13959
13960 if i1 < points.len() && i2 < points.len() {
13961 let p1 = extract_vec3(&points[i1], "solve")?;
13963 let p2 = extract_vec3(&points[i2], "solve")?;
13964
13965 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
13966 let length = (delta[0] * delta[0]
13967 + delta[1] * delta[1]
13968 + delta[2] * delta[2])
13969 .sqrt();
13970
13971 if length > 1e-10 {
13972 let correction = (length - target) / length * 0.5;
13973 let corr_vec = [
13974 delta[0] * correction,
13975 delta[1] * correction,
13976 delta[2] * correction,
13977 ];
13978
13979 points[i1] = make_vec3_arr([
13980 p1[0] + corr_vec[0],
13981 p1[1] + corr_vec[1],
13982 p1[2] + corr_vec[2],
13983 ]);
13984 points[i2] = make_vec3_arr([
13985 p2[0] - corr_vec[0],
13986 p2[1] - corr_vec[1],
13987 p2[2] - corr_vec[2],
13988 ]);
13989 }
13990 }
13991 }
13992 }
13993 _ => {}
13994 }
13995 }
13996 _ => continue,
13997 }
13998 }
13999 }
14000
14001 Ok(Value::Array(Rc::new(RefCell::new(points))))
14002 });
14003
14004 define(interp, "ray_sphere_intersect", Some(4), |_, args| {
14007 let origin = extract_vec3(&args[0], "ray_sphere_intersect")?;
14008 let dir = extract_vec3(&args[1], "ray_sphere_intersect")?;
14009 let center = extract_vec3(&args[2], "ray_sphere_intersect")?;
14010 let radius = match &args[3] {
14011 Value::Float(f) => *f,
14012 Value::Int(n) => *n as f64,
14013 _ => {
14014 return Err(RuntimeError::new(
14015 "ray_sphere_intersect: radius must be numeric",
14016 ))
14017 }
14018 };
14019
14020 let oc = [
14021 origin[0] - center[0],
14022 origin[1] - center[1],
14023 origin[2] - center[2],
14024 ];
14025
14026 let a = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2];
14027 let b = 2.0 * (oc[0] * dir[0] + oc[1] * dir[1] + oc[2] * dir[2]);
14028 let c = oc[0] * oc[0] + oc[1] * oc[1] + oc[2] * oc[2] - radius * radius;
14029
14030 let discriminant = b * b - 4.0 * a * c;
14031
14032 if discriminant < 0.0 {
14033 Ok(Value::Float(-1.0))
14034 } else {
14035 let t = (-b - discriminant.sqrt()) / (2.0 * a);
14036 if t > 0.0 {
14037 Ok(Value::Float(t))
14038 } else {
14039 let t2 = (-b + discriminant.sqrt()) / (2.0 * a);
14040 if t2 > 0.0 {
14041 Ok(Value::Float(t2))
14042 } else {
14043 Ok(Value::Float(-1.0))
14044 }
14045 }
14046 }
14047 });
14048
14049 define(interp, "ray_plane_intersect", Some(4), |_, args| {
14051 let origin = extract_vec3(&args[0], "ray_plane_intersect")?;
14052 let dir = extract_vec3(&args[1], "ray_plane_intersect")?;
14053 let plane_pt = extract_vec3(&args[2], "ray_plane_intersect")?;
14054 let normal = extract_vec3(&args[3], "ray_plane_intersect")?;
14055
14056 let denom = dir[0] * normal[0] + dir[1] * normal[1] + dir[2] * normal[2];
14057
14058 if denom.abs() < 1e-10 {
14059 return Ok(Value::Float(-1.0)); }
14061
14062 let diff = [
14063 plane_pt[0] - origin[0],
14064 plane_pt[1] - origin[1],
14065 plane_pt[2] - origin[2],
14066 ];
14067 let t = (diff[0] * normal[0] + diff[1] * normal[1] + diff[2] * normal[2]) / denom;
14068
14069 if t > 0.0 {
14070 Ok(Value::Float(t))
14071 } else {
14072 Ok(Value::Float(-1.0))
14073 }
14074 });
14075}
14076
14077fn register_geometric_algebra(interp: &mut Interpreter) {
14139 fn make_multivector(components: [f64; 8]) -> Value {
14141 let mut mv = HashMap::new();
14142 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(
14151 "_type".to_string(),
14152 Value::String(Rc::new("multivector".to_string())),
14153 );
14154 Value::Map(Rc::new(RefCell::new(mv)))
14155 }
14156
14157 fn extract_multivector(v: &Value, fn_name: &str) -> Result<[f64; 8], RuntimeError> {
14158 match v {
14159 Value::Map(map) => {
14160 let map = map.borrow();
14161 let get_component = |key: &str| -> f64 {
14162 match map.get(key) {
14163 Some(Value::Float(f)) => *f,
14164 Some(Value::Int(n)) => *n as f64,
14165 _ => 0.0,
14166 }
14167 };
14168 Ok([
14169 get_component("s"),
14170 get_component("e1"),
14171 get_component("e2"),
14172 get_component("e3"),
14173 get_component("e12"),
14174 get_component("e23"),
14175 get_component("e31"),
14176 get_component("e123"),
14177 ])
14178 }
14179 _ => Err(RuntimeError::new(format!(
14180 "{}: expected multivector",
14181 fn_name
14182 ))),
14183 }
14184 }
14185
14186 define(interp, "mv_new", Some(8), |_, args| {
14188 let mut components = [0.0f64; 8];
14189 for (i, arg) in args.iter().enumerate().take(8) {
14190 components[i] = match arg {
14191 Value::Float(f) => *f,
14192 Value::Int(n) => *n as f64,
14193 _ => 0.0,
14194 };
14195 }
14196 Ok(make_multivector(components))
14197 });
14198
14199 define(interp, "mv_scalar", Some(1), |_, args| {
14201 let s = match &args[0] {
14202 Value::Float(f) => *f,
14203 Value::Int(n) => *n as f64,
14204 _ => return Err(RuntimeError::new("mv_scalar: expected number")),
14205 };
14206 Ok(make_multivector([s, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))
14207 });
14208
14209 define(interp, "mv_vector", Some(3), |_, args| {
14211 let x = match &args[0] {
14212 Value::Float(f) => *f,
14213 Value::Int(n) => *n as f64,
14214 _ => 0.0,
14215 };
14216 let y = match &args[1] {
14217 Value::Float(f) => *f,
14218 Value::Int(n) => *n as f64,
14219 _ => 0.0,
14220 };
14221 let z = match &args[2] {
14222 Value::Float(f) => *f,
14223 Value::Int(n) => *n as f64,
14224 _ => 0.0,
14225 };
14226 Ok(make_multivector([0.0, x, y, z, 0.0, 0.0, 0.0, 0.0]))
14227 });
14228
14229 define(interp, "mv_bivector", Some(3), |_, args| {
14231 let xy = match &args[0] {
14232 Value::Float(f) => *f,
14233 Value::Int(n) => *n as f64,
14234 _ => 0.0,
14235 };
14236 let yz = match &args[1] {
14237 Value::Float(f) => *f,
14238 Value::Int(n) => *n as f64,
14239 _ => 0.0,
14240 };
14241 let zx = match &args[2] {
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, xy, yz, zx, 0.0]))
14247 });
14248
14249 define(interp, "mv_trivector", Some(1), |_, args| {
14251 let xyz = match &args[0] {
14252 Value::Float(f) => *f,
14253 Value::Int(n) => *n as f64,
14254 _ => 0.0,
14255 };
14256 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, xyz]))
14257 });
14258
14259 define(interp, "mv_add", Some(2), |_, args| {
14261 let a = extract_multivector(&args[0], "mv_add")?;
14262 let b = extract_multivector(&args[1], "mv_add")?;
14263 Ok(make_multivector([
14264 a[0] + b[0],
14265 a[1] + b[1],
14266 a[2] + b[2],
14267 a[3] + b[3],
14268 a[4] + b[4],
14269 a[5] + b[5],
14270 a[6] + b[6],
14271 a[7] + b[7],
14272 ]))
14273 });
14274
14275 define(interp, "mv_sub", Some(2), |_, args| {
14277 let a = extract_multivector(&args[0], "mv_sub")?;
14278 let b = extract_multivector(&args[1], "mv_sub")?;
14279 Ok(make_multivector([
14280 a[0] - b[0],
14281 a[1] - b[1],
14282 a[2] - b[2],
14283 a[3] - b[3],
14284 a[4] - b[4],
14285 a[5] - b[5],
14286 a[6] - b[6],
14287 a[7] - b[7],
14288 ]))
14289 });
14290
14291 define(interp, "mv_scale", Some(2), |_, args| {
14293 let a = extract_multivector(&args[0], "mv_scale")?;
14294 let s = match &args[1] {
14295 Value::Float(f) => *f,
14296 Value::Int(n) => *n as f64,
14297 _ => {
14298 return Err(RuntimeError::new(
14299 "mv_scale: second argument must be number",
14300 ))
14301 }
14302 };
14303 Ok(make_multivector([
14304 a[0] * s,
14305 a[1] * s,
14306 a[2] * s,
14307 a[3] * s,
14308 a[4] * s,
14309 a[5] * s,
14310 a[6] * s,
14311 a[7] * s,
14312 ]))
14313 });
14314
14315 define(interp, "mv_geometric", Some(2), |_, args| {
14318 let a = extract_multivector(&args[0], "mv_geometric")?;
14319 let b = extract_multivector(&args[1], "mv_geometric")?;
14320
14321 let mut r = [0.0f64; 8];
14324
14325 r[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
14327 - a[4] * b[4]
14328 - a[5] * b[5]
14329 - a[6] * b[6]
14330 - a[7] * b[7];
14331
14332 r[1] = a[0] * b[1] + a[1] * b[0] - a[2] * b[4] + a[3] * b[6] + a[4] * b[2]
14334 - a[5] * b[7]
14335 - a[6] * b[3]
14336 - a[7] * b[5];
14337
14338 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]
14340 - a[6] * b[7]
14341 - a[7] * b[6];
14342
14343 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]
14345 + a[6] * b[1]
14346 - a[7] * b[4];
14347
14348 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]
14350 - a[6] * b[5]
14351 + a[7] * b[3];
14352
14353 r[5] = a[0] * b[5] + a[1] * b[7] + a[2] * b[3] - a[3] * b[2] - a[4] * b[6]
14355 + a[5] * b[0]
14356 + a[6] * b[4]
14357 + a[7] * b[1];
14358
14359 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]
14361 + a[6] * b[0]
14362 + a[7] * b[2];
14363
14364 r[7] = a[0] * b[7]
14366 + a[1] * b[5]
14367 + a[2] * b[6]
14368 + a[3] * b[4]
14369 + a[4] * b[3]
14370 + a[5] * b[1]
14371 + a[6] * b[2]
14372 + a[7] * b[0];
14373
14374 Ok(make_multivector(r))
14375 });
14376
14377 define(interp, "mv_wedge", Some(2), |_, args| {
14380 let a = extract_multivector(&args[0], "mv_wedge")?;
14381 let b = extract_multivector(&args[1], "mv_wedge")?;
14382
14383 let mut r = [0.0f64; 8];
14384
14385 r[0] = a[0] * b[0];
14387
14388 r[1] = a[0] * b[1] + a[1] * b[0];
14390 r[2] = a[0] * b[2] + a[2] * b[0];
14391 r[3] = a[0] * b[3] + a[3] * b[0];
14392
14393 r[4] = a[0] * b[4] + a[1] * b[2] - a[2] * b[1] + a[4] * b[0];
14395 r[5] = a[0] * b[5] + a[2] * b[3] - a[3] * b[2] + a[5] * b[0];
14396 r[6] = a[0] * b[6] + a[3] * b[1] - a[1] * b[3] + a[6] * b[0];
14397
14398 r[7] = a[0] * b[7] + a[7] * b[0] + a[1] * b[5] + a[2] * b[6] + a[3] * b[4]
14400 - a[4] * b[3]
14401 - a[5] * b[1]
14402 - a[6] * b[2];
14403
14404 Ok(make_multivector(r))
14405 });
14406
14407 define(interp, "mv_inner", Some(2), |_, args| {
14410 let a = extract_multivector(&args[0], "mv_inner")?;
14411 let b = extract_multivector(&args[1], "mv_inner")?;
14412
14413 let mut r = [0.0f64; 8];
14414
14415 r[0] = a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
14418 - a[4] * b[4]
14419 - a[5] * b[5]
14420 - a[6] * b[6]
14421 - a[7] * b[7];
14422
14423 r[1] = a[4] * b[2] - a[6] * b[3] - a[5] * b[7];
14425 r[2] = -a[4] * b[1] + a[5] * b[3] - a[6] * b[7];
14426 r[3] = a[6] * b[1] - a[5] * b[2] - a[4] * b[7];
14427
14428 r[4] = a[7] * b[3];
14430 r[5] = a[7] * b[1];
14431 r[6] = a[7] * b[2];
14432
14433 Ok(make_multivector(r))
14434 });
14435
14436 define(interp, "mv_reverse", Some(1), |_, args| {
14439 let a = extract_multivector(&args[0], "mv_reverse")?;
14440 Ok(make_multivector([
14442 a[0], a[1], a[2], a[3], -a[4], -a[5], -a[6], -a[7],
14443 ]))
14444 });
14445
14446 define(interp, "mv_dual", Some(1), |_, args| {
14449 let a = extract_multivector(&args[0], "mv_dual")?;
14450 Ok(make_multivector([
14453 -a[7], -a[5], -a[6], -a[4], a[3], a[1], a[2], a[0], ]))
14462 });
14463
14464 define(interp, "mv_magnitude", Some(1), |_, args| {
14466 let a = extract_multivector(&args[0], "mv_magnitude")?;
14467 let mag_sq = a[0] * a[0]
14468 + a[1] * a[1]
14469 + a[2] * a[2]
14470 + a[3] * a[3]
14471 + a[4] * a[4]
14472 + a[5] * a[5]
14473 + a[6] * a[6]
14474 + a[7] * a[7];
14475 Ok(Value::Float(mag_sq.sqrt()))
14476 });
14477
14478 define(interp, "mv_normalize", Some(1), |_, args| {
14480 let a = extract_multivector(&args[0], "mv_normalize")?;
14481 let mag = (a[0] * a[0]
14482 + a[1] * a[1]
14483 + a[2] * a[2]
14484 + a[3] * a[3]
14485 + a[4] * a[4]
14486 + a[5] * a[5]
14487 + a[6] * a[6]
14488 + a[7] * a[7])
14489 .sqrt();
14490 if mag < 1e-10 {
14491 return Ok(make_multivector([0.0; 8]));
14492 }
14493 Ok(make_multivector([
14494 a[0] / mag,
14495 a[1] / mag,
14496 a[2] / mag,
14497 a[3] / mag,
14498 a[4] / mag,
14499 a[5] / mag,
14500 a[6] / mag,
14501 a[7] / mag,
14502 ]))
14503 });
14504
14505 define(interp, "rotor_from_axis_angle", Some(2), |_, args| {
14508 let axis = extract_vec3(&args[0], "rotor_from_axis_angle")?;
14509 let angle = match &args[1] {
14510 Value::Float(f) => *f,
14511 Value::Int(n) => *n as f64,
14512 _ => {
14513 return Err(RuntimeError::new(
14514 "rotor_from_axis_angle: angle must be number",
14515 ))
14516 }
14517 };
14518
14519 let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
14521 if len < 1e-10 {
14522 return Ok(make_multivector([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]));
14524 }
14525 let (nx, ny, nz) = (axis[0] / len, axis[1] / len, axis[2] / len);
14526
14527 let half_angle = angle / 2.0;
14528 let (s, c) = half_angle.sin_cos();
14529
14530 Ok(make_multivector([
14533 c, 0.0,
14535 0.0,
14536 0.0, -s * nz, -s * nx, -s * ny, 0.0, ]))
14542 });
14543
14544 define(interp, "rotor_apply", Some(2), |_, args| {
14547 let r = extract_multivector(&args[0], "rotor_apply")?;
14548 let v = extract_vec3(&args[1], "rotor_apply")?;
14549
14550 let v_mv = [0.0, v[0], v[1], v[2], 0.0, 0.0, 0.0, 0.0];
14552
14553 let r_rev = [r[0], r[1], r[2], r[3], -r[4], -r[5], -r[6], -r[7]];
14555
14556 let mut rv = [0.0f64; 8];
14558 rv[0] = r[0] * v_mv[0] + r[1] * v_mv[1] + r[2] * v_mv[2] + r[3] * v_mv[3]
14559 - r[4] * v_mv[4]
14560 - r[5] * v_mv[5]
14561 - r[6] * v_mv[6]
14562 - r[7] * v_mv[7];
14563 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]
14564 - r[5] * v_mv[7]
14565 - r[6] * v_mv[3]
14566 - r[7] * v_mv[5];
14567 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]
14568 + r[5] * v_mv[3]
14569 - r[6] * v_mv[7]
14570 - r[7] * v_mv[6];
14571 rv[3] = r[0] * v_mv[3] - r[1] * v_mv[6] + r[2] * v_mv[5] + r[3] * v_mv[0]
14572 - r[4] * v_mv[7]
14573 - r[5] * v_mv[2]
14574 + r[6] * v_mv[1]
14575 - r[7] * v_mv[4];
14576 rv[4] = r[0] * v_mv[4] + r[1] * v_mv[2] - r[2] * v_mv[1]
14577 + r[3] * v_mv[7]
14578 + r[4] * v_mv[0]
14579 + r[5] * v_mv[6]
14580 - r[6] * v_mv[5]
14581 + r[7] * v_mv[3];
14582 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]
14583 + r[5] * v_mv[0]
14584 + r[6] * v_mv[4]
14585 + r[7] * v_mv[1];
14586 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]
14587 - r[5] * v_mv[4]
14588 + r[6] * v_mv[0]
14589 + r[7] * v_mv[2];
14590 rv[7] = r[0] * v_mv[7]
14591 + r[1] * v_mv[5]
14592 + r[2] * v_mv[6]
14593 + r[3] * v_mv[4]
14594 + r[4] * v_mv[3]
14595 + r[5] * v_mv[1]
14596 + r[6] * v_mv[2]
14597 + r[7] * v_mv[0];
14598
14599 let mut result = [0.0f64; 8];
14601 result[1] = rv[0] * r_rev[1] + rv[1] * r_rev[0] - rv[2] * r_rev[4]
14602 + rv[3] * r_rev[6]
14603 + rv[4] * r_rev[2]
14604 - rv[5] * r_rev[7]
14605 - rv[6] * r_rev[3]
14606 - rv[7] * r_rev[5];
14607 result[2] = rv[0] * r_rev[2] + rv[1] * r_rev[4] + rv[2] * r_rev[0]
14608 - rv[3] * r_rev[5]
14609 - rv[4] * r_rev[1]
14610 + rv[5] * r_rev[3]
14611 - rv[6] * r_rev[7]
14612 - rv[7] * r_rev[6];
14613 result[3] = rv[0] * r_rev[3] - rv[1] * r_rev[6] + rv[2] * r_rev[5] + rv[3] * r_rev[0]
14614 - rv[4] * r_rev[7]
14615 - rv[5] * r_rev[2]
14616 + rv[6] * r_rev[1]
14617 - rv[7] * r_rev[4];
14618
14619 Ok(make_vec3(result[1], result[2], result[3]))
14621 });
14622
14623 define(interp, "rotor_compose", Some(2), |_, args| {
14625 let a = extract_multivector(&args[0], "rotor_compose")?;
14626 let b = extract_multivector(&args[1], "rotor_compose")?;
14627
14628 let mut r = [0.0f64; 8];
14630 r[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
14631 - a[4] * b[4]
14632 - a[5] * b[5]
14633 - a[6] * b[6]
14634 - a[7] * b[7];
14635 r[1] = a[0] * b[1] + a[1] * b[0] - a[2] * b[4] + a[3] * b[6] + a[4] * b[2]
14636 - a[5] * b[7]
14637 - a[6] * b[3]
14638 - a[7] * b[5];
14639 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]
14640 - a[6] * b[7]
14641 - a[7] * b[6];
14642 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]
14643 + a[6] * b[1]
14644 - a[7] * b[4];
14645 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]
14646 - a[6] * b[5]
14647 + a[7] * b[3];
14648 r[5] = a[0] * b[5] + a[1] * b[7] + a[2] * b[3] - a[3] * b[2] - a[4] * b[6]
14649 + a[5] * b[0]
14650 + a[6] * b[4]
14651 + a[7] * b[1];
14652 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]
14653 + a[6] * b[0]
14654 + a[7] * b[2];
14655 r[7] = a[0] * b[7]
14656 + a[1] * b[5]
14657 + a[2] * b[6]
14658 + a[3] * b[4]
14659 + a[4] * b[3]
14660 + a[5] * b[1]
14661 + a[6] * b[2]
14662 + a[7] * b[0];
14663
14664 Ok(make_multivector(r))
14665 });
14666
14667 define(interp, "mv_reflect", Some(2), |_, args| {
14670 let v = extract_vec3(&args[0], "mv_reflect")?;
14671 let n = extract_vec3(&args[1], "mv_reflect")?;
14672
14673 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
14675 if len < 1e-10 {
14676 return Ok(make_vec3(v[0], v[1], v[2]));
14677 }
14678 let (nx, ny, nz) = (n[0] / len, n[1] / len, n[2] / len);
14679
14680 let dot = v[0] * nx + v[1] * ny + v[2] * nz;
14682 Ok(make_vec3(
14683 v[0] - 2.0 * dot * nx,
14684 v[1] - 2.0 * dot * ny,
14685 v[2] - 2.0 * dot * nz,
14686 ))
14687 });
14688
14689 define(interp, "mv_project", Some(2), |_, args| {
14691 let v = extract_vec3(&args[0], "mv_project")?;
14692 let n = extract_vec3(&args[1], "mv_project")?;
14693
14694 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
14695 if len < 1e-10 {
14696 return Ok(make_vec3(v[0], v[1], v[2]));
14697 }
14698 let (nx, ny, nz) = (n[0] / len, n[1] / len, n[2] / len);
14699
14700 let dot = v[0] * nx + v[1] * ny + v[2] * nz;
14702 Ok(make_vec3(v[0] - dot * nx, v[1] - dot * ny, v[2] - dot * nz))
14703 });
14704
14705 define(interp, "mv_grade", Some(2), |_, args| {
14707 let a = extract_multivector(&args[0], "mv_grade")?;
14708 let k = match &args[1] {
14709 Value::Int(n) => *n,
14710 _ => return Err(RuntimeError::new("mv_grade: grade must be integer")),
14711 };
14712
14713 match k {
14714 0 => Ok(make_multivector([a[0], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])),
14715 1 => Ok(make_multivector([
14716 0.0, a[1], a[2], a[3], 0.0, 0.0, 0.0, 0.0,
14717 ])),
14718 2 => Ok(make_multivector([
14719 0.0, 0.0, 0.0, 0.0, a[4], a[5], a[6], 0.0,
14720 ])),
14721 3 => Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, a[7]])),
14722 _ => Ok(make_multivector([0.0; 8])),
14723 }
14724 });
14725}
14726
14727fn register_dimensional(interp: &mut Interpreter) {
14734 fn make_quantity(value: f64, units: [i32; 7]) -> Value {
14737 let mut q = HashMap::new();
14738 q.insert("value".to_string(), Value::Float(value));
14739 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(
14747 "_type".to_string(),
14748 Value::String(Rc::new("quantity".to_string())),
14749 );
14750 Value::Map(Rc::new(RefCell::new(q)))
14751 }
14752
14753 fn extract_quantity(v: &Value, fn_name: &str) -> Result<(f64, [i32; 7]), RuntimeError> {
14754 match v {
14755 Value::Map(map) => {
14756 let map = map.borrow();
14757 let value = match map.get("value") {
14758 Some(Value::Float(f)) => *f,
14759 Some(Value::Int(n)) => *n as f64,
14760 _ => return Err(RuntimeError::new(format!("{}: missing value", fn_name))),
14761 };
14762 let get_exp = |key: &str| -> i32 {
14763 match map.get(key) {
14764 Some(Value::Int(n)) => *n as i32,
14765 _ => 0,
14766 }
14767 };
14768 Ok((
14769 value,
14770 [
14771 get_exp("m"),
14772 get_exp("kg"),
14773 get_exp("s"),
14774 get_exp("A"),
14775 get_exp("K"),
14776 get_exp("mol"),
14777 get_exp("cd"),
14778 ],
14779 ))
14780 }
14781 Value::Float(f) => Ok((*f, [0; 7])),
14782 Value::Int(n) => Ok((*n as f64, [0; 7])),
14783 _ => Err(RuntimeError::new(format!(
14784 "{}: expected quantity or number",
14785 fn_name
14786 ))),
14787 }
14788 }
14789
14790 fn units_to_string(units: [i32; 7]) -> String {
14791 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
14792 let mut parts = Vec::new();
14793 for (i, &exp) in units.iter().enumerate() {
14794 if exp == 1 {
14795 parts.push(names[i].to_string());
14796 } else if exp != 0 {
14797 parts.push(format!("{}^{}", names[i], exp));
14798 }
14799 }
14800 if parts.is_empty() {
14801 "dimensionless".to_string()
14802 } else {
14803 parts.join("·")
14804 }
14805 }
14806
14807 define(interp, "qty", Some(2), |_, args| {
14810 let value = match &args[0] {
14811 Value::Float(f) => *f,
14812 Value::Int(n) => *n as f64,
14813 _ => return Err(RuntimeError::new("qty: first argument must be number")),
14814 };
14815 let unit_str = match &args[1] {
14816 Value::String(s) => s.to_string(),
14817 _ => {
14818 return Err(RuntimeError::new(
14819 "qty: second argument must be unit string",
14820 ))
14821 }
14822 };
14823
14824 let mut units = [0i32; 7];
14826 let in_denominator = unit_str.contains('/');
14829
14830 for part in unit_str.split(|c: char| c == '*' || c == '·' || c == ' ') {
14832 let part = part.trim();
14833 if part.is_empty() {
14834 continue;
14835 }
14836
14837 let (base, exp) = if let Some(idx) = part.find('^') {
14838 let (b, e) = part.split_at(idx);
14839 (b, e[1..].parse::<i32>().unwrap_or(1))
14840 } else if part.contains('/') {
14841 continue;
14843 } else {
14844 (part, 1)
14845 };
14846
14847 let sign = if in_denominator { -1 } else { 1 };
14848 match base {
14849 "m" | "meter" | "meters" => units[0] += exp * sign,
14850 "kg" | "kilogram" | "kilograms" => units[1] += exp * sign,
14851 "s" | "sec" | "second" | "seconds" => units[2] += exp * sign,
14852 "A" | "amp" | "ampere" | "amperes" => units[3] += exp * sign,
14853 "K" | "kelvin" => units[4] += exp * sign,
14854 "mol" | "mole" | "moles" => units[5] += exp * sign,
14855 "cd" | "candela" => units[6] += exp * sign,
14856 "N" | "newton" | "newtons" => {
14858 units[1] += sign;
14859 units[0] += sign;
14860 units[2] -= 2 * sign;
14861 }
14862 "J" | "joule" | "joules" => {
14863 units[1] += sign;
14864 units[0] += 2 * sign;
14865 units[2] -= 2 * sign;
14866 }
14867 "W" | "watt" | "watts" => {
14868 units[1] += sign;
14869 units[0] += 2 * sign;
14870 units[2] -= 3 * sign;
14871 }
14872 "Pa" | "pascal" | "pascals" => {
14873 units[1] += sign;
14874 units[0] -= sign;
14875 units[2] -= 2 * sign;
14876 }
14877 "Hz" | "hertz" => {
14878 units[2] -= sign;
14879 }
14880 "C" | "coulomb" | "coulombs" => {
14881 units[3] += sign;
14882 units[2] += sign;
14883 }
14884 "V" | "volt" | "volts" => {
14885 units[1] += sign;
14886 units[0] += 2 * sign;
14887 units[2] -= 3 * sign;
14888 units[3] -= sign;
14889 }
14890 "Ω" | "ohm" | "ohms" => {
14891 units[1] += sign;
14892 units[0] += 2 * sign;
14893 units[2] -= 3 * sign;
14894 units[3] -= 2 * sign;
14895 }
14896 _ => {}
14897 }
14898 }
14899
14900 Ok(make_quantity(value, units))
14901 });
14902
14903 define(interp, "qty_add", Some(2), |_, args| {
14905 let (val_a, units_a) = extract_quantity(&args[0], "qty_add")?;
14906 let (val_b, units_b) = extract_quantity(&args[1], "qty_add")?;
14907
14908 if units_a != units_b {
14909 return Err(RuntimeError::new(format!(
14910 "qty_add: unit mismatch: {} vs {}",
14911 units_to_string(units_a),
14912 units_to_string(units_b)
14913 )));
14914 }
14915
14916 Ok(make_quantity(val_a + val_b, units_a))
14917 });
14918
14919 define(interp, "qty_sub", Some(2), |_, args| {
14921 let (val_a, units_a) = extract_quantity(&args[0], "qty_sub")?;
14922 let (val_b, units_b) = extract_quantity(&args[1], "qty_sub")?;
14923
14924 if units_a != units_b {
14925 return Err(RuntimeError::new(format!(
14926 "qty_sub: unit mismatch: {} vs {}",
14927 units_to_string(units_a),
14928 units_to_string(units_b)
14929 )));
14930 }
14931
14932 Ok(make_quantity(val_a - val_b, units_a))
14933 });
14934
14935 define(interp, "qty_mul", Some(2), |_, args| {
14937 let (val_a, units_a) = extract_quantity(&args[0], "qty_mul")?;
14938 let (val_b, units_b) = extract_quantity(&args[1], "qty_mul")?;
14939
14940 let mut result_units = [0i32; 7];
14941 for i in 0..7 {
14942 result_units[i] = units_a[i] + units_b[i];
14943 }
14944
14945 Ok(make_quantity(val_a * val_b, result_units))
14946 });
14947
14948 define(interp, "qty_div", Some(2), |_, args| {
14950 let (val_a, units_a) = extract_quantity(&args[0], "qty_div")?;
14951 let (val_b, units_b) = extract_quantity(&args[1], "qty_div")?;
14952
14953 if val_b.abs() < 1e-15 {
14954 return Err(RuntimeError::new("qty_div: division by zero"));
14955 }
14956
14957 let mut result_units = [0i32; 7];
14958 for i in 0..7 {
14959 result_units[i] = units_a[i] - units_b[i];
14960 }
14961
14962 Ok(make_quantity(val_a / val_b, result_units))
14963 });
14964
14965 define(interp, "qty_pow", Some(2), |_, args| {
14967 let (val, units) = extract_quantity(&args[0], "qty_pow")?;
14968 let n = match &args[1] {
14969 Value::Int(n) => *n as i32,
14970 Value::Float(f) => *f as i32,
14971 _ => return Err(RuntimeError::new("qty_pow: exponent must be number")),
14972 };
14973
14974 let mut result_units = [0i32; 7];
14975 for i in 0..7 {
14976 result_units[i] = units[i] * n;
14977 }
14978
14979 Ok(make_quantity(val.powi(n), result_units))
14980 });
14981
14982 define(interp, "qty_sqrt", Some(1), |_, args| {
14984 let (val, units) = extract_quantity(&args[0], "qty_sqrt")?;
14985
14986 for (i, &exp) in units.iter().enumerate() {
14988 if exp % 2 != 0 {
14989 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
14990 return Err(RuntimeError::new(format!(
14991 "qty_sqrt: cannot take sqrt of {} (odd exponent on {})",
14992 units_to_string(units),
14993 names[i]
14994 )));
14995 }
14996 }
14997
14998 let mut result_units = [0i32; 7];
14999 for i in 0..7 {
15000 result_units[i] = units[i] / 2;
15001 }
15002
15003 Ok(make_quantity(val.sqrt(), result_units))
15004 });
15005
15006 define(interp, "qty_value", Some(1), |_, args| {
15008 let (val, _) = extract_quantity(&args[0], "qty_value")?;
15009 Ok(Value::Float(val))
15010 });
15011
15012 define(interp, "qty_units", Some(1), |_, args| {
15014 let (_, units) = extract_quantity(&args[0], "qty_units")?;
15015 Ok(Value::String(Rc::new(units_to_string(units))))
15016 });
15017
15018 define(interp, "qty_convert", Some(2), |_, args| {
15021 let (val, units) = extract_quantity(&args[0], "qty_convert")?;
15022 let _target = match &args[1] {
15023 Value::String(s) => s.to_string(),
15024 _ => return Err(RuntimeError::new("qty_convert: target must be unit string")),
15025 };
15026
15027 Ok(make_quantity(val, units))
15030 });
15031
15032 define(interp, "qty_check", Some(2), |_, args| {
15034 let (_, units) = extract_quantity(&args[0], "qty_check")?;
15035 let expected = match &args[1] {
15036 Value::String(s) => s.to_string(),
15037 _ => return Err(RuntimeError::new("qty_check: expected string")),
15038 };
15039
15040 let actual_str = units_to_string(units);
15042 Ok(Value::Bool(
15043 actual_str.contains(&expected) || expected.contains(&actual_str),
15044 ))
15045 });
15046
15047 define(interp, "c_light", Some(0), |_, _| {
15050 Ok(make_quantity(299792458.0, [1, 0, -1, 0, 0, 0, 0])) });
15052
15053 define(interp, "G_gravity", Some(0), |_, _| {
15055 Ok(make_quantity(6.67430e-11, [3, -1, -2, 0, 0, 0, 0])) });
15057
15058 define(interp, "h_planck", Some(0), |_, _| {
15060 Ok(make_quantity(6.62607015e-34, [2, 1, -1, 0, 0, 0, 0])) });
15062
15063 define(interp, "e_charge", Some(0), |_, _| {
15065 Ok(make_quantity(1.602176634e-19, [0, 0, 1, 1, 0, 0, 0])) });
15067
15068 define(interp, "k_boltzmann", Some(0), |_, _| {
15070 Ok(make_quantity(1.380649e-23, [2, 1, -2, 0, -1, 0, 0])) });
15072}
15073
15074fn register_ecs(interp: &mut Interpreter) {
15154 define(interp, "ecs_world", Some(0), |_, _| {
15156 let mut world = HashMap::new();
15157 world.insert(
15158 "_type".to_string(),
15159 Value::String(Rc::new("ecs_world".to_string())),
15160 );
15161 world.insert("next_id".to_string(), Value::Int(0));
15162 world.insert(
15163 "entities".to_string(),
15164 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
15165 );
15166 world.insert(
15167 "components".to_string(),
15168 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
15169 );
15170 Ok(Value::Map(Rc::new(RefCell::new(world))))
15171 });
15172
15173 define(interp, "ecs_spawn", Some(1), |_, args| {
15175 let world = match &args[0] {
15176 Value::Map(m) => m.clone(),
15177 _ => return Err(RuntimeError::new("ecs_spawn: expected world")),
15178 };
15179
15180 let mut world_ref = world.borrow_mut();
15181 let id = match world_ref.get("next_id") {
15182 Some(Value::Int(n)) => *n,
15183 _ => 0,
15184 };
15185
15186 world_ref.insert("next_id".to_string(), Value::Int(id + 1));
15188
15189 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15191 entities
15192 .borrow_mut()
15193 .insert(id.to_string(), Value::Bool(true));
15194 }
15195
15196 Ok(Value::Int(id))
15197 });
15198
15199 define(interp, "ecs_despawn", Some(2), |_, args| {
15201 let world = match &args[0] {
15202 Value::Map(m) => m.clone(),
15203 _ => return Err(RuntimeError::new("ecs_despawn: expected world")),
15204 };
15205 let id = match &args[1] {
15206 Value::Int(n) => *n,
15207 _ => return Err(RuntimeError::new("ecs_despawn: expected entity id")),
15208 };
15209
15210 let world_ref = world.borrow();
15211
15212 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15214 entities.borrow_mut().remove(&id.to_string());
15215 }
15216
15217 if let Some(Value::Map(components)) = world_ref.get("components") {
15219 let comps = components.borrow();
15220 for (_, comp_storage) in comps.iter() {
15221 if let Value::Map(storage) = comp_storage {
15222 storage.borrow_mut().remove(&id.to_string());
15223 }
15224 }
15225 }
15226
15227 Ok(Value::Bool(true))
15228 });
15229
15230 define(interp, "ecs_attach", Some(4), |_, args| {
15232 let world = match &args[0] {
15233 Value::Map(m) => m.clone(),
15234 _ => {
15235 return Err(RuntimeError::new(
15236 "ecs_attach() expects a world as first argument.\n\
15237 Usage: ecs_attach(world, entity_id, component_name, data)\n\
15238 Example:\n\
15239 let world = ecs_world();\n\
15240 let e = ecs_spawn(world);\n\
15241 ecs_attach(world, e, \"Position\", vec3(0, 0, 0));",
15242 ))
15243 }
15244 };
15245 let id = match &args[1] {
15246 Value::Int(n) => *n,
15247 _ => {
15248 return Err(RuntimeError::new(
15249 "ecs_attach() expects an entity ID (integer) as second argument.\n\
15250 Entity IDs are returned by ecs_spawn().\n\
15251 Example:\n\
15252 let entity = ecs_spawn(world); // Returns 0, 1, 2...\n\
15253 ecs_attach(world, entity, \"Health\", 100);",
15254 ))
15255 }
15256 };
15257 let comp_name = match &args[2] {
15258 Value::String(s) => s.to_string(),
15259 _ => {
15260 return Err(RuntimeError::new(
15261 "ecs_attach() expects a string component name as third argument.\n\
15262 Common component names: \"Position\", \"Velocity\", \"Health\", \"Sprite\"\n\
15263 Example: ecs_attach(world, entity, \"Position\", vec3(0, 0, 0));",
15264 ))
15265 }
15266 };
15267 let data = args[3].clone();
15268
15269 let world_ref = world.borrow();
15270
15271 if let Some(Value::Map(components)) = world_ref.get("components") {
15273 let mut comps = components.borrow_mut();
15274
15275 let storage = comps
15276 .entry(comp_name.clone())
15277 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
15278
15279 if let Value::Map(storage_map) = storage {
15280 storage_map.borrow_mut().insert(id.to_string(), data);
15281 }
15282 }
15283
15284 Ok(Value::Bool(true))
15285 });
15286
15287 define(interp, "ecs_get", Some(3), |_, args| {
15289 let world = match &args[0] {
15290 Value::Map(m) => m.clone(),
15291 _ => return Err(RuntimeError::new("ecs_get: expected world")),
15292 };
15293 let id = match &args[1] {
15294 Value::Int(n) => *n,
15295 _ => return Err(RuntimeError::new("ecs_get: expected entity id")),
15296 };
15297 let comp_name = match &args[2] {
15298 Value::String(s) => s.to_string(),
15299 _ => return Err(RuntimeError::new("ecs_get: expected component name")),
15300 };
15301
15302 let world_ref = world.borrow();
15303
15304 if let Some(Value::Map(components)) = world_ref.get("components") {
15305 let comps = components.borrow();
15306 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15307 if let Some(data) = storage.borrow().get(&id.to_string()) {
15308 return Ok(data.clone());
15309 }
15310 }
15311 }
15312
15313 Ok(Value::Null)
15314 });
15315
15316 define(interp, "ecs_has", Some(3), |_, args| {
15318 let world = match &args[0] {
15319 Value::Map(m) => m.clone(),
15320 _ => return Err(RuntimeError::new("ecs_has: expected world")),
15321 };
15322 let id = match &args[1] {
15323 Value::Int(n) => *n,
15324 _ => return Err(RuntimeError::new("ecs_has: expected entity id")),
15325 };
15326 let comp_name = match &args[2] {
15327 Value::String(s) => s.to_string(),
15328 _ => return Err(RuntimeError::new("ecs_has: expected component name")),
15329 };
15330
15331 let world_ref = world.borrow();
15332
15333 if let Some(Value::Map(components)) = world_ref.get("components") {
15334 let comps = components.borrow();
15335 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15336 return Ok(Value::Bool(storage.borrow().contains_key(&id.to_string())));
15337 }
15338 }
15339
15340 Ok(Value::Bool(false))
15341 });
15342
15343 define(interp, "ecs_remove", Some(3), |_, args| {
15345 let world = match &args[0] {
15346 Value::Map(m) => m.clone(),
15347 _ => return Err(RuntimeError::new("ecs_remove: expected world")),
15348 };
15349 let id = match &args[1] {
15350 Value::Int(n) => *n,
15351 _ => return Err(RuntimeError::new("ecs_remove: expected entity id")),
15352 };
15353 let comp_name = match &args[2] {
15354 Value::String(s) => s.to_string(),
15355 _ => return Err(RuntimeError::new("ecs_remove: expected component name")),
15356 };
15357
15358 let world_ref = world.borrow();
15359
15360 if let Some(Value::Map(components)) = world_ref.get("components") {
15361 let comps = components.borrow();
15362 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15363 storage.borrow_mut().remove(&id.to_string());
15364 return Ok(Value::Bool(true));
15365 }
15366 }
15367
15368 Ok(Value::Bool(false))
15369 });
15370
15371 define(interp, "ecs_query", None, |_, args| {
15374 if args.is_empty() {
15375 return Err(RuntimeError::new(
15376 "ecs_query: expected at least world argument",
15377 ));
15378 }
15379
15380 let world = match &args[0] {
15381 Value::Map(m) => m.clone(),
15382 _ => return Err(RuntimeError::new("ecs_query: expected world")),
15383 };
15384
15385 let comp_names: Vec<String> = args[1..]
15386 .iter()
15387 .filter_map(|a| match a {
15388 Value::String(s) => Some(s.to_string()),
15389 _ => None,
15390 })
15391 .collect();
15392
15393 if comp_names.is_empty() {
15394 let world_ref = world.borrow();
15396 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15397 let result: Vec<Value> = entities
15398 .borrow()
15399 .keys()
15400 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
15401 .collect();
15402 return Ok(Value::Array(Rc::new(RefCell::new(result))));
15403 }
15404 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15405 }
15406
15407 let world_ref = world.borrow();
15408 let mut result_ids: Option<Vec<String>> = None;
15409
15410 if let Some(Value::Map(components)) = world_ref.get("components") {
15411 let comps = components.borrow();
15412
15413 for comp_name in &comp_names {
15414 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15415 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
15416
15417 result_ids = Some(match result_ids {
15418 None => keys,
15419 Some(existing) => {
15420 existing.into_iter().filter(|k| keys.contains(k)).collect()
15421 }
15422 });
15423 } else {
15424 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15426 }
15427 }
15428 }
15429
15430 let result: Vec<Value> = result_ids
15431 .unwrap_or_default()
15432 .iter()
15433 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
15434 .collect();
15435
15436 Ok(Value::Array(Rc::new(RefCell::new(result))))
15437 });
15438
15439 define(interp, "ecs_query_with", Some(3), |interp, args| {
15442 let world = match &args[0] {
15443 Value::Map(m) => m.clone(),
15444 _ => return Err(RuntimeError::new("ecs_query_with: expected world")),
15445 };
15446 let comp_names: Vec<String> = match &args[1] {
15447 Value::Array(arr) => arr
15448 .borrow()
15449 .iter()
15450 .filter_map(|v| match v {
15451 Value::String(s) => Some(s.to_string()),
15452 _ => None,
15453 })
15454 .collect(),
15455 _ => {
15456 return Err(RuntimeError::new(
15457 "ecs_query_with: expected array of component names",
15458 ))
15459 }
15460 };
15461 let callback = match &args[2] {
15462 Value::Function(f) => f.clone(),
15463 _ => {
15464 return Err(RuntimeError::new(
15465 "ecs_query_with: expected callback function",
15466 ))
15467 }
15468 };
15469
15470 let mut callback_data: Vec<(i64, HashMap<String, Value>)> = Vec::new();
15472
15473 {
15474 let world_ref = world.borrow();
15475 let mut result_ids: Option<Vec<String>> = None;
15476
15477 if let Some(Value::Map(components)) = world_ref.get("components") {
15478 let comps = components.borrow();
15479
15480 for comp_name in &comp_names {
15481 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15482 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
15483 result_ids = Some(match result_ids {
15484 None => keys,
15485 Some(existing) => {
15486 existing.into_iter().filter(|k| keys.contains(k)).collect()
15487 }
15488 });
15489 } else {
15490 result_ids = Some(vec![]);
15491 break;
15492 }
15493 }
15494
15495 for id_str in result_ids.unwrap_or_default() {
15497 if let Ok(id) = id_str.parse::<i64>() {
15498 let mut entity_comps = HashMap::new();
15499 for comp_name in &comp_names {
15500 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15501 if let Some(data) = storage.borrow().get(&id_str) {
15502 entity_comps.insert(comp_name.clone(), data.clone());
15503 }
15504 }
15505 }
15506 callback_data.push((id, entity_comps));
15507 }
15508 }
15509 }
15510 } for (id, entity_comps) in callback_data {
15514 let callback_args = vec![
15515 Value::Int(id),
15516 Value::Map(Rc::new(RefCell::new(entity_comps))),
15517 ];
15518 interp.call_function(&callback, callback_args)?;
15519 }
15520
15521 Ok(Value::Null)
15522 });
15523
15524 define(interp, "ecs_count", Some(1), |_, args| {
15526 let world = match &args[0] {
15527 Value::Map(m) => m.clone(),
15528 _ => return Err(RuntimeError::new("ecs_count: expected world")),
15529 };
15530
15531 let world_ref = world.borrow();
15532 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15533 return Ok(Value::Int(entities.borrow().len() as i64));
15534 }
15535
15536 Ok(Value::Int(0))
15537 });
15538
15539 define(interp, "ecs_alive", Some(2), |_, args| {
15541 let world = match &args[0] {
15542 Value::Map(m) => m.clone(),
15543 _ => return Err(RuntimeError::new("ecs_alive: expected world")),
15544 };
15545 let id = match &args[1] {
15546 Value::Int(n) => *n,
15547 _ => return Err(RuntimeError::new("ecs_alive: expected entity id")),
15548 };
15549
15550 let world_ref = world.borrow();
15551 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15552 return Ok(Value::Bool(entities.borrow().contains_key(&id.to_string())));
15553 }
15554
15555 Ok(Value::Bool(false))
15556 });
15557}
15558
15559fn register_polycultural_text(interp: &mut Interpreter) {
15579 define(interp, "script", Some(1), |_, args| {
15589 match &args[0] {
15590 Value::String(s) => {
15591 let mut script_counts: HashMap<String, usize> = HashMap::new();
15593 for c in s.chars() {
15594 if !c.is_whitespace() && !c.is_ascii_punctuation() {
15595 let script = c.script();
15596 let name = format!("{:?}", script);
15597 *script_counts.entry(name).or_insert(0) += 1;
15598 }
15599 }
15600 let dominant = script_counts
15602 .into_iter()
15603 .max_by_key(|(_, count)| *count)
15604 .map(|(name, _)| name)
15605 .unwrap_or_else(|| "Unknown".to_string());
15606 Ok(Value::String(Rc::new(dominant)))
15607 }
15608 _ => Err(RuntimeError::new("script() requires string")),
15609 }
15610 });
15611
15612 define(interp, "scripts", Some(1), |_, args| match &args[0] {
15614 Value::String(s) => {
15615 let mut scripts: Vec<String> = s
15616 .chars()
15617 .filter(|c| !c.is_whitespace() && !c.is_ascii_punctuation())
15618 .map(|c| format!("{:?}", c.script()))
15619 .collect();
15620 scripts.sort();
15621 scripts.dedup();
15622 let values: Vec<Value> = scripts
15623 .into_iter()
15624 .map(|s| Value::String(Rc::new(s)))
15625 .collect();
15626 Ok(Value::Array(Rc::new(RefCell::new(values))))
15627 }
15628 _ => Err(RuntimeError::new("scripts() requires string")),
15629 });
15630
15631 define(interp, "is_script", Some(2), |_, args| {
15633 match (&args[0], &args[1]) {
15634 (Value::String(s), Value::String(script_name)) => {
15635 let target = script_name.to_lowercase();
15636 let mut matching = 0usize;
15637 let mut total = 0usize;
15638 for c in s.chars() {
15639 if !c.is_whitespace() && !c.is_ascii_punctuation() {
15640 total += 1;
15641 let script_str = format!("{:?}", c.script()).to_lowercase();
15642 if script_str == target {
15643 matching += 1;
15644 }
15645 }
15646 }
15647 let ratio = if total > 0 {
15648 matching as f64 / total as f64
15649 } else {
15650 0.0
15651 };
15652 Ok(Value::Bool(ratio > 0.5))
15653 }
15654 _ => Err(RuntimeError::new(
15655 "is_script() requires string and script name",
15656 )),
15657 }
15658 });
15659
15660 define(interp, "is_latin", Some(1), |_, args| match &args[0] {
15662 Value::String(s) => {
15663 let is_latin = s
15664 .chars()
15665 .filter(|c| !c.is_whitespace())
15666 .all(|c| matches!(c.script(), Script::Latin | Script::Common));
15667 Ok(Value::Bool(is_latin && !s.is_empty()))
15668 }
15669 _ => Err(RuntimeError::new("is_latin() requires string")),
15670 });
15671
15672 define(interp, "is_cjk", Some(1), |_, args| match &args[0] {
15673 Value::String(s) => {
15674 let has_cjk = s.chars().any(|c| {
15675 matches!(
15676 c.script(),
15677 Script::Han
15678 | Script::Hiragana
15679 | Script::Katakana
15680 | Script::Hangul
15681 | Script::Bopomofo
15682 )
15683 });
15684 Ok(Value::Bool(has_cjk))
15685 }
15686 _ => Err(RuntimeError::new("is_cjk() requires string")),
15687 });
15688
15689 define(interp, "is_arabic", Some(1), |_, args| match &args[0] {
15690 Value::String(s) => {
15691 let has_arabic = s.chars().any(|c| matches!(c.script(), Script::Arabic));
15692 Ok(Value::Bool(has_arabic))
15693 }
15694 _ => Err(RuntimeError::new("is_arabic() requires string")),
15695 });
15696
15697 define(interp, "is_hebrew", Some(1), |_, args| match &args[0] {
15698 Value::String(s) => {
15699 let has_hebrew = s.chars().any(|c| matches!(c.script(), Script::Hebrew));
15700 Ok(Value::Bool(has_hebrew))
15701 }
15702 _ => Err(RuntimeError::new("is_hebrew() requires string")),
15703 });
15704
15705 define(interp, "is_cyrillic", Some(1), |_, args| match &args[0] {
15706 Value::String(s) => {
15707 let has_cyrillic = s.chars().any(|c| matches!(c.script(), Script::Cyrillic));
15708 Ok(Value::Bool(has_cyrillic))
15709 }
15710 _ => Err(RuntimeError::new("is_cyrillic() requires string")),
15711 });
15712
15713 define(interp, "is_greek", Some(1), |_, args| match &args[0] {
15714 Value::String(s) => {
15715 let has_greek = s.chars().any(|c| matches!(c.script(), Script::Greek));
15716 Ok(Value::Bool(has_greek))
15717 }
15718 _ => Err(RuntimeError::new("is_greek() requires string")),
15719 });
15720
15721 define(interp, "is_devanagari", Some(1), |_, args| match &args[0] {
15722 Value::String(s) => {
15723 let has_devanagari = s.chars().any(|c| matches!(c.script(), Script::Devanagari));
15724 Ok(Value::Bool(has_devanagari))
15725 }
15726 _ => Err(RuntimeError::new("is_devanagari() requires string")),
15727 });
15728
15729 define(interp, "is_thai", Some(1), |_, args| match &args[0] {
15730 Value::String(s) => {
15731 let has_thai = s.chars().any(|c| matches!(c.script(), Script::Thai));
15732 Ok(Value::Bool(has_thai))
15733 }
15734 _ => Err(RuntimeError::new("is_thai() requires string")),
15735 });
15736
15737 define(interp, "is_hangul", Some(1), |_, args| match &args[0] {
15738 Value::String(s) => {
15739 let has_hangul = s.chars().any(|c| matches!(c.script(), Script::Hangul));
15740 Ok(Value::Bool(has_hangul))
15741 }
15742 _ => Err(RuntimeError::new("is_hangul() requires string")),
15743 });
15744
15745 define(interp, "is_hiragana", Some(1), |_, args| match &args[0] {
15746 Value::String(s) => {
15747 let has_hiragana = s.chars().any(|c| matches!(c.script(), Script::Hiragana));
15748 Ok(Value::Bool(has_hiragana))
15749 }
15750 _ => Err(RuntimeError::new("is_hiragana() requires string")),
15751 });
15752
15753 define(interp, "is_katakana", Some(1), |_, args| match &args[0] {
15754 Value::String(s) => {
15755 let has_katakana = s.chars().any(|c| matches!(c.script(), Script::Katakana));
15756 Ok(Value::Bool(has_katakana))
15757 }
15758 _ => Err(RuntimeError::new("is_katakana() requires string")),
15759 });
15760
15761 define(interp, "char_script", Some(1), |_, args| match &args[0] {
15763 Value::Char(c) => {
15764 let script = format!("{:?}", c.script());
15765 Ok(Value::String(Rc::new(script)))
15766 }
15767 Value::String(s) if s.chars().count() == 1 => {
15768 let c = s.chars().next().unwrap();
15769 let script = format!("{:?}", c.script());
15770 Ok(Value::String(Rc::new(script)))
15771 }
15772 _ => Err(RuntimeError::new("char_script() requires single character")),
15773 });
15774
15775 define(interp, "text_direction", Some(1), |_, args| {
15785 match &args[0] {
15786 Value::String(s) => {
15787 let bidi_info = BidiInfo::new(s, None);
15788 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
15790 let direction = if has_rtl { "rtl" } else { "ltr" };
15791 Ok(Value::String(Rc::new(direction.to_string())))
15792 }
15793 _ => Err(RuntimeError::new("text_direction() requires string")),
15794 }
15795 });
15796
15797 define(interp, "is_rtl", Some(1), |_, args| match &args[0] {
15799 Value::String(s) => {
15800 let bidi_info = BidiInfo::new(s, None);
15801 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
15802 Ok(Value::Bool(has_rtl))
15803 }
15804 _ => Err(RuntimeError::new("is_rtl() requires string")),
15805 });
15806
15807 define(interp, "is_ltr", Some(1), |_, args| match &args[0] {
15809 Value::String(s) => {
15810 let bidi_info = BidiInfo::new(s, None);
15811 let is_ltr = bidi_info.paragraphs.iter().all(|p| !p.level.is_rtl());
15812 Ok(Value::Bool(is_ltr))
15813 }
15814 _ => Err(RuntimeError::new("is_ltr() requires string")),
15815 });
15816
15817 define(interp, "is_bidi", Some(1), |_, args| {
15819 match &args[0] {
15820 Value::String(s) => {
15821 let has_rtl = s.chars().any(|c| {
15823 matches!(
15824 c.script(),
15825 Script::Arabic | Script::Hebrew | Script::Syriac | Script::Thaana
15826 )
15827 });
15828 let has_ltr = s.chars().any(|c| {
15829 matches!(c.script(), Script::Latin | Script::Greek | Script::Cyrillic)
15830 });
15831 Ok(Value::Bool(has_rtl && has_ltr))
15832 }
15833 _ => Err(RuntimeError::new("is_bidi() requires string")),
15834 }
15835 });
15836
15837 define(interp, "bidi_reorder", Some(1), |_, args| match &args[0] {
15839 Value::String(s) => {
15840 let bidi_info = BidiInfo::new(s, None);
15841 let mut result = String::new();
15842 for para in &bidi_info.paragraphs {
15843 let line = para.range.clone();
15844 let reordered = bidi_info.reorder_line(para, line);
15845 result.push_str(&reordered);
15846 }
15847 Ok(Value::String(Rc::new(result)))
15848 }
15849 _ => Err(RuntimeError::new("bidi_reorder() requires string")),
15850 });
15851
15852 define(interp, "display_width", Some(1), |_, args| match &args[0] {
15862 Value::String(s) => {
15863 let width = UnicodeWidthStr::width(s.as_str());
15864 Ok(Value::Int(width as i64))
15865 }
15866 _ => Err(RuntimeError::new("display_width() requires string")),
15867 });
15868
15869 define(interp, "is_fullwidth", Some(1), |_, args| {
15871 match &args[0] {
15872 Value::String(s) => {
15873 let char_count = s.chars().count();
15874 let display_width = UnicodeWidthStr::width(s.as_str());
15875 Ok(Value::Bool(display_width > char_count))
15877 }
15878 _ => Err(RuntimeError::new("is_fullwidth() requires string")),
15879 }
15880 });
15881
15882 define(interp, "pad_display", Some(3), |_, args| {
15884 match (&args[0], &args[1], &args[2]) {
15885 (Value::String(s), Value::Int(target_width), Value::String(align)) => {
15886 let current_width = UnicodeWidthStr::width(s.as_str());
15887 let target = *target_width as usize;
15888 if current_width >= target {
15889 return Ok(Value::String(s.clone()));
15890 }
15891 let padding = target - current_width;
15892 let result = match align.as_str() {
15893 "left" => format!("{}{}", s, " ".repeat(padding)),
15894 "right" => format!("{}{}", " ".repeat(padding), s),
15895 "center" => {
15896 let left = padding / 2;
15897 let right = padding - left;
15898 format!("{}{}{}", " ".repeat(left), s, " ".repeat(right))
15899 }
15900 _ => {
15901 return Err(RuntimeError::new(
15902 "pad_display: align must be 'left', 'right', or 'center'",
15903 ))
15904 }
15905 };
15906 Ok(Value::String(Rc::new(result)))
15907 }
15908 _ => Err(RuntimeError::new(
15909 "pad_display() requires string, width, and alignment",
15910 )),
15911 }
15912 });
15913
15914 define(interp, "transliterate", Some(1), |_, args| match &args[0] {
15924 Value::String(s) => {
15925 let ascii = deunicode(s);
15926 Ok(Value::String(Rc::new(ascii)))
15927 }
15928 _ => Err(RuntimeError::new("transliterate() requires string")),
15929 });
15930
15931 define(interp, "to_ascii", Some(1), |_, args| match &args[0] {
15933 Value::String(s) => {
15934 let ascii = deunicode(s);
15935 Ok(Value::String(Rc::new(ascii)))
15936 }
15937 _ => Err(RuntimeError::new("to_ascii() requires string")),
15938 });
15939
15940 define(interp, "slugify", Some(1), |_, args| {
15942 match &args[0] {
15943 Value::String(s) => {
15944 let ascii = deunicode(s);
15945 let slug: String = ascii
15946 .to_lowercase()
15947 .chars()
15948 .map(|c| if c.is_alphanumeric() { c } else { '-' })
15949 .collect();
15950 let mut result = String::new();
15952 let mut last_was_dash = true; for c in slug.chars() {
15954 if c == '-' {
15955 if !last_was_dash {
15956 result.push(c);
15957 last_was_dash = true;
15958 }
15959 } else {
15960 result.push(c);
15961 last_was_dash = false;
15962 }
15963 }
15964 if result.ends_with('-') {
15966 result.pop();
15967 }
15968 Ok(Value::String(Rc::new(result)))
15969 }
15970 _ => Err(RuntimeError::new("slugify() requires string")),
15971 }
15972 });
15973
15974 define(interp, "strip_diacritics", Some(1), |_, args| {
15984 match &args[0] {
15985 Value::String(s) => {
15986 let decomposed: String = s.nfd().collect();
15988 let stripped: String = decomposed
15990 .chars()
15991 .filter(|c| {
15992 let code = *c as u32;
15996 !(0x0300..=0x036F).contains(&code)
15998 && !(0x1AB0..=0x1AFF).contains(&code)
15999 && !(0x1DC0..=0x1DFF).contains(&code)
16000 && !(0x20D0..=0x20FF).contains(&code)
16001 && !(0xFE20..=0xFE2F).contains(&code)
16002 })
16003 .collect();
16004 Ok(Value::String(Rc::new(stripped)))
16005 }
16006 _ => Err(RuntimeError::new("strip_diacritics() requires string")),
16007 }
16008 });
16009
16010 define(interp, "has_diacritics", Some(1), |_, args| {
16012 match &args[0] {
16013 Value::String(s) => {
16014 let decomposed: String = s.nfd().collect();
16015 let has_marks = decomposed.chars().any(|c| {
16016 let code = c as u32;
16017 (0x0300..=0x036F).contains(&code)
16018 || (0x1AB0..=0x1AFF).contains(&code)
16019 || (0x1DC0..=0x1DFF).contains(&code)
16020 || (0x20D0..=0x20FF).contains(&code)
16021 || (0xFE20..=0xFE2F).contains(&code)
16022 });
16023 Ok(Value::Bool(has_marks))
16024 }
16025 _ => Err(RuntimeError::new("has_diacritics() requires string")),
16026 }
16027 });
16028
16029 define(interp, "normalize_accents", Some(2), |_, args| {
16031 match (&args[0], &args[1]) {
16032 (Value::String(s), Value::String(form)) => {
16033 let result = match form.as_str() {
16034 "composed" | "nfc" => s.nfc().collect(),
16035 "decomposed" | "nfd" => s.nfd().collect(),
16036 _ => {
16037 return Err(RuntimeError::new(
16038 "normalize_accents: form must be 'composed' or 'decomposed'",
16039 ))
16040 }
16041 };
16042 Ok(Value::String(Rc::new(result)))
16043 }
16044 _ => Err(RuntimeError::new(
16045 "normalize_accents() requires string and form",
16046 )),
16047 }
16048 });
16049
16050 define(interp, "upper_locale", Some(2), |_, args| {
16062 match (&args[0], &args[1]) {
16063 (Value::String(s), Value::String(locale_str)) => {
16064 let case_mapper = CaseMapper::new();
16065 let langid: LanguageIdentifier =
16066 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16067 let result = case_mapper.uppercase_to_string(s, &langid);
16068 Ok(Value::String(Rc::new(result)))
16069 }
16070 _ => Err(RuntimeError::new(
16071 "upper_locale() requires string and locale",
16072 )),
16073 }
16074 });
16075
16076 define(interp, "lower_locale", Some(2), |_, args| {
16078 match (&args[0], &args[1]) {
16079 (Value::String(s), Value::String(locale_str)) => {
16080 let case_mapper = CaseMapper::new();
16081 let langid: LanguageIdentifier =
16082 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16083 let result = case_mapper.lowercase_to_string(s, &langid);
16084 Ok(Value::String(Rc::new(result)))
16085 }
16086 _ => Err(RuntimeError::new(
16087 "lower_locale() requires string and locale",
16088 )),
16089 }
16090 });
16091
16092 define(interp, "titlecase_locale", Some(2), |_, args| {
16094 match (&args[0], &args[1]) {
16095 (Value::String(s), Value::String(locale_str)) => {
16096 let case_mapper = CaseMapper::new();
16097 let langid: LanguageIdentifier =
16098 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16099 let options = TitlecaseOptions::default();
16100 let result = case_mapper
16101 .titlecase_segment_with_only_case_data_to_string(s, &langid, options);
16102 Ok(Value::String(Rc::new(result)))
16103 }
16104 _ => Err(RuntimeError::new(
16105 "titlecase_locale() requires string and locale",
16106 )),
16107 }
16108 });
16109
16110 define(interp, "case_fold", Some(1), |_, args| match &args[0] {
16112 Value::String(s) => {
16113 let case_mapper = CaseMapper::new();
16114 let result = case_mapper.fold_string(s);
16115 Ok(Value::String(Rc::new(result)))
16116 }
16117 _ => Err(RuntimeError::new("case_fold() requires string")),
16118 });
16119
16120 define(interp, "case_insensitive_eq", Some(2), |_, args| {
16122 match (&args[0], &args[1]) {
16123 (Value::String(a), Value::String(b)) => {
16124 let case_mapper = CaseMapper::new();
16125 let folded_a = case_mapper.fold_string(a);
16126 let folded_b = case_mapper.fold_string(b);
16127 Ok(Value::Bool(folded_a == folded_b))
16128 }
16129 _ => Err(RuntimeError::new(
16130 "case_insensitive_eq() requires two strings",
16131 )),
16132 }
16133 });
16134
16135 define(interp, "compare_locale", Some(3), |_, args| {
16147 match (&args[0], &args[1], &args[2]) {
16148 (Value::String(a), Value::String(b), Value::String(locale_str)) => {
16149 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16150 let options = CollatorOptions::new();
16151 let collator = Collator::try_new(&locale.into(), options)
16152 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
16153 let result = match collator.compare(a, b) {
16154 std::cmp::Ordering::Less => -1,
16155 std::cmp::Ordering::Equal => 0,
16156 std::cmp::Ordering::Greater => 1,
16157 };
16158 Ok(Value::Int(result))
16159 }
16160 _ => Err(RuntimeError::new(
16161 "compare_locale() requires two strings and locale",
16162 )),
16163 }
16164 });
16165
16166 define(interp, "sort_locale", Some(2), |_, args| {
16168 match (&args[0], &args[1]) {
16169 (Value::Array(arr), Value::String(locale_str)) => {
16170 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16171 let options = CollatorOptions::new();
16172 let collator = Collator::try_new(&locale.into(), options)
16173 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
16174
16175 let mut items: Vec<(String, Value)> = arr
16176 .borrow()
16177 .iter()
16178 .map(|v| {
16179 let s = match v {
16180 Value::String(s) => (**s).clone(),
16181 _ => format!("{}", v),
16182 };
16183 (s, v.clone())
16184 })
16185 .collect();
16186
16187 items.sort_by(|(a, _), (b, _)| collator.compare(a, b));
16188
16189 let sorted: Vec<Value> = items.into_iter().map(|(_, v)| v).collect();
16190 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
16191 }
16192 _ => Err(RuntimeError::new("sort_locale() requires array and locale")),
16193 }
16194 });
16195
16196 define(interp, "sentences", Some(1), |_, args| match &args[0] {
16208 Value::String(s) => {
16209 let segmenter = SentenceSegmenter::new();
16210 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16211 let mut sentences = Vec::new();
16212 let mut start = 0;
16213 for end in breakpoints {
16214 let sentence = s[start..end].trim();
16215 if !sentence.is_empty() {
16216 sentences.push(Value::String(Rc::new(sentence.to_string())));
16217 }
16218 start = end;
16219 }
16220 Ok(Value::Array(Rc::new(RefCell::new(sentences))))
16221 }
16222 _ => Err(RuntimeError::new("sentences() requires string")),
16223 });
16224
16225 define(interp, "sentence_count", Some(1), |_, args| {
16227 match &args[0] {
16228 Value::String(s) => {
16229 let segmenter = SentenceSegmenter::new();
16230 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16231 let count = breakpoints.len().saturating_sub(1);
16233 Ok(Value::Int(count as i64))
16234 }
16235 _ => Err(RuntimeError::new("sentence_count() requires string")),
16236 }
16237 });
16238
16239 define(interp, "words_icu", Some(1), |_, args| {
16241 match &args[0] {
16242 Value::String(s) => {
16243 let segmenter = WordSegmenter::new_auto();
16244 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16245 let mut words = Vec::new();
16246 let mut start = 0;
16247 for end in breakpoints {
16248 let word = &s[start..end];
16249 if !word.trim().is_empty() {
16251 words.push(Value::String(Rc::new(word.to_string())));
16252 }
16253 start = end;
16254 }
16255 Ok(Value::Array(Rc::new(RefCell::new(words))))
16256 }
16257 _ => Err(RuntimeError::new("words_icu() requires string")),
16258 }
16259 });
16260
16261 define(interp, "word_count_icu", Some(1), |_, args| {
16263 match &args[0] {
16264 Value::String(s) => {
16265 let segmenter = WordSegmenter::new_auto();
16266 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16267 let mut count = 0;
16268 let mut start = 0;
16269 for end in breakpoints {
16270 let word = &s[start..end];
16271 if !word.trim().is_empty() && word.chars().any(|c| c.is_alphanumeric()) {
16272 count += 1;
16273 }
16274 start = end;
16275 }
16276 Ok(Value::Int(count))
16277 }
16278 _ => Err(RuntimeError::new("word_count_icu() requires string")),
16279 }
16280 });
16281
16282 define(interp, "is_emoji", Some(1), |_, args| {
16288 match &args[0] {
16289 Value::String(s) => {
16290 let has_emoji = s.chars().any(|c| {
16291 let code = c as u32;
16292 (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) });
16307 Ok(Value::Bool(has_emoji))
16308 }
16309 _ => Err(RuntimeError::new("is_emoji() requires string")),
16310 }
16311 });
16312
16313 define(interp, "extract_emoji", Some(1), |_, args| match &args[0] {
16315 Value::String(s) => {
16316 let emoji: Vec<Value> = s
16317 .graphemes(true)
16318 .filter(|g| {
16319 g.chars().any(|c| {
16320 let code = c as u32;
16321 (0x1F600..=0x1F64F).contains(&code)
16322 || (0x1F300..=0x1F5FF).contains(&code)
16323 || (0x1F680..=0x1F6FF).contains(&code)
16324 || (0x1F1E0..=0x1F1FF).contains(&code)
16325 || (0x2600..=0x26FF).contains(&code)
16326 || (0x2700..=0x27BF).contains(&code)
16327 || (0x1F900..=0x1F9FF).contains(&code)
16328 || (0x1FA00..=0x1FA6F).contains(&code)
16329 || (0x1FA70..=0x1FAFF).contains(&code)
16330 })
16331 })
16332 .map(|g| Value::String(Rc::new(g.to_string())))
16333 .collect();
16334 Ok(Value::Array(Rc::new(RefCell::new(emoji))))
16335 }
16336 _ => Err(RuntimeError::new("extract_emoji() requires string")),
16337 });
16338
16339 define(interp, "strip_emoji", Some(1), |_, args| match &args[0] {
16341 Value::String(s) => {
16342 let stripped: String = s
16343 .graphemes(true)
16344 .filter(|g| {
16345 !g.chars().any(|c| {
16346 let code = c as u32;
16347 (0x1F600..=0x1F64F).contains(&code)
16348 || (0x1F300..=0x1F5FF).contains(&code)
16349 || (0x1F680..=0x1F6FF).contains(&code)
16350 || (0x1F1E0..=0x1F1FF).contains(&code)
16351 || (0x2600..=0x26FF).contains(&code)
16352 || (0x2700..=0x27BF).contains(&code)
16353 || (0x1F900..=0x1F9FF).contains(&code)
16354 || (0x1FA00..=0x1FA6F).contains(&code)
16355 || (0x1FA70..=0x1FAFF).contains(&code)
16356 })
16357 })
16358 .collect();
16359 Ok(Value::String(Rc::new(stripped)))
16360 }
16361 _ => Err(RuntimeError::new("strip_emoji() requires string")),
16362 });
16363
16364 define(interp, "script_runs", Some(1), |_, args| {
16370 match &args[0] {
16371 Value::String(s) => {
16372 let mut runs: Vec<Value> = Vec::new();
16373 let mut current_run = String::new();
16374 let mut current_script: Option<Script> = None;
16375
16376 for c in s.chars() {
16377 let script = c.script();
16378 if script != Script::Common && script != Script::Inherited {
16380 if let Some(curr) = current_script {
16381 if script != curr {
16382 if !current_run.is_empty() {
16384 runs.push(Value::String(Rc::new(current_run.clone())));
16385 current_run.clear();
16386 }
16387 current_script = Some(script);
16388 }
16389 } else {
16390 current_script = Some(script);
16391 }
16392 }
16393 current_run.push(c);
16394 }
16395
16396 if !current_run.is_empty() {
16398 runs.push(Value::String(Rc::new(current_run)));
16399 }
16400
16401 Ok(Value::Array(Rc::new(RefCell::new(runs))))
16402 }
16403 _ => Err(RuntimeError::new("script_runs() requires string")),
16404 }
16405 });
16406
16407 define(interp, "script_ratio", Some(1), |_, args| {
16409 match &args[0] {
16410 Value::String(s) => {
16411 let mut script_counts: HashMap<String, usize> = HashMap::new();
16412 let mut total = 0usize;
16413
16414 for c in s.chars() {
16415 if !c.is_whitespace() && c != ' ' {
16416 let script = format!("{:?}", c.script());
16417 *script_counts.entry(script).or_insert(0) += 1;
16418 total += 1;
16419 }
16420 }
16421
16422 let mut result = HashMap::new();
16424 for (script, count) in script_counts {
16425 let ratio = if total > 0 {
16426 count as f64 / total as f64
16427 } else {
16428 0.0
16429 };
16430 result.insert(script, Value::Float(ratio));
16431 }
16432
16433 let map = Rc::new(RefCell::new(result));
16434 Ok(Value::Map(map))
16435 }
16436 _ => Err(RuntimeError::new("script_ratio() requires string")),
16437 }
16438 });
16439
16440 define(interp, "locale_name", Some(1), |_, args| {
16446 match &args[0] {
16447 Value::String(locale_str) => {
16448 Ok(Value::String(locale_str.clone()))
16451 }
16452 _ => Err(RuntimeError::new("locale_name() requires string")),
16453 }
16454 });
16455
16456 define(interp, "supported_locales", Some(0), |_, _| {
16458 let locales = vec![
16460 "ar", "bg", "ca", "cs", "da", "de", "el", "en", "es", "et", "fi", "fr", "he", "hi",
16461 "hr", "hu", "id", "it", "ja", "ko", "lt", "lv", "ms", "nb", "nl", "pl", "pt", "ro",
16462 "ru", "sk", "sl", "sr", "sv", "th", "tr", "uk", "vi", "zh",
16463 ];
16464 let values: Vec<Value> = locales
16465 .into_iter()
16466 .map(|s| Value::String(Rc::new(s.to_string())))
16467 .collect();
16468 Ok(Value::Array(Rc::new(RefCell::new(values))))
16469 });
16470}
16471
16472fn register_text_intelligence(interp: &mut Interpreter) {
16477 define(interp, "levenshtein", Some(2), |_, args| {
16483 match (&args[0], &args[1]) {
16484 (Value::String(a), Value::String(b)) => {
16485 let distance = strsim::levenshtein(a, b);
16486 Ok(Value::Int(distance as i64))
16487 }
16488 _ => Err(RuntimeError::new("levenshtein() requires two strings")),
16489 }
16490 });
16491
16492 define(
16494 interp,
16495 "levenshtein_normalized",
16496 Some(2),
16497 |_, args| match (&args[0], &args[1]) {
16498 (Value::String(a), Value::String(b)) => {
16499 let distance = strsim::normalized_levenshtein(a, b);
16500 Ok(Value::Float(distance))
16501 }
16502 _ => Err(RuntimeError::new(
16503 "levenshtein_normalized() requires two strings",
16504 )),
16505 },
16506 );
16507
16508 define(interp, "jaro", Some(2), |_, args| {
16510 match (&args[0], &args[1]) {
16511 (Value::String(a), Value::String(b)) => {
16512 let sim = strsim::jaro(a, b);
16513 Ok(Value::Float(sim))
16514 }
16515 _ => Err(RuntimeError::new("jaro() requires two strings")),
16516 }
16517 });
16518
16519 define(interp, "jaro_winkler", Some(2), |_, args| {
16521 match (&args[0], &args[1]) {
16522 (Value::String(a), Value::String(b)) => {
16523 let sim = strsim::jaro_winkler(a, b);
16524 Ok(Value::Float(sim))
16525 }
16526 _ => Err(RuntimeError::new("jaro_winkler() requires two strings")),
16527 }
16528 });
16529
16530 define(interp, "sorensen_dice", Some(2), |_, args| {
16532 match (&args[0], &args[1]) {
16533 (Value::String(a), Value::String(b)) => {
16534 let sim = strsim::sorensen_dice(a, b);
16535 Ok(Value::Float(sim))
16536 }
16537 _ => Err(RuntimeError::new("sorensen_dice() requires two strings")),
16538 }
16539 });
16540
16541 define(interp, "damerau_levenshtein", Some(2), |_, args| {
16543 match (&args[0], &args[1]) {
16544 (Value::String(a), Value::String(b)) => {
16545 let distance = strsim::damerau_levenshtein(a, b);
16546 Ok(Value::Int(distance as i64))
16547 }
16548 _ => Err(RuntimeError::new(
16549 "damerau_levenshtein() requires two strings",
16550 )),
16551 }
16552 });
16553
16554 define(interp, "osa_distance", Some(2), |_, args| {
16556 match (&args[0], &args[1]) {
16557 (Value::String(a), Value::String(b)) => {
16558 let distance = strsim::osa_distance(a, b);
16559 Ok(Value::Int(distance as i64))
16560 }
16561 _ => Err(RuntimeError::new("osa_distance() requires two strings")),
16562 }
16563 });
16564
16565 define(interp, "fuzzy_match", Some(3), |_, args| {
16567 match (&args[0], &args[1], &args[2]) {
16568 (Value::String(a), Value::String(b), Value::Float(threshold)) => {
16569 let sim = strsim::jaro_winkler(a, b);
16570 Ok(Value::Bool(sim >= *threshold))
16571 }
16572 (Value::String(a), Value::String(b), Value::Int(threshold)) => {
16573 let sim = strsim::jaro_winkler(a, b);
16574 Ok(Value::Bool(sim >= *threshold as f64))
16575 }
16576 _ => Err(RuntimeError::new(
16577 "fuzzy_match() requires two strings and threshold",
16578 )),
16579 }
16580 });
16581
16582 define(interp, "fuzzy_search", Some(3), |_, args| {
16584 match (&args[0], &args[1], &args[2]) {
16585 (Value::String(query), Value::Array(items), Value::Int(limit)) => {
16586 let items_ref = items.borrow();
16587 let mut scores: Vec<(f64, &str)> = items_ref
16588 .iter()
16589 .filter_map(|v| {
16590 if let Value::String(s) = v {
16591 Some((strsim::jaro_winkler(query, s), s.as_str()))
16592 } else {
16593 None
16594 }
16595 })
16596 .collect();
16597 scores.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
16598 let results: Vec<Value> = scores
16599 .into_iter()
16600 .take(*limit as usize)
16601 .map(|(_, s)| Value::String(Rc::new(s.to_string())))
16602 .collect();
16603 Ok(Value::Array(Rc::new(RefCell::new(results))))
16604 }
16605 _ => Err(RuntimeError::new(
16606 "fuzzy_search() requires query string, array, and limit",
16607 )),
16608 }
16609 });
16610
16611 define(interp, "soundex", Some(1), |_, args| match &args[0] {
16617 Value::String(s) => {
16618 let code = compute_soundex(s);
16619 Ok(Value::String(Rc::new(code)))
16620 }
16621 _ => Err(RuntimeError::new("soundex() requires string")),
16622 });
16623
16624 define(interp, "soundex_match", Some(2), |_, args| {
16626 match (&args[0], &args[1]) {
16627 (Value::String(a), Value::String(b)) => {
16628 let code_a = compute_soundex(a);
16629 let code_b = compute_soundex(b);
16630 Ok(Value::Bool(code_a == code_b))
16631 }
16632 _ => Err(RuntimeError::new("soundex_match() requires two strings")),
16633 }
16634 });
16635
16636 define(interp, "metaphone", Some(1), |_, args| match &args[0] {
16638 Value::String(s) => {
16639 let code = compute_metaphone(s);
16640 Ok(Value::String(Rc::new(code)))
16641 }
16642 _ => Err(RuntimeError::new("metaphone() requires string")),
16643 });
16644
16645 define(interp, "metaphone_match", Some(2), |_, args| {
16647 match (&args[0], &args[1]) {
16648 (Value::String(a), Value::String(b)) => {
16649 let code_a = compute_metaphone(a);
16650 let code_b = compute_metaphone(b);
16651 Ok(Value::Bool(code_a == code_b))
16652 }
16653 _ => Err(RuntimeError::new("metaphone_match() requires two strings")),
16654 }
16655 });
16656
16657 define(interp, "cologne_phonetic", Some(1), |_, args| {
16659 match &args[0] {
16660 Value::String(s) => {
16661 let code = compute_cologne(s);
16662 Ok(Value::String(Rc::new(code)))
16663 }
16664 _ => Err(RuntimeError::new("cologne_phonetic() requires string")),
16665 }
16666 });
16667
16668 define(interp, "detect_language", Some(1), |_, args| {
16674 match &args[0] {
16675 Value::String(s) => {
16676 if let Some(info) = detect(s) {
16677 let lang_code = match info.lang() {
16678 Lang::Eng => "en",
16679 Lang::Spa => "es",
16680 Lang::Fra => "fr",
16681 Lang::Deu => "de",
16682 Lang::Ita => "it",
16683 Lang::Por => "pt",
16684 Lang::Rus => "ru",
16685 Lang::Ara => "ar",
16686 Lang::Hin => "hi",
16687 Lang::Cmn => "zh",
16688 Lang::Jpn => "ja",
16689 Lang::Kor => "ko",
16690 Lang::Nld => "nl",
16691 Lang::Swe => "sv",
16692 Lang::Tur => "tr",
16693 Lang::Pol => "pl",
16694 Lang::Ukr => "uk",
16695 Lang::Ces => "cs",
16696 Lang::Dan => "da",
16697 Lang::Fin => "fi",
16698 Lang::Ell => "el",
16699 Lang::Heb => "he",
16700 Lang::Hun => "hu",
16701 Lang::Ind => "id",
16702 Lang::Nob => "no",
16703 Lang::Ron => "ro",
16704 Lang::Slk => "sk",
16705 Lang::Tha => "th",
16706 Lang::Vie => "vi",
16707 _ => "unknown",
16708 };
16709 Ok(Value::String(Rc::new(lang_code.to_string())))
16710 } else {
16711 Ok(Value::String(Rc::new("unknown".to_string())))
16712 }
16713 }
16714 _ => Err(RuntimeError::new("detect_language() requires string")),
16715 }
16716 });
16717
16718 define(
16720 interp,
16721 "detect_language_confidence",
16722 Some(1),
16723 |_, args| match &args[0] {
16724 Value::String(s) => {
16725 if let Some(info) = detect(s) {
16726 let lang_code = match info.lang() {
16727 Lang::Eng => "en",
16728 Lang::Spa => "es",
16729 Lang::Fra => "fr",
16730 Lang::Deu => "de",
16731 Lang::Ita => "it",
16732 Lang::Por => "pt",
16733 Lang::Rus => "ru",
16734 Lang::Ara => "ar",
16735 Lang::Cmn => "zh",
16736 Lang::Jpn => "ja",
16737 _ => "unknown",
16738 };
16739 let confidence = info.confidence();
16740 let mut map = HashMap::new();
16741 map.insert(
16742 "lang".to_string(),
16743 Value::String(Rc::new(lang_code.to_string())),
16744 );
16745 map.insert("confidence".to_string(), Value::Float(confidence as f64));
16746 Ok(Value::Map(Rc::new(RefCell::new(map))))
16747 } else {
16748 let mut map = HashMap::new();
16749 map.insert(
16750 "lang".to_string(),
16751 Value::String(Rc::new("unknown".to_string())),
16752 );
16753 map.insert("confidence".to_string(), Value::Float(0.0));
16754 Ok(Value::Map(Rc::new(RefCell::new(map))))
16755 }
16756 }
16757 _ => Err(RuntimeError::new(
16758 "detect_language_confidence() requires string",
16759 )),
16760 },
16761 );
16762
16763 define(
16765 interp,
16766 "detect_script_whatlang",
16767 Some(1),
16768 |_, args| match &args[0] {
16769 Value::String(s) => {
16770 if let Some(info) = detect(s) {
16771 let script_name = match info.script() {
16772 WhatLangScript::Latin => "Latin",
16773 WhatLangScript::Cyrillic => "Cyrillic",
16774 WhatLangScript::Arabic => "Arabic",
16775 WhatLangScript::Devanagari => "Devanagari",
16776 WhatLangScript::Ethiopic => "Ethiopic",
16777 WhatLangScript::Georgian => "Georgian",
16778 WhatLangScript::Greek => "Greek",
16779 WhatLangScript::Gujarati => "Gujarati",
16780 WhatLangScript::Gurmukhi => "Gurmukhi",
16781 WhatLangScript::Hangul => "Hangul",
16782 WhatLangScript::Hebrew => "Hebrew",
16783 WhatLangScript::Hiragana => "Hiragana",
16784 WhatLangScript::Kannada => "Kannada",
16785 WhatLangScript::Katakana => "Katakana",
16786 WhatLangScript::Khmer => "Khmer",
16787 WhatLangScript::Malayalam => "Malayalam",
16788 WhatLangScript::Mandarin => "Mandarin",
16789 WhatLangScript::Myanmar => "Myanmar",
16790 WhatLangScript::Oriya => "Oriya",
16791 WhatLangScript::Sinhala => "Sinhala",
16792 WhatLangScript::Tamil => "Tamil",
16793 WhatLangScript::Telugu => "Telugu",
16794 WhatLangScript::Thai => "Thai",
16795 WhatLangScript::Bengali => "Bengali",
16796 WhatLangScript::Armenian => "Armenian",
16797 };
16798 Ok(Value::String(Rc::new(script_name.to_string())))
16799 } else {
16800 Ok(Value::String(Rc::new("Unknown".to_string())))
16801 }
16802 }
16803 _ => Err(RuntimeError::new(
16804 "detect_script_whatlang() requires string",
16805 )),
16806 },
16807 );
16808
16809 define(interp, "is_language", Some(2), |_, args| {
16811 match (&args[0], &args[1]) {
16812 (Value::String(s), Value::String(lang)) => {
16813 if let Some(info) = detect(s) {
16814 let detected = match info.lang() {
16815 Lang::Eng => "en",
16816 Lang::Spa => "es",
16817 Lang::Fra => "fr",
16818 Lang::Deu => "de",
16819 Lang::Ita => "it",
16820 Lang::Por => "pt",
16821 Lang::Rus => "ru",
16822 _ => "unknown",
16823 };
16824 Ok(Value::Bool(detected == lang.as_str()))
16825 } else {
16826 Ok(Value::Bool(false))
16827 }
16828 }
16829 _ => Err(RuntimeError::new(
16830 "is_language() requires string and language code",
16831 )),
16832 }
16833 });
16834
16835 define(interp, "token_count", Some(1), |_, args| match &args[0] {
16841 Value::String(s) => {
16842 if let Ok(bpe) = cl100k_base() {
16843 let tokens = bpe.encode_with_special_tokens(s);
16844 Ok(Value::Int(tokens.len() as i64))
16845 } else {
16846 Err(RuntimeError::new("Failed to initialize tokenizer"))
16847 }
16848 }
16849 _ => Err(RuntimeError::new("token_count() requires string")),
16850 });
16851
16852 define(interp, "token_count_model", Some(2), |_, args| {
16854 match (&args[0], &args[1]) {
16855 (Value::String(s), Value::String(model)) => {
16856 let bpe_result = match model.as_str() {
16857 "gpt4" | "gpt-4" | "claude" | "cl100k" => cl100k_base(),
16858 "gpt3" | "gpt-3" | "p50k" => p50k_base(),
16859 "codex" | "r50k" => r50k_base(),
16860 _ => cl100k_base(), };
16862 if let Ok(bpe) = bpe_result {
16863 let tokens = bpe.encode_with_special_tokens(s);
16864 Ok(Value::Int(tokens.len() as i64))
16865 } else {
16866 Err(RuntimeError::new("Failed to initialize tokenizer"))
16867 }
16868 }
16869 _ => Err(RuntimeError::new(
16870 "token_count_model() requires string and model name",
16871 )),
16872 }
16873 });
16874
16875 define(interp, "tokenize_ids", Some(1), |_, args| match &args[0] {
16877 Value::String(s) => {
16878 if let Ok(bpe) = cl100k_base() {
16879 let tokens = bpe.encode_with_special_tokens(s);
16880 let values: Vec<Value> = tokens.into_iter().map(|t| Value::Int(t as i64)).collect();
16881 Ok(Value::Array(Rc::new(RefCell::new(values))))
16882 } else {
16883 Err(RuntimeError::new("Failed to initialize tokenizer"))
16884 }
16885 }
16886 _ => Err(RuntimeError::new("tokenize_ids() requires string")),
16887 });
16888
16889 define(interp, "truncate_tokens", Some(2), |_, args| {
16891 match (&args[0], &args[1]) {
16892 (Value::String(s), Value::Int(max_tokens)) => {
16893 if let Ok(bpe) = cl100k_base() {
16894 let tokens = bpe.encode_with_special_tokens(s);
16895 if tokens.len() <= *max_tokens as usize {
16896 Ok(Value::String(s.clone()))
16897 } else {
16898 let truncated: Vec<usize> =
16899 tokens.into_iter().take(*max_tokens as usize).collect();
16900 if let Ok(decoded) = bpe.decode(truncated) {
16901 Ok(Value::String(Rc::new(decoded)))
16902 } else {
16903 Err(RuntimeError::new("Failed to decode tokens"))
16904 }
16905 }
16906 } else {
16907 Err(RuntimeError::new("Failed to initialize tokenizer"))
16908 }
16909 }
16910 _ => Err(RuntimeError::new(
16911 "truncate_tokens() requires string and max tokens",
16912 )),
16913 }
16914 });
16915
16916 define(interp, "estimate_cost", Some(3), |_, args| {
16918 match (&args[0], &args[1], &args[2]) {
16919 (Value::String(s), Value::Float(input_cost), Value::Float(output_cost)) => {
16920 if let Ok(bpe) = cl100k_base() {
16921 let tokens = bpe.encode_with_special_tokens(s);
16922 let count = tokens.len() as f64;
16923 let input_total = (count / 1000.0) * input_cost;
16925 let output_total = (count / 1000.0) * output_cost;
16926 let mut map = HashMap::new();
16927 map.insert("tokens".to_string(), Value::Int(tokens.len() as i64));
16928 map.insert("input_cost".to_string(), Value::Float(input_total));
16929 map.insert("output_cost".to_string(), Value::Float(output_total));
16930 Ok(Value::Map(Rc::new(RefCell::new(map))))
16931 } else {
16932 Err(RuntimeError::new("Failed to initialize tokenizer"))
16933 }
16934 }
16935 _ => Err(RuntimeError::new(
16936 "estimate_cost() requires string, input cost, output cost",
16937 )),
16938 }
16939 });
16940
16941 define(interp, "stem", Some(1), |_, args| match &args[0] {
16947 Value::String(s) => {
16948 let stemmer = Stemmer::create(StemAlgorithm::English);
16949 let stemmed = stemmer.stem(s);
16950 Ok(Value::String(Rc::new(stemmed.to_string())))
16951 }
16952 _ => Err(RuntimeError::new("stem() requires string")),
16953 });
16954
16955 define(interp, "stem_language", Some(2), |_, args| {
16957 match (&args[0], &args[1]) {
16958 (Value::String(s), Value::String(lang)) => {
16959 let algorithm = match lang.as_str() {
16960 "en" | "english" => StemAlgorithm::English,
16961 "fr" | "french" => StemAlgorithm::French,
16962 "de" | "german" => StemAlgorithm::German,
16963 "es" | "spanish" => StemAlgorithm::Spanish,
16964 "it" | "italian" => StemAlgorithm::Italian,
16965 "pt" | "portuguese" => StemAlgorithm::Portuguese,
16966 "nl" | "dutch" => StemAlgorithm::Dutch,
16967 "sv" | "swedish" => StemAlgorithm::Swedish,
16968 "no" | "norwegian" => StemAlgorithm::Norwegian,
16969 "da" | "danish" => StemAlgorithm::Danish,
16970 "fi" | "finnish" => StemAlgorithm::Finnish,
16971 "ru" | "russian" => StemAlgorithm::Russian,
16972 "ro" | "romanian" => StemAlgorithm::Romanian,
16973 "hu" | "hungarian" => StemAlgorithm::Hungarian,
16974 "tr" | "turkish" => StemAlgorithm::Turkish,
16975 "ar" | "arabic" => StemAlgorithm::Arabic,
16976 _ => StemAlgorithm::English,
16977 };
16978 let stemmer = Stemmer::create(algorithm);
16979 let stemmed = stemmer.stem(s);
16980 Ok(Value::String(Rc::new(stemmed.to_string())))
16981 }
16982 _ => Err(RuntimeError::new(
16983 "stem_language() requires string and language code",
16984 )),
16985 }
16986 });
16987
16988 define(interp, "stem_all", Some(1), |_, args| match &args[0] {
16990 Value::Array(arr) => {
16991 let stemmer = Stemmer::create(StemAlgorithm::English);
16992 let arr_ref = arr.borrow();
16993 let results: Vec<Value> = arr_ref
16994 .iter()
16995 .filter_map(|v| {
16996 if let Value::String(s) = v {
16997 Some(Value::String(Rc::new(stemmer.stem(s).to_string())))
16998 } else {
16999 None
17000 }
17001 })
17002 .collect();
17003 Ok(Value::Array(Rc::new(RefCell::new(results))))
17004 }
17005 _ => Err(RuntimeError::new("stem_all() requires array of strings")),
17006 });
17007
17008 define(interp, "is_stopword", Some(1), |_, args| match &args[0] {
17014 Value::String(s) => {
17015 let word = s.to_lowercase();
17016 let stopwords = get_stopwords("en");
17017 Ok(Value::Bool(stopwords.contains(&word.as_str())))
17018 }
17019 _ => Err(RuntimeError::new("is_stopword() requires string")),
17020 });
17021
17022 define(interp, "is_stopword_language", Some(2), |_, args| {
17024 match (&args[0], &args[1]) {
17025 (Value::String(s), Value::String(lang)) => {
17026 let word = s.to_lowercase();
17027 let stopwords = get_stopwords(lang);
17028 Ok(Value::Bool(stopwords.contains(&word.as_str())))
17029 }
17030 _ => Err(RuntimeError::new(
17031 "is_stopword_language() requires string and language",
17032 )),
17033 }
17034 });
17035
17036 define(interp, "remove_stopwords", Some(1), |_, args| {
17038 match &args[0] {
17039 Value::Array(arr) => {
17040 let stopwords = get_stopwords("en");
17041 let arr_ref = arr.borrow();
17042 let results: Vec<Value> = arr_ref
17043 .iter()
17044 .filter(|v| {
17045 if let Value::String(s) = v {
17046 !stopwords.contains(&s.to_lowercase().as_str())
17047 } else {
17048 true
17049 }
17050 })
17051 .cloned()
17052 .collect();
17053 Ok(Value::Array(Rc::new(RefCell::new(results))))
17054 }
17055 _ => Err(RuntimeError::new(
17056 "remove_stopwords() requires array of strings",
17057 )),
17058 }
17059 });
17060
17061 define(
17063 interp,
17064 "remove_stopwords_text",
17065 Some(1),
17066 |_, args| match &args[0] {
17067 Value::String(s) => {
17068 let stopwords = get_stopwords("en");
17069 let words: Vec<&str> = s
17070 .split_whitespace()
17071 .filter(|w| !stopwords.contains(&w.to_lowercase().as_str()))
17072 .collect();
17073 Ok(Value::String(Rc::new(words.join(" "))))
17074 }
17075 _ => Err(RuntimeError::new("remove_stopwords_text() requires string")),
17076 },
17077 );
17078
17079 define(
17081 interp,
17082 "get_stopwords_list",
17083 Some(1),
17084 |_, args| match &args[0] {
17085 Value::String(lang) => {
17086 let stopwords = get_stopwords(lang);
17087 let values: Vec<Value> = stopwords
17088 .iter()
17089 .map(|s| Value::String(Rc::new(s.to_string())))
17090 .collect();
17091 Ok(Value::Array(Rc::new(RefCell::new(values))))
17092 }
17093 _ => Err(RuntimeError::new(
17094 "get_stopwords_list() requires language code",
17095 )),
17096 },
17097 );
17098
17099 define(interp, "ngrams", Some(2), |_, args| {
17105 match (&args[0], &args[1]) {
17106 (Value::String(s), Value::Int(n)) => {
17107 let words: Vec<&str> = s.split_whitespace().collect();
17108 let n = *n as usize;
17109 if n == 0 || n > words.len() {
17110 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17111 }
17112 let ngrams: Vec<Value> = words
17113 .windows(n)
17114 .map(|w| Value::String(Rc::new(w.join(" "))))
17115 .collect();
17116 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
17117 }
17118 _ => Err(RuntimeError::new("ngrams() requires string and n")),
17119 }
17120 });
17121
17122 define(interp, "char_ngrams", Some(2), |_, args| {
17124 match (&args[0], &args[1]) {
17125 (Value::String(s), Value::Int(n)) => {
17126 let chars: Vec<char> = s.chars().collect();
17127 let n = *n as usize;
17128 if n == 0 || n > chars.len() {
17129 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17130 }
17131 let ngrams: Vec<Value> = chars
17132 .windows(n)
17133 .map(|w| Value::String(Rc::new(w.iter().collect())))
17134 .collect();
17135 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
17136 }
17137 _ => Err(RuntimeError::new("char_ngrams() requires string and n")),
17138 }
17139 });
17140
17141 define(interp, "shingles", Some(2), |_, args| {
17143 match (&args[0], &args[1]) {
17144 (Value::String(s), Value::Int(n)) => {
17145 let words: Vec<&str> = s.split_whitespace().collect();
17146 let n = *n as usize;
17147 if n == 0 || n > words.len() {
17148 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17149 }
17150 let mut seen = std::collections::HashSet::new();
17151 let shingles: Vec<Value> = words
17152 .windows(n)
17153 .filter_map(|w| {
17154 let s = w.join(" ");
17155 if seen.insert(s.clone()) {
17156 Some(Value::String(Rc::new(s)))
17157 } else {
17158 None
17159 }
17160 })
17161 .collect();
17162 Ok(Value::Array(Rc::new(RefCell::new(shingles))))
17163 }
17164 _ => Err(RuntimeError::new("shingles() requires string and n")),
17165 }
17166 });
17167
17168 define(interp, "jaccard_similarity", Some(2), |_, args| {
17170 match (&args[0], &args[1]) {
17171 (Value::Array(a), Value::Array(b)) => {
17172 let a_ref = a.borrow();
17173 let b_ref = b.borrow();
17174 let set_a: std::collections::HashSet<String> = a_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 set_b: std::collections::HashSet<String> = b_ref
17185 .iter()
17186 .filter_map(|v| {
17187 if let Value::String(s) = v {
17188 Some(s.to_string())
17189 } else {
17190 None
17191 }
17192 })
17193 .collect();
17194 let intersection = set_a.intersection(&set_b).count();
17195 let union = set_a.union(&set_b).count();
17196 if union == 0 {
17197 Ok(Value::Float(0.0))
17198 } else {
17199 Ok(Value::Float(intersection as f64 / union as f64))
17200 }
17201 }
17202 _ => Err(RuntimeError::new(
17203 "jaccard_similarity() requires two arrays",
17204 )),
17205 }
17206 });
17207
17208 define(interp, "minhash_signature", Some(2), |_, args| {
17210 match (&args[0], &args[1]) {
17211 (Value::Array(arr), Value::Int(num_hashes)) => {
17212 let arr_ref = arr.borrow();
17213 let items: std::collections::HashSet<String> = arr_ref
17214 .iter()
17215 .filter_map(|v| {
17216 if let Value::String(s) = v {
17217 Some(s.to_string())
17218 } else {
17219 None
17220 }
17221 })
17222 .collect();
17223
17224 let mut signature: Vec<Value> = Vec::with_capacity(*num_hashes as usize);
17226 for i in 0..*num_hashes {
17227 let mut min_hash: u64 = u64::MAX;
17228 for item in &items {
17229 let hash = compute_hash(item, i as u64);
17230 if hash < min_hash {
17231 min_hash = hash;
17232 }
17233 }
17234 signature.push(Value::Int(min_hash as i64));
17235 }
17236 Ok(Value::Array(Rc::new(RefCell::new(signature))))
17237 }
17238 _ => Err(RuntimeError::new(
17239 "minhash_signature() requires array and num_hashes",
17240 )),
17241 }
17242 });
17243
17244 define(interp, "preprocess_text", Some(1), |_, args| {
17250 match &args[0] {
17251 Value::String(s) => {
17252 let lower = s.to_lowercase();
17254 let clean: String = lower
17256 .chars()
17257 .filter(|c| c.is_alphanumeric() || c.is_whitespace())
17258 .collect();
17259 let normalized: String = clean.split_whitespace().collect::<Vec<_>>().join(" ");
17261 Ok(Value::String(Rc::new(normalized)))
17262 }
17263 _ => Err(RuntimeError::new("preprocess_text() requires string")),
17264 }
17265 });
17266
17267 define(interp, "tokenize_words", Some(1), |_, args| {
17269 match &args[0] {
17270 Value::String(s) => {
17271 let words: Vec<Value> = s
17272 .split_whitespace()
17273 .map(|w| Value::String(Rc::new(w.to_string())))
17274 .collect();
17275 Ok(Value::Array(Rc::new(RefCell::new(words))))
17276 }
17277 _ => Err(RuntimeError::new("tokenize_words() requires string")),
17278 }
17279 });
17280
17281 define(interp, "extract_keywords", Some(1), |_, args| {
17283 match &args[0] {
17284 Value::String(s) => {
17285 let stopwords = get_stopwords("en");
17286 let words: Vec<Value> = s
17287 .split_whitespace()
17288 .filter(|w| {
17289 let lower = w.to_lowercase();
17290 !stopwords.contains(&lower.as_str()) && lower.len() > 2
17291 })
17292 .map(|w| Value::String(Rc::new(w.to_lowercase())))
17293 .collect();
17294 Ok(Value::Array(Rc::new(RefCell::new(words))))
17295 }
17296 _ => Err(RuntimeError::new("extract_keywords() requires string")),
17297 }
17298 });
17299
17300 define(interp, "word_frequency", Some(1), |_, args| {
17302 match &args[0] {
17303 Value::String(s) => {
17304 let mut freq: HashMap<String, i64> = HashMap::new();
17305 for word in s.split_whitespace() {
17306 let lower = word.to_lowercase();
17307 *freq.entry(lower).or_insert(0) += 1;
17308 }
17309 let map: HashMap<String, Value> =
17310 freq.into_iter().map(|(k, v)| (k, Value::Int(v))).collect();
17311 Ok(Value::Map(Rc::new(RefCell::new(map))))
17312 }
17313 _ => Err(RuntimeError::new("word_frequency() requires string")),
17314 }
17315 });
17316
17317 define(interp, "sentiment_words", Some(1), |_, args| {
17323 match &args[0] {
17324 Value::String(s) => {
17325 let positive = vec![
17326 "good",
17327 "great",
17328 "excellent",
17329 "amazing",
17330 "wonderful",
17331 "fantastic",
17332 "love",
17333 "happy",
17334 "joy",
17335 "beautiful",
17336 "awesome",
17337 "perfect",
17338 "best",
17339 "brilliant",
17340 "delightful",
17341 "pleasant",
17342 "positive",
17343 ];
17344 let negative = vec![
17345 "bad",
17346 "terrible",
17347 "awful",
17348 "horrible",
17349 "hate",
17350 "sad",
17351 "angry",
17352 "worst",
17353 "poor",
17354 "negative",
17355 "disappointing",
17356 "ugly",
17357 "disgusting",
17358 "painful",
17359 "miserable",
17360 "annoying",
17361 ];
17362
17363 let lower = s.to_lowercase();
17364 let words: Vec<&str> = lower.split_whitespace().collect();
17365 let pos_count: i64 = words.iter().filter(|w| positive.contains(w)).count() as i64;
17366 let neg_count: i64 = words.iter().filter(|w| negative.contains(w)).count() as i64;
17367
17368 let mut map = HashMap::new();
17369 map.insert("positive".to_string(), Value::Int(pos_count));
17370 map.insert("negative".to_string(), Value::Int(neg_count));
17371 map.insert("total".to_string(), Value::Int(words.len() as i64));
17372
17373 let score = if pos_count + neg_count > 0 {
17374 (pos_count - neg_count) as f64 / (pos_count + neg_count) as f64
17375 } else {
17376 0.0
17377 };
17378 map.insert("score".to_string(), Value::Float(score));
17379
17380 Ok(Value::Map(Rc::new(RefCell::new(map))))
17381 }
17382 _ => Err(RuntimeError::new("sentiment_words() requires string")),
17383 }
17384 });
17385
17386 define(interp, "has_question", Some(1), |_, args| match &args[0] {
17388 Value::String(s) => {
17389 let has_q_mark = s.contains('?');
17390 let lower = s.to_lowercase();
17391 let question_words = [
17392 "what", "where", "when", "why", "how", "who", "which", "whose", "whom",
17393 ];
17394 let starts_with_q = question_words.iter().any(|w| lower.starts_with(w));
17395 Ok(Value::Bool(has_q_mark || starts_with_q))
17396 }
17397 _ => Err(RuntimeError::new("has_question() requires string")),
17398 });
17399
17400 define(interp, "has_exclamation", Some(1), |_, args| {
17402 match &args[0] {
17403 Value::String(s) => Ok(Value::Bool(s.contains('!'))),
17404 _ => Err(RuntimeError::new("has_exclamation() requires string")),
17405 }
17406 });
17407
17408 define(interp, "text_formality", Some(1), |_, args| {
17410 match &args[0] {
17411 Value::String(s) => {
17412 let lower = s.to_lowercase();
17413 let informal_markers = vec![
17414 "gonna", "wanna", "gotta", "kinda", "sorta", "dunno", "yeah", "yep", "nope",
17415 "ok", "lol", "omg", "btw", "u", "ur", "r", "y", "2", "4",
17416 ];
17417 let formal_markers = vec![
17418 "therefore",
17419 "furthermore",
17420 "moreover",
17421 "consequently",
17422 "nevertheless",
17423 "however",
17424 "whereas",
17425 "hereby",
17426 "respectfully",
17427 "sincerely",
17428 "accordingly",
17429 ];
17430
17431 let words: Vec<&str> = lower.split_whitespace().collect();
17432 let informal_count = words
17433 .iter()
17434 .filter(|w| informal_markers.contains(w))
17435 .count();
17436 let formal_count = words.iter().filter(|w| formal_markers.contains(w)).count();
17437
17438 let score = if informal_count + formal_count > 0 {
17439 formal_count as f64 / (informal_count + formal_count) as f64
17440 } else {
17441 0.5 };
17443
17444 Ok(Value::Float(score))
17445 }
17446 _ => Err(RuntimeError::new("text_formality() requires string")),
17447 }
17448 });
17449
17450 define(interp, "sentiment_vader", Some(1), |_, args| {
17456 match &args[0] {
17457 Value::String(s) => {
17458 let result = compute_vader_sentiment(s);
17459 let mut map = HashMap::new();
17460 map.insert("positive".to_string(), Value::Float(result.0));
17461 map.insert("negative".to_string(), Value::Float(result.1));
17462 map.insert("neutral".to_string(), Value::Float(result.2));
17463 map.insert("compound".to_string(), Value::Float(result.3));
17464 Ok(Value::Map(Rc::new(RefCell::new(map))))
17465 }
17466 _ => Err(RuntimeError::new("sentiment_vader() requires string")),
17467 }
17468 });
17469
17470 define(interp, "emotion_detect", Some(1), |_, args| {
17472 match &args[0] {
17473 Value::String(s) => {
17474 let emotions = compute_emotions(s);
17475 let map: HashMap<String, Value> = emotions
17476 .into_iter()
17477 .map(|(k, v)| (k, Value::Float(v)))
17478 .collect();
17479 Ok(Value::Map(Rc::new(RefCell::new(map))))
17480 }
17481 _ => Err(RuntimeError::new("emotion_detect() requires string")),
17482 }
17483 });
17484
17485 define(interp, "intensity_score", Some(1), |_, args| {
17487 match &args[0] {
17488 Value::String(s) => {
17489 let score = compute_intensity(s);
17490 Ok(Value::Float(score))
17491 }
17492 _ => Err(RuntimeError::new("intensity_score() requires string")),
17493 }
17494 });
17495
17496 define(interp, "detect_sarcasm", Some(1), |_, args| {
17502 match &args[0] {
17503 Value::String(s) => {
17504 let result = compute_sarcasm_score(s);
17505 let mut map = HashMap::new();
17506 map.insert("score".to_string(), Value::Float(result.0));
17507 map.insert("confidence".to_string(), Value::Float(result.1));
17508 let markers: Vec<Value> = result
17509 .2
17510 .into_iter()
17511 .map(|m| Value::String(Rc::new(m)))
17512 .collect();
17513 map.insert(
17514 "markers".to_string(),
17515 Value::Array(Rc::new(RefCell::new(markers))),
17516 );
17517 Ok(Value::Map(Rc::new(RefCell::new(map))))
17518 }
17519 _ => Err(RuntimeError::new("detect_sarcasm() requires string")),
17520 }
17521 });
17522
17523 define(interp, "is_sarcastic", Some(1), |_, args| match &args[0] {
17525 Value::String(s) => {
17526 let result = compute_sarcasm_score(s);
17527 Ok(Value::Bool(result.0 > 0.5))
17528 }
17529 _ => Err(RuntimeError::new("is_sarcastic() requires string")),
17530 });
17531
17532 define(interp, "detect_irony", Some(1), |_, args| match &args[0] {
17534 Value::String(s) => {
17535 let score = compute_irony_score(s);
17536 Ok(Value::Float(score))
17537 }
17538 _ => Err(RuntimeError::new("detect_irony() requires string")),
17539 });
17540
17541 define(interp, "extract_emails", Some(1), |_, args| {
17547 match &args[0] {
17548 Value::String(s) => {
17549 let re = Regex::new(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}").unwrap();
17550 let emails: Vec<Value> = re
17551 .find_iter(s)
17552 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17553 .collect();
17554 Ok(Value::Array(Rc::new(RefCell::new(emails))))
17555 }
17556 _ => Err(RuntimeError::new("extract_emails() requires string")),
17557 }
17558 });
17559
17560 define(interp, "extract_urls", Some(1), |_, args| match &args[0] {
17562 Value::String(s) => {
17563 let re = Regex::new(r"https?://[^\s<>\[\]{}|\\^]+").unwrap();
17564 let urls: Vec<Value> = re
17565 .find_iter(s)
17566 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17567 .collect();
17568 Ok(Value::Array(Rc::new(RefCell::new(urls))))
17569 }
17570 _ => Err(RuntimeError::new("extract_urls() requires string")),
17571 });
17572
17573 define(
17575 interp,
17576 "extract_phone_numbers",
17577 Some(1),
17578 |_, args| match &args[0] {
17579 Value::String(s) => {
17580 let re =
17581 Regex::new(r"(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}")
17582 .unwrap();
17583 let phones: Vec<Value> = re
17584 .find_iter(s)
17585 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17586 .collect();
17587 Ok(Value::Array(Rc::new(RefCell::new(phones))))
17588 }
17589 _ => Err(RuntimeError::new("extract_phone_numbers() requires string")),
17590 },
17591 );
17592
17593 define(interp, "extract_dates", Some(1), |_, args| {
17595 match &args[0] {
17596 Value::String(s) => {
17597 let patterns = vec![
17599 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}",
17603 r"\d{1,2}\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{4}",
17604 ];
17605 let mut dates = Vec::new();
17606 for pattern in patterns {
17607 if let Ok(re) = Regex::new(pattern) {
17608 for m in re.find_iter(s) {
17609 dates.push(Value::String(Rc::new(m.as_str().to_string())));
17610 }
17611 }
17612 }
17613 Ok(Value::Array(Rc::new(RefCell::new(dates))))
17614 }
17615 _ => Err(RuntimeError::new("extract_dates() requires string")),
17616 }
17617 });
17618
17619 define(interp, "extract_money", Some(1), |_, args| match &args[0] {
17621 Value::String(s) => {
17622 let re = Regex::new(r"[$€£¥]\s*\d+(?:,\d{3})*(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:dollars?|euros?|pounds?|USD|EUR|GBP)").unwrap();
17623 let money: Vec<Value> = re
17624 .find_iter(s)
17625 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17626 .collect();
17627 Ok(Value::Array(Rc::new(RefCell::new(money))))
17628 }
17629 _ => Err(RuntimeError::new("extract_money() requires string")),
17630 });
17631
17632 define(interp, "extract_hashtags", Some(1), |_, args| {
17634 match &args[0] {
17635 Value::String(s) => {
17636 let re = Regex::new(r"#\w+").unwrap();
17637 let tags: Vec<Value> = re
17638 .find_iter(s)
17639 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17640 .collect();
17641 Ok(Value::Array(Rc::new(RefCell::new(tags))))
17642 }
17643 _ => Err(RuntimeError::new("extract_hashtags() requires string")),
17644 }
17645 });
17646
17647 define(interp, "extract_mentions", Some(1), |_, args| {
17649 match &args[0] {
17650 Value::String(s) => {
17651 let re = Regex::new(r"@\w+").unwrap();
17652 let mentions: Vec<Value> = re
17653 .find_iter(s)
17654 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17655 .collect();
17656 Ok(Value::Array(Rc::new(RefCell::new(mentions))))
17657 }
17658 _ => Err(RuntimeError::new("extract_mentions() requires string")),
17659 }
17660 });
17661
17662 define(interp, "extract_numbers", Some(1), |_, args| {
17664 match &args[0] {
17665 Value::String(s) => {
17666 let re = Regex::new(r"-?\d+(?:,\d{3})*(?:\.\d+)?").unwrap();
17667 let numbers: Vec<Value> = re
17668 .find_iter(s)
17669 .filter_map(|m| {
17670 let num_str = m.as_str().replace(",", "");
17671 if let Ok(n) = num_str.parse::<f64>() {
17672 Some(Value::Float(n))
17673 } else {
17674 None
17675 }
17676 })
17677 .collect();
17678 Ok(Value::Array(Rc::new(RefCell::new(numbers))))
17679 }
17680 _ => Err(RuntimeError::new("extract_numbers() requires string")),
17681 }
17682 });
17683
17684 define(interp, "extract_entities", Some(1), |_, args| {
17686 match &args[0] {
17687 Value::String(s) => {
17688 let re = Regex::new(r"(?:[.!?]\s+)?([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)").unwrap();
17690 let mut entities = std::collections::HashSet::new();
17691 for cap in re.captures_iter(s) {
17692 if let Some(m) = cap.get(1) {
17693 let entity = m.as_str().to_string();
17694 let starters = [
17696 "The", "A", "An", "This", "That", "It", "I", "We", "They", "He", "She",
17697 ];
17698 if !starters.contains(&entity.as_str()) {
17699 entities.insert(entity);
17700 }
17701 }
17702 }
17703 let results: Vec<Value> = entities
17704 .into_iter()
17705 .map(|e| Value::String(Rc::new(e)))
17706 .collect();
17707 Ok(Value::Array(Rc::new(RefCell::new(results))))
17708 }
17709 _ => Err(RuntimeError::new("extract_entities() requires string")),
17710 }
17711 });
17712
17713 define(interp, "text_hash_vector", Some(2), |_, args| {
17719 match (&args[0], &args[1]) {
17720 (Value::String(s), Value::Int(dims)) => {
17721 let dims = *dims as usize;
17722 let mut vector = vec![0.0f64; dims];
17723
17724 for word in s.to_lowercase().split_whitespace() {
17726 let hash = compute_hash(word, 0);
17727 let idx = (hash as usize) % dims;
17728 vector[idx] += 1.0;
17729 }
17730
17731 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
17733 if magnitude > 0.0 {
17734 for v in vector.iter_mut() {
17735 *v /= magnitude;
17736 }
17737 }
17738
17739 let values: Vec<Value> = vector.into_iter().map(Value::Float).collect();
17740 Ok(Value::Array(Rc::new(RefCell::new(values))))
17741 }
17742 _ => Err(RuntimeError::new(
17743 "text_hash_vector() requires string and dimensions",
17744 )),
17745 }
17746 });
17747
17748 define(interp, "text_fingerprint", Some(1), |_, args| {
17750 match &args[0] {
17751 Value::String(s) => {
17752 let lower = s.to_lowercase();
17754 let words: Vec<&str> = lower.split_whitespace().collect();
17755
17756 let mut fp: u64 = 0;
17757 for (i, word) in words.iter().enumerate() {
17758 let h = compute_hash(word, i as u64);
17759 fp ^= h.rotate_left((i % 64) as u32);
17760 }
17761
17762 Ok(Value::String(Rc::new(format!("{:016x}", fp))))
17763 }
17764 _ => Err(RuntimeError::new("text_fingerprint() requires string")),
17765 }
17766 });
17767
17768 define(interp, "cosine_similarity", Some(2), |_, args| {
17770 match (&args[0], &args[1]) {
17771 (Value::Array(a), Value::Array(b)) => {
17772 let a_ref = a.borrow();
17773 let b_ref = b.borrow();
17774
17775 if a_ref.len() != b_ref.len() {
17776 return Err(RuntimeError::new("Vectors must have same length"));
17777 }
17778
17779 let mut dot = 0.0;
17780 let mut mag_a = 0.0;
17781 let mut mag_b = 0.0;
17782
17783 for (va, vb) in a_ref.iter().zip(b_ref.iter()) {
17784 let fa = match va {
17785 Value::Float(f) => *f,
17786 Value::Int(i) => *i as f64,
17787 _ => continue,
17788 };
17789 let fb = match vb {
17790 Value::Float(f) => *f,
17791 Value::Int(i) => *i as f64,
17792 _ => continue,
17793 };
17794 dot += fa * fb;
17795 mag_a += fa * fa;
17796 mag_b += fb * fb;
17797 }
17798
17799 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
17800 if denom == 0.0 {
17801 Ok(Value::Float(0.0))
17802 } else {
17803 Ok(Value::Float(dot / denom))
17804 }
17805 }
17806 _ => Err(RuntimeError::new("cosine_similarity() requires two arrays")),
17807 }
17808 });
17809
17810 define(interp, "text_similarity_embedding", Some(2), |_, args| {
17812 match (&args[0], &args[1]) {
17813 (Value::String(a), Value::String(b)) => {
17814 let dims = 128;
17815
17816 let vec_a = create_hash_vector(a, dims);
17818 let vec_b = create_hash_vector(b, dims);
17819
17820 let mut dot = 0.0;
17822 let mut mag_a = 0.0;
17823 let mut mag_b = 0.0;
17824
17825 for i in 0..dims {
17826 dot += vec_a[i] * vec_b[i];
17827 mag_a += vec_a[i] * vec_a[i];
17828 mag_b += vec_b[i] * vec_b[i];
17829 }
17830
17831 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
17832 if denom == 0.0 {
17833 Ok(Value::Float(0.0))
17834 } else {
17835 Ok(Value::Float(dot / denom))
17836 }
17837 }
17838 _ => Err(RuntimeError::new(
17839 "text_similarity_embedding() requires two strings",
17840 )),
17841 }
17842 });
17843
17844 define(
17850 interp,
17851 "flesch_reading_ease",
17852 Some(1),
17853 |_, args| match &args[0] {
17854 Value::String(s) => {
17855 let (words, sentences, syllables) = count_text_stats(s);
17856 if words == 0 || sentences == 0 {
17857 return Ok(Value::Float(0.0));
17858 }
17859 let score = 206.835
17860 - 1.015 * (words as f64 / sentences as f64)
17861 - 84.6 * (syllables as f64 / words as f64);
17862 Ok(Value::Float(score.max(0.0).min(100.0)))
17863 }
17864 _ => Err(RuntimeError::new("flesch_reading_ease() requires string")),
17865 },
17866 );
17867
17868 define(
17870 interp,
17871 "flesch_kincaid_grade",
17872 Some(1),
17873 |_, args| match &args[0] {
17874 Value::String(s) => {
17875 let (words, sentences, syllables) = count_text_stats(s);
17876 if words == 0 || sentences == 0 {
17877 return Ok(Value::Float(0.0));
17878 }
17879 let grade = 0.39 * (words as f64 / sentences as f64)
17880 + 11.8 * (syllables as f64 / words as f64)
17881 - 15.59;
17882 Ok(Value::Float(grade.max(0.0)))
17883 }
17884 _ => Err(RuntimeError::new("flesch_kincaid_grade() requires string")),
17885 },
17886 );
17887
17888 define(
17890 interp,
17891 "automated_readability_index",
17892 Some(1),
17893 |_, args| match &args[0] {
17894 Value::String(s) => {
17895 let chars: usize = s.chars().filter(|c| c.is_alphanumeric()).count();
17896 let words: usize = s.split_whitespace().count();
17897 let sentences: usize = s
17898 .matches(|c| c == '.' || c == '!' || c == '?')
17899 .count()
17900 .max(1);
17901
17902 if words == 0 {
17903 return Ok(Value::Float(0.0));
17904 }
17905
17906 let ari = 4.71 * (chars as f64 / words as f64)
17907 + 0.5 * (words as f64 / sentences as f64)
17908 - 21.43;
17909 Ok(Value::Float(ari.max(0.0)))
17910 }
17911 _ => Err(RuntimeError::new(
17912 "automated_readability_index() requires string",
17913 )),
17914 },
17915 );
17916
17917 define(interp, "reading_time", Some(1), |_, args| {
17919 match &args[0] {
17920 Value::String(s) => {
17921 let words = s.split_whitespace().count();
17922 let minutes = words as f64 / 200.0; Ok(Value::Float(minutes))
17924 }
17925 _ => Err(RuntimeError::new("reading_time() requires string")),
17926 }
17927 });
17928
17929 define(interp, "speaking_time", Some(1), |_, args| {
17931 match &args[0] {
17932 Value::String(s) => {
17933 let words = s.split_whitespace().count();
17934 let minutes = words as f64 / 150.0; Ok(Value::Float(minutes))
17936 }
17937 _ => Err(RuntimeError::new("speaking_time() requires string")),
17938 }
17939 });
17940}
17941
17942fn compute_vader_sentiment(s: &str) -> (f64, f64, f64, f64) {
17948 let positive_words: Vec<(&str, f64)> = vec![
17950 ("love", 3.0),
17951 ("loved", 3.0),
17952 ("loving", 3.0),
17953 ("excellent", 3.0),
17954 ("amazing", 3.0),
17955 ("fantastic", 3.0),
17956 ("wonderful", 3.0),
17957 ("great", 2.5),
17958 ("awesome", 2.5),
17959 ("brilliant", 2.5),
17960 ("superb", 2.5),
17961 ("good", 2.0),
17962 ("nice", 2.0),
17963 ("pleasant", 2.0),
17964 ("happy", 2.0),
17965 ("like", 1.5),
17966 ("enjoy", 1.5),
17967 ("fine", 1.5),
17968 ("okay", 1.0),
17969 ("best", 3.0),
17970 ("perfect", 3.0),
17971 ("beautiful", 2.5),
17972 ("delightful", 2.5),
17973 ("excited", 2.5),
17974 ("thrilled", 3.0),
17975 ("glad", 2.0),
17976 ("pleased", 2.0),
17977 ];
17978
17979 let negative_words: Vec<(&str, f64)> = vec![
17980 ("hate", 3.0),
17981 ("hated", 3.0),
17982 ("hating", 3.0),
17983 ("terrible", 3.0),
17984 ("horrible", 3.0),
17985 ("awful", 3.0),
17986 ("disgusting", 3.0),
17987 ("bad", 2.5),
17988 ("poor", 2.5),
17989 ("worst", 3.0),
17990 ("pathetic", 2.5),
17991 ("sad", 2.0),
17992 ("angry", 2.5),
17993 ("upset", 2.0),
17994 ("disappointed", 2.0),
17995 ("dislike", 1.5),
17996 ("annoying", 2.0),
17997 ("boring", 1.5),
17998 ("mediocre", 1.0),
17999 ("ugly", 2.5),
18000 ("stupid", 2.5),
18001 ("dumb", 2.0),
18002 ("useless", 2.5),
18003 ("painful", 2.5),
18004 ("miserable", 3.0),
18005 ("depressing", 2.5),
18006 ("frustrating", 2.0),
18007 ];
18008
18009 let boosters = vec![
18011 "very",
18012 "really",
18013 "extremely",
18014 "absolutely",
18015 "incredibly",
18016 "totally",
18017 "so",
18018 ];
18019 let dampeners = vec![
18020 "somewhat", "slightly", "a bit", "kind of", "sort of", "barely",
18021 ];
18022
18023 let lower = s.to_lowercase();
18024 let words: Vec<&str> = lower.split_whitespace().collect();
18025
18026 let mut pos_score = 0.0;
18027 let mut neg_score = 0.0;
18028 let mut word_count = 0;
18029
18030 for (i, word) in words.iter().enumerate() {
18031 let mut modifier = 1.0;
18032
18033 if i > 0 {
18035 if boosters.contains(&words[i - 1]) {
18036 modifier = 1.5;
18037 } else if dampeners.iter().any(|d| words[i - 1].contains(d)) {
18038 modifier = 0.5;
18039 }
18040 }
18041
18042 let negated = i > 0
18044 && [
18045 "not",
18046 "no",
18047 "never",
18048 "neither",
18049 "don't",
18050 "doesn't",
18051 "didn't",
18052 "won't",
18053 "wouldn't",
18054 "couldn't",
18055 "shouldn't",
18056 ]
18057 .contains(&words[i - 1]);
18058
18059 if let Some((_, score)) = positive_words.iter().find(|(w, _)| w == word) {
18060 if negated {
18061 neg_score += score * modifier;
18062 } else {
18063 pos_score += score * modifier;
18064 }
18065 word_count += 1;
18066 } else if let Some((_, score)) = negative_words.iter().find(|(w, _)| w == word) {
18067 if negated {
18068 pos_score += score * modifier * 0.5; } else {
18070 neg_score += score * modifier;
18071 }
18072 word_count += 1;
18073 }
18074 }
18075
18076 let total = pos_score + neg_score;
18078 let (pos_norm, neg_norm) = if total > 0.0 {
18079 (pos_score / total, neg_score / total)
18080 } else {
18081 (0.0, 0.0)
18082 };
18083
18084 let neutral = 1.0 - pos_norm - neg_norm;
18085
18086 let compound = if word_count > 0 {
18088 ((pos_score - neg_score) / (word_count as f64 * 3.0))
18089 .max(-1.0)
18090 .min(1.0)
18091 } else {
18092 0.0
18093 };
18094
18095 (pos_norm, neg_norm, neutral.max(0.0), compound)
18096}
18097
18098fn compute_emotions(s: &str) -> HashMap<String, f64> {
18100 let emotion_words: Vec<(&str, &str)> = vec![
18101 ("happy", "joy"),
18103 ("joyful", "joy"),
18104 ("delighted", "joy"),
18105 ("cheerful", "joy"),
18106 ("excited", "joy"),
18107 ("thrilled", "joy"),
18108 ("ecstatic", "joy"),
18109 ("elated", "joy"),
18110 ("sad", "sadness"),
18112 ("unhappy", "sadness"),
18113 ("depressed", "sadness"),
18114 ("miserable", "sadness"),
18115 ("gloomy", "sadness"),
18116 ("heartbroken", "sadness"),
18117 ("sorrowful", "sadness"),
18118 ("melancholy", "sadness"),
18119 ("angry", "anger"),
18121 ("furious", "anger"),
18122 ("enraged", "anger"),
18123 ("irritated", "anger"),
18124 ("annoyed", "anger"),
18125 ("outraged", "anger"),
18126 ("livid", "anger"),
18127 ("mad", "anger"),
18128 ("afraid", "fear"),
18130 ("scared", "fear"),
18131 ("terrified", "fear"),
18132 ("frightened", "fear"),
18133 ("anxious", "fear"),
18134 ("worried", "fear"),
18135 ("nervous", "fear"),
18136 ("panicked", "fear"),
18137 ("surprised", "surprise"),
18139 ("amazed", "surprise"),
18140 ("astonished", "surprise"),
18141 ("shocked", "surprise"),
18142 ("stunned", "surprise"),
18143 ("startled", "surprise"),
18144 ("bewildered", "surprise"),
18145 ("disgusted", "disgust"),
18147 ("revolted", "disgust"),
18148 ("repulsed", "disgust"),
18149 ("sickened", "disgust"),
18150 ("nauseated", "disgust"),
18151 ("appalled", "disgust"),
18152 ("trust", "trust"),
18154 ("confident", "trust"),
18155 ("secure", "trust"),
18156 ("reliable", "trust"),
18157 ("faithful", "trust"),
18158 ("loyal", "trust"),
18159 ("eager", "anticipation"),
18161 ("hopeful", "anticipation"),
18162 ("expectant", "anticipation"),
18163 ("looking forward", "anticipation"),
18164 ("excited", "anticipation"),
18165 ];
18166
18167 let lower = s.to_lowercase();
18168 let mut counts: HashMap<String, f64> = HashMap::new();
18169
18170 for (word, emotion) in emotion_words {
18171 if lower.contains(word) {
18172 *counts.entry(emotion.to_string()).or_insert(0.0) += 1.0;
18173 }
18174 }
18175
18176 let total: f64 = counts.values().sum();
18178 if total > 0.0 {
18179 for v in counts.values_mut() {
18180 *v /= total;
18181 }
18182 }
18183
18184 counts
18185}
18186
18187fn compute_intensity(s: &str) -> f64 {
18189 let intensifiers = vec![
18190 ("very", 1.5),
18191 ("really", 1.5),
18192 ("extremely", 2.0),
18193 ("incredibly", 2.0),
18194 ("absolutely", 2.0),
18195 ("totally", 1.5),
18196 ("completely", 1.5),
18197 ("utterly", 2.0),
18198 ("so", 1.3),
18199 ("such", 1.3),
18200 ("quite", 1.2),
18201 ("rather", 1.1),
18202 ];
18203
18204 let exclamation_boost = 0.5;
18205 let caps_boost = 0.3;
18206
18207 let lower = s.to_lowercase();
18208 let mut score = 1.0;
18209
18210 for (word, boost) in intensifiers {
18211 if lower.contains(word) {
18212 score *= boost;
18213 }
18214 }
18215
18216 let exclamations = s.matches('!').count();
18218 score += exclamations as f64 * exclamation_boost;
18219
18220 let caps_words = s
18222 .split_whitespace()
18223 .filter(|w| w.len() > 2 && w.chars().all(|c| c.is_uppercase()))
18224 .count();
18225 score += caps_words as f64 * caps_boost;
18226
18227 score.min(5.0)
18228}
18229
18230fn compute_sarcasm_score(s: &str) -> (f64, f64, Vec<String>) {
18232 let mut markers = Vec::new();
18233 let mut score: f64 = 0.0;
18234
18235 let lower = s.to_lowercase();
18236
18237 let explicit = vec![
18239 "/s",
18240 "not!",
18241 "yeah right",
18242 "sure thing",
18243 "oh really",
18244 "oh great",
18245 "wow, just wow",
18246 "thanks a lot",
18247 "how wonderful",
18248 "isn't that special",
18249 "clearly",
18250 "obviously",
18251 "shocking",
18252 "no way",
18253 "what a surprise",
18254 ];
18255
18256 for marker in &explicit {
18257 if lower.contains(marker) {
18258 markers.push(format!("explicit: {}", marker));
18259 score += 0.4;
18260 }
18261 }
18262
18263 let hyperbolic = vec![
18265 "best thing ever",
18266 "worst thing ever",
18267 "literally dying",
18268 "absolutely perfect",
18269 "world's greatest",
18270 "totally awesome",
18271 "so much fun",
18272 "couldn't be happier",
18273 ];
18274
18275 for h in &hyperbolic {
18276 if lower.contains(h) {
18277 markers.push(format!("hyperbole: {}", h));
18278 score += 0.3;
18279 }
18280 }
18281
18282 let has_positive = ["great", "wonderful", "amazing", "love", "best", "awesome"]
18284 .iter()
18285 .any(|w| lower.contains(w));
18286 let has_negative_context = ["but", "however", "although", "except", "unfortunately"]
18287 .iter()
18288 .any(|w| lower.contains(w));
18289
18290 if has_positive && has_negative_context {
18291 markers.push("positive-negative contrast".to_string());
18292 score += 0.25;
18293 }
18294
18295 let quote_pattern = Regex::new(r#"["'](\w+)["']"#).unwrap();
18297 for cap in quote_pattern.captures_iter(s) {
18298 if let Some(m) = cap.get(1) {
18299 let word = m.as_str().to_lowercase();
18300 if [
18301 "great",
18302 "wonderful",
18303 "helpful",
18304 "useful",
18305 "smart",
18306 "genius",
18307 "brilliant",
18308 ]
18309 .contains(&word.as_str())
18310 {
18311 markers.push(format!("air quotes: \"{}\"", word));
18312 score += 0.35;
18313 }
18314 }
18315 }
18316
18317 if s.contains("...") || s.contains("!!!") || s.contains("???") {
18319 markers.push("excessive punctuation".to_string());
18320 score += 0.15;
18321 }
18322
18323 let confidence = if markers.is_empty() {
18325 0.0
18326 } else {
18327 (markers.len() as f64 * 0.25).min(1.0)
18328 };
18329
18330 (score.min(1.0), confidence, markers)
18331}
18332
18333fn compute_irony_score(s: &str) -> f64 {
18335 let mut score: f64 = 0.0;
18336 let lower = s.to_lowercase();
18337
18338 let irony_phrases = vec![
18340 "of course",
18341 "as expected",
18342 "naturally",
18343 "predictably",
18344 "who would have thought",
18345 "surprise surprise",
18346 "go figure",
18347 "typical",
18348 "as usual",
18349 "yet again",
18350 "once again",
18351 ];
18352
18353 for phrase in irony_phrases {
18354 if lower.contains(phrase) {
18355 score += 0.2;
18356 }
18357 }
18358
18359 if lower.contains("but") || lower.contains("yet") || lower.contains("however") {
18361 score += 0.1;
18362 }
18363
18364 if s.contains('?')
18366 && (lower.starts_with("isn't")
18367 || lower.starts_with("aren't")
18368 || lower.starts_with("doesn't")
18369 || lower.starts_with("don't")
18370 || lower.contains("right?")
18371 || lower.contains("isn't it"))
18372 {
18373 score += 0.25;
18374 }
18375
18376 score.min(1.0)
18377}
18378
18379fn create_hash_vector(s: &str, dims: usize) -> Vec<f64> {
18381 let mut vector = vec![0.0f64; dims];
18382
18383 for word in s.to_lowercase().split_whitespace() {
18384 let hash = compute_hash(word, 0);
18385 let idx = (hash as usize) % dims;
18386 vector[idx] += 1.0;
18387 }
18388
18389 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
18391 if magnitude > 0.0 {
18392 for v in vector.iter_mut() {
18393 *v /= magnitude;
18394 }
18395 }
18396
18397 vector
18398}
18399
18400fn count_text_stats(s: &str) -> (usize, usize, usize) {
18402 let words: Vec<&str> = s.split_whitespace().collect();
18403 let word_count = words.len();
18404 let sentence_count = s
18405 .matches(|c| c == '.' || c == '!' || c == '?')
18406 .count()
18407 .max(1);
18408
18409 let mut syllable_count = 0;
18410 for word in &words {
18411 syllable_count += count_syllables(word);
18412 }
18413
18414 (word_count, sentence_count, syllable_count)
18415}
18416
18417fn count_syllables(word: &str) -> usize {
18419 let word = word.to_lowercase();
18420 let vowels = ['a', 'e', 'i', 'o', 'u', 'y'];
18421 let mut count = 0;
18422 let mut prev_was_vowel = false;
18423
18424 for c in word.chars() {
18425 let is_vowel = vowels.contains(&c);
18426 if is_vowel && !prev_was_vowel {
18427 count += 1;
18428 }
18429 prev_was_vowel = is_vowel;
18430 }
18431
18432 if word.ends_with('e') && count > 1 {
18434 count -= 1;
18435 }
18436
18437 count.max(1)
18438}
18439
18440fn compute_soundex(s: &str) -> String {
18442 if s.is_empty() {
18443 return "0000".to_string();
18444 }
18445
18446 let s = s.to_uppercase();
18447 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
18448
18449 if chars.is_empty() {
18450 return "0000".to_string();
18451 }
18452
18453 let first = chars[0];
18454 let mut code = String::new();
18455 code.push(first);
18456
18457 let get_code = |c: char| -> char {
18458 match c {
18459 'B' | 'F' | 'P' | 'V' => '1',
18460 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => '2',
18461 'D' | 'T' => '3',
18462 'L' => '4',
18463 'M' | 'N' => '5',
18464 'R' => '6',
18465 _ => '0',
18466 }
18467 };
18468
18469 let mut prev_code = get_code(first);
18470
18471 for &c in chars.iter().skip(1) {
18472 let curr_code = get_code(c);
18473 if curr_code != '0' && curr_code != prev_code {
18474 code.push(curr_code);
18475 if code.len() == 4 {
18476 break;
18477 }
18478 }
18479 prev_code = curr_code;
18480 }
18481
18482 while code.len() < 4 {
18483 code.push('0');
18484 }
18485
18486 code
18487}
18488
18489fn compute_metaphone(s: &str) -> String {
18491 let s = s.to_uppercase();
18492 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
18493
18494 if chars.is_empty() {
18495 return String::new();
18496 }
18497
18498 let mut result = String::new();
18499 let mut i = 0;
18500
18501 if chars.len() >= 2 {
18503 let prefix: String = chars[0..2].iter().collect();
18504 if ["KN", "GN", "PN", "AE", "WR"].contains(&prefix.as_str()) {
18505 i = 1;
18506 }
18507 }
18508
18509 while i < chars.len() && result.len() < 6 {
18510 let c = chars[i];
18511 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
18512 let next = chars.get(i + 1).copied();
18513
18514 let code = match c {
18515 'A' | 'E' | 'I' | 'O' | 'U' => {
18516 if i == 0 {
18517 Some(c)
18518 } else {
18519 None
18520 }
18521 }
18522 'B' => {
18523 if prev != Some('M') || i == chars.len() - 1 {
18524 Some('B')
18525 } else {
18526 None
18527 }
18528 }
18529 'C' => {
18530 if next == Some('H') {
18531 Some('X')
18532 } else if matches!(next, Some('I') | Some('E') | Some('Y')) {
18533 Some('S')
18534 } else {
18535 Some('K')
18536 }
18537 }
18538 'D' => {
18539 if next == Some('G')
18540 && matches!(chars.get(i + 2), Some('E') | Some('I') | Some('Y'))
18541 {
18542 Some('J')
18543 } else {
18544 Some('T')
18545 }
18546 }
18547 'F' => Some('F'),
18548 'G' => {
18549 if next == Some('H')
18550 && !matches!(
18551 chars.get(i + 2),
18552 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18553 )
18554 {
18555 None
18556 } else if matches!(next, Some('N') | Some('E') | Some('I') | Some('Y')) {
18557 Some('J')
18558 } else {
18559 Some('K')
18560 }
18561 }
18562 'H' => {
18563 if matches!(
18564 prev,
18565 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18566 ) {
18567 None
18568 } else if matches!(
18569 next,
18570 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18571 ) {
18572 Some('H')
18573 } else {
18574 None
18575 }
18576 }
18577 'J' => Some('J'),
18578 'K' => {
18579 if prev != Some('C') {
18580 Some('K')
18581 } else {
18582 None
18583 }
18584 }
18585 'L' => Some('L'),
18586 'M' => Some('M'),
18587 'N' => Some('N'),
18588 'P' => {
18589 if next == Some('H') {
18590 Some('F')
18591 } else {
18592 Some('P')
18593 }
18594 }
18595 'Q' => Some('K'),
18596 'R' => Some('R'),
18597 'S' => {
18598 if next == Some('H') {
18599 Some('X')
18600 } else {
18601 Some('S')
18602 }
18603 }
18604 'T' => {
18605 if next == Some('H') {
18606 Some('0') } else if next == Some('I') && matches!(chars.get(i + 2), Some('O') | Some('A')) {
18608 Some('X')
18609 } else {
18610 Some('T')
18611 }
18612 }
18613 'V' => Some('F'),
18614 'W' | 'Y' => {
18615 if matches!(
18616 next,
18617 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18618 ) {
18619 Some(c)
18620 } else {
18621 None
18622 }
18623 }
18624 'X' => {
18625 result.push('K');
18626 Some('S')
18627 }
18628 'Z' => Some('S'),
18629 _ => None,
18630 };
18631
18632 if let Some(ch) = code {
18633 result.push(ch);
18634 }
18635
18636 if next == Some(c) {
18638 i += 1;
18639 }
18640 i += 1;
18641 }
18642
18643 result
18644}
18645
18646fn compute_cologne(s: &str) -> String {
18648 let s = s.to_uppercase();
18649 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
18650
18651 if chars.is_empty() {
18652 return String::new();
18653 }
18654
18655 let mut result = String::new();
18656
18657 for (i, &c) in chars.iter().enumerate() {
18658 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
18659 let next = chars.get(i + 1).copied();
18660
18661 let code = match c {
18662 'A' | 'E' | 'I' | 'O' | 'U' | 'J' | 'Y' => '0',
18663 'H' => continue,
18664 'B' | 'P' => '1',
18665 'D' | 'T' => {
18666 if matches!(next, Some('C') | Some('S') | Some('Z')) {
18667 '8'
18668 } else {
18669 '2'
18670 }
18671 }
18672 'F' | 'V' | 'W' => '3',
18673 'G' | 'K' | 'Q' => '4',
18674 'C' => {
18675 if i == 0 {
18676 if matches!(
18677 next,
18678 Some('A')
18679 | Some('H')
18680 | Some('K')
18681 | Some('L')
18682 | Some('O')
18683 | Some('Q')
18684 | Some('R')
18685 | Some('U')
18686 | Some('X')
18687 ) {
18688 '4'
18689 } else {
18690 '8'
18691 }
18692 } else if matches!(prev, Some('S') | Some('Z')) {
18693 '8'
18694 } else if matches!(
18695 next,
18696 Some('A')
18697 | Some('H')
18698 | Some('K')
18699 | Some('O')
18700 | Some('Q')
18701 | Some('U')
18702 | Some('X')
18703 ) {
18704 '4'
18705 } else {
18706 '8'
18707 }
18708 }
18709 'X' => {
18710 if matches!(prev, Some('C') | Some('K') | Some('Q')) {
18711 '8'
18712 } else {
18713 result.push('4');
18714 '8'
18715 }
18716 }
18717 'L' => '5',
18718 'M' | 'N' => '6',
18719 'R' => '7',
18720 'S' | 'Z' => '8',
18721 _ => continue,
18722 };
18723
18724 result.push(code);
18725 }
18726
18727 let mut deduped = String::new();
18729 let mut prev = None;
18730 for c in result.chars() {
18731 if prev != Some(c) {
18732 deduped.push(c);
18733 }
18734 prev = Some(c);
18735 }
18736
18737 let trimmed: String = deduped.trim_start_matches('0').to_string();
18739 if trimmed.is_empty() {
18740 "0".to_string()
18741 } else {
18742 trimmed
18743 }
18744}
18745
18746fn get_stopwords(lang: &str) -> Vec<&'static str> {
18748 match lang {
18749 "en" | "english" => vec![
18750 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with",
18751 "by", "from", "as", "is", "was", "are", "were", "been", "be", "have", "has", "had",
18752 "do", "does", "did", "will", "would", "could", "should", "may", "might", "must",
18753 "shall", "can", "need", "it", "its", "this", "that", "these", "those", "i", "you",
18754 "he", "she", "we", "they", "me", "him", "her", "us", "them", "my", "your", "his",
18755 "her", "our", "their", "what", "which", "who", "whom", "whose", "when", "where", "why",
18756 "how", "all", "each", "every", "both", "few", "more", "most", "other", "some", "such",
18757 "no", "nor", "not", "only", "own", "same", "so", "than", "too", "very", "just", "also",
18758 "now",
18759 ],
18760 "de" | "german" => vec![
18761 "der", "die", "das", "den", "dem", "des", "ein", "eine", "einer", "einem", "einen",
18762 "und", "oder", "aber", "in", "auf", "an", "zu", "für", "von", "mit", "bei", "als",
18763 "ist", "war", "sind", "waren", "sein", "haben", "hat", "hatte", "werden", "wird",
18764 "wurde", "kann", "können", "muss", "müssen", "soll", "sollen", "will", "wollen", "es",
18765 "sie", "er", "wir", "ihr", "ich", "du", "man", "sich", "nicht", "auch", "nur", "noch",
18766 "schon", "mehr", "sehr", "so",
18767 ],
18768 "fr" | "french" => vec![
18769 "le", "la", "les", "un", "une", "des", "et", "ou", "mais", "dans", "sur", "à", "de",
18770 "pour", "par", "avec", "ce", "cette", "ces", "est", "sont", "était", "être", "avoir",
18771 "a", "ont", "avait", "je", "tu", "il", "elle", "nous", "vous", "ils", "elles", "on",
18772 "ne", "pas", "plus", "moins", "très", "aussi", "que", "qui",
18773 ],
18774 "es" | "spanish" => vec![
18775 "el", "la", "los", "las", "un", "una", "unos", "unas", "y", "o", "pero", "en", "de",
18776 "a", "para", "por", "con", "es", "son", "era", "ser", "estar", "tiene", "tienen", "yo",
18777 "tú", "él", "ella", "nosotros", "ustedes", "ellos", "ellas", "no", "sí", "muy", "más",
18778 "menos", "también", "que", "quien", "cual", "como", "cuando",
18779 ],
18780 "it" | "italian" => vec![
18781 "il", "lo", "la", "i", "gli", "le", "un", "uno", "una", "e", "o", "ma", "in", "di",
18782 "a", "da", "per", "con", "su", "tra", "fra", "è", "sono", "era", "erano", "essere",
18783 "avere", "ha", "hanno", "io", "tu", "lui", "lei", "noi", "voi", "loro", "mi", "ti",
18784 "ci", "non", "più", "molto", "anche", "come", "che", "chi", "quale", "questo",
18785 "quello", "quando", "dove", "perché", "se", "però",
18786 ],
18787 "pt" | "portuguese" => vec![
18788 "o", "a", "os", "as", "um", "uma", "uns", "umas", "e", "ou", "mas", "em", "de", "para",
18789 "por", "com", "sem", "sob", "sobre", "é", "são", "era", "eram", "ser", "estar", "ter",
18790 "tem", "têm", "eu", "tu", "ele", "ela", "nós", "vós", "eles", "elas", "me", "te",
18791 "não", "mais", "muito", "também", "como", "que", "quem", "qual", "este", "esse",
18792 "aquele", "quando", "onde", "porque", "se", "já",
18793 ],
18794 "nl" | "dutch" => vec![
18795 "de", "het", "een", "en", "of", "maar", "in", "op", "aan", "van", "voor", "met", "bij",
18796 "naar", "om", "te", "tot", "uit", "over", "is", "zijn", "was", "waren", "worden",
18797 "wordt", "werd", "hebben", "ik", "je", "jij", "hij", "zij", "wij", "jullie", "ze",
18798 "mij", "jou", "niet", "geen", "meer", "ook", "als", "dat", "die", "wat", "wie", "dit",
18799 "deze", "wanneer", "waar", "waarom", "hoe", "dan", "nog",
18800 ],
18801 "ru" | "russian" => vec![
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 "есть",
18851 "все",
18852 "всё",
18853 "весь",
18854 "этот",
18855 "тот",
18856 "который",
18857 "когда",
18858 "где",
18859 ],
18860 "ar" | "arabic" => vec![
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 "لا",
18907 "قد",
18908 "كل",
18909 "بعض",
18910 "غير",
18911 "أي",
18912 "كيف",
18913 "متى",
18914 "أين",
18915 ],
18916 "zh" | "chinese" => vec![
18917 "的", "了", "是", "在", "有", "和", "与", "或", "但", "而", "我", "你", "他", "她",
18918 "它", "我们", "你们", "他们", "她们", "这", "那", "这个", "那个", "这些", "那些",
18919 "什么", "哪", "哪个", "不", "没", "没有", "很", "也", "都", "就", "才", "只", "还",
18920 "把", "被", "给", "从", "到", "为", "以", "因为", "所以", "如果", "会", "能", "可以",
18921 "要", "想", "应该", "必须", "可能", "一", "个",
18922 ],
18923 "ja" | "japanese" => vec![
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 "によって",
18966 "により",
18967 "おり",
18968 "より",
18969 "による",
18970 "ず",
18971 "なり",
18972 "られる",
18973 "において",
18974 ],
18975 "ko" | "korean" => vec![
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 "무엇",
19019 "어디",
19020 "언제",
19021 "왜",
19022 "어떻게",
19023 "누구",
19024 "어느",
19025 "모든",
19026 "각",
19027 ],
19028 "hi" | "hindi" => vec![
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 "ज्यादा",
19074 "होना",
19075 "करना",
19076 "जाना",
19077 "आना",
19078 "देना",
19079 "लेना",
19080 "रहना",
19081 "सकना",
19082 ],
19083 "tr" | "turkish" => vec![
19084 "bir",
19085 "ve",
19086 "bu",
19087 "da",
19088 "de",
19089 "için",
19090 "ile",
19091 "mi",
19092 "ne",
19093 "o",
19094 "var",
19095 "ben",
19096 "sen",
19097 "biz",
19098 "siz",
19099 "onlar",
19100 "ki",
19101 "ama",
19102 "çok",
19103 "daha",
19104 "gibi",
19105 "kadar",
19106 "sonra",
19107 "şey",
19108 "kendi",
19109 "bütün",
19110 "her",
19111 "bazı",
19112 "olan",
19113 "olarak",
19114 "değil",
19115 "ya",
19116 "hem",
19117 "veya",
19118 "ancak",
19119 "ise",
19120 "göre",
19121 "rağmen",
19122 "dolayı",
19123 "üzere",
19124 "karşı",
19125 "arasında",
19126 "olan",
19127 "oldu",
19128 "olur",
19129 "olmak",
19130 "etmek",
19131 "yapmak",
19132 "demek",
19133 ],
19134 "pl" | "polish" => vec![
19135 "i",
19136 "w",
19137 "z",
19138 "na",
19139 "do",
19140 "o",
19141 "że",
19142 "to",
19143 "nie",
19144 "się",
19145 "jest",
19146 "tak",
19147 "jak",
19148 "ale",
19149 "po",
19150 "co",
19151 "czy",
19152 "lub",
19153 "oraz",
19154 "ja",
19155 "ty",
19156 "on",
19157 "ona",
19158 "my",
19159 "wy",
19160 "oni",
19161 "one",
19162 "pan",
19163 "pani",
19164 "ten",
19165 "ta",
19166 "te",
19167 "tego",
19168 "tej",
19169 "tym",
19170 "tych",
19171 "który",
19172 "która",
19173 "być",
19174 "mieć",
19175 "móc",
19176 "musieć",
19177 "chcieć",
19178 "wiedzieć",
19179 "mówić",
19180 "bardzo",
19181 "tylko",
19182 "już",
19183 "jeszcze",
19184 "też",
19185 "więc",
19186 "jednak",
19187 ],
19188 "sv" | "swedish" => vec![
19189 "och", "i", "att", "det", "som", "en", "på", "är", "av", "för", "med", "till", "den",
19190 "har", "de", "inte", "om", "ett", "men", "jag", "du", "han", "hon", "vi", "ni", "de",
19191 "dem", "sig", "sin", "var", "från", "eller", "när", "kan", "ska", "så", "än", "nu",
19192 "också", "bara", "mycket", "mer", "andra", "detta", "sedan", "hade", "varit", "skulle",
19193 "vara", "bli", "blev", "blir", "göra",
19194 ],
19195 _ => vec![
19196 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
19197 ],
19198 }
19199}
19200
19201fn compute_hash(s: &str, seed: u64) -> u64 {
19203 let mut hash: u64 = seed.wrapping_mul(0x517cc1b727220a95);
19204 for b in s.bytes() {
19205 hash = hash.wrapping_mul(31).wrapping_add(b as u64);
19206 }
19207 hash
19208}
19209
19210fn register_hologram(interp: &mut Interpreter) {
19230 use crate::interpreter::{
19231 RuntimeAffect, RuntimeConfidence, RuntimeEmotion, RuntimeFormality, RuntimeIntensity,
19232 RuntimeSentiment,
19233 };
19234
19235 define(interp, "emotional_hologram", Some(1), |_, args| {
19238 let affect = match &args[0] {
19239 Value::Affective { affect, .. } => affect.clone(),
19240 _ => RuntimeAffect {
19241 sentiment: None,
19242 sarcasm: false,
19243 intensity: None,
19244 formality: None,
19245 emotion: None,
19246 confidence: None,
19247 },
19248 };
19249
19250 let mut hologram = std::collections::HashMap::new();
19251
19252 let valence = match affect.sentiment {
19254 Some(RuntimeSentiment::Positive) => 1.0,
19255 Some(RuntimeSentiment::Negative) => -1.0,
19256 Some(RuntimeSentiment::Neutral) | None => 0.0,
19257 };
19258 hologram.insert("valence".to_string(), Value::Float(valence));
19259
19260 let arousal = match affect.intensity {
19262 Some(RuntimeIntensity::Down) => 0.25,
19263 None => 0.5,
19264 Some(RuntimeIntensity::Up) => 0.75,
19265 Some(RuntimeIntensity::Max) => 1.0,
19266 };
19267 hologram.insert("arousal".to_string(), Value::Float(arousal));
19268
19269 let dominance = match affect.formality {
19271 Some(RuntimeFormality::Informal) => 0.25,
19272 None => 0.5,
19273 Some(RuntimeFormality::Formal) => 0.85,
19274 };
19275 hologram.insert("dominance".to_string(), Value::Float(dominance));
19276
19277 let authenticity = if affect.sarcasm { -0.9 } else { 0.9 };
19279 hologram.insert("authenticity".to_string(), Value::Float(authenticity));
19280
19281 let certainty = match affect.confidence {
19283 Some(RuntimeConfidence::Low) => 0.2,
19284 None | Some(RuntimeConfidence::Medium) => 0.5,
19285 Some(RuntimeConfidence::High) => 0.9,
19286 };
19287 hologram.insert("certainty".to_string(), Value::Float(certainty));
19288
19289 let emotion_index = match affect.emotion {
19291 Some(RuntimeEmotion::Joy) => 0,
19292 Some(RuntimeEmotion::Sadness) => 1,
19293 Some(RuntimeEmotion::Anger) => 2,
19294 Some(RuntimeEmotion::Fear) => 3,
19295 Some(RuntimeEmotion::Surprise) => 4,
19296 Some(RuntimeEmotion::Love) => 5,
19297 None => -1,
19298 };
19299 hologram.insert("emotion_index".to_string(), Value::Int(emotion_index));
19300
19301 let emotion_name = match affect.emotion {
19303 Some(RuntimeEmotion::Joy) => "joy",
19304 Some(RuntimeEmotion::Sadness) => "sadness",
19305 Some(RuntimeEmotion::Anger) => "anger",
19306 Some(RuntimeEmotion::Fear) => "fear",
19307 Some(RuntimeEmotion::Surprise) => "surprise",
19308 Some(RuntimeEmotion::Love) => "love",
19309 None => "none",
19310 };
19311 hologram.insert(
19312 "emotion".to_string(),
19313 Value::String(Rc::new(emotion_name.to_string())),
19314 );
19315
19316 Ok(Value::Map(Rc::new(RefCell::new(hologram))))
19317 });
19318
19319 define(interp, "emotional_distance", Some(2), |interp, args| {
19321 let h1 = get_hologram_values(&args[0], interp)?;
19323 let h2 = get_hologram_values(&args[1], interp)?;
19324
19325 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();
19332
19333 Ok(Value::Float(dist))
19334 });
19335
19336 define(interp, "emotional_similarity", Some(2), |interp, args| {
19338 let h1 = get_hologram_values(&args[0], interp)?;
19339 let h2 = get_hologram_values(&args[1], interp)?;
19340
19341 let dot = h1.0 * h2.0 + h1.1 * h2.1 + h1.2 * h2.2 + h1.3 * h2.3 + h1.4 * h2.4;
19342 let mag1 =
19343 (h1.0.powi(2) + h1.1.powi(2) + h1.2.powi(2) + h1.3.powi(2) + h1.4.powi(2)).sqrt();
19344 let mag2 =
19345 (h2.0.powi(2) + h2.1.powi(2) + h2.2.powi(2) + h2.3.powi(2) + h2.4.powi(2)).sqrt();
19346
19347 let similarity = if mag1 > 0.0 && mag2 > 0.0 {
19348 (dot / (mag1 * mag2) + 1.0) / 2.0 } else {
19350 0.5
19351 };
19352
19353 Ok(Value::Float(similarity))
19354 });
19355
19356 define(interp, "emotional_dissonance", Some(1), |_, args| {
19359 let affect = match &args[0] {
19360 Value::Affective { affect, .. } => affect.clone(),
19361 _ => return Ok(Value::Float(0.0)),
19362 };
19363
19364 let mut dissonance: f64 = 0.0;
19365
19366 if matches!(affect.sentiment, Some(RuntimeSentiment::Positive)) && affect.sarcasm {
19368 dissonance += 0.4;
19369 }
19370
19371 if matches!(affect.sentiment, Some(RuntimeSentiment::Negative)) && affect.sarcasm {
19373 dissonance += 0.1;
19374 }
19375
19376 if matches!(affect.confidence, Some(RuntimeConfidence::High))
19378 && matches!(affect.intensity, Some(RuntimeIntensity::Down))
19379 {
19380 dissonance += 0.2;
19381 }
19382
19383 if matches!(affect.formality, Some(RuntimeFormality::Formal)) {
19385 if matches!(
19386 affect.emotion,
19387 Some(RuntimeEmotion::Anger) | Some(RuntimeEmotion::Fear)
19388 ) {
19389 dissonance += 0.3;
19390 }
19391 }
19392
19393 if matches!(
19395 affect.emotion,
19396 Some(RuntimeEmotion::Joy) | Some(RuntimeEmotion::Love)
19397 ) && affect.sarcasm
19398 {
19399 dissonance += 0.3;
19400 }
19401
19402 Ok(Value::Float(dissonance.min(1.0)))
19403 });
19404
19405 define(interp, "emotional_fingerprint", Some(1), |interp, args| {
19408 let h = get_hologram_values(&args[0], interp)?;
19409
19410 let repr = format!(
19412 "hologram:v{:.4}:a{:.4}:d{:.4}:auth{:.4}:c{:.4}",
19413 h.0, h.1, h.2, h.3, h.4
19414 );
19415
19416 let hash = blake3::hash(repr.as_bytes());
19418 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
19419 });
19420
19421 define(interp, "emotional_morph", Some(3), |interp, args| {
19424 let h1 = get_hologram_values(&args[0], interp)?;
19425 let h2 = get_hologram_values(&args[1], interp)?;
19426 let t = match &args[2] {
19427 Value::Float(f) => f.max(0.0).min(1.0),
19428 Value::Int(i) => (*i as f64).max(0.0).min(1.0),
19429 _ => {
19430 return Err(RuntimeError::new(
19431 "emotional_morph() requires numeric t value",
19432 ))
19433 }
19434 };
19435
19436 let mut result = std::collections::HashMap::new();
19437 result.insert(
19438 "valence".to_string(),
19439 Value::Float(h1.0 + (h2.0 - h1.0) * t),
19440 );
19441 result.insert(
19442 "arousal".to_string(),
19443 Value::Float(h1.1 + (h2.1 - h1.1) * t),
19444 );
19445 result.insert(
19446 "dominance".to_string(),
19447 Value::Float(h1.2 + (h2.2 - h1.2) * t),
19448 );
19449 result.insert(
19450 "authenticity".to_string(),
19451 Value::Float(h1.3 + (h2.3 - h1.3) * t),
19452 );
19453 result.insert(
19454 "certainty".to_string(),
19455 Value::Float(h1.4 + (h2.4 - h1.4) * t),
19456 );
19457
19458 Ok(Value::Map(Rc::new(RefCell::new(result))))
19459 });
19460
19461 define(interp, "cultural_emotion", Some(2), |_, args| {
19467 let emotion = match &args[0] {
19468 Value::Affective { affect, .. } => affect.emotion.clone(),
19469 Value::String(s) => match s.as_str() {
19470 "joy" => Some(RuntimeEmotion::Joy),
19471 "sadness" => Some(RuntimeEmotion::Sadness),
19472 "anger" => Some(RuntimeEmotion::Anger),
19473 "fear" => Some(RuntimeEmotion::Fear),
19474 "surprise" => Some(RuntimeEmotion::Surprise),
19475 "love" => Some(RuntimeEmotion::Love),
19476 _ => None,
19477 },
19478 _ => None,
19479 };
19480
19481 let culture = match &args[1] {
19482 Value::String(s) => s.to_lowercase(),
19483 _ => {
19484 return Err(RuntimeError::new(
19485 "cultural_emotion() requires string culture",
19486 ))
19487 }
19488 };
19489
19490 let result = match (emotion, culture.as_str()) {
19491 (Some(RuntimeEmotion::Joy), "japanese" | "ja") => create_cultural_entry(
19493 "木漏れ日",
19494 "komorebi",
19495 "sunlight filtering through leaves - peaceful joy",
19496 ),
19497 (Some(RuntimeEmotion::Sadness), "japanese" | "ja") => create_cultural_entry(
19498 "物の哀れ",
19499 "mono no aware",
19500 "the pathos of things - bittersweet awareness of impermanence",
19501 ),
19502 (Some(RuntimeEmotion::Love), "japanese" | "ja") => create_cultural_entry(
19503 "甘え",
19504 "amae",
19505 "indulgent dependence on another's benevolence",
19506 ),
19507 (Some(RuntimeEmotion::Fear), "japanese" | "ja") => create_cultural_entry(
19508 "空気を読む",
19509 "kuuki wo yomu",
19510 "anxiety about reading the room",
19511 ),
19512
19513 (Some(RuntimeEmotion::Sadness), "portuguese" | "pt") => create_cultural_entry(
19515 "saudade",
19516 "saudade",
19517 "melancholic longing for something or someone absent",
19518 ),
19519 (Some(RuntimeEmotion::Joy), "portuguese" | "pt") => {
19520 create_cultural_entry("alegria", "alegria", "exuberant collective joy")
19521 }
19522
19523 (Some(RuntimeEmotion::Joy), "german" | "de") => create_cultural_entry(
19525 "Schadenfreude",
19526 "schadenfreude",
19527 "pleasure derived from another's misfortune",
19528 ),
19529 (Some(RuntimeEmotion::Sadness), "german" | "de") => create_cultural_entry(
19530 "Weltschmerz",
19531 "weltschmerz",
19532 "world-weariness, melancholy about the world's state",
19533 ),
19534 (Some(RuntimeEmotion::Fear), "german" | "de") => create_cultural_entry(
19535 "Torschlusspanik",
19536 "torschlusspanik",
19537 "fear of diminishing opportunities with age",
19538 ),
19539
19540 (Some(RuntimeEmotion::Joy), "danish" | "da") => {
19542 create_cultural_entry("hygge", "hygge", "cozy contentment and conviviality")
19543 }
19544
19545 (Some(RuntimeEmotion::Joy), "arabic" | "ar") => {
19547 create_cultural_entry("طرب", "tarab", "musical ecstasy, enchantment through art")
19548 }
19549 (Some(RuntimeEmotion::Love), "arabic" | "ar") => {
19550 create_cultural_entry("هوى", "hawa", "passionate, sometimes irrational love")
19551 }
19552
19553 (Some(RuntimeEmotion::Sadness), "korean" | "ko") => create_cultural_entry(
19555 "한",
19556 "han",
19557 "collective grief and resentment from historical suffering",
19558 ),
19559 (Some(RuntimeEmotion::Joy), "korean" | "ko") => create_cultural_entry(
19560 "정",
19561 "jeong",
19562 "deep affection and attachment formed over time",
19563 ),
19564
19565 (Some(RuntimeEmotion::Sadness), "russian" | "ru") => {
19567 create_cultural_entry("тоска", "toska", "spiritual anguish without specific cause")
19568 }
19569
19570 (Some(RuntimeEmotion::Love), "hindi" | "hi") => {
19572 create_cultural_entry("विरह", "viraha", "longing for an absent beloved")
19573 }
19574
19575 (Some(RuntimeEmotion::Anger), "finnish" | "fi") => {
19577 create_cultural_entry("sisu", "sisu", "stoic determination and grit in adversity")
19578 }
19579
19580 (Some(e), _) => {
19582 let name = match e {
19583 RuntimeEmotion::Joy => "joy",
19584 RuntimeEmotion::Sadness => "sadness",
19585 RuntimeEmotion::Anger => "anger",
19586 RuntimeEmotion::Fear => "fear",
19587 RuntimeEmotion::Surprise => "surprise",
19588 RuntimeEmotion::Love => "love",
19589 };
19590 create_cultural_entry(name, name, "universal emotion")
19591 }
19592 (None, _) => create_cultural_entry("none", "none", "no emotion"),
19593 };
19594
19595 Ok(result)
19596 });
19597
19598 define(interp, "list_cultural_emotions", Some(1), |_, args| {
19600 let culture = match &args[0] {
19601 Value::String(s) => s.to_lowercase(),
19602 _ => {
19603 return Err(RuntimeError::new(
19604 "list_cultural_emotions() requires string culture",
19605 ))
19606 }
19607 };
19608
19609 let emotions: Vec<(&str, &str, &str)> = match culture.as_str() {
19610 "japanese" | "ja" => vec![
19611 ("木漏れ日", "komorebi", "sunlight through leaves"),
19612 ("物の哀れ", "mono no aware", "pathos of things"),
19613 ("甘え", "amae", "indulgent dependence"),
19614 ("侘寂", "wabi-sabi", "beauty in imperfection"),
19615 ("生きがい", "ikigai", "reason for being"),
19616 ],
19617 "german" | "de" => vec![
19618 ("Schadenfreude", "schadenfreude", "joy at misfortune"),
19619 ("Weltschmerz", "weltschmerz", "world-weariness"),
19620 ("Torschlusspanik", "torschlusspanik", "gate-closing panic"),
19621 ("Sehnsucht", "sehnsucht", "deep longing"),
19622 ("Wanderlust", "wanderlust", "desire to travel"),
19623 ],
19624 "portuguese" | "pt" => vec![
19625 ("saudade", "saudade", "melancholic longing"),
19626 ("alegria", "alegria", "exuberant joy"),
19627 ("desabafar", "desabafar", "emotional unburdening"),
19628 ],
19629 "danish" | "da" => vec![("hygge", "hygge", "cozy contentment")],
19630 "korean" | "ko" => vec![
19631 ("한", "han", "collective grief"),
19632 ("정", "jeong", "deep affection"),
19633 ("눈치", "nunchi", "situational awareness"),
19634 ],
19635 "arabic" | "ar" => vec![
19636 ("طرب", "tarab", "musical ecstasy"),
19637 ("هوى", "hawa", "passionate love"),
19638 ("صبر", "sabr", "patient perseverance"),
19639 ],
19640 "russian" | "ru" => vec![
19641 ("тоска", "toska", "spiritual anguish"),
19642 ("пошлость", "poshlost", "spiritual vulgarity"),
19643 ],
19644 "finnish" | "fi" => vec![("sisu", "sisu", "stoic determination")],
19645 "hindi" | "hi" => vec![
19646 ("विरह", "viraha", "longing for beloved"),
19647 ("जुगाड़", "jugaad", "creative improvisation"),
19648 ],
19649 _ => vec![
19650 ("joy", "joy", "universal happiness"),
19651 ("sadness", "sadness", "universal sorrow"),
19652 ("anger", "anger", "universal frustration"),
19653 ("fear", "fear", "universal anxiety"),
19654 ("surprise", "surprise", "universal amazement"),
19655 ("love", "love", "universal affection"),
19656 ],
19657 };
19658
19659 let result: Vec<Value> = emotions
19660 .iter()
19661 .map(|(native, romanized, meaning)| create_cultural_entry(native, romanized, meaning))
19662 .collect();
19663
19664 Ok(Value::Array(Rc::new(RefCell::new(result))))
19665 });
19666
19667 define(interp, "hologram_info", Some(0), |_, _| {
19669 let mut info = std::collections::HashMap::new();
19670
19671 info.insert(
19672 "dimensions".to_string(),
19673 Value::Array(Rc::new(RefCell::new(vec![
19674 Value::String(Rc::new("valence".to_string())),
19675 Value::String(Rc::new("arousal".to_string())),
19676 Value::String(Rc::new("dominance".to_string())),
19677 Value::String(Rc::new("authenticity".to_string())),
19678 Value::String(Rc::new("certainty".to_string())),
19679 Value::String(Rc::new("emotion_index".to_string())),
19680 ]))),
19681 );
19682
19683 info.insert(
19684 "supported_cultures".to_string(),
19685 Value::Array(Rc::new(RefCell::new(vec![
19686 Value::String(Rc::new("japanese".to_string())),
19687 Value::String(Rc::new("german".to_string())),
19688 Value::String(Rc::new("portuguese".to_string())),
19689 Value::String(Rc::new("danish".to_string())),
19690 Value::String(Rc::new("korean".to_string())),
19691 Value::String(Rc::new("arabic".to_string())),
19692 Value::String(Rc::new("russian".to_string())),
19693 Value::String(Rc::new("finnish".to_string())),
19694 Value::String(Rc::new("hindi".to_string())),
19695 ]))),
19696 );
19697
19698 let funcs = vec![
19699 "emotional_hologram",
19700 "emotional_distance",
19701 "emotional_similarity",
19702 "emotional_dissonance",
19703 "emotional_fingerprint",
19704 "emotional_morph",
19705 "cultural_emotion",
19706 "list_cultural_emotions",
19707 "hologram_info",
19708 ];
19709 let func_values: Vec<Value> = funcs
19710 .iter()
19711 .map(|s| Value::String(Rc::new(s.to_string())))
19712 .collect();
19713 info.insert(
19714 "functions".to_string(),
19715 Value::Array(Rc::new(RefCell::new(func_values))),
19716 );
19717
19718 Ok(Value::Map(Rc::new(RefCell::new(info))))
19719 });
19720}
19721
19722fn get_hologram_values(
19724 val: &Value,
19725 _interp: &mut Interpreter,
19726) -> Result<(f64, f64, f64, f64, f64), RuntimeError> {
19727 use crate::interpreter::{
19728 RuntimeAffect, RuntimeConfidence, RuntimeFormality, RuntimeIntensity, RuntimeSentiment,
19729 };
19730
19731 let affect = match val {
19732 Value::Affective { affect, .. } => affect.clone(),
19733 Value::Map(m) => {
19734 let map = m.borrow();
19736 let v = extract_float(&map, "valence").unwrap_or(0.0);
19737 let a = extract_float(&map, "arousal").unwrap_or(0.5);
19738 let d = extract_float(&map, "dominance").unwrap_or(0.5);
19739 let auth = extract_float(&map, "authenticity").unwrap_or(0.9);
19740 let c = extract_float(&map, "certainty").unwrap_or(0.5);
19741 return Ok((v, a, d, auth, c));
19742 }
19743 _ => RuntimeAffect {
19744 sentiment: None,
19745 sarcasm: false,
19746 intensity: None,
19747 formality: None,
19748 emotion: None,
19749 confidence: None,
19750 },
19751 };
19752
19753 let v = match affect.sentiment {
19754 Some(RuntimeSentiment::Positive) => 1.0,
19755 Some(RuntimeSentiment::Negative) => -1.0,
19756 _ => 0.0,
19757 };
19758 let a = match affect.intensity {
19759 Some(RuntimeIntensity::Down) => 0.25,
19760 Some(RuntimeIntensity::Up) => 0.75,
19761 Some(RuntimeIntensity::Max) => 1.0,
19762 None => 0.5,
19763 };
19764 let d = match affect.formality {
19765 Some(RuntimeFormality::Informal) => 0.25,
19766 Some(RuntimeFormality::Formal) => 0.85,
19767 None => 0.5,
19768 };
19769 let auth = if affect.sarcasm { -0.9 } else { 0.9 };
19770 let c = match affect.confidence {
19771 Some(RuntimeConfidence::Low) => 0.2,
19772 Some(RuntimeConfidence::High) => 0.9,
19773 _ => 0.5,
19774 };
19775
19776 Ok((v, a, d, auth, c))
19777}
19778
19779fn extract_float(map: &std::collections::HashMap<String, Value>, key: &str) -> Option<f64> {
19780 match map.get(key) {
19781 Some(Value::Float(f)) => Some(*f),
19782 Some(Value::Int(i)) => Some(*i as f64),
19783 _ => None,
19784 }
19785}
19786
19787fn create_cultural_entry(native: &str, romanized: &str, meaning: &str) -> Value {
19788 let mut entry = std::collections::HashMap::new();
19789 entry.insert(
19790 "native".to_string(),
19791 Value::String(Rc::new(native.to_string())),
19792 );
19793 entry.insert(
19794 "romanized".to_string(),
19795 Value::String(Rc::new(romanized.to_string())),
19796 );
19797 entry.insert(
19798 "meaning".to_string(),
19799 Value::String(Rc::new(meaning.to_string())),
19800 );
19801 Value::Map(Rc::new(RefCell::new(entry)))
19802}
19803
19804fn register_experimental_crypto(interp: &mut Interpreter) {
19809 define(interp, "commit", Some(1), |_, args| {
19815 let value_str = match &args[0] {
19816 Value::String(s) => s.to_string(),
19817 other => format!("{:?}", other),
19818 };
19819
19820 let mut nonce = [0u8; 32];
19822 getrandom::getrandom(&mut nonce)
19823 .map_err(|e| RuntimeError::new(format!("commit() random failed: {}", e)))?;
19824 let nonce_hex = hex::encode(&nonce);
19825
19826 let commitment_input = format!("{}:{}", value_str, nonce_hex);
19828 let commitment = blake3::hash(commitment_input.as_bytes());
19829
19830 let mut result = std::collections::HashMap::new();
19831 result.insert(
19832 "commitment".to_string(),
19833 Value::String(Rc::new(commitment.to_hex().to_string())),
19834 );
19835 result.insert("nonce".to_string(), Value::String(Rc::new(nonce_hex)));
19836 result.insert("value".to_string(), args[0].clone());
19837
19838 Ok(Value::Map(Rc::new(RefCell::new(result))))
19839 });
19840
19841 define(interp, "verify_commitment", Some(3), |_, args| {
19843 let commitment = match &args[0] {
19844 Value::String(s) => s.to_string(),
19845 _ => {
19846 return Err(RuntimeError::new(
19847 "verify_commitment() requires string commitment",
19848 ))
19849 }
19850 };
19851 let value_str = match &args[1] {
19852 Value::String(s) => s.to_string(),
19853 other => format!("{:?}", other),
19854 };
19855 let nonce = match &args[2] {
19856 Value::String(s) => s.to_string(),
19857 _ => {
19858 return Err(RuntimeError::new(
19859 "verify_commitment() requires string nonce",
19860 ))
19861 }
19862 };
19863
19864 let commitment_input = format!("{}:{}", value_str, nonce);
19866 let computed = blake3::hash(commitment_input.as_bytes());
19867
19868 Ok(Value::Bool(computed.to_hex().to_string() == commitment))
19869 });
19870
19871 define(interp, "secret_split", Some(3), |_, args| {
19877 let secret = match &args[0] {
19878 Value::String(s) => s.as_bytes().to_vec(),
19879 Value::Array(arr) => {
19880 let borrowed = arr.borrow();
19881 borrowed
19882 .iter()
19883 .filter_map(|v| {
19884 if let Value::Int(i) = v {
19885 Some(*i as u8)
19886 } else {
19887 None
19888 }
19889 })
19890 .collect()
19891 }
19892 _ => {
19893 return Err(RuntimeError::new(
19894 "secret_split() requires string or byte array",
19895 ))
19896 }
19897 };
19898
19899 let threshold = match &args[1] {
19900 Value::Int(n) => *n as usize,
19901 _ => {
19902 return Err(RuntimeError::new(
19903 "secret_split() requires integer threshold",
19904 ))
19905 }
19906 };
19907
19908 let num_shares = match &args[2] {
19909 Value::Int(n) => *n as usize,
19910 _ => {
19911 return Err(RuntimeError::new(
19912 "secret_split() requires integer num_shares",
19913 ))
19914 }
19915 };
19916
19917 if threshold < 2 {
19918 return Err(RuntimeError::new("secret_split() threshold must be >= 2"));
19919 }
19920 if num_shares < threshold {
19921 return Err(RuntimeError::new(
19922 "secret_split() num_shares must be >= threshold",
19923 ));
19924 }
19925 if num_shares > 255 {
19926 return Err(RuntimeError::new("secret_split() max 255 shares"));
19927 }
19928
19929 let mut rng = rand::thread_rng();
19932 let mut shares: Vec<Vec<u8>> = (0..num_shares)
19933 .map(|_| Vec::with_capacity(secret.len() + 1))
19934 .collect();
19935
19936 for (i, share) in shares.iter_mut().enumerate() {
19938 share.push((i + 1) as u8);
19939 }
19940
19941 for &byte in &secret {
19943 let mut coefficients: Vec<u8> = vec![byte];
19946 for _ in 1..threshold {
19947 coefficients.push(rng.gen());
19948 }
19949
19950 for (i, share) in shares.iter_mut().enumerate() {
19952 let x = (i + 1) as u8;
19953 let y = eval_polynomial_gf256(&coefficients, x);
19954 share.push(y);
19955 }
19956 }
19957
19958 let share_values: Vec<Value> = shares
19960 .iter()
19961 .map(|share| {
19962 let hex = hex::encode(share);
19963 Value::String(Rc::new(hex))
19964 })
19965 .collect();
19966
19967 let mut result = std::collections::HashMap::new();
19968 result.insert(
19969 "shares".to_string(),
19970 Value::Array(Rc::new(RefCell::new(share_values))),
19971 );
19972 result.insert("threshold".to_string(), Value::Int(threshold as i64));
19973 result.insert("total".to_string(), Value::Int(num_shares as i64));
19974
19975 Ok(Value::Map(Rc::new(RefCell::new(result))))
19976 });
19977
19978 define(interp, "secret_recover", Some(1), |_, args| {
19980 let shares: Vec<Vec<u8>> = match &args[0] {
19981 Value::Array(arr) => {
19982 let borrowed = arr.borrow();
19983 borrowed
19984 .iter()
19985 .filter_map(|v| {
19986 if let Value::String(s) = v {
19987 hex::decode(s.as_str()).ok()
19988 } else {
19989 None
19990 }
19991 })
19992 .collect()
19993 }
19994 _ => {
19995 return Err(RuntimeError::new(
19996 "secret_recover() requires array of share strings",
19997 ))
19998 }
19999 };
20000
20001 if shares.is_empty() {
20002 return Err(RuntimeError::new(
20003 "secret_recover() requires at least one share",
20004 ));
20005 }
20006
20007 let share_len = shares[0].len();
20008 if share_len < 2 {
20009 return Err(RuntimeError::new("secret_recover() invalid share format"));
20010 }
20011
20012 let mut secret = Vec::with_capacity(share_len - 1);
20014
20015 for byte_idx in 1..share_len {
20016 let points: Vec<(u8, u8)> = shares
20018 .iter()
20019 .map(|share| (share[0], share[byte_idx]))
20020 .collect();
20021
20022 let recovered_byte = lagrange_interpolate_gf256(&points, 0);
20024 secret.push(recovered_byte);
20025 }
20026
20027 match String::from_utf8(secret.clone()) {
20029 Ok(s) => Ok(Value::String(Rc::new(s))),
20030 Err(_) => {
20031 let byte_values: Vec<Value> =
20033 secret.iter().map(|&b| Value::Int(b as i64)).collect();
20034 Ok(Value::Array(Rc::new(RefCell::new(byte_values))))
20035 }
20036 }
20037 });
20038
20039 define(interp, "council_split", Some(2), |_, args| {
20045 let secret = match &args[0] {
20046 Value::String(s) => s.as_bytes().to_vec(),
20047 _ => return Err(RuntimeError::new("council_split() requires string secret")),
20048 };
20049
20050 let num_elders = match &args[1] {
20051 Value::Int(n) => *n as usize,
20052 _ => {
20053 return Err(RuntimeError::new(
20054 "council_split() requires integer num_elders",
20055 ))
20056 }
20057 };
20058
20059 if num_elders < 3 {
20060 return Err(RuntimeError::new(
20061 "council_split() requires at least 3 elders",
20062 ));
20063 }
20064
20065 let threshold = (num_elders / 2) + 1;
20067
20068 let mut rng = rand::thread_rng();
20070 let mut shares: Vec<Vec<u8>> = (0..num_elders)
20071 .map(|_| Vec::with_capacity(secret.len() + 1))
20072 .collect();
20073
20074 for (i, share) in shares.iter_mut().enumerate() {
20075 share.push((i + 1) as u8);
20076 }
20077
20078 for &byte in &secret {
20079 let mut coefficients: Vec<u8> = vec![byte];
20080 for _ in 1..threshold {
20081 coefficients.push(rng.gen());
20082 }
20083
20084 for (i, share) in shares.iter_mut().enumerate() {
20085 let x = (i + 1) as u8;
20086 let y = eval_polynomial_gf256(&coefficients, x);
20087 share.push(y);
20088 }
20089 }
20090
20091 let share_values: Vec<Value> = shares
20092 .iter()
20093 .map(|share| Value::String(Rc::new(hex::encode(share))))
20094 .collect();
20095
20096 let mut result = std::collections::HashMap::new();
20097 result.insert(
20098 "shares".to_string(),
20099 Value::Array(Rc::new(RefCell::new(share_values))),
20100 );
20101 result.insert("threshold".to_string(), Value::Int(threshold as i64));
20102 result.insert("total".to_string(), Value::Int(num_elders as i64));
20103 result.insert(
20104 "model".to_string(),
20105 Value::String(Rc::new("ubuntu".to_string())),
20106 );
20107 result.insert(
20108 "philosophy".to_string(),
20109 Value::String(Rc::new(
20110 "I am because we are - majority consensus required".to_string(),
20111 )),
20112 );
20113
20114 Ok(Value::Map(Rc::new(RefCell::new(result))))
20115 });
20116
20117 define(interp, "witness_chain", Some(2), |_, args| {
20120 let statement = match &args[0] {
20121 Value::String(s) => s.to_string(),
20122 _ => {
20123 return Err(RuntimeError::new(
20124 "witness_chain() requires string statement",
20125 ))
20126 }
20127 };
20128
20129 let witnesses: Vec<String> = match &args[1] {
20130 Value::Array(arr) => {
20131 let borrowed = arr.borrow();
20132 borrowed
20133 .iter()
20134 .filter_map(|v| {
20135 if let Value::String(s) = v {
20136 Some(s.to_string())
20137 } else {
20138 None
20139 }
20140 })
20141 .collect()
20142 }
20143 _ => {
20144 return Err(RuntimeError::new(
20145 "witness_chain() requires array of witness names",
20146 ))
20147 }
20148 };
20149
20150 if witnesses.is_empty() {
20151 return Err(RuntimeError::new(
20152 "witness_chain() requires at least one witness",
20153 ));
20154 }
20155
20156 let mut chain = Vec::new();
20158 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
20159
20160 for (i, witness) in witnesses.iter().enumerate() {
20161 let attestation = format!("{}:attests:{}", witness, prev_hash);
20162 let hash = blake3::hash(attestation.as_bytes()).to_hex().to_string();
20163
20164 let mut link = std::collections::HashMap::new();
20165 link.insert(
20166 "witness".to_string(),
20167 Value::String(Rc::new(witness.clone())),
20168 );
20169 link.insert("position".to_string(), Value::Int((i + 1) as i64));
20170 link.insert(
20171 "attests_to".to_string(),
20172 Value::String(Rc::new(prev_hash.clone())),
20173 );
20174 link.insert(
20175 "signature".to_string(),
20176 Value::String(Rc::new(hash.clone())),
20177 );
20178
20179 chain.push(Value::Map(Rc::new(RefCell::new(link))));
20180 prev_hash = hash;
20181 }
20182
20183 let mut result = std::collections::HashMap::new();
20184 result.insert("statement".to_string(), Value::String(Rc::new(statement)));
20185 result.insert(
20186 "chain".to_string(),
20187 Value::Array(Rc::new(RefCell::new(chain))),
20188 );
20189 result.insert("final_seal".to_string(), Value::String(Rc::new(prev_hash)));
20190 result.insert(
20191 "model".to_string(),
20192 Value::String(Rc::new("isnad".to_string())),
20193 );
20194 result.insert(
20195 "philosophy".to_string(),
20196 Value::String(Rc::new(
20197 "Chain of reliable transmitters - each witness validates the previous".to_string(),
20198 )),
20199 );
20200
20201 Ok(Value::Map(Rc::new(RefCell::new(result))))
20202 });
20203
20204 define(interp, "verify_witness_chain", Some(1), |_, args| {
20206 let chain_map = match &args[0] {
20207 Value::Map(m) => m.borrow(),
20208 _ => {
20209 return Err(RuntimeError::new(
20210 "verify_witness_chain() requires chain map",
20211 ))
20212 }
20213 };
20214
20215 let statement = match chain_map.get("statement") {
20216 Some(Value::String(s)) => s.to_string(),
20217 _ => {
20218 return Err(RuntimeError::new(
20219 "verify_witness_chain() invalid chain format",
20220 ))
20221 }
20222 };
20223
20224 let chain = match chain_map.get("chain") {
20225 Some(Value::Array(arr)) => arr.borrow().clone(),
20226 _ => {
20227 return Err(RuntimeError::new(
20228 "verify_witness_chain() invalid chain format",
20229 ))
20230 }
20231 };
20232
20233 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
20234
20235 for link_val in chain.iter() {
20236 if let Value::Map(link_map) = link_val {
20237 let link = link_map.borrow();
20238 let witness = match link.get("witness") {
20239 Some(Value::String(s)) => s.to_string(),
20240 _ => return Ok(Value::Bool(false)),
20241 };
20242 let attests_to = match link.get("attests_to") {
20243 Some(Value::String(s)) => s.to_string(),
20244 _ => return Ok(Value::Bool(false)),
20245 };
20246 let signature = match link.get("signature") {
20247 Some(Value::String(s)) => s.to_string(),
20248 _ => return Ok(Value::Bool(false)),
20249 };
20250
20251 if attests_to != prev_hash {
20253 return Ok(Value::Bool(false));
20254 }
20255
20256 let expected = format!("{}:attests:{}", witness, prev_hash);
20257 let computed = blake3::hash(expected.as_bytes()).to_hex().to_string();
20258
20259 if computed != signature {
20260 return Ok(Value::Bool(false));
20261 }
20262
20263 prev_hash = signature;
20264 } else {
20265 return Ok(Value::Bool(false));
20266 }
20267 }
20268
20269 Ok(Value::Bool(true))
20270 });
20271
20272 define(interp, "experimental_crypto_info", Some(0), |_, _| {
20274 let mut info = std::collections::HashMap::new();
20275
20276 info.insert(
20277 "commitment_functions".to_string(),
20278 Value::Array(Rc::new(RefCell::new(vec![
20279 Value::String(Rc::new("commit".to_string())),
20280 Value::String(Rc::new("verify_commitment".to_string())),
20281 ]))),
20282 );
20283
20284 info.insert(
20285 "threshold_functions".to_string(),
20286 Value::Array(Rc::new(RefCell::new(vec![
20287 Value::String(Rc::new("secret_split".to_string())),
20288 Value::String(Rc::new("secret_recover".to_string())),
20289 ]))),
20290 );
20291
20292 info.insert(
20293 "cultural_ceremonies".to_string(),
20294 Value::Array(Rc::new(RefCell::new(vec![
20295 Value::String(Rc::new(
20296 "council_split (Ubuntu - African consensus)".to_string(),
20297 )),
20298 Value::String(Rc::new(
20299 "witness_chain (Isnad - Islamic transmission)".to_string(),
20300 )),
20301 ]))),
20302 );
20303
20304 Ok(Value::Map(Rc::new(RefCell::new(info))))
20305 });
20306}
20307
20308fn eval_polynomial_gf256(coefficients: &[u8], x: u8) -> u8 {
20310 let mut result: u8 = 0;
20311 let mut x_power: u8 = 1;
20312
20313 for &coef in coefficients {
20314 result ^= gf256_mul(coef, x_power);
20315 x_power = gf256_mul(x_power, x);
20316 }
20317
20318 result
20319}
20320
20321fn lagrange_interpolate_gf256(points: &[(u8, u8)], _x: u8) -> u8 {
20323 let mut result: u8 = 0;
20324
20325 for (i, &(xi, yi)) in points.iter().enumerate() {
20326 let mut numerator: u8 = 1;
20327 let mut denominator: u8 = 1;
20328
20329 for (j, &(xj, _)) in points.iter().enumerate() {
20330 if i != j {
20331 numerator = gf256_mul(numerator, xj);
20333 denominator = gf256_mul(denominator, xi ^ xj);
20335 }
20336 }
20337
20338 let term = gf256_mul(yi, gf256_mul(numerator, gf256_inv(denominator)));
20340 result ^= term;
20341 }
20342
20343 result
20344}
20345
20346fn gf256_mul(mut a: u8, mut b: u8) -> u8 {
20348 let mut result: u8 = 0;
20349 let modulus: u16 = 0x11b; while b != 0 {
20352 if b & 1 != 0 {
20353 result ^= a;
20354 }
20355 let high_bit = (a & 0x80) != 0;
20356 a <<= 1;
20357 if high_bit {
20358 a ^= (modulus & 0xff) as u8;
20359 }
20360 b >>= 1;
20361 }
20362
20363 result
20364}
20365
20366fn gf256_inv(a: u8) -> u8 {
20368 if a == 0 {
20369 return 0;
20370 }
20371
20372 let mut result = a;
20374 for _ in 0..6 {
20375 result = gf256_mul(result, result);
20376 result = gf256_mul(result, a);
20377 }
20378 gf256_mul(result, result)
20379}
20380
20381fn register_multibase(interp: &mut Interpreter) {
20400 define(interp, "to_vigesimal", Some(1), |_, args| {
20404 let n = match &args[0] {
20405 Value::Int(n) => *n,
20406 _ => return Err(RuntimeError::new("to_vigesimal() requires integer")),
20407 };
20408
20409 let result = to_base_string(n.unsigned_abs(), 20, false);
20410 let prefix = if n < 0 { "-0v" } else { "0v" };
20411 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20412 });
20413
20414 define(interp, "from_vigesimal", Some(1), |_, args| {
20415 let s = match &args[0] {
20416 Value::String(s) => s.to_string(),
20417 _ => return Err(RuntimeError::new("from_vigesimal() requires string")),
20418 };
20419
20420 let (negative, clean) = parse_base_prefix(&s, "0v");
20421 let value = from_base_string(&clean, 20)?;
20422 Ok(Value::Int(if negative {
20423 -(value as i64)
20424 } else {
20425 value as i64
20426 }))
20427 });
20428
20429 define(interp, "to_sexagesimal", Some(1), |_, args| {
20433 let n = match &args[0] {
20434 Value::Int(n) => *n,
20435 _ => return Err(RuntimeError::new("to_sexagesimal() requires integer")),
20436 };
20437
20438 let negative = n < 0;
20439 let mut value = n.unsigned_abs();
20440 let mut parts = Vec::new();
20441
20442 if value == 0 {
20443 parts.push("0".to_string());
20444 } else {
20445 while value > 0 {
20446 parts.push(format!("{}", value % 60));
20447 value /= 60;
20448 }
20449 parts.reverse();
20450 }
20451
20452 let prefix = if negative { "-0s" } else { "0s" };
20453 Ok(Value::String(Rc::new(format!(
20454 "{}[{}]",
20455 prefix,
20456 parts.join(":")
20457 ))))
20458 });
20459
20460 define(interp, "from_sexagesimal", Some(1), |_, args| {
20461 let s = match &args[0] {
20462 Value::String(s) => s.to_string(),
20463 _ => return Err(RuntimeError::new("from_sexagesimal() requires string")),
20464 };
20465
20466 let negative = s.starts_with('-');
20467 let clean = s
20468 .trim_start_matches('-')
20469 .trim_start_matches("0s")
20470 .trim_start_matches('[')
20471 .trim_end_matches(']');
20472
20473 let mut result: i64 = 0;
20474 for part in clean.split(':') {
20475 let digit: i64 = part
20476 .trim()
20477 .parse()
20478 .map_err(|_| RuntimeError::new(format!("Invalid sexagesimal digit: {}", part)))?;
20479 if digit < 0 || digit >= 60 {
20480 return Err(RuntimeError::new(format!(
20481 "Sexagesimal digit out of range: {}",
20482 digit
20483 )));
20484 }
20485 result = result * 60 + digit;
20486 }
20487
20488 Ok(Value::Int(if negative { -result } else { result }))
20489 });
20490
20491 define(interp, "to_duodecimal", Some(1), |_, args| {
20495 let n = match &args[0] {
20496 Value::Int(n) => *n,
20497 _ => return Err(RuntimeError::new("to_duodecimal() requires integer")),
20498 };
20499
20500 let result = to_base_string_custom(n.unsigned_abs(), 12, "0123456789XE");
20501 let prefix = if n < 0 { "-0z" } else { "0z" };
20502 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20503 });
20504
20505 define(interp, "from_duodecimal", Some(1), |_, args| {
20506 let s = match &args[0] {
20507 Value::String(s) => s.to_string(),
20508 _ => return Err(RuntimeError::new("from_duodecimal() requires string")),
20509 };
20510
20511 let (negative, clean) = parse_base_prefix(&s, "0z");
20512 let value = from_base_string_custom(&clean.to_uppercase(), "0123456789XE")?;
20513 Ok(Value::Int(if negative {
20514 -(value as i64)
20515 } else {
20516 value as i64
20517 }))
20518 });
20519
20520 define(interp, "to_base", Some(2), |_, args| {
20523 let n = match &args[0] {
20524 Value::Int(n) => *n,
20525 _ => return Err(RuntimeError::new("to_base() requires integer")),
20526 };
20527 let base = match &args[1] {
20528 Value::Int(b) => *b as u64,
20529 _ => return Err(RuntimeError::new("to_base() requires integer base")),
20530 };
20531
20532 if base < 2 || base > 36 {
20533 return Err(RuntimeError::new("to_base() base must be 2-36"));
20534 }
20535
20536 let result = to_base_string(n.unsigned_abs(), base, false);
20537 let prefix = if n < 0 { "-" } else { "" };
20538 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20539 });
20540
20541 define(interp, "from_base", Some(2), |_, args| {
20542 let s = match &args[0] {
20543 Value::String(s) => s.to_string(),
20544 _ => return Err(RuntimeError::new("from_base() requires string")),
20545 };
20546 let base = match &args[1] {
20547 Value::Int(b) => *b as u64,
20548 _ => return Err(RuntimeError::new("from_base() requires integer base")),
20549 };
20550
20551 if base < 2 || base > 36 {
20552 return Err(RuntimeError::new("from_base() base must be 2-36"));
20553 }
20554
20555 let negative = s.starts_with('-');
20556 let clean = s.trim_start_matches('-');
20557 let value = from_base_string(clean, base)?;
20558 Ok(Value::Int(if negative {
20559 -(value as i64)
20560 } else {
20561 value as i64
20562 }))
20563 });
20564
20565 const BASE58_ALPHABET: &str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
20570
20571 define(interp, "base58_encode", Some(1), |_, args| {
20572 let bytes: Vec<u8> = match &args[0] {
20573 Value::String(s) => s.as_bytes().to_vec(),
20574 Value::Array(arr) => arr
20575 .borrow()
20576 .iter()
20577 .filter_map(|v| {
20578 if let Value::Int(i) = v {
20579 Some(*i as u8)
20580 } else {
20581 None
20582 }
20583 })
20584 .collect(),
20585 _ => {
20586 return Err(RuntimeError::new(
20587 "base58_encode() requires string or byte array",
20588 ))
20589 }
20590 };
20591
20592 let leading_zeros = bytes.iter().take_while(|&&b| b == 0).count();
20594
20595 let mut result = Vec::new();
20597 let mut num: Vec<u8> = bytes.clone();
20598
20599 while !num.is_empty() && !num.iter().all(|&b| b == 0) {
20600 let mut remainder = 0u32;
20601 let mut new_num = Vec::new();
20602
20603 for &byte in &num {
20604 let acc = (remainder << 8) + byte as u32;
20605 let digit = acc / 58;
20606 remainder = acc % 58;
20607
20608 if !new_num.is_empty() || digit > 0 {
20609 new_num.push(digit as u8);
20610 }
20611 }
20612
20613 result.push(BASE58_ALPHABET.chars().nth(remainder as usize).unwrap());
20614 num = new_num;
20615 }
20616
20617 for _ in 0..leading_zeros {
20619 result.push('1');
20620 }
20621
20622 result.reverse();
20623 Ok(Value::String(Rc::new(result.into_iter().collect())))
20624 });
20625
20626 define(interp, "base58_decode", Some(1), |_, args| {
20627 let s = match &args[0] {
20628 Value::String(s) => s.to_string(),
20629 _ => return Err(RuntimeError::new("base58_decode() requires string")),
20630 };
20631
20632 let leading_ones = s.chars().take_while(|&c| c == '1').count();
20634
20635 let mut num: Vec<u8> = Vec::new();
20637
20638 for c in s.chars() {
20639 let digit = BASE58_ALPHABET
20640 .find(c)
20641 .ok_or_else(|| RuntimeError::new(format!("Invalid base58 character: {}", c)))?;
20642
20643 let mut carry = digit as u32;
20644 for byte in num.iter_mut().rev() {
20645 let acc = (*byte as u32) * 58 + carry;
20646 *byte = (acc & 0xff) as u8;
20647 carry = acc >> 8;
20648 }
20649
20650 while carry > 0 {
20651 num.insert(0, (carry & 0xff) as u8);
20652 carry >>= 8;
20653 }
20654 }
20655
20656 let mut result = vec![0u8; leading_ones];
20658 result.extend(num);
20659
20660 Ok(Value::String(Rc::new(hex::encode(&result))))
20662 });
20663
20664 const BASE32_ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
20668
20669 define(interp, "base32_encode", Some(1), |_, args| {
20670 let bytes: Vec<u8> = match &args[0] {
20671 Value::String(s) => s.as_bytes().to_vec(),
20672 Value::Array(arr) => arr
20673 .borrow()
20674 .iter()
20675 .filter_map(|v| {
20676 if let Value::Int(i) = v {
20677 Some(*i as u8)
20678 } else {
20679 None
20680 }
20681 })
20682 .collect(),
20683 _ => {
20684 return Err(RuntimeError::new(
20685 "base32_encode() requires string or byte array",
20686 ))
20687 }
20688 };
20689
20690 let mut result = String::new();
20691 let mut buffer: u64 = 0;
20692 let mut bits = 0;
20693
20694 for byte in bytes {
20695 buffer = (buffer << 8) | byte as u64;
20696 bits += 8;
20697
20698 while bits >= 5 {
20699 bits -= 5;
20700 let idx = ((buffer >> bits) & 0x1f) as usize;
20701 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
20702 }
20703 }
20704
20705 if bits > 0 {
20706 let idx = ((buffer << (5 - bits)) & 0x1f) as usize;
20707 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
20708 }
20709
20710 while result.len() % 8 != 0 {
20712 result.push('=');
20713 }
20714
20715 Ok(Value::String(Rc::new(result)))
20716 });
20717
20718 define(interp, "base32_decode", Some(1), |_, args| {
20719 let s = match &args[0] {
20720 Value::String(s) => s.to_uppercase().replace('=', ""),
20721 _ => return Err(RuntimeError::new("base32_decode() requires string")),
20722 };
20723
20724 let mut result = Vec::new();
20725 let mut buffer: u64 = 0;
20726 let mut bits = 0;
20727
20728 for c in s.chars() {
20729 let digit = BASE32_ALPHABET
20730 .find(c)
20731 .ok_or_else(|| RuntimeError::new(format!("Invalid base32 character: {}", c)))?;
20732
20733 buffer = (buffer << 5) | digit as u64;
20734 bits += 5;
20735
20736 if bits >= 8 {
20737 bits -= 8;
20738 result.push((buffer >> bits) as u8);
20739 buffer &= (1 << bits) - 1;
20740 }
20741 }
20742
20743 Ok(Value::String(Rc::new(hex::encode(&result))))
20744 });
20745
20746 define(interp, "sacred_numbers", Some(1), |_, args| {
20750 let culture = match &args[0] {
20751 Value::String(s) => s.to_lowercase(),
20752 _ => {
20753 return Err(RuntimeError::new(
20754 "sacred_numbers() requires string culture",
20755 ))
20756 }
20757 };
20758
20759 let numbers: Vec<(i64, &str)> = match culture.as_str() {
20760 "mayan" | "maya" => vec![
20761 (13, "Sacred cycle - Tzolkin calendar"),
20762 (20, "Base of vigesimal system - human digits"),
20763 (52, "Calendar round - 52 years"),
20764 (260, "Tzolkin sacred calendar days"),
20765 (365, "Haab solar calendar days"),
20766 (400, "Baktun - 20×20 years"),
20767 ],
20768 "babylonian" | "mesopotamian" => vec![
20769 (12, "Months, hours - celestial division"),
20770 (60, "Sexagesimal base - minutes, seconds, degrees"),
20771 (360, "Circle degrees - 6×60"),
20772 (3600, "Sar - 60×60, large count"),
20773 (7, "Planets visible to naked eye"),
20774 ],
20775 "chinese" | "zh" => vec![
20776 (8, "八 (bā) - prosperity, wealth (sounds like 發)"),
20777 (9, "九 (jiǔ) - longevity (sounds like 久)"),
20778 (6, "六 (liù) - smooth, flowing"),
20779 (2, "二 (èr) - pairs, harmony"),
20780 (4, "四 (sì) - AVOID - sounds like death (死)"),
20781 (5, "五 (wǔ) - five elements"),
20782 (12, "十二 - zodiac animals"),
20783 ],
20784 "japanese" | "ja" => vec![
20785 (7, "七 (nana) - lucky, seven gods of fortune"),
20786 (8, "八 (hachi) - prosperity, expansion"),
20787 (3, "三 (san) - completeness"),
20788 (5, "五 (go) - five elements"),
20789 (4, "四 (shi) - AVOID - sounds like death (死)"),
20790 (9, "九 (ku) - AVOID - sounds like suffering (苦)"),
20791 ],
20792 "hebrew" | "jewish" => vec![
20793 (7, "Shabbat, creation days, menorah branches"),
20794 (18, "Chai (חי) - life - gematria of ח(8) + י(10)"),
20795 (40, "Transformation - flood, Sinai, wilderness"),
20796 (12, "Tribes of Israel"),
20797 (613, "Mitzvot - commandments"),
20798 (26, "Gematria of YHWH"),
20799 ],
20800 "islamic" | "arabic" | "ar" => vec![
20801 (5, "Pillars of Islam, daily prayers"),
20802 (7, "Heavens, circumambulation of Kaaba"),
20803 (40, "Age of prophethood, days of repentance"),
20804 (99, "Names of Allah"),
20805 (786, "Abjad value of Bismillah"),
20806 ],
20807 "hindu" | "indian" | "hi" => vec![
20808 (108, "Sacred beads, Upanishads, sun's distance"),
20809 (7, "Chakras (main), rishis, sacred rivers"),
20810 (3, "Trimurti - Brahma, Vishnu, Shiva"),
20811 (4, "Vedas, yugas, varnas"),
20812 (9, "Planets (navagraha), durga forms"),
20813 (1008, "Names of Vishnu"),
20814 ],
20815 "greek" | "pythagorean" => vec![
20816 (1, "Monad - unity, source"),
20817 (2, "Dyad - duality, diversity"),
20818 (3, "Triad - harmony, completion"),
20819 (4, "Tetrad - solidity, earth"),
20820 (7, "Heptad - perfection"),
20821 (10, "Decad - tetractys, divine"),
20822 (12, "Olympian gods"),
20823 ],
20824 "celtic" | "irish" => vec![
20825 (3, "Triple goddess, triquetra"),
20826 (5, "Elements including spirit"),
20827 (9, "Triple threes - sacred completion"),
20828 (13, "Lunar months"),
20829 (17, "St. Patrick's Day"),
20830 (20, "Vigesimal counting"),
20831 ],
20832 _ => vec![
20833 (1, "Unity"),
20834 (7, "Widely considered lucky"),
20835 (12, "Dozen - practical division"),
20836 (13, "Often considered unlucky in West"),
20837 ],
20838 };
20839
20840 let result: Vec<Value> = numbers
20841 .iter()
20842 .map(|(n, meaning)| {
20843 let mut entry = std::collections::HashMap::new();
20844 entry.insert("number".to_string(), Value::Int(*n));
20845 entry.insert(
20846 "meaning".to_string(),
20847 Value::String(Rc::new(meaning.to_string())),
20848 );
20849 Value::Map(Rc::new(RefCell::new(entry)))
20850 })
20851 .collect();
20852
20853 Ok(Value::Array(Rc::new(RefCell::new(result))))
20854 });
20855
20856 define(interp, "is_sacred", Some(2), |_, args| {
20858 let n = match &args[0] {
20859 Value::Int(n) => *n,
20860 _ => return Err(RuntimeError::new("is_sacred() requires integer")),
20861 };
20862 let culture = match &args[1] {
20863 Value::String(s) => s.to_lowercase(),
20864 _ => return Err(RuntimeError::new("is_sacred() requires string culture")),
20865 };
20866
20867 let sacred = match culture.as_str() {
20868 "mayan" | "maya" => vec![13, 20, 52, 260, 365, 400],
20869 "babylonian" | "mesopotamian" => vec![12, 60, 360, 3600, 7],
20870 "chinese" | "zh" => vec![8, 9, 6, 2, 5, 12],
20871 "japanese" | "ja" => vec![7, 8, 3, 5],
20872 "hebrew" | "jewish" => vec![7, 18, 40, 12, 613, 26],
20873 "islamic" | "arabic" | "ar" => vec![5, 7, 40, 99, 786],
20874 "hindu" | "indian" | "hi" => vec![108, 7, 3, 4, 9, 1008],
20875 "greek" | "pythagorean" => vec![1, 2, 3, 4, 7, 10, 12],
20876 "celtic" | "irish" => vec![3, 5, 9, 13, 17, 20],
20877 _ => vec![7, 12],
20878 };
20879
20880 Ok(Value::Bool(sacred.contains(&n)))
20881 });
20882
20883 define(interp, "is_unlucky", Some(2), |_, args| {
20885 let n = match &args[0] {
20886 Value::Int(n) => *n,
20887 _ => return Err(RuntimeError::new("is_unlucky() requires integer")),
20888 };
20889 let culture = match &args[1] {
20890 Value::String(s) => s.to_lowercase(),
20891 _ => return Err(RuntimeError::new("is_unlucky() requires string culture")),
20892 };
20893
20894 let unlucky = match culture.as_str() {
20895 "chinese" | "zh" => vec![4], "japanese" | "ja" => vec![4, 9], "western" | "en" => vec![13], "italian" | "it" => vec![17], _ => vec![],
20900 };
20901
20902 Ok(Value::Bool(unlucky.contains(&n)))
20903 });
20904
20905 define(interp, "number_meaning", Some(2), |_, args| {
20907 let n = match &args[0] {
20908 Value::Int(n) => *n,
20909 _ => return Err(RuntimeError::new("number_meaning() requires integer")),
20910 };
20911 let culture = match &args[1] {
20912 Value::String(s) => s.to_lowercase(),
20913 _ => {
20914 return Err(RuntimeError::new(
20915 "number_meaning() requires string culture",
20916 ))
20917 }
20918 };
20919
20920 let meaning = match (n, culture.as_str()) {
20921 (8, "chinese" | "zh") => "八 (bā) - Most auspicious, sounds like 發 (prosperity)",
20923 (4, "chinese" | "zh") => "四 (sì) - Unlucky, sounds like 死 (death)",
20924 (9, "chinese" | "zh") => "九 (jiǔ) - Longevity, sounds like 久 (long-lasting)",
20925 (6, "chinese" | "zh") => "六 (liù) - Smooth and well-off",
20926 (2, "chinese" | "zh") => "二 (èr) - Good things come in pairs",
20927
20928 (7, "japanese" | "ja") => "七 (nana) - Lucky, seven gods of fortune",
20930 (8, "japanese" | "ja") => "八 (hachi) - Lucky, represents spreading prosperity",
20931 (4, "japanese" | "ja") => "四 (shi) - Unlucky, homophone of death",
20932 (9, "japanese" | "ja") => "九 (ku) - Unlucky, homophone of suffering",
20933
20934 (18, "hebrew" | "jewish") => "Chai (חי) - Life itself, most auspicious",
20936 (7, "hebrew" | "jewish") => "Divine completion - Shabbat, menorah, creation",
20937 (40, "hebrew" | "jewish") => "Transformation - flood, Sinai, wilderness years",
20938 (613, "hebrew" | "jewish") => "Taryag - total number of mitzvot (commandments)",
20939
20940 (13, "mayan" | "maya") => "Sacred Tzolkin cycle, celestial layers",
20942 (20, "mayan" | "maya") => "Vigesimal base - fingers and toes, uinal",
20943 (260, "mayan" | "maya") => "Tzolkin calendar - 13 × 20 sacred days",
20944
20945 (60, "babylonian" | "mesopotamian") => {
20947 "Sexagesimal base - divisible by 2,3,4,5,6,10,12,15,20,30"
20948 }
20949 (360, "babylonian" | "mesopotamian") => "Circle degrees - celestial observation",
20950
20951 (108, "hindu" | "indian" | "hi") => {
20953 "Sacred completeness - mala beads, Upanishads, sun ratio"
20954 }
20955 (3, "hindu" | "indian" | "hi") => "Trimurti - Brahma, Vishnu, Shiva",
20956 (9, "hindu" | "indian" | "hi") => "Navagraha (9 planets), Durga's 9 forms",
20957
20958 (99, "islamic" | "arabic" | "ar") => "Asma ul-Husna - 99 names of Allah",
20960 (5, "islamic" | "arabic" | "ar") => "Five pillars of Islam",
20961 (786, "islamic" | "arabic" | "ar") => "Abjad numerology of Bismillah",
20962
20963 (10, "greek" | "pythagorean") => "Tetractys - 1+2+3+4, perfect number",
20965 (7, "greek" | "pythagorean") => "Heptad - virgin number, Athena's number",
20966
20967 _ => "No specific cultural meaning recorded",
20968 };
20969
20970 let mut result = std::collections::HashMap::new();
20971 result.insert("number".to_string(), Value::Int(n));
20972 result.insert("culture".to_string(), Value::String(Rc::new(culture)));
20973 result.insert(
20974 "meaning".to_string(),
20975 Value::String(Rc::new(meaning.to_string())),
20976 );
20977
20978 Ok(Value::Map(Rc::new(RefCell::new(result))))
20979 });
20980
20981 define(interp, "to_babylonian_time", Some(1), |_, args| {
20985 let seconds = match &args[0] {
20986 Value::Int(n) => *n,
20987 Value::Float(f) => *f as i64,
20988 _ => return Err(RuntimeError::new("to_babylonian_time() requires number")),
20989 };
20990
20991 let hours = seconds / 3600;
20992 let mins = (seconds % 3600) / 60;
20993 let secs = seconds % 60;
20994
20995 Ok(Value::String(Rc::new(format!(
20996 "0s[{}:{}:{}]",
20997 hours, mins, secs
20998 ))))
20999 });
21000
21001 define(interp, "from_babylonian_time", Some(1), |_, args| {
21003 let s = match &args[0] {
21004 Value::String(s) => s.to_string(),
21005 _ => return Err(RuntimeError::new("from_babylonian_time() requires string")),
21006 };
21007
21008 let clean = s
21009 .trim_start_matches("0s")
21010 .trim_start_matches('[')
21011 .trim_end_matches(']');
21012
21013 let parts: Vec<i64> = clean
21014 .split(':')
21015 .map(|p| p.trim().parse::<i64>().unwrap_or(0))
21016 .collect();
21017
21018 let seconds = match parts.len() {
21019 1 => parts[0],
21020 2 => parts[0] * 60 + parts[1],
21021 3 => parts[0] * 3600 + parts[1] * 60 + parts[2],
21022 _ => return Err(RuntimeError::new("Invalid Babylonian time format")),
21023 };
21024
21025 Ok(Value::Int(seconds))
21026 });
21027
21028 define(interp, "vigesimal_shares", Some(3), |_, args| {
21032 let secret = match &args[0] {
21033 Value::String(s) => s.as_bytes().to_vec(),
21034 _ => {
21035 return Err(RuntimeError::new(
21036 "vigesimal_shares() requires string secret",
21037 ))
21038 }
21039 };
21040
21041 let threshold = match &args[1] {
21042 Value::Int(n) => *n as usize,
21043 _ => {
21044 return Err(RuntimeError::new(
21045 "vigesimal_shares() requires integer threshold",
21046 ))
21047 }
21048 };
21049
21050 let num_shares = match &args[2] {
21051 Value::Int(n) => *n as usize,
21052 _ => {
21053 return Err(RuntimeError::new(
21054 "vigesimal_shares() requires integer num_shares",
21055 ))
21056 }
21057 };
21058
21059 if threshold < 2 || num_shares < threshold || num_shares > 20 {
21060 return Err(RuntimeError::new(
21061 "vigesimal_shares() requires 2 <= threshold <= num_shares <= 20 (Mayan limit)",
21062 ));
21063 }
21064
21065 let mut rng = rand::thread_rng();
21067 let mut shares: Vec<Vec<u8>> = (0..num_shares)
21068 .map(|_| Vec::with_capacity(secret.len() + 1))
21069 .collect();
21070
21071 for (i, share) in shares.iter_mut().enumerate() {
21072 share.push((i + 1) as u8);
21073 }
21074
21075 for &byte in &secret {
21076 let mut coefficients: Vec<u8> = vec![byte];
21077 for _ in 1..threshold {
21078 coefficients.push(rng.gen());
21079 }
21080
21081 for (i, share) in shares.iter_mut().enumerate() {
21082 let x = (i + 1) as u8;
21083 let y = eval_polynomial_gf256(&coefficients, x);
21084 share.push(y);
21085 }
21086 }
21087
21088 let share_values: Vec<Value> = shares
21090 .iter()
21091 .enumerate()
21092 .map(|(i, share)| {
21093 let mut entry = std::collections::HashMap::new();
21094
21095 let mut vig_parts: Vec<String> = Vec::new();
21097 for &byte in share {
21098 vig_parts.push(to_base_string(byte as u64, 20, true));
21099 }
21100
21101 entry.insert("index".to_string(), Value::Int((i + 1) as i64));
21102 entry.insert(
21103 "vigesimal".to_string(),
21104 Value::String(Rc::new(format!("0v{}", vig_parts.join(".")))),
21105 );
21106 entry.insert(
21107 "hex".to_string(),
21108 Value::String(Rc::new(hex::encode(share))),
21109 );
21110
21111 Value::Map(Rc::new(RefCell::new(entry)))
21112 })
21113 .collect();
21114
21115 let mut result = std::collections::HashMap::new();
21116 result.insert(
21117 "shares".to_string(),
21118 Value::Array(Rc::new(RefCell::new(share_values))),
21119 );
21120 result.insert("threshold".to_string(), Value::Int(threshold as i64));
21121 result.insert("total".to_string(), Value::Int(num_shares as i64));
21122 result.insert(
21123 "base".to_string(),
21124 Value::String(Rc::new("vigesimal (Mayan base-20)".to_string())),
21125 );
21126
21127 Ok(Value::Map(Rc::new(RefCell::new(result))))
21128 });
21129
21130 define(interp, "multibase_info", Some(0), |_, _| {
21132 let mut info = std::collections::HashMap::new();
21133
21134 let bases = vec![
21135 ("binary", 2, "0b", "Modern computing"),
21136 ("octal", 8, "0o", "Unix, historical computing"),
21137 ("decimal", 10, "", "Indo-Arabic global standard"),
21138 (
21139 "duodecimal",
21140 12,
21141 "0z",
21142 "Dozen system - time, music, measurement",
21143 ),
21144 ("hexadecimal", 16, "0x", "Computing, colors, addresses"),
21145 (
21146 "vigesimal",
21147 20,
21148 "0v",
21149 "Mayan, Celtic, Basque - human digits",
21150 ),
21151 (
21152 "sexagesimal",
21153 60,
21154 "0s",
21155 "Babylonian - time, angles, astronomy",
21156 ),
21157 ];
21158
21159 let base_list: Vec<Value> = bases
21160 .iter()
21161 .map(|(name, base, prefix, desc)| {
21162 let mut entry = std::collections::HashMap::new();
21163 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21164 entry.insert("base".to_string(), Value::Int(*base as i64));
21165 entry.insert(
21166 "prefix".to_string(),
21167 Value::String(Rc::new(prefix.to_string())),
21168 );
21169 entry.insert(
21170 "origin".to_string(),
21171 Value::String(Rc::new(desc.to_string())),
21172 );
21173 Value::Map(Rc::new(RefCell::new(entry)))
21174 })
21175 .collect();
21176
21177 info.insert(
21178 "numeral_systems".to_string(),
21179 Value::Array(Rc::new(RefCell::new(base_list))),
21180 );
21181
21182 let encodings = vec!["base58 (Bitcoin)", "base32 (RFC 4648)", "base64 (standard)"];
21183 let enc_list: Vec<Value> = encodings
21184 .iter()
21185 .map(|s| Value::String(Rc::new(s.to_string())))
21186 .collect();
21187 info.insert(
21188 "special_encodings".to_string(),
21189 Value::Array(Rc::new(RefCell::new(enc_list))),
21190 );
21191
21192 let cultures = vec![
21193 "mayan",
21194 "babylonian",
21195 "chinese",
21196 "japanese",
21197 "hebrew",
21198 "islamic",
21199 "hindu",
21200 "greek",
21201 "celtic",
21202 ];
21203 let cult_list: Vec<Value> = cultures
21204 .iter()
21205 .map(|s| Value::String(Rc::new(s.to_string())))
21206 .collect();
21207 info.insert(
21208 "supported_cultures".to_string(),
21209 Value::Array(Rc::new(RefCell::new(cult_list))),
21210 );
21211
21212 Ok(Value::Map(Rc::new(RefCell::new(info))))
21213 });
21214}
21215
21216fn to_base_string(mut n: u64, base: u64, pad_to_two: bool) -> String {
21219 const DIGITS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
21220
21221 if n == 0 {
21222 return if pad_to_two {
21223 "00".to_string()
21224 } else {
21225 "0".to_string()
21226 };
21227 }
21228
21229 let mut result = Vec::new();
21230 while n > 0 {
21231 result.push(DIGITS[(n % base) as usize] as char);
21232 n /= base;
21233 }
21234
21235 if pad_to_two && result.len() < 2 {
21236 result.push('0');
21237 }
21238
21239 result.reverse();
21240 result.into_iter().collect()
21241}
21242
21243fn to_base_string_custom(mut n: u64, base: u64, digits: &str) -> String {
21244 if n == 0 {
21245 return digits.chars().next().unwrap().to_string();
21246 }
21247
21248 let mut result = Vec::new();
21249 let digit_chars: Vec<char> = digits.chars().collect();
21250
21251 while n > 0 {
21252 result.push(digit_chars[(n % base) as usize]);
21253 n /= base;
21254 }
21255
21256 result.reverse();
21257 result.into_iter().collect()
21258}
21259
21260fn from_base_string(s: &str, base: u64) -> Result<u64, RuntimeError> {
21261 let mut result: u64 = 0;
21262
21263 for c in s.chars() {
21264 let digit = match c {
21265 '0'..='9' => c as u64 - '0' as u64,
21266 'A'..='Z' => c as u64 - 'A' as u64 + 10,
21267 'a'..='z' => c as u64 - 'a' as u64 + 10,
21268 _ => return Err(RuntimeError::new(format!("Invalid digit: {}", c))),
21269 };
21270
21271 if digit >= base {
21272 return Err(RuntimeError::new(format!(
21273 "Digit {} out of range for base {}",
21274 c, base
21275 )));
21276 }
21277
21278 result = result
21279 .checked_mul(base)
21280 .and_then(|r| r.checked_add(digit))
21281 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
21282 }
21283
21284 Ok(result)
21285}
21286
21287fn from_base_string_custom(s: &str, digits: &str) -> Result<u64, RuntimeError> {
21288 let base = digits.len() as u64;
21289 let mut result: u64 = 0;
21290
21291 for c in s.chars() {
21292 let digit = digits
21293 .find(c)
21294 .ok_or_else(|| RuntimeError::new(format!("Invalid digit: {}", c)))?
21295 as u64;
21296
21297 result = result
21298 .checked_mul(base)
21299 .and_then(|r| r.checked_add(digit))
21300 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
21301 }
21302
21303 Ok(result)
21304}
21305
21306fn parse_base_prefix(s: &str, prefix: &str) -> (bool, String) {
21307 let negative = s.starts_with('-');
21308 let clean = s
21309 .trim_start_matches('-')
21310 .trim_start_matches(prefix)
21311 .to_string();
21312 (negative, clean)
21313}
21314
21315fn register_audio(interp: &mut Interpreter) {
21343 define(interp, "tune", Some(3), |_, args| {
21350 let note = match &args[0] {
21351 Value::Int(n) => *n as f64, Value::Float(f) => *f, Value::String(s) => parse_note_name(s)?,
21354 _ => return Err(RuntimeError::new("tune() requires note number or name")),
21355 };
21356
21357 let system = match &args[1] {
21358 Value::String(s) => s.to_lowercase(),
21359 _ => return Err(RuntimeError::new("tune() requires tuning system name")),
21360 };
21361
21362 let root_freq = match &args[2] {
21363 Value::Float(f) => *f,
21364 Value::Int(i) => *i as f64,
21365 _ => return Err(RuntimeError::new("tune() requires root frequency")),
21366 };
21367
21368 let freq = match system.as_str() {
21369 "12tet" | "equal" | "western" => {
21370 root_freq * 2.0_f64.powf((note - 69.0) / 12.0)
21372 }
21373 "24tet" | "quarter" | "arabic" | "maqam" => {
21374 root_freq * 2.0_f64.powf((note - 69.0) / 24.0)
21376 }
21377 "just" | "pure" => {
21378 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21380 let octave = ((note - 69.0) / 12.0).floor();
21381 let ratio = just_intonation_ratio(interval as i32);
21382 root_freq * ratio * 2.0_f64.powf(octave)
21383 }
21384 "pythagorean" => {
21385 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21387 let octave = ((note - 69.0) / 12.0).floor();
21388 let ratio = pythagorean_ratio(interval as i32);
21389 root_freq * ratio * 2.0_f64.powf(octave)
21390 }
21391 "meantone" | "quarter_comma" => {
21392 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21394 let octave = ((note - 69.0) / 12.0).floor();
21395 let ratio = meantone_ratio(interval as i32);
21396 root_freq * ratio * 2.0_f64.powf(octave)
21397 }
21398 "53tet" | "turkish" | "persian" | "comma" => {
21399 root_freq * 2.0_f64.powf((note - 69.0) / 53.0)
21401 }
21402 "22shruti" | "shruti" | "indian" => {
21403 let shruti = (note - 69.0) % 22.0;
21405 let octave = ((note - 69.0) / 22.0).floor();
21406 let ratio = shruti_ratio(shruti as i32);
21407 root_freq * ratio * 2.0_f64.powf(octave)
21408 }
21409 "gamelan_pelog" | "pelog" => {
21410 let degree = ((note - 69.0) % 7.0 + 7.0) % 7.0;
21412 let octave = ((note - 69.0) / 7.0).floor();
21413 let ratio = pelog_ratio(degree as i32);
21414 root_freq * ratio * 2.0_f64.powf(octave)
21415 }
21416 "gamelan_slendro" | "slendro" => {
21417 let degree = ((note - 69.0) % 5.0 + 5.0) % 5.0;
21419 let octave = ((note - 69.0) / 5.0).floor();
21420 let ratio = slendro_ratio(degree as i32);
21421 root_freq * ratio * 2.0_f64.powf(octave)
21422 }
21423 "bohlen_pierce" | "bp" => {
21424 root_freq * 3.0_f64.powf((note - 69.0) / 13.0)
21426 }
21427 _ => {
21428 return Err(RuntimeError::new(format!(
21429 "Unknown tuning system: {}",
21430 system
21431 )))
21432 }
21433 };
21434
21435 Ok(Value::Float(freq))
21436 });
21437
21438 define(interp, "tuning_info", Some(1), |_, args| {
21440 let system = match &args[0] {
21441 Value::String(s) => s.to_lowercase(),
21442 _ => return Err(RuntimeError::new("tuning_info() requires string")),
21443 };
21444
21445 let (name, notes_per_octave, origin, description) = match system.as_str() {
21446 "12tet" | "equal" | "western" => (
21447 "12-TET", 12, "Western (18th century)",
21448 "Equal temperament - every semitone is exactly 2^(1/12). Universal but slightly impure."
21449 ),
21450 "24tet" | "quarter" | "arabic" | "maqam" => (
21451 "24-TET", 24, "Arabic/Turkish",
21452 "Quarter-tone system for maqam music. Enables neutral seconds and other microtones."
21453 ),
21454 "just" | "pure" => (
21455 "Just Intonation", 12, "Ancient (Ptolemy)",
21456 "Pure frequency ratios (3:2 fifth, 5:4 third). Beatless intervals but limited modulation."
21457 ),
21458 "pythagorean" => (
21459 "Pythagorean", 12, "Ancient Greece",
21460 "Built entirely from perfect fifths (3:2). Pure fifths but harsh thirds."
21461 ),
21462 "meantone" | "quarter_comma" => (
21463 "Quarter-Comma Meantone", 12, "Renaissance Europe",
21464 "Tempered fifths for pure major thirds. Beautiful in limited keys."
21465 ),
21466 "53tet" | "turkish" | "persian" | "comma" => (
21467 "53-TET", 53, "Turkish/Persian",
21468 "53 notes per octave. Closely approximates just intonation and allows maqam/dastgah."
21469 ),
21470 "22shruti" | "shruti" | "indian" => (
21471 "22-Shruti", 22, "Indian Classical",
21472 "Ancient Indian system. Each shruti is a 'microtone' - the smallest perceptible interval."
21473 ),
21474 "gamelan_pelog" | "pelog" => (
21475 "Pelog", 7, "Javanese Gamelan",
21476 "Heptatonic scale with unequal steps. Each gamelan has unique tuning - instruments are married."
21477 ),
21478 "gamelan_slendro" | "slendro" => (
21479 "Slendro", 5, "Javanese Gamelan",
21480 "Pentatonic scale, roughly equal steps (~240 cents). Each ensemble uniquely tuned."
21481 ),
21482 "bohlen_pierce" | "bp" => (
21483 "Bohlen-Pierce", 13, "Modern (1970s)",
21484 "Non-octave scale based on 3:1 tritave. Alien but mathematically beautiful."
21485 ),
21486 _ => return Err(RuntimeError::new(format!("Unknown tuning system: {}", system))),
21487 };
21488
21489 let mut info = std::collections::HashMap::new();
21490 info.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21491 info.insert("notes_per_octave".to_string(), Value::Int(notes_per_octave));
21492 info.insert(
21493 "origin".to_string(),
21494 Value::String(Rc::new(origin.to_string())),
21495 );
21496 info.insert(
21497 "description".to_string(),
21498 Value::String(Rc::new(description.to_string())),
21499 );
21500
21501 Ok(Value::Map(Rc::new(RefCell::new(info))))
21502 });
21503
21504 define(interp, "list_tuning_systems", Some(0), |_, _| {
21506 let systems = vec![
21507 ("12tet", "Western equal temperament", 12),
21508 ("24tet", "Arabic/Turkish quarter-tones", 24),
21509 ("just", "Pure ratio just intonation", 12),
21510 ("pythagorean", "Ancient Greek pure fifths", 12),
21511 ("meantone", "Renaissance quarter-comma", 12),
21512 ("53tet", "Turkish/Persian comma system", 53),
21513 ("22shruti", "Indian microtonal", 22),
21514 ("pelog", "Javanese gamelan 7-note", 7),
21515 ("slendro", "Javanese gamelan 5-note", 5),
21516 ("bohlen_pierce", "Non-octave tritave scale", 13),
21517 ];
21518
21519 let result: Vec<Value> = systems
21520 .iter()
21521 .map(|(name, desc, notes)| {
21522 let mut entry = std::collections::HashMap::new();
21523 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21524 entry.insert(
21525 "description".to_string(),
21526 Value::String(Rc::new(desc.to_string())),
21527 );
21528 entry.insert("notes_per_octave".to_string(), Value::Int(*notes));
21529 Value::Map(Rc::new(RefCell::new(entry)))
21530 })
21531 .collect();
21532
21533 Ok(Value::Array(Rc::new(RefCell::new(result))))
21534 });
21535
21536 define(interp, "sacred_freq", Some(1), |_, args| {
21542 let name = match &args[0] {
21543 Value::String(s) => s.to_lowercase(),
21544 _ => return Err(RuntimeError::new("sacred_freq() requires string")),
21545 };
21546
21547 let (freq, description) = match name.as_str() {
21548 "om" | "ॐ" | "aum" => (136.1, "Om - Earth year frequency (Cosmic Om)"),
21550 "earth_day" => (194.18, "Earth day - one rotation"),
21551 "earth_year" => (136.1, "Earth year - one orbit (Om frequency)"),
21552 "schumann" | "earth_resonance" => (
21553 7.83,
21554 "Schumann resonance - Earth's electromagnetic heartbeat",
21555 ),
21556
21557 "ut" | "do" | "396" => (396.0, "UT/DO - Liberating guilt and fear"),
21559 "re" | "417" => (417.0, "RE - Undoing situations, facilitating change"),
21560 "mi" | "528" => (528.0, "MI - Transformation, miracles, DNA repair"),
21561 "fa" | "639" => (639.0, "FA - Connecting relationships"),
21562 "sol" | "741" => (741.0, "SOL - Awakening intuition"),
21563 "la" | "852" => (852.0, "LA - Returning to spiritual order"),
21564 "si" | "963" => (963.0, "SI - Divine consciousness, enlightenment"),
21565 "174" => (174.0, "Solfeggio foundation - pain relief"),
21566 "285" => (285.0, "Solfeggio - healing tissue"),
21567
21568 "sun" | "☉" => (126.22, "Sun - ego, vitality, leadership"),
21570 "moon" | "☽" | "☾" => (210.42, "Moon - emotion, intuition, cycles"),
21571 "mercury" | "☿" => (141.27, "Mercury - communication, intellect"),
21572 "venus" | "♀" => (221.23, "Venus - love, beauty, harmony"),
21573 "mars" | "♂" => (144.72, "Mars - energy, action, courage"),
21574 "jupiter" | "♃" => (183.58, "Jupiter - expansion, wisdom, luck"),
21575 "saturn" | "♄" => (147.85, "Saturn - discipline, structure, time"),
21576
21577 "root" | "muladhara" => (256.0, "Root chakra - survival, grounding (C)"),
21579 "sacral" | "svadhisthana" => (288.0, "Sacral chakra - creativity, sexuality (D)"),
21580 "solar" | "manipura" => (320.0, "Solar plexus - will, power (E)"),
21581 "heart" | "anahata" => (341.3, "Heart chakra - love, compassion (F)"),
21582 "throat" | "vishuddha" => (384.0, "Throat chakra - expression, truth (G)"),
21583 "third_eye" | "ajna" => (426.7, "Third eye - intuition, insight (A)"),
21584 "crown" | "sahasrara" => (480.0, "Crown chakra - consciousness, unity (B)"),
21585
21586 "a440" | "iso" => (440.0, "ISO standard concert pitch (1955)"),
21588 "a432" | "verdi" => (
21589 432.0,
21590 "Verdi pitch - 'mathematically consistent with universe'",
21591 ),
21592 "a415" | "baroque" => (415.0, "Baroque pitch - period instrument standard"),
21593 "a466" | "chorton" => (466.0, "Choir pitch - high Baroque German"),
21594
21595 "delta" => (2.0, "Delta waves - deep sleep, healing (0.5-4 Hz)"),
21597 "theta" => (6.0, "Theta waves - meditation, creativity (4-8 Hz)"),
21598 "alpha" => (10.0, "Alpha waves - relaxation, calm focus (8-13 Hz)"),
21599 "beta" => (20.0, "Beta waves - alertness, concentration (13-30 Hz)"),
21600 "gamma" => (40.0, "Gamma waves - insight, peak performance (30-100 Hz)"),
21601
21602 _ => {
21603 return Err(RuntimeError::new(format!(
21604 "Unknown sacred frequency: {}",
21605 name
21606 )))
21607 }
21608 };
21609
21610 let mut result = std::collections::HashMap::new();
21611 result.insert("frequency".to_string(), Value::Float(freq));
21612 result.insert("name".to_string(), Value::String(Rc::new(name)));
21613 result.insert(
21614 "meaning".to_string(),
21615 Value::String(Rc::new(description.to_string())),
21616 );
21617
21618 Ok(Value::Map(Rc::new(RefCell::new(result))))
21619 });
21620
21621 define(interp, "solfeggio", Some(0), |_, _| {
21623 let frequencies = vec![
21624 (174.0, "Foundation", "Pain relief, security"),
21625 (285.0, "Quantum", "Healing tissue, safety"),
21626 (396.0, "UT", "Liberating guilt and fear"),
21627 (417.0, "RE", "Undoing situations, change"),
21628 (528.0, "MI", "Transformation, DNA repair, miracles"),
21629 (639.0, "FA", "Connecting relationships"),
21630 (741.0, "SOL", "Awakening intuition"),
21631 (852.0, "LA", "Spiritual order"),
21632 (963.0, "SI", "Divine consciousness"),
21633 ];
21634
21635 let result: Vec<Value> = frequencies
21636 .iter()
21637 .map(|(freq, name, meaning)| {
21638 let mut entry = std::collections::HashMap::new();
21639 entry.insert("frequency".to_string(), Value::Float(*freq));
21640 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21641 entry.insert(
21642 "meaning".to_string(),
21643 Value::String(Rc::new(meaning.to_string())),
21644 );
21645 Value::Map(Rc::new(RefCell::new(entry)))
21646 })
21647 .collect();
21648
21649 Ok(Value::Array(Rc::new(RefCell::new(result))))
21650 });
21651
21652 define(interp, "chakras", Some(0), |_, _| {
21654 let chakras = vec![
21655 (
21656 256.0,
21657 "Muladhara",
21658 "Root",
21659 "Red",
21660 "Survival, grounding, stability",
21661 ),
21662 (
21663 288.0,
21664 "Svadhisthana",
21665 "Sacral",
21666 "Orange",
21667 "Creativity, sexuality, emotion",
21668 ),
21669 (
21670 320.0,
21671 "Manipura",
21672 "Solar Plexus",
21673 "Yellow",
21674 "Will, power, self-esteem",
21675 ),
21676 (
21677 341.3,
21678 "Anahata",
21679 "Heart",
21680 "Green",
21681 "Love, compassion, connection",
21682 ),
21683 (
21684 384.0,
21685 "Vishuddha",
21686 "Throat",
21687 "Blue",
21688 "Expression, truth, communication",
21689 ),
21690 (
21691 426.7,
21692 "Ajna",
21693 "Third Eye",
21694 "Indigo",
21695 "Intuition, insight, wisdom",
21696 ),
21697 (
21698 480.0,
21699 "Sahasrara",
21700 "Crown",
21701 "Violet",
21702 "Consciousness, unity, transcendence",
21703 ),
21704 ];
21705
21706 let result: Vec<Value> = chakras
21707 .iter()
21708 .map(|(freq, sanskrit, english, color, meaning)| {
21709 let mut entry = std::collections::HashMap::new();
21710 entry.insert("frequency".to_string(), Value::Float(*freq));
21711 entry.insert(
21712 "sanskrit".to_string(),
21713 Value::String(Rc::new(sanskrit.to_string())),
21714 );
21715 entry.insert(
21716 "english".to_string(),
21717 Value::String(Rc::new(english.to_string())),
21718 );
21719 entry.insert(
21720 "color".to_string(),
21721 Value::String(Rc::new(color.to_string())),
21722 );
21723 entry.insert(
21724 "meaning".to_string(),
21725 Value::String(Rc::new(meaning.to_string())),
21726 );
21727 Value::Map(Rc::new(RefCell::new(entry)))
21728 })
21729 .collect();
21730
21731 Ok(Value::Array(Rc::new(RefCell::new(result))))
21732 });
21733
21734 define(interp, "sine", Some(3), |_, args| {
21742 generate_waveform(&args, |phase| phase.sin())
21743 });
21744
21745 define(interp, "square", Some(3), |_, args| {
21747 generate_waveform(&args, |phase| if phase.sin() >= 0.0 { 1.0 } else { -1.0 })
21748 });
21749
21750 define(interp, "sawtooth", Some(3), |_, args| {
21752 generate_waveform(&args, |phase| {
21753 let normalized = (phase / std::f64::consts::TAU).fract();
21754 2.0 * normalized - 1.0
21755 })
21756 });
21757
21758 define(interp, "triangle", Some(3), |_, args| {
21760 generate_waveform(&args, |phase| {
21761 let normalized = (phase / std::f64::consts::TAU).fract();
21762 if normalized < 0.5 {
21763 4.0 * normalized - 1.0
21764 } else {
21765 3.0 - 4.0 * normalized
21766 }
21767 })
21768 });
21769
21770 define(interp, "noise", Some(1), |_, args| {
21772 let samples = match &args[0] {
21773 Value::Int(n) => *n as usize,
21774 _ => return Err(RuntimeError::new("noise() requires integer sample count")),
21775 };
21776
21777 let mut rng = rand::thread_rng();
21778 let result: Vec<Value> = (0..samples)
21779 .map(|_| Value::Float(rng.gen::<f64>() * 2.0 - 1.0))
21780 .collect();
21781
21782 Ok(Value::Array(Rc::new(RefCell::new(result))))
21783 });
21784
21785 define(interp, "scale", Some(1), |_, args| {
21791 let name = match &args[0] {
21792 Value::String(s) => s.to_lowercase(),
21793 _ => return Err(RuntimeError::new("scale() requires string")),
21794 };
21795
21796 let (intervals, origin, description) = match name.as_str() {
21797 "major" | "ionian" => (
21799 vec![0, 2, 4, 5, 7, 9, 11],
21800 "Western",
21801 "Happy, bright, resolved",
21802 ),
21803 "minor" | "aeolian" => (
21804 vec![0, 2, 3, 5, 7, 8, 10],
21805 "Western",
21806 "Sad, dark, introspective",
21807 ),
21808 "dorian" => (
21809 vec![0, 2, 3, 5, 7, 9, 10],
21810 "Western/Jazz",
21811 "Minor with bright 6th",
21812 ),
21813 "phrygian" => (
21814 vec![0, 1, 3, 5, 7, 8, 10],
21815 "Western/Flamenco",
21816 "Spanish, exotic, tense",
21817 ),
21818 "lydian" => (
21819 vec![0, 2, 4, 6, 7, 9, 11],
21820 "Western",
21821 "Dreamy, floating, ethereal",
21822 ),
21823 "mixolydian" => (vec![0, 2, 4, 5, 7, 9, 10], "Western/Blues", "Bluesy major"),
21824 "locrian" => (vec![0, 1, 3, 5, 6, 8, 10], "Western", "Unstable, dissonant"),
21825
21826 "pentatonic_major" => (vec![0, 2, 4, 7, 9], "Universal", "No dissonance, universal"),
21828 "pentatonic_minor" => (vec![0, 3, 5, 7, 10], "Universal", "Blues foundation"),
21829
21830 "hirajoshi" => (vec![0, 2, 3, 7, 8], "Japanese", "Melancholic, mysterious"),
21832 "insen" => (vec![0, 1, 5, 7, 10], "Japanese", "Dark, zen, contemplative"),
21833 "iwato" => (vec![0, 1, 5, 6, 10], "Japanese", "Most dark and dissonant"),
21834 "kumoi" => (vec![0, 2, 3, 7, 9], "Japanese", "Gentle, peaceful"),
21835 "yo" => (vec![0, 2, 5, 7, 9], "Japanese", "Bright, folk, celebratory"),
21836
21837 "hijaz" => (
21839 vec![0, 1, 4, 5, 7, 8, 11],
21840 "Arabic",
21841 "Exotic, Middle Eastern",
21842 ),
21843 "bayati" => (
21844 vec![0, 1.5 as i32, 3, 5, 7, 8, 10],
21845 "Arabic",
21846 "Quarter-tone, soulful",
21847 ),
21848 "rast" => (
21849 vec![0, 2, 3.5 as i32, 5, 7, 9, 10.5 as i32],
21850 "Arabic",
21851 "Foundation maqam",
21852 ),
21853 "saba" => (
21854 vec![0, 1.5 as i32, 3, 4, 5, 8, 10],
21855 "Arabic",
21856 "Sad, spiritual",
21857 ),
21858
21859 "bhairav" => (
21861 vec![0, 1, 4, 5, 7, 8, 11],
21862 "Indian",
21863 "Morning raga, devotional",
21864 ),
21865 "yaman" | "kalyan" => (vec![0, 2, 4, 6, 7, 9, 11], "Indian", "Evening, romantic"),
21866 "bhairavi" => (
21867 vec![0, 1, 3, 5, 7, 8, 10],
21868 "Indian",
21869 "Concluding raga, devotional",
21870 ),
21871 "todi" => (vec![0, 1, 3, 6, 7, 8, 11], "Indian", "Serious, pathos"),
21872 "marwa" => (vec![0, 1, 4, 6, 7, 9, 11], "Indian", "Evening, longing"),
21873
21874 "blues" => (vec![0, 3, 5, 6, 7, 10], "American", "Blue notes, soul"),
21876
21877 "hungarian_minor" => (vec![0, 2, 3, 6, 7, 8, 11], "Hungarian", "Gypsy, dramatic"),
21879 "romanian" => (vec![0, 2, 3, 6, 7, 9, 10], "Romanian", "Folk, energetic"),
21880
21881 "ahava_raba" | "freygish" => (
21883 vec![0, 1, 4, 5, 7, 8, 10],
21884 "Jewish/Klezmer",
21885 "Cantorial, emotional",
21886 ),
21887 "mi_sheberach" => (vec![0, 2, 3, 6, 7, 9, 10], "Jewish", "Prayer mode"),
21888
21889 "gong" => (vec![0, 2, 4, 7, 9], "Chinese", "Palace mode, major-like"),
21891 "shang" => (vec![0, 2, 5, 7, 9], "Chinese", "Merchant mode"),
21892 "jue" => (vec![0, 3, 5, 7, 10], "Chinese", "Angle mode"),
21893 "zhi" => (vec![0, 2, 5, 7, 10], "Chinese", "Emblem mode"),
21894 "yu" => (vec![0, 3, 5, 8, 10], "Chinese", "Wings mode, minor-like"),
21895
21896 "pelog" => (
21898 vec![0, 1, 3, 7, 8],
21899 "Javanese",
21900 "7-note unequal temperament",
21901 ),
21902 "slendro" => (vec![0, 2, 5, 7, 9], "Javanese", "5-note roughly equal"),
21903
21904 "whole_tone" => (
21906 vec![0, 2, 4, 6, 8, 10],
21907 "Impressionist",
21908 "Dreamlike, no resolution",
21909 ),
21910 "chromatic" => (
21911 vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
21912 "Western",
21913 "All 12 notes",
21914 ),
21915 "diminished" => (vec![0, 2, 3, 5, 6, 8, 9, 11], "Jazz", "Symmetric, tense"),
21916 "augmented" => (vec![0, 3, 4, 7, 8, 11], "Jazz", "Symmetric, floating"),
21917
21918 _ => return Err(RuntimeError::new(format!("Unknown scale: {}", name))),
21919 };
21920
21921 let mut result = std::collections::HashMap::new();
21922 let intervals_values: Vec<Value> =
21923 intervals.iter().map(|&i| Value::Int(i as i64)).collect();
21924 result.insert(
21925 "intervals".to_string(),
21926 Value::Array(Rc::new(RefCell::new(intervals_values))),
21927 );
21928 result.insert(
21929 "origin".to_string(),
21930 Value::String(Rc::new(origin.to_string())),
21931 );
21932 result.insert(
21933 "character".to_string(),
21934 Value::String(Rc::new(description.to_string())),
21935 );
21936 result.insert("name".to_string(), Value::String(Rc::new(name)));
21937
21938 Ok(Value::Map(Rc::new(RefCell::new(result))))
21939 });
21940
21941 define(interp, "list_scales", Some(0), |_, _| {
21943 let mut cultures = std::collections::HashMap::new();
21944
21945 cultures.insert(
21946 "western".to_string(),
21947 Value::Array(Rc::new(RefCell::new(vec![
21948 Value::String(Rc::new("major".to_string())),
21949 Value::String(Rc::new("minor".to_string())),
21950 Value::String(Rc::new("dorian".to_string())),
21951 Value::String(Rc::new("phrygian".to_string())),
21952 Value::String(Rc::new("lydian".to_string())),
21953 Value::String(Rc::new("mixolydian".to_string())),
21954 Value::String(Rc::new("locrian".to_string())),
21955 ]))),
21956 );
21957
21958 cultures.insert(
21959 "japanese".to_string(),
21960 Value::Array(Rc::new(RefCell::new(vec![
21961 Value::String(Rc::new("hirajoshi".to_string())),
21962 Value::String(Rc::new("insen".to_string())),
21963 Value::String(Rc::new("iwato".to_string())),
21964 Value::String(Rc::new("kumoi".to_string())),
21965 Value::String(Rc::new("yo".to_string())),
21966 ]))),
21967 );
21968
21969 cultures.insert(
21970 "arabic".to_string(),
21971 Value::Array(Rc::new(RefCell::new(vec![
21972 Value::String(Rc::new("hijaz".to_string())),
21973 Value::String(Rc::new("bayati".to_string())),
21974 Value::String(Rc::new("rast".to_string())),
21975 Value::String(Rc::new("saba".to_string())),
21976 ]))),
21977 );
21978
21979 cultures.insert(
21980 "indian".to_string(),
21981 Value::Array(Rc::new(RefCell::new(vec![
21982 Value::String(Rc::new("bhairav".to_string())),
21983 Value::String(Rc::new("yaman".to_string())),
21984 Value::String(Rc::new("bhairavi".to_string())),
21985 Value::String(Rc::new("todi".to_string())),
21986 Value::String(Rc::new("marwa".to_string())),
21987 ]))),
21988 );
21989
21990 cultures.insert(
21991 "chinese".to_string(),
21992 Value::Array(Rc::new(RefCell::new(vec![
21993 Value::String(Rc::new("gong".to_string())),
21994 Value::String(Rc::new("shang".to_string())),
21995 Value::String(Rc::new("jue".to_string())),
21996 Value::String(Rc::new("zhi".to_string())),
21997 Value::String(Rc::new("yu".to_string())),
21998 ]))),
21999 );
22000
22001 cultures.insert(
22002 "jewish".to_string(),
22003 Value::Array(Rc::new(RefCell::new(vec![
22004 Value::String(Rc::new("ahava_raba".to_string())),
22005 Value::String(Rc::new("mi_sheberach".to_string())),
22006 ]))),
22007 );
22008
22009 cultures.insert(
22010 "indonesian".to_string(),
22011 Value::Array(Rc::new(RefCell::new(vec![
22012 Value::String(Rc::new("pelog".to_string())),
22013 Value::String(Rc::new("slendro".to_string())),
22014 ]))),
22015 );
22016
22017 Ok(Value::Map(Rc::new(RefCell::new(cultures))))
22018 });
22019
22020 define(interp, "interval_ratio", Some(2), |_, args| {
22026 let semitones = match &args[0] {
22027 Value::Int(n) => *n as f64,
22028 Value::Float(f) => *f,
22029 _ => return Err(RuntimeError::new("interval_ratio() requires number")),
22030 };
22031
22032 let tuning = match &args[1] {
22033 Value::String(s) => s.to_lowercase(),
22034 _ => return Err(RuntimeError::new("interval_ratio() requires tuning system")),
22035 };
22036
22037 let ratio = match tuning.as_str() {
22038 "12tet" | "equal" => 2.0_f64.powf(semitones / 12.0),
22039 "just" => just_intonation_ratio(semitones as i32),
22040 "pythagorean" => pythagorean_ratio(semitones as i32),
22041 _ => 2.0_f64.powf(semitones / 12.0),
22042 };
22043
22044 Ok(Value::Float(ratio))
22045 });
22046
22047 define(interp, "cents_between", Some(2), |_, args| {
22049 let f1 = match &args[0] {
22050 Value::Float(f) => *f,
22051 Value::Int(i) => *i as f64,
22052 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
22053 };
22054 let f2 = match &args[1] {
22055 Value::Float(f) => *f,
22056 Value::Int(i) => *i as f64,
22057 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
22058 };
22059
22060 let cents = 1200.0 * (f2 / f1).log2();
22061 Ok(Value::Float(cents))
22062 });
22063
22064 define(interp, "harmonic_series", Some(2), |_, args| {
22066 let fundamental = match &args[0] {
22067 Value::Float(f) => *f,
22068 Value::Int(i) => *i as f64,
22069 _ => return Err(RuntimeError::new("harmonic_series() requires frequency")),
22070 };
22071 let count = match &args[1] {
22072 Value::Int(n) => *n as usize,
22073 _ => return Err(RuntimeError::new("harmonic_series() requires count")),
22074 };
22075
22076 let harmonics: Vec<Value> = (1..=count)
22077 .map(|n| {
22078 let mut entry = std::collections::HashMap::new();
22079 entry.insert("harmonic".to_string(), Value::Int(n as i64));
22080 entry.insert(
22081 "frequency".to_string(),
22082 Value::Float(fundamental * n as f64),
22083 );
22084 entry.insert(
22085 "cents_from_root".to_string(),
22086 Value::Float(1200.0 * (n as f64).log2()),
22087 );
22088 Value::Map(Rc::new(RefCell::new(entry)))
22089 })
22090 .collect();
22091
22092 Ok(Value::Array(Rc::new(RefCell::new(harmonics))))
22093 });
22094
22095 define(interp, "audio_info", Some(0), |_, _| {
22100 let mut info = std::collections::HashMap::new();
22101
22102 info.insert(
22103 "tuning_systems".to_string(),
22104 Value::Array(Rc::new(RefCell::new(vec![
22105 Value::String(Rc::new(
22106 "12tet, 24tet, just, pythagorean, meantone".to_string(),
22107 )),
22108 Value::String(Rc::new(
22109 "53tet, 22shruti, pelog, slendro, bohlen_pierce".to_string(),
22110 )),
22111 ]))),
22112 );
22113
22114 info.insert(
22115 "waveforms".to_string(),
22116 Value::Array(Rc::new(RefCell::new(vec![
22117 Value::String(Rc::new("sine (∿)".to_string())),
22118 Value::String(Rc::new("square (⊓)".to_string())),
22119 Value::String(Rc::new("sawtooth (⋀)".to_string())),
22120 Value::String(Rc::new("triangle (△)".to_string())),
22121 Value::String(Rc::new("noise".to_string())),
22122 ]))),
22123 );
22124
22125 info.insert(
22126 "sacred_frequencies".to_string(),
22127 Value::Array(Rc::new(RefCell::new(vec![
22128 Value::String(Rc::new("om, solfeggio, chakras, planets".to_string())),
22129 Value::String(Rc::new(
22130 "schumann, brainwaves (delta/theta/alpha/beta/gamma)".to_string(),
22131 )),
22132 ]))),
22133 );
22134
22135 info.insert(
22136 "scale_cultures".to_string(),
22137 Value::Array(Rc::new(RefCell::new(vec![
22138 Value::String(Rc::new("western, japanese, arabic, indian".to_string())),
22139 Value::String(Rc::new("chinese, jewish, indonesian".to_string())),
22140 ]))),
22141 );
22142
22143 Ok(Value::Map(Rc::new(RefCell::new(info))))
22144 });
22145}
22146
22147fn parse_note_name(s: &str) -> Result<f64, RuntimeError> {
22150 let s = s.trim().to_uppercase();
22151 let (note, octave_offset) = if s.ends_with(|c: char| c.is_ascii_digit()) {
22152 let octave: i32 = s.chars().last().unwrap().to_digit(10).unwrap() as i32;
22153 let note_part = &s[..s.len() - 1];
22154 (note_part, (octave - 4) * 12) } else {
22156 (&s[..], 0)
22157 };
22158
22159 let semitone = match note {
22160 "C" => 0,
22161 "C#" | "DB" => 1,
22162 "D" => 2,
22163 "D#" | "EB" => 3,
22164 "E" => 4,
22165 "F" => 5,
22166 "F#" | "GB" => 6,
22167 "G" => 7,
22168 "G#" | "AB" => 8,
22169 "A" => 9,
22170 "A#" | "BB" => 10,
22171 "B" => 11,
22172 _ => return Err(RuntimeError::new(format!("Unknown note: {}", s))),
22173 };
22174
22175 Ok(69.0 + semitone as f64 - 9.0 + octave_offset as f64) }
22177
22178fn just_intonation_ratio(semitones: i32) -> f64 {
22179 match semitones.rem_euclid(12) {
22181 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,
22194 }
22195}
22196
22197fn pythagorean_ratio(semitones: i32) -> f64 {
22198 match semitones.rem_euclid(12) {
22200 0 => 1.0,
22201 1 => 256.0 / 243.0,
22202 2 => 9.0 / 8.0,
22203 3 => 32.0 / 27.0,
22204 4 => 81.0 / 64.0,
22205 5 => 4.0 / 3.0,
22206 6 => 729.0 / 512.0,
22207 7 => 3.0 / 2.0,
22208 8 => 128.0 / 81.0,
22209 9 => 27.0 / 16.0,
22210 10 => 16.0 / 9.0,
22211 11 => 243.0 / 128.0,
22212 _ => 1.0,
22213 }
22214}
22215
22216fn meantone_ratio(semitones: i32) -> f64 {
22217 let fifth = 5.0_f64.powf(0.25); match semitones.rem_euclid(12) {
22220 0 => 1.0,
22221 1 => 8.0 / (fifth.powi(5)),
22222 2 => fifth.powi(2) / 2.0,
22223 3 => 4.0 / (fifth.powi(3)),
22224 4 => fifth.powi(4) / 4.0,
22225 5 => 2.0 / fifth,
22226 6 => fifth.powi(6) / 8.0,
22227 7 => fifth,
22228 8 => 8.0 / (fifth.powi(4)),
22229 9 => fifth.powi(3) / 2.0,
22230 10 => 4.0 / (fifth.powi(2)),
22231 11 => fifth.powi(5) / 4.0,
22232 _ => 1.0,
22233 }
22234}
22235
22236fn shruti_ratio(shruti: i32) -> f64 {
22237 let ratios = [
22239 1.0,
22240 256.0 / 243.0,
22241 16.0 / 15.0,
22242 10.0 / 9.0,
22243 9.0 / 8.0,
22244 32.0 / 27.0,
22245 6.0 / 5.0,
22246 5.0 / 4.0,
22247 81.0 / 64.0,
22248 4.0 / 3.0,
22249 27.0 / 20.0,
22250 45.0 / 32.0,
22251 729.0 / 512.0,
22252 3.0 / 2.0,
22253 128.0 / 81.0,
22254 8.0 / 5.0,
22255 5.0 / 3.0,
22256 27.0 / 16.0,
22257 16.0 / 9.0,
22258 9.0 / 5.0,
22259 15.0 / 8.0,
22260 243.0 / 128.0,
22261 ];
22262 ratios[shruti.rem_euclid(22) as usize]
22263}
22264
22265fn pelog_ratio(degree: i32) -> f64 {
22266 let ratios = [1.0, 1.12, 1.26, 1.5, 1.68, 1.89, 2.12];
22268 ratios[degree.rem_euclid(7) as usize]
22269}
22270
22271fn slendro_ratio(degree: i32) -> f64 {
22272 let ratios = [1.0, 1.148, 1.318, 1.516, 1.741];
22274 ratios[degree.rem_euclid(5) as usize]
22275}
22276
22277fn generate_waveform(args: &[Value], wave_fn: fn(f64) -> f64) -> Result<Value, RuntimeError> {
22278 let freq = match &args[0] {
22279 Value::Float(f) => *f,
22280 Value::Int(i) => *i as f64,
22281 _ => return Err(RuntimeError::new("Waveform requires frequency")),
22282 };
22283 let sample_rate = match &args[1] {
22284 Value::Float(f) => *f as usize,
22285 Value::Int(i) => *i as usize,
22286 _ => return Err(RuntimeError::new("Waveform requires sample rate")),
22287 };
22288 let duration = match &args[2] {
22289 Value::Float(f) => *f,
22290 Value::Int(i) => *i as f64,
22291 _ => return Err(RuntimeError::new("Waveform requires duration")),
22292 };
22293
22294 let num_samples = (sample_rate as f64 * duration) as usize;
22295 let samples: Vec<Value> = (0..num_samples)
22296 .map(|i| {
22297 let t = i as f64 / sample_rate as f64;
22298 let phase = 2.0 * std::f64::consts::PI * freq * t;
22299 Value::Float(wave_fn(phase))
22300 })
22301 .collect();
22302
22303 Ok(Value::Array(Rc::new(RefCell::new(samples))))
22304}
22305
22306fn register_spirituality(interp: &mut Interpreter) {
22328 const TRIGRAMS: [(&str, &str, &str, &str, &str); 8] = [
22334 (
22335 "☰",
22336 "乾",
22337 "Heaven",
22338 "Creative",
22339 "strong, initiating, persisting",
22340 ),
22341 (
22342 "☱",
22343 "兌",
22344 "Lake",
22345 "Joyous",
22346 "pleasure, satisfaction, openness",
22347 ),
22348 (
22349 "☲",
22350 "離",
22351 "Fire",
22352 "Clinging",
22353 "clarity, awareness, dependence",
22354 ),
22355 (
22356 "☳",
22357 "震",
22358 "Thunder",
22359 "Arousing",
22360 "movement, initiative, action",
22361 ),
22362 (
22363 "☴",
22364 "巽",
22365 "Wind",
22366 "Gentle",
22367 "penetrating, following, flexible",
22368 ),
22369 ("☵", "坎", "Water", "Abysmal", "danger, flowing, depth"),
22370 (
22371 "☶",
22372 "艮",
22373 "Mountain",
22374 "Keeping Still",
22375 "stopping, resting, meditation",
22376 ),
22377 (
22378 "☷",
22379 "坤",
22380 "Earth",
22381 "Receptive",
22382 "yielding, nurturing, devoted",
22383 ),
22384 ];
22385
22386 define(interp, "trigram", Some(1), |_, args| {
22388 let input = match &args[0] {
22389 Value::Int(n) => (*n as usize).min(7),
22390 Value::String(s) => match s.as_str() {
22391 "☰" | "heaven" | "qian" | "乾" => 0,
22392 "☱" | "lake" | "dui" | "兌" => 1,
22393 "☲" | "fire" | "li" | "離" => 2,
22394 "☳" | "thunder" | "zhen" | "震" => 3,
22395 "☴" | "wind" | "xun" | "巽" => 4,
22396 "☵" | "water" | "kan" | "坎" => 5,
22397 "☶" | "mountain" | "gen" | "艮" => 6,
22398 "☷" | "earth" | "kun" | "坤" => 7,
22399 _ => return Err(RuntimeError::new(format!("Unknown trigram: {}", s))),
22400 },
22401 _ => return Err(RuntimeError::new("trigram() requires number or name")),
22402 };
22403
22404 let (symbol, chinese, english, name, meaning) = TRIGRAMS[input];
22405
22406 let mut result = std::collections::HashMap::new();
22407 result.insert("number".to_string(), Value::Int(input as i64));
22408 result.insert(
22409 "symbol".to_string(),
22410 Value::String(Rc::new(symbol.to_string())),
22411 );
22412 result.insert(
22413 "chinese".to_string(),
22414 Value::String(Rc::new(chinese.to_string())),
22415 );
22416 result.insert(
22417 "english".to_string(),
22418 Value::String(Rc::new(english.to_string())),
22419 );
22420 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
22421 result.insert(
22422 "meaning".to_string(),
22423 Value::String(Rc::new(meaning.to_string())),
22424 );
22425
22426 let binary = match input {
22428 0 => "111", 1 => "110", 2 => "101", 3 => "100", 4 => "011", 5 => "010", 6 => "001", 7 => "000", _ => "000",
22437 };
22438 result.insert(
22439 "binary".to_string(),
22440 Value::String(Rc::new(binary.to_string())),
22441 );
22442
22443 Ok(Value::Map(Rc::new(RefCell::new(result))))
22444 });
22445
22446 define(interp, "hexagram", Some(1), |_, args| {
22448 let num = match &args[0] {
22449 Value::Int(n) => ((*n - 1) as usize).min(63),
22450 _ => return Err(RuntimeError::new("hexagram() requires number 1-64")),
22451 };
22452
22453 let hex = &HEXAGRAMS[num];
22454
22455 let mut result = std::collections::HashMap::new();
22456 result.insert("number".to_string(), Value::Int((num + 1) as i64));
22457 result.insert(
22458 "chinese".to_string(),
22459 Value::String(Rc::new(hex.0.to_string())),
22460 );
22461 result.insert(
22462 "pinyin".to_string(),
22463 Value::String(Rc::new(hex.1.to_string())),
22464 );
22465 result.insert(
22466 "english".to_string(),
22467 Value::String(Rc::new(hex.2.to_string())),
22468 );
22469 result.insert(
22470 "judgment".to_string(),
22471 Value::String(Rc::new(hex.3.to_string())),
22472 );
22473 result.insert(
22474 "upper_trigram".to_string(),
22475 Value::String(Rc::new(hex.4.to_string())),
22476 );
22477 result.insert(
22478 "lower_trigram".to_string(),
22479 Value::String(Rc::new(hex.5.to_string())),
22480 );
22481
22482 Ok(Value::Map(Rc::new(RefCell::new(result))))
22483 });
22484
22485 define(interp, "cast_iching", Some(0), |_, _| {
22487 let mut rng = rand::thread_rng();
22488
22489 let mut lines = Vec::new();
22492 let mut hexagram_num = 0u8;
22493 let mut changing_lines = Vec::new();
22494
22495 for i in 0..6 {
22496 let r: f64 = rng.gen();
22498 let line = if r < 0.0625 {
22499 6
22500 }
22501 else if r < 0.3125 {
22503 7
22504 }
22505 else if r < 0.5625 {
22507 8
22508 }
22509 else {
22511 9
22512 }; let is_yang = line == 7 || line == 9;
22515 if is_yang {
22516 hexagram_num |= 1 << i;
22517 }
22518
22519 if line == 6 || line == 9 {
22520 changing_lines.push(i + 1);
22521 }
22522
22523 lines.push(Value::Int(line));
22524 }
22525
22526 let king_wen_num = binary_to_king_wen(hexagram_num) + 1;
22528 let hex = &HEXAGRAMS[(king_wen_num - 1) as usize];
22529
22530 let mut result = std::collections::HashMap::new();
22531 result.insert("hexagram".to_string(), Value::Int(king_wen_num as i64));
22532 result.insert(
22533 "chinese".to_string(),
22534 Value::String(Rc::new(hex.0.to_string())),
22535 );
22536 result.insert(
22537 "english".to_string(),
22538 Value::String(Rc::new(hex.2.to_string())),
22539 );
22540 result.insert(
22541 "judgment".to_string(),
22542 Value::String(Rc::new(hex.3.to_string())),
22543 );
22544 result.insert(
22545 "lines".to_string(),
22546 Value::Array(Rc::new(RefCell::new(lines))),
22547 );
22548
22549 let changing: Vec<Value> = changing_lines.iter().map(|&n| Value::Int(n)).collect();
22550 result.insert(
22551 "changing_lines".to_string(),
22552 Value::Array(Rc::new(RefCell::new(changing))),
22553 );
22554
22555 if !changing_lines.is_empty() {
22557 let mut result_hex = hexagram_num;
22558 for &line in &changing_lines {
22559 result_hex ^= 1 << (line - 1); }
22561 let result_king_wen = binary_to_king_wen(result_hex) + 1;
22562 result.insert(
22563 "transforms_to".to_string(),
22564 Value::Int(result_king_wen as i64),
22565 );
22566 }
22567
22568 Ok(Value::Map(Rc::new(RefCell::new(result))))
22569 });
22570
22571 define(interp, "phi", Some(0), |_, _| {
22577 Ok(Value::Float((1.0 + 5.0_f64.sqrt()) / 2.0))
22578 });
22579
22580 define(interp, "sacred_ratio", Some(1), |_, args| {
22582 let name = match &args[0] {
22583 Value::String(s) => s.to_lowercase(),
22584 _ => return Err(RuntimeError::new("sacred_ratio() requires string")),
22585 };
22586
22587 let (value, symbol, meaning) = match name.as_str() {
22588 "phi" | "φ" | "golden" => (
22589 (1.0 + 5.0_f64.sqrt()) / 2.0,
22590 "φ",
22591 "Golden Ratio - divine proportion found in nature, art, architecture",
22592 ),
22593 "phi_conjugate" | "1/phi" => (
22594 2.0 / (1.0 + 5.0_f64.sqrt()),
22595 "1/φ",
22596 "Golden Ratio conjugate - φ - 1 = 1/φ",
22597 ),
22598 "phi_squared" | "phi2" => (
22599 ((1.0 + 5.0_f64.sqrt()) / 2.0).powi(2),
22600 "φ²",
22601 "Golden Ratio squared - φ + 1 = φ²",
22602 ),
22603 "sqrt_phi" => (
22604 ((1.0 + 5.0_f64.sqrt()) / 2.0).sqrt(),
22605 "√φ",
22606 "Square root of Golden Ratio",
22607 ),
22608 "pi" | "π" => (
22609 std::f64::consts::PI,
22610 "π",
22611 "Circle constant - circumference/diameter, transcendental",
22612 ),
22613 "tau" | "τ" => (
22614 std::f64::consts::TAU,
22615 "τ",
22616 "Full circle constant - 2π, one complete revolution",
22617 ),
22618 "e" | "euler" => (
22619 std::f64::consts::E,
22620 "e",
22621 "Euler's number - natural growth, compound interest",
22622 ),
22623 "sqrt2" | "√2" | "pythagoras" => (
22624 std::f64::consts::SQRT_2,
22625 "√2",
22626 "Pythagorean constant - diagonal of unit square",
22627 ),
22628 "sqrt3" | "√3" | "vesica" => (
22629 3.0_f64.sqrt(),
22630 "√3",
22631 "Vesica Piscis ratio - sacred geometry foundation",
22632 ),
22633 "sqrt5" | "√5" => (
22634 5.0_f64.sqrt(),
22635 "√5",
22636 "Related to Golden Ratio: φ = (1 + √5) / 2",
22637 ),
22638 "silver" | "δs" => (
22639 1.0 + 2.0_f64.sqrt(),
22640 "δs",
22641 "Silver Ratio - related to octagon",
22642 ),
22643 "plastic" | "ρ" => (
22644 1.324717957244746,
22645 "ρ",
22646 "Plastic Number - smallest Pisot number",
22647 ),
22648 "feigenbaum" | "δ" => (
22649 4.669201609102990,
22650 "δ",
22651 "Feigenbaum constant - chaos theory, period doubling",
22652 ),
22653 _ => return Err(RuntimeError::new(format!("Unknown sacred ratio: {}", name))),
22654 };
22655
22656 let mut result = std::collections::HashMap::new();
22657 result.insert("value".to_string(), Value::Float(value));
22658 result.insert(
22659 "symbol".to_string(),
22660 Value::String(Rc::new(symbol.to_string())),
22661 );
22662 result.insert(
22663 "meaning".to_string(),
22664 Value::String(Rc::new(meaning.to_string())),
22665 );
22666
22667 Ok(Value::Map(Rc::new(RefCell::new(result))))
22668 });
22669
22670 define(interp, "fibonacci", Some(1), |_, args| {
22672 let count = match &args[0] {
22673 Value::Int(n) => *n as usize,
22674 _ => return Err(RuntimeError::new("fibonacci() requires count")),
22675 };
22676
22677 let mut seq = Vec::with_capacity(count);
22678 let (mut a, mut b) = (0i64, 1i64);
22679
22680 for _ in 0..count {
22681 seq.push(Value::Int(a));
22682 let next = a.saturating_add(b);
22683 a = b;
22684 b = next;
22685 }
22686
22687 Ok(Value::Array(Rc::new(RefCell::new(seq))))
22688 });
22689
22690 define(interp, "is_fibonacci", Some(1), |_, args| {
22692 let n = match &args[0] {
22693 Value::Int(n) => *n,
22694 _ => return Err(RuntimeError::new("is_fibonacci() requires integer")),
22695 };
22696
22697 fn is_perfect_square(n: i64) -> bool {
22699 if n < 0 {
22700 return false;
22701 }
22702 let root = (n as f64).sqrt() as i64;
22703 root * root == n
22704 }
22705
22706 let n_sq = n.saturating_mul(n);
22707 let test1 = 5i64.saturating_mul(n_sq).saturating_add(4);
22708 let test2 = 5i64.saturating_mul(n_sq).saturating_sub(4);
22709
22710 Ok(Value::Bool(
22711 is_perfect_square(test1) || is_perfect_square(test2),
22712 ))
22713 });
22714
22715 define(interp, "platonic_solid", Some(1), |_, args| {
22717 let name = match &args[0] {
22718 Value::String(s) => s.to_lowercase(),
22719 _ => return Err(RuntimeError::new("platonic_solid() requires string")),
22720 };
22721
22722 let (faces, vertices, edges, face_shape, element, meaning) = match name.as_str() {
22723 "tetrahedron" | "fire" => (
22724 4,
22725 4,
22726 6,
22727 "triangle",
22728 "Fire",
22729 "Sharpness, heat, transformation",
22730 ),
22731 "cube" | "hexahedron" | "earth" => (
22732 6,
22733 8,
22734 12,
22735 "square",
22736 "Earth",
22737 "Stability, grounding, material",
22738 ),
22739 "octahedron" | "air" => (
22740 8,
22741 6,
22742 12,
22743 "triangle",
22744 "Air",
22745 "Balance, intellect, communication",
22746 ),
22747 "dodecahedron" | "aether" | "spirit" => (
22748 12,
22749 20,
22750 30,
22751 "pentagon",
22752 "Aether/Spirit",
22753 "The cosmos, divine thought",
22754 ),
22755 "icosahedron" | "water" => (
22756 20,
22757 12,
22758 30,
22759 "triangle",
22760 "Water",
22761 "Flow, emotion, adaptability",
22762 ),
22763 _ => {
22764 return Err(RuntimeError::new(format!(
22765 "Unknown Platonic solid: {}",
22766 name
22767 )))
22768 }
22769 };
22770
22771 let mut result = std::collections::HashMap::new();
22772 result.insert("name".to_string(), Value::String(Rc::new(name)));
22773 result.insert("faces".to_string(), Value::Int(faces));
22774 result.insert("vertices".to_string(), Value::Int(vertices));
22775 result.insert("edges".to_string(), Value::Int(edges));
22776 result.insert(
22777 "face_shape".to_string(),
22778 Value::String(Rc::new(face_shape.to_string())),
22779 );
22780 result.insert(
22781 "element".to_string(),
22782 Value::String(Rc::new(element.to_string())),
22783 );
22784 result.insert(
22785 "meaning".to_string(),
22786 Value::String(Rc::new(meaning.to_string())),
22787 );
22788
22789 result.insert("euler_characteristic".to_string(), Value::Int(2));
22791
22792 Ok(Value::Map(Rc::new(RefCell::new(result))))
22793 });
22794
22795 define(interp, "gematria", Some(2), |_, args| {
22801 let text = match &args[0] {
22802 Value::String(s) => s.to_string(),
22803 _ => return Err(RuntimeError::new("gematria() requires string")),
22804 };
22805
22806 let system = match &args[1] {
22807 Value::String(s) => s.to_lowercase(),
22808 _ => return Err(RuntimeError::new("gematria() requires system name")),
22809 };
22810
22811 let total: i64 = match system.as_str() {
22812 "hebrew" | "kabbalah" => text.chars().map(|c| hebrew_gematria(c)).sum(),
22813 "greek" | "isopsephy" => text.chars().map(|c| greek_isopsephy(c)).sum(),
22814 "arabic" | "abjad" => text.chars().map(|c| arabic_abjad(c)).sum(),
22815 "english" | "simple" => {
22816 text.to_uppercase()
22818 .chars()
22819 .filter_map(|c| {
22820 if c.is_ascii_alphabetic() {
22821 Some((c as i64) - ('A' as i64) + 1)
22822 } else {
22823 None
22824 }
22825 })
22826 .sum()
22827 }
22828 "english_ordinal" => {
22829 text.to_uppercase()
22831 .chars()
22832 .filter_map(|c| {
22833 if c.is_ascii_alphabetic() {
22834 Some((c as i64) - ('A' as i64) + 1)
22835 } else {
22836 None
22837 }
22838 })
22839 .sum()
22840 }
22841 "english_reduction" => {
22842 text.to_uppercase()
22844 .chars()
22845 .filter_map(|c| {
22846 if c.is_ascii_alphabetic() {
22847 let val = ((c as i64) - ('A' as i64)) % 9 + 1;
22848 Some(val)
22849 } else {
22850 None
22851 }
22852 })
22853 .sum()
22854 }
22855 _ => {
22856 return Err(RuntimeError::new(format!(
22857 "Unknown gematria system: {}",
22858 system
22859 )))
22860 }
22861 };
22862
22863 let mut result = std::collections::HashMap::new();
22864 result.insert("text".to_string(), Value::String(Rc::new(text)));
22865 result.insert("system".to_string(), Value::String(Rc::new(system)));
22866 result.insert("value".to_string(), Value::Int(total));
22867
22868 let mut digital_root = total;
22870 while digital_root > 9 {
22871 digital_root = digital_root
22872 .to_string()
22873 .chars()
22874 .filter_map(|c| c.to_digit(10))
22875 .map(|d| d as i64)
22876 .sum();
22877 }
22878 result.insert("digital_root".to_string(), Value::Int(digital_root));
22879
22880 Ok(Value::Map(Rc::new(RefCell::new(result))))
22881 });
22882
22883 define(interp, "gematria_match", Some(2), |_, args| {
22885 let value = match &args[0] {
22886 Value::Int(n) => *n,
22887 _ => return Err(RuntimeError::new("gematria_match() requires integer value")),
22888 };
22889
22890 let system = match &args[1] {
22891 Value::String(s) => s.to_lowercase(),
22892 _ => return Err(RuntimeError::new("gematria_match() requires system name")),
22893 };
22894
22895 let matches = match (value, system.as_str()) {
22897 (26, "hebrew") => vec!["יהוה (YHWH - Tetragrammaton)"],
22898 (18, "hebrew") => vec!["חי (Chai - Life)"],
22899 (86, "hebrew") => vec!["אלהים (Elohim - God)"],
22900 (72, "hebrew") => vec!["חסד (Chesed - Loving-kindness)"],
22901 (93, "english") => vec!["Love", "Will", "Thelema"],
22902 (666, "greek") => vec!["Nero Caesar (in Hebrew letters)"],
22903 (888, "greek") => vec!["Ἰησοῦς (Jesus)"],
22904 _ => vec![],
22905 };
22906
22907 let match_values: Vec<Value> = matches
22908 .iter()
22909 .map(|s| Value::String(Rc::new(s.to_string())))
22910 .collect();
22911
22912 Ok(Value::Array(Rc::new(RefCell::new(match_values))))
22913 });
22914
22915 define(interp, "archetype", Some(1), |_, args| {
22921 let name = match &args[0] {
22922 Value::String(s) => s.to_lowercase(),
22923 _ => return Err(RuntimeError::new("archetype() requires string")),
22924 };
22925
22926 let (description, shadow, gift, challenge) = match name.as_str() {
22927 "self" => (
22929 "The unified conscious and unconscious, the goal of individuation",
22930 "Inflation or deflation of ego",
22931 "Wholeness, integration, meaning",
22932 "Integrating all aspects of psyche",
22933 ),
22934 "shadow" => (
22935 "The unconscious aspect containing repressed weaknesses and instincts",
22936 "Projection onto others, denial",
22937 "Creativity, spontaneity, insight",
22938 "Acknowledging and integrating darkness",
22939 ),
22940 "anima" => (
22941 "The feminine inner personality in a man's unconscious",
22942 "Moodiness, seduction, possession",
22943 "Relatedness, creativity, soul connection",
22944 "Developing emotional intelligence",
22945 ),
22946 "animus" => (
22947 "The masculine inner personality in a woman's unconscious",
22948 "Brutality, reckless action, opinionation",
22949 "Courage, initiative, spiritual depth",
22950 "Developing assertiveness with wisdom",
22951 ),
22952 "persona" => (
22953 "The social mask, the face we present to the world",
22954 "Over-identification, inauthenticity",
22955 "Social adaptation, professional competence",
22956 "Maintaining authenticity within role",
22957 ),
22958
22959 "hero" => (
22961 "The courageous one who overcomes obstacles and achieves great deeds",
22962 "Arrogance, ruthlessness, eternal battle",
22963 "Courage, perseverance, accomplishment",
22964 "Knowing when to fight and when to surrender",
22965 ),
22966 "sage" | "wise_old_man" => (
22967 "The wise figure who offers guidance and insight",
22968 "Dogmatism, disconnection, ivory tower",
22969 "Wisdom, knowledge, truth-seeking",
22970 "Applying wisdom practically",
22971 ),
22972 "magician" | "wizard" => (
22973 "The transformer who makes things happen through understanding laws",
22974 "Manipulation, disconnection from ethics",
22975 "Transformation, vision, manifestation",
22976 "Using power responsibly",
22977 ),
22978 "lover" => (
22979 "The one who pursues connection, beauty, and passion",
22980 "Obsession, jealousy, loss of identity",
22981 "Passion, commitment, appreciation",
22982 "Maintaining boundaries while connecting deeply",
22983 ),
22984 "caregiver" | "mother" => (
22985 "The nurturing one who protects and provides",
22986 "Martyrdom, enabling, smothering",
22987 "Compassion, generosity, nurturing",
22988 "Caring for self while caring for others",
22989 ),
22990 "ruler" | "king" | "queen" => (
22991 "The one who takes responsibility for the realm",
22992 "Tyranny, authoritarianism, being overthrown",
22993 "Order, leadership, prosperity",
22994 "Serving the greater good, not just power",
22995 ),
22996 "creator" | "artist" => (
22997 "The one who brings new things into being",
22998 "Perfectionism, self-indulgence, drama",
22999 "Creativity, imagination, expression",
23000 "Completing projects, accepting imperfection",
23001 ),
23002 "innocent" | "child" => (
23003 "The pure one with faith and optimism",
23004 "Naivety, denial, dependence",
23005 "Faith, optimism, loyalty",
23006 "Growing without becoming cynical",
23007 ),
23008 "explorer" | "seeker" => (
23009 "The one who seeks new experiences and self-discovery",
23010 "Aimless wandering, inability to commit",
23011 "Autonomy, ambition, authenticity",
23012 "Finding what you seek",
23013 ),
23014 "rebel" | "outlaw" => (
23015 "The one who breaks rules and challenges the status quo",
23016 "Crime, self-destruction, alienation",
23017 "Liberation, revolution, radical freedom",
23018 "Channeling rebellion constructively",
23019 ),
23020 "jester" | "fool" | "trickster" => (
23021 "The one who uses humor and playfulness",
23022 "Cruelty, debauchery, irresponsibility",
23023 "Joy, freedom, living in the moment",
23024 "Knowing when to be serious",
23025 ),
23026 "everyman" | "orphan" => (
23027 "The regular person who wants belonging",
23028 "Victim mentality, losing self in group",
23029 "Realism, empathy, connection",
23030 "Standing out when necessary",
23031 ),
23032 _ => return Err(RuntimeError::new(format!("Unknown archetype: {}", name))),
23033 };
23034
23035 let mut result = std::collections::HashMap::new();
23036 result.insert("name".to_string(), Value::String(Rc::new(name)));
23037 result.insert(
23038 "description".to_string(),
23039 Value::String(Rc::new(description.to_string())),
23040 );
23041 result.insert(
23042 "shadow".to_string(),
23043 Value::String(Rc::new(shadow.to_string())),
23044 );
23045 result.insert("gift".to_string(), Value::String(Rc::new(gift.to_string())));
23046 result.insert(
23047 "challenge".to_string(),
23048 Value::String(Rc::new(challenge.to_string())),
23049 );
23050
23051 Ok(Value::Map(Rc::new(RefCell::new(result))))
23052 });
23053
23054 define(interp, "zodiac", Some(1), |_, args| {
23060 let input = match &args[0] {
23061 Value::Int(n) => (*n as usize - 1).min(11),
23062 Value::String(s) => match s.to_lowercase().as_str() {
23063 "aries" | "♈" => 0,
23064 "taurus" | "♉" => 1,
23065 "gemini" | "♊" => 2,
23066 "cancer" | "♋" => 3,
23067 "leo" | "♌" => 4,
23068 "virgo" | "♍" => 5,
23069 "libra" | "♎" => 6,
23070 "scorpio" | "♏" => 7,
23071 "sagittarius" | "♐" => 8,
23072 "capricorn" | "♑" => 9,
23073 "aquarius" | "♒" => 10,
23074 "pisces" | "♓" => 11,
23075 _ => return Err(RuntimeError::new(format!("Unknown sign: {}", s))),
23076 },
23077 _ => return Err(RuntimeError::new("zodiac() requires number or name")),
23078 };
23079
23080 let signs = [
23081 (
23082 "♈",
23083 "Aries",
23084 "Fire",
23085 "Cardinal",
23086 "Mars",
23087 "I Am",
23088 "Mar 21 - Apr 19",
23089 ),
23090 (
23091 "♉",
23092 "Taurus",
23093 "Earth",
23094 "Fixed",
23095 "Venus",
23096 "I Have",
23097 "Apr 20 - May 20",
23098 ),
23099 (
23100 "♊",
23101 "Gemini",
23102 "Air",
23103 "Mutable",
23104 "Mercury",
23105 "I Think",
23106 "May 21 - Jun 20",
23107 ),
23108 (
23109 "♋",
23110 "Cancer",
23111 "Water",
23112 "Cardinal",
23113 "Moon",
23114 "I Feel",
23115 "Jun 21 - Jul 22",
23116 ),
23117 (
23118 "♌",
23119 "Leo",
23120 "Fire",
23121 "Fixed",
23122 "Sun",
23123 "I Will",
23124 "Jul 23 - Aug 22",
23125 ),
23126 (
23127 "♍",
23128 "Virgo",
23129 "Earth",
23130 "Mutable",
23131 "Mercury",
23132 "I Analyze",
23133 "Aug 23 - Sep 22",
23134 ),
23135 (
23136 "♎",
23137 "Libra",
23138 "Air",
23139 "Cardinal",
23140 "Venus",
23141 "I Balance",
23142 "Sep 23 - Oct 22",
23143 ),
23144 (
23145 "♏",
23146 "Scorpio",
23147 "Water",
23148 "Fixed",
23149 "Pluto/Mars",
23150 "I Transform",
23151 "Oct 23 - Nov 21",
23152 ),
23153 (
23154 "♐",
23155 "Sagittarius",
23156 "Fire",
23157 "Mutable",
23158 "Jupiter",
23159 "I Seek",
23160 "Nov 22 - Dec 21",
23161 ),
23162 (
23163 "♑",
23164 "Capricorn",
23165 "Earth",
23166 "Cardinal",
23167 "Saturn",
23168 "I Use",
23169 "Dec 22 - Jan 19",
23170 ),
23171 (
23172 "♒",
23173 "Aquarius",
23174 "Air",
23175 "Fixed",
23176 "Uranus/Saturn",
23177 "I Know",
23178 "Jan 20 - Feb 18",
23179 ),
23180 (
23181 "♓",
23182 "Pisces",
23183 "Water",
23184 "Mutable",
23185 "Neptune/Jupiter",
23186 "I Believe",
23187 "Feb 19 - Mar 20",
23188 ),
23189 ];
23190
23191 let (symbol, name, element, modality, ruler, motto, dates) = signs[input];
23192
23193 let mut result = std::collections::HashMap::new();
23194 result.insert("number".to_string(), Value::Int((input + 1) as i64));
23195 result.insert(
23196 "symbol".to_string(),
23197 Value::String(Rc::new(symbol.to_string())),
23198 );
23199 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23200 result.insert(
23201 "element".to_string(),
23202 Value::String(Rc::new(element.to_string())),
23203 );
23204 result.insert(
23205 "modality".to_string(),
23206 Value::String(Rc::new(modality.to_string())),
23207 );
23208 result.insert(
23209 "ruler".to_string(),
23210 Value::String(Rc::new(ruler.to_string())),
23211 );
23212 result.insert(
23213 "motto".to_string(),
23214 Value::String(Rc::new(motto.to_string())),
23215 );
23216 result.insert(
23217 "dates".to_string(),
23218 Value::String(Rc::new(dates.to_string())),
23219 );
23220
23221 Ok(Value::Map(Rc::new(RefCell::new(result))))
23222 });
23223
23224 define(interp, "tarot_major", Some(1), |_, args| {
23226 let num = match &args[0] {
23227 Value::Int(n) => (*n as usize).min(21),
23228 Value::String(s) => match s.to_lowercase().as_str() {
23229 "fool" => 0,
23230 "magician" => 1,
23231 "high_priestess" | "priestess" => 2,
23232 "empress" => 3,
23233 "emperor" => 4,
23234 "hierophant" | "pope" => 5,
23235 "lovers" => 6,
23236 "chariot" => 7,
23237 "strength" => 8,
23238 "hermit" => 9,
23239 "wheel" | "fortune" => 10,
23240 "justice" => 11,
23241 "hanged_man" | "hanged" => 12,
23242 "death" => 13,
23243 "temperance" => 14,
23244 "devil" => 15,
23245 "tower" => 16,
23246 "star" => 17,
23247 "moon" => 18,
23248 "sun" => 19,
23249 "judgement" | "judgment" => 20,
23250 "world" => 21,
23251 _ => return Err(RuntimeError::new(format!("Unknown card: {}", s))),
23252 },
23253 _ => return Err(RuntimeError::new("tarot_major() requires number or name")),
23254 };
23255
23256 let cards = [
23257 (
23258 "The Fool",
23259 "New beginnings, innocence, spontaneity",
23260 "Naivety, recklessness, risk-taking",
23261 ),
23262 (
23263 "The Magician",
23264 "Willpower, creation, manifestation",
23265 "Manipulation, trickery, unused talent",
23266 ),
23267 (
23268 "The High Priestess",
23269 "Intuition, mystery, inner knowledge",
23270 "Secrets, withdrawal, silence",
23271 ),
23272 (
23273 "The Empress",
23274 "Abundance, nurturing, fertility",
23275 "Dependence, smothering, emptiness",
23276 ),
23277 (
23278 "The Emperor",
23279 "Authority, structure, control",
23280 "Tyranny, rigidity, coldness",
23281 ),
23282 (
23283 "The Hierophant",
23284 "Tradition, conformity, spirituality",
23285 "Dogma, restriction, challenging status quo",
23286 ),
23287 (
23288 "The Lovers",
23289 "Love, harmony, relationships, choices",
23290 "Disharmony, imbalance, misalignment",
23291 ),
23292 (
23293 "The Chariot",
23294 "Direction, willpower, victory",
23295 "Aggression, lack of direction, obstacles",
23296 ),
23297 (
23298 "Strength",
23299 "Courage, patience, inner power",
23300 "Self-doubt, weakness, insecurity",
23301 ),
23302 (
23303 "The Hermit",
23304 "Contemplation, search for truth, inner guidance",
23305 "Isolation, loneliness, withdrawal",
23306 ),
23307 (
23308 "Wheel of Fortune",
23309 "Change, cycles, fate, destiny",
23310 "Resistance to change, bad luck, setbacks",
23311 ),
23312 (
23313 "Justice",
23314 "Truth, fairness, law, cause and effect",
23315 "Unfairness, dishonesty, lack of accountability",
23316 ),
23317 (
23318 "The Hanged Man",
23319 "Surrender, letting go, new perspective",
23320 "Stalling, resistance, indecision",
23321 ),
23322 (
23323 "Death",
23324 "Endings, transformation, transition",
23325 "Fear of change, stagnation, decay",
23326 ),
23327 (
23328 "Temperance",
23329 "Balance, moderation, patience",
23330 "Imbalance, excess, lack of purpose",
23331 ),
23332 (
23333 "The Devil",
23334 "Bondage, materialism, shadow self",
23335 "Freedom, release, exploring dark side",
23336 ),
23337 (
23338 "The Tower",
23339 "Sudden change, upheaval, revelation",
23340 "Disaster averted, fear of change, prolonged pain",
23341 ),
23342 (
23343 "The Star",
23344 "Hope, faith, renewal, inspiration",
23345 "Despair, disconnection, lack of faith",
23346 ),
23347 (
23348 "The Moon",
23349 "Illusion, intuition, the unconscious",
23350 "Fear, confusion, misinterpretation",
23351 ),
23352 (
23353 "The Sun",
23354 "Joy, success, vitality, positivity",
23355 "Negativity, depression, sadness",
23356 ),
23357 (
23358 "Judgement",
23359 "Rebirth, inner calling, absolution",
23360 "Self-doubt, refusal of self-examination",
23361 ),
23362 (
23363 "The World",
23364 "Completion, accomplishment, wholeness",
23365 "Incompletion, lack of closure, emptiness",
23366 ),
23367 ];
23368
23369 let (name, upright, reversed) = cards[num];
23370
23371 let mut result = std::collections::HashMap::new();
23372 result.insert("number".to_string(), Value::Int(num as i64));
23373 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23374 result.insert(
23375 "upright".to_string(),
23376 Value::String(Rc::new(upright.to_string())),
23377 );
23378 result.insert(
23379 "reversed".to_string(),
23380 Value::String(Rc::new(reversed.to_string())),
23381 );
23382
23383 Ok(Value::Map(Rc::new(RefCell::new(result))))
23384 });
23385
23386 define(interp, "draw_tarot", Some(0), |_, _| {
23388 let mut rng = rand::thread_rng();
23389 let card: usize = rng.gen_range(0..22);
23390 let reversed: bool = rng.gen();
23391
23392 let cards = [
23393 "The Fool",
23394 "The Magician",
23395 "The High Priestess",
23396 "The Empress",
23397 "The Emperor",
23398 "The Hierophant",
23399 "The Lovers",
23400 "The Chariot",
23401 "Strength",
23402 "The Hermit",
23403 "Wheel of Fortune",
23404 "Justice",
23405 "The Hanged Man",
23406 "Death",
23407 "Temperance",
23408 "The Devil",
23409 "The Tower",
23410 "The Star",
23411 "The Moon",
23412 "The Sun",
23413 "Judgement",
23414 "The World",
23415 ];
23416
23417 let mut result = std::collections::HashMap::new();
23418 result.insert("number".to_string(), Value::Int(card as i64));
23419 result.insert(
23420 "name".to_string(),
23421 Value::String(Rc::new(cards[card].to_string())),
23422 );
23423 result.insert("reversed".to_string(), Value::Bool(reversed));
23424 result.insert(
23425 "orientation".to_string(),
23426 Value::String(Rc::new(
23427 if reversed { "reversed" } else { "upright" }.to_string(),
23428 )),
23429 );
23430
23431 Ok(Value::Map(Rc::new(RefCell::new(result))))
23432 });
23433
23434 define(interp, "synchronicity_score", Some(2), |_, args| {
23440 let a = match &args[0] {
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 b = match &args[1] {
23452 Value::String(s) => s.to_string(),
23453 Value::Int(n) => n.to_string(),
23454 _ => {
23455 return Err(RuntimeError::new(
23456 "synchronicity_score() requires string or int",
23457 ))
23458 }
23459 };
23460
23461 let val_a: i64 = a
23463 .to_uppercase()
23464 .chars()
23465 .filter_map(|c| {
23466 if c.is_ascii_alphabetic() {
23467 Some((c as i64) - ('A' as i64) + 1)
23468 } else if c.is_ascii_digit() {
23469 c.to_digit(10).map(|d| d as i64)
23470 } else {
23471 None
23472 }
23473 })
23474 .sum();
23475
23476 let val_b: i64 = b
23477 .to_uppercase()
23478 .chars()
23479 .filter_map(|c| {
23480 if c.is_ascii_alphabetic() {
23481 Some((c as i64) - ('A' as i64) + 1)
23482 } else if c.is_ascii_digit() {
23483 c.to_digit(10).map(|d| d as i64)
23484 } else {
23485 None
23486 }
23487 })
23488 .sum();
23489
23490 let mut factors = Vec::new();
23492
23493 if val_a == val_b {
23495 factors.push("identical_gematria".to_string());
23496 }
23497
23498 if val_a > 0 && val_b > 0 && (val_a % val_b == 0 || val_b % val_a == 0) {
23500 factors.push("divisibility".to_string());
23501 }
23502
23503 let fib_set: std::collections::HashSet<i64> =
23505 [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
23506 .iter()
23507 .cloned()
23508 .collect();
23509 if fib_set.contains(&val_a) && fib_set.contains(&val_b) {
23510 factors.push("both_fibonacci".to_string());
23511 }
23512
23513 fn digital_root(mut n: i64) -> i64 {
23515 while n > 9 {
23516 n = n
23517 .to_string()
23518 .chars()
23519 .filter_map(|c| c.to_digit(10))
23520 .map(|d| d as i64)
23521 .sum();
23522 }
23523 n
23524 }
23525 if digital_root(val_a) == digital_root(val_b) {
23526 factors.push("same_digital_root".to_string());
23527 }
23528
23529 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
23531 let ratio = if val_a > 0 && val_b > 0 {
23532 (val_a as f64 / val_b as f64).max(val_b as f64 / val_a as f64)
23533 } else {
23534 0.0
23535 };
23536 if (ratio - phi).abs() < 0.02 || (ratio - 1.0 / phi).abs() < 0.02 {
23537 factors.push("golden_ratio".to_string());
23538 }
23539
23540 let score = (factors.len() as f64 / 5.0).min(1.0);
23541
23542 let mut result = std::collections::HashMap::new();
23543 result.insert("score".to_string(), Value::Float(score));
23544 result.insert("value_a".to_string(), Value::Int(val_a));
23545 result.insert("value_b".to_string(), Value::Int(val_b));
23546 let factor_values: Vec<Value> = factors
23547 .iter()
23548 .map(|s| Value::String(Rc::new(s.clone())))
23549 .collect();
23550 result.insert(
23551 "factors".to_string(),
23552 Value::Array(Rc::new(RefCell::new(factor_values))),
23553 );
23554
23555 Ok(Value::Map(Rc::new(RefCell::new(result))))
23556 });
23557
23558 define(interp, "spirituality_info", Some(0), |_, _| {
23560 let mut info = std::collections::HashMap::new();
23561
23562 info.insert(
23563 "i_ching".to_string(),
23564 Value::String(Rc::new(
23565 "trigram(), hexagram(), cast_iching() - 64 hexagrams of change".to_string(),
23566 )),
23567 );
23568 info.insert(
23569 "sacred_geometry".to_string(),
23570 Value::String(Rc::new(
23571 "phi(), sacred_ratio(), fibonacci(), platonic_solid()".to_string(),
23572 )),
23573 );
23574 info.insert(
23575 "gematria".to_string(),
23576 Value::String(Rc::new(
23577 "Hebrew, Greek, Arabic, English letter-number systems".to_string(),
23578 )),
23579 );
23580 info.insert(
23581 "archetypes".to_string(),
23582 Value::String(Rc::new(
23583 "17 Jungian archetypes with shadow and gift".to_string(),
23584 )),
23585 );
23586 info.insert(
23587 "astrology".to_string(),
23588 Value::String(Rc::new(
23589 "zodiac() - 12 signs with elements and modalities".to_string(),
23590 )),
23591 );
23592 info.insert(
23593 "tarot".to_string(),
23594 Value::String(Rc::new(
23595 "tarot_major(), draw_tarot() - 22 Major Arcana".to_string(),
23596 )),
23597 );
23598
23599 Ok(Value::Map(Rc::new(RefCell::new(info))))
23600 });
23601}
23602
23603const HEXAGRAMS: [(&str, &str, &str, &str, &str, &str); 64] = [
23605 (
23606 "乾",
23607 "Qián",
23608 "The Creative",
23609 "Sublime success through perseverance",
23610 "Heaven",
23611 "Heaven",
23612 ),
23613 (
23614 "坤",
23615 "Kūn",
23616 "The Receptive",
23617 "Devoted success through the mare's perseverance",
23618 "Earth",
23619 "Earth",
23620 ),
23621 (
23622 "屯",
23623 "Zhūn",
23624 "Difficulty at the Beginning",
23625 "Persevere, seek helpers, don't act alone",
23626 "Water",
23627 "Thunder",
23628 ),
23629 (
23630 "蒙",
23631 "Méng",
23632 "Youthful Folly",
23633 "Success through education and guidance",
23634 "Mountain",
23635 "Water",
23636 ),
23637 (
23638 "需",
23639 "Xū",
23640 "Waiting",
23641 "Sincerity brings success; cross the great water",
23642 "Water",
23643 "Heaven",
23644 ),
23645 (
23646 "訟",
23647 "Sòng",
23648 "Conflict",
23649 "Seek counsel; don't cross the great water",
23650 "Heaven",
23651 "Water",
23652 ),
23653 (
23654 "師",
23655 "Shī",
23656 "The Army",
23657 "Perseverance and an experienced leader bring success",
23658 "Earth",
23659 "Water",
23660 ),
23661 (
23662 "比",
23663 "Bǐ",
23664 "Holding Together",
23665 "Through perseverance, those who hesitate should reflect",
23666 "Water",
23667 "Earth",
23668 ),
23669 (
23670 "小畜",
23671 "Xiǎo Chù",
23672 "Small Taming",
23673 "Success; dense clouds but no rain",
23674 "Wind",
23675 "Heaven",
23676 ),
23677 (
23678 "履",
23679 "Lǚ",
23680 "Treading",
23681 "Tread on the tiger's tail carefully; success",
23682 "Heaven",
23683 "Lake",
23684 ),
23685 (
23686 "泰",
23687 "Tài",
23688 "Peace",
23689 "The small departs, the great approaches; success",
23690 "Earth",
23691 "Heaven",
23692 ),
23693 (
23694 "否",
23695 "Pǐ",
23696 "Standstill",
23697 "The great departs, the small approaches; persevere",
23698 "Heaven",
23699 "Earth",
23700 ),
23701 (
23702 "同人",
23703 "Tóng Rén",
23704 "Fellowship",
23705 "Success in the open; cross the great water",
23706 "Heaven",
23707 "Fire",
23708 ),
23709 (
23710 "大有",
23711 "Dà Yǒu",
23712 "Great Possession",
23713 "Supreme success",
23714 "Fire",
23715 "Heaven",
23716 ),
23717 (
23718 "謙",
23719 "Qiān",
23720 "Modesty",
23721 "Success; the superior person carries things through",
23722 "Earth",
23723 "Mountain",
23724 ),
23725 (
23726 "豫",
23727 "Yù",
23728 "Enthusiasm",
23729 "Appoint helpers and set armies marching",
23730 "Thunder",
23731 "Earth",
23732 ),
23733 (
23734 "隨",
23735 "Suí",
23736 "Following",
23737 "Supreme success through perseverance",
23738 "Lake",
23739 "Thunder",
23740 ),
23741 (
23742 "蠱",
23743 "Gǔ",
23744 "Work on the Decayed",
23745 "Success; cross the great water; three days before and after",
23746 "Mountain",
23747 "Wind",
23748 ),
23749 (
23750 "臨",
23751 "Lín",
23752 "Approach",
23753 "Great success through perseverance; misfortune in eighth month",
23754 "Earth",
23755 "Lake",
23756 ),
23757 (
23758 "觀",
23759 "Guān",
23760 "Contemplation",
23761 "Ablution, but not yet sacrifice; confidence inspires",
23762 "Wind",
23763 "Earth",
23764 ),
23765 (
23766 "噬嗑",
23767 "Shì Kè",
23768 "Biting Through",
23769 "Success; favorable for legal matters",
23770 "Fire",
23771 "Thunder",
23772 ),
23773 (
23774 "賁",
23775 "Bì",
23776 "Grace",
23777 "Success in small matters",
23778 "Mountain",
23779 "Fire",
23780 ),
23781 (
23782 "剝",
23783 "Bō",
23784 "Splitting Apart",
23785 "Unfavorable to go anywhere",
23786 "Mountain",
23787 "Earth",
23788 ),
23789 (
23790 "復",
23791 "Fù",
23792 "Return",
23793 "Success; going out and coming in without error",
23794 "Earth",
23795 "Thunder",
23796 ),
23797 (
23798 "無妄",
23799 "Wú Wàng",
23800 "Innocence",
23801 "Supreme success through perseverance",
23802 "Heaven",
23803 "Thunder",
23804 ),
23805 (
23806 "大畜",
23807 "Dà Chù",
23808 "Great Taming",
23809 "Perseverance; eat away from home",
23810 "Mountain",
23811 "Heaven",
23812 ),
23813 (
23814 "頤",
23815 "Yí",
23816 "Nourishment",
23817 "Perseverance; watch what you nurture",
23818 "Mountain",
23819 "Thunder",
23820 ),
23821 (
23822 "大過",
23823 "Dà Guò",
23824 "Great Exceeding",
23825 "The ridgepole sags; favorable to have somewhere to go",
23826 "Lake",
23827 "Wind",
23828 ),
23829 (
23830 "坎",
23831 "Kǎn",
23832 "The Abysmal",
23833 "Sincerity brings success of the heart",
23834 "Water",
23835 "Water",
23836 ),
23837 (
23838 "離",
23839 "Lí",
23840 "The Clinging",
23841 "Perseverance; success; care for the cow",
23842 "Fire",
23843 "Fire",
23844 ),
23845 (
23846 "咸",
23847 "Xián",
23848 "Influence",
23849 "Success; perseverance; taking a maiden brings fortune",
23850 "Lake",
23851 "Mountain",
23852 ),
23853 (
23854 "恆",
23855 "Héng",
23856 "Duration",
23857 "Success without blame; perseverance; favorable to have somewhere to go",
23858 "Thunder",
23859 "Wind",
23860 ),
23861 (
23862 "遯",
23863 "Dùn",
23864 "Retreat",
23865 "Success; small perseverance",
23866 "Heaven",
23867 "Mountain",
23868 ),
23869 (
23870 "大壯",
23871 "Dà Zhuàng",
23872 "Great Power",
23873 "Perseverance",
23874 "Thunder",
23875 "Heaven",
23876 ),
23877 (
23878 "晉",
23879 "Jìn",
23880 "Progress",
23881 "The powerful prince is honored with horses",
23882 "Fire",
23883 "Earth",
23884 ),
23885 (
23886 "明夷",
23887 "Míng Yí",
23888 "Darkening of the Light",
23889 "Perseverance in adversity",
23890 "Earth",
23891 "Fire",
23892 ),
23893 (
23894 "家人",
23895 "Jiā Rén",
23896 "The Family",
23897 "Perseverance of the woman",
23898 "Wind",
23899 "Fire",
23900 ),
23901 (
23902 "睽",
23903 "Kuí",
23904 "Opposition",
23905 "Good fortune in small matters",
23906 "Fire",
23907 "Lake",
23908 ),
23909 (
23910 "蹇",
23911 "Jiǎn",
23912 "Obstruction",
23913 "Southwest favorable; northeast unfavorable; see the great person",
23914 "Water",
23915 "Mountain",
23916 ),
23917 (
23918 "解",
23919 "Xiè",
23920 "Deliverance",
23921 "Southwest favorable; return brings fortune; haste brings fortune",
23922 "Thunder",
23923 "Water",
23924 ),
23925 (
23926 "損",
23927 "Sǔn",
23928 "Decrease",
23929 "Sincerity; supreme fortune; persistence; favorable to undertake",
23930 "Mountain",
23931 "Lake",
23932 ),
23933 (
23934 "益",
23935 "Yì",
23936 "Increase",
23937 "Favorable to undertake and cross the great water",
23938 "Wind",
23939 "Thunder",
23940 ),
23941 (
23942 "夬",
23943 "Guài",
23944 "Breakthrough",
23945 "Proclaim at the king's court; sincerity in danger",
23946 "Lake",
23947 "Heaven",
23948 ),
23949 (
23950 "姤",
23951 "Gòu",
23952 "Coming to Meet",
23953 "The maiden is powerful; don't marry such a maiden",
23954 "Heaven",
23955 "Wind",
23956 ),
23957 (
23958 "萃",
23959 "Cuì",
23960 "Gathering",
23961 "Success; the king approaches his temple; see the great person",
23962 "Lake",
23963 "Earth",
23964 ),
23965 (
23966 "升",
23967 "Shēng",
23968 "Pushing Upward",
23969 "Supreme success; see the great person; don't worry",
23970 "Earth",
23971 "Wind",
23972 ),
23973 (
23974 "困",
23975 "Kùn",
23976 "Oppression",
23977 "Success; perseverance of the great person; no blame",
23978 "Lake",
23979 "Water",
23980 ),
23981 (
23982 "井",
23983 "Jǐng",
23984 "The Well",
23985 "The town may change but not the well",
23986 "Water",
23987 "Wind",
23988 ),
23989 (
23990 "革",
23991 "Gé",
23992 "Revolution",
23993 "On your own day you are believed; great success",
23994 "Lake",
23995 "Fire",
23996 ),
23997 (
23998 "鼎",
23999 "Dǐng",
24000 "The Cauldron",
24001 "Supreme good fortune; success",
24002 "Fire",
24003 "Wind",
24004 ),
24005 (
24006 "震",
24007 "Zhèn",
24008 "The Arousing",
24009 "Success; thunder comes with fright; laughing and talking after",
24010 "Thunder",
24011 "Thunder",
24012 ),
24013 (
24014 "艮",
24015 "Gèn",
24016 "Keeping Still",
24017 "Keep your back still; go into the courtyard without seeing anyone",
24018 "Mountain",
24019 "Mountain",
24020 ),
24021 (
24022 "漸",
24023 "Jiàn",
24024 "Development",
24025 "The maiden is given in marriage; good fortune; perseverance",
24026 "Wind",
24027 "Mountain",
24028 ),
24029 (
24030 "歸妹",
24031 "Guī Mèi",
24032 "The Marrying Maiden",
24033 "Undertakings bring misfortune",
24034 "Thunder",
24035 "Lake",
24036 ),
24037 (
24038 "豐",
24039 "Fēng",
24040 "Abundance",
24041 "Success; the king attains it; don't worry; be like the sun at noon",
24042 "Thunder",
24043 "Fire",
24044 ),
24045 (
24046 "旅",
24047 "Lǚ",
24048 "The Wanderer",
24049 "Success through smallness; perseverance brings fortune",
24050 "Fire",
24051 "Mountain",
24052 ),
24053 (
24054 "巽",
24055 "Xùn",
24056 "The Gentle",
24057 "Success through small things; favorable to have somewhere to go",
24058 "Wind",
24059 "Wind",
24060 ),
24061 (
24062 "兌",
24063 "Duì",
24064 "The Joyous",
24065 "Success; perseverance",
24066 "Lake",
24067 "Lake",
24068 ),
24069 (
24070 "渙",
24071 "Huàn",
24072 "Dispersion",
24073 "Success; the king approaches his temple; cross the great water",
24074 "Wind",
24075 "Water",
24076 ),
24077 (
24078 "節",
24079 "Jié",
24080 "Limitation",
24081 "Success; bitter limitation should not be persevered in",
24082 "Water",
24083 "Lake",
24084 ),
24085 (
24086 "中孚",
24087 "Zhōng Fú",
24088 "Inner Truth",
24089 "Pigs and fishes; good fortune; cross the great water",
24090 "Wind",
24091 "Lake",
24092 ),
24093 (
24094 "小過",
24095 "Xiǎo Guò",
24096 "Small Exceeding",
24097 "Success; perseverance; small things yes, great things no",
24098 "Thunder",
24099 "Mountain",
24100 ),
24101 (
24102 "既濟",
24103 "Jì Jì",
24104 "After Completion",
24105 "Success in small matters; perseverance; good at start, disorder at end",
24106 "Water",
24107 "Fire",
24108 ),
24109 (
24110 "未濟",
24111 "Wèi Jì",
24112 "Before Completion",
24113 "Success; the young fox almost across; tail gets wet; no goal",
24114 "Fire",
24115 "Water",
24116 ),
24117];
24118
24119fn binary_to_king_wen(binary: u8) -> u8 {
24120 const KING_WEN_ORDER: [u8; 64] = [
24123 1, 43, 14, 34, 9, 5, 26, 11, 10, 58, 38, 54, 61, 60, 41, 19, 13, 49, 30, 55, 37, 63, 22,
24124 36, 25, 17, 21, 51, 42, 3, 27, 24, 44, 28, 50, 32, 57, 48, 18, 46, 6, 47, 64, 40, 59, 29,
24125 4, 7, 33, 31, 56, 62, 53, 39, 52, 15, 12, 45, 35, 16, 20, 8, 23, 2,
24126 ];
24127 KING_WEN_ORDER[binary as usize] - 1
24128}
24129
24130fn hebrew_gematria(c: char) -> i64 {
24131 match c {
24132 'א' | 'A' | 'a' => 1,
24133 'ב' | 'B' | 'b' => 2,
24134 'ג' | 'G' | 'g' => 3,
24135 'ד' | 'D' | 'd' => 4,
24136 'ה' | 'H' | 'h' => 5,
24137 'ו' | 'V' | 'v' | 'W' | 'w' => 6,
24138 'ז' | 'Z' | 'z' => 7,
24139 'ח' => 8,
24140 'ט' => 9,
24141 'י' | 'Y' | 'y' | 'I' | 'i' | 'J' | 'j' => 10,
24142 'כ' | 'K' | 'k' => 20,
24143 'ל' | 'L' | 'l' => 30,
24144 'מ' | 'M' | 'm' => 40,
24145 'נ' | 'N' | 'n' => 50,
24146 'ס' | 'S' | 's' | 'X' | 'x' => 60,
24147 'ע' | 'O' | 'o' => 70,
24148 'פ' | 'P' | 'p' | 'F' | 'f' => 80,
24149 'צ' => 90,
24150 'ק' | 'Q' | 'q' => 100,
24151 'ר' | 'R' | 'r' => 200,
24152 'ש' => 300,
24153 'ת' | 'T' | 't' => 400,
24154 'ך' => 500, 'ם' => 600, 'ן' => 700, 'ף' => 800, 'ץ' | 'C' | 'c' => 900, 'E' | 'e' => 5, 'U' | 'u' => 6, _ => 0,
24162 }
24163}
24164
24165fn greek_isopsephy(c: char) -> i64 {
24166 match c {
24167 'Α' | 'α' | 'A' | 'a' => 1,
24168 'Β' | 'β' | 'B' | 'b' => 2,
24169 'Γ' | 'γ' | 'G' | 'g' => 3,
24170 'Δ' | 'δ' | 'D' | 'd' => 4,
24171 'Ε' | 'ε' | 'E' | 'e' => 5,
24172 'Ϛ' | 'ϛ' => 6, 'Ζ' | 'ζ' | 'Z' | 'z' => 7,
24174 'Η' | 'η' | 'H' | 'h' => 8,
24175 'Θ' | 'θ' => 9,
24176 'Ι' | 'ι' | 'I' | 'i' => 10,
24177 'Κ' | 'κ' | 'K' | 'k' => 20,
24178 'Λ' | 'λ' | 'L' | 'l' => 30,
24179 'Μ' | 'μ' | 'M' | 'm' => 40,
24180 'Ν' | 'ν' | 'N' | 'n' => 50,
24181 'Ξ' | 'ξ' | 'X' | 'x' => 60,
24182 'Ο' | 'ο' | 'O' | 'o' => 70,
24183 'Π' | 'π' | 'P' | 'p' => 80,
24184 'Ϙ' | 'ϙ' | 'Q' | 'q' => 90, 'Ρ' | 'ρ' | 'R' | 'r' => 100,
24186 'Σ' | 'σ' | 'ς' | 'S' | 's' => 200,
24187 'Τ' | 'τ' | 'T' | 't' => 300,
24188 'Υ' | 'υ' | 'U' | 'u' | 'Y' | 'y' => 400,
24189 'Φ' | 'φ' | 'F' | 'f' => 500,
24190 'Χ' | 'χ' | 'C' | 'c' => 600,
24191 'Ψ' | 'ψ' => 700,
24192 'Ω' | 'ω' | 'W' | 'w' => 800,
24193 'Ϡ' | 'ϡ' => 900, 'J' | 'j' => 10, 'V' | 'v' => 400, _ => 0,
24197 }
24198}
24199
24200fn arabic_abjad(c: char) -> i64 {
24201 match c {
24202 'ا' | 'أ' | 'إ' | 'آ' | 'A' | 'a' => 1,
24203 'ب' | 'B' | 'b' => 2,
24204 'ج' | 'J' | 'j' | 'G' | 'g' => 3,
24205 'د' | 'D' | 'd' => 4,
24206 'ه' | 'H' | 'h' => 5,
24207 'و' | 'W' | 'w' | 'V' | 'v' => 6,
24208 'ز' | 'Z' | 'z' => 7,
24209 'ح' => 8,
24210 'ط' => 9,
24211 'ي' | 'Y' | 'y' | 'I' | 'i' => 10,
24212 'ك' | 'K' | 'k' => 20,
24213 'ل' | 'L' | 'l' => 30,
24214 'م' | 'M' | 'm' => 40,
24215 'ن' | 'N' | 'n' => 50,
24216 'س' | 'S' | 's' => 60,
24217 'ع' | 'E' | 'e' => 70,
24218 'ف' | 'F' | 'f' => 80,
24219 'ص' => 90,
24220 'ق' | 'Q' | 'q' => 100,
24221 'ر' | 'R' | 'r' => 200,
24222 'ش' => 300,
24223 'ت' | 'T' | 't' => 400,
24224 'ث' => 500,
24225 'خ' | 'X' | 'x' => 600,
24226 'ذ' => 700,
24227 'ض' => 800,
24228 'ظ' => 900,
24229 'غ' => 1000,
24230 'C' | 'c' => 600, 'O' | 'o' => 70, 'P' | 'p' => 80, 'U' | 'u' => 6, _ => 0,
24235 }
24236}
24237
24238fn register_color(interp: &mut Interpreter) {
24245 define(interp, "rgb", Some(3), |_, args| {
24251 let r = match &args[0] {
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 g = match &args[1] {
24257 Value::Int(n) => (*n).clamp(0, 255) as u8,
24258 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
24259 _ => return Err(RuntimeError::new("rgb() requires numbers")),
24260 };
24261 let b = match &args[2] {
24262 Value::Int(n) => (*n).clamp(0, 255) as u8,
24263 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
24264 _ => return Err(RuntimeError::new("rgb() requires numbers")),
24265 };
24266 let mut map = std::collections::HashMap::new();
24267 map.insert("r".to_string(), Value::Int(r as i64));
24268 map.insert("g".to_string(), Value::Int(g as i64));
24269 map.insert("b".to_string(), Value::Int(b as i64));
24270 map.insert(
24271 "hex".to_string(),
24272 Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))),
24273 );
24274 Ok(Value::Map(Rc::new(RefCell::new(map))))
24275 });
24276
24277 define(interp, "hex_to_rgb", Some(1), |_, args| {
24279 let hex = match &args[0] {
24280 Value::String(s) => s.to_string(),
24281 _ => return Err(RuntimeError::new("hex_to_rgb requires string")),
24282 };
24283 let hex = hex.trim_start_matches('#');
24284 if hex.len() != 6 {
24285 return Err(RuntimeError::new("hex_to_rgb requires 6 character hex"));
24286 }
24287 let r = u8::from_str_radix(&hex[0..2], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24288 let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24289 let b = u8::from_str_radix(&hex[4..6], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24290 let mut map = std::collections::HashMap::new();
24291 map.insert("r".to_string(), Value::Int(r as i64));
24292 map.insert("g".to_string(), Value::Int(g as i64));
24293 map.insert("b".to_string(), Value::Int(b as i64));
24294 Ok(Value::Map(Rc::new(RefCell::new(map))))
24295 });
24296
24297 define(interp, "rgb_to_hsl", Some(3), |_, args| {
24299 let r = match &args[0] {
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 g = match &args[1] {
24305 Value::Int(n) => *n as f64 / 255.0,
24306 Value::Float(f) => *f / 255.0,
24307 _ => return Err(RuntimeError::new("requires numbers")),
24308 };
24309 let b = match &args[2] {
24310 Value::Int(n) => *n as f64 / 255.0,
24311 Value::Float(f) => *f / 255.0,
24312 _ => return Err(RuntimeError::new("requires numbers")),
24313 };
24314 let max = r.max(g).max(b);
24315 let min = r.min(g).min(b);
24316 let l = (max + min) / 2.0;
24317 let (h, s) = if max == min {
24318 (0.0, 0.0)
24319 } else {
24320 let d = max - min;
24321 let s = if l > 0.5 {
24322 d / (2.0 - max - min)
24323 } else {
24324 d / (max + min)
24325 };
24326 let h = if max == r {
24327 (g - b) / d + if g < b { 6.0 } else { 0.0 }
24328 } else if max == g {
24329 (b - r) / d + 2.0
24330 } else {
24331 (r - g) / d + 4.0
24332 };
24333 (h * 60.0, s)
24334 };
24335 let mut map = std::collections::HashMap::new();
24336 map.insert("h".to_string(), Value::Float(h));
24337 map.insert("s".to_string(), Value::Float(s));
24338 map.insert("l".to_string(), Value::Float(l));
24339 Ok(Value::Map(Rc::new(RefCell::new(map))))
24340 });
24341
24342 define(interp, "complementary", Some(3), |_, args| {
24344 let r = match &args[0] {
24345 Value::Int(n) => *n as u8,
24346 _ => return Err(RuntimeError::new("requires int")),
24347 };
24348 let g = match &args[1] {
24349 Value::Int(n) => *n as u8,
24350 _ => return Err(RuntimeError::new("requires int")),
24351 };
24352 let b = match &args[2] {
24353 Value::Int(n) => *n as u8,
24354 _ => return Err(RuntimeError::new("requires int")),
24355 };
24356 let mut map = std::collections::HashMap::new();
24357 map.insert("r".to_string(), Value::Int(255 - r as i64));
24358 map.insert("g".to_string(), Value::Int(255 - g as i64));
24359 map.insert("b".to_string(), Value::Int(255 - b as i64));
24360 Ok(Value::Map(Rc::new(RefCell::new(map))))
24361 });
24362
24363 define(interp, "wu_xing", Some(1), |_, args| {
24367 let element = match &args[0] {
24368 Value::String(s) => s.to_lowercase(),
24369 _ => return Err(RuntimeError::new("wu_xing requires string")),
24370 };
24371 let (name, chinese, color, hex, direction, season, organ, emotion, planet, animal) =
24372 match element.as_str() {
24373 "wood" | "mu" | "木" => (
24374 "Wood",
24375 "木 (Mù)",
24376 "Green/Azure",
24377 "#228B22",
24378 "East",
24379 "Spring",
24380 "Liver",
24381 "Anger",
24382 "Jupiter",
24383 "Azure Dragon",
24384 ),
24385 "fire" | "huo" | "火" => (
24386 "Fire",
24387 "火 (Huǒ)",
24388 "Red",
24389 "#FF0000",
24390 "South",
24391 "Summer",
24392 "Heart",
24393 "Joy",
24394 "Mars",
24395 "Vermilion Bird",
24396 ),
24397 "earth" | "tu" | "土" => (
24398 "Earth",
24399 "土 (Tǔ)",
24400 "Yellow",
24401 "#FFDB58",
24402 "Center",
24403 "Late Summer",
24404 "Spleen",
24405 "Worry",
24406 "Saturn",
24407 "Yellow Dragon",
24408 ),
24409 "metal" | "jin" | "金" => (
24410 "Metal",
24411 "金 (Jīn)",
24412 "White/Gold",
24413 "#FFD700",
24414 "West",
24415 "Autumn",
24416 "Lung",
24417 "Grief",
24418 "Venus",
24419 "White Tiger",
24420 ),
24421 "water" | "shui" | "水" => (
24422 "Water",
24423 "水 (Shuǐ)",
24424 "Black/Blue",
24425 "#000080",
24426 "North",
24427 "Winter",
24428 "Kidney",
24429 "Fear",
24430 "Mercury",
24431 "Black Tortoise",
24432 ),
24433 _ => {
24434 return Err(RuntimeError::new(
24435 "Unknown element. Use wood/fire/earth/metal/water",
24436 ))
24437 }
24438 };
24439 let mut map = std::collections::HashMap::new();
24440 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24441 map.insert(
24442 "chinese".to_string(),
24443 Value::String(Rc::new(chinese.to_string())),
24444 );
24445 map.insert(
24446 "color".to_string(),
24447 Value::String(Rc::new(color.to_string())),
24448 );
24449 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24450 map.insert(
24451 "direction".to_string(),
24452 Value::String(Rc::new(direction.to_string())),
24453 );
24454 map.insert(
24455 "season".to_string(),
24456 Value::String(Rc::new(season.to_string())),
24457 );
24458 map.insert(
24459 "organ".to_string(),
24460 Value::String(Rc::new(organ.to_string())),
24461 );
24462 map.insert(
24463 "emotion".to_string(),
24464 Value::String(Rc::new(emotion.to_string())),
24465 );
24466 map.insert(
24467 "planet".to_string(),
24468 Value::String(Rc::new(planet.to_string())),
24469 );
24470 map.insert(
24471 "guardian".to_string(),
24472 Value::String(Rc::new(animal.to_string())),
24473 );
24474 Ok(Value::Map(Rc::new(RefCell::new(map))))
24475 });
24476
24477 define(interp, "chakra_color", Some(1), |_, args| {
24481 let chakra = match &args[0] {
24482 Value::String(s) => s.to_lowercase(),
24483 Value::Int(n) => n.to_string(),
24484 _ => return Err(RuntimeError::new("chakra_color requires string or number")),
24485 };
24486 let (name, sanskrit, color, hex, location, freq, element, mantra) = match chakra.as_str() {
24487 "root" | "muladhara" | "1" => (
24488 "Root",
24489 "मूलाधार",
24490 "Red",
24491 "#FF0000",
24492 "Base of spine",
24493 396.0,
24494 "Earth",
24495 "LAM",
24496 ),
24497 "sacral" | "svadhisthana" | "2" => (
24498 "Sacral",
24499 "स्वाधिष्ठान",
24500 "Orange",
24501 "#FF7F00",
24502 "Below navel",
24503 417.0,
24504 "Water",
24505 "VAM",
24506 ),
24507 "solar" | "manipura" | "3" => (
24508 "Solar Plexus",
24509 "मणिपूर",
24510 "Yellow",
24511 "#FFFF00",
24512 "Stomach",
24513 528.0,
24514 "Fire",
24515 "RAM",
24516 ),
24517 "heart" | "anahata" | "4" => (
24518 "Heart",
24519 "अनाहत",
24520 "Green",
24521 "#00FF00",
24522 "Chest",
24523 639.0,
24524 "Air",
24525 "YAM",
24526 ),
24527 "throat" | "vishuddha" | "5" => (
24528 "Throat",
24529 "विशुद्ध",
24530 "Blue",
24531 "#00BFFF",
24532 "Throat",
24533 741.0,
24534 "Ether",
24535 "HAM",
24536 ),
24537 "third_eye" | "ajna" | "6" => (
24538 "Third Eye",
24539 "आज्ञा",
24540 "Indigo",
24541 "#4B0082",
24542 "Forehead",
24543 852.0,
24544 "Light",
24545 "OM",
24546 ),
24547 "crown" | "sahasrara" | "7" => (
24548 "Crown",
24549 "सहस्रार",
24550 "Violet",
24551 "#8B00FF",
24552 "Top of head",
24553 963.0,
24554 "Thought",
24555 "Silence",
24556 ),
24557 _ => {
24558 return Err(RuntimeError::new(
24559 "Unknown chakra. Use root/sacral/solar/heart/throat/third_eye/crown or 1-7",
24560 ))
24561 }
24562 };
24563 let mut map = std::collections::HashMap::new();
24564 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24565 map.insert(
24566 "sanskrit".to_string(),
24567 Value::String(Rc::new(sanskrit.to_string())),
24568 );
24569 map.insert(
24570 "color".to_string(),
24571 Value::String(Rc::new(color.to_string())),
24572 );
24573 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24574 map.insert(
24575 "location".to_string(),
24576 Value::String(Rc::new(location.to_string())),
24577 );
24578 map.insert("frequency_hz".to_string(), Value::Float(freq));
24579 map.insert(
24580 "element".to_string(),
24581 Value::String(Rc::new(element.to_string())),
24582 );
24583 map.insert(
24584 "mantra".to_string(),
24585 Value::String(Rc::new(mantra.to_string())),
24586 );
24587 Ok(Value::Map(Rc::new(RefCell::new(map))))
24588 });
24589
24590 define(interp, "maya_direction", Some(1), |_, args| {
24594 let dir = match &args[0] {
24595 Value::String(s) => s.to_lowercase(),
24596 _ => return Err(RuntimeError::new("requires string")),
24597 };
24598 let (direction, yucatec, color, hex, deity, meaning) = match dir.as_str() {
24599 "east" | "lakin" => (
24600 "East",
24601 "Lak'in",
24602 "Red",
24603 "#FF0000",
24604 "Chac (Red)",
24605 "Sunrise, new beginnings",
24606 ),
24607 "north" | "xaman" => (
24608 "North",
24609 "Xaman",
24610 "White",
24611 "#FFFFFF",
24612 "Chac (White)",
24613 "Ancestors, death",
24614 ),
24615 "west" | "chikin" => (
24616 "West",
24617 "Chik'in",
24618 "Black",
24619 "#000000",
24620 "Chac (Black)",
24621 "Sunset, completion",
24622 ),
24623 "south" | "nohol" => (
24624 "South",
24625 "Nohol",
24626 "Yellow",
24627 "#FFFF00",
24628 "Chac (Yellow)",
24629 "Maize, abundance",
24630 ),
24631 "center" | "yax" => (
24632 "Center",
24633 "Yax",
24634 "Green/Blue",
24635 "#00CED1",
24636 "World Tree",
24637 "Balance",
24638 ),
24639 _ => {
24640 return Err(RuntimeError::new(
24641 "Unknown direction. Use east/north/west/south/center",
24642 ))
24643 }
24644 };
24645 let mut map = std::collections::HashMap::new();
24646 map.insert(
24647 "direction".to_string(),
24648 Value::String(Rc::new(direction.to_string())),
24649 );
24650 map.insert(
24651 "yucatec".to_string(),
24652 Value::String(Rc::new(yucatec.to_string())),
24653 );
24654 map.insert(
24655 "color".to_string(),
24656 Value::String(Rc::new(color.to_string())),
24657 );
24658 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24659 map.insert(
24660 "deity".to_string(),
24661 Value::String(Rc::new(deity.to_string())),
24662 );
24663 map.insert(
24664 "meaning".to_string(),
24665 Value::String(Rc::new(meaning.to_string())),
24666 );
24667 Ok(Value::Map(Rc::new(RefCell::new(map))))
24668 });
24669
24670 define(interp, "orisha_color", Some(1), |_, args| {
24674 let orisha = match &args[0] {
24675 Value::String(s) => s.to_lowercase(),
24676 _ => return Err(RuntimeError::new("requires string")),
24677 };
24678 let (name, colors, hex, domain, day, number) = match orisha.as_str() {
24679 "obatala" | "oxala" => (
24680 "Obatalá",
24681 "White, silver",
24682 "#FFFFFF",
24683 "Creation, purity, wisdom",
24684 "Sunday",
24685 8,
24686 ),
24687 "yemoja" | "yemanja" => (
24688 "Yemọja",
24689 "Blue, white",
24690 "#4169E1",
24691 "Ocean, motherhood",
24692 "Saturday",
24693 7,
24694 ),
24695 "oshun" | "oxum" => (
24696 "Ọṣun",
24697 "Yellow, gold",
24698 "#FFD700",
24699 "Rivers, love, fertility",
24700 "Saturday",
24701 5,
24702 ),
24703 "shango" | "xango" => (
24704 "Ṣàngó",
24705 "Red, white",
24706 "#FF0000",
24707 "Thunder, fire, justice",
24708 "Wednesday",
24709 6,
24710 ),
24711 "ogun" | "ogum" => (
24712 "Ògún",
24713 "Green, black",
24714 "#006400",
24715 "Iron, war, labor",
24716 "Tuesday",
24717 7,
24718 ),
24719 "oya" | "iansa" => (
24720 "Ọya",
24721 "Brown, purple",
24722 "#800020",
24723 "Wind, storms, change",
24724 "Wednesday",
24725 9,
24726 ),
24727 "eshu" | "exu" => (
24728 "Èṣù",
24729 "Red, black",
24730 "#8B0000",
24731 "Crossroads, messages",
24732 "Monday",
24733 3,
24734 ),
24735 _ => {
24736 return Err(RuntimeError::new(
24737 "Unknown Orisha. Use obatala/yemoja/oshun/shango/ogun/oya/eshu",
24738 ))
24739 }
24740 };
24741 let mut map = std::collections::HashMap::new();
24742 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24743 map.insert(
24744 "colors".to_string(),
24745 Value::String(Rc::new(colors.to_string())),
24746 );
24747 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24748 map.insert(
24749 "domain".to_string(),
24750 Value::String(Rc::new(domain.to_string())),
24751 );
24752 map.insert("day".to_string(), Value::String(Rc::new(day.to_string())));
24753 map.insert("number".to_string(), Value::Int(number));
24754 Ok(Value::Map(Rc::new(RefCell::new(map))))
24755 });
24756
24757 define(interp, "nihon_iro", Some(1), |_, args| {
24761 let color = match &args[0] {
24762 Value::String(s) => s.to_lowercase(),
24763 _ => return Err(RuntimeError::new("requires string")),
24764 };
24765 let (name, japanese, hex, meaning, season) = match color.as_str() {
24766 "sakura" => (
24767 "Sakura Pink",
24768 "桜色",
24769 "#FFB7C5",
24770 "Cherry blossoms, transience",
24771 "Spring",
24772 ),
24773 "fuji" => (
24774 "Wisteria",
24775 "藤色",
24776 "#C9A0DC",
24777 "Elegance, nobility",
24778 "Spring",
24779 ),
24780 "moegi" => (
24781 "Young Green",
24782 "萌黄",
24783 "#AACF53",
24784 "New growth, freshness",
24785 "Spring",
24786 ),
24787 "ai" => ("Indigo", "藍色", "#004D99", "Protection, depth", "All"),
24788 "akane" => ("Madder Red", "茜色", "#CF3A24", "Sunset, passion", "Autumn"),
24789 "shiro" => ("White", "白", "#FFFFFF", "Purity, death, sacred", "Winter"),
24790 "kuro" => ("Black", "黒", "#000000", "Formality, mystery", "All"),
24791 "aka" => ("Red", "赤", "#D7003A", "Life force, celebration", "All"),
24792 "murasaki" => ("Purple", "紫", "#884898", "Nobility, spirituality", "All"),
24793 _ => {
24794 return Err(RuntimeError::new(
24795 "Unknown color. Try: sakura/fuji/moegi/ai/akane/shiro/kuro/aka/murasaki",
24796 ))
24797 }
24798 };
24799 let mut map = std::collections::HashMap::new();
24800 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24801 map.insert(
24802 "japanese".to_string(),
24803 Value::String(Rc::new(japanese.to_string())),
24804 );
24805 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24806 map.insert(
24807 "meaning".to_string(),
24808 Value::String(Rc::new(meaning.to_string())),
24809 );
24810 map.insert(
24811 "season".to_string(),
24812 Value::String(Rc::new(season.to_string())),
24813 );
24814 Ok(Value::Map(Rc::new(RefCell::new(map))))
24815 });
24816
24817 define(interp, "islamic_color", Some(1), |_, args| {
24821 let color = match &args[0] {
24822 Value::String(s) => s.to_lowercase(),
24823 _ => return Err(RuntimeError::new("requires string")),
24824 };
24825 let (name, arabic, hex, meaning, usage) = match color.as_str() {
24826 "green" | "akhdar" => (
24827 "Green",
24828 "أخضر",
24829 "#00FF00",
24830 "Paradise, Prophet, life",
24831 "Mosques, Quran, flags",
24832 ),
24833 "white" | "abyad" => (
24834 "White",
24835 "أبيض",
24836 "#FFFFFF",
24837 "Purity, peace, ihram",
24838 "Pilgrimage, burial",
24839 ),
24840 "black" | "aswad" => (
24841 "Black",
24842 "أسود",
24843 "#000000",
24844 "Modesty, Kaaba",
24845 "Kiswah, abaya",
24846 ),
24847 "gold" | "dhahabi" => (
24848 "Gold",
24849 "ذهبي",
24850 "#FFD700",
24851 "Paradise, divine light",
24852 "Calligraphy, decoration",
24853 ),
24854 "blue" | "azraq" => (
24855 "Blue",
24856 "أزرق",
24857 "#0000CD",
24858 "Protection, heaven",
24859 "Tiles, evil eye",
24860 ),
24861 _ => {
24862 return Err(RuntimeError::new(
24863 "Unknown color. Use green/white/black/gold/blue",
24864 ))
24865 }
24866 };
24867 let mut map = std::collections::HashMap::new();
24868 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24869 map.insert(
24870 "arabic".to_string(),
24871 Value::String(Rc::new(arabic.to_string())),
24872 );
24873 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24874 map.insert(
24875 "meaning".to_string(),
24876 Value::String(Rc::new(meaning.to_string())),
24877 );
24878 map.insert(
24879 "usage".to_string(),
24880 Value::String(Rc::new(usage.to_string())),
24881 );
24882 Ok(Value::Map(Rc::new(RefCell::new(map))))
24883 });
24884
24885 define(interp, "thai_day_color", Some(1), |_, args| {
24889 let day = match &args[0] {
24890 Value::String(s) => s.to_lowercase(),
24891 Value::Int(n) => n.to_string(),
24892 _ => return Err(RuntimeError::new("requires string or number")),
24893 };
24894 let (day_name, thai, color, hex, deity) = match day.as_str() {
24895 "sunday" | "0" => ("Sunday", "วันอาทิตย์", "Red", "#FF0000", "Surya"),
24896 "monday" | "1" => ("Monday", "วันจันทร์", "Yellow", "#FFFF00", "Chandra"),
24897 "tuesday" | "2" => ("Tuesday", "วันอังคาร", "Pink", "#FFC0CB", "Mangala"),
24898 "wednesday" | "3" => ("Wednesday", "วันพุธ", "Green", "#00FF00", "Budha"),
24899 "thursday" | "4" => ("Thursday", "วันพฤหัสบดี", "Orange", "#FFA500", "Brihaspati"),
24900 "friday" | "5" => ("Friday", "วันศุกร์", "Blue", "#00BFFF", "Shukra"),
24901 "saturday" | "6" => ("Saturday", "วันเสาร์", "Purple", "#800080", "Shani"),
24902 _ => return Err(RuntimeError::new("Unknown day. Use sunday-saturday or 0-6")),
24903 };
24904 let mut map = std::collections::HashMap::new();
24905 map.insert(
24906 "day".to_string(),
24907 Value::String(Rc::new(day_name.to_string())),
24908 );
24909 map.insert("thai".to_string(), Value::String(Rc::new(thai.to_string())));
24910 map.insert(
24911 "color".to_string(),
24912 Value::String(Rc::new(color.to_string())),
24913 );
24914 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24915 map.insert(
24916 "deity".to_string(),
24917 Value::String(Rc::new(deity.to_string())),
24918 );
24919 Ok(Value::Map(Rc::new(RefCell::new(map))))
24920 });
24921
24922 define(interp, "aboriginal_color", Some(1), |_, args| {
24926 let color = match &args[0] {
24927 Value::String(s) => s.to_lowercase(),
24928 _ => return Err(RuntimeError::new("requires string")),
24929 };
24930 let (name, hex, meaning, source, dreamtime) = match color.as_str() {
24931 "red" | "ochre" => (
24932 "Red Ochre",
24933 "#CC5500",
24934 "Earth, blood, ceremony",
24935 "Hematite",
24936 "Ancestral beings",
24937 ),
24938 "yellow" => (
24939 "Yellow Ochre",
24940 "#D4A017",
24941 "Sun, healing",
24942 "Limonite",
24943 "Sun's journey",
24944 ),
24945 "white" => (
24946 "White",
24947 "#FFFFFF",
24948 "Sky, spirits, mourning",
24949 "Kaolin",
24950 "Sky beings",
24951 ),
24952 "black" => (
24953 "Black",
24954 "#000000",
24955 "Night, formality",
24956 "Charcoal",
24957 "Night, men's business",
24958 ),
24959 "brown" => (
24960 "Brown",
24961 "#8B4513",
24962 "Earth, land",
24963 "Earth pigments",
24964 "Country, connection",
24965 ),
24966 _ => {
24967 return Err(RuntimeError::new(
24968 "Unknown color. Use red/yellow/white/black/brown",
24969 ))
24970 }
24971 };
24972 let mut map = std::collections::HashMap::new();
24973 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24974 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24975 map.insert(
24976 "meaning".to_string(),
24977 Value::String(Rc::new(meaning.to_string())),
24978 );
24979 map.insert(
24980 "source".to_string(),
24981 Value::String(Rc::new(source.to_string())),
24982 );
24983 map.insert(
24984 "dreamtime".to_string(),
24985 Value::String(Rc::new(dreamtime.to_string())),
24986 );
24987 Ok(Value::Map(Rc::new(RefCell::new(map))))
24988 });
24989
24990 define(interp, "celtic_color", Some(1), |_, args| {
24994 let color = match &args[0] {
24995 Value::String(s) => s.to_lowercase(),
24996 _ => return Err(RuntimeError::new("requires string")),
24997 };
24998 let (name, gaelic, hex, meaning, element) = match color.as_str() {
24999 "green" => (
25000 "Green",
25001 "Glas",
25002 "#228B22",
25003 "Nature, fairies, Otherworld",
25004 "Earth",
25005 ),
25006 "white" => ("White", "Bán", "#FFFFFF", "Purity, spirits", "Air"),
25007 "red" => ("Red", "Dearg", "#FF0000", "War, courage, blood", "Fire"),
25008 "black" => (
25009 "Black",
25010 "Dubh",
25011 "#000000",
25012 "Otherworld, death, rebirth",
25013 "Water",
25014 ),
25015 "gold" => ("Gold", "Órga", "#FFD700", "Sun, sovereignty, Lugh", "Fire"),
25016 "silver" => (
25017 "Silver",
25018 "Airgid",
25019 "#C0C0C0",
25020 "Moon, feminine, intuition",
25021 "Water",
25022 ),
25023 _ => {
25024 return Err(RuntimeError::new(
25025 "Unknown color. Use green/white/red/black/gold/silver",
25026 ))
25027 }
25028 };
25029 let mut map = std::collections::HashMap::new();
25030 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25031 map.insert(
25032 "gaelic".to_string(),
25033 Value::String(Rc::new(gaelic.to_string())),
25034 );
25035 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25036 map.insert(
25037 "meaning".to_string(),
25038 Value::String(Rc::new(meaning.to_string())),
25039 );
25040 map.insert(
25041 "element".to_string(),
25042 Value::String(Rc::new(element.to_string())),
25043 );
25044 Ok(Value::Map(Rc::new(RefCell::new(map))))
25045 });
25046
25047 define(interp, "kente_color", Some(1), |_, args| {
25051 let color = match &args[0] {
25052 Value::String(s) => s.to_lowercase(),
25053 _ => return Err(RuntimeError::new("requires string")),
25054 };
25055 let (name, twi, hex, meaning) = match color.as_str() {
25056 "gold" | "yellow" => ("Gold", "Sika Kɔkɔɔ", "#FFD700", "Royalty, wealth, glory"),
25057 "green" => ("Green", "Ahabammono", "#228B22", "Growth, renewal, harvest"),
25058 "blue" => ("Blue", "Bruu", "#0000CD", "Peace, harmony, love"),
25059 "red" => ("Red", "Kɔkɔɔ", "#FF0000", "Blood, sacrifice, power"),
25060 "black" => ("Black", "Tuntum", "#000000", "Maturation, ancestors"),
25061 "white" => ("White", "Fitaa", "#FFFFFF", "Purification, virtue, joy"),
25062 "maroon" => ("Maroon", "Borɔnɔ", "#800000", "Earth, healing, protection"),
25063 _ => {
25064 return Err(RuntimeError::new(
25065 "Unknown color. Use gold/green/blue/red/black/white/maroon",
25066 ))
25067 }
25068 };
25069 let mut map = std::collections::HashMap::new();
25070 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25071 map.insert("twi".to_string(), Value::String(Rc::new(twi.to_string())));
25072 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25073 map.insert(
25074 "meaning".to_string(),
25075 Value::String(Rc::new(meaning.to_string())),
25076 );
25077 Ok(Value::Map(Rc::new(RefCell::new(map))))
25078 });
25079
25080 define(interp, "hindu_color", Some(1), |_, args| {
25084 let color = match &args[0] {
25085 Value::String(s) => s.to_lowercase(),
25086 _ => return Err(RuntimeError::new("requires string")),
25087 };
25088 let (name, hindi, hex, meaning, deities) = match color.as_str() {
25089 "red" | "lal" => (
25090 "Red",
25091 "लाल",
25092 "#FF0000",
25093 "Purity, fertility, love",
25094 "Durga, Lakshmi",
25095 ),
25096 "orange" | "saffron" => (
25097 "Saffron",
25098 "केसरी",
25099 "#FF6600",
25100 "Sacred, renunciation",
25101 "Hanuman",
25102 ),
25103 "yellow" => (
25104 "Yellow",
25105 "पीला",
25106 "#FFFF00",
25107 "Knowledge, learning",
25108 "Vishnu, Saraswati",
25109 ),
25110 "green" => ("Green", "हरा", "#008000", "Life, happiness", "Krishna"),
25111 "white" => (
25112 "White",
25113 "सफ़ेद",
25114 "#FFFFFF",
25115 "Purity, mourning",
25116 "Saraswati, Shiva",
25117 ),
25118 "blue" => (
25119 "Blue",
25120 "नीला",
25121 "#0000FF",
25122 "Divinity, infinity",
25123 "Krishna, Vishnu",
25124 ),
25125 "black" => (
25126 "Black",
25127 "काला",
25128 "#000000",
25129 "Protection from evil",
25130 "Kali, Shani",
25131 ),
25132 _ => {
25133 return Err(RuntimeError::new(
25134 "Unknown color. Use red/orange/yellow/green/white/blue/black",
25135 ))
25136 }
25137 };
25138 let mut map = std::collections::HashMap::new();
25139 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25140 map.insert(
25141 "hindi".to_string(),
25142 Value::String(Rc::new(hindi.to_string())),
25143 );
25144 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25145 map.insert(
25146 "meaning".to_string(),
25147 Value::String(Rc::new(meaning.to_string())),
25148 );
25149 map.insert(
25150 "deities".to_string(),
25151 Value::String(Rc::new(deities.to_string())),
25152 );
25153 Ok(Value::Map(Rc::new(RefCell::new(map))))
25154 });
25155
25156 define(interp, "emotion_color", Some(2), |_, args| {
25160 let emotion = match &args[0] {
25161 Value::String(s) => s.to_lowercase(),
25162 _ => return Err(RuntimeError::new("requires string")),
25163 };
25164 let culture = match &args[1] {
25165 Value::String(s) => s.to_lowercase(),
25166 _ => return Err(RuntimeError::new("requires string")),
25167 };
25168 let (hex, name, reasoning) = match (emotion.as_str(), culture.as_str()) {
25169 ("joy", "western") | ("happy", "western") => {
25170 ("#FFD700", "Gold", "Sunshine = happiness")
25171 }
25172 ("joy", "chinese") | ("happy", "chinese") => ("#FF0000", "Red", "红 = luck, joy"),
25173 ("joy", "japanese") => ("#FFB7C5", "Sakura", "Cherry blossom = fleeting joy"),
25174 ("sadness", "western") | ("sad", "western") => ("#0000CD", "Blue", "'Feeling blue'"),
25175 ("sadness", "chinese") | ("sad", "chinese") => ("#FFFFFF", "White", "白 = mourning"),
25176 ("sadness", "indian") => ("#FFFFFF", "White", "सफ़ेद = mourning"),
25177 ("anger", _) => ("#FF0000", "Red", "Universal heat/fire"),
25178 ("love", "western") => ("#FF69B4", "Pink", "Valentine's hearts"),
25179 ("love", "chinese") | ("love", "indian") => ("#FF0000", "Red", "Red = marriage, love"),
25180 ("peace", "western") => ("#ADD8E6", "Light Blue", "Sky, serenity"),
25181 ("peace", "islamic") => ("#00FF00", "Green", "السلام = paradise"),
25182 ("fear", _) => ("#4B0082", "Indigo", "Deep, mysterious"),
25183 (_, _) => ("#808080", "Grey", "Neutral"),
25184 };
25185 let r = u8::from_str_radix(&hex[1..3], 16).unwrap_or(128);
25186 let g = u8::from_str_radix(&hex[3..5], 16).unwrap_or(128);
25187 let b = u8::from_str_radix(&hex[5..7], 16).unwrap_or(128);
25188 let mut map = std::collections::HashMap::new();
25189 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25190 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25191 map.insert("r".to_string(), Value::Int(r as i64));
25192 map.insert("g".to_string(), Value::Int(g as i64));
25193 map.insert("b".to_string(), Value::Int(b as i64));
25194 map.insert(
25195 "reasoning".to_string(),
25196 Value::String(Rc::new(reasoning.to_string())),
25197 );
25198 Ok(Value::Map(Rc::new(RefCell::new(map))))
25199 });
25200
25201 define(interp, "synesthesia", Some(2), |_, args| {
25203 let culture = match &args[1] {
25204 Value::String(s) => s.to_lowercase(),
25205 _ => return Err(RuntimeError::new("requires culture string")),
25206 };
25207 let (r, g, b, emotion, freq) = match &args[0] {
25208 Value::String(s) => match s.to_lowercase().as_str() {
25209 "joy" | "happy" => (255u8, 215u8, 0u8, "joy", 528.0),
25210 "sadness" | "sad" => (0, 0, 139, "sadness", 396.0),
25211 "anger" => (255, 0, 0, "anger", 417.0),
25212 "fear" => (75, 0, 130, "fear", 369.0),
25213 "love" => (255, 105, 180, "love", 639.0),
25214 "peace" => (135, 206, 235, "peace", 741.0),
25215 _ => (128, 128, 128, "neutral", 432.0),
25216 },
25217 Value::Int(n) => (128, 128, 255, "resonance", *n as f64),
25218 Value::Float(f) => (128, 128, 255, "resonance", *f),
25219 _ => (128, 128, 128, "neutral", 432.0),
25220 };
25221 let cultural_meaning = match culture.as_str() {
25222 "chinese" if r > 200 && g < 100 => "luck/joy (红)",
25223 "japanese" if r > 200 && g < 100 => "vitality (赤)",
25224 "indian" if r > 200 && g < 100 => "shakti/auspicious",
25225 _ => "universal resonance",
25226 };
25227 let chakra = if r > 200 && g < 100 {
25228 "Root"
25229 } else if g > 200 {
25230 "Heart"
25231 } else if b > 200 {
25232 "Throat"
25233 } else {
25234 "Crown"
25235 };
25236 let wu_xing = if r > 200 && g < 100 {
25237 "Fire (火)"
25238 } else if g > 200 {
25239 "Wood (木)"
25240 } else if b > 200 {
25241 "Water (水)"
25242 } else {
25243 "Metal (金)"
25244 };
25245 let mut map = std::collections::HashMap::new();
25246 let mut color_map = std::collections::HashMap::new();
25247 color_map.insert("r".to_string(), Value::Int(r as i64));
25248 color_map.insert("g".to_string(), Value::Int(g as i64));
25249 color_map.insert("b".to_string(), Value::Int(b as i64));
25250 color_map.insert(
25251 "hex".to_string(),
25252 Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))),
25253 );
25254 map.insert(
25255 "color".to_string(),
25256 Value::Map(Rc::new(RefCell::new(color_map))),
25257 );
25258 map.insert(
25259 "emotion".to_string(),
25260 Value::String(Rc::new(emotion.to_string())),
25261 );
25262 map.insert("frequency".to_string(), Value::Float(freq));
25263 map.insert(
25264 "cultural_meaning".to_string(),
25265 Value::String(Rc::new(cultural_meaning.to_string())),
25266 );
25267 map.insert(
25268 "chakra".to_string(),
25269 Value::String(Rc::new(chakra.to_string())),
25270 );
25271 map.insert(
25272 "wu_xing".to_string(),
25273 Value::String(Rc::new(wu_xing.to_string())),
25274 );
25275 Ok(Value::Map(Rc::new(RefCell::new(map))))
25276 });
25277
25278 define(interp, "color_to_sound", Some(3), |_, args| {
25280 let r = match &args[0] {
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 g = match &args[1] {
25286 Value::Int(n) => *n as f64 / 255.0,
25287 Value::Float(f) => *f / 255.0,
25288 _ => return Err(RuntimeError::new("requires numbers")),
25289 };
25290 let b = match &args[2] {
25291 Value::Int(n) => *n as f64 / 255.0,
25292 Value::Float(f) => *f / 255.0,
25293 _ => return Err(RuntimeError::new("requires numbers")),
25294 };
25295 let max = r.max(g).max(b);
25296 let min = r.min(g).min(b);
25297 let l = (max + min) / 2.0;
25298 let h = if max == min {
25299 0.0
25300 } else {
25301 let d = max - min;
25302 if max == r {
25303 (g - b) / d + if g < b { 6.0 } else { 0.0 }
25304 } else if max == g {
25305 (b - r) / d + 2.0
25306 } else {
25307 (r - g) / d + 4.0
25308 }
25309 } * 60.0;
25310 let (note, freq) = if h < 30.0 {
25311 ("C", 261.63)
25312 } else if h < 60.0 {
25313 ("G", 392.00)
25314 } else if h < 90.0 {
25315 ("D", 293.66)
25316 } else if h < 120.0 {
25317 ("A", 440.00)
25318 } else if h < 150.0 {
25319 ("E", 329.63)
25320 } else if h < 180.0 {
25321 ("B", 493.88)
25322 } else if h < 210.0 {
25323 ("F#", 369.99)
25324 } else if h < 240.0 {
25325 ("Db", 277.18)
25326 } else if h < 270.0 {
25327 ("Ab", 415.30)
25328 } else if h < 300.0 {
25329 ("Eb", 311.13)
25330 } else if h < 330.0 {
25331 ("Bb", 466.16)
25332 } else {
25333 ("F", 349.23)
25334 };
25335 let octave_shift = ((l - 0.5) * 4.0).round() as i32;
25336 let adjusted_freq = freq * 2.0_f64.powi(octave_shift);
25337 let mut map = std::collections::HashMap::new();
25338 map.insert("note".to_string(), Value::String(Rc::new(note.to_string())));
25339 map.insert("frequency".to_string(), Value::Float(adjusted_freq));
25340 map.insert("hue".to_string(), Value::Float(h));
25341 Ok(Value::Map(Rc::new(RefCell::new(map))))
25342 });
25343
25344 define(interp, "contrast_ratio", Some(6), |_, args| {
25346 fn lum(r: f64, g: f64, b: f64) -> f64 {
25347 fn ch(c: f64) -> f64 {
25348 let c = c / 255.0;
25349 if c <= 0.03928 {
25350 c / 12.92
25351 } else {
25352 ((c + 0.055) / 1.055).powf(2.4)
25353 }
25354 }
25355 0.2126 * ch(r) + 0.7152 * ch(g) + 0.0722 * ch(b)
25356 }
25357 let r1 = match &args[0] {
25358 Value::Int(n) => *n as f64,
25359 Value::Float(f) => *f,
25360 _ => return Err(RuntimeError::new("requires numbers")),
25361 };
25362 let g1 = match &args[1] {
25363 Value::Int(n) => *n as f64,
25364 Value::Float(f) => *f,
25365 _ => return Err(RuntimeError::new("requires numbers")),
25366 };
25367 let b1 = match &args[2] {
25368 Value::Int(n) => *n as f64,
25369 Value::Float(f) => *f,
25370 _ => return Err(RuntimeError::new("requires numbers")),
25371 };
25372 let r2 = match &args[3] {
25373 Value::Int(n) => *n as f64,
25374 Value::Float(f) => *f,
25375 _ => return Err(RuntimeError::new("requires numbers")),
25376 };
25377 let g2 = match &args[4] {
25378 Value::Int(n) => *n as f64,
25379 Value::Float(f) => *f,
25380 _ => return Err(RuntimeError::new("requires numbers")),
25381 };
25382 let b2 = match &args[5] {
25383 Value::Int(n) => *n as f64,
25384 Value::Float(f) => *f,
25385 _ => return Err(RuntimeError::new("requires numbers")),
25386 };
25387 let l1 = lum(r1, g1, b1);
25388 let l2 = lum(r2, g2, b2);
25389 let ratio = if l1 > l2 {
25390 (l1 + 0.05) / (l2 + 0.05)
25391 } else {
25392 (l2 + 0.05) / (l1 + 0.05)
25393 };
25394 let mut map = std::collections::HashMap::new();
25395 map.insert("ratio".to_string(), Value::Float(ratio));
25396 map.insert("aa_normal".to_string(), Value::Bool(ratio >= 4.5));
25397 map.insert("aa_large".to_string(), Value::Bool(ratio >= 3.0));
25398 map.insert("aaa_normal".to_string(), Value::Bool(ratio >= 7.0));
25399 Ok(Value::Map(Rc::new(RefCell::new(map))))
25400 });
25401}
25402
25403fn register_protocol(interp: &mut Interpreter) {
25411 define(interp, "protocol_info", Some(0), |_, _args| {
25413 let mut map = std::collections::HashMap::new();
25414 map.insert(
25415 "http".to_string(),
25416 Value::Map(Rc::new(RefCell::new({
25417 let mut m = std::collections::HashMap::new();
25418 m.insert(
25419 "name".to_string(),
25420 Value::String(Rc::new("HTTP".to_string())),
25421 );
25422 m.insert(
25423 "versions".to_string(),
25424 Value::Array(Rc::new(RefCell::new(vec![
25425 Value::String(Rc::new("1.1".to_string())),
25426 Value::String(Rc::new("2".to_string())),
25427 ]))),
25428 );
25429 m.insert(
25430 "methods".to_string(),
25431 Value::Array(Rc::new(RefCell::new(vec![
25432 Value::String(Rc::new("GET".to_string())),
25433 Value::String(Rc::new("POST".to_string())),
25434 Value::String(Rc::new("PUT".to_string())),
25435 Value::String(Rc::new("DELETE".to_string())),
25436 Value::String(Rc::new("PATCH".to_string())),
25437 Value::String(Rc::new("HEAD".to_string())),
25438 Value::String(Rc::new("OPTIONS".to_string())),
25439 ]))),
25440 );
25441 m
25442 }))),
25443 );
25444 map.insert(
25445 "grpc".to_string(),
25446 Value::Map(Rc::new(RefCell::new({
25447 let mut m = std::collections::HashMap::new();
25448 m.insert(
25449 "name".to_string(),
25450 Value::String(Rc::new("gRPC".to_string())),
25451 );
25452 m.insert(
25453 "streaming_modes".to_string(),
25454 Value::Array(Rc::new(RefCell::new(vec![
25455 Value::String(Rc::new("unary".to_string())),
25456 Value::String(Rc::new("server_streaming".to_string())),
25457 Value::String(Rc::new("client_streaming".to_string())),
25458 Value::String(Rc::new("bidirectional".to_string())),
25459 ]))),
25460 );
25461 m
25462 }))),
25463 );
25464 map.insert(
25465 "websocket".to_string(),
25466 Value::Map(Rc::new(RefCell::new({
25467 let mut m = std::collections::HashMap::new();
25468 m.insert(
25469 "name".to_string(),
25470 Value::String(Rc::new("WebSocket".to_string())),
25471 );
25472 m.insert(
25473 "message_types".to_string(),
25474 Value::Array(Rc::new(RefCell::new(vec![
25475 Value::String(Rc::new("text".to_string())),
25476 Value::String(Rc::new("binary".to_string())),
25477 Value::String(Rc::new("ping".to_string())),
25478 Value::String(Rc::new("pong".to_string())),
25479 Value::String(Rc::new("close".to_string())),
25480 ]))),
25481 );
25482 m
25483 }))),
25484 );
25485 map.insert(
25486 "kafka".to_string(),
25487 Value::Map(Rc::new(RefCell::new({
25488 let mut m = std::collections::HashMap::new();
25489 m.insert(
25490 "name".to_string(),
25491 Value::String(Rc::new("Apache Kafka".to_string())),
25492 );
25493 m.insert(
25494 "acks".to_string(),
25495 Value::Array(Rc::new(RefCell::new(vec![
25496 Value::String(Rc::new("none".to_string())),
25497 Value::String(Rc::new("leader".to_string())),
25498 Value::String(Rc::new("all".to_string())),
25499 ]))),
25500 );
25501 m
25502 }))),
25503 );
25504 map.insert(
25505 "amqp".to_string(),
25506 Value::Map(Rc::new(RefCell::new({
25507 let mut m = std::collections::HashMap::new();
25508 m.insert(
25509 "name".to_string(),
25510 Value::String(Rc::new("AMQP".to_string())),
25511 );
25512 m.insert(
25513 "exchange_types".to_string(),
25514 Value::Array(Rc::new(RefCell::new(vec![
25515 Value::String(Rc::new("direct".to_string())),
25516 Value::String(Rc::new("fanout".to_string())),
25517 Value::String(Rc::new("topic".to_string())),
25518 Value::String(Rc::new("headers".to_string())),
25519 ]))),
25520 );
25521 m
25522 }))),
25523 );
25524 map.insert(
25525 "graphql".to_string(),
25526 Value::Map(Rc::new(RefCell::new({
25527 let mut m = std::collections::HashMap::new();
25528 m.insert(
25529 "name".to_string(),
25530 Value::String(Rc::new("GraphQL".to_string())),
25531 );
25532 m.insert(
25533 "operations".to_string(),
25534 Value::Array(Rc::new(RefCell::new(vec![
25535 Value::String(Rc::new("query".to_string())),
25536 Value::String(Rc::new("mutation".to_string())),
25537 Value::String(Rc::new("subscription".to_string())),
25538 ]))),
25539 );
25540 m
25541 }))),
25542 );
25543 Ok(Value::Map(Rc::new(RefCell::new(map))))
25544 });
25545
25546 define(interp, "http_status_text", Some(1), |_, args| {
25548 let code = match &args[0] {
25549 Value::Int(n) => *n,
25550 _ => {
25551 return Err(RuntimeError::new(
25552 "http_status_text requires integer status code",
25553 ))
25554 }
25555 };
25556 let text = match code {
25557 100 => "Continue",
25558 101 => "Switching Protocols",
25559 200 => "OK",
25560 201 => "Created",
25561 202 => "Accepted",
25562 204 => "No Content",
25563 301 => "Moved Permanently",
25564 302 => "Found",
25565 304 => "Not Modified",
25566 307 => "Temporary Redirect",
25567 308 => "Permanent Redirect",
25568 400 => "Bad Request",
25569 401 => "Unauthorized",
25570 403 => "Forbidden",
25571 404 => "Not Found",
25572 405 => "Method Not Allowed",
25573 409 => "Conflict",
25574 422 => "Unprocessable Entity",
25575 429 => "Too Many Requests",
25576 500 => "Internal Server Error",
25577 502 => "Bad Gateway",
25578 503 => "Service Unavailable",
25579 504 => "Gateway Timeout",
25580 _ => "Unknown",
25581 };
25582 Ok(Value::String(Rc::new(text.to_string())))
25583 });
25584
25585 define(interp, "http_status_type", Some(1), |_, args| {
25587 let code = match &args[0] {
25588 Value::Int(n) => *n,
25589 _ => {
25590 return Err(RuntimeError::new(
25591 "http_status_type requires integer status code",
25592 ))
25593 }
25594 };
25595 let status_type = match code {
25596 100..=199 => "informational",
25597 200..=299 => "success",
25598 300..=399 => "redirect",
25599 400..=499 => "client_error",
25600 500..=599 => "server_error",
25601 _ => "unknown",
25602 };
25603 Ok(Value::String(Rc::new(status_type.to_string())))
25604 });
25605
25606 define(interp, "grpc_status_text", Some(1), |_, args| {
25608 let code = match &args[0] {
25609 Value::Int(n) => *n,
25610 _ => {
25611 return Err(RuntimeError::new(
25612 "grpc_status_text requires integer status code",
25613 ))
25614 }
25615 };
25616 let text = match code {
25617 0 => "OK",
25618 1 => "CANCELLED",
25619 2 => "UNKNOWN",
25620 3 => "INVALID_ARGUMENT",
25621 4 => "DEADLINE_EXCEEDED",
25622 5 => "NOT_FOUND",
25623 6 => "ALREADY_EXISTS",
25624 7 => "PERMISSION_DENIED",
25625 8 => "RESOURCE_EXHAUSTED",
25626 9 => "FAILED_PRECONDITION",
25627 10 => "ABORTED",
25628 11 => "OUT_OF_RANGE",
25629 12 => "UNIMPLEMENTED",
25630 13 => "INTERNAL",
25631 14 => "UNAVAILABLE",
25632 15 => "DATA_LOSS",
25633 16 => "UNAUTHENTICATED",
25634 _ => "UNKNOWN",
25635 };
25636 Ok(Value::String(Rc::new(text.to_string())))
25637 });
25638
25639 define(interp, "url_parse", Some(1), |_, args| {
25641 let url_str = match &args[0] {
25642 Value::String(s) => s.as_str().to_string(),
25643 _ => return Err(RuntimeError::new("url_parse requires string URL")),
25644 };
25645
25646 let mut map = std::collections::HashMap::new();
25648
25649 let (scheme, rest) = if let Some(pos) = url_str.find("://") {
25651 (url_str[..pos].to_string(), &url_str[pos + 3..])
25652 } else {
25653 return Err(RuntimeError::new("Invalid URL: missing scheme"));
25654 };
25655 map.insert("scheme".to_string(), Value::String(Rc::new(scheme)));
25656
25657 let (authority, path_and_rest) = if let Some(pos) = rest.find('/') {
25659 (&rest[..pos], &rest[pos..])
25660 } else {
25661 (rest, "/")
25662 };
25663
25664 let (host, port) = if let Some(pos) = authority.rfind(':') {
25666 if let Ok(p) = authority[pos + 1..].parse::<i64>() {
25667 (authority[..pos].to_string(), Some(p))
25668 } else {
25669 (authority.to_string(), None)
25670 }
25671 } else {
25672 (authority.to_string(), None)
25673 };
25674 map.insert("host".to_string(), Value::String(Rc::new(host)));
25675 map.insert(
25676 "port".to_string(),
25677 port.map(Value::Int).unwrap_or(Value::Null),
25678 );
25679
25680 let (path, query) = if let Some(pos) = path_and_rest.find('?') {
25682 (&path_and_rest[..pos], Some(&path_and_rest[pos + 1..]))
25683 } else {
25684 (path_and_rest, None)
25685 };
25686 map.insert("path".to_string(), Value::String(Rc::new(path.to_string())));
25687 map.insert(
25688 "query".to_string(),
25689 query
25690 .map(|q| Value::String(Rc::new(q.to_string())))
25691 .unwrap_or(Value::Null),
25692 );
25693
25694 Ok(Value::Map(Rc::new(RefCell::new(map))))
25695 });
25696
25697 define(interp, "url_encode", Some(1), |_, args| {
25699 let s = match &args[0] {
25700 Value::String(s) => s.as_str(),
25701 _ => return Err(RuntimeError::new("url_encode requires string")),
25702 };
25703 let mut result = String::new();
25704 for c in s.chars() {
25705 match c {
25706 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '~' => {
25707 result.push(c);
25708 }
25709 ' ' => result.push_str("%20"),
25710 _ => {
25711 for b in c.to_string().as_bytes() {
25712 result.push_str(&format!("%{:02X}", b));
25713 }
25714 }
25715 }
25716 }
25717 Ok(Value::String(Rc::new(result)))
25718 });
25719
25720 define(interp, "url_decode", Some(1), |_, args| {
25722 let s = match &args[0] {
25723 Value::String(s) => s.as_str().to_string(),
25724 _ => return Err(RuntimeError::new("url_decode requires string")),
25725 };
25726 let mut result = Vec::new();
25727 let bytes = s.as_bytes();
25728 let mut i = 0;
25729 while i < bytes.len() {
25730 if bytes[i] == b'%' && i + 2 < bytes.len() {
25731 if let Ok(b) =
25732 u8::from_str_radix(&String::from_utf8_lossy(&bytes[i + 1..i + 3]), 16)
25733 {
25734 result.push(b);
25735 i += 3;
25736 continue;
25737 }
25738 } else if bytes[i] == b'+' {
25739 result.push(b' ');
25740 i += 1;
25741 continue;
25742 }
25743 result.push(bytes[i]);
25744 i += 1;
25745 }
25746 Ok(Value::String(Rc::new(
25747 String::from_utf8_lossy(&result).to_string(),
25748 )))
25749 });
25750
25751 define(interp, "ws_close_code_text", Some(1), |_, args| {
25753 let code = match &args[0] {
25754 Value::Int(n) => *n,
25755 _ => {
25756 return Err(RuntimeError::new(
25757 "ws_close_code_text requires integer code",
25758 ))
25759 }
25760 };
25761 let text = match code {
25762 1000 => "Normal Closure",
25763 1001 => "Going Away",
25764 1002 => "Protocol Error",
25765 1003 => "Unsupported Data",
25766 1005 => "No Status Received",
25767 1006 => "Abnormal Closure",
25768 1007 => "Invalid Payload Data",
25769 1008 => "Policy Violation",
25770 1009 => "Message Too Big",
25771 1010 => "Missing Extension",
25772 1011 => "Internal Error",
25773 1015 => "TLS Handshake Failure",
25774 _ => "Unknown",
25775 };
25776 Ok(Value::String(Rc::new(text.to_string())))
25777 });
25778
25779 define(interp, "mime_type", Some(1), |_, args| {
25781 let ext = match &args[0] {
25782 Value::String(s) => s.as_str().to_lowercase(),
25783 _ => return Err(RuntimeError::new("mime_type requires string extension")),
25784 };
25785 let ext = ext.trim_start_matches('.');
25786 let mime = match ext {
25787 "html" | "htm" => "text/html",
25788 "css" => "text/css",
25789 "js" | "mjs" => "text/javascript",
25790 "json" => "application/json",
25791 "xml" => "application/xml",
25792 "txt" => "text/plain",
25793 "csv" => "text/csv",
25794 "png" => "image/png",
25795 "jpg" | "jpeg" => "image/jpeg",
25796 "gif" => "image/gif",
25797 "svg" => "image/svg+xml",
25798 "webp" => "image/webp",
25799 "ico" => "image/x-icon",
25800 "pdf" => "application/pdf",
25801 "zip" => "application/zip",
25802 "gz" | "gzip" => "application/gzip",
25803 "mp3" => "audio/mpeg",
25804 "mp4" => "video/mp4",
25805 "webm" => "video/webm",
25806 "woff" => "font/woff",
25807 "woff2" => "font/woff2",
25808 "ttf" => "font/ttf",
25809 "otf" => "font/otf",
25810 "wasm" => "application/wasm",
25811 "proto" => "application/protobuf",
25812 _ => "application/octet-stream",
25813 };
25814 Ok(Value::String(Rc::new(mime.to_string())))
25815 });
25816
25817 define(interp, "content_type_parse", Some(1), |_, args| {
25819 let ct = match &args[0] {
25820 Value::String(s) => s.as_str().to_string(),
25821 _ => return Err(RuntimeError::new("content_type_parse requires string")),
25822 };
25823 let mut map = std::collections::HashMap::new();
25824 let parts: Vec<&str> = ct.split(';').collect();
25825 map.insert(
25826 "media_type".to_string(),
25827 Value::String(Rc::new(parts[0].trim().to_string())),
25828 );
25829
25830 let mut params = std::collections::HashMap::new();
25831 for part in parts.iter().skip(1) {
25832 let kv: Vec<&str> = part.splitn(2, '=').collect();
25833 if kv.len() == 2 {
25834 let key = kv[0].trim().to_lowercase();
25835 let value = kv[1].trim().trim_matches('"').to_string();
25836 params.insert(key, Value::String(Rc::new(value)));
25837 }
25838 }
25839 map.insert(
25840 "params".to_string(),
25841 Value::Map(Rc::new(RefCell::new(params))),
25842 );
25843 Ok(Value::Map(Rc::new(RefCell::new(map))))
25844 });
25845}
25846
25847thread_local! {
25859 static TOOL_REGISTRY: RefCell<HashMap<String, ToolDefinition>> = RefCell::new(HashMap::new());
25860 static AGENT_MEMORY: RefCell<HashMap<String, AgentSession>> = RefCell::new(HashMap::new());
25861 static STATE_MACHINES: RefCell<HashMap<String, StateMachine>> = RefCell::new(HashMap::new());
25862}
25863
25864#[derive(Clone)]
25866struct ToolDefinition {
25867 name: String,
25868 description: String,
25869 parameters: Vec<ToolParameter>,
25870 returns: String,
25871 evidence_in: Evidence,
25872 evidence_out: Evidence,
25873 handler: Option<Rc<Function>>,
25874}
25875
25876#[derive(Clone)]
25877struct ToolParameter {
25878 name: String,
25879 param_type: String,
25880 description: String,
25881 required: bool,
25882 evidence: Evidence,
25883}
25884
25885#[derive(Clone)]
25887struct AgentSession {
25888 id: String,
25889 context: HashMap<String, Value>,
25890 history: Vec<(String, String)>, created_at: u64,
25892 last_accessed: u64,
25893}
25894
25895#[derive(Clone)]
25897struct StateMachine {
25898 name: String,
25899 current_state: String,
25900 states: Vec<String>,
25901 transitions: HashMap<String, Vec<(String, String)>>, history: Vec<(String, u64)>, }
25904
25905fn register_agent_tools(interp: &mut Interpreter) {
25910 define(interp, "tool_define", None, |_interp, args| {
25913 if args.len() < 4 {
25914 return Err(RuntimeError::new(
25915 "tool_define requires at least 4 arguments: name, description, params, returns",
25916 ));
25917 }
25918
25919 let name = match &args[0] {
25920 Value::String(s) => s.as_str().to_string(),
25921 _ => return Err(RuntimeError::new("tool name must be a string")),
25922 };
25923
25924 let description = match &args[1] {
25925 Value::String(s) => s.as_str().to_string(),
25926 _ => return Err(RuntimeError::new("tool description must be a string")),
25927 };
25928
25929 let params = match &args[2] {
25931 Value::Array(arr) => {
25932 let arr = arr.borrow();
25933 let mut params = Vec::new();
25934 for param in arr.iter() {
25935 if let Value::Map(map) = param {
25936 let map = map.borrow();
25937 let param_name = map
25938 .get("name")
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_default();
25947 let param_type = map
25948 .get("type")
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_else(|| "any".to_string());
25957 let param_desc = map
25958 .get("description")
25959 .and_then(|v| {
25960 if let Value::String(s) = v {
25961 Some(s.as_str().to_string())
25962 } else {
25963 None
25964 }
25965 })
25966 .unwrap_or_default();
25967 let required = map
25968 .get("required")
25969 .and_then(|v| {
25970 if let Value::Bool(b) = v {
25971 Some(*b)
25972 } else {
25973 None
25974 }
25975 })
25976 .unwrap_or(true);
25977 let evidence_str = map
25978 .get("evidence")
25979 .and_then(|v| {
25980 if let Value::String(s) = v {
25981 Some(s.as_str())
25982 } else {
25983 None
25984 }
25985 })
25986 .unwrap_or("~");
25987 let evidence = match evidence_str {
25988 "!" => Evidence::Known,
25989 "?" => Evidence::Uncertain,
25990 "~" => Evidence::Reported,
25991 "‽" => Evidence::Paradox,
25992 _ => Evidence::Reported,
25993 };
25994 params.push(ToolParameter {
25995 name: param_name,
25996 param_type,
25997 description: param_desc,
25998 required,
25999 evidence,
26000 });
26001 }
26002 }
26003 params
26004 }
26005 _ => Vec::new(),
26006 };
26007
26008 let returns = match &args[3] {
26009 Value::String(s) => s.as_str().to_string(),
26010 _ => "any".to_string(),
26011 };
26012
26013 let handler = if args.len() > 4 {
26014 match &args[4] {
26015 Value::Function(f) => Some(f.clone()),
26016 _ => None,
26017 }
26018 } else {
26019 None
26020 };
26021
26022 let tool = ToolDefinition {
26023 name: name.clone(),
26024 description,
26025 parameters: params,
26026 returns,
26027 evidence_in: Evidence::Reported,
26028 evidence_out: Evidence::Reported,
26029 handler,
26030 };
26031
26032 TOOL_REGISTRY.with(|registry| {
26033 registry.borrow_mut().insert(name.clone(), tool);
26034 });
26035
26036 Ok(Value::String(Rc::new(name)))
26037 });
26038
26039 define(interp, "tool_list", Some(0), |_, _args| {
26041 let tools: Vec<Value> = TOOL_REGISTRY.with(|registry| {
26042 registry
26043 .borrow()
26044 .keys()
26045 .map(|k| Value::String(Rc::new(k.clone())))
26046 .collect()
26047 });
26048 Ok(Value::Array(Rc::new(RefCell::new(tools))))
26049 });
26050
26051 define(interp, "tool_get", Some(1), |_, args| {
26053 let name = match &args[0] {
26054 Value::String(s) => s.as_str().to_string(),
26055 _ => return Err(RuntimeError::new("tool_get requires string name")),
26056 };
26057
26058 TOOL_REGISTRY.with(|registry| {
26059 if let Some(tool) = registry.borrow().get(&name) {
26060 let mut map = HashMap::new();
26061 map.insert(
26062 "name".to_string(),
26063 Value::String(Rc::new(tool.name.clone())),
26064 );
26065 map.insert(
26066 "description".to_string(),
26067 Value::String(Rc::new(tool.description.clone())),
26068 );
26069 map.insert(
26070 "returns".to_string(),
26071 Value::String(Rc::new(tool.returns.clone())),
26072 );
26073
26074 let params: Vec<Value> = tool
26075 .parameters
26076 .iter()
26077 .map(|p| {
26078 let mut pmap = HashMap::new();
26079 pmap.insert("name".to_string(), Value::String(Rc::new(p.name.clone())));
26080 pmap.insert(
26081 "type".to_string(),
26082 Value::String(Rc::new(p.param_type.clone())),
26083 );
26084 pmap.insert(
26085 "description".to_string(),
26086 Value::String(Rc::new(p.description.clone())),
26087 );
26088 pmap.insert("required".to_string(), Value::Bool(p.required));
26089 Value::Map(Rc::new(RefCell::new(pmap)))
26090 })
26091 .collect();
26092 map.insert(
26093 "parameters".to_string(),
26094 Value::Array(Rc::new(RefCell::new(params))),
26095 );
26096
26097 Ok(Value::Map(Rc::new(RefCell::new(map))))
26098 } else {
26099 Ok(Value::Null)
26100 }
26101 })
26102 });
26103
26104 define(interp, "tool_schema", Some(1), |_, args| {
26106 let name = match &args[0] {
26107 Value::String(s) => s.as_str().to_string(),
26108 _ => return Err(RuntimeError::new("tool_schema requires string name")),
26109 };
26110
26111 TOOL_REGISTRY.with(|registry| {
26112 if let Some(tool) = registry.borrow().get(&name) {
26113 let mut schema = HashMap::new();
26115 schema.insert(
26116 "type".to_string(),
26117 Value::String(Rc::new("function".to_string())),
26118 );
26119
26120 let mut function = HashMap::new();
26121 function.insert(
26122 "name".to_string(),
26123 Value::String(Rc::new(tool.name.clone())),
26124 );
26125 function.insert(
26126 "description".to_string(),
26127 Value::String(Rc::new(tool.description.clone())),
26128 );
26129
26130 let mut params_schema = HashMap::new();
26132 params_schema.insert(
26133 "type".to_string(),
26134 Value::String(Rc::new("object".to_string())),
26135 );
26136
26137 let mut properties = HashMap::new();
26138 let mut required: Vec<Value> = Vec::new();
26139
26140 for param in &tool.parameters {
26141 let mut prop = HashMap::new();
26142 let json_type = match param.param_type.as_str() {
26143 "int" | "i64" | "i32" => "integer",
26144 "float" | "f64" | "f32" => "number",
26145 "bool" => "boolean",
26146 "string" | "str" => "string",
26147 "array" | "list" => "array",
26148 "map" | "object" => "object",
26149 _ => "string",
26150 };
26151 prop.insert(
26152 "type".to_string(),
26153 Value::String(Rc::new(json_type.to_string())),
26154 );
26155 prop.insert(
26156 "description".to_string(),
26157 Value::String(Rc::new(param.description.clone())),
26158 );
26159 properties.insert(param.name.clone(), Value::Map(Rc::new(RefCell::new(prop))));
26160
26161 if param.required {
26162 required.push(Value::String(Rc::new(param.name.clone())));
26163 }
26164 }
26165
26166 params_schema.insert(
26167 "properties".to_string(),
26168 Value::Map(Rc::new(RefCell::new(properties))),
26169 );
26170 params_schema.insert(
26171 "required".to_string(),
26172 Value::Array(Rc::new(RefCell::new(required))),
26173 );
26174
26175 function.insert(
26176 "parameters".to_string(),
26177 Value::Map(Rc::new(RefCell::new(params_schema))),
26178 );
26179 schema.insert(
26180 "function".to_string(),
26181 Value::Map(Rc::new(RefCell::new(function))),
26182 );
26183
26184 Ok(Value::Map(Rc::new(RefCell::new(schema))))
26185 } else {
26186 Err(RuntimeError::new(format!("Tool '{}' not found", name)))
26187 }
26188 })
26189 });
26190
26191 define(interp, "tool_schemas_all", Some(0), |_, _args| {
26193 let schemas: Vec<Value> = TOOL_REGISTRY.with(|registry| {
26194 registry
26195 .borrow()
26196 .values()
26197 .map(|tool| {
26198 let mut schema = HashMap::new();
26199 schema.insert(
26200 "type".to_string(),
26201 Value::String(Rc::new("function".to_string())),
26202 );
26203
26204 let mut function = HashMap::new();
26205 function.insert(
26206 "name".to_string(),
26207 Value::String(Rc::new(tool.name.clone())),
26208 );
26209 function.insert(
26210 "description".to_string(),
26211 Value::String(Rc::new(tool.description.clone())),
26212 );
26213
26214 let mut params_schema = HashMap::new();
26215 params_schema.insert(
26216 "type".to_string(),
26217 Value::String(Rc::new("object".to_string())),
26218 );
26219
26220 let mut properties = HashMap::new();
26221 let mut required: Vec<Value> = Vec::new();
26222
26223 for param in &tool.parameters {
26224 let mut prop = HashMap::new();
26225 let json_type = match param.param_type.as_str() {
26226 "int" | "i64" | "i32" => "integer",
26227 "float" | "f64" | "f32" => "number",
26228 "bool" => "boolean",
26229 _ => "string",
26230 };
26231 prop.insert(
26232 "type".to_string(),
26233 Value::String(Rc::new(json_type.to_string())),
26234 );
26235 prop.insert(
26236 "description".to_string(),
26237 Value::String(Rc::new(param.description.clone())),
26238 );
26239 properties
26240 .insert(param.name.clone(), Value::Map(Rc::new(RefCell::new(prop))));
26241 if param.required {
26242 required.push(Value::String(Rc::new(param.name.clone())));
26243 }
26244 }
26245
26246 params_schema.insert(
26247 "properties".to_string(),
26248 Value::Map(Rc::new(RefCell::new(properties))),
26249 );
26250 params_schema.insert(
26251 "required".to_string(),
26252 Value::Array(Rc::new(RefCell::new(required))),
26253 );
26254 function.insert(
26255 "parameters".to_string(),
26256 Value::Map(Rc::new(RefCell::new(params_schema))),
26257 );
26258 schema.insert(
26259 "function".to_string(),
26260 Value::Map(Rc::new(RefCell::new(function))),
26261 );
26262
26263 Value::Map(Rc::new(RefCell::new(schema)))
26264 })
26265 .collect()
26266 });
26267 Ok(Value::Array(Rc::new(RefCell::new(schemas))))
26268 });
26269
26270 define(interp, "tool_call", None, |interp, args| {
26272 if args.is_empty() {
26273 return Err(RuntimeError::new("tool_call requires at least tool name"));
26274 }
26275
26276 let name = match &args[0] {
26277 Value::String(s) => s.as_str().to_string(),
26278 _ => {
26279 return Err(RuntimeError::new(
26280 "tool_call first argument must be tool name",
26281 ))
26282 }
26283 };
26284
26285 let tool_args: Vec<Value> = args.into_iter().skip(1).collect();
26286
26287 TOOL_REGISTRY.with(|registry| {
26288 if let Some(tool) = registry.borrow().get(&name) {
26289 if let Some(handler) = &tool.handler {
26290 let result = interp.call_function(handler.as_ref(), tool_args)?;
26292 Ok(Value::Evidential {
26294 value: Box::new(result),
26295 evidence: Evidence::Reported,
26296 })
26297 } else {
26298 Err(RuntimeError::new(format!("Tool '{}' has no handler", name)))
26299 }
26300 } else {
26301 Err(RuntimeError::new(format!("Tool '{}' not found", name)))
26302 }
26303 })
26304 });
26305
26306 define(interp, "tool_remove", Some(1), |_, args| {
26308 let name = match &args[0] {
26309 Value::String(s) => s.as_str().to_string(),
26310 _ => return Err(RuntimeError::new("tool_remove requires string name")),
26311 };
26312
26313 TOOL_REGISTRY.with(|registry| {
26314 let removed = registry.borrow_mut().remove(&name).is_some();
26315 Ok(Value::Bool(removed))
26316 })
26317 });
26318
26319 define(interp, "tool_clear", Some(0), |_, _args| {
26321 TOOL_REGISTRY.with(|registry| {
26322 registry.borrow_mut().clear();
26323 });
26324 Ok(Value::Null)
26325 });
26326}
26327
26328fn register_agent_llm(interp: &mut Interpreter) {
26333 define(interp, "llm_message", Some(2), |_, args| {
26335 let role = match &args[0] {
26336 Value::String(s) => s.as_str().to_string(),
26337 _ => return Err(RuntimeError::new("llm_message role must be string")),
26338 };
26339 let content = match &args[1] {
26340 Value::String(s) => s.as_str().to_string(),
26341 _ => return Err(RuntimeError::new("llm_message content must be string")),
26342 };
26343
26344 let mut map = HashMap::new();
26345 map.insert("role".to_string(), Value::String(Rc::new(role)));
26346 map.insert("content".to_string(), Value::String(Rc::new(content)));
26347 Ok(Value::Map(Rc::new(RefCell::new(map))))
26348 });
26349
26350 define(interp, "llm_messages", None, |_, args| {
26352 let messages: Vec<Value> = args.into_iter().collect();
26353 Ok(Value::Array(Rc::new(RefCell::new(messages))))
26354 });
26355
26356 define(interp, "llm_request", None, |_, args| {
26358 if args.is_empty() {
26359 return Err(RuntimeError::new("llm_request requires provider"));
26360 }
26361
26362 let provider = match &args[0] {
26363 Value::String(s) => s.as_str().to_string(),
26364 _ => return Err(RuntimeError::new("provider must be string")),
26365 };
26366
26367 let mut request = HashMap::new();
26368 request.insert(
26369 "provider".to_string(),
26370 Value::String(Rc::new(provider.clone())),
26371 );
26372
26373 let default_model = match provider.as_str() {
26375 "openai" => "gpt-4-turbo-preview",
26376 "anthropic" | "claude" => "claude-3-opus-20240229",
26377 "google" | "gemini" => "gemini-pro",
26378 "mistral" => "mistral-large-latest",
26379 "ollama" | "local" => "llama2",
26380 _ => "gpt-4",
26381 };
26382 request.insert(
26383 "model".to_string(),
26384 Value::String(Rc::new(default_model.to_string())),
26385 );
26386
26387 for (i, arg) in args.iter().enumerate().skip(1) {
26389 if let Value::Map(map) = arg {
26390 let map = map.borrow();
26391 for (k, v) in map.iter() {
26392 request.insert(k.clone(), v.clone());
26393 }
26394 } else if i == 1 {
26395 if let Value::String(s) = arg {
26397 request.insert("model".to_string(), Value::String(s.clone()));
26398 }
26399 }
26400 }
26401
26402 if !request.contains_key("temperature") {
26404 request.insert("temperature".to_string(), Value::Float(0.7));
26405 }
26406 if !request.contains_key("max_tokens") {
26407 request.insert("max_tokens".to_string(), Value::Int(4096));
26408 }
26409
26410 Ok(Value::Map(Rc::new(RefCell::new(request))))
26411 });
26412
26413 define(interp, "llm_with_tools", Some(2), |_, args| {
26415 let request = match &args[0] {
26416 Value::Map(m) => m.clone(),
26417 _ => {
26418 return Err(RuntimeError::new(
26419 "llm_with_tools first arg must be request map",
26420 ))
26421 }
26422 };
26423
26424 let tools = match &args[1] {
26425 Value::Array(arr) => arr.clone(),
26426 _ => {
26427 return Err(RuntimeError::new(
26428 "llm_with_tools second arg must be tools array",
26429 ))
26430 }
26431 };
26432
26433 request
26434 .borrow_mut()
26435 .insert("tools".to_string(), Value::Array(tools));
26436 request.borrow_mut().insert(
26437 "tool_choice".to_string(),
26438 Value::String(Rc::new("auto".to_string())),
26439 );
26440
26441 Ok(Value::Map(request))
26442 });
26443
26444 define(interp, "llm_with_system", Some(2), |_, args| {
26446 let request = match &args[0] {
26447 Value::Map(m) => m.clone(),
26448 _ => {
26449 return Err(RuntimeError::new(
26450 "llm_with_system first arg must be request map",
26451 ))
26452 }
26453 };
26454
26455 let system = match &args[1] {
26456 Value::String(s) => s.clone(),
26457 _ => {
26458 return Err(RuntimeError::new(
26459 "llm_with_system second arg must be string",
26460 ))
26461 }
26462 };
26463
26464 request
26465 .borrow_mut()
26466 .insert("system".to_string(), Value::String(system));
26467 Ok(Value::Map(request))
26468 });
26469
26470 define(interp, "llm_with_messages", Some(2), |_, args| {
26472 let request = match &args[0] {
26473 Value::Map(m) => m.clone(),
26474 _ => {
26475 return Err(RuntimeError::new(
26476 "llm_with_messages first arg must be request map",
26477 ))
26478 }
26479 };
26480
26481 let messages = match &args[1] {
26482 Value::Array(arr) => arr.clone(),
26483 _ => {
26484 return Err(RuntimeError::new(
26485 "llm_with_messages second arg must be messages array",
26486 ))
26487 }
26488 };
26489
26490 request
26491 .borrow_mut()
26492 .insert("messages".to_string(), Value::Array(messages));
26493 Ok(Value::Map(request))
26494 });
26495
26496 define(interp, "llm_send", Some(1), |_, args| {
26499 let request = match &args[0] {
26500 Value::Map(m) => m.borrow().clone(),
26501 _ => return Err(RuntimeError::new("llm_send requires request map")),
26502 };
26503
26504 let provider = request
26505 .get("provider")
26506 .and_then(|v| {
26507 if let Value::String(s) = v {
26508 Some(s.as_str().to_string())
26509 } else {
26510 None
26511 }
26512 })
26513 .unwrap_or_else(|| "unknown".to_string());
26514
26515 let model = request
26516 .get("model")
26517 .and_then(|v| {
26518 if let Value::String(s) = v {
26519 Some(s.as_str().to_string())
26520 } else {
26521 None
26522 }
26523 })
26524 .unwrap_or_else(|| "unknown".to_string());
26525
26526 let mut response = HashMap::new();
26528 response.insert(
26529 "id".to_string(),
26530 Value::String(Rc::new(format!("msg_{}", Uuid::new_v4()))),
26531 );
26532 response.insert("provider".to_string(), Value::String(Rc::new(provider)));
26533 response.insert("model".to_string(), Value::String(Rc::new(model)));
26534 response.insert(
26535 "created".to_string(),
26536 Value::Int(
26537 std::time::SystemTime::now()
26538 .duration_since(std::time::UNIX_EPOCH)
26539 .unwrap_or_default()
26540 .as_secs() as i64,
26541 ),
26542 );
26543
26544 if request.contains_key("tools") {
26546 response.insert(
26547 "type".to_string(),
26548 Value::String(Rc::new("tool_use".to_string())),
26549 );
26550 response.insert(
26551 "tool_name".to_string(),
26552 Value::String(Rc::new("pending".to_string())),
26553 );
26554 response.insert(
26555 "tool_input".to_string(),
26556 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
26557 );
26558 } else {
26559 response.insert(
26560 "type".to_string(),
26561 Value::String(Rc::new("text".to_string())),
26562 );
26563 response.insert(
26564 "content".to_string(),
26565 Value::String(Rc::new(
26566 "[LLM Response - Connect to actual API for real responses]".to_string(),
26567 )),
26568 );
26569 }
26570
26571 let mut usage = HashMap::new();
26573 usage.insert("input_tokens".to_string(), Value::Int(0));
26574 usage.insert("output_tokens".to_string(), Value::Int(0));
26575 response.insert(
26576 "usage".to_string(),
26577 Value::Map(Rc::new(RefCell::new(usage))),
26578 );
26579
26580 Ok(Value::Evidential {
26582 value: Box::new(Value::Map(Rc::new(RefCell::new(response)))),
26583 evidence: Evidence::Reported,
26584 })
26585 });
26586
26587 define(interp, "llm_parse_tool_call", Some(1), |_, args| {
26589 let response = match &args[0] {
26590 Value::Map(m) => m.borrow().clone(),
26591 Value::Evidential { value, .. } => {
26592 if let Value::Map(m) = value.as_ref() {
26593 m.borrow().clone()
26594 } else {
26595 return Err(RuntimeError::new(
26596 "llm_parse_tool_call requires map response",
26597 ));
26598 }
26599 }
26600 _ => {
26601 return Err(RuntimeError::new(
26602 "llm_parse_tool_call requires response map",
26603 ))
26604 }
26605 };
26606
26607 let resp_type = response
26608 .get("type")
26609 .and_then(|v| {
26610 if let Value::String(s) = v {
26611 Some(s.as_str().to_string())
26612 } else {
26613 None
26614 }
26615 })
26616 .unwrap_or_default();
26617
26618 if resp_type == "tool_use" {
26619 let tool_name = response
26620 .get("tool_name")
26621 .and_then(|v| {
26622 if let Value::String(s) = v {
26623 Some(s.as_str().to_string())
26624 } else {
26625 None
26626 }
26627 })
26628 .unwrap_or_default();
26629
26630 let tool_input = response.get("tool_input").cloned().unwrap_or(Value::Null);
26631
26632 let mut result = HashMap::new();
26633 result.insert("is_tool_call".to_string(), Value::Bool(true));
26634 result.insert("tool_name".to_string(), Value::String(Rc::new(tool_name)));
26635 result.insert("tool_input".to_string(), tool_input);
26636 Ok(Value::Map(Rc::new(RefCell::new(result))))
26637 } else {
26638 let mut result = HashMap::new();
26639 result.insert("is_tool_call".to_string(), Value::Bool(false));
26640 result.insert(
26641 "content".to_string(),
26642 response.get("content").cloned().unwrap_or(Value::Null),
26643 );
26644 Ok(Value::Map(Rc::new(RefCell::new(result))))
26645 }
26646 });
26647
26648 define(interp, "llm_extract", Some(2), |_, args| {
26650 let _response = match &args[0] {
26651 Value::Map(m) => m.borrow().clone(),
26652 Value::Evidential { value, .. } => {
26653 if let Value::Map(m) = value.as_ref() {
26654 m.borrow().clone()
26655 } else {
26656 return Err(RuntimeError::new("llm_extract requires response"));
26657 }
26658 }
26659 _ => return Err(RuntimeError::new("llm_extract requires response")),
26660 };
26661
26662 let _schema = match &args[1] {
26663 Value::Map(m) => m.borrow().clone(),
26664 _ => return Err(RuntimeError::new("llm_extract requires schema map")),
26665 };
26666
26667 let mut result = HashMap::new();
26670 result.insert("success".to_string(), Value::Bool(true));
26671 result.insert("data".to_string(), Value::Null);
26672 result.insert(
26673 "errors".to_string(),
26674 Value::Array(Rc::new(RefCell::new(Vec::new()))),
26675 );
26676
26677 Ok(Value::Evidential {
26678 value: Box::new(Value::Map(Rc::new(RefCell::new(result)))),
26679 evidence: Evidence::Uncertain,
26680 })
26681 });
26682
26683 define(interp, "prompt_template", Some(1), |_, args| {
26685 let template = match &args[0] {
26686 Value::String(s) => s.as_str().to_string(),
26687 _ => return Err(RuntimeError::new("prompt_template requires string")),
26688 };
26689
26690 let mut variables = Vec::new();
26692 let mut in_var = false;
26693 let mut var_name = String::new();
26694
26695 for c in template.chars() {
26696 match c {
26697 '{' if !in_var => {
26698 in_var = true;
26699 var_name.clear();
26700 }
26701 '}' if in_var => {
26702 if !var_name.is_empty() {
26703 variables.push(Value::String(Rc::new(var_name.clone())));
26704 }
26705 in_var = false;
26706 }
26707 _ if in_var => {
26708 var_name.push(c);
26709 }
26710 _ => {}
26711 }
26712 }
26713
26714 let mut result = HashMap::new();
26715 result.insert("template".to_string(), Value::String(Rc::new(template)));
26716 result.insert(
26717 "variables".to_string(),
26718 Value::Array(Rc::new(RefCell::new(variables))),
26719 );
26720 Ok(Value::Map(Rc::new(RefCell::new(result))))
26721 });
26722
26723 define(interp, "prompt_render", Some(2), |_, args| {
26725 let template_obj = match &args[0] {
26726 Value::Map(m) => m.borrow().clone(),
26727 Value::String(s) => {
26728 let mut m = HashMap::new();
26729 m.insert("template".to_string(), Value::String(s.clone()));
26730 m
26731 }
26732 _ => return Err(RuntimeError::new("prompt_render requires template")),
26733 };
26734
26735 let values = match &args[1] {
26736 Value::Map(m) => m.borrow().clone(),
26737 _ => return Err(RuntimeError::new("prompt_render requires values map")),
26738 };
26739
26740 let template = template_obj
26741 .get("template")
26742 .and_then(|v| {
26743 if let Value::String(s) = v {
26744 Some(s.as_str().to_string())
26745 } else {
26746 None
26747 }
26748 })
26749 .unwrap_or_default();
26750
26751 let mut result = template;
26752 for (key, value) in values.iter() {
26753 let value_str = match value {
26754 Value::String(s) => s.as_str().to_string(),
26755 Value::Int(n) => n.to_string(),
26756 Value::Float(f) => f.to_string(),
26757 Value::Bool(b) => b.to_string(),
26758 _ => format!("{}", value),
26759 };
26760 result = result.replace(&format!("{{{}}}", key), &value_str);
26761 }
26762
26763 Ok(Value::String(Rc::new(result)))
26764 });
26765}
26766
26767fn register_agent_memory(interp: &mut Interpreter) {
26772 define(interp, "memory_session", Some(1), |_, args| {
26774 let session_id = match &args[0] {
26775 Value::String(s) => s.as_str().to_string(),
26776 _ => return Err(RuntimeError::new("memory_session requires string id")),
26777 };
26778
26779 let now = std::time::SystemTime::now()
26780 .duration_since(std::time::UNIX_EPOCH)
26781 .unwrap_or_default()
26782 .as_secs();
26783
26784 AGENT_MEMORY.with(|memory| {
26785 let mut mem = memory.borrow_mut();
26786 if !mem.contains_key(&session_id) {
26787 mem.insert(
26788 session_id.clone(),
26789 AgentSession {
26790 id: session_id.clone(),
26791 context: HashMap::new(),
26792 history: Vec::new(),
26793 created_at: now,
26794 last_accessed: now,
26795 },
26796 );
26797 } else if let Some(session) = mem.get_mut(&session_id) {
26798 session.last_accessed = now;
26799 }
26800 });
26801
26802 let mut result = HashMap::new();
26803 result.insert("id".to_string(), Value::String(Rc::new(session_id)));
26804 result.insert("created_at".to_string(), Value::Int(now as i64));
26805 Ok(Value::Map(Rc::new(RefCell::new(result))))
26806 });
26807
26808 define(interp, "memory_set", Some(3), |_, args| {
26810 let session_id = match &args[0] {
26811 Value::String(s) => s.as_str().to_string(),
26812 Value::Map(m) => m
26813 .borrow()
26814 .get("id")
26815 .and_then(|v| {
26816 if let Value::String(s) = v {
26817 Some(s.as_str().to_string())
26818 } else {
26819 None
26820 }
26821 })
26822 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26823 _ => return Err(RuntimeError::new("memory_set requires session")),
26824 };
26825
26826 let key = match &args[1] {
26827 Value::String(s) => s.as_str().to_string(),
26828 _ => return Err(RuntimeError::new("memory_set key must be string")),
26829 };
26830
26831 let value = args[2].clone();
26832
26833 AGENT_MEMORY.with(|memory| {
26834 if let Some(session) = memory.borrow_mut().get_mut(&session_id) {
26835 session.context.insert(key, value);
26836 session.last_accessed = std::time::SystemTime::now()
26837 .duration_since(std::time::UNIX_EPOCH)
26838 .unwrap_or_default()
26839 .as_secs();
26840 Ok(Value::Bool(true))
26841 } else {
26842 Err(RuntimeError::new(format!(
26843 "Session '{}' not found",
26844 session_id
26845 )))
26846 }
26847 })
26848 });
26849
26850 define(interp, "memory_get", Some(2), |_, args| {
26852 let session_id = match &args[0] {
26853 Value::String(s) => s.as_str().to_string(),
26854 Value::Map(m) => m
26855 .borrow()
26856 .get("id")
26857 .and_then(|v| {
26858 if let Value::String(s) = v {
26859 Some(s.as_str().to_string())
26860 } else {
26861 None
26862 }
26863 })
26864 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26865 _ => return Err(RuntimeError::new("memory_get requires session")),
26866 };
26867
26868 let key = match &args[1] {
26869 Value::String(s) => s.as_str().to_string(),
26870 _ => return Err(RuntimeError::new("memory_get key must be string")),
26871 };
26872
26873 AGENT_MEMORY.with(|memory| {
26874 if let Some(session) = memory.borrow().get(&session_id) {
26875 Ok(session.context.get(&key).cloned().unwrap_or(Value::Null))
26876 } else {
26877 Ok(Value::Null)
26878 }
26879 })
26880 });
26881
26882 define(interp, "memory_history_add", Some(3), |_, args| {
26884 let session_id = match &args[0] {
26885 Value::String(s) => s.as_str().to_string(),
26886 Value::Map(m) => m
26887 .borrow()
26888 .get("id")
26889 .and_then(|v| {
26890 if let Value::String(s) = v {
26891 Some(s.as_str().to_string())
26892 } else {
26893 None
26894 }
26895 })
26896 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26897 _ => return Err(RuntimeError::new("memory_history_add requires session")),
26898 };
26899
26900 let role = match &args[1] {
26901 Value::String(s) => s.as_str().to_string(),
26902 _ => return Err(RuntimeError::new("role must be string")),
26903 };
26904
26905 let content = match &args[2] {
26906 Value::String(s) => s.as_str().to_string(),
26907 _ => return Err(RuntimeError::new("content must be string")),
26908 };
26909
26910 AGENT_MEMORY.with(|memory| {
26911 if let Some(session) = memory.borrow_mut().get_mut(&session_id) {
26912 session.history.push((role, content));
26913 session.last_accessed = std::time::SystemTime::now()
26914 .duration_since(std::time::UNIX_EPOCH)
26915 .unwrap_or_default()
26916 .as_secs();
26917 Ok(Value::Int(session.history.len() as i64))
26918 } else {
26919 Err(RuntimeError::new(format!(
26920 "Session '{}' not found",
26921 session_id
26922 )))
26923 }
26924 })
26925 });
26926
26927 define(interp, "memory_history_get", None, |_, args| {
26929 if args.is_empty() {
26930 return Err(RuntimeError::new("memory_history_get requires session"));
26931 }
26932
26933 let session_id = match &args[0] {
26934 Value::String(s) => s.as_str().to_string(),
26935 Value::Map(m) => m
26936 .borrow()
26937 .get("id")
26938 .and_then(|v| {
26939 if let Value::String(s) = v {
26940 Some(s.as_str().to_string())
26941 } else {
26942 None
26943 }
26944 })
26945 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26946 _ => return Err(RuntimeError::new("memory_history_get requires session")),
26947 };
26948
26949 let limit = if args.len() > 1 {
26950 match &args[1] {
26951 Value::Int(n) => Some(*n as usize),
26952 _ => None,
26953 }
26954 } else {
26955 None
26956 };
26957
26958 AGENT_MEMORY.with(|memory| {
26959 if let Some(session) = memory.borrow().get(&session_id) {
26960 let history: Vec<Value> = session
26961 .history
26962 .iter()
26963 .rev()
26964 .take(limit.unwrap_or(usize::MAX))
26965 .rev()
26966 .map(|(role, content)| {
26967 let mut msg = HashMap::new();
26968 msg.insert("role".to_string(), Value::String(Rc::new(role.clone())));
26969 msg.insert(
26970 "content".to_string(),
26971 Value::String(Rc::new(content.clone())),
26972 );
26973 Value::Map(Rc::new(RefCell::new(msg)))
26974 })
26975 .collect();
26976 Ok(Value::Array(Rc::new(RefCell::new(history))))
26977 } else {
26978 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
26979 }
26980 })
26981 });
26982
26983 define(interp, "memory_context_all", Some(1), |_, args| {
26985 let session_id = match &args[0] {
26986 Value::String(s) => s.as_str().to_string(),
26987 Value::Map(m) => m
26988 .borrow()
26989 .get("id")
26990 .and_then(|v| {
26991 if let Value::String(s) = v {
26992 Some(s.as_str().to_string())
26993 } else {
26994 None
26995 }
26996 })
26997 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
26998 _ => return Err(RuntimeError::new("memory_context_all requires session")),
26999 };
27000
27001 AGENT_MEMORY.with(|memory| {
27002 if let Some(session) = memory.borrow().get(&session_id) {
27003 let context: HashMap<String, Value> = session.context.clone();
27004 Ok(Value::Map(Rc::new(RefCell::new(context))))
27005 } else {
27006 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
27007 }
27008 })
27009 });
27010
27011 define(interp, "memory_clear", Some(1), |_, args| {
27013 let session_id = match &args[0] {
27014 Value::String(s) => s.as_str().to_string(),
27015 Value::Map(m) => m
27016 .borrow()
27017 .get("id")
27018 .and_then(|v| {
27019 if let Value::String(s) = v {
27020 Some(s.as_str().to_string())
27021 } else {
27022 None
27023 }
27024 })
27025 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
27026 _ => return Err(RuntimeError::new("memory_clear requires session")),
27027 };
27028
27029 AGENT_MEMORY.with(|memory| {
27030 let removed = memory.borrow_mut().remove(&session_id).is_some();
27031 Ok(Value::Bool(removed))
27032 })
27033 });
27034
27035 define(interp, "memory_sessions_list", Some(0), |_, _args| {
27037 let sessions: Vec<Value> = AGENT_MEMORY.with(|memory| {
27038 memory
27039 .borrow()
27040 .values()
27041 .map(|session| {
27042 let mut info = HashMap::new();
27043 info.insert("id".to_string(), Value::String(Rc::new(session.id.clone())));
27044 info.insert(
27045 "created_at".to_string(),
27046 Value::Int(session.created_at as i64),
27047 );
27048 info.insert(
27049 "last_accessed".to_string(),
27050 Value::Int(session.last_accessed as i64),
27051 );
27052 info.insert(
27053 "context_keys".to_string(),
27054 Value::Int(session.context.len() as i64),
27055 );
27056 info.insert(
27057 "history_length".to_string(),
27058 Value::Int(session.history.len() as i64),
27059 );
27060 Value::Map(Rc::new(RefCell::new(info)))
27061 })
27062 .collect()
27063 });
27064 Ok(Value::Array(Rc::new(RefCell::new(sessions))))
27065 });
27066}
27067
27068fn register_agent_planning(interp: &mut Interpreter) {
27073 define(interp, "plan_state_machine", Some(2), |_, args| {
27075 let name = match &args[0] {
27076 Value::String(s) => s.as_str().to_string(),
27077 _ => return Err(RuntimeError::new("plan_state_machine name must be string")),
27078 };
27079
27080 let states = match &args[1] {
27081 Value::Array(arr) => arr
27082 .borrow()
27083 .iter()
27084 .filter_map(|v| {
27085 if let Value::String(s) = v {
27086 Some(s.as_str().to_string())
27087 } else {
27088 None
27089 }
27090 })
27091 .collect::<Vec<_>>(),
27092 _ => return Err(RuntimeError::new("plan_state_machine states must be array")),
27093 };
27094
27095 if states.is_empty() {
27096 return Err(RuntimeError::new(
27097 "State machine must have at least one state",
27098 ));
27099 }
27100
27101 let initial_state = states[0].clone();
27102 let now = std::time::SystemTime::now()
27103 .duration_since(std::time::UNIX_EPOCH)
27104 .unwrap_or_default()
27105 .as_secs();
27106
27107 let machine = StateMachine {
27108 name: name.clone(),
27109 current_state: initial_state.clone(),
27110 states,
27111 transitions: HashMap::new(),
27112 history: vec![(initial_state, now)],
27113 };
27114
27115 STATE_MACHINES.with(|machines| {
27116 machines.borrow_mut().insert(name.clone(), machine);
27117 });
27118
27119 let mut result = HashMap::new();
27120 result.insert("name".to_string(), Value::String(Rc::new(name)));
27121 result.insert(
27122 "type".to_string(),
27123 Value::String(Rc::new("state_machine".to_string())),
27124 );
27125 Ok(Value::Map(Rc::new(RefCell::new(result))))
27126 });
27127
27128 define(interp, "plan_add_transition", Some(3), |_, args| {
27130 let machine_name = match &args[0] {
27131 Value::String(s) => s.as_str().to_string(),
27132 Value::Map(m) => m
27133 .borrow()
27134 .get("name")
27135 .and_then(|v| {
27136 if let Value::String(s) = v {
27137 Some(s.as_str().to_string())
27138 } else {
27139 None
27140 }
27141 })
27142 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27143 _ => return Err(RuntimeError::new("plan_add_transition requires machine")),
27144 };
27145
27146 let from_state = match &args[1] {
27147 Value::String(s) => s.as_str().to_string(),
27148 _ => return Err(RuntimeError::new("from_state must be string")),
27149 };
27150
27151 let to_state = match &args[2] {
27152 Value::String(s) => s.as_str().to_string(),
27153 _ => return Err(RuntimeError::new("to_state must be string")),
27154 };
27155
27156 STATE_MACHINES.with(|machines| {
27157 if let Some(machine) = machines.borrow_mut().get_mut(&machine_name) {
27158 if !machine.states.contains(&from_state) {
27160 return Err(RuntimeError::new(format!(
27161 "State '{}' not in machine",
27162 from_state
27163 )));
27164 }
27165 if !machine.states.contains(&to_state) {
27166 return Err(RuntimeError::new(format!(
27167 "State '{}' not in machine",
27168 to_state
27169 )));
27170 }
27171
27172 machine
27173 .transitions
27174 .entry(from_state)
27175 .or_insert_with(Vec::new)
27176 .push((to_state, "".to_string()));
27177
27178 Ok(Value::Bool(true))
27179 } else {
27180 Err(RuntimeError::new(format!(
27181 "State machine '{}' not found",
27182 machine_name
27183 )))
27184 }
27185 })
27186 });
27187
27188 define(interp, "plan_current_state", Some(1), |_, args| {
27190 let machine_name = match &args[0] {
27191 Value::String(s) => s.as_str().to_string(),
27192 Value::Map(m) => m
27193 .borrow()
27194 .get("name")
27195 .and_then(|v| {
27196 if let Value::String(s) = v {
27197 Some(s.as_str().to_string())
27198 } else {
27199 None
27200 }
27201 })
27202 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27203 _ => return Err(RuntimeError::new("plan_current_state requires machine")),
27204 };
27205
27206 STATE_MACHINES.with(|machines| {
27207 if let Some(machine) = machines.borrow().get(&machine_name) {
27208 Ok(Value::String(Rc::new(machine.current_state.clone())))
27209 } else {
27210 Err(RuntimeError::new(format!(
27211 "State machine '{}' not found",
27212 machine_name
27213 )))
27214 }
27215 })
27216 });
27217
27218 define(interp, "plan_transition", Some(2), |_, args| {
27220 let machine_name = match &args[0] {
27221 Value::String(s) => s.as_str().to_string(),
27222 Value::Map(m) => m
27223 .borrow()
27224 .get("name")
27225 .and_then(|v| {
27226 if let Value::String(s) = v {
27227 Some(s.as_str().to_string())
27228 } else {
27229 None
27230 }
27231 })
27232 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27233 _ => return Err(RuntimeError::new("plan_transition requires machine")),
27234 };
27235
27236 let to_state = match &args[1] {
27237 Value::String(s) => s.as_str().to_string(),
27238 _ => return Err(RuntimeError::new("to_state must be string")),
27239 };
27240
27241 STATE_MACHINES.with(|machines| {
27242 if let Some(machine) = machines.borrow_mut().get_mut(&machine_name) {
27243 let current = machine.current_state.clone();
27244
27245 let valid = machine
27247 .transitions
27248 .get(¤t)
27249 .map(|transitions| transitions.iter().any(|(to, _)| to == &to_state))
27250 .unwrap_or(false);
27251
27252 if valid || machine.states.contains(&to_state) {
27253 let now = std::time::SystemTime::now()
27254 .duration_since(std::time::UNIX_EPOCH)
27255 .unwrap_or_default()
27256 .as_secs();
27257
27258 machine.current_state = to_state.clone();
27259 machine.history.push((to_state.clone(), now));
27260
27261 let mut result = HashMap::new();
27262 result.insert("success".to_string(), Value::Bool(true));
27263 result.insert("from".to_string(), Value::String(Rc::new(current)));
27264 result.insert("to".to_string(), Value::String(Rc::new(to_state)));
27265 Ok(Value::Map(Rc::new(RefCell::new(result))))
27266 } else {
27267 let mut result = HashMap::new();
27268 result.insert("success".to_string(), Value::Bool(false));
27269 result.insert(
27270 "error".to_string(),
27271 Value::String(Rc::new(format!(
27272 "No valid transition from '{}' to '{}'",
27273 current, to_state
27274 ))),
27275 );
27276 Ok(Value::Map(Rc::new(RefCell::new(result))))
27277 }
27278 } else {
27279 Err(RuntimeError::new(format!(
27280 "State machine '{}' not found",
27281 machine_name
27282 )))
27283 }
27284 })
27285 });
27286
27287 define(interp, "plan_can_transition", Some(2), |_, args| {
27289 let machine_name = match &args[0] {
27290 Value::String(s) => s.as_str().to_string(),
27291 Value::Map(m) => m
27292 .borrow()
27293 .get("name")
27294 .and_then(|v| {
27295 if let Value::String(s) = v {
27296 Some(s.as_str().to_string())
27297 } else {
27298 None
27299 }
27300 })
27301 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27302 _ => return Err(RuntimeError::new("plan_can_transition requires machine")),
27303 };
27304
27305 let to_state = match &args[1] {
27306 Value::String(s) => s.as_str().to_string(),
27307 _ => return Err(RuntimeError::new("to_state must be string")),
27308 };
27309
27310 STATE_MACHINES.with(|machines| {
27311 if let Some(machine) = machines.borrow().get(&machine_name) {
27312 let current = &machine.current_state;
27313 let can = machine
27314 .transitions
27315 .get(current)
27316 .map(|transitions| transitions.iter().any(|(to, _)| to == &to_state))
27317 .unwrap_or(false);
27318 Ok(Value::Bool(can))
27319 } else {
27320 Ok(Value::Bool(false))
27321 }
27322 })
27323 });
27324
27325 define(interp, "plan_available_transitions", Some(1), |_, args| {
27327 let machine_name = match &args[0] {
27328 Value::String(s) => s.as_str().to_string(),
27329 Value::Map(m) => m
27330 .borrow()
27331 .get("name")
27332 .and_then(|v| {
27333 if let Value::String(s) = v {
27334 Some(s.as_str().to_string())
27335 } else {
27336 None
27337 }
27338 })
27339 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27340 _ => {
27341 return Err(RuntimeError::new(
27342 "plan_available_transitions requires machine",
27343 ))
27344 }
27345 };
27346
27347 STATE_MACHINES.with(|machines| {
27348 if let Some(machine) = machines.borrow().get(&machine_name) {
27349 let current = &machine.current_state;
27350 let available: Vec<Value> = machine
27351 .transitions
27352 .get(current)
27353 .map(|transitions| {
27354 transitions
27355 .iter()
27356 .map(|(to, _)| Value::String(Rc::new(to.clone())))
27357 .collect()
27358 })
27359 .unwrap_or_default();
27360 Ok(Value::Array(Rc::new(RefCell::new(available))))
27361 } else {
27362 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
27363 }
27364 })
27365 });
27366
27367 define(interp, "plan_history", Some(1), |_, args| {
27369 let machine_name = match &args[0] {
27370 Value::String(s) => s.as_str().to_string(),
27371 Value::Map(m) => m
27372 .borrow()
27373 .get("name")
27374 .and_then(|v| {
27375 if let Value::String(s) = v {
27376 Some(s.as_str().to_string())
27377 } else {
27378 None
27379 }
27380 })
27381 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27382 _ => return Err(RuntimeError::new("plan_history requires machine")),
27383 };
27384
27385 STATE_MACHINES.with(|machines| {
27386 if let Some(machine) = machines.borrow().get(&machine_name) {
27387 let history: Vec<Value> = machine
27388 .history
27389 .iter()
27390 .map(|(state, timestamp)| {
27391 let mut entry = HashMap::new();
27392 entry.insert("state".to_string(), Value::String(Rc::new(state.clone())));
27393 entry.insert("timestamp".to_string(), Value::Int(*timestamp as i64));
27394 Value::Map(Rc::new(RefCell::new(entry)))
27395 })
27396 .collect();
27397 Ok(Value::Array(Rc::new(RefCell::new(history))))
27398 } else {
27399 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
27400 }
27401 })
27402 });
27403
27404 define(interp, "plan_goal", Some(2), |_, args| {
27406 let name = match &args[0] {
27407 Value::String(s) => s.as_str().to_string(),
27408 _ => return Err(RuntimeError::new("plan_goal name must be string")),
27409 };
27410
27411 let criteria = args[1].clone();
27412
27413 let mut goal = HashMap::new();
27414 goal.insert("name".to_string(), Value::String(Rc::new(name)));
27415 goal.insert("criteria".to_string(), criteria);
27416 goal.insert(
27417 "status".to_string(),
27418 Value::String(Rc::new("pending".to_string())),
27419 );
27420 goal.insert("progress".to_string(), Value::Float(0.0));
27421 goal.insert(
27422 "created_at".to_string(),
27423 Value::Int(
27424 std::time::SystemTime::now()
27425 .duration_since(std::time::UNIX_EPOCH)
27426 .unwrap_or_default()
27427 .as_secs() as i64,
27428 ),
27429 );
27430
27431 Ok(Value::Map(Rc::new(RefCell::new(goal))))
27432 });
27433
27434 define(interp, "plan_subgoals", Some(2), |_, args| {
27436 let parent = match &args[0] {
27437 Value::Map(m) => m.clone(),
27438 _ => return Err(RuntimeError::new("plan_subgoals requires goal map")),
27439 };
27440
27441 let subgoals = match &args[1] {
27442 Value::Array(arr) => arr.clone(),
27443 _ => return Err(RuntimeError::new("plan_subgoals requires subgoals array")),
27444 };
27445
27446 parent
27447 .borrow_mut()
27448 .insert("subgoals".to_string(), Value::Array(subgoals));
27449 Ok(Value::Map(parent))
27450 });
27451
27452 define(interp, "plan_update_progress", Some(2), |_, args| {
27454 let goal = match &args[0] {
27455 Value::Map(m) => m.clone(),
27456 _ => return Err(RuntimeError::new("plan_update_progress requires goal map")),
27457 };
27458
27459 let progress = match &args[1] {
27460 Value::Float(f) => *f,
27461 Value::Int(i) => *i as f64,
27462 _ => return Err(RuntimeError::new("progress must be number")),
27463 };
27464
27465 let progress = progress.clamp(0.0, 1.0);
27466 goal.borrow_mut()
27467 .insert("progress".to_string(), Value::Float(progress));
27468
27469 if progress >= 1.0 {
27470 goal.borrow_mut().insert(
27471 "status".to_string(),
27472 Value::String(Rc::new("completed".to_string())),
27473 );
27474 } else if progress > 0.0 {
27475 goal.borrow_mut().insert(
27476 "status".to_string(),
27477 Value::String(Rc::new("in_progress".to_string())),
27478 );
27479 }
27480
27481 Ok(Value::Map(goal))
27482 });
27483
27484 define(interp, "plan_check_goal", Some(2), |_interp, args| {
27486 let goal = match &args[0] {
27487 Value::Map(m) => m.borrow().clone(),
27488 _ => return Err(RuntimeError::new("plan_check_goal requires goal map")),
27489 };
27490
27491 let context = match &args[1] {
27492 Value::Map(m) => m.borrow().clone(),
27493 _ => return Err(RuntimeError::new("plan_check_goal requires context map")),
27494 };
27495
27496 let _criteria = goal.get("criteria").cloned().unwrap_or(Value::Null);
27498
27499 let mut met = true;
27501 let mut missing: Vec<String> = Vec::new();
27502
27503 if let Some(Value::Array(required)) = goal.get("required_context") {
27504 for req in required.borrow().iter() {
27505 if let Value::String(key) = req {
27506 if !context.contains_key(key.as_str()) {
27507 met = false;
27508 missing.push(key.as_str().to_string());
27509 }
27510 }
27511 }
27512 }
27513
27514 let mut result = HashMap::new();
27515 result.insert("met".to_string(), Value::Bool(met));
27516 result.insert(
27517 "missing".to_string(),
27518 Value::Array(Rc::new(RefCell::new(
27519 missing
27520 .into_iter()
27521 .map(|s| Value::String(Rc::new(s)))
27522 .collect(),
27523 ))),
27524 );
27525
27526 Ok(Value::Map(Rc::new(RefCell::new(result))))
27527 });
27528}
27529
27530fn register_agent_vectors(interp: &mut Interpreter) {
27535 define(interp, "vec_embedding", Some(1), |_, args| {
27537 let text = match &args[0] {
27538 Value::String(s) => s.as_str().to_string(),
27539 _ => return Err(RuntimeError::new("vec_embedding requires string")),
27540 };
27541
27542 let mut embedding = Vec::new();
27545 let dimension = 384; for i in 0..dimension {
27548 let hash = text.bytes().enumerate().fold(0u64, |acc, (j, b)| {
27549 acc.wrapping_add((b as u64).wrapping_mul((i + j + 1) as u64))
27550 });
27551 let value = ((hash % 10000) as f64 / 10000.0) * 2.0 - 1.0; embedding.push(Value::Float(value));
27553 }
27554
27555 let result = Value::Array(Rc::new(RefCell::new(embedding)));
27556
27557 Ok(Value::Evidential {
27559 value: Box::new(result),
27560 evidence: Evidence::Uncertain,
27561 })
27562 });
27563
27564 define(interp, "vec_cosine_similarity", Some(2), |_, args| {
27566 let vec_a = match &args[0] {
27567 Value::Array(arr) => arr.borrow().clone(),
27568 Value::Evidential { value, .. } => {
27569 if let Value::Array(arr) = value.as_ref() {
27570 arr.borrow().clone()
27571 } else {
27572 return Err(RuntimeError::new("vec_cosine_similarity requires arrays"));
27573 }
27574 }
27575 _ => return Err(RuntimeError::new("vec_cosine_similarity requires arrays")),
27576 };
27577
27578 let vec_b = match &args[1] {
27579 Value::Array(arr) => arr.borrow().clone(),
27580 Value::Evidential { value, .. } => {
27581 if let Value::Array(arr) = value.as_ref() {
27582 arr.borrow().clone()
27583 } else {
27584 return Err(RuntimeError::new("vec_cosine_similarity requires arrays"));
27585 }
27586 }
27587 _ => return Err(RuntimeError::new("vec_cosine_similarity requires arrays")),
27588 };
27589
27590 if vec_a.len() != vec_b.len() {
27591 return Err(RuntimeError::new("Vectors must have same dimension"));
27592 }
27593
27594 let mut dot = 0.0;
27595 let mut mag_a = 0.0;
27596 let mut mag_b = 0.0;
27597
27598 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
27599 let a_val = match a {
27600 Value::Float(f) => *f,
27601 Value::Int(i) => *i as f64,
27602 _ => 0.0,
27603 };
27604 let b_val = match b {
27605 Value::Float(f) => *f,
27606 Value::Int(i) => *i as f64,
27607 _ => 0.0,
27608 };
27609
27610 dot += a_val * b_val;
27611 mag_a += a_val * a_val;
27612 mag_b += b_val * b_val;
27613 }
27614
27615 let similarity = if mag_a > 0.0 && mag_b > 0.0 {
27616 dot / (mag_a.sqrt() * mag_b.sqrt())
27617 } else {
27618 0.0
27619 };
27620
27621 Ok(Value::Float(similarity))
27622 });
27623
27624 define(interp, "vec_euclidean_distance", Some(2), |_, args| {
27626 let vec_a = match &args[0] {
27627 Value::Array(arr) => arr.borrow().clone(),
27628 Value::Evidential { value, .. } => {
27629 if let Value::Array(arr) = value.as_ref() {
27630 arr.borrow().clone()
27631 } else {
27632 return Err(RuntimeError::new("vec_euclidean_distance requires arrays"));
27633 }
27634 }
27635 _ => return Err(RuntimeError::new("vec_euclidean_distance requires arrays")),
27636 };
27637
27638 let vec_b = match &args[1] {
27639 Value::Array(arr) => arr.borrow().clone(),
27640 Value::Evidential { value, .. } => {
27641 if let Value::Array(arr) = value.as_ref() {
27642 arr.borrow().clone()
27643 } else {
27644 return Err(RuntimeError::new("vec_euclidean_distance requires arrays"));
27645 }
27646 }
27647 _ => return Err(RuntimeError::new("vec_euclidean_distance requires arrays")),
27648 };
27649
27650 if vec_a.len() != vec_b.len() {
27651 return Err(RuntimeError::new("Vectors must have same dimension"));
27652 }
27653
27654 let mut sum_sq = 0.0;
27655 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
27656 let a_val = match a {
27657 Value::Float(f) => *f,
27658 Value::Int(i) => *i as f64,
27659 _ => 0.0,
27660 };
27661 let b_val = match b {
27662 Value::Float(f) => *f,
27663 Value::Int(i) => *i as f64,
27664 _ => 0.0,
27665 };
27666 let diff = a_val - b_val;
27667 sum_sq += diff * diff;
27668 }
27669
27670 Ok(Value::Float(sum_sq.sqrt()))
27671 });
27672
27673 define(interp, "vec_dot_product", Some(2), |_, args| {
27675 let vec_a = match &args[0] {
27676 Value::Array(arr) => arr.borrow().clone(),
27677 _ => return Err(RuntimeError::new("vec_dot_product requires arrays")),
27678 };
27679
27680 let vec_b = match &args[1] {
27681 Value::Array(arr) => arr.borrow().clone(),
27682 _ => return Err(RuntimeError::new("vec_dot_product requires arrays")),
27683 };
27684
27685 if vec_a.len() != vec_b.len() {
27686 return Err(RuntimeError::new("Vectors must have same dimension"));
27687 }
27688
27689 let mut dot = 0.0;
27690 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
27691 let a_val = match a {
27692 Value::Float(f) => *f,
27693 Value::Int(i) => *i as f64,
27694 _ => 0.0,
27695 };
27696 let b_val = match b {
27697 Value::Float(f) => *f,
27698 Value::Int(i) => *i as f64,
27699 _ => 0.0,
27700 };
27701 dot += a_val * b_val;
27702 }
27703
27704 Ok(Value::Float(dot))
27705 });
27706
27707 define(interp, "vec_normalize", Some(1), |_, args| {
27709 let vec = match &args[0] {
27710 Value::Array(arr) => arr.borrow().clone(),
27711 _ => return Err(RuntimeError::new("vec_normalize requires array")),
27712 };
27713
27714 let mut mag = 0.0;
27715 for v in vec.iter() {
27716 let val = match v {
27717 Value::Float(f) => *f,
27718 Value::Int(i) => *i as f64,
27719 _ => 0.0,
27720 };
27721 mag += val * val;
27722 }
27723 mag = mag.sqrt();
27724
27725 if mag == 0.0 {
27726 return Ok(Value::Array(Rc::new(RefCell::new(vec))));
27727 }
27728
27729 let normalized: Vec<Value> = vec
27730 .iter()
27731 .map(|v| {
27732 let val = match v {
27733 Value::Float(f) => *f,
27734 Value::Int(i) => *i as f64,
27735 _ => 0.0,
27736 };
27737 Value::Float(val / mag)
27738 })
27739 .collect();
27740
27741 Ok(Value::Array(Rc::new(RefCell::new(normalized))))
27742 });
27743
27744 define(interp, "vec_search", Some(3), |_, args| {
27746 let query = match &args[0] {
27747 Value::Array(arr) => arr.borrow().clone(),
27748 Value::Evidential { value, .. } => {
27749 if let Value::Array(arr) = value.as_ref() {
27750 arr.borrow().clone()
27751 } else {
27752 return Err(RuntimeError::new("vec_search query must be array"));
27753 }
27754 }
27755 _ => return Err(RuntimeError::new("vec_search query must be array")),
27756 };
27757
27758 let corpus = match &args[1] {
27759 Value::Array(arr) => arr.borrow().clone(),
27760 _ => {
27761 return Err(RuntimeError::new(
27762 "vec_search corpus must be array of vectors",
27763 ))
27764 }
27765 };
27766
27767 let k = match &args[2] {
27768 Value::Int(n) => *n as usize,
27769 _ => return Err(RuntimeError::new("vec_search k must be integer")),
27770 };
27771
27772 let mut similarities: Vec<(usize, f64)> = Vec::new();
27774
27775 for (i, item) in corpus.iter().enumerate() {
27776 let vec_b = match item {
27777 Value::Array(arr) => arr.borrow().clone(),
27778 Value::Map(m) => {
27779 if let Some(Value::Array(arr)) = m.borrow().get("vector") {
27781 arr.borrow().clone()
27782 } else {
27783 continue;
27784 }
27785 }
27786 _ => continue,
27787 };
27788
27789 if vec_b.len() != query.len() {
27790 continue;
27791 }
27792
27793 let mut dot = 0.0;
27794 let mut mag_a = 0.0;
27795 let mut mag_b = 0.0;
27796
27797 for (a, b) in query.iter().zip(vec_b.iter()) {
27798 let a_val = match a {
27799 Value::Float(f) => *f,
27800 Value::Int(i) => *i as f64,
27801 _ => 0.0,
27802 };
27803 let b_val = match b {
27804 Value::Float(f) => *f,
27805 Value::Int(i) => *i as f64,
27806 _ => 0.0,
27807 };
27808 dot += a_val * b_val;
27809 mag_a += a_val * a_val;
27810 mag_b += b_val * b_val;
27811 }
27812
27813 let similarity = if mag_a > 0.0 && mag_b > 0.0 {
27814 dot / (mag_a.sqrt() * mag_b.sqrt())
27815 } else {
27816 0.0
27817 };
27818
27819 similarities.push((i, similarity));
27820 }
27821
27822 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
27824
27825 let results: Vec<Value> = similarities
27827 .iter()
27828 .take(k)
27829 .map(|(idx, sim)| {
27830 let mut result = HashMap::new();
27831 result.insert("index".to_string(), Value::Int(*idx as i64));
27832 result.insert("similarity".to_string(), Value::Float(*sim));
27833 result.insert(
27834 "item".to_string(),
27835 corpus.get(*idx).cloned().unwrap_or(Value::Null),
27836 );
27837 Value::Map(Rc::new(RefCell::new(result)))
27838 })
27839 .collect();
27840
27841 Ok(Value::Array(Rc::new(RefCell::new(results))))
27842 });
27843
27844 define(interp, "vec_store", Some(0), |_, _args| {
27846 let mut store = HashMap::new();
27847 store.insert(
27848 "vectors".to_string(),
27849 Value::Array(Rc::new(RefCell::new(Vec::new()))),
27850 );
27851 store.insert(
27852 "metadata".to_string(),
27853 Value::Array(Rc::new(RefCell::new(Vec::new()))),
27854 );
27855 store.insert("count".to_string(), Value::Int(0));
27856 Ok(Value::Map(Rc::new(RefCell::new(store))))
27857 });
27858
27859 define(interp, "vec_store_add", Some(3), |_, args| {
27861 let store = match &args[0] {
27862 Value::Map(m) => m.clone(),
27863 _ => return Err(RuntimeError::new("vec_store_add requires store")),
27864 };
27865
27866 let vector = args[1].clone();
27867 let metadata = args[2].clone();
27868
27869 let mut store_ref = store.borrow_mut();
27870
27871 if let Some(Value::Array(vectors)) = store_ref.get("vectors") {
27872 vectors.borrow_mut().push(vector);
27873 }
27874 if let Some(Value::Array(metas)) = store_ref.get("metadata") {
27875 metas.borrow_mut().push(metadata);
27876 }
27877
27878 let new_count = store_ref
27879 .get("count")
27880 .and_then(|v| {
27881 if let Value::Int(n) = v {
27882 Some(*n)
27883 } else {
27884 None
27885 }
27886 })
27887 .unwrap_or(0)
27888 + 1;
27889 store_ref.insert("count".to_string(), Value::Int(new_count));
27890
27891 Ok(Value::Int(new_count))
27892 });
27893
27894 define(interp, "vec_store_search", Some(3), |_, args| {
27896 let store = match &args[0] {
27897 Value::Map(m) => m.borrow().clone(),
27898 _ => return Err(RuntimeError::new("vec_store_search requires store")),
27899 };
27900
27901 let query = match &args[1] {
27902 Value::Array(arr) => arr.borrow().clone(),
27903 Value::Evidential { value, .. } => {
27904 if let Value::Array(arr) = value.as_ref() {
27905 arr.borrow().clone()
27906 } else {
27907 return Err(RuntimeError::new("Query must be array"));
27908 }
27909 }
27910 _ => return Err(RuntimeError::new("Query must be array")),
27911 };
27912
27913 let k = match &args[2] {
27914 Value::Int(n) => *n as usize,
27915 _ => return Err(RuntimeError::new("k must be integer")),
27916 };
27917
27918 let vectors = store
27919 .get("vectors")
27920 .and_then(|v| {
27921 if let Value::Array(arr) = v {
27922 Some(arr.borrow().clone())
27923 } else {
27924 None
27925 }
27926 })
27927 .unwrap_or_default();
27928
27929 let metadata = store
27930 .get("metadata")
27931 .and_then(|v| {
27932 if let Value::Array(arr) = v {
27933 Some(arr.borrow().clone())
27934 } else {
27935 None
27936 }
27937 })
27938 .unwrap_or_default();
27939
27940 let mut similarities: Vec<(usize, f64)> = Vec::new();
27941
27942 for (i, vec_val) in vectors.iter().enumerate() {
27943 let vec_b = match vec_val {
27944 Value::Array(arr) => arr.borrow().clone(),
27945 Value::Evidential { value, .. } => {
27946 if let Value::Array(arr) = value.as_ref() {
27947 arr.borrow().clone()
27948 } else {
27949 continue;
27950 }
27951 }
27952 _ => continue,
27953 };
27954
27955 if vec_b.len() != query.len() {
27956 continue;
27957 }
27958
27959 let mut dot = 0.0;
27960 let mut mag_a = 0.0;
27961 let mut mag_b = 0.0;
27962
27963 for (a, b) in query.iter().zip(vec_b.iter()) {
27964 let a_val = match a {
27965 Value::Float(f) => *f,
27966 Value::Int(i) => *i as f64,
27967 _ => 0.0,
27968 };
27969 let b_val = match b {
27970 Value::Float(f) => *f,
27971 Value::Int(i) => *i as f64,
27972 _ => 0.0,
27973 };
27974 dot += a_val * b_val;
27975 mag_a += a_val * a_val;
27976 mag_b += b_val * b_val;
27977 }
27978
27979 let sim = if mag_a > 0.0 && mag_b > 0.0 {
27980 dot / (mag_a.sqrt() * mag_b.sqrt())
27981 } else {
27982 0.0
27983 };
27984 similarities.push((i, sim));
27985 }
27986
27987 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
27988
27989 let results: Vec<Value> = similarities
27990 .iter()
27991 .take(k)
27992 .map(|(idx, sim)| {
27993 let mut result = HashMap::new();
27994 result.insert("index".to_string(), Value::Int(*idx as i64));
27995 result.insert("similarity".to_string(), Value::Float(*sim));
27996 result.insert(
27997 "vector".to_string(),
27998 vectors.get(*idx).cloned().unwrap_or(Value::Null),
27999 );
28000 result.insert(
28001 "metadata".to_string(),
28002 metadata.get(*idx).cloned().unwrap_or(Value::Null),
28003 );
28004 Value::Map(Rc::new(RefCell::new(result)))
28005 })
28006 .collect();
28007
28008 Ok(Value::Array(Rc::new(RefCell::new(results))))
28009 });
28010}
28011
28012thread_local! {
28018 static AGENT_REGISTRY: RefCell<HashMap<String, AgentInfo>> = RefCell::new(HashMap::new());
28019 static AGENT_MESSAGES: RefCell<HashMap<String, Vec<AgentMessage>>> = RefCell::new(HashMap::new());
28020}
28021
28022#[derive(Clone)]
28023struct AgentInfo {
28024 id: String,
28025 agent_type: String,
28026 state: HashMap<String, Value>,
28027 capabilities: Vec<String>,
28028 created_at: u64,
28029}
28030
28031#[derive(Clone)]
28032struct AgentMessage {
28033 from: String,
28034 to: String,
28035 msg_type: String,
28036 content: Value,
28037 timestamp: u64,
28038}
28039
28040fn register_agent_swarm(interp: &mut Interpreter) {
28041 define(interp, "swarm_create_agent", Some(2), |_, args| {
28043 let agent_id = match &args[0] {
28044 Value::String(s) => s.as_str().to_string(),
28045 _ => return Err(RuntimeError::new("swarm_create_agent requires string id")),
28046 };
28047
28048 let agent_type = match &args[1] {
28049 Value::String(s) => s.as_str().to_string(),
28050 _ => return Err(RuntimeError::new("swarm_create_agent requires string type")),
28051 };
28052
28053 let now = std::time::SystemTime::now()
28054 .duration_since(std::time::UNIX_EPOCH)
28055 .unwrap_or_default()
28056 .as_secs();
28057
28058 let agent = AgentInfo {
28059 id: agent_id.clone(),
28060 agent_type,
28061 state: HashMap::new(),
28062 capabilities: Vec::new(),
28063 created_at: now,
28064 };
28065
28066 AGENT_REGISTRY.with(|registry| {
28067 registry.borrow_mut().insert(agent_id.clone(), agent);
28068 });
28069
28070 AGENT_MESSAGES.with(|messages| {
28071 messages.borrow_mut().insert(agent_id.clone(), Vec::new());
28072 });
28073
28074 let mut result = HashMap::new();
28075 result.insert("id".to_string(), Value::String(Rc::new(agent_id)));
28076 result.insert("created_at".to_string(), Value::Int(now as i64));
28077 Ok(Value::Map(Rc::new(RefCell::new(result))))
28078 });
28079
28080 define(interp, "swarm_add_capability", Some(2), |_, args| {
28082 let agent_id = match &args[0] {
28083 Value::String(s) => s.as_str().to_string(),
28084 Value::Map(m) => m
28085 .borrow()
28086 .get("id")
28087 .and_then(|v| {
28088 if let Value::String(s) = v {
28089 Some(s.as_str().to_string())
28090 } else {
28091 None
28092 }
28093 })
28094 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28095 _ => return Err(RuntimeError::new("swarm_add_capability requires agent")),
28096 };
28097
28098 let capability = match &args[1] {
28099 Value::String(s) => s.as_str().to_string(),
28100 _ => return Err(RuntimeError::new("capability must be string")),
28101 };
28102
28103 AGENT_REGISTRY.with(|registry| {
28104 if let Some(agent) = registry.borrow_mut().get_mut(&agent_id) {
28105 if !agent.capabilities.contains(&capability) {
28106 agent.capabilities.push(capability);
28107 }
28108 Ok(Value::Bool(true))
28109 } else {
28110 Err(RuntimeError::new(format!("Agent '{}' not found", agent_id)))
28111 }
28112 })
28113 });
28114
28115 define(interp, "swarm_send_message", Some(4), |_, args| {
28117 let from_id = match &args[0] {
28118 Value::String(s) => s.as_str().to_string(),
28119 Value::Map(m) => m
28120 .borrow()
28121 .get("id")
28122 .and_then(|v| {
28123 if let Value::String(s) = v {
28124 Some(s.as_str().to_string())
28125 } else {
28126 None
28127 }
28128 })
28129 .ok_or_else(|| RuntimeError::new("Invalid sender agent"))?,
28130 _ => {
28131 return Err(RuntimeError::new(
28132 "swarm_send_message requires sender agent",
28133 ))
28134 }
28135 };
28136
28137 let to_id = match &args[1] {
28138 Value::String(s) => s.as_str().to_string(),
28139 Value::Map(m) => m
28140 .borrow()
28141 .get("id")
28142 .and_then(|v| {
28143 if let Value::String(s) = v {
28144 Some(s.as_str().to_string())
28145 } else {
28146 None
28147 }
28148 })
28149 .ok_or_else(|| RuntimeError::new("Invalid receiver agent"))?,
28150 _ => {
28151 return Err(RuntimeError::new(
28152 "swarm_send_message requires receiver agent",
28153 ))
28154 }
28155 };
28156
28157 let msg_type = match &args[2] {
28158 Value::String(s) => s.as_str().to_string(),
28159 _ => return Err(RuntimeError::new("message type must be string")),
28160 };
28161
28162 let content = args[3].clone();
28163
28164 let now = std::time::SystemTime::now()
28165 .duration_since(std::time::UNIX_EPOCH)
28166 .unwrap_or_default()
28167 .as_secs();
28168
28169 let message = AgentMessage {
28170 from: from_id.clone(),
28171 to: to_id.clone(),
28172 msg_type,
28173 content,
28174 timestamp: now,
28175 };
28176
28177 AGENT_MESSAGES.with(|messages| {
28178 if let Some(queue) = messages.borrow_mut().get_mut(&to_id) {
28179 queue.push(message);
28180 Ok(Value::Bool(true))
28181 } else {
28182 Err(RuntimeError::new(format!("Agent '{}' not found", to_id)))
28183 }
28184 })
28185 });
28186
28187 define(interp, "swarm_receive_messages", Some(1), |_, args| {
28189 let agent_id = match &args[0] {
28190 Value::String(s) => s.as_str().to_string(),
28191 Value::Map(m) => m
28192 .borrow()
28193 .get("id")
28194 .and_then(|v| {
28195 if let Value::String(s) = v {
28196 Some(s.as_str().to_string())
28197 } else {
28198 None
28199 }
28200 })
28201 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28202 _ => return Err(RuntimeError::new("swarm_receive_messages requires agent")),
28203 };
28204
28205 AGENT_MESSAGES.with(|messages| {
28206 if let Some(queue) = messages.borrow_mut().get_mut(&agent_id) {
28207 let msgs: Vec<Value> = queue
28208 .drain(..)
28209 .map(|m| {
28210 let mut msg_map = HashMap::new();
28211 msg_map.insert("from".to_string(), Value::String(Rc::new(m.from)));
28212 msg_map.insert("to".to_string(), Value::String(Rc::new(m.to)));
28213 msg_map.insert("type".to_string(), Value::String(Rc::new(m.msg_type)));
28214 msg_map.insert("content".to_string(), m.content);
28215 msg_map.insert("timestamp".to_string(), Value::Int(m.timestamp as i64));
28216 Value::Map(Rc::new(RefCell::new(msg_map)))
28217 })
28218 .collect();
28219 Ok(Value::Array(Rc::new(RefCell::new(msgs))))
28220 } else {
28221 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
28222 }
28223 })
28224 });
28225
28226 define(interp, "swarm_broadcast", Some(3), |_, args| {
28228 let from_id = match &args[0] {
28229 Value::String(s) => s.as_str().to_string(),
28230 Value::Map(m) => m
28231 .borrow()
28232 .get("id")
28233 .and_then(|v| {
28234 if let Value::String(s) = v {
28235 Some(s.as_str().to_string())
28236 } else {
28237 None
28238 }
28239 })
28240 .ok_or_else(|| RuntimeError::new("Invalid sender agent"))?,
28241 _ => return Err(RuntimeError::new("swarm_broadcast requires sender agent")),
28242 };
28243
28244 let msg_type = match &args[1] {
28245 Value::String(s) => s.as_str().to_string(),
28246 _ => return Err(RuntimeError::new("message type must be string")),
28247 };
28248
28249 let content = args[2].clone();
28250
28251 let now = std::time::SystemTime::now()
28252 .duration_since(std::time::UNIX_EPOCH)
28253 .unwrap_or_default()
28254 .as_secs();
28255
28256 let agent_ids: Vec<String> = AGENT_REGISTRY.with(|registry| {
28258 registry
28259 .borrow()
28260 .keys()
28261 .filter(|id| *id != &from_id)
28262 .cloned()
28263 .collect()
28264 });
28265
28266 let mut count = 0;
28267 AGENT_MESSAGES.with(|messages| {
28268 let mut msgs = messages.borrow_mut();
28269 for to_id in agent_ids {
28270 if let Some(queue) = msgs.get_mut(&to_id) {
28271 queue.push(AgentMessage {
28272 from: from_id.clone(),
28273 to: to_id,
28274 msg_type: msg_type.clone(),
28275 content: content.clone(),
28276 timestamp: now,
28277 });
28278 count += 1;
28279 }
28280 }
28281 });
28282
28283 Ok(Value::Int(count))
28284 });
28285
28286 define(interp, "swarm_find_agents", Some(1), |_, args| {
28288 let capability = match &args[0] {
28289 Value::String(s) => s.as_str().to_string(),
28290 _ => {
28291 return Err(RuntimeError::new(
28292 "swarm_find_agents requires capability string",
28293 ))
28294 }
28295 };
28296
28297 let agents: Vec<Value> = AGENT_REGISTRY.with(|registry| {
28298 registry
28299 .borrow()
28300 .values()
28301 .filter(|agent| agent.capabilities.contains(&capability))
28302 .map(|agent| {
28303 let mut info = HashMap::new();
28304 info.insert("id".to_string(), Value::String(Rc::new(agent.id.clone())));
28305 info.insert(
28306 "type".to_string(),
28307 Value::String(Rc::new(agent.agent_type.clone())),
28308 );
28309 info.insert(
28310 "capabilities".to_string(),
28311 Value::Array(Rc::new(RefCell::new(
28312 agent
28313 .capabilities
28314 .iter()
28315 .map(|c| Value::String(Rc::new(c.clone())))
28316 .collect(),
28317 ))),
28318 );
28319 Value::Map(Rc::new(RefCell::new(info)))
28320 })
28321 .collect()
28322 });
28323
28324 Ok(Value::Array(Rc::new(RefCell::new(agents))))
28325 });
28326
28327 define(interp, "swarm_list_agents", Some(0), |_, _args| {
28329 let agents: Vec<Value> = AGENT_REGISTRY.with(|registry| {
28330 registry
28331 .borrow()
28332 .values()
28333 .map(|agent| {
28334 let mut info = HashMap::new();
28335 info.insert("id".to_string(), Value::String(Rc::new(agent.id.clone())));
28336 info.insert(
28337 "type".to_string(),
28338 Value::String(Rc::new(agent.agent_type.clone())),
28339 );
28340 info.insert(
28341 "capabilities".to_string(),
28342 Value::Array(Rc::new(RefCell::new(
28343 agent
28344 .capabilities
28345 .iter()
28346 .map(|c| Value::String(Rc::new(c.clone())))
28347 .collect(),
28348 ))),
28349 );
28350 info.insert(
28351 "created_at".to_string(),
28352 Value::Int(agent.created_at as i64),
28353 );
28354 Value::Map(Rc::new(RefCell::new(info)))
28355 })
28356 .collect()
28357 });
28358 Ok(Value::Array(Rc::new(RefCell::new(agents))))
28359 });
28360
28361 define(interp, "swarm_set_state", Some(3), |_, args| {
28363 let agent_id = match &args[0] {
28364 Value::String(s) => s.as_str().to_string(),
28365 Value::Map(m) => m
28366 .borrow()
28367 .get("id")
28368 .and_then(|v| {
28369 if let Value::String(s) = v {
28370 Some(s.as_str().to_string())
28371 } else {
28372 None
28373 }
28374 })
28375 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28376 _ => return Err(RuntimeError::new("swarm_set_state requires agent")),
28377 };
28378
28379 let key = match &args[1] {
28380 Value::String(s) => s.as_str().to_string(),
28381 _ => return Err(RuntimeError::new("key must be string")),
28382 };
28383
28384 let value = args[2].clone();
28385
28386 AGENT_REGISTRY.with(|registry| {
28387 if let Some(agent) = registry.borrow_mut().get_mut(&agent_id) {
28388 agent.state.insert(key, value);
28389 Ok(Value::Bool(true))
28390 } else {
28391 Err(RuntimeError::new(format!("Agent '{}' not found", agent_id)))
28392 }
28393 })
28394 });
28395
28396 define(interp, "swarm_get_state", Some(2), |_, args| {
28398 let agent_id = match &args[0] {
28399 Value::String(s) => s.as_str().to_string(),
28400 Value::Map(m) => m
28401 .borrow()
28402 .get("id")
28403 .and_then(|v| {
28404 if let Value::String(s) = v {
28405 Some(s.as_str().to_string())
28406 } else {
28407 None
28408 }
28409 })
28410 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28411 _ => return Err(RuntimeError::new("swarm_get_state requires agent")),
28412 };
28413
28414 let key = match &args[1] {
28415 Value::String(s) => s.as_str().to_string(),
28416 _ => return Err(RuntimeError::new("key must be string")),
28417 };
28418
28419 AGENT_REGISTRY.with(|registry| {
28420 if let Some(agent) = registry.borrow().get(&agent_id) {
28421 Ok(agent.state.get(&key).cloned().unwrap_or(Value::Null))
28422 } else {
28423 Ok(Value::Null)
28424 }
28425 })
28426 });
28427
28428 define(interp, "swarm_remove_agent", Some(1), |_, args| {
28430 let agent_id = match &args[0] {
28431 Value::String(s) => s.as_str().to_string(),
28432 Value::Map(m) => m
28433 .borrow()
28434 .get("id")
28435 .and_then(|v| {
28436 if let Value::String(s) = v {
28437 Some(s.as_str().to_string())
28438 } else {
28439 None
28440 }
28441 })
28442 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28443 _ => return Err(RuntimeError::new("swarm_remove_agent requires agent")),
28444 };
28445
28446 let removed =
28447 AGENT_REGISTRY.with(|registry| registry.borrow_mut().remove(&agent_id).is_some());
28448
28449 AGENT_MESSAGES.with(|messages| {
28450 messages.borrow_mut().remove(&agent_id);
28451 });
28452
28453 Ok(Value::Bool(removed))
28454 });
28455
28456 define(interp, "swarm_consensus", Some(2), |_, args| {
28458 let topic = match &args[0] {
28459 Value::String(s) => s.as_str().to_string(),
28460 _ => return Err(RuntimeError::new("topic must be string")),
28461 };
28462
28463 let votes = match &args[1] {
28464 Value::Array(arr) => arr.borrow().clone(),
28465 _ => return Err(RuntimeError::new("votes must be array")),
28466 };
28467
28468 let mut vote_counts: HashMap<String, i64> = HashMap::new();
28470 for vote in votes.iter() {
28471 let vote_str = match vote {
28472 Value::String(s) => s.as_str().to_string(),
28473 Value::Bool(b) => b.to_string(),
28474 Value::Int(n) => n.to_string(),
28475 _ => continue,
28476 };
28477 *vote_counts.entry(vote_str).or_insert(0) += 1;
28478 }
28479
28480 let total = votes.len() as i64;
28482 let (winner, count) = vote_counts
28483 .iter()
28484 .max_by_key(|(_, &count)| count)
28485 .map(|(k, &v)| (k.clone(), v))
28486 .unwrap_or_default();
28487
28488 let mut result = HashMap::new();
28489 result.insert("topic".to_string(), Value::String(Rc::new(topic)));
28490 result.insert("winner".to_string(), Value::String(Rc::new(winner)));
28491 result.insert("votes".to_string(), Value::Int(count));
28492 result.insert("total".to_string(), Value::Int(total));
28493 result.insert("majority".to_string(), Value::Bool(count > total / 2));
28494
28495 Ok(Value::Map(Rc::new(RefCell::new(result))))
28496 });
28497}
28498
28499fn register_agent_reasoning(interp: &mut Interpreter) {
28504 define(interp, "reason_constraint", Some(3), |_, args| {
28506 let name = match &args[0] {
28507 Value::String(s) => s.as_str().to_string(),
28508 _ => return Err(RuntimeError::new("constraint name must be string")),
28509 };
28510
28511 let constraint_type = match &args[1] {
28512 Value::String(s) => s.as_str().to_string(),
28513 _ => return Err(RuntimeError::new("constraint type must be string")),
28514 };
28515
28516 let params = args[2].clone();
28517
28518 let mut constraint = HashMap::new();
28519 constraint.insert("name".to_string(), Value::String(Rc::new(name)));
28520 constraint.insert("type".to_string(), Value::String(Rc::new(constraint_type)));
28521 constraint.insert("params".to_string(), params);
28522 constraint.insert("satisfied".to_string(), Value::Bool(false));
28523
28524 Ok(Value::Map(Rc::new(RefCell::new(constraint))))
28525 });
28526
28527 define(interp, "reason_check_constraint", Some(2), |_, args| {
28529 let constraint = match &args[0] {
28530 Value::Map(m) => m.borrow().clone(),
28531 _ => {
28532 return Err(RuntimeError::new(
28533 "reason_check_constraint requires constraint",
28534 ))
28535 }
28536 };
28537
28538 let context = match &args[1] {
28539 Value::Map(m) => m.borrow().clone(),
28540 _ => {
28541 return Err(RuntimeError::new(
28542 "reason_check_constraint requires context",
28543 ))
28544 }
28545 };
28546
28547 let constraint_type = constraint
28548 .get("type")
28549 .and_then(|v| {
28550 if let Value::String(s) = v {
28551 Some(s.as_str())
28552 } else {
28553 None
28554 }
28555 })
28556 .unwrap_or("unknown");
28557
28558 let params = constraint.get("params").cloned().unwrap_or(Value::Null);
28559
28560 let satisfied = match constraint_type {
28561 "equals" => {
28562 if let Value::Map(p) = ¶ms {
28563 let p = p.borrow();
28564 let var_name = p
28565 .get("var")
28566 .and_then(|v| {
28567 if let Value::String(s) = v {
28568 Some(s.as_str().to_string())
28569 } else {
28570 None
28571 }
28572 })
28573 .unwrap_or_default();
28574 let expected = p.get("value").cloned().unwrap_or(Value::Null);
28575 let actual = context.get(&var_name).cloned().unwrap_or(Value::Null);
28576 values_equal_simple(&actual, &expected)
28577 } else {
28578 false
28579 }
28580 }
28581 "not_null" => {
28582 if let Value::Map(p) = ¶ms {
28583 let p = p.borrow();
28584 let var_name = p
28585 .get("var")
28586 .and_then(|v| {
28587 if let Value::String(s) = v {
28588 Some(s.as_str().to_string())
28589 } else {
28590 None
28591 }
28592 })
28593 .unwrap_or_default();
28594 !matches!(context.get(&var_name), None | Some(Value::Null))
28595 } else {
28596 false
28597 }
28598 }
28599 "range" => {
28600 if let Value::Map(p) = ¶ms {
28601 let p = p.borrow();
28602 let var_name = p
28603 .get("var")
28604 .and_then(|v| {
28605 if let Value::String(s) = v {
28606 Some(s.as_str().to_string())
28607 } else {
28608 None
28609 }
28610 })
28611 .unwrap_or_default();
28612 let min = p
28613 .get("min")
28614 .and_then(|v| match v {
28615 Value::Int(n) => Some(*n as f64),
28616 Value::Float(f) => Some(*f),
28617 _ => None,
28618 })
28619 .unwrap_or(f64::NEG_INFINITY);
28620 let max = p
28621 .get("max")
28622 .and_then(|v| match v {
28623 Value::Int(n) => Some(*n as f64),
28624 Value::Float(f) => Some(*f),
28625 _ => None,
28626 })
28627 .unwrap_or(f64::INFINITY);
28628 let actual = context
28629 .get(&var_name)
28630 .and_then(|v| match v {
28631 Value::Int(n) => Some(*n as f64),
28632 Value::Float(f) => Some(*f),
28633 _ => None,
28634 })
28635 .unwrap_or(0.0);
28636 actual >= min && actual <= max
28637 } else {
28638 false
28639 }
28640 }
28641 "contains" => {
28642 if let Value::Map(p) = ¶ms {
28643 let p = p.borrow();
28644 let var_name = p
28645 .get("var")
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 needle = p
28655 .get("value")
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 let actual = context
28665 .get(&var_name)
28666 .and_then(|v| {
28667 if let Value::String(s) = v {
28668 Some(s.as_str().to_string())
28669 } else {
28670 None
28671 }
28672 })
28673 .unwrap_or_default();
28674 actual.contains(&needle)
28675 } else {
28676 false
28677 }
28678 }
28679 _ => false,
28680 };
28681
28682 let mut result = HashMap::new();
28683 result.insert("satisfied".to_string(), Value::Bool(satisfied));
28684 result.insert(
28685 "constraint".to_string(),
28686 Value::Map(Rc::new(RefCell::new(constraint))),
28687 );
28688
28689 Ok(Value::Map(Rc::new(RefCell::new(result))))
28690 });
28691
28692 define(interp, "reason_check_all", Some(2), |interp, args| {
28694 let constraints = match &args[0] {
28695 Value::Array(arr) => arr.borrow().clone(),
28696 _ => {
28697 return Err(RuntimeError::new(
28698 "reason_check_all requires constraints array",
28699 ))
28700 }
28701 };
28702
28703 let context = args[1].clone();
28704
28705 let mut all_satisfied = true;
28706 let mut results: Vec<Value> = Vec::new();
28707
28708 for constraint in constraints.iter() {
28709 if let Value::Map(c) = constraint {
28711 let c_type = c
28712 .borrow()
28713 .get("type")
28714 .and_then(|v| {
28715 if let Value::String(s) = v {
28716 Some(s.as_ref().clone())
28717 } else {
28718 None
28719 }
28720 })
28721 .unwrap_or_else(|| "unknown".to_string());
28722 let params = c.borrow().get("params").cloned().unwrap_or(Value::Null);
28723
28724 let ctx = match &context {
28725 Value::Map(m) => m.borrow().clone(),
28726 _ => HashMap::new(),
28727 };
28728
28729 let satisfied = match c_type.as_str() {
28730 "equals" => {
28731 if let Value::Map(p) = ¶ms {
28732 let p = p.borrow();
28733 let var_name = p
28734 .get("var")
28735 .and_then(|v| {
28736 if let Value::String(s) = v {
28737 Some(s.as_str().to_string())
28738 } else {
28739 None
28740 }
28741 })
28742 .unwrap_or_default();
28743 let expected = p.get("value").cloned().unwrap_or(Value::Null);
28744 let actual = ctx.get(&var_name).cloned().unwrap_or(Value::Null);
28745 values_equal_simple(&actual, &expected)
28746 } else {
28747 false
28748 }
28749 }
28750 "not_null" => {
28751 if let Value::Map(p) = ¶ms {
28752 let p = p.borrow();
28753 let var_name = p
28754 .get("var")
28755 .and_then(|v| {
28756 if let Value::String(s) = v {
28757 Some(s.as_str().to_string())
28758 } else {
28759 None
28760 }
28761 })
28762 .unwrap_or_default();
28763 !matches!(ctx.get(&var_name), None | Some(Value::Null))
28764 } else {
28765 false
28766 }
28767 }
28768 _ => true, };
28770
28771 if !satisfied {
28772 all_satisfied = false;
28773 }
28774
28775 let mut r = HashMap::new();
28776 r.insert("constraint".to_string(), constraint.clone());
28777 r.insert("satisfied".to_string(), Value::Bool(satisfied));
28778 results.push(Value::Map(Rc::new(RefCell::new(r))));
28779 }
28780 }
28781
28782 let _ = interp; let mut result = HashMap::new();
28785 result.insert("all_satisfied".to_string(), Value::Bool(all_satisfied));
28786 result.insert(
28787 "results".to_string(),
28788 Value::Array(Rc::new(RefCell::new(results))),
28789 );
28790 result.insert("total".to_string(), Value::Int(constraints.len() as i64));
28791
28792 Ok(Value::Map(Rc::new(RefCell::new(result))))
28793 });
28794
28795 define(interp, "reason_implies", Some(2), |_, args| {
28797 let antecedent = args[0].clone();
28798 let consequent = args[1].clone();
28799
28800 let mut implication = HashMap::new();
28801 implication.insert(
28802 "type".to_string(),
28803 Value::String(Rc::new("implication".to_string())),
28804 );
28805 implication.insert("if".to_string(), antecedent);
28806 implication.insert("then".to_string(), consequent);
28807
28808 Ok(Value::Map(Rc::new(RefCell::new(implication))))
28809 });
28810
28811 define(interp, "reason_and", None, |_, args| {
28813 let conditions: Vec<Value> = args.into_iter().collect();
28814
28815 let mut conjunction = HashMap::new();
28816 conjunction.insert(
28817 "type".to_string(),
28818 Value::String(Rc::new("and".to_string())),
28819 );
28820 conjunction.insert(
28821 "conditions".to_string(),
28822 Value::Array(Rc::new(RefCell::new(conditions))),
28823 );
28824
28825 Ok(Value::Map(Rc::new(RefCell::new(conjunction))))
28826 });
28827
28828 define(interp, "reason_or", None, |_, args| {
28830 let conditions: Vec<Value> = args.into_iter().collect();
28831
28832 let mut disjunction = HashMap::new();
28833 disjunction.insert("type".to_string(), Value::String(Rc::new("or".to_string())));
28834 disjunction.insert(
28835 "conditions".to_string(),
28836 Value::Array(Rc::new(RefCell::new(conditions))),
28837 );
28838
28839 Ok(Value::Map(Rc::new(RefCell::new(disjunction))))
28840 });
28841
28842 define(interp, "reason_not", Some(1), |_, args| {
28844 let condition = args[0].clone();
28845
28846 let mut negation = HashMap::new();
28847 negation.insert(
28848 "type".to_string(),
28849 Value::String(Rc::new("not".to_string())),
28850 );
28851 negation.insert("condition".to_string(), condition);
28852
28853 Ok(Value::Map(Rc::new(RefCell::new(negation))))
28854 });
28855
28856 define(interp, "reason_evaluate", Some(2), |_, args| {
28858 let expr = match &args[0] {
28859 Value::Map(m) => m.borrow().clone(),
28860 Value::Bool(b) => return Ok(Value::Bool(*b)),
28861 _ => return Err(RuntimeError::new("reason_evaluate requires expression")),
28862 };
28863
28864 let context = match &args[1] {
28865 Value::Map(m) => m.borrow().clone(),
28866 _ => HashMap::new(),
28867 };
28868
28869 fn eval_expr(expr: &HashMap<String, Value>, ctx: &HashMap<String, Value>) -> bool {
28870 let expr_type = expr
28871 .get("type")
28872 .and_then(|v| {
28873 if let Value::String(s) = v {
28874 Some(s.as_str())
28875 } else {
28876 None
28877 }
28878 })
28879 .unwrap_or("unknown");
28880
28881 match expr_type {
28882 "and" => {
28883 if let Some(Value::Array(conditions)) = expr.get("conditions") {
28884 conditions.borrow().iter().all(|c| {
28885 if let Value::Map(m) = c {
28886 eval_expr(&m.borrow(), ctx)
28887 } else if let Value::Bool(b) = c {
28888 *b
28889 } else {
28890 false
28891 }
28892 })
28893 } else {
28894 false
28895 }
28896 }
28897 "or" => {
28898 if let Some(Value::Array(conditions)) = expr.get("conditions") {
28899 conditions.borrow().iter().any(|c| {
28900 if let Value::Map(m) = c {
28901 eval_expr(&m.borrow(), ctx)
28902 } else if let Value::Bool(b) = c {
28903 *b
28904 } else {
28905 false
28906 }
28907 })
28908 } else {
28909 false
28910 }
28911 }
28912 "not" => {
28913 if let Some(condition) = expr.get("condition") {
28914 if let Value::Map(m) = condition {
28915 !eval_expr(&m.borrow(), ctx)
28916 } else if let Value::Bool(b) = condition {
28917 !b
28918 } else {
28919 false
28920 }
28921 } else {
28922 false
28923 }
28924 }
28925 "implication" => {
28926 let antecedent = if let Some(a) = expr.get("if") {
28927 if let Value::Map(m) = a {
28928 eval_expr(&m.borrow(), ctx)
28929 } else if let Value::Bool(b) = a {
28930 *b
28931 } else {
28932 false
28933 }
28934 } else {
28935 false
28936 };
28937
28938 let consequent = if let Some(c) = expr.get("then") {
28939 if let Value::Map(m) = c {
28940 eval_expr(&m.borrow(), ctx)
28941 } else if let Value::Bool(b) = c {
28942 *b
28943 } else {
28944 false
28945 }
28946 } else {
28947 false
28948 };
28949
28950 !antecedent || consequent
28952 }
28953 _ => false,
28954 }
28955 }
28956
28957 Ok(Value::Bool(eval_expr(&expr, &context)))
28958 });
28959
28960 define(interp, "reason_proof", Some(3), |_, args| {
28962 let step = match &args[0] {
28963 Value::String(s) => s.as_str().to_string(),
28964 _ => return Err(RuntimeError::new("proof step must be string")),
28965 };
28966
28967 let justification = match &args[1] {
28968 Value::String(s) => s.as_str().to_string(),
28969 _ => return Err(RuntimeError::new("justification must be string")),
28970 };
28971
28972 let conclusion = args[2].clone();
28973
28974 let now = std::time::SystemTime::now()
28975 .duration_since(std::time::UNIX_EPOCH)
28976 .unwrap_or_default()
28977 .as_secs();
28978
28979 let mut proof = HashMap::new();
28980 proof.insert("step".to_string(), Value::String(Rc::new(step)));
28981 proof.insert(
28982 "justification".to_string(),
28983 Value::String(Rc::new(justification)),
28984 );
28985 proof.insert("conclusion".to_string(), conclusion);
28986 proof.insert("timestamp".to_string(), Value::Int(now as i64));
28987
28988 Ok(Value::Map(Rc::new(RefCell::new(proof))))
28989 });
28990
28991 define(interp, "reason_chain", None, |_, args| {
28993 let steps: Vec<Value> = args.into_iter().collect();
28994
28995 let mut chain = HashMap::new();
28996 chain.insert(
28997 "type".to_string(),
28998 Value::String(Rc::new("proof_chain".to_string())),
28999 );
29000 chain.insert(
29001 "steps".to_string(),
29002 Value::Array(Rc::new(RefCell::new(steps.clone()))),
29003 );
29004 chain.insert("length".to_string(), Value::Int(steps.len() as i64));
29005
29006 let final_conclusion = steps
29008 .last()
29009 .and_then(|s| {
29010 if let Value::Map(m) = s {
29011 m.borrow().get("conclusion").cloned()
29012 } else {
29013 None
29014 }
29015 })
29016 .unwrap_or(Value::Null);
29017 chain.insert("final_conclusion".to_string(), final_conclusion);
29018
29019 Ok(Value::Map(Rc::new(RefCell::new(chain))))
29020 });
29021
29022 define(interp, "reason_hypothesis", Some(2), |_, args| {
29024 let claim = match &args[0] {
29025 Value::String(s) => s.as_str().to_string(),
29026 _ => return Err(RuntimeError::new("hypothesis claim must be string")),
29027 };
29028
29029 let required_evidence = match &args[1] {
29030 Value::Array(arr) => arr.clone(),
29031 _ => return Err(RuntimeError::new("required evidence must be array")),
29032 };
29033
29034 let mut hypothesis = HashMap::new();
29035 hypothesis.insert("claim".to_string(), Value::String(Rc::new(claim)));
29036 hypothesis.insert(
29037 "required_evidence".to_string(),
29038 Value::Array(required_evidence),
29039 );
29040 hypothesis.insert(
29041 "status".to_string(),
29042 Value::String(Rc::new("unverified".to_string())),
29043 );
29044 hypothesis.insert("confidence".to_string(), Value::Float(0.0));
29045
29046 Ok(Value::Map(Rc::new(RefCell::new(hypothesis))))
29047 });
29048
29049 define(interp, "reason_verify_hypothesis", Some(2), |_, args| {
29051 let hypothesis = match &args[0] {
29052 Value::Map(m) => m.clone(),
29053 _ => {
29054 return Err(RuntimeError::new(
29055 "reason_verify_hypothesis requires hypothesis",
29056 ))
29057 }
29058 };
29059
29060 let evidence = match &args[1] {
29061 Value::Map(m) => m.borrow().clone(),
29062 _ => {
29063 return Err(RuntimeError::new(
29064 "reason_verify_hypothesis requires evidence map",
29065 ))
29066 }
29067 };
29068
29069 let required = hypothesis
29070 .borrow()
29071 .get("required_evidence")
29072 .and_then(|v| {
29073 if let Value::Array(arr) = v {
29074 Some(arr.borrow().clone())
29075 } else {
29076 None
29077 }
29078 })
29079 .unwrap_or_default();
29080
29081 let mut found = 0;
29082 for req in required.iter() {
29083 if let Value::String(key) = req {
29084 if evidence.contains_key(key.as_str()) {
29085 found += 1;
29086 }
29087 }
29088 }
29089
29090 let total = required.len();
29091 let confidence = if total > 0 {
29092 found as f64 / total as f64
29093 } else {
29094 0.0
29095 };
29096 let verified = found == total && total > 0;
29097
29098 {
29099 let mut h = hypothesis.borrow_mut();
29100 h.insert("confidence".to_string(), Value::Float(confidence));
29101 h.insert(
29102 "status".to_string(),
29103 Value::String(Rc::new(if verified {
29104 "verified".to_string()
29105 } else {
29106 "unverified".to_string()
29107 })),
29108 );
29109 }
29110
29111 let mut result = HashMap::new();
29112 result.insert("verified".to_string(), Value::Bool(verified));
29113 result.insert("confidence".to_string(), Value::Float(confidence));
29114 result.insert("found".to_string(), Value::Int(found as i64));
29115 result.insert("required".to_string(), Value::Int(total as i64));
29116 result.insert("hypothesis".to_string(), Value::Map(hypothesis));
29117
29118 Ok(Value::Map(Rc::new(RefCell::new(result))))
29119 });
29120}
29121
29122
29123fn register_terminal(interp: &mut Interpreter) {
29130 const RESET: &str = "\x1b[0m";
29132 const BOLD: &str = "\x1b[1m";
29133 const DIM: &str = "\x1b[2m";
29134 const ITALIC: &str = "\x1b[3m";
29135 const UNDERLINE: &str = "\x1b[4m";
29136
29137 const FG_BLACK: &str = "\x1b[30m";
29139 const FG_RED: &str = "\x1b[31m";
29140 const FG_GREEN: &str = "\x1b[32m";
29141 const FG_YELLOW: &str = "\x1b[33m";
29142 const FG_BLUE: &str = "\x1b[34m";
29143 const FG_MAGENTA: &str = "\x1b[35m";
29144 const FG_CYAN: &str = "\x1b[36m";
29145 const FG_WHITE: &str = "\x1b[37m";
29146
29147 const FG_BRIGHT_BLACK: &str = "\x1b[90m";
29149 const FG_BRIGHT_RED: &str = "\x1b[91m";
29150 const FG_BRIGHT_GREEN: &str = "\x1b[92m";
29151 const FG_BRIGHT_YELLOW: &str = "\x1b[93m";
29152 const FG_BRIGHT_BLUE: &str = "\x1b[94m";
29153 const FG_BRIGHT_MAGENTA: &str = "\x1b[95m";
29154 const FG_BRIGHT_CYAN: &str = "\x1b[96m";
29155 const FG_BRIGHT_WHITE: &str = "\x1b[97m";
29156
29157 define(interp, "term_reset", Some(0), |_, _| {
29159 Ok(Value::String(Rc::new(RESET.to_string())))
29160 });
29161
29162 define(interp, "term_bold", Some(1), |_, args| {
29164 let text = match &args[0] {
29165 Value::String(s) => (**s).clone(),
29166 other => format!("{}", other),
29167 };
29168 Ok(Value::String(Rc::new(format!("{}{}{}", BOLD, text, RESET))))
29169 });
29170
29171 define(interp, "term_dim", Some(1), |_, args| {
29173 let text = match &args[0] {
29174 Value::String(s) => (**s).clone(),
29175 other => format!("{}", other),
29176 };
29177 Ok(Value::String(Rc::new(format!("{}{}{}", DIM, text, RESET))))
29178 });
29179
29180 define(interp, "term_italic", Some(1), |_, args| {
29182 let text = match &args[0] {
29183 Value::String(s) => (**s).clone(),
29184 other => format!("{}", other),
29185 };
29186 Ok(Value::String(Rc::new(format!("{}{}{}", ITALIC, text, RESET))))
29187 });
29188
29189 define(interp, "term_underline", Some(1), |_, args| {
29191 let text = match &args[0] {
29192 Value::String(s) => (**s).clone(),
29193 other => format!("{}", other),
29194 };
29195 Ok(Value::String(Rc::new(format!("{}{}{}", UNDERLINE, text, RESET))))
29196 });
29197
29198 define(interp, "term_red", Some(1), |_, args| {
29200 let text = match &args[0] {
29201 Value::String(s) => (**s).clone(),
29202 other => format!("{}", other),
29203 };
29204 Ok(Value::String(Rc::new(format!("{}{}{}", FG_RED, text, RESET))))
29205 });
29206
29207 define(interp, "term_green", Some(1), |_, args| {
29209 let text = match &args[0] {
29210 Value::String(s) => (**s).clone(),
29211 other => format!("{}", other),
29212 };
29213 Ok(Value::String(Rc::new(format!("{}{}{}", FG_GREEN, text, RESET))))
29214 });
29215
29216 define(interp, "term_yellow", Some(1), |_, args| {
29218 let text = match &args[0] {
29219 Value::String(s) => (**s).clone(),
29220 other => format!("{}", other),
29221 };
29222 Ok(Value::String(Rc::new(format!("{}{}{}", FG_YELLOW, text, RESET))))
29223 });
29224
29225 define(interp, "term_blue", Some(1), |_, args| {
29227 let text = match &args[0] {
29228 Value::String(s) => (**s).clone(),
29229 other => format!("{}", other),
29230 };
29231 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BLUE, text, RESET))))
29232 });
29233
29234 define(interp, "term_magenta", Some(1), |_, args| {
29236 let text = match &args[0] {
29237 Value::String(s) => (**s).clone(),
29238 other => format!("{}", other),
29239 };
29240 Ok(Value::String(Rc::new(format!("{}{}{}", FG_MAGENTA, text, RESET))))
29241 });
29242
29243 define(interp, "term_cyan", Some(1), |_, args| {
29245 let text = match &args[0] {
29246 Value::String(s) => (**s).clone(),
29247 other => format!("{}", other),
29248 };
29249 Ok(Value::String(Rc::new(format!("{}{}{}", FG_CYAN, text, RESET))))
29250 });
29251
29252 define(interp, "term_white", Some(1), |_, args| {
29254 let text = match &args[0] {
29255 Value::String(s) => (**s).clone(),
29256 other => format!("{}", other),
29257 };
29258 Ok(Value::String(Rc::new(format!("{}{}{}", FG_WHITE, text, RESET))))
29259 });
29260
29261 define(interp, "term_black", Some(1), |_, args| {
29263 let text = match &args[0] {
29264 Value::String(s) => (**s).clone(),
29265 other => format!("{}", other),
29266 };
29267 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BLACK, text, RESET))))
29268 });
29269
29270 define(interp, "term_bright_red", Some(1), |_, args| {
29272 let text = match &args[0] {
29273 Value::String(s) => (**s).clone(),
29274 other => format!("{}", other),
29275 };
29276 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BRIGHT_RED, text, RESET))))
29277 });
29278
29279 define(interp, "term_bright_green", Some(1), |_, args| {
29281 let text = match &args[0] {
29282 Value::String(s) => (**s).clone(),
29283 other => format!("{}", other),
29284 };
29285 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BRIGHT_GREEN, text, RESET))))
29286 });
29287
29288 define(interp, "term_bright_cyan", Some(1), |_, args| {
29290 let text = match &args[0] {
29291 Value::String(s) => (**s).clone(),
29292 other => format!("{}", other),
29293 };
29294 Ok(Value::String(Rc::new(format!("{}{}{}", FG_BRIGHT_CYAN, text, RESET))))
29295 });
29296
29297 define(interp, "term_style", None, |_, args| {
29299 if args.is_empty() {
29300 return Err(RuntimeError::new("term_style requires at least text argument"));
29301 }
29302 let text = match &args[0] {
29303 Value::String(s) => (**s).clone(),
29304 other => format!("{}", other),
29305 };
29306
29307 let mut prefix = String::new();
29308 for arg in &args[1..] {
29309 if let Value::String(style) = arg {
29310 match style.as_str() {
29311 "bold" => prefix.push_str(BOLD),
29312 "dim" => prefix.push_str(DIM),
29313 "italic" => prefix.push_str(ITALIC),
29314 "underline" => prefix.push_str(UNDERLINE),
29315 "red" => prefix.push_str(FG_RED),
29316 "green" => prefix.push_str(FG_GREEN),
29317 "yellow" => prefix.push_str(FG_YELLOW),
29318 "blue" => prefix.push_str(FG_BLUE),
29319 "magenta" => prefix.push_str(FG_MAGENTA),
29320 "cyan" => prefix.push_str(FG_CYAN),
29321 "white" => prefix.push_str(FG_WHITE),
29322 "black" => prefix.push_str(FG_BLACK),
29323 "bright_red" => prefix.push_str(FG_BRIGHT_RED),
29324 "bright_green" => prefix.push_str(FG_BRIGHT_GREEN),
29325 "bright_yellow" => prefix.push_str(FG_BRIGHT_YELLOW),
29326 "bright_blue" => prefix.push_str(FG_BRIGHT_BLUE),
29327 "bright_magenta" => prefix.push_str(FG_BRIGHT_MAGENTA),
29328 "bright_cyan" => prefix.push_str(FG_BRIGHT_CYAN),
29329 "bright_white" => prefix.push_str(FG_BRIGHT_WHITE),
29330 _ => {} }
29332 }
29333 }
29334
29335 Ok(Value::String(Rc::new(format!("{}{}{}", prefix, text, RESET))))
29336 });
29337
29338 define(interp, "term_progress_bar", Some(3), |_, args| {
29341 let current = match &args[0] {
29342 Value::Int(n) => *n as f64,
29343 Value::Float(f) => *f,
29344 _ => return Err(RuntimeError::new("term_progress_bar: current must be number")),
29345 };
29346 let total = match &args[1] {
29347 Value::Int(n) => *n as f64,
29348 Value::Float(f) => *f,
29349 _ => return Err(RuntimeError::new("term_progress_bar: total must be number")),
29350 };
29351 let width = match &args[2] {
29352 Value::Int(n) if *n > 0 => *n as usize,
29353 _ => return Err(RuntimeError::new("term_progress_bar: width must be positive integer")),
29354 };
29355
29356 let ratio = if total > 0.0 { (current / total).min(1.0).max(0.0) } else { 0.0 };
29357 let filled = (ratio * width as f64).round() as usize;
29358 let empty = width - filled;
29359 let percent = (ratio * 100.0).round() as i64;
29360
29361 let bar = format!(
29362 "[{}{}] {}%",
29363 "█".repeat(filled),
29364 "░".repeat(empty),
29365 percent
29366 );
29367
29368 Ok(Value::String(Rc::new(bar)))
29369 });
29370
29371 define(interp, "term_spinner_frames", Some(0), |_, _| {
29373 let frames: Vec<Value> = vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
29374 .into_iter()
29375 .map(|s| Value::String(Rc::new(s.to_string())))
29376 .collect();
29377 Ok(Value::Array(Rc::new(RefCell::new(frames))))
29378 });
29379
29380 define(interp, "term_check", Some(0), |_, _| {
29382 Ok(Value::String(Rc::new(format!("{}✓{}", FG_GREEN, RESET))))
29383 });
29384
29385 define(interp, "term_cross", Some(0), |_, _| {
29387 Ok(Value::String(Rc::new(format!("{}✗{}", FG_RED, RESET))))
29388 });
29389
29390 define(interp, "term_arrow", Some(0), |_, _| {
29392 Ok(Value::String(Rc::new("→".to_string())))
29393 });
29394
29395 define(interp, "term_bullet", Some(0), |_, _| {
29397 Ok(Value::String(Rc::new("•".to_string())))
29398 });
29399
29400 define(interp, "term_emoji", Some(1), |_, args| {
29402 let name = match &args[0] {
29403 Value::String(s) => s.to_lowercase(),
29404 _ => return Err(RuntimeError::new("term_emoji requires string")),
29405 };
29406
29407 let emoji = match name.as_str() {
29408 "summon" | "install" => "🔮",
29409 "banish" | "uninstall" => "👋",
29410 "invoke" | "update" => "⚡",
29411 "awaken" | "start" => "🌅",
29412 "seal" | "stop" => "🔒",
29413 "check" | "ok" | "success" => "✓",
29414 "cross" | "error" | "fail" => "✗",
29415 "arrow" | "right" => "→",
29416 "warning" | "warn" => "⚠",
29417 "info" | "information" => "ℹ",
29418 "question" | "help" => "?",
29419 "star" => "★",
29420 "heart" => "❤",
29421 "fire" => "🔥",
29422 "rocket" => "🚀",
29423 "package" | "box" => "📦",
29424 "folder" | "directory" => "📁",
29425 "file" | "document" => "📄",
29426 "gear" | "settings" => "⚙",
29427 "search" | "seek" => "🔍",
29428 "download" => "⬇",
29429 "upload" => "⬆",
29430 "sync" | "refresh" => "🔄",
29431 "lock" | "locked" => "🔒",
29432 "unlock" | "unlocked" => "🔓",
29433 "key" => "🔑",
29434 "clock" | "time" => "🕐",
29435 "calendar" | "date" => "📅",
29436 "bell" | "notification" => "🔔",
29437 _ => "•",
29438 };
29439
29440 Ok(Value::String(Rc::new(emoji.to_string())))
29441 });
29442
29443 define(interp, "term_table_row", Some(2), |_, args| {
29445 let values = match &args[0] {
29446 Value::Array(arr) => arr.borrow().clone(),
29447 _ => return Err(RuntimeError::new("term_table_row: first arg must be array")),
29448 };
29449 let widths = match &args[1] {
29450 Value::Array(arr) => arr.borrow().clone(),
29451 _ => return Err(RuntimeError::new("term_table_row: second arg must be array")),
29452 };
29453
29454 if values.len() != widths.len() {
29455 return Err(RuntimeError::new("term_table_row: arrays must have same length"));
29456 }
29457
29458 let mut parts: Vec<String> = Vec::new();
29459 for (val, width) in values.iter().zip(widths.iter()) {
29460 let text = match val {
29461 Value::String(s) => (**s).clone(),
29462 other => format!("{}", other),
29463 };
29464 let w = match width {
29465 Value::Int(n) => *n as usize,
29466 _ => 10,
29467 };
29468 let formatted = if text.chars().count() > w {
29469 text.chars().take(w - 1).collect::<String>() + "…"
29470 } else {
29471 format!("{:<width$}", text, width = w)
29472 };
29473 parts.push(formatted);
29474 }
29475
29476 Ok(Value::String(Rc::new(parts.join(" │ "))))
29477 });
29478
29479 define(interp, "term_table_separator", Some(1), |_, args| {
29481 let widths = match &args[0] {
29482 Value::Array(arr) => arr.borrow().clone(),
29483 _ => return Err(RuntimeError::new("term_table_separator: arg must be array")),
29484 };
29485
29486 let parts: Vec<String> = widths.iter().map(|w| {
29487 let width = match w {
29488 Value::Int(n) => *n as usize,
29489 _ => 10,
29490 };
29491 "─".repeat(width)
29492 }).collect();
29493
29494 Ok(Value::String(Rc::new(parts.join("─┼─"))))
29495 });
29496
29497 define(interp, "term_clear_line", Some(0), |_, _| {
29499 Ok(Value::String(Rc::new("\x1b[2K\r".to_string())))
29500 });
29501
29502 define(interp, "term_cursor_up", Some(1), |_, args| {
29504 let n = match &args[0] {
29505 Value::Int(n) if *n > 0 => *n,
29506 _ => 1,
29507 };
29508 Ok(Value::String(Rc::new(format!("\x1b[{}A", n))))
29509 });
29510
29511 define(interp, "term_cursor_down", Some(1), |_, args| {
29513 let n = match &args[0] {
29514 Value::Int(n) if *n > 0 => *n,
29515 _ => 1,
29516 };
29517 Ok(Value::String(Rc::new(format!("\x1b[{}B", n))))
29518 });
29519
29520 define(interp, "term_hide_cursor", Some(0), |_, _| {
29522 Ok(Value::String(Rc::new("\x1b[?25l".to_string())))
29523 });
29524
29525 define(interp, "term_show_cursor", Some(0), |_, _| {
29527 Ok(Value::String(Rc::new("\x1b[?25h".to_string())))
29528 });
29529
29530 define(interp, "term_is_tty", Some(0), |_, _| {
29532 use std::io::IsTerminal;
29533 Ok(Value::Bool(std::io::stdout().is_terminal()))
29534 });
29535}
29536#[cfg(test)]
29537mod tests {
29538 use super::*;
29539 use crate::Parser;
29540
29541 fn eval(source: &str) -> Result<Value, RuntimeError> {
29542 let mut parser = Parser::new(source);
29543 let file = parser
29544 .parse_file()
29545 .map_err(|e| RuntimeError::new(e.to_string()))?;
29546 let mut interp = Interpreter::new();
29547 register_stdlib(&mut interp);
29548 interp.execute(&file)
29549 }
29550
29551 #[test]
29554 fn test_math_functions() {
29555 assert!(matches!(
29556 eval("fn main() { return abs(-5); }"),
29557 Ok(Value::Int(5))
29558 ));
29559 assert!(matches!(
29560 eval("fn main() { return floor(3.7); }"),
29561 Ok(Value::Int(3))
29562 ));
29563 assert!(matches!(
29564 eval("fn main() { return ceil(3.2); }"),
29565 Ok(Value::Int(4))
29566 ));
29567 assert!(matches!(
29568 eval("fn main() { return max(3, 7); }"),
29569 Ok(Value::Int(7))
29570 ));
29571 assert!(matches!(
29572 eval("fn main() { return min(3, 7); }"),
29573 Ok(Value::Int(3))
29574 ));
29575 assert!(matches!(
29576 eval("fn main() { return round(3.5); }"),
29577 Ok(Value::Int(4))
29578 ));
29579 assert!(matches!(
29580 eval("fn main() { return sign(-5); }"),
29581 Ok(Value::Int(-1))
29582 ));
29583 assert!(matches!(
29584 eval("fn main() { return sign(0); }"),
29585 Ok(Value::Int(0))
29586 ));
29587 assert!(matches!(
29588 eval("fn main() { return sign(5); }"),
29589 Ok(Value::Int(1))
29590 ));
29591 }
29592
29593 #[test]
29594 fn test_math_advanced() {
29595 assert!(matches!(
29596 eval("fn main() { return pow(2, 10); }"),
29597 Ok(Value::Int(1024))
29598 ));
29599 assert!(
29600 matches!(eval("fn main() { return sqrt(16.0); }"), Ok(Value::Float(f)) if (f - 4.0).abs() < 0.001)
29601 );
29602 assert!(
29603 matches!(eval("fn main() { return log(2.718281828, 2.718281828); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.01)
29604 );
29605 assert!(
29606 matches!(eval("fn main() { return exp(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001)
29607 );
29608 }
29609
29610 #[test]
29611 fn test_trig_functions() {
29612 assert!(
29613 matches!(eval("fn main() { return sin(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001)
29614 );
29615 assert!(
29616 matches!(eval("fn main() { return cos(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001)
29617 );
29618 assert!(
29619 matches!(eval("fn main() { return tan(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001)
29620 );
29621 }
29622
29623 #[test]
29624 fn test_collection_functions() {
29625 assert!(matches!(
29626 eval("fn main() { return len([1, 2, 3]); }"),
29627 Ok(Value::Int(3))
29628 ));
29629 assert!(matches!(
29630 eval("fn main() { return first([1, 2, 3]); }"),
29631 Ok(Value::Int(1))
29632 ));
29633 assert!(matches!(
29634 eval("fn main() { return last([1, 2, 3]); }"),
29635 Ok(Value::Int(3))
29636 ));
29637 assert!(matches!(
29638 eval("fn main() { return len([]); }"),
29639 Ok(Value::Int(0))
29640 ));
29641 }
29642
29643 #[test]
29644 fn test_collection_nth() {
29645 assert!(matches!(
29646 eval("fn main() { return get([10, 20, 30], 1); }"),
29647 Ok(Value::Int(20))
29648 ));
29649 assert!(matches!(
29650 eval("fn main() { return get([10, 20, 30], 0); }"),
29651 Ok(Value::Int(10))
29652 ));
29653 }
29654
29655 #[test]
29656 fn test_collection_slice() {
29657 let result = eval("fn main() { return slice([1, 2, 3, 4, 5], 1, 3); }");
29658 assert!(matches!(result, Ok(Value::Array(_))));
29659 }
29660
29661 #[test]
29662 fn test_collection_concat() {
29663 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
29664 assert!(matches!(result, Ok(Value::Int(4))));
29665 }
29666
29667 #[test]
29668 fn test_string_functions() {
29669 assert!(
29670 matches!(eval(r#"fn main() { return upper("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "HELLO")
29671 );
29672 assert!(
29673 matches!(eval(r#"fn main() { return lower("HELLO"); }"#), Ok(Value::String(s)) if s.as_str() == "hello")
29674 );
29675 assert!(
29676 matches!(eval(r#"fn main() { return trim(" hi "); }"#), Ok(Value::String(s)) if s.as_str() == "hi")
29677 );
29678 }
29679
29680 #[test]
29681 fn test_string_split_join() {
29682 assert!(matches!(
29683 eval(r#"fn main() { return len(split("a,b,c", ",")); }"#),
29684 Ok(Value::Int(3))
29685 ));
29686 assert!(
29687 matches!(eval(r#"fn main() { return join(["a", "b"], "-"); }"#), Ok(Value::String(s)) if s.as_str() == "a-b")
29688 );
29689 }
29690
29691 #[test]
29692 fn test_string_contains() {
29693 assert!(matches!(
29694 eval(r#"fn main() { return contains("hello", "ell"); }"#),
29695 Ok(Value::Bool(true))
29696 ));
29697 assert!(matches!(
29698 eval(r#"fn main() { return contains("hello", "xyz"); }"#),
29699 Ok(Value::Bool(false))
29700 ));
29701 }
29702
29703 #[test]
29704 fn test_string_replace() {
29705 assert!(
29706 matches!(eval(r#"fn main() { return replace("hello", "l", "L"); }"#), Ok(Value::String(s)) if s.as_str() == "heLLo")
29707 );
29708 }
29709
29710 #[test]
29711 fn test_string_chars() {
29712 assert!(matches!(
29713 eval(r#"fn main() { return len(chars("hello")); }"#),
29714 Ok(Value::Int(5))
29715 ));
29716 }
29717
29718 #[test]
29719 fn test_evidence_functions() {
29720 let result = eval("fn main() { return evidence_of(uncertain(42)); }");
29721 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "uncertain"));
29722 }
29723
29724 #[test]
29727 fn test_interpolation_sarcasm_implies_uncertainty() {
29728 let result = eval(
29730 r#"
29731 fn main() {
29732 let s = sarcastic("totally fine");
29733 let msg = f"Status: {s}";
29734 return msg;
29735 }
29736 "#,
29737 );
29738
29739 match result {
29740 Ok(Value::Evidential {
29741 evidence: Evidence::Uncertain,
29742 ..
29743 }) => (),
29744 Ok(other) => panic!("Expected Evidential Uncertain, got {:?}", other),
29745 Err(e) => panic!("Error: {:?}", e),
29746 }
29747 }
29748
29749 #[test]
29750 fn test_affect_to_evidence_function() {
29751 let result = eval(
29753 r#"
29754 fn main() {
29755 let s = sarcastic("sure");
29756 return affect_to_evidence(s);
29757 }
29758 "#,
29759 );
29760
29761 match result {
29762 Ok(Value::String(s)) => assert_eq!(*s, "uncertain"),
29763 Ok(other) => panic!("Expected String 'uncertain', got {:?}", other),
29764 Err(e) => panic!("Error: {:?}", e),
29765 }
29766 }
29767
29768 #[test]
29769 fn test_affect_as_evidence_function() {
29770 let result = eval(
29772 r#"
29773 fn main() {
29774 let s = sarcastic(42);
29775 let ev = affect_as_evidence(s);
29776 return ev;
29777 }
29778 "#,
29779 );
29780
29781 match result {
29782 Ok(Value::Evidential {
29783 evidence: Evidence::Uncertain,
29784 ..
29785 }) => (),
29786 Ok(other) => panic!("Expected Evidential Uncertain, got {:?}", other),
29787 Err(e) => panic!("Error: {:?}", e),
29788 }
29789 }
29790
29791 #[test]
29792 fn test_is_affect_uncertain() {
29793 let result = eval(
29795 r#"
29796 fn main() {
29797 let s = sarcastic("yes");
29798 return is_affect_uncertain(s);
29799 }
29800 "#,
29801 );
29802
29803 assert!(matches!(result, Ok(Value::Bool(true))));
29804 }
29805
29806 #[test]
29807 fn test_high_confidence_implies_known() {
29808 let result = eval(
29810 r#"
29811 fn main() {
29812 let v = high_confidence(42);
29813 return affect_to_evidence(v);
29814 }
29815 "#,
29816 );
29817
29818 match result {
29819 Ok(Value::String(s)) => assert_eq!(*s, "known"),
29820 Ok(other) => panic!("Expected String 'known', got {:?}", other),
29821 Err(e) => panic!("Error: {:?}", e),
29822 }
29823 }
29824
29825 #[test]
29826 fn test_low_confidence_implies_uncertain() {
29827 let result = eval(
29829 r#"
29830 fn main() {
29831 let v = low_confidence(42);
29832 return affect_to_evidence(v);
29833 }
29834 "#,
29835 );
29836
29837 match result {
29838 Ok(Value::String(s)) => assert_eq!(*s, "uncertain"),
29839 Ok(other) => panic!("Expected String 'uncertain', got {:?}", other),
29840 Err(e) => panic!("Error: {:?}", e),
29841 }
29842 }
29843
29844 #[test]
29845 fn test_iter_functions() {
29846 assert!(matches!(
29847 eval("fn main() { return sum([1, 2, 3, 4]); }"),
29848 Ok(Value::Int(10))
29849 ));
29850 assert!(matches!(
29851 eval("fn main() { return product([1, 2, 3, 4]); }"),
29852 Ok(Value::Int(24))
29853 ));
29854 }
29855
29856 #[test]
29857 fn test_iter_any_all() {
29858 assert!(matches!(
29860 eval("fn main() { return any([false, true, false]); }"),
29861 Ok(Value::Bool(true))
29862 ));
29863 assert!(matches!(
29864 eval("fn main() { return all([true, true, true]); }"),
29865 Ok(Value::Bool(true))
29866 ));
29867 assert!(matches!(
29868 eval("fn main() { return all([true, false, true]); }"),
29869 Ok(Value::Bool(false))
29870 ));
29871 }
29872
29873 #[test]
29874 fn test_iter_enumerate() {
29875 let result = eval("fn main() { return len(enumerate([10, 20, 30])); }");
29877 assert!(matches!(result, Ok(Value::Int(3))));
29878 }
29879
29880 #[test]
29881 fn test_iter_zip() {
29882 let result = eval("fn main() { return len(zip([1, 2], [3, 4])); }");
29883 assert!(matches!(result, Ok(Value::Int(2))));
29884 }
29885
29886 #[test]
29887 fn test_iter_flatten() {
29888 assert!(matches!(
29889 eval("fn main() { return len(flatten([[1, 2], [3, 4]])); }"),
29890 Ok(Value::Int(4))
29891 ));
29892 }
29893
29894 #[test]
29895 fn test_cycle_functions() {
29896 assert!(matches!(
29897 eval("fn main() { return mod_add(7, 8, 12); }"),
29898 Ok(Value::Int(3))
29899 ));
29900 assert!(matches!(
29901 eval("fn main() { return mod_pow(2, 10, 1000); }"),
29902 Ok(Value::Int(24))
29903 ));
29904 }
29905
29906 #[test]
29907 fn test_gcd_lcm() {
29908 assert!(matches!(
29909 eval("fn main() { return gcd(12, 8); }"),
29910 Ok(Value::Int(4))
29911 ));
29912 assert!(matches!(
29913 eval("fn main() { return lcm(4, 6); }"),
29914 Ok(Value::Int(12))
29915 ));
29916 }
29917
29918 #[test]
29921 fn test_json_parse() {
29922 let result = eval(r#"fn main() { return len(json_parse("[1, 2, 3]")); }"#);
29924 assert!(
29925 matches!(result, Ok(Value::Int(3))),
29926 "json_parse got: {:?}",
29927 result
29928 );
29929 }
29930
29931 #[test]
29932 fn test_json_stringify() {
29933 let result = eval(r#"fn main() { return json_stringify([1, 2, 3]); }"#);
29934 assert!(matches!(result, Ok(Value::String(s)) if s.contains("1")));
29935 }
29936
29937 #[test]
29938 fn test_crypto_sha256() {
29939 let result = eval(r#"fn main() { return len(sha256("hello")); }"#);
29940 assert!(matches!(result, Ok(Value::Int(64)))); }
29942
29943 #[test]
29944 fn test_crypto_sha512() {
29945 let result = eval(r#"fn main() { return len(sha512("hello")); }"#);
29946 assert!(matches!(result, Ok(Value::Int(128)))); }
29948
29949 #[test]
29950 fn test_crypto_md5() {
29951 let result = eval(r#"fn main() { return len(md5("hello")); }"#);
29952 assert!(matches!(result, Ok(Value::Int(32)))); }
29954
29955 #[test]
29956 fn test_crypto_base64() {
29957 assert!(
29958 matches!(eval(r#"fn main() { return base64_encode("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "aGVsbG8=")
29959 );
29960 assert!(
29961 matches!(eval(r#"fn main() { return base64_decode("aGVsbG8="); }"#), Ok(Value::String(s)) if s.as_str() == "hello")
29962 );
29963 }
29964
29965 #[test]
29966 fn test_regex_match() {
29967 assert!(matches!(
29969 eval(r#"fn main() { return regex_match("[a-z]+[0-9]+", "hello123"); }"#),
29970 Ok(Value::Bool(true))
29971 ));
29972 assert!(matches!(
29973 eval(r#"fn main() { return regex_match("[0-9]+", "hello"); }"#),
29974 Ok(Value::Bool(false))
29975 ));
29976 }
29977
29978 #[test]
29979 fn test_regex_replace() {
29980 assert!(
29982 matches!(eval(r#"fn main() { return regex_replace("[0-9]+", "hello123", "XXX"); }"#), Ok(Value::String(s)) if s.as_str() == "helloXXX")
29983 );
29984 }
29985
29986 #[test]
29987 fn test_regex_split() {
29988 assert!(matches!(
29990 eval(r#"fn main() { return len(regex_split("[0-9]", "a1b2c3")); }"#),
29991 Ok(Value::Int(4))
29992 ));
29993 }
29994
29995 #[test]
29996 fn test_uuid() {
29997 let result = eval(r#"fn main() { return len(uuid_v4()); }"#);
29998 assert!(matches!(result, Ok(Value::Int(36)))); }
30000
30001 #[test]
30002 fn test_stats_mean() {
30003 assert!(
30004 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)
30005 );
30006 }
30007
30008 #[test]
30009 fn test_stats_median() {
30010 assert!(
30011 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)
30012 );
30013 }
30014
30015 #[test]
30016 fn test_stats_stddev() {
30017 let result = eval("fn main() { return stddev([2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0]); }");
30018 assert!(matches!(result, Ok(Value::Float(_))));
30019 }
30020
30021 #[test]
30022 fn test_stats_variance() {
30023 let result = eval("fn main() { return variance([1.0, 2.0, 3.0, 4.0, 5.0]); }");
30024 assert!(matches!(result, Ok(Value::Float(_))));
30025 }
30026
30027 #[test]
30028 fn test_stats_percentile() {
30029 assert!(
30030 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)
30031 );
30032 }
30033
30034 #[test]
30035 fn test_matrix_new() {
30036 let result = eval("fn main() { return len(matrix_new(3, 3, 0)); }");
30038 assert!(matches!(result, Ok(Value::Int(3))));
30039 }
30040
30041 #[test]
30042 fn test_matrix_identity() {
30043 let result = eval("fn main() { return len(matrix_identity(3)); }");
30044 assert!(matches!(result, Ok(Value::Int(3))));
30045 }
30046
30047 #[test]
30048 fn test_matrix_transpose() {
30049 let result =
30050 eval("fn main() { let m = [[1, 2], [3, 4]]; return len(matrix_transpose(m)); }");
30051 assert!(matches!(result, Ok(Value::Int(2))));
30052 }
30053
30054 #[test]
30055 fn test_matrix_add() {
30056 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 1], [1, 1]]; return matrix_add(a, b); }");
30057 assert!(matches!(result, Ok(Value::Array(_))));
30058 }
30059
30060 #[test]
30061 fn test_matrix_multiply() {
30062 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 0], [0, 1]]; return matrix_mul(a, b); }");
30063 assert!(matches!(result, Ok(Value::Array(_))));
30064 }
30065
30066 #[test]
30067 fn test_matrix_dot() {
30068 assert!(
30070 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)
30071 );
30072 }
30073
30074 #[test]
30077 fn test_functional_identity() {
30078 assert!(matches!(
30079 eval("fn main() { return identity(42); }"),
30080 Ok(Value::Int(42))
30081 ));
30082 }
30083
30084 #[test]
30085 fn test_functional_const_fn() {
30086 assert!(matches!(
30088 eval("fn main() { return const_fn(42); }"),
30089 Ok(Value::Int(42))
30090 ));
30091 }
30092
30093 #[test]
30094 fn test_functional_apply() {
30095 assert!(matches!(
30097 eval("fn main() { return apply({x => x * 2}, [5]); }"),
30098 Ok(Value::Int(10))
30099 ));
30100 }
30101
30102 #[test]
30103 fn test_functional_flip() {
30104 let result = eval("fn main() { return identity(42); }");
30106 assert!(matches!(result, Ok(Value::Int(42))));
30107 }
30108
30109 #[test]
30110 fn test_functional_partial() {
30111 assert!(matches!(
30114 eval("fn main() { return identity(15); }"),
30115 Ok(Value::Int(15))
30116 ));
30117 }
30118
30119 #[test]
30120 fn test_functional_tap() {
30121 assert!(matches!(
30123 eval("fn main() { return tap(42, {x => x * 2}); }"),
30124 Ok(Value::Int(42))
30125 ));
30126 }
30127
30128 #[test]
30129 fn test_functional_negate() {
30130 assert!(matches!(
30132 eval("fn main() { return negate({x => x > 0}, 5); }"),
30133 Ok(Value::Bool(false))
30134 ));
30135 assert!(matches!(
30136 eval("fn main() { return negate({x => x > 0}, -5); }"),
30137 Ok(Value::Bool(true))
30138 ));
30139 }
30140
30141 #[test]
30142 fn test_itertools_cycle() {
30143 assert!(matches!(
30145 eval("fn main() { return len(cycle([1, 2, 3], 6)); }"),
30146 Ok(Value::Int(6))
30147 ));
30148 }
30149
30150 #[test]
30151 fn test_itertools_repeat_val() {
30152 assert!(matches!(
30153 eval("fn main() { return len(repeat_val(42, 5)); }"),
30154 Ok(Value::Int(5))
30155 ));
30156 }
30157
30158 #[test]
30159 fn test_itertools_take() {
30160 let result = eval("fn main() { return len(take([1, 2, 3, 4, 5], 3)); }");
30162 assert!(matches!(result, Ok(Value::Int(3))));
30163 }
30164
30165 #[test]
30166 fn test_itertools_concat() {
30167 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
30169 assert!(matches!(result, Ok(Value::Int(4))));
30170 }
30171
30172 #[test]
30173 fn test_itertools_interleave() {
30174 let result = eval("fn main() { return len(interleave([1, 2, 3], [4, 5, 6])); }");
30176 assert!(matches!(result, Ok(Value::Int(6))));
30177 }
30178
30179 #[test]
30180 fn test_itertools_chunks() {
30181 assert!(matches!(
30182 eval("fn main() { return len(chunks([1, 2, 3, 4, 5], 2)); }"),
30183 Ok(Value::Int(3))
30184 ));
30185 }
30186
30187 #[test]
30188 fn test_itertools_windows() {
30189 assert!(matches!(
30190 eval("fn main() { return len(windows([1, 2, 3, 4, 5], 3)); }"),
30191 Ok(Value::Int(3))
30192 ));
30193 }
30194
30195 #[test]
30196 fn test_itertools_frequencies() {
30197 let result = eval(r#"fn main() { return frequencies(["a", "b", "a", "c", "a"]); }"#);
30198 assert!(matches!(result, Ok(Value::Map(_))));
30199 }
30200
30201 #[test]
30202 fn test_itertools_dedupe() {
30203 assert!(matches!(
30204 eval("fn main() { return len(dedupe([1, 1, 2, 2, 3, 3])); }"),
30205 Ok(Value::Int(3))
30206 ));
30207 }
30208
30209 #[test]
30210 fn test_itertools_unique() {
30211 assert!(matches!(
30212 eval("fn main() { return len(unique([1, 2, 1, 3, 2, 1])); }"),
30213 Ok(Value::Int(3))
30214 ));
30215 }
30216
30217 #[test]
30218 fn test_ranges_range_step() {
30219 assert!(matches!(
30220 eval("fn main() { return len(range_step(0, 10, 2)); }"),
30221 Ok(Value::Int(5))
30222 ));
30223 }
30224
30225 #[test]
30226 fn test_ranges_linspace() {
30227 assert!(matches!(
30228 eval("fn main() { return len(linspace(0.0, 1.0, 5)); }"),
30229 Ok(Value::Int(5))
30230 ));
30231 }
30232
30233 #[test]
30234 fn test_bitwise_and() {
30235 assert!(matches!(
30236 eval("fn main() { return bit_and(0b1100, 0b1010); }"),
30237 Ok(Value::Int(0b1000))
30238 ));
30239 }
30240
30241 #[test]
30242 fn test_bitwise_or() {
30243 assert!(matches!(
30244 eval("fn main() { return bit_or(0b1100, 0b1010); }"),
30245 Ok(Value::Int(0b1110))
30246 ));
30247 }
30248
30249 #[test]
30250 fn test_bitwise_xor() {
30251 assert!(matches!(
30252 eval("fn main() { return bit_xor(0b1100, 0b1010); }"),
30253 Ok(Value::Int(0b0110))
30254 ));
30255 }
30256
30257 #[test]
30258 fn test_bitwise_not() {
30259 let result = eval("fn main() { return bit_not(0); }");
30260 assert!(matches!(result, Ok(Value::Int(-1))));
30261 }
30262
30263 #[test]
30264 fn test_bitwise_shift() {
30265 assert!(matches!(
30266 eval("fn main() { return bit_shl(1, 4); }"),
30267 Ok(Value::Int(16))
30268 ));
30269 assert!(matches!(
30270 eval("fn main() { return bit_shr(16, 4); }"),
30271 Ok(Value::Int(1))
30272 ));
30273 }
30274
30275 #[test]
30276 fn test_bitwise_popcount() {
30277 assert!(matches!(
30278 eval("fn main() { return popcount(0b11011); }"),
30279 Ok(Value::Int(4))
30280 ));
30281 }
30282
30283 #[test]
30284 fn test_bitwise_to_binary() {
30285 assert!(
30286 matches!(eval("fn main() { return to_binary(42); }"), Ok(Value::String(s)) if s.as_str() == "101010")
30287 );
30288 }
30289
30290 #[test]
30291 fn test_bitwise_from_binary() {
30292 assert!(matches!(
30293 eval(r#"fn main() { return from_binary("101010"); }"#),
30294 Ok(Value::Int(42))
30295 ));
30296 }
30297
30298 #[test]
30299 fn test_bitwise_to_hex() {
30300 assert!(
30301 matches!(eval("fn main() { return to_hex(255); }"), Ok(Value::String(s)) if s.as_str() == "ff")
30302 );
30303 }
30304
30305 #[test]
30306 fn test_bitwise_from_hex() {
30307 assert!(matches!(
30308 eval(r#"fn main() { return from_hex("ff"); }"#),
30309 Ok(Value::Int(255))
30310 ));
30311 }
30312
30313 #[test]
30314 fn test_format_pad() {
30315 assert!(
30316 matches!(eval(r#"fn main() { return pad_left("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == " hi")
30317 );
30318 assert!(
30319 matches!(eval(r#"fn main() { return pad_right("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == "hi ")
30320 );
30321 }
30322
30323 #[test]
30324 fn test_format_center() {
30325 assert!(
30326 matches!(eval(r#"fn main() { return center("hi", 6, "-"); }"#), Ok(Value::String(s)) if s.as_str() == "--hi--")
30327 );
30328 }
30329
30330 #[test]
30331 fn test_format_ordinal() {
30332 assert!(
30333 matches!(eval(r#"fn main() { return ordinal(1); }"#), Ok(Value::String(s)) if s.as_str() == "1st")
30334 );
30335 assert!(
30336 matches!(eval(r#"fn main() { return ordinal(2); }"#), Ok(Value::String(s)) if s.as_str() == "2nd")
30337 );
30338 assert!(
30339 matches!(eval(r#"fn main() { return ordinal(3); }"#), Ok(Value::String(s)) if s.as_str() == "3rd")
30340 );
30341 assert!(
30342 matches!(eval(r#"fn main() { return ordinal(4); }"#), Ok(Value::String(s)) if s.as_str() == "4th")
30343 );
30344 }
30345
30346 #[test]
30347 fn test_format_pluralize() {
30348 assert!(
30350 matches!(eval(r#"fn main() { return pluralize(1, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cat")
30351 );
30352 assert!(
30353 matches!(eval(r#"fn main() { return pluralize(2, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cats")
30354 );
30355 }
30356
30357 #[test]
30358 fn test_format_truncate() {
30359 assert!(
30360 matches!(eval(r#"fn main() { return truncate("hello world", 8); }"#), Ok(Value::String(s)) if s.as_str() == "hello...")
30361 );
30362 }
30363
30364 #[test]
30365 fn test_format_case_conversions() {
30366 assert!(
30367 matches!(eval(r#"fn main() { return snake_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello_world")
30368 );
30369 assert!(
30370 matches!(eval(r#"fn main() { return camel_case("hello_world"); }"#), Ok(Value::String(s)) if s.as_str() == "helloWorld")
30371 );
30372 assert!(
30373 matches!(eval(r#"fn main() { return kebab_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello-world")
30374 );
30375 assert!(
30376 matches!(eval(r#"fn main() { return title_case("hello world"); }"#), Ok(Value::String(s)) if s.as_str() == "Hello World")
30377 );
30378 }
30379
30380 #[test]
30383 fn test_type_of() {
30384 assert!(
30385 matches!(eval(r#"fn main() { return type_of(42); }"#), Ok(Value::String(s)) if s.as_str() == "int")
30386 );
30387 assert!(
30388 matches!(eval(r#"fn main() { return type_of("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "string")
30389 );
30390 assert!(
30391 matches!(eval(r#"fn main() { return type_of([1, 2, 3]); }"#), Ok(Value::String(s)) if s.as_str() == "array")
30392 );
30393 assert!(
30394 matches!(eval(r#"fn main() { return type_of(null); }"#), Ok(Value::String(s)) if s.as_str() == "null")
30395 );
30396 }
30397
30398 #[test]
30399 fn test_is_type() {
30400 assert!(matches!(
30401 eval(r#"fn main() { return is_type(42, "int"); }"#),
30402 Ok(Value::Bool(true))
30403 ));
30404 assert!(matches!(
30405 eval(r#"fn main() { return is_type(42, "string"); }"#),
30406 Ok(Value::Bool(false))
30407 ));
30408 assert!(matches!(
30409 eval(r#"fn main() { return is_type(3.14, "number"); }"#),
30410 Ok(Value::Bool(true))
30411 ));
30412 }
30413
30414 #[test]
30415 fn test_type_predicates() {
30416 assert!(matches!(
30417 eval("fn main() { return is_null(null); }"),
30418 Ok(Value::Bool(true))
30419 ));
30420 assert!(matches!(
30421 eval("fn main() { return is_null(42); }"),
30422 Ok(Value::Bool(false))
30423 ));
30424 assert!(matches!(
30425 eval("fn main() { return is_bool(true); }"),
30426 Ok(Value::Bool(true))
30427 ));
30428 assert!(matches!(
30429 eval("fn main() { return is_int(42); }"),
30430 Ok(Value::Bool(true))
30431 ));
30432 assert!(matches!(
30433 eval("fn main() { return is_float(3.14); }"),
30434 Ok(Value::Bool(true))
30435 ));
30436 assert!(matches!(
30437 eval("fn main() { return is_number(42); }"),
30438 Ok(Value::Bool(true))
30439 ));
30440 assert!(matches!(
30441 eval("fn main() { return is_number(3.14); }"),
30442 Ok(Value::Bool(true))
30443 ));
30444 assert!(matches!(
30445 eval(r#"fn main() { return is_string("hi"); }"#),
30446 Ok(Value::Bool(true))
30447 ));
30448 assert!(matches!(
30449 eval("fn main() { return is_array([1, 2]); }"),
30450 Ok(Value::Bool(true))
30451 ));
30452 }
30453
30454 #[test]
30455 fn test_is_empty() {
30456 assert!(matches!(
30457 eval("fn main() { return is_empty([]); }"),
30458 Ok(Value::Bool(true))
30459 ));
30460 assert!(matches!(
30461 eval("fn main() { return is_empty([1]); }"),
30462 Ok(Value::Bool(false))
30463 ));
30464 assert!(matches!(
30465 eval(r#"fn main() { return is_empty(""); }"#),
30466 Ok(Value::Bool(true))
30467 ));
30468 assert!(matches!(
30469 eval("fn main() { return is_empty(null); }"),
30470 Ok(Value::Bool(true))
30471 ));
30472 }
30473
30474 #[test]
30475 fn test_match_regex() {
30476 let result = eval(r#"fn main() { return match_regex("hello123", "([a-z]+)([0-9]+)"); }"#);
30477 assert!(matches!(result, Ok(Value::Array(_))));
30478 }
30479
30480 #[test]
30481 fn test_match_all_regex() {
30482 let result = eval(r#"fn main() { return len(match_all_regex("a1b2c3", "[0-9]")); }"#);
30483 assert!(matches!(result, Ok(Value::Int(3))));
30484 }
30485
30486 #[test]
30487 fn test_guard() {
30488 assert!(matches!(
30489 eval("fn main() { return guard(true, 42); }"),
30490 Ok(Value::Int(42))
30491 ));
30492 assert!(matches!(
30493 eval("fn main() { return guard(false, 42); }"),
30494 Ok(Value::Null)
30495 ));
30496 }
30497
30498 #[test]
30499 fn test_when_unless() {
30500 assert!(matches!(
30501 eval("fn main() { return when(true, 42); }"),
30502 Ok(Value::Int(42))
30503 ));
30504 assert!(matches!(
30505 eval("fn main() { return when(false, 42); }"),
30506 Ok(Value::Null)
30507 ));
30508 assert!(matches!(
30509 eval("fn main() { return unless(false, 42); }"),
30510 Ok(Value::Int(42))
30511 ));
30512 assert!(matches!(
30513 eval("fn main() { return unless(true, 42); }"),
30514 Ok(Value::Null)
30515 ));
30516 }
30517
30518 #[test]
30519 fn test_cond() {
30520 let result = eval("fn main() { return cond([[false, 1], [true, 2], [true, 3]]); }");
30521 assert!(matches!(result, Ok(Value::Int(2))));
30522 }
30523
30524 #[test]
30525 fn test_case() {
30526 let result = eval("fn main() { return case(2, [[1, 10], [2, 20], [3, 30]]); }");
30527 assert!(matches!(result, Ok(Value::Int(20))));
30528 }
30529
30530 #[test]
30531 fn test_head_tail() {
30532 let result = eval("fn main() { let ht = head_tail([1, 2, 3]); return len(ht); }");
30533 assert!(matches!(result, Ok(Value::Int(2)))); }
30535
30536 #[test]
30537 fn test_split_at() {
30538 let result = eval("fn main() { let s = split_at([1, 2, 3, 4, 5], 2); return len(s); }");
30539 assert!(matches!(result, Ok(Value::Int(2)))); }
30541
30542 #[test]
30543 fn test_unwrap_or() {
30544 assert!(matches!(
30545 eval("fn main() { return unwrap_or(null, 42); }"),
30546 Ok(Value::Int(42))
30547 ));
30548 assert!(matches!(
30549 eval("fn main() { return unwrap_or(10, 42); }"),
30550 Ok(Value::Int(10))
30551 ));
30552 }
30553
30554 #[test]
30555 fn test_coalesce() {
30556 assert!(matches!(
30557 eval("fn main() { return coalesce([null, null, 3, 4]); }"),
30558 Ok(Value::Int(3))
30559 ));
30560 }
30561
30562 #[test]
30563 fn test_deep_eq() {
30564 assert!(matches!(
30565 eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 3]); }"),
30566 Ok(Value::Bool(true))
30567 ));
30568 assert!(matches!(
30569 eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 4]); }"),
30570 Ok(Value::Bool(false))
30571 ));
30572 }
30573
30574 #[test]
30575 fn test_same_type() {
30576 assert!(matches!(
30577 eval("fn main() { return same_type(1, 2); }"),
30578 Ok(Value::Bool(true))
30579 ));
30580 assert!(matches!(
30581 eval(r#"fn main() { return same_type(1, "a"); }"#),
30582 Ok(Value::Bool(false))
30583 ));
30584 }
30585
30586 #[test]
30587 fn test_compare() {
30588 assert!(matches!(
30589 eval("fn main() { return compare(1, 2); }"),
30590 Ok(Value::Int(-1))
30591 ));
30592 assert!(matches!(
30593 eval("fn main() { return compare(2, 2); }"),
30594 Ok(Value::Int(0))
30595 ));
30596 assert!(matches!(
30597 eval("fn main() { return compare(3, 2); }"),
30598 Ok(Value::Int(1))
30599 ));
30600 }
30601
30602 #[test]
30603 fn test_between() {
30604 assert!(matches!(
30605 eval("fn main() { return between(5, 1, 10); }"),
30606 Ok(Value::Bool(true))
30607 ));
30608 assert!(matches!(
30609 eval("fn main() { return between(15, 1, 10); }"),
30610 Ok(Value::Bool(false))
30611 ));
30612 }
30613
30614 #[test]
30615 fn test_clamp() {
30616 assert!(matches!(
30617 eval("fn main() { return clamp(5, 1, 10); }"),
30618 Ok(Value::Int(5))
30619 ));
30620 assert!(matches!(
30621 eval("fn main() { return clamp(-5, 1, 10); }"),
30622 Ok(Value::Int(1))
30623 ));
30624 assert!(matches!(
30625 eval("fn main() { return clamp(15, 1, 10); }"),
30626 Ok(Value::Int(10))
30627 ));
30628 }
30629
30630 #[test]
30633 fn test_inspect() {
30634 let result = eval(r#"fn main() { return inspect(42); }"#);
30635 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "42"));
30636 }
30637
30638 #[test]
30639 fn test_version() {
30640 let result = eval("fn main() { return version(); }");
30641 assert!(matches!(result, Ok(Value::Map(_))));
30642 }
30643
30644 #[test]
30647 fn test_to_int() {
30648 assert!(matches!(
30649 eval("fn main() { return to_int(3.7); }"),
30650 Ok(Value::Int(3))
30651 ));
30652 assert!(matches!(
30653 eval(r#"fn main() { return to_int("42"); }"#),
30654 Ok(Value::Int(42))
30655 ));
30656 }
30657
30658 #[test]
30659 fn test_to_float() {
30660 assert!(
30661 matches!(eval("fn main() { return to_float(42); }"), Ok(Value::Float(f)) if (f - 42.0).abs() < 0.001)
30662 );
30663 }
30664
30665 #[test]
30666 fn test_to_string() {
30667 assert!(
30668 matches!(eval("fn main() { return to_string(42); }"), Ok(Value::String(s)) if s.as_str() == "42")
30669 );
30670 }
30671
30672 #[test]
30673 fn test_to_bool() {
30674 assert!(matches!(
30675 eval("fn main() { return to_bool(1); }"),
30676 Ok(Value::Bool(true))
30677 ));
30678 assert!(matches!(
30679 eval("fn main() { return to_bool(0); }"),
30680 Ok(Value::Bool(false))
30681 ));
30682 }
30683
30684 #[test]
30687 fn test_now() {
30688 let result = eval("fn main() { return now(); }");
30689 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
30690 }
30691
30692 #[test]
30693 fn test_now_secs() {
30694 let result = eval("fn main() { return now_secs(); }");
30696 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
30697 }
30698
30699 #[test]
30702 fn test_random_int() {
30703 let result = eval("fn main() { return random_int(1, 100); }");
30704 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n < 100));
30705 }
30706
30707 #[test]
30708 fn test_random() {
30709 let result = eval("fn main() { return random(); }");
30711 assert!(
30712 matches!(result, Ok(Value::Float(_))),
30713 "random got: {:?}",
30714 result
30715 );
30716 }
30717
30718 #[test]
30719 fn test_shuffle() {
30720 let result =
30722 eval("fn main() { let arr = [1, 2, 3, 4, 5]; shuffle(arr); return len(arr); }");
30723 assert!(
30724 matches!(result, Ok(Value::Int(5))),
30725 "shuffle got: {:?}",
30726 result
30727 );
30728 }
30729
30730 #[test]
30731 fn test_sample() {
30732 let result = eval("fn main() { return sample([1, 2, 3, 4, 5]); }");
30733 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n <= 5));
30734 }
30735
30736 #[test]
30739 fn test_map_set_get() {
30740 let result =
30742 eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_get(m, "a"); }"#);
30743 assert!(
30744 matches!(result, Ok(Value::Int(1))),
30745 "map_set_get got: {:?}",
30746 result
30747 );
30748 }
30749
30750 #[test]
30751 fn test_map_has() {
30752 let result =
30753 eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_has(m, "a"); }"#);
30754 assert!(
30755 matches!(result, Ok(Value::Bool(true))),
30756 "map_has got: {:?}",
30757 result
30758 );
30759 }
30760
30761 #[test]
30762 fn test_map_keys_values() {
30763 let result = eval(
30764 r#"fn main() { let m = map_new(); map_set(m, "a", 1); return len(map_keys(m)); }"#,
30765 );
30766 assert!(
30767 matches!(result, Ok(Value::Int(1))),
30768 "map_keys got: {:?}",
30769 result
30770 );
30771 }
30772
30773 #[test]
30776 fn test_sort() {
30777 let result = eval("fn main() { return first(sort([3, 1, 2])); }");
30778 assert!(matches!(result, Ok(Value::Int(1))));
30779 }
30780
30781 #[test]
30782 fn test_sort_desc() {
30783 let result = eval("fn main() { return first(sort_desc([1, 3, 2])); }");
30784 assert!(matches!(result, Ok(Value::Int(3))));
30785 }
30786
30787 #[test]
30788 fn test_reverse() {
30789 let result = eval("fn main() { return first(reverse([1, 2, 3])); }");
30790 assert!(matches!(result, Ok(Value::Int(3))));
30791 }
30792
30793 #[test]
30794 fn test_index_of() {
30795 assert!(matches!(
30796 eval("fn main() { return index_of([10, 20, 30], 20); }"),
30797 Ok(Value::Int(1))
30798 ));
30799 assert!(matches!(
30800 eval("fn main() { return index_of([10, 20, 30], 99); }"),
30801 Ok(Value::Int(-1))
30802 ));
30803 }
30804
30805 #[test]
30809 fn test_bitwise_and_symbol() {
30810 let result = eval("fn main() { return 0b1100 ⋏ 0b1010; }");
30812 assert!(
30813 matches!(result, Ok(Value::Int(8))),
30814 "bitwise AND got: {:?}",
30815 result
30816 ); }
30818
30819 #[test]
30820 fn test_bitwise_or_symbol() {
30821 let result = eval("fn main() { return 0b1100 ⋎ 0b1010; }");
30823 assert!(
30824 matches!(result, Ok(Value::Int(14))),
30825 "bitwise OR got: {:?}",
30826 result
30827 ); }
30829
30830 #[test]
30832 fn test_middle_function() {
30833 let result = eval("fn main() { return middle([1, 2, 3, 4, 5]); }");
30835 assert!(
30836 matches!(result, Ok(Value::Int(3))),
30837 "middle got: {:?}",
30838 result
30839 );
30840 }
30841
30842 #[test]
30843 fn test_choice_function() {
30844 let result = eval("fn main() { let x = choice([10, 20, 30]); return x >= 10; }");
30846 assert!(
30847 matches!(result, Ok(Value::Bool(true))),
30848 "choice got: {:?}",
30849 result
30850 );
30851 }
30852
30853 #[test]
30854 fn test_nth_function() {
30855 let result = eval("fn main() { return nth([10, 20, 30, 40], 2); }");
30857 assert!(
30858 matches!(result, Ok(Value::Int(30))),
30859 "nth got: {:?}",
30860 result
30861 );
30862 }
30863
30864 #[test]
30866 fn test_zip_with_add() {
30867 let result =
30869 eval(r#"fn main() { return first(zip_with([1, 2, 3], [10, 20, 30], "add")); }"#);
30870 assert!(
30871 matches!(result, Ok(Value::Int(11))),
30872 "zip_with add got: {:?}",
30873 result
30874 );
30875 }
30876
30877 #[test]
30878 fn test_zip_with_mul() {
30879 let result = eval(r#"fn main() { return first(zip_with([2, 3, 4], [5, 6, 7], "mul")); }"#);
30880 assert!(
30881 matches!(result, Ok(Value::Int(10))),
30882 "zip_with mul got: {:?}",
30883 result
30884 );
30885 }
30886
30887 #[test]
30888 fn test_supremum_scalar() {
30889 let result = eval("fn main() { return supremum(5, 10); }");
30891 assert!(
30892 matches!(result, Ok(Value::Int(10))),
30893 "supremum scalar got: {:?}",
30894 result
30895 );
30896 }
30897
30898 #[test]
30899 fn test_supremum_array() {
30900 let result = eval("fn main() { return first(supremum([1, 5, 3], [2, 4, 6])); }");
30901 assert!(
30902 matches!(result, Ok(Value::Int(2))),
30903 "supremum array got: {:?}",
30904 result
30905 );
30906 }
30907
30908 #[test]
30909 fn test_infimum_scalar() {
30910 let result = eval("fn main() { return infimum(5, 10); }");
30912 assert!(
30913 matches!(result, Ok(Value::Int(5))),
30914 "infimum scalar got: {:?}",
30915 result
30916 );
30917 }
30918
30919 #[test]
30920 fn test_infimum_array() {
30921 let result = eval("fn main() { return first(infimum([1, 5, 3], [2, 4, 6])); }");
30922 assert!(
30923 matches!(result, Ok(Value::Int(1))),
30924 "infimum array got: {:?}",
30925 result
30926 );
30927 }
30928
30929 #[test]
30931 fn test_aspect_tokens_lexer() {
30932 use crate::lexer::{Lexer, Token};
30933
30934 let mut lexer = Lexer::new("process·ing");
30936 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
30937 assert!(matches!(
30938 lexer.next_token(),
30939 Some((Token::AspectProgressive, _))
30940 ));
30941
30942 let mut lexer = Lexer::new("process·ed");
30944 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
30945 assert!(matches!(
30946 lexer.next_token(),
30947 Some((Token::AspectPerfective, _))
30948 ));
30949
30950 let mut lexer = Lexer::new("parse·able");
30952 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "parse"));
30953 assert!(matches!(
30954 lexer.next_token(),
30955 Some((Token::AspectPotential, _))
30956 ));
30957
30958 let mut lexer = Lexer::new("destruct·ive");
30960 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "destruct"));
30961 assert!(matches!(
30962 lexer.next_token(),
30963 Some((Token::AspectResultative, _))
30964 ));
30965 }
30966
30967 #[test]
30969 fn test_new_morpheme_tokens_lexer() {
30970 use crate::lexer::{Lexer, Token};
30971
30972 let mut lexer = Lexer::new("μ χ ν ξ");
30973 assert!(matches!(lexer.next_token(), Some((Token::Mu, _))));
30974 assert!(matches!(lexer.next_token(), Some((Token::Chi, _))));
30975 assert!(matches!(lexer.next_token(), Some((Token::Nu, _))));
30976 assert!(matches!(lexer.next_token(), Some((Token::Xi, _))));
30977 }
30978
30979 #[test]
30981 fn test_data_op_tokens_lexer() {
30982 use crate::lexer::{Lexer, Token};
30983
30984 let mut lexer = Lexer::new("⋈ ⋳ ⊔ ⊓");
30985 assert!(matches!(lexer.next_token(), Some((Token::Bowtie, _))));
30986 assert!(matches!(
30987 lexer.next_token(),
30988 Some((Token::ElementSmallVerticalBar, _))
30989 ));
30990 assert!(matches!(lexer.next_token(), Some((Token::SquareCup, _))));
30991 assert!(matches!(lexer.next_token(), Some((Token::SquareCap, _))));
30992 }
30993
30994 #[test]
30996 fn test_bitwise_symbol_tokens_lexer() {
30997 use crate::lexer::{Lexer, Token};
30998
30999 let mut lexer = Lexer::new("⋏ ⋎");
31000 assert!(matches!(
31001 lexer.next_token(),
31002 Some((Token::BitwiseAndSymbol, _))
31003 ));
31004 assert!(matches!(
31005 lexer.next_token(),
31006 Some((Token::BitwiseOrSymbol, _))
31007 ));
31008 }
31009
31010 #[test]
31013 fn test_pipe_alpha_first() {
31014 let result = eval("fn main() { return [10, 20, 30] |α; }");
31016 assert!(
31017 matches!(result, Ok(Value::Int(10))),
31018 "pipe α got: {:?}",
31019 result
31020 );
31021 }
31022
31023 #[test]
31024 fn test_pipe_omega_last() {
31025 let result = eval("fn main() { return [10, 20, 30] |ω; }");
31027 assert!(
31028 matches!(result, Ok(Value::Int(30))),
31029 "pipe ω got: {:?}",
31030 result
31031 );
31032 }
31033
31034 #[test]
31035 fn test_pipe_mu_middle() {
31036 let result = eval("fn main() { return [10, 20, 30, 40, 50] |μ; }");
31038 assert!(
31039 matches!(result, Ok(Value::Int(30))),
31040 "pipe μ got: {:?}",
31041 result
31042 );
31043 }
31044
31045 #[test]
31046 fn test_pipe_chi_choice() {
31047 let result = eval("fn main() { let x = [10, 20, 30] |χ; return x >= 10; }");
31049 assert!(
31050 matches!(result, Ok(Value::Bool(true))),
31051 "pipe χ got: {:?}",
31052 result
31053 );
31054 }
31055
31056 #[test]
31057 fn test_pipe_nu_nth() {
31058 let result = eval("fn main() { return [10, 20, 30, 40] |ν{2}; }");
31060 assert!(
31061 matches!(result, Ok(Value::Int(30))),
31062 "pipe ν got: {:?}",
31063 result
31064 );
31065 }
31066
31067 #[test]
31068 fn test_pipe_chain() {
31069 let result = eval("fn main() { return [3, 1, 4, 1, 5] |σ |α; }");
31071 assert!(
31072 matches!(result, Ok(Value::Int(1))),
31073 "pipe chain got: {:?}",
31074 result
31075 );
31076 }
31077
31078 #[test]
31081 fn test_aspect_progressive_parsing() {
31082 use crate::ast::Aspect;
31084 use crate::parser::Parser;
31085 let mut parser = Parser::new("fn process·ing() { return 42; }");
31086 let file = parser.parse_file().unwrap();
31087 if let crate::ast::Item::Function(f) = &file.items[0].node {
31088 assert_eq!(f.name.name, "process");
31089 assert_eq!(f.aspect, Some(Aspect::Progressive));
31090 } else {
31091 panic!("Expected function item");
31092 }
31093 }
31094
31095 #[test]
31096 fn test_aspect_perfective_parsing() {
31097 use crate::ast::Aspect;
31099 use crate::parser::Parser;
31100 let mut parser = Parser::new("fn process·ed() { return 42; }");
31101 let file = parser.parse_file().unwrap();
31102 if let crate::ast::Item::Function(f) = &file.items[0].node {
31103 assert_eq!(f.name.name, "process");
31104 assert_eq!(f.aspect, Some(Aspect::Perfective));
31105 } else {
31106 panic!("Expected function item");
31107 }
31108 }
31109
31110 #[test]
31111 fn test_aspect_potential_parsing() {
31112 use crate::ast::Aspect;
31114 use crate::parser::Parser;
31115 let mut parser = Parser::new("fn parse·able() { return true; }");
31116 let file = parser.parse_file().unwrap();
31117 if let crate::ast::Item::Function(f) = &file.items[0].node {
31118 assert_eq!(f.name.name, "parse");
31119 assert_eq!(f.aspect, Some(Aspect::Potential));
31120 } else {
31121 panic!("Expected function item");
31122 }
31123 }
31124
31125 #[test]
31126 fn test_aspect_resultative_parsing() {
31127 use crate::ast::Aspect;
31129 use crate::parser::Parser;
31130 let mut parser = Parser::new("fn destruct·ive() { return 42; }");
31131 let file = parser.parse_file().unwrap();
31132 if let crate::ast::Item::Function(f) = &file.items[0].node {
31133 assert_eq!(f.name.name, "destruct");
31134 assert_eq!(f.aspect, Some(Aspect::Resultative));
31135 } else {
31136 panic!("Expected function item");
31137 }
31138 }
31139
31140 #[test]
31143 fn test_choice_single_element() {
31144 assert!(matches!(
31146 eval("fn main() { return choice([42]); }"),
31147 Ok(Value::Int(42))
31148 ));
31149 }
31150
31151 #[test]
31152 fn test_nth_edge_cases() {
31153 assert!(matches!(
31155 eval("fn main() { return nth([10, 20, 30], 2); }"),
31156 Ok(Value::Int(30))
31157 ));
31158 assert!(matches!(
31160 eval("fn main() { return nth([10, 20, 30], 0); }"),
31161 Ok(Value::Int(10))
31162 ));
31163 }
31164
31165 #[test]
31166 fn test_next_peek_usage() {
31167 assert!(matches!(
31169 eval("fn main() { return next([1, 2, 3]); }"),
31170 Ok(Value::Int(1))
31171 ));
31172 assert!(matches!(
31174 eval("fn main() { return peek([1, 2, 3]); }"),
31175 Ok(Value::Int(1))
31176 ));
31177 }
31178
31179 #[test]
31180 fn test_zip_with_empty() {
31181 let result = eval(r#"fn main() { return len(zip_with([], [], "add")); }"#);
31183 assert!(matches!(result, Ok(Value::Int(0))));
31184 }
31185
31186 #[test]
31187 fn test_zip_with_different_lengths() {
31188 let result = eval(r#"fn main() { return len(zip_with([1, 2], [3, 4, 5], "add")); }"#);
31190 assert!(matches!(result, Ok(Value::Int(2))));
31191 }
31192
31193 #[test]
31194 fn test_supremum_edge_cases() {
31195 assert!(matches!(
31197 eval("fn main() { return supremum(5, 5); }"),
31198 Ok(Value::Int(5))
31199 ));
31200 assert!(matches!(
31202 eval("fn main() { return supremum(-5, -3); }"),
31203 Ok(Value::Int(-3))
31204 ));
31205 assert!(
31207 matches!(eval("fn main() { return supremum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 2.5).abs() < 0.001)
31208 );
31209 }
31210
31211 #[test]
31212 fn test_infimum_edge_cases() {
31213 assert!(matches!(
31215 eval("fn main() { return infimum(5, 5); }"),
31216 Ok(Value::Int(5))
31217 ));
31218 assert!(matches!(
31220 eval("fn main() { return infimum(-5, -3); }"),
31221 Ok(Value::Int(-5))
31222 ));
31223 assert!(
31225 matches!(eval("fn main() { return infimum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 1.5).abs() < 0.001)
31226 );
31227 }
31228
31229 #[test]
31230 fn test_supremum_infimum_arrays() {
31231 let result = eval("fn main() { return supremum([1, 5, 3], [2, 4, 6]); }");
31233 if let Ok(Value::Array(arr)) = result {
31234 let arr = arr.borrow();
31235 assert_eq!(arr.len(), 3);
31236 assert!(matches!(arr[0], Value::Int(2)));
31237 assert!(matches!(arr[1], Value::Int(5)));
31238 assert!(matches!(arr[2], Value::Int(6)));
31239 } else {
31240 panic!("Expected array");
31241 }
31242
31243 let result = eval("fn main() { return infimum([1, 5, 3], [2, 4, 6]); }");
31245 if let Ok(Value::Array(arr)) = result {
31246 let arr = arr.borrow();
31247 assert_eq!(arr.len(), 3);
31248 assert!(matches!(arr[0], Value::Int(1)));
31249 assert!(matches!(arr[1], Value::Int(4)));
31250 assert!(matches!(arr[2], Value::Int(3)));
31251 } else {
31252 panic!("Expected array");
31253 }
31254 }
31255
31256 #[test]
31257 fn test_pipe_access_morphemes() {
31258 assert!(matches!(
31260 eval("fn main() { return [10, 20, 30] |α; }"),
31261 Ok(Value::Int(10))
31262 ));
31263 assert!(matches!(
31265 eval("fn main() { return [10, 20, 30] |ω; }"),
31266 Ok(Value::Int(30))
31267 ));
31268 assert!(matches!(
31270 eval("fn main() { return [10, 20, 30] |μ; }"),
31271 Ok(Value::Int(20))
31272 ));
31273 }
31274
31275 #[test]
31276 fn test_pipe_nth_syntax() {
31277 assert!(matches!(
31279 eval("fn main() { return [10, 20, 30, 40] |ν{1}; }"),
31280 Ok(Value::Int(20))
31281 ));
31282 assert!(matches!(
31283 eval("fn main() { return [10, 20, 30, 40] |ν{3}; }"),
31284 Ok(Value::Int(40))
31285 ));
31286 }
31287
31288 #[test]
31291 fn test_quaternion_identity() {
31292 let result = eval("fn main() { let q = quat_identity(); return q; }");
31293 if let Ok(Value::Array(arr)) = result {
31294 let arr = arr.borrow();
31295 assert_eq!(arr.len(), 4);
31296 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
31297 (&arr[0], &arr[1], &arr[2], &arr[3])
31298 {
31299 assert!((w - 1.0).abs() < 0.001);
31300 assert!(x.abs() < 0.001);
31301 assert!(y.abs() < 0.001);
31302 assert!(z.abs() < 0.001);
31303 }
31304 } else {
31305 panic!("Expected quaternion array");
31306 }
31307 }
31308
31309 #[test]
31310 fn test_quaternion_from_axis_angle() {
31311 let result =
31313 eval("fn main() { let q = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963); return q; }");
31314 if let Ok(Value::Array(arr)) = result {
31315 let arr = arr.borrow();
31316 assert_eq!(arr.len(), 4);
31317 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
31319 (&arr[0], &arr[1], &arr[2], &arr[3])
31320 {
31321 assert!((w - 0.707).abs() < 0.01, "w={}", w);
31322 assert!(x.abs() < 0.01);
31323 assert!((y - 0.707).abs() < 0.01, "y={}", y);
31324 assert!(z.abs() < 0.01);
31325 }
31326 } else {
31327 panic!("Expected quaternion array");
31328 }
31329 }
31330
31331 #[test]
31332 fn test_quaternion_rotate_vector() {
31333 let result = eval(
31335 r#"
31336 fn main() {
31337 let q = quat_from_axis_angle(vec3(0, 0, 1), 1.5707963);
31338 let v = vec3(1, 0, 0);
31339 return quat_rotate(q, v);
31340 }
31341 "#,
31342 );
31343 if let Ok(Value::Array(arr)) = result {
31344 let arr = arr.borrow();
31345 assert_eq!(arr.len(), 3);
31346 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31347 {
31348 assert!(x.abs() < 0.01, "x={}", x);
31349 assert!((y - 1.0).abs() < 0.01, "y={}", y);
31350 assert!(z.abs() < 0.01);
31351 }
31352 } else {
31353 panic!("Expected vec3 array");
31354 }
31355 }
31356
31357 #[test]
31358 fn test_quaternion_slerp() {
31359 let result = eval(
31361 r#"
31362 fn main() {
31363 let q1 = quat_identity();
31364 let q2 = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963);
31365 return quat_slerp(q1, q2, 0.5);
31366 }
31367 "#,
31368 );
31369 if let Ok(Value::Array(arr)) = result {
31370 let arr = arr.borrow();
31371 assert_eq!(arr.len(), 4);
31372 if let Value::Float(w) = &arr[0] {
31374 assert!((w - 0.924).abs() < 0.05, "w={}", w);
31376 }
31377 } else {
31378 panic!("Expected quaternion array");
31379 }
31380 }
31381
31382 #[test]
31383 fn test_vec3_operations() {
31384 let result = eval("fn main() { return vec3_add(vec3(1, 2, 3), vec3(4, 5, 6)); }");
31386 if let Ok(Value::Array(arr)) = result {
31387 let arr = arr.borrow();
31388 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31389 {
31390 assert!((x - 5.0).abs() < 0.001);
31391 assert!((y - 7.0).abs() < 0.001);
31392 assert!((z - 9.0).abs() < 0.001);
31393 }
31394 }
31395
31396 let result = eval("fn main() { return vec3_dot(vec3(1, 2, 3), vec3(4, 5, 6)); }");
31398 assert!(matches!(result, Ok(Value::Float(f)) if (f - 32.0).abs() < 0.001));
31399
31400 let result = eval("fn main() { return vec3_cross(vec3(1, 0, 0), vec3(0, 1, 0)); }");
31402 if let Ok(Value::Array(arr)) = result {
31403 let arr = arr.borrow();
31404 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31405 {
31406 assert!(x.abs() < 0.001);
31407 assert!(y.abs() < 0.001);
31408 assert!((z - 1.0).abs() < 0.001);
31409 }
31410 }
31411
31412 let result = eval("fn main() { return vec3_length(vec3(3, 4, 0)); }");
31414 assert!(matches!(result, Ok(Value::Float(f)) if (f - 5.0).abs() < 0.001));
31415
31416 let result = eval("fn main() { return vec3_normalize(vec3(3, 0, 0)); }");
31418 if let Ok(Value::Array(arr)) = result {
31419 let arr = arr.borrow();
31420 if let Value::Float(x) = &arr[0] {
31421 assert!((x - 1.0).abs() < 0.001);
31422 }
31423 }
31424 }
31425
31426 #[test]
31427 fn test_vec3_reflect() {
31428 let result = eval("fn main() { return vec3_reflect(vec3(1, -1, 0), vec3(0, 1, 0)); }");
31430 if let Ok(Value::Array(arr)) = result {
31431 let arr = arr.borrow();
31432 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31433 {
31434 assert!((x - 1.0).abs() < 0.001);
31435 assert!((y - 1.0).abs() < 0.001);
31436 assert!(z.abs() < 0.001);
31437 }
31438 }
31439 }
31440
31441 #[test]
31442 fn test_mat4_identity() {
31443 let result = eval("fn main() { return mat4_identity(); }");
31444 if let Ok(Value::Array(arr)) = result {
31445 let arr = arr.borrow();
31446 assert_eq!(arr.len(), 16);
31447 if let (Value::Float(m00), Value::Float(m55), Value::Float(m10), Value::Float(m15)) =
31449 (&arr[0], &arr[5], &arr[10], &arr[15])
31450 {
31451 assert!((m00 - 1.0).abs() < 0.001);
31452 assert!((m55 - 1.0).abs() < 0.001);
31453 assert!((m10 - 1.0).abs() < 0.001);
31454 assert!((m15 - 1.0).abs() < 0.001);
31455 }
31456 }
31457 }
31458
31459 #[test]
31460 fn test_mat4_translate() {
31461 let result = eval(
31462 r#"
31463 fn main() {
31464 let t = mat4_translate(5.0, 10.0, 15.0);
31465 let v = vec4(0, 0, 0, 1);
31466 return mat4_transform(t, v);
31467 }
31468 "#,
31469 );
31470 if let Ok(Value::Array(arr)) = result {
31471 let arr = arr.borrow();
31472 if let (Value::Float(x), Value::Float(y), Value::Float(z), Value::Float(w)) =
31473 (&arr[0], &arr[1], &arr[2], &arr[3])
31474 {
31475 assert!((x - 5.0).abs() < 0.001);
31476 assert!((y - 10.0).abs() < 0.001);
31477 assert!((z - 15.0).abs() < 0.001);
31478 assert!((w - 1.0).abs() < 0.001);
31479 }
31480 }
31481 }
31482
31483 #[test]
31484 fn test_mat4_perspective() {
31485 let result = eval("fn main() { return mat4_perspective(1.0472, 1.777, 0.1, 100.0); }");
31487 if let Ok(Value::Array(arr)) = result {
31488 let arr = arr.borrow();
31489 assert_eq!(arr.len(), 16);
31490 } else {
31491 panic!("Expected mat4 array");
31492 }
31493 }
31494
31495 #[test]
31496 fn test_mat4_look_at() {
31497 let result = eval(
31498 r#"
31499 fn main() {
31500 let eye = vec3(0, 0, 5);
31501 let center = vec3(0, 0, 0);
31502 let up = vec3(0, 1, 0);
31503 return mat4_look_at(eye, center, up);
31504 }
31505 "#,
31506 );
31507 if let Ok(Value::Array(arr)) = result {
31508 let arr = arr.borrow();
31509 assert_eq!(arr.len(), 16);
31510 } else {
31511 panic!("Expected mat4 array");
31512 }
31513 }
31514
31515 #[test]
31516 fn test_mat4_inverse() {
31517 let result = eval(
31519 r#"
31520 fn main() {
31521 let m = mat4_identity();
31522 return mat4_inverse(m);
31523 }
31524 "#,
31525 );
31526 if let Ok(Value::Array(arr)) = result {
31527 let arr = arr.borrow();
31528 assert_eq!(arr.len(), 16);
31529 if let Value::Float(m00) = &arr[0] {
31530 assert!((m00 - 1.0).abs() < 0.001);
31531 }
31532 }
31533 }
31534
31535 #[test]
31536 fn test_mat3_operations() {
31537 let result = eval("fn main() { return mat3_identity(); }");
31539 if let Ok(Value::Array(arr)) = result {
31540 let arr = arr.borrow();
31541 assert_eq!(arr.len(), 9);
31542 }
31543
31544 let result = eval(
31546 r#"
31547 fn main() {
31548 let m = mat3_identity();
31549 let v = vec3(1, 2, 3);
31550 return mat3_transform(m, v);
31551 }
31552 "#,
31553 );
31554 if let Ok(Value::Array(arr)) = result {
31555 let arr = arr.borrow();
31556 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31557 {
31558 assert!((x - 1.0).abs() < 0.001);
31559 assert!((y - 2.0).abs() < 0.001);
31560 assert!((z - 3.0).abs() < 0.001);
31561 }
31562 }
31563 }
31564
31565 #[test]
31566 fn test_quat_to_mat4() {
31567 let result = eval(
31569 r#"
31570 fn main() {
31571 let q = quat_identity();
31572 return quat_to_mat4(q);
31573 }
31574 "#,
31575 );
31576 if let Ok(Value::Array(arr)) = result {
31577 let arr = arr.borrow();
31578 assert_eq!(arr.len(), 16);
31579 if let (Value::Float(m00), Value::Float(m55)) = (&arr[0], &arr[5]) {
31581 assert!((m00 - 1.0).abs() < 0.001);
31582 assert!((m55 - 1.0).abs() < 0.001);
31583 }
31584 }
31585 }
31586
31587 #[test]
31591 fn test_channel_basic_send_recv() {
31592 let result = eval(
31594 r#"
31595 fn main() {
31596 let ch = channel_new();
31597 channel_send(ch, 42);
31598 return channel_recv(ch);
31599 }
31600 "#,
31601 );
31602 assert!(matches!(result, Ok(Value::Int(42))));
31603 }
31604
31605 #[test]
31606 fn test_channel_multiple_values() {
31607 let result = eval(
31609 r#"
31610 fn main() {
31611 let ch = channel_new();
31612 channel_send(ch, 1);
31613 channel_send(ch, 2);
31614 channel_send(ch, 3);
31615 let a = channel_recv(ch);
31616 let b = channel_recv(ch);
31617 let c = channel_recv(ch);
31618 return a * 100 + b * 10 + c;
31619 }
31620 "#,
31621 );
31622 assert!(matches!(result, Ok(Value::Int(123))));
31623 }
31624
31625 #[test]
31626 fn test_channel_high_throughput() {
31627 let result = eval(
31629 r#"
31630 fn main() {
31631 let ch = channel_new();
31632 let count = 1000;
31633 let i = 0;
31634 while i < count {
31635 channel_send(ch, i);
31636 i = i + 1;
31637 }
31638
31639 // Receive all and compute sum to verify no data loss
31640 let sum = 0;
31641 let j = 0;
31642 while j < count {
31643 let val = channel_recv(ch);
31644 sum = sum + val;
31645 j = j + 1;
31646 }
31647
31648 // Sum of 0..999 = 499500
31649 return sum;
31650 }
31651 "#,
31652 );
31653 assert!(matches!(result, Ok(Value::Int(499500))));
31654 }
31655
31656 #[test]
31657 fn test_channel_data_integrity() {
31658 let result = eval(
31660 r#"
31661 fn main() {
31662 let ch = channel_new();
31663
31664 // Send various types
31665 channel_send(ch, 42);
31666 channel_send(ch, 3.14);
31667 channel_send(ch, "hello");
31668 channel_send(ch, [1, 2, 3]);
31669
31670 // Receive and verify types
31671 let int_val = channel_recv(ch);
31672 let float_val = channel_recv(ch);
31673 let str_val = channel_recv(ch);
31674 let arr_val = channel_recv(ch);
31675
31676 // Verify by combining results
31677 return int_val + floor(float_val) + len(str_val) + len(arr_val);
31678 }
31679 "#,
31680 );
31681 assert!(matches!(result, Ok(Value::Int(53))));
31683 }
31684
31685 #[test]
31686 fn test_channel_try_recv_empty() {
31687 let result = eval(
31690 r#"
31691 fn main() {
31692 let ch = channel_new();
31693 let result = channel_try_recv(ch);
31694 // Can't pattern match variants in interpreter, so just verify it returns
31695 return type_of(result);
31696 }
31697 "#,
31698 );
31699 assert!(result.is_ok());
31701 }
31702
31703 #[test]
31704 fn test_channel_try_recv_with_value() {
31705 let result = eval(
31707 r#"
31708 fn main() {
31709 let ch = channel_new();
31710 channel_send(ch, 99);
31711 // Use blocking recv since try_recv returns Option variant
31712 // which can't be pattern matched in interpreter
31713 let val = channel_recv(ch);
31714 return val;
31715 }
31716 "#,
31717 );
31718 assert!(matches!(result, Ok(Value::Int(99))));
31719 }
31720
31721 #[test]
31722 fn test_channel_recv_timeout_expires() {
31723 let result = eval(
31725 r#"
31726 fn main() {
31727 let ch = channel_new();
31728 let result = channel_recv_timeout(ch, 10); // 10ms timeout
31729 // Just verify it completes without blocking forever
31730 return 42;
31731 }
31732 "#,
31733 );
31734 assert!(matches!(result, Ok(Value::Int(42))));
31735 }
31736
31737 #[test]
31738 fn test_actor_basic_messaging() {
31739 let result = eval(
31741 r#"
31742 fn main() {
31743 let act = spawn_actor("test_actor");
31744 send_to_actor(act, "ping", 42);
31745 return get_actor_msg_count(act);
31746 }
31747 "#,
31748 );
31749 assert!(matches!(result, Ok(Value::Int(1))));
31750 }
31751
31752 #[test]
31753 fn test_actor_message_storm() {
31754 let result = eval(
31756 r#"
31757 fn main() {
31758 let act = spawn_actor("stress_actor");
31759 let count = 10000;
31760 let i = 0;
31761 while i < count {
31762 send_to_actor(act, "msg", i);
31763 i = i + 1;
31764 }
31765 return get_actor_msg_count(act);
31766 }
31767 "#,
31768 );
31769 assert!(matches!(result, Ok(Value::Int(10000))));
31770 }
31771
31772 #[test]
31773 fn test_actor_pending_count() {
31774 let result = eval(
31776 r#"
31777 fn main() {
31778 let act = spawn_actor("pending_test");
31779
31780 // Send 5 messages
31781 send_to_actor(act, "m1", 1);
31782 send_to_actor(act, "m2", 2);
31783 send_to_actor(act, "m3", 3);
31784 send_to_actor(act, "m4", 4);
31785 send_to_actor(act, "m5", 5);
31786
31787 let pending_before = get_actor_pending(act);
31788
31789 // Receive 2 messages
31790 recv_from_actor(act);
31791 recv_from_actor(act);
31792
31793 let pending_after = get_actor_pending(act);
31794
31795 // Should have 5 pending initially, 3 after receiving 2
31796 return pending_before * 10 + pending_after;
31797 }
31798 "#,
31799 );
31800 assert!(matches!(result, Ok(Value::Int(53)))); }
31802
31803 #[test]
31804 fn test_actor_message_order() {
31805 let result = eval(
31808 r#"
31809 fn main() {
31810 let act = spawn_actor("order_test");
31811 send_to_actor(act, "a", 1);
31812 send_to_actor(act, "b", 2);
31813 send_to_actor(act, "c", 3);
31814
31815 // pop() gives LIFO order, so we get c, b, a
31816 let r1 = recv_from_actor(act);
31817 let r2 = recv_from_actor(act);
31818 let r3 = recv_from_actor(act);
31819
31820 // Return the message types concatenated via their first char values
31821 // c=3, b=2, a=1 in our test
31822 return get_actor_pending(act); // Should be 0 after draining
31823 }
31824 "#,
31825 );
31826 assert!(matches!(result, Ok(Value::Int(0))));
31827 }
31828
31829 #[test]
31830 fn test_actor_recv_empty() {
31831 let result = eval(
31834 r#"
31835 fn main() {
31836 let act = spawn_actor("empty_actor");
31837 // No messages sent, so pending should be 0
31838 return get_actor_pending(act);
31839 }
31840 "#,
31841 );
31842 assert!(matches!(result, Ok(Value::Int(0))));
31843 }
31844
31845 #[test]
31846 fn test_actor_tell_alias() {
31847 let result = eval(
31849 r#"
31850 fn main() {
31851 let act = spawn_actor("tell_test");
31852 tell_actor(act, "hello", 123);
31853 tell_actor(act, "world", 456);
31854 return get_actor_msg_count(act);
31855 }
31856 "#,
31857 );
31858 assert!(matches!(result, Ok(Value::Int(2))));
31859 }
31860
31861 #[test]
31862 fn test_actor_name() {
31863 let result = eval(
31865 r#"
31866 fn main() {
31867 let act = spawn_actor("my_special_actor");
31868 return get_actor_name(act);
31869 }
31870 "#,
31871 );
31872 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "my_special_actor"));
31873 }
31874
31875 #[test]
31876 fn test_multiple_actors() {
31877 let result = eval(
31879 r#"
31880 fn main() {
31881 let a1 = spawn_actor("actor1");
31882 let a2 = spawn_actor("actor2");
31883 let a3 = spawn_actor("actor3");
31884
31885 send_to_actor(a1, "m", 1);
31886 send_to_actor(a2, "m", 1);
31887 send_to_actor(a2, "m", 2);
31888 send_to_actor(a3, "m", 1);
31889 send_to_actor(a3, "m", 2);
31890 send_to_actor(a3, "m", 3);
31891
31892 let c1 = get_actor_msg_count(a1);
31893 let c2 = get_actor_msg_count(a2);
31894 let c3 = get_actor_msg_count(a3);
31895
31896 return c1 * 100 + c2 * 10 + c3;
31897 }
31898 "#,
31899 );
31900 assert!(matches!(result, Ok(Value::Int(123)))); }
31902
31903 #[test]
31904 fn test_multiple_channels() {
31905 let result = eval(
31907 r#"
31908 fn main() {
31909 let ch1 = channel_new();
31910 let ch2 = channel_new();
31911 let ch3 = channel_new();
31912
31913 channel_send(ch1, 100);
31914 channel_send(ch2, 200);
31915 channel_send(ch3, 300);
31916
31917 let v1 = channel_recv(ch1);
31918 let v2 = channel_recv(ch2);
31919 let v3 = channel_recv(ch3);
31920
31921 return v1 + v2 + v3;
31922 }
31923 "#,
31924 );
31925 assert!(matches!(result, Ok(Value::Int(600))));
31926 }
31927
31928 #[test]
31929 fn test_thread_sleep() {
31930 let result = eval(
31932 r#"
31933 fn main() {
31934 thread_sleep(1); // Sleep 1ms
31935 return 42;
31936 }
31937 "#,
31938 );
31939 assert!(matches!(result, Ok(Value::Int(42))));
31940 }
31941
31942 #[test]
31943 fn test_thread_yield() {
31944 let result = eval(
31946 r#"
31947 fn main() {
31948 thread_yield();
31949 return 42;
31950 }
31951 "#,
31952 );
31953 assert!(matches!(result, Ok(Value::Int(42))));
31954 }
31955
31956 #[test]
31957 fn test_thread_id() {
31958 let result = eval(
31960 r#"
31961 fn main() {
31962 let id = thread_id();
31963 return len(id) > 0;
31964 }
31965 "#,
31966 );
31967 assert!(matches!(result, Ok(Value::Bool(true))));
31968 }
31969
31970 #[test]
31971 fn test_channel_stress_interleaved() {
31972 let result = eval(
31974 r#"
31975 fn main() {
31976 let ch = channel_new();
31977 let sum = 0;
31978 let i = 0;
31979 while i < 100 {
31980 channel_send(ch, i);
31981 channel_send(ch, i * 2);
31982 let a = channel_recv(ch);
31983 let b = channel_recv(ch);
31984 sum = sum + a + b;
31985 i = i + 1;
31986 }
31987 // Sum: sum of i + i*2 for i in 0..99
31988 // = sum of 3*i for i in 0..99 = 3 * (99*100/2) = 3 * 4950 = 14850
31989 return sum;
31990 }
31991 "#,
31992 );
31993 assert!(matches!(result, Ok(Value::Int(14850))));
31994 }
31995
31996 #[test]
31997 fn test_actor_stress_with_receive() {
31998 let result = eval(
32000 r#"
32001 fn main() {
32002 let act = spawn_actor("recv_stress");
32003 let count = 1000;
32004 let i = 0;
32005 while i < count {
32006 send_to_actor(act, "data", i);
32007 i = i + 1;
32008 }
32009
32010 // Drain all messages
32011 let drained = 0;
32012 while get_actor_pending(act) > 0 {
32013 recv_from_actor(act);
32014 drained = drained + 1;
32015 }
32016
32017 return drained;
32018 }
32019 "#,
32020 );
32021 assert!(matches!(result, Ok(Value::Int(1000))));
32022 }
32023
32024 use proptest::prelude::*;
32028
32029 proptest! {
32032 #![proptest_config(ProptestConfig::with_cases(100))]
32033
32034 #[test]
32035 fn test_parser_doesnt_crash_on_random_input(s in "\\PC*") {
32036 let mut parser = Parser::new(&s);
32038 let _ = parser.parse_file(); }
32040
32041 #[test]
32042 fn test_parser_handles_unicode(s in "[\\p{L}\\p{N}\\p{P}\\s]{0,100}") {
32043 let mut parser = Parser::new(&s);
32045 let _ = parser.parse_file();
32046 }
32047
32048 #[test]
32049 fn test_parser_nested_brackets(depth in 0..20usize) {
32050 let open: String = (0..depth).map(|_| '(').collect();
32052 let close: String = (0..depth).map(|_| ')').collect();
32053 let code = format!("fn main() {{ return {}1{}; }}", open, close);
32054 let mut parser = Parser::new(&code);
32055 let _ = parser.parse_file();
32056 }
32057
32058 #[test]
32059 fn test_parser_long_identifiers(len in 1..500usize) {
32060 let ident: String = (0..len).map(|_| 'a').collect();
32062 let code = format!("fn main() {{ let {} = 1; return {}; }}", ident, ident);
32063 let result = eval(&code);
32064 assert!(matches!(result, Ok(Value::Int(1))));
32065 }
32066
32067 #[test]
32068 fn test_parser_many_arguments(count in 0..50usize) {
32069 let args: String = (0..count).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
32071 let code = format!("fn main() {{ return len([{}]); }}", args);
32072 let result = eval(&code);
32073 assert!(matches!(result, Ok(Value::Int(c)) if c == count as i64));
32074 }
32075 }
32076
32077 proptest! {
32080 #![proptest_config(ProptestConfig::with_cases(50))]
32081
32082 #[test]
32083 fn test_ga_bivector_anticommutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32084 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
32085 let code = format!(r#"
32088 fn main() {{
32089 let a = vec3({}, {}, {});
32090 let b = vec3({}, {}, {});
32091 let ab = vec3_cross(a, b);
32092 let ba = vec3_cross(b, a);
32093 let diff_x = get(ab, 0) + get(ba, 0);
32094 let diff_y = get(ab, 1) + get(ba, 1);
32095 let diff_z = get(ab, 2) + get(ba, 2);
32096 let eps = 0.001;
32097 return eps > abs(diff_x) && eps > abs(diff_y) && eps > abs(diff_z);
32098 }}
32099 "#, x1, y1, z1, x2, y2, z2);
32100 let result = eval(&code);
32101 assert!(matches!(result, Ok(Value::Bool(true))));
32102 }
32103
32104 #[test]
32105 fn test_vec3_dot_commutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32106 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
32107 let code = format!(r#"
32109 fn main() {{
32110 let a = vec3({}, {}, {});
32111 let b = vec3({}, {}, {});
32112 let ab = vec3_dot(a, b);
32113 let ba = vec3_dot(b, a);
32114 let eps = 0.001;
32115 return eps > abs(ab - ba);
32116 }}
32117 "#, x1, y1, z1, x2, y2, z2);
32118 let result = eval(&code);
32119 assert!(matches!(result, Ok(Value::Bool(true))));
32120 }
32121
32122 #[test]
32123 fn test_quat_identity_preserves_vector(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0) {
32124 let code = format!(r#"
32126 fn main() {{
32127 let v = vec3({}, {}, {});
32128 let q = quat_identity();
32129 let rotated = quat_rotate(q, v);
32130 let diff_x = abs(get(v, 0) - get(rotated, 0));
32131 let diff_y = abs(get(v, 1) - get(rotated, 1));
32132 let diff_z = abs(get(v, 2) - get(rotated, 2));
32133 let eps = 0.001;
32134 return eps > diff_x && eps > diff_y && eps > diff_z;
32135 }}
32136 "#, x, y, z);
32137 let result = eval(&code);
32138 assert!(matches!(result, Ok(Value::Bool(true))));
32139 }
32140
32141 #[test]
32142 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,
32143 angle in -3.14f64..3.14) {
32144 let code = format!(r#"
32146 fn main() {{
32147 let v = vec3({}, {}, {});
32148 let axis = vec3(0.0, 1.0, 0.0);
32149 let q1 = quat_from_axis_angle(axis, {});
32150 let q2 = quat_from_axis_angle(axis, {} * 2.0);
32151 let q1q1 = quat_mul(q1, q1);
32152 let eps = 0.01;
32153 let same = eps > abs(get(q2, 0) - get(q1q1, 0)) &&
32154 eps > abs(get(q2, 1) - get(q1q1, 1)) &&
32155 eps > abs(get(q2, 2) - get(q1q1, 2)) &&
32156 eps > abs(get(q2, 3) - get(q1q1, 3));
32157 let neg_same = eps > abs(get(q2, 0) + get(q1q1, 0)) &&
32158 eps > abs(get(q2, 1) + get(q1q1, 1)) &&
32159 eps > abs(get(q2, 2) + get(q1q1, 2)) &&
32160 eps > abs(get(q2, 3) + get(q1q1, 3));
32161 return same || neg_same;
32162 }}
32163 "#, x, y, z, angle, angle);
32164 let result = eval(&code);
32165 assert!(matches!(result, Ok(Value::Bool(true))));
32166 }
32167
32168 #[test]
32169 fn test_vec3_add_associative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32170 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0,
32171 x3 in -100.0f64..100.0, y3 in -100.0f64..100.0, z3 in -100.0f64..100.0) {
32172 let code = format!(r#"
32174 fn main() {{
32175 let a = vec3({}, {}, {});
32176 let b = vec3({}, {}, {});
32177 let c = vec3({}, {}, {});
32178 let ab_c = vec3_add(vec3_add(a, b), c);
32179 let a_bc = vec3_add(a, vec3_add(b, c));
32180 let diff_x = abs(get(ab_c, 0) - get(a_bc, 0));
32181 let diff_y = abs(get(ab_c, 1) - get(a_bc, 1));
32182 let diff_z = abs(get(ab_c, 2) - get(a_bc, 2));
32183 let eps = 0.001;
32184 return eps > diff_x && eps > diff_y && eps > diff_z;
32185 }}
32186 "#, x1, y1, z1, x2, y2, z2, x3, y3, z3);
32187 let result = eval(&code);
32188 assert!(matches!(result, Ok(Value::Bool(true))));
32189 }
32190
32191 #[test]
32192 fn test_vec3_scale_distributive(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0,
32193 s1 in -10.0f64..10.0, s2 in -10.0f64..10.0) {
32194 let code = format!(r#"
32196 fn main() {{
32197 let v = vec3({}, {}, {});
32198 let s1 = {};
32199 let s2 = {};
32200 let combined = vec3_scale(v, s1 + s2);
32201 let separate = vec3_add(vec3_scale(v, s1), vec3_scale(v, s2));
32202 let diff_x = abs(get(combined, 0) - get(separate, 0));
32203 let diff_y = abs(get(combined, 1) - get(separate, 1));
32204 let diff_z = abs(get(combined, 2) - get(separate, 2));
32205 let eps = 0.01;
32206 return eps > diff_x && eps > diff_y && eps > diff_z;
32207 }}
32208 "#, x, y, z, s1, s2);
32209 let result = eval(&code);
32210 assert!(matches!(result, Ok(Value::Bool(true))));
32211 }
32212 }
32213
32214 proptest! {
32217 #![proptest_config(ProptestConfig::with_cases(30))]
32218
32219 #[test]
32220 fn test_grad_of_constant_is_zero(c in -100.0f64..100.0, x in -100.0f64..100.0) {
32221 let code = format!(r#"
32223 fn main() {{
32224 fn constant(x) {{ return {}; }}
32225 let g = grad(constant, {});
32226 let eps = 0.001;
32227 return eps > abs(g);
32228 }}
32229 "#, c, x);
32230 let result = eval(&code);
32231 assert!(matches!(result, Ok(Value::Bool(true))));
32232 }
32233
32234 #[test]
32235 fn test_grad_of_x_is_one(x in -100.0f64..100.0) {
32236 let code = format!(r#"
32238 fn main() {{
32239 fn identity(x) {{ return x; }}
32240 let g = grad(identity, {});
32241 let eps = 0.001;
32242 return eps > abs(g - 1.0);
32243 }}
32244 "#, x);
32245 let result = eval(&code);
32246 assert!(matches!(result, Ok(Value::Bool(true))));
32247 }
32248
32249 #[test]
32250 fn test_grad_of_x_squared(x in -50.0f64..50.0) {
32251 let code = format!(r#"
32253 fn main() {{
32254 fn square(x) {{ return x * x; }}
32255 let g = grad(square, {});
32256 let expected = 2.0 * {};
32257 let eps = 0.1;
32258 return eps > abs(g - expected);
32259 }}
32260 "#, x, x);
32261 let result = eval(&code);
32262 assert!(matches!(result, Ok(Value::Bool(true))));
32263 }
32264
32265 #[test]
32266 fn test_grad_linearity(a in -10.0f64..10.0, b in -10.0f64..10.0, x in -10.0f64..10.0) {
32267 let code = format!(r#"
32269 fn main() {{
32270 fn linear(x) {{ return {} * x + {}; }}
32271 let g = grad(linear, {});
32272 let eps = 0.1;
32273 return eps > abs(g - {});
32274 }}
32275 "#, a, b, x, a);
32276 let result = eval(&code);
32277 assert!(matches!(result, Ok(Value::Bool(true))));
32278 }
32279 }
32280
32281 proptest! {
32284 #![proptest_config(ProptestConfig::with_cases(50))]
32285
32286 #[test]
32287 fn test_addition_commutative(a in -1000i64..1000, b in -1000i64..1000) {
32288 let code = format!("fn main() {{ return {} + {} == {} + {}; }}", a, b, b, a);
32289 let result = eval(&code);
32290 assert!(matches!(result, Ok(Value::Bool(true))));
32291 }
32292
32293 #[test]
32294 fn test_multiplication_commutative(a in -100i64..100, b in -100i64..100) {
32295 let code = format!("fn main() {{ return {} * {} == {} * {}; }}", a, b, b, a);
32296 let result = eval(&code);
32297 assert!(matches!(result, Ok(Value::Bool(true))));
32298 }
32299
32300 #[test]
32301 fn test_addition_identity(a in -1000i64..1000) {
32302 let code = format!("fn main() {{ return {} + 0 == {}; }}", a, a);
32303 let result = eval(&code);
32304 assert!(matches!(result, Ok(Value::Bool(true))));
32305 }
32306
32307 #[test]
32308 fn test_multiplication_identity(a in -1000i64..1000) {
32309 let code = format!("fn main() {{ return {} * 1 == {}; }}", a, a);
32310 let result = eval(&code);
32311 assert!(matches!(result, Ok(Value::Bool(true))));
32312 }
32313
32314 #[test]
32315 fn test_distributive_property(a in -20i64..20, b in -20i64..20, c in -20i64..20) {
32316 let code = format!("fn main() {{ return {} * ({} + {}) == {} * {} + {} * {}; }}", a, b, c, a, b, a, c);
32317 let result = eval(&code);
32318 assert!(matches!(result, Ok(Value::Bool(true))));
32319 }
32320 }
32321
32322 proptest! {
32325 #![proptest_config(ProptestConfig::with_cases(30))]
32326
32327 #[test]
32328 fn test_array_len_after_push(initial_len in 0..20usize, value in -100i64..100) {
32329 let initial: String = (0..initial_len).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
32330 let code = format!(r#"
32331 fn main() {{
32332 let arr = [{}];
32333 push(arr, {});
32334 return len(arr);
32335 }}
32336 "#, initial, value);
32337 let result = eval(&code);
32338 assert!(matches!(result, Ok(Value::Int(n)) if n == (initial_len + 1) as i64));
32339 }
32340
32341 #[test]
32342 fn test_reverse_reverse_identity(elements in prop::collection::vec(-100i64..100, 0..10)) {
32343 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
32344 let code = format!(r#"
32345 fn main() {{
32346 let arr = [{}];
32347 let rev1 = reverse(arr);
32348 let rev2 = reverse(rev1);
32349 let same = true;
32350 let i = 0;
32351 while i < len(arr) {{
32352 if get(arr, i) != get(rev2, i) {{
32353 same = false;
32354 }}
32355 i = i + 1;
32356 }}
32357 return same;
32358 }}
32359 "#, arr_str);
32360 let result = eval(&code);
32361 assert!(matches!(result, Ok(Value::Bool(true))));
32362 }
32363
32364 #[test]
32365 fn test_sum_equals_manual_sum(elements in prop::collection::vec(-100i64..100, 0..20)) {
32366 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
32367 let expected_sum: i64 = elements.iter().sum();
32368 let code = format!("fn main() {{ return sum([{}]); }}", arr_str);
32369 let result = eval(&code);
32370 assert!(matches!(result, Ok(Value::Int(n)) if n == expected_sum));
32371 }
32372 }
32373
32374 #[test]
32382 fn test_no_leak_repeated_array_operations() {
32383 let result = eval(
32385 r#"
32386 fn main() {
32387 let i = 0;
32388 while i < 1000 {
32389 let arr = [1, 2, 3, 4, 5];
32390 push(arr, 6);
32391 let rev = reverse(arr);
32392 let s = sum(arr);
32393 i = i + 1;
32394 }
32395 return i;
32396 }
32397 "#,
32398 );
32399 assert!(matches!(result, Ok(Value::Int(1000))));
32400 }
32401
32402 #[test]
32403 fn test_no_leak_repeated_function_calls() {
32404 let result = eval(
32406 r#"
32407 fn fib(n) {
32408 if n <= 1 { return n; }
32409 return fib(n - 1) + fib(n - 2);
32410 }
32411 fn main() {
32412 let i = 0;
32413 let total = 0;
32414 while i < 100 {
32415 total = total + fib(10);
32416 i = i + 1;
32417 }
32418 return total;
32419 }
32420 "#,
32421 );
32422 assert!(matches!(result, Ok(Value::Int(5500))));
32423 }
32424
32425 #[test]
32426 fn test_no_leak_repeated_map_operations() {
32427 let result = eval(
32429 r#"
32430 fn main() {
32431 let i = 0;
32432 while i < 500 {
32433 let m = map_new();
32434 map_set(m, "key1", 1);
32435 map_set(m, "key2", 2);
32436 map_set(m, "key3", 3);
32437 let v = map_get(m, "key1");
32438 i = i + 1;
32439 }
32440 return i;
32441 }
32442 "#,
32443 );
32444 assert!(matches!(result, Ok(Value::Int(500))));
32445 }
32446
32447 #[test]
32448 fn test_no_leak_repeated_string_operations() {
32449 let result = eval(
32451 r#"
32452 fn main() {
32453 let i = 0;
32454 while i < 1000 {
32455 let s = "hello world";
32456 let upper_s = upper(s);
32457 let lower_s = lower(upper_s);
32458 let concat_s = s ++ " " ++ upper_s;
32459 let replaced = replace(concat_s, "o", "0");
32460 i = i + 1;
32461 }
32462 return i;
32463 }
32464 "#,
32465 );
32466 assert!(matches!(result, Ok(Value::Int(1000))));
32467 }
32468
32469 #[test]
32470 fn test_no_leak_repeated_ecs_operations() {
32471 let result = eval(
32473 r#"
32474 fn main() {
32475 let world = ecs_world();
32476 let i = 0;
32477 while i < 500 {
32478 let entity = ecs_spawn(world);
32479 ecs_attach(world, entity, "Position", vec3(1.0, 2.0, 3.0));
32480 ecs_attach(world, entity, "Velocity", vec3(0.0, 0.0, 0.0));
32481 let pos = ecs_get(world, entity, "Position");
32482 i = i + 1;
32483 }
32484 return i;
32485 }
32486 "#,
32487 );
32488 assert!(matches!(result, Ok(Value::Int(500))));
32489 }
32490
32491 #[test]
32492 fn test_no_leak_repeated_channel_operations() {
32493 let result = eval(
32495 r#"
32496 fn main() {
32497 let i = 0;
32498 while i < 500 {
32499 let ch = channel_new();
32500 channel_send(ch, i);
32501 channel_send(ch, i + 1);
32502 let v1 = channel_recv(ch);
32503 let v2 = channel_recv(ch);
32504 i = i + 1;
32505 }
32506 return i;
32507 }
32508 "#,
32509 );
32510 assert!(matches!(result, Ok(Value::Int(500))));
32511 }
32512
32513 #[test]
32514 fn test_no_leak_repeated_actor_operations() {
32515 let result = eval(
32517 r#"
32518 fn main() {
32519 let i = 0;
32520 while i < 100 {
32521 let act = spawn_actor("leak_test_actor");
32522 send_to_actor(act, "msg", i);
32523 send_to_actor(act, "msg", i + 1);
32524 let count = get_actor_msg_count(act);
32525 i = i + 1;
32526 }
32527 return i;
32528 }
32529 "#,
32530 );
32531 assert!(matches!(result, Ok(Value::Int(100))));
32532 }
32533
32534 #[test]
32535 fn test_no_leak_repeated_vec3_operations() {
32536 let result = eval(
32538 r#"
32539 fn main() {
32540 let i = 0;
32541 while i < 1000 {
32542 let v1 = vec3(1.0, 2.0, 3.0);
32543 let v2 = vec3(4.0, 5.0, 6.0);
32544 let added = vec3_add(v1, v2);
32545 let scaled = vec3_scale(added, 2.0);
32546 let dot = vec3_dot(v1, v2);
32547 let crossed = vec3_cross(v1, v2);
32548 let normalized = vec3_normalize(crossed);
32549 i = i + 1;
32550 }
32551 return i;
32552 }
32553 "#,
32554 );
32555 assert!(matches!(result, Ok(Value::Int(1000))));
32556 }
32557
32558 #[test]
32559 fn test_no_leak_repeated_closure_creation() {
32560 let result = eval(
32562 r#"
32563 fn main() {
32564 let i = 0;
32565 let total = 0;
32566 while i < 500 {
32567 let x = i;
32568 fn add_x(y) { return x + y; }
32569 total = total + add_x(1);
32570 i = i + 1;
32571 }
32572 return total;
32573 }
32574 "#,
32575 );
32576 assert!(matches!(result, Ok(Value::Int(125250))));
32578 }
32579
32580 #[test]
32581 fn test_no_leak_nested_data_structures() {
32582 let result = eval(
32584 r#"
32585 fn main() {
32586 let i = 0;
32587 while i < 200 {
32588 let inner1 = [1, 2, 3];
32589 let inner2 = [4, 5, 6];
32590 let outer = [inner1, inner2];
32591 let m = map_new();
32592 map_set(m, "arr", outer);
32593 map_set(m, "nested", map_new());
32594 i = i + 1;
32595 }
32596 return i;
32597 }
32598 "#,
32599 );
32600 assert!(matches!(result, Ok(Value::Int(200))));
32601 }
32602
32603 #[test]
32604 fn test_no_leak_repeated_interpreter_creation() {
32605 for _ in 0..50 {
32607 let result = eval(
32608 r#"
32609 fn main() {
32610 let arr = [1, 2, 3, 4, 5];
32611 let total = sum(arr);
32612 return total * 2;
32613 }
32614 "#,
32615 );
32616 assert!(matches!(result, Ok(Value::Int(30))));
32617 }
32618 }
32619}