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
254 .globals
255 .borrow_mut()
256 .define("u64·MAX".to_string(), Value::Int(u64::MAX as i64));
257 interp
258 .globals
259 .borrow_mut()
260 .define("u64·MIN".to_string(), Value::Int(0));
261 interp
262 .globals
263 .borrow_mut()
264 .define("i64·MAX".to_string(), Value::Int(i64::MAX));
265 interp
266 .globals
267 .borrow_mut()
268 .define("i64·MIN".to_string(), Value::Int(i64::MIN));
269 interp
270 .globals
271 .borrow_mut()
272 .define("u32·MAX".to_string(), Value::Int(u32::MAX as i64));
273 interp
274 .globals
275 .borrow_mut()
276 .define("u32·MIN".to_string(), Value::Int(0));
277 interp
278 .globals
279 .borrow_mut()
280 .define("i32·MAX".to_string(), Value::Int(i32::MAX as i64));
281 interp
282 .globals
283 .borrow_mut()
284 .define("i32·MIN".to_string(), Value::Int(i32::MIN as i64));
285 interp
286 .globals
287 .borrow_mut()
288 .define("u16·MAX".to_string(), Value::Int(u16::MAX as i64));
289 interp
290 .globals
291 .borrow_mut()
292 .define("u8·MAX".to_string(), Value::Int(u8::MAX as i64));
293 interp
294 .globals
295 .borrow_mut()
296 .define("usize·MAX".to_string(), Value::Int(usize::MAX as i64));
297 interp
298 .globals
299 .borrow_mut()
300 .define("isize·MAX".to_string(), Value::Int(isize::MAX as i64));
301 interp
302 .globals
303 .borrow_mut()
304 .define("isize·MIN".to_string(), Value::Int(isize::MIN as i64));
305 interp
306 .globals
307 .borrow_mut()
308 .define("f64·INFINITY".to_string(), Value::Float(f64::INFINITY));
309 interp.globals.borrow_mut().define(
310 "f64·NEG_INFINITY".to_string(),
311 Value::Float(f64::NEG_INFINITY),
312 );
313 interp
314 .globals
315 .borrow_mut()
316 .define("f64·NAN".to_string(), Value::Float(f64::NAN));
317
318 interp.variant_constructors.insert(
320 "SeekFrom·Start".to_string(),
321 ("SeekFrom".to_string(), "Start".to_string(), 1),
322 );
323 interp.variant_constructors.insert(
324 "SeekFrom·End".to_string(),
325 ("SeekFrom".to_string(), "End".to_string(), 1),
326 );
327 interp.variant_constructors.insert(
328 "SeekFrom·Current".to_string(),
329 ("SeekFrom".to_string(), "Current".to_string(), 1),
330 );
331
332 let ordering_variants = ["SeqCst", "Acquire", "Release", "AcqRel", "Relaxed"];
334 for variant in ordering_variants {
335 let full_name = format!("std·sync·atomic·Ordering·{}", variant);
336 let short_name = format!("Ordering·{}", variant);
337 interp.globals.borrow_mut().define(
338 full_name,
339 Value::Variant {
340 enum_name: "Ordering".to_string(),
341 variant_name: variant.to_string(),
342 fields: None,
343 },
344 );
345 interp.globals.borrow_mut().define(
346 short_name,
347 Value::Variant {
348 enum_name: "Ordering".to_string(),
349 variant_name: variant.to_string(),
350 fields: None,
351 },
352 );
353 }
354
355 let error_kind_variants = [
357 "NotFound",
358 "PermissionDenied",
359 "ConnectionRefused",
360 "ConnectionReset",
361 "ConnectionAborted",
362 "NotConnected",
363 "AddrInUse",
364 "AddrNotAvailable",
365 "BrokenPipe",
366 "AlreadyExists",
367 "WouldBlock",
368 "InvalidInput",
369 "InvalidData",
370 "TimedOut",
371 "WriteZero",
372 "Interrupted",
373 "UnexpectedEof",
374 "Other",
375 ];
376 for variant in error_kind_variants {
377 let full_name = format!("std·io·ErrorKind·{}", variant);
378 let short_name = format!("ErrorKind·{}", variant);
379 interp.globals.borrow_mut().define(
380 full_name,
381 Value::Variant {
382 enum_name: "ErrorKind".to_string(),
383 variant_name: variant.to_string(),
384 fields: None,
385 },
386 );
387 interp.globals.borrow_mut().define(
388 short_name,
389 Value::Variant {
390 enum_name: "ErrorKind".to_string(),
391 variant_name: variant.to_string(),
392 fields: None,
393 },
394 );
395 }
396
397 define(interp, "print", None, |interp, args| {
399 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
400 let line = output.join(" ");
401 print!("{}", line);
402 std::io::stdout().flush().ok();
403 interp.output.push(line);
404 Ok(Value::Null)
405 });
406
407 define(interp, "println", None, |interp, args| {
409 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
410 let line = output.join(" ");
411 println!("{}", line);
412 interp.output.push(line);
413 Ok(Value::Null)
414 });
415
416 define(interp, "eprint", None, |interp, args| {
418 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
419 let line = output.join(" ");
420 eprint!("{}", line);
421 std::io::stderr().flush().ok();
422 interp.output.push(line);
423 Ok(Value::Null)
424 });
425
426 define(interp, "eprintln", None, |interp, args| {
428 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
429 let line = output.join(" ");
430 eprintln!("{}", line);
431 interp.output.push(line);
432 Ok(Value::Null)
433 });
434
435 define(interp, "dbg", Some(1), |interp, args| {
437 let output = format!("[DEBUG] {:?}", args[0]);
438 println!("{}", output);
439 interp.output.push(output);
440 Ok(args[0].clone())
441 });
442
443 define(interp, "type_of", Some(1), |_, args| {
445 let type_name = match &args[0] {
446 Value::Null => "null",
447 Value::Bool(_) => "bool",
448 Value::Int(_) => "i64",
449 Value::Float(_) => "f64",
450 Value::String(_) => "str",
451 Value::Char(_) => "char",
452 Value::Array(_) => "array",
453 Value::Tuple(_) => "tuple",
454 Value::Struct { name, .. } => name,
455 Value::Variant { enum_name, .. } => enum_name,
456 Value::Function(_) => "fn",
457 Value::BuiltIn(_) => "builtin",
458 Value::Ref(_) => "ref",
459 Value::Infinity => "infinity",
460 Value::Empty => "empty",
461 Value::Evidential { evidence, .. } => match evidence {
462 Evidence::Known => "known",
463 Evidence::Uncertain => "uncertain",
464 Evidence::Reported => "reported",
465 Evidence::Paradox => "paradox",
466 },
467 Value::Affective { .. } => "affective",
468 Value::Map(_) => "map",
469 Value::Set(_) => "set",
470 Value::Channel(_) => "channel",
471 Value::ThreadHandle(_) => "thread",
472 Value::Actor(_) => "actor",
473 Value::Future(_) => "future",
474 Value::VariantConstructor { .. } => "variant_constructor",
475 Value::DefaultConstructor { .. } => "default_constructor",
476 Value::Range { .. } => "range",
477 };
478 Ok(Value::String(Rc::new(type_name.to_string())))
479 });
480
481 define(interp, "assert", None, |_, args| {
483 if args.is_empty() {
484 return Err(RuntimeError::new("assert() requires at least one argument"));
485 }
486 let condition = match &args[0] {
487 Value::Bool(b) => *b,
488 _ => return Err(RuntimeError::new("assert() condition must be bool")),
489 };
490 if !condition {
491 let msg = if args.len() > 1 {
492 format!("{}", args[1])
493 } else {
494 "assertion failed".to_string()
495 };
496 return Err(RuntimeError::new(format!("Assertion failed: {}", msg)));
497 }
498 Ok(Value::Null)
499 });
500
501 define(interp, "panic", None, |_, args| {
503 let msg = if args.is_empty() {
504 "explicit panic".to_string()
505 } else {
506 args.iter()
507 .map(|v| format!("{}", v))
508 .collect::<Vec<_>>()
509 .join(" ")
510 };
511 Err(RuntimeError::new(format!("PANIC: {}", msg)))
512 });
513
514 define(interp, "todo", None, |_, args| {
516 let msg = if args.is_empty() {
517 "not yet implemented".to_string()
518 } else {
519 format!("{}", args[0])
520 };
521 Err(RuntimeError::new(format!("TODO: {}", msg)))
522 });
523
524 define(interp, "unreachable", None, |_, args| {
526 let msg = if args.is_empty() {
527 "entered unreachable code".to_string()
528 } else {
529 format!("{}", args[0])
530 };
531 Err(RuntimeError::new(format!("UNREACHABLE: {}", msg)))
532 });
533
534 define(interp, "clone", Some(1), |_, args| Ok(deep_clone(&args[0])));
536
537 define(interp, "id", Some(1), |_, args| Ok(args[0].clone()));
539
540 define(interp, "default", None, |interp, args| {
543 let type_name = if args.is_empty() {
544 match &interp.current_self_type {
547 Some(t) => t.clone(),
548 None => {
549 return Ok(Value::Struct {
550 name: "Default".to_string(),
551 fields: Rc::new(RefCell::new(std::collections::HashMap::new())),
552 })
553 }
554 }
555 } else {
556 match &args[0] {
557 Value::String(s) => s.to_string(),
558 _ => return Err(RuntimeError::new("default() requires type name string")),
559 }
560 };
561 let type_name = type_name.as_str();
562 match type_name {
563 "bool" => Ok(Value::Bool(false)),
564 "i64" | "int" => Ok(Value::Int(0)),
565 "f64" | "float" => Ok(Value::Float(0.0)),
566 "str" | "string" => Ok(Value::String(Rc::new(String::new()))),
567 "array" => Ok(Value::Array(Rc::new(RefCell::new(Vec::new())))),
568 _ => {
569 if let Some(struct_def) = interp.default_structs.get(type_name).cloned() {
571 use crate::ast::StructFields;
572 let mut fields = std::collections::HashMap::new();
573 if let StructFields::Named(field_defs) = &struct_def.fields {
574 for field in field_defs {
575 let default_val = if let Some(ref default_expr) = field.default {
577 match interp.evaluate(default_expr) {
578 Ok(v) => v,
579 Err(_) => Value::Null,
580 }
581 } else {
582 Value::Null
583 };
584 fields.insert(field.name.name.clone(), default_val);
585 }
586 }
587 Ok(Value::Struct {
588 name: type_name.to_string(),
589 fields: Rc::new(RefCell::new(fields)),
590 })
591 } else {
592 Ok(Value::Struct {
594 name: type_name.to_string(),
595 fields: Rc::new(RefCell::new(std::collections::HashMap::new())),
596 })
597 }
598 }
599 }
600 });
601
602 define(interp, "Result·Ok", Some(1), |_, args| {
604 Ok(Value::Variant {
605 enum_name: "Result".to_string(),
606 variant_name: "Ok".to_string(),
607 fields: Some(Rc::new(vec![args[0].clone()])),
608 })
609 });
610
611 define(interp, "Ok", Some(1), |_, args| {
613 Ok(Value::Variant {
614 enum_name: "Result".to_string(),
615 variant_name: "Ok".to_string(),
616 fields: Some(Rc::new(vec![args[0].clone()])),
617 })
618 });
619
620 define(interp, "Result·Err", Some(1), |_, args| {
622 Ok(Value::Variant {
623 enum_name: "Result".to_string(),
624 variant_name: "Err".to_string(),
625 fields: Some(Rc::new(vec![args[0].clone()])),
626 })
627 });
628
629 define(interp, "Err", Some(1), |_, args| {
631 Ok(Value::Variant {
632 enum_name: "Result".to_string(),
633 variant_name: "Err".to_string(),
634 fields: Some(Rc::new(vec![args[0].clone()])),
635 })
636 });
637
638 define(interp, "Option·Some", Some(1), |_, args| {
640 Ok(Value::Variant {
641 enum_name: "Option".to_string(),
642 variant_name: "Some".to_string(),
643 fields: Some(Rc::new(vec![args[0].clone()])),
644 })
645 });
646
647 define(interp, "Some", Some(1), |_, args| {
649 Ok(Value::Variant {
650 enum_name: "Option".to_string(),
651 variant_name: "Some".to_string(),
652 fields: Some(Rc::new(vec![args[0].clone()])),
653 })
654 });
655
656 interp.globals.borrow_mut().define(
658 "Option·None".to_string(),
659 Value::Variant {
660 enum_name: "Option".to_string(),
661 variant_name: "None".to_string(),
662 fields: None,
663 },
664 );
665
666 interp.globals.borrow_mut().define(
668 "None".to_string(),
669 Value::Variant {
670 enum_name: "Option".to_string(),
671 variant_name: "None".to_string(),
672 fields: None,
673 },
674 );
675
676 define(interp, "Map·new", Some(0), |_, _| {
678 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
679 });
680
681 define(interp, "HashMap·new", Some(0), |_, _| {
683 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
684 });
685
686 define(interp, "HashMap·with_capacity", Some(1), |_, _args| {
688 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
690 });
691
692 define(interp, "std·collections·HashMap·new", Some(0), |_, _| {
694 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
695 });
696
697 define(
699 interp,
700 "std·collections·HashMap·with_capacity",
701 Some(1),
702 |_, _args| Ok(Value::Map(Rc::new(RefCell::new(HashMap::new())))),
703 );
704
705 define(interp, "HashSet·new", Some(0), |_, _| {
707 Ok(Value::Set(Rc::new(RefCell::new(
708 std::collections::HashSet::new(),
709 ))))
710 });
711
712 define(interp, "HashSet·with_capacity", Some(1), |_, _args| {
714 Ok(Value::Set(Rc::new(RefCell::new(
715 std::collections::HashSet::new(),
716 ))))
717 });
718
719 define(interp, "std·collections·HashSet·new", Some(0), |_, _| {
721 Ok(Value::Set(Rc::new(RefCell::new(
722 std::collections::HashSet::new(),
723 ))))
724 });
725
726 define(interp, "Vec·new", Some(0), |_, _| {
728 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
729 });
730
731 define(interp, "String·new", Some(0), |_, _| {
733 Ok(Value::String(Rc::new(String::new())))
734 });
735
736 define(interp, "String·from", Some(1), |_, args| {
738 let s = match &args[0] {
739 Value::String(s) => (**s).clone(),
740 Value::Int(n) => n.to_string(),
741 Value::Float(f) => f.to_string(),
742 Value::Bool(b) => b.to_string(),
743 Value::Char(c) => c.to_string(),
744 _ => format!("{}", args[0]),
745 };
746 Ok(Value::String(Rc::new(s)))
747 });
748
749 define(interp, "Box·new", Some(1), |_, args| Ok(args[0].clone()));
751
752 define(interp, "String·from_raw_parts", Some(3), |_, args| {
754 match &args[0] {
756 Value::String(s) => Ok(Value::String(s.clone())),
757 Value::Null => Ok(Value::String(Rc::new(String::new()))),
758 _ => Ok(Value::String(Rc::new(format!("{}", args[0])))),
759 }
760 });
761
762 define(interp, "slice·from_raw_parts", Some(2), |_, args| {
764 match &args[0] {
766 Value::String(s) => Ok(Value::String(s.clone())),
767 Value::Array(arr) => Ok(Value::Array(arr.clone())),
768 _ => Ok(args[0].clone()),
769 }
770 });
771}
772
773fn deep_clone(value: &Value) -> Value {
775 match value {
776 Value::Array(arr) => {
777 let cloned: Vec<Value> = arr.borrow().iter().map(deep_clone).collect();
778 Value::Array(Rc::new(RefCell::new(cloned)))
779 }
780 Value::Struct { name, fields } => {
781 let cloned: HashMap<String, Value> = fields
782 .borrow()
783 .iter()
784 .map(|(k, v)| (k.clone(), deep_clone(v)))
785 .collect();
786 Value::Struct {
787 name: name.clone(),
788 fields: Rc::new(RefCell::new(cloned)),
789 }
790 }
791 Value::Evidential { value, evidence } => Value::Evidential {
792 value: Box::new(deep_clone(value)),
793 evidence: *evidence,
794 },
795 other => other.clone(),
796 }
797}
798
799fn register_math(interp: &mut Interpreter) {
804 define(interp, "abs", Some(1), |_, args| match &args[0] {
806 Value::Int(n) => Ok(Value::Int(n.abs())),
807 Value::Float(n) => Ok(Value::Float(n.abs())),
808 _ => Err(RuntimeError::new("abs() requires number")),
809 });
810
811 define(interp, "neg", Some(1), |_, args| match &args[0] {
812 Value::Int(n) => Ok(Value::Int(-n)),
813 Value::Float(n) => Ok(Value::Float(-n)),
814 _ => Err(RuntimeError::new("neg() requires number")),
815 });
816
817 define(interp, "sqrt", Some(1), |_, args| match &args[0] {
818 Value::Int(n) => Ok(Value::Float((*n as f64).sqrt())),
819 Value::Float(n) => Ok(Value::Float(n.sqrt())),
820 _ => Err(RuntimeError::new("sqrt() requires number")),
821 });
822
823 define(interp, "cbrt", Some(1), |_, args| match &args[0] {
824 Value::Int(n) => Ok(Value::Float((*n as f64).cbrt())),
825 Value::Float(n) => Ok(Value::Float(n.cbrt())),
826 _ => Err(RuntimeError::new("cbrt() requires number")),
827 });
828
829 define(interp, "pow", Some(2), |_, args| {
830 match (&args[0], &args[1]) {
831 (Value::Int(base), Value::Int(exp)) => {
832 if *exp >= 0 {
833 Ok(Value::Int(base.pow(*exp as u32)))
834 } else {
835 Ok(Value::Float((*base as f64).powi(*exp as i32)))
836 }
837 }
838 (Value::Float(base), Value::Int(exp)) => Ok(Value::Float(base.powi(*exp as i32))),
839 (Value::Float(base), Value::Float(exp)) => Ok(Value::Float(base.powf(*exp))),
840 (Value::Int(base), Value::Float(exp)) => Ok(Value::Float((*base as f64).powf(*exp))),
841 _ => Err(RuntimeError::new("pow() requires numbers")),
842 }
843 });
844
845 define(interp, "exp", Some(1), |_, args| match &args[0] {
846 Value::Int(n) => Ok(Value::Float((*n as f64).exp())),
847 Value::Float(n) => Ok(Value::Float(n.exp())),
848 _ => Err(RuntimeError::new("exp() requires number")),
849 });
850
851 define(interp, "ln", Some(1), |_, args| match &args[0] {
852 Value::Int(n) => Ok(Value::Float((*n as f64).ln())),
853 Value::Float(n) => Ok(Value::Float(n.ln())),
854 _ => Err(RuntimeError::new("ln() requires number")),
855 });
856
857 define(interp, "log", Some(2), |_, args| {
858 let (value, base) = match (&args[0], &args[1]) {
859 (Value::Int(v), Value::Int(b)) => (*v as f64, *b as f64),
860 (Value::Float(v), Value::Int(b)) => (*v, *b as f64),
861 (Value::Int(v), Value::Float(b)) => (*v as f64, *b),
862 (Value::Float(v), Value::Float(b)) => (*v, *b),
863 _ => return Err(RuntimeError::new("log() requires numbers")),
864 };
865 Ok(Value::Float(value.log(base)))
866 });
867
868 define(interp, "log10", Some(1), |_, args| match &args[0] {
869 Value::Int(n) => Ok(Value::Float((*n as f64).log10())),
870 Value::Float(n) => Ok(Value::Float(n.log10())),
871 _ => Err(RuntimeError::new("log10() requires number")),
872 });
873
874 define(interp, "log2", Some(1), |_, args| match &args[0] {
875 Value::Int(n) => Ok(Value::Float((*n as f64).log2())),
876 Value::Float(n) => Ok(Value::Float(n.log2())),
877 _ => Err(RuntimeError::new("log2() requires number")),
878 });
879
880 define(interp, "sin", Some(1), |_, args| match &args[0] {
882 Value::Int(n) => Ok(Value::Float((*n as f64).sin())),
883 Value::Float(n) => Ok(Value::Float(n.sin())),
884 _ => Err(RuntimeError::new("sin() requires number")),
885 });
886
887 define(interp, "cos", Some(1), |_, args| match &args[0] {
888 Value::Int(n) => Ok(Value::Float((*n as f64).cos())),
889 Value::Float(n) => Ok(Value::Float(n.cos())),
890 _ => Err(RuntimeError::new("cos() requires number")),
891 });
892
893 define(interp, "tan", Some(1), |_, args| match &args[0] {
894 Value::Int(n) => Ok(Value::Float((*n as f64).tan())),
895 Value::Float(n) => Ok(Value::Float(n.tan())),
896 _ => Err(RuntimeError::new("tan() requires number")),
897 });
898
899 define(interp, "asin", Some(1), |_, args| match &args[0] {
900 Value::Int(n) => Ok(Value::Float((*n as f64).asin())),
901 Value::Float(n) => Ok(Value::Float(n.asin())),
902 _ => Err(RuntimeError::new("asin() requires number")),
903 });
904
905 define(interp, "acos", Some(1), |_, args| match &args[0] {
906 Value::Int(n) => Ok(Value::Float((*n as f64).acos())),
907 Value::Float(n) => Ok(Value::Float(n.acos())),
908 _ => Err(RuntimeError::new("acos() requires number")),
909 });
910
911 define(interp, "atan", Some(1), |_, args| match &args[0] {
912 Value::Int(n) => Ok(Value::Float((*n as f64).atan())),
913 Value::Float(n) => Ok(Value::Float(n.atan())),
914 _ => Err(RuntimeError::new("atan() requires number")),
915 });
916
917 define(interp, "atan2", Some(2), |_, args| {
918 let (y, x) = match (&args[0], &args[1]) {
919 (Value::Int(y), Value::Int(x)) => (*y as f64, *x as f64),
920 (Value::Float(y), Value::Int(x)) => (*y, *x as f64),
921 (Value::Int(y), Value::Float(x)) => (*y as f64, *x),
922 (Value::Float(y), Value::Float(x)) => (*y, *x),
923 _ => return Err(RuntimeError::new("atan2() requires numbers")),
924 };
925 Ok(Value::Float(y.atan2(x)))
926 });
927
928 define(interp, "sinh", Some(1), |_, args| match &args[0] {
930 Value::Int(n) => Ok(Value::Float((*n as f64).sinh())),
931 Value::Float(n) => Ok(Value::Float(n.sinh())),
932 _ => Err(RuntimeError::new("sinh() requires number")),
933 });
934
935 define(interp, "cosh", Some(1), |_, args| match &args[0] {
936 Value::Int(n) => Ok(Value::Float((*n as f64).cosh())),
937 Value::Float(n) => Ok(Value::Float(n.cosh())),
938 _ => Err(RuntimeError::new("cosh() requires number")),
939 });
940
941 define(interp, "tanh", Some(1), |_, args| match &args[0] {
942 Value::Int(n) => Ok(Value::Float((*n as f64).tanh())),
943 Value::Float(n) => Ok(Value::Float(n.tanh())),
944 _ => Err(RuntimeError::new("tanh() requires number")),
945 });
946
947 define(interp, "floor", Some(1), |_, args| match &args[0] {
949 Value::Int(n) => Ok(Value::Int(*n)),
950 Value::Float(n) => Ok(Value::Int(n.floor() as i64)),
951 _ => Err(RuntimeError::new("floor() requires number")),
952 });
953
954 define(interp, "ceil", Some(1), |_, args| match &args[0] {
955 Value::Int(n) => Ok(Value::Int(*n)),
956 Value::Float(n) => Ok(Value::Int(n.ceil() as i64)),
957 _ => Err(RuntimeError::new("ceil() requires number")),
958 });
959
960 define(interp, "round", Some(1), |_, args| match &args[0] {
961 Value::Int(n) => Ok(Value::Int(*n)),
962 Value::Float(n) => Ok(Value::Int(n.round() as i64)),
963 _ => Err(RuntimeError::new("round() requires number")),
964 });
965
966 define(interp, "trunc", Some(1), |_, args| match &args[0] {
967 Value::Int(n) => Ok(Value::Int(*n)),
968 Value::Float(n) => Ok(Value::Int(n.trunc() as i64)),
969 _ => Err(RuntimeError::new("trunc() requires number")),
970 });
971
972 define(interp, "fract", Some(1), |_, args| match &args[0] {
973 Value::Int(_) => Ok(Value::Float(0.0)),
974 Value::Float(n) => Ok(Value::Float(n.fract())),
975 _ => Err(RuntimeError::new("fract() requires number")),
976 });
977
978 define(interp, "min", Some(2), |_, args| {
980 match (&args[0], &args[1]) {
981 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
982 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
983 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).min(*b))),
984 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.min(*b as f64))),
985 _ => Err(RuntimeError::new("min() requires numbers")),
986 }
987 });
988
989 define(interp, "max", Some(2), |_, args| {
990 match (&args[0], &args[1]) {
991 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
992 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
993 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).max(*b))),
994 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.max(*b as f64))),
995 _ => Err(RuntimeError::new("max() requires numbers")),
996 }
997 });
998
999 define(interp, "clamp", Some(3), |_, args| {
1000 match (&args[0], &args[1], &args[2]) {
1001 (Value::Int(val), Value::Int(min), Value::Int(max)) => {
1002 Ok(Value::Int(*val.max(min).min(max)))
1003 }
1004 (Value::Float(val), Value::Float(min), Value::Float(max)) => {
1005 Ok(Value::Float(val.max(*min).min(*max)))
1006 }
1007 _ => Err(RuntimeError::new("clamp() requires matching number types")),
1008 }
1009 });
1010
1011 define(interp, "sign", Some(1), |_, args| match &args[0] {
1013 Value::Int(n) => Ok(Value::Int(n.signum())),
1014 Value::Float(n) => Ok(Value::Float(if *n > 0.0 {
1015 1.0
1016 } else if *n < 0.0 {
1017 -1.0
1018 } else {
1019 0.0
1020 })),
1021 _ => Err(RuntimeError::new("sign() requires number")),
1022 });
1023
1024 define(interp, "PI", Some(0), |_, _| {
1026 Ok(Value::Float(std::f64::consts::PI))
1027 });
1028 define(interp, "E", Some(0), |_, _| {
1029 Ok(Value::Float(std::f64::consts::E))
1030 });
1031 define(interp, "TAU", Some(0), |_, _| {
1032 Ok(Value::Float(std::f64::consts::TAU))
1033 });
1034 define(interp, "PHI", Some(0), |_, _| {
1035 Ok(Value::Float(1.618033988749895))
1036 }); define(interp, "gcd", Some(2), |_, args| {
1040 match (&args[0], &args[1]) {
1041 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(gcd(*a, *b))),
1042 _ => Err(RuntimeError::new("gcd() requires integers")),
1043 }
1044 });
1045
1046 define(interp, "lcm", Some(2), |_, args| {
1047 match (&args[0], &args[1]) {
1048 (Value::Int(a), Value::Int(b)) => {
1049 let g = gcd(*a, *b);
1050 Ok(Value::Int((a * b).abs() / g))
1051 }
1052 _ => Err(RuntimeError::new("lcm() requires integers")),
1053 }
1054 });
1055
1056 define(interp, "factorial", Some(1), |_, args| match &args[0] {
1058 Value::Int(n) if *n >= 0 => {
1059 let mut result: i64 = 1;
1060 for i in 2..=(*n as u64) {
1061 result = result.saturating_mul(i as i64);
1062 }
1063 Ok(Value::Int(result))
1064 }
1065 Value::Int(_) => Err(RuntimeError::new(
1066 "factorial() requires non-negative integer",
1067 )),
1068 _ => Err(RuntimeError::new("factorial() requires integer")),
1069 });
1070
1071 define(interp, "is_nan", Some(1), |_, args| match &args[0] {
1073 Value::Float(n) => Ok(Value::Bool(n.is_nan())),
1074 Value::Int(_) => Ok(Value::Bool(false)),
1075 _ => Err(RuntimeError::new("is_nan() requires number")),
1076 });
1077
1078 define(interp, "is_infinite", Some(1), |_, args| match &args[0] {
1079 Value::Float(n) => Ok(Value::Bool(n.is_infinite())),
1080 Value::Int(_) => Ok(Value::Bool(false)),
1081 Value::Infinity => Ok(Value::Bool(true)),
1082 _ => Err(RuntimeError::new("is_infinite() requires number")),
1083 });
1084
1085 define(interp, "is_finite", Some(1), |_, args| match &args[0] {
1086 Value::Float(n) => Ok(Value::Bool(n.is_finite())),
1087 Value::Int(_) => Ok(Value::Bool(true)),
1088 Value::Infinity => Ok(Value::Bool(false)),
1089 _ => Err(RuntimeError::new("is_finite() requires number")),
1090 });
1091
1092 define(interp, "is_even", Some(1), |_, args| match &args[0] {
1093 Value::Int(n) => Ok(Value::Bool(n % 2 == 0)),
1094 _ => Err(RuntimeError::new("is_even() requires integer")),
1095 });
1096
1097 define(interp, "is_odd", Some(1), |_, args| match &args[0] {
1098 Value::Int(n) => Ok(Value::Bool(n % 2 != 0)),
1099 _ => Err(RuntimeError::new("is_odd() requires integer")),
1100 });
1101
1102 define(interp, "is_prime", Some(1), |_, args| match &args[0] {
1103 Value::Int(n) => Ok(Value::Bool(is_prime(*n))),
1104 _ => Err(RuntimeError::new("is_prime() requires integer")),
1105 });
1106}
1107
1108fn gcd(mut a: i64, mut b: i64) -> i64 {
1109 a = a.abs();
1110 b = b.abs();
1111 while b != 0 {
1112 let t = b;
1113 b = a % b;
1114 a = t;
1115 }
1116 a
1117}
1118
1119fn is_prime(n: i64) -> bool {
1120 if n < 2 {
1121 return false;
1122 }
1123 if n == 2 {
1124 return true;
1125 }
1126 if n % 2 == 0 {
1127 return false;
1128 }
1129 let sqrt = (n as f64).sqrt() as i64;
1130 for i in (3..=sqrt).step_by(2) {
1131 if n % i == 0 {
1132 return false;
1133 }
1134 }
1135 true
1136}
1137
1138fn register_collections(interp: &mut Interpreter) {
1143 define(interp, "len", Some(1), |_, args| match &args[0] {
1145 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
1146 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
1147 Value::Tuple(t) => Ok(Value::Int(t.len() as i64)),
1148 Value::Map(m) => Ok(Value::Int(m.borrow().len() as i64)),
1149 Value::Set(s) => Ok(Value::Int(s.borrow().len() as i64)),
1150 _ => Err(RuntimeError::new(
1151 "len() requires array, string, tuple, map, or set",
1152 )),
1153 });
1154
1155 define(interp, "is_empty", Some(1), |_, args| match &args[0] {
1156 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
1157 Value::String(s) => Ok(Value::Bool(s.is_empty())),
1158 Value::Tuple(t) => Ok(Value::Bool(t.is_empty())),
1159 Value::Map(m) => Ok(Value::Bool(m.borrow().is_empty())),
1160 Value::Set(s) => Ok(Value::Bool(s.borrow().is_empty())),
1161 _ => Err(RuntimeError::new("is_empty() requires collection")),
1162 });
1163
1164 define(interp, "push", Some(2), |_, args| match &args[0] {
1166 Value::Array(arr) => {
1167 arr.borrow_mut().push(args[1].clone());
1168 Ok(Value::Null)
1169 }
1170 _ => Err(RuntimeError::new("push() requires array")),
1171 });
1172
1173 define(interp, "pop", Some(1), |_, args| match &args[0] {
1174 Value::Array(arr) => arr
1175 .borrow_mut()
1176 .pop()
1177 .ok_or_else(|| RuntimeError::new("pop() on empty array")),
1178 _ => Err(RuntimeError::new("pop() requires array")),
1179 });
1180
1181 define(interp, "first", Some(1), |_, args| match &args[0] {
1182 Value::Array(arr) => arr
1183 .borrow()
1184 .first()
1185 .cloned()
1186 .ok_or_else(|| RuntimeError::new("first() on empty array")),
1187 Value::Tuple(t) => t
1188 .first()
1189 .cloned()
1190 .ok_or_else(|| RuntimeError::new("first() on empty tuple")),
1191 _ => Err(RuntimeError::new("first() requires array or tuple")),
1192 });
1193
1194 define(interp, "last", Some(1), |_, args| match &args[0] {
1195 Value::Array(arr) => arr
1196 .borrow()
1197 .last()
1198 .cloned()
1199 .ok_or_else(|| RuntimeError::new("last() on empty array")),
1200 Value::Tuple(t) => t
1201 .last()
1202 .cloned()
1203 .ok_or_else(|| RuntimeError::new("last() on empty tuple")),
1204 _ => Err(RuntimeError::new("last() requires array or tuple")),
1205 });
1206
1207 define(interp, "middle", Some(1), |_, args| match &args[0] {
1209 Value::Array(arr) => {
1210 let arr = arr.borrow();
1211 if arr.is_empty() {
1212 return Err(RuntimeError::new("middle() on empty array"));
1213 }
1214 let mid = arr.len() / 2;
1215 Ok(arr[mid].clone())
1216 }
1217 Value::Tuple(t) => {
1218 if t.is_empty() {
1219 return Err(RuntimeError::new("middle() on empty tuple"));
1220 }
1221 let mid = t.len() / 2;
1222 Ok(t[mid].clone())
1223 }
1224 _ => Err(RuntimeError::new("middle() requires array or tuple")),
1225 });
1226
1227 define(interp, "choice", Some(1), |_, args| {
1229 use std::time::{SystemTime, UNIX_EPOCH};
1230 match &args[0] {
1231 Value::Array(arr) => {
1232 let arr = arr.borrow();
1233 if arr.is_empty() {
1234 return Err(RuntimeError::new("choice() on empty array"));
1235 }
1236 let seed = SystemTime::now()
1237 .duration_since(UNIX_EPOCH)
1238 .unwrap_or(std::time::Duration::ZERO)
1239 .as_nanos() as u64;
1240 let idx = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize
1241 % arr.len();
1242 Ok(arr[idx].clone())
1243 }
1244 Value::Tuple(t) => {
1245 if t.is_empty() {
1246 return Err(RuntimeError::new("choice() on empty tuple"));
1247 }
1248 let seed = SystemTime::now()
1249 .duration_since(UNIX_EPOCH)
1250 .unwrap_or(std::time::Duration::ZERO)
1251 .as_nanos() as u64;
1252 let idx =
1253 ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % t.len();
1254 Ok(t[idx].clone())
1255 }
1256 _ => Err(RuntimeError::new("choice() requires array or tuple")),
1257 }
1258 });
1259
1260 define(interp, "nth", Some(2), |_, args| {
1262 let n = match &args[1] {
1263 Value::Int(i) => *i,
1264 _ => return Err(RuntimeError::new("nth() index must be integer")),
1265 };
1266 match &args[0] {
1267 Value::Array(arr) => {
1268 let arr = arr.borrow();
1269 if n < 0 || n as usize >= arr.len() {
1270 return Err(RuntimeError::new("nth() index out of bounds"));
1271 }
1272 Ok(arr[n as usize].clone())
1273 }
1274 Value::Tuple(t) => {
1275 if n < 0 || n as usize >= t.len() {
1276 return Err(RuntimeError::new("nth() index out of bounds"));
1277 }
1278 Ok(t[n as usize].clone())
1279 }
1280 _ => Err(RuntimeError::new("nth() requires array or tuple")),
1281 }
1282 });
1283
1284 define(interp, "next", Some(1), |_, args| match &args[0] {
1286 Value::Array(arr) => {
1287 let mut arr = arr.borrow_mut();
1288 if arr.is_empty() {
1289 return Err(RuntimeError::new("next() on empty array"));
1290 }
1291 Ok(arr.remove(0))
1292 }
1293 _ => Err(RuntimeError::new("next() requires array")),
1294 });
1295
1296 define(interp, "peek", Some(1), |_, args| match &args[0] {
1298 Value::Array(arr) => arr
1299 .borrow()
1300 .first()
1301 .cloned()
1302 .ok_or_else(|| RuntimeError::new("peek() on empty array")),
1303 _ => Err(RuntimeError::new("peek() requires array")),
1304 });
1305
1306 define(interp, "get", Some(2), |_, args| {
1307 let index = match &args[1] {
1308 Value::Int(i) => *i,
1309 _ => return Err(RuntimeError::new("get() index must be integer")),
1310 };
1311 match &args[0] {
1312 Value::Array(arr) => {
1313 let arr = arr.borrow();
1314 let idx = if index < 0 {
1315 arr.len() as i64 + index
1316 } else {
1317 index
1318 } as usize;
1319 arr.get(idx)
1320 .cloned()
1321 .ok_or_else(|| RuntimeError::new("index out of bounds"))
1322 }
1323 Value::Tuple(t) => {
1324 let idx = if index < 0 {
1325 t.len() as i64 + index
1326 } else {
1327 index
1328 } as usize;
1329 t.get(idx)
1330 .cloned()
1331 .ok_or_else(|| RuntimeError::new("index out of bounds"))
1332 }
1333 _ => Err(RuntimeError::new("get() requires array or tuple")),
1334 }
1335 });
1336
1337 define(interp, "set", Some(3), |_, args| {
1338 let index = match &args[1] {
1339 Value::Int(i) => *i as usize,
1340 _ => return Err(RuntimeError::new("set() index must be integer")),
1341 };
1342 match &args[0] {
1343 Value::Array(arr) => {
1344 let mut arr = arr.borrow_mut();
1345 if index >= arr.len() {
1346 return Err(RuntimeError::new("index out of bounds"));
1347 }
1348 arr[index] = args[2].clone();
1349 Ok(Value::Null)
1350 }
1351 _ => Err(RuntimeError::new("set() requires array")),
1352 }
1353 });
1354
1355 define(interp, "insert", Some(3), |_, args| {
1356 let index = match &args[1] {
1357 Value::Int(i) => *i as usize,
1358 _ => return Err(RuntimeError::new("insert() index must be integer")),
1359 };
1360 match &args[0] {
1361 Value::Array(arr) => {
1362 let mut arr = arr.borrow_mut();
1363 if index > arr.len() {
1364 return Err(RuntimeError::new("index out of bounds"));
1365 }
1366 arr.insert(index, args[2].clone());
1367 Ok(Value::Null)
1368 }
1369 _ => Err(RuntimeError::new("insert() requires array")),
1370 }
1371 });
1372
1373 define(interp, "remove", Some(2), |_, args| {
1374 let index = match &args[1] {
1375 Value::Int(i) => *i as usize,
1376 _ => return Err(RuntimeError::new("remove() index must be integer")),
1377 };
1378 match &args[0] {
1379 Value::Array(arr) => {
1380 let mut arr = arr.borrow_mut();
1381 if index >= arr.len() {
1382 return Err(RuntimeError::new("index out of bounds"));
1383 }
1384 Ok(arr.remove(index))
1385 }
1386 _ => Err(RuntimeError::new("remove() requires array")),
1387 }
1388 });
1389
1390 define(interp, "clear", Some(1), |_, args| match &args[0] {
1391 Value::Array(arr) => {
1392 arr.borrow_mut().clear();
1393 Ok(Value::Null)
1394 }
1395 _ => Err(RuntimeError::new("clear() requires array")),
1396 });
1397
1398 define(interp, "contains", Some(2), |_, args| match &args[0] {
1400 Value::Array(arr) => Ok(Value::Bool(
1401 arr.borrow().iter().any(|v| values_equal(v, &args[1])),
1402 )),
1403 Value::String(s) => match &args[1] {
1404 Value::String(sub) => Ok(Value::Bool(s.contains(sub.as_str()))),
1405 Value::Char(c) => Ok(Value::Bool(s.contains(*c))),
1406 _ => Err(RuntimeError::new(
1407 "string contains() requires string or char",
1408 )),
1409 },
1410 _ => Err(RuntimeError::new("contains() requires array or string")),
1411 });
1412
1413 define(interp, "index_of", Some(2), |_, args| match &args[0] {
1414 Value::Array(arr) => {
1415 let idx = arr.borrow().iter().position(|v| values_equal(v, &args[1]));
1416 match idx {
1417 Some(i) => Ok(Value::Int(i as i64)),
1418 None => Ok(Value::Int(-1)),
1419 }
1420 }
1421 Value::String(s) => match &args[1] {
1422 Value::String(sub) => match s.find(sub.as_str()) {
1423 Some(i) => Ok(Value::Int(i as i64)),
1424 None => Ok(Value::Int(-1)),
1425 },
1426 Value::Char(c) => match s.find(*c) {
1427 Some(i) => Ok(Value::Int(i as i64)),
1428 None => Ok(Value::Int(-1)),
1429 },
1430 _ => Err(RuntimeError::new(
1431 "string index_of() requires string or char",
1432 )),
1433 },
1434 _ => Err(RuntimeError::new("index_of() requires array or string")),
1435 });
1436
1437 define(interp, "reverse", Some(1), |_, args| match &args[0] {
1439 Value::Array(arr) => {
1440 let mut reversed: Vec<Value> = arr.borrow().clone();
1441 reversed.reverse();
1442 Ok(Value::Array(Rc::new(RefCell::new(reversed))))
1443 }
1444 Value::String(s) => {
1445 let reversed: String = s.chars().rev().collect();
1446 Ok(Value::String(Rc::new(reversed)))
1447 }
1448 _ => Err(RuntimeError::new("reverse() requires array or string")),
1449 });
1450
1451 define(interp, "sort", Some(1), |_, args| match &args[0] {
1452 Value::Array(arr) => {
1453 let mut sorted: Vec<Value> = arr.borrow().clone();
1454 sorted.sort_by(compare_values);
1455 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1456 }
1457 _ => Err(RuntimeError::new("sort() requires array")),
1458 });
1459
1460 define(interp, "sort_desc", Some(1), |_, args| match &args[0] {
1461 Value::Array(arr) => {
1462 let mut sorted: Vec<Value> = arr.borrow().clone();
1463 sorted.sort_by(|a, b| compare_values(b, a));
1464 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1465 }
1466 _ => Err(RuntimeError::new("sort_desc() requires array")),
1467 });
1468
1469 define(interp, "unique", Some(1), |_, args| match &args[0] {
1470 Value::Array(arr) => {
1471 let arr = arr.borrow();
1472 let mut seen = Vec::new();
1473 let unique: Vec<Value> = arr
1474 .iter()
1475 .filter(|v| {
1476 if seen.iter().any(|s| values_equal(s, v)) {
1477 false
1478 } else {
1479 seen.push((*v).clone());
1480 true
1481 }
1482 })
1483 .cloned()
1484 .collect();
1485 Ok(Value::Array(Rc::new(RefCell::new(unique))))
1486 }
1487 _ => Err(RuntimeError::new("unique() requires array")),
1488 });
1489
1490 define(interp, "flatten", Some(1), |_, args| match &args[0] {
1491 Value::Array(arr) => {
1492 let mut flattened = Vec::new();
1493 for item in arr.borrow().iter() {
1494 match item {
1495 Value::Array(inner) => flattened.extend(inner.borrow().clone()),
1496 other => flattened.push(other.clone()),
1497 }
1498 }
1499 Ok(Value::Array(Rc::new(RefCell::new(flattened))))
1500 }
1501 _ => Err(RuntimeError::new("flatten() requires array")),
1502 });
1503
1504 define(interp, "concat", Some(2), |_, args| {
1506 match (&args[0], &args[1]) {
1507 (Value::Array(a), Value::Array(b)) => {
1508 let mut result = a.borrow().clone();
1509 result.extend(b.borrow().clone());
1510 Ok(Value::Array(Rc::new(RefCell::new(result))))
1511 }
1512 (Value::String(a), Value::String(b)) => {
1513 Ok(Value::String(Rc::new(format!("{}{}", a, b))))
1514 }
1515 _ => Err(RuntimeError::new(
1516 "concat() requires two arrays or two strings",
1517 )),
1518 }
1519 });
1520
1521 define(interp, "zip", Some(2), |_, args| {
1522 match (&args[0], &args[1]) {
1523 (Value::Array(a), Value::Array(b)) => {
1524 let a = a.borrow();
1525 let b = b.borrow();
1526 let zipped: Vec<Value> = a
1527 .iter()
1528 .zip(b.iter())
1529 .map(|(x, y)| Value::Tuple(Rc::new(vec![x.clone(), y.clone()])))
1530 .collect();
1531 Ok(Value::Array(Rc::new(RefCell::new(zipped))))
1532 }
1533 _ => Err(RuntimeError::new("zip() requires two arrays")),
1534 }
1535 });
1536
1537 define(interp, "enumerate", Some(1), |_, args| match &args[0] {
1538 Value::Array(arr) => {
1539 let enumerated: Vec<Value> = arr
1540 .borrow()
1541 .iter()
1542 .enumerate()
1543 .map(|(i, v)| Value::Tuple(Rc::new(vec![Value::Int(i as i64), v.clone()])))
1544 .collect();
1545 Ok(Value::Array(Rc::new(RefCell::new(enumerated))))
1546 }
1547 _ => Err(RuntimeError::new("enumerate() requires array")),
1548 });
1549
1550 define(interp, "zip_with", Some(3), |_, args| {
1553 let mode = match &args[2] {
1554 Value::String(s) => s.as_str(),
1555 _ => return Err(RuntimeError::new("zip_with() mode must be string")),
1556 };
1557 match (&args[0], &args[1]) {
1558 (Value::Array(a), Value::Array(b)) => {
1559 let a = a.borrow();
1560 let b = b.borrow();
1561 let result: Result<Vec<Value>, RuntimeError> = a
1562 .iter()
1563 .zip(b.iter())
1564 .map(|(x, y)| match (x, y, mode) {
1565 (Value::Int(a), Value::Int(b), "add") => Ok(Value::Int(a + b)),
1566 (Value::Int(a), Value::Int(b), "sub") => Ok(Value::Int(a - b)),
1567 (Value::Int(a), Value::Int(b), "mul") => Ok(Value::Int(a * b)),
1568 (Value::Float(a), Value::Float(b), "add") => Ok(Value::Float(a + b)),
1569 (Value::Float(a), Value::Float(b), "sub") => Ok(Value::Float(a - b)),
1570 (Value::Float(a), Value::Float(b), "mul") => Ok(Value::Float(a * b)),
1571 (_, _, "pair") => Ok(Value::Tuple(Rc::new(vec![x.clone(), y.clone()]))),
1572 _ => Err(RuntimeError::new("zip_with() incompatible types or mode")),
1573 })
1574 .collect();
1575 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1576 }
1577 _ => Err(RuntimeError::new("zip_with() requires two arrays")),
1578 }
1579 });
1580
1581 define(interp, "supremum", Some(2), |_, args| {
1583 match (&args[0], &args[1]) {
1584 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1585 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1586 (Value::Array(a), Value::Array(b)) => {
1587 let a = a.borrow();
1589 let b = b.borrow();
1590 let result: Result<Vec<Value>, RuntimeError> = a
1591 .iter()
1592 .zip(b.iter())
1593 .map(|(x, y)| match (x, y) {
1594 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1595 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1596 _ => Err(RuntimeError::new("supremum() requires numeric arrays")),
1597 })
1598 .collect();
1599 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1600 }
1601 _ => Err(RuntimeError::new(
1602 "supremum() requires numeric values or arrays",
1603 )),
1604 }
1605 });
1606
1607 define(interp, "infimum", Some(2), |_, args| {
1609 match (&args[0], &args[1]) {
1610 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1611 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1612 (Value::Array(a), Value::Array(b)) => {
1613 let a = a.borrow();
1615 let b = b.borrow();
1616 let result: Result<Vec<Value>, RuntimeError> = a
1617 .iter()
1618 .zip(b.iter())
1619 .map(|(x, y)| match (x, y) {
1620 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1621 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1622 _ => Err(RuntimeError::new("infimum() requires numeric arrays")),
1623 })
1624 .collect();
1625 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1626 }
1627 _ => Err(RuntimeError::new(
1628 "infimum() requires numeric values or arrays",
1629 )),
1630 }
1631 });
1632
1633 define(interp, "slice", Some(3), |_, args| {
1635 let start = match &args[1] {
1636 Value::Int(i) => *i as usize,
1637 _ => return Err(RuntimeError::new("slice() start must be integer")),
1638 };
1639 let end = match &args[2] {
1640 Value::Int(i) => *i as usize,
1641 _ => return Err(RuntimeError::new("slice() end must be integer")),
1642 };
1643 match &args[0] {
1644 Value::Array(arr) => {
1645 let arr = arr.borrow();
1646 let end = end.min(arr.len());
1647 let sliced: Vec<Value> = arr[start..end].to_vec();
1648 Ok(Value::Array(Rc::new(RefCell::new(sliced))))
1649 }
1650 Value::String(s) => {
1651 let chars: Vec<char> = s.chars().collect();
1652 let end = end.min(chars.len());
1653 let sliced: String = chars[start..end].iter().collect();
1654 Ok(Value::String(Rc::new(sliced)))
1655 }
1656 _ => Err(RuntimeError::new("slice() requires array or string")),
1657 }
1658 });
1659
1660 define(interp, "take", Some(2), |_, args| {
1661 let n = match &args[1] {
1662 Value::Int(i) => *i as usize,
1663 _ => return Err(RuntimeError::new("take() n must be integer")),
1664 };
1665 match &args[0] {
1666 Value::Array(arr) => {
1667 let taken: Vec<Value> = arr.borrow().iter().take(n).cloned().collect();
1668 Ok(Value::Array(Rc::new(RefCell::new(taken))))
1669 }
1670 _ => Err(RuntimeError::new("take() requires array")),
1671 }
1672 });
1673
1674 define(interp, "skip", Some(2), |_, args| {
1675 let n = match &args[1] {
1676 Value::Int(i) => *i as usize,
1677 _ => return Err(RuntimeError::new("skip() n must be integer")),
1678 };
1679 match &args[0] {
1680 Value::Array(arr) => {
1681 let skipped: Vec<Value> = arr.borrow().iter().skip(n).cloned().collect();
1682 Ok(Value::Array(Rc::new(RefCell::new(skipped))))
1683 }
1684 _ => Err(RuntimeError::new("skip() requires array")),
1685 }
1686 });
1687
1688 define(interp, "chunk", Some(2), |_, args| {
1689 let size = match &args[1] {
1690 Value::Int(i) if *i > 0 => *i as usize,
1691 _ => return Err(RuntimeError::new("chunk() size must be positive integer")),
1692 };
1693 match &args[0] {
1694 Value::Array(arr) => {
1695 let chunks: Vec<Value> = arr
1696 .borrow()
1697 .chunks(size)
1698 .map(|c| Value::Array(Rc::new(RefCell::new(c.to_vec()))))
1699 .collect();
1700 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
1701 }
1702 _ => Err(RuntimeError::new("chunk() requires array")),
1703 }
1704 });
1705
1706 define(interp, "range", Some(2), |_, args| {
1708 let start = match &args[0] {
1709 Value::Int(n) => *n,
1710 _ => return Err(RuntimeError::new("range() requires integers")),
1711 };
1712 let end = match &args[1] {
1713 Value::Int(n) => *n,
1714 _ => return Err(RuntimeError::new("range() requires integers")),
1715 };
1716 let values: Vec<Value> = (start..end).map(Value::Int).collect();
1717 Ok(Value::Array(Rc::new(RefCell::new(values))))
1718 });
1719
1720 define(interp, "range_inclusive", Some(2), |_, args| {
1721 let start = match &args[0] {
1722 Value::Int(n) => *n,
1723 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1724 };
1725 let end = match &args[1] {
1726 Value::Int(n) => *n,
1727 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1728 };
1729 let values: Vec<Value> = (start..=end).map(Value::Int).collect();
1730 Ok(Value::Array(Rc::new(RefCell::new(values))))
1731 });
1732
1733 define(interp, "repeat", Some(2), |_, args| {
1734 let n = match &args[1] {
1735 Value::Int(i) if *i >= 0 => *i as usize,
1736 _ => {
1737 return Err(RuntimeError::new(
1738 "repeat() count must be non-negative integer",
1739 ))
1740 }
1741 };
1742 let repeated: Vec<Value> = std::iter::repeat(args[0].clone()).take(n).collect();
1743 Ok(Value::Array(Rc::new(RefCell::new(repeated))))
1744 });
1745
1746 define(interp, "map_new", Some(0), |_, _| {
1752 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
1753 });
1754
1755 define(interp, "map_get", Some(2), |_, args| {
1757 let key = match &args[1] {
1758 Value::String(s) => s.to_string(),
1759 _ => return Err(RuntimeError::new("map_get() key must be string")),
1760 };
1761 match &args[0] {
1762 Value::Map(map) => Ok(map.borrow().get(&key).cloned().unwrap_or(Value::Null)),
1763 _ => Err(RuntimeError::new("map_get() requires map")),
1764 }
1765 });
1766
1767 define(interp, "map_set", Some(3), |_, args| {
1769 let key = match &args[1] {
1770 Value::String(s) => s.to_string(),
1771 _ => return Err(RuntimeError::new("map_set() key must be string")),
1772 };
1773 match &args[0] {
1774 Value::Map(map) => {
1775 map.borrow_mut().insert(key, args[2].clone());
1776 Ok(Value::Null)
1777 }
1778 _ => Err(RuntimeError::new("map_set() requires map")),
1779 }
1780 });
1781
1782 define(interp, "map_has", Some(2), |_, args| {
1784 let key = match &args[1] {
1785 Value::String(s) => s.to_string(),
1786 _ => return Err(RuntimeError::new("map_has() key must be string")),
1787 };
1788 match &args[0] {
1789 Value::Map(map) => Ok(Value::Bool(map.borrow().contains_key(&key))),
1790 _ => Err(RuntimeError::new("map_has() requires map")),
1791 }
1792 });
1793
1794 define(interp, "map_remove", Some(2), |_, args| {
1796 let key = match &args[1] {
1797 Value::String(s) => s.to_string(),
1798 _ => return Err(RuntimeError::new("map_remove() key must be string")),
1799 };
1800 match &args[0] {
1801 Value::Map(map) => Ok(map.borrow_mut().remove(&key).unwrap_or(Value::Null)),
1802 _ => Err(RuntimeError::new("map_remove() requires map")),
1803 }
1804 });
1805
1806 define(interp, "map_keys", Some(1), |_, args| match &args[0] {
1808 Value::Map(map) => {
1809 let keys: Vec<Value> = map
1810 .borrow()
1811 .keys()
1812 .map(|k| Value::String(Rc::new(k.clone())))
1813 .collect();
1814 Ok(Value::Array(Rc::new(RefCell::new(keys))))
1815 }
1816 _ => Err(RuntimeError::new("map_keys() requires map")),
1817 });
1818
1819 define(interp, "map_values", Some(1), |_, args| match &args[0] {
1821 Value::Map(map) => {
1822 let values: Vec<Value> = map.borrow().values().cloned().collect();
1823 Ok(Value::Array(Rc::new(RefCell::new(values))))
1824 }
1825 _ => Err(RuntimeError::new("map_values() requires map")),
1826 });
1827
1828 define(interp, "map_len", Some(1), |_, args| match &args[0] {
1830 Value::Map(map) => Ok(Value::Int(map.borrow().len() as i64)),
1831 _ => Err(RuntimeError::new("map_len() requires map")),
1832 });
1833
1834 define(interp, "map_clear", Some(1), |_, args| match &args[0] {
1836 Value::Map(map) => {
1837 map.borrow_mut().clear();
1838 Ok(Value::Null)
1839 }
1840 _ => Err(RuntimeError::new("map_clear() requires map")),
1841 });
1842
1843 define(interp, "set_new", Some(0), |_, _| {
1849 Ok(Value::Set(Rc::new(RefCell::new(
1850 std::collections::HashSet::new(),
1851 ))))
1852 });
1853
1854 define(interp, "set_add", Some(2), |_, args| {
1856 let item = match &args[1] {
1857 Value::String(s) => s.to_string(),
1858 _ => return Err(RuntimeError::new("set_add() item must be string")),
1859 };
1860 match &args[0] {
1861 Value::Set(set) => {
1862 set.borrow_mut().insert(item);
1863 Ok(Value::Null)
1864 }
1865 _ => Err(RuntimeError::new("set_add() requires set")),
1866 }
1867 });
1868
1869 define(interp, "set_has", Some(2), |_, args| {
1871 let item = match &args[1] {
1872 Value::String(s) => s.to_string(),
1873 _ => return Err(RuntimeError::new("set_has() item must be string")),
1874 };
1875 match &args[0] {
1876 Value::Set(set) => Ok(Value::Bool(set.borrow().contains(&item))),
1877 _ => Err(RuntimeError::new("set_has() requires set")),
1878 }
1879 });
1880
1881 define(interp, "set_remove", Some(2), |_, args| {
1883 let item = match &args[1] {
1884 Value::String(s) => s.to_string(),
1885 _ => return Err(RuntimeError::new("set_remove() item must be string")),
1886 };
1887 match &args[0] {
1888 Value::Set(set) => Ok(Value::Bool(set.borrow_mut().remove(&item))),
1889 _ => Err(RuntimeError::new("set_remove() requires set")),
1890 }
1891 });
1892
1893 define(interp, "set_to_array", Some(1), |_, args| match &args[0] {
1895 Value::Set(set) => {
1896 let items: Vec<Value> = set
1897 .borrow()
1898 .iter()
1899 .map(|s| Value::String(Rc::new(s.clone())))
1900 .collect();
1901 Ok(Value::Array(Rc::new(RefCell::new(items))))
1902 }
1903 _ => Err(RuntimeError::new("set_to_array() requires set")),
1904 });
1905
1906 define(interp, "set_len", Some(1), |_, args| match &args[0] {
1908 Value::Set(set) => Ok(Value::Int(set.borrow().len() as i64)),
1909 _ => Err(RuntimeError::new("set_len() requires set")),
1910 });
1911
1912 define(interp, "set_clear", Some(1), |_, args| match &args[0] {
1914 Value::Set(set) => {
1915 set.borrow_mut().clear();
1916 Ok(Value::Null)
1917 }
1918 _ => Err(RuntimeError::new("set_clear() requires set")),
1919 });
1920}
1921
1922fn values_equal(a: &Value, b: &Value) -> bool {
1923 match (a, b) {
1924 (Value::Null, Value::Null) => true,
1925 (Value::Bool(a), Value::Bool(b)) => a == b,
1926 (Value::Int(a), Value::Int(b)) => a == b,
1927 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
1928 (Value::String(a), Value::String(b)) => a == b,
1929 (Value::Char(a), Value::Char(b)) => a == b,
1930 (Value::Array(a), Value::Array(b)) => {
1931 let a = a.borrow();
1932 let b = b.borrow();
1933 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1934 }
1935 (Value::Tuple(a), Value::Tuple(b)) => {
1936 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1937 }
1938 _ => false,
1939 }
1940}
1941
1942fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
1943 match (a, b) {
1944 (Value::Int(a), Value::Int(b)) => a.cmp(b),
1945 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal),
1946 (Value::String(a), Value::String(b)) => a.cmp(b),
1947 (Value::Char(a), Value::Char(b)) => a.cmp(b),
1948 _ => std::cmp::Ordering::Equal,
1949 }
1950}
1951
1952fn register_string(interp: &mut Interpreter) {
1957 define(interp, "chars", Some(1), |_, args| match &args[0] {
1958 Value::String(s) => {
1959 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
1960 Ok(Value::Array(Rc::new(RefCell::new(chars))))
1961 }
1962 _ => Err(RuntimeError::new("chars() requires string")),
1963 });
1964
1965 define(interp, "bytes", Some(1), |_, args| match &args[0] {
1966 Value::String(s) => {
1967 let bytes: Vec<Value> = s.bytes().map(|b| Value::Int(b as i64)).collect();
1968 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
1969 }
1970 _ => Err(RuntimeError::new("bytes() requires string")),
1971 });
1972
1973 define(interp, "split", Some(2), |_, args| {
1974 match (&args[0], &args[1]) {
1975 (Value::String(s), Value::String(sep)) => {
1976 let parts: Vec<Value> = s
1977 .split(sep.as_str())
1978 .map(|p| Value::String(Rc::new(p.to_string())))
1979 .collect();
1980 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1981 }
1982 (Value::String(s), Value::Char(sep)) => {
1983 let parts: Vec<Value> = s
1984 .split(*sep)
1985 .map(|p| Value::String(Rc::new(p.to_string())))
1986 .collect();
1987 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1988 }
1989 _ => Err(RuntimeError::new("split() requires string and separator")),
1990 }
1991 });
1992
1993 define(interp, "join", Some(2), |_, args| {
1994 match (&args[0], &args[1]) {
1995 (Value::Array(arr), Value::String(sep)) => {
1996 let parts: Vec<String> = arr.borrow().iter().map(|v| format!("{}", v)).collect();
1997 Ok(Value::String(Rc::new(parts.join(sep.as_str()))))
1998 }
1999 _ => Err(RuntimeError::new(
2000 "join() requires array and separator string",
2001 )),
2002 }
2003 });
2004
2005 define(interp, "trim", Some(1), |_, args| match &args[0] {
2006 Value::String(s) => Ok(Value::String(Rc::new(s.trim().to_string()))),
2007 _ => Err(RuntimeError::new("trim() requires string")),
2008 });
2009
2010 define(interp, "trim_start", Some(1), |_, args| match &args[0] {
2011 Value::String(s) => Ok(Value::String(Rc::new(s.trim_start().to_string()))),
2012 _ => Err(RuntimeError::new("trim_start() requires string")),
2013 });
2014
2015 define(interp, "trim_end", Some(1), |_, args| match &args[0] {
2016 Value::String(s) => Ok(Value::String(Rc::new(s.trim_end().to_string()))),
2017 _ => Err(RuntimeError::new("trim_end() requires string")),
2018 });
2019
2020 define(interp, "upper", Some(1), |_, args| match &args[0] {
2021 Value::String(s) => Ok(Value::String(Rc::new(s.to_uppercase()))),
2022 Value::Char(c) => Ok(Value::Char(c.to_uppercase().next().unwrap_or(*c))),
2023 _ => Err(RuntimeError::new("upper() requires string or char")),
2024 });
2025
2026 define(interp, "lower", Some(1), |_, args| match &args[0] {
2027 Value::String(s) => Ok(Value::String(Rc::new(s.to_lowercase()))),
2028 Value::Char(c) => Ok(Value::Char(c.to_lowercase().next().unwrap_or(*c))),
2029 _ => Err(RuntimeError::new("lower() requires string or char")),
2030 });
2031
2032 define(interp, "capitalize", Some(1), |_, args| match &args[0] {
2033 Value::String(s) => {
2034 let mut chars = s.chars();
2035 let capitalized = match chars.next() {
2036 None => String::new(),
2037 Some(c) => c.to_uppercase().chain(chars).collect(),
2038 };
2039 Ok(Value::String(Rc::new(capitalized)))
2040 }
2041 _ => Err(RuntimeError::new("capitalize() requires string")),
2042 });
2043
2044 define(interp, "replace", Some(3), |_, args| {
2045 match (&args[0], &args[1], &args[2]) {
2046 (Value::String(s), Value::String(from), Value::String(to)) => Ok(Value::String(
2047 Rc::new(s.replace(from.as_str(), to.as_str())),
2048 )),
2049 _ => Err(RuntimeError::new("replace() requires three strings")),
2050 }
2051 });
2052
2053 define(interp, "starts_with", Some(2), |_, args| {
2054 match (&args[0], &args[1]) {
2055 (Value::String(s), Value::String(prefix)) => {
2056 Ok(Value::Bool(s.starts_with(prefix.as_str())))
2057 }
2058 _ => Err(RuntimeError::new("starts_with() requires two strings")),
2059 }
2060 });
2061
2062 define(interp, "ends_with", Some(2), |_, args| {
2063 match (&args[0], &args[1]) {
2064 (Value::String(s), Value::String(suffix)) => {
2065 Ok(Value::Bool(s.ends_with(suffix.as_str())))
2066 }
2067 _ => Err(RuntimeError::new("ends_with() requires two strings")),
2068 }
2069 });
2070
2071 define(interp, "repeat_str", Some(2), |_, args| {
2072 match (&args[0], &args[1]) {
2073 (Value::String(s), Value::Int(n)) if *n >= 0 => {
2074 Ok(Value::String(Rc::new(s.repeat(*n as usize))))
2075 }
2076 _ => Err(RuntimeError::new(
2077 "repeat_str() requires string and non-negative integer",
2078 )),
2079 }
2080 });
2081
2082 define(interp, "pad_left", Some(3), |_, args| {
2083 match (&args[0], &args[1], &args[2]) {
2084 (Value::String(s), Value::Int(width), Value::Char(c)) => {
2085 let width = *width as usize;
2086 if s.len() >= width {
2087 Ok(Value::String(s.clone()))
2088 } else {
2089 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
2090 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
2091 }
2092 }
2093 _ => Err(RuntimeError::new(
2094 "pad_left() requires string, width, and char",
2095 )),
2096 }
2097 });
2098
2099 define(interp, "pad_right", Some(3), |_, args| {
2100 match (&args[0], &args[1], &args[2]) {
2101 (Value::String(s), Value::Int(width), Value::Char(c)) => {
2102 let width = *width as usize;
2103 if s.len() >= width {
2104 Ok(Value::String(s.clone()))
2105 } else {
2106 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
2107 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
2108 }
2109 }
2110 _ => Err(RuntimeError::new(
2111 "pad_right() requires string, width, and char",
2112 )),
2113 }
2114 });
2115
2116 define(interp, "lines", Some(1), |_, args| match &args[0] {
2117 Value::String(s) => {
2118 let lines: Vec<Value> = s
2119 .lines()
2120 .map(|l| Value::String(Rc::new(l.to_string())))
2121 .collect();
2122 Ok(Value::Array(Rc::new(RefCell::new(lines))))
2123 }
2124 _ => Err(RuntimeError::new("lines() requires string")),
2125 });
2126
2127 define(interp, "words", Some(1), |_, args| match &args[0] {
2128 Value::String(s) => {
2129 let words: Vec<Value> = s
2130 .split_whitespace()
2131 .map(|w| Value::String(Rc::new(w.to_string())))
2132 .collect();
2133 Ok(Value::Array(Rc::new(RefCell::new(words))))
2134 }
2135 _ => Err(RuntimeError::new("words() requires string")),
2136 });
2137
2138 define(interp, "is_alpha", Some(1), |_, args| match &args[0] {
2139 Value::String(s) => Ok(Value::Bool(
2140 !s.is_empty() && s.chars().all(|c| c.is_alphabetic()),
2141 )),
2142 Value::Char(c) => Ok(Value::Bool(c.is_alphabetic())),
2143 _ => Err(RuntimeError::new("is_alpha() requires string or char")),
2144 });
2145
2146 define(interp, "is_digit", Some(1), |_, args| match &args[0] {
2147 Value::String(s) => Ok(Value::Bool(
2148 !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()),
2149 )),
2150 Value::Char(c) => Ok(Value::Bool(c.is_ascii_digit())),
2151 _ => Err(RuntimeError::new("is_digit() requires string or char")),
2152 });
2153
2154 define(interp, "is_alnum", Some(1), |_, args| match &args[0] {
2155 Value::String(s) => Ok(Value::Bool(
2156 !s.is_empty() && s.chars().all(|c| c.is_alphanumeric()),
2157 )),
2158 Value::Char(c) => Ok(Value::Bool(c.is_alphanumeric())),
2159 _ => Err(RuntimeError::new("is_alnum() requires string or char")),
2160 });
2161
2162 define(interp, "is_space", Some(1), |_, args| match &args[0] {
2163 Value::String(s) => Ok(Value::Bool(
2164 !s.is_empty() && s.chars().all(|c| c.is_whitespace()),
2165 )),
2166 Value::Char(c) => Ok(Value::Bool(c.is_whitespace())),
2167 _ => Err(RuntimeError::new("is_space() requires string or char")),
2168 });
2169
2170 define(interp, "find", Some(2), |_, args| {
2176 match (&args[0], &args[1]) {
2177 (Value::String(s), Value::String(sub)) => {
2178 match s.find(sub.as_str()) {
2179 Some(byte_idx) => {
2180 let char_idx = s[..byte_idx].chars().count() as i64;
2182 Ok(Value::Int(char_idx))
2183 }
2184 None => Ok(Value::Int(-1)),
2185 }
2186 }
2187 (Value::String(s), Value::Char(c)) => match s.find(*c) {
2188 Some(byte_idx) => {
2189 let char_idx = s[..byte_idx].chars().count() as i64;
2190 Ok(Value::Int(char_idx))
2191 }
2192 None => Ok(Value::Int(-1)),
2193 },
2194 _ => Err(RuntimeError::new(
2195 "find() requires string and substring/char",
2196 )),
2197 }
2198 });
2199
2200 define(interp, "index_of", Some(2), |_, args| {
2202 match (&args[0], &args[1]) {
2203 (Value::String(s), Value::String(sub)) => match s.find(sub.as_str()) {
2204 Some(byte_idx) => {
2205 let char_idx = s[..byte_idx].chars().count() as i64;
2206 Ok(Value::Int(char_idx))
2207 }
2208 None => Ok(Value::Int(-1)),
2209 },
2210 (Value::String(s), Value::Char(c)) => match s.find(*c) {
2211 Some(byte_idx) => {
2212 let char_idx = s[..byte_idx].chars().count() as i64;
2213 Ok(Value::Int(char_idx))
2214 }
2215 None => Ok(Value::Int(-1)),
2216 },
2217 (Value::Array(arr), search) => {
2218 for (i, v) in arr.borrow().iter().enumerate() {
2220 if values_equal_simple(v, search) {
2221 return Ok(Value::Int(i as i64));
2222 }
2223 }
2224 Ok(Value::Int(-1))
2225 }
2226 _ => Err(RuntimeError::new(
2227 "index_of() requires array/string and element/substring",
2228 )),
2229 }
2230 });
2231
2232 define(interp, "last_index_of", Some(2), |_, args| {
2234 match (&args[0], &args[1]) {
2235 (Value::String(s), Value::String(sub)) => match s.rfind(sub.as_str()) {
2236 Some(byte_idx) => {
2237 let char_idx = s[..byte_idx].chars().count() as i64;
2238 Ok(Value::Int(char_idx))
2239 }
2240 None => Ok(Value::Int(-1)),
2241 },
2242 (Value::String(s), Value::Char(c)) => match s.rfind(*c) {
2243 Some(byte_idx) => {
2244 let char_idx = s[..byte_idx].chars().count() as i64;
2245 Ok(Value::Int(char_idx))
2246 }
2247 None => Ok(Value::Int(-1)),
2248 },
2249 _ => Err(RuntimeError::new(
2250 "last_index_of() requires string and substring/char",
2251 )),
2252 }
2253 });
2254
2255 define(interp, "substring", Some(3), |_, args| {
2257 let s = match &args[0] {
2258 Value::String(s) => (**s).clone(),
2259 _ => {
2260 return Err(RuntimeError::new(
2261 "substring: first argument must be a string",
2262 ))
2263 }
2264 };
2265 let start = match &args[1] {
2266 Value::Int(n) if *n >= 0 => *n as usize,
2267 _ => {
2268 return Err(RuntimeError::new(
2269 "substring: start must be a non-negative integer",
2270 ))
2271 }
2272 };
2273 let end = match &args[2] {
2274 Value::Int(n) if *n >= 0 => *n as usize,
2275 _ => {
2276 return Err(RuntimeError::new(
2277 "substring: end must be a non-negative integer",
2278 ))
2279 }
2280 };
2281 let chars: Vec<char> = s.chars().collect();
2282 let len = chars.len();
2283 let actual_start = start.min(len);
2284 let actual_end = end.min(len);
2285 if actual_start >= actual_end {
2286 return Ok(Value::String(Rc::new(String::new())));
2287 }
2288 let result: String = chars[actual_start..actual_end].iter().collect();
2289 Ok(Value::String(Rc::new(result)))
2290 });
2291
2292 define(interp, "count", Some(2), |_, args| {
2294 match (&args[0], &args[1]) {
2295 (Value::String(s), Value::String(sub)) => {
2296 if sub.is_empty() {
2297 return Err(RuntimeError::new("count: cannot count empty string"));
2298 }
2299 let count = s.matches(sub.as_str()).count() as i64;
2300 Ok(Value::Int(count))
2301 }
2302 (Value::String(s), Value::Char(c)) => {
2303 let count = s.chars().filter(|&ch| ch == *c).count() as i64;
2304 Ok(Value::Int(count))
2305 }
2306 _ => Err(RuntimeError::new(
2307 "count() requires string and substring/char",
2308 )),
2309 }
2310 });
2311
2312 define(interp, "char_at", Some(2), |_, args| {
2315 let s = match &args[0] {
2316 Value::String(s) => (**s).clone(),
2317 _ => {
2318 return Err(RuntimeError::new(
2319 "char_at: first argument must be a string",
2320 ))
2321 }
2322 };
2323 let idx = match &args[1] {
2324 Value::Int(n) => *n,
2325 _ => {
2326 return Err(RuntimeError::new(
2327 "char_at: second argument must be an integer",
2328 ))
2329 }
2330 };
2331 let actual_idx = if idx < 0 {
2333 (s.len() as i64 + idx) as usize
2335 } else {
2336 idx as usize
2337 };
2338 if actual_idx < s.len() {
2339 let remaining = &s[actual_idx..];
2340 match remaining.chars().next() {
2341 Some(c) => Ok(Value::Char(c)),
2342 None => Ok(Value::Null),
2343 }
2344 } else {
2345 Ok(Value::Null)
2346 }
2347 });
2348
2349 define(interp, "char_code_at", Some(2), |_, args| {
2351 let s = match &args[0] {
2352 Value::String(s) => (**s).clone(),
2353 _ => {
2354 return Err(RuntimeError::new(
2355 "char_code_at: first argument must be a string",
2356 ))
2357 }
2358 };
2359 let idx = match &args[1] {
2360 Value::Int(n) => *n,
2361 _ => {
2362 return Err(RuntimeError::new(
2363 "char_code_at: second argument must be an integer",
2364 ))
2365 }
2366 };
2367 let chars: Vec<char> = s.chars().collect();
2368 let actual_idx = if idx < 0 {
2369 (chars.len() as i64 + idx) as usize
2370 } else {
2371 idx as usize
2372 };
2373 match chars.get(actual_idx) {
2374 Some(c) => Ok(Value::Int(*c as i64)),
2375 None => Ok(Value::Null),
2376 }
2377 });
2378
2379 define(interp, "from_char_code", Some(1), |_, args| {
2381 let code = match &args[0] {
2382 Value::Int(n) => *n as u32,
2383 _ => {
2384 return Err(RuntimeError::new(
2385 "from_char_code: argument must be an integer",
2386 ))
2387 }
2388 };
2389 match char::from_u32(code) {
2390 Some(c) => Ok(Value::String(Rc::new(c.to_string()))),
2391 None => Err(RuntimeError::new(
2392 "from_char_code: invalid Unicode code point",
2393 )),
2394 }
2395 });
2396
2397 define(interp, "insert", Some(3), |_, args| {
2399 let s = match &args[0] {
2400 Value::String(s) => (**s).clone(),
2401 _ => return Err(RuntimeError::new("insert: first argument must be a string")),
2402 };
2403 let idx = match &args[1] {
2404 Value::Int(n) if *n >= 0 => *n as usize,
2405 _ => {
2406 return Err(RuntimeError::new(
2407 "insert: index must be a non-negative integer",
2408 ))
2409 }
2410 };
2411 let insertion = match &args[2] {
2412 Value::String(s) => (**s).clone(),
2413 _ => return Err(RuntimeError::new("insert: third argument must be a string")),
2414 };
2415 let chars: Vec<char> = s.chars().collect();
2416 let actual_idx = idx.min(chars.len());
2417 let mut result: String = chars[..actual_idx].iter().collect();
2418 result.push_str(&insertion);
2419 result.extend(chars[actual_idx..].iter());
2420 Ok(Value::String(Rc::new(result)))
2421 });
2422
2423 define(interp, "remove", Some(3), |_, args| {
2425 let s = match &args[0] {
2426 Value::String(s) => (**s).clone(),
2427 _ => return Err(RuntimeError::new("remove: first argument must be a string")),
2428 };
2429 let start = match &args[1] {
2430 Value::Int(n) if *n >= 0 => *n as usize,
2431 _ => {
2432 return Err(RuntimeError::new(
2433 "remove: start must be a non-negative integer",
2434 ))
2435 }
2436 };
2437 let len = match &args[2] {
2438 Value::Int(n) if *n >= 0 => *n as usize,
2439 _ => {
2440 return Err(RuntimeError::new(
2441 "remove: length must be a non-negative integer",
2442 ))
2443 }
2444 };
2445 let chars: Vec<char> = s.chars().collect();
2446 let str_len = chars.len();
2447 let actual_start = start.min(str_len);
2448 let actual_end = (start + len).min(str_len);
2449 let mut result: String = chars[..actual_start].iter().collect();
2450 result.extend(chars[actual_end..].iter());
2451 Ok(Value::String(Rc::new(result)))
2452 });
2453
2454 define(interp, "compare", Some(2), |_, args| {
2456 match (&args[0], &args[1]) {
2457 (Value::String(a), Value::String(b)) => {
2458 let result = match a.cmp(b) {
2459 std::cmp::Ordering::Less => -1,
2460 std::cmp::Ordering::Equal => 0,
2461 std::cmp::Ordering::Greater => 1,
2462 };
2463 Ok(Value::Int(result))
2464 }
2465 _ => Err(RuntimeError::new("compare() requires two strings")),
2466 }
2467 });
2468
2469 define(interp, "compare_ignore_case", Some(2), |_, args| {
2471 match (&args[0], &args[1]) {
2472 (Value::String(a), Value::String(b)) => {
2473 let result = match a.to_lowercase().cmp(&b.to_lowercase()) {
2474 std::cmp::Ordering::Less => -1,
2475 std::cmp::Ordering::Equal => 0,
2476 std::cmp::Ordering::Greater => 1,
2477 };
2478 Ok(Value::Int(result))
2479 }
2480 _ => Err(RuntimeError::new(
2481 "compare_ignore_case() requires two strings",
2482 )),
2483 }
2484 });
2485
2486 define(interp, "char_count", Some(1), |_, args| match &args[0] {
2488 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
2489 _ => Err(RuntimeError::new("char_count() requires string")),
2490 });
2491
2492 define(interp, "byte_count", Some(1), |_, args| match &args[0] {
2494 Value::String(s) => Ok(Value::Int(s.len() as i64)),
2495 _ => Err(RuntimeError::new("byte_count() requires string")),
2496 });
2497
2498 define(interp, "is_empty", Some(1), |_, args| match &args[0] {
2500 Value::String(s) => Ok(Value::Bool(s.is_empty())),
2501 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
2502 _ => Err(RuntimeError::new("is_empty() requires string or array")),
2503 });
2504
2505 define(interp, "is_blank", Some(1), |_, args| match &args[0] {
2507 Value::String(s) => Ok(Value::Bool(s.trim().is_empty())),
2508 _ => Err(RuntimeError::new("is_blank() requires string")),
2509 });
2510
2511 define(interp, "nfc", Some(1), |_, args| match &args[0] {
2517 Value::String(s) => Ok(Value::String(Rc::new(s.nfc().collect()))),
2518 _ => Err(RuntimeError::new("nfc() requires string")),
2519 });
2520
2521 define(interp, "nfd", Some(1), |_, args| match &args[0] {
2523 Value::String(s) => Ok(Value::String(Rc::new(s.nfd().collect()))),
2524 _ => Err(RuntimeError::new("nfd() requires string")),
2525 });
2526
2527 define(interp, "nfkc", Some(1), |_, args| match &args[0] {
2529 Value::String(s) => Ok(Value::String(Rc::new(s.nfkc().collect()))),
2530 _ => Err(RuntimeError::new("nfkc() requires string")),
2531 });
2532
2533 define(interp, "nfkd", Some(1), |_, args| match &args[0] {
2535 Value::String(s) => Ok(Value::String(Rc::new(s.nfkd().collect()))),
2536 _ => Err(RuntimeError::new("nfkd() requires string")),
2537 });
2538
2539 define(interp, "is_nfc", Some(1), |_, args| match &args[0] {
2541 Value::String(s) => {
2542 let normalized: String = s.nfc().collect();
2543 Ok(Value::Bool(*s.as_ref() == normalized))
2544 }
2545 _ => Err(RuntimeError::new("is_nfc() requires string")),
2546 });
2547
2548 define(interp, "is_nfd", Some(1), |_, args| match &args[0] {
2550 Value::String(s) => {
2551 let normalized: String = s.nfd().collect();
2552 Ok(Value::Bool(*s.as_ref() == normalized))
2553 }
2554 _ => Err(RuntimeError::new("is_nfd() requires string")),
2555 });
2556
2557 define(interp, "graphemes", Some(1), |_, args| match &args[0] {
2563 Value::String(s) => {
2564 let graphemes: Vec<Value> = s
2565 .graphemes(true)
2566 .map(|g| Value::String(Rc::new(g.to_string())))
2567 .collect();
2568 Ok(Value::Array(Rc::new(RefCell::new(graphemes))))
2569 }
2570 _ => Err(RuntimeError::new("graphemes() requires string")),
2571 });
2572
2573 define(interp, "grapheme_count", Some(1), |_, args| {
2575 match &args[0] {
2576 Value::String(s) => Ok(Value::Int(s.graphemes(true).count() as i64)),
2577 _ => Err(RuntimeError::new("grapheme_count() requires string")),
2578 }
2579 });
2580
2581 define(interp, "grapheme_at", Some(2), |_, args| {
2583 let s = match &args[0] {
2584 Value::String(s) => (**s).clone(),
2585 _ => {
2586 return Err(RuntimeError::new(
2587 "grapheme_at: first argument must be a string",
2588 ))
2589 }
2590 };
2591 let idx = match &args[1] {
2592 Value::Int(n) => *n,
2593 _ => {
2594 return Err(RuntimeError::new(
2595 "grapheme_at: second argument must be an integer",
2596 ))
2597 }
2598 };
2599 let graphemes: Vec<&str> = s.graphemes(true).collect();
2600 let actual_idx = if idx < 0 {
2601 (graphemes.len() as i64 + idx) as usize
2602 } else {
2603 idx as usize
2604 };
2605 match graphemes.get(actual_idx) {
2606 Some(g) => Ok(Value::String(Rc::new(g.to_string()))),
2607 None => Ok(Value::Null),
2608 }
2609 });
2610
2611 define(interp, "grapheme_slice", Some(3), |_, args| {
2613 let s = match &args[0] {
2614 Value::String(s) => (**s).clone(),
2615 _ => {
2616 return Err(RuntimeError::new(
2617 "grapheme_slice: first argument must be a string",
2618 ))
2619 }
2620 };
2621 let start = match &args[1] {
2622 Value::Int(n) if *n >= 0 => *n as usize,
2623 _ => {
2624 return Err(RuntimeError::new(
2625 "grapheme_slice: start must be a non-negative integer",
2626 ))
2627 }
2628 };
2629 let end = match &args[2] {
2630 Value::Int(n) if *n >= 0 => *n as usize,
2631 _ => {
2632 return Err(RuntimeError::new(
2633 "grapheme_slice: end must be a non-negative integer",
2634 ))
2635 }
2636 };
2637 let graphemes: Vec<&str> = s.graphemes(true).collect();
2638 let len = graphemes.len();
2639 let actual_start = start.min(len);
2640 let actual_end = end.min(len);
2641 if actual_start >= actual_end {
2642 return Ok(Value::String(Rc::new(String::new())));
2643 }
2644 let result: String = graphemes[actual_start..actual_end].join("");
2645 Ok(Value::String(Rc::new(result)))
2646 });
2647
2648 define(interp, "grapheme_reverse", Some(1), |_, args| {
2650 match &args[0] {
2651 Value::String(s) => {
2652 let reversed: String = s.graphemes(true).rev().collect();
2653 Ok(Value::String(Rc::new(reversed)))
2654 }
2655 _ => Err(RuntimeError::new("grapheme_reverse() requires string")),
2656 }
2657 });
2658
2659 define(interp, "word_boundaries", Some(1), |_, args| {
2661 match &args[0] {
2662 Value::String(s) => {
2663 let words: Vec<Value> = s
2664 .unicode_words()
2665 .map(|w| Value::String(Rc::new(w.to_string())))
2666 .collect();
2667 Ok(Value::Array(Rc::new(RefCell::new(words))))
2668 }
2669 _ => Err(RuntimeError::new("word_boundaries() requires string")),
2670 }
2671 });
2672
2673 define(interp, "string_builder", Some(0), |_, _| {
2680 Ok(Value::String(Rc::new(String::new())))
2681 });
2682
2683 define(interp, "concat_all", Some(1), |_, args| match &args[0] {
2685 Value::Array(arr) => {
2686 let parts: Vec<String> = arr
2687 .borrow()
2688 .iter()
2689 .map(|v| match v {
2690 Value::String(s) => (**s).clone(),
2691 other => format!("{}", other),
2692 })
2693 .collect();
2694 Ok(Value::String(Rc::new(parts.join(""))))
2695 }
2696 _ => Err(RuntimeError::new("concat_all() requires array")),
2697 });
2698
2699 define(interp, "repeat_join", Some(3), |_, args| {
2701 let s = match &args[0] {
2702 Value::String(s) => (**s).clone(),
2703 _ => {
2704 return Err(RuntimeError::new(
2705 "repeat_join: first argument must be a string",
2706 ))
2707 }
2708 };
2709 let n = match &args[1] {
2710 Value::Int(n) if *n >= 0 => *n as usize,
2711 _ => {
2712 return Err(RuntimeError::new(
2713 "repeat_join: count must be a non-negative integer",
2714 ))
2715 }
2716 };
2717 let sep = match &args[2] {
2718 Value::String(s) => (**s).clone(),
2719 _ => return Err(RuntimeError::new("repeat_join: separator must be a string")),
2720 };
2721 if n == 0 {
2722 return Ok(Value::String(Rc::new(String::new())));
2723 }
2724 let parts: Vec<&str> = std::iter::repeat(s.as_str()).take(n).collect();
2725 Ok(Value::String(Rc::new(parts.join(&sep))))
2726 });
2727}
2728
2729fn register_evidence(interp: &mut Interpreter) {
2734 use crate::interpreter::RuntimeConfidence;
2735
2736 define(interp, "known", Some(1), |_, args| {
2738 Ok(Value::Evidential {
2739 value: Box::new(args[0].clone()),
2740 evidence: Evidence::Known,
2741 })
2742 });
2743
2744 define(interp, "uncertain", Some(1), |_, args| {
2745 Ok(Value::Evidential {
2746 value: Box::new(args[0].clone()),
2747 evidence: Evidence::Uncertain,
2748 })
2749 });
2750
2751 define(interp, "reported", Some(1), |_, args| {
2752 Ok(Value::Evidential {
2753 value: Box::new(args[0].clone()),
2754 evidence: Evidence::Reported,
2755 })
2756 });
2757
2758 define(interp, "paradox", Some(1), |_, args| {
2759 Ok(Value::Evidential {
2760 value: Box::new(args[0].clone()),
2761 evidence: Evidence::Paradox,
2762 })
2763 });
2764
2765 define(interp, "evidence_of", Some(1), |_, args| {
2767 match &args[0] {
2768 Value::Evidential { evidence, .. } => {
2769 let level = match evidence {
2770 Evidence::Known => "known",
2771 Evidence::Uncertain => "uncertain",
2772 Evidence::Reported => "reported",
2773 Evidence::Paradox => "paradox",
2774 };
2775 Ok(Value::String(Rc::new(level.to_string())))
2776 }
2777 _ => Ok(Value::String(Rc::new("known".to_string()))), }
2779 });
2780
2781 define(interp, "is_known", Some(1), |_, args| {
2782 match &args[0] {
2783 Value::Evidential {
2784 evidence: Evidence::Known,
2785 ..
2786 } => Ok(Value::Bool(true)),
2787 Value::Evidential { .. } => Ok(Value::Bool(false)),
2788 _ => Ok(Value::Bool(true)), }
2790 });
2791
2792 define(interp, "is_uncertain", Some(1), |_, args| match &args[0] {
2793 Value::Evidential {
2794 evidence: Evidence::Uncertain,
2795 ..
2796 } => Ok(Value::Bool(true)),
2797 _ => Ok(Value::Bool(false)),
2798 });
2799
2800 define(interp, "is_reported", Some(1), |_, args| match &args[0] {
2801 Value::Evidential {
2802 evidence: Evidence::Reported,
2803 ..
2804 } => Ok(Value::Bool(true)),
2805 _ => Ok(Value::Bool(false)),
2806 });
2807
2808 define(interp, "is_paradox", Some(1), |_, args| match &args[0] {
2809 Value::Evidential {
2810 evidence: Evidence::Paradox,
2811 ..
2812 } => Ok(Value::Bool(true)),
2813 _ => Ok(Value::Bool(false)),
2814 });
2815
2816 define(interp, "strip_evidence", Some(1), |_, args| {
2818 match &args[0] {
2819 Value::Evidential { value, .. } => Ok(*value.clone()),
2820 other => Ok(other.clone()),
2821 }
2822 });
2823
2824 define(interp, "trust", Some(1), |_, args| {
2826 match &args[0] {
2828 Value::Evidential { value, .. } => Ok(Value::Evidential {
2829 value: value.clone(),
2830 evidence: Evidence::Known,
2831 }),
2832 other => Ok(other.clone()),
2833 }
2834 });
2835
2836 define(interp, "verify", Some(2), |_, args| {
2837 let pred_result = match &args[1] {
2839 Value::Bool(b) => *b,
2840 _ => return Err(RuntimeError::new("verify() predicate must be bool")),
2841 };
2842
2843 if pred_result {
2844 match &args[0] {
2845 Value::Evidential { value, .. } => Ok(Value::Evidential {
2846 value: value.clone(),
2847 evidence: Evidence::Known,
2848 }),
2849 other => Ok(other.clone()),
2850 }
2851 } else {
2852 Ok(args[0].clone()) }
2854 });
2855
2856 define(interp, "combine_evidence", Some(2), |_, args| {
2858 let ev1 = match &args[0] {
2859 Value::Evidential { evidence, .. } => *evidence,
2860 _ => Evidence::Known,
2861 };
2862 let ev2 = match &args[1] {
2863 Value::Evidential { evidence, .. } => *evidence,
2864 _ => Evidence::Known,
2865 };
2866
2867 let combined = match (ev1, ev2) {
2869 (Evidence::Paradox, _) | (_, Evidence::Paradox) => Evidence::Paradox,
2870 (Evidence::Reported, _) | (_, Evidence::Reported) => Evidence::Reported,
2871 (Evidence::Uncertain, _) | (_, Evidence::Uncertain) => Evidence::Uncertain,
2872 _ => Evidence::Known,
2873 };
2874
2875 Ok(Value::String(Rc::new(
2876 match combined {
2877 Evidence::Known => "known",
2878 Evidence::Uncertain => "uncertain",
2879 Evidence::Reported => "reported",
2880 Evidence::Paradox => "paradox",
2881 }
2882 .to_string(),
2883 )))
2884 });
2885
2886 define(interp, "affect_to_evidence", Some(1), |_, args| {
2890 match &args[0] {
2891 Value::Affective { affect, .. } => {
2892 if affect.sarcasm {
2894 return Ok(Value::String(Rc::new("uncertain".to_string())));
2895 }
2896 match affect.confidence {
2898 Some(RuntimeConfidence::High) => {
2899 Ok(Value::String(Rc::new("known".to_string())))
2900 }
2901 Some(RuntimeConfidence::Low) => {
2902 Ok(Value::String(Rc::new("uncertain".to_string())))
2903 }
2904 _ => Ok(Value::String(Rc::new("known".to_string()))),
2905 }
2906 }
2907 _ => Ok(Value::String(Rc::new("known".to_string()))),
2908 }
2909 });
2910
2911 define(
2913 interp,
2914 "affect_as_evidence",
2915 Some(1),
2916 |_, args| match &args[0] {
2917 Value::Affective { value, affect } => {
2918 let evidence = if affect.sarcasm {
2919 Evidence::Uncertain
2920 } else {
2921 match affect.confidence {
2922 Some(RuntimeConfidence::High) => Evidence::Known,
2923 Some(RuntimeConfidence::Low) => Evidence::Uncertain,
2924 _ => Evidence::Known,
2925 }
2926 };
2927 Ok(Value::Evidential {
2928 value: value.clone(),
2929 evidence,
2930 })
2931 }
2932 other => Ok(other.clone()),
2933 },
2934 );
2935
2936 define(
2938 interp,
2939 "is_affect_uncertain",
2940 Some(1),
2941 |_, args| match &args[0] {
2942 Value::Affective { affect, .. } => {
2943 let uncertain =
2944 affect.sarcasm || matches!(affect.confidence, Some(RuntimeConfidence::Low));
2945 Ok(Value::Bool(uncertain))
2946 }
2947 _ => Ok(Value::Bool(false)),
2948 },
2949 );
2950
2951 define(interp, "with_affect_evidence", Some(2), |_, args| {
2953 match &args[0] {
2955 Value::Affective { affect, .. } => {
2956 let evidence = if affect.sarcasm {
2957 Evidence::Uncertain
2958 } else {
2959 match affect.confidence {
2960 Some(RuntimeConfidence::High) => Evidence::Known,
2961 Some(RuntimeConfidence::Low) => Evidence::Uncertain,
2962 _ => Evidence::Known,
2963 }
2964 };
2965 Ok(Value::Evidential {
2966 value: Box::new(args[1].clone()),
2967 evidence,
2968 })
2969 }
2970 Value::Evidential { evidence, .. } => {
2971 Ok(Value::Evidential {
2973 value: Box::new(args[1].clone()),
2974 evidence: *evidence,
2975 })
2976 }
2977 _ => Ok(args[1].clone()),
2978 }
2979 });
2980}
2981
2982fn register_affect(interp: &mut Interpreter) {
2987 use crate::interpreter::{
2988 RuntimeAffect, RuntimeConfidence, RuntimeEmotion, RuntimeFormality, RuntimeIntensity,
2989 RuntimeSentiment,
2990 };
2991
2992 define(interp, "positive", Some(1), |_, args| {
2996 Ok(Value::Affective {
2997 value: Box::new(args[0].clone()),
2998 affect: RuntimeAffect {
2999 sentiment: Some(RuntimeSentiment::Positive),
3000 sarcasm: false,
3001 intensity: None,
3002 formality: None,
3003 emotion: None,
3004 confidence: None,
3005 },
3006 })
3007 });
3008
3009 define(interp, "negative", Some(1), |_, args| {
3010 Ok(Value::Affective {
3011 value: Box::new(args[0].clone()),
3012 affect: RuntimeAffect {
3013 sentiment: Some(RuntimeSentiment::Negative),
3014 sarcasm: false,
3015 intensity: None,
3016 formality: None,
3017 emotion: None,
3018 confidence: None,
3019 },
3020 })
3021 });
3022
3023 define(interp, "neutral", Some(1), |_, args| {
3024 Ok(Value::Affective {
3025 value: Box::new(args[0].clone()),
3026 affect: RuntimeAffect {
3027 sentiment: Some(RuntimeSentiment::Neutral),
3028 sarcasm: false,
3029 intensity: None,
3030 formality: None,
3031 emotion: None,
3032 confidence: None,
3033 },
3034 })
3035 });
3036
3037 define(interp, "sarcastic", Some(1), |_, args| {
3039 Ok(Value::Affective {
3040 value: Box::new(args[0].clone()),
3041 affect: RuntimeAffect {
3042 sentiment: None,
3043 sarcasm: true,
3044 intensity: None,
3045 formality: None,
3046 emotion: None,
3047 confidence: None,
3048 },
3049 })
3050 });
3051
3052 define(interp, "intensify", Some(1), |_, args| {
3054 Ok(Value::Affective {
3055 value: Box::new(args[0].clone()),
3056 affect: RuntimeAffect {
3057 sentiment: None,
3058 sarcasm: false,
3059 intensity: Some(RuntimeIntensity::Up),
3060 formality: None,
3061 emotion: None,
3062 confidence: None,
3063 },
3064 })
3065 });
3066
3067 define(interp, "dampen", Some(1), |_, args| {
3068 Ok(Value::Affective {
3069 value: Box::new(args[0].clone()),
3070 affect: RuntimeAffect {
3071 sentiment: None,
3072 sarcasm: false,
3073 intensity: Some(RuntimeIntensity::Down),
3074 formality: None,
3075 emotion: None,
3076 confidence: None,
3077 },
3078 })
3079 });
3080
3081 define(interp, "maximize", Some(1), |_, args| {
3082 Ok(Value::Affective {
3083 value: Box::new(args[0].clone()),
3084 affect: RuntimeAffect {
3085 sentiment: None,
3086 sarcasm: false,
3087 intensity: Some(RuntimeIntensity::Max),
3088 formality: None,
3089 emotion: None,
3090 confidence: None,
3091 },
3092 })
3093 });
3094
3095 define(interp, "formal", Some(1), |_, args| {
3097 Ok(Value::Affective {
3098 value: Box::new(args[0].clone()),
3099 affect: RuntimeAffect {
3100 sentiment: None,
3101 sarcasm: false,
3102 intensity: None,
3103 formality: Some(RuntimeFormality::Formal),
3104 emotion: None,
3105 confidence: None,
3106 },
3107 })
3108 });
3109
3110 define(interp, "informal", Some(1), |_, args| {
3111 Ok(Value::Affective {
3112 value: Box::new(args[0].clone()),
3113 affect: RuntimeAffect {
3114 sentiment: None,
3115 sarcasm: false,
3116 intensity: None,
3117 formality: Some(RuntimeFormality::Informal),
3118 emotion: None,
3119 confidence: None,
3120 },
3121 })
3122 });
3123
3124 define(interp, "joyful", Some(1), |_, args| {
3126 Ok(Value::Affective {
3127 value: Box::new(args[0].clone()),
3128 affect: RuntimeAffect {
3129 sentiment: None,
3130 sarcasm: false,
3131 intensity: None,
3132 formality: None,
3133 emotion: Some(RuntimeEmotion::Joy),
3134 confidence: None,
3135 },
3136 })
3137 });
3138
3139 define(interp, "sad", Some(1), |_, args| {
3140 Ok(Value::Affective {
3141 value: Box::new(args[0].clone()),
3142 affect: RuntimeAffect {
3143 sentiment: None,
3144 sarcasm: false,
3145 intensity: None,
3146 formality: None,
3147 emotion: Some(RuntimeEmotion::Sadness),
3148 confidence: None,
3149 },
3150 })
3151 });
3152
3153 define(interp, "angry", Some(1), |_, args| {
3154 Ok(Value::Affective {
3155 value: Box::new(args[0].clone()),
3156 affect: RuntimeAffect {
3157 sentiment: None,
3158 sarcasm: false,
3159 intensity: None,
3160 formality: None,
3161 emotion: Some(RuntimeEmotion::Anger),
3162 confidence: None,
3163 },
3164 })
3165 });
3166
3167 define(interp, "fearful", Some(1), |_, args| {
3168 Ok(Value::Affective {
3169 value: Box::new(args[0].clone()),
3170 affect: RuntimeAffect {
3171 sentiment: None,
3172 sarcasm: false,
3173 intensity: None,
3174 formality: None,
3175 emotion: Some(RuntimeEmotion::Fear),
3176 confidence: None,
3177 },
3178 })
3179 });
3180
3181 define(interp, "surprised", Some(1), |_, args| {
3182 Ok(Value::Affective {
3183 value: Box::new(args[0].clone()),
3184 affect: RuntimeAffect {
3185 sentiment: None,
3186 sarcasm: false,
3187 intensity: None,
3188 formality: None,
3189 emotion: Some(RuntimeEmotion::Surprise),
3190 confidence: None,
3191 },
3192 })
3193 });
3194
3195 define(interp, "loving", Some(1), |_, args| {
3196 Ok(Value::Affective {
3197 value: Box::new(args[0].clone()),
3198 affect: RuntimeAffect {
3199 sentiment: None,
3200 sarcasm: false,
3201 intensity: None,
3202 formality: None,
3203 emotion: Some(RuntimeEmotion::Love),
3204 confidence: None,
3205 },
3206 })
3207 });
3208
3209 define(interp, "high_confidence", Some(1), |_, args| {
3211 Ok(Value::Affective {
3212 value: Box::new(args[0].clone()),
3213 affect: RuntimeAffect {
3214 sentiment: None,
3215 sarcasm: false,
3216 intensity: None,
3217 formality: None,
3218 emotion: None,
3219 confidence: Some(RuntimeConfidence::High),
3220 },
3221 })
3222 });
3223
3224 define(interp, "medium_confidence", Some(1), |_, args| {
3225 Ok(Value::Affective {
3226 value: Box::new(args[0].clone()),
3227 affect: RuntimeAffect {
3228 sentiment: None,
3229 sarcasm: false,
3230 intensity: None,
3231 formality: None,
3232 emotion: None,
3233 confidence: Some(RuntimeConfidence::Medium),
3234 },
3235 })
3236 });
3237
3238 define(interp, "low_confidence", Some(1), |_, args| {
3239 Ok(Value::Affective {
3240 value: Box::new(args[0].clone()),
3241 affect: RuntimeAffect {
3242 sentiment: None,
3243 sarcasm: false,
3244 intensity: None,
3245 formality: None,
3246 emotion: None,
3247 confidence: Some(RuntimeConfidence::Low),
3248 },
3249 })
3250 });
3251
3252 define(interp, "affect_of", Some(1), |_, args| match &args[0] {
3255 Value::Affective { affect, .. } => {
3256 let mut parts = Vec::new();
3257 if let Some(s) = &affect.sentiment {
3258 parts.push(match s {
3259 RuntimeSentiment::Positive => "positive",
3260 RuntimeSentiment::Negative => "negative",
3261 RuntimeSentiment::Neutral => "neutral",
3262 });
3263 }
3264 if affect.sarcasm {
3265 parts.push("sarcastic");
3266 }
3267 if let Some(i) = &affect.intensity {
3268 parts.push(match i {
3269 RuntimeIntensity::Up => "intensified",
3270 RuntimeIntensity::Down => "dampened",
3271 RuntimeIntensity::Max => "maximized",
3272 });
3273 }
3274 if let Some(f) = &affect.formality {
3275 parts.push(match f {
3276 RuntimeFormality::Formal => "formal",
3277 RuntimeFormality::Informal => "informal",
3278 });
3279 }
3280 if let Some(e) = &affect.emotion {
3281 parts.push(match e {
3282 RuntimeEmotion::Joy => "joyful",
3283 RuntimeEmotion::Sadness => "sad",
3284 RuntimeEmotion::Anger => "angry",
3285 RuntimeEmotion::Fear => "fearful",
3286 RuntimeEmotion::Surprise => "surprised",
3287 RuntimeEmotion::Love => "loving",
3288 });
3289 }
3290 if let Some(c) = &affect.confidence {
3291 parts.push(match c {
3292 RuntimeConfidence::High => "high_confidence",
3293 RuntimeConfidence::Medium => "medium_confidence",
3294 RuntimeConfidence::Low => "low_confidence",
3295 });
3296 }
3297 Ok(Value::String(Rc::new(parts.join(", "))))
3298 }
3299 _ => Ok(Value::String(Rc::new("none".to_string()))),
3300 });
3301
3302 define(interp, "is_sarcastic", Some(1), |_, args| match &args[0] {
3303 Value::Affective { affect, .. } => Ok(Value::Bool(affect.sarcasm)),
3304 _ => Ok(Value::Bool(false)),
3305 });
3306
3307 define(interp, "is_positive", Some(1), |_, args| match &args[0] {
3308 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3309 affect.sentiment,
3310 Some(RuntimeSentiment::Positive)
3311 ))),
3312 _ => Ok(Value::Bool(false)),
3313 });
3314
3315 define(interp, "is_negative", Some(1), |_, args| match &args[0] {
3316 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3317 affect.sentiment,
3318 Some(RuntimeSentiment::Negative)
3319 ))),
3320 _ => Ok(Value::Bool(false)),
3321 });
3322
3323 define(interp, "is_formal", Some(1), |_, args| match &args[0] {
3324 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3325 affect.formality,
3326 Some(RuntimeFormality::Formal)
3327 ))),
3328 _ => Ok(Value::Bool(false)),
3329 });
3330
3331 define(interp, "is_informal", Some(1), |_, args| match &args[0] {
3332 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
3333 affect.formality,
3334 Some(RuntimeFormality::Informal)
3335 ))),
3336 _ => Ok(Value::Bool(false)),
3337 });
3338
3339 define(interp, "emotion_of", Some(1), |_, args| match &args[0] {
3340 Value::Affective { affect, .. } => {
3341 let emotion_str = match &affect.emotion {
3342 Some(RuntimeEmotion::Joy) => "joy",
3343 Some(RuntimeEmotion::Sadness) => "sadness",
3344 Some(RuntimeEmotion::Anger) => "anger",
3345 Some(RuntimeEmotion::Fear) => "fear",
3346 Some(RuntimeEmotion::Surprise) => "surprise",
3347 Some(RuntimeEmotion::Love) => "love",
3348 None => "none",
3349 };
3350 Ok(Value::String(Rc::new(emotion_str.to_string())))
3351 }
3352 _ => Ok(Value::String(Rc::new("none".to_string()))),
3353 });
3354
3355 define(interp, "confidence_of", Some(1), |_, args| match &args[0] {
3356 Value::Affective { affect, .. } => {
3357 let conf_str = match &affect.confidence {
3358 Some(RuntimeConfidence::High) => "high",
3359 Some(RuntimeConfidence::Medium) => "medium",
3360 Some(RuntimeConfidence::Low) => "low",
3361 None => "none",
3362 };
3363 Ok(Value::String(Rc::new(conf_str.to_string())))
3364 }
3365 _ => Ok(Value::String(Rc::new("none".to_string()))),
3366 });
3367
3368 define(interp, "strip_affect", Some(1), |_, args| match &args[0] {
3370 Value::Affective { value, .. } => Ok(*value.clone()),
3371 other => Ok(other.clone()),
3372 });
3373
3374 define(interp, "with_affect", None, |_, args| {
3376 if args.is_empty() {
3377 return Err(RuntimeError::new(
3378 "with_affect requires at least one argument",
3379 ));
3380 }
3381
3382 let base_value = args[0].clone();
3383 let mut affect = RuntimeAffect {
3384 sentiment: None,
3385 sarcasm: false,
3386 intensity: None,
3387 formality: None,
3388 emotion: None,
3389 confidence: None,
3390 };
3391
3392 for arg in args.iter().skip(1) {
3394 if let Value::String(s) = arg {
3395 match s.as_str() {
3396 "positive" | "⊕" => affect.sentiment = Some(RuntimeSentiment::Positive),
3397 "negative" | "⊖" => affect.sentiment = Some(RuntimeSentiment::Negative),
3398 "neutral" | "⊜" => affect.sentiment = Some(RuntimeSentiment::Neutral),
3399 "sarcastic" | "⸮" => affect.sarcasm = true,
3400 "intensify" | "↑" => affect.intensity = Some(RuntimeIntensity::Up),
3401 "dampen" | "↓" => affect.intensity = Some(RuntimeIntensity::Down),
3402 "maximize" | "⇈" => affect.intensity = Some(RuntimeIntensity::Max),
3403 "formal" | "♔" => affect.formality = Some(RuntimeFormality::Formal),
3404 "informal" | "♟" => affect.formality = Some(RuntimeFormality::Informal),
3405 "joy" | "☺" => affect.emotion = Some(RuntimeEmotion::Joy),
3406 "sadness" | "☹" => affect.emotion = Some(RuntimeEmotion::Sadness),
3407 "anger" | "⚡" => affect.emotion = Some(RuntimeEmotion::Anger),
3408 "fear" | "❄" => affect.emotion = Some(RuntimeEmotion::Fear),
3409 "surprise" | "✦" => affect.emotion = Some(RuntimeEmotion::Surprise),
3410 "love" | "♡" => affect.emotion = Some(RuntimeEmotion::Love),
3411 "high" | "◉" => affect.confidence = Some(RuntimeConfidence::High),
3412 "medium" | "◎" => affect.confidence = Some(RuntimeConfidence::Medium),
3413 "low" | "○" => affect.confidence = Some(RuntimeConfidence::Low),
3414 _ => {}
3415 }
3416 }
3417 }
3418
3419 Ok(Value::Affective {
3420 value: Box::new(base_value),
3421 affect,
3422 })
3423 });
3424}
3425
3426fn register_iter(interp: &mut Interpreter) {
3431 define(interp, "sum", Some(1), |_, args| match &args[0] {
3433 Value::Array(arr) => {
3434 let mut sum_int: i64 = 0;
3435 let mut sum_float: f64 = 0.0;
3436 let mut is_float = false;
3437
3438 for val in arr.borrow().iter() {
3439 match val {
3440 Value::Int(n) => {
3441 if is_float {
3442 sum_float += *n as f64;
3443 } else {
3444 sum_int += n;
3445 }
3446 }
3447 Value::Float(n) => {
3448 if !is_float {
3449 sum_float = sum_int as f64;
3450 is_float = true;
3451 }
3452 sum_float += n;
3453 }
3454 _ => return Err(RuntimeError::new("sum() requires array of numbers")),
3455 }
3456 }
3457
3458 if is_float {
3459 Ok(Value::Float(sum_float))
3460 } else {
3461 Ok(Value::Int(sum_int))
3462 }
3463 }
3464 _ => Err(RuntimeError::new("sum() requires array")),
3465 });
3466
3467 define(interp, "product", Some(1), |_, args| match &args[0] {
3469 Value::Array(arr) => {
3470 let mut prod_int: i64 = 1;
3471 let mut prod_float: f64 = 1.0;
3472 let mut is_float = false;
3473
3474 for val in arr.borrow().iter() {
3475 match val {
3476 Value::Int(n) => {
3477 if is_float {
3478 prod_float *= *n as f64;
3479 } else {
3480 prod_int *= n;
3481 }
3482 }
3483 Value::Float(n) => {
3484 if !is_float {
3485 prod_float = prod_int as f64;
3486 is_float = true;
3487 }
3488 prod_float *= n;
3489 }
3490 _ => return Err(RuntimeError::new("product() requires array of numbers")),
3491 }
3492 }
3493
3494 if is_float {
3495 Ok(Value::Float(prod_float))
3496 } else {
3497 Ok(Value::Int(prod_int))
3498 }
3499 }
3500 _ => Err(RuntimeError::new("product() requires array")),
3501 });
3502
3503 define(interp, "mean", Some(1), |_, args| match &args[0] {
3505 Value::Array(arr) => {
3506 let arr = arr.borrow();
3507 if arr.is_empty() {
3508 return Err(RuntimeError::new("mean() on empty array"));
3509 }
3510
3511 let mut sum: f64 = 0.0;
3512 for val in arr.iter() {
3513 match val {
3514 Value::Int(n) => sum += *n as f64,
3515 Value::Float(n) => sum += n,
3516 _ => return Err(RuntimeError::new("mean() requires array of numbers")),
3517 }
3518 }
3519
3520 Ok(Value::Float(sum / arr.len() as f64))
3521 }
3522 _ => Err(RuntimeError::new("mean() requires array")),
3523 });
3524
3525 define(interp, "median", Some(1), |_, args| match &args[0] {
3527 Value::Array(arr) => {
3528 let arr = arr.borrow();
3529 if arr.is_empty() {
3530 return Err(RuntimeError::new("median() on empty array"));
3531 }
3532
3533 let mut nums: Vec<f64> = Vec::new();
3534 for val in arr.iter() {
3535 match val {
3536 Value::Int(n) => nums.push(*n as f64),
3537 Value::Float(n) => nums.push(*n),
3538 _ => return Err(RuntimeError::new("median() requires array of numbers")),
3539 }
3540 }
3541
3542 nums.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
3543 let mid = nums.len() / 2;
3544
3545 if nums.len() % 2 == 0 {
3546 Ok(Value::Float((nums[mid - 1] + nums[mid]) / 2.0))
3547 } else {
3548 Ok(Value::Float(nums[mid]))
3549 }
3550 }
3551 _ => Err(RuntimeError::new("median() requires array")),
3552 });
3553
3554 define(interp, "min_of", Some(1), |_, args| match &args[0] {
3556 Value::Array(arr) => {
3557 let arr = arr.borrow();
3558 if arr.is_empty() {
3559 return Err(RuntimeError::new("min_of() on empty array"));
3560 }
3561
3562 let mut min = &arr[0];
3563 for val in arr.iter().skip(1) {
3564 if matches!(compare_values(val, min), std::cmp::Ordering::Less) {
3565 min = val;
3566 }
3567 }
3568 Ok(min.clone())
3569 }
3570 _ => Err(RuntimeError::new("min_of() requires array")),
3571 });
3572
3573 define(interp, "max_of", Some(1), |_, args| match &args[0] {
3575 Value::Array(arr) => {
3576 let arr = arr.borrow();
3577 if arr.is_empty() {
3578 return Err(RuntimeError::new("max_of() on empty array"));
3579 }
3580
3581 let mut max = &arr[0];
3582 for val in arr.iter().skip(1) {
3583 if matches!(compare_values(val, max), std::cmp::Ordering::Greater) {
3584 max = val;
3585 }
3586 }
3587 Ok(max.clone())
3588 }
3589 _ => Err(RuntimeError::new("max_of() requires array")),
3590 });
3591
3592 define(interp, "count", Some(1), |_, args| match &args[0] {
3594 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
3595 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
3596 _ => Err(RuntimeError::new("count() requires array or string")),
3597 });
3598
3599 define(interp, "any", Some(1), |_, args| match &args[0] {
3601 Value::Array(arr) => {
3602 for val in arr.borrow().iter() {
3603 if is_truthy(val) {
3604 return Ok(Value::Bool(true));
3605 }
3606 }
3607 Ok(Value::Bool(false))
3608 }
3609 _ => Err(RuntimeError::new("any() requires array")),
3610 });
3611
3612 define(interp, "all", Some(1), |_, args| match &args[0] {
3614 Value::Array(arr) => {
3615 for val in arr.borrow().iter() {
3616 if !is_truthy(val) {
3617 return Ok(Value::Bool(false));
3618 }
3619 }
3620 Ok(Value::Bool(true))
3621 }
3622 _ => Err(RuntimeError::new("all() requires array")),
3623 });
3624
3625 define(interp, "none", Some(1), |_, args| match &args[0] {
3627 Value::Array(arr) => {
3628 for val in arr.borrow().iter() {
3629 if is_truthy(val) {
3630 return Ok(Value::Bool(false));
3631 }
3632 }
3633 Ok(Value::Bool(true))
3634 }
3635 _ => Err(RuntimeError::new("none() requires array")),
3636 });
3637}
3638
3639fn is_truthy(val: &Value) -> bool {
3640 match val {
3641 Value::Null | Value::Empty => false,
3642 Value::Bool(b) => *b,
3643 Value::Int(n) => *n != 0,
3644 Value::Float(n) => *n != 0.0 && !n.is_nan(),
3645 Value::String(s) => !s.is_empty(),
3646 Value::Array(arr) => !arr.borrow().is_empty(),
3647 Value::Evidential { value, .. } => is_truthy(value),
3648 _ => true,
3649 }
3650}
3651
3652fn register_io(interp: &mut Interpreter) {
3657 define(interp, "read_file", Some(1), |_, args| {
3659 match &args[0] {
3660 Value::String(path) => {
3661 match std::fs::read_to_string(path.as_str()) {
3662 Ok(content) => Ok(Value::Evidential {
3663 value: Box::new(Value::String(Rc::new(content))),
3664 evidence: Evidence::Reported, }),
3666 Err(e) => Err(RuntimeError::new(format!("read_file failed: {}", e))),
3667 }
3668 }
3669 _ => Err(RuntimeError::new("read_file() requires path string")),
3670 }
3671 });
3672
3673 define(interp, "write_file", Some(2), |_, args| {
3675 match (&args[0], &args[1]) {
3676 (Value::String(path), Value::String(content)) => {
3677 match std::fs::write(path.as_str(), content.as_str()) {
3678 Ok(_) => Ok(Value::Bool(true)),
3679 Err(e) => Err(RuntimeError::new(format!("write_file failed: {}", e))),
3680 }
3681 }
3682 _ => Err(RuntimeError::new(
3683 "write_file() requires path and content strings",
3684 )),
3685 }
3686 });
3687
3688 define(interp, "append_file", Some(2), |_, args| {
3690 match (&args[0], &args[1]) {
3691 (Value::String(path), Value::String(content)) => {
3692 use std::fs::OpenOptions;
3693 let result = OpenOptions::new()
3694 .create(true)
3695 .append(true)
3696 .open(path.as_str())
3697 .and_then(|mut f| f.write_all(content.as_bytes()));
3698 match result {
3699 Ok(_) => Ok(Value::Bool(true)),
3700 Err(e) => Err(RuntimeError::new(format!("append_file failed: {}", e))),
3701 }
3702 }
3703 _ => Err(RuntimeError::new(
3704 "append_file() requires path and content strings",
3705 )),
3706 }
3707 });
3708
3709 define(interp, "file_exists", Some(1), |_, args| match &args[0] {
3711 Value::String(path) => Ok(Value::Bool(std::path::Path::new(path.as_str()).exists())),
3712 _ => Err(RuntimeError::new("file_exists() requires path string")),
3713 });
3714
3715 define(interp, "read_lines", Some(1), |_, args| match &args[0] {
3717 Value::String(path) => match std::fs::read_to_string(path.as_str()) {
3718 Ok(content) => {
3719 let lines: Vec<Value> = content
3720 .lines()
3721 .map(|l| Value::String(Rc::new(l.to_string())))
3722 .collect();
3723 Ok(Value::Evidential {
3724 value: Box::new(Value::Array(Rc::new(RefCell::new(lines)))),
3725 evidence: Evidence::Reported,
3726 })
3727 }
3728 Err(e) => Err(RuntimeError::new(format!("read_lines failed: {}", e))),
3729 },
3730 _ => Err(RuntimeError::new("read_lines() requires path string")),
3731 });
3732
3733 define(interp, "env", Some(1), |_, args| {
3735 match &args[0] {
3736 Value::String(name) => {
3737 match std::env::var(name.as_str()) {
3738 Ok(value) => Ok(Value::Evidential {
3739 value: Box::new(Value::String(Rc::new(value))),
3740 evidence: Evidence::Reported, }),
3742 Err(_) => Ok(Value::Null),
3743 }
3744 }
3745 _ => Err(RuntimeError::new("env() requires variable name string")),
3746 }
3747 });
3748
3749 define(interp, "env·var", Some(1), |_, args| match &args[0] {
3751 Value::String(name) => match std::env::var(name.as_str()) {
3752 Ok(value) => Ok(Value::Variant {
3753 enum_name: "Result".to_string(),
3754 variant_name: "Ok".to_string(),
3755 fields: Some(Rc::new(vec![Value::String(Rc::new(value))])),
3756 }),
3757 Err(_) => Ok(Value::Variant {
3758 enum_name: "Result".to_string(),
3759 variant_name: "Err".to_string(),
3760 fields: Some(Rc::new(vec![Value::String(Rc::new(
3761 "environment variable not found".to_string(),
3762 ))])),
3763 }),
3764 },
3765 _ => Err(RuntimeError::new(
3766 "env::var() requires variable name string",
3767 )),
3768 });
3769
3770 define(interp, "env_or", Some(2), |_, args| {
3772 match (&args[0], &args[1]) {
3773 (Value::String(name), default) => match std::env::var(name.as_str()) {
3774 Ok(value) => Ok(Value::Evidential {
3775 value: Box::new(Value::String(Rc::new(value))),
3776 evidence: Evidence::Reported,
3777 }),
3778 Err(_) => Ok(default.clone()),
3779 },
3780 _ => Err(RuntimeError::new("env_or() requires variable name string")),
3781 }
3782 });
3783
3784 define(interp, "cwd", Some(0), |_, _| {
3786 match std::env::current_dir() {
3787 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
3788 Err(e) => Err(RuntimeError::new(format!("cwd() failed: {}", e))),
3789 }
3790 });
3791
3792 define(interp, "args", Some(0), |interp, _| {
3794 let args: Vec<Value> = if interp
3795 .program_args
3796 .as_ref()
3797 .map(|v| v.is_empty())
3798 .unwrap_or(true)
3799 {
3800 std::env::args()
3802 .map(|a| Value::String(Rc::new(a)))
3803 .collect()
3804 } else {
3805 interp
3807 .program_args
3808 .as_ref()
3809 .unwrap()
3810 .iter()
3811 .map(|a| Value::String(Rc::new(a.clone())))
3812 .collect()
3813 };
3814 Ok(Value::Array(Rc::new(RefCell::new(args))))
3815 });
3816}
3817
3818fn register_time(interp: &mut Interpreter) {
3823 define(interp, "now", Some(0), |_, _| {
3825 let duration = SystemTime::now()
3826 .duration_since(UNIX_EPOCH)
3827 .unwrap_or(Duration::ZERO);
3828 Ok(Value::Int(duration.as_millis() as i64))
3829 });
3830
3831 define(interp, "now_secs", Some(0), |_, _| {
3833 let duration = SystemTime::now()
3834 .duration_since(UNIX_EPOCH)
3835 .unwrap_or(Duration::ZERO);
3836 Ok(Value::Int(duration.as_secs() as i64))
3837 });
3838
3839 define(interp, "now_micros", Some(0), |_, _| {
3841 let duration = SystemTime::now()
3842 .duration_since(UNIX_EPOCH)
3843 .unwrap_or(Duration::ZERO);
3844 Ok(Value::Int(duration.as_micros() as i64))
3845 });
3846
3847 define(interp, "sleep", Some(1), |_, args| match &args[0] {
3849 Value::Int(ms) if *ms >= 0 => {
3850 std::thread::sleep(Duration::from_millis(*ms as u64));
3851 Ok(Value::Null)
3852 }
3853 _ => Err(RuntimeError::new(
3854 "sleep() requires non-negative integer milliseconds",
3855 )),
3856 });
3857
3858 define(interp, "UNIX_EPOCH", Some(0), |_, _| {
3864 let mut fields = std::collections::HashMap::new();
3866 fields.insert("secs".to_string(), Value::Int(0));
3867 fields.insert("nanos".to_string(), Value::Int(0));
3868 Ok(Value::Struct {
3869 name: "SystemTime".to_string(),
3870 fields: Rc::new(RefCell::new(fields)),
3871 })
3872 });
3873
3874 define(interp, "std·time·UNIX_EPOCH", Some(0), |_, _| {
3876 let mut fields = std::collections::HashMap::new();
3877 fields.insert("secs".to_string(), Value::Int(0));
3878 fields.insert("nanos".to_string(), Value::Int(0));
3879 Ok(Value::Struct {
3880 name: "SystemTime".to_string(),
3881 fields: Rc::new(RefCell::new(fields)),
3882 })
3883 });
3884
3885 define(interp, "timer_start", Some(0), |_, _| {
3887 let now = Instant::now();
3888 Ok(Value::Int(now.elapsed().as_nanos() as i64)) });
3891}
3892
3893fn register_random(interp: &mut Interpreter) {
3898 define(interp, "random", Some(0), |_, _| {
3900 use std::time::SystemTime;
3902 let seed = SystemTime::now()
3903 .duration_since(UNIX_EPOCH)
3904 .unwrap_or(Duration::ZERO)
3905 .as_nanos() as u64;
3906 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as f64;
3907 Ok(Value::Float(rand / u32::MAX as f64))
3908 });
3909
3910 define(interp, "random_int", Some(2), |_, args| {
3912 match (&args[0], &args[1]) {
3913 (Value::Int(min), Value::Int(max)) if max > min => {
3914 use std::time::SystemTime;
3915 let seed = SystemTime::now()
3916 .duration_since(UNIX_EPOCH)
3917 .unwrap_or(Duration::ZERO)
3918 .as_nanos() as u64;
3919 let range = (max - min) as u64;
3920 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) % range;
3921 Ok(Value::Int(*min + rand as i64))
3922 }
3923 _ => Err(RuntimeError::new(
3924 "random_int() requires min < max integers",
3925 )),
3926 }
3927 });
3928
3929 define(interp, "shuffle", Some(1), |_, args| match &args[0] {
3931 Value::Array(arr) => {
3932 let mut arr = arr.borrow_mut();
3933 use std::time::SystemTime;
3934 let mut seed = SystemTime::now()
3935 .duration_since(UNIX_EPOCH)
3936 .unwrap_or(Duration::ZERO)
3937 .as_nanos() as u64;
3938
3939 for i in (1..arr.len()).rev() {
3940 seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
3941 let j = ((seed >> 16) as usize) % (i + 1);
3942 arr.swap(i, j);
3943 }
3944 Ok(Value::Null)
3945 }
3946 _ => Err(RuntimeError::new("shuffle() requires array")),
3947 });
3948
3949 define(interp, "sample", Some(1), |_, args| match &args[0] {
3951 Value::Array(arr) => {
3952 let arr = arr.borrow();
3953 if arr.is_empty() {
3954 return Err(RuntimeError::new("sample() on empty array"));
3955 }
3956
3957 use std::time::SystemTime;
3958 let seed = SystemTime::now()
3959 .duration_since(UNIX_EPOCH)
3960 .unwrap_or(Duration::ZERO)
3961 .as_nanos() as u64;
3962 let idx =
3963 ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % arr.len();
3964 Ok(arr[idx].clone())
3965 }
3966 _ => Err(RuntimeError::new("sample() requires array")),
3967 });
3968}
3969
3970fn register_convert(interp: &mut Interpreter) {
3975 define(interp, "to_string", Some(1), |_, args| {
3977 Ok(Value::String(Rc::new(format!("{}", args[0]))))
3978 });
3979
3980 define(interp, "to_int", Some(1), |_, args| match &args[0] {
3982 Value::Int(n) => Ok(Value::Int(*n)),
3983 Value::Float(n) => Ok(Value::Int(*n as i64)),
3984 Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
3985 Value::Char(c) => Ok(Value::Int(*c as i64)),
3986 Value::String(s) => s
3987 .parse::<i64>()
3988 .map(Value::Int)
3989 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as integer", s))),
3990 _ => Err(RuntimeError::new("to_int() cannot convert this type")),
3991 });
3992
3993 define(interp, "to_float", Some(1), |_, args| match &args[0] {
3995 Value::Int(n) => Ok(Value::Float(*n as f64)),
3996 Value::Float(n) => Ok(Value::Float(*n)),
3997 Value::Bool(b) => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
3998 Value::String(s) => s
3999 .parse::<f64>()
4000 .map(Value::Float)
4001 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as float", s))),
4002 _ => Err(RuntimeError::new("to_float() cannot convert this type")),
4003 });
4004
4005 define(interp, "to_bool", Some(1), |_, args| {
4007 Ok(Value::Bool(is_truthy(&args[0])))
4008 });
4009
4010 define(interp, "to_char", Some(1), |_, args| match &args[0] {
4012 Value::Char(c) => Ok(Value::Char(*c)),
4013 Value::Int(n) => char::from_u32(*n as u32)
4014 .map(Value::Char)
4015 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n))),
4016 Value::String(s) => s
4017 .chars()
4018 .next()
4019 .map(Value::Char)
4020 .ok_or_else(|| RuntimeError::new("to_char() on empty string")),
4021 _ => Err(RuntimeError::new("to_char() cannot convert this type")),
4022 });
4023
4024 define(interp, "to_array", Some(1), |_, args| match &args[0] {
4026 Value::Array(arr) => Ok(Value::Array(arr.clone())),
4027 Value::Tuple(t) => Ok(Value::Array(Rc::new(RefCell::new(t.as_ref().clone())))),
4028 Value::String(s) => {
4029 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
4030 Ok(Value::Array(Rc::new(RefCell::new(chars))))
4031 }
4032 _ => Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()])))),
4033 });
4034
4035 define(interp, "to_tuple", Some(1), |_, args| match &args[0] {
4037 Value::Tuple(t) => Ok(Value::Tuple(t.clone())),
4038 Value::Array(arr) => Ok(Value::Tuple(Rc::new(arr.borrow().clone()))),
4039 _ => Ok(Value::Tuple(Rc::new(vec![args[0].clone()]))),
4040 });
4041
4042 define(interp, "char_code", Some(1), |_, args| match &args[0] {
4044 Value::Char(c) => Ok(Value::Int(*c as i64)),
4045 _ => Err(RuntimeError::new("char_code() requires char")),
4046 });
4047
4048 define(interp, "from_char_code", Some(1), |_, args| {
4050 match &args[0] {
4051 Value::Int(n) => char::from_u32(*n as u32)
4052 .map(Value::Char)
4053 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n))),
4054 _ => Err(RuntimeError::new("from_char_code() requires integer")),
4055 }
4056 });
4057
4058 define(interp, "hex", Some(1), |_, args| match &args[0] {
4060 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:x}", n)))),
4061 _ => Err(RuntimeError::new("hex() requires integer")),
4062 });
4063
4064 define(interp, "oct", Some(1), |_, args| match &args[0] {
4066 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:o}", n)))),
4067 _ => Err(RuntimeError::new("oct() requires integer")),
4068 });
4069
4070 define(interp, "bin", Some(1), |_, args| match &args[0] {
4072 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:b}", n)))),
4073 _ => Err(RuntimeError::new("bin() requires integer")),
4074 });
4075
4076 define(interp, "parse_int", Some(2), |_, args| {
4078 match (&args[0], &args[1]) {
4079 (Value::String(s), Value::Int(base)) if *base >= 2 && *base <= 36 => {
4080 i64::from_str_radix(s.trim(), *base as u32)
4081 .map(Value::Int)
4082 .map_err(|_| {
4083 RuntimeError::new(format!("cannot parse '{}' as base-{} integer", s, base))
4084 })
4085 }
4086 _ => Err(RuntimeError::new(
4087 "parse_int() requires string and base 2-36",
4088 )),
4089 }
4090 });
4091}
4092
4093fn register_cycle(interp: &mut Interpreter) {
4099 define(interp, "cycle", Some(2), |_, args| {
4101 match (&args[0], &args[1]) {
4102 (Value::Int(value), Value::Int(modulus)) if *modulus > 0 => {
4103 let normalized = value.rem_euclid(*modulus);
4104 Ok(Value::Tuple(Rc::new(vec![
4105 Value::Int(normalized),
4106 Value::Int(*modulus),
4107 ])))
4108 }
4109 _ => Err(RuntimeError::new(
4110 "cycle() requires value and positive modulus",
4111 )),
4112 }
4113 });
4114
4115 define(interp, "mod_add", Some(3), |_, args| {
4117 match (&args[0], &args[1], &args[2]) {
4118 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4119 Ok(Value::Int((a + b).rem_euclid(*m)))
4120 }
4121 _ => Err(RuntimeError::new(
4122 "mod_add() requires two integers and positive modulus",
4123 )),
4124 }
4125 });
4126
4127 define(interp, "mod_sub", Some(3), |_, args| {
4129 match (&args[0], &args[1], &args[2]) {
4130 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4131 Ok(Value::Int((a - b).rem_euclid(*m)))
4132 }
4133 _ => Err(RuntimeError::new(
4134 "mod_sub() requires two integers and positive modulus",
4135 )),
4136 }
4137 });
4138
4139 define(interp, "mod_mul", Some(3), |_, args| {
4141 match (&args[0], &args[1], &args[2]) {
4142 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
4143 Ok(Value::Int((a * b).rem_euclid(*m)))
4144 }
4145 _ => Err(RuntimeError::new(
4146 "mod_mul() requires two integers and positive modulus",
4147 )),
4148 }
4149 });
4150
4151 define(interp, "mod_pow", Some(3), |_, args| {
4153 match (&args[0], &args[1], &args[2]) {
4154 (Value::Int(base), Value::Int(exp), Value::Int(m)) if *m > 0 && *exp >= 0 => {
4155 Ok(Value::Int(mod_pow(*base, *exp as u64, *m)))
4156 }
4157 _ => Err(RuntimeError::new(
4158 "mod_pow() requires base, non-negative exp, and positive modulus",
4159 )),
4160 }
4161 });
4162
4163 define(interp, "mod_inv", Some(2), |_, args| {
4165 match (&args[0], &args[1]) {
4166 (Value::Int(a), Value::Int(m)) if *m > 0 => match mod_inverse(*a, *m) {
4167 Some(inv) => Ok(Value::Int(inv)),
4168 None => Err(RuntimeError::new(format!(
4169 "no modular inverse of {} mod {}",
4170 a, m
4171 ))),
4172 },
4173 _ => Err(RuntimeError::new(
4174 "mod_inv() requires integer and positive modulus",
4175 )),
4176 }
4177 });
4178
4179 define(interp, "octave", Some(1), |_, args| {
4182 match &args[0] {
4183 Value::Int(note) => Ok(Value::Int(note.rem_euclid(12))),
4184 Value::Float(freq) => {
4185 let semitones = 12.0 * (freq / 440.0).log2();
4187 Ok(Value::Float(semitones.rem_euclid(12.0)))
4188 }
4189 _ => Err(RuntimeError::new("octave() requires number")),
4190 }
4191 });
4192
4193 define(interp, "interval", Some(2), |_, args| {
4195 match (&args[0], &args[1]) {
4196 (Value::Int(a), Value::Int(b)) => Ok(Value::Int((b - a).rem_euclid(12))),
4197 _ => Err(RuntimeError::new("interval() requires two integers")),
4198 }
4199 });
4200
4201 define(interp, "cents", Some(1), |_, args| match &args[0] {
4203 Value::Int(semitones) => Ok(Value::Int(*semitones * 100)),
4204 Value::Float(semitones) => Ok(Value::Float(*semitones * 100.0)),
4205 _ => Err(RuntimeError::new("cents() requires number")),
4206 });
4207
4208 define(interp, "freq", Some(1), |_, args| match &args[0] {
4210 Value::Int(midi) => {
4211 let freq = 440.0 * 2.0_f64.powf((*midi as f64 - 69.0) / 12.0);
4212 Ok(Value::Float(freq))
4213 }
4214 _ => Err(RuntimeError::new("freq() requires integer MIDI note")),
4215 });
4216
4217 define(interp, "midi", Some(1), |_, args| match &args[0] {
4219 Value::Float(freq) if *freq > 0.0 => {
4220 let midi = 69.0 + 12.0 * (freq / 440.0).log2();
4221 Ok(Value::Int(midi.round() as i64))
4222 }
4223 Value::Int(freq) if *freq > 0 => {
4224 let midi = 69.0 + 12.0 * (*freq as f64 / 440.0).log2();
4225 Ok(Value::Int(midi.round() as i64))
4226 }
4227 _ => Err(RuntimeError::new("midi() requires positive frequency")),
4228 });
4229}
4230
4231fn mod_pow(mut base: i64, mut exp: u64, modulus: i64) -> i64 {
4233 if modulus == 1 {
4234 return 0;
4235 }
4236 let mut result: i64 = 1;
4237 base = base.rem_euclid(modulus);
4238 while exp > 0 {
4239 if exp % 2 == 1 {
4240 result = (result * base).rem_euclid(modulus);
4241 }
4242 exp /= 2;
4243 base = (base * base).rem_euclid(modulus);
4244 }
4245 result
4246}
4247
4248fn register_simd(interp: &mut Interpreter) {
4254 define(interp, "simd_new", Some(4), |_, args| {
4256 let values: Result<Vec<f64>, _> = args
4257 .iter()
4258 .map(|v| match v {
4259 Value::Float(f) => Ok(*f),
4260 Value::Int(i) => Ok(*i as f64),
4261 _ => Err(RuntimeError::new("simd_new() requires numbers")),
4262 })
4263 .collect();
4264 let values = values?;
4265 Ok(Value::Array(Rc::new(RefCell::new(vec![
4266 Value::Float(values[0]),
4267 Value::Float(values[1]),
4268 Value::Float(values[2]),
4269 Value::Float(values[3]),
4270 ]))))
4271 });
4272
4273 define(interp, "simd_splat", Some(1), |_, args| {
4275 let v = match &args[0] {
4276 Value::Float(f) => *f,
4277 Value::Int(i) => *i as f64,
4278 _ => return Err(RuntimeError::new("simd_splat() requires number")),
4279 };
4280 Ok(Value::Array(Rc::new(RefCell::new(vec![
4281 Value::Float(v),
4282 Value::Float(v),
4283 Value::Float(v),
4284 Value::Float(v),
4285 ]))))
4286 });
4287
4288 define(interp, "simd_add", Some(2), |_, args| {
4290 simd_binary_op(&args[0], &args[1], |a, b| a + b, "simd_add")
4291 });
4292
4293 define(interp, "simd_sub", Some(2), |_, args| {
4295 simd_binary_op(&args[0], &args[1], |a, b| a - b, "simd_sub")
4296 });
4297
4298 define(interp, "simd_mul", Some(2), |_, args| {
4300 simd_binary_op(&args[0], &args[1], |a, b| a * b, "simd_mul")
4301 });
4302
4303 define(interp, "simd_div", Some(2), |_, args| {
4305 simd_binary_op(&args[0], &args[1], |a, b| a / b, "simd_div")
4306 });
4307
4308 define(interp, "simd_dot", Some(2), |_, args| {
4310 let a = extract_simd(&args[0], "simd_dot")?;
4311 let b = extract_simd(&args[1], "simd_dot")?;
4312 let dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
4313 Ok(Value::Float(dot))
4314 });
4315
4316 define(interp, "simd_cross", Some(2), |_, args| {
4318 let a = extract_simd(&args[0], "simd_cross")?;
4319 let b = extract_simd(&args[1], "simd_cross")?;
4320 Ok(Value::Array(Rc::new(RefCell::new(vec![
4321 Value::Float(a[1] * b[2] - a[2] * b[1]),
4322 Value::Float(a[2] * b[0] - a[0] * b[2]),
4323 Value::Float(a[0] * b[1] - a[1] * b[0]),
4324 Value::Float(0.0),
4325 ]))))
4326 });
4327
4328 define(interp, "simd_length", Some(1), |_, args| {
4330 let v = extract_simd(&args[0], "simd_length")?;
4331 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
4332 Ok(Value::Float(len_sq.sqrt()))
4333 });
4334
4335 define(interp, "simd_normalize", Some(1), |_, args| {
4337 let v = extract_simd(&args[0], "simd_normalize")?;
4338 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
4339 let len = len_sq.sqrt();
4340 if len < 1e-10 {
4341 return Ok(Value::Array(Rc::new(RefCell::new(vec![
4342 Value::Float(0.0),
4343 Value::Float(0.0),
4344 Value::Float(0.0),
4345 Value::Float(0.0),
4346 ]))));
4347 }
4348 let inv_len = 1.0 / len;
4349 Ok(Value::Array(Rc::new(RefCell::new(vec![
4350 Value::Float(v[0] * inv_len),
4351 Value::Float(v[1] * inv_len),
4352 Value::Float(v[2] * inv_len),
4353 Value::Float(v[3] * inv_len),
4354 ]))))
4355 });
4356
4357 define(interp, "simd_min", Some(2), |_, args| {
4359 simd_binary_op(&args[0], &args[1], |a, b| a.min(b), "simd_min")
4360 });
4361
4362 define(interp, "simd_max", Some(2), |_, args| {
4364 simd_binary_op(&args[0], &args[1], |a, b| a.max(b), "simd_max")
4365 });
4366
4367 define(interp, "simd_hadd", Some(1), |_, args| {
4369 let v = extract_simd(&args[0], "simd_hadd")?;
4370 Ok(Value::Float(v[0] + v[1] + v[2] + v[3]))
4371 });
4372
4373 define(interp, "simd_extract", Some(2), |_, args| {
4375 let v = extract_simd(&args[0], "simd_extract")?;
4376 let idx = match &args[1] {
4377 Value::Int(i) => *i as usize,
4378 _ => return Err(RuntimeError::new("simd_extract() requires integer index")),
4379 };
4380 if idx > 3 {
4381 return Err(RuntimeError::new("simd_extract() index must be 0-3"));
4382 }
4383 Ok(Value::Float(v[idx]))
4384 });
4385
4386 define(interp, "simd_free", Some(1), |_, _| Ok(Value::Null));
4388
4389 define(interp, "simd_lerp", Some(3), |_, args| {
4391 let a = extract_simd(&args[0], "simd_lerp")?;
4392 let b = extract_simd(&args[1], "simd_lerp")?;
4393 let t = match &args[2] {
4394 Value::Float(f) => *f,
4395 Value::Int(i) => *i as f64,
4396 _ => return Err(RuntimeError::new("simd_lerp() requires float t")),
4397 };
4398 let one_t = 1.0 - t;
4399 Ok(Value::Array(Rc::new(RefCell::new(vec![
4400 Value::Float(a[0] * one_t + b[0] * t),
4401 Value::Float(a[1] * one_t + b[1] * t),
4402 Value::Float(a[2] * one_t + b[2] * t),
4403 Value::Float(a[3] * one_t + b[3] * t),
4404 ]))))
4405 });
4406}
4407
4408fn extract_simd(val: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
4410 match val {
4411 Value::Array(arr) => {
4412 let arr = arr.borrow();
4413 if arr.len() < 4 {
4414 return Err(RuntimeError::new(format!(
4415 "{}() requires 4-element array",
4416 fn_name
4417 )));
4418 }
4419 let mut result = [0.0; 4];
4420 for (i, v) in arr.iter().take(4).enumerate() {
4421 result[i] = match v {
4422 Value::Float(f) => *f,
4423 Value::Int(n) => *n as f64,
4424 _ => {
4425 return Err(RuntimeError::new(format!(
4426 "{}() requires numeric array",
4427 fn_name
4428 )))
4429 }
4430 };
4431 }
4432 Ok(result)
4433 }
4434 _ => Err(RuntimeError::new(format!(
4435 "{}() requires array argument",
4436 fn_name
4437 ))),
4438 }
4439}
4440
4441fn simd_binary_op<F>(a: &Value, b: &Value, op: F, fn_name: &str) -> Result<Value, RuntimeError>
4443where
4444 F: Fn(f64, f64) -> f64,
4445{
4446 let a = extract_simd(a, fn_name)?;
4447 let b = extract_simd(b, fn_name)?;
4448 Ok(Value::Array(Rc::new(RefCell::new(vec![
4449 Value::Float(op(a[0], b[0])),
4450 Value::Float(op(a[1], b[1])),
4451 Value::Float(op(a[2], b[2])),
4452 Value::Float(op(a[3], b[3])),
4453 ]))))
4454}
4455
4456fn register_graphics_math(interp: &mut Interpreter) {
4467 define(interp, "quat_new", Some(4), |_, args| {
4475 let w = extract_number(&args[0], "quat_new")?;
4476 let x = extract_number(&args[1], "quat_new")?;
4477 let y = extract_number(&args[2], "quat_new")?;
4478 let z = extract_number(&args[3], "quat_new")?;
4479 Ok(make_vec4(w, x, y, z))
4480 });
4481
4482 define(interp, "quat_identity", Some(0), |_, _| {
4484 Ok(make_vec4(1.0, 0.0, 0.0, 0.0))
4485 });
4486
4487 define(interp, "quat_from_axis_angle", Some(2), |_, args| {
4489 let axis = extract_vec3(&args[0], "quat_from_axis_angle")?;
4490 let angle = extract_number(&args[1], "quat_from_axis_angle")?;
4491
4492 let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
4494 if len < 1e-10 {
4495 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0)); }
4497 let ax = axis[0] / len;
4498 let ay = axis[1] / len;
4499 let az = axis[2] / len;
4500
4501 let half_angle = angle / 2.0;
4502 let s = half_angle.sin();
4503 let c = half_angle.cos();
4504
4505 Ok(make_vec4(c, ax * s, ay * s, az * s))
4506 });
4507
4508 define(interp, "quat_from_euler", Some(3), |_, args| {
4511 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();
4516 let (sy, cy) = (yaw / 2.0).sin_cos();
4517 let (sr, cr) = (roll / 2.0).sin_cos();
4518
4519 let w = cp * cy * cr + sp * sy * sr;
4521 let x = sp * cy * cr - cp * sy * sr;
4522 let y = cp * sy * cr + sp * cy * sr;
4523 let z = cp * cy * sr - sp * sy * cr;
4524
4525 Ok(make_vec4(w, x, y, z))
4526 });
4527
4528 define(interp, "quat_mul", Some(2), |_, args| {
4530 let q1 = extract_vec4(&args[0], "quat_mul")?;
4531 let q2 = extract_vec4(&args[1], "quat_mul")?;
4532
4533 let w = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
4535 let x = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
4536 let y = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
4537 let z = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
4538
4539 Ok(make_vec4(w, x, y, z))
4540 });
4541
4542 define(interp, "quat_conjugate", Some(1), |_, args| {
4544 let q = extract_vec4(&args[0], "quat_conjugate")?;
4545 Ok(make_vec4(q[0], -q[1], -q[2], -q[3]))
4546 });
4547
4548 define(interp, "quat_inverse", Some(1), |_, args| {
4550 let q = extract_vec4(&args[0], "quat_inverse")?;
4551 let norm_sq = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
4552 if norm_sq < 1e-10 {
4553 return Err(RuntimeError::new(
4554 "quat_inverse: cannot invert zero quaternion",
4555 ));
4556 }
4557 Ok(make_vec4(
4558 q[0] / norm_sq,
4559 -q[1] / norm_sq,
4560 -q[2] / norm_sq,
4561 -q[3] / norm_sq,
4562 ))
4563 });
4564
4565 define(interp, "quat_normalize", Some(1), |_, args| {
4567 let q = extract_vec4(&args[0], "quat_normalize")?;
4568 let len = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]).sqrt();
4569 if len < 1e-10 {
4570 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0));
4571 }
4572 Ok(make_vec4(q[0] / len, q[1] / len, q[2] / len, q[3] / len))
4573 });
4574
4575 define(interp, "quat_rotate", Some(2), |_, args| {
4577 let q = extract_vec4(&args[0], "quat_rotate")?;
4578 let v = extract_vec3(&args[1], "quat_rotate")?;
4579
4580 let qw = q[0];
4582 let qx = q[1];
4583 let qy = q[2];
4584 let qz = q[3];
4585 let vx = v[0];
4586 let vy = v[1];
4587 let vz = v[2];
4588
4589 let tx = 2.0 * (qy * vz - qz * vy);
4591 let ty = 2.0 * (qz * vx - qx * vz);
4592 let tz = 2.0 * (qx * vy - qy * vx);
4593
4594 let rx = vx + qw * tx + (qy * tz - qz * ty);
4596 let ry = vy + qw * ty + (qz * tx - qx * tz);
4597 let rz = vz + qw * tz + (qx * ty - qy * tx);
4598
4599 Ok(make_vec3(rx, ry, rz))
4600 });
4601
4602 define(interp, "quat_slerp", Some(3), |_, args| {
4604 let q1 = extract_vec4(&args[0], "quat_slerp")?;
4605 let mut q2 = extract_vec4(&args[1], "quat_slerp")?;
4606 let t = extract_number(&args[2], "quat_slerp")?;
4607
4608 let mut dot = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
4610
4611 if dot < 0.0 {
4613 q2 = [-q2[0], -q2[1], -q2[2], -q2[3]];
4614 dot = -dot;
4615 }
4616
4617 if dot > 0.9995 {
4619 let w = q1[0] + t * (q2[0] - q1[0]);
4620 let x = q1[1] + t * (q2[1] - q1[1]);
4621 let y = q1[2] + t * (q2[2] - q1[2]);
4622 let z = q1[3] + t * (q2[3] - q1[3]);
4623 let len = (w * w + x * x + y * y + z * z).sqrt();
4624 return Ok(make_vec4(w / len, x / len, y / len, z / len));
4625 }
4626
4627 let theta_0 = dot.acos();
4629 let theta = theta_0 * t;
4630 let sin_theta = theta.sin();
4631 let sin_theta_0 = theta_0.sin();
4632
4633 let s0 = (theta_0 - theta).cos() - dot * sin_theta / sin_theta_0;
4634 let s1 = sin_theta / sin_theta_0;
4635
4636 Ok(make_vec4(
4637 s0 * q1[0] + s1 * q2[0],
4638 s0 * q1[1] + s1 * q2[1],
4639 s0 * q1[2] + s1 * q2[2],
4640 s0 * q1[3] + s1 * q2[3],
4641 ))
4642 });
4643
4644 define(interp, "quat_to_euler", Some(1), |_, args| {
4646 let q = extract_vec4(&args[0], "quat_to_euler")?;
4647 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4648
4649 let sinr_cosp = 2.0 * (w * x + y * z);
4651 let cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
4652 let roll = sinr_cosp.atan2(cosr_cosp);
4653
4654 let sinp = 2.0 * (w * y - z * x);
4656 let pitch = if sinp.abs() >= 1.0 {
4657 std::f64::consts::FRAC_PI_2.copysign(sinp)
4658 } else {
4659 sinp.asin()
4660 };
4661
4662 let siny_cosp = 2.0 * (w * z + x * y);
4664 let cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
4665 let yaw = siny_cosp.atan2(cosy_cosp);
4666
4667 Ok(make_vec3(pitch, yaw, roll))
4668 });
4669
4670 define(interp, "quat_to_mat4", Some(1), |_, args| {
4672 let q = extract_vec4(&args[0], "quat_to_mat4")?;
4673 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4674
4675 let xx = x * x;
4676 let yy = y * y;
4677 let zz = z * z;
4678 let xy = x * y;
4679 let xz = x * z;
4680 let yz = y * z;
4681 let wx = w * x;
4682 let wy = w * y;
4683 let wz = w * z;
4684
4685 Ok(make_mat4([
4687 1.0 - 2.0 * (yy + zz),
4688 2.0 * (xy + wz),
4689 2.0 * (xz - wy),
4690 0.0,
4691 2.0 * (xy - wz),
4692 1.0 - 2.0 * (xx + zz),
4693 2.0 * (yz + wx),
4694 0.0,
4695 2.0 * (xz + wy),
4696 2.0 * (yz - wx),
4697 1.0 - 2.0 * (xx + yy),
4698 0.0,
4699 0.0,
4700 0.0,
4701 0.0,
4702 1.0,
4703 ]))
4704 });
4705
4706 define(interp, "vec2", Some(2), |_, args| {
4712 let x = extract_number(&args[0], "vec2")?;
4713 let y = extract_number(&args[1], "vec2")?;
4714 Ok(make_vec2(x, y))
4715 });
4716
4717 define(interp, "vec3", Some(3), |_, args| {
4719 let x = extract_number(&args[0], "vec3")?;
4720 let y = extract_number(&args[1], "vec3")?;
4721 let z = extract_number(&args[2], "vec3")?;
4722 Ok(make_vec3(x, y, z))
4723 });
4724
4725 define(interp, "vec4", Some(4), |_, args| {
4727 let x = extract_number(&args[0], "vec4")?;
4728 let y = extract_number(&args[1], "vec4")?;
4729 let z = extract_number(&args[2], "vec4")?;
4730 let w = extract_number(&args[3], "vec4")?;
4731 Ok(make_vec4(x, y, z, w))
4732 });
4733
4734 define(interp, "vec3_add", Some(2), |_, args| {
4736 let a = extract_vec3(&args[0], "vec3_add")?;
4737 let b = extract_vec3(&args[1], "vec3_add")?;
4738 Ok(make_vec3(a[0] + b[0], a[1] + b[1], a[2] + b[2]))
4739 });
4740
4741 define(interp, "vec3_sub", Some(2), |_, args| {
4743 let a = extract_vec3(&args[0], "vec3_sub")?;
4744 let b = extract_vec3(&args[1], "vec3_sub")?;
4745 Ok(make_vec3(a[0] - b[0], a[1] - b[1], a[2] - b[2]))
4746 });
4747
4748 define(interp, "vec3_scale", Some(2), |_, args| {
4750 let v = extract_vec3(&args[0], "vec3_scale")?;
4751 let s = extract_number(&args[1], "vec3_scale")?;
4752 Ok(make_vec3(v[0] * s, v[1] * s, v[2] * s))
4753 });
4754
4755 define(interp, "vec3_dot", Some(2), |_, args| {
4757 let a = extract_vec3(&args[0], "vec3_dot")?;
4758 let b = extract_vec3(&args[1], "vec3_dot")?;
4759 Ok(Value::Float(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]))
4760 });
4761
4762 define(interp, "vec3_cross", Some(2), |_, args| {
4764 let a = extract_vec3(&args[0], "vec3_cross")?;
4765 let b = extract_vec3(&args[1], "vec3_cross")?;
4766 Ok(make_vec3(
4767 a[1] * b[2] - a[2] * b[1],
4768 a[2] * b[0] - a[0] * b[2],
4769 a[0] * b[1] - a[1] * b[0],
4770 ))
4771 });
4772
4773 define(interp, "vec3_length", Some(1), |_, args| {
4775 let v = extract_vec3(&args[0], "vec3_length")?;
4776 Ok(Value::Float(
4777 (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt(),
4778 ))
4779 });
4780
4781 define(interp, "vec3_normalize", Some(1), |_, args| {
4783 let v = extract_vec3(&args[0], "vec3_normalize")?;
4784 let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
4785 if len < 1e-10 {
4786 return Ok(make_vec3(0.0, 0.0, 0.0));
4787 }
4788 Ok(make_vec3(v[0] / len, v[1] / len, v[2] / len))
4789 });
4790
4791 define(interp, "vec3_lerp", Some(3), |_, args| {
4793 let a = extract_vec3(&args[0], "vec3_lerp")?;
4794 let b = extract_vec3(&args[1], "vec3_lerp")?;
4795 let t = extract_number(&args[2], "vec3_lerp")?;
4796 Ok(make_vec3(
4797 a[0] + t * (b[0] - a[0]),
4798 a[1] + t * (b[1] - a[1]),
4799 a[2] + t * (b[2] - a[2]),
4800 ))
4801 });
4802
4803 define(interp, "vec3_reflect", Some(2), |_, args| {
4805 let i = extract_vec3(&args[0], "vec3_reflect")?;
4806 let n = extract_vec3(&args[1], "vec3_reflect")?;
4807 let dot = i[0] * n[0] + i[1] * n[1] + i[2] * n[2];
4808 Ok(make_vec3(
4809 i[0] - 2.0 * dot * n[0],
4810 i[1] - 2.0 * dot * n[1],
4811 i[2] - 2.0 * dot * n[2],
4812 ))
4813 });
4814
4815 define(interp, "vec3_refract", Some(3), |_, args| {
4817 let i = extract_vec3(&args[0], "vec3_refract")?;
4818 let n = extract_vec3(&args[1], "vec3_refract")?;
4819 let eta = extract_number(&args[2], "vec3_refract")?;
4820
4821 let dot = i[0] * n[0] + i[1] * n[1] + i[2] * n[2];
4822 let k = 1.0 - eta * eta * (1.0 - dot * dot);
4823
4824 if k < 0.0 {
4825 return Ok(make_vec3(0.0, 0.0, 0.0));
4827 }
4828
4829 let coeff = eta * dot + k.sqrt();
4830 Ok(make_vec3(
4831 eta * i[0] - coeff * n[0],
4832 eta * i[1] - coeff * n[1],
4833 eta * i[2] - coeff * n[2],
4834 ))
4835 });
4836
4837 define(interp, "vec4_dot", Some(2), |_, args| {
4839 let a = extract_vec4(&args[0], "vec4_dot")?;
4840 let b = extract_vec4(&args[1], "vec4_dot")?;
4841 Ok(Value::Float(
4842 a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3],
4843 ))
4844 });
4845
4846 define(interp, "mat4_identity", Some(0), |_, _| {
4852 Ok(make_mat4([
4853 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,
4854 ]))
4855 });
4856
4857 define(interp, "mat4_mul", Some(2), |_, args| {
4859 let a = extract_mat4(&args[0], "mat4_mul")?;
4860 let b = extract_mat4(&args[1], "mat4_mul")?;
4861
4862 let mut result = [0.0f64; 16];
4863 for col in 0..4 {
4864 for row in 0..4 {
4865 let mut sum = 0.0;
4866 for k in 0..4 {
4867 sum += a[k * 4 + row] * b[col * 4 + k];
4868 }
4869 result[col * 4 + row] = sum;
4870 }
4871 }
4872 Ok(make_mat4(result))
4873 });
4874
4875 define(interp, "mat4_transform", Some(2), |_, args| {
4877 let m = extract_mat4(&args[0], "mat4_transform")?;
4878 let v = extract_vec4(&args[1], "mat4_transform")?;
4879
4880 Ok(make_vec4(
4881 m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3],
4882 m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3],
4883 m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3],
4884 m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3],
4885 ))
4886 });
4887
4888 define(interp, "mat4_translate", Some(3), |_, args| {
4890 let tx = extract_number(&args[0], "mat4_translate")?;
4891 let ty = extract_number(&args[1], "mat4_translate")?;
4892 let tz = extract_number(&args[2], "mat4_translate")?;
4893 Ok(make_mat4([
4894 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,
4895 ]))
4896 });
4897
4898 define(interp, "mat4_scale", Some(3), |_, args| {
4900 let sx = extract_number(&args[0], "mat4_scale")?;
4901 let sy = extract_number(&args[1], "mat4_scale")?;
4902 let sz = extract_number(&args[2], "mat4_scale")?;
4903 Ok(make_mat4([
4904 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,
4905 ]))
4906 });
4907
4908 define(interp, "mat4_rotate_x", Some(1), |_, args| {
4910 let angle = extract_number(&args[0], "mat4_rotate_x")?;
4911 let (s, c) = angle.sin_cos();
4912 Ok(make_mat4([
4913 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,
4914 ]))
4915 });
4916
4917 define(interp, "mat4_rotate_y", Some(1), |_, args| {
4919 let angle = extract_number(&args[0], "mat4_rotate_y")?;
4920 let (s, c) = angle.sin_cos();
4921 Ok(make_mat4([
4922 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,
4923 ]))
4924 });
4925
4926 define(interp, "mat4_rotate_z", Some(1), |_, args| {
4928 let angle = extract_number(&args[0], "mat4_rotate_z")?;
4929 let (s, c) = angle.sin_cos();
4930 Ok(make_mat4([
4931 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,
4932 ]))
4933 });
4934
4935 define(interp, "mat4_perspective", Some(4), |_, args| {
4937 let fov_y = extract_number(&args[0], "mat4_perspective")?;
4938 let aspect = extract_number(&args[1], "mat4_perspective")?;
4939 let near = extract_number(&args[2], "mat4_perspective")?;
4940 let far = extract_number(&args[3], "mat4_perspective")?;
4941
4942 let f = 1.0 / (fov_y / 2.0).tan();
4943 let nf = 1.0 / (near - far);
4944
4945 Ok(make_mat4([
4946 f / aspect,
4947 0.0,
4948 0.0,
4949 0.0,
4950 0.0,
4951 f,
4952 0.0,
4953 0.0,
4954 0.0,
4955 0.0,
4956 (far + near) * nf,
4957 -1.0,
4958 0.0,
4959 0.0,
4960 2.0 * far * near * nf,
4961 0.0,
4962 ]))
4963 });
4964
4965 define(interp, "mat4_ortho", Some(6), |_, args| {
4967 let left = extract_number(&args[0], "mat4_ortho")?;
4968 let right = extract_number(&args[1], "mat4_ortho")?;
4969 let bottom = extract_number(&args[2], "mat4_ortho")?;
4970 let top = extract_number(&args[3], "mat4_ortho")?;
4971 let near = extract_number(&args[4], "mat4_ortho")?;
4972 let far = extract_number(&args[5], "mat4_ortho")?;
4973
4974 let lr = 1.0 / (left - right);
4975 let bt = 1.0 / (bottom - top);
4976 let nf = 1.0 / (near - far);
4977
4978 Ok(make_mat4([
4979 -2.0 * lr,
4980 0.0,
4981 0.0,
4982 0.0,
4983 0.0,
4984 -2.0 * bt,
4985 0.0,
4986 0.0,
4987 0.0,
4988 0.0,
4989 2.0 * nf,
4990 0.0,
4991 (left + right) * lr,
4992 (top + bottom) * bt,
4993 (far + near) * nf,
4994 1.0,
4995 ]))
4996 });
4997
4998 define(interp, "mat4_look_at", Some(3), |_, args| {
5000 let eye = extract_vec3(&args[0], "mat4_look_at")?;
5001 let center = extract_vec3(&args[1], "mat4_look_at")?;
5002 let up = extract_vec3(&args[2], "mat4_look_at")?;
5003
5004 let fx = center[0] - eye[0];
5006 let fy = center[1] - eye[1];
5007 let fz = center[2] - eye[2];
5008 let flen = (fx * fx + fy * fy + fz * fz).sqrt();
5009 let (fx, fy, fz) = (fx / flen, fy / flen, fz / flen);
5010
5011 let rx = fy * up[2] - fz * up[1];
5013 let ry = fz * up[0] - fx * up[2];
5014 let rz = fx * up[1] - fy * up[0];
5015 let rlen = (rx * rx + ry * ry + rz * rz).sqrt();
5016 let (rx, ry, rz) = (rx / rlen, ry / rlen, rz / rlen);
5017
5018 let ux = ry * fz - rz * fy;
5020 let uy = rz * fx - rx * fz;
5021 let uz = rx * fy - ry * fx;
5022
5023 Ok(make_mat4([
5024 rx,
5025 ux,
5026 -fx,
5027 0.0,
5028 ry,
5029 uy,
5030 -fy,
5031 0.0,
5032 rz,
5033 uz,
5034 -fz,
5035 0.0,
5036 -(rx * eye[0] + ry * eye[1] + rz * eye[2]),
5037 -(ux * eye[0] + uy * eye[1] + uz * eye[2]),
5038 -(-fx * eye[0] - fy * eye[1] - fz * eye[2]),
5039 1.0,
5040 ]))
5041 });
5042
5043 define(interp, "mat4_inverse", Some(1), |_, args| {
5045 let m = extract_mat4(&args[0], "mat4_inverse")?;
5046
5047 let a00 = m[0];
5049 let a01 = m[1];
5050 let a02 = m[2];
5051 let a03 = m[3];
5052 let a10 = m[4];
5053 let a11 = m[5];
5054 let a12 = m[6];
5055 let a13 = m[7];
5056 let a20 = m[8];
5057 let a21 = m[9];
5058 let a22 = m[10];
5059 let a23 = m[11];
5060 let a30 = m[12];
5061 let a31 = m[13];
5062 let a32 = m[14];
5063 let a33 = m[15];
5064
5065 let b00 = a00 * a11 - a01 * a10;
5066 let b01 = a00 * a12 - a02 * a10;
5067 let b02 = a00 * a13 - a03 * a10;
5068 let b03 = a01 * a12 - a02 * a11;
5069 let b04 = a01 * a13 - a03 * a11;
5070 let b05 = a02 * a13 - a03 * a12;
5071 let b06 = a20 * a31 - a21 * a30;
5072 let b07 = a20 * a32 - a22 * a30;
5073 let b08 = a20 * a33 - a23 * a30;
5074 let b09 = a21 * a32 - a22 * a31;
5075 let b10 = a21 * a33 - a23 * a31;
5076 let b11 = a22 * a33 - a23 * a32;
5077
5078 let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
5079
5080 if det.abs() < 1e-10 {
5081 return Err(RuntimeError::new("mat4_inverse: singular matrix"));
5082 }
5083
5084 let inv_det = 1.0 / det;
5085
5086 Ok(make_mat4([
5087 (a11 * b11 - a12 * b10 + a13 * b09) * inv_det,
5088 (a02 * b10 - a01 * b11 - a03 * b09) * inv_det,
5089 (a31 * b05 - a32 * b04 + a33 * b03) * inv_det,
5090 (a22 * b04 - a21 * b05 - a23 * b03) * inv_det,
5091 (a12 * b08 - a10 * b11 - a13 * b07) * inv_det,
5092 (a00 * b11 - a02 * b08 + a03 * b07) * inv_det,
5093 (a32 * b02 - a30 * b05 - a33 * b01) * inv_det,
5094 (a20 * b05 - a22 * b02 + a23 * b01) * inv_det,
5095 (a10 * b10 - a11 * b08 + a13 * b06) * inv_det,
5096 (a01 * b08 - a00 * b10 - a03 * b06) * inv_det,
5097 (a30 * b04 - a31 * b02 + a33 * b00) * inv_det,
5098 (a21 * b02 - a20 * b04 - a23 * b00) * inv_det,
5099 (a11 * b07 - a10 * b09 - a12 * b06) * inv_det,
5100 (a00 * b09 - a01 * b07 + a02 * b06) * inv_det,
5101 (a31 * b01 - a30 * b03 - a32 * b00) * inv_det,
5102 (a20 * b03 - a21 * b01 + a22 * b00) * inv_det,
5103 ]))
5104 });
5105
5106 define(interp, "mat4_transpose", Some(1), |_, args| {
5108 let m = extract_mat4(&args[0], "mat4_transpose")?;
5109 Ok(make_mat4([
5110 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],
5111 m[11], m[15],
5112 ]))
5113 });
5114
5115 define(interp, "mat3_identity", Some(0), |_, _| {
5121 Ok(make_mat3([1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]))
5122 });
5123
5124 define(interp, "mat3_from_mat4", Some(1), |_, args| {
5126 let m = extract_mat4(&args[0], "mat3_from_mat4")?;
5127 Ok(make_mat3([
5128 m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10],
5129 ]))
5130 });
5131
5132 define(interp, "mat3_mul", Some(2), |_, args| {
5134 let a = extract_mat3(&args[0], "mat3_mul")?;
5135 let b = extract_mat3(&args[1], "mat3_mul")?;
5136
5137 let mut result = [0.0f64; 9];
5138 for col in 0..3 {
5139 for row in 0..3 {
5140 let mut sum = 0.0;
5141 for k in 0..3 {
5142 sum += a[k * 3 + row] * b[col * 3 + k];
5143 }
5144 result[col * 3 + row] = sum;
5145 }
5146 }
5147 Ok(make_mat3(result))
5148 });
5149
5150 define(interp, "mat3_transform", Some(2), |_, args| {
5152 let m = extract_mat3(&args[0], "mat3_transform")?;
5153 let v = extract_vec3(&args[1], "mat3_transform")?;
5154
5155 Ok(make_vec3(
5156 m[0] * v[0] + m[3] * v[1] + m[6] * v[2],
5157 m[1] * v[0] + m[4] * v[1] + m[7] * v[2],
5158 m[2] * v[0] + m[5] * v[1] + m[8] * v[2],
5159 ))
5160 });
5161
5162 define(interp, "mat3_inverse", Some(1), |_, args| {
5164 let m = extract_mat3(&args[0], "mat3_inverse")?;
5165
5166 let det = m[0] * (m[4] * m[8] - m[5] * m[7]) - m[3] * (m[1] * m[8] - m[2] * m[7])
5167 + m[6] * (m[1] * m[5] - m[2] * m[4]);
5168
5169 if det.abs() < 1e-10 {
5170 return Err(RuntimeError::new("mat3_inverse: singular matrix"));
5171 }
5172
5173 let inv_det = 1.0 / det;
5174
5175 Ok(make_mat3([
5176 (m[4] * m[8] - m[5] * m[7]) * inv_det,
5177 (m[2] * m[7] - m[1] * m[8]) * inv_det,
5178 (m[1] * m[5] - m[2] * m[4]) * inv_det,
5179 (m[5] * m[6] - m[3] * m[8]) * inv_det,
5180 (m[0] * m[8] - m[2] * m[6]) * inv_det,
5181 (m[2] * m[3] - m[0] * m[5]) * inv_det,
5182 (m[3] * m[7] - m[4] * m[6]) * inv_det,
5183 (m[1] * m[6] - m[0] * m[7]) * inv_det,
5184 (m[0] * m[4] - m[1] * m[3]) * inv_det,
5185 ]))
5186 });
5187
5188 define(interp, "mat3_transpose", Some(1), |_, args| {
5190 let m = extract_mat3(&args[0], "mat3_transpose")?;
5191 Ok(make_mat3([
5192 m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8],
5193 ]))
5194 });
5195}
5196
5197fn extract_number(v: &Value, fn_name: &str) -> Result<f64, RuntimeError> {
5199 match v {
5200 Value::Float(f) => Ok(*f),
5201 Value::Int(i) => Ok(*i as f64),
5202 _ => Err(RuntimeError::new(format!(
5203 "{}() requires number argument",
5204 fn_name
5205 ))),
5206 }
5207}
5208
5209fn extract_vec2(v: &Value, fn_name: &str) -> Result<[f64; 2], RuntimeError> {
5210 match v {
5211 Value::Array(arr) => {
5212 let arr = arr.borrow();
5213 if arr.len() < 2 {
5214 return Err(RuntimeError::new(format!(
5215 "{}() requires vec2 (2 elements)",
5216 fn_name
5217 )));
5218 }
5219 Ok([
5220 extract_number(&arr[0], fn_name)?,
5221 extract_number(&arr[1], fn_name)?,
5222 ])
5223 }
5224 _ => Err(RuntimeError::new(format!(
5225 "{}() requires vec2 array",
5226 fn_name
5227 ))),
5228 }
5229}
5230
5231fn extract_vec3(v: &Value, fn_name: &str) -> Result<[f64; 3], RuntimeError> {
5232 match v {
5233 Value::Array(arr) => {
5234 let arr = arr.borrow();
5235 if arr.len() < 3 {
5236 return Err(RuntimeError::new(format!(
5237 "{}() requires vec3 (3 elements)",
5238 fn_name
5239 )));
5240 }
5241 Ok([
5242 extract_number(&arr[0], fn_name)?,
5243 extract_number(&arr[1], fn_name)?,
5244 extract_number(&arr[2], fn_name)?,
5245 ])
5246 }
5247 _ => Err(RuntimeError::new(format!(
5248 "{}() requires vec3 array",
5249 fn_name
5250 ))),
5251 }
5252}
5253
5254fn extract_vec4(v: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
5255 match v {
5256 Value::Array(arr) => {
5257 let arr = arr.borrow();
5258 if arr.len() < 4 {
5259 return Err(RuntimeError::new(format!(
5260 "{}() requires vec4 (4 elements)",
5261 fn_name
5262 )));
5263 }
5264 Ok([
5265 extract_number(&arr[0], fn_name)?,
5266 extract_number(&arr[1], fn_name)?,
5267 extract_number(&arr[2], fn_name)?,
5268 extract_number(&arr[3], fn_name)?,
5269 ])
5270 }
5271 _ => Err(RuntimeError::new(format!(
5272 "{}() requires vec4 array",
5273 fn_name
5274 ))),
5275 }
5276}
5277
5278fn extract_mat3(v: &Value, fn_name: &str) -> Result<[f64; 9], RuntimeError> {
5279 match v {
5280 Value::Array(arr) => {
5281 let arr = arr.borrow();
5282 if arr.len() < 9 {
5283 return Err(RuntimeError::new(format!(
5284 "{}() requires mat3 (9 elements)",
5285 fn_name
5286 )));
5287 }
5288 let mut result = [0.0f64; 9];
5289 for i in 0..9 {
5290 result[i] = extract_number(&arr[i], fn_name)?;
5291 }
5292 Ok(result)
5293 }
5294 _ => Err(RuntimeError::new(format!(
5295 "{}() requires mat3 array",
5296 fn_name
5297 ))),
5298 }
5299}
5300
5301fn extract_mat4(v: &Value, fn_name: &str) -> Result<[f64; 16], RuntimeError> {
5302 match v {
5303 Value::Array(arr) => {
5304 let arr = arr.borrow();
5305 if arr.len() < 16 {
5306 return Err(RuntimeError::new(format!(
5307 "{}() requires mat4 (16 elements)",
5308 fn_name
5309 )));
5310 }
5311 let mut result = [0.0f64; 16];
5312 for i in 0..16 {
5313 result[i] = extract_number(&arr[i], fn_name)?;
5314 }
5315 Ok(result)
5316 }
5317 _ => Err(RuntimeError::new(format!(
5318 "{}() requires mat4 array",
5319 fn_name
5320 ))),
5321 }
5322}
5323
5324fn make_vec2(x: f64, y: f64) -> Value {
5325 Value::Array(Rc::new(RefCell::new(vec![
5326 Value::Float(x),
5327 Value::Float(y),
5328 ])))
5329}
5330
5331fn make_vec3(x: f64, y: f64, z: f64) -> Value {
5332 Value::Array(Rc::new(RefCell::new(vec![
5333 Value::Float(x),
5334 Value::Float(y),
5335 Value::Float(z),
5336 ])))
5337}
5338
5339fn make_vec3_arr(v: [f64; 3]) -> Value {
5341 make_vec3(v[0], v[1], v[2])
5342}
5343
5344fn make_vec4(x: f64, y: f64, z: f64, w: f64) -> Value {
5345 Value::Array(Rc::new(RefCell::new(vec![
5346 Value::Float(x),
5347 Value::Float(y),
5348 Value::Float(z),
5349 Value::Float(w),
5350 ])))
5351}
5352
5353fn make_mat3(m: [f64; 9]) -> Value {
5354 Value::Array(Rc::new(RefCell::new(
5355 m.iter().map(|&v| Value::Float(v)).collect(),
5356 )))
5357}
5358
5359fn make_mat4(m: [f64; 16]) -> Value {
5360 Value::Array(Rc::new(RefCell::new(
5361 m.iter().map(|&v| Value::Float(v)).collect(),
5362 )))
5363}
5364
5365fn register_concurrency(interp: &mut Interpreter) {
5382 define(interp, "channel_new", Some(0), |_, _| {
5386 let (sender, receiver) = mpsc::channel();
5387 let inner = ChannelInner {
5388 sender: Mutex::new(sender),
5389 receiver: Mutex::new(receiver),
5390 };
5391 Ok(Value::Channel(Arc::new(inner)))
5392 });
5393
5394 define(interp, "channel_send", Some(2), |_, args| {
5396 let channel = match &args[0] {
5397 Value::Channel(ch) => ch.clone(),
5398 _ => {
5399 return Err(RuntimeError::new(
5400 "channel_send() requires channel as first argument",
5401 ))
5402 }
5403 };
5404 let value = args[1].clone();
5405
5406 let sender = channel
5407 .sender
5408 .lock()
5409 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5410 sender
5411 .send(value)
5412 .map_err(|_| RuntimeError::new("channel_send() failed: receiver dropped"))?;
5413
5414 Ok(Value::Null)
5415 });
5416
5417 define(interp, "channel_recv", Some(1), |_, args| {
5419 let channel = match &args[0] {
5420 Value::Channel(ch) => ch.clone(),
5421 _ => {
5422 return Err(RuntimeError::new(
5423 "channel_recv() requires channel argument",
5424 ))
5425 }
5426 };
5427
5428 let receiver = channel
5429 .receiver
5430 .lock()
5431 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5432 match receiver.recv() {
5433 Ok(value) => Ok(value),
5434 Err(_) => Err(RuntimeError::new("channel_recv() failed: sender dropped")),
5435 }
5436 });
5437
5438 define(interp, "channel_try_recv", Some(1), |_, args| {
5440 let channel = match &args[0] {
5441 Value::Channel(ch) => ch.clone(),
5442 _ => {
5443 return Err(RuntimeError::new(
5444 "channel_try_recv() requires channel argument",
5445 ))
5446 }
5447 };
5448
5449 let receiver = channel
5450 .receiver
5451 .lock()
5452 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5453 match receiver.try_recv() {
5454 Ok(value) => {
5455 Ok(Value::Variant {
5457 enum_name: "Option".to_string(),
5458 variant_name: "Some".to_string(),
5459 fields: Some(Rc::new(vec![value])),
5460 })
5461 }
5462 Err(mpsc::TryRecvError::Empty) => {
5463 Ok(Value::Variant {
5465 enum_name: "Option".to_string(),
5466 variant_name: "None".to_string(),
5467 fields: None,
5468 })
5469 }
5470 Err(mpsc::TryRecvError::Disconnected) => Err(RuntimeError::new(
5471 "channel_try_recv() failed: sender dropped",
5472 )),
5473 }
5474 });
5475
5476 define(interp, "channel_recv_timeout", Some(2), |_, args| {
5478 let channel = match &args[0] {
5479 Value::Channel(ch) => ch.clone(),
5480 _ => {
5481 return Err(RuntimeError::new(
5482 "channel_recv_timeout() requires a channel as first argument.\n\
5483 Create a channel with channel_new():\n\
5484 let ch = channel_new();\n\
5485 channel_send(ch, value);\n\
5486 let result = channel_recv_timeout(ch, 1000); // 1 second timeout",
5487 ))
5488 }
5489 };
5490 let timeout_ms = match &args[1] {
5491 Value::Int(ms) => *ms as u64,
5492 _ => {
5493 return Err(RuntimeError::new(
5494 "channel_recv_timeout() requires timeout in milliseconds (integer).\n\
5495 Example: channel_recv_timeout(ch, 1000) // Wait up to 1 second",
5496 ))
5497 }
5498 };
5499
5500 let receiver = channel
5501 .receiver
5502 .lock()
5503 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5504 match receiver.recv_timeout(std::time::Duration::from_millis(timeout_ms)) {
5505 Ok(value) => Ok(Value::Variant {
5506 enum_name: "Option".to_string(),
5507 variant_name: "Some".to_string(),
5508 fields: Some(Rc::new(vec![value])),
5509 }),
5510 Err(mpsc::RecvTimeoutError::Timeout) => Ok(Value::Variant {
5511 enum_name: "Option".to_string(),
5512 variant_name: "None".to_string(),
5513 fields: None,
5514 }),
5515 Err(mpsc::RecvTimeoutError::Disconnected) => Err(RuntimeError::new(
5516 "channel_recv_timeout() failed: sender dropped",
5517 )),
5518 }
5519 });
5520
5521 define(interp, "thread_spawn_detached", Some(0), |_, _| {
5529 thread::spawn(|| {
5532 });
5534 Ok(Value::Null)
5535 });
5536
5537 define(interp, "std·thread·spawn", Some(1), |interp, args| {
5541 match &args[0] {
5543 Value::Function(f) => {
5544 match interp.call_function(f, vec![]) {
5547 Ok(_) => {}
5548 Err(e) => eprintln!("[Sigil thread] Error: {}", e),
5549 }
5550 let mut map = HashMap::new();
5552 map.insert(
5553 "__type__".to_string(),
5554 Value::String(Rc::new("JoinHandle".to_string())),
5555 );
5556 map.insert("done".to_string(), Value::Bool(true));
5557 Ok(Value::Map(Rc::new(RefCell::new(map))))
5558 }
5559 Value::BuiltIn(b) => {
5560 match (b.func)(interp, vec![]) {
5561 Ok(_) => {}
5562 Err(e) => eprintln!("[Sigil thread] Error: {}", e),
5563 }
5564 let mut map = HashMap::new();
5565 map.insert(
5566 "__type__".to_string(),
5567 Value::String(Rc::new("JoinHandle".to_string())),
5568 );
5569 map.insert("done".to_string(), Value::Bool(true));
5570 Ok(Value::Map(Rc::new(RefCell::new(map))))
5571 }
5572 _ => Err(RuntimeError::new("std::thread::spawn requires a closure")),
5573 }
5574 });
5575
5576 define(interp, "thread_join", Some(1), |_, args| {
5579 match &args[0] {
5580 Value::ThreadHandle(h) => {
5581 let mut guard = h
5582 .lock()
5583 .map_err(|_| RuntimeError::new("thread handle mutex poisoned"))?;
5584 if let Some(handle) = guard.take() {
5585 match handle.join() {
5586 Ok(v) => Ok(v),
5587 Err(_) => Err(RuntimeError::new("thread panicked")),
5588 }
5589 } else {
5590 Err(RuntimeError::new("thread already joined"))
5591 }
5592 }
5593 _ => Ok(args[0].clone()),
5595 }
5596 });
5597
5598 define(interp, "thread_sleep", Some(1), |_, args| {
5600 let ms = match &args[0] {
5601 Value::Int(ms) => *ms as u64,
5602 Value::Float(ms) => *ms as u64,
5603 _ => {
5604 return Err(RuntimeError::new(
5605 "thread_sleep() requires integer milliseconds",
5606 ))
5607 }
5608 };
5609
5610 thread::sleep(std::time::Duration::from_millis(ms));
5611 Ok(Value::Null)
5612 });
5613
5614 define(interp, "thread_yield", Some(0), |_, _| {
5616 thread::yield_now();
5617 Ok(Value::Null)
5618 });
5619
5620 define(interp, "thread_id", Some(0), |_, _| {
5622 let id = thread::current().id();
5623 Ok(Value::String(Rc::new(format!("{:?}", id))))
5624 });
5625
5626 define(interp, "parking_lot·Mutex·new", Some(1), |_, args| {
5630 let mut map = HashMap::new();
5631 map.insert(
5632 "__type__".to_string(),
5633 Value::String(Rc::new("Mutex".to_string())),
5634 );
5635 map.insert("inner".to_string(), args[0].clone());
5636 Ok(Value::Map(Rc::new(RefCell::new(map))))
5637 });
5638
5639 define(interp, "std·sync·Mutex·new", Some(1), |_, args| {
5641 let mut map = HashMap::new();
5642 map.insert(
5643 "__type__".to_string(),
5644 Value::String(Rc::new("Mutex".to_string())),
5645 );
5646 map.insert("inner".to_string(), args[0].clone());
5647 Ok(Value::Map(Rc::new(RefCell::new(map))))
5648 });
5649
5650 define(interp, "parking_lot·RwLock·new", Some(1), |_, args| {
5652 let mut map = HashMap::new();
5653 map.insert(
5654 "__type__".to_string(),
5655 Value::String(Rc::new("RwLock".to_string())),
5656 );
5657 map.insert("inner".to_string(), args[0].clone());
5658 Ok(Value::Map(Rc::new(RefCell::new(map))))
5659 });
5660
5661 define(interp, "std·sync·RwLock·new", Some(1), |_, args| {
5663 let mut map = HashMap::new();
5664 map.insert(
5665 "__type__".to_string(),
5666 Value::String(Rc::new("RwLock".to_string())),
5667 );
5668 map.insert("inner".to_string(), args[0].clone());
5669 Ok(Value::Map(Rc::new(RefCell::new(map))))
5670 });
5671
5672 define(interp, "RwLock·new", Some(1), |_, args| {
5674 let mut map = HashMap::new();
5675 map.insert(
5676 "__type__".to_string(),
5677 Value::String(Rc::new("RwLock".to_string())),
5678 );
5679 map.insert("inner".to_string(), args[0].clone());
5680 Ok(Value::Map(Rc::new(RefCell::new(map))))
5681 });
5682
5683 define(interp, "AtomicU64·new", Some(1), |_, args| {
5685 let val = match &args[0] {
5686 Value::Int(i) => *i,
5687 _ => 0,
5688 };
5689 let mut map = HashMap::new();
5690 map.insert(
5691 "__type__".to_string(),
5692 Value::String(Rc::new("AtomicU64".to_string())),
5693 );
5694 map.insert("value".to_string(), Value::Int(val));
5695 Ok(Value::Map(Rc::new(RefCell::new(map))))
5696 });
5697
5698 define(
5700 interp,
5701 "std·sync·atomic·AtomicU64·new",
5702 Some(1),
5703 |_, args| {
5704 let val = match &args[0] {
5705 Value::Int(i) => *i,
5706 _ => 0,
5707 };
5708 let mut map = HashMap::new();
5709 map.insert(
5710 "__type__".to_string(),
5711 Value::String(Rc::new("AtomicU64".to_string())),
5712 );
5713 map.insert("value".to_string(), Value::Int(val));
5714 Ok(Value::Map(Rc::new(RefCell::new(map))))
5715 },
5716 );
5717
5718 define(interp, "AtomicBool·new", Some(1), |_, args| {
5720 let val = match &args[0] {
5721 Value::Bool(b) => *b,
5722 _ => false,
5723 };
5724 let mut map = HashMap::new();
5725 map.insert(
5726 "__type__".to_string(),
5727 Value::String(Rc::new("AtomicBool".to_string())),
5728 );
5729 map.insert("value".to_string(), Value::Bool(val));
5730 Ok(Value::Map(Rc::new(RefCell::new(map))))
5731 });
5732
5733 define(
5734 interp,
5735 "std·sync·atomic·AtomicBool·new",
5736 Some(1),
5737 |_, args| {
5738 let val = match &args[0] {
5739 Value::Bool(b) => *b,
5740 _ => false,
5741 };
5742 let mut map = HashMap::new();
5743 map.insert(
5744 "__type__".to_string(),
5745 Value::String(Rc::new("AtomicBool".to_string())),
5746 );
5747 map.insert("value".to_string(), Value::Bool(val));
5748 Ok(Value::Map(Rc::new(RefCell::new(map))))
5749 },
5750 );
5751
5752 define(interp, "Arc·new", Some(1), |_, args| {
5754 let mut map = HashMap::new();
5755 map.insert(
5756 "__type__".to_string(),
5757 Value::String(Rc::new("Arc".to_string())),
5758 );
5759 map.insert("inner".to_string(), args[0].clone());
5760 Ok(Value::Map(Rc::new(RefCell::new(map))))
5761 });
5762
5763 define(interp, "std·sync·Arc·new", Some(1), |_, args| {
5764 let mut map = HashMap::new();
5765 map.insert(
5766 "__type__".to_string(),
5767 Value::String(Rc::new("Arc".to_string())),
5768 );
5769 map.insert("inner".to_string(), args[0].clone());
5770 Ok(Value::Map(Rc::new(RefCell::new(map))))
5771 });
5772
5773 define(interp, "TcpListener·bind", Some(1), |_, args| {
5778 let addr_str = match &args[0] {
5779 Value::String(s) => s.to_string(),
5780 Value::Ref(r) => {
5781 if let Value::String(s) = &*r.borrow() {
5782 s.to_string()
5783 } else {
5784 return Err(RuntimeError::new(
5785 "TcpListener::bind requires string address",
5786 ));
5787 }
5788 }
5789 Value::Map(m) => {
5791 let borrowed = m.borrow();
5792 if let Some(Value::String(addr)) = borrowed.get("addr") {
5793 addr.to_string()
5794 } else if let Some(Value::String(_)) = borrowed.get("__type__") {
5795 if let Some(Value::String(addr)) = borrowed.get("addr") {
5797 addr.to_string()
5798 } else {
5799 return Err(RuntimeError::new("SocketAddr missing addr field"));
5800 }
5801 } else {
5802 return Err(RuntimeError::new(
5803 "TcpListener::bind requires string or SocketAddr",
5804 ));
5805 }
5806 }
5807 _ => {
5808 return Err(RuntimeError::new(
5809 "TcpListener::bind requires string address",
5810 ))
5811 }
5812 };
5813
5814 let addr: std::net::SocketAddr = match addr_str.parse() {
5816 Ok(a) => a,
5817 Err(e) => return Err(RuntimeError::new(format!("Invalid address: {}", e))),
5818 };
5819
5820 let listener = match std::net::TcpListener::bind(addr) {
5822 Ok(l) => l,
5823 Err(e) => return Err(RuntimeError::new(format!("Failed to bind: {}", e))),
5824 };
5825
5826 let local_addr = listener
5827 .local_addr()
5828 .map(|a| a.to_string())
5829 .unwrap_or_default();
5830
5831 let listener_id = store_listener(listener);
5833
5834 let mut map = HashMap::new();
5835 map.insert(
5836 "__type__".to_string(),
5837 Value::String(Rc::new("TcpListener".to_string())),
5838 );
5839 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5840 map.insert("local_addr".to_string(), Value::String(Rc::new(local_addr)));
5841 map.insert(
5842 "__listener_id__".to_string(),
5843 Value::Int(listener_id as i64),
5844 );
5845
5846 eprintln!("[Sigil] TcpListener bound to {} (id={})", addr, listener_id);
5847
5848 Ok(Value::Variant {
5849 enum_name: "Result".to_string(),
5850 variant_name: "Ok".to_string(),
5851 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5852 })
5853 });
5854
5855 define(interp, "std·net·TcpListener·bind", Some(1), |_, args| {
5856 let addr_str = match &args[0] {
5857 Value::String(s) => s.to_string(),
5858 Value::Ref(r) => {
5859 if let Value::String(s) = &*r.borrow() {
5860 s.to_string()
5861 } else {
5862 return Err(RuntimeError::new(
5863 "TcpListener::bind requires string address",
5864 ));
5865 }
5866 }
5867 Value::Map(m) => {
5869 let borrowed = m.borrow();
5870 if let Some(Value::String(addr)) = borrowed.get("addr") {
5871 addr.to_string()
5872 } else {
5873 return Err(RuntimeError::new(
5874 "TcpListener::bind requires string or SocketAddr",
5875 ));
5876 }
5877 }
5878 _ => {
5879 return Err(RuntimeError::new(
5880 "TcpListener::bind requires string address",
5881 ))
5882 }
5883 };
5884
5885 let addr: std::net::SocketAddr = match addr_str.parse() {
5886 Ok(a) => a,
5887 Err(e) => return Err(RuntimeError::new(format!("Invalid address: {}", e))),
5888 };
5889
5890 let _listener = match std::net::TcpListener::bind(addr) {
5891 Ok(l) => l,
5892 Err(e) => return Err(RuntimeError::new(format!("Failed to bind: {}", e))),
5893 };
5894
5895 let mut map = HashMap::new();
5896 map.insert(
5897 "__type__".to_string(),
5898 Value::String(Rc::new("TcpListener".to_string())),
5899 );
5900 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5901
5902 eprintln!("[Sigil] TcpListener bound to {}", addr);
5903
5904 Ok(Value::Variant {
5905 enum_name: "Result".to_string(),
5906 variant_name: "Ok".to_string(),
5907 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5908 })
5909 });
5910
5911 define(interp, "SocketAddr·parse", Some(1), |_, args| {
5913 let addr_str = match &args[0] {
5914 Value::String(s) => s.to_string(),
5915 _ => return Err(RuntimeError::new("SocketAddr::parse requires string")),
5916 };
5917
5918 match addr_str.parse::<std::net::SocketAddr>() {
5919 Ok(_) => {
5920 let mut map = HashMap::new();
5921 map.insert(
5922 "__type__".to_string(),
5923 Value::String(Rc::new("SocketAddr".to_string())),
5924 );
5925 map.insert("addr".to_string(), Value::String(Rc::new(addr_str)));
5926 Ok(Value::Variant {
5927 enum_name: "Result".to_string(),
5928 variant_name: "Ok".to_string(),
5929 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5930 })
5931 }
5932 Err(e) => Ok(Value::Variant {
5933 enum_name: "Result".to_string(),
5934 variant_name: "Err".to_string(),
5935 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5936 }),
5937 }
5938 });
5939
5940 define(interp, "TcpStream·peer_addr", Some(1), |_, args| {
5945 let stream_id = match &args[0] {
5946 Value::Map(m) => {
5947 let borrowed = m.borrow();
5948 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5949 *id as u64
5950 } else {
5951 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5952 }
5953 }
5954 _ => return Err(RuntimeError::new("peer_addr requires TcpStream")),
5955 };
5956
5957 if let Some(guard) = get_stream_registry().lock().ok() {
5958 if let Some(stream) = guard.get(&stream_id) {
5959 match stream.peer_addr() {
5960 Ok(addr) => {
5961 let mut map = HashMap::new();
5962 map.insert(
5963 "__type__".to_string(),
5964 Value::String(Rc::new("SocketAddr".to_string())),
5965 );
5966 map.insert("addr".to_string(), Value::String(Rc::new(addr.to_string())));
5967 Ok(Value::Variant {
5968 enum_name: "Result".to_string(),
5969 variant_name: "Ok".to_string(),
5970 fields: Some(Rc::new(vec![Value::Map(Rc::new(RefCell::new(map)))])),
5971 })
5972 }
5973 Err(e) => Ok(Value::Variant {
5974 enum_name: "Result".to_string(),
5975 variant_name: "Err".to_string(),
5976 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
5977 }),
5978 }
5979 } else {
5980 Err(RuntimeError::new("TcpStream not found in registry"))
5981 }
5982 } else {
5983 Err(RuntimeError::new("Failed to lock stream registry"))
5984 }
5985 });
5986
5987 define(interp, "TcpStream·read", Some(2), |_, args| {
5989 use std::io::Read;
5990
5991 let stream_id = match &args[0] {
5992 Value::Map(m) => {
5993 let borrowed = m.borrow();
5994 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
5995 *id as u64
5996 } else {
5997 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
5998 }
5999 }
6000 _ => return Err(RuntimeError::new("read requires TcpStream")),
6001 };
6002
6003 let size = match &args[1] {
6004 Value::Int(n) => *n as usize,
6005 _ => return Err(RuntimeError::new("read requires size as integer")),
6006 };
6007
6008 if let Some(mut guard) = get_stream_registry().lock().ok() {
6009 if let Some(stream) = guard.get_mut(&stream_id) {
6010 let mut buf = vec![0u8; size];
6011 match stream.read(&mut buf) {
6012 Ok(n) => {
6013 buf.truncate(n);
6014 let bytes: Vec<Value> = buf.iter().map(|b| Value::Int(*b as i64)).collect();
6015 Ok(Value::Variant {
6016 enum_name: "Result".to_string(),
6017 variant_name: "Ok".to_string(),
6018 fields: Some(Rc::new(vec![Value::Array(Rc::new(RefCell::new(bytes)))])),
6019 })
6020 }
6021 Err(e) => Ok(Value::Variant {
6022 enum_name: "Result".to_string(),
6023 variant_name: "Err".to_string(),
6024 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
6025 }),
6026 }
6027 } else {
6028 Err(RuntimeError::new("TcpStream not found in registry"))
6029 }
6030 } else {
6031 Err(RuntimeError::new("Failed to lock stream registry"))
6032 }
6033 });
6034
6035 define(interp, "TcpStream·read_exact", Some(2), |_, args| {
6037 use std::io::Read;
6038
6039 let stream_id = match &args[0] {
6040 Value::Map(m) => {
6041 let borrowed = m.borrow();
6042 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6043 *id as u64
6044 } else {
6045 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
6046 }
6047 }
6048 _ => return Err(RuntimeError::new("read_exact requires TcpStream")),
6049 };
6050
6051 let size = match &args[1] {
6052 Value::Int(n) => *n as usize,
6053 _ => return Err(RuntimeError::new("read_exact requires size as integer")),
6054 };
6055
6056 if let Some(mut guard) = get_stream_registry().lock().ok() {
6057 if let Some(stream) = guard.get_mut(&stream_id) {
6058 let mut buf = vec![0u8; size];
6059 match stream.read_exact(&mut buf) {
6060 Ok(()) => {
6061 let bytes: Vec<Value> = buf.iter().map(|b| Value::Int(*b as i64)).collect();
6062 Ok(Value::Variant {
6063 enum_name: "Result".to_string(),
6064 variant_name: "Ok".to_string(),
6065 fields: Some(Rc::new(vec![Value::Array(Rc::new(RefCell::new(bytes)))])),
6066 })
6067 }
6068 Err(e) => Ok(Value::Variant {
6069 enum_name: "Result".to_string(),
6070 variant_name: "Err".to_string(),
6071 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
6072 }),
6073 }
6074 } else {
6075 Err(RuntimeError::new("TcpStream not found in registry"))
6076 }
6077 } else {
6078 Err(RuntimeError::new("Failed to lock stream registry"))
6079 }
6080 });
6081
6082 define(interp, "TcpStream·write_all", Some(2), |_, args| {
6084 use std::io::Write;
6085
6086 let stream_id = match &args[0] {
6087 Value::Map(m) => {
6088 let borrowed = m.borrow();
6089 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6090 *id as u64
6091 } else {
6092 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
6093 }
6094 }
6095 _ => return Err(RuntimeError::new("write_all requires TcpStream")),
6096 };
6097
6098 let data: Vec<u8> = match &args[1] {
6100 Value::String(s) => s.as_bytes().to_vec(),
6101 Value::Array(arr) => arr
6102 .borrow()
6103 .iter()
6104 .filter_map(|v| {
6105 if let Value::Int(n) = v {
6106 Some(*n as u8)
6107 } else {
6108 None
6109 }
6110 })
6111 .collect(),
6112 Value::Ref(r) => {
6113 if let Value::String(s) = &*r.borrow() {
6114 s.as_bytes().to_vec()
6115 } else {
6116 return Err(RuntimeError::new("write_all requires string or byte array"));
6117 }
6118 }
6119 _ => return Err(RuntimeError::new("write_all requires string or byte array")),
6120 };
6121
6122 if let Some(mut guard) = get_stream_registry().lock().ok() {
6123 if let Some(stream) = guard.get_mut(&stream_id) {
6124 match stream.write_all(&data) {
6125 Ok(()) => Ok(Value::Variant {
6126 enum_name: "Result".to_string(),
6127 variant_name: "Ok".to_string(),
6128 fields: Some(Rc::new(vec![Value::Null])),
6129 }),
6130 Err(e) => Ok(Value::Variant {
6131 enum_name: "Result".to_string(),
6132 variant_name: "Err".to_string(),
6133 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
6134 }),
6135 }
6136 } else {
6137 Err(RuntimeError::new("TcpStream not found in registry"))
6138 }
6139 } else {
6140 Err(RuntimeError::new("Failed to lock stream registry"))
6141 }
6142 });
6143
6144 define(interp, "TcpStream·flush", Some(1), |_, args| {
6146 use std::io::Write;
6147
6148 let stream_id = match &args[0] {
6149 Value::Map(m) => {
6150 let borrowed = m.borrow();
6151 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6152 *id as u64
6153 } else {
6154 return Err(RuntimeError::new("TcpStream missing __stream_id__"));
6155 }
6156 }
6157 _ => return Err(RuntimeError::new("flush requires TcpStream")),
6158 };
6159
6160 if let Some(mut guard) = get_stream_registry().lock().ok() {
6161 if let Some(stream) = guard.get_mut(&stream_id) {
6162 match stream.flush() {
6163 Ok(()) => Ok(Value::Variant {
6164 enum_name: "Result".to_string(),
6165 variant_name: "Ok".to_string(),
6166 fields: Some(Rc::new(vec![Value::Null])),
6167 }),
6168 Err(e) => Ok(Value::Variant {
6169 enum_name: "Result".to_string(),
6170 variant_name: "Err".to_string(),
6171 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
6172 }),
6173 }
6174 } else {
6175 Err(RuntimeError::new("TcpStream not found in registry"))
6176 }
6177 } else {
6178 Err(RuntimeError::new("Failed to lock stream registry"))
6179 }
6180 });
6181
6182 define(interp, "BufReader·new", Some(1), |_, args| {
6186 use std::io::BufReader as StdBufReader;
6187
6188 let stream_id = match &args[0] {
6190 Value::Map(m) => {
6191 let borrowed = m.borrow();
6192 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6193 *id as u64
6194 } else {
6195 return Err(RuntimeError::new("BufReader::new requires TcpStream"));
6196 }
6197 }
6198 Value::Ref(r) => {
6199 let inner = r.borrow();
6201 if let Value::Map(m) = &*inner {
6202 let borrowed = m.borrow();
6203 if let Some(Value::Int(id)) = borrowed.get("__stream_id__") {
6204 *id as u64
6205 } else {
6206 return Err(RuntimeError::new(
6207 "BufReader::new requires TcpStream (missing stream_id in Ref)",
6208 ));
6209 }
6210 } else {
6211 return Err(RuntimeError::new(
6212 "BufReader::new requires TcpStream (Ref does not contain Map)",
6213 ));
6214 }
6215 }
6216 _ => return Err(RuntimeError::new("BufReader::new requires TcpStream")),
6217 };
6218
6219 let reader_id = if let Some(mut guard) = get_stream_registry().lock().ok() {
6221 if let Some(stream) = guard.get_mut(&stream_id) {
6222 let stream_clone = match stream.try_clone() {
6223 Ok(s) => s,
6224 Err(e) => {
6225 return Err(RuntimeError::new(format!("Failed to clone stream: {}", e)))
6226 }
6227 };
6228 let reader = StdBufReader::new(stream_clone);
6229 store_bufreader(reader)
6230 } else {
6231 return Err(RuntimeError::new("Stream not found in registry"));
6232 }
6233 } else {
6234 return Err(RuntimeError::new("Failed to lock stream registry"));
6235 };
6236
6237 let mut map = HashMap::new();
6238 map.insert(
6239 "__type__".to_string(),
6240 Value::String(Rc::new("BufReader".to_string())),
6241 );
6242 map.insert("__stream_id__".to_string(), Value::Int(stream_id as i64));
6243 map.insert("__reader_id__".to_string(), Value::Int(reader_id as i64));
6244 Ok(Value::Map(Rc::new(RefCell::new(map))))
6245 });
6246
6247 define(interp, "BufReader·read_line", Some(1), |_, args| {
6249 use std::io::BufRead;
6250
6251 let reader_id = match &args[0] {
6252 Value::Map(m) => {
6253 let borrowed = m.borrow();
6254 if let Some(Value::Int(id)) = borrowed.get("__reader_id__") {
6255 *id as u64
6256 } else {
6257 return Err(RuntimeError::new("BufReader missing __reader_id__"));
6258 }
6259 }
6260 _ => return Err(RuntimeError::new("read_line requires BufReader")),
6261 };
6262
6263 if let Some(mut guard) = get_bufreader_registry().lock().ok() {
6264 if let Some(reader) = guard.get_mut(&reader_id) {
6265 let mut line = String::new();
6266
6267 match reader.read_line(&mut line) {
6268 Ok(n) => {
6269 if n == 0 {
6270 Ok(Value::Variant {
6272 enum_name: "Result".to_string(),
6273 variant_name: "Ok".to_string(),
6274 fields: Some(Rc::new(vec![Value::Null])),
6275 })
6276 } else {
6277 Ok(Value::Variant {
6278 enum_name: "Result".to_string(),
6279 variant_name: "Ok".to_string(),
6280 fields: Some(Rc::new(vec![Value::String(Rc::new(line))])),
6281 })
6282 }
6283 }
6284 Err(e) => Ok(Value::Variant {
6285 enum_name: "Result".to_string(),
6286 variant_name: "Err".to_string(),
6287 fields: Some(Rc::new(vec![Value::String(Rc::new(e.to_string()))])),
6288 }),
6289 }
6290 } else {
6291 Err(RuntimeError::new("BufReader not found in registry"))
6292 }
6293 } else {
6294 Err(RuntimeError::new("Failed to lock bufreader registry"))
6295 }
6296 });
6297
6298 define(
6303 interp,
6304 "styx_http·middleware·Logger·new",
6305 Some(0),
6306 |_, _| {
6307 let mut map = HashMap::new();
6308 map.insert(
6309 "__type__".to_string(),
6310 Value::String(Rc::new("Logger".to_string())),
6311 );
6312 map.insert(
6313 "format".to_string(),
6314 Value::String(Rc::new("Common".to_string())),
6315 );
6316 Ok(Value::Map(Rc::new(RefCell::new(map))))
6317 },
6318 );
6319
6320 define(interp, "Logger·new", Some(0), |_, _| {
6321 let mut map = HashMap::new();
6322 map.insert(
6323 "__type__".to_string(),
6324 Value::String(Rc::new("Logger".to_string())),
6325 );
6326 map.insert(
6327 "format".to_string(),
6328 Value::String(Rc::new("Common".to_string())),
6329 );
6330 Ok(Value::Map(Rc::new(RefCell::new(map))))
6331 });
6332
6333 define(
6335 interp,
6336 "styx_http·middleware·Cors·new",
6337 Some(0),
6338 |_, _| {
6339 let mut map = HashMap::new();
6340 map.insert(
6341 "__type__".to_string(),
6342 Value::String(Rc::new("Cors".to_string())),
6343 );
6344 map.insert(
6345 "origins".to_string(),
6346 Value::Array(Rc::new(RefCell::new(vec![]))),
6347 );
6348 Ok(Value::Map(Rc::new(RefCell::new(map))))
6349 },
6350 );
6351
6352 define(interp, "Cors·new", Some(0), |_, _| {
6353 let mut map = HashMap::new();
6354 map.insert(
6355 "__type__".to_string(),
6356 Value::String(Rc::new("Cors".to_string())),
6357 );
6358 map.insert(
6359 "origins".to_string(),
6360 Value::Array(Rc::new(RefCell::new(vec![]))),
6361 );
6362 Ok(Value::Map(Rc::new(RefCell::new(map))))
6363 });
6364
6365 define(
6367 interp,
6368 "styx_http·middleware·SecurityHeaders·new",
6369 Some(0),
6370 |_, _| {
6371 let mut map = HashMap::new();
6372 map.insert(
6373 "__type__".to_string(),
6374 Value::String(Rc::new("SecurityHeaders".to_string())),
6375 );
6376 Ok(Value::Map(Rc::new(RefCell::new(map))))
6377 },
6378 );
6379
6380 define(interp, "SecurityHeaders·new", Some(0), |_, _| {
6381 let mut map = HashMap::new();
6382 map.insert(
6383 "__type__".to_string(),
6384 Value::String(Rc::new("SecurityHeaders".to_string())),
6385 );
6386 Ok(Value::Map(Rc::new(RefCell::new(map))))
6387 });
6388
6389 define(
6391 interp,
6392 "styx_http·middleware·RateLimiter·new",
6393 Some(0),
6394 |_, _| {
6395 let mut map = HashMap::new();
6396 map.insert(
6397 "__type__".to_string(),
6398 Value::String(Rc::new("RateLimiter".to_string())),
6399 );
6400 Ok(Value::Map(Rc::new(RefCell::new(map))))
6401 },
6402 );
6403
6404 define(interp, "RateLimiter·new", Some(0), |_, _| {
6405 let mut map = HashMap::new();
6406 map.insert(
6407 "__type__".to_string(),
6408 Value::String(Rc::new("RateLimiter".to_string())),
6409 );
6410 Ok(Value::Map(Rc::new(RefCell::new(map))))
6411 });
6412
6413 define(
6415 interp,
6416 "styx_http·middleware·RateLimit·new",
6417 None,
6418 |_, args| {
6419 let mut map = HashMap::new();
6420 map.insert(
6421 "__type__".to_string(),
6422 Value::String(Rc::new("RateLimit".to_string())),
6423 );
6424 if args.len() >= 2 {
6425 map.insert("rate".to_string(), args[0].clone());
6426 map.insert("burst".to_string(), args[1].clone());
6427 }
6428 Ok(Value::Map(Rc::new(RefCell::new(map))))
6429 },
6430 );
6431
6432 define(interp, "RateLimit·new", None, |_, args| {
6433 let mut map = HashMap::new();
6434 map.insert(
6435 "__type__".to_string(),
6436 Value::String(Rc::new("RateLimit".to_string())),
6437 );
6438 if args.len() >= 2 {
6439 map.insert("rate".to_string(), args[0].clone());
6440 map.insert("burst".to_string(), args[1].clone());
6441 }
6442 Ok(Value::Map(Rc::new(RefCell::new(map))))
6443 });
6444
6445 define(
6447 interp,
6448 "styx_http·middleware·Compression·new",
6449 Some(0),
6450 |_, _| {
6451 let mut map = HashMap::new();
6452 map.insert(
6453 "__type__".to_string(),
6454 Value::String(Rc::new("Compression".to_string())),
6455 );
6456 Ok(Value::Map(Rc::new(RefCell::new(map))))
6457 },
6458 );
6459
6460 define(interp, "Compression·new", Some(0), |_, _| {
6461 let mut map = HashMap::new();
6462 map.insert(
6463 "__type__".to_string(),
6464 Value::String(Rc::new("Compression".to_string())),
6465 );
6466 Ok(Value::Map(Rc::new(RefCell::new(map))))
6467 });
6468
6469 define(interp, "AuthMiddleware·optional", Some(0), |_, _| {
6471 let mut map = HashMap::new();
6472 map.insert(
6473 "__type__".to_string(),
6474 Value::String(Rc::new("AuthMiddleware".to_string())),
6475 );
6476 map.insert(
6477 "mode".to_string(),
6478 Value::String(Rc::new("optional".to_string())),
6479 );
6480 Ok(Value::Map(Rc::new(RefCell::new(map))))
6481 });
6482
6483 define(interp, "AuthMiddleware·required", Some(0), |_, _| {
6484 let mut map = HashMap::new();
6485 map.insert(
6486 "__type__".to_string(),
6487 Value::String(Rc::new("AuthMiddleware".to_string())),
6488 );
6489 map.insert(
6490 "mode".to_string(),
6491 Value::String(Rc::new("required".to_string())),
6492 );
6493 Ok(Value::Map(Rc::new(RefCell::new(map))))
6494 });
6495
6496 define(interp, "AuthMiddleware·new", Some(0), |_, _| {
6497 let mut map = HashMap::new();
6498 map.insert(
6499 "__type__".to_string(),
6500 Value::String(Rc::new("AuthMiddleware".to_string())),
6501 );
6502 map.insert(
6503 "mode".to_string(),
6504 Value::String(Rc::new("required".to_string())),
6505 );
6506 Ok(Value::Map(Rc::new(RefCell::new(map))))
6507 });
6508
6509 define(interp, "spawn_actor", Some(1), |_, args| {
6516 let name = match &args[0] {
6517 Value::String(s) => s.to_string(),
6518 _ => return Err(RuntimeError::new("actor_spawn() requires string name")),
6519 };
6520
6521 let inner = ActorInner {
6522 name,
6523 message_queue: Mutex::new(Vec::new()),
6524 message_count: std::sync::atomic::AtomicUsize::new(0),
6525 };
6526
6527 Ok(Value::Actor(Arc::new(inner)))
6528 });
6529
6530 define(interp, "send_to_actor", Some(3), |_, args| {
6533 let actor = match &args[0] {
6534 Value::Actor(a) => a.clone(),
6535 _ => {
6536 return Err(RuntimeError::new(
6537 "actor_send() requires actor as first argument",
6538 ))
6539 }
6540 };
6541 let msg_type = match &args[1] {
6542 Value::String(s) => s.to_string(),
6543 _ => {
6544 return Err(RuntimeError::new(
6545 "actor_send() requires string message type",
6546 ))
6547 }
6548 };
6549 let msg_data = format!("{}", args[2]);
6550
6551 let mut queue = actor
6552 .message_queue
6553 .lock()
6554 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6555 queue.push((msg_type, msg_data));
6556 actor
6557 .message_count
6558 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
6559
6560 Ok(Value::Null)
6561 });
6562
6563 define(interp, "tell_actor", Some(3), |_, args| {
6565 let actor = match &args[0] {
6566 Value::Actor(a) => a.clone(),
6567 _ => {
6568 return Err(RuntimeError::new(
6569 "actor_tell() requires actor as first argument",
6570 ))
6571 }
6572 };
6573 let msg_type = match &args[1] {
6574 Value::String(s) => s.to_string(),
6575 _ => {
6576 return Err(RuntimeError::new(
6577 "actor_tell() requires string message type",
6578 ))
6579 }
6580 };
6581 let msg_data = format!("{}", args[2]);
6582
6583 let mut queue = actor
6584 .message_queue
6585 .lock()
6586 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6587 queue.push((msg_type, msg_data));
6588 actor
6589 .message_count
6590 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
6591
6592 Ok(Value::Null)
6593 });
6594
6595 define(interp, "recv_from_actor", Some(1), |_, args| {
6598 let actor = match &args[0] {
6599 Value::Actor(a) => a.clone(),
6600 _ => return Err(RuntimeError::new("actor_recv() requires actor argument")),
6601 };
6602
6603 let mut queue = actor
6604 .message_queue
6605 .lock()
6606 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6607 match queue.pop() {
6608 Some((msg_type, msg_data)) => {
6609 Ok(Value::Variant {
6611 enum_name: "Option".to_string(),
6612 variant_name: "Some".to_string(),
6613 fields: Some(Rc::new(vec![Value::Tuple(Rc::new(vec![
6614 Value::String(Rc::new(msg_type)),
6615 Value::String(Rc::new(msg_data)),
6616 ]))])),
6617 })
6618 }
6619 None => Ok(Value::Variant {
6620 enum_name: "Option".to_string(),
6621 variant_name: "None".to_string(),
6622 fields: None,
6623 }),
6624 }
6625 });
6626
6627 define(interp, "get_actor_msg_count", Some(1), |_, args| {
6629 let a = match &args[0] {
6630 Value::Actor(a) => a.clone(),
6631 _ => {
6632 return Err(RuntimeError::new(
6633 "get_actor_msg_count() requires actor argument",
6634 ))
6635 }
6636 };
6637
6638 let count = a.message_count.load(std::sync::atomic::Ordering::SeqCst);
6639 Ok(Value::Int(count as i64))
6640 });
6641
6642 define(interp, "get_actor_name", Some(1), |_, args| {
6644 let a = match &args[0] {
6645 Value::Actor(a) => a.clone(),
6646 _ => {
6647 return Err(RuntimeError::new(
6648 "get_actor_name() requires actor argument",
6649 ))
6650 }
6651 };
6652
6653 Ok(Value::String(Rc::new(a.name.clone())))
6654 });
6655
6656 define(interp, "get_actor_pending", Some(1), |_, args| {
6658 let a = match &args[0] {
6659 Value::Actor(a) => a.clone(),
6660 _ => {
6661 return Err(RuntimeError::new(
6662 "get_actor_pending() requires actor argument",
6663 ))
6664 }
6665 };
6666
6667 let queue = a
6668 .message_queue
6669 .lock()
6670 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
6671 Ok(Value::Int(queue.len() as i64))
6672 });
6673
6674 define(interp, "mutex_new", Some(1), |_, args| {
6678 let value = args[0].clone();
6679 let mut map = std::collections::HashMap::new();
6681 map.insert("__mutex_value".to_string(), value);
6682 map.insert("__mutex_locked".to_string(), Value::Bool(false));
6683 Ok(Value::Map(Rc::new(RefCell::new(map))))
6684 });
6685
6686 define(interp, "mutex_lock", Some(1), |_, args| {
6688 let mutex = match &args[0] {
6689 Value::Map(m) => m.clone(),
6690 _ => return Err(RuntimeError::new("mutex_lock() requires mutex")),
6691 };
6692
6693 let mut map = mutex.borrow_mut();
6694 map.insert("__mutex_locked".to_string(), Value::Bool(true));
6696
6697 match map.get("__mutex_value") {
6698 Some(v) => Ok(v.clone()),
6699 None => Err(RuntimeError::new("invalid mutex")),
6700 }
6701 });
6702
6703 define(interp, "mutex_unlock", Some(2), |_, args| {
6705 let mutex = match &args[0] {
6706 Value::Map(m) => m.clone(),
6707 _ => return Err(RuntimeError::new("mutex_unlock() requires mutex")),
6708 };
6709 let new_value = args[1].clone();
6710
6711 let mut map = mutex.borrow_mut();
6712 map.insert("__mutex_value".to_string(), new_value);
6713 map.insert("__mutex_locked".to_string(), Value::Bool(false));
6714
6715 Ok(Value::Null)
6716 });
6717
6718 define(interp, "atomic_new", Some(1), |_, args| {
6720 let value = match &args[0] {
6721 Value::Int(i) => *i,
6722 _ => return Err(RuntimeError::new("atomic_new() requires integer")),
6723 };
6724
6725 let mut map = std::collections::HashMap::new();
6727 map.insert("__atomic_value".to_string(), Value::Int(value));
6728 Ok(Value::Map(Rc::new(RefCell::new(map))))
6729 });
6730
6731 define(interp, "atomic_load", Some(1), |_, args| {
6733 let atomic = match &args[0] {
6734 Value::Map(m) => m.clone(),
6735 _ => return Err(RuntimeError::new("atomic_load() requires atomic")),
6736 };
6737
6738 let map = atomic.borrow();
6739 match map.get("__atomic_value") {
6740 Some(v) => Ok(v.clone()),
6741 None => Err(RuntimeError::new("invalid atomic")),
6742 }
6743 });
6744
6745 define(interp, "atomic_store", Some(2), |_, args| {
6747 let atomic = match &args[0] {
6748 Value::Map(m) => m.clone(),
6749 _ => return Err(RuntimeError::new("atomic_store() requires atomic")),
6750 };
6751 let value = match &args[1] {
6752 Value::Int(i) => *i,
6753 _ => return Err(RuntimeError::new("atomic_store() requires integer value")),
6754 };
6755
6756 let mut map = atomic.borrow_mut();
6757 map.insert("__atomic_value".to_string(), Value::Int(value));
6758 Ok(Value::Null)
6759 });
6760
6761 define(interp, "atomic_add", Some(2), |_, args| {
6763 let atomic = match &args[0] {
6764 Value::Map(m) => m.clone(),
6765 _ => return Err(RuntimeError::new("atomic_add() requires atomic")),
6766 };
6767 let delta = match &args[1] {
6768 Value::Int(i) => *i,
6769 _ => return Err(RuntimeError::new("atomic_add() requires integer delta")),
6770 };
6771
6772 let mut map = atomic.borrow_mut();
6773 let old = match map.get("__atomic_value") {
6774 Some(Value::Int(i)) => *i,
6775 _ => return Err(RuntimeError::new("invalid atomic")),
6776 };
6777 map.insert("__atomic_value".to_string(), Value::Int(old + delta));
6778 Ok(Value::Int(old))
6779 });
6780
6781 define(interp, "atomic_cas", Some(3), |_, args| {
6783 let atomic = match &args[0] {
6784 Value::Map(m) => m.clone(),
6785 _ => return Err(RuntimeError::new("atomic_cas() requires atomic")),
6786 };
6787 let expected = match &args[1] {
6788 Value::Int(i) => *i,
6789 _ => return Err(RuntimeError::new("atomic_cas() requires integer expected")),
6790 };
6791 let new_value = match &args[2] {
6792 Value::Int(i) => *i,
6793 _ => return Err(RuntimeError::new("atomic_cas() requires integer new value")),
6794 };
6795
6796 let mut map = atomic.borrow_mut();
6797 let current = match map.get("__atomic_value") {
6798 Some(Value::Int(i)) => *i,
6799 _ => return Err(RuntimeError::new("invalid atomic")),
6800 };
6801
6802 if current == expected {
6803 map.insert("__atomic_value".to_string(), Value::Int(new_value));
6804 Ok(Value::Bool(true))
6805 } else {
6806 Ok(Value::Bool(false))
6807 }
6808 });
6809
6810 define(interp, "parallel_map", Some(2), |_, args| {
6814 let arr = match &args[0] {
6815 Value::Array(a) => a.borrow().clone(),
6816 _ => return Err(RuntimeError::new("parallel_map() requires array")),
6817 };
6818 let _func = args[1].clone();
6819
6820 Ok(Value::Array(Rc::new(RefCell::new(arr))))
6823 });
6824
6825 define(interp, "parallel_for", Some(3), |_, args| {
6827 let start = match &args[0] {
6828 Value::Int(i) => *i,
6829 _ => return Err(RuntimeError::new("parallel_for() requires integer start")),
6830 };
6831 let end = match &args[1] {
6832 Value::Int(i) => *i,
6833 _ => return Err(RuntimeError::new("parallel_for() requires integer end")),
6834 };
6835 let _func = args[2].clone();
6836
6837 let range: Vec<Value> = (start..end).map(|i| Value::Int(i)).collect();
6840 Ok(Value::Array(Rc::new(RefCell::new(range))))
6841 });
6842
6843 define(interp, "async_sleep", Some(1), |interp, args| {
6861 let ms = match &args[0] {
6862 Value::Int(ms) => *ms as u64,
6863 Value::Float(ms) => *ms as u64,
6864 _ => {
6865 return Err(RuntimeError::new(
6866 "async_sleep() requires integer milliseconds",
6867 ))
6868 }
6869 };
6870
6871 Ok(interp.make_future_timer(std::time::Duration::from_millis(ms)))
6872 });
6873
6874 define(interp, "future_ready", Some(1), |interp, args| {
6876 Ok(interp.make_future_immediate(args[0].clone()))
6877 });
6878
6879 define(interp, "future_pending", Some(0), |_, _| {
6881 Ok(Value::Future(Rc::new(RefCell::new(
6882 crate::interpreter::FutureInner {
6883 state: crate::interpreter::FutureState::Pending,
6884 computation: None,
6885 complete_at: None,
6886 },
6887 ))))
6888 });
6889
6890 define(interp, "is_future", Some(1), |_, args| {
6892 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
6893 });
6894
6895 define(interp, "is_ready", Some(1), |_, args| {
6897 match &args[0] {
6898 Value::Future(fut) => {
6899 let f = fut.borrow();
6900 Ok(Value::Bool(matches!(
6901 f.state,
6902 crate::interpreter::FutureState::Ready(_)
6903 )))
6904 }
6905 _ => Ok(Value::Bool(true)), }
6907 });
6908
6909 define(interp, "join_futures", Some(1), |_, args| {
6911 let futures = match &args[0] {
6912 Value::Array(arr) => {
6913 let arr = arr.borrow();
6914 let mut futs = Vec::new();
6915 for v in arr.iter() {
6916 match v {
6917 Value::Future(f) => futs.push(f.clone()),
6918 _ => {
6919 return Err(RuntimeError::new(
6920 "join_futures() requires array of futures",
6921 ))
6922 }
6923 }
6924 }
6925 futs
6926 }
6927 _ => {
6928 return Err(RuntimeError::new(
6929 "join_futures() requires array of futures",
6930 ))
6931 }
6932 };
6933
6934 Ok(Value::Future(Rc::new(RefCell::new(
6935 crate::interpreter::FutureInner {
6936 state: crate::interpreter::FutureState::Pending,
6937 computation: Some(crate::interpreter::FutureComputation::Join(futures)),
6938 complete_at: None,
6939 },
6940 ))))
6941 });
6942
6943 define(interp, "race_futures", Some(1), |_, args| {
6945 let futures = match &args[0] {
6946 Value::Array(arr) => {
6947 let arr = arr.borrow();
6948 let mut futs = Vec::new();
6949 for v in arr.iter() {
6950 match v {
6951 Value::Future(f) => futs.push(f.clone()),
6952 _ => {
6953 return Err(RuntimeError::new(
6954 "race_futures() requires array of futures",
6955 ))
6956 }
6957 }
6958 }
6959 futs
6960 }
6961 _ => {
6962 return Err(RuntimeError::new(
6963 "race_futures() requires array of futures",
6964 ))
6965 }
6966 };
6967
6968 Ok(Value::Future(Rc::new(RefCell::new(
6969 crate::interpreter::FutureInner {
6970 state: crate::interpreter::FutureState::Pending,
6971 computation: Some(crate::interpreter::FutureComputation::Race(futures)),
6972 complete_at: None,
6973 },
6974 ))))
6975 });
6976
6977 define(interp, "poll_future", Some(1), |_, args| {
6979 match &args[0] {
6980 Value::Future(fut) => {
6981 let f = fut.borrow();
6982 match &f.state {
6983 crate::interpreter::FutureState::Ready(v) => Ok(Value::Variant {
6984 enum_name: "Option".to_string(),
6985 variant_name: "Some".to_string(),
6986 fields: Some(Rc::new(vec![(**v).clone()])),
6987 }),
6988 _ => Ok(Value::Variant {
6989 enum_name: "Option".to_string(),
6990 variant_name: "None".to_string(),
6991 fields: None,
6992 }),
6993 }
6994 }
6995 other => Ok(Value::Variant {
6997 enum_name: "Option".to_string(),
6998 variant_name: "Some".to_string(),
6999 fields: Some(Rc::new(vec![other.clone()])),
7000 }),
7001 }
7002 });
7003}
7004
7005fn register_json(interp: &mut Interpreter) {
7010 define(interp, "json_parse", Some(1), |_, args| {
7012 let json_str = match &args[0] {
7013 Value::String(s) => s.as_str(),
7014 _ => return Err(RuntimeError::new("json_parse() requires string argument")),
7015 };
7016
7017 fn json_to_value(json: &serde_json::Value) -> Value {
7018 match json {
7019 serde_json::Value::Null => Value::Null,
7020 serde_json::Value::Bool(b) => Value::Bool(*b),
7021 serde_json::Value::Number(n) => {
7022 if let Some(i) = n.as_i64() {
7023 Value::Int(i)
7024 } else if let Some(f) = n.as_f64() {
7025 Value::Float(f)
7026 } else {
7027 Value::Null
7028 }
7029 }
7030 serde_json::Value::String(s) => Value::String(Rc::new(s.clone())),
7031 serde_json::Value::Array(arr) => {
7032 let values: Vec<Value> = arr.iter().map(json_to_value).collect();
7033 Value::Array(Rc::new(RefCell::new(values)))
7034 }
7035 serde_json::Value::Object(obj) => {
7036 let mut map = HashMap::new();
7037 for (k, v) in obj {
7038 map.insert(k.clone(), json_to_value(v));
7039 }
7040 Value::Map(Rc::new(RefCell::new(map)))
7041 }
7042 }
7043 }
7044
7045 match serde_json::from_str(json_str) {
7046 Ok(json) => Ok(json_to_value(&json)),
7047 Err(e) => Err(RuntimeError::new(format!("JSON parse error: {}", e))),
7048 }
7049 });
7050
7051 define(interp, "json_stringify", Some(1), |_, args| {
7053 fn value_to_json(val: &Value) -> serde_json::Value {
7054 match val {
7055 Value::Null => serde_json::Value::Null,
7056 Value::Bool(b) => serde_json::Value::Bool(*b),
7057 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
7058 Value::Float(f) => serde_json::Number::from_f64(*f)
7059 .map(serde_json::Value::Number)
7060 .unwrap_or(serde_json::Value::Null),
7061 Value::String(s) => serde_json::Value::String(s.to_string()),
7062 Value::Array(arr) => {
7063 let arr = arr.borrow();
7064 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
7065 }
7066 Value::Tuple(t) => serde_json::Value::Array(t.iter().map(value_to_json).collect()),
7067 Value::Map(map) => {
7068 let map = map.borrow();
7069 let obj: serde_json::Map<String, serde_json::Value> = map
7070 .iter()
7071 .map(|(k, v)| (k.clone(), value_to_json(v)))
7072 .collect();
7073 serde_json::Value::Object(obj)
7074 }
7075 Value::Struct { fields, .. } => {
7076 let fields = fields.borrow();
7077 let obj: serde_json::Map<String, serde_json::Value> = fields
7078 .iter()
7079 .map(|(k, v)| (k.clone(), value_to_json(v)))
7080 .collect();
7081 serde_json::Value::Object(obj)
7082 }
7083 _ => serde_json::Value::String(format!("{}", val)),
7084 }
7085 }
7086
7087 let json = value_to_json(&args[0]);
7088 Ok(Value::String(Rc::new(json.to_string())))
7089 });
7090
7091 define(interp, "json_pretty", Some(1), |_, args| {
7093 fn value_to_json(val: &Value) -> serde_json::Value {
7094 match val {
7095 Value::Null => serde_json::Value::Null,
7096 Value::Bool(b) => serde_json::Value::Bool(*b),
7097 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
7098 Value::Float(f) => serde_json::Number::from_f64(*f)
7099 .map(serde_json::Value::Number)
7100 .unwrap_or(serde_json::Value::Null),
7101 Value::String(s) => serde_json::Value::String(s.to_string()),
7102 Value::Array(arr) => {
7103 let arr = arr.borrow();
7104 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
7105 }
7106 Value::Map(map) => {
7107 let map = map.borrow();
7108 let obj: serde_json::Map<String, serde_json::Value> = map
7109 .iter()
7110 .map(|(k, v)| (k.clone(), value_to_json(v)))
7111 .collect();
7112 serde_json::Value::Object(obj)
7113 }
7114 _ => serde_json::Value::String(format!("{}", val)),
7115 }
7116 }
7117
7118 let json = value_to_json(&args[0]);
7119 match serde_json::to_string_pretty(&json) {
7120 Ok(s) => Ok(Value::String(Rc::new(s))),
7121 Err(e) => Err(RuntimeError::new(format!("JSON stringify error: {}", e))),
7122 }
7123 });
7124
7125 define(interp, "json_get", Some(2), |_, args| {
7127 let path = match &args[1] {
7128 Value::String(s) => s.to_string(),
7129 _ => return Err(RuntimeError::new("json_get() requires string path")),
7130 };
7131
7132 let mut current = args[0].clone();
7133 for key in path.split('.') {
7134 current = match ¤t {
7135 Value::Map(map) => {
7136 let map = map.borrow();
7137 map.get(key).cloned().unwrap_or(Value::Null)
7138 }
7139 Value::Array(arr) => {
7140 if let Ok(idx) = key.parse::<usize>() {
7141 let arr = arr.borrow();
7142 arr.get(idx).cloned().unwrap_or(Value::Null)
7143 } else {
7144 Value::Null
7145 }
7146 }
7147 _ => Value::Null,
7148 };
7149 }
7150 Ok(current)
7151 });
7152
7153 define(interp, "json_set", Some(3), |_, args| {
7155 let path = match &args[1] {
7156 Value::String(s) => s.to_string(),
7157 _ => return Err(RuntimeError::new("json_set() requires string path")),
7158 };
7159 let new_value = args[2].clone();
7160
7161 match &args[0] {
7163 Value::Map(map) => {
7164 let mut map = map.borrow_mut();
7165 map.insert(path, new_value);
7166 Ok(Value::Map(Rc::new(RefCell::new(map.clone()))))
7167 }
7168 _ => Err(RuntimeError::new("json_set() requires map/object")),
7169 }
7170 });
7171}
7172
7173fn register_fs(interp: &mut Interpreter) {
7178 define(interp, "fs_read", Some(1), |_, args| {
7180 let path = match &args[0] {
7181 Value::String(s) => s.to_string(),
7182 _ => return Err(RuntimeError::new("fs_read() requires string path")),
7183 };
7184
7185 match std::fs::read_to_string(&path) {
7186 Ok(content) => Ok(Value::String(Rc::new(content))),
7187 Err(e) => Err(RuntimeError::new(format!("fs_read() error: {}", e))),
7188 }
7189 });
7190
7191 define(interp, "fs_read_bytes", Some(1), |_, args| {
7193 let path = match &args[0] {
7194 Value::String(s) => s.to_string(),
7195 _ => return Err(RuntimeError::new("fs_read_bytes() requires string path")),
7196 };
7197
7198 match std::fs::read(&path) {
7199 Ok(bytes) => {
7200 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
7201 Ok(Value::Array(Rc::new(RefCell::new(values))))
7202 }
7203 Err(e) => Err(RuntimeError::new(format!("fs_read_bytes() error: {}", e))),
7204 }
7205 });
7206
7207 define(interp, "fs_write", Some(2), |_, args| {
7209 let path = match &args[0] {
7210 Value::String(s) => s.to_string(),
7211 _ => return Err(RuntimeError::new("fs_write() requires string path")),
7212 };
7213 let content = format!("{}", args[1]);
7214
7215 match std::fs::write(&path, content) {
7216 Ok(()) => Ok(Value::Null),
7217 Err(e) => Err(RuntimeError::new(format!("fs_write() error: {}", e))),
7218 }
7219 });
7220
7221 define(interp, "fs_append", Some(2), |_, args| {
7223 let path = match &args[0] {
7224 Value::String(s) => s.to_string(),
7225 _ => return Err(RuntimeError::new("fs_append() requires string path")),
7226 };
7227 let content = format!("{}", args[1]);
7228
7229 use std::fs::OpenOptions;
7230 match OpenOptions::new().append(true).create(true).open(&path) {
7231 Ok(mut file) => {
7232 use std::io::Write;
7233 match file.write_all(content.as_bytes()) {
7234 Ok(()) => Ok(Value::Null),
7235 Err(e) => Err(RuntimeError::new(format!("fs_append() write error: {}", e))),
7236 }
7237 }
7238 Err(e) => Err(RuntimeError::new(format!("fs_append() error: {}", e))),
7239 }
7240 });
7241
7242 define(interp, "fs_exists", Some(1), |_, args| {
7244 let path = match &args[0] {
7245 Value::String(s) => s.to_string(),
7246 _ => return Err(RuntimeError::new("fs_exists() requires string path")),
7247 };
7248 Ok(Value::Bool(std::path::Path::new(&path).exists()))
7249 });
7250
7251 define(interp, "fs_is_file", Some(1), |_, args| {
7253 let path = match &args[0] {
7254 Value::String(s) => s.to_string(),
7255 _ => return Err(RuntimeError::new("fs_is_file() requires string path")),
7256 };
7257 Ok(Value::Bool(std::path::Path::new(&path).is_file()))
7258 });
7259
7260 define(interp, "fs_is_dir", Some(1), |_, args| {
7262 let path = match &args[0] {
7263 Value::String(s) => s.to_string(),
7264 _ => return Err(RuntimeError::new("fs_is_dir() requires string path")),
7265 };
7266 Ok(Value::Bool(std::path::Path::new(&path).is_dir()))
7267 });
7268
7269 define(interp, "fs_mkdir", Some(1), |_, args| {
7271 let path = match &args[0] {
7272 Value::String(s) => s.to_string(),
7273 _ => return Err(RuntimeError::new("fs_mkdir() requires string path")),
7274 };
7275
7276 match std::fs::create_dir_all(&path) {
7277 Ok(()) => Ok(Value::Null),
7278 Err(e) => Err(RuntimeError::new(format!("fs_mkdir() error: {}", e))),
7279 }
7280 });
7281
7282 define(interp, "fs_remove", Some(1), |_, args| {
7284 let path = match &args[0] {
7285 Value::String(s) => s.to_string(),
7286 _ => return Err(RuntimeError::new("fs_remove() requires string path")),
7287 };
7288
7289 let p = std::path::Path::new(&path);
7290 let result = if p.is_dir() {
7291 std::fs::remove_dir_all(&path)
7292 } else {
7293 std::fs::remove_file(&path)
7294 };
7295
7296 match result {
7297 Ok(()) => Ok(Value::Null),
7298 Err(e) => Err(RuntimeError::new(format!("fs_remove() error: {}", e))),
7299 }
7300 });
7301
7302 define(interp, "fs_list", Some(1), |_, args| {
7304 let path = match &args[0] {
7305 Value::String(s) => s.to_string(),
7306 _ => return Err(RuntimeError::new("fs_list() requires string path")),
7307 };
7308
7309 match std::fs::read_dir(&path) {
7310 Ok(entries) => {
7311 let mut files = Vec::new();
7312 for entry in entries.flatten() {
7313 if let Some(name) = entry.file_name().to_str() {
7314 files.push(Value::String(Rc::new(name.to_string())));
7315 }
7316 }
7317 Ok(Value::Array(Rc::new(RefCell::new(files))))
7318 }
7319 Err(e) => Err(RuntimeError::new(format!("fs_list() error: {}", e))),
7320 }
7321 });
7322
7323 define(interp, "fs_copy", Some(2), |_, args| {
7325 let src = match &args[0] {
7326 Value::String(s) => s.to_string(),
7327 _ => return Err(RuntimeError::new("fs_copy() requires string source path")),
7328 };
7329 let dst = match &args[1] {
7330 Value::String(s) => s.to_string(),
7331 _ => {
7332 return Err(RuntimeError::new(
7333 "fs_copy() requires string destination path",
7334 ))
7335 }
7336 };
7337
7338 match std::fs::copy(&src, &dst) {
7339 Ok(bytes) => Ok(Value::Int(bytes as i64)),
7340 Err(e) => Err(RuntimeError::new(format!("fs_copy() error: {}", e))),
7341 }
7342 });
7343
7344 define(interp, "fs_rename", Some(2), |_, args| {
7346 let src = match &args[0] {
7347 Value::String(s) => s.to_string(),
7348 _ => return Err(RuntimeError::new("fs_rename() requires string source path")),
7349 };
7350 let dst = match &args[1] {
7351 Value::String(s) => s.to_string(),
7352 _ => {
7353 return Err(RuntimeError::new(
7354 "fs_rename() requires string destination path",
7355 ))
7356 }
7357 };
7358
7359 match std::fs::rename(&src, &dst) {
7360 Ok(()) => Ok(Value::Null),
7361 Err(e) => Err(RuntimeError::new(format!("fs_rename() error: {}", e))),
7362 }
7363 });
7364
7365 define(interp, "fs_size", Some(1), |_, args| {
7367 let path = match &args[0] {
7368 Value::String(s) => s.to_string(),
7369 _ => return Err(RuntimeError::new("fs_size() requires string path")),
7370 };
7371
7372 match std::fs::metadata(&path) {
7373 Ok(meta) => Ok(Value::Int(meta.len() as i64)),
7374 Err(e) => Err(RuntimeError::new(format!("fs_size() error: {}", e))),
7375 }
7376 });
7377
7378 define(interp, "path_join", None, |_, args| {
7380 let mut path = std::path::PathBuf::new();
7381 for arg in &args {
7382 match arg {
7383 Value::String(s) => path.push(s.as_str()),
7384 Value::Array(arr) => {
7385 for v in arr.borrow().iter() {
7386 if let Value::String(s) = v {
7387 path.push(s.as_str());
7388 }
7389 }
7390 }
7391 _ => {}
7392 }
7393 }
7394 Ok(Value::String(Rc::new(path.to_string_lossy().to_string())))
7395 });
7396
7397 define(interp, "path_parent", Some(1), |_, args| {
7399 let path = match &args[0] {
7400 Value::String(s) => s.to_string(),
7401 _ => return Err(RuntimeError::new("path_parent() requires string path")),
7402 };
7403
7404 let p = std::path::Path::new(&path);
7405 match p.parent() {
7406 Some(parent) => Ok(Value::String(Rc::new(parent.to_string_lossy().to_string()))),
7407 None => Ok(Value::Null),
7408 }
7409 });
7410
7411 define(interp, "path_filename", Some(1), |_, args| {
7413 let path = match &args[0] {
7414 Value::String(s) => s.to_string(),
7415 _ => return Err(RuntimeError::new("path_filename() requires string path")),
7416 };
7417
7418 let p = std::path::Path::new(&path);
7419 match p.file_name() {
7420 Some(name) => Ok(Value::String(Rc::new(name.to_string_lossy().to_string()))),
7421 None => Ok(Value::Null),
7422 }
7423 });
7424
7425 define(interp, "path_extension", Some(1), |_, args| {
7427 let path = match &args[0] {
7428 Value::String(s) => s.to_string(),
7429 _ => return Err(RuntimeError::new("path_extension() requires string path")),
7430 };
7431
7432 let p = std::path::Path::new(&path);
7433 match p.extension() {
7434 Some(ext) => Ok(Value::String(Rc::new(ext.to_string_lossy().to_string()))),
7435 None => Ok(Value::Null),
7436 }
7437 });
7438
7439 use std::cell::RefCell;
7446 use std::collections::HashMap;
7447 thread_local! {
7448 static LAST_FILE_CONTENT: RefCell<String> = RefCell::new(String::new());
7449 static FAKE_PTR_MAP: RefCell<HashMap<i64, String>> = RefCell::new(HashMap::new());
7451 }
7452
7453 define(interp, "sigil_read_file", Some(2), |_, args| {
7457 let path = match &args[0] {
7459 Value::String(s) => s.to_string(),
7460 Value::Int(ptr_id) => {
7461 FAKE_PTR_MAP
7463 .with(|map| map.borrow().get(ptr_id).cloned())
7464 .ok_or_else(|| {
7465 RuntimeError::new(format!("sigil_read_file: invalid pointer {}", ptr_id))
7466 })?
7467 }
7468 _ => return Err(RuntimeError::new("sigil_read_file() requires string path")),
7469 };
7470
7471 match std::fs::read_to_string(&path) {
7472 Ok(content) => {
7473 LAST_FILE_CONTENT.with(|last| {
7475 *last.borrow_mut() = content.clone();
7476 });
7477 Ok(Value::String(Rc::new(content)))
7479 }
7480 Err(_) => Ok(Value::Null), }
7482 });
7483
7484 define(interp, "sigil_file_len", Some(0), |_, _| {
7486 LAST_FILE_CONTENT.with(|last| Ok(Value::Int(last.borrow().len() as i64)))
7487 });
7488
7489 define(interp, "sigil_write_file", Some(4), |_, args| {
7491 let path = match &args[0] {
7493 Value::String(s) => s.to_string(),
7494 _ => return Err(RuntimeError::new("sigil_write_file() requires string path")),
7495 };
7496 let content = match &args[2] {
7497 Value::String(s) => s.to_string(),
7498 _ => {
7499 return Err(RuntimeError::new(
7500 "sigil_write_file() requires string content",
7501 ))
7502 }
7503 };
7504
7505 match std::fs::write(&path, content) {
7506 Ok(()) => Ok(Value::Bool(true)),
7507 Err(_) => Ok(Value::Bool(false)),
7508 }
7509 });
7510
7511 define(interp, "write", Some(3), |_, args| {
7513 let fd = match &args[0] {
7514 Value::Int(n) => *n,
7515 _ => return Err(RuntimeError::new("write() requires int fd")),
7516 };
7517
7518 let content = match &args[1] {
7520 Value::String(s) => s.to_string(),
7521 Value::Int(ptr_id) => {
7522 FAKE_PTR_MAP
7524 .with(|map| map.borrow().get(ptr_id).cloned())
7525 .unwrap_or_else(|| format!("{}", ptr_id))
7526 }
7527 _ => format!("{}", args[1]),
7528 };
7529
7530 let len = match &args[2] {
7532 Value::Int(n) => *n as usize,
7533 _ => content.len(),
7534 };
7535
7536 let output = &content[..std::cmp::min(len, content.len())];
7537
7538 match fd {
7539 1 => {
7540 print!("{}", output);
7541 use std::io::Write;
7542 std::io::stdout().flush().ok();
7543 Ok(Value::Int(output.len() as i64))
7544 }
7545 2 => {
7546 eprint!("{}", output);
7547 use std::io::Write;
7548 std::io::stderr().flush().ok();
7549 Ok(Value::Int(output.len() as i64))
7550 }
7551 _ => Err(RuntimeError::new(format!("write() unsupported fd: {}", fd))),
7552 }
7553 });
7554
7555 define(interp, "PathBuf·from", Some(1), |_, args| {
7557 let path = match &args[0] {
7558 Value::String(s) => s.to_string(),
7559 Value::Ref(r) => {
7560 if let Value::String(s) = &*r.borrow() {
7561 s.to_string()
7562 } else {
7563 return Err(RuntimeError::new("PathBuf::from() requires string"));
7564 }
7565 }
7566 _ => return Err(RuntimeError::new("PathBuf::from() requires string")),
7567 };
7568 Ok(Value::String(Rc::new(path)))
7569 });
7570
7571 define(interp, "std·path·PathBuf·from", Some(1), |_, args| {
7573 let path = match &args[0] {
7574 Value::String(s) => s.to_string(),
7575 Value::Ref(r) => {
7576 if let Value::String(s) = &*r.borrow() {
7577 s.to_string()
7578 } else {
7579 return Err(RuntimeError::new("PathBuf::from() requires string"));
7580 }
7581 }
7582 _ => return Err(RuntimeError::new("PathBuf::from() requires string")),
7583 };
7584 Ok(Value::String(Rc::new(path)))
7585 });
7586
7587 define(interp, "Path·new", Some(1), |_, args| {
7589 let path = match &args[0] {
7590 Value::String(s) => s.to_string(),
7591 Value::Ref(r) => {
7592 if let Value::String(s) = &*r.borrow() {
7593 s.to_string()
7594 } else {
7595 return Err(RuntimeError::new("Path::new() requires string"));
7596 }
7597 }
7598 _ => return Err(RuntimeError::new("Path::new() requires string")),
7599 };
7600 Ok(Value::String(Rc::new(path)))
7601 });
7602
7603 define(interp, "std·path·Path·new", Some(1), |_, args| {
7605 let path = match &args[0] {
7606 Value::String(s) => s.to_string(),
7607 _ => return Err(RuntimeError::new("Path::new() requires string")),
7608 };
7609 Ok(Value::String(Rc::new(path)))
7610 });
7611
7612 define(interp, "std·fs·read_to_string", Some(1), |_, args| {
7614 let path = match &args[0] {
7615 Value::String(s) => s.to_string(),
7616 _ => return Err(RuntimeError::new("read_to_string() requires string path")),
7617 };
7618 match std::fs::read_to_string(&path) {
7619 Ok(content) => Ok(Value::String(Rc::new(content))),
7620 Err(e) => Err(RuntimeError::new(format!("read_to_string() error: {}", e))),
7621 }
7622 });
7623
7624 define(interp, "std·fs·write", Some(2), |_, args| {
7626 let path = match &args[0] {
7627 Value::String(s) => s.to_string(),
7628 _ => return Err(RuntimeError::new("fs::write() requires string path")),
7629 };
7630 let content = format!("{}", args[1]);
7631 match std::fs::write(&path, content) {
7632 Ok(()) => Ok(Value::Null),
7633 Err(e) => Err(RuntimeError::new(format!("fs::write() error: {}", e))),
7634 }
7635 });
7636
7637 define(interp, "std·fs·create_dir_all", Some(1), |_, args| {
7639 let path = match &args[0] {
7640 Value::String(s) => s.to_string(),
7641 _ => return Err(RuntimeError::new("create_dir_all() requires string path")),
7642 };
7643 match std::fs::create_dir_all(&path) {
7644 Ok(()) => Ok(Value::Null),
7645 Err(e) => Err(RuntimeError::new(format!("create_dir_all() error: {}", e))),
7646 }
7647 });
7648
7649 define(interp, "OpenOptions·new", Some(0), |_, _| {
7652 let mut opts = HashMap::new();
7653 opts.insert("read".to_string(), Value::Bool(false));
7654 opts.insert("write".to_string(), Value::Bool(false));
7655 opts.insert("append".to_string(), Value::Bool(false));
7656 opts.insert("truncate".to_string(), Value::Bool(false));
7657 opts.insert("create".to_string(), Value::Bool(false));
7658 opts.insert("create_new".to_string(), Value::Bool(false));
7659 opts.insert(
7660 "__type__".to_string(),
7661 Value::String(Rc::new("OpenOptions".to_string())),
7662 );
7663 Ok(Value::Map(Rc::new(RefCell::new(opts))))
7664 });
7665
7666 define(interp, "std·fs·OpenOptions·new", Some(0), |_, _| {
7668 let mut opts = HashMap::new();
7669 opts.insert("read".to_string(), Value::Bool(false));
7670 opts.insert("write".to_string(), Value::Bool(false));
7671 opts.insert("append".to_string(), Value::Bool(false));
7672 opts.insert("truncate".to_string(), Value::Bool(false));
7673 opts.insert("create".to_string(), Value::Bool(false));
7674 opts.insert("create_new".to_string(), Value::Bool(false));
7675 opts.insert(
7676 "__type__".to_string(),
7677 Value::String(Rc::new("OpenOptions".to_string())),
7678 );
7679 Ok(Value::Map(Rc::new(RefCell::new(opts))))
7680 });
7681
7682 define(interp, "File·create", Some(1), |_, args| {
7684 let path = match &args[0] {
7685 Value::String(s) => s.to_string(),
7686 _ => return Err(RuntimeError::new("File::create() requires string path")),
7687 };
7688 let mut handle = HashMap::new();
7690 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7691 handle.insert(
7692 "mode".to_string(),
7693 Value::String(Rc::new("write".to_string())),
7694 );
7695 handle.insert(
7696 "__type__".to_string(),
7697 Value::String(Rc::new("File".to_string())),
7698 );
7699 match std::fs::File::create(&path) {
7701 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7702 Err(e) => Err(RuntimeError::new(format!("File::create() error: {}", e))),
7703 }
7704 });
7705
7706 define(interp, "std·fs·File·create", Some(1), |_, args| {
7708 let path = match &args[0] {
7709 Value::String(s) => s.to_string(),
7710 _ => return Err(RuntimeError::new("File::create() requires string path")),
7711 };
7712 let mut handle = HashMap::new();
7713 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7714 handle.insert(
7715 "mode".to_string(),
7716 Value::String(Rc::new("write".to_string())),
7717 );
7718 handle.insert(
7719 "__type__".to_string(),
7720 Value::String(Rc::new("File".to_string())),
7721 );
7722 match std::fs::File::create(&path) {
7723 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7724 Err(e) => Err(RuntimeError::new(format!("File::create() error: {}", e))),
7725 }
7726 });
7727
7728 define(interp, "File·open", Some(1), |_, args| {
7730 let path = match &args[0] {
7731 Value::String(s) => s.to_string(),
7732 _ => return Err(RuntimeError::new("File::open() requires string path")),
7733 };
7734 let mut handle = HashMap::new();
7735 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7736 handle.insert(
7737 "mode".to_string(),
7738 Value::String(Rc::new("read".to_string())),
7739 );
7740 handle.insert(
7741 "__type__".to_string(),
7742 Value::String(Rc::new("File".to_string())),
7743 );
7744 match std::fs::File::open(&path) {
7745 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7746 Err(e) => Err(RuntimeError::new(format!("File::open() error: {}", e))),
7747 }
7748 });
7749
7750 define(interp, "std·fs·File·open", Some(1), |_, args| {
7752 let path = match &args[0] {
7753 Value::String(s) => s.to_string(),
7754 _ => return Err(RuntimeError::new("File::open() requires string path")),
7755 };
7756 let mut handle = HashMap::new();
7757 handle.insert("path".to_string(), Value::String(Rc::new(path.clone())));
7758 handle.insert(
7759 "mode".to_string(),
7760 Value::String(Rc::new("read".to_string())),
7761 );
7762 handle.insert(
7763 "__type__".to_string(),
7764 Value::String(Rc::new("File".to_string())),
7765 );
7766 match std::fs::File::open(&path) {
7767 Ok(_) => Ok(Value::Map(Rc::new(RefCell::new(handle)))),
7768 Err(e) => Err(RuntimeError::new(format!("File::open() error: {}", e))),
7769 }
7770 });
7771
7772 define(interp, "BufWriter·new", Some(1), |_, args| {
7774 match &args[0] {
7777 Value::Map(file_map) => {
7778 let mut wrapper = HashMap::new();
7779 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7780 wrapper.insert(
7781 "buffer".to_string(),
7782 Value::Array(Rc::new(RefCell::new(Vec::new()))),
7783 );
7784 wrapper.insert(
7785 "__type__".to_string(),
7786 Value::String(Rc::new("BufWriter".to_string())),
7787 );
7788 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7789 }
7790 _ => Err(RuntimeError::new("BufWriter::new requires a file handle")),
7791 }
7792 });
7793
7794 define(
7796 interp,
7797 "std·io·BufWriter·new",
7798 Some(1),
7799 |_, args| match &args[0] {
7800 Value::Map(file_map) => {
7801 let mut wrapper = HashMap::new();
7802 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7803 wrapper.insert(
7804 "buffer".to_string(),
7805 Value::Array(Rc::new(RefCell::new(Vec::new()))),
7806 );
7807 wrapper.insert(
7808 "__type__".to_string(),
7809 Value::String(Rc::new("BufWriter".to_string())),
7810 );
7811 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7812 }
7813 _ => Err(RuntimeError::new("BufWriter::new requires a file handle")),
7814 },
7815 );
7816
7817 define(interp, "BufReader·new", Some(1), |_, args| {
7819 use std::io::BufReader as StdBufReader;
7820
7821 let get_map = |val: &Value| -> Option<Rc<RefCell<HashMap<String, Value>>>> {
7823 match val {
7824 Value::Map(m) => Some(m.clone()),
7825 Value::Ref(r) => {
7826 let inner = r.borrow();
7827 if let Value::Map(m) = &*inner {
7828 Some(m.clone())
7829 } else {
7830 None
7831 }
7832 }
7833 _ => None,
7834 }
7835 };
7836
7837 if let Some(file_map) = get_map(&args[0]) {
7838 let borrowed = file_map.borrow();
7839 let mut wrapper = HashMap::new();
7840
7841 if let Some(Value::String(t)) = borrowed.get("__type__") {
7843 if t.as_str() == "TcpStream" {
7844 if let Some(Value::Int(stream_id)) = borrowed.get("__stream_id__") {
7845 let stream_id_val = *stream_id as u64;
7846 drop(borrowed);
7847
7848 if let Some(mut guard) = get_stream_registry().lock().ok() {
7850 if let Some(stream) = guard.get_mut(&stream_id_val) {
7851 let stream_clone = match stream.try_clone() {
7852 Ok(s) => s,
7853 Err(e) => {
7854 return Err(RuntimeError::new(format!(
7855 "Failed to clone stream: {}",
7856 e
7857 )))
7858 }
7859 };
7860 let reader = StdBufReader::new(stream_clone);
7861 let reader_id = store_bufreader(reader);
7862
7863 wrapper.insert(
7864 "__type__".to_string(),
7865 Value::String(Rc::new("BufReader".to_string())),
7866 );
7867 wrapper.insert(
7868 "__stream_id__".to_string(),
7869 Value::Int(stream_id_val as i64),
7870 );
7871 wrapper.insert(
7872 "__reader_id__".to_string(),
7873 Value::Int(reader_id as i64),
7874 );
7875 return Ok(Value::Map(Rc::new(RefCell::new(wrapper))));
7876 }
7877 }
7878 return Err(RuntimeError::new("TcpStream not found in registry"));
7879 }
7880 }
7881 }
7882
7883 drop(borrowed);
7885 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7886 wrapper.insert(
7887 "__type__".to_string(),
7888 Value::String(Rc::new("BufReader".to_string())),
7889 );
7890 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7891 } else {
7892 Err(RuntimeError::new(
7893 "BufReader::new requires a file handle or TcpStream",
7894 ))
7895 }
7896 });
7897
7898 define(interp, "std·io·BufReader·new", Some(1), |_, args| {
7900 let get_map = |val: &Value| -> Option<Rc<RefCell<HashMap<String, Value>>>> {
7902 match val {
7903 Value::Map(m) => Some(m.clone()),
7904 Value::Ref(r) => {
7905 let inner = r.borrow();
7906 if let Value::Map(m) = &*inner {
7907 Some(m.clone())
7908 } else {
7909 None
7910 }
7911 }
7912 _ => None,
7913 }
7914 };
7915
7916 if let Some(file_map) = get_map(&args[0]) {
7917 let borrowed = file_map.borrow();
7918 let mut wrapper = HashMap::new();
7919
7920 if let Some(Value::String(t)) = borrowed.get("__type__") {
7922 if t.as_str() == "TcpStream" {
7923 if let Some(Value::Int(stream_id)) = borrowed.get("__stream_id__") {
7924 wrapper.insert("__stream_id__".to_string(), Value::Int(*stream_id));
7925 }
7926 }
7927 }
7928
7929 drop(borrowed);
7930 wrapper.insert("inner".to_string(), Value::Map(file_map.clone()));
7931 wrapper.insert(
7932 "__type__".to_string(),
7933 Value::String(Rc::new("BufReader".to_string())),
7934 );
7935 Ok(Value::Map(Rc::new(RefCell::new(wrapper))))
7936 } else {
7937 Err(RuntimeError::new(
7938 "BufReader::new requires a file handle or TcpStream",
7939 ))
7940 }
7941 });
7942
7943 define(
7945 interp,
7946 "dirs_next·config_dir",
7947 Some(0),
7948 |_, _| match dirs::config_dir() {
7949 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7950 None => Ok(Value::Null),
7951 },
7952 );
7953
7954 define(
7956 interp,
7957 "dirs_next·data_dir",
7958 Some(0),
7959 |_, _| match dirs::data_dir() {
7960 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7961 None => Ok(Value::Null),
7962 },
7963 );
7964
7965 define(
7967 interp,
7968 "dirs_next·home_dir",
7969 Some(0),
7970 |_, _| match dirs::home_dir() {
7971 Some(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7972 None => Ok(Value::Null),
7973 },
7974 );
7975}
7976
7977fn register_crypto(interp: &mut Interpreter) {
8009 fn extract_bytes(v: &Value, fn_name: &str) -> Result<Vec<u8>, RuntimeError> {
8011 match v {
8012 Value::String(s) => Ok(s.as_bytes().to_vec()),
8013 Value::Array(arr) => {
8014 let arr = arr.borrow();
8015 Ok(arr
8016 .iter()
8017 .filter_map(|v| {
8018 if let Value::Int(n) = v {
8019 Some(*n as u8)
8020 } else {
8021 None
8022 }
8023 })
8024 .collect())
8025 }
8026 _ => Err(RuntimeError::new(format!(
8027 "{}() requires string or byte array",
8028 fn_name
8029 ))),
8030 }
8031 }
8032
8033 fn bytes_to_array(bytes: &[u8]) -> Value {
8034 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
8035 Value::Array(Rc::new(RefCell::new(values)))
8036 }
8037
8038 define(interp, "sha256", Some(1), |_, args| {
8044 let data = extract_bytes(&args[0], "sha256")?;
8045 let mut hasher = Sha256::new();
8046 hasher.update(&data);
8047 let result = hasher.finalize();
8048 Ok(Value::String(Rc::new(
8049 result.iter().map(|b| format!("{:02x}", b)).collect(),
8050 )))
8051 });
8052
8053 define(interp, "sha512", Some(1), |_, args| {
8055 let data = extract_bytes(&args[0], "sha512")?;
8056 let mut hasher = Sha512::new();
8057 hasher.update(&data);
8058 let result = hasher.finalize();
8059 Ok(Value::String(Rc::new(
8060 result.iter().map(|b| format!("{:02x}", b)).collect(),
8061 )))
8062 });
8063
8064 define(interp, "sha3_256", Some(1), |_, args| {
8066 use sha3::{Digest as Sha3Digest, Sha3_256};
8067 let data = extract_bytes(&args[0], "sha3_256")?;
8068 let mut hasher = Sha3_256::new();
8069 hasher.update(&data);
8070 let result = hasher.finalize();
8071 Ok(Value::String(Rc::new(
8072 result.iter().map(|b| format!("{:02x}", b)).collect(),
8073 )))
8074 });
8075
8076 define(interp, "sha3_512", Some(1), |_, args| {
8078 use sha3::{Digest as Sha3Digest, Sha3_512};
8079 let data = extract_bytes(&args[0], "sha3_512")?;
8080 let mut hasher = Sha3_512::new();
8081 hasher.update(&data);
8082 let result = hasher.finalize();
8083 Ok(Value::String(Rc::new(
8084 result.iter().map(|b| format!("{:02x}", b)).collect(),
8085 )))
8086 });
8087
8088 define(interp, "blake3", Some(1), |_, args| {
8090 let data = extract_bytes(&args[0], "blake3")?;
8091 let hash = blake3::hash(&data);
8092 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
8093 });
8094
8095 define(interp, "blake3_keyed", Some(2), |_, args| {
8097 let key = extract_bytes(&args[0], "blake3_keyed")?;
8098 let data = extract_bytes(&args[1], "blake3_keyed")?;
8099 if key.len() != 32 {
8100 return Err(RuntimeError::new("blake3_keyed() requires 32-byte key"));
8101 }
8102 let mut key_arr = [0u8; 32];
8103 key_arr.copy_from_slice(&key);
8104 let hash = blake3::keyed_hash(&key_arr, &data);
8105 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
8106 });
8107
8108 define(interp, "md5", Some(1), |_, args| {
8110 let data = extract_bytes(&args[0], "md5")?;
8111 let mut hasher = Md5::new();
8112 hasher.update(&data);
8113 let result = hasher.finalize();
8114 Ok(Value::String(Rc::new(
8115 result.iter().map(|b| format!("{:02x}", b)).collect(),
8116 )))
8117 });
8118
8119 define(interp, "aes_gcm_encrypt", Some(2), |_, args| {
8125 use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
8126 use rand::RngCore;
8127
8128 let key = extract_bytes(&args[0], "aes_gcm_encrypt")?;
8129 let plaintext = extract_bytes(&args[1], "aes_gcm_encrypt")?;
8130
8131 if key.len() != 32 {
8132 return Err(RuntimeError::new("aes_gcm_encrypt() requires 32-byte key"));
8133 }
8134
8135 let cipher = Aes256Gcm::new_from_slice(&key)
8136 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
8137
8138 let mut nonce_bytes = [0u8; 12];
8139 rand::thread_rng().fill_bytes(&mut nonce_bytes);
8140 let nonce = Nonce::from_slice(&nonce_bytes);
8141
8142 let ciphertext = cipher
8143 .encrypt(nonce, plaintext.as_ref())
8144 .map_err(|e| RuntimeError::new(format!("AES encryption error: {}", e)))?;
8145
8146 let mut result = HashMap::new();
8147 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
8148 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
8149 Ok(Value::Map(Rc::new(RefCell::new(result))))
8150 });
8151
8152 define(interp, "aes_gcm_decrypt", Some(3), |_, args| {
8154 use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
8155
8156 let key = extract_bytes(&args[0], "aes_gcm_decrypt")?;
8157 let ciphertext = extract_bytes(&args[1], "aes_gcm_decrypt")?;
8158 let nonce_bytes = extract_bytes(&args[2], "aes_gcm_decrypt")?;
8159
8160 if key.len() != 32 {
8161 return Err(RuntimeError::new("aes_gcm_decrypt() requires 32-byte key"));
8162 }
8163 if nonce_bytes.len() != 12 {
8164 return Err(RuntimeError::new(
8165 "aes_gcm_decrypt() requires 12-byte nonce",
8166 ));
8167 }
8168
8169 let cipher = Aes256Gcm::new_from_slice(&key)
8170 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
8171 let nonce = Nonce::from_slice(&nonce_bytes);
8172
8173 let plaintext = cipher
8174 .decrypt(nonce, ciphertext.as_ref())
8175 .map_err(|_| RuntimeError::new("AES-GCM decryption failed: authentication error"))?;
8176
8177 match String::from_utf8(plaintext.clone()) {
8178 Ok(s) => Ok(Value::String(Rc::new(s))),
8179 Err(_) => Ok(bytes_to_array(&plaintext)),
8180 }
8181 });
8182
8183 define(interp, "chacha20_encrypt", Some(2), |_, args| {
8185 use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
8186 use rand::RngCore;
8187
8188 let key = extract_bytes(&args[0], "chacha20_encrypt")?;
8189 let plaintext = extract_bytes(&args[1], "chacha20_encrypt")?;
8190
8191 if key.len() != 32 {
8192 return Err(RuntimeError::new("chacha20_encrypt() requires 32-byte key"));
8193 }
8194
8195 let cipher = ChaCha20Poly1305::new_from_slice(&key)
8196 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
8197
8198 let mut nonce_bytes = [0u8; 12];
8199 rand::thread_rng().fill_bytes(&mut nonce_bytes);
8200 let nonce = Nonce::from_slice(&nonce_bytes);
8201
8202 let ciphertext = cipher
8203 .encrypt(nonce, plaintext.as_ref())
8204 .map_err(|e| RuntimeError::new(format!("ChaCha20 encryption error: {}", e)))?;
8205
8206 let mut result = HashMap::new();
8207 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
8208 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
8209 Ok(Value::Map(Rc::new(RefCell::new(result))))
8210 });
8211
8212 define(interp, "chacha20_decrypt", Some(3), |_, args| {
8214 use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
8215
8216 let key = extract_bytes(&args[0], "chacha20_decrypt")?;
8217 let ciphertext = extract_bytes(&args[1], "chacha20_decrypt")?;
8218 let nonce_bytes = extract_bytes(&args[2], "chacha20_decrypt")?;
8219
8220 if key.len() != 32 {
8221 return Err(RuntimeError::new("chacha20_decrypt() requires 32-byte key"));
8222 }
8223 if nonce_bytes.len() != 12 {
8224 return Err(RuntimeError::new(
8225 "chacha20_decrypt() requires 12-byte nonce",
8226 ));
8227 }
8228
8229 let cipher = ChaCha20Poly1305::new_from_slice(&key)
8230 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
8231 let nonce = Nonce::from_slice(&nonce_bytes);
8232
8233 let plaintext = cipher
8234 .decrypt(nonce, ciphertext.as_ref())
8235 .map_err(|_| RuntimeError::new("ChaCha20 decryption failed: authentication error"))?;
8236
8237 match String::from_utf8(plaintext.clone()) {
8238 Ok(s) => Ok(Value::String(Rc::new(s))),
8239 Err(_) => Ok(bytes_to_array(&plaintext)),
8240 }
8241 });
8242
8243 define(interp, "ed25519_keygen", Some(0), |_, _| {
8249 use ed25519_dalek::SigningKey;
8250 use rand::rngs::OsRng;
8251
8252 let signing_key = SigningKey::generate(&mut OsRng);
8253 let verifying_key = signing_key.verifying_key();
8254
8255 let mut result = HashMap::new();
8256 result.insert(
8257 "private_key".to_string(),
8258 Value::String(Rc::new(
8259 signing_key
8260 .to_bytes()
8261 .iter()
8262 .map(|b| format!("{:02x}", b))
8263 .collect(),
8264 )),
8265 );
8266 result.insert(
8267 "public_key".to_string(),
8268 Value::String(Rc::new(
8269 verifying_key
8270 .to_bytes()
8271 .iter()
8272 .map(|b| format!("{:02x}", b))
8273 .collect(),
8274 )),
8275 );
8276 Ok(Value::Map(Rc::new(RefCell::new(result))))
8277 });
8278
8279 define(interp, "ed25519_sign", Some(2), |_, args| {
8281 use ed25519_dalek::{Signer, SigningKey};
8282
8283 let private_key_hex = match &args[0] {
8284 Value::String(s) => s.to_string(),
8285 _ => return Err(RuntimeError::new("ed25519_sign() requires hex private key")),
8286 };
8287 let message = extract_bytes(&args[1], "ed25519_sign")?;
8288
8289 let key_bytes: Vec<u8> = (0..private_key_hex.len())
8290 .step_by(2)
8291 .map(|i| u8::from_str_radix(&private_key_hex[i..i + 2], 16))
8292 .collect::<Result<Vec<_>, _>>()
8293 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
8294
8295 if key_bytes.len() != 32 {
8296 return Err(RuntimeError::new(
8297 "ed25519_sign() requires 32-byte private key",
8298 ));
8299 }
8300
8301 let mut key_arr = [0u8; 32];
8302 key_arr.copy_from_slice(&key_bytes);
8303 let signing_key = SigningKey::from_bytes(&key_arr);
8304 let signature = signing_key.sign(&message);
8305
8306 Ok(Value::String(Rc::new(
8307 signature
8308 .to_bytes()
8309 .iter()
8310 .map(|b| format!("{:02x}", b))
8311 .collect(),
8312 )))
8313 });
8314
8315 define(interp, "ed25519_verify", Some(3), |_, args| {
8317 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
8318
8319 let public_key_hex = match &args[0] {
8320 Value::String(s) => s.to_string(),
8321 _ => {
8322 return Err(RuntimeError::new(
8323 "ed25519_verify() requires hex public key",
8324 ))
8325 }
8326 };
8327 let message = extract_bytes(&args[1], "ed25519_verify")?;
8328 let signature_hex = match &args[2] {
8329 Value::String(s) => s.to_string(),
8330 _ => return Err(RuntimeError::new("ed25519_verify() requires hex signature")),
8331 };
8332
8333 let key_bytes: Vec<u8> = (0..public_key_hex.len())
8334 .step_by(2)
8335 .map(|i| u8::from_str_radix(&public_key_hex[i..i + 2], 16))
8336 .collect::<Result<Vec<_>, _>>()
8337 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
8338 let sig_bytes: Vec<u8> = (0..signature_hex.len())
8339 .step_by(2)
8340 .map(|i| u8::from_str_radix(&signature_hex[i..i + 2], 16))
8341 .collect::<Result<Vec<_>, _>>()
8342 .map_err(|_| RuntimeError::new("Invalid signature hex"))?;
8343
8344 if key_bytes.len() != 32 {
8345 return Err(RuntimeError::new(
8346 "ed25519_verify() requires 32-byte public key",
8347 ));
8348 }
8349 if sig_bytes.len() != 64 {
8350 return Err(RuntimeError::new(
8351 "ed25519_verify() requires 64-byte signature",
8352 ));
8353 }
8354
8355 let mut key_arr = [0u8; 32];
8356 key_arr.copy_from_slice(&key_bytes);
8357 let mut sig_arr = [0u8; 64];
8358 sig_arr.copy_from_slice(&sig_bytes);
8359
8360 let verifying_key = VerifyingKey::from_bytes(&key_arr)
8361 .map_err(|e| RuntimeError::new(format!("Invalid public key: {}", e)))?;
8362 let signature = Signature::from_bytes(&sig_arr);
8363
8364 match verifying_key.verify(&message, &signature) {
8365 Ok(_) => Ok(Value::Bool(true)),
8366 Err(_) => Ok(Value::Bool(false)),
8367 }
8368 });
8369
8370 define(interp, "x25519_keygen", Some(0), |_, _| {
8372 use rand::rngs::OsRng;
8373 use x25519_dalek::{PublicKey, StaticSecret};
8374
8375 let secret = StaticSecret::random_from_rng(OsRng);
8376 let public = PublicKey::from(&secret);
8377
8378 let mut result = HashMap::new();
8379 result.insert(
8380 "private_key".to_string(),
8381 Value::String(Rc::new(
8382 secret
8383 .as_bytes()
8384 .iter()
8385 .map(|b| format!("{:02x}", b))
8386 .collect(),
8387 )),
8388 );
8389 result.insert(
8390 "public_key".to_string(),
8391 Value::String(Rc::new(
8392 public
8393 .as_bytes()
8394 .iter()
8395 .map(|b| format!("{:02x}", b))
8396 .collect(),
8397 )),
8398 );
8399 Ok(Value::Map(Rc::new(RefCell::new(result))))
8400 });
8401
8402 define(interp, "x25519_exchange", Some(2), |_, args| {
8404 use x25519_dalek::{PublicKey, StaticSecret};
8405
8406 let my_private_hex = match &args[0] {
8407 Value::String(s) => s.to_string(),
8408 _ => {
8409 return Err(RuntimeError::new(
8410 "x25519_exchange() requires hex private key",
8411 ))
8412 }
8413 };
8414 let their_public_hex = match &args[1] {
8415 Value::String(s) => s.to_string(),
8416 _ => {
8417 return Err(RuntimeError::new(
8418 "x25519_exchange() requires hex public key",
8419 ))
8420 }
8421 };
8422
8423 let my_private_bytes: Vec<u8> = (0..my_private_hex.len())
8424 .step_by(2)
8425 .map(|i| u8::from_str_radix(&my_private_hex[i..i + 2], 16))
8426 .collect::<Result<Vec<_>, _>>()
8427 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
8428 let their_public_bytes: Vec<u8> = (0..their_public_hex.len())
8429 .step_by(2)
8430 .map(|i| u8::from_str_radix(&their_public_hex[i..i + 2], 16))
8431 .collect::<Result<Vec<_>, _>>()
8432 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
8433
8434 if my_private_bytes.len() != 32 || their_public_bytes.len() != 32 {
8435 return Err(RuntimeError::new("x25519_exchange() requires 32-byte keys"));
8436 }
8437
8438 let mut priv_arr = [0u8; 32];
8439 priv_arr.copy_from_slice(&my_private_bytes);
8440 let mut pub_arr = [0u8; 32];
8441 pub_arr.copy_from_slice(&their_public_bytes);
8442
8443 let my_secret = StaticSecret::from(priv_arr);
8444 let their_public = PublicKey::from(pub_arr);
8445 let shared_secret = my_secret.diffie_hellman(&their_public);
8446
8447 Ok(Value::String(Rc::new(
8448 shared_secret
8449 .as_bytes()
8450 .iter()
8451 .map(|b| format!("{:02x}", b))
8452 .collect(),
8453 )))
8454 });
8455
8456 define(interp, "argon2_hash", Some(1), |_, args| {
8462 use argon2::{
8463 password_hash::{PasswordHasher, SaltString},
8464 Argon2,
8465 };
8466 use rand::rngs::OsRng;
8467
8468 let password = extract_bytes(&args[0], "argon2_hash")?;
8469 let salt = SaltString::generate(&mut OsRng);
8470 let argon2 = Argon2::default();
8471
8472 let hash = argon2
8473 .hash_password(&password, &salt)
8474 .map_err(|e| RuntimeError::new(format!("Argon2 error: {}", e)))?;
8475
8476 let mut result = HashMap::new();
8477 result.insert("hash".to_string(), Value::String(Rc::new(hash.to_string())));
8478 result.insert("salt".to_string(), Value::String(Rc::new(salt.to_string())));
8479 Ok(Value::Map(Rc::new(RefCell::new(result))))
8480 });
8481
8482 define(interp, "argon2_verify", Some(2), |_, args| {
8484 use argon2::{Argon2, PasswordHash, PasswordVerifier};
8485
8486 let password = extract_bytes(&args[0], "argon2_verify")?;
8487 let hash_str = match &args[1] {
8488 Value::String(s) => s.to_string(),
8489 _ => return Err(RuntimeError::new("argon2_verify() requires hash string")),
8490 };
8491
8492 let parsed_hash = PasswordHash::new(&hash_str)
8493 .map_err(|e| RuntimeError::new(format!("Invalid hash: {}", e)))?;
8494
8495 match Argon2::default().verify_password(&password, &parsed_hash) {
8496 Ok(_) => Ok(Value::Bool(true)),
8497 Err(_) => Ok(Value::Bool(false)),
8498 }
8499 });
8500
8501 define(interp, "hkdf_expand", Some(3), |_, args| {
8503 use hkdf::Hkdf;
8504
8505 let ikm = extract_bytes(&args[0], "hkdf_expand")?;
8506 let salt = extract_bytes(&args[1], "hkdf_expand")?;
8507 let info = extract_bytes(&args[2], "hkdf_expand")?;
8508
8509 let hk = Hkdf::<Sha256>::new(Some(&salt), &ikm);
8510 let mut okm = [0u8; 32];
8511 hk.expand(&info, &mut okm)
8512 .map_err(|e| RuntimeError::new(format!("HKDF error: {}", e)))?;
8513
8514 Ok(Value::String(Rc::new(
8515 okm.iter().map(|b| format!("{:02x}", b)).collect(),
8516 )))
8517 });
8518
8519 define(interp, "pbkdf2_derive", Some(3), |_, args| {
8521 let password = extract_bytes(&args[0], "pbkdf2_derive")?;
8522 let salt = extract_bytes(&args[1], "pbkdf2_derive")?;
8523 let iterations = match &args[2] {
8524 Value::Int(n) => *n as u32,
8525 _ => {
8526 return Err(RuntimeError::new(
8527 "pbkdf2_derive() requires integer iterations",
8528 ))
8529 }
8530 };
8531
8532 let mut key = [0u8; 32];
8533 pbkdf2::pbkdf2_hmac::<Sha256>(&password, &salt, iterations, &mut key);
8534 Ok(Value::String(Rc::new(
8535 key.iter().map(|b| format!("{:02x}", b)).collect(),
8536 )))
8537 });
8538
8539 define(interp, "hmac_sha256", Some(2), |_, args| {
8545 use hmac::{Hmac, Mac};
8546 type HmacSha256 = Hmac<Sha256>;
8547
8548 let key = extract_bytes(&args[0], "hmac_sha256")?;
8549 let message = extract_bytes(&args[1], "hmac_sha256")?;
8550
8551 let mut mac = HmacSha256::new_from_slice(&key)
8552 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8553 mac.update(&message);
8554 let result = mac.finalize();
8555 Ok(Value::String(Rc::new(
8556 result
8557 .into_bytes()
8558 .iter()
8559 .map(|b| format!("{:02x}", b))
8560 .collect(),
8561 )))
8562 });
8563
8564 define(interp, "hmac_sha512", Some(2), |_, args| {
8566 use hmac::{Hmac, Mac};
8567 type HmacSha512 = Hmac<Sha512>;
8568
8569 let key = extract_bytes(&args[0], "hmac_sha512")?;
8570 let message = extract_bytes(&args[1], "hmac_sha512")?;
8571
8572 let mut mac = HmacSha512::new_from_slice(&key)
8573 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8574 mac.update(&message);
8575 let result = mac.finalize();
8576 Ok(Value::String(Rc::new(
8577 result
8578 .into_bytes()
8579 .iter()
8580 .map(|b| format!("{:02x}", b))
8581 .collect(),
8582 )))
8583 });
8584
8585 define(interp, "hmac_verify", Some(3), |_, args| {
8587 use hmac::{Hmac, Mac};
8588 type HmacSha256 = Hmac<Sha256>;
8589
8590 let key = extract_bytes(&args[0], "hmac_verify")?;
8591 let message = extract_bytes(&args[1], "hmac_verify")?;
8592 let expected_hex = match &args[2] {
8593 Value::String(s) => s.to_string(),
8594 _ => return Err(RuntimeError::new("hmac_verify() requires hex MAC")),
8595 };
8596
8597 let expected: Vec<u8> = (0..expected_hex.len())
8598 .step_by(2)
8599 .map(|i| u8::from_str_radix(&expected_hex[i..i + 2], 16))
8600 .collect::<Result<Vec<_>, _>>()
8601 .map_err(|_| RuntimeError::new("Invalid MAC hex"))?;
8602
8603 let mut mac = HmacSha256::new_from_slice(&key)
8604 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
8605 mac.update(&message);
8606
8607 match mac.verify_slice(&expected) {
8608 Ok(_) => Ok(Value::Bool(true)),
8609 Err(_) => Ok(Value::Bool(false)),
8610 }
8611 });
8612
8613 define(interp, "secure_random_bytes", Some(1), |_, args| {
8619 use rand::RngCore;
8620
8621 let length = match &args[0] {
8622 Value::Int(n) => *n as usize,
8623 _ => {
8624 return Err(RuntimeError::new(
8625 "secure_random_bytes() requires integer length",
8626 ))
8627 }
8628 };
8629
8630 if length > 1024 * 1024 {
8631 return Err(RuntimeError::new("secure_random_bytes() max 1MB"));
8632 }
8633
8634 let mut bytes = vec![0u8; length];
8635 rand::thread_rng().fill_bytes(&mut bytes);
8636 Ok(bytes_to_array(&bytes))
8637 });
8638
8639 define(interp, "secure_random_hex", Some(1), |_, args| {
8641 use rand::RngCore;
8642
8643 let byte_length = match &args[0] {
8644 Value::Int(n) => *n as usize,
8645 _ => {
8646 return Err(RuntimeError::new(
8647 "secure_random_hex() requires integer length",
8648 ))
8649 }
8650 };
8651
8652 if byte_length > 1024 * 1024 {
8653 return Err(RuntimeError::new("secure_random_hex() max 1MB"));
8654 }
8655
8656 let mut bytes = vec![0u8; byte_length];
8657 rand::thread_rng().fill_bytes(&mut bytes);
8658 Ok(Value::String(Rc::new(
8659 bytes.iter().map(|b| format!("{:02x}", b)).collect(),
8660 )))
8661 });
8662
8663 define(interp, "generate_key", Some(1), |_, args| {
8665 use rand::RngCore;
8666
8667 let bits = match &args[0] {
8668 Value::Int(n) => *n as usize,
8669 _ => return Err(RuntimeError::new("generate_key() requires bit length")),
8670 };
8671
8672 if bits % 8 != 0 {
8673 return Err(RuntimeError::new(
8674 "generate_key() bit length must be multiple of 8",
8675 ));
8676 }
8677 if bits > 512 {
8678 return Err(RuntimeError::new("generate_key() max 512 bits"));
8679 }
8680
8681 let bytes = bits / 8;
8682 let mut key = vec![0u8; bytes];
8683 rand::thread_rng().fill_bytes(&mut key);
8684 Ok(Value::String(Rc::new(
8685 key.iter().map(|b| format!("{:02x}", b)).collect(),
8686 )))
8687 });
8688
8689 define(interp, "base64_encode", Some(1), |_, args| {
8695 let data = extract_bytes(&args[0], "base64_encode")?;
8696 Ok(Value::String(Rc::new(
8697 general_purpose::STANDARD.encode(&data),
8698 )))
8699 });
8700
8701 define(interp, "base64_decode", Some(1), |_, args| {
8703 let encoded = match &args[0] {
8704 Value::String(s) => s.to_string(),
8705 _ => return Err(RuntimeError::new("base64_decode() requires string")),
8706 };
8707
8708 match general_purpose::STANDARD.decode(&encoded) {
8709 Ok(bytes) => match String::from_utf8(bytes.clone()) {
8710 Ok(s) => Ok(Value::String(Rc::new(s))),
8711 Err(_) => Ok(bytes_to_array(&bytes)),
8712 },
8713 Err(e) => Err(RuntimeError::new(format!("base64_decode() error: {}", e))),
8714 }
8715 });
8716
8717 define(interp, "hex_encode", Some(1), |_, args| {
8719 let data = extract_bytes(&args[0], "hex_encode")?;
8720 Ok(Value::String(Rc::new(
8721 data.iter().map(|b| format!("{:02x}", b)).collect(),
8722 )))
8723 });
8724
8725 define(interp, "hex_decode", Some(1), |_, args| {
8727 let hex_str = match &args[0] {
8728 Value::String(s) => s.to_string(),
8729 _ => return Err(RuntimeError::new("hex_decode() requires string")),
8730 };
8731
8732 let hex_str = hex_str.trim();
8733 if hex_str.len() % 2 != 0 {
8734 return Err(RuntimeError::new(
8735 "hex_decode() requires even-length hex string",
8736 ));
8737 }
8738
8739 let bytes: Vec<Value> = (0..hex_str.len())
8740 .step_by(2)
8741 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).map(|b| Value::Int(b as i64)))
8742 .collect::<Result<Vec<_>, _>>()
8743 .map_err(|_| RuntimeError::new("hex_decode() invalid hex"))?;
8744 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
8745 });
8746
8747 define(interp, "constant_time_eq", Some(2), |_, args| {
8753 let a = extract_bytes(&args[0], "constant_time_eq")?;
8754 let b = extract_bytes(&args[1], "constant_time_eq")?;
8755
8756 if a.len() != b.len() {
8757 return Ok(Value::Bool(false));
8758 }
8759
8760 let mut result = 0u8;
8761 for (x, y) in a.iter().zip(b.iter()) {
8762 result |= x ^ y;
8763 }
8764 Ok(Value::Bool(result == 0))
8765 });
8766
8767 define(interp, "crypto_info", Some(0), |_, _| {
8773 let mut info = HashMap::new();
8774 info.insert(
8775 "version".to_string(),
8776 Value::String(Rc::new("2.0".to_string())),
8777 );
8778 info.insert(
8779 "phase".to_string(),
8780 Value::String(Rc::new("Evidential Cryptography".to_string())),
8781 );
8782
8783 let capabilities = vec![
8784 "sha256",
8785 "sha512",
8786 "sha3_256",
8787 "sha3_512",
8788 "blake3",
8789 "md5",
8790 "aes_gcm_encrypt",
8791 "aes_gcm_decrypt",
8792 "chacha20_encrypt",
8793 "chacha20_decrypt",
8794 "ed25519_keygen",
8795 "ed25519_sign",
8796 "ed25519_verify",
8797 "x25519_keygen",
8798 "x25519_exchange",
8799 "argon2_hash",
8800 "argon2_verify",
8801 "hkdf_expand",
8802 "pbkdf2_derive",
8803 "hmac_sha256",
8804 "hmac_sha512",
8805 "hmac_verify",
8806 "secure_random_bytes",
8807 "secure_random_hex",
8808 "generate_key",
8809 "base64_encode",
8810 "base64_decode",
8811 "hex_encode",
8812 "hex_decode",
8813 "constant_time_eq",
8814 ];
8815 let cap_values: Vec<Value> = capabilities
8816 .iter()
8817 .map(|s| Value::String(Rc::new(s.to_string())))
8818 .collect();
8819 info.insert(
8820 "functions".to_string(),
8821 Value::Array(Rc::new(RefCell::new(cap_values))),
8822 );
8823
8824 Ok(Value::Map(Rc::new(RefCell::new(info))))
8825 });
8826}
8827
8828fn register_regex(interp: &mut Interpreter) {
8833 define(interp, "regex_match", Some(2), |_, args| {
8835 let pattern = match &args[0] {
8836 Value::String(s) => s.to_string(),
8837 _ => return Err(RuntimeError::new("regex_match() requires string pattern")),
8838 };
8839 let text = match &args[1] {
8840 Value::String(s) => s.to_string(),
8841 _ => return Err(RuntimeError::new("regex_match() requires string text")),
8842 };
8843
8844 match Regex::new(&pattern) {
8845 Ok(re) => Ok(Value::Bool(re.is_match(&text))),
8846 Err(e) => Err(RuntimeError::new(format!(
8847 "regex_match() invalid pattern: {}",
8848 e
8849 ))),
8850 }
8851 });
8852
8853 define(interp, "regex_find", Some(2), |_, args| {
8855 let pattern = match &args[0] {
8856 Value::String(s) => s.to_string(),
8857 _ => return Err(RuntimeError::new("regex_find() requires string pattern")),
8858 };
8859 let text = match &args[1] {
8860 Value::String(s) => s.to_string(),
8861 _ => return Err(RuntimeError::new("regex_find() requires string text")),
8862 };
8863
8864 match Regex::new(&pattern) {
8865 Ok(re) => match re.find(&text) {
8866 Some(m) => Ok(Value::String(Rc::new(m.as_str().to_string()))),
8867 None => Ok(Value::Null),
8868 },
8869 Err(e) => Err(RuntimeError::new(format!(
8870 "regex_find() invalid pattern: {}",
8871 e
8872 ))),
8873 }
8874 });
8875
8876 define(interp, "regex_find_all", Some(2), |_, args| {
8878 let pattern = match &args[0] {
8879 Value::String(s) => s.to_string(),
8880 _ => {
8881 return Err(RuntimeError::new(
8882 "regex_find_all() requires string pattern",
8883 ))
8884 }
8885 };
8886 let text = match &args[1] {
8887 Value::String(s) => s.to_string(),
8888 _ => return Err(RuntimeError::new("regex_find_all() requires string text")),
8889 };
8890
8891 match Regex::new(&pattern) {
8892 Ok(re) => {
8893 let matches: Vec<Value> = re
8894 .find_iter(&text)
8895 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
8896 .collect();
8897 Ok(Value::Array(Rc::new(RefCell::new(matches))))
8898 }
8899 Err(e) => Err(RuntimeError::new(format!(
8900 "regex_find_all() invalid pattern: {}",
8901 e
8902 ))),
8903 }
8904 });
8905
8906 define(interp, "regex_replace", Some(3), |_, args| {
8908 let pattern = match &args[0] {
8909 Value::String(s) => s.to_string(),
8910 _ => return Err(RuntimeError::new("regex_replace() requires string pattern")),
8911 };
8912 let text = match &args[1] {
8913 Value::String(s) => s.to_string(),
8914 _ => return Err(RuntimeError::new("regex_replace() requires string text")),
8915 };
8916 let replacement = match &args[2] {
8917 Value::String(s) => s.to_string(),
8918 _ => {
8919 return Err(RuntimeError::new(
8920 "regex_replace() requires string replacement",
8921 ))
8922 }
8923 };
8924
8925 match Regex::new(&pattern) {
8926 Ok(re) => {
8927 let result = re.replace(&text, replacement.as_str());
8928 Ok(Value::String(Rc::new(result.to_string())))
8929 }
8930 Err(e) => Err(RuntimeError::new(format!(
8931 "regex_replace() invalid pattern: {}",
8932 e
8933 ))),
8934 }
8935 });
8936
8937 define(interp, "regex_replace_all", Some(3), |_, args| {
8939 let pattern = match &args[0] {
8940 Value::String(s) => s.to_string(),
8941 _ => {
8942 return Err(RuntimeError::new(
8943 "regex_replace_all() requires string pattern",
8944 ))
8945 }
8946 };
8947 let text = match &args[1] {
8948 Value::String(s) => s.to_string(),
8949 _ => {
8950 return Err(RuntimeError::new(
8951 "regex_replace_all() requires string text",
8952 ))
8953 }
8954 };
8955 let replacement = match &args[2] {
8956 Value::String(s) => s.to_string(),
8957 _ => {
8958 return Err(RuntimeError::new(
8959 "regex_replace_all() requires string replacement",
8960 ))
8961 }
8962 };
8963
8964 match Regex::new(&pattern) {
8965 Ok(re) => {
8966 let result = re.replace_all(&text, replacement.as_str());
8967 Ok(Value::String(Rc::new(result.to_string())))
8968 }
8969 Err(e) => Err(RuntimeError::new(format!(
8970 "regex_replace_all() invalid pattern: {}",
8971 e
8972 ))),
8973 }
8974 });
8975
8976 define(interp, "regex_split", Some(2), |_, args| {
8978 let pattern = match &args[0] {
8979 Value::String(s) => s.to_string(),
8980 _ => return Err(RuntimeError::new("regex_split() requires string pattern")),
8981 };
8982 let text = match &args[1] {
8983 Value::String(s) => s.to_string(),
8984 _ => return Err(RuntimeError::new("regex_split() requires string text")),
8985 };
8986
8987 match Regex::new(&pattern) {
8988 Ok(re) => {
8989 let parts: Vec<Value> = re
8990 .split(&text)
8991 .map(|s| Value::String(Rc::new(s.to_string())))
8992 .collect();
8993 Ok(Value::Array(Rc::new(RefCell::new(parts))))
8994 }
8995 Err(e) => Err(RuntimeError::new(format!(
8996 "regex_split() invalid pattern: {}",
8997 e
8998 ))),
8999 }
9000 });
9001
9002 define(interp, "regex_captures", Some(2), |_, args| {
9004 let pattern = match &args[0] {
9005 Value::String(s) => s.to_string(),
9006 _ => {
9007 return Err(RuntimeError::new(
9008 "regex_captures() requires string pattern",
9009 ))
9010 }
9011 };
9012 let text = match &args[1] {
9013 Value::String(s) => s.to_string(),
9014 _ => return Err(RuntimeError::new("regex_captures() requires string text")),
9015 };
9016
9017 match Regex::new(&pattern) {
9018 Ok(re) => match re.captures(&text) {
9019 Some(caps) => {
9020 let captures: Vec<Value> = caps
9021 .iter()
9022 .map(|m| {
9023 m.map(|m| Value::String(Rc::new(m.as_str().to_string())))
9024 .unwrap_or(Value::Null)
9025 })
9026 .collect();
9027 Ok(Value::Array(Rc::new(RefCell::new(captures))))
9028 }
9029 None => Ok(Value::Null),
9030 },
9031 Err(e) => Err(RuntimeError::new(format!(
9032 "regex_captures() invalid pattern: {}",
9033 e
9034 ))),
9035 }
9036 });
9037}
9038
9039fn register_uuid(interp: &mut Interpreter) {
9044 define(interp, "uuid_v4", Some(0), |_, _| {
9046 let id = Uuid::new_v4();
9047 Ok(Value::String(Rc::new(id.to_string())))
9048 });
9049
9050 define(interp, "uuid_nil", Some(0), |_, _| {
9052 Ok(Value::String(Rc::new(Uuid::nil().to_string())))
9053 });
9054
9055 define(interp, "uuid_parse", Some(1), |_, args| {
9057 let s = match &args[0] {
9058 Value::String(s) => s.to_string(),
9059 _ => return Err(RuntimeError::new("uuid_parse() requires string")),
9060 };
9061
9062 match Uuid::parse_str(&s) {
9063 Ok(id) => Ok(Value::String(Rc::new(id.to_string()))),
9064 Err(e) => Err(RuntimeError::new(format!("uuid_parse() error: {}", e))),
9065 }
9066 });
9067
9068 define(interp, "uuid_is_valid", Some(1), |_, args| {
9070 let s = match &args[0] {
9071 Value::String(s) => s.to_string(),
9072 _ => return Ok(Value::Bool(false)),
9073 };
9074 Ok(Value::Bool(Uuid::parse_str(&s).is_ok()))
9075 });
9076}
9077
9078fn register_system(interp: &mut Interpreter) {
9083 define(interp, "env_get", Some(1), |_, args| {
9085 let key = match &args[0] {
9086 Value::String(s) => s.to_string(),
9087 _ => return Err(RuntimeError::new("env_get() requires string key")),
9088 };
9089
9090 match std::env::var(&key) {
9091 Ok(val) => Ok(Value::String(Rc::new(val))),
9092 Err(_) => Ok(Value::Null),
9093 }
9094 });
9095
9096 define(interp, "env_set", Some(2), |_, args| {
9098 let key = match &args[0] {
9099 Value::String(s) => s.to_string(),
9100 _ => return Err(RuntimeError::new("env_set() requires string key")),
9101 };
9102 let val = match &args[1] {
9103 Value::String(s) => s.to_string(),
9104 _ => format!("{}", args[1]),
9105 };
9106
9107 std::env::set_var(&key, &val);
9108 Ok(Value::Null)
9109 });
9110
9111 define(interp, "env_remove", Some(1), |_, args| {
9113 let key = match &args[0] {
9114 Value::String(s) => s.to_string(),
9115 _ => return Err(RuntimeError::new("env_remove() requires string key")),
9116 };
9117
9118 std::env::remove_var(&key);
9119 Ok(Value::Null)
9120 });
9121
9122 define(interp, "env_vars", Some(0), |_, _| {
9124 let mut map = HashMap::new();
9125 for (key, val) in std::env::vars() {
9126 map.insert(key, Value::String(Rc::new(val)));
9127 }
9128 Ok(Value::Map(Rc::new(RefCell::new(map))))
9129 });
9130
9131 define(interp, "std·env·var", Some(1), |_, args| {
9133 let key = match &args[0] {
9134 Value::String(s) => s.as_str().to_string(),
9135 _ => return Err(RuntimeError::new("env::var expects string key")),
9136 };
9137 match std::env::var(&key) {
9138 Ok(val) => Ok(Value::Variant {
9139 enum_name: "Result".to_string(),
9140 variant_name: "Ok".to_string(),
9141 fields: Some(Rc::new(vec![Value::String(Rc::new(val))])),
9142 }),
9143 Err(_) => Ok(Value::Variant {
9144 enum_name: "Result".to_string(),
9145 variant_name: "Err".to_string(),
9146 fields: Some(Rc::new(vec![Value::String(Rc::new(
9147 "environment variable not found".to_string(),
9148 ))])),
9149 }),
9150 }
9151 });
9152
9153 define(interp, "std·env·temp_dir", Some(0), |_, _| {
9155 let temp_dir = std::env::temp_dir();
9156 Ok(Value::String(Rc::new(
9157 temp_dir.to_string_lossy().to_string(),
9158 )))
9159 });
9160
9161 define(interp, "temp_dir", Some(0), |_, _| {
9163 let temp_dir = std::env::temp_dir();
9164 Ok(Value::String(Rc::new(
9165 temp_dir.to_string_lossy().to_string(),
9166 )))
9167 });
9168
9169 define(
9171 interp,
9172 "std·env·current_dir",
9173 Some(0),
9174 |_, _| match std::env::current_dir() {
9175 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
9176 Err(e) => Err(RuntimeError::new(format!("current_dir() error: {}", e))),
9177 },
9178 );
9179
9180 define(interp, "std·env·args", Some(0), |interp, _| {
9182 let args: Vec<Value> = if interp
9183 .program_args
9184 .as_ref()
9185 .map(|v| v.is_empty())
9186 .unwrap_or(true)
9187 {
9188 std::env::args()
9190 .map(|s| Value::String(Rc::new(s)))
9191 .collect()
9192 } else {
9193 interp
9195 .program_args
9196 .as_ref()
9197 .unwrap()
9198 .iter()
9199 .map(|a| Value::String(Rc::new(a.clone())))
9200 .collect()
9201 };
9202 Ok(Value::Array(Rc::new(RefCell::new(args))))
9203 });
9204
9205 define(interp, "args", Some(0), |interp, _| {
9207 let args: Vec<Value> = if interp
9208 .program_args
9209 .as_ref()
9210 .map(|v| v.is_empty())
9211 .unwrap_or(true)
9212 {
9213 std::env::args()
9215 .map(|s| Value::String(Rc::new(s)))
9216 .collect()
9217 } else {
9218 interp
9220 .program_args
9221 .as_ref()
9222 .unwrap()
9223 .iter()
9224 .map(|a| Value::String(Rc::new(a.clone())))
9225 .collect()
9226 };
9227 Ok(Value::Array(Rc::new(RefCell::new(args))))
9228 });
9229
9230 define(interp, "cwd", Some(0), |_, _| {
9232 match std::env::current_dir() {
9233 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
9234 Err(e) => Err(RuntimeError::new(format!("cwd() error: {}", e))),
9235 }
9236 });
9237
9238 define(interp, "chdir", Some(1), |_, args| {
9240 let path = match &args[0] {
9241 Value::String(s) => s.to_string(),
9242 _ => return Err(RuntimeError::new("chdir() requires string path")),
9243 };
9244
9245 match std::env::set_current_dir(&path) {
9246 Ok(()) => Ok(Value::Null),
9247 Err(e) => Err(RuntimeError::new(format!("chdir() error: {}", e))),
9248 }
9249 });
9250
9251 define(interp, "hostname", Some(0), |_, _| {
9253 match std::fs::read_to_string("/etc/hostname") {
9255 Ok(name) => Ok(Value::String(Rc::new(name.trim().to_string()))),
9256 Err(_) => Ok(Value::String(Rc::new("unknown".to_string()))),
9257 }
9258 });
9259
9260 define(interp, "pid", Some(0), |_, _| {
9262 Ok(Value::Int(std::process::id() as i64))
9263 });
9264
9265 define(interp, "exit", Some(1), |_, args| {
9267 let code = match &args[0] {
9268 Value::Int(n) => *n as i32,
9269 _ => 0,
9270 };
9271 std::process::exit(code);
9272 });
9273
9274 define(interp, "std·process·exit", Some(1), |_, args| {
9276 let code = match &args[0] {
9277 Value::Int(n) => *n as i32,
9278 _ => 0,
9279 };
9280 std::process::exit(code);
9281 });
9282
9283 define(interp, "shell", Some(1), |_, args| {
9285 let cmd = match &args[0] {
9286 Value::String(s) => s.to_string(),
9287 _ => return Err(RuntimeError::new("shell() requires string command")),
9288 };
9289
9290 match std::process::Command::new("sh")
9291 .arg("-c")
9292 .arg(&cmd)
9293 .output()
9294 {
9295 Ok(output) => {
9296 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
9297 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
9298 let code = output.status.code().unwrap_or(-1);
9299
9300 let mut result = HashMap::new();
9301 result.insert("stdout".to_string(), Value::String(Rc::new(stdout)));
9302 result.insert("stderr".to_string(), Value::String(Rc::new(stderr)));
9303 result.insert("code".to_string(), Value::Int(code as i64));
9304 result.insert("success".to_string(), Value::Bool(output.status.success()));
9305
9306 Ok(Value::Map(Rc::new(RefCell::new(result))))
9307 }
9308 Err(e) => Err(RuntimeError::new(format!("shell() error: {}", e))),
9309 }
9310 });
9311
9312 define(interp, "platform", Some(0), |_, _| {
9314 Ok(Value::String(Rc::new(std::env::consts::OS.to_string())))
9315 });
9316
9317 define(interp, "arch", Some(0), |_, _| {
9319 Ok(Value::String(Rc::new(std::env::consts::ARCH.to_string())))
9320 });
9321
9322 define(interp, "num_cpus·get", Some(0), |_, _| {
9324 Ok(Value::Int(num_cpus::get() as i64))
9325 });
9326
9327 define(interp, "num_cpus·get_physical", Some(0), |_, _| {
9329 Ok(Value::Int(num_cpus::get_physical() as i64))
9330 });
9331}
9332
9333fn register_stats(interp: &mut Interpreter) {
9338 fn extract_numbers(val: &Value) -> Result<Vec<f64>, RuntimeError> {
9340 match val {
9341 Value::Array(arr) => {
9342 let arr = arr.borrow();
9343 let mut nums = Vec::new();
9344 for v in arr.iter() {
9345 match v {
9346 Value::Int(n) => nums.push(*n as f64),
9347 Value::Float(f) => nums.push(*f),
9348 _ => {
9349 return Err(RuntimeError::new("stats functions require numeric array"))
9350 }
9351 }
9352 }
9353 Ok(nums)
9354 }
9355 _ => Err(RuntimeError::new("stats functions require array")),
9356 }
9357 }
9358
9359 define(interp, "mean", Some(1), |_, args| {
9361 let nums = extract_numbers(&args[0])?;
9362 if nums.is_empty() {
9363 return Ok(Value::Float(0.0));
9364 }
9365 let sum: f64 = nums.iter().sum();
9366 Ok(Value::Float(sum / nums.len() as f64))
9367 });
9368
9369 define(interp, "median", Some(1), |_, args| {
9371 let mut nums = extract_numbers(&args[0])?;
9372 if nums.is_empty() {
9373 return Ok(Value::Float(0.0));
9374 }
9375 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
9376 let len = nums.len();
9377 if len % 2 == 0 {
9378 Ok(Value::Float((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
9379 } else {
9380 Ok(Value::Float(nums[len / 2]))
9381 }
9382 });
9383
9384 define(interp, "mode", Some(1), |_, args| {
9386 let nums = extract_numbers(&args[0])?;
9387 if nums.is_empty() {
9388 return Ok(Value::Null);
9389 }
9390
9391 let mut counts: HashMap<String, usize> = HashMap::new();
9392 for n in &nums {
9393 let key = format!("{:.10}", n);
9394 *counts.entry(key).or_insert(0) += 1;
9395 }
9396
9397 let max_count = counts.values().max().unwrap_or(&0);
9398 for n in &nums {
9399 let key = format!("{:.10}", n);
9400 if counts.get(&key) == Some(max_count) {
9401 return Ok(Value::Float(*n));
9402 }
9403 }
9404 Ok(Value::Null)
9405 });
9406
9407 define(interp, "variance", Some(1), |_, args| {
9409 let nums = extract_numbers(&args[0])?;
9410 if nums.is_empty() {
9411 return Ok(Value::Float(0.0));
9412 }
9413 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9414 let variance: f64 =
9415 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9416 Ok(Value::Float(variance))
9417 });
9418
9419 define(interp, "stddev", Some(1), |_, args| {
9421 let nums = extract_numbers(&args[0])?;
9422 if nums.is_empty() {
9423 return Ok(Value::Float(0.0));
9424 }
9425 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9426 let variance: f64 =
9427 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9428 Ok(Value::Float(variance.sqrt()))
9429 });
9430
9431 define(interp, "percentile", Some(2), |_, args| {
9433 let mut nums = extract_numbers(&args[0])?;
9434 let p = match &args[1] {
9435 Value::Int(n) => *n as f64,
9436 Value::Float(f) => *f,
9437 _ => {
9438 return Err(RuntimeError::new(
9439 "percentile() requires numeric percentile",
9440 ))
9441 }
9442 };
9443
9444 if nums.is_empty() {
9445 return Ok(Value::Float(0.0));
9446 }
9447
9448 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
9449 let idx = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
9450 Ok(Value::Float(nums[idx.min(nums.len() - 1)]))
9451 });
9452
9453 define(interp, "correlation", Some(2), |_, args| {
9455 let x = extract_numbers(&args[0])?;
9456 let y = extract_numbers(&args[1])?;
9457
9458 if x.len() != y.len() || x.is_empty() {
9459 return Err(RuntimeError::new(
9460 "correlation() requires equal-length non-empty arrays",
9461 ));
9462 }
9463
9464 let n = x.len() as f64;
9465 let mean_x: f64 = x.iter().sum::<f64>() / n;
9466 let mean_y: f64 = y.iter().sum::<f64>() / n;
9467
9468 let mut cov = 0.0;
9469 let mut var_x = 0.0;
9470 let mut var_y = 0.0;
9471
9472 for i in 0..x.len() {
9473 let dx = x[i] - mean_x;
9474 let dy = y[i] - mean_y;
9475 cov += dx * dy;
9476 var_x += dx * dx;
9477 var_y += dy * dy;
9478 }
9479
9480 if var_x == 0.0 || var_y == 0.0 {
9481 return Ok(Value::Float(0.0));
9482 }
9483
9484 Ok(Value::Float(cov / (var_x.sqrt() * var_y.sqrt())))
9485 });
9486
9487 define(interp, "range", Some(1), |_, args| {
9489 let nums = extract_numbers(&args[0])?;
9490 if nums.is_empty() {
9491 return Ok(Value::Float(0.0));
9492 }
9493 let min = nums.iter().cloned().fold(f64::INFINITY, f64::min);
9494 let max = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
9495 Ok(Value::Float(max - min))
9496 });
9497
9498 define(interp, "zscore", Some(1), |_, args| {
9500 let nums = extract_numbers(&args[0])?;
9501 if nums.is_empty() {
9502 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9503 }
9504
9505 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
9506 let variance: f64 =
9507 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
9508 let stddev = variance.sqrt();
9509
9510 if stddev == 0.0 {
9511 let zeros: Vec<Value> = nums.iter().map(|_| Value::Float(0.0)).collect();
9512 return Ok(Value::Array(Rc::new(RefCell::new(zeros))));
9513 }
9514
9515 let zscores: Vec<Value> = nums
9516 .iter()
9517 .map(|x| Value::Float((x - mean) / stddev))
9518 .collect();
9519 Ok(Value::Array(Rc::new(RefCell::new(zscores))))
9520 });
9521}
9522
9523fn register_matrix(interp: &mut Interpreter) {
9528 fn extract_matrix(val: &Value) -> Result<Vec<Vec<f64>>, RuntimeError> {
9530 match val {
9531 Value::Array(arr) => {
9532 let arr = arr.borrow();
9533 let mut matrix = Vec::new();
9534 for row in arr.iter() {
9535 match row {
9536 Value::Array(row_arr) => {
9537 let row_arr = row_arr.borrow();
9538 let mut row_vec = Vec::new();
9539 for v in row_arr.iter() {
9540 match v {
9541 Value::Int(n) => row_vec.push(*n as f64),
9542 Value::Float(f) => row_vec.push(*f),
9543 _ => {
9544 return Err(RuntimeError::new(
9545 "matrix requires numeric values",
9546 ))
9547 }
9548 }
9549 }
9550 matrix.push(row_vec);
9551 }
9552 _ => return Err(RuntimeError::new("matrix requires 2D array")),
9553 }
9554 }
9555 Ok(matrix)
9556 }
9557 _ => Err(RuntimeError::new("matrix requires array")),
9558 }
9559 }
9560
9561 fn matrix_to_value(m: Vec<Vec<f64>>) -> Value {
9562 let rows: Vec<Value> = m
9563 .into_iter()
9564 .map(|row| {
9565 let cols: Vec<Value> = row.into_iter().map(Value::Float).collect();
9566 Value::Array(Rc::new(RefCell::new(cols)))
9567 })
9568 .collect();
9569 Value::Array(Rc::new(RefCell::new(rows)))
9570 }
9571
9572 define(interp, "matrix_new", Some(3), |_, args| {
9574 let rows = match &args[0] {
9575 Value::Int(n) => *n as usize,
9576 _ => return Err(RuntimeError::new("matrix_new() requires integer rows")),
9577 };
9578 let cols = match &args[1] {
9579 Value::Int(n) => *n as usize,
9580 _ => return Err(RuntimeError::new("matrix_new() requires integer cols")),
9581 };
9582 let fill = match &args[2] {
9583 Value::Int(n) => *n as f64,
9584 Value::Float(f) => *f,
9585 _ => 0.0,
9586 };
9587
9588 let matrix = vec![vec![fill; cols]; rows];
9589 Ok(matrix_to_value(matrix))
9590 });
9591
9592 define(interp, "matrix_identity", Some(1), |_, args| {
9594 let size = match &args[0] {
9595 Value::Int(n) => *n as usize,
9596 _ => return Err(RuntimeError::new("matrix_identity() requires integer size")),
9597 };
9598
9599 let mut matrix = vec![vec![0.0; size]; size];
9600 for i in 0..size {
9601 matrix[i][i] = 1.0;
9602 }
9603 Ok(matrix_to_value(matrix))
9604 });
9605
9606 define(interp, "matrix_add", Some(2), |_, args| {
9608 let a = extract_matrix(&args[0])?;
9609 let b = extract_matrix(&args[1])?;
9610
9611 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
9612 return Err(RuntimeError::new(
9613 "matrix_add() requires same-size matrices",
9614 ));
9615 }
9616
9617 let result: Vec<Vec<f64>> = a
9618 .iter()
9619 .zip(b.iter())
9620 .map(|(row_a, row_b)| row_a.iter().zip(row_b.iter()).map(|(x, y)| x + y).collect())
9621 .collect();
9622
9623 Ok(matrix_to_value(result))
9624 });
9625
9626 define(interp, "matrix_sub", Some(2), |_, args| {
9628 let a = extract_matrix(&args[0])?;
9629 let b = extract_matrix(&args[1])?;
9630
9631 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
9632 return Err(RuntimeError::new(
9633 "matrix_sub() requires same-size matrices",
9634 ));
9635 }
9636
9637 let result: Vec<Vec<f64>> = a
9638 .iter()
9639 .zip(b.iter())
9640 .map(|(row_a, row_b)| row_a.iter().zip(row_b.iter()).map(|(x, y)| x - y).collect())
9641 .collect();
9642
9643 Ok(matrix_to_value(result))
9644 });
9645
9646 define(interp, "matrix_mul", Some(2), |_, args| {
9648 let a = extract_matrix(&args[0])?;
9649 let b = extract_matrix(&args[1])?;
9650
9651 if a.is_empty() || b.is_empty() || a[0].len() != b.len() {
9652 return Err(RuntimeError::new(
9653 "matrix_mul() requires compatible matrices (a.cols == b.rows)",
9654 ));
9655 }
9656
9657 let rows = a.len();
9658 let cols = b[0].len();
9659 let inner = b.len();
9660
9661 let mut result = vec![vec![0.0; cols]; rows];
9662 for i in 0..rows {
9663 for j in 0..cols {
9664 for k in 0..inner {
9665 result[i][j] += a[i][k] * b[k][j];
9666 }
9667 }
9668 }
9669
9670 Ok(matrix_to_value(result))
9671 });
9672
9673 define(interp, "matrix_scale", Some(2), |_, args| {
9675 let m = extract_matrix(&args[0])?;
9676 let scale = match &args[1] {
9677 Value::Int(n) => *n as f64,
9678 Value::Float(f) => *f,
9679 _ => return Err(RuntimeError::new("matrix_scale() requires numeric scalar")),
9680 };
9681
9682 let result: Vec<Vec<f64>> = m
9683 .iter()
9684 .map(|row| row.iter().map(|x| x * scale).collect())
9685 .collect();
9686
9687 Ok(matrix_to_value(result))
9688 });
9689
9690 define(interp, "matrix_transpose", Some(1), |_, args| {
9692 let m = extract_matrix(&args[0])?;
9693 if m.is_empty() {
9694 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9695 }
9696
9697 let rows = m.len();
9698 let cols = m[0].len();
9699 let mut result = vec![vec![0.0; rows]; cols];
9700
9701 for i in 0..rows {
9702 for j in 0..cols {
9703 result[j][i] = m[i][j];
9704 }
9705 }
9706
9707 Ok(matrix_to_value(result))
9708 });
9709
9710 define(interp, "matrix_det", Some(1), |_, args| {
9712 let m = extract_matrix(&args[0])?;
9713
9714 if m.is_empty() || m.len() != m[0].len() {
9715 return Err(RuntimeError::new("matrix_det() requires square matrix"));
9716 }
9717
9718 let n = m.len();
9719 match n {
9720 1 => Ok(Value::Float(m[0][0])),
9721 2 => Ok(Value::Float(m[0][0] * m[1][1] - m[0][1] * m[1][0])),
9722 3 => {
9723 let det = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
9724 - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
9725 + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
9726 Ok(Value::Float(det))
9727 }
9728 _ => Err(RuntimeError::new(
9729 "matrix_det() only supports up to 3x3 matrices",
9730 )),
9731 }
9732 });
9733
9734 define(interp, "matrix_trace", Some(1), |_, args| {
9736 let m = extract_matrix(&args[0])?;
9737
9738 let size = m.len().min(if m.is_empty() { 0 } else { m[0].len() });
9739 let trace: f64 = (0..size).map(|i| m[i][i]).sum();
9740
9741 Ok(Value::Float(trace))
9742 });
9743
9744 define(interp, "matrix_dot", Some(2), |_, args| {
9746 fn extract_vector(val: &Value) -> Result<Vec<f64>, RuntimeError> {
9747 match val {
9748 Value::Array(arr) => {
9749 let arr = arr.borrow();
9750 let mut vec = Vec::new();
9751 for v in arr.iter() {
9752 match v {
9753 Value::Int(n) => vec.push(*n as f64),
9754 Value::Float(f) => vec.push(*f),
9755 _ => {
9756 return Err(RuntimeError::new(
9757 "dot product requires numeric vectors",
9758 ))
9759 }
9760 }
9761 }
9762 Ok(vec)
9763 }
9764 _ => Err(RuntimeError::new("dot product requires arrays")),
9765 }
9766 }
9767
9768 let a = extract_vector(&args[0])?;
9769 let b = extract_vector(&args[1])?;
9770
9771 if a.len() != b.len() {
9772 return Err(RuntimeError::new(
9773 "matrix_dot() requires same-length vectors",
9774 ));
9775 }
9776
9777 let dot: f64 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
9778 Ok(Value::Float(dot))
9779 });
9780}
9781
9782fn mod_inverse(a: i64, m: i64) -> Option<i64> {
9784 let (mut old_r, mut r) = (a, m);
9785 let (mut old_s, mut s) = (1i64, 0i64);
9786
9787 while r != 0 {
9788 let q = old_r / r;
9789 (old_r, r) = (r, old_r - q * r);
9790 (old_s, s) = (s, old_s - q * s);
9791 }
9792
9793 if old_r != 1 {
9794 None } else {
9796 Some(old_s.rem_euclid(m))
9797 }
9798}
9799
9800fn register_functional(interp: &mut Interpreter) {
9806 define(interp, "identity", Some(1), |_, args| Ok(args[0].clone()));
9808
9809 define(interp, "const_fn", Some(1), |_, args| Ok(args[0].clone()));
9811
9812 define(interp, "apply", Some(2), |interp, args| {
9814 let func = match &args[0] {
9815 Value::Function(f) => f.clone(),
9816 _ => {
9817 return Err(RuntimeError::new(
9818 "apply: first argument must be a function",
9819 ))
9820 }
9821 };
9822 let fn_args = match &args[1] {
9823 Value::Array(arr) => arr.borrow().clone(),
9824 _ => return Err(RuntimeError::new("apply: second argument must be an array")),
9825 };
9826 interp.call_function(&func, fn_args)
9827 });
9828
9829 define(interp, "flip", Some(3), |interp, args| {
9831 let func = match &args[0] {
9832 Value::Function(f) => f.clone(),
9833 _ => return Err(RuntimeError::new("flip: first argument must be a function")),
9834 };
9835 let flipped_args = vec![args[2].clone(), args[1].clone()];
9836 interp.call_function(&func, flipped_args)
9837 });
9838
9839 define(interp, "tap", Some(2), |interp, args| {
9841 let val = args[0].clone();
9842 let func = match &args[1] {
9843 Value::Function(f) => f.clone(),
9844 _ => return Err(RuntimeError::new("tap: second argument must be a function")),
9845 };
9846 let _ = interp.call_function(&func, vec![val.clone()]);
9847 Ok(val)
9848 });
9849
9850 define(interp, "thunk", Some(1), |_, args| {
9852 Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()]))))
9853 });
9854
9855 define(interp, "force", Some(1), |interp, args| match &args[0] {
9857 Value::Array(arr) => {
9858 let arr = arr.borrow();
9859 if arr.len() == 1 {
9860 if let Value::Function(f) = &arr[0] {
9861 return interp.call_function(f, vec![]);
9862 }
9863 }
9864 Ok(arr.get(0).cloned().unwrap_or(Value::Null))
9865 }
9866 v => Ok(v.clone()),
9867 });
9868
9869 define(interp, "negate", Some(2), |interp, args| {
9871 let func = match &args[0] {
9872 Value::Function(f) => f.clone(),
9873 _ => {
9874 return Err(RuntimeError::new(
9875 "negate: first argument must be a function",
9876 ))
9877 }
9878 };
9879 let result = interp.call_function(&func, vec![args[1].clone()])?;
9880 Ok(Value::Bool(!is_truthy(&result)))
9881 });
9882
9883 define(interp, "complement", Some(2), |interp, args| {
9885 let func = match &args[0] {
9886 Value::Function(f) => f.clone(),
9887 _ => {
9888 return Err(RuntimeError::new(
9889 "complement: first argument must be a function",
9890 ))
9891 }
9892 };
9893 let result = interp.call_function(&func, vec![args[1].clone()])?;
9894 Ok(Value::Bool(!is_truthy(&result)))
9895 });
9896
9897 define(interp, "partial", None, |interp, args| {
9899 if args.len() < 2 {
9900 return Err(RuntimeError::new(
9901 "partial: requires at least function and one argument",
9902 ));
9903 }
9904 let func = match &args[0] {
9905 Value::Function(f) => f.clone(),
9906 _ => {
9907 return Err(RuntimeError::new(
9908 "partial: first argument must be a function",
9909 ))
9910 }
9911 };
9912 let partial_args: Vec<Value> = args[1..].to_vec();
9913 interp.call_function(&func, partial_args)
9914 });
9915
9916 define(interp, "juxt", None, |interp, args| {
9918 if args.len() < 2 {
9919 return Err(RuntimeError::new("juxt: requires functions and a value"));
9920 }
9921 let val = args.last().unwrap().clone();
9922 let results: Result<Vec<Value>, _> = args[..args.len() - 1]
9923 .iter()
9924 .map(|f| match f {
9925 Value::Function(func) => interp.call_function(func, vec![val.clone()]),
9926 _ => Err(RuntimeError::new(
9927 "juxt: all but last argument must be functions",
9928 )),
9929 })
9930 .collect();
9931 Ok(Value::Array(Rc::new(RefCell::new(results?))))
9932 });
9933}
9934
9935fn register_benchmark(interp: &mut Interpreter) {
9937 define(interp, "bench", Some(2), |interp, args| {
9939 let func = match &args[0] {
9940 Value::Function(f) => f.clone(),
9941 _ => {
9942 return Err(RuntimeError::new(
9943 "bench: first argument must be a function",
9944 ))
9945 }
9946 };
9947 let iterations = match &args[1] {
9948 Value::Int(n) => *n as usize,
9949 _ => {
9950 return Err(RuntimeError::new(
9951 "bench: second argument must be an integer",
9952 ))
9953 }
9954 };
9955
9956 let start = std::time::Instant::now();
9957 for _ in 0..iterations {
9958 let _ = interp.call_function(&func, vec![])?;
9959 }
9960 let elapsed = start.elapsed();
9961 let avg_ms = elapsed.as_secs_f64() * 1000.0 / iterations as f64;
9962 Ok(Value::Float(avg_ms))
9963 });
9964
9965 define(interp, "time_it", Some(1), |interp, args| {
9967 let func = match &args[0] {
9968 Value::Function(f) => f.clone(),
9969 _ => return Err(RuntimeError::new("time_it: argument must be a function")),
9970 };
9971
9972 let start = std::time::Instant::now();
9973 let result = interp.call_function(&func, vec![])?;
9974 let elapsed_ms = start.elapsed().as_secs_f64() * 1000.0;
9975
9976 Ok(Value::Tuple(Rc::new(vec![
9977 result,
9978 Value::Float(elapsed_ms),
9979 ])))
9980 });
9981
9982 define(interp, "stopwatch_start", Some(0), |_, _| {
9984 let elapsed = std::time::SystemTime::now()
9985 .duration_since(std::time::UNIX_EPOCH)
9986 .unwrap_or_default();
9987 Ok(Value::Float(elapsed.as_secs_f64() * 1000.0))
9988 });
9989
9990 define(interp, "stopwatch_elapsed", Some(1), |_, args| {
9992 let start_ms = match &args[0] {
9993 Value::Float(f) => *f,
9994 Value::Int(n) => *n as f64,
9995 _ => {
9996 return Err(RuntimeError::new(
9997 "stopwatch_elapsed: argument must be a number",
9998 ))
9999 }
10000 };
10001 let now = std::time::SystemTime::now()
10002 .duration_since(std::time::UNIX_EPOCH)
10003 .unwrap_or_default();
10004 let now_ms = now.as_secs_f64() * 1000.0;
10005 Ok(Value::Float(now_ms - start_ms))
10006 });
10007
10008 define(interp, "compare_bench", Some(3), |interp, args| {
10010 let func1 = match &args[0] {
10011 Value::Function(f) => f.clone(),
10012 _ => {
10013 return Err(RuntimeError::new(
10014 "compare_bench: first argument must be a function",
10015 ))
10016 }
10017 };
10018 let func2 = match &args[1] {
10019 Value::Function(f) => f.clone(),
10020 _ => {
10021 return Err(RuntimeError::new(
10022 "compare_bench: second argument must be a function",
10023 ))
10024 }
10025 };
10026 let iterations = match &args[2] {
10027 Value::Int(n) => *n as usize,
10028 _ => {
10029 return Err(RuntimeError::new(
10030 "compare_bench: third argument must be an integer",
10031 ))
10032 }
10033 };
10034
10035 let start1 = std::time::Instant::now();
10036 for _ in 0..iterations {
10037 let _ = interp.call_function(&func1, vec![])?;
10038 }
10039 let time1 = start1.elapsed().as_secs_f64();
10040
10041 let start2 = std::time::Instant::now();
10042 for _ in 0..iterations {
10043 let _ = interp.call_function(&func2, vec![])?;
10044 }
10045 let time2 = start2.elapsed().as_secs_f64();
10046
10047 let mut results = std::collections::HashMap::new();
10048 results.insert("time1_ms".to_string(), Value::Float(time1 * 1000.0));
10049 results.insert("time2_ms".to_string(), Value::Float(time2 * 1000.0));
10050 results.insert("speedup".to_string(), Value::Float(time1 / time2));
10051 results.insert("iterations".to_string(), Value::Int(iterations as i64));
10052
10053 Ok(Value::Struct {
10054 name: "BenchResult".to_string(),
10055 fields: Rc::new(RefCell::new(results)),
10056 })
10057 });
10058
10059 define(interp, "memory_usage", Some(0), |_, _| Ok(Value::Int(0)));
10061}
10062
10063fn register_itertools(interp: &mut Interpreter) {
10065 define(interp, "cycle", Some(2), |_, args| {
10067 let arr = match &args[0] {
10068 Value::Array(a) => a.borrow().clone(),
10069 _ => return Err(RuntimeError::new("cycle: first argument must be an array")),
10070 };
10071 let n = match &args[1] {
10072 Value::Int(n) => *n as usize,
10073 _ => {
10074 return Err(RuntimeError::new(
10075 "cycle: second argument must be an integer",
10076 ))
10077 }
10078 };
10079
10080 if arr.is_empty() {
10081 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
10082 }
10083
10084 let result: Vec<Value> = (0..n).map(|i| arr[i % arr.len()].clone()).collect();
10085 Ok(Value::Array(Rc::new(RefCell::new(result))))
10086 });
10087
10088 define(interp, "repeat_val", Some(2), |_, args| {
10090 let val = args[0].clone();
10091 let n = match &args[1] {
10092 Value::Int(n) => *n as usize,
10093 _ => {
10094 return Err(RuntimeError::new(
10095 "repeat_val: second argument must be an integer",
10096 ))
10097 }
10098 };
10099
10100 let result: Vec<Value> = std::iter::repeat(val).take(n).collect();
10101 Ok(Value::Array(Rc::new(RefCell::new(result))))
10102 });
10103
10104 define(interp, "take_while", Some(2), |interp, args| {
10106 let arr = match &args[0] {
10107 Value::Array(a) => a.borrow().clone(),
10108 _ => {
10109 return Err(RuntimeError::new(
10110 "take_while: first argument must be an array",
10111 ))
10112 }
10113 };
10114 let pred = match &args[1] {
10115 Value::Function(f) => f.clone(),
10116 _ => {
10117 return Err(RuntimeError::new(
10118 "take_while: second argument must be a function",
10119 ))
10120 }
10121 };
10122
10123 let mut result = Vec::new();
10124 for item in arr {
10125 let keep = interp.call_function(&pred, vec![item.clone()])?;
10126 if is_truthy(&keep) {
10127 result.push(item);
10128 } else {
10129 break;
10130 }
10131 }
10132 Ok(Value::Array(Rc::new(RefCell::new(result))))
10133 });
10134
10135 define(interp, "drop_while", Some(2), |interp, args| {
10137 let arr = match &args[0] {
10138 Value::Array(a) => a.borrow().clone(),
10139 _ => {
10140 return Err(RuntimeError::new(
10141 "drop_while: first argument must be an array",
10142 ))
10143 }
10144 };
10145 let pred = match &args[1] {
10146 Value::Function(f) => f.clone(),
10147 _ => {
10148 return Err(RuntimeError::new(
10149 "drop_while: second argument must be a function",
10150 ))
10151 }
10152 };
10153
10154 let mut dropping = true;
10155 let mut result = Vec::new();
10156 for item in arr {
10157 if dropping {
10158 let drop = interp.call_function(&pred, vec![item.clone()])?;
10159 if !is_truthy(&drop) {
10160 dropping = false;
10161 result.push(item);
10162 }
10163 } else {
10164 result.push(item);
10165 }
10166 }
10167 Ok(Value::Array(Rc::new(RefCell::new(result))))
10168 });
10169
10170 define(interp, "group_by", Some(2), |interp, args| {
10172 let arr = match &args[0] {
10173 Value::Array(a) => a.borrow().clone(),
10174 _ => {
10175 return Err(RuntimeError::new(
10176 "group_by: first argument must be an array",
10177 ))
10178 }
10179 };
10180 let key_fn = match &args[1] {
10181 Value::Function(f) => f.clone(),
10182 _ => {
10183 return Err(RuntimeError::new(
10184 "group_by: second argument must be a function",
10185 ))
10186 }
10187 };
10188
10189 let mut groups: Vec<Value> = Vec::new();
10190 let mut current_group: Vec<Value> = Vec::new();
10191 let mut current_key: Option<Value> = None;
10192
10193 for item in arr {
10194 let key = interp.call_function(&key_fn, vec![item.clone()])?;
10195 match ¤t_key {
10196 Some(k) if value_eq(k, &key) => {
10197 current_group.push(item);
10198 }
10199 _ => {
10200 if !current_group.is_empty() {
10201 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
10202 }
10203 current_group = vec![item];
10204 current_key = Some(key);
10205 }
10206 }
10207 }
10208 if !current_group.is_empty() {
10209 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
10210 }
10211
10212 Ok(Value::Array(Rc::new(RefCell::new(groups))))
10213 });
10214
10215 define(interp, "partition", Some(2), |interp, args| {
10217 let arr = match &args[0] {
10218 Value::Array(a) => a.borrow().clone(),
10219 _ => {
10220 return Err(RuntimeError::new(
10221 "partition: first argument must be an array",
10222 ))
10223 }
10224 };
10225 let pred = match &args[1] {
10226 Value::Function(f) => f.clone(),
10227 _ => {
10228 return Err(RuntimeError::new(
10229 "partition: second argument must be a function",
10230 ))
10231 }
10232 };
10233
10234 let mut true_items = Vec::new();
10235 let mut false_items = Vec::new();
10236
10237 for item in arr {
10238 let result = interp.call_function(&pred, vec![item.clone()])?;
10239 if is_truthy(&result) {
10240 true_items.push(item);
10241 } else {
10242 false_items.push(item);
10243 }
10244 }
10245
10246 Ok(Value::Tuple(Rc::new(vec![
10247 Value::Array(Rc::new(RefCell::new(true_items))),
10248 Value::Array(Rc::new(RefCell::new(false_items))),
10249 ])))
10250 });
10251
10252 define(interp, "interleave", Some(2), |_, args| {
10254 let arr1 = match &args[0] {
10255 Value::Array(a) => a.borrow().clone(),
10256 _ => {
10257 return Err(RuntimeError::new(
10258 "interleave: first argument must be an array",
10259 ))
10260 }
10261 };
10262 let arr2 = match &args[1] {
10263 Value::Array(a) => a.borrow().clone(),
10264 _ => {
10265 return Err(RuntimeError::new(
10266 "interleave: second argument must be an array",
10267 ))
10268 }
10269 };
10270
10271 let mut result = Vec::new();
10272 let mut i1 = arr1.into_iter();
10273 let mut i2 = arr2.into_iter();
10274
10275 loop {
10276 match (i1.next(), i2.next()) {
10277 (Some(a), Some(b)) => {
10278 result.push(a);
10279 result.push(b);
10280 }
10281 (Some(a), None) => {
10282 result.push(a);
10283 result.extend(i1);
10284 break;
10285 }
10286 (None, Some(b)) => {
10287 result.push(b);
10288 result.extend(i2);
10289 break;
10290 }
10291 (None, None) => break,
10292 }
10293 }
10294
10295 Ok(Value::Array(Rc::new(RefCell::new(result))))
10296 });
10297
10298 define(interp, "chunks", Some(2), |_, args| {
10300 let arr = match &args[0] {
10301 Value::Array(a) => a.borrow().clone(),
10302 _ => return Err(RuntimeError::new("chunks: first argument must be an array")),
10303 };
10304 let size = match &args[1] {
10305 Value::Int(n) if *n > 0 => *n as usize,
10306 _ => {
10307 return Err(RuntimeError::new(
10308 "chunks: second argument must be a positive integer",
10309 ))
10310 }
10311 };
10312
10313 let chunks: Vec<Value> = arr
10314 .chunks(size)
10315 .map(|chunk| Value::Array(Rc::new(RefCell::new(chunk.to_vec()))))
10316 .collect();
10317
10318 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
10319 });
10320
10321 define(interp, "windows", Some(2), |_, args| {
10323 let arr = match &args[0] {
10324 Value::Array(a) => a.borrow().clone(),
10325 _ => {
10326 return Err(RuntimeError::new(
10327 "windows: first argument must be an array",
10328 ))
10329 }
10330 };
10331 let size = match &args[1] {
10332 Value::Int(n) if *n > 0 => *n as usize,
10333 _ => {
10334 return Err(RuntimeError::new(
10335 "windows: second argument must be a positive integer",
10336 ))
10337 }
10338 };
10339
10340 if arr.len() < size {
10341 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
10342 }
10343
10344 let windows: Vec<Value> = arr
10345 .windows(size)
10346 .map(|window| Value::Array(Rc::new(RefCell::new(window.to_vec()))))
10347 .collect();
10348
10349 Ok(Value::Array(Rc::new(RefCell::new(windows))))
10350 });
10351
10352 define(interp, "scan", Some(3), |interp, args| {
10354 let arr = match &args[0] {
10355 Value::Array(a) => a.borrow().clone(),
10356 _ => return Err(RuntimeError::new("scan: first argument must be an array")),
10357 };
10358 let init = args[1].clone();
10359 let func = match &args[2] {
10360 Value::Function(f) => f.clone(),
10361 _ => return Err(RuntimeError::new("scan: third argument must be a function")),
10362 };
10363
10364 let mut results = vec![init.clone()];
10365 let mut acc = init;
10366
10367 for item in arr {
10368 acc = interp.call_function(&func, vec![acc, item])?;
10369 results.push(acc.clone());
10370 }
10371
10372 Ok(Value::Array(Rc::new(RefCell::new(results))))
10373 });
10374
10375 define(interp, "frequencies", Some(1), |_, args| {
10377 let arr = match &args[0] {
10378 Value::Array(a) => a.borrow().clone(),
10379 _ => return Err(RuntimeError::new("frequencies: argument must be an array")),
10380 };
10381
10382 let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
10383 for item in &arr {
10384 let key = format!("{}", item);
10385 *counts.entry(key).or_insert(0) += 1;
10386 }
10387
10388 let result: std::collections::HashMap<String, Value> = counts
10389 .into_iter()
10390 .map(|(k, v)| (k, Value::Int(v)))
10391 .collect();
10392
10393 Ok(Value::Map(Rc::new(RefCell::new(result))))
10394 });
10395
10396 define(interp, "dedupe", Some(1), |_, args| {
10398 let arr = match &args[0] {
10399 Value::Array(a) => a.borrow().clone(),
10400 _ => return Err(RuntimeError::new("dedupe: argument must be an array")),
10401 };
10402
10403 let mut result = Vec::new();
10404 let mut prev: Option<Value> = None;
10405
10406 for item in arr {
10407 match &prev {
10408 Some(p) if value_eq(p, &item) => continue,
10409 _ => {
10410 result.push(item.clone());
10411 prev = Some(item);
10412 }
10413 }
10414 }
10415
10416 Ok(Value::Array(Rc::new(RefCell::new(result))))
10417 });
10418
10419 define(interp, "unique", Some(1), |_, args| {
10421 let arr = match &args[0] {
10422 Value::Array(a) => a.borrow().clone(),
10423 _ => return Err(RuntimeError::new("unique: argument must be an array")),
10424 };
10425
10426 let mut seen = std::collections::HashSet::new();
10427 let mut result = Vec::new();
10428
10429 for item in arr {
10430 let key = format!("{}", item);
10431 if seen.insert(key) {
10432 result.push(item);
10433 }
10434 }
10435
10436 Ok(Value::Array(Rc::new(RefCell::new(result))))
10437 });
10438}
10439
10440fn register_ranges(interp: &mut Interpreter) {
10442 define(interp, "range_step", Some(3), |_, args| {
10444 let start = match &args[0] {
10445 Value::Int(n) => *n,
10446 Value::Float(f) => *f as i64,
10447 _ => return Err(RuntimeError::new("range_step: start must be a number")),
10448 };
10449 let end = match &args[1] {
10450 Value::Int(n) => *n,
10451 Value::Float(f) => *f as i64,
10452 _ => return Err(RuntimeError::new("range_step: end must be a number")),
10453 };
10454 let step = match &args[2] {
10455 Value::Int(n) if *n != 0 => *n,
10456 Value::Float(f) if *f != 0.0 => *f as i64,
10457 _ => {
10458 return Err(RuntimeError::new(
10459 "range_step: step must be a non-zero number",
10460 ))
10461 }
10462 };
10463
10464 let mut result = Vec::new();
10465 if step > 0 {
10466 let mut i = start;
10467 while i < end {
10468 result.push(Value::Int(i));
10469 i += step;
10470 }
10471 } else {
10472 let mut i = start;
10473 while i > end {
10474 result.push(Value::Int(i));
10475 i += step;
10476 }
10477 }
10478
10479 Ok(Value::Array(Rc::new(RefCell::new(result))))
10480 });
10481
10482 define(interp, "linspace", Some(3), |_, args| {
10484 let start = match &args[0] {
10485 Value::Int(n) => *n as f64,
10486 Value::Float(f) => *f,
10487 _ => return Err(RuntimeError::new("linspace: start must be a number")),
10488 };
10489 let end = match &args[1] {
10490 Value::Int(n) => *n as f64,
10491 Value::Float(f) => *f,
10492 _ => return Err(RuntimeError::new("linspace: end must be a number")),
10493 };
10494 let n = match &args[2] {
10495 Value::Int(n) if *n > 0 => *n as usize,
10496 _ => {
10497 return Err(RuntimeError::new(
10498 "linspace: count must be a positive integer",
10499 ))
10500 }
10501 };
10502
10503 if n == 1 {
10504 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10505 start,
10506 )]))));
10507 }
10508
10509 let step = (end - start) / (n - 1) as f64;
10510 let result: Vec<Value> = (0..n)
10511 .map(|i| Value::Float(start + step * i as f64))
10512 .collect();
10513
10514 Ok(Value::Array(Rc::new(RefCell::new(result))))
10515 });
10516
10517 define(interp, "logspace", Some(3), |_, args| {
10519 let start_exp = match &args[0] {
10520 Value::Int(n) => *n as f64,
10521 Value::Float(f) => *f,
10522 _ => {
10523 return Err(RuntimeError::new(
10524 "logspace: start exponent must be a number",
10525 ))
10526 }
10527 };
10528 let end_exp = match &args[1] {
10529 Value::Int(n) => *n as f64,
10530 Value::Float(f) => *f,
10531 _ => return Err(RuntimeError::new("logspace: end exponent must be a number")),
10532 };
10533 let n = match &args[2] {
10534 Value::Int(n) if *n > 0 => *n as usize,
10535 _ => {
10536 return Err(RuntimeError::new(
10537 "logspace: count must be a positive integer",
10538 ))
10539 }
10540 };
10541
10542 if n == 1 {
10543 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10544 10f64.powf(start_exp),
10545 )]))));
10546 }
10547
10548 let step = (end_exp - start_exp) / (n - 1) as f64;
10549 let result: Vec<Value> = (0..n)
10550 .map(|i| Value::Float(10f64.powf(start_exp + step * i as f64)))
10551 .collect();
10552
10553 Ok(Value::Array(Rc::new(RefCell::new(result))))
10554 });
10555
10556 define(interp, "arange", Some(3), |_, args| {
10558 let start = match &args[0] {
10559 Value::Int(n) => *n as f64,
10560 Value::Float(f) => *f,
10561 _ => return Err(RuntimeError::new("arange: start must be a number")),
10562 };
10563 let stop = match &args[1] {
10564 Value::Int(n) => *n as f64,
10565 Value::Float(f) => *f,
10566 _ => return Err(RuntimeError::new("arange: stop must be a number")),
10567 };
10568 let step = match &args[2] {
10569 Value::Int(n) if *n != 0 => *n as f64,
10570 Value::Float(f) if *f != 0.0 => *f,
10571 _ => return Err(RuntimeError::new("arange: step must be a non-zero number")),
10572 };
10573
10574 let mut result = Vec::new();
10575 if step > 0.0 {
10576 let mut x = start;
10577 while x < stop {
10578 result.push(Value::Float(x));
10579 x += step;
10580 }
10581 } else {
10582 let mut x = start;
10583 while x > stop {
10584 result.push(Value::Float(x));
10585 x += step;
10586 }
10587 }
10588
10589 Ok(Value::Array(Rc::new(RefCell::new(result))))
10590 });
10591
10592 define(interp, "geomspace", Some(3), |_, args| {
10594 let start = match &args[0] {
10595 Value::Int(n) if *n > 0 => *n as f64,
10596 Value::Float(f) if *f > 0.0 => *f,
10597 _ => {
10598 return Err(RuntimeError::new(
10599 "geomspace: start must be a positive number",
10600 ))
10601 }
10602 };
10603 let end = match &args[1] {
10604 Value::Int(n) if *n > 0 => *n as f64,
10605 Value::Float(f) if *f > 0.0 => *f,
10606 _ => {
10607 return Err(RuntimeError::new(
10608 "geomspace: end must be a positive number",
10609 ))
10610 }
10611 };
10612 let n = match &args[2] {
10613 Value::Int(n) if *n > 0 => *n as usize,
10614 _ => {
10615 return Err(RuntimeError::new(
10616 "geomspace: count must be a positive integer",
10617 ))
10618 }
10619 };
10620
10621 if n == 1 {
10622 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
10623 start,
10624 )]))));
10625 }
10626
10627 let ratio = (end / start).powf(1.0 / (n - 1) as f64);
10628 let result: Vec<Value> = (0..n)
10629 .map(|i| Value::Float(start * ratio.powi(i as i32)))
10630 .collect();
10631
10632 Ok(Value::Array(Rc::new(RefCell::new(result))))
10633 });
10634}
10635
10636fn register_bitwise(interp: &mut Interpreter) {
10638 define(interp, "bit_and", Some(2), |_, args| {
10639 let a = match &args[0] {
10640 Value::Int(n) => *n,
10641 _ => return Err(RuntimeError::new("bit_and: arguments must be integers")),
10642 };
10643 let b = match &args[1] {
10644 Value::Int(n) => *n,
10645 _ => return Err(RuntimeError::new("bit_and: arguments must be integers")),
10646 };
10647 Ok(Value::Int(a & b))
10648 });
10649
10650 define(interp, "bit_or", Some(2), |_, args| {
10651 let a = match &args[0] {
10652 Value::Int(n) => *n,
10653 _ => return Err(RuntimeError::new("bit_or: arguments must be integers")),
10654 };
10655 let b = match &args[1] {
10656 Value::Int(n) => *n,
10657 _ => return Err(RuntimeError::new("bit_or: arguments must be integers")),
10658 };
10659 Ok(Value::Int(a | b))
10660 });
10661
10662 define(interp, "bit_xor", Some(2), |_, args| {
10663 let a = match &args[0] {
10664 Value::Int(n) => *n,
10665 _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")),
10666 };
10667 let b = match &args[1] {
10668 Value::Int(n) => *n,
10669 _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")),
10670 };
10671 Ok(Value::Int(a ^ b))
10672 });
10673
10674 define(interp, "bit_not", Some(1), |_, args| {
10675 let a = match &args[0] {
10676 Value::Int(n) => *n,
10677 _ => return Err(RuntimeError::new("bit_not: argument must be an integer")),
10678 };
10679 Ok(Value::Int(!a))
10680 });
10681
10682 define(interp, "bit_shl", Some(2), |_, args| {
10683 let a = match &args[0] {
10684 Value::Int(n) => *n,
10685 _ => {
10686 return Err(RuntimeError::new(
10687 "bit_shl: first argument must be an integer",
10688 ))
10689 }
10690 };
10691 let b = match &args[1] {
10692 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10693 _ => return Err(RuntimeError::new("bit_shl: shift amount must be 0-63")),
10694 };
10695 Ok(Value::Int(a << b))
10696 });
10697
10698 define(interp, "bit_shr", Some(2), |_, args| {
10699 let a = match &args[0] {
10700 Value::Int(n) => *n,
10701 _ => {
10702 return Err(RuntimeError::new(
10703 "bit_shr: first argument must be an integer",
10704 ))
10705 }
10706 };
10707 let b = match &args[1] {
10708 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10709 _ => return Err(RuntimeError::new("bit_shr: shift amount must be 0-63")),
10710 };
10711 Ok(Value::Int(a >> b))
10712 });
10713
10714 define(interp, "popcount", Some(1), |_, args| {
10715 let a = match &args[0] {
10716 Value::Int(n) => *n,
10717 _ => return Err(RuntimeError::new("popcount: argument must be an integer")),
10718 };
10719 Ok(Value::Int(a.count_ones() as i64))
10720 });
10721
10722 define(interp, "leading_zeros", Some(1), |_, args| {
10723 let a = match &args[0] {
10724 Value::Int(n) => *n,
10725 _ => {
10726 return Err(RuntimeError::new(
10727 "leading_zeros: argument must be an integer",
10728 ))
10729 }
10730 };
10731 Ok(Value::Int(a.leading_zeros() as i64))
10732 });
10733
10734 define(interp, "trailing_zeros", Some(1), |_, args| {
10735 let a = match &args[0] {
10736 Value::Int(n) => *n,
10737 _ => {
10738 return Err(RuntimeError::new(
10739 "trailing_zeros: argument must be an integer",
10740 ))
10741 }
10742 };
10743 Ok(Value::Int(a.trailing_zeros() as i64))
10744 });
10745
10746 define(interp, "bit_test", Some(2), |_, args| {
10747 let a = match &args[0] {
10748 Value::Int(n) => *n,
10749 _ => {
10750 return Err(RuntimeError::new(
10751 "bit_test: first argument must be an integer",
10752 ))
10753 }
10754 };
10755 let pos = match &args[1] {
10756 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10757 _ => return Err(RuntimeError::new("bit_test: position must be 0-63")),
10758 };
10759 Ok(Value::Bool((a >> pos) & 1 == 1))
10760 });
10761
10762 define(interp, "bit_set", Some(2), |_, args| {
10763 let a = match &args[0] {
10764 Value::Int(n) => *n,
10765 _ => {
10766 return Err(RuntimeError::new(
10767 "bit_set: first argument must be an integer",
10768 ))
10769 }
10770 };
10771 let pos = match &args[1] {
10772 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10773 _ => return Err(RuntimeError::new("bit_set: position must be 0-63")),
10774 };
10775 Ok(Value::Int(a | (1 << pos)))
10776 });
10777
10778 define(interp, "bit_clear", Some(2), |_, args| {
10779 let a = match &args[0] {
10780 Value::Int(n) => *n,
10781 _ => {
10782 return Err(RuntimeError::new(
10783 "bit_clear: first argument must be an integer",
10784 ))
10785 }
10786 };
10787 let pos = match &args[1] {
10788 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10789 _ => return Err(RuntimeError::new("bit_clear: position must be 0-63")),
10790 };
10791 Ok(Value::Int(a & !(1 << pos)))
10792 });
10793
10794 define(interp, "bit_toggle", Some(2), |_, args| {
10795 let a = match &args[0] {
10796 Value::Int(n) => *n,
10797 _ => {
10798 return Err(RuntimeError::new(
10799 "bit_toggle: first argument must be an integer",
10800 ))
10801 }
10802 };
10803 let pos = match &args[1] {
10804 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
10805 _ => return Err(RuntimeError::new("bit_toggle: position must be 0-63")),
10806 };
10807 Ok(Value::Int(a ^ (1 << pos)))
10808 });
10809
10810 define(interp, "to_binary", Some(1), |_, args| {
10811 let a = match &args[0] {
10812 Value::Int(n) => *n,
10813 _ => return Err(RuntimeError::new("to_binary: argument must be an integer")),
10814 };
10815 Ok(Value::String(Rc::new(format!("{:b}", a))))
10816 });
10817
10818 define(interp, "from_binary", Some(1), |_, args| {
10819 let s = match &args[0] {
10820 Value::String(s) => (**s).clone(),
10821 _ => return Err(RuntimeError::new("from_binary: argument must be a string")),
10822 };
10823 match i64::from_str_radix(&s, 2) {
10824 Ok(n) => Ok(Value::Int(n)),
10825 Err(_) => Err(RuntimeError::new("from_binary: invalid binary string")),
10826 }
10827 });
10828
10829 define(interp, "to_hex", Some(1), |_, args| {
10830 let a = match &args[0] {
10831 Value::Int(n) => *n,
10832 _ => return Err(RuntimeError::new("to_hex: argument must be an integer")),
10833 };
10834 Ok(Value::String(Rc::new(format!("{:x}", a))))
10835 });
10836
10837 define(interp, "from_hex", Some(1), |_, args| {
10838 let s = match &args[0] {
10839 Value::String(s) => s.trim_start_matches("0x").to_string(),
10840 _ => return Err(RuntimeError::new("from_hex: argument must be a string")),
10841 };
10842 match i64::from_str_radix(&s, 16) {
10843 Ok(n) => Ok(Value::Int(n)),
10844 Err(_) => Err(RuntimeError::new("from_hex: invalid hex string")),
10845 }
10846 });
10847
10848 define(interp, "to_octal", Some(1), |_, args| {
10849 let a = match &args[0] {
10850 Value::Int(n) => *n,
10851 _ => return Err(RuntimeError::new("to_octal: argument must be an integer")),
10852 };
10853 Ok(Value::String(Rc::new(format!("{:o}", a))))
10854 });
10855
10856 define(interp, "from_octal", Some(1), |_, args| {
10857 let s = match &args[0] {
10858 Value::String(s) => s.trim_start_matches("0o").to_string(),
10859 _ => return Err(RuntimeError::new("from_octal: argument must be a string")),
10860 };
10861 match i64::from_str_radix(&s, 8) {
10862 Ok(n) => Ok(Value::Int(n)),
10863 Err(_) => Err(RuntimeError::new("from_octal: invalid octal string")),
10864 }
10865 });
10866}
10867
10868fn register_format(interp: &mut Interpreter) {
10870 define(interp, "format", None, |_, args| {
10872 if args.is_empty() {
10873 return Err(RuntimeError::new(
10874 "format: requires at least a format string",
10875 ));
10876 }
10877 let template = match &args[0] {
10878 Value::String(s) => (**s).clone(),
10879 _ => return Err(RuntimeError::new("format: first argument must be a string")),
10880 };
10881 let mut result = template;
10882 for arg in &args[1..] {
10883 if let Some(pos) = result.find("{}") {
10884 result = format!("{}{}{}", &result[..pos], arg, &result[pos + 2..]);
10885 }
10886 }
10887 Ok(Value::String(Rc::new(result)))
10888 });
10889
10890 define(interp, "pad_left", Some(3), |_, args| {
10892 let s = match &args[0] {
10893 Value::String(s) => (**s).clone(),
10894 _ => {
10895 return Err(RuntimeError::new(
10896 "pad_left: first argument must be a string",
10897 ))
10898 }
10899 };
10900 let width = match &args[1] {
10901 Value::Int(n) if *n >= 0 => *n as usize,
10902 _ => {
10903 return Err(RuntimeError::new(
10904 "pad_left: width must be a non-negative integer",
10905 ))
10906 }
10907 };
10908 let pad_char = match &args[2] {
10909 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10910 Value::Char(c) => *c,
10911 _ => {
10912 return Err(RuntimeError::new(
10913 "pad_left: pad character must be a non-empty string or char",
10914 ))
10915 }
10916 };
10917 let char_count = s.chars().count();
10918 if char_count >= width {
10919 return Ok(Value::String(Rc::new(s)));
10920 }
10921 let padding: String = std::iter::repeat(pad_char)
10922 .take(width - char_count)
10923 .collect();
10924 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
10925 });
10926
10927 define(interp, "pad_right", Some(3), |_, args| {
10929 let s = match &args[0] {
10930 Value::String(s) => (**s).clone(),
10931 _ => {
10932 return Err(RuntimeError::new(
10933 "pad_right: first argument must be a string",
10934 ))
10935 }
10936 };
10937 let width = match &args[1] {
10938 Value::Int(n) if *n >= 0 => *n as usize,
10939 _ => {
10940 return Err(RuntimeError::new(
10941 "pad_right: width must be a non-negative integer",
10942 ))
10943 }
10944 };
10945 let pad_char = match &args[2] {
10946 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10947 Value::Char(c) => *c,
10948 _ => {
10949 return Err(RuntimeError::new(
10950 "pad_right: pad character must be a non-empty string or char",
10951 ))
10952 }
10953 };
10954 let char_count = s.chars().count();
10955 if char_count >= width {
10956 return Ok(Value::String(Rc::new(s)));
10957 }
10958 let padding: String = std::iter::repeat(pad_char)
10959 .take(width - char_count)
10960 .collect();
10961 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
10962 });
10963
10964 define(interp, "center", Some(3), |_, args| {
10966 let s = match &args[0] {
10967 Value::String(s) => (**s).clone(),
10968 _ => return Err(RuntimeError::new("center: first argument must be a string")),
10969 };
10970 let width = match &args[1] {
10971 Value::Int(n) if *n >= 0 => *n as usize,
10972 _ => {
10973 return Err(RuntimeError::new(
10974 "center: width must be a non-negative integer",
10975 ))
10976 }
10977 };
10978 let pad_char = match &args[2] {
10979 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
10980 Value::Char(c) => *c,
10981 _ => {
10982 return Err(RuntimeError::new(
10983 "center: pad character must be a non-empty string or char",
10984 ))
10985 }
10986 };
10987 let char_count = s.chars().count();
10988 if char_count >= width {
10989 return Ok(Value::String(Rc::new(s)));
10990 }
10991 let total_padding = width - char_count;
10992 let left_padding = total_padding / 2;
10993 let right_padding = total_padding - left_padding;
10994 let left: String = std::iter::repeat(pad_char).take(left_padding).collect();
10995 let right: String = std::iter::repeat(pad_char).take(right_padding).collect();
10996 Ok(Value::String(Rc::new(format!("{}{}{}", left, s, right))))
10997 });
10998
10999 define(interp, "number_format", Some(1), |_, args| {
11001 let n = match &args[0] {
11002 Value::Int(n) => *n,
11003 Value::Float(f) => *f as i64,
11004 _ => {
11005 return Err(RuntimeError::new(
11006 "number_format: argument must be a number",
11007 ))
11008 }
11009 };
11010 let s = n.abs().to_string();
11011 let mut result = String::new();
11012 for (i, c) in s.chars().rev().enumerate() {
11013 if i > 0 && i % 3 == 0 {
11014 result.push(',');
11015 }
11016 result.push(c);
11017 }
11018 let formatted: String = result.chars().rev().collect();
11019 if n < 0 {
11020 Ok(Value::String(Rc::new(format!("-{}", formatted))))
11021 } else {
11022 Ok(Value::String(Rc::new(formatted)))
11023 }
11024 });
11025
11026 define(interp, "ordinal", Some(1), |_, args| {
11028 let n = match &args[0] {
11029 Value::Int(n) => *n,
11030 _ => return Err(RuntimeError::new("ordinal: argument must be an integer")),
11031 };
11032 let suffix = match (n % 10, n % 100) {
11033 (1, 11) => "th",
11034 (2, 12) => "th",
11035 (3, 13) => "th",
11036 (1, _) => "st",
11037 (2, _) => "nd",
11038 (3, _) => "rd",
11039 _ => "th",
11040 };
11041 Ok(Value::String(Rc::new(format!("{}{}", n, suffix))))
11042 });
11043
11044 define(interp, "pluralize", Some(3), |_, args| {
11046 let count = match &args[0] {
11047 Value::Int(n) => *n,
11048 _ => {
11049 return Err(RuntimeError::new(
11050 "pluralize: first argument must be an integer",
11051 ))
11052 }
11053 };
11054 let singular = match &args[1] {
11055 Value::String(s) => s.clone(),
11056 _ => {
11057 return Err(RuntimeError::new(
11058 "pluralize: second argument must be a string",
11059 ))
11060 }
11061 };
11062 let plural = match &args[2] {
11063 Value::String(s) => s.clone(),
11064 _ => {
11065 return Err(RuntimeError::new(
11066 "pluralize: third argument must be a string",
11067 ))
11068 }
11069 };
11070 if count == 1 || count == -1 {
11071 Ok(Value::String(singular))
11072 } else {
11073 Ok(Value::String(plural))
11074 }
11075 });
11076
11077 define(interp, "truncate", Some(2), |_, args| {
11079 let s = match &args[0] {
11080 Value::String(s) => (**s).clone(),
11081 _ => {
11082 return Err(RuntimeError::new(
11083 "truncate: first argument must be a string",
11084 ))
11085 }
11086 };
11087 let max_len = match &args[1] {
11088 Value::Int(n) if *n >= 0 => *n as usize,
11089 _ => {
11090 return Err(RuntimeError::new(
11091 "truncate: max length must be a non-negative integer",
11092 ))
11093 }
11094 };
11095 let char_count = s.chars().count();
11096 if char_count <= max_len {
11097 return Ok(Value::String(Rc::new(s)));
11098 }
11099 if max_len <= 3 {
11100 return Ok(Value::String(Rc::new(s.chars().take(max_len).collect())));
11101 }
11102 let truncated: String = s.chars().take(max_len - 3).collect();
11103 Ok(Value::String(Rc::new(format!("{}...", truncated))))
11104 });
11105
11106 define(interp, "word_wrap", Some(2), |_, args| {
11108 let s = match &args[0] {
11109 Value::String(s) => (**s).clone(),
11110 _ => {
11111 return Err(RuntimeError::new(
11112 "word_wrap: first argument must be a string",
11113 ))
11114 }
11115 };
11116 let width = match &args[1] {
11117 Value::Int(n) if *n > 0 => *n as usize,
11118 _ => {
11119 return Err(RuntimeError::new(
11120 "word_wrap: width must be a positive integer",
11121 ))
11122 }
11123 };
11124 let mut result = String::new();
11125 let mut line_len = 0;
11126 for word in s.split_whitespace() {
11127 if line_len > 0 && line_len + 1 + word.len() > width {
11128 result.push('\n');
11129 line_len = 0;
11130 } else if line_len > 0 {
11131 result.push(' ');
11132 line_len += 1;
11133 }
11134 result.push_str(word);
11135 line_len += word.len();
11136 }
11137 Ok(Value::String(Rc::new(result)))
11138 });
11139
11140 define(interp, "snake_case", Some(1), |_, args| {
11142 let s = match &args[0] {
11143 Value::String(s) => (**s).clone(),
11144 _ => return Err(RuntimeError::new("snake_case: argument must be a string")),
11145 };
11146 let mut result = String::new();
11147 for (i, c) in s.chars().enumerate() {
11148 if c.is_uppercase() {
11149 if i > 0 {
11150 result.push('_');
11151 }
11152 result.push(c.to_lowercase().next().unwrap());
11153 } else if c == ' ' || c == '-' {
11154 result.push('_');
11155 } else {
11156 result.push(c);
11157 }
11158 }
11159 Ok(Value::String(Rc::new(result)))
11160 });
11161
11162 define(interp, "camel_case", Some(1), |_, args| {
11164 let s = match &args[0] {
11165 Value::String(s) => (**s).clone(),
11166 _ => return Err(RuntimeError::new("camel_case: argument must be a string")),
11167 };
11168 let mut result = String::new();
11169 let mut capitalize_next = false;
11170 for (i, c) in s.chars().enumerate() {
11171 if c == '_' || c == '-' || c == ' ' {
11172 capitalize_next = true;
11173 } else if capitalize_next {
11174 result.push(c.to_uppercase().next().unwrap());
11175 capitalize_next = false;
11176 } else if i == 0 {
11177 result.push(c.to_lowercase().next().unwrap());
11178 } else {
11179 result.push(c);
11180 }
11181 }
11182 Ok(Value::String(Rc::new(result)))
11183 });
11184
11185 define(interp, "kebab_case", Some(1), |_, args| {
11187 let s = match &args[0] {
11188 Value::String(s) => (**s).clone(),
11189 _ => return Err(RuntimeError::new("kebab_case: argument must be a string")),
11190 };
11191 let mut result = String::new();
11192 for (i, c) in s.chars().enumerate() {
11193 if c.is_uppercase() {
11194 if i > 0 {
11195 result.push('-');
11196 }
11197 result.push(c.to_lowercase().next().unwrap());
11198 } else if c == '_' || c == ' ' {
11199 result.push('-');
11200 } else {
11201 result.push(c);
11202 }
11203 }
11204 Ok(Value::String(Rc::new(result)))
11205 });
11206
11207 define(interp, "title_case", Some(1), |_, args| {
11209 let s = match &args[0] {
11210 Value::String(s) => (**s).clone(),
11211 _ => return Err(RuntimeError::new("title_case: argument must be a string")),
11212 };
11213 let result: String = s
11214 .split_whitespace()
11215 .map(|word| {
11216 let mut chars = word.chars();
11217 match chars.next() {
11218 None => String::new(),
11219 Some(first) => {
11220 first.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase()
11221 }
11222 }
11223 })
11224 .collect::<Vec<_>>()
11225 .join(" ");
11226 Ok(Value::String(Rc::new(result)))
11227 });
11228}
11229
11230fn register_pattern(interp: &mut Interpreter) {
11238 define(interp, "type_of", Some(1), |_, args| {
11242 let type_name = match &args[0] {
11243 Value::Null => "null",
11244 Value::Bool(_) => "bool",
11245 Value::Int(_) => "int",
11246 Value::Float(_) => "float",
11247 Value::String(_) => "string",
11248 Value::Char(_) => "char",
11249 Value::Array(_) => "array",
11250 Value::Tuple(_) => "tuple",
11251 Value::Map(_) => "map",
11252 Value::Set(_) => "set",
11253 Value::Struct { name, .. } => {
11254 return Ok(Value::String(Rc::new(format!("struct:{}", name))))
11255 }
11256 Value::Variant {
11257 enum_name,
11258 variant_name,
11259 ..
11260 } => {
11261 return Ok(Value::String(Rc::new(format!(
11262 "{}::{}",
11263 enum_name, variant_name
11264 ))))
11265 }
11266 Value::Function(_) => "function",
11267 Value::BuiltIn(_) => "builtin",
11268 Value::Ref(_) => "ref",
11269 Value::Infinity => "infinity",
11270 Value::Empty => "empty",
11271 Value::Evidential { .. } => "evidential",
11272 Value::Affective { .. } => "affective",
11273 Value::Channel(_) => "channel",
11274 Value::ThreadHandle(_) => "thread",
11275 Value::Actor(_) => "actor",
11276 Value::Future(_) => "future",
11277 Value::VariantConstructor { .. } => "variant_constructor",
11278 Value::DefaultConstructor { .. } => "default_constructor",
11279 Value::Range { .. } => "range",
11280 };
11281 Ok(Value::String(Rc::new(type_name.to_string())))
11282 });
11283
11284 define(interp, "is_type", Some(2), |_, args| {
11286 let type_name = match &args[1] {
11287 Value::String(s) => s.to_lowercase(),
11288 _ => {
11289 return Err(RuntimeError::new(
11290 "is_type: second argument must be type name string",
11291 ))
11292 }
11293 };
11294 let matches = match (&args[0], type_name.as_str()) {
11295 (Value::Null, "null") => true,
11296 (Value::Bool(_), "bool") => true,
11297 (Value::Int(_), "int") | (Value::Int(_), "integer") => true,
11298 (Value::Float(_), "float") | (Value::Float(_), "number") => true,
11299 (Value::Int(_), "number") => true,
11300 (Value::String(_), "string") => true,
11301 (Value::Array(_), "array") | (Value::Array(_), "list") => true,
11302 (Value::Tuple(_), "tuple") => true,
11303 (Value::Map(_), "map") | (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
11304 (Value::Set(_), "set") => true,
11305 (Value::Function(_), "function") | (Value::Function(_), "fn") => true,
11306 (Value::BuiltIn(_), "function") | (Value::BuiltIn(_), "builtin") => true,
11307 (Value::Struct { name, .. }, t) => t == "struct" || t == &name.to_lowercase(),
11308 (Value::Variant { enum_name, .. }, t) => {
11309 t == "variant" || t == "enum" || t == &enum_name.to_lowercase()
11310 }
11311 (Value::Channel(_), "channel") => true,
11312 (Value::ThreadHandle(_), "thread") => true,
11313 (Value::Actor(_), "actor") => true,
11314 (Value::Future(_), "future") => true,
11315 _ => false,
11316 };
11317 Ok(Value::Bool(matches))
11318 });
11319
11320 define(interp, "is_null", Some(1), |_, args| {
11322 Ok(Value::Bool(matches!(&args[0], Value::Null)))
11323 });
11324 define(interp, "is_bool", Some(1), |_, args| {
11325 Ok(Value::Bool(matches!(&args[0], Value::Bool(_))))
11326 });
11327 define(interp, "is_int", Some(1), |_, args| {
11328 Ok(Value::Bool(matches!(&args[0], Value::Int(_))))
11329 });
11330 define(interp, "is_float", Some(1), |_, args| {
11331 Ok(Value::Bool(matches!(&args[0], Value::Float(_))))
11332 });
11333 define(interp, "is_number", Some(1), |_, args| {
11334 Ok(Value::Bool(matches!(
11335 &args[0],
11336 Value::Int(_) | Value::Float(_)
11337 )))
11338 });
11339 define(interp, "is_string", Some(1), |_, args| {
11340 Ok(Value::Bool(matches!(&args[0], Value::String(_))))
11341 });
11342 define(interp, "is_array", Some(1), |_, args| {
11343 Ok(Value::Bool(matches!(&args[0], Value::Array(_))))
11344 });
11345 define(interp, "is_tuple", Some(1), |_, args| {
11346 Ok(Value::Bool(matches!(&args[0], Value::Tuple(_))))
11347 });
11348 define(interp, "is_map", Some(1), |_, args| {
11349 Ok(Value::Bool(matches!(&args[0], Value::Map(_))))
11350 });
11351 define(interp, "is_set", Some(1), |_, args| {
11352 Ok(Value::Bool(matches!(&args[0], Value::Set(_))))
11353 });
11354 define(interp, "is_function", Some(1), |_, args| {
11355 Ok(Value::Bool(matches!(
11356 &args[0],
11357 Value::Function(_) | Value::BuiltIn(_)
11358 )))
11359 });
11360 define(interp, "is_struct", Some(1), |_, args| {
11361 Ok(Value::Bool(matches!(&args[0], Value::Struct { .. })))
11362 });
11363 define(interp, "is_variant", Some(1), |_, args| {
11364 Ok(Value::Bool(matches!(&args[0], Value::Variant { .. })))
11365 });
11366 define(interp, "is_future", Some(1), |_, args| {
11367 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
11368 });
11369 define(interp, "is_channel", Some(1), |_, args| {
11370 Ok(Value::Bool(matches!(&args[0], Value::Channel(_))))
11371 });
11372
11373 define(interp, "is_empty", Some(1), |_, args| {
11375 let empty = match &args[0] {
11376 Value::Null => true,
11377 Value::String(s) => s.is_empty(),
11378 Value::Array(a) => a.borrow().is_empty(),
11379 Value::Tuple(t) => t.is_empty(),
11380 Value::Map(m) => m.borrow().is_empty(),
11381 Value::Set(s) => s.borrow().is_empty(),
11382 _ => false,
11383 };
11384 Ok(Value::Bool(empty))
11385 });
11386
11387 define(interp, "match_regex", Some(2), |_, args| {
11391 let text = match &args[0] {
11392 Value::String(s) => (**s).clone(),
11393 _ => {
11394 return Err(RuntimeError::new(
11395 "match_regex: first argument must be a string",
11396 ))
11397 }
11398 };
11399 let pattern = match &args[1] {
11400 Value::String(s) => (**s).clone(),
11401 _ => {
11402 return Err(RuntimeError::new(
11403 "match_regex: second argument must be a regex pattern string",
11404 ))
11405 }
11406 };
11407
11408 let re = match Regex::new(&pattern) {
11409 Ok(r) => r,
11410 Err(e) => {
11411 return Err(RuntimeError::new(format!(
11412 "match_regex: invalid regex: {}",
11413 e
11414 )))
11415 }
11416 };
11417
11418 match re.captures(&text) {
11419 Some(caps) => {
11420 let mut captures: Vec<Value> = Vec::new();
11421 for i in 0..caps.len() {
11422 if let Some(m) = caps.get(i) {
11423 captures.push(Value::String(Rc::new(m.as_str().to_string())));
11424 } else {
11425 captures.push(Value::Null);
11426 }
11427 }
11428 Ok(Value::Array(Rc::new(RefCell::new(captures))))
11429 }
11430 None => Ok(Value::Null),
11431 }
11432 });
11433
11434 define(interp, "match_all_regex", Some(2), |_, args| {
11436 let text = match &args[0] {
11437 Value::String(s) => (**s).clone(),
11438 _ => {
11439 return Err(RuntimeError::new(
11440 "match_all_regex: first argument must be a string",
11441 ))
11442 }
11443 };
11444 let pattern = match &args[1] {
11445 Value::String(s) => (**s).clone(),
11446 _ => {
11447 return Err(RuntimeError::new(
11448 "match_all_regex: second argument must be a regex pattern string",
11449 ))
11450 }
11451 };
11452
11453 let re = match Regex::new(&pattern) {
11454 Ok(r) => r,
11455 Err(e) => {
11456 return Err(RuntimeError::new(format!(
11457 "match_all_regex: invalid regex: {}",
11458 e
11459 )))
11460 }
11461 };
11462
11463 let matches: Vec<Value> = re
11464 .find_iter(&text)
11465 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
11466 .collect();
11467 Ok(Value::Array(Rc::new(RefCell::new(matches))))
11468 });
11469
11470 define(interp, "capture_named", Some(2), |_, args| {
11472 let text = match &args[0] {
11473 Value::String(s) => (**s).clone(),
11474 _ => {
11475 return Err(RuntimeError::new(
11476 "capture_named: first argument must be a string",
11477 ))
11478 }
11479 };
11480 let pattern = match &args[1] {
11481 Value::String(s) => (**s).clone(),
11482 _ => {
11483 return Err(RuntimeError::new(
11484 "capture_named: second argument must be a regex pattern string",
11485 ))
11486 }
11487 };
11488
11489 let re = match Regex::new(&pattern) {
11490 Ok(r) => r,
11491 Err(e) => {
11492 return Err(RuntimeError::new(format!(
11493 "capture_named: invalid regex: {}",
11494 e
11495 )))
11496 }
11497 };
11498
11499 match re.captures(&text) {
11500 Some(caps) => {
11501 let mut result: HashMap<String, Value> = HashMap::new();
11502 for name in re.capture_names().flatten() {
11503 if let Some(m) = caps.name(name) {
11504 result.insert(
11505 name.to_string(),
11506 Value::String(Rc::new(m.as_str().to_string())),
11507 );
11508 }
11509 }
11510 Ok(Value::Map(Rc::new(RefCell::new(result))))
11511 }
11512 None => Ok(Value::Null),
11513 }
11514 });
11515
11516 define(interp, "match_struct", Some(2), |_, args| {
11520 let expected_name = match &args[1] {
11521 Value::String(s) => (**s).clone(),
11522 _ => {
11523 return Err(RuntimeError::new(
11524 "match_struct: second argument must be struct name string",
11525 ))
11526 }
11527 };
11528 match &args[0] {
11529 Value::Struct { name, .. } => Ok(Value::Bool(name == &expected_name)),
11530 _ => Ok(Value::Bool(false)),
11531 }
11532 });
11533
11534 define(interp, "match_variant", Some(3), |_, args| {
11536 let expected_enum = match &args[1] {
11537 Value::String(s) => (**s).clone(),
11538 _ => {
11539 return Err(RuntimeError::new(
11540 "match_variant: second argument must be enum name string",
11541 ))
11542 }
11543 };
11544 let expected_variant = match &args[2] {
11545 Value::String(s) => (**s).clone(),
11546 _ => {
11547 return Err(RuntimeError::new(
11548 "match_variant: third argument must be variant name string",
11549 ))
11550 }
11551 };
11552 match &args[0] {
11553 Value::Variant {
11554 enum_name,
11555 variant_name,
11556 ..
11557 } => Ok(Value::Bool(
11558 enum_name == &expected_enum && variant_name == &expected_variant,
11559 )),
11560 _ => Ok(Value::Bool(false)),
11561 }
11562 });
11563
11564 define(interp, "get_field", Some(2), |_, args| {
11566 let field_name = match &args[1] {
11567 Value::String(s) => (**s).clone(),
11568 _ => {
11569 return Err(RuntimeError::new(
11570 "get_field: second argument must be field name string",
11571 ))
11572 }
11573 };
11574 match &args[0] {
11575 Value::Struct { fields, .. } => Ok(fields
11576 .borrow()
11577 .get(&field_name)
11578 .cloned()
11579 .unwrap_or(Value::Null)),
11580 Value::Map(m) => Ok(m.borrow().get(&field_name).cloned().unwrap_or(Value::Null)),
11581 _ => Ok(Value::Null),
11582 }
11583 });
11584
11585 define(interp, "has_field", Some(2), |_, args| {
11587 let field_name = match &args[1] {
11588 Value::String(s) => (**s).clone(),
11589 _ => {
11590 return Err(RuntimeError::new(
11591 "has_field: second argument must be field name string",
11592 ))
11593 }
11594 };
11595 match &args[0] {
11596 Value::Struct { fields, .. } => {
11597 Ok(Value::Bool(fields.borrow().contains_key(&field_name)))
11598 }
11599 Value::Map(m) => Ok(Value::Bool(m.borrow().contains_key(&field_name))),
11600 _ => Ok(Value::Bool(false)),
11601 }
11602 });
11603
11604 define(interp, "get_fields", Some(1), |_, args| {
11606 let fields: Vec<Value> = match &args[0] {
11607 Value::Struct { fields, .. } => fields
11608 .borrow()
11609 .keys()
11610 .map(|k| Value::String(Rc::new(k.clone())))
11611 .collect(),
11612 Value::Map(m) => m
11613 .borrow()
11614 .keys()
11615 .map(|k| Value::String(Rc::new(k.clone())))
11616 .collect(),
11617 _ => {
11618 return Err(RuntimeError::new(
11619 "get_fields: argument must be struct or map",
11620 ))
11621 }
11622 };
11623 Ok(Value::Array(Rc::new(RefCell::new(fields))))
11624 });
11625
11626 define(interp, "struct_name", Some(1), |_, args| match &args[0] {
11628 Value::Struct { name, .. } => Ok(Value::String(Rc::new(name.clone()))),
11629 _ => Ok(Value::Null),
11630 });
11631
11632 define(interp, "variant_name", Some(1), |_, args| match &args[0] {
11634 Value::Variant { variant_name, .. } => Ok(Value::String(Rc::new(variant_name.clone()))),
11635 _ => Ok(Value::Null),
11636 });
11637
11638 define(interp, "variant_data", Some(1), |_, args| match &args[0] {
11640 Value::Variant { fields, .. } => match fields {
11641 Some(f) => Ok(Value::Array(Rc::new(RefCell::new((**f).clone())))),
11642 None => Ok(Value::Null),
11643 },
11644 _ => Ok(Value::Null),
11645 });
11646
11647 define(interp, "guard", Some(2), |_, args| {
11651 if is_truthy(&args[0]) {
11652 Ok(args[1].clone())
11653 } else {
11654 Ok(Value::Null)
11655 }
11656 });
11657
11658 define(interp, "when", Some(2), |interp, args| {
11660 if is_truthy(&args[0]) {
11661 match &args[1] {
11662 Value::Function(f) => interp.call_function(f, vec![]),
11663 other => Ok(other.clone()),
11664 }
11665 } else {
11666 Ok(Value::Null)
11667 }
11668 });
11669
11670 define(interp, "unless", Some(2), |interp, args| {
11672 if !is_truthy(&args[0]) {
11673 match &args[1] {
11674 Value::Function(f) => interp.call_function(f, vec![]),
11675 other => Ok(other.clone()),
11676 }
11677 } else {
11678 Ok(Value::Null)
11679 }
11680 });
11681
11682 define(interp, "cond", Some(1), |interp, args| {
11685 let clauses = match &args[0] {
11686 Value::Array(a) => a.borrow().clone(),
11687 _ => {
11688 return Err(RuntimeError::new(
11689 "cond: argument must be array of [condition, value] pairs",
11690 ))
11691 }
11692 };
11693
11694 for clause in clauses {
11695 let pair = match &clause {
11696 Value::Array(a) => a.borrow().clone(),
11697 Value::Tuple(t) => (**t).clone(),
11698 _ => {
11699 return Err(RuntimeError::new(
11700 "cond: each clause must be [condition, value] pair",
11701 ))
11702 }
11703 };
11704 if pair.len() != 2 {
11705 return Err(RuntimeError::new(
11706 "cond: each clause must have exactly 2 elements",
11707 ));
11708 }
11709
11710 if is_truthy(&pair[0]) {
11711 return match &pair[1] {
11712 Value::Function(f) => interp.call_function(f, vec![]),
11713 other => Ok(other.clone()),
11714 };
11715 }
11716 }
11717 Ok(Value::Null)
11718 });
11719
11720 define(interp, "case", Some(2), |interp, args| {
11723 let value = &args[0];
11724 let clauses = match &args[1] {
11725 Value::Array(a) => a.borrow().clone(),
11726 _ => {
11727 return Err(RuntimeError::new(
11728 "case: second argument must be array of [pattern, result] pairs",
11729 ))
11730 }
11731 };
11732
11733 for clause in clauses {
11734 let pair = match &clause {
11735 Value::Array(a) => a.borrow().clone(),
11736 Value::Tuple(t) => (**t).clone(),
11737 _ => {
11738 return Err(RuntimeError::new(
11739 "case: each clause must be [pattern, result] pair",
11740 ))
11741 }
11742 };
11743 if pair.len() != 2 {
11744 return Err(RuntimeError::new(
11745 "case: each clause must have exactly 2 elements",
11746 ));
11747 }
11748
11749 if value_eq(value, &pair[0]) {
11750 return match &pair[1] {
11751 Value::Function(f) => interp.call_function(f, vec![value.clone()]),
11752 other => Ok(other.clone()),
11753 };
11754 }
11755 }
11756 Ok(Value::Null)
11757 });
11758
11759 define(interp, "destructure_array", Some(2), |_, args| {
11763 let arr = match &args[0] {
11764 Value::Array(a) => a.borrow().clone(),
11765 Value::Tuple(t) => (**t).clone(),
11766 _ => {
11767 return Err(RuntimeError::new(
11768 "destructure_array: first argument must be array or tuple",
11769 ))
11770 }
11771 };
11772 let indices = match &args[1] {
11773 Value::Array(a) => a.borrow().clone(),
11774 _ => {
11775 return Err(RuntimeError::new(
11776 "destructure_array: second argument must be array of indices",
11777 ))
11778 }
11779 };
11780
11781 let mut result = Vec::new();
11782 for idx in indices {
11783 match idx {
11784 Value::Int(i) => {
11785 let i = if i < 0 { arr.len() as i64 + i } else { i } as usize;
11786 result.push(arr.get(i).cloned().unwrap_or(Value::Null));
11787 }
11788 _ => result.push(Value::Null),
11789 }
11790 }
11791 Ok(Value::Array(Rc::new(RefCell::new(result))))
11792 });
11793
11794 define(interp, "destructure_map", Some(2), |_, args| {
11796 let map = match &args[0] {
11797 Value::Map(m) => m.borrow().clone(),
11798 Value::Struct { fields, .. } => fields.borrow().clone(),
11799 _ => {
11800 return Err(RuntimeError::new(
11801 "destructure_map: first argument must be map or struct",
11802 ))
11803 }
11804 };
11805 let keys = match &args[1] {
11806 Value::Array(a) => a.borrow().clone(),
11807 _ => {
11808 return Err(RuntimeError::new(
11809 "destructure_map: second argument must be array of keys",
11810 ))
11811 }
11812 };
11813
11814 let mut result = Vec::new();
11815 for key in keys {
11816 match key {
11817 Value::String(k) => {
11818 result.push(map.get(&*k).cloned().unwrap_or(Value::Null));
11819 }
11820 _ => result.push(Value::Null),
11821 }
11822 }
11823 Ok(Value::Array(Rc::new(RefCell::new(result))))
11824 });
11825
11826 define(interp, "head_tail", Some(1), |_, args| {
11828 let arr = match &args[0] {
11829 Value::Array(a) => a.borrow().clone(),
11830 _ => return Err(RuntimeError::new("head_tail: argument must be array")),
11831 };
11832
11833 if arr.is_empty() {
11834 Ok(Value::Tuple(Rc::new(vec![
11835 Value::Null,
11836 Value::Array(Rc::new(RefCell::new(vec![]))),
11837 ])))
11838 } else {
11839 let head = arr[0].clone();
11840 let tail = arr[1..].to_vec();
11841 Ok(Value::Tuple(Rc::new(vec![
11842 head,
11843 Value::Array(Rc::new(RefCell::new(tail))),
11844 ])))
11845 }
11846 });
11847
11848 define(interp, "init_last", Some(1), |_, args| {
11850 let arr = match &args[0] {
11851 Value::Array(a) => a.borrow().clone(),
11852 _ => return Err(RuntimeError::new("init_last: argument must be array")),
11853 };
11854
11855 if arr.is_empty() {
11856 Ok(Value::Tuple(Rc::new(vec![
11857 Value::Array(Rc::new(RefCell::new(vec![]))),
11858 Value::Null,
11859 ])))
11860 } else {
11861 let last = arr[arr.len() - 1].clone();
11862 let init = arr[..arr.len() - 1].to_vec();
11863 Ok(Value::Tuple(Rc::new(vec![
11864 Value::Array(Rc::new(RefCell::new(init))),
11865 last,
11866 ])))
11867 }
11868 });
11869
11870 define(interp, "split_at", Some(2), |_, args| {
11872 let arr = match &args[0] {
11873 Value::Array(a) => a.borrow().clone(),
11874 _ => return Err(RuntimeError::new("split_at: first argument must be array")),
11875 };
11876 let idx = match &args[1] {
11877 Value::Int(i) => *i as usize,
11878 _ => {
11879 return Err(RuntimeError::new(
11880 "split_at: second argument must be integer",
11881 ))
11882 }
11883 };
11884
11885 let idx = idx.min(arr.len());
11886 let left = arr[..idx].to_vec();
11887 let right = arr[idx..].to_vec();
11888 Ok(Value::Tuple(Rc::new(vec![
11889 Value::Array(Rc::new(RefCell::new(left))),
11890 Value::Array(Rc::new(RefCell::new(right))),
11891 ])))
11892 });
11893
11894 define(interp, "unwrap_or", Some(2), |_, args| {
11898 if matches!(&args[0], Value::Null) {
11899 Ok(args[1].clone())
11900 } else {
11901 Ok(args[0].clone())
11902 }
11903 });
11904
11905 define(interp, "unwrap_or_else", Some(2), |interp, args| {
11907 if matches!(&args[0], Value::Null) {
11908 match &args[1] {
11909 Value::Function(f) => interp.call_function(f, vec![]),
11910 other => Ok(other.clone()),
11911 }
11912 } else {
11913 Ok(args[0].clone())
11914 }
11915 });
11916
11917 define(interp, "map_or", Some(3), |interp, args| {
11919 if matches!(&args[0], Value::Null) {
11920 Ok(args[1].clone())
11921 } else {
11922 match &args[2] {
11923 Value::Function(f) => interp.call_function(f, vec![args[0].clone()]),
11924 _ => Err(RuntimeError::new(
11925 "map_or: third argument must be a function",
11926 )),
11927 }
11928 }
11929 });
11930
11931 define(interp, "coalesce", Some(1), |_, args| {
11933 let values = match &args[0] {
11934 Value::Array(a) => a.borrow().clone(),
11935 _ => return Err(RuntimeError::new("coalesce: argument must be array")),
11936 };
11937
11938 for v in values {
11939 if !matches!(v, Value::Null) {
11940 return Ok(v);
11941 }
11942 }
11943 Ok(Value::Null)
11944 });
11945
11946 define(interp, "deep_eq", Some(2), |_, args| {
11950 Ok(Value::Bool(deep_value_eq(&args[0], &args[1])))
11951 });
11952
11953 define(interp, "same_type", Some(2), |_, args| {
11955 let same = match (&args[0], &args[1]) {
11956 (Value::Null, Value::Null) => true,
11957 (Value::Bool(_), Value::Bool(_)) => true,
11958 (Value::Int(_), Value::Int(_)) => true,
11959 (Value::Float(_), Value::Float(_)) => true,
11960 (Value::String(_), Value::String(_)) => true,
11961 (Value::Array(_), Value::Array(_)) => true,
11962 (Value::Tuple(_), Value::Tuple(_)) => true,
11963 (Value::Map(_), Value::Map(_)) => true,
11964 (Value::Set(_), Value::Set(_)) => true,
11965 (Value::Function(_), Value::Function(_)) => true,
11966 (Value::BuiltIn(_), Value::BuiltIn(_)) => true,
11967 (Value::Struct { name: n1, .. }, Value::Struct { name: n2, .. }) => n1 == n2,
11968 (Value::Variant { enum_name: e1, .. }, Value::Variant { enum_name: e2, .. }) => {
11969 e1 == e2
11970 }
11971 _ => false,
11972 };
11973 Ok(Value::Bool(same))
11974 });
11975
11976 define(interp, "compare", Some(2), |_, args| {
11978 let cmp = match (&args[0], &args[1]) {
11979 (Value::Int(a), Value::Int(b)) => a.cmp(b),
11980 (Value::Float(a), Value::Float(b)) => {
11981 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
11982 }
11983 (Value::Int(a), Value::Float(b)) => (*a as f64)
11984 .partial_cmp(b)
11985 .unwrap_or(std::cmp::Ordering::Equal),
11986 (Value::Float(a), Value::Int(b)) => a
11987 .partial_cmp(&(*b as f64))
11988 .unwrap_or(std::cmp::Ordering::Equal),
11989 (Value::String(a), Value::String(b)) => a.cmp(b),
11990 _ => {
11991 return Err(RuntimeError::new(
11992 "compare: can only compare numbers or strings",
11993 ))
11994 }
11995 };
11996 Ok(Value::Int(match cmp {
11997 std::cmp::Ordering::Less => -1,
11998 std::cmp::Ordering::Equal => 0,
11999 std::cmp::Ordering::Greater => 1,
12000 }))
12001 });
12002
12003 define(interp, "between", Some(3), |_, args| {
12005 let in_range = match (&args[0], &args[1], &args[2]) {
12006 (Value::Int(v), Value::Int(min), Value::Int(max)) => v >= min && v <= max,
12007 (Value::Float(v), Value::Float(min), Value::Float(max)) => v >= min && v <= max,
12008 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
12009 (*v as f64) >= (*min as f64) && (*v as f64) <= *max
12010 }
12011 (Value::Int(v), Value::Float(min), Value::Int(max)) => {
12012 (*v as f64) >= *min && (*v as f64) <= (*max as f64)
12013 }
12014 (Value::Float(v), Value::Int(min), Value::Int(max)) => {
12015 *v >= (*min as f64) && *v <= (*max as f64)
12016 }
12017 (Value::String(v), Value::String(min), Value::String(max)) => v >= min && v <= max,
12018 _ => {
12019 return Err(RuntimeError::new(
12020 "between: arguments must be comparable (numbers or strings)",
12021 ))
12022 }
12023 };
12024 Ok(Value::Bool(in_range))
12025 });
12026
12027 define(interp, "clamp", Some(3), |_, args| {
12029 match (&args[0], &args[1], &args[2]) {
12030 (Value::Int(v), Value::Int(min), Value::Int(max)) => {
12031 Ok(Value::Int((*v).max(*min).min(*max)))
12032 }
12033 (Value::Float(v), Value::Float(min), Value::Float(max)) => {
12034 Ok(Value::Float(v.max(*min).min(*max)))
12035 }
12036 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
12037 Ok(Value::Float((*v as f64).max(*min as f64).min(*max)))
12038 }
12039 _ => Err(RuntimeError::new("clamp: arguments must be numbers")),
12040 }
12041 });
12042}
12043
12044fn deep_value_eq(a: &Value, b: &Value) -> bool {
12046 match (a, b) {
12047 (Value::Null, Value::Null) => true,
12048 (Value::Bool(a), Value::Bool(b)) => a == b,
12049 (Value::Int(a), Value::Int(b)) => a == b,
12050 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
12051 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
12052 (*a as f64 - b).abs() < f64::EPSILON
12053 }
12054 (Value::String(a), Value::String(b)) => a == b,
12055 (Value::Array(a), Value::Array(b)) => {
12056 let a = a.borrow();
12057 let b = b.borrow();
12058 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
12059 }
12060 (Value::Tuple(a), Value::Tuple(b)) => {
12061 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
12062 }
12063 (Value::Map(a), Value::Map(b)) => {
12064 let a = a.borrow();
12065 let b = b.borrow();
12066 a.len() == b.len()
12067 && a.iter()
12068 .all(|(k, v)| b.get(k).map_or(false, |bv| deep_value_eq(v, bv)))
12069 }
12070 (Value::Set(a), Value::Set(b)) => {
12071 let a = a.borrow();
12072 let b = b.borrow();
12073 a.len() == b.len() && a.iter().all(|k| b.contains(k))
12074 }
12075 (
12076 Value::Struct {
12077 name: n1,
12078 fields: f1,
12079 },
12080 Value::Struct {
12081 name: n2,
12082 fields: f2,
12083 },
12084 ) => {
12085 let f1 = f1.borrow();
12086 let f2 = f2.borrow();
12087 n1 == n2
12088 && f1.len() == f2.len()
12089 && f1
12090 .iter()
12091 .all(|(k, v)| f2.get(k).map_or(false, |v2| deep_value_eq(v, v2)))
12092 }
12093 (
12094 Value::Variant {
12095 enum_name: e1,
12096 variant_name: v1,
12097 fields: d1,
12098 },
12099 Value::Variant {
12100 enum_name: e2,
12101 variant_name: v2,
12102 fields: d2,
12103 },
12104 ) => {
12105 if e1 != e2 || v1 != v2 {
12106 return false;
12107 }
12108 match (d1, d2) {
12109 (Some(f1), Some(f2)) => {
12110 f1.len() == f2.len()
12111 && f1.iter().zip(f2.iter()).all(|(x, y)| deep_value_eq(x, y))
12112 }
12113 (None, None) => true,
12114 _ => false,
12115 }
12116 }
12117 _ => false,
12118 }
12119}
12120
12121fn value_eq(a: &Value, b: &Value) -> bool {
12123 match (a, b) {
12124 (Value::Null, Value::Null) => true,
12125 (Value::Bool(a), Value::Bool(b)) => a == b,
12126 (Value::Int(a), Value::Int(b)) => a == b,
12127 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
12128 (Value::String(a), Value::String(b)) => a == b,
12129 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
12130 (*a as f64 - b).abs() < f64::EPSILON
12131 }
12132 _ => false,
12133 }
12134}
12135
12136fn register_devex(interp: &mut Interpreter) {
12144 define(interp, "debug", Some(1), |_, args| {
12148 let type_name = match &args[0] {
12149 Value::Null => "null".to_string(),
12150 Value::Bool(_) => "bool".to_string(),
12151 Value::Int(_) => "int".to_string(),
12152 Value::Float(_) => "float".to_string(),
12153 Value::String(_) => "string".to_string(),
12154 Value::Char(_) => "char".to_string(),
12155 Value::Array(a) => format!("array[{}]", a.borrow().len()),
12156 Value::Tuple(t) => format!("tuple[{}]", t.len()),
12157 Value::Map(m) => format!("map[{}]", m.borrow().len()),
12158 Value::Set(s) => format!("set[{}]", s.borrow().len()),
12159 Value::Struct { name, fields } => format!("struct {}[{}]", name, fields.borrow().len()),
12160 Value::Variant {
12161 enum_name,
12162 variant_name,
12163 ..
12164 } => format!("{}::{}", enum_name, variant_name),
12165 Value::Function(_) => "function".to_string(),
12166 Value::BuiltIn(_) => "builtin".to_string(),
12167 Value::Ref(_) => "ref".to_string(),
12168 Value::Infinity => "infinity".to_string(),
12169 Value::Empty => "empty".to_string(),
12170 Value::Evidential { evidence, .. } => format!("evidential[{:?}]", evidence),
12171 Value::Affective { affect, .. } => format!("affective[sarcasm={}]", affect.sarcasm),
12172 Value::Channel(_) => "channel".to_string(),
12173 Value::ThreadHandle(_) => "thread".to_string(),
12174 Value::Actor(_) => "actor".to_string(),
12175 Value::Future(_) => "future".to_string(),
12176 Value::VariantConstructor {
12177 enum_name,
12178 variant_name,
12179 } => {
12180 format!("<constructor {}::{}>", enum_name, variant_name)
12181 }
12182 Value::DefaultConstructor { type_name } => {
12183 format!("<default {}>", type_name)
12184 }
12185 Value::Range {
12186 start,
12187 end,
12188 inclusive,
12189 } => match (start, end) {
12190 (Some(s), Some(e)) => {
12191 if *inclusive {
12192 format!("range({}..={})", s, e)
12193 } else {
12194 format!("range({}..{})", s, e)
12195 }
12196 }
12197 (Some(s), None) => format!("range({}..)", s),
12198 (None, Some(e)) => {
12199 if *inclusive {
12200 format!("range(..={})", e)
12201 } else {
12202 format!("range(..{})", e)
12203 }
12204 }
12205 (None, None) => "range(..)".to_string(),
12206 },
12207 };
12208 let value_repr = format_value_debug(&args[0]);
12209 println!("[DEBUG] {}: {}", type_name, value_repr);
12210 Ok(args[0].clone())
12211 });
12212
12213 define(interp, "inspect", Some(1), |_, args| {
12215 Ok(Value::String(Rc::new(format_value_debug(&args[0]))))
12216 });
12217
12218 define(interp, "dbg", Some(1), |_, args| {
12220 println!("{}", format_value_debug(&args[0]));
12221 Ok(args[0].clone())
12222 });
12223
12224 define(interp, "trace", Some(2), |_, args| {
12226 let label = match &args[0] {
12227 Value::String(s) => (**s).clone(),
12228 _ => format_value_debug(&args[0]),
12229 };
12230 println!("[TRACE] {}: {}", label, format_value_debug(&args[1]));
12231 Ok(args[1].clone())
12232 });
12233
12234 define(interp, "pp", Some(1), |_, args| {
12236 println!("{}", pretty_print_value(&args[0], 0));
12237 Ok(Value::Null)
12238 });
12239
12240 define(interp, "assert_eq", Some(2), |_, args| {
12244 if deep_value_eq(&args[0], &args[1]) {
12245 Ok(Value::Bool(true))
12246 } else {
12247 Err(RuntimeError::new(format!(
12248 "Assertion failed: expected {} to equal {}",
12249 format_value_debug(&args[0]),
12250 format_value_debug(&args[1])
12251 )))
12252 }
12253 });
12254
12255 define(interp, "assert_ne", Some(2), |_, args| {
12257 if !deep_value_eq(&args[0], &args[1]) {
12258 Ok(Value::Bool(true))
12259 } else {
12260 Err(RuntimeError::new(format!(
12261 "Assertion failed: expected {} to not equal {}",
12262 format_value_debug(&args[0]),
12263 format_value_debug(&args[1])
12264 )))
12265 }
12266 });
12267
12268 define(interp, "assert_lt", Some(2), |_, args| {
12270 let cmp = devex_compare(&args[0], &args[1])?;
12271 if cmp < 0 {
12272 Ok(Value::Bool(true))
12273 } else {
12274 Err(RuntimeError::new(format!(
12275 "Assertion failed: expected {} < {}",
12276 format_value_debug(&args[0]),
12277 format_value_debug(&args[1])
12278 )))
12279 }
12280 });
12281
12282 define(interp, "assert_le", Some(2), |_, args| {
12284 let cmp = devex_compare(&args[0], &args[1])?;
12285 if cmp <= 0 {
12286 Ok(Value::Bool(true))
12287 } else {
12288 Err(RuntimeError::new(format!(
12289 "Assertion failed: expected {} <= {}",
12290 format_value_debug(&args[0]),
12291 format_value_debug(&args[1])
12292 )))
12293 }
12294 });
12295
12296 define(interp, "assert_gt", Some(2), |_, args| {
12298 let cmp = devex_compare(&args[0], &args[1])?;
12299 if cmp > 0 {
12300 Ok(Value::Bool(true))
12301 } else {
12302 Err(RuntimeError::new(format!(
12303 "Assertion failed: expected {} > {}",
12304 format_value_debug(&args[0]),
12305 format_value_debug(&args[1])
12306 )))
12307 }
12308 });
12309
12310 define(interp, "assert_ge", Some(2), |_, args| {
12312 let cmp = devex_compare(&args[0], &args[1])?;
12313 if cmp >= 0 {
12314 Ok(Value::Bool(true))
12315 } else {
12316 Err(RuntimeError::new(format!(
12317 "Assertion failed: expected {} >= {}",
12318 format_value_debug(&args[0]),
12319 format_value_debug(&args[1])
12320 )))
12321 }
12322 });
12323
12324 define(interp, "assert_true", Some(1), |_, args| {
12326 if is_truthy(&args[0]) {
12327 Ok(Value::Bool(true))
12328 } else {
12329 Err(RuntimeError::new(format!(
12330 "Assertion failed: expected {} to be truthy",
12331 format_value_debug(&args[0])
12332 )))
12333 }
12334 });
12335
12336 define(interp, "assert_false", Some(1), |_, args| {
12338 if !is_truthy(&args[0]) {
12339 Ok(Value::Bool(true))
12340 } else {
12341 Err(RuntimeError::new(format!(
12342 "Assertion failed: expected {} to be falsy",
12343 format_value_debug(&args[0])
12344 )))
12345 }
12346 });
12347
12348 define(interp, "assert_null", Some(1), |_, args| {
12350 if matches!(&args[0], Value::Null) {
12351 Ok(Value::Bool(true))
12352 } else {
12353 Err(RuntimeError::new(format!(
12354 "Assertion failed: expected null, got {}",
12355 format_value_debug(&args[0])
12356 )))
12357 }
12358 });
12359
12360 define(interp, "assert_not_null", Some(1), |_, args| {
12362 if !matches!(&args[0], Value::Null) {
12363 Ok(Value::Bool(true))
12364 } else {
12365 Err(RuntimeError::new(
12366 "Assertion failed: expected non-null value, got null",
12367 ))
12368 }
12369 });
12370
12371 define(interp, "assert_type", Some(2), |_, args| {
12373 let expected = match &args[1] {
12374 Value::String(s) => s.to_lowercase(),
12375 _ => {
12376 return Err(RuntimeError::new(
12377 "assert_type: second argument must be type name string",
12378 ))
12379 }
12380 };
12381 let actual = get_type_name(&args[0]).to_lowercase();
12382 if actual == expected || matches_type_alias(&args[0], &expected) {
12383 Ok(Value::Bool(true))
12384 } else {
12385 Err(RuntimeError::new(format!(
12386 "Assertion failed: expected type '{}', got '{}'",
12387 expected, actual
12388 )))
12389 }
12390 });
12391
12392 define(interp, "assert_contains", Some(2), |_, args| {
12394 let contains = match &args[0] {
12395 Value::Array(a) => a.borrow().iter().any(|v| deep_value_eq(v, &args[1])),
12396 Value::String(s) => {
12397 if let Value::String(sub) = &args[1] {
12398 s.contains(&**sub)
12399 } else {
12400 false
12401 }
12402 }
12403 Value::Map(m) => {
12404 if let Value::String(k) = &args[1] {
12405 m.borrow().contains_key(&**k)
12406 } else {
12407 false
12408 }
12409 }
12410 Value::Set(s) => {
12411 if let Value::String(k) = &args[1] {
12412 s.borrow().contains(&**k)
12413 } else {
12414 false
12415 }
12416 }
12417 _ => false,
12418 };
12419 if contains {
12420 Ok(Value::Bool(true))
12421 } else {
12422 Err(RuntimeError::new(format!(
12423 "Assertion failed: {} does not contain {}",
12424 format_value_debug(&args[0]),
12425 format_value_debug(&args[1])
12426 )))
12427 }
12428 });
12429
12430 define(interp, "assert_len", Some(2), |_, args| {
12432 let expected = match &args[1] {
12433 Value::Int(n) => *n as usize,
12434 _ => {
12435 return Err(RuntimeError::new(
12436 "assert_len: second argument must be integer",
12437 ))
12438 }
12439 };
12440 let actual = match &args[0] {
12441 Value::String(s) => s.len(),
12442 Value::Array(a) => a.borrow().len(),
12443 Value::Tuple(t) => t.len(),
12444 Value::Map(m) => m.borrow().len(),
12445 Value::Set(s) => s.borrow().len(),
12446 _ => {
12447 return Err(RuntimeError::new(
12448 "assert_len: first argument must be a collection",
12449 ))
12450 }
12451 };
12452 if actual == expected {
12453 Ok(Value::Bool(true))
12454 } else {
12455 Err(RuntimeError::new(format!(
12456 "Assertion failed: expected length {}, got {}",
12457 expected, actual
12458 )))
12459 }
12460 });
12461
12462 define(interp, "assert_match", Some(2), |_, args| {
12464 let text = match &args[0] {
12465 Value::String(s) => (**s).clone(),
12466 _ => {
12467 return Err(RuntimeError::new(
12468 "assert_match: first argument must be string",
12469 ))
12470 }
12471 };
12472 let pattern = match &args[1] {
12473 Value::String(s) => (**s).clone(),
12474 _ => {
12475 return Err(RuntimeError::new(
12476 "assert_match: second argument must be regex pattern",
12477 ))
12478 }
12479 };
12480 let re =
12481 Regex::new(&pattern).map_err(|e| RuntimeError::new(format!("Invalid regex: {}", e)))?;
12482 if re.is_match(&text) {
12483 Ok(Value::Bool(true))
12484 } else {
12485 Err(RuntimeError::new(format!(
12486 "Assertion failed: '{}' does not match pattern '{}'",
12487 text, pattern
12488 )))
12489 }
12490 });
12491
12492 define(interp, "test", Some(2), |interp, args| {
12496 let name = match &args[0] {
12497 Value::String(s) => (**s).clone(),
12498 _ => {
12499 return Err(RuntimeError::new(
12500 "test: first argument must be test name string",
12501 ))
12502 }
12503 };
12504 let func = match &args[1] {
12505 Value::Function(f) => f.clone(),
12506 _ => {
12507 return Err(RuntimeError::new(
12508 "test: second argument must be test function",
12509 ))
12510 }
12511 };
12512
12513 let start = Instant::now();
12514 let result = interp.call_function(&func, vec![]);
12515 let elapsed = start.elapsed();
12516
12517 match result {
12518 Ok(_) => {
12519 println!("✓ {} ({:.2}ms)", name, elapsed.as_secs_f64() * 1000.0);
12520 Ok(Value::Bool(true))
12521 }
12522 Err(e) => {
12523 println!(
12524 "✗ {} ({:.2}ms): {}",
12525 name,
12526 elapsed.as_secs_f64() * 1000.0,
12527 e
12528 );
12529 Ok(Value::Bool(false))
12530 }
12531 }
12532 });
12533
12534 define(interp, "skip", Some(1), |_, args| {
12536 let reason = match &args[0] {
12537 Value::String(s) => (**s).clone(),
12538 _ => "skipped".to_string(),
12539 };
12540 println!("⊘ {}", reason);
12541 Ok(Value::Null)
12542 });
12543
12544 define(interp, "profile", Some(1), |interp, args| {
12548 let func = match &args[0] {
12549 Value::Function(f) => f.clone(),
12550 _ => return Err(RuntimeError::new("profile: argument must be function")),
12551 };
12552
12553 let start = Instant::now();
12554 let result = interp.call_function(&func, vec![])?;
12555 let elapsed = start.elapsed();
12556
12557 let mut timing = HashMap::new();
12558 timing.insert(
12559 "ms".to_string(),
12560 Value::Float(elapsed.as_secs_f64() * 1000.0),
12561 );
12562 timing.insert("us".to_string(), Value::Float(elapsed.as_micros() as f64));
12563 timing.insert("ns".to_string(), Value::Int(elapsed.as_nanos() as i64));
12564
12565 Ok(Value::Tuple(Rc::new(vec![
12566 result,
12567 Value::Map(Rc::new(RefCell::new(timing))),
12568 ])))
12569 });
12570
12571 define(interp, "measure", Some(2), |interp, args| {
12573 let func = match &args[0] {
12574 Value::Function(f) => f.clone(),
12575 _ => {
12576 return Err(RuntimeError::new(
12577 "measure: first argument must be function",
12578 ))
12579 }
12580 };
12581 let iterations = match &args[1] {
12582 Value::Int(n) => *n as usize,
12583 _ => {
12584 return Err(RuntimeError::new(
12585 "measure: second argument must be iteration count",
12586 ))
12587 }
12588 };
12589
12590 let mut times: Vec<f64> = Vec::new();
12591 let mut last_result = Value::Null;
12592
12593 for _ in 0..iterations {
12594 let start = Instant::now();
12595 last_result = interp.call_function(&func, vec![])?;
12596 times.push(start.elapsed().as_secs_f64() * 1000.0);
12597 }
12598
12599 let sum: f64 = times.iter().sum();
12600 let avg = sum / iterations as f64;
12601 let min = times.iter().cloned().fold(f64::INFINITY, f64::min);
12602 let max = times.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
12603
12604 let variance: f64 =
12605 times.iter().map(|t| (t - avg).powi(2)).sum::<f64>() / iterations as f64;
12606 let stddev = variance.sqrt();
12607
12608 let mut stats = HashMap::new();
12609 stats.insert("iterations".to_string(), Value::Int(iterations as i64));
12610 stats.insert("total_ms".to_string(), Value::Float(sum));
12611 stats.insert("avg_ms".to_string(), Value::Float(avg));
12612 stats.insert("min_ms".to_string(), Value::Float(min));
12613 stats.insert("max_ms".to_string(), Value::Float(max));
12614 stats.insert("stddev_ms".to_string(), Value::Float(stddev));
12615
12616 Ok(Value::Tuple(Rc::new(vec![
12617 last_result,
12618 Value::Map(Rc::new(RefCell::new(stats))),
12619 ])))
12620 });
12621
12622 define(interp, "help", Some(1), |_, args| {
12626 let name = match &args[0] {
12627 Value::String(s) => (**s).clone(),
12628 Value::BuiltIn(f) => f.name.clone(),
12629 _ => {
12630 return Err(RuntimeError::new(
12631 "help: argument must be function name or builtin",
12632 ))
12633 }
12634 };
12635
12636 let doc = get_function_doc(&name);
12638 Ok(Value::String(Rc::new(doc)))
12639 });
12640
12641 define(interp, "list_builtins", Some(0), |_, _| {
12643 let categories = vec![
12644 "Core: print, println, assert, panic, len, type_of",
12645 "Math: abs, floor, ceil, round, sqrt, pow, log, sin, cos, tan",
12646 "Collections: map, filter, reduce, zip, flatten, first, last, sort, reverse",
12647 "Strings: upper, lower, trim, split, join, contains, replace, format",
12648 "IO: read_file, write_file, file_exists, read_line",
12649 "Time: now, sleep, timestamp, format_time",
12650 "JSON: json_parse, json_stringify",
12651 "Crypto: sha256, sha512, md5, base64_encode, base64_decode",
12652 "Regex: regex_match, regex_replace, regex_split",
12653 "Pattern: type_of, is_type, match_regex, match_struct, guard, when",
12654 "DevEx: debug, inspect, trace, assert_eq, assert_ne, test, profile",
12655 ];
12656 let values: Vec<Value> = categories
12657 .iter()
12658 .map(|s| Value::String(Rc::new(s.to_string())))
12659 .collect();
12660 Ok(Value::Array(Rc::new(RefCell::new(values))))
12661 });
12662
12663 define(interp, "todo", Some(0), |_, _| {
12667 Err(RuntimeError::new("not yet implemented"))
12668 });
12669
12670 define(interp, "unreachable", Some(0), |_, _| {
12672 Err(RuntimeError::new("reached unreachable code"))
12673 });
12674
12675 define(interp, "unimplemented", Some(1), |_, args| {
12677 let msg = match &args[0] {
12678 Value::String(s) => (**s).clone(),
12679 _ => "unimplemented".to_string(),
12680 };
12681 Err(RuntimeError::new(format!("unimplemented: {}", msg)))
12682 });
12683
12684 define(interp, "deprecated", Some(2), |_, args| {
12686 let msg = match &args[0] {
12687 Value::String(s) => (**s).clone(),
12688 _ => "deprecated".to_string(),
12689 };
12690 eprintln!("[DEPRECATED] {}", msg);
12691 Ok(args[1].clone())
12692 });
12693
12694 define(interp, "version", Some(0), |_, _| {
12696 let mut info = HashMap::new();
12697 info.insert(
12698 "sigil".to_string(),
12699 Value::String(Rc::new("0.1.0".to_string())),
12700 );
12701 info.insert(
12702 "stdlib".to_string(),
12703 Value::String(Rc::new("7.0".to_string())),
12704 );
12705 info.insert(
12706 "phase".to_string(),
12707 Value::String(Rc::new("Phase 7 - DevEx".to_string())),
12708 );
12709 Ok(Value::Map(Rc::new(RefCell::new(info))))
12710 });
12711}
12712
12713fn format_value_debug(value: &Value) -> String {
12715 match value {
12716 Value::Null => "null".to_string(),
12717 Value::Bool(b) => b.to_string(),
12718 Value::Int(n) => n.to_string(),
12719 Value::Float(f) => format!("{:.6}", f),
12720 Value::String(s) => format!("\"{}\"", s),
12721 Value::Char(c) => format!("'{}'", c),
12722 Value::Array(a) => {
12723 let items: Vec<String> = a.borrow().iter().take(10).map(format_value_debug).collect();
12724 if a.borrow().len() > 10 {
12725 format!(
12726 "[{}, ... ({} more)]",
12727 items.join(", "),
12728 a.borrow().len() - 10
12729 )
12730 } else {
12731 format!("[{}]", items.join(", "))
12732 }
12733 }
12734 Value::Tuple(t) => {
12735 let items: Vec<String> = t.iter().map(format_value_debug).collect();
12736 format!("({})", items.join(", "))
12737 }
12738 Value::Map(m) => {
12739 let items: Vec<String> = m
12740 .borrow()
12741 .iter()
12742 .take(5)
12743 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
12744 .collect();
12745 if m.borrow().len() > 5 {
12746 format!(
12747 "{{{}, ... ({} more)}}",
12748 items.join(", "),
12749 m.borrow().len() - 5
12750 )
12751 } else {
12752 format!("{{{}}}", items.join(", "))
12753 }
12754 }
12755 Value::Set(s) => {
12756 let items: Vec<String> = s.borrow().iter().take(5).cloned().collect();
12757 if s.borrow().len() > 5 {
12758 format!(
12759 "#{{{}, ... ({} more)}}",
12760 items.join(", "),
12761 s.borrow().len() - 5
12762 )
12763 } else {
12764 format!("#{{{}}}", items.join(", "))
12765 }
12766 }
12767 Value::Struct { name, fields } => {
12768 let items: Vec<String> = fields
12769 .borrow()
12770 .iter()
12771 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
12772 .collect();
12773 format!("{} {{{}}}", name, items.join(", "))
12774 }
12775 Value::Variant {
12776 enum_name,
12777 variant_name,
12778 fields,
12779 } => match fields {
12780 Some(f) => {
12781 let items: Vec<String> = f.iter().map(format_value_debug).collect();
12782 format!("{}::{}({})", enum_name, variant_name, items.join(", "))
12783 }
12784 None => format!("{}::{}", enum_name, variant_name),
12785 },
12786 Value::Function(_) => "<function>".to_string(),
12787 Value::BuiltIn(f) => format!("<builtin:{}>", f.name),
12788 Value::Ref(r) => format!("&{}", format_value_debug(&r.borrow())),
12789 Value::Infinity => "∞".to_string(),
12790 Value::Empty => "∅".to_string(),
12791 Value::Evidential { value, evidence } => {
12792 format!("{:?}({})", evidence, format_value_debug(value))
12793 }
12794 Value::Affective { value, affect } => {
12795 let mut markers = Vec::new();
12796 if let Some(s) = &affect.sentiment {
12797 markers.push(format!("{:?}", s));
12798 }
12799 if affect.sarcasm {
12800 markers.push("sarcasm".to_string());
12801 }
12802 if let Some(i) = &affect.intensity {
12803 markers.push(format!("{:?}", i));
12804 }
12805 if let Some(f) = &affect.formality {
12806 markers.push(format!("{:?}", f));
12807 }
12808 if let Some(e) = &affect.emotion {
12809 markers.push(format!("{:?}", e));
12810 }
12811 if let Some(c) = &affect.confidence {
12812 markers.push(format!("{:?}", c));
12813 }
12814 format!("{}[{}]", format_value_debug(value), markers.join(","))
12815 }
12816 Value::Channel(_) => "<channel>".to_string(),
12817 Value::ThreadHandle(_) => "<thread>".to_string(),
12818 Value::Actor(_) => "<actor>".to_string(),
12819 Value::Future(_) => "<future>".to_string(),
12820 Value::VariantConstructor {
12821 enum_name,
12822 variant_name,
12823 } => {
12824 format!("<constructor {}::{}>", enum_name, variant_name)
12825 }
12826 Value::DefaultConstructor { type_name } => {
12827 format!("<default {}>", type_name)
12828 }
12829 Value::Range {
12830 start,
12831 end,
12832 inclusive,
12833 } => match (start, end) {
12834 (Some(s), Some(e)) => {
12835 if *inclusive {
12836 format!("{}..={}", s, e)
12837 } else {
12838 format!("{}..{}", s, e)
12839 }
12840 }
12841 (Some(s), None) => format!("{}..", s),
12842 (None, Some(e)) => {
12843 if *inclusive {
12844 format!("..={}", e)
12845 } else {
12846 format!("..{}", e)
12847 }
12848 }
12849 (None, None) => "..".to_string(),
12850 },
12851 }
12852}
12853
12854fn pretty_print_value(value: &Value, indent: usize) -> String {
12856 let prefix = " ".repeat(indent);
12857 match value {
12858 Value::Array(a) => {
12859 if a.borrow().is_empty() {
12860 "[]".to_string()
12861 } else {
12862 let items: Vec<String> = a
12863 .borrow()
12864 .iter()
12865 .map(|v| {
12866 format!(
12867 "{}{}",
12868 " ".repeat(indent + 1),
12869 pretty_print_value(v, indent + 1)
12870 )
12871 })
12872 .collect();
12873 format!("[\n{}\n{}]", items.join(",\n"), prefix)
12874 }
12875 }
12876 Value::Map(m) => {
12877 if m.borrow().is_empty() {
12878 "{}".to_string()
12879 } else {
12880 let items: Vec<String> = m
12881 .borrow()
12882 .iter()
12883 .map(|(k, v)| {
12884 format!(
12885 "{}\"{}\": {}",
12886 " ".repeat(indent + 1),
12887 k,
12888 pretty_print_value(v, indent + 1)
12889 )
12890 })
12891 .collect();
12892 format!("{{\n{}\n{}}}", items.join(",\n"), prefix)
12893 }
12894 }
12895 Value::Struct { name, fields } => {
12896 if fields.borrow().is_empty() {
12897 format!("{} {{}}", name)
12898 } else {
12899 let items: Vec<String> = fields
12900 .borrow()
12901 .iter()
12902 .map(|(k, v)| {
12903 format!(
12904 "{}{}: {}",
12905 " ".repeat(indent + 1),
12906 k,
12907 pretty_print_value(v, indent + 1)
12908 )
12909 })
12910 .collect();
12911 format!("{} {{\n{}\n{}}}", name, items.join(",\n"), prefix)
12912 }
12913 }
12914 _ => format_value_debug(value),
12915 }
12916}
12917
12918fn devex_compare(a: &Value, b: &Value) -> Result<i64, RuntimeError> {
12920 match (a, b) {
12921 (Value::Int(a), Value::Int(b)) => Ok(if a < b {
12922 -1
12923 } else if a > b {
12924 1
12925 } else {
12926 0
12927 }),
12928 (Value::Float(a), Value::Float(b)) => Ok(if a < b {
12929 -1
12930 } else if a > b {
12931 1
12932 } else {
12933 0
12934 }),
12935 (Value::Int(a), Value::Float(b)) => {
12936 let a = *a as f64;
12937 Ok(if a < *b {
12938 -1
12939 } else if a > *b {
12940 1
12941 } else {
12942 0
12943 })
12944 }
12945 (Value::Float(a), Value::Int(b)) => {
12946 let b = *b as f64;
12947 Ok(if *a < b {
12948 -1
12949 } else if *a > b {
12950 1
12951 } else {
12952 0
12953 })
12954 }
12955 (Value::String(a), Value::String(b)) => Ok(if a < b {
12956 -1
12957 } else if a > b {
12958 1
12959 } else {
12960 0
12961 }),
12962 _ => Err(RuntimeError::new("cannot compare these types")),
12963 }
12964}
12965
12966fn get_type_name(value: &Value) -> String {
12968 match value {
12969 Value::Null => "null".to_string(),
12970 Value::Bool(_) => "bool".to_string(),
12971 Value::Int(_) => "int".to_string(),
12972 Value::Float(_) => "float".to_string(),
12973 Value::String(_) => "string".to_string(),
12974 Value::Char(_) => "char".to_string(),
12975 Value::Array(_) => "array".to_string(),
12976 Value::Tuple(_) => "tuple".to_string(),
12977 Value::Map(_) => "map".to_string(),
12978 Value::Set(_) => "set".to_string(),
12979 Value::Struct { name, .. } => name.clone(),
12980 Value::Variant { enum_name, .. } => enum_name.clone(),
12981 Value::Function(_) => "function".to_string(),
12982 Value::BuiltIn(_) => "builtin".to_string(),
12983 Value::Ref(_) => "ref".to_string(),
12984 Value::Infinity => "infinity".to_string(),
12985 Value::Empty => "empty".to_string(),
12986 Value::Evidential { .. } => "evidential".to_string(),
12987 Value::Affective { .. } => "affective".to_string(),
12988 Value::Channel(_) => "channel".to_string(),
12989 Value::ThreadHandle(_) => "thread".to_string(),
12990 Value::Actor(_) => "actor".to_string(),
12991 Value::Future(_) => "future".to_string(),
12992 Value::VariantConstructor { enum_name, .. } => format!("{}_constructor", enum_name),
12993 Value::DefaultConstructor { type_name } => format!("{}_default", type_name),
12994 Value::Range { .. } => "range".to_string(),
12995 }
12996}
12997
12998fn matches_type_alias(value: &Value, type_name: &str) -> bool {
13000 match (value, type_name) {
13001 (Value::Int(_), "number") | (Value::Float(_), "number") => true,
13002 (Value::Int(_), "integer") => true,
13003 (Value::Array(_), "list") => true,
13004 (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
13005 (Value::Function(_), "fn") | (Value::BuiltIn(_), "fn") => true,
13006 (Value::BuiltIn(_), "function") => true,
13007 _ => false,
13008 }
13009}
13010
13011fn get_function_doc(name: &str) -> String {
13013 match name {
13014 "print" => "print(value) - Print value to stdout".to_string(),
13015 "println" => "println(value) - Print value with newline".to_string(),
13016 "len" => "len(collection) - Get length of string, array, map, or set".to_string(),
13017 "type_of" => "type_of(value) - Get type name as string".to_string(),
13018 "assert" => "assert(condition) - Assert condition is truthy, panic if false".to_string(),
13019 "assert_eq" => "assert_eq(a, b) - Assert two values are deeply equal".to_string(),
13020 "debug" => "debug(value) - Print value with type info and return it".to_string(),
13021 "map" => "map(array, fn) - Apply function to each element".to_string(),
13022 "filter" => "filter(array, fn) - Keep elements where predicate is true".to_string(),
13023 "reduce" => "reduce(array, init, fn) - Fold array with function".to_string(),
13024 "range" => "range(start, end) - Create array of integers from start to end".to_string(),
13025 "sum" => "sum(array) - Sum all numeric elements".to_string(),
13026 "product" => "product(array) - Multiply all numeric elements".to_string(),
13027 "sort" => "sort(array) - Sort array in ascending order".to_string(),
13028 "reverse" => "reverse(array) - Reverse array order".to_string(),
13029 "join" => "join(array, sep) - Join array elements with separator".to_string(),
13030 "split" => "split(string, sep) - Split string by separator".to_string(),
13031 "trim" => "trim(string) - Remove leading/trailing whitespace".to_string(),
13032 "upper" => "upper(string) - Convert to uppercase".to_string(),
13033 "lower" => "lower(string) - Convert to lowercase".to_string(),
13034 _ => format!("No documentation available for '{}'", name),
13035 }
13036}
13037
13038fn register_soa(interp: &mut Interpreter) {
13050 define(interp, "aos_to_soa", Some(2), |_, args| {
13053 let arr = match &args[0] {
13054 Value::Array(arr) => arr.borrow().clone(),
13055 _ => {
13056 return Err(RuntimeError::new(
13057 "aos_to_soa: first argument must be array",
13058 ))
13059 }
13060 };
13061 let keys = match &args[1] {
13062 Value::Array(keys) => keys.borrow().clone(),
13063 _ => {
13064 return Err(RuntimeError::new(
13065 "aos_to_soa: second argument must be array of keys",
13066 ))
13067 }
13068 };
13069
13070 if arr.is_empty() {
13071 let mut result = HashMap::new();
13073 for key in &keys {
13074 if let Value::String(k) = key {
13075 result.insert((**k).clone(), Value::Array(Rc::new(RefCell::new(vec![]))));
13076 }
13077 }
13078 return Ok(Value::Map(Rc::new(RefCell::new(result))));
13079 }
13080
13081 let key_names: Vec<String> = keys
13083 .iter()
13084 .filter_map(|k| {
13085 if let Value::String(s) = k {
13086 Some((**s).clone())
13087 } else {
13088 None
13089 }
13090 })
13091 .collect();
13092
13093 let mut soa: HashMap<String, Vec<Value>> = HashMap::new();
13095 for key in &key_names {
13096 soa.insert(key.clone(), Vec::with_capacity(arr.len()));
13097 }
13098
13099 for item in &arr {
13101 match item {
13102 Value::Map(map) => {
13103 let map = map.borrow();
13104 for key in &key_names {
13105 let val = map.get(key).cloned().unwrap_or(Value::Null);
13106 soa.get_mut(key).unwrap().push(val);
13107 }
13108 }
13109 Value::Struct { fields, .. } => {
13110 let fields = fields.borrow();
13111 for key in &key_names {
13112 let val = fields.get(key).cloned().unwrap_or(Value::Null);
13113 soa.get_mut(key).unwrap().push(val);
13114 }
13115 }
13116 _ => {
13117 return Err(RuntimeError::new(
13118 "aos_to_soa: array must contain structs or maps",
13119 ))
13120 }
13121 }
13122 }
13123
13124 let result: HashMap<String, Value> = soa
13126 .into_iter()
13127 .map(|(k, v)| (k, Value::Array(Rc::new(RefCell::new(v)))))
13128 .collect();
13129
13130 Ok(Value::Map(Rc::new(RefCell::new(result))))
13131 });
13132
13133 define(interp, "soa_to_aos", Some(1), |_, args| {
13136 let soa = match &args[0] {
13137 Value::Map(map) => map.borrow().clone(),
13138 _ => return Err(RuntimeError::new("soa_to_aos: argument must be map")),
13139 };
13140
13141 if soa.is_empty() {
13142 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13143 }
13144
13145 let len = soa
13147 .values()
13148 .next()
13149 .and_then(|v| {
13150 if let Value::Array(arr) = v {
13151 Some(arr.borrow().len())
13152 } else {
13153 None
13154 }
13155 })
13156 .unwrap_or(0);
13157
13158 let mut aos: Vec<Value> = Vec::with_capacity(len);
13160 for i in 0..len {
13161 let mut fields = HashMap::new();
13162 for (key, value) in &soa {
13163 if let Value::Array(arr) = value {
13164 let arr = arr.borrow();
13165 if i < arr.len() {
13166 fields.insert(key.clone(), arr[i].clone());
13167 }
13168 }
13169 }
13170 aos.push(Value::Map(Rc::new(RefCell::new(fields))));
13171 }
13172
13173 Ok(Value::Array(Rc::new(RefCell::new(aos))))
13174 });
13175
13176 define(interp, "soa_map", Some(3), |interp, args| {
13179 let mut soa = match &args[0] {
13180 Value::Map(map) => map.borrow().clone(),
13181 _ => return Err(RuntimeError::new("soa_map: first argument must be SoA map")),
13182 };
13183 let key = match &args[1] {
13184 Value::String(s) => (**s).clone(),
13185 _ => {
13186 return Err(RuntimeError::new(
13187 "soa_map: second argument must be key string",
13188 ))
13189 }
13190 };
13191 let func = match &args[2] {
13192 Value::Function(f) => f.clone(),
13193 _ => {
13194 return Err(RuntimeError::new(
13195 "soa_map: third argument must be a function",
13196 ))
13197 }
13198 };
13199
13200 let arr = soa
13202 .get(&key)
13203 .ok_or_else(|| RuntimeError::new(format!("soa_map: key '{}' not found", key)))?;
13204
13205 let arr_vals = match arr {
13206 Value::Array(a) => a.borrow().clone(),
13207 _ => return Err(RuntimeError::new("soa_map: key must map to array")),
13208 };
13209
13210 let results: Vec<Value> = arr_vals
13212 .iter()
13213 .map(|val| interp.call_function(&func, vec![val.clone()]))
13214 .collect::<Result<_, _>>()?;
13215
13216 soa.insert(key, Value::Array(Rc::new(RefCell::new(results))));
13218
13219 Ok(Value::Map(Rc::new(RefCell::new(soa))))
13220 });
13221
13222 define(interp, "soa_zip", Some(3), |interp, args| {
13225 let soa = match &args[0] {
13226 Value::Map(map) => map.borrow().clone(),
13227 _ => return Err(RuntimeError::new("soa_zip: first argument must be SoA map")),
13228 };
13229 let keys = match &args[1] {
13230 Value::Array(keys) => keys.borrow().clone(),
13231 _ => {
13232 return Err(RuntimeError::new(
13233 "soa_zip: second argument must be array of keys",
13234 ))
13235 }
13236 };
13237 let func = match &args[2] {
13238 Value::Function(f) => f.clone(),
13239 _ => {
13240 return Err(RuntimeError::new(
13241 "soa_zip: third argument must be a function",
13242 ))
13243 }
13244 };
13245
13246 let arrays: Vec<Vec<Value>> = keys
13248 .iter()
13249 .filter_map(|k| {
13250 if let Value::String(s) = k {
13251 if let Some(Value::Array(arr)) = soa.get(&**s) {
13252 return Some(arr.borrow().clone());
13253 }
13254 }
13255 None
13256 })
13257 .collect();
13258
13259 if arrays.is_empty() {
13260 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13261 }
13262
13263 let len = arrays[0].len();
13264
13265 let results: Vec<Value> = (0..len)
13267 .map(|i| {
13268 let fn_args: Vec<Value> = arrays
13269 .iter()
13270 .filter_map(|arr| arr.get(i).cloned())
13271 .collect();
13272 interp.call_function(&func, fn_args)
13273 })
13274 .collect::<Result<_, _>>()?;
13275
13276 Ok(Value::Array(Rc::new(RefCell::new(results))))
13277 });
13278
13279 define(interp, "interleave", None, |_, args| {
13282 if args.is_empty() {
13283 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13284 }
13285
13286 let arrays: Vec<Vec<Value>> = args
13287 .iter()
13288 .filter_map(|arg| {
13289 if let Value::Array(arr) = arg {
13290 Some(arr.borrow().clone())
13291 } else {
13292 None
13293 }
13294 })
13295 .collect();
13296
13297 if arrays.is_empty() {
13298 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13299 }
13300
13301 let len = arrays[0].len();
13302 let stride = arrays.len();
13303 let mut result = Vec::with_capacity(len * stride);
13304
13305 for i in 0..len {
13306 for arr in &arrays {
13307 if let Some(val) = arr.get(i) {
13308 result.push(val.clone());
13309 }
13310 }
13311 }
13312
13313 Ok(Value::Array(Rc::new(RefCell::new(result))))
13314 });
13315
13316 define(interp, "deinterleave", Some(2), |_, args| {
13319 let arr = match &args[0] {
13320 Value::Array(arr) => arr.borrow().clone(),
13321 _ => {
13322 return Err(RuntimeError::new(
13323 "deinterleave: first argument must be array",
13324 ))
13325 }
13326 };
13327 let stride = match &args[1] {
13328 Value::Int(n) => *n as usize,
13329 _ => {
13330 return Err(RuntimeError::new(
13331 "deinterleave: second argument must be integer stride",
13332 ))
13333 }
13334 };
13335
13336 if stride == 0 {
13337 return Err(RuntimeError::new("deinterleave: stride must be > 0"));
13338 }
13339
13340 let mut result: Vec<Vec<Value>> = (0..stride).map(|_| Vec::new()).collect();
13341
13342 for (i, val) in arr.iter().enumerate() {
13343 result[i % stride].push(val.clone());
13344 }
13345
13346 Ok(Value::Array(Rc::new(RefCell::new(
13347 result
13348 .into_iter()
13349 .map(|v| Value::Array(Rc::new(RefCell::new(v))))
13350 .collect(),
13351 ))))
13352 });
13353}
13354
13355fn register_tensor(interp: &mut Interpreter) {
13361 define(interp, "outer_product", Some(2), |_, args| {
13364 let a = match &args[0] {
13365 Value::Array(arr) => arr.borrow().clone(),
13366 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
13367 };
13368 let b = match &args[1] {
13369 Value::Array(arr) => arr.borrow().clone(),
13370 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
13371 };
13372
13373 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
13375 for ai in &a {
13376 for bi in &b {
13377 let product = match (ai, bi) {
13378 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
13379 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
13380 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
13381 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
13382 _ => return Err(RuntimeError::new("outer_product: elements must be numeric")),
13383 };
13384 result.push(product);
13385 }
13386 }
13387
13388 Ok(Value::Array(Rc::new(RefCell::new(result))))
13389 });
13390
13391 define(interp, "tensor_contract", Some(4), |_, args| {
13394 let a = match &args[0] {
13395 Value::Array(arr) => arr.borrow().clone(),
13396 _ => {
13397 return Err(RuntimeError::new(
13398 "tensor_contract: first argument must be array",
13399 ))
13400 }
13401 };
13402 let b = match &args[1] {
13403 Value::Array(arr) => arr.borrow().clone(),
13404 _ => {
13405 return Err(RuntimeError::new(
13406 "tensor_contract: second argument must be array",
13407 ))
13408 }
13409 };
13410 let _axis_a = match &args[2] {
13411 Value::Int(n) => *n as usize,
13412 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
13413 };
13414 let _axis_b = match &args[3] {
13415 Value::Int(n) => *n as usize,
13416 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
13417 };
13418
13419 if a.len() != b.len() {
13421 return Err(RuntimeError::new(
13422 "tensor_contract: vectors must have same length for contraction",
13423 ));
13424 }
13425
13426 let mut sum = 0.0f64;
13427 for (ai, bi) in a.iter().zip(b.iter()) {
13428 let product = match (ai, bi) {
13429 (Value::Float(x), Value::Float(y)) => x * y,
13430 (Value::Int(x), Value::Int(y)) => (*x as f64) * (*y as f64),
13431 (Value::Float(x), Value::Int(y)) => x * (*y as f64),
13432 (Value::Int(x), Value::Float(y)) => (*x as f64) * y,
13433 _ => {
13434 return Err(RuntimeError::new(
13435 "tensor_contract: elements must be numeric",
13436 ))
13437 }
13438 };
13439 sum += product;
13440 }
13441
13442 Ok(Value::Float(sum))
13443 });
13444
13445 define(interp, "kronecker_product", Some(2), |_, args| {
13448 let a = match &args[0] {
13449 Value::Array(arr) => arr.borrow().clone(),
13450 _ => {
13451 return Err(RuntimeError::new(
13452 "kronecker_product: arguments must be arrays",
13453 ))
13454 }
13455 };
13456 let b = match &args[1] {
13457 Value::Array(arr) => arr.borrow().clone(),
13458 _ => {
13459 return Err(RuntimeError::new(
13460 "kronecker_product: arguments must be arrays",
13461 ))
13462 }
13463 };
13464
13465 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
13467 for ai in &a {
13468 for bi in &b {
13469 let product = match (ai, bi) {
13470 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
13471 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
13472 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
13473 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
13474 _ => {
13475 return Err(RuntimeError::new(
13476 "kronecker_product: elements must be numeric",
13477 ))
13478 }
13479 };
13480 result.push(product);
13481 }
13482 }
13483
13484 Ok(Value::Array(Rc::new(RefCell::new(result))))
13485 });
13486
13487 define(interp, "hadamard_product", Some(2), |_, args| {
13489 let a = match &args[0] {
13490 Value::Array(arr) => arr.borrow().clone(),
13491 _ => {
13492 return Err(RuntimeError::new(
13493 "hadamard_product: arguments must be arrays",
13494 ))
13495 }
13496 };
13497 let b = match &args[1] {
13498 Value::Array(arr) => arr.borrow().clone(),
13499 _ => {
13500 return Err(RuntimeError::new(
13501 "hadamard_product: arguments must be arrays",
13502 ))
13503 }
13504 };
13505
13506 if a.len() != b.len() {
13507 return Err(RuntimeError::new(
13508 "hadamard_product: arrays must have same length",
13509 ));
13510 }
13511
13512 let result: Vec<Value> = a
13513 .iter()
13514 .zip(b.iter())
13515 .map(|(ai, bi)| match (ai, bi) {
13516 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
13517 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
13518 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * (*y as f64))),
13519 (Value::Int(x), Value::Float(y)) => Ok(Value::Float((*x as f64) * y)),
13520 _ => Err(RuntimeError::new(
13521 "hadamard_product: elements must be numeric",
13522 )),
13523 })
13524 .collect::<Result<_, _>>()?;
13525
13526 Ok(Value::Array(Rc::new(RefCell::new(result))))
13527 });
13528
13529 define(interp, "trace", Some(2), |_, args| {
13531 let arr = match &args[0] {
13532 Value::Array(arr) => arr.borrow().clone(),
13533 _ => return Err(RuntimeError::new("trace: first argument must be array")),
13534 };
13535 let size = match &args[1] {
13536 Value::Int(n) => *n as usize,
13537 _ => {
13538 return Err(RuntimeError::new(
13539 "trace: second argument must be matrix size",
13540 ))
13541 }
13542 };
13543
13544 let mut sum = 0.0f64;
13545 for i in 0..size {
13546 let idx = i * size + i;
13547 if idx < arr.len() {
13548 sum += match &arr[idx] {
13549 Value::Float(f) => *f,
13550 Value::Int(n) => *n as f64,
13551 _ => return Err(RuntimeError::new("trace: elements must be numeric")),
13552 };
13553 }
13554 }
13555
13556 Ok(Value::Float(sum))
13557 });
13558}
13559
13560fn register_autodiff(interp: &mut Interpreter) {
13606 define(interp, "grad", None, |interp, args| {
13609 if args.len() < 2 {
13610 return Err(RuntimeError::new(
13611 "grad() requires function and point arguments.\n\
13612 Usage: grad(f, x) or grad(f, x, step_size)\n\
13613 Example:\n\
13614 fn f(x) { return x * x; }\n\
13615 let derivative = grad(f, 3.0); // Returns 6.0",
13616 ));
13617 }
13618
13619 let func = match &args[0] {
13620 Value::Function(f) => f.clone(),
13621 _ => {
13622 return Err(RuntimeError::new(
13623 "grad() first argument must be a function.\n\
13624 Got non-function value. Define a function first:\n\
13625 fn my_func(x) { return x * x; }\n\
13626 grad(my_func, 2.0)",
13627 ))
13628 }
13629 };
13630 let x = match &args[1] {
13631 Value::Float(f) => *f,
13632 Value::Int(n) => *n as f64,
13633 Value::Array(arr) => {
13634 let arr = arr.borrow().clone();
13636 let h = if args.len() > 2 {
13637 match &args[2] {
13638 Value::Float(f) => *f,
13639 Value::Int(n) => *n as f64,
13640 _ => 1e-7,
13641 }
13642 } else {
13643 1e-7
13644 };
13645
13646 let mut gradient = Vec::with_capacity(arr.len());
13647 for (i, xi) in arr.iter().enumerate() {
13648 let xi_val = match xi {
13649 Value::Float(f) => *f,
13650 Value::Int(n) => *n as f64,
13651 _ => continue,
13652 };
13653
13654 let mut x_plus = arr.clone();
13656 let mut x_minus = arr.clone();
13657 x_plus[i] = Value::Float(xi_val + h);
13658 x_minus[i] = Value::Float(xi_val - h);
13659
13660 let f_plus = interp
13661 .call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13662 let f_minus = interp
13663 .call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13664
13665 let grad_i = match (f_plus, f_minus) {
13666 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
13667 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
13668 _ => return Err(RuntimeError::new("grad: function must return numeric")),
13669 };
13670
13671 gradient.push(Value::Float(grad_i));
13672 }
13673
13674 return Ok(Value::Array(Rc::new(RefCell::new(gradient))));
13675 }
13676 _ => return Err(RuntimeError::new("grad: x must be numeric or array")),
13677 };
13678
13679 let h = if args.len() > 2 {
13680 match &args[2] {
13681 Value::Float(f) => *f,
13682 Value::Int(n) => *n as f64,
13683 _ => 1e-7,
13684 }
13685 } else {
13686 1e-7
13687 };
13688
13689 let f_plus = interp.call_function(&func, vec![Value::Float(x + h)])?;
13691 let f_minus = interp.call_function(&func, vec![Value::Float(x - h)])?;
13692
13693 let derivative = match (f_plus, f_minus) {
13694 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
13695 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
13696 _ => return Err(RuntimeError::new("grad: function must return numeric")),
13697 };
13698
13699 Ok(Value::Float(derivative))
13700 });
13701
13702 define(interp, "jacobian", Some(2), |interp, args| {
13704 let func = match &args[0] {
13705 Value::Function(f) => f.clone(),
13706 _ => {
13707 return Err(RuntimeError::new(
13708 "jacobian: first argument must be a function",
13709 ))
13710 }
13711 };
13712 let x = match &args[1] {
13713 Value::Array(arr) => arr.borrow().clone(),
13714 _ => return Err(RuntimeError::new("jacobian: second argument must be array")),
13715 };
13716
13717 let h = 1e-7;
13718 let n = x.len();
13719
13720 let f_x =
13722 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x.clone())))])?;
13723 let m = match &f_x {
13724 Value::Array(arr) => arr.borrow().len(),
13725 _ => 1,
13726 };
13727
13728 let mut jacobian: Vec<Value> = Vec::with_capacity(m * n);
13730
13731 for j in 0..n {
13732 let xj = match &x[j] {
13733 Value::Float(f) => *f,
13734 Value::Int(i) => *i as f64,
13735 _ => continue,
13736 };
13737
13738 let mut x_plus = x.clone();
13739 let mut x_minus = x.clone();
13740 x_plus[j] = Value::Float(xj + h);
13741 x_minus[j] = Value::Float(xj - h);
13742
13743 let f_plus =
13744 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13745 let f_minus =
13746 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13747
13748 match (&f_plus, &f_minus) {
13750 (Value::Array(fp), Value::Array(fm)) => {
13751 let fp = fp.borrow();
13752 let fm = fm.borrow();
13753 for i in 0..m {
13754 let dfi_dxj = match (&fp[i], &fm[i]) {
13755 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
13756 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
13757 _ => 0.0,
13758 };
13759 jacobian.push(Value::Float(dfi_dxj));
13760 }
13761 }
13762 (Value::Float(fp), Value::Float(fm)) => {
13763 jacobian.push(Value::Float((fp - fm) / (2.0 * h)));
13764 }
13765 _ => {
13766 return Err(RuntimeError::new(
13767 "jacobian: function must return array or numeric",
13768 ))
13769 }
13770 }
13771 }
13772
13773 Ok(Value::Array(Rc::new(RefCell::new(jacobian))))
13774 });
13775
13776 define(interp, "hessian", Some(2), |interp, args| {
13778 let func = match &args[0] {
13779 Value::Function(f) => f.clone(),
13780 _ => {
13781 return Err(RuntimeError::new(
13782 "hessian: first argument must be a function",
13783 ))
13784 }
13785 };
13786 let x = match &args[1] {
13787 Value::Array(arr) => arr.borrow().clone(),
13788 _ => return Err(RuntimeError::new("hessian: second argument must be array")),
13789 };
13790
13791 let h = 1e-5; let n = x.len();
13793
13794 let mut hessian: Vec<Value> = Vec::with_capacity(n * n);
13795
13796 for i in 0..n {
13797 for j in 0..n {
13798 let xi = match &x[i] {
13799 Value::Float(f) => *f,
13800 Value::Int(k) => *k as f64,
13801 _ => continue,
13802 };
13803 let xj = match &x[j] {
13804 Value::Float(f) => *f,
13805 Value::Int(k) => *k as f64,
13806 _ => continue,
13807 };
13808
13809 let mut x_pp = x.clone();
13811 let mut x_pm = x.clone();
13812 let mut x_mp = x.clone();
13813 let mut x_mm = x.clone();
13814
13815 x_pp[i] = Value::Float(xi + h);
13816 x_pp[j] = Value::Float(if i == j { xi + 2.0 * h } else { xj + h });
13817 x_pm[i] = Value::Float(xi + h);
13818 x_pm[j] = Value::Float(if i == j { xi } else { xj - h });
13819 x_mp[i] = Value::Float(xi - h);
13820 x_mp[j] = Value::Float(if i == j { xi } else { xj + h });
13821 x_mm[i] = Value::Float(xi - h);
13822 x_mm[j] = Value::Float(if i == j { xi - 2.0 * h } else { xj - h });
13823
13824 let f_pp =
13825 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pp)))])?;
13826 let f_pm =
13827 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pm)))])?;
13828 let f_mp =
13829 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mp)))])?;
13830 let f_mm =
13831 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mm)))])?;
13832
13833 let d2f = match (f_pp, f_pm, f_mp, f_mm) {
13834 (
13835 Value::Float(fpp),
13836 Value::Float(fpm),
13837 Value::Float(fmp),
13838 Value::Float(fmm),
13839 ) => (fpp - fpm - fmp + fmm) / (4.0 * h * h),
13840 _ => 0.0,
13841 };
13842
13843 hessian.push(Value::Float(d2f));
13844 }
13845 }
13846
13847 Ok(Value::Array(Rc::new(RefCell::new(hessian))))
13848 });
13849
13850 define(interp, "divergence", Some(2), |interp, args| {
13852 let func = match &args[0] {
13853 Value::Function(f) => f.clone(),
13854 _ => {
13855 return Err(RuntimeError::new(
13856 "divergence: first argument must be a function",
13857 ))
13858 }
13859 };
13860 let x = match &args[1] {
13861 Value::Array(arr) => arr.borrow().clone(),
13862 _ => {
13863 return Err(RuntimeError::new(
13864 "divergence: second argument must be array",
13865 ))
13866 }
13867 };
13868
13869 let h = 1e-7;
13870 let mut div = 0.0f64;
13871
13872 for (i, xi) in x.iter().enumerate() {
13873 let xi_val = match xi {
13874 Value::Float(f) => *f,
13875 Value::Int(n) => *n as f64,
13876 _ => continue,
13877 };
13878
13879 let mut x_plus = x.clone();
13880 let mut x_minus = x.clone();
13881 x_plus[i] = Value::Float(xi_val + h);
13882 x_minus[i] = Value::Float(xi_val - h);
13883
13884 let f_plus =
13885 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
13886 let f_minus =
13887 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
13888
13889 let df_i = match (&f_plus, &f_minus) {
13891 (Value::Array(fp), Value::Array(fm)) => {
13892 let fp = fp.borrow();
13893 let fm = fm.borrow();
13894 if i < fp.len() && i < fm.len() {
13895 match (&fp[i], &fm[i]) {
13896 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
13897 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
13898 _ => 0.0,
13899 }
13900 } else {
13901 0.0
13902 }
13903 }
13904 _ => 0.0,
13905 };
13906
13907 div += df_i;
13908 }
13909
13910 Ok(Value::Float(div))
13911 });
13912}
13913
13914fn register_spatial(interp: &mut Interpreter) {
13920 define(interp, "spatial_hash_new", Some(1), |_, args| {
13922 let cell_size = match &args[0] {
13923 Value::Float(f) => *f,
13924 Value::Int(n) => *n as f64,
13925 _ => {
13926 return Err(RuntimeError::new(
13927 "spatial_hash_new: cell_size must be numeric",
13928 ))
13929 }
13930 };
13931
13932 let mut config = HashMap::new();
13933 config.insert("cell_size".to_string(), Value::Float(cell_size));
13934 config.insert(
13935 "buckets".to_string(),
13936 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
13937 );
13938
13939 Ok(Value::Map(Rc::new(RefCell::new(config))))
13940 });
13941
13942 define(interp, "spatial_hash_insert", Some(3), |_, args| {
13944 let hash = match &args[0] {
13945 Value::Map(map) => map.clone(),
13946 _ => {
13947 return Err(RuntimeError::new(
13948 "spatial_hash_insert: first argument must be spatial hash",
13949 ))
13950 }
13951 };
13952 let id = args[1].clone();
13953 let pos = match &args[2] {
13954 Value::Array(arr) => arr.borrow().clone(),
13955 _ => {
13956 return Err(RuntimeError::new(
13957 "spatial_hash_insert: position must be array",
13958 ))
13959 }
13960 };
13961
13962 let cell_size = {
13963 let h = hash.borrow();
13964 match h.get("cell_size") {
13965 Some(Value::Float(f)) => *f,
13966 _ => 1.0,
13967 }
13968 };
13969
13970 let key = pos
13972 .iter()
13973 .filter_map(|v| match v {
13974 Value::Float(f) => Some((*f / cell_size).floor() as i64),
13975 Value::Int(n) => Some(*n / (cell_size as i64)),
13976 _ => None,
13977 })
13978 .map(|n| n.to_string())
13979 .collect::<Vec<_>>()
13980 .join(",");
13981
13982 {
13984 let mut h = hash.borrow_mut();
13985 let buckets = h
13986 .entry("buckets".to_string())
13987 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
13988
13989 if let Value::Map(buckets_map) = buckets {
13990 let mut bm = buckets_map.borrow_mut();
13991 let bucket = bm
13992 .entry(key)
13993 .or_insert_with(|| Value::Array(Rc::new(RefCell::new(vec![]))));
13994
13995 if let Value::Array(arr) = bucket {
13996 arr.borrow_mut().push(id);
13997 }
13998 }
13999 }
14000
14001 Ok(Value::Map(hash))
14002 });
14003
14004 define(interp, "spatial_hash_query", Some(3), |_, args| {
14006 let hash = match &args[0] {
14007 Value::Map(map) => map.borrow().clone(),
14008 _ => {
14009 return Err(RuntimeError::new(
14010 "spatial_hash_query: first argument must be spatial hash",
14011 ))
14012 }
14013 };
14014 let pos = match &args[1] {
14015 Value::Array(arr) => arr.borrow().clone(),
14016 _ => {
14017 return Err(RuntimeError::new(
14018 "spatial_hash_query: position must be array",
14019 ))
14020 }
14021 };
14022 let radius = match &args[2] {
14023 Value::Float(f) => *f,
14024 Value::Int(n) => *n as f64,
14025 _ => {
14026 return Err(RuntimeError::new(
14027 "spatial_hash_query: radius must be numeric",
14028 ))
14029 }
14030 };
14031
14032 let cell_size = match hash.get("cell_size") {
14033 Some(Value::Float(f)) => *f,
14034 _ => 1.0,
14035 };
14036
14037 let center: Vec<i64> = pos
14039 .iter()
14040 .filter_map(|v| match v {
14041 Value::Float(f) => Some((*f / cell_size).floor() as i64),
14042 Value::Int(n) => Some(*n / (cell_size as i64)),
14043 _ => None,
14044 })
14045 .collect();
14046
14047 let cells_to_check = (radius / cell_size).ceil() as i64;
14049
14050 let mut results: Vec<Value> = Vec::new();
14051
14052 if let Some(Value::Map(buckets)) = hash.get("buckets") {
14053 let buckets = buckets.borrow();
14054
14055 if center.len() >= 2 {
14057 for dx in -cells_to_check..=cells_to_check {
14058 for dy in -cells_to_check..=cells_to_check {
14059 let key = format!("{},{}", center[0] + dx, center[1] + dy);
14060 if let Some(Value::Array(bucket)) = buckets.get(&key) {
14061 for item in bucket.borrow().iter() {
14062 results.push(item.clone());
14065 }
14066 }
14067 }
14068 }
14069 }
14070 }
14071
14072 Ok(Value::Array(Rc::new(RefCell::new(results))))
14073 });
14074
14075 define(interp, "aabb_new", Some(2), |_, args| {
14077 let min = match &args[0] {
14078 Value::Array(arr) => arr.borrow().clone(),
14079 _ => return Err(RuntimeError::new("aabb_new: min must be array")),
14080 };
14081 let max = match &args[1] {
14082 Value::Array(arr) => arr.borrow().clone(),
14083 _ => return Err(RuntimeError::new("aabb_new: max must be array")),
14084 };
14085
14086 let mut aabb = HashMap::new();
14087 aabb.insert("min".to_string(), Value::Array(Rc::new(RefCell::new(min))));
14088 aabb.insert("max".to_string(), Value::Array(Rc::new(RefCell::new(max))));
14089
14090 Ok(Value::Map(Rc::new(RefCell::new(aabb))))
14091 });
14092
14093 define(interp, "aabb_intersects", Some(2), |_, args| {
14095 let a = match &args[0] {
14096 Value::Map(map) => map.borrow().clone(),
14097 _ => {
14098 return Err(RuntimeError::new(
14099 "aabb_intersects: arguments must be AABBs",
14100 ))
14101 }
14102 };
14103 let b = match &args[1] {
14104 Value::Map(map) => map.borrow().clone(),
14105 _ => {
14106 return Err(RuntimeError::new(
14107 "aabb_intersects: arguments must be AABBs",
14108 ))
14109 }
14110 };
14111
14112 let a_min = extract_vec_from_map(&a, "min")?;
14113 let a_max = extract_vec_from_map(&a, "max")?;
14114 let b_min = extract_vec_from_map(&b, "min")?;
14115 let b_max = extract_vec_from_map(&b, "max")?;
14116
14117 for i in 0..a_min
14119 .len()
14120 .min(a_max.len())
14121 .min(b_min.len())
14122 .min(b_max.len())
14123 {
14124 if a_max[i] < b_min[i] || b_max[i] < a_min[i] {
14125 return Ok(Value::Bool(false));
14126 }
14127 }
14128
14129 Ok(Value::Bool(true))
14130 });
14131
14132 define(interp, "aabb_contains", Some(2), |_, args| {
14134 let aabb = match &args[0] {
14135 Value::Map(map) => map.borrow().clone(),
14136 _ => {
14137 return Err(RuntimeError::new(
14138 "aabb_contains: first argument must be AABB",
14139 ))
14140 }
14141 };
14142 let point = match &args[1] {
14143 Value::Array(arr) => arr.borrow().clone(),
14144 _ => {
14145 return Err(RuntimeError::new(
14146 "aabb_contains: second argument must be point array",
14147 ))
14148 }
14149 };
14150
14151 let min = extract_vec_from_map(&aabb, "min")?;
14152 let max = extract_vec_from_map(&aabb, "max")?;
14153
14154 for (i, p) in point.iter().enumerate() {
14155 let p_val = match p {
14156 Value::Float(f) => *f,
14157 Value::Int(n) => *n as f64,
14158 _ => continue,
14159 };
14160
14161 if i < min.len() && p_val < min[i] {
14162 return Ok(Value::Bool(false));
14163 }
14164 if i < max.len() && p_val > max[i] {
14165 return Ok(Value::Bool(false));
14166 }
14167 }
14168
14169 Ok(Value::Bool(true))
14170 });
14171}
14172
14173fn extract_vec_from_map(map: &HashMap<String, Value>, key: &str) -> Result<Vec<f64>, RuntimeError> {
14175 match map.get(key) {
14176 Some(Value::Array(arr)) => arr
14177 .borrow()
14178 .iter()
14179 .map(|v| match v {
14180 Value::Float(f) => Ok(*f),
14181 Value::Int(n) => Ok(*n as f64),
14182 _ => Err(RuntimeError::new("Expected numeric value")),
14183 })
14184 .collect(),
14185 _ => Err(RuntimeError::new(format!(
14186 "Missing or invalid '{}' in AABB",
14187 key
14188 ))),
14189 }
14190}
14191
14192fn register_physics(interp: &mut Interpreter) {
14198 define(interp, "verlet_integrate", Some(4), |_, args| {
14201 let pos = extract_vec3(&args[0], "verlet_integrate")?;
14202 let prev = extract_vec3(&args[1], "verlet_integrate")?;
14203 let accel = extract_vec3(&args[2], "verlet_integrate")?;
14204 let dt = match &args[3] {
14205 Value::Float(f) => *f,
14206 Value::Int(n) => *n as f64,
14207 _ => return Err(RuntimeError::new("verlet_integrate: dt must be numeric")),
14208 };
14209
14210 let dt2 = dt * dt;
14211 let new_pos = [
14212 pos[0] + (pos[0] - prev[0]) + accel[0] * dt2,
14213 pos[1] + (pos[1] - prev[1]) + accel[1] * dt2,
14214 pos[2] + (pos[2] - prev[2]) + accel[2] * dt2,
14215 ];
14216
14217 Ok(make_vec3_arr(new_pos))
14218 });
14219
14220 define(interp, "spring_force", Some(4), |_, args| {
14222 let p1 = extract_vec3(&args[0], "spring_force")?;
14223 let p2 = extract_vec3(&args[1], "spring_force")?;
14224 let rest_length = match &args[2] {
14225 Value::Float(f) => *f,
14226 Value::Int(n) => *n as f64,
14227 _ => {
14228 return Err(RuntimeError::new(
14229 "spring_force: rest_length must be numeric",
14230 ))
14231 }
14232 };
14233 let stiffness = match &args[3] {
14234 Value::Float(f) => *f,
14235 Value::Int(n) => *n as f64,
14236 _ => return Err(RuntimeError::new("spring_force: stiffness must be numeric")),
14237 };
14238
14239 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
14240 let length = (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]).sqrt();
14241
14242 if length < 1e-10 {
14243 return Ok(make_vec3_arr([0.0, 0.0, 0.0]));
14244 }
14245
14246 let displacement = length - rest_length;
14247 let force_mag = stiffness * displacement;
14248 let normalized = [delta[0] / length, delta[1] / length, delta[2] / length];
14249
14250 Ok(make_vec3_arr([
14251 normalized[0] * force_mag,
14252 normalized[1] * force_mag,
14253 normalized[2] * force_mag,
14254 ]))
14255 });
14256
14257 define(interp, "distance_constraint", Some(3), |_, args| {
14260 let p1 = extract_vec3(&args[0], "distance_constraint")?;
14261 let p2 = extract_vec3(&args[1], "distance_constraint")?;
14262 let target = match &args[2] {
14263 Value::Float(f) => *f,
14264 Value::Int(n) => *n as f64,
14265 _ => {
14266 return Err(RuntimeError::new(
14267 "distance_constraint: target must be numeric",
14268 ))
14269 }
14270 };
14271
14272 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
14273 let length = (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]).sqrt();
14274
14275 if length < 1e-10 {
14276 return Ok(Value::Tuple(Rc::new(vec![
14277 make_vec3_arr(p1),
14278 make_vec3_arr(p2),
14279 ])));
14280 }
14281
14282 let correction = (length - target) / length * 0.5;
14283 let corr_vec = [
14284 delta[0] * correction,
14285 delta[1] * correction,
14286 delta[2] * correction,
14287 ];
14288
14289 let new_p1 = [
14290 p1[0] + corr_vec[0],
14291 p1[1] + corr_vec[1],
14292 p1[2] + corr_vec[2],
14293 ];
14294 let new_p2 = [
14295 p2[0] - corr_vec[0],
14296 p2[1] - corr_vec[1],
14297 p2[2] - corr_vec[2],
14298 ];
14299
14300 Ok(Value::Tuple(Rc::new(vec![
14301 make_vec3_arr(new_p1),
14302 make_vec3_arr(new_p2),
14303 ])))
14304 });
14305
14306 define(interp, "solve_constraints", Some(3), |_, args| {
14309 let mut points = match &args[0] {
14310 Value::Array(arr) => arr.borrow().clone(),
14311 _ => {
14312 return Err(RuntimeError::new(
14313 "solve_constraints: first argument must be array of points",
14314 ))
14315 }
14316 };
14317 let constraints = match &args[1] {
14318 Value::Array(arr) => arr.borrow().clone(),
14319 _ => {
14320 return Err(RuntimeError::new(
14321 "solve_constraints: second argument must be array of constraints",
14322 ))
14323 }
14324 };
14325 let iterations = match &args[2] {
14326 Value::Int(n) => *n as usize,
14327 _ => {
14328 return Err(RuntimeError::new(
14329 "solve_constraints: iterations must be integer",
14330 ))
14331 }
14332 };
14333
14334 for _ in 0..iterations {
14335 for constraint in &constraints {
14336 match constraint {
14337 Value::Map(c) => {
14338 let c = c.borrow();
14339 let constraint_type = c
14340 .get("type")
14341 .and_then(|v| {
14342 if let Value::String(s) = v {
14343 Some((**s).clone())
14344 } else {
14345 None
14346 }
14347 })
14348 .unwrap_or_default();
14349
14350 match constraint_type.as_str() {
14351 "distance" => {
14352 let indices = match c.get("indices") {
14353 Some(Value::Array(arr)) => arr.borrow().clone(),
14354 _ => continue,
14355 };
14356 let target = match c.get("distance") {
14357 Some(Value::Float(f)) => *f,
14358 Some(Value::Int(n)) => *n as f64,
14359 _ => continue,
14360 };
14361
14362 if indices.len() >= 2 {
14363 let i1 = match &indices[0] {
14364 Value::Int(n) => *n as usize,
14365 _ => continue,
14366 };
14367 let i2 = match &indices[1] {
14368 Value::Int(n) => *n as usize,
14369 _ => continue,
14370 };
14371
14372 if i1 < points.len() && i2 < points.len() {
14373 let p1 = extract_vec3(&points[i1], "solve")?;
14375 let p2 = extract_vec3(&points[i2], "solve")?;
14376
14377 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
14378 let length = (delta[0] * delta[0]
14379 + delta[1] * delta[1]
14380 + delta[2] * delta[2])
14381 .sqrt();
14382
14383 if length > 1e-10 {
14384 let correction = (length - target) / length * 0.5;
14385 let corr_vec = [
14386 delta[0] * correction,
14387 delta[1] * correction,
14388 delta[2] * correction,
14389 ];
14390
14391 points[i1] = make_vec3_arr([
14392 p1[0] + corr_vec[0],
14393 p1[1] + corr_vec[1],
14394 p1[2] + corr_vec[2],
14395 ]);
14396 points[i2] = make_vec3_arr([
14397 p2[0] - corr_vec[0],
14398 p2[1] - corr_vec[1],
14399 p2[2] - corr_vec[2],
14400 ]);
14401 }
14402 }
14403 }
14404 }
14405 _ => {}
14406 }
14407 }
14408 _ => continue,
14409 }
14410 }
14411 }
14412
14413 Ok(Value::Array(Rc::new(RefCell::new(points))))
14414 });
14415
14416 define(interp, "ray_sphere_intersect", Some(4), |_, args| {
14419 let origin = extract_vec3(&args[0], "ray_sphere_intersect")?;
14420 let dir = extract_vec3(&args[1], "ray_sphere_intersect")?;
14421 let center = extract_vec3(&args[2], "ray_sphere_intersect")?;
14422 let radius = match &args[3] {
14423 Value::Float(f) => *f,
14424 Value::Int(n) => *n as f64,
14425 _ => {
14426 return Err(RuntimeError::new(
14427 "ray_sphere_intersect: radius must be numeric",
14428 ))
14429 }
14430 };
14431
14432 let oc = [
14433 origin[0] - center[0],
14434 origin[1] - center[1],
14435 origin[2] - center[2],
14436 ];
14437
14438 let a = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2];
14439 let b = 2.0 * (oc[0] * dir[0] + oc[1] * dir[1] + oc[2] * dir[2]);
14440 let c = oc[0] * oc[0] + oc[1] * oc[1] + oc[2] * oc[2] - radius * radius;
14441
14442 let discriminant = b * b - 4.0 * a * c;
14443
14444 if discriminant < 0.0 {
14445 Ok(Value::Float(-1.0))
14446 } else {
14447 let t = (-b - discriminant.sqrt()) / (2.0 * a);
14448 if t > 0.0 {
14449 Ok(Value::Float(t))
14450 } else {
14451 let t2 = (-b + discriminant.sqrt()) / (2.0 * a);
14452 if t2 > 0.0 {
14453 Ok(Value::Float(t2))
14454 } else {
14455 Ok(Value::Float(-1.0))
14456 }
14457 }
14458 }
14459 });
14460
14461 define(interp, "ray_plane_intersect", Some(4), |_, args| {
14463 let origin = extract_vec3(&args[0], "ray_plane_intersect")?;
14464 let dir = extract_vec3(&args[1], "ray_plane_intersect")?;
14465 let plane_pt = extract_vec3(&args[2], "ray_plane_intersect")?;
14466 let normal = extract_vec3(&args[3], "ray_plane_intersect")?;
14467
14468 let denom = dir[0] * normal[0] + dir[1] * normal[1] + dir[2] * normal[2];
14469
14470 if denom.abs() < 1e-10 {
14471 return Ok(Value::Float(-1.0)); }
14473
14474 let diff = [
14475 plane_pt[0] - origin[0],
14476 plane_pt[1] - origin[1],
14477 plane_pt[2] - origin[2],
14478 ];
14479 let t = (diff[0] * normal[0] + diff[1] * normal[1] + diff[2] * normal[2]) / denom;
14480
14481 if t > 0.0 {
14482 Ok(Value::Float(t))
14483 } else {
14484 Ok(Value::Float(-1.0))
14485 }
14486 });
14487}
14488
14489fn register_geometric_algebra(interp: &mut Interpreter) {
14551 fn make_multivector(components: [f64; 8]) -> Value {
14553 let mut mv = HashMap::new();
14554 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(
14563 "_type".to_string(),
14564 Value::String(Rc::new("multivector".to_string())),
14565 );
14566 Value::Map(Rc::new(RefCell::new(mv)))
14567 }
14568
14569 fn extract_multivector(v: &Value, fn_name: &str) -> Result<[f64; 8], RuntimeError> {
14570 match v {
14571 Value::Map(map) => {
14572 let map = map.borrow();
14573 let get_component = |key: &str| -> f64 {
14574 match map.get(key) {
14575 Some(Value::Float(f)) => *f,
14576 Some(Value::Int(n)) => *n as f64,
14577 _ => 0.0,
14578 }
14579 };
14580 Ok([
14581 get_component("s"),
14582 get_component("e1"),
14583 get_component("e2"),
14584 get_component("e3"),
14585 get_component("e12"),
14586 get_component("e23"),
14587 get_component("e31"),
14588 get_component("e123"),
14589 ])
14590 }
14591 _ => Err(RuntimeError::new(format!(
14592 "{}: expected multivector",
14593 fn_name
14594 ))),
14595 }
14596 }
14597
14598 define(interp, "mv_new", Some(8), |_, args| {
14600 let mut components = [0.0f64; 8];
14601 for (i, arg) in args.iter().enumerate().take(8) {
14602 components[i] = match arg {
14603 Value::Float(f) => *f,
14604 Value::Int(n) => *n as f64,
14605 _ => 0.0,
14606 };
14607 }
14608 Ok(make_multivector(components))
14609 });
14610
14611 define(interp, "mv_scalar", Some(1), |_, args| {
14613 let s = match &args[0] {
14614 Value::Float(f) => *f,
14615 Value::Int(n) => *n as f64,
14616 _ => return Err(RuntimeError::new("mv_scalar: expected number")),
14617 };
14618 Ok(make_multivector([s, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))
14619 });
14620
14621 define(interp, "mv_vector", Some(3), |_, args| {
14623 let x = match &args[0] {
14624 Value::Float(f) => *f,
14625 Value::Int(n) => *n as f64,
14626 _ => 0.0,
14627 };
14628 let y = match &args[1] {
14629 Value::Float(f) => *f,
14630 Value::Int(n) => *n as f64,
14631 _ => 0.0,
14632 };
14633 let z = match &args[2] {
14634 Value::Float(f) => *f,
14635 Value::Int(n) => *n as f64,
14636 _ => 0.0,
14637 };
14638 Ok(make_multivector([0.0, x, y, z, 0.0, 0.0, 0.0, 0.0]))
14639 });
14640
14641 define(interp, "mv_bivector", Some(3), |_, args| {
14643 let xy = match &args[0] {
14644 Value::Float(f) => *f,
14645 Value::Int(n) => *n as f64,
14646 _ => 0.0,
14647 };
14648 let yz = match &args[1] {
14649 Value::Float(f) => *f,
14650 Value::Int(n) => *n as f64,
14651 _ => 0.0,
14652 };
14653 let zx = match &args[2] {
14654 Value::Float(f) => *f,
14655 Value::Int(n) => *n as f64,
14656 _ => 0.0,
14657 };
14658 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, xy, yz, zx, 0.0]))
14659 });
14660
14661 define(interp, "mv_trivector", Some(1), |_, args| {
14663 let xyz = match &args[0] {
14664 Value::Float(f) => *f,
14665 Value::Int(n) => *n as f64,
14666 _ => 0.0,
14667 };
14668 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, xyz]))
14669 });
14670
14671 define(interp, "mv_add", Some(2), |_, args| {
14673 let a = extract_multivector(&args[0], "mv_add")?;
14674 let b = extract_multivector(&args[1], "mv_add")?;
14675 Ok(make_multivector([
14676 a[0] + b[0],
14677 a[1] + b[1],
14678 a[2] + b[2],
14679 a[3] + b[3],
14680 a[4] + b[4],
14681 a[5] + b[5],
14682 a[6] + b[6],
14683 a[7] + b[7],
14684 ]))
14685 });
14686
14687 define(interp, "mv_sub", Some(2), |_, args| {
14689 let a = extract_multivector(&args[0], "mv_sub")?;
14690 let b = extract_multivector(&args[1], "mv_sub")?;
14691 Ok(make_multivector([
14692 a[0] - b[0],
14693 a[1] - b[1],
14694 a[2] - b[2],
14695 a[3] - b[3],
14696 a[4] - b[4],
14697 a[5] - b[5],
14698 a[6] - b[6],
14699 a[7] - b[7],
14700 ]))
14701 });
14702
14703 define(interp, "mv_scale", Some(2), |_, args| {
14705 let a = extract_multivector(&args[0], "mv_scale")?;
14706 let s = match &args[1] {
14707 Value::Float(f) => *f,
14708 Value::Int(n) => *n as f64,
14709 _ => {
14710 return Err(RuntimeError::new(
14711 "mv_scale: second argument must be number",
14712 ))
14713 }
14714 };
14715 Ok(make_multivector([
14716 a[0] * s,
14717 a[1] * s,
14718 a[2] * s,
14719 a[3] * s,
14720 a[4] * s,
14721 a[5] * s,
14722 a[6] * s,
14723 a[7] * s,
14724 ]))
14725 });
14726
14727 define(interp, "mv_geometric", Some(2), |_, args| {
14730 let a = extract_multivector(&args[0], "mv_geometric")?;
14731 let b = extract_multivector(&args[1], "mv_geometric")?;
14732
14733 let mut r = [0.0f64; 8];
14736
14737 r[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
14739 - a[4] * b[4]
14740 - a[5] * b[5]
14741 - a[6] * b[6]
14742 - a[7] * b[7];
14743
14744 r[1] = a[0] * b[1] + a[1] * b[0] - a[2] * b[4] + a[3] * b[6] + a[4] * b[2]
14746 - a[5] * b[7]
14747 - a[6] * b[3]
14748 - a[7] * b[5];
14749
14750 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]
14752 - a[6] * b[7]
14753 - a[7] * b[6];
14754
14755 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]
14757 + a[6] * b[1]
14758 - a[7] * b[4];
14759
14760 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]
14762 - a[6] * b[5]
14763 + a[7] * b[3];
14764
14765 r[5] = a[0] * b[5] + a[1] * b[7] + a[2] * b[3] - a[3] * b[2] - a[4] * b[6]
14767 + a[5] * b[0]
14768 + a[6] * b[4]
14769 + a[7] * b[1];
14770
14771 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]
14773 + a[6] * b[0]
14774 + a[7] * b[2];
14775
14776 r[7] = a[0] * b[7]
14778 + a[1] * b[5]
14779 + a[2] * b[6]
14780 + a[3] * b[4]
14781 + a[4] * b[3]
14782 + a[5] * b[1]
14783 + a[6] * b[2]
14784 + a[7] * b[0];
14785
14786 Ok(make_multivector(r))
14787 });
14788
14789 define(interp, "mv_wedge", Some(2), |_, args| {
14792 let a = extract_multivector(&args[0], "mv_wedge")?;
14793 let b = extract_multivector(&args[1], "mv_wedge")?;
14794
14795 let mut r = [0.0f64; 8];
14796
14797 r[0] = a[0] * b[0];
14799
14800 r[1] = a[0] * b[1] + a[1] * b[0];
14802 r[2] = a[0] * b[2] + a[2] * b[0];
14803 r[3] = a[0] * b[3] + a[3] * b[0];
14804
14805 r[4] = a[0] * b[4] + a[1] * b[2] - a[2] * b[1] + a[4] * b[0];
14807 r[5] = a[0] * b[5] + a[2] * b[3] - a[3] * b[2] + a[5] * b[0];
14808 r[6] = a[0] * b[6] + a[3] * b[1] - a[1] * b[3] + a[6] * b[0];
14809
14810 r[7] = a[0] * b[7] + a[7] * b[0] + a[1] * b[5] + a[2] * b[6] + a[3] * b[4]
14812 - a[4] * b[3]
14813 - a[5] * b[1]
14814 - a[6] * b[2];
14815
14816 Ok(make_multivector(r))
14817 });
14818
14819 define(interp, "mv_inner", Some(2), |_, args| {
14822 let a = extract_multivector(&args[0], "mv_inner")?;
14823 let b = extract_multivector(&args[1], "mv_inner")?;
14824
14825 let mut r = [0.0f64; 8];
14826
14827 r[0] = a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
14830 - a[4] * b[4]
14831 - a[5] * b[5]
14832 - a[6] * b[6]
14833 - a[7] * b[7];
14834
14835 r[1] = a[4] * b[2] - a[6] * b[3] - a[5] * b[7];
14837 r[2] = -a[4] * b[1] + a[5] * b[3] - a[6] * b[7];
14838 r[3] = a[6] * b[1] - a[5] * b[2] - a[4] * b[7];
14839
14840 r[4] = a[7] * b[3];
14842 r[5] = a[7] * b[1];
14843 r[6] = a[7] * b[2];
14844
14845 Ok(make_multivector(r))
14846 });
14847
14848 define(interp, "mv_reverse", Some(1), |_, args| {
14851 let a = extract_multivector(&args[0], "mv_reverse")?;
14852 Ok(make_multivector([
14854 a[0], a[1], a[2], a[3], -a[4], -a[5], -a[6], -a[7],
14855 ]))
14856 });
14857
14858 define(interp, "mv_dual", Some(1), |_, args| {
14861 let a = extract_multivector(&args[0], "mv_dual")?;
14862 Ok(make_multivector([
14865 -a[7], -a[5], -a[6], -a[4], a[3], a[1], a[2], a[0], ]))
14874 });
14875
14876 define(interp, "mv_magnitude", Some(1), |_, args| {
14878 let a = extract_multivector(&args[0], "mv_magnitude")?;
14879 let mag_sq = a[0] * a[0]
14880 + a[1] * a[1]
14881 + a[2] * a[2]
14882 + a[3] * a[3]
14883 + a[4] * a[4]
14884 + a[5] * a[5]
14885 + a[6] * a[6]
14886 + a[7] * a[7];
14887 Ok(Value::Float(mag_sq.sqrt()))
14888 });
14889
14890 define(interp, "mv_normalize", Some(1), |_, args| {
14892 let a = extract_multivector(&args[0], "mv_normalize")?;
14893 let mag = (a[0] * a[0]
14894 + a[1] * a[1]
14895 + a[2] * a[2]
14896 + a[3] * a[3]
14897 + a[4] * a[4]
14898 + a[5] * a[5]
14899 + a[6] * a[6]
14900 + a[7] * a[7])
14901 .sqrt();
14902 if mag < 1e-10 {
14903 return Ok(make_multivector([0.0; 8]));
14904 }
14905 Ok(make_multivector([
14906 a[0] / mag,
14907 a[1] / mag,
14908 a[2] / mag,
14909 a[3] / mag,
14910 a[4] / mag,
14911 a[5] / mag,
14912 a[6] / mag,
14913 a[7] / mag,
14914 ]))
14915 });
14916
14917 define(interp, "rotor_from_axis_angle", Some(2), |_, args| {
14920 let axis = extract_vec3(&args[0], "rotor_from_axis_angle")?;
14921 let angle = match &args[1] {
14922 Value::Float(f) => *f,
14923 Value::Int(n) => *n as f64,
14924 _ => {
14925 return Err(RuntimeError::new(
14926 "rotor_from_axis_angle: angle must be number",
14927 ))
14928 }
14929 };
14930
14931 let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
14933 if len < 1e-10 {
14934 return Ok(make_multivector([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]));
14936 }
14937 let (nx, ny, nz) = (axis[0] / len, axis[1] / len, axis[2] / len);
14938
14939 let half_angle = angle / 2.0;
14940 let (s, c) = half_angle.sin_cos();
14941
14942 Ok(make_multivector([
14945 c, 0.0,
14947 0.0,
14948 0.0, -s * nz, -s * nx, -s * ny, 0.0, ]))
14954 });
14955
14956 define(interp, "rotor_apply", Some(2), |_, args| {
14959 let r = extract_multivector(&args[0], "rotor_apply")?;
14960 let v = extract_vec3(&args[1], "rotor_apply")?;
14961
14962 let v_mv = [0.0, v[0], v[1], v[2], 0.0, 0.0, 0.0, 0.0];
14964
14965 let r_rev = [r[0], r[1], r[2], r[3], -r[4], -r[5], -r[6], -r[7]];
14967
14968 let mut rv = [0.0f64; 8];
14970 rv[0] = r[0] * v_mv[0] + r[1] * v_mv[1] + r[2] * v_mv[2] + r[3] * v_mv[3]
14971 - r[4] * v_mv[4]
14972 - r[5] * v_mv[5]
14973 - r[6] * v_mv[6]
14974 - r[7] * v_mv[7];
14975 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]
14976 - r[5] * v_mv[7]
14977 - r[6] * v_mv[3]
14978 - r[7] * v_mv[5];
14979 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]
14980 + r[5] * v_mv[3]
14981 - r[6] * v_mv[7]
14982 - r[7] * v_mv[6];
14983 rv[3] = r[0] * v_mv[3] - r[1] * v_mv[6] + r[2] * v_mv[5] + r[3] * v_mv[0]
14984 - r[4] * v_mv[7]
14985 - r[5] * v_mv[2]
14986 + r[6] * v_mv[1]
14987 - r[7] * v_mv[4];
14988 rv[4] = r[0] * v_mv[4] + r[1] * v_mv[2] - r[2] * v_mv[1]
14989 + r[3] * v_mv[7]
14990 + r[4] * v_mv[0]
14991 + r[5] * v_mv[6]
14992 - r[6] * v_mv[5]
14993 + r[7] * v_mv[3];
14994 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]
14995 + r[5] * v_mv[0]
14996 + r[6] * v_mv[4]
14997 + r[7] * v_mv[1];
14998 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]
14999 - r[5] * v_mv[4]
15000 + r[6] * v_mv[0]
15001 + r[7] * v_mv[2];
15002 rv[7] = r[0] * v_mv[7]
15003 + r[1] * v_mv[5]
15004 + r[2] * v_mv[6]
15005 + r[3] * v_mv[4]
15006 + r[4] * v_mv[3]
15007 + r[5] * v_mv[1]
15008 + r[6] * v_mv[2]
15009 + r[7] * v_mv[0];
15010
15011 let mut result = [0.0f64; 8];
15013 result[1] = rv[0] * r_rev[1] + rv[1] * r_rev[0] - rv[2] * r_rev[4]
15014 + rv[3] * r_rev[6]
15015 + rv[4] * r_rev[2]
15016 - rv[5] * r_rev[7]
15017 - rv[6] * r_rev[3]
15018 - rv[7] * r_rev[5];
15019 result[2] = rv[0] * r_rev[2] + rv[1] * r_rev[4] + rv[2] * r_rev[0]
15020 - rv[3] * r_rev[5]
15021 - rv[4] * r_rev[1]
15022 + rv[5] * r_rev[3]
15023 - rv[6] * r_rev[7]
15024 - rv[7] * r_rev[6];
15025 result[3] = rv[0] * r_rev[3] - rv[1] * r_rev[6] + rv[2] * r_rev[5] + rv[3] * r_rev[0]
15026 - rv[4] * r_rev[7]
15027 - rv[5] * r_rev[2]
15028 + rv[6] * r_rev[1]
15029 - rv[7] * r_rev[4];
15030
15031 Ok(make_vec3(result[1], result[2], result[3]))
15033 });
15034
15035 define(interp, "rotor_compose", Some(2), |_, args| {
15037 let a = extract_multivector(&args[0], "rotor_compose")?;
15038 let b = extract_multivector(&args[1], "rotor_compose")?;
15039
15040 let mut r = [0.0f64; 8];
15042 r[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
15043 - a[4] * b[4]
15044 - a[5] * b[5]
15045 - a[6] * b[6]
15046 - a[7] * b[7];
15047 r[1] = a[0] * b[1] + a[1] * b[0] - a[2] * b[4] + a[3] * b[6] + a[4] * b[2]
15048 - a[5] * b[7]
15049 - a[6] * b[3]
15050 - a[7] * b[5];
15051 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]
15052 - a[6] * b[7]
15053 - a[7] * b[6];
15054 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]
15055 + a[6] * b[1]
15056 - a[7] * b[4];
15057 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]
15058 - a[6] * b[5]
15059 + a[7] * b[3];
15060 r[5] = a[0] * b[5] + a[1] * b[7] + a[2] * b[3] - a[3] * b[2] - a[4] * b[6]
15061 + a[5] * b[0]
15062 + a[6] * b[4]
15063 + a[7] * b[1];
15064 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]
15065 + a[6] * b[0]
15066 + a[7] * b[2];
15067 r[7] = a[0] * b[7]
15068 + a[1] * b[5]
15069 + a[2] * b[6]
15070 + a[3] * b[4]
15071 + a[4] * b[3]
15072 + a[5] * b[1]
15073 + a[6] * b[2]
15074 + a[7] * b[0];
15075
15076 Ok(make_multivector(r))
15077 });
15078
15079 define(interp, "mv_reflect", Some(2), |_, args| {
15082 let v = extract_vec3(&args[0], "mv_reflect")?;
15083 let n = extract_vec3(&args[1], "mv_reflect")?;
15084
15085 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
15087 if len < 1e-10 {
15088 return Ok(make_vec3(v[0], v[1], v[2]));
15089 }
15090 let (nx, ny, nz) = (n[0] / len, n[1] / len, n[2] / len);
15091
15092 let dot = v[0] * nx + v[1] * ny + v[2] * nz;
15094 Ok(make_vec3(
15095 v[0] - 2.0 * dot * nx,
15096 v[1] - 2.0 * dot * ny,
15097 v[2] - 2.0 * dot * nz,
15098 ))
15099 });
15100
15101 define(interp, "mv_project", Some(2), |_, args| {
15103 let v = extract_vec3(&args[0], "mv_project")?;
15104 let n = extract_vec3(&args[1], "mv_project")?;
15105
15106 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
15107 if len < 1e-10 {
15108 return Ok(make_vec3(v[0], v[1], v[2]));
15109 }
15110 let (nx, ny, nz) = (n[0] / len, n[1] / len, n[2] / len);
15111
15112 let dot = v[0] * nx + v[1] * ny + v[2] * nz;
15114 Ok(make_vec3(v[0] - dot * nx, v[1] - dot * ny, v[2] - dot * nz))
15115 });
15116
15117 define(interp, "mv_grade", Some(2), |_, args| {
15119 let a = extract_multivector(&args[0], "mv_grade")?;
15120 let k = match &args[1] {
15121 Value::Int(n) => *n,
15122 _ => return Err(RuntimeError::new("mv_grade: grade must be integer")),
15123 };
15124
15125 match k {
15126 0 => Ok(make_multivector([a[0], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])),
15127 1 => Ok(make_multivector([
15128 0.0, a[1], a[2], a[3], 0.0, 0.0, 0.0, 0.0,
15129 ])),
15130 2 => Ok(make_multivector([
15131 0.0, 0.0, 0.0, 0.0, a[4], a[5], a[6], 0.0,
15132 ])),
15133 3 => Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, a[7]])),
15134 _ => Ok(make_multivector([0.0; 8])),
15135 }
15136 });
15137}
15138
15139fn register_dimensional(interp: &mut Interpreter) {
15146 fn make_quantity(value: f64, units: [i32; 7]) -> Value {
15149 let mut q = HashMap::new();
15150 q.insert("value".to_string(), Value::Float(value));
15151 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(
15159 "_type".to_string(),
15160 Value::String(Rc::new("quantity".to_string())),
15161 );
15162 Value::Map(Rc::new(RefCell::new(q)))
15163 }
15164
15165 fn extract_quantity(v: &Value, fn_name: &str) -> Result<(f64, [i32; 7]), RuntimeError> {
15166 match v {
15167 Value::Map(map) => {
15168 let map = map.borrow();
15169 let value = match map.get("value") {
15170 Some(Value::Float(f)) => *f,
15171 Some(Value::Int(n)) => *n as f64,
15172 _ => return Err(RuntimeError::new(format!("{}: missing value", fn_name))),
15173 };
15174 let get_exp = |key: &str| -> i32 {
15175 match map.get(key) {
15176 Some(Value::Int(n)) => *n as i32,
15177 _ => 0,
15178 }
15179 };
15180 Ok((
15181 value,
15182 [
15183 get_exp("m"),
15184 get_exp("kg"),
15185 get_exp("s"),
15186 get_exp("A"),
15187 get_exp("K"),
15188 get_exp("mol"),
15189 get_exp("cd"),
15190 ],
15191 ))
15192 }
15193 Value::Float(f) => Ok((*f, [0; 7])),
15194 Value::Int(n) => Ok((*n as f64, [0; 7])),
15195 _ => Err(RuntimeError::new(format!(
15196 "{}: expected quantity or number",
15197 fn_name
15198 ))),
15199 }
15200 }
15201
15202 fn units_to_string(units: [i32; 7]) -> String {
15203 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
15204 let mut parts = Vec::new();
15205 for (i, &exp) in units.iter().enumerate() {
15206 if exp == 1 {
15207 parts.push(names[i].to_string());
15208 } else if exp != 0 {
15209 parts.push(format!("{}^{}", names[i], exp));
15210 }
15211 }
15212 if parts.is_empty() {
15213 "dimensionless".to_string()
15214 } else {
15215 parts.join("·")
15216 }
15217 }
15218
15219 define(interp, "qty", Some(2), |_, args| {
15222 let value = match &args[0] {
15223 Value::Float(f) => *f,
15224 Value::Int(n) => *n as f64,
15225 _ => return Err(RuntimeError::new("qty: first argument must be number")),
15226 };
15227 let unit_str = match &args[1] {
15228 Value::String(s) => s.to_string(),
15229 _ => {
15230 return Err(RuntimeError::new(
15231 "qty: second argument must be unit string",
15232 ))
15233 }
15234 };
15235
15236 let mut units = [0i32; 7];
15238 let in_denominator = unit_str.contains('/');
15241
15242 for part in unit_str.split(|c: char| c == '*' || c == '·' || c == ' ') {
15244 let part = part.trim();
15245 if part.is_empty() {
15246 continue;
15247 }
15248
15249 let (base, exp) = if let Some(idx) = part.find('^') {
15250 let (b, e) = part.split_at(idx);
15251 (b, e[1..].parse::<i32>().unwrap_or(1))
15252 } else if part.contains('/') {
15253 continue;
15255 } else {
15256 (part, 1)
15257 };
15258
15259 let sign = if in_denominator { -1 } else { 1 };
15260 match base {
15261 "m" | "meter" | "meters" => units[0] += exp * sign,
15262 "kg" | "kilogram" | "kilograms" => units[1] += exp * sign,
15263 "s" | "sec" | "second" | "seconds" => units[2] += exp * sign,
15264 "A" | "amp" | "ampere" | "amperes" => units[3] += exp * sign,
15265 "K" | "kelvin" => units[4] += exp * sign,
15266 "mol" | "mole" | "moles" => units[5] += exp * sign,
15267 "cd" | "candela" => units[6] += exp * sign,
15268 "N" | "newton" | "newtons" => {
15270 units[1] += sign;
15271 units[0] += sign;
15272 units[2] -= 2 * sign;
15273 }
15274 "J" | "joule" | "joules" => {
15275 units[1] += sign;
15276 units[0] += 2 * sign;
15277 units[2] -= 2 * sign;
15278 }
15279 "W" | "watt" | "watts" => {
15280 units[1] += sign;
15281 units[0] += 2 * sign;
15282 units[2] -= 3 * sign;
15283 }
15284 "Pa" | "pascal" | "pascals" => {
15285 units[1] += sign;
15286 units[0] -= sign;
15287 units[2] -= 2 * sign;
15288 }
15289 "Hz" | "hertz" => {
15290 units[2] -= sign;
15291 }
15292 "C" | "coulomb" | "coulombs" => {
15293 units[3] += sign;
15294 units[2] += sign;
15295 }
15296 "V" | "volt" | "volts" => {
15297 units[1] += sign;
15298 units[0] += 2 * sign;
15299 units[2] -= 3 * sign;
15300 units[3] -= sign;
15301 }
15302 "Ω" | "ohm" | "ohms" => {
15303 units[1] += sign;
15304 units[0] += 2 * sign;
15305 units[2] -= 3 * sign;
15306 units[3] -= 2 * sign;
15307 }
15308 _ => {}
15309 }
15310 }
15311
15312 Ok(make_quantity(value, units))
15313 });
15314
15315 define(interp, "qty_add", Some(2), |_, args| {
15317 let (val_a, units_a) = extract_quantity(&args[0], "qty_add")?;
15318 let (val_b, units_b) = extract_quantity(&args[1], "qty_add")?;
15319
15320 if units_a != units_b {
15321 return Err(RuntimeError::new(format!(
15322 "qty_add: unit mismatch: {} vs {}",
15323 units_to_string(units_a),
15324 units_to_string(units_b)
15325 )));
15326 }
15327
15328 Ok(make_quantity(val_a + val_b, units_a))
15329 });
15330
15331 define(interp, "qty_sub", Some(2), |_, args| {
15333 let (val_a, units_a) = extract_quantity(&args[0], "qty_sub")?;
15334 let (val_b, units_b) = extract_quantity(&args[1], "qty_sub")?;
15335
15336 if units_a != units_b {
15337 return Err(RuntimeError::new(format!(
15338 "qty_sub: unit mismatch: {} vs {}",
15339 units_to_string(units_a),
15340 units_to_string(units_b)
15341 )));
15342 }
15343
15344 Ok(make_quantity(val_a - val_b, units_a))
15345 });
15346
15347 define(interp, "qty_mul", Some(2), |_, args| {
15349 let (val_a, units_a) = extract_quantity(&args[0], "qty_mul")?;
15350 let (val_b, units_b) = extract_quantity(&args[1], "qty_mul")?;
15351
15352 let mut result_units = [0i32; 7];
15353 for i in 0..7 {
15354 result_units[i] = units_a[i] + units_b[i];
15355 }
15356
15357 Ok(make_quantity(val_a * val_b, result_units))
15358 });
15359
15360 define(interp, "qty_div", Some(2), |_, args| {
15362 let (val_a, units_a) = extract_quantity(&args[0], "qty_div")?;
15363 let (val_b, units_b) = extract_quantity(&args[1], "qty_div")?;
15364
15365 if val_b.abs() < 1e-15 {
15366 return Err(RuntimeError::new("qty_div: division by zero"));
15367 }
15368
15369 let mut result_units = [0i32; 7];
15370 for i in 0..7 {
15371 result_units[i] = units_a[i] - units_b[i];
15372 }
15373
15374 Ok(make_quantity(val_a / val_b, result_units))
15375 });
15376
15377 define(interp, "qty_pow", Some(2), |_, args| {
15379 let (val, units) = extract_quantity(&args[0], "qty_pow")?;
15380 let n = match &args[1] {
15381 Value::Int(n) => *n as i32,
15382 Value::Float(f) => *f as i32,
15383 _ => return Err(RuntimeError::new("qty_pow: exponent must be number")),
15384 };
15385
15386 let mut result_units = [0i32; 7];
15387 for i in 0..7 {
15388 result_units[i] = units[i] * n;
15389 }
15390
15391 Ok(make_quantity(val.powi(n), result_units))
15392 });
15393
15394 define(interp, "qty_sqrt", Some(1), |_, args| {
15396 let (val, units) = extract_quantity(&args[0], "qty_sqrt")?;
15397
15398 for (i, &exp) in units.iter().enumerate() {
15400 if exp % 2 != 0 {
15401 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
15402 return Err(RuntimeError::new(format!(
15403 "qty_sqrt: cannot take sqrt of {} (odd exponent on {})",
15404 units_to_string(units),
15405 names[i]
15406 )));
15407 }
15408 }
15409
15410 let mut result_units = [0i32; 7];
15411 for i in 0..7 {
15412 result_units[i] = units[i] / 2;
15413 }
15414
15415 Ok(make_quantity(val.sqrt(), result_units))
15416 });
15417
15418 define(interp, "qty_value", Some(1), |_, args| {
15420 let (val, _) = extract_quantity(&args[0], "qty_value")?;
15421 Ok(Value::Float(val))
15422 });
15423
15424 define(interp, "qty_units", Some(1), |_, args| {
15426 let (_, units) = extract_quantity(&args[0], "qty_units")?;
15427 Ok(Value::String(Rc::new(units_to_string(units))))
15428 });
15429
15430 define(interp, "qty_convert", Some(2), |_, args| {
15433 let (val, units) = extract_quantity(&args[0], "qty_convert")?;
15434 let _target = match &args[1] {
15435 Value::String(s) => s.to_string(),
15436 _ => return Err(RuntimeError::new("qty_convert: target must be unit string")),
15437 };
15438
15439 Ok(make_quantity(val, units))
15442 });
15443
15444 define(interp, "qty_check", Some(2), |_, args| {
15446 let (_, units) = extract_quantity(&args[0], "qty_check")?;
15447 let expected = match &args[1] {
15448 Value::String(s) => s.to_string(),
15449 _ => return Err(RuntimeError::new("qty_check: expected string")),
15450 };
15451
15452 let actual_str = units_to_string(units);
15454 Ok(Value::Bool(
15455 actual_str.contains(&expected) || expected.contains(&actual_str),
15456 ))
15457 });
15458
15459 define(interp, "c_light", Some(0), |_, _| {
15462 Ok(make_quantity(299792458.0, [1, 0, -1, 0, 0, 0, 0])) });
15464
15465 define(interp, "G_gravity", Some(0), |_, _| {
15467 Ok(make_quantity(6.67430e-11, [3, -1, -2, 0, 0, 0, 0])) });
15469
15470 define(interp, "h_planck", Some(0), |_, _| {
15472 Ok(make_quantity(6.62607015e-34, [2, 1, -1, 0, 0, 0, 0])) });
15474
15475 define(interp, "e_charge", Some(0), |_, _| {
15477 Ok(make_quantity(1.602176634e-19, [0, 0, 1, 1, 0, 0, 0])) });
15479
15480 define(interp, "k_boltzmann", Some(0), |_, _| {
15482 Ok(make_quantity(1.380649e-23, [2, 1, -2, 0, -1, 0, 0])) });
15484}
15485
15486fn register_ecs(interp: &mut Interpreter) {
15566 define(interp, "ecs_world", Some(0), |_, _| {
15568 let mut world = HashMap::new();
15569 world.insert(
15570 "_type".to_string(),
15571 Value::String(Rc::new("ecs_world".to_string())),
15572 );
15573 world.insert("next_id".to_string(), Value::Int(0));
15574 world.insert(
15575 "entities".to_string(),
15576 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
15577 );
15578 world.insert(
15579 "components".to_string(),
15580 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
15581 );
15582 Ok(Value::Map(Rc::new(RefCell::new(world))))
15583 });
15584
15585 define(interp, "ecs_spawn", Some(1), |_, args| {
15587 let world = match &args[0] {
15588 Value::Map(m) => m.clone(),
15589 _ => return Err(RuntimeError::new("ecs_spawn: expected world")),
15590 };
15591
15592 let mut world_ref = world.borrow_mut();
15593 let id = match world_ref.get("next_id") {
15594 Some(Value::Int(n)) => *n,
15595 _ => 0,
15596 };
15597
15598 world_ref.insert("next_id".to_string(), Value::Int(id + 1));
15600
15601 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15603 entities
15604 .borrow_mut()
15605 .insert(id.to_string(), Value::Bool(true));
15606 }
15607
15608 Ok(Value::Int(id))
15609 });
15610
15611 define(interp, "ecs_despawn", Some(2), |_, args| {
15613 let world = match &args[0] {
15614 Value::Map(m) => m.clone(),
15615 _ => return Err(RuntimeError::new("ecs_despawn: expected world")),
15616 };
15617 let id = match &args[1] {
15618 Value::Int(n) => *n,
15619 _ => return Err(RuntimeError::new("ecs_despawn: expected entity id")),
15620 };
15621
15622 let world_ref = world.borrow();
15623
15624 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15626 entities.borrow_mut().remove(&id.to_string());
15627 }
15628
15629 if let Some(Value::Map(components)) = world_ref.get("components") {
15631 let comps = components.borrow();
15632 for (_, comp_storage) in comps.iter() {
15633 if let Value::Map(storage) = comp_storage {
15634 storage.borrow_mut().remove(&id.to_string());
15635 }
15636 }
15637 }
15638
15639 Ok(Value::Bool(true))
15640 });
15641
15642 define(interp, "ecs_attach", Some(4), |_, args| {
15644 let world = match &args[0] {
15645 Value::Map(m) => m.clone(),
15646 _ => {
15647 return Err(RuntimeError::new(
15648 "ecs_attach() expects a world as first argument.\n\
15649 Usage: ecs_attach(world, entity_id, component_name, data)\n\
15650 Example:\n\
15651 let world = ecs_world();\n\
15652 let e = ecs_spawn(world);\n\
15653 ecs_attach(world, e, \"Position\", vec3(0, 0, 0));",
15654 ))
15655 }
15656 };
15657 let id = match &args[1] {
15658 Value::Int(n) => *n,
15659 _ => {
15660 return Err(RuntimeError::new(
15661 "ecs_attach() expects an entity ID (integer) as second argument.\n\
15662 Entity IDs are returned by ecs_spawn().\n\
15663 Example:\n\
15664 let entity = ecs_spawn(world); // Returns 0, 1, 2...\n\
15665 ecs_attach(world, entity, \"Health\", 100);",
15666 ))
15667 }
15668 };
15669 let comp_name = match &args[2] {
15670 Value::String(s) => s.to_string(),
15671 _ => {
15672 return Err(RuntimeError::new(
15673 "ecs_attach() expects a string component name as third argument.\n\
15674 Common component names: \"Position\", \"Velocity\", \"Health\", \"Sprite\"\n\
15675 Example: ecs_attach(world, entity, \"Position\", vec3(0, 0, 0));",
15676 ))
15677 }
15678 };
15679 let data = args[3].clone();
15680
15681 let world_ref = world.borrow();
15682
15683 if let Some(Value::Map(components)) = world_ref.get("components") {
15685 let mut comps = components.borrow_mut();
15686
15687 let storage = comps
15688 .entry(comp_name.clone())
15689 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
15690
15691 if let Value::Map(storage_map) = storage {
15692 storage_map.borrow_mut().insert(id.to_string(), data);
15693 }
15694 }
15695
15696 Ok(Value::Bool(true))
15697 });
15698
15699 define(interp, "ecs_get", Some(3), |_, args| {
15701 let world = match &args[0] {
15702 Value::Map(m) => m.clone(),
15703 _ => return Err(RuntimeError::new("ecs_get: expected world")),
15704 };
15705 let id = match &args[1] {
15706 Value::Int(n) => *n,
15707 _ => return Err(RuntimeError::new("ecs_get: expected entity id")),
15708 };
15709 let comp_name = match &args[2] {
15710 Value::String(s) => s.to_string(),
15711 _ => return Err(RuntimeError::new("ecs_get: expected component name")),
15712 };
15713
15714 let world_ref = world.borrow();
15715
15716 if let Some(Value::Map(components)) = world_ref.get("components") {
15717 let comps = components.borrow();
15718 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15719 if let Some(data) = storage.borrow().get(&id.to_string()) {
15720 return Ok(data.clone());
15721 }
15722 }
15723 }
15724
15725 Ok(Value::Null)
15726 });
15727
15728 define(interp, "ecs_has", Some(3), |_, args| {
15730 let world = match &args[0] {
15731 Value::Map(m) => m.clone(),
15732 _ => return Err(RuntimeError::new("ecs_has: expected world")),
15733 };
15734 let id = match &args[1] {
15735 Value::Int(n) => *n,
15736 _ => return Err(RuntimeError::new("ecs_has: expected entity id")),
15737 };
15738 let comp_name = match &args[2] {
15739 Value::String(s) => s.to_string(),
15740 _ => return Err(RuntimeError::new("ecs_has: expected component name")),
15741 };
15742
15743 let world_ref = world.borrow();
15744
15745 if let Some(Value::Map(components)) = world_ref.get("components") {
15746 let comps = components.borrow();
15747 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15748 return Ok(Value::Bool(storage.borrow().contains_key(&id.to_string())));
15749 }
15750 }
15751
15752 Ok(Value::Bool(false))
15753 });
15754
15755 define(interp, "ecs_remove", Some(3), |_, args| {
15757 let world = match &args[0] {
15758 Value::Map(m) => m.clone(),
15759 _ => return Err(RuntimeError::new("ecs_remove: expected world")),
15760 };
15761 let id = match &args[1] {
15762 Value::Int(n) => *n,
15763 _ => return Err(RuntimeError::new("ecs_remove: expected entity id")),
15764 };
15765 let comp_name = match &args[2] {
15766 Value::String(s) => s.to_string(),
15767 _ => return Err(RuntimeError::new("ecs_remove: expected component name")),
15768 };
15769
15770 let world_ref = world.borrow();
15771
15772 if let Some(Value::Map(components)) = world_ref.get("components") {
15773 let comps = components.borrow();
15774 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
15775 storage.borrow_mut().remove(&id.to_string());
15776 return Ok(Value::Bool(true));
15777 }
15778 }
15779
15780 Ok(Value::Bool(false))
15781 });
15782
15783 define(interp, "ecs_query", None, |_, args| {
15786 if args.is_empty() {
15787 return Err(RuntimeError::new(
15788 "ecs_query: expected at least world argument",
15789 ));
15790 }
15791
15792 let world = match &args[0] {
15793 Value::Map(m) => m.clone(),
15794 _ => return Err(RuntimeError::new("ecs_query: expected world")),
15795 };
15796
15797 let comp_names: Vec<String> = args[1..]
15798 .iter()
15799 .filter_map(|a| match a {
15800 Value::String(s) => Some(s.to_string()),
15801 _ => None,
15802 })
15803 .collect();
15804
15805 if comp_names.is_empty() {
15806 let world_ref = world.borrow();
15808 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15809 let result: Vec<Value> = entities
15810 .borrow()
15811 .keys()
15812 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
15813 .collect();
15814 return Ok(Value::Array(Rc::new(RefCell::new(result))));
15815 }
15816 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15817 }
15818
15819 let world_ref = world.borrow();
15820 let mut result_ids: Option<Vec<String>> = None;
15821
15822 if let Some(Value::Map(components)) = world_ref.get("components") {
15823 let comps = components.borrow();
15824
15825 for comp_name in &comp_names {
15826 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15827 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
15828
15829 result_ids = Some(match result_ids {
15830 None => keys,
15831 Some(existing) => {
15832 existing.into_iter().filter(|k| keys.contains(k)).collect()
15833 }
15834 });
15835 } else {
15836 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15838 }
15839 }
15840 }
15841
15842 let result: Vec<Value> = result_ids
15843 .unwrap_or_default()
15844 .iter()
15845 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
15846 .collect();
15847
15848 Ok(Value::Array(Rc::new(RefCell::new(result))))
15849 });
15850
15851 define(interp, "ecs_query_with", Some(3), |interp, args| {
15854 let world = match &args[0] {
15855 Value::Map(m) => m.clone(),
15856 _ => return Err(RuntimeError::new("ecs_query_with: expected world")),
15857 };
15858 let comp_names: Vec<String> = match &args[1] {
15859 Value::Array(arr) => arr
15860 .borrow()
15861 .iter()
15862 .filter_map(|v| match v {
15863 Value::String(s) => Some(s.to_string()),
15864 _ => None,
15865 })
15866 .collect(),
15867 _ => {
15868 return Err(RuntimeError::new(
15869 "ecs_query_with: expected array of component names",
15870 ))
15871 }
15872 };
15873 let callback = match &args[2] {
15874 Value::Function(f) => f.clone(),
15875 _ => {
15876 return Err(RuntimeError::new(
15877 "ecs_query_with: expected callback function",
15878 ))
15879 }
15880 };
15881
15882 let mut callback_data: Vec<(i64, HashMap<String, Value>)> = Vec::new();
15884
15885 {
15886 let world_ref = world.borrow();
15887 let mut result_ids: Option<Vec<String>> = None;
15888
15889 if let Some(Value::Map(components)) = world_ref.get("components") {
15890 let comps = components.borrow();
15891
15892 for comp_name in &comp_names {
15893 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15894 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
15895 result_ids = Some(match result_ids {
15896 None => keys,
15897 Some(existing) => {
15898 existing.into_iter().filter(|k| keys.contains(k)).collect()
15899 }
15900 });
15901 } else {
15902 result_ids = Some(vec![]);
15903 break;
15904 }
15905 }
15906
15907 for id_str in result_ids.unwrap_or_default() {
15909 if let Ok(id) = id_str.parse::<i64>() {
15910 let mut entity_comps = HashMap::new();
15911 for comp_name in &comp_names {
15912 if let Some(Value::Map(storage)) = comps.get(comp_name) {
15913 if let Some(data) = storage.borrow().get(&id_str) {
15914 entity_comps.insert(comp_name.clone(), data.clone());
15915 }
15916 }
15917 }
15918 callback_data.push((id, entity_comps));
15919 }
15920 }
15921 }
15922 } for (id, entity_comps) in callback_data {
15926 let callback_args = vec![
15927 Value::Int(id),
15928 Value::Map(Rc::new(RefCell::new(entity_comps))),
15929 ];
15930 interp.call_function(&callback, callback_args)?;
15931 }
15932
15933 Ok(Value::Null)
15934 });
15935
15936 define(interp, "ecs_count", Some(1), |_, args| {
15938 let world = match &args[0] {
15939 Value::Map(m) => m.clone(),
15940 _ => return Err(RuntimeError::new("ecs_count: expected world")),
15941 };
15942
15943 let world_ref = world.borrow();
15944 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15945 return Ok(Value::Int(entities.borrow().len() as i64));
15946 }
15947
15948 Ok(Value::Int(0))
15949 });
15950
15951 define(interp, "ecs_alive", Some(2), |_, args| {
15953 let world = match &args[0] {
15954 Value::Map(m) => m.clone(),
15955 _ => return Err(RuntimeError::new("ecs_alive: expected world")),
15956 };
15957 let id = match &args[1] {
15958 Value::Int(n) => *n,
15959 _ => return Err(RuntimeError::new("ecs_alive: expected entity id")),
15960 };
15961
15962 let world_ref = world.borrow();
15963 if let Some(Value::Map(entities)) = world_ref.get("entities") {
15964 return Ok(Value::Bool(entities.borrow().contains_key(&id.to_string())));
15965 }
15966
15967 Ok(Value::Bool(false))
15968 });
15969}
15970
15971fn register_polycultural_text(interp: &mut Interpreter) {
15991 define(interp, "script", Some(1), |_, args| {
16001 match &args[0] {
16002 Value::String(s) => {
16003 let mut script_counts: HashMap<String, usize> = HashMap::new();
16005 for c in s.chars() {
16006 if !c.is_whitespace() && !c.is_ascii_punctuation() {
16007 let script = c.script();
16008 let name = format!("{:?}", script);
16009 *script_counts.entry(name).or_insert(0) += 1;
16010 }
16011 }
16012 let dominant = script_counts
16014 .into_iter()
16015 .max_by_key(|(_, count)| *count)
16016 .map(|(name, _)| name)
16017 .unwrap_or_else(|| "Unknown".to_string());
16018 Ok(Value::String(Rc::new(dominant)))
16019 }
16020 _ => Err(RuntimeError::new("script() requires string")),
16021 }
16022 });
16023
16024 define(interp, "scripts", Some(1), |_, args| match &args[0] {
16026 Value::String(s) => {
16027 let mut scripts: Vec<String> = s
16028 .chars()
16029 .filter(|c| !c.is_whitespace() && !c.is_ascii_punctuation())
16030 .map(|c| format!("{:?}", c.script()))
16031 .collect();
16032 scripts.sort();
16033 scripts.dedup();
16034 let values: Vec<Value> = scripts
16035 .into_iter()
16036 .map(|s| Value::String(Rc::new(s)))
16037 .collect();
16038 Ok(Value::Array(Rc::new(RefCell::new(values))))
16039 }
16040 _ => Err(RuntimeError::new("scripts() requires string")),
16041 });
16042
16043 define(interp, "is_script", Some(2), |_, args| {
16045 match (&args[0], &args[1]) {
16046 (Value::String(s), Value::String(script_name)) => {
16047 let target = script_name.to_lowercase();
16048 let mut matching = 0usize;
16049 let mut total = 0usize;
16050 for c in s.chars() {
16051 if !c.is_whitespace() && !c.is_ascii_punctuation() {
16052 total += 1;
16053 let script_str = format!("{:?}", c.script()).to_lowercase();
16054 if script_str == target {
16055 matching += 1;
16056 }
16057 }
16058 }
16059 let ratio = if total > 0 {
16060 matching as f64 / total as f64
16061 } else {
16062 0.0
16063 };
16064 Ok(Value::Bool(ratio > 0.5))
16065 }
16066 _ => Err(RuntimeError::new(
16067 "is_script() requires string and script name",
16068 )),
16069 }
16070 });
16071
16072 define(interp, "is_latin", Some(1), |_, args| match &args[0] {
16074 Value::String(s) => {
16075 let is_latin = s
16076 .chars()
16077 .filter(|c| !c.is_whitespace())
16078 .all(|c| matches!(c.script(), Script::Latin | Script::Common));
16079 Ok(Value::Bool(is_latin && !s.is_empty()))
16080 }
16081 _ => Err(RuntimeError::new("is_latin() requires string")),
16082 });
16083
16084 define(interp, "is_cjk", Some(1), |_, args| match &args[0] {
16085 Value::String(s) => {
16086 let has_cjk = s.chars().any(|c| {
16087 matches!(
16088 c.script(),
16089 Script::Han
16090 | Script::Hiragana
16091 | Script::Katakana
16092 | Script::Hangul
16093 | Script::Bopomofo
16094 )
16095 });
16096 Ok(Value::Bool(has_cjk))
16097 }
16098 _ => Err(RuntimeError::new("is_cjk() requires string")),
16099 });
16100
16101 define(interp, "is_arabic", Some(1), |_, args| match &args[0] {
16102 Value::String(s) => {
16103 let has_arabic = s.chars().any(|c| matches!(c.script(), Script::Arabic));
16104 Ok(Value::Bool(has_arabic))
16105 }
16106 _ => Err(RuntimeError::new("is_arabic() requires string")),
16107 });
16108
16109 define(interp, "is_hebrew", Some(1), |_, args| match &args[0] {
16110 Value::String(s) => {
16111 let has_hebrew = s.chars().any(|c| matches!(c.script(), Script::Hebrew));
16112 Ok(Value::Bool(has_hebrew))
16113 }
16114 _ => Err(RuntimeError::new("is_hebrew() requires string")),
16115 });
16116
16117 define(interp, "is_cyrillic", Some(1), |_, args| match &args[0] {
16118 Value::String(s) => {
16119 let has_cyrillic = s.chars().any(|c| matches!(c.script(), Script::Cyrillic));
16120 Ok(Value::Bool(has_cyrillic))
16121 }
16122 _ => Err(RuntimeError::new("is_cyrillic() requires string")),
16123 });
16124
16125 define(interp, "is_greek", Some(1), |_, args| match &args[0] {
16126 Value::String(s) => {
16127 let has_greek = s.chars().any(|c| matches!(c.script(), Script::Greek));
16128 Ok(Value::Bool(has_greek))
16129 }
16130 _ => Err(RuntimeError::new("is_greek() requires string")),
16131 });
16132
16133 define(interp, "is_devanagari", Some(1), |_, args| match &args[0] {
16134 Value::String(s) => {
16135 let has_devanagari = s.chars().any(|c| matches!(c.script(), Script::Devanagari));
16136 Ok(Value::Bool(has_devanagari))
16137 }
16138 _ => Err(RuntimeError::new("is_devanagari() requires string")),
16139 });
16140
16141 define(interp, "is_thai", Some(1), |_, args| match &args[0] {
16142 Value::String(s) => {
16143 let has_thai = s.chars().any(|c| matches!(c.script(), Script::Thai));
16144 Ok(Value::Bool(has_thai))
16145 }
16146 _ => Err(RuntimeError::new("is_thai() requires string")),
16147 });
16148
16149 define(interp, "is_hangul", Some(1), |_, args| match &args[0] {
16150 Value::String(s) => {
16151 let has_hangul = s.chars().any(|c| matches!(c.script(), Script::Hangul));
16152 Ok(Value::Bool(has_hangul))
16153 }
16154 _ => Err(RuntimeError::new("is_hangul() requires string")),
16155 });
16156
16157 define(interp, "is_hiragana", Some(1), |_, args| match &args[0] {
16158 Value::String(s) => {
16159 let has_hiragana = s.chars().any(|c| matches!(c.script(), Script::Hiragana));
16160 Ok(Value::Bool(has_hiragana))
16161 }
16162 _ => Err(RuntimeError::new("is_hiragana() requires string")),
16163 });
16164
16165 define(interp, "is_katakana", Some(1), |_, args| match &args[0] {
16166 Value::String(s) => {
16167 let has_katakana = s.chars().any(|c| matches!(c.script(), Script::Katakana));
16168 Ok(Value::Bool(has_katakana))
16169 }
16170 _ => Err(RuntimeError::new("is_katakana() requires string")),
16171 });
16172
16173 define(interp, "char_script", Some(1), |_, args| match &args[0] {
16175 Value::Char(c) => {
16176 let script = format!("{:?}", c.script());
16177 Ok(Value::String(Rc::new(script)))
16178 }
16179 Value::String(s) if s.chars().count() == 1 => {
16180 let c = s.chars().next().unwrap();
16181 let script = format!("{:?}", c.script());
16182 Ok(Value::String(Rc::new(script)))
16183 }
16184 _ => Err(RuntimeError::new("char_script() requires single character")),
16185 });
16186
16187 define(interp, "text_direction", Some(1), |_, args| {
16197 match &args[0] {
16198 Value::String(s) => {
16199 let bidi_info = BidiInfo::new(s, None);
16200 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
16202 let direction = if has_rtl { "rtl" } else { "ltr" };
16203 Ok(Value::String(Rc::new(direction.to_string())))
16204 }
16205 _ => Err(RuntimeError::new("text_direction() requires string")),
16206 }
16207 });
16208
16209 define(interp, "is_rtl", Some(1), |_, args| match &args[0] {
16211 Value::String(s) => {
16212 let bidi_info = BidiInfo::new(s, None);
16213 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
16214 Ok(Value::Bool(has_rtl))
16215 }
16216 _ => Err(RuntimeError::new("is_rtl() requires string")),
16217 });
16218
16219 define(interp, "is_ltr", Some(1), |_, args| match &args[0] {
16221 Value::String(s) => {
16222 let bidi_info = BidiInfo::new(s, None);
16223 let is_ltr = bidi_info.paragraphs.iter().all(|p| !p.level.is_rtl());
16224 Ok(Value::Bool(is_ltr))
16225 }
16226 _ => Err(RuntimeError::new("is_ltr() requires string")),
16227 });
16228
16229 define(interp, "is_bidi", Some(1), |_, args| {
16231 match &args[0] {
16232 Value::String(s) => {
16233 let has_rtl = s.chars().any(|c| {
16235 matches!(
16236 c.script(),
16237 Script::Arabic | Script::Hebrew | Script::Syriac | Script::Thaana
16238 )
16239 });
16240 let has_ltr = s.chars().any(|c| {
16241 matches!(c.script(), Script::Latin | Script::Greek | Script::Cyrillic)
16242 });
16243 Ok(Value::Bool(has_rtl && has_ltr))
16244 }
16245 _ => Err(RuntimeError::new("is_bidi() requires string")),
16246 }
16247 });
16248
16249 define(interp, "bidi_reorder", Some(1), |_, args| match &args[0] {
16251 Value::String(s) => {
16252 let bidi_info = BidiInfo::new(s, None);
16253 let mut result = String::new();
16254 for para in &bidi_info.paragraphs {
16255 let line = para.range.clone();
16256 let reordered = bidi_info.reorder_line(para, line);
16257 result.push_str(&reordered);
16258 }
16259 Ok(Value::String(Rc::new(result)))
16260 }
16261 _ => Err(RuntimeError::new("bidi_reorder() requires string")),
16262 });
16263
16264 define(interp, "display_width", Some(1), |_, args| match &args[0] {
16274 Value::String(s) => {
16275 let width = UnicodeWidthStr::width(s.as_str());
16276 Ok(Value::Int(width as i64))
16277 }
16278 _ => Err(RuntimeError::new("display_width() requires string")),
16279 });
16280
16281 define(interp, "is_fullwidth", Some(1), |_, args| {
16283 match &args[0] {
16284 Value::String(s) => {
16285 let char_count = s.chars().count();
16286 let display_width = UnicodeWidthStr::width(s.as_str());
16287 Ok(Value::Bool(display_width > char_count))
16289 }
16290 _ => Err(RuntimeError::new("is_fullwidth() requires string")),
16291 }
16292 });
16293
16294 define(interp, "pad_display", Some(3), |_, args| {
16296 match (&args[0], &args[1], &args[2]) {
16297 (Value::String(s), Value::Int(target_width), Value::String(align)) => {
16298 let current_width = UnicodeWidthStr::width(s.as_str());
16299 let target = *target_width as usize;
16300 if current_width >= target {
16301 return Ok(Value::String(s.clone()));
16302 }
16303 let padding = target - current_width;
16304 let result = match align.as_str() {
16305 "left" => format!("{}{}", s, " ".repeat(padding)),
16306 "right" => format!("{}{}", " ".repeat(padding), s),
16307 "center" => {
16308 let left = padding / 2;
16309 let right = padding - left;
16310 format!("{}{}{}", " ".repeat(left), s, " ".repeat(right))
16311 }
16312 _ => {
16313 return Err(RuntimeError::new(
16314 "pad_display: align must be 'left', 'right', or 'center'",
16315 ))
16316 }
16317 };
16318 Ok(Value::String(Rc::new(result)))
16319 }
16320 _ => Err(RuntimeError::new(
16321 "pad_display() requires string, width, and alignment",
16322 )),
16323 }
16324 });
16325
16326 define(interp, "transliterate", Some(1), |_, args| match &args[0] {
16336 Value::String(s) => {
16337 let ascii = deunicode(s);
16338 Ok(Value::String(Rc::new(ascii)))
16339 }
16340 _ => Err(RuntimeError::new("transliterate() requires string")),
16341 });
16342
16343 define(interp, "to_ascii", Some(1), |_, args| match &args[0] {
16345 Value::String(s) => {
16346 let ascii = deunicode(s);
16347 Ok(Value::String(Rc::new(ascii)))
16348 }
16349 _ => Err(RuntimeError::new("to_ascii() requires string")),
16350 });
16351
16352 define(interp, "slugify", Some(1), |_, args| {
16354 match &args[0] {
16355 Value::String(s) => {
16356 let ascii = deunicode(s);
16357 let slug: String = ascii
16358 .to_lowercase()
16359 .chars()
16360 .map(|c| if c.is_alphanumeric() { c } else { '-' })
16361 .collect();
16362 let mut result = String::new();
16364 let mut last_was_dash = true; for c in slug.chars() {
16366 if c == '-' {
16367 if !last_was_dash {
16368 result.push(c);
16369 last_was_dash = true;
16370 }
16371 } else {
16372 result.push(c);
16373 last_was_dash = false;
16374 }
16375 }
16376 if result.ends_with('-') {
16378 result.pop();
16379 }
16380 Ok(Value::String(Rc::new(result)))
16381 }
16382 _ => Err(RuntimeError::new("slugify() requires string")),
16383 }
16384 });
16385
16386 define(interp, "strip_diacritics", Some(1), |_, args| {
16396 match &args[0] {
16397 Value::String(s) => {
16398 let decomposed: String = s.nfd().collect();
16400 let stripped: String = decomposed
16402 .chars()
16403 .filter(|c| {
16404 let code = *c as u32;
16408 !(0x0300..=0x036F).contains(&code)
16410 && !(0x1AB0..=0x1AFF).contains(&code)
16411 && !(0x1DC0..=0x1DFF).contains(&code)
16412 && !(0x20D0..=0x20FF).contains(&code)
16413 && !(0xFE20..=0xFE2F).contains(&code)
16414 })
16415 .collect();
16416 Ok(Value::String(Rc::new(stripped)))
16417 }
16418 _ => Err(RuntimeError::new("strip_diacritics() requires string")),
16419 }
16420 });
16421
16422 define(interp, "has_diacritics", Some(1), |_, args| {
16424 match &args[0] {
16425 Value::String(s) => {
16426 let decomposed: String = s.nfd().collect();
16427 let has_marks = decomposed.chars().any(|c| {
16428 let code = c as u32;
16429 (0x0300..=0x036F).contains(&code)
16430 || (0x1AB0..=0x1AFF).contains(&code)
16431 || (0x1DC0..=0x1DFF).contains(&code)
16432 || (0x20D0..=0x20FF).contains(&code)
16433 || (0xFE20..=0xFE2F).contains(&code)
16434 });
16435 Ok(Value::Bool(has_marks))
16436 }
16437 _ => Err(RuntimeError::new("has_diacritics() requires string")),
16438 }
16439 });
16440
16441 define(interp, "normalize_accents", Some(2), |_, args| {
16443 match (&args[0], &args[1]) {
16444 (Value::String(s), Value::String(form)) => {
16445 let result = match form.as_str() {
16446 "composed" | "nfc" => s.nfc().collect(),
16447 "decomposed" | "nfd" => s.nfd().collect(),
16448 _ => {
16449 return Err(RuntimeError::new(
16450 "normalize_accents: form must be 'composed' or 'decomposed'",
16451 ))
16452 }
16453 };
16454 Ok(Value::String(Rc::new(result)))
16455 }
16456 _ => Err(RuntimeError::new(
16457 "normalize_accents() requires string and form",
16458 )),
16459 }
16460 });
16461
16462 define(interp, "upper_locale", Some(2), |_, args| {
16474 match (&args[0], &args[1]) {
16475 (Value::String(s), Value::String(locale_str)) => {
16476 let case_mapper = CaseMapper::new();
16477 let langid: LanguageIdentifier =
16478 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16479 let result = case_mapper.uppercase_to_string(s, &langid);
16480 Ok(Value::String(Rc::new(result)))
16481 }
16482 _ => Err(RuntimeError::new(
16483 "upper_locale() requires string and locale",
16484 )),
16485 }
16486 });
16487
16488 define(interp, "lower_locale", Some(2), |_, args| {
16490 match (&args[0], &args[1]) {
16491 (Value::String(s), Value::String(locale_str)) => {
16492 let case_mapper = CaseMapper::new();
16493 let langid: LanguageIdentifier =
16494 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16495 let result = case_mapper.lowercase_to_string(s, &langid);
16496 Ok(Value::String(Rc::new(result)))
16497 }
16498 _ => Err(RuntimeError::new(
16499 "lower_locale() requires string and locale",
16500 )),
16501 }
16502 });
16503
16504 define(interp, "titlecase_locale", Some(2), |_, args| {
16506 match (&args[0], &args[1]) {
16507 (Value::String(s), Value::String(locale_str)) => {
16508 let case_mapper = CaseMapper::new();
16509 let langid: LanguageIdentifier =
16510 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16511 let options = TitlecaseOptions::default();
16512 let result = case_mapper
16513 .titlecase_segment_with_only_case_data_to_string(s, &langid, options);
16514 Ok(Value::String(Rc::new(result)))
16515 }
16516 _ => Err(RuntimeError::new(
16517 "titlecase_locale() requires string and locale",
16518 )),
16519 }
16520 });
16521
16522 define(interp, "case_fold", Some(1), |_, args| match &args[0] {
16524 Value::String(s) => {
16525 let case_mapper = CaseMapper::new();
16526 let result = case_mapper.fold_string(s);
16527 Ok(Value::String(Rc::new(result)))
16528 }
16529 _ => Err(RuntimeError::new("case_fold() requires string")),
16530 });
16531
16532 define(interp, "case_insensitive_eq", Some(2), |_, args| {
16534 match (&args[0], &args[1]) {
16535 (Value::String(a), Value::String(b)) => {
16536 let case_mapper = CaseMapper::new();
16537 let folded_a = case_mapper.fold_string(a);
16538 let folded_b = case_mapper.fold_string(b);
16539 Ok(Value::Bool(folded_a == folded_b))
16540 }
16541 _ => Err(RuntimeError::new(
16542 "case_insensitive_eq() requires two strings",
16543 )),
16544 }
16545 });
16546
16547 define(interp, "compare_locale", Some(3), |_, args| {
16559 match (&args[0], &args[1], &args[2]) {
16560 (Value::String(a), Value::String(b), Value::String(locale_str)) => {
16561 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16562 let options = CollatorOptions::new();
16563 let collator = Collator::try_new(&locale.into(), options)
16564 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
16565 let result = match collator.compare(a, b) {
16566 std::cmp::Ordering::Less => -1,
16567 std::cmp::Ordering::Equal => 0,
16568 std::cmp::Ordering::Greater => 1,
16569 };
16570 Ok(Value::Int(result))
16571 }
16572 _ => Err(RuntimeError::new(
16573 "compare_locale() requires two strings and locale",
16574 )),
16575 }
16576 });
16577
16578 define(interp, "sort_locale", Some(2), |_, args| {
16580 match (&args[0], &args[1]) {
16581 (Value::Array(arr), Value::String(locale_str)) => {
16582 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
16583 let options = CollatorOptions::new();
16584 let collator = Collator::try_new(&locale.into(), options)
16585 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
16586
16587 let mut items: Vec<(String, Value)> = arr
16588 .borrow()
16589 .iter()
16590 .map(|v| {
16591 let s = match v {
16592 Value::String(s) => (**s).clone(),
16593 _ => format!("{}", v),
16594 };
16595 (s, v.clone())
16596 })
16597 .collect();
16598
16599 items.sort_by(|(a, _), (b, _)| collator.compare(a, b));
16600
16601 let sorted: Vec<Value> = items.into_iter().map(|(_, v)| v).collect();
16602 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
16603 }
16604 _ => Err(RuntimeError::new("sort_locale() requires array and locale")),
16605 }
16606 });
16607
16608 define(interp, "sentences", Some(1), |_, args| match &args[0] {
16620 Value::String(s) => {
16621 let segmenter = SentenceSegmenter::new();
16622 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16623 let mut sentences = Vec::new();
16624 let mut start = 0;
16625 for end in breakpoints {
16626 let sentence = s[start..end].trim();
16627 if !sentence.is_empty() {
16628 sentences.push(Value::String(Rc::new(sentence.to_string())));
16629 }
16630 start = end;
16631 }
16632 Ok(Value::Array(Rc::new(RefCell::new(sentences))))
16633 }
16634 _ => Err(RuntimeError::new("sentences() requires string")),
16635 });
16636
16637 define(interp, "sentence_count", Some(1), |_, args| {
16639 match &args[0] {
16640 Value::String(s) => {
16641 let segmenter = SentenceSegmenter::new();
16642 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16643 let count = breakpoints.len().saturating_sub(1);
16645 Ok(Value::Int(count as i64))
16646 }
16647 _ => Err(RuntimeError::new("sentence_count() requires string")),
16648 }
16649 });
16650
16651 define(interp, "words_icu", Some(1), |_, args| {
16653 match &args[0] {
16654 Value::String(s) => {
16655 let segmenter = WordSegmenter::new_auto();
16656 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16657 let mut words = Vec::new();
16658 let mut start = 0;
16659 for end in breakpoints {
16660 let word = &s[start..end];
16661 if !word.trim().is_empty() {
16663 words.push(Value::String(Rc::new(word.to_string())));
16664 }
16665 start = end;
16666 }
16667 Ok(Value::Array(Rc::new(RefCell::new(words))))
16668 }
16669 _ => Err(RuntimeError::new("words_icu() requires string")),
16670 }
16671 });
16672
16673 define(interp, "word_count_icu", Some(1), |_, args| {
16675 match &args[0] {
16676 Value::String(s) => {
16677 let segmenter = WordSegmenter::new_auto();
16678 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
16679 let mut count = 0;
16680 let mut start = 0;
16681 for end in breakpoints {
16682 let word = &s[start..end];
16683 if !word.trim().is_empty() && word.chars().any(|c| c.is_alphanumeric()) {
16684 count += 1;
16685 }
16686 start = end;
16687 }
16688 Ok(Value::Int(count))
16689 }
16690 _ => Err(RuntimeError::new("word_count_icu() requires string")),
16691 }
16692 });
16693
16694 define(interp, "is_emoji", Some(1), |_, args| {
16700 match &args[0] {
16701 Value::String(s) => {
16702 let has_emoji = s.chars().any(|c| {
16703 let code = c as u32;
16704 (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) });
16719 Ok(Value::Bool(has_emoji))
16720 }
16721 _ => Err(RuntimeError::new("is_emoji() requires string")),
16722 }
16723 });
16724
16725 define(interp, "extract_emoji", Some(1), |_, args| match &args[0] {
16727 Value::String(s) => {
16728 let emoji: Vec<Value> = s
16729 .graphemes(true)
16730 .filter(|g| {
16731 g.chars().any(|c| {
16732 let code = c as u32;
16733 (0x1F600..=0x1F64F).contains(&code)
16734 || (0x1F300..=0x1F5FF).contains(&code)
16735 || (0x1F680..=0x1F6FF).contains(&code)
16736 || (0x1F1E0..=0x1F1FF).contains(&code)
16737 || (0x2600..=0x26FF).contains(&code)
16738 || (0x2700..=0x27BF).contains(&code)
16739 || (0x1F900..=0x1F9FF).contains(&code)
16740 || (0x1FA00..=0x1FA6F).contains(&code)
16741 || (0x1FA70..=0x1FAFF).contains(&code)
16742 })
16743 })
16744 .map(|g| Value::String(Rc::new(g.to_string())))
16745 .collect();
16746 Ok(Value::Array(Rc::new(RefCell::new(emoji))))
16747 }
16748 _ => Err(RuntimeError::new("extract_emoji() requires string")),
16749 });
16750
16751 define(interp, "strip_emoji", Some(1), |_, args| match &args[0] {
16753 Value::String(s) => {
16754 let stripped: String = s
16755 .graphemes(true)
16756 .filter(|g| {
16757 !g.chars().any(|c| {
16758 let code = c as u32;
16759 (0x1F600..=0x1F64F).contains(&code)
16760 || (0x1F300..=0x1F5FF).contains(&code)
16761 || (0x1F680..=0x1F6FF).contains(&code)
16762 || (0x1F1E0..=0x1F1FF).contains(&code)
16763 || (0x2600..=0x26FF).contains(&code)
16764 || (0x2700..=0x27BF).contains(&code)
16765 || (0x1F900..=0x1F9FF).contains(&code)
16766 || (0x1FA00..=0x1FA6F).contains(&code)
16767 || (0x1FA70..=0x1FAFF).contains(&code)
16768 })
16769 })
16770 .collect();
16771 Ok(Value::String(Rc::new(stripped)))
16772 }
16773 _ => Err(RuntimeError::new("strip_emoji() requires string")),
16774 });
16775
16776 define(interp, "script_runs", Some(1), |_, args| {
16782 match &args[0] {
16783 Value::String(s) => {
16784 let mut runs: Vec<Value> = Vec::new();
16785 let mut current_run = String::new();
16786 let mut current_script: Option<Script> = None;
16787
16788 for c in s.chars() {
16789 let script = c.script();
16790 if script != Script::Common && script != Script::Inherited {
16792 if let Some(curr) = current_script {
16793 if script != curr {
16794 if !current_run.is_empty() {
16796 runs.push(Value::String(Rc::new(current_run.clone())));
16797 current_run.clear();
16798 }
16799 current_script = Some(script);
16800 }
16801 } else {
16802 current_script = Some(script);
16803 }
16804 }
16805 current_run.push(c);
16806 }
16807
16808 if !current_run.is_empty() {
16810 runs.push(Value::String(Rc::new(current_run)));
16811 }
16812
16813 Ok(Value::Array(Rc::new(RefCell::new(runs))))
16814 }
16815 _ => Err(RuntimeError::new("script_runs() requires string")),
16816 }
16817 });
16818
16819 define(interp, "script_ratio", Some(1), |_, args| {
16821 match &args[0] {
16822 Value::String(s) => {
16823 let mut script_counts: HashMap<String, usize> = HashMap::new();
16824 let mut total = 0usize;
16825
16826 for c in s.chars() {
16827 if !c.is_whitespace() && c != ' ' {
16828 let script = format!("{:?}", c.script());
16829 *script_counts.entry(script).or_insert(0) += 1;
16830 total += 1;
16831 }
16832 }
16833
16834 let mut result = HashMap::new();
16836 for (script, count) in script_counts {
16837 let ratio = if total > 0 {
16838 count as f64 / total as f64
16839 } else {
16840 0.0
16841 };
16842 result.insert(script, Value::Float(ratio));
16843 }
16844
16845 let map = Rc::new(RefCell::new(result));
16846 Ok(Value::Map(map))
16847 }
16848 _ => Err(RuntimeError::new("script_ratio() requires string")),
16849 }
16850 });
16851
16852 define(interp, "locale_name", Some(1), |_, args| {
16858 match &args[0] {
16859 Value::String(locale_str) => {
16860 Ok(Value::String(locale_str.clone()))
16863 }
16864 _ => Err(RuntimeError::new("locale_name() requires string")),
16865 }
16866 });
16867
16868 define(interp, "supported_locales", Some(0), |_, _| {
16870 let locales = vec![
16872 "ar", "bg", "ca", "cs", "da", "de", "el", "en", "es", "et", "fi", "fr", "he", "hi",
16873 "hr", "hu", "id", "it", "ja", "ko", "lt", "lv", "ms", "nb", "nl", "pl", "pt", "ro",
16874 "ru", "sk", "sl", "sr", "sv", "th", "tr", "uk", "vi", "zh",
16875 ];
16876 let values: Vec<Value> = locales
16877 .into_iter()
16878 .map(|s| Value::String(Rc::new(s.to_string())))
16879 .collect();
16880 Ok(Value::Array(Rc::new(RefCell::new(values))))
16881 });
16882}
16883
16884fn register_text_intelligence(interp: &mut Interpreter) {
16889 define(interp, "levenshtein", Some(2), |_, args| {
16895 match (&args[0], &args[1]) {
16896 (Value::String(a), Value::String(b)) => {
16897 let distance = strsim::levenshtein(a, b);
16898 Ok(Value::Int(distance as i64))
16899 }
16900 _ => Err(RuntimeError::new("levenshtein() requires two strings")),
16901 }
16902 });
16903
16904 define(
16906 interp,
16907 "levenshtein_normalized",
16908 Some(2),
16909 |_, args| match (&args[0], &args[1]) {
16910 (Value::String(a), Value::String(b)) => {
16911 let distance = strsim::normalized_levenshtein(a, b);
16912 Ok(Value::Float(distance))
16913 }
16914 _ => Err(RuntimeError::new(
16915 "levenshtein_normalized() requires two strings",
16916 )),
16917 },
16918 );
16919
16920 define(interp, "jaro", Some(2), |_, args| {
16922 match (&args[0], &args[1]) {
16923 (Value::String(a), Value::String(b)) => {
16924 let sim = strsim::jaro(a, b);
16925 Ok(Value::Float(sim))
16926 }
16927 _ => Err(RuntimeError::new("jaro() requires two strings")),
16928 }
16929 });
16930
16931 define(interp, "jaro_winkler", Some(2), |_, args| {
16933 match (&args[0], &args[1]) {
16934 (Value::String(a), Value::String(b)) => {
16935 let sim = strsim::jaro_winkler(a, b);
16936 Ok(Value::Float(sim))
16937 }
16938 _ => Err(RuntimeError::new("jaro_winkler() requires two strings")),
16939 }
16940 });
16941
16942 define(interp, "sorensen_dice", Some(2), |_, args| {
16944 match (&args[0], &args[1]) {
16945 (Value::String(a), Value::String(b)) => {
16946 let sim = strsim::sorensen_dice(a, b);
16947 Ok(Value::Float(sim))
16948 }
16949 _ => Err(RuntimeError::new("sorensen_dice() requires two strings")),
16950 }
16951 });
16952
16953 define(interp, "damerau_levenshtein", Some(2), |_, args| {
16955 match (&args[0], &args[1]) {
16956 (Value::String(a), Value::String(b)) => {
16957 let distance = strsim::damerau_levenshtein(a, b);
16958 Ok(Value::Int(distance as i64))
16959 }
16960 _ => Err(RuntimeError::new(
16961 "damerau_levenshtein() requires two strings",
16962 )),
16963 }
16964 });
16965
16966 define(interp, "osa_distance", Some(2), |_, args| {
16968 match (&args[0], &args[1]) {
16969 (Value::String(a), Value::String(b)) => {
16970 let distance = strsim::osa_distance(a, b);
16971 Ok(Value::Int(distance as i64))
16972 }
16973 _ => Err(RuntimeError::new("osa_distance() requires two strings")),
16974 }
16975 });
16976
16977 define(interp, "fuzzy_match", Some(3), |_, args| {
16979 match (&args[0], &args[1], &args[2]) {
16980 (Value::String(a), Value::String(b), Value::Float(threshold)) => {
16981 let sim = strsim::jaro_winkler(a, b);
16982 Ok(Value::Bool(sim >= *threshold))
16983 }
16984 (Value::String(a), Value::String(b), Value::Int(threshold)) => {
16985 let sim = strsim::jaro_winkler(a, b);
16986 Ok(Value::Bool(sim >= *threshold as f64))
16987 }
16988 _ => Err(RuntimeError::new(
16989 "fuzzy_match() requires two strings and threshold",
16990 )),
16991 }
16992 });
16993
16994 define(interp, "fuzzy_search", Some(3), |_, args| {
16996 match (&args[0], &args[1], &args[2]) {
16997 (Value::String(query), Value::Array(items), Value::Int(limit)) => {
16998 let items_ref = items.borrow();
16999 let mut scores: Vec<(f64, &str)> = items_ref
17000 .iter()
17001 .filter_map(|v| {
17002 if let Value::String(s) = v {
17003 Some((strsim::jaro_winkler(query, s), s.as_str()))
17004 } else {
17005 None
17006 }
17007 })
17008 .collect();
17009 scores.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
17010 let results: Vec<Value> = scores
17011 .into_iter()
17012 .take(*limit as usize)
17013 .map(|(_, s)| Value::String(Rc::new(s.to_string())))
17014 .collect();
17015 Ok(Value::Array(Rc::new(RefCell::new(results))))
17016 }
17017 _ => Err(RuntimeError::new(
17018 "fuzzy_search() requires query string, array, and limit",
17019 )),
17020 }
17021 });
17022
17023 define(interp, "soundex", Some(1), |_, args| match &args[0] {
17029 Value::String(s) => {
17030 let code = compute_soundex(s);
17031 Ok(Value::String(Rc::new(code)))
17032 }
17033 _ => Err(RuntimeError::new("soundex() requires string")),
17034 });
17035
17036 define(interp, "soundex_match", Some(2), |_, args| {
17038 match (&args[0], &args[1]) {
17039 (Value::String(a), Value::String(b)) => {
17040 let code_a = compute_soundex(a);
17041 let code_b = compute_soundex(b);
17042 Ok(Value::Bool(code_a == code_b))
17043 }
17044 _ => Err(RuntimeError::new("soundex_match() requires two strings")),
17045 }
17046 });
17047
17048 define(interp, "metaphone", Some(1), |_, args| match &args[0] {
17050 Value::String(s) => {
17051 let code = compute_metaphone(s);
17052 Ok(Value::String(Rc::new(code)))
17053 }
17054 _ => Err(RuntimeError::new("metaphone() requires string")),
17055 });
17056
17057 define(interp, "metaphone_match", Some(2), |_, args| {
17059 match (&args[0], &args[1]) {
17060 (Value::String(a), Value::String(b)) => {
17061 let code_a = compute_metaphone(a);
17062 let code_b = compute_metaphone(b);
17063 Ok(Value::Bool(code_a == code_b))
17064 }
17065 _ => Err(RuntimeError::new("metaphone_match() requires two strings")),
17066 }
17067 });
17068
17069 define(interp, "cologne_phonetic", Some(1), |_, args| {
17071 match &args[0] {
17072 Value::String(s) => {
17073 let code = compute_cologne(s);
17074 Ok(Value::String(Rc::new(code)))
17075 }
17076 _ => Err(RuntimeError::new("cologne_phonetic() requires string")),
17077 }
17078 });
17079
17080 define(interp, "detect_language", Some(1), |_, args| {
17086 match &args[0] {
17087 Value::String(s) => {
17088 if let Some(info) = detect(s) {
17089 let lang_code = match info.lang() {
17090 Lang::Eng => "en",
17091 Lang::Spa => "es",
17092 Lang::Fra => "fr",
17093 Lang::Deu => "de",
17094 Lang::Ita => "it",
17095 Lang::Por => "pt",
17096 Lang::Rus => "ru",
17097 Lang::Ara => "ar",
17098 Lang::Hin => "hi",
17099 Lang::Cmn => "zh",
17100 Lang::Jpn => "ja",
17101 Lang::Kor => "ko",
17102 Lang::Nld => "nl",
17103 Lang::Swe => "sv",
17104 Lang::Tur => "tr",
17105 Lang::Pol => "pl",
17106 Lang::Ukr => "uk",
17107 Lang::Ces => "cs",
17108 Lang::Dan => "da",
17109 Lang::Fin => "fi",
17110 Lang::Ell => "el",
17111 Lang::Heb => "he",
17112 Lang::Hun => "hu",
17113 Lang::Ind => "id",
17114 Lang::Nob => "no",
17115 Lang::Ron => "ro",
17116 Lang::Slk => "sk",
17117 Lang::Tha => "th",
17118 Lang::Vie => "vi",
17119 _ => "unknown",
17120 };
17121 Ok(Value::String(Rc::new(lang_code.to_string())))
17122 } else {
17123 Ok(Value::String(Rc::new("unknown".to_string())))
17124 }
17125 }
17126 _ => Err(RuntimeError::new("detect_language() requires string")),
17127 }
17128 });
17129
17130 define(
17132 interp,
17133 "detect_language_confidence",
17134 Some(1),
17135 |_, args| match &args[0] {
17136 Value::String(s) => {
17137 if let Some(info) = detect(s) {
17138 let lang_code = match info.lang() {
17139 Lang::Eng => "en",
17140 Lang::Spa => "es",
17141 Lang::Fra => "fr",
17142 Lang::Deu => "de",
17143 Lang::Ita => "it",
17144 Lang::Por => "pt",
17145 Lang::Rus => "ru",
17146 Lang::Ara => "ar",
17147 Lang::Cmn => "zh",
17148 Lang::Jpn => "ja",
17149 _ => "unknown",
17150 };
17151 let confidence = info.confidence();
17152 let mut map = HashMap::new();
17153 map.insert(
17154 "lang".to_string(),
17155 Value::String(Rc::new(lang_code.to_string())),
17156 );
17157 map.insert("confidence".to_string(), Value::Float(confidence as f64));
17158 Ok(Value::Map(Rc::new(RefCell::new(map))))
17159 } else {
17160 let mut map = HashMap::new();
17161 map.insert(
17162 "lang".to_string(),
17163 Value::String(Rc::new("unknown".to_string())),
17164 );
17165 map.insert("confidence".to_string(), Value::Float(0.0));
17166 Ok(Value::Map(Rc::new(RefCell::new(map))))
17167 }
17168 }
17169 _ => Err(RuntimeError::new(
17170 "detect_language_confidence() requires string",
17171 )),
17172 },
17173 );
17174
17175 define(
17177 interp,
17178 "detect_script_whatlang",
17179 Some(1),
17180 |_, args| match &args[0] {
17181 Value::String(s) => {
17182 if let Some(info) = detect(s) {
17183 let script_name = match info.script() {
17184 WhatLangScript::Latin => "Latin",
17185 WhatLangScript::Cyrillic => "Cyrillic",
17186 WhatLangScript::Arabic => "Arabic",
17187 WhatLangScript::Devanagari => "Devanagari",
17188 WhatLangScript::Ethiopic => "Ethiopic",
17189 WhatLangScript::Georgian => "Georgian",
17190 WhatLangScript::Greek => "Greek",
17191 WhatLangScript::Gujarati => "Gujarati",
17192 WhatLangScript::Gurmukhi => "Gurmukhi",
17193 WhatLangScript::Hangul => "Hangul",
17194 WhatLangScript::Hebrew => "Hebrew",
17195 WhatLangScript::Hiragana => "Hiragana",
17196 WhatLangScript::Kannada => "Kannada",
17197 WhatLangScript::Katakana => "Katakana",
17198 WhatLangScript::Khmer => "Khmer",
17199 WhatLangScript::Malayalam => "Malayalam",
17200 WhatLangScript::Mandarin => "Mandarin",
17201 WhatLangScript::Myanmar => "Myanmar",
17202 WhatLangScript::Oriya => "Oriya",
17203 WhatLangScript::Sinhala => "Sinhala",
17204 WhatLangScript::Tamil => "Tamil",
17205 WhatLangScript::Telugu => "Telugu",
17206 WhatLangScript::Thai => "Thai",
17207 WhatLangScript::Bengali => "Bengali",
17208 WhatLangScript::Armenian => "Armenian",
17209 };
17210 Ok(Value::String(Rc::new(script_name.to_string())))
17211 } else {
17212 Ok(Value::String(Rc::new("Unknown".to_string())))
17213 }
17214 }
17215 _ => Err(RuntimeError::new(
17216 "detect_script_whatlang() requires string",
17217 )),
17218 },
17219 );
17220
17221 define(interp, "is_language", Some(2), |_, args| {
17223 match (&args[0], &args[1]) {
17224 (Value::String(s), Value::String(lang)) => {
17225 if let Some(info) = detect(s) {
17226 let detected = match info.lang() {
17227 Lang::Eng => "en",
17228 Lang::Spa => "es",
17229 Lang::Fra => "fr",
17230 Lang::Deu => "de",
17231 Lang::Ita => "it",
17232 Lang::Por => "pt",
17233 Lang::Rus => "ru",
17234 _ => "unknown",
17235 };
17236 Ok(Value::Bool(detected == lang.as_str()))
17237 } else {
17238 Ok(Value::Bool(false))
17239 }
17240 }
17241 _ => Err(RuntimeError::new(
17242 "is_language() requires string and language code",
17243 )),
17244 }
17245 });
17246
17247 define(interp, "token_count", Some(1), |_, args| match &args[0] {
17253 Value::String(s) => {
17254 if let Ok(bpe) = cl100k_base() {
17255 let tokens = bpe.encode_with_special_tokens(s);
17256 Ok(Value::Int(tokens.len() as i64))
17257 } else {
17258 Err(RuntimeError::new("Failed to initialize tokenizer"))
17259 }
17260 }
17261 _ => Err(RuntimeError::new("token_count() requires string")),
17262 });
17263
17264 define(interp, "token_count_model", Some(2), |_, args| {
17266 match (&args[0], &args[1]) {
17267 (Value::String(s), Value::String(model)) => {
17268 let bpe_result = match model.as_str() {
17269 "gpt4" | "gpt-4" | "claude" | "cl100k" => cl100k_base(),
17270 "gpt3" | "gpt-3" | "p50k" => p50k_base(),
17271 "codex" | "r50k" => r50k_base(),
17272 _ => cl100k_base(), };
17274 if let Ok(bpe) = bpe_result {
17275 let tokens = bpe.encode_with_special_tokens(s);
17276 Ok(Value::Int(tokens.len() as i64))
17277 } else {
17278 Err(RuntimeError::new("Failed to initialize tokenizer"))
17279 }
17280 }
17281 _ => Err(RuntimeError::new(
17282 "token_count_model() requires string and model name",
17283 )),
17284 }
17285 });
17286
17287 define(interp, "tokenize_ids", Some(1), |_, args| match &args[0] {
17289 Value::String(s) => {
17290 if let Ok(bpe) = cl100k_base() {
17291 let tokens = bpe.encode_with_special_tokens(s);
17292 let values: Vec<Value> = tokens.into_iter().map(|t| Value::Int(t as i64)).collect();
17293 Ok(Value::Array(Rc::new(RefCell::new(values))))
17294 } else {
17295 Err(RuntimeError::new("Failed to initialize tokenizer"))
17296 }
17297 }
17298 _ => Err(RuntimeError::new("tokenize_ids() requires string")),
17299 });
17300
17301 define(interp, "truncate_tokens", Some(2), |_, args| {
17303 match (&args[0], &args[1]) {
17304 (Value::String(s), Value::Int(max_tokens)) => {
17305 if let Ok(bpe) = cl100k_base() {
17306 let tokens = bpe.encode_with_special_tokens(s);
17307 if tokens.len() <= *max_tokens as usize {
17308 Ok(Value::String(s.clone()))
17309 } else {
17310 let truncated: Vec<usize> =
17311 tokens.into_iter().take(*max_tokens as usize).collect();
17312 if let Ok(decoded) = bpe.decode(truncated) {
17313 Ok(Value::String(Rc::new(decoded)))
17314 } else {
17315 Err(RuntimeError::new("Failed to decode tokens"))
17316 }
17317 }
17318 } else {
17319 Err(RuntimeError::new("Failed to initialize tokenizer"))
17320 }
17321 }
17322 _ => Err(RuntimeError::new(
17323 "truncate_tokens() requires string and max tokens",
17324 )),
17325 }
17326 });
17327
17328 define(interp, "estimate_cost", Some(3), |_, args| {
17330 match (&args[0], &args[1], &args[2]) {
17331 (Value::String(s), Value::Float(input_cost), Value::Float(output_cost)) => {
17332 if let Ok(bpe) = cl100k_base() {
17333 let tokens = bpe.encode_with_special_tokens(s);
17334 let count = tokens.len() as f64;
17335 let input_total = (count / 1000.0) * input_cost;
17337 let output_total = (count / 1000.0) * output_cost;
17338 let mut map = HashMap::new();
17339 map.insert("tokens".to_string(), Value::Int(tokens.len() as i64));
17340 map.insert("input_cost".to_string(), Value::Float(input_total));
17341 map.insert("output_cost".to_string(), Value::Float(output_total));
17342 Ok(Value::Map(Rc::new(RefCell::new(map))))
17343 } else {
17344 Err(RuntimeError::new("Failed to initialize tokenizer"))
17345 }
17346 }
17347 _ => Err(RuntimeError::new(
17348 "estimate_cost() requires string, input cost, output cost",
17349 )),
17350 }
17351 });
17352
17353 define(interp, "stem", Some(1), |_, args| match &args[0] {
17359 Value::String(s) => {
17360 let stemmer = Stemmer::create(StemAlgorithm::English);
17361 let stemmed = stemmer.stem(s);
17362 Ok(Value::String(Rc::new(stemmed.to_string())))
17363 }
17364 _ => Err(RuntimeError::new("stem() requires string")),
17365 });
17366
17367 define(interp, "stem_language", Some(2), |_, args| {
17369 match (&args[0], &args[1]) {
17370 (Value::String(s), Value::String(lang)) => {
17371 let algorithm = match lang.as_str() {
17372 "en" | "english" => StemAlgorithm::English,
17373 "fr" | "french" => StemAlgorithm::French,
17374 "de" | "german" => StemAlgorithm::German,
17375 "es" | "spanish" => StemAlgorithm::Spanish,
17376 "it" | "italian" => StemAlgorithm::Italian,
17377 "pt" | "portuguese" => StemAlgorithm::Portuguese,
17378 "nl" | "dutch" => StemAlgorithm::Dutch,
17379 "sv" | "swedish" => StemAlgorithm::Swedish,
17380 "no" | "norwegian" => StemAlgorithm::Norwegian,
17381 "da" | "danish" => StemAlgorithm::Danish,
17382 "fi" | "finnish" => StemAlgorithm::Finnish,
17383 "ru" | "russian" => StemAlgorithm::Russian,
17384 "ro" | "romanian" => StemAlgorithm::Romanian,
17385 "hu" | "hungarian" => StemAlgorithm::Hungarian,
17386 "tr" | "turkish" => StemAlgorithm::Turkish,
17387 "ar" | "arabic" => StemAlgorithm::Arabic,
17388 _ => StemAlgorithm::English,
17389 };
17390 let stemmer = Stemmer::create(algorithm);
17391 let stemmed = stemmer.stem(s);
17392 Ok(Value::String(Rc::new(stemmed.to_string())))
17393 }
17394 _ => Err(RuntimeError::new(
17395 "stem_language() requires string and language code",
17396 )),
17397 }
17398 });
17399
17400 define(interp, "stem_all", Some(1), |_, args| match &args[0] {
17402 Value::Array(arr) => {
17403 let stemmer = Stemmer::create(StemAlgorithm::English);
17404 let arr_ref = arr.borrow();
17405 let results: Vec<Value> = arr_ref
17406 .iter()
17407 .filter_map(|v| {
17408 if let Value::String(s) = v {
17409 Some(Value::String(Rc::new(stemmer.stem(s).to_string())))
17410 } else {
17411 None
17412 }
17413 })
17414 .collect();
17415 Ok(Value::Array(Rc::new(RefCell::new(results))))
17416 }
17417 _ => Err(RuntimeError::new("stem_all() requires array of strings")),
17418 });
17419
17420 define(interp, "is_stopword", Some(1), |_, args| match &args[0] {
17426 Value::String(s) => {
17427 let word = s.to_lowercase();
17428 let stopwords = get_stopwords("en");
17429 Ok(Value::Bool(stopwords.contains(&word.as_str())))
17430 }
17431 _ => Err(RuntimeError::new("is_stopword() requires string")),
17432 });
17433
17434 define(interp, "is_stopword_language", Some(2), |_, args| {
17436 match (&args[0], &args[1]) {
17437 (Value::String(s), Value::String(lang)) => {
17438 let word = s.to_lowercase();
17439 let stopwords = get_stopwords(lang);
17440 Ok(Value::Bool(stopwords.contains(&word.as_str())))
17441 }
17442 _ => Err(RuntimeError::new(
17443 "is_stopword_language() requires string and language",
17444 )),
17445 }
17446 });
17447
17448 define(interp, "remove_stopwords", Some(1), |_, args| {
17450 match &args[0] {
17451 Value::Array(arr) => {
17452 let stopwords = get_stopwords("en");
17453 let arr_ref = arr.borrow();
17454 let results: Vec<Value> = arr_ref
17455 .iter()
17456 .filter(|v| {
17457 if let Value::String(s) = v {
17458 !stopwords.contains(&s.to_lowercase().as_str())
17459 } else {
17460 true
17461 }
17462 })
17463 .cloned()
17464 .collect();
17465 Ok(Value::Array(Rc::new(RefCell::new(results))))
17466 }
17467 _ => Err(RuntimeError::new(
17468 "remove_stopwords() requires array of strings",
17469 )),
17470 }
17471 });
17472
17473 define(
17475 interp,
17476 "remove_stopwords_text",
17477 Some(1),
17478 |_, args| match &args[0] {
17479 Value::String(s) => {
17480 let stopwords = get_stopwords("en");
17481 let words: Vec<&str> = s
17482 .split_whitespace()
17483 .filter(|w| !stopwords.contains(&w.to_lowercase().as_str()))
17484 .collect();
17485 Ok(Value::String(Rc::new(words.join(" "))))
17486 }
17487 _ => Err(RuntimeError::new("remove_stopwords_text() requires string")),
17488 },
17489 );
17490
17491 define(
17493 interp,
17494 "get_stopwords_list",
17495 Some(1),
17496 |_, args| match &args[0] {
17497 Value::String(lang) => {
17498 let stopwords = get_stopwords(lang);
17499 let values: Vec<Value> = stopwords
17500 .iter()
17501 .map(|s| Value::String(Rc::new(s.to_string())))
17502 .collect();
17503 Ok(Value::Array(Rc::new(RefCell::new(values))))
17504 }
17505 _ => Err(RuntimeError::new(
17506 "get_stopwords_list() requires language code",
17507 )),
17508 },
17509 );
17510
17511 define(interp, "ngrams", Some(2), |_, args| {
17517 match (&args[0], &args[1]) {
17518 (Value::String(s), Value::Int(n)) => {
17519 let words: Vec<&str> = s.split_whitespace().collect();
17520 let n = *n as usize;
17521 if n == 0 || n > words.len() {
17522 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17523 }
17524 let ngrams: Vec<Value> = words
17525 .windows(n)
17526 .map(|w| Value::String(Rc::new(w.join(" "))))
17527 .collect();
17528 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
17529 }
17530 _ => Err(RuntimeError::new("ngrams() requires string and n")),
17531 }
17532 });
17533
17534 define(interp, "char_ngrams", Some(2), |_, args| {
17536 match (&args[0], &args[1]) {
17537 (Value::String(s), Value::Int(n)) => {
17538 let chars: Vec<char> = s.chars().collect();
17539 let n = *n as usize;
17540 if n == 0 || n > chars.len() {
17541 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17542 }
17543 let ngrams: Vec<Value> = chars
17544 .windows(n)
17545 .map(|w| Value::String(Rc::new(w.iter().collect())))
17546 .collect();
17547 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
17548 }
17549 _ => Err(RuntimeError::new("char_ngrams() requires string and n")),
17550 }
17551 });
17552
17553 define(interp, "shingles", Some(2), |_, args| {
17555 match (&args[0], &args[1]) {
17556 (Value::String(s), Value::Int(n)) => {
17557 let words: Vec<&str> = s.split_whitespace().collect();
17558 let n = *n as usize;
17559 if n == 0 || n > words.len() {
17560 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
17561 }
17562 let mut seen = std::collections::HashSet::new();
17563 let shingles: Vec<Value> = words
17564 .windows(n)
17565 .filter_map(|w| {
17566 let s = w.join(" ");
17567 if seen.insert(s.clone()) {
17568 Some(Value::String(Rc::new(s)))
17569 } else {
17570 None
17571 }
17572 })
17573 .collect();
17574 Ok(Value::Array(Rc::new(RefCell::new(shingles))))
17575 }
17576 _ => Err(RuntimeError::new("shingles() requires string and n")),
17577 }
17578 });
17579
17580 define(interp, "jaccard_similarity", Some(2), |_, args| {
17582 match (&args[0], &args[1]) {
17583 (Value::Array(a), Value::Array(b)) => {
17584 let a_ref = a.borrow();
17585 let b_ref = b.borrow();
17586 let set_a: std::collections::HashSet<String> = a_ref
17587 .iter()
17588 .filter_map(|v| {
17589 if let Value::String(s) = v {
17590 Some(s.to_string())
17591 } else {
17592 None
17593 }
17594 })
17595 .collect();
17596 let set_b: std::collections::HashSet<String> = b_ref
17597 .iter()
17598 .filter_map(|v| {
17599 if let Value::String(s) = v {
17600 Some(s.to_string())
17601 } else {
17602 None
17603 }
17604 })
17605 .collect();
17606 let intersection = set_a.intersection(&set_b).count();
17607 let union = set_a.union(&set_b).count();
17608 if union == 0 {
17609 Ok(Value::Float(0.0))
17610 } else {
17611 Ok(Value::Float(intersection as f64 / union as f64))
17612 }
17613 }
17614 _ => Err(RuntimeError::new(
17615 "jaccard_similarity() requires two arrays",
17616 )),
17617 }
17618 });
17619
17620 define(interp, "minhash_signature", Some(2), |_, args| {
17622 match (&args[0], &args[1]) {
17623 (Value::Array(arr), Value::Int(num_hashes)) => {
17624 let arr_ref = arr.borrow();
17625 let items: std::collections::HashSet<String> = arr_ref
17626 .iter()
17627 .filter_map(|v| {
17628 if let Value::String(s) = v {
17629 Some(s.to_string())
17630 } else {
17631 None
17632 }
17633 })
17634 .collect();
17635
17636 let mut signature: Vec<Value> = Vec::with_capacity(*num_hashes as usize);
17638 for i in 0..*num_hashes {
17639 let mut min_hash: u64 = u64::MAX;
17640 for item in &items {
17641 let hash = compute_hash(item, i as u64);
17642 if hash < min_hash {
17643 min_hash = hash;
17644 }
17645 }
17646 signature.push(Value::Int(min_hash as i64));
17647 }
17648 Ok(Value::Array(Rc::new(RefCell::new(signature))))
17649 }
17650 _ => Err(RuntimeError::new(
17651 "minhash_signature() requires array and num_hashes",
17652 )),
17653 }
17654 });
17655
17656 define(interp, "preprocess_text", Some(1), |_, args| {
17662 match &args[0] {
17663 Value::String(s) => {
17664 let lower = s.to_lowercase();
17666 let clean: String = lower
17668 .chars()
17669 .filter(|c| c.is_alphanumeric() || c.is_whitespace())
17670 .collect();
17671 let normalized: String = clean.split_whitespace().collect::<Vec<_>>().join(" ");
17673 Ok(Value::String(Rc::new(normalized)))
17674 }
17675 _ => Err(RuntimeError::new("preprocess_text() requires string")),
17676 }
17677 });
17678
17679 define(interp, "tokenize_words", Some(1), |_, args| {
17681 match &args[0] {
17682 Value::String(s) => {
17683 let words: Vec<Value> = s
17684 .split_whitespace()
17685 .map(|w| Value::String(Rc::new(w.to_string())))
17686 .collect();
17687 Ok(Value::Array(Rc::new(RefCell::new(words))))
17688 }
17689 _ => Err(RuntimeError::new("tokenize_words() requires string")),
17690 }
17691 });
17692
17693 define(interp, "extract_keywords", Some(1), |_, args| {
17695 match &args[0] {
17696 Value::String(s) => {
17697 let stopwords = get_stopwords("en");
17698 let words: Vec<Value> = s
17699 .split_whitespace()
17700 .filter(|w| {
17701 let lower = w.to_lowercase();
17702 !stopwords.contains(&lower.as_str()) && lower.len() > 2
17703 })
17704 .map(|w| Value::String(Rc::new(w.to_lowercase())))
17705 .collect();
17706 Ok(Value::Array(Rc::new(RefCell::new(words))))
17707 }
17708 _ => Err(RuntimeError::new("extract_keywords() requires string")),
17709 }
17710 });
17711
17712 define(interp, "word_frequency", Some(1), |_, args| {
17714 match &args[0] {
17715 Value::String(s) => {
17716 let mut freq: HashMap<String, i64> = HashMap::new();
17717 for word in s.split_whitespace() {
17718 let lower = word.to_lowercase();
17719 *freq.entry(lower).or_insert(0) += 1;
17720 }
17721 let map: HashMap<String, Value> =
17722 freq.into_iter().map(|(k, v)| (k, Value::Int(v))).collect();
17723 Ok(Value::Map(Rc::new(RefCell::new(map))))
17724 }
17725 _ => Err(RuntimeError::new("word_frequency() requires string")),
17726 }
17727 });
17728
17729 define(interp, "sentiment_words", Some(1), |_, args| {
17735 match &args[0] {
17736 Value::String(s) => {
17737 let positive = vec![
17738 "good",
17739 "great",
17740 "excellent",
17741 "amazing",
17742 "wonderful",
17743 "fantastic",
17744 "love",
17745 "happy",
17746 "joy",
17747 "beautiful",
17748 "awesome",
17749 "perfect",
17750 "best",
17751 "brilliant",
17752 "delightful",
17753 "pleasant",
17754 "positive",
17755 ];
17756 let negative = vec![
17757 "bad",
17758 "terrible",
17759 "awful",
17760 "horrible",
17761 "hate",
17762 "sad",
17763 "angry",
17764 "worst",
17765 "poor",
17766 "negative",
17767 "disappointing",
17768 "ugly",
17769 "disgusting",
17770 "painful",
17771 "miserable",
17772 "annoying",
17773 ];
17774
17775 let lower = s.to_lowercase();
17776 let words: Vec<&str> = lower.split_whitespace().collect();
17777 let pos_count: i64 = words.iter().filter(|w| positive.contains(w)).count() as i64;
17778 let neg_count: i64 = words.iter().filter(|w| negative.contains(w)).count() as i64;
17779
17780 let mut map = HashMap::new();
17781 map.insert("positive".to_string(), Value::Int(pos_count));
17782 map.insert("negative".to_string(), Value::Int(neg_count));
17783 map.insert("total".to_string(), Value::Int(words.len() as i64));
17784
17785 let score = if pos_count + neg_count > 0 {
17786 (pos_count - neg_count) as f64 / (pos_count + neg_count) as f64
17787 } else {
17788 0.0
17789 };
17790 map.insert("score".to_string(), Value::Float(score));
17791
17792 Ok(Value::Map(Rc::new(RefCell::new(map))))
17793 }
17794 _ => Err(RuntimeError::new("sentiment_words() requires string")),
17795 }
17796 });
17797
17798 define(interp, "has_question", Some(1), |_, args| match &args[0] {
17800 Value::String(s) => {
17801 let has_q_mark = s.contains('?');
17802 let lower = s.to_lowercase();
17803 let question_words = [
17804 "what", "where", "when", "why", "how", "who", "which", "whose", "whom",
17805 ];
17806 let starts_with_q = question_words.iter().any(|w| lower.starts_with(w));
17807 Ok(Value::Bool(has_q_mark || starts_with_q))
17808 }
17809 _ => Err(RuntimeError::new("has_question() requires string")),
17810 });
17811
17812 define(interp, "has_exclamation", Some(1), |_, args| {
17814 match &args[0] {
17815 Value::String(s) => Ok(Value::Bool(s.contains('!'))),
17816 _ => Err(RuntimeError::new("has_exclamation() requires string")),
17817 }
17818 });
17819
17820 define(interp, "text_formality", Some(1), |_, args| {
17822 match &args[0] {
17823 Value::String(s) => {
17824 let lower = s.to_lowercase();
17825 let informal_markers = vec![
17826 "gonna", "wanna", "gotta", "kinda", "sorta", "dunno", "yeah", "yep", "nope",
17827 "ok", "lol", "omg", "btw", "u", "ur", "r", "y", "2", "4",
17828 ];
17829 let formal_markers = vec![
17830 "therefore",
17831 "furthermore",
17832 "moreover",
17833 "consequently",
17834 "nevertheless",
17835 "however",
17836 "whereas",
17837 "hereby",
17838 "respectfully",
17839 "sincerely",
17840 "accordingly",
17841 ];
17842
17843 let words: Vec<&str> = lower.split_whitespace().collect();
17844 let informal_count = words
17845 .iter()
17846 .filter(|w| informal_markers.contains(w))
17847 .count();
17848 let formal_count = words.iter().filter(|w| formal_markers.contains(w)).count();
17849
17850 let score = if informal_count + formal_count > 0 {
17851 formal_count as f64 / (informal_count + formal_count) as f64
17852 } else {
17853 0.5 };
17855
17856 Ok(Value::Float(score))
17857 }
17858 _ => Err(RuntimeError::new("text_formality() requires string")),
17859 }
17860 });
17861
17862 define(interp, "sentiment_vader", Some(1), |_, args| {
17868 match &args[0] {
17869 Value::String(s) => {
17870 let result = compute_vader_sentiment(s);
17871 let mut map = HashMap::new();
17872 map.insert("positive".to_string(), Value::Float(result.0));
17873 map.insert("negative".to_string(), Value::Float(result.1));
17874 map.insert("neutral".to_string(), Value::Float(result.2));
17875 map.insert("compound".to_string(), Value::Float(result.3));
17876 Ok(Value::Map(Rc::new(RefCell::new(map))))
17877 }
17878 _ => Err(RuntimeError::new("sentiment_vader() requires string")),
17879 }
17880 });
17881
17882 define(interp, "emotion_detect", Some(1), |_, args| {
17884 match &args[0] {
17885 Value::String(s) => {
17886 let emotions = compute_emotions(s);
17887 let map: HashMap<String, Value> = emotions
17888 .into_iter()
17889 .map(|(k, v)| (k, Value::Float(v)))
17890 .collect();
17891 Ok(Value::Map(Rc::new(RefCell::new(map))))
17892 }
17893 _ => Err(RuntimeError::new("emotion_detect() requires string")),
17894 }
17895 });
17896
17897 define(interp, "intensity_score", Some(1), |_, args| {
17899 match &args[0] {
17900 Value::String(s) => {
17901 let score = compute_intensity(s);
17902 Ok(Value::Float(score))
17903 }
17904 _ => Err(RuntimeError::new("intensity_score() requires string")),
17905 }
17906 });
17907
17908 define(interp, "detect_sarcasm", Some(1), |_, args| {
17914 match &args[0] {
17915 Value::String(s) => {
17916 let result = compute_sarcasm_score(s);
17917 let mut map = HashMap::new();
17918 map.insert("score".to_string(), Value::Float(result.0));
17919 map.insert("confidence".to_string(), Value::Float(result.1));
17920 let markers: Vec<Value> = result
17921 .2
17922 .into_iter()
17923 .map(|m| Value::String(Rc::new(m)))
17924 .collect();
17925 map.insert(
17926 "markers".to_string(),
17927 Value::Array(Rc::new(RefCell::new(markers))),
17928 );
17929 Ok(Value::Map(Rc::new(RefCell::new(map))))
17930 }
17931 _ => Err(RuntimeError::new("detect_sarcasm() requires string")),
17932 }
17933 });
17934
17935 define(interp, "is_sarcastic", Some(1), |_, args| match &args[0] {
17937 Value::String(s) => {
17938 let result = compute_sarcasm_score(s);
17939 Ok(Value::Bool(result.0 > 0.5))
17940 }
17941 _ => Err(RuntimeError::new("is_sarcastic() requires string")),
17942 });
17943
17944 define(interp, "detect_irony", Some(1), |_, args| match &args[0] {
17946 Value::String(s) => {
17947 let score = compute_irony_score(s);
17948 Ok(Value::Float(score))
17949 }
17950 _ => Err(RuntimeError::new("detect_irony() requires string")),
17951 });
17952
17953 define(interp, "extract_emails", Some(1), |_, args| {
17959 match &args[0] {
17960 Value::String(s) => {
17961 let re = Regex::new(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}").unwrap();
17962 let emails: Vec<Value> = re
17963 .find_iter(s)
17964 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17965 .collect();
17966 Ok(Value::Array(Rc::new(RefCell::new(emails))))
17967 }
17968 _ => Err(RuntimeError::new("extract_emails() requires string")),
17969 }
17970 });
17971
17972 define(interp, "extract_urls", Some(1), |_, args| match &args[0] {
17974 Value::String(s) => {
17975 let re = Regex::new(r"https?://[^\s<>\[\]{}|\\^]+").unwrap();
17976 let urls: Vec<Value> = re
17977 .find_iter(s)
17978 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17979 .collect();
17980 Ok(Value::Array(Rc::new(RefCell::new(urls))))
17981 }
17982 _ => Err(RuntimeError::new("extract_urls() requires string")),
17983 });
17984
17985 define(
17987 interp,
17988 "extract_phone_numbers",
17989 Some(1),
17990 |_, args| match &args[0] {
17991 Value::String(s) => {
17992 let re =
17993 Regex::new(r"(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}")
17994 .unwrap();
17995 let phones: Vec<Value> = re
17996 .find_iter(s)
17997 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
17998 .collect();
17999 Ok(Value::Array(Rc::new(RefCell::new(phones))))
18000 }
18001 _ => Err(RuntimeError::new("extract_phone_numbers() requires string")),
18002 },
18003 );
18004
18005 define(interp, "extract_dates", Some(1), |_, args| {
18007 match &args[0] {
18008 Value::String(s) => {
18009 let patterns = vec![
18011 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}",
18015 r"\d{1,2}\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{4}",
18016 ];
18017 let mut dates = Vec::new();
18018 for pattern in patterns {
18019 if let Ok(re) = Regex::new(pattern) {
18020 for m in re.find_iter(s) {
18021 dates.push(Value::String(Rc::new(m.as_str().to_string())));
18022 }
18023 }
18024 }
18025 Ok(Value::Array(Rc::new(RefCell::new(dates))))
18026 }
18027 _ => Err(RuntimeError::new("extract_dates() requires string")),
18028 }
18029 });
18030
18031 define(interp, "extract_money", Some(1), |_, args| match &args[0] {
18033 Value::String(s) => {
18034 let re = Regex::new(r"[$€£¥]\s*\d+(?:,\d{3})*(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:dollars?|euros?|pounds?|USD|EUR|GBP)").unwrap();
18035 let money: Vec<Value> = re
18036 .find_iter(s)
18037 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
18038 .collect();
18039 Ok(Value::Array(Rc::new(RefCell::new(money))))
18040 }
18041 _ => Err(RuntimeError::new("extract_money() requires string")),
18042 });
18043
18044 define(interp, "extract_hashtags", Some(1), |_, args| {
18046 match &args[0] {
18047 Value::String(s) => {
18048 let re = Regex::new(r"#\w+").unwrap();
18049 let tags: Vec<Value> = re
18050 .find_iter(s)
18051 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
18052 .collect();
18053 Ok(Value::Array(Rc::new(RefCell::new(tags))))
18054 }
18055 _ => Err(RuntimeError::new("extract_hashtags() requires string")),
18056 }
18057 });
18058
18059 define(interp, "extract_mentions", Some(1), |_, args| {
18061 match &args[0] {
18062 Value::String(s) => {
18063 let re = Regex::new(r"@\w+").unwrap();
18064 let mentions: Vec<Value> = re
18065 .find_iter(s)
18066 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
18067 .collect();
18068 Ok(Value::Array(Rc::new(RefCell::new(mentions))))
18069 }
18070 _ => Err(RuntimeError::new("extract_mentions() requires string")),
18071 }
18072 });
18073
18074 define(interp, "extract_numbers", Some(1), |_, args| {
18076 match &args[0] {
18077 Value::String(s) => {
18078 let re = Regex::new(r"-?\d+(?:,\d{3})*(?:\.\d+)?").unwrap();
18079 let numbers: Vec<Value> = re
18080 .find_iter(s)
18081 .filter_map(|m| {
18082 let num_str = m.as_str().replace(",", "");
18083 if let Ok(n) = num_str.parse::<f64>() {
18084 Some(Value::Float(n))
18085 } else {
18086 None
18087 }
18088 })
18089 .collect();
18090 Ok(Value::Array(Rc::new(RefCell::new(numbers))))
18091 }
18092 _ => Err(RuntimeError::new("extract_numbers() requires string")),
18093 }
18094 });
18095
18096 define(interp, "extract_entities", Some(1), |_, args| {
18098 match &args[0] {
18099 Value::String(s) => {
18100 let re = Regex::new(r"(?:[.!?]\s+)?([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)").unwrap();
18102 let mut entities = std::collections::HashSet::new();
18103 for cap in re.captures_iter(s) {
18104 if let Some(m) = cap.get(1) {
18105 let entity = m.as_str().to_string();
18106 let starters = [
18108 "The", "A", "An", "This", "That", "It", "I", "We", "They", "He", "She",
18109 ];
18110 if !starters.contains(&entity.as_str()) {
18111 entities.insert(entity);
18112 }
18113 }
18114 }
18115 let results: Vec<Value> = entities
18116 .into_iter()
18117 .map(|e| Value::String(Rc::new(e)))
18118 .collect();
18119 Ok(Value::Array(Rc::new(RefCell::new(results))))
18120 }
18121 _ => Err(RuntimeError::new("extract_entities() requires string")),
18122 }
18123 });
18124
18125 define(interp, "text_hash_vector", Some(2), |_, args| {
18131 match (&args[0], &args[1]) {
18132 (Value::String(s), Value::Int(dims)) => {
18133 let dims = *dims as usize;
18134 let mut vector = vec![0.0f64; dims];
18135
18136 for word in s.to_lowercase().split_whitespace() {
18138 let hash = compute_hash(word, 0);
18139 let idx = (hash as usize) % dims;
18140 vector[idx] += 1.0;
18141 }
18142
18143 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
18145 if magnitude > 0.0 {
18146 for v in vector.iter_mut() {
18147 *v /= magnitude;
18148 }
18149 }
18150
18151 let values: Vec<Value> = vector.into_iter().map(Value::Float).collect();
18152 Ok(Value::Array(Rc::new(RefCell::new(values))))
18153 }
18154 _ => Err(RuntimeError::new(
18155 "text_hash_vector() requires string and dimensions",
18156 )),
18157 }
18158 });
18159
18160 define(interp, "text_fingerprint", Some(1), |_, args| {
18162 match &args[0] {
18163 Value::String(s) => {
18164 let lower = s.to_lowercase();
18166 let words: Vec<&str> = lower.split_whitespace().collect();
18167
18168 let mut fp: u64 = 0;
18169 for (i, word) in words.iter().enumerate() {
18170 let h = compute_hash(word, i as u64);
18171 fp ^= h.rotate_left((i % 64) as u32);
18172 }
18173
18174 Ok(Value::String(Rc::new(format!("{:016x}", fp))))
18175 }
18176 _ => Err(RuntimeError::new("text_fingerprint() requires string")),
18177 }
18178 });
18179
18180 define(interp, "cosine_similarity", Some(2), |_, args| {
18182 match (&args[0], &args[1]) {
18183 (Value::Array(a), Value::Array(b)) => {
18184 let a_ref = a.borrow();
18185 let b_ref = b.borrow();
18186
18187 if a_ref.len() != b_ref.len() {
18188 return Err(RuntimeError::new("Vectors must have same length"));
18189 }
18190
18191 let mut dot = 0.0;
18192 let mut mag_a = 0.0;
18193 let mut mag_b = 0.0;
18194
18195 for (va, vb) in a_ref.iter().zip(b_ref.iter()) {
18196 let fa = match va {
18197 Value::Float(f) => *f,
18198 Value::Int(i) => *i as f64,
18199 _ => continue,
18200 };
18201 let fb = match vb {
18202 Value::Float(f) => *f,
18203 Value::Int(i) => *i as f64,
18204 _ => continue,
18205 };
18206 dot += fa * fb;
18207 mag_a += fa * fa;
18208 mag_b += fb * fb;
18209 }
18210
18211 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
18212 if denom == 0.0 {
18213 Ok(Value::Float(0.0))
18214 } else {
18215 Ok(Value::Float(dot / denom))
18216 }
18217 }
18218 _ => Err(RuntimeError::new("cosine_similarity() requires two arrays")),
18219 }
18220 });
18221
18222 define(interp, "text_similarity_embedding", Some(2), |_, args| {
18224 match (&args[0], &args[1]) {
18225 (Value::String(a), Value::String(b)) => {
18226 let dims = 128;
18227
18228 let vec_a = create_hash_vector(a, dims);
18230 let vec_b = create_hash_vector(b, dims);
18231
18232 let mut dot = 0.0;
18234 let mut mag_a = 0.0;
18235 let mut mag_b = 0.0;
18236
18237 for i in 0..dims {
18238 dot += vec_a[i] * vec_b[i];
18239 mag_a += vec_a[i] * vec_a[i];
18240 mag_b += vec_b[i] * vec_b[i];
18241 }
18242
18243 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
18244 if denom == 0.0 {
18245 Ok(Value::Float(0.0))
18246 } else {
18247 Ok(Value::Float(dot / denom))
18248 }
18249 }
18250 _ => Err(RuntimeError::new(
18251 "text_similarity_embedding() requires two strings",
18252 )),
18253 }
18254 });
18255
18256 define(
18262 interp,
18263 "flesch_reading_ease",
18264 Some(1),
18265 |_, args| match &args[0] {
18266 Value::String(s) => {
18267 let (words, sentences, syllables) = count_text_stats(s);
18268 if words == 0 || sentences == 0 {
18269 return Ok(Value::Float(0.0));
18270 }
18271 let score = 206.835
18272 - 1.015 * (words as f64 / sentences as f64)
18273 - 84.6 * (syllables as f64 / words as f64);
18274 Ok(Value::Float(score.max(0.0).min(100.0)))
18275 }
18276 _ => Err(RuntimeError::new("flesch_reading_ease() requires string")),
18277 },
18278 );
18279
18280 define(
18282 interp,
18283 "flesch_kincaid_grade",
18284 Some(1),
18285 |_, args| match &args[0] {
18286 Value::String(s) => {
18287 let (words, sentences, syllables) = count_text_stats(s);
18288 if words == 0 || sentences == 0 {
18289 return Ok(Value::Float(0.0));
18290 }
18291 let grade = 0.39 * (words as f64 / sentences as f64)
18292 + 11.8 * (syllables as f64 / words as f64)
18293 - 15.59;
18294 Ok(Value::Float(grade.max(0.0)))
18295 }
18296 _ => Err(RuntimeError::new("flesch_kincaid_grade() requires string")),
18297 },
18298 );
18299
18300 define(
18302 interp,
18303 "automated_readability_index",
18304 Some(1),
18305 |_, args| match &args[0] {
18306 Value::String(s) => {
18307 let chars: usize = s.chars().filter(|c| c.is_alphanumeric()).count();
18308 let words: usize = s.split_whitespace().count();
18309 let sentences: usize = s
18310 .matches(|c| c == '.' || c == '!' || c == '?')
18311 .count()
18312 .max(1);
18313
18314 if words == 0 {
18315 return Ok(Value::Float(0.0));
18316 }
18317
18318 let ari = 4.71 * (chars as f64 / words as f64)
18319 + 0.5 * (words as f64 / sentences as f64)
18320 - 21.43;
18321 Ok(Value::Float(ari.max(0.0)))
18322 }
18323 _ => Err(RuntimeError::new(
18324 "automated_readability_index() requires string",
18325 )),
18326 },
18327 );
18328
18329 define(interp, "reading_time", Some(1), |_, args| {
18331 match &args[0] {
18332 Value::String(s) => {
18333 let words = s.split_whitespace().count();
18334 let minutes = words as f64 / 200.0; Ok(Value::Float(minutes))
18336 }
18337 _ => Err(RuntimeError::new("reading_time() requires string")),
18338 }
18339 });
18340
18341 define(interp, "speaking_time", Some(1), |_, args| {
18343 match &args[0] {
18344 Value::String(s) => {
18345 let words = s.split_whitespace().count();
18346 let minutes = words as f64 / 150.0; Ok(Value::Float(minutes))
18348 }
18349 _ => Err(RuntimeError::new("speaking_time() requires string")),
18350 }
18351 });
18352}
18353
18354fn compute_vader_sentiment(s: &str) -> (f64, f64, f64, f64) {
18360 let positive_words: Vec<(&str, f64)> = vec![
18362 ("love", 3.0),
18363 ("loved", 3.0),
18364 ("loving", 3.0),
18365 ("excellent", 3.0),
18366 ("amazing", 3.0),
18367 ("fantastic", 3.0),
18368 ("wonderful", 3.0),
18369 ("great", 2.5),
18370 ("awesome", 2.5),
18371 ("brilliant", 2.5),
18372 ("superb", 2.5),
18373 ("good", 2.0),
18374 ("nice", 2.0),
18375 ("pleasant", 2.0),
18376 ("happy", 2.0),
18377 ("like", 1.5),
18378 ("enjoy", 1.5),
18379 ("fine", 1.5),
18380 ("okay", 1.0),
18381 ("best", 3.0),
18382 ("perfect", 3.0),
18383 ("beautiful", 2.5),
18384 ("delightful", 2.5),
18385 ("excited", 2.5),
18386 ("thrilled", 3.0),
18387 ("glad", 2.0),
18388 ("pleased", 2.0),
18389 ];
18390
18391 let negative_words: Vec<(&str, f64)> = vec![
18392 ("hate", 3.0),
18393 ("hated", 3.0),
18394 ("hating", 3.0),
18395 ("terrible", 3.0),
18396 ("horrible", 3.0),
18397 ("awful", 3.0),
18398 ("disgusting", 3.0),
18399 ("bad", 2.5),
18400 ("poor", 2.5),
18401 ("worst", 3.0),
18402 ("pathetic", 2.5),
18403 ("sad", 2.0),
18404 ("angry", 2.5),
18405 ("upset", 2.0),
18406 ("disappointed", 2.0),
18407 ("dislike", 1.5),
18408 ("annoying", 2.0),
18409 ("boring", 1.5),
18410 ("mediocre", 1.0),
18411 ("ugly", 2.5),
18412 ("stupid", 2.5),
18413 ("dumb", 2.0),
18414 ("useless", 2.5),
18415 ("painful", 2.5),
18416 ("miserable", 3.0),
18417 ("depressing", 2.5),
18418 ("frustrating", 2.0),
18419 ];
18420
18421 let boosters = vec![
18423 "very",
18424 "really",
18425 "extremely",
18426 "absolutely",
18427 "incredibly",
18428 "totally",
18429 "so",
18430 ];
18431 let dampeners = vec![
18432 "somewhat", "slightly", "a bit", "kind of", "sort of", "barely",
18433 ];
18434
18435 let lower = s.to_lowercase();
18436 let words: Vec<&str> = lower.split_whitespace().collect();
18437
18438 let mut pos_score = 0.0;
18439 let mut neg_score = 0.0;
18440 let mut word_count = 0;
18441
18442 for (i, word) in words.iter().enumerate() {
18443 let mut modifier = 1.0;
18444
18445 if i > 0 {
18447 if boosters.contains(&words[i - 1]) {
18448 modifier = 1.5;
18449 } else if dampeners.iter().any(|d| words[i - 1].contains(d)) {
18450 modifier = 0.5;
18451 }
18452 }
18453
18454 let negated = i > 0
18456 && [
18457 "not",
18458 "no",
18459 "never",
18460 "neither",
18461 "don't",
18462 "doesn't",
18463 "didn't",
18464 "won't",
18465 "wouldn't",
18466 "couldn't",
18467 "shouldn't",
18468 ]
18469 .contains(&words[i - 1]);
18470
18471 if let Some((_, score)) = positive_words.iter().find(|(w, _)| w == word) {
18472 if negated {
18473 neg_score += score * modifier;
18474 } else {
18475 pos_score += score * modifier;
18476 }
18477 word_count += 1;
18478 } else if let Some((_, score)) = negative_words.iter().find(|(w, _)| w == word) {
18479 if negated {
18480 pos_score += score * modifier * 0.5; } else {
18482 neg_score += score * modifier;
18483 }
18484 word_count += 1;
18485 }
18486 }
18487
18488 let total = pos_score + neg_score;
18490 let (pos_norm, neg_norm) = if total > 0.0 {
18491 (pos_score / total, neg_score / total)
18492 } else {
18493 (0.0, 0.0)
18494 };
18495
18496 let neutral = 1.0 - pos_norm - neg_norm;
18497
18498 let compound = if word_count > 0 {
18500 ((pos_score - neg_score) / (word_count as f64 * 3.0))
18501 .max(-1.0)
18502 .min(1.0)
18503 } else {
18504 0.0
18505 };
18506
18507 (pos_norm, neg_norm, neutral.max(0.0), compound)
18508}
18509
18510fn compute_emotions(s: &str) -> HashMap<String, f64> {
18512 let emotion_words: Vec<(&str, &str)> = vec![
18513 ("happy", "joy"),
18515 ("joyful", "joy"),
18516 ("delighted", "joy"),
18517 ("cheerful", "joy"),
18518 ("excited", "joy"),
18519 ("thrilled", "joy"),
18520 ("ecstatic", "joy"),
18521 ("elated", "joy"),
18522 ("sad", "sadness"),
18524 ("unhappy", "sadness"),
18525 ("depressed", "sadness"),
18526 ("miserable", "sadness"),
18527 ("gloomy", "sadness"),
18528 ("heartbroken", "sadness"),
18529 ("sorrowful", "sadness"),
18530 ("melancholy", "sadness"),
18531 ("angry", "anger"),
18533 ("furious", "anger"),
18534 ("enraged", "anger"),
18535 ("irritated", "anger"),
18536 ("annoyed", "anger"),
18537 ("outraged", "anger"),
18538 ("livid", "anger"),
18539 ("mad", "anger"),
18540 ("afraid", "fear"),
18542 ("scared", "fear"),
18543 ("terrified", "fear"),
18544 ("frightened", "fear"),
18545 ("anxious", "fear"),
18546 ("worried", "fear"),
18547 ("nervous", "fear"),
18548 ("panicked", "fear"),
18549 ("surprised", "surprise"),
18551 ("amazed", "surprise"),
18552 ("astonished", "surprise"),
18553 ("shocked", "surprise"),
18554 ("stunned", "surprise"),
18555 ("startled", "surprise"),
18556 ("bewildered", "surprise"),
18557 ("disgusted", "disgust"),
18559 ("revolted", "disgust"),
18560 ("repulsed", "disgust"),
18561 ("sickened", "disgust"),
18562 ("nauseated", "disgust"),
18563 ("appalled", "disgust"),
18564 ("trust", "trust"),
18566 ("confident", "trust"),
18567 ("secure", "trust"),
18568 ("reliable", "trust"),
18569 ("faithful", "trust"),
18570 ("loyal", "trust"),
18571 ("eager", "anticipation"),
18573 ("hopeful", "anticipation"),
18574 ("expectant", "anticipation"),
18575 ("looking forward", "anticipation"),
18576 ("excited", "anticipation"),
18577 ];
18578
18579 let lower = s.to_lowercase();
18580 let mut counts: HashMap<String, f64> = HashMap::new();
18581
18582 for (word, emotion) in emotion_words {
18583 if lower.contains(word) {
18584 *counts.entry(emotion.to_string()).or_insert(0.0) += 1.0;
18585 }
18586 }
18587
18588 let total: f64 = counts.values().sum();
18590 if total > 0.0 {
18591 for v in counts.values_mut() {
18592 *v /= total;
18593 }
18594 }
18595
18596 counts
18597}
18598
18599fn compute_intensity(s: &str) -> f64 {
18601 let intensifiers = vec![
18602 ("very", 1.5),
18603 ("really", 1.5),
18604 ("extremely", 2.0),
18605 ("incredibly", 2.0),
18606 ("absolutely", 2.0),
18607 ("totally", 1.5),
18608 ("completely", 1.5),
18609 ("utterly", 2.0),
18610 ("so", 1.3),
18611 ("such", 1.3),
18612 ("quite", 1.2),
18613 ("rather", 1.1),
18614 ];
18615
18616 let exclamation_boost = 0.5;
18617 let caps_boost = 0.3;
18618
18619 let lower = s.to_lowercase();
18620 let mut score = 1.0;
18621
18622 for (word, boost) in intensifiers {
18623 if lower.contains(word) {
18624 score *= boost;
18625 }
18626 }
18627
18628 let exclamations = s.matches('!').count();
18630 score += exclamations as f64 * exclamation_boost;
18631
18632 let caps_words = s
18634 .split_whitespace()
18635 .filter(|w| w.len() > 2 && w.chars().all(|c| c.is_uppercase()))
18636 .count();
18637 score += caps_words as f64 * caps_boost;
18638
18639 score.min(5.0)
18640}
18641
18642fn compute_sarcasm_score(s: &str) -> (f64, f64, Vec<String>) {
18644 let mut markers = Vec::new();
18645 let mut score: f64 = 0.0;
18646
18647 let lower = s.to_lowercase();
18648
18649 let explicit = vec![
18651 "/s",
18652 "not!",
18653 "yeah right",
18654 "sure thing",
18655 "oh really",
18656 "oh great",
18657 "wow, just wow",
18658 "thanks a lot",
18659 "how wonderful",
18660 "isn't that special",
18661 "clearly",
18662 "obviously",
18663 "shocking",
18664 "no way",
18665 "what a surprise",
18666 ];
18667
18668 for marker in &explicit {
18669 if lower.contains(marker) {
18670 markers.push(format!("explicit: {}", marker));
18671 score += 0.4;
18672 }
18673 }
18674
18675 let hyperbolic = vec![
18677 "best thing ever",
18678 "worst thing ever",
18679 "literally dying",
18680 "absolutely perfect",
18681 "world's greatest",
18682 "totally awesome",
18683 "so much fun",
18684 "couldn't be happier",
18685 ];
18686
18687 for h in &hyperbolic {
18688 if lower.contains(h) {
18689 markers.push(format!("hyperbole: {}", h));
18690 score += 0.3;
18691 }
18692 }
18693
18694 let has_positive = ["great", "wonderful", "amazing", "love", "best", "awesome"]
18696 .iter()
18697 .any(|w| lower.contains(w));
18698 let has_negative_context = ["but", "however", "although", "except", "unfortunately"]
18699 .iter()
18700 .any(|w| lower.contains(w));
18701
18702 if has_positive && has_negative_context {
18703 markers.push("positive-negative contrast".to_string());
18704 score += 0.25;
18705 }
18706
18707 let quote_pattern = Regex::new(r#"["'](\w+)["']"#).unwrap();
18709 for cap in quote_pattern.captures_iter(s) {
18710 if let Some(m) = cap.get(1) {
18711 let word = m.as_str().to_lowercase();
18712 if [
18713 "great",
18714 "wonderful",
18715 "helpful",
18716 "useful",
18717 "smart",
18718 "genius",
18719 "brilliant",
18720 ]
18721 .contains(&word.as_str())
18722 {
18723 markers.push(format!("air quotes: \"{}\"", word));
18724 score += 0.35;
18725 }
18726 }
18727 }
18728
18729 if s.contains("...") || s.contains("!!!") || s.contains("???") {
18731 markers.push("excessive punctuation".to_string());
18732 score += 0.15;
18733 }
18734
18735 let confidence = if markers.is_empty() {
18737 0.0
18738 } else {
18739 (markers.len() as f64 * 0.25).min(1.0)
18740 };
18741
18742 (score.min(1.0), confidence, markers)
18743}
18744
18745fn compute_irony_score(s: &str) -> f64 {
18747 let mut score: f64 = 0.0;
18748 let lower = s.to_lowercase();
18749
18750 let irony_phrases = vec![
18752 "of course",
18753 "as expected",
18754 "naturally",
18755 "predictably",
18756 "who would have thought",
18757 "surprise surprise",
18758 "go figure",
18759 "typical",
18760 "as usual",
18761 "yet again",
18762 "once again",
18763 ];
18764
18765 for phrase in irony_phrases {
18766 if lower.contains(phrase) {
18767 score += 0.2;
18768 }
18769 }
18770
18771 if lower.contains("but") || lower.contains("yet") || lower.contains("however") {
18773 score += 0.1;
18774 }
18775
18776 if s.contains('?')
18778 && (lower.starts_with("isn't")
18779 || lower.starts_with("aren't")
18780 || lower.starts_with("doesn't")
18781 || lower.starts_with("don't")
18782 || lower.contains("right?")
18783 || lower.contains("isn't it"))
18784 {
18785 score += 0.25;
18786 }
18787
18788 score.min(1.0)
18789}
18790
18791fn create_hash_vector(s: &str, dims: usize) -> Vec<f64> {
18793 let mut vector = vec![0.0f64; dims];
18794
18795 for word in s.to_lowercase().split_whitespace() {
18796 let hash = compute_hash(word, 0);
18797 let idx = (hash as usize) % dims;
18798 vector[idx] += 1.0;
18799 }
18800
18801 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
18803 if magnitude > 0.0 {
18804 for v in vector.iter_mut() {
18805 *v /= magnitude;
18806 }
18807 }
18808
18809 vector
18810}
18811
18812fn count_text_stats(s: &str) -> (usize, usize, usize) {
18814 let words: Vec<&str> = s.split_whitespace().collect();
18815 let word_count = words.len();
18816 let sentence_count = s
18817 .matches(|c| c == '.' || c == '!' || c == '?')
18818 .count()
18819 .max(1);
18820
18821 let mut syllable_count = 0;
18822 for word in &words {
18823 syllable_count += count_syllables(word);
18824 }
18825
18826 (word_count, sentence_count, syllable_count)
18827}
18828
18829fn count_syllables(word: &str) -> usize {
18831 let word = word.to_lowercase();
18832 let vowels = ['a', 'e', 'i', 'o', 'u', 'y'];
18833 let mut count = 0;
18834 let mut prev_was_vowel = false;
18835
18836 for c in word.chars() {
18837 let is_vowel = vowels.contains(&c);
18838 if is_vowel && !prev_was_vowel {
18839 count += 1;
18840 }
18841 prev_was_vowel = is_vowel;
18842 }
18843
18844 if word.ends_with('e') && count > 1 {
18846 count -= 1;
18847 }
18848
18849 count.max(1)
18850}
18851
18852fn compute_soundex(s: &str) -> String {
18854 if s.is_empty() {
18855 return "0000".to_string();
18856 }
18857
18858 let s = s.to_uppercase();
18859 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
18860
18861 if chars.is_empty() {
18862 return "0000".to_string();
18863 }
18864
18865 let first = chars[0];
18866 let mut code = String::new();
18867 code.push(first);
18868
18869 let get_code = |c: char| -> char {
18870 match c {
18871 'B' | 'F' | 'P' | 'V' => '1',
18872 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => '2',
18873 'D' | 'T' => '3',
18874 'L' => '4',
18875 'M' | 'N' => '5',
18876 'R' => '6',
18877 _ => '0',
18878 }
18879 };
18880
18881 let mut prev_code = get_code(first);
18882
18883 for &c in chars.iter().skip(1) {
18884 let curr_code = get_code(c);
18885 if curr_code != '0' && curr_code != prev_code {
18886 code.push(curr_code);
18887 if code.len() == 4 {
18888 break;
18889 }
18890 }
18891 prev_code = curr_code;
18892 }
18893
18894 while code.len() < 4 {
18895 code.push('0');
18896 }
18897
18898 code
18899}
18900
18901fn compute_metaphone(s: &str) -> String {
18903 let s = s.to_uppercase();
18904 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
18905
18906 if chars.is_empty() {
18907 return String::new();
18908 }
18909
18910 let mut result = String::new();
18911 let mut i = 0;
18912
18913 if chars.len() >= 2 {
18915 let prefix: String = chars[0..2].iter().collect();
18916 if ["KN", "GN", "PN", "AE", "WR"].contains(&prefix.as_str()) {
18917 i = 1;
18918 }
18919 }
18920
18921 while i < chars.len() && result.len() < 6 {
18922 let c = chars[i];
18923 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
18924 let next = chars.get(i + 1).copied();
18925
18926 let code = match c {
18927 'A' | 'E' | 'I' | 'O' | 'U' => {
18928 if i == 0 {
18929 Some(c)
18930 } else {
18931 None
18932 }
18933 }
18934 'B' => {
18935 if prev != Some('M') || i == chars.len() - 1 {
18936 Some('B')
18937 } else {
18938 None
18939 }
18940 }
18941 'C' => {
18942 if next == Some('H') {
18943 Some('X')
18944 } else if matches!(next, Some('I') | Some('E') | Some('Y')) {
18945 Some('S')
18946 } else {
18947 Some('K')
18948 }
18949 }
18950 'D' => {
18951 if next == Some('G')
18952 && matches!(chars.get(i + 2), Some('E') | Some('I') | Some('Y'))
18953 {
18954 Some('J')
18955 } else {
18956 Some('T')
18957 }
18958 }
18959 'F' => Some('F'),
18960 'G' => {
18961 if next == Some('H')
18962 && !matches!(
18963 chars.get(i + 2),
18964 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18965 )
18966 {
18967 None
18968 } else if matches!(next, Some('N') | Some('E') | Some('I') | Some('Y')) {
18969 Some('J')
18970 } else {
18971 Some('K')
18972 }
18973 }
18974 'H' => {
18975 if matches!(
18976 prev,
18977 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18978 ) {
18979 None
18980 } else if matches!(
18981 next,
18982 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
18983 ) {
18984 Some('H')
18985 } else {
18986 None
18987 }
18988 }
18989 'J' => Some('J'),
18990 'K' => {
18991 if prev != Some('C') {
18992 Some('K')
18993 } else {
18994 None
18995 }
18996 }
18997 'L' => Some('L'),
18998 'M' => Some('M'),
18999 'N' => Some('N'),
19000 'P' => {
19001 if next == Some('H') {
19002 Some('F')
19003 } else {
19004 Some('P')
19005 }
19006 }
19007 'Q' => Some('K'),
19008 'R' => Some('R'),
19009 'S' => {
19010 if next == Some('H') {
19011 Some('X')
19012 } else {
19013 Some('S')
19014 }
19015 }
19016 'T' => {
19017 if next == Some('H') {
19018 Some('0') } else if next == Some('I') && matches!(chars.get(i + 2), Some('O') | Some('A')) {
19020 Some('X')
19021 } else {
19022 Some('T')
19023 }
19024 }
19025 'V' => Some('F'),
19026 'W' | 'Y' => {
19027 if matches!(
19028 next,
19029 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
19030 ) {
19031 Some(c)
19032 } else {
19033 None
19034 }
19035 }
19036 'X' => {
19037 result.push('K');
19038 Some('S')
19039 }
19040 'Z' => Some('S'),
19041 _ => None,
19042 };
19043
19044 if let Some(ch) = code {
19045 result.push(ch);
19046 }
19047
19048 if next == Some(c) {
19050 i += 1;
19051 }
19052 i += 1;
19053 }
19054
19055 result
19056}
19057
19058fn compute_cologne(s: &str) -> String {
19060 let s = s.to_uppercase();
19061 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
19062
19063 if chars.is_empty() {
19064 return String::new();
19065 }
19066
19067 let mut result = String::new();
19068
19069 for (i, &c) in chars.iter().enumerate() {
19070 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
19071 let next = chars.get(i + 1).copied();
19072
19073 let code = match c {
19074 'A' | 'E' | 'I' | 'O' | 'U' | 'J' | 'Y' => '0',
19075 'H' => continue,
19076 'B' | 'P' => '1',
19077 'D' | 'T' => {
19078 if matches!(next, Some('C') | Some('S') | Some('Z')) {
19079 '8'
19080 } else {
19081 '2'
19082 }
19083 }
19084 'F' | 'V' | 'W' => '3',
19085 'G' | 'K' | 'Q' => '4',
19086 'C' => {
19087 if i == 0 {
19088 if matches!(
19089 next,
19090 Some('A')
19091 | Some('H')
19092 | Some('K')
19093 | Some('L')
19094 | Some('O')
19095 | Some('Q')
19096 | Some('R')
19097 | Some('U')
19098 | Some('X')
19099 ) {
19100 '4'
19101 } else {
19102 '8'
19103 }
19104 } else if matches!(prev, Some('S') | Some('Z')) {
19105 '8'
19106 } else if matches!(
19107 next,
19108 Some('A')
19109 | Some('H')
19110 | Some('K')
19111 | Some('O')
19112 | Some('Q')
19113 | Some('U')
19114 | Some('X')
19115 ) {
19116 '4'
19117 } else {
19118 '8'
19119 }
19120 }
19121 'X' => {
19122 if matches!(prev, Some('C') | Some('K') | Some('Q')) {
19123 '8'
19124 } else {
19125 result.push('4');
19126 '8'
19127 }
19128 }
19129 'L' => '5',
19130 'M' | 'N' => '6',
19131 'R' => '7',
19132 'S' | 'Z' => '8',
19133 _ => continue,
19134 };
19135
19136 result.push(code);
19137 }
19138
19139 let mut deduped = String::new();
19141 let mut prev = None;
19142 for c in result.chars() {
19143 if prev != Some(c) {
19144 deduped.push(c);
19145 }
19146 prev = Some(c);
19147 }
19148
19149 let trimmed: String = deduped.trim_start_matches('0').to_string();
19151 if trimmed.is_empty() {
19152 "0".to_string()
19153 } else {
19154 trimmed
19155 }
19156}
19157
19158fn get_stopwords(lang: &str) -> Vec<&'static str> {
19160 match lang {
19161 "en" | "english" => vec![
19162 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with",
19163 "by", "from", "as", "is", "was", "are", "were", "been", "be", "have", "has", "had",
19164 "do", "does", "did", "will", "would", "could", "should", "may", "might", "must",
19165 "shall", "can", "need", "it", "its", "this", "that", "these", "those", "i", "you",
19166 "he", "she", "we", "they", "me", "him", "her", "us", "them", "my", "your", "his",
19167 "her", "our", "their", "what", "which", "who", "whom", "whose", "when", "where", "why",
19168 "how", "all", "each", "every", "both", "few", "more", "most", "other", "some", "such",
19169 "no", "nor", "not", "only", "own", "same", "so", "than", "too", "very", "just", "also",
19170 "now",
19171 ],
19172 "de" | "german" => vec![
19173 "der", "die", "das", "den", "dem", "des", "ein", "eine", "einer", "einem", "einen",
19174 "und", "oder", "aber", "in", "auf", "an", "zu", "für", "von", "mit", "bei", "als",
19175 "ist", "war", "sind", "waren", "sein", "haben", "hat", "hatte", "werden", "wird",
19176 "wurde", "kann", "können", "muss", "müssen", "soll", "sollen", "will", "wollen", "es",
19177 "sie", "er", "wir", "ihr", "ich", "du", "man", "sich", "nicht", "auch", "nur", "noch",
19178 "schon", "mehr", "sehr", "so",
19179 ],
19180 "fr" | "french" => vec![
19181 "le", "la", "les", "un", "une", "des", "et", "ou", "mais", "dans", "sur", "à", "de",
19182 "pour", "par", "avec", "ce", "cette", "ces", "est", "sont", "était", "être", "avoir",
19183 "a", "ont", "avait", "je", "tu", "il", "elle", "nous", "vous", "ils", "elles", "on",
19184 "ne", "pas", "plus", "moins", "très", "aussi", "que", "qui",
19185 ],
19186 "es" | "spanish" => vec![
19187 "el", "la", "los", "las", "un", "una", "unos", "unas", "y", "o", "pero", "en", "de",
19188 "a", "para", "por", "con", "es", "son", "era", "ser", "estar", "tiene", "tienen", "yo",
19189 "tú", "él", "ella", "nosotros", "ustedes", "ellos", "ellas", "no", "sí", "muy", "más",
19190 "menos", "también", "que", "quien", "cual", "como", "cuando",
19191 ],
19192 "it" | "italian" => vec![
19193 "il", "lo", "la", "i", "gli", "le", "un", "uno", "una", "e", "o", "ma", "in", "di",
19194 "a", "da", "per", "con", "su", "tra", "fra", "è", "sono", "era", "erano", "essere",
19195 "avere", "ha", "hanno", "io", "tu", "lui", "lei", "noi", "voi", "loro", "mi", "ti",
19196 "ci", "non", "più", "molto", "anche", "come", "che", "chi", "quale", "questo",
19197 "quello", "quando", "dove", "perché", "se", "però",
19198 ],
19199 "pt" | "portuguese" => vec![
19200 "o", "a", "os", "as", "um", "uma", "uns", "umas", "e", "ou", "mas", "em", "de", "para",
19201 "por", "com", "sem", "sob", "sobre", "é", "são", "era", "eram", "ser", "estar", "ter",
19202 "tem", "têm", "eu", "tu", "ele", "ela", "nós", "vós", "eles", "elas", "me", "te",
19203 "não", "mais", "muito", "também", "como", "que", "quem", "qual", "este", "esse",
19204 "aquele", "quando", "onde", "porque", "se", "já",
19205 ],
19206 "nl" | "dutch" => vec![
19207 "de", "het", "een", "en", "of", "maar", "in", "op", "aan", "van", "voor", "met", "bij",
19208 "naar", "om", "te", "tot", "uit", "over", "is", "zijn", "was", "waren", "worden",
19209 "wordt", "werd", "hebben", "ik", "je", "jij", "hij", "zij", "wij", "jullie", "ze",
19210 "mij", "jou", "niet", "geen", "meer", "ook", "als", "dat", "die", "wat", "wie", "dit",
19211 "deze", "wanneer", "waar", "waarom", "hoe", "dan", "nog",
19212 ],
19213 "ru" | "russian" => vec![
19214 "и",
19215 "в",
19216 "на",
19217 "с",
19218 "к",
19219 "по",
19220 "за",
19221 "из",
19222 "у",
19223 "о",
19224 "от",
19225 "до",
19226 "для",
19227 "при",
19228 "без",
19229 "под",
19230 "над",
19231 "между",
19232 "через",
19233 "после",
19234 "это",
19235 "то",
19236 "что",
19237 "как",
19238 "так",
19239 "но",
19240 "а",
19241 "или",
19242 "если",
19243 "же",
19244 "я",
19245 "ты",
19246 "он",
19247 "она",
19248 "мы",
19249 "вы",
19250 "они",
19251 "его",
19252 "её",
19253 "их",
19254 "не",
19255 "ни",
19256 "да",
19257 "нет",
19258 "был",
19259 "была",
19260 "были",
19261 "быть",
19262 "есть",
19263 "все",
19264 "всё",
19265 "весь",
19266 "этот",
19267 "тот",
19268 "который",
19269 "когда",
19270 "где",
19271 ],
19272 "ar" | "arabic" => vec![
19273 "في",
19274 "من",
19275 "إلى",
19276 "على",
19277 "عن",
19278 "مع",
19279 "هذا",
19280 "هذه",
19281 "ذلك",
19282 "تلك",
19283 "التي",
19284 "الذي",
19285 "اللذان",
19286 "اللتان",
19287 "الذين",
19288 "اللاتي",
19289 "اللواتي",
19290 "هو",
19291 "هي",
19292 "هم",
19293 "هن",
19294 "أنا",
19295 "أنت",
19296 "نحن",
19297 "أنتم",
19298 "أنتن",
19299 "كان",
19300 "كانت",
19301 "كانوا",
19302 "يكون",
19303 "تكون",
19304 "ليس",
19305 "ليست",
19306 "ليسوا",
19307 "و",
19308 "أو",
19309 "ثم",
19310 "لكن",
19311 "بل",
19312 "إن",
19313 "أن",
19314 "لأن",
19315 "كي",
19316 "حتى",
19317 "ما",
19318 "لا",
19319 "قد",
19320 "كل",
19321 "بعض",
19322 "غير",
19323 "أي",
19324 "كيف",
19325 "متى",
19326 "أين",
19327 ],
19328 "zh" | "chinese" => vec![
19329 "的", "了", "是", "在", "有", "和", "与", "或", "但", "而", "我", "你", "他", "她",
19330 "它", "我们", "你们", "他们", "她们", "这", "那", "这个", "那个", "这些", "那些",
19331 "什么", "哪", "哪个", "不", "没", "没有", "很", "也", "都", "就", "才", "只", "还",
19332 "把", "被", "给", "从", "到", "为", "以", "因为", "所以", "如果", "会", "能", "可以",
19333 "要", "想", "应该", "必须", "可能", "一", "个",
19334 ],
19335 "ja" | "japanese" => vec![
19336 "の",
19337 "に",
19338 "は",
19339 "を",
19340 "た",
19341 "が",
19342 "で",
19343 "て",
19344 "と",
19345 "し",
19346 "れ",
19347 "さ",
19348 "ある",
19349 "いる",
19350 "も",
19351 "する",
19352 "から",
19353 "な",
19354 "こと",
19355 "として",
19356 "い",
19357 "や",
19358 "など",
19359 "なっ",
19360 "ない",
19361 "この",
19362 "ため",
19363 "その",
19364 "あっ",
19365 "よう",
19366 "また",
19367 "もの",
19368 "という",
19369 "あり",
19370 "まで",
19371 "られ",
19372 "なる",
19373 "へ",
19374 "か",
19375 "だ",
19376 "これ",
19377 "によって",
19378 "により",
19379 "おり",
19380 "より",
19381 "による",
19382 "ず",
19383 "なり",
19384 "られる",
19385 "において",
19386 ],
19387 "ko" | "korean" => vec![
19388 "이",
19389 "그",
19390 "저",
19391 "것",
19392 "수",
19393 "등",
19394 "들",
19395 "및",
19396 "에",
19397 "의",
19398 "가",
19399 "을",
19400 "를",
19401 "은",
19402 "는",
19403 "로",
19404 "으로",
19405 "와",
19406 "과",
19407 "도",
19408 "에서",
19409 "까지",
19410 "부터",
19411 "만",
19412 "뿐",
19413 "처럼",
19414 "같이",
19415 "보다",
19416 "하다",
19417 "있다",
19418 "되다",
19419 "없다",
19420 "않다",
19421 "이다",
19422 "아니다",
19423 "나",
19424 "너",
19425 "우리",
19426 "그들",
19427 "이것",
19428 "그것",
19429 "저것",
19430 "무엇",
19431 "어디",
19432 "언제",
19433 "왜",
19434 "어떻게",
19435 "누구",
19436 "어느",
19437 "모든",
19438 "각",
19439 ],
19440 "hi" | "hindi" => vec![
19441 "का",
19442 "के",
19443 "की",
19444 "में",
19445 "है",
19446 "हैं",
19447 "को",
19448 "से",
19449 "पर",
19450 "था",
19451 "थे",
19452 "थी",
19453 "और",
19454 "या",
19455 "लेकिन",
19456 "अगर",
19457 "तो",
19458 "भी",
19459 "ही",
19460 "यह",
19461 "वह",
19462 "इस",
19463 "उस",
19464 "ये",
19465 "वे",
19466 "जो",
19467 "कि",
19468 "क्या",
19469 "कैसे",
19470 "मैं",
19471 "तुम",
19472 "आप",
19473 "हम",
19474 "वे",
19475 "उन्हें",
19476 "उनके",
19477 "अपने",
19478 "नहीं",
19479 "न",
19480 "कुछ",
19481 "कोई",
19482 "सब",
19483 "बहुत",
19484 "कम",
19485 "ज्यादा",
19486 "होना",
19487 "करना",
19488 "जाना",
19489 "आना",
19490 "देना",
19491 "लेना",
19492 "रहना",
19493 "सकना",
19494 ],
19495 "tr" | "turkish" => vec![
19496 "bir",
19497 "ve",
19498 "bu",
19499 "da",
19500 "de",
19501 "için",
19502 "ile",
19503 "mi",
19504 "ne",
19505 "o",
19506 "var",
19507 "ben",
19508 "sen",
19509 "biz",
19510 "siz",
19511 "onlar",
19512 "ki",
19513 "ama",
19514 "çok",
19515 "daha",
19516 "gibi",
19517 "kadar",
19518 "sonra",
19519 "şey",
19520 "kendi",
19521 "bütün",
19522 "her",
19523 "bazı",
19524 "olan",
19525 "olarak",
19526 "değil",
19527 "ya",
19528 "hem",
19529 "veya",
19530 "ancak",
19531 "ise",
19532 "göre",
19533 "rağmen",
19534 "dolayı",
19535 "üzere",
19536 "karşı",
19537 "arasında",
19538 "olan",
19539 "oldu",
19540 "olur",
19541 "olmak",
19542 "etmek",
19543 "yapmak",
19544 "demek",
19545 ],
19546 "pl" | "polish" => vec![
19547 "i",
19548 "w",
19549 "z",
19550 "na",
19551 "do",
19552 "o",
19553 "że",
19554 "to",
19555 "nie",
19556 "się",
19557 "jest",
19558 "tak",
19559 "jak",
19560 "ale",
19561 "po",
19562 "co",
19563 "czy",
19564 "lub",
19565 "oraz",
19566 "ja",
19567 "ty",
19568 "on",
19569 "ona",
19570 "my",
19571 "wy",
19572 "oni",
19573 "one",
19574 "pan",
19575 "pani",
19576 "ten",
19577 "ta",
19578 "te",
19579 "tego",
19580 "tej",
19581 "tym",
19582 "tych",
19583 "który",
19584 "która",
19585 "być",
19586 "mieć",
19587 "móc",
19588 "musieć",
19589 "chcieć",
19590 "wiedzieć",
19591 "mówić",
19592 "bardzo",
19593 "tylko",
19594 "już",
19595 "jeszcze",
19596 "też",
19597 "więc",
19598 "jednak",
19599 ],
19600 "sv" | "swedish" => vec![
19601 "och", "i", "att", "det", "som", "en", "på", "är", "av", "för", "med", "till", "den",
19602 "har", "de", "inte", "om", "ett", "men", "jag", "du", "han", "hon", "vi", "ni", "de",
19603 "dem", "sig", "sin", "var", "från", "eller", "när", "kan", "ska", "så", "än", "nu",
19604 "också", "bara", "mycket", "mer", "andra", "detta", "sedan", "hade", "varit", "skulle",
19605 "vara", "bli", "blev", "blir", "göra",
19606 ],
19607 _ => vec![
19608 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
19609 ],
19610 }
19611}
19612
19613fn compute_hash(s: &str, seed: u64) -> u64 {
19615 let mut hash: u64 = seed.wrapping_mul(0x517cc1b727220a95);
19616 for b in s.bytes() {
19617 hash = hash.wrapping_mul(31).wrapping_add(b as u64);
19618 }
19619 hash
19620}
19621
19622fn register_hologram(interp: &mut Interpreter) {
19642 use crate::interpreter::{
19643 RuntimeAffect, RuntimeConfidence, RuntimeEmotion, RuntimeFormality, RuntimeIntensity,
19644 RuntimeSentiment,
19645 };
19646
19647 define(interp, "emotional_hologram", Some(1), |_, args| {
19650 let affect = match &args[0] {
19651 Value::Affective { affect, .. } => affect.clone(),
19652 _ => RuntimeAffect {
19653 sentiment: None,
19654 sarcasm: false,
19655 intensity: None,
19656 formality: None,
19657 emotion: None,
19658 confidence: None,
19659 },
19660 };
19661
19662 let mut hologram = std::collections::HashMap::new();
19663
19664 let valence = match affect.sentiment {
19666 Some(RuntimeSentiment::Positive) => 1.0,
19667 Some(RuntimeSentiment::Negative) => -1.0,
19668 Some(RuntimeSentiment::Neutral) | None => 0.0,
19669 };
19670 hologram.insert("valence".to_string(), Value::Float(valence));
19671
19672 let arousal = match affect.intensity {
19674 Some(RuntimeIntensity::Down) => 0.25,
19675 None => 0.5,
19676 Some(RuntimeIntensity::Up) => 0.75,
19677 Some(RuntimeIntensity::Max) => 1.0,
19678 };
19679 hologram.insert("arousal".to_string(), Value::Float(arousal));
19680
19681 let dominance = match affect.formality {
19683 Some(RuntimeFormality::Informal) => 0.25,
19684 None => 0.5,
19685 Some(RuntimeFormality::Formal) => 0.85,
19686 };
19687 hologram.insert("dominance".to_string(), Value::Float(dominance));
19688
19689 let authenticity = if affect.sarcasm { -0.9 } else { 0.9 };
19691 hologram.insert("authenticity".to_string(), Value::Float(authenticity));
19692
19693 let certainty = match affect.confidence {
19695 Some(RuntimeConfidence::Low) => 0.2,
19696 None | Some(RuntimeConfidence::Medium) => 0.5,
19697 Some(RuntimeConfidence::High) => 0.9,
19698 };
19699 hologram.insert("certainty".to_string(), Value::Float(certainty));
19700
19701 let emotion_index = match affect.emotion {
19703 Some(RuntimeEmotion::Joy) => 0,
19704 Some(RuntimeEmotion::Sadness) => 1,
19705 Some(RuntimeEmotion::Anger) => 2,
19706 Some(RuntimeEmotion::Fear) => 3,
19707 Some(RuntimeEmotion::Surprise) => 4,
19708 Some(RuntimeEmotion::Love) => 5,
19709 None => -1,
19710 };
19711 hologram.insert("emotion_index".to_string(), Value::Int(emotion_index));
19712
19713 let emotion_name = match affect.emotion {
19715 Some(RuntimeEmotion::Joy) => "joy",
19716 Some(RuntimeEmotion::Sadness) => "sadness",
19717 Some(RuntimeEmotion::Anger) => "anger",
19718 Some(RuntimeEmotion::Fear) => "fear",
19719 Some(RuntimeEmotion::Surprise) => "surprise",
19720 Some(RuntimeEmotion::Love) => "love",
19721 None => "none",
19722 };
19723 hologram.insert(
19724 "emotion".to_string(),
19725 Value::String(Rc::new(emotion_name.to_string())),
19726 );
19727
19728 Ok(Value::Map(Rc::new(RefCell::new(hologram))))
19729 });
19730
19731 define(interp, "emotional_distance", Some(2), |interp, args| {
19733 let h1 = get_hologram_values(&args[0], interp)?;
19735 let h2 = get_hologram_values(&args[1], interp)?;
19736
19737 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();
19744
19745 Ok(Value::Float(dist))
19746 });
19747
19748 define(interp, "emotional_similarity", Some(2), |interp, args| {
19750 let h1 = get_hologram_values(&args[0], interp)?;
19751 let h2 = get_hologram_values(&args[1], interp)?;
19752
19753 let dot = h1.0 * h2.0 + h1.1 * h2.1 + h1.2 * h2.2 + h1.3 * h2.3 + h1.4 * h2.4;
19754 let mag1 =
19755 (h1.0.powi(2) + h1.1.powi(2) + h1.2.powi(2) + h1.3.powi(2) + h1.4.powi(2)).sqrt();
19756 let mag2 =
19757 (h2.0.powi(2) + h2.1.powi(2) + h2.2.powi(2) + h2.3.powi(2) + h2.4.powi(2)).sqrt();
19758
19759 let similarity = if mag1 > 0.0 && mag2 > 0.0 {
19760 (dot / (mag1 * mag2) + 1.0) / 2.0 } else {
19762 0.5
19763 };
19764
19765 Ok(Value::Float(similarity))
19766 });
19767
19768 define(interp, "emotional_dissonance", Some(1), |_, args| {
19771 let affect = match &args[0] {
19772 Value::Affective { affect, .. } => affect.clone(),
19773 _ => return Ok(Value::Float(0.0)),
19774 };
19775
19776 let mut dissonance: f64 = 0.0;
19777
19778 if matches!(affect.sentiment, Some(RuntimeSentiment::Positive)) && affect.sarcasm {
19780 dissonance += 0.4;
19781 }
19782
19783 if matches!(affect.sentiment, Some(RuntimeSentiment::Negative)) && affect.sarcasm {
19785 dissonance += 0.1;
19786 }
19787
19788 if matches!(affect.confidence, Some(RuntimeConfidence::High))
19790 && matches!(affect.intensity, Some(RuntimeIntensity::Down))
19791 {
19792 dissonance += 0.2;
19793 }
19794
19795 if matches!(affect.formality, Some(RuntimeFormality::Formal)) {
19797 if matches!(
19798 affect.emotion,
19799 Some(RuntimeEmotion::Anger) | Some(RuntimeEmotion::Fear)
19800 ) {
19801 dissonance += 0.3;
19802 }
19803 }
19804
19805 if matches!(
19807 affect.emotion,
19808 Some(RuntimeEmotion::Joy) | Some(RuntimeEmotion::Love)
19809 ) && affect.sarcasm
19810 {
19811 dissonance += 0.3;
19812 }
19813
19814 Ok(Value::Float(dissonance.min(1.0)))
19815 });
19816
19817 define(interp, "emotional_fingerprint", Some(1), |interp, args| {
19820 let h = get_hologram_values(&args[0], interp)?;
19821
19822 let repr = format!(
19824 "hologram:v{:.4}:a{:.4}:d{:.4}:auth{:.4}:c{:.4}",
19825 h.0, h.1, h.2, h.3, h.4
19826 );
19827
19828 let hash = blake3::hash(repr.as_bytes());
19830 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
19831 });
19832
19833 define(interp, "emotional_morph", Some(3), |interp, args| {
19836 let h1 = get_hologram_values(&args[0], interp)?;
19837 let h2 = get_hologram_values(&args[1], interp)?;
19838 let t = match &args[2] {
19839 Value::Float(f) => f.max(0.0).min(1.0),
19840 Value::Int(i) => (*i as f64).max(0.0).min(1.0),
19841 _ => {
19842 return Err(RuntimeError::new(
19843 "emotional_morph() requires numeric t value",
19844 ))
19845 }
19846 };
19847
19848 let mut result = std::collections::HashMap::new();
19849 result.insert(
19850 "valence".to_string(),
19851 Value::Float(h1.0 + (h2.0 - h1.0) * t),
19852 );
19853 result.insert(
19854 "arousal".to_string(),
19855 Value::Float(h1.1 + (h2.1 - h1.1) * t),
19856 );
19857 result.insert(
19858 "dominance".to_string(),
19859 Value::Float(h1.2 + (h2.2 - h1.2) * t),
19860 );
19861 result.insert(
19862 "authenticity".to_string(),
19863 Value::Float(h1.3 + (h2.3 - h1.3) * t),
19864 );
19865 result.insert(
19866 "certainty".to_string(),
19867 Value::Float(h1.4 + (h2.4 - h1.4) * t),
19868 );
19869
19870 Ok(Value::Map(Rc::new(RefCell::new(result))))
19871 });
19872
19873 define(interp, "cultural_emotion", Some(2), |_, args| {
19879 let emotion = match &args[0] {
19880 Value::Affective { affect, .. } => affect.emotion.clone(),
19881 Value::String(s) => match s.as_str() {
19882 "joy" => Some(RuntimeEmotion::Joy),
19883 "sadness" => Some(RuntimeEmotion::Sadness),
19884 "anger" => Some(RuntimeEmotion::Anger),
19885 "fear" => Some(RuntimeEmotion::Fear),
19886 "surprise" => Some(RuntimeEmotion::Surprise),
19887 "love" => Some(RuntimeEmotion::Love),
19888 _ => None,
19889 },
19890 _ => None,
19891 };
19892
19893 let culture = match &args[1] {
19894 Value::String(s) => s.to_lowercase(),
19895 _ => {
19896 return Err(RuntimeError::new(
19897 "cultural_emotion() requires string culture",
19898 ))
19899 }
19900 };
19901
19902 let result = match (emotion, culture.as_str()) {
19903 (Some(RuntimeEmotion::Joy), "japanese" | "ja") => create_cultural_entry(
19905 "木漏れ日",
19906 "komorebi",
19907 "sunlight filtering through leaves - peaceful joy",
19908 ),
19909 (Some(RuntimeEmotion::Sadness), "japanese" | "ja") => create_cultural_entry(
19910 "物の哀れ",
19911 "mono no aware",
19912 "the pathos of things - bittersweet awareness of impermanence",
19913 ),
19914 (Some(RuntimeEmotion::Love), "japanese" | "ja") => create_cultural_entry(
19915 "甘え",
19916 "amae",
19917 "indulgent dependence on another's benevolence",
19918 ),
19919 (Some(RuntimeEmotion::Fear), "japanese" | "ja") => create_cultural_entry(
19920 "空気を読む",
19921 "kuuki wo yomu",
19922 "anxiety about reading the room",
19923 ),
19924
19925 (Some(RuntimeEmotion::Sadness), "portuguese" | "pt") => create_cultural_entry(
19927 "saudade",
19928 "saudade",
19929 "melancholic longing for something or someone absent",
19930 ),
19931 (Some(RuntimeEmotion::Joy), "portuguese" | "pt") => {
19932 create_cultural_entry("alegria", "alegria", "exuberant collective joy")
19933 }
19934
19935 (Some(RuntimeEmotion::Joy), "german" | "de") => create_cultural_entry(
19937 "Schadenfreude",
19938 "schadenfreude",
19939 "pleasure derived from another's misfortune",
19940 ),
19941 (Some(RuntimeEmotion::Sadness), "german" | "de") => create_cultural_entry(
19942 "Weltschmerz",
19943 "weltschmerz",
19944 "world-weariness, melancholy about the world's state",
19945 ),
19946 (Some(RuntimeEmotion::Fear), "german" | "de") => create_cultural_entry(
19947 "Torschlusspanik",
19948 "torschlusspanik",
19949 "fear of diminishing opportunities with age",
19950 ),
19951
19952 (Some(RuntimeEmotion::Joy), "danish" | "da") => {
19954 create_cultural_entry("hygge", "hygge", "cozy contentment and conviviality")
19955 }
19956
19957 (Some(RuntimeEmotion::Joy), "arabic" | "ar") => {
19959 create_cultural_entry("طرب", "tarab", "musical ecstasy, enchantment through art")
19960 }
19961 (Some(RuntimeEmotion::Love), "arabic" | "ar") => {
19962 create_cultural_entry("هوى", "hawa", "passionate, sometimes irrational love")
19963 }
19964
19965 (Some(RuntimeEmotion::Sadness), "korean" | "ko") => create_cultural_entry(
19967 "한",
19968 "han",
19969 "collective grief and resentment from historical suffering",
19970 ),
19971 (Some(RuntimeEmotion::Joy), "korean" | "ko") => create_cultural_entry(
19972 "정",
19973 "jeong",
19974 "deep affection and attachment formed over time",
19975 ),
19976
19977 (Some(RuntimeEmotion::Sadness), "russian" | "ru") => {
19979 create_cultural_entry("тоска", "toska", "spiritual anguish without specific cause")
19980 }
19981
19982 (Some(RuntimeEmotion::Love), "hindi" | "hi") => {
19984 create_cultural_entry("विरह", "viraha", "longing for an absent beloved")
19985 }
19986
19987 (Some(RuntimeEmotion::Anger), "finnish" | "fi") => {
19989 create_cultural_entry("sisu", "sisu", "stoic determination and grit in adversity")
19990 }
19991
19992 (Some(e), _) => {
19994 let name = match e {
19995 RuntimeEmotion::Joy => "joy",
19996 RuntimeEmotion::Sadness => "sadness",
19997 RuntimeEmotion::Anger => "anger",
19998 RuntimeEmotion::Fear => "fear",
19999 RuntimeEmotion::Surprise => "surprise",
20000 RuntimeEmotion::Love => "love",
20001 };
20002 create_cultural_entry(name, name, "universal emotion")
20003 }
20004 (None, _) => create_cultural_entry("none", "none", "no emotion"),
20005 };
20006
20007 Ok(result)
20008 });
20009
20010 define(interp, "list_cultural_emotions", Some(1), |_, args| {
20012 let culture = match &args[0] {
20013 Value::String(s) => s.to_lowercase(),
20014 _ => {
20015 return Err(RuntimeError::new(
20016 "list_cultural_emotions() requires string culture",
20017 ))
20018 }
20019 };
20020
20021 let emotions: Vec<(&str, &str, &str)> = match culture.as_str() {
20022 "japanese" | "ja" => vec![
20023 ("木漏れ日", "komorebi", "sunlight through leaves"),
20024 ("物の哀れ", "mono no aware", "pathos of things"),
20025 ("甘え", "amae", "indulgent dependence"),
20026 ("侘寂", "wabi-sabi", "beauty in imperfection"),
20027 ("生きがい", "ikigai", "reason for being"),
20028 ],
20029 "german" | "de" => vec![
20030 ("Schadenfreude", "schadenfreude", "joy at misfortune"),
20031 ("Weltschmerz", "weltschmerz", "world-weariness"),
20032 ("Torschlusspanik", "torschlusspanik", "gate-closing panic"),
20033 ("Sehnsucht", "sehnsucht", "deep longing"),
20034 ("Wanderlust", "wanderlust", "desire to travel"),
20035 ],
20036 "portuguese" | "pt" => vec![
20037 ("saudade", "saudade", "melancholic longing"),
20038 ("alegria", "alegria", "exuberant joy"),
20039 ("desabafar", "desabafar", "emotional unburdening"),
20040 ],
20041 "danish" | "da" => vec![("hygge", "hygge", "cozy contentment")],
20042 "korean" | "ko" => vec![
20043 ("한", "han", "collective grief"),
20044 ("정", "jeong", "deep affection"),
20045 ("눈치", "nunchi", "situational awareness"),
20046 ],
20047 "arabic" | "ar" => vec![
20048 ("طرب", "tarab", "musical ecstasy"),
20049 ("هوى", "hawa", "passionate love"),
20050 ("صبر", "sabr", "patient perseverance"),
20051 ],
20052 "russian" | "ru" => vec![
20053 ("тоска", "toska", "spiritual anguish"),
20054 ("пошлость", "poshlost", "spiritual vulgarity"),
20055 ],
20056 "finnish" | "fi" => vec![("sisu", "sisu", "stoic determination")],
20057 "hindi" | "hi" => vec![
20058 ("विरह", "viraha", "longing for beloved"),
20059 ("जुगाड़", "jugaad", "creative improvisation"),
20060 ],
20061 _ => vec![
20062 ("joy", "joy", "universal happiness"),
20063 ("sadness", "sadness", "universal sorrow"),
20064 ("anger", "anger", "universal frustration"),
20065 ("fear", "fear", "universal anxiety"),
20066 ("surprise", "surprise", "universal amazement"),
20067 ("love", "love", "universal affection"),
20068 ],
20069 };
20070
20071 let result: Vec<Value> = emotions
20072 .iter()
20073 .map(|(native, romanized, meaning)| create_cultural_entry(native, romanized, meaning))
20074 .collect();
20075
20076 Ok(Value::Array(Rc::new(RefCell::new(result))))
20077 });
20078
20079 define(interp, "hologram_info", Some(0), |_, _| {
20081 let mut info = std::collections::HashMap::new();
20082
20083 info.insert(
20084 "dimensions".to_string(),
20085 Value::Array(Rc::new(RefCell::new(vec![
20086 Value::String(Rc::new("valence".to_string())),
20087 Value::String(Rc::new("arousal".to_string())),
20088 Value::String(Rc::new("dominance".to_string())),
20089 Value::String(Rc::new("authenticity".to_string())),
20090 Value::String(Rc::new("certainty".to_string())),
20091 Value::String(Rc::new("emotion_index".to_string())),
20092 ]))),
20093 );
20094
20095 info.insert(
20096 "supported_cultures".to_string(),
20097 Value::Array(Rc::new(RefCell::new(vec![
20098 Value::String(Rc::new("japanese".to_string())),
20099 Value::String(Rc::new("german".to_string())),
20100 Value::String(Rc::new("portuguese".to_string())),
20101 Value::String(Rc::new("danish".to_string())),
20102 Value::String(Rc::new("korean".to_string())),
20103 Value::String(Rc::new("arabic".to_string())),
20104 Value::String(Rc::new("russian".to_string())),
20105 Value::String(Rc::new("finnish".to_string())),
20106 Value::String(Rc::new("hindi".to_string())),
20107 ]))),
20108 );
20109
20110 let funcs = vec![
20111 "emotional_hologram",
20112 "emotional_distance",
20113 "emotional_similarity",
20114 "emotional_dissonance",
20115 "emotional_fingerprint",
20116 "emotional_morph",
20117 "cultural_emotion",
20118 "list_cultural_emotions",
20119 "hologram_info",
20120 ];
20121 let func_values: Vec<Value> = funcs
20122 .iter()
20123 .map(|s| Value::String(Rc::new(s.to_string())))
20124 .collect();
20125 info.insert(
20126 "functions".to_string(),
20127 Value::Array(Rc::new(RefCell::new(func_values))),
20128 );
20129
20130 Ok(Value::Map(Rc::new(RefCell::new(info))))
20131 });
20132}
20133
20134fn get_hologram_values(
20136 val: &Value,
20137 _interp: &mut Interpreter,
20138) -> Result<(f64, f64, f64, f64, f64), RuntimeError> {
20139 use crate::interpreter::{
20140 RuntimeAffect, RuntimeConfidence, RuntimeFormality, RuntimeIntensity, RuntimeSentiment,
20141 };
20142
20143 let affect = match val {
20144 Value::Affective { affect, .. } => affect.clone(),
20145 Value::Map(m) => {
20146 let map = m.borrow();
20148 let v = extract_float(&map, "valence").unwrap_or(0.0);
20149 let a = extract_float(&map, "arousal").unwrap_or(0.5);
20150 let d = extract_float(&map, "dominance").unwrap_or(0.5);
20151 let auth = extract_float(&map, "authenticity").unwrap_or(0.9);
20152 let c = extract_float(&map, "certainty").unwrap_or(0.5);
20153 return Ok((v, a, d, auth, c));
20154 }
20155 _ => RuntimeAffect {
20156 sentiment: None,
20157 sarcasm: false,
20158 intensity: None,
20159 formality: None,
20160 emotion: None,
20161 confidence: None,
20162 },
20163 };
20164
20165 let v = match affect.sentiment {
20166 Some(RuntimeSentiment::Positive) => 1.0,
20167 Some(RuntimeSentiment::Negative) => -1.0,
20168 _ => 0.0,
20169 };
20170 let a = match affect.intensity {
20171 Some(RuntimeIntensity::Down) => 0.25,
20172 Some(RuntimeIntensity::Up) => 0.75,
20173 Some(RuntimeIntensity::Max) => 1.0,
20174 None => 0.5,
20175 };
20176 let d = match affect.formality {
20177 Some(RuntimeFormality::Informal) => 0.25,
20178 Some(RuntimeFormality::Formal) => 0.85,
20179 None => 0.5,
20180 };
20181 let auth = if affect.sarcasm { -0.9 } else { 0.9 };
20182 let c = match affect.confidence {
20183 Some(RuntimeConfidence::Low) => 0.2,
20184 Some(RuntimeConfidence::High) => 0.9,
20185 _ => 0.5,
20186 };
20187
20188 Ok((v, a, d, auth, c))
20189}
20190
20191fn extract_float(map: &std::collections::HashMap<String, Value>, key: &str) -> Option<f64> {
20192 match map.get(key) {
20193 Some(Value::Float(f)) => Some(*f),
20194 Some(Value::Int(i)) => Some(*i as f64),
20195 _ => None,
20196 }
20197}
20198
20199fn create_cultural_entry(native: &str, romanized: &str, meaning: &str) -> Value {
20200 let mut entry = std::collections::HashMap::new();
20201 entry.insert(
20202 "native".to_string(),
20203 Value::String(Rc::new(native.to_string())),
20204 );
20205 entry.insert(
20206 "romanized".to_string(),
20207 Value::String(Rc::new(romanized.to_string())),
20208 );
20209 entry.insert(
20210 "meaning".to_string(),
20211 Value::String(Rc::new(meaning.to_string())),
20212 );
20213 Value::Map(Rc::new(RefCell::new(entry)))
20214}
20215
20216fn register_experimental_crypto(interp: &mut Interpreter) {
20221 define(interp, "commit", Some(1), |_, args| {
20227 let value_str = match &args[0] {
20228 Value::String(s) => s.to_string(),
20229 other => format!("{:?}", other),
20230 };
20231
20232 let mut nonce = [0u8; 32];
20234 getrandom::getrandom(&mut nonce)
20235 .map_err(|e| RuntimeError::new(format!("commit() random failed: {}", e)))?;
20236 let nonce_hex = hex::encode(&nonce);
20237
20238 let commitment_input = format!("{}:{}", value_str, nonce_hex);
20240 let commitment = blake3::hash(commitment_input.as_bytes());
20241
20242 let mut result = std::collections::HashMap::new();
20243 result.insert(
20244 "commitment".to_string(),
20245 Value::String(Rc::new(commitment.to_hex().to_string())),
20246 );
20247 result.insert("nonce".to_string(), Value::String(Rc::new(nonce_hex)));
20248 result.insert("value".to_string(), args[0].clone());
20249
20250 Ok(Value::Map(Rc::new(RefCell::new(result))))
20251 });
20252
20253 define(interp, "verify_commitment", Some(3), |_, args| {
20255 let commitment = match &args[0] {
20256 Value::String(s) => s.to_string(),
20257 _ => {
20258 return Err(RuntimeError::new(
20259 "verify_commitment() requires string commitment",
20260 ))
20261 }
20262 };
20263 let value_str = match &args[1] {
20264 Value::String(s) => s.to_string(),
20265 other => format!("{:?}", other),
20266 };
20267 let nonce = match &args[2] {
20268 Value::String(s) => s.to_string(),
20269 _ => {
20270 return Err(RuntimeError::new(
20271 "verify_commitment() requires string nonce",
20272 ))
20273 }
20274 };
20275
20276 let commitment_input = format!("{}:{}", value_str, nonce);
20278 let computed = blake3::hash(commitment_input.as_bytes());
20279
20280 Ok(Value::Bool(computed.to_hex().to_string() == commitment))
20281 });
20282
20283 define(interp, "secret_split", Some(3), |_, args| {
20289 let secret = match &args[0] {
20290 Value::String(s) => s.as_bytes().to_vec(),
20291 Value::Array(arr) => {
20292 let borrowed = arr.borrow();
20293 borrowed
20294 .iter()
20295 .filter_map(|v| {
20296 if let Value::Int(i) = v {
20297 Some(*i as u8)
20298 } else {
20299 None
20300 }
20301 })
20302 .collect()
20303 }
20304 _ => {
20305 return Err(RuntimeError::new(
20306 "secret_split() requires string or byte array",
20307 ))
20308 }
20309 };
20310
20311 let threshold = match &args[1] {
20312 Value::Int(n) => *n as usize,
20313 _ => {
20314 return Err(RuntimeError::new(
20315 "secret_split() requires integer threshold",
20316 ))
20317 }
20318 };
20319
20320 let num_shares = match &args[2] {
20321 Value::Int(n) => *n as usize,
20322 _ => {
20323 return Err(RuntimeError::new(
20324 "secret_split() requires integer num_shares",
20325 ))
20326 }
20327 };
20328
20329 if threshold < 2 {
20330 return Err(RuntimeError::new("secret_split() threshold must be >= 2"));
20331 }
20332 if num_shares < threshold {
20333 return Err(RuntimeError::new(
20334 "secret_split() num_shares must be >= threshold",
20335 ));
20336 }
20337 if num_shares > 255 {
20338 return Err(RuntimeError::new("secret_split() max 255 shares"));
20339 }
20340
20341 let mut rng = rand::thread_rng();
20344 let mut shares: Vec<Vec<u8>> = (0..num_shares)
20345 .map(|_| Vec::with_capacity(secret.len() + 1))
20346 .collect();
20347
20348 for (i, share) in shares.iter_mut().enumerate() {
20350 share.push((i + 1) as u8);
20351 }
20352
20353 for &byte in &secret {
20355 let mut coefficients: Vec<u8> = vec![byte];
20358 for _ in 1..threshold {
20359 coefficients.push(rng.gen());
20360 }
20361
20362 for (i, share) in shares.iter_mut().enumerate() {
20364 let x = (i + 1) as u8;
20365 let y = eval_polynomial_gf256(&coefficients, x);
20366 share.push(y);
20367 }
20368 }
20369
20370 let share_values: Vec<Value> = shares
20372 .iter()
20373 .map(|share| {
20374 let hex = hex::encode(share);
20375 Value::String(Rc::new(hex))
20376 })
20377 .collect();
20378
20379 let mut result = std::collections::HashMap::new();
20380 result.insert(
20381 "shares".to_string(),
20382 Value::Array(Rc::new(RefCell::new(share_values))),
20383 );
20384 result.insert("threshold".to_string(), Value::Int(threshold as i64));
20385 result.insert("total".to_string(), Value::Int(num_shares as i64));
20386
20387 Ok(Value::Map(Rc::new(RefCell::new(result))))
20388 });
20389
20390 define(interp, "secret_recover", Some(1), |_, args| {
20392 let shares: Vec<Vec<u8>> = match &args[0] {
20393 Value::Array(arr) => {
20394 let borrowed = arr.borrow();
20395 borrowed
20396 .iter()
20397 .filter_map(|v| {
20398 if let Value::String(s) = v {
20399 hex::decode(s.as_str()).ok()
20400 } else {
20401 None
20402 }
20403 })
20404 .collect()
20405 }
20406 _ => {
20407 return Err(RuntimeError::new(
20408 "secret_recover() requires array of share strings",
20409 ))
20410 }
20411 };
20412
20413 if shares.is_empty() {
20414 return Err(RuntimeError::new(
20415 "secret_recover() requires at least one share",
20416 ));
20417 }
20418
20419 let share_len = shares[0].len();
20420 if share_len < 2 {
20421 return Err(RuntimeError::new("secret_recover() invalid share format"));
20422 }
20423
20424 let mut secret = Vec::with_capacity(share_len - 1);
20426
20427 for byte_idx in 1..share_len {
20428 let points: Vec<(u8, u8)> = shares
20430 .iter()
20431 .map(|share| (share[0], share[byte_idx]))
20432 .collect();
20433
20434 let recovered_byte = lagrange_interpolate_gf256(&points, 0);
20436 secret.push(recovered_byte);
20437 }
20438
20439 match String::from_utf8(secret.clone()) {
20441 Ok(s) => Ok(Value::String(Rc::new(s))),
20442 Err(_) => {
20443 let byte_values: Vec<Value> =
20445 secret.iter().map(|&b| Value::Int(b as i64)).collect();
20446 Ok(Value::Array(Rc::new(RefCell::new(byte_values))))
20447 }
20448 }
20449 });
20450
20451 define(interp, "council_split", Some(2), |_, args| {
20457 let secret = match &args[0] {
20458 Value::String(s) => s.as_bytes().to_vec(),
20459 _ => return Err(RuntimeError::new("council_split() requires string secret")),
20460 };
20461
20462 let num_elders = match &args[1] {
20463 Value::Int(n) => *n as usize,
20464 _ => {
20465 return Err(RuntimeError::new(
20466 "council_split() requires integer num_elders",
20467 ))
20468 }
20469 };
20470
20471 if num_elders < 3 {
20472 return Err(RuntimeError::new(
20473 "council_split() requires at least 3 elders",
20474 ));
20475 }
20476
20477 let threshold = (num_elders / 2) + 1;
20479
20480 let mut rng = rand::thread_rng();
20482 let mut shares: Vec<Vec<u8>> = (0..num_elders)
20483 .map(|_| Vec::with_capacity(secret.len() + 1))
20484 .collect();
20485
20486 for (i, share) in shares.iter_mut().enumerate() {
20487 share.push((i + 1) as u8);
20488 }
20489
20490 for &byte in &secret {
20491 let mut coefficients: Vec<u8> = vec![byte];
20492 for _ in 1..threshold {
20493 coefficients.push(rng.gen());
20494 }
20495
20496 for (i, share) in shares.iter_mut().enumerate() {
20497 let x = (i + 1) as u8;
20498 let y = eval_polynomial_gf256(&coefficients, x);
20499 share.push(y);
20500 }
20501 }
20502
20503 let share_values: Vec<Value> = shares
20504 .iter()
20505 .map(|share| Value::String(Rc::new(hex::encode(share))))
20506 .collect();
20507
20508 let mut result = std::collections::HashMap::new();
20509 result.insert(
20510 "shares".to_string(),
20511 Value::Array(Rc::new(RefCell::new(share_values))),
20512 );
20513 result.insert("threshold".to_string(), Value::Int(threshold as i64));
20514 result.insert("total".to_string(), Value::Int(num_elders as i64));
20515 result.insert(
20516 "model".to_string(),
20517 Value::String(Rc::new("ubuntu".to_string())),
20518 );
20519 result.insert(
20520 "philosophy".to_string(),
20521 Value::String(Rc::new(
20522 "I am because we are - majority consensus required".to_string(),
20523 )),
20524 );
20525
20526 Ok(Value::Map(Rc::new(RefCell::new(result))))
20527 });
20528
20529 define(interp, "witness_chain", Some(2), |_, args| {
20532 let statement = match &args[0] {
20533 Value::String(s) => s.to_string(),
20534 _ => {
20535 return Err(RuntimeError::new(
20536 "witness_chain() requires string statement",
20537 ))
20538 }
20539 };
20540
20541 let witnesses: Vec<String> = match &args[1] {
20542 Value::Array(arr) => {
20543 let borrowed = arr.borrow();
20544 borrowed
20545 .iter()
20546 .filter_map(|v| {
20547 if let Value::String(s) = v {
20548 Some(s.to_string())
20549 } else {
20550 None
20551 }
20552 })
20553 .collect()
20554 }
20555 _ => {
20556 return Err(RuntimeError::new(
20557 "witness_chain() requires array of witness names",
20558 ))
20559 }
20560 };
20561
20562 if witnesses.is_empty() {
20563 return Err(RuntimeError::new(
20564 "witness_chain() requires at least one witness",
20565 ));
20566 }
20567
20568 let mut chain = Vec::new();
20570 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
20571
20572 for (i, witness) in witnesses.iter().enumerate() {
20573 let attestation = format!("{}:attests:{}", witness, prev_hash);
20574 let hash = blake3::hash(attestation.as_bytes()).to_hex().to_string();
20575
20576 let mut link = std::collections::HashMap::new();
20577 link.insert(
20578 "witness".to_string(),
20579 Value::String(Rc::new(witness.clone())),
20580 );
20581 link.insert("position".to_string(), Value::Int((i + 1) as i64));
20582 link.insert(
20583 "attests_to".to_string(),
20584 Value::String(Rc::new(prev_hash.clone())),
20585 );
20586 link.insert(
20587 "signature".to_string(),
20588 Value::String(Rc::new(hash.clone())),
20589 );
20590
20591 chain.push(Value::Map(Rc::new(RefCell::new(link))));
20592 prev_hash = hash;
20593 }
20594
20595 let mut result = std::collections::HashMap::new();
20596 result.insert("statement".to_string(), Value::String(Rc::new(statement)));
20597 result.insert(
20598 "chain".to_string(),
20599 Value::Array(Rc::new(RefCell::new(chain))),
20600 );
20601 result.insert("final_seal".to_string(), Value::String(Rc::new(prev_hash)));
20602 result.insert(
20603 "model".to_string(),
20604 Value::String(Rc::new("isnad".to_string())),
20605 );
20606 result.insert(
20607 "philosophy".to_string(),
20608 Value::String(Rc::new(
20609 "Chain of reliable transmitters - each witness validates the previous".to_string(),
20610 )),
20611 );
20612
20613 Ok(Value::Map(Rc::new(RefCell::new(result))))
20614 });
20615
20616 define(interp, "verify_witness_chain", Some(1), |_, args| {
20618 let chain_map = match &args[0] {
20619 Value::Map(m) => m.borrow(),
20620 _ => {
20621 return Err(RuntimeError::new(
20622 "verify_witness_chain() requires chain map",
20623 ))
20624 }
20625 };
20626
20627 let statement = match chain_map.get("statement") {
20628 Some(Value::String(s)) => s.to_string(),
20629 _ => {
20630 return Err(RuntimeError::new(
20631 "verify_witness_chain() invalid chain format",
20632 ))
20633 }
20634 };
20635
20636 let chain = match chain_map.get("chain") {
20637 Some(Value::Array(arr)) => arr.borrow().clone(),
20638 _ => {
20639 return Err(RuntimeError::new(
20640 "verify_witness_chain() invalid chain format",
20641 ))
20642 }
20643 };
20644
20645 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
20646
20647 for link_val in chain.iter() {
20648 if let Value::Map(link_map) = link_val {
20649 let link = link_map.borrow();
20650 let witness = match link.get("witness") {
20651 Some(Value::String(s)) => s.to_string(),
20652 _ => return Ok(Value::Bool(false)),
20653 };
20654 let attests_to = match link.get("attests_to") {
20655 Some(Value::String(s)) => s.to_string(),
20656 _ => return Ok(Value::Bool(false)),
20657 };
20658 let signature = match link.get("signature") {
20659 Some(Value::String(s)) => s.to_string(),
20660 _ => return Ok(Value::Bool(false)),
20661 };
20662
20663 if attests_to != prev_hash {
20665 return Ok(Value::Bool(false));
20666 }
20667
20668 let expected = format!("{}:attests:{}", witness, prev_hash);
20669 let computed = blake3::hash(expected.as_bytes()).to_hex().to_string();
20670
20671 if computed != signature {
20672 return Ok(Value::Bool(false));
20673 }
20674
20675 prev_hash = signature;
20676 } else {
20677 return Ok(Value::Bool(false));
20678 }
20679 }
20680
20681 Ok(Value::Bool(true))
20682 });
20683
20684 define(interp, "experimental_crypto_info", Some(0), |_, _| {
20686 let mut info = std::collections::HashMap::new();
20687
20688 info.insert(
20689 "commitment_functions".to_string(),
20690 Value::Array(Rc::new(RefCell::new(vec![
20691 Value::String(Rc::new("commit".to_string())),
20692 Value::String(Rc::new("verify_commitment".to_string())),
20693 ]))),
20694 );
20695
20696 info.insert(
20697 "threshold_functions".to_string(),
20698 Value::Array(Rc::new(RefCell::new(vec![
20699 Value::String(Rc::new("secret_split".to_string())),
20700 Value::String(Rc::new("secret_recover".to_string())),
20701 ]))),
20702 );
20703
20704 info.insert(
20705 "cultural_ceremonies".to_string(),
20706 Value::Array(Rc::new(RefCell::new(vec![
20707 Value::String(Rc::new(
20708 "council_split (Ubuntu - African consensus)".to_string(),
20709 )),
20710 Value::String(Rc::new(
20711 "witness_chain (Isnad - Islamic transmission)".to_string(),
20712 )),
20713 ]))),
20714 );
20715
20716 Ok(Value::Map(Rc::new(RefCell::new(info))))
20717 });
20718}
20719
20720fn eval_polynomial_gf256(coefficients: &[u8], x: u8) -> u8 {
20722 let mut result: u8 = 0;
20723 let mut x_power: u8 = 1;
20724
20725 for &coef in coefficients {
20726 result ^= gf256_mul(coef, x_power);
20727 x_power = gf256_mul(x_power, x);
20728 }
20729
20730 result
20731}
20732
20733fn lagrange_interpolate_gf256(points: &[(u8, u8)], _x: u8) -> u8 {
20735 let mut result: u8 = 0;
20736
20737 for (i, &(xi, yi)) in points.iter().enumerate() {
20738 let mut numerator: u8 = 1;
20739 let mut denominator: u8 = 1;
20740
20741 for (j, &(xj, _)) in points.iter().enumerate() {
20742 if i != j {
20743 numerator = gf256_mul(numerator, xj);
20745 denominator = gf256_mul(denominator, xi ^ xj);
20747 }
20748 }
20749
20750 let term = gf256_mul(yi, gf256_mul(numerator, gf256_inv(denominator)));
20752 result ^= term;
20753 }
20754
20755 result
20756}
20757
20758fn gf256_mul(mut a: u8, mut b: u8) -> u8 {
20760 let mut result: u8 = 0;
20761 let modulus: u16 = 0x11b; while b != 0 {
20764 if b & 1 != 0 {
20765 result ^= a;
20766 }
20767 let high_bit = (a & 0x80) != 0;
20768 a <<= 1;
20769 if high_bit {
20770 a ^= (modulus & 0xff) as u8;
20771 }
20772 b >>= 1;
20773 }
20774
20775 result
20776}
20777
20778fn gf256_inv(a: u8) -> u8 {
20780 if a == 0 {
20781 return 0;
20782 }
20783
20784 let mut result = a;
20786 for _ in 0..6 {
20787 result = gf256_mul(result, result);
20788 result = gf256_mul(result, a);
20789 }
20790 gf256_mul(result, result)
20791}
20792
20793fn register_multibase(interp: &mut Interpreter) {
20812 define(interp, "to_vigesimal", Some(1), |_, args| {
20816 let n = match &args[0] {
20817 Value::Int(n) => *n,
20818 _ => return Err(RuntimeError::new("to_vigesimal() requires integer")),
20819 };
20820
20821 let result = to_base_string(n.unsigned_abs(), 20, false);
20822 let prefix = if n < 0 { "-0v" } else { "0v" };
20823 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20824 });
20825
20826 define(interp, "from_vigesimal", Some(1), |_, args| {
20827 let s = match &args[0] {
20828 Value::String(s) => s.to_string(),
20829 _ => return Err(RuntimeError::new("from_vigesimal() requires string")),
20830 };
20831
20832 let (negative, clean) = parse_base_prefix(&s, "0v");
20833 let value = from_base_string(&clean, 20)?;
20834 Ok(Value::Int(if negative {
20835 -(value as i64)
20836 } else {
20837 value as i64
20838 }))
20839 });
20840
20841 define(interp, "to_sexagesimal", Some(1), |_, args| {
20845 let n = match &args[0] {
20846 Value::Int(n) => *n,
20847 _ => return Err(RuntimeError::new("to_sexagesimal() requires integer")),
20848 };
20849
20850 let negative = n < 0;
20851 let mut value = n.unsigned_abs();
20852 let mut parts = Vec::new();
20853
20854 if value == 0 {
20855 parts.push("0".to_string());
20856 } else {
20857 while value > 0 {
20858 parts.push(format!("{}", value % 60));
20859 value /= 60;
20860 }
20861 parts.reverse();
20862 }
20863
20864 let prefix = if negative { "-0s" } else { "0s" };
20865 Ok(Value::String(Rc::new(format!(
20866 "{}[{}]",
20867 prefix,
20868 parts.join(":")
20869 ))))
20870 });
20871
20872 define(interp, "from_sexagesimal", Some(1), |_, args| {
20873 let s = match &args[0] {
20874 Value::String(s) => s.to_string(),
20875 _ => return Err(RuntimeError::new("from_sexagesimal() requires string")),
20876 };
20877
20878 let negative = s.starts_with('-');
20879 let clean = s
20880 .trim_start_matches('-')
20881 .trim_start_matches("0s")
20882 .trim_start_matches('[')
20883 .trim_end_matches(']');
20884
20885 let mut result: i64 = 0;
20886 for part in clean.split(':') {
20887 let digit: i64 = part
20888 .trim()
20889 .parse()
20890 .map_err(|_| RuntimeError::new(format!("Invalid sexagesimal digit: {}", part)))?;
20891 if digit < 0 || digit >= 60 {
20892 return Err(RuntimeError::new(format!(
20893 "Sexagesimal digit out of range: {}",
20894 digit
20895 )));
20896 }
20897 result = result * 60 + digit;
20898 }
20899
20900 Ok(Value::Int(if negative { -result } else { result }))
20901 });
20902
20903 define(interp, "to_duodecimal", Some(1), |_, args| {
20907 let n = match &args[0] {
20908 Value::Int(n) => *n,
20909 _ => return Err(RuntimeError::new("to_duodecimal() requires integer")),
20910 };
20911
20912 let result = to_base_string_custom(n.unsigned_abs(), 12, "0123456789XE");
20913 let prefix = if n < 0 { "-0z" } else { "0z" };
20914 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20915 });
20916
20917 define(interp, "from_duodecimal", Some(1), |_, args| {
20918 let s = match &args[0] {
20919 Value::String(s) => s.to_string(),
20920 _ => return Err(RuntimeError::new("from_duodecimal() requires string")),
20921 };
20922
20923 let (negative, clean) = parse_base_prefix(&s, "0z");
20924 let value = from_base_string_custom(&clean.to_uppercase(), "0123456789XE")?;
20925 Ok(Value::Int(if negative {
20926 -(value as i64)
20927 } else {
20928 value as i64
20929 }))
20930 });
20931
20932 define(interp, "to_base", Some(2), |_, args| {
20935 let n = match &args[0] {
20936 Value::Int(n) => *n,
20937 _ => return Err(RuntimeError::new("to_base() requires integer")),
20938 };
20939 let base = match &args[1] {
20940 Value::Int(b) => *b as u64,
20941 _ => return Err(RuntimeError::new("to_base() requires integer base")),
20942 };
20943
20944 if base < 2 || base > 36 {
20945 return Err(RuntimeError::new("to_base() base must be 2-36"));
20946 }
20947
20948 let result = to_base_string(n.unsigned_abs(), base, false);
20949 let prefix = if n < 0 { "-" } else { "" };
20950 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
20951 });
20952
20953 define(interp, "from_base", Some(2), |_, args| {
20954 let s = match &args[0] {
20955 Value::String(s) => s.to_string(),
20956 _ => return Err(RuntimeError::new("from_base() requires string")),
20957 };
20958 let base = match &args[1] {
20959 Value::Int(b) => *b as u64,
20960 _ => return Err(RuntimeError::new("from_base() requires integer base")),
20961 };
20962
20963 if base < 2 || base > 36 {
20964 return Err(RuntimeError::new("from_base() base must be 2-36"));
20965 }
20966
20967 let negative = s.starts_with('-');
20968 let clean = s.trim_start_matches('-');
20969 let value = from_base_string(clean, base)?;
20970 Ok(Value::Int(if negative {
20971 -(value as i64)
20972 } else {
20973 value as i64
20974 }))
20975 });
20976
20977 const BASE58_ALPHABET: &str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
20982
20983 define(interp, "base58_encode", Some(1), |_, args| {
20984 let bytes: Vec<u8> = match &args[0] {
20985 Value::String(s) => s.as_bytes().to_vec(),
20986 Value::Array(arr) => arr
20987 .borrow()
20988 .iter()
20989 .filter_map(|v| {
20990 if let Value::Int(i) = v {
20991 Some(*i as u8)
20992 } else {
20993 None
20994 }
20995 })
20996 .collect(),
20997 _ => {
20998 return Err(RuntimeError::new(
20999 "base58_encode() requires string or byte array",
21000 ))
21001 }
21002 };
21003
21004 let leading_zeros = bytes.iter().take_while(|&&b| b == 0).count();
21006
21007 let mut result = Vec::new();
21009 let mut num: Vec<u8> = bytes.clone();
21010
21011 while !num.is_empty() && !num.iter().all(|&b| b == 0) {
21012 let mut remainder = 0u32;
21013 let mut new_num = Vec::new();
21014
21015 for &byte in &num {
21016 let acc = (remainder << 8) + byte as u32;
21017 let digit = acc / 58;
21018 remainder = acc % 58;
21019
21020 if !new_num.is_empty() || digit > 0 {
21021 new_num.push(digit as u8);
21022 }
21023 }
21024
21025 result.push(BASE58_ALPHABET.chars().nth(remainder as usize).unwrap());
21026 num = new_num;
21027 }
21028
21029 for _ in 0..leading_zeros {
21031 result.push('1');
21032 }
21033
21034 result.reverse();
21035 Ok(Value::String(Rc::new(result.into_iter().collect())))
21036 });
21037
21038 define(interp, "base58_decode", Some(1), |_, args| {
21039 let s = match &args[0] {
21040 Value::String(s) => s.to_string(),
21041 _ => return Err(RuntimeError::new("base58_decode() requires string")),
21042 };
21043
21044 let leading_ones = s.chars().take_while(|&c| c == '1').count();
21046
21047 let mut num: Vec<u8> = Vec::new();
21049
21050 for c in s.chars() {
21051 let digit = BASE58_ALPHABET
21052 .find(c)
21053 .ok_or_else(|| RuntimeError::new(format!("Invalid base58 character: {}", c)))?;
21054
21055 let mut carry = digit as u32;
21056 for byte in num.iter_mut().rev() {
21057 let acc = (*byte as u32) * 58 + carry;
21058 *byte = (acc & 0xff) as u8;
21059 carry = acc >> 8;
21060 }
21061
21062 while carry > 0 {
21063 num.insert(0, (carry & 0xff) as u8);
21064 carry >>= 8;
21065 }
21066 }
21067
21068 let mut result = vec![0u8; leading_ones];
21070 result.extend(num);
21071
21072 Ok(Value::String(Rc::new(hex::encode(&result))))
21074 });
21075
21076 const BASE32_ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
21080
21081 define(interp, "base32_encode", Some(1), |_, args| {
21082 let bytes: Vec<u8> = match &args[0] {
21083 Value::String(s) => s.as_bytes().to_vec(),
21084 Value::Array(arr) => arr
21085 .borrow()
21086 .iter()
21087 .filter_map(|v| {
21088 if let Value::Int(i) = v {
21089 Some(*i as u8)
21090 } else {
21091 None
21092 }
21093 })
21094 .collect(),
21095 _ => {
21096 return Err(RuntimeError::new(
21097 "base32_encode() requires string or byte array",
21098 ))
21099 }
21100 };
21101
21102 let mut result = String::new();
21103 let mut buffer: u64 = 0;
21104 let mut bits = 0;
21105
21106 for byte in bytes {
21107 buffer = (buffer << 8) | byte as u64;
21108 bits += 8;
21109
21110 while bits >= 5 {
21111 bits -= 5;
21112 let idx = ((buffer >> bits) & 0x1f) as usize;
21113 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
21114 }
21115 }
21116
21117 if bits > 0 {
21118 let idx = ((buffer << (5 - bits)) & 0x1f) as usize;
21119 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
21120 }
21121
21122 while result.len() % 8 != 0 {
21124 result.push('=');
21125 }
21126
21127 Ok(Value::String(Rc::new(result)))
21128 });
21129
21130 define(interp, "base32_decode", Some(1), |_, args| {
21131 let s = match &args[0] {
21132 Value::String(s) => s.to_uppercase().replace('=', ""),
21133 _ => return Err(RuntimeError::new("base32_decode() requires string")),
21134 };
21135
21136 let mut result = Vec::new();
21137 let mut buffer: u64 = 0;
21138 let mut bits = 0;
21139
21140 for c in s.chars() {
21141 let digit = BASE32_ALPHABET
21142 .find(c)
21143 .ok_or_else(|| RuntimeError::new(format!("Invalid base32 character: {}", c)))?;
21144
21145 buffer = (buffer << 5) | digit as u64;
21146 bits += 5;
21147
21148 if bits >= 8 {
21149 bits -= 8;
21150 result.push((buffer >> bits) as u8);
21151 buffer &= (1 << bits) - 1;
21152 }
21153 }
21154
21155 Ok(Value::String(Rc::new(hex::encode(&result))))
21156 });
21157
21158 define(interp, "sacred_numbers", Some(1), |_, args| {
21162 let culture = match &args[0] {
21163 Value::String(s) => s.to_lowercase(),
21164 _ => {
21165 return Err(RuntimeError::new(
21166 "sacred_numbers() requires string culture",
21167 ))
21168 }
21169 };
21170
21171 let numbers: Vec<(i64, &str)> = match culture.as_str() {
21172 "mayan" | "maya" => vec![
21173 (13, "Sacred cycle - Tzolkin calendar"),
21174 (20, "Base of vigesimal system - human digits"),
21175 (52, "Calendar round - 52 years"),
21176 (260, "Tzolkin sacred calendar days"),
21177 (365, "Haab solar calendar days"),
21178 (400, "Baktun - 20×20 years"),
21179 ],
21180 "babylonian" | "mesopotamian" => vec![
21181 (12, "Months, hours - celestial division"),
21182 (60, "Sexagesimal base - minutes, seconds, degrees"),
21183 (360, "Circle degrees - 6×60"),
21184 (3600, "Sar - 60×60, large count"),
21185 (7, "Planets visible to naked eye"),
21186 ],
21187 "chinese" | "zh" => vec![
21188 (8, "八 (bā) - prosperity, wealth (sounds like 發)"),
21189 (9, "九 (jiǔ) - longevity (sounds like 久)"),
21190 (6, "六 (liù) - smooth, flowing"),
21191 (2, "二 (èr) - pairs, harmony"),
21192 (4, "四 (sì) - AVOID - sounds like death (死)"),
21193 (5, "五 (wǔ) - five elements"),
21194 (12, "十二 - zodiac animals"),
21195 ],
21196 "japanese" | "ja" => vec![
21197 (7, "七 (nana) - lucky, seven gods of fortune"),
21198 (8, "八 (hachi) - prosperity, expansion"),
21199 (3, "三 (san) - completeness"),
21200 (5, "五 (go) - five elements"),
21201 (4, "四 (shi) - AVOID - sounds like death (死)"),
21202 (9, "九 (ku) - AVOID - sounds like suffering (苦)"),
21203 ],
21204 "hebrew" | "jewish" => vec![
21205 (7, "Shabbat, creation days, menorah branches"),
21206 (18, "Chai (חי) - life - gematria of ח(8) + י(10)"),
21207 (40, "Transformation - flood, Sinai, wilderness"),
21208 (12, "Tribes of Israel"),
21209 (613, "Mitzvot - commandments"),
21210 (26, "Gematria of YHWH"),
21211 ],
21212 "islamic" | "arabic" | "ar" => vec![
21213 (5, "Pillars of Islam, daily prayers"),
21214 (7, "Heavens, circumambulation of Kaaba"),
21215 (40, "Age of prophethood, days of repentance"),
21216 (99, "Names of Allah"),
21217 (786, "Abjad value of Bismillah"),
21218 ],
21219 "hindu" | "indian" | "hi" => vec![
21220 (108, "Sacred beads, Upanishads, sun's distance"),
21221 (7, "Chakras (main), rishis, sacred rivers"),
21222 (3, "Trimurti - Brahma, Vishnu, Shiva"),
21223 (4, "Vedas, yugas, varnas"),
21224 (9, "Planets (navagraha), durga forms"),
21225 (1008, "Names of Vishnu"),
21226 ],
21227 "greek" | "pythagorean" => vec![
21228 (1, "Monad - unity, source"),
21229 (2, "Dyad - duality, diversity"),
21230 (3, "Triad - harmony, completion"),
21231 (4, "Tetrad - solidity, earth"),
21232 (7, "Heptad - perfection"),
21233 (10, "Decad - tetractys, divine"),
21234 (12, "Olympian gods"),
21235 ],
21236 "celtic" | "irish" => vec![
21237 (3, "Triple goddess, triquetra"),
21238 (5, "Elements including spirit"),
21239 (9, "Triple threes - sacred completion"),
21240 (13, "Lunar months"),
21241 (17, "St. Patrick's Day"),
21242 (20, "Vigesimal counting"),
21243 ],
21244 _ => vec![
21245 (1, "Unity"),
21246 (7, "Widely considered lucky"),
21247 (12, "Dozen - practical division"),
21248 (13, "Often considered unlucky in West"),
21249 ],
21250 };
21251
21252 let result: Vec<Value> = numbers
21253 .iter()
21254 .map(|(n, meaning)| {
21255 let mut entry = std::collections::HashMap::new();
21256 entry.insert("number".to_string(), Value::Int(*n));
21257 entry.insert(
21258 "meaning".to_string(),
21259 Value::String(Rc::new(meaning.to_string())),
21260 );
21261 Value::Map(Rc::new(RefCell::new(entry)))
21262 })
21263 .collect();
21264
21265 Ok(Value::Array(Rc::new(RefCell::new(result))))
21266 });
21267
21268 define(interp, "is_sacred", Some(2), |_, args| {
21270 let n = match &args[0] {
21271 Value::Int(n) => *n,
21272 _ => return Err(RuntimeError::new("is_sacred() requires integer")),
21273 };
21274 let culture = match &args[1] {
21275 Value::String(s) => s.to_lowercase(),
21276 _ => return Err(RuntimeError::new("is_sacred() requires string culture")),
21277 };
21278
21279 let sacred = match culture.as_str() {
21280 "mayan" | "maya" => vec![13, 20, 52, 260, 365, 400],
21281 "babylonian" | "mesopotamian" => vec![12, 60, 360, 3600, 7],
21282 "chinese" | "zh" => vec![8, 9, 6, 2, 5, 12],
21283 "japanese" | "ja" => vec![7, 8, 3, 5],
21284 "hebrew" | "jewish" => vec![7, 18, 40, 12, 613, 26],
21285 "islamic" | "arabic" | "ar" => vec![5, 7, 40, 99, 786],
21286 "hindu" | "indian" | "hi" => vec![108, 7, 3, 4, 9, 1008],
21287 "greek" | "pythagorean" => vec![1, 2, 3, 4, 7, 10, 12],
21288 "celtic" | "irish" => vec![3, 5, 9, 13, 17, 20],
21289 _ => vec![7, 12],
21290 };
21291
21292 Ok(Value::Bool(sacred.contains(&n)))
21293 });
21294
21295 define(interp, "is_unlucky", Some(2), |_, args| {
21297 let n = match &args[0] {
21298 Value::Int(n) => *n,
21299 _ => return Err(RuntimeError::new("is_unlucky() requires integer")),
21300 };
21301 let culture = match &args[1] {
21302 Value::String(s) => s.to_lowercase(),
21303 _ => return Err(RuntimeError::new("is_unlucky() requires string culture")),
21304 };
21305
21306 let unlucky = match culture.as_str() {
21307 "chinese" | "zh" => vec![4], "japanese" | "ja" => vec![4, 9], "western" | "en" => vec![13], "italian" | "it" => vec![17], _ => vec![],
21312 };
21313
21314 Ok(Value::Bool(unlucky.contains(&n)))
21315 });
21316
21317 define(interp, "number_meaning", Some(2), |_, args| {
21319 let n = match &args[0] {
21320 Value::Int(n) => *n,
21321 _ => return Err(RuntimeError::new("number_meaning() requires integer")),
21322 };
21323 let culture = match &args[1] {
21324 Value::String(s) => s.to_lowercase(),
21325 _ => {
21326 return Err(RuntimeError::new(
21327 "number_meaning() requires string culture",
21328 ))
21329 }
21330 };
21331
21332 let meaning = match (n, culture.as_str()) {
21333 (8, "chinese" | "zh") => "八 (bā) - Most auspicious, sounds like 發 (prosperity)",
21335 (4, "chinese" | "zh") => "四 (sì) - Unlucky, sounds like 死 (death)",
21336 (9, "chinese" | "zh") => "九 (jiǔ) - Longevity, sounds like 久 (long-lasting)",
21337 (6, "chinese" | "zh") => "六 (liù) - Smooth and well-off",
21338 (2, "chinese" | "zh") => "二 (èr) - Good things come in pairs",
21339
21340 (7, "japanese" | "ja") => "七 (nana) - Lucky, seven gods of fortune",
21342 (8, "japanese" | "ja") => "八 (hachi) - Lucky, represents spreading prosperity",
21343 (4, "japanese" | "ja") => "四 (shi) - Unlucky, homophone of death",
21344 (9, "japanese" | "ja") => "九 (ku) - Unlucky, homophone of suffering",
21345
21346 (18, "hebrew" | "jewish") => "Chai (חי) - Life itself, most auspicious",
21348 (7, "hebrew" | "jewish") => "Divine completion - Shabbat, menorah, creation",
21349 (40, "hebrew" | "jewish") => "Transformation - flood, Sinai, wilderness years",
21350 (613, "hebrew" | "jewish") => "Taryag - total number of mitzvot (commandments)",
21351
21352 (13, "mayan" | "maya") => "Sacred Tzolkin cycle, celestial layers",
21354 (20, "mayan" | "maya") => "Vigesimal base - fingers and toes, uinal",
21355 (260, "mayan" | "maya") => "Tzolkin calendar - 13 × 20 sacred days",
21356
21357 (60, "babylonian" | "mesopotamian") => {
21359 "Sexagesimal base - divisible by 2,3,4,5,6,10,12,15,20,30"
21360 }
21361 (360, "babylonian" | "mesopotamian") => "Circle degrees - celestial observation",
21362
21363 (108, "hindu" | "indian" | "hi") => {
21365 "Sacred completeness - mala beads, Upanishads, sun ratio"
21366 }
21367 (3, "hindu" | "indian" | "hi") => "Trimurti - Brahma, Vishnu, Shiva",
21368 (9, "hindu" | "indian" | "hi") => "Navagraha (9 planets), Durga's 9 forms",
21369
21370 (99, "islamic" | "arabic" | "ar") => "Asma ul-Husna - 99 names of Allah",
21372 (5, "islamic" | "arabic" | "ar") => "Five pillars of Islam",
21373 (786, "islamic" | "arabic" | "ar") => "Abjad numerology of Bismillah",
21374
21375 (10, "greek" | "pythagorean") => "Tetractys - 1+2+3+4, perfect number",
21377 (7, "greek" | "pythagorean") => "Heptad - virgin number, Athena's number",
21378
21379 _ => "No specific cultural meaning recorded",
21380 };
21381
21382 let mut result = std::collections::HashMap::new();
21383 result.insert("number".to_string(), Value::Int(n));
21384 result.insert("culture".to_string(), Value::String(Rc::new(culture)));
21385 result.insert(
21386 "meaning".to_string(),
21387 Value::String(Rc::new(meaning.to_string())),
21388 );
21389
21390 Ok(Value::Map(Rc::new(RefCell::new(result))))
21391 });
21392
21393 define(interp, "to_babylonian_time", Some(1), |_, args| {
21397 let seconds = match &args[0] {
21398 Value::Int(n) => *n,
21399 Value::Float(f) => *f as i64,
21400 _ => return Err(RuntimeError::new("to_babylonian_time() requires number")),
21401 };
21402
21403 let hours = seconds / 3600;
21404 let mins = (seconds % 3600) / 60;
21405 let secs = seconds % 60;
21406
21407 Ok(Value::String(Rc::new(format!(
21408 "0s[{}:{}:{}]",
21409 hours, mins, secs
21410 ))))
21411 });
21412
21413 define(interp, "from_babylonian_time", Some(1), |_, args| {
21415 let s = match &args[0] {
21416 Value::String(s) => s.to_string(),
21417 _ => return Err(RuntimeError::new("from_babylonian_time() requires string")),
21418 };
21419
21420 let clean = s
21421 .trim_start_matches("0s")
21422 .trim_start_matches('[')
21423 .trim_end_matches(']');
21424
21425 let parts: Vec<i64> = clean
21426 .split(':')
21427 .map(|p| p.trim().parse::<i64>().unwrap_or(0))
21428 .collect();
21429
21430 let seconds = match parts.len() {
21431 1 => parts[0],
21432 2 => parts[0] * 60 + parts[1],
21433 3 => parts[0] * 3600 + parts[1] * 60 + parts[2],
21434 _ => return Err(RuntimeError::new("Invalid Babylonian time format")),
21435 };
21436
21437 Ok(Value::Int(seconds))
21438 });
21439
21440 define(interp, "vigesimal_shares", Some(3), |_, args| {
21444 let secret = match &args[0] {
21445 Value::String(s) => s.as_bytes().to_vec(),
21446 _ => {
21447 return Err(RuntimeError::new(
21448 "vigesimal_shares() requires string secret",
21449 ))
21450 }
21451 };
21452
21453 let threshold = match &args[1] {
21454 Value::Int(n) => *n as usize,
21455 _ => {
21456 return Err(RuntimeError::new(
21457 "vigesimal_shares() requires integer threshold",
21458 ))
21459 }
21460 };
21461
21462 let num_shares = match &args[2] {
21463 Value::Int(n) => *n as usize,
21464 _ => {
21465 return Err(RuntimeError::new(
21466 "vigesimal_shares() requires integer num_shares",
21467 ))
21468 }
21469 };
21470
21471 if threshold < 2 || num_shares < threshold || num_shares > 20 {
21472 return Err(RuntimeError::new(
21473 "vigesimal_shares() requires 2 <= threshold <= num_shares <= 20 (Mayan limit)",
21474 ));
21475 }
21476
21477 let mut rng = rand::thread_rng();
21479 let mut shares: Vec<Vec<u8>> = (0..num_shares)
21480 .map(|_| Vec::with_capacity(secret.len() + 1))
21481 .collect();
21482
21483 for (i, share) in shares.iter_mut().enumerate() {
21484 share.push((i + 1) as u8);
21485 }
21486
21487 for &byte in &secret {
21488 let mut coefficients: Vec<u8> = vec![byte];
21489 for _ in 1..threshold {
21490 coefficients.push(rng.gen());
21491 }
21492
21493 for (i, share) in shares.iter_mut().enumerate() {
21494 let x = (i + 1) as u8;
21495 let y = eval_polynomial_gf256(&coefficients, x);
21496 share.push(y);
21497 }
21498 }
21499
21500 let share_values: Vec<Value> = shares
21502 .iter()
21503 .enumerate()
21504 .map(|(i, share)| {
21505 let mut entry = std::collections::HashMap::new();
21506
21507 let mut vig_parts: Vec<String> = Vec::new();
21509 for &byte in share {
21510 vig_parts.push(to_base_string(byte as u64, 20, true));
21511 }
21512
21513 entry.insert("index".to_string(), Value::Int((i + 1) as i64));
21514 entry.insert(
21515 "vigesimal".to_string(),
21516 Value::String(Rc::new(format!("0v{}", vig_parts.join(".")))),
21517 );
21518 entry.insert(
21519 "hex".to_string(),
21520 Value::String(Rc::new(hex::encode(share))),
21521 );
21522
21523 Value::Map(Rc::new(RefCell::new(entry)))
21524 })
21525 .collect();
21526
21527 let mut result = std::collections::HashMap::new();
21528 result.insert(
21529 "shares".to_string(),
21530 Value::Array(Rc::new(RefCell::new(share_values))),
21531 );
21532 result.insert("threshold".to_string(), Value::Int(threshold as i64));
21533 result.insert("total".to_string(), Value::Int(num_shares as i64));
21534 result.insert(
21535 "base".to_string(),
21536 Value::String(Rc::new("vigesimal (Mayan base-20)".to_string())),
21537 );
21538
21539 Ok(Value::Map(Rc::new(RefCell::new(result))))
21540 });
21541
21542 define(interp, "multibase_info", Some(0), |_, _| {
21544 let mut info = std::collections::HashMap::new();
21545
21546 let bases = vec![
21547 ("binary", 2, "0b", "Modern computing"),
21548 ("octal", 8, "0o", "Unix, historical computing"),
21549 ("decimal", 10, "", "Indo-Arabic global standard"),
21550 (
21551 "duodecimal",
21552 12,
21553 "0z",
21554 "Dozen system - time, music, measurement",
21555 ),
21556 ("hexadecimal", 16, "0x", "Computing, colors, addresses"),
21557 (
21558 "vigesimal",
21559 20,
21560 "0v",
21561 "Mayan, Celtic, Basque - human digits",
21562 ),
21563 (
21564 "sexagesimal",
21565 60,
21566 "0s",
21567 "Babylonian - time, angles, astronomy",
21568 ),
21569 ];
21570
21571 let base_list: Vec<Value> = bases
21572 .iter()
21573 .map(|(name, base, prefix, desc)| {
21574 let mut entry = std::collections::HashMap::new();
21575 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21576 entry.insert("base".to_string(), Value::Int(*base as i64));
21577 entry.insert(
21578 "prefix".to_string(),
21579 Value::String(Rc::new(prefix.to_string())),
21580 );
21581 entry.insert(
21582 "origin".to_string(),
21583 Value::String(Rc::new(desc.to_string())),
21584 );
21585 Value::Map(Rc::new(RefCell::new(entry)))
21586 })
21587 .collect();
21588
21589 info.insert(
21590 "numeral_systems".to_string(),
21591 Value::Array(Rc::new(RefCell::new(base_list))),
21592 );
21593
21594 let encodings = vec!["base58 (Bitcoin)", "base32 (RFC 4648)", "base64 (standard)"];
21595 let enc_list: Vec<Value> = encodings
21596 .iter()
21597 .map(|s| Value::String(Rc::new(s.to_string())))
21598 .collect();
21599 info.insert(
21600 "special_encodings".to_string(),
21601 Value::Array(Rc::new(RefCell::new(enc_list))),
21602 );
21603
21604 let cultures = vec![
21605 "mayan",
21606 "babylonian",
21607 "chinese",
21608 "japanese",
21609 "hebrew",
21610 "islamic",
21611 "hindu",
21612 "greek",
21613 "celtic",
21614 ];
21615 let cult_list: Vec<Value> = cultures
21616 .iter()
21617 .map(|s| Value::String(Rc::new(s.to_string())))
21618 .collect();
21619 info.insert(
21620 "supported_cultures".to_string(),
21621 Value::Array(Rc::new(RefCell::new(cult_list))),
21622 );
21623
21624 Ok(Value::Map(Rc::new(RefCell::new(info))))
21625 });
21626}
21627
21628fn to_base_string(mut n: u64, base: u64, pad_to_two: bool) -> String {
21631 const DIGITS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
21632
21633 if n == 0 {
21634 return if pad_to_two {
21635 "00".to_string()
21636 } else {
21637 "0".to_string()
21638 };
21639 }
21640
21641 let mut result = Vec::new();
21642 while n > 0 {
21643 result.push(DIGITS[(n % base) as usize] as char);
21644 n /= base;
21645 }
21646
21647 if pad_to_two && result.len() < 2 {
21648 result.push('0');
21649 }
21650
21651 result.reverse();
21652 result.into_iter().collect()
21653}
21654
21655fn to_base_string_custom(mut n: u64, base: u64, digits: &str) -> String {
21656 if n == 0 {
21657 return digits.chars().next().unwrap().to_string();
21658 }
21659
21660 let mut result = Vec::new();
21661 let digit_chars: Vec<char> = digits.chars().collect();
21662
21663 while n > 0 {
21664 result.push(digit_chars[(n % base) as usize]);
21665 n /= base;
21666 }
21667
21668 result.reverse();
21669 result.into_iter().collect()
21670}
21671
21672fn from_base_string(s: &str, base: u64) -> Result<u64, RuntimeError> {
21673 let mut result: u64 = 0;
21674
21675 for c in s.chars() {
21676 let digit = match c {
21677 '0'..='9' => c as u64 - '0' as u64,
21678 'A'..='Z' => c as u64 - 'A' as u64 + 10,
21679 'a'..='z' => c as u64 - 'a' as u64 + 10,
21680 _ => return Err(RuntimeError::new(format!("Invalid digit: {}", c))),
21681 };
21682
21683 if digit >= base {
21684 return Err(RuntimeError::new(format!(
21685 "Digit {} out of range for base {}",
21686 c, base
21687 )));
21688 }
21689
21690 result = result
21691 .checked_mul(base)
21692 .and_then(|r| r.checked_add(digit))
21693 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
21694 }
21695
21696 Ok(result)
21697}
21698
21699fn from_base_string_custom(s: &str, digits: &str) -> Result<u64, RuntimeError> {
21700 let base = digits.len() as u64;
21701 let mut result: u64 = 0;
21702
21703 for c in s.chars() {
21704 let digit = digits
21705 .find(c)
21706 .ok_or_else(|| RuntimeError::new(format!("Invalid digit: {}", c)))?
21707 as u64;
21708
21709 result = result
21710 .checked_mul(base)
21711 .and_then(|r| r.checked_add(digit))
21712 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
21713 }
21714
21715 Ok(result)
21716}
21717
21718fn parse_base_prefix(s: &str, prefix: &str) -> (bool, String) {
21719 let negative = s.starts_with('-');
21720 let clean = s
21721 .trim_start_matches('-')
21722 .trim_start_matches(prefix)
21723 .to_string();
21724 (negative, clean)
21725}
21726
21727fn register_audio(interp: &mut Interpreter) {
21755 define(interp, "tune", Some(3), |_, args| {
21762 let note = match &args[0] {
21763 Value::Int(n) => *n as f64, Value::Float(f) => *f, Value::String(s) => parse_note_name(s)?,
21766 _ => return Err(RuntimeError::new("tune() requires note number or name")),
21767 };
21768
21769 let system = match &args[1] {
21770 Value::String(s) => s.to_lowercase(),
21771 _ => return Err(RuntimeError::new("tune() requires tuning system name")),
21772 };
21773
21774 let root_freq = match &args[2] {
21775 Value::Float(f) => *f,
21776 Value::Int(i) => *i as f64,
21777 _ => return Err(RuntimeError::new("tune() requires root frequency")),
21778 };
21779
21780 let freq = match system.as_str() {
21781 "12tet" | "equal" | "western" => {
21782 root_freq * 2.0_f64.powf((note - 69.0) / 12.0)
21784 }
21785 "24tet" | "quarter" | "arabic" | "maqam" => {
21786 root_freq * 2.0_f64.powf((note - 69.0) / 24.0)
21788 }
21789 "just" | "pure" => {
21790 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21792 let octave = ((note - 69.0) / 12.0).floor();
21793 let ratio = just_intonation_ratio(interval as i32);
21794 root_freq * ratio * 2.0_f64.powf(octave)
21795 }
21796 "pythagorean" => {
21797 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21799 let octave = ((note - 69.0) / 12.0).floor();
21800 let ratio = pythagorean_ratio(interval as i32);
21801 root_freq * ratio * 2.0_f64.powf(octave)
21802 }
21803 "meantone" | "quarter_comma" => {
21804 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
21806 let octave = ((note - 69.0) / 12.0).floor();
21807 let ratio = meantone_ratio(interval as i32);
21808 root_freq * ratio * 2.0_f64.powf(octave)
21809 }
21810 "53tet" | "turkish" | "persian" | "comma" => {
21811 root_freq * 2.0_f64.powf((note - 69.0) / 53.0)
21813 }
21814 "22shruti" | "shruti" | "indian" => {
21815 let shruti = (note - 69.0) % 22.0;
21817 let octave = ((note - 69.0) / 22.0).floor();
21818 let ratio = shruti_ratio(shruti as i32);
21819 root_freq * ratio * 2.0_f64.powf(octave)
21820 }
21821 "gamelan_pelog" | "pelog" => {
21822 let degree = ((note - 69.0) % 7.0 + 7.0) % 7.0;
21824 let octave = ((note - 69.0) / 7.0).floor();
21825 let ratio = pelog_ratio(degree as i32);
21826 root_freq * ratio * 2.0_f64.powf(octave)
21827 }
21828 "gamelan_slendro" | "slendro" => {
21829 let degree = ((note - 69.0) % 5.0 + 5.0) % 5.0;
21831 let octave = ((note - 69.0) / 5.0).floor();
21832 let ratio = slendro_ratio(degree as i32);
21833 root_freq * ratio * 2.0_f64.powf(octave)
21834 }
21835 "bohlen_pierce" | "bp" => {
21836 root_freq * 3.0_f64.powf((note - 69.0) / 13.0)
21838 }
21839 _ => {
21840 return Err(RuntimeError::new(format!(
21841 "Unknown tuning system: {}",
21842 system
21843 )))
21844 }
21845 };
21846
21847 Ok(Value::Float(freq))
21848 });
21849
21850 define(interp, "tuning_info", Some(1), |_, args| {
21852 let system = match &args[0] {
21853 Value::String(s) => s.to_lowercase(),
21854 _ => return Err(RuntimeError::new("tuning_info() requires string")),
21855 };
21856
21857 let (name, notes_per_octave, origin, description) = match system.as_str() {
21858 "12tet" | "equal" | "western" => (
21859 "12-TET", 12, "Western (18th century)",
21860 "Equal temperament - every semitone is exactly 2^(1/12). Universal but slightly impure."
21861 ),
21862 "24tet" | "quarter" | "arabic" | "maqam" => (
21863 "24-TET", 24, "Arabic/Turkish",
21864 "Quarter-tone system for maqam music. Enables neutral seconds and other microtones."
21865 ),
21866 "just" | "pure" => (
21867 "Just Intonation", 12, "Ancient (Ptolemy)",
21868 "Pure frequency ratios (3:2 fifth, 5:4 third). Beatless intervals but limited modulation."
21869 ),
21870 "pythagorean" => (
21871 "Pythagorean", 12, "Ancient Greece",
21872 "Built entirely from perfect fifths (3:2). Pure fifths but harsh thirds."
21873 ),
21874 "meantone" | "quarter_comma" => (
21875 "Quarter-Comma Meantone", 12, "Renaissance Europe",
21876 "Tempered fifths for pure major thirds. Beautiful in limited keys."
21877 ),
21878 "53tet" | "turkish" | "persian" | "comma" => (
21879 "53-TET", 53, "Turkish/Persian",
21880 "53 notes per octave. Closely approximates just intonation and allows maqam/dastgah."
21881 ),
21882 "22shruti" | "shruti" | "indian" => (
21883 "22-Shruti", 22, "Indian Classical",
21884 "Ancient Indian system. Each shruti is a 'microtone' - the smallest perceptible interval."
21885 ),
21886 "gamelan_pelog" | "pelog" => (
21887 "Pelog", 7, "Javanese Gamelan",
21888 "Heptatonic scale with unequal steps. Each gamelan has unique tuning - instruments are married."
21889 ),
21890 "gamelan_slendro" | "slendro" => (
21891 "Slendro", 5, "Javanese Gamelan",
21892 "Pentatonic scale, roughly equal steps (~240 cents). Each ensemble uniquely tuned."
21893 ),
21894 "bohlen_pierce" | "bp" => (
21895 "Bohlen-Pierce", 13, "Modern (1970s)",
21896 "Non-octave scale based on 3:1 tritave. Alien but mathematically beautiful."
21897 ),
21898 _ => return Err(RuntimeError::new(format!("Unknown tuning system: {}", system))),
21899 };
21900
21901 let mut info = std::collections::HashMap::new();
21902 info.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21903 info.insert("notes_per_octave".to_string(), Value::Int(notes_per_octave));
21904 info.insert(
21905 "origin".to_string(),
21906 Value::String(Rc::new(origin.to_string())),
21907 );
21908 info.insert(
21909 "description".to_string(),
21910 Value::String(Rc::new(description.to_string())),
21911 );
21912
21913 Ok(Value::Map(Rc::new(RefCell::new(info))))
21914 });
21915
21916 define(interp, "list_tuning_systems", Some(0), |_, _| {
21918 let systems = vec![
21919 ("12tet", "Western equal temperament", 12),
21920 ("24tet", "Arabic/Turkish quarter-tones", 24),
21921 ("just", "Pure ratio just intonation", 12),
21922 ("pythagorean", "Ancient Greek pure fifths", 12),
21923 ("meantone", "Renaissance quarter-comma", 12),
21924 ("53tet", "Turkish/Persian comma system", 53),
21925 ("22shruti", "Indian microtonal", 22),
21926 ("pelog", "Javanese gamelan 7-note", 7),
21927 ("slendro", "Javanese gamelan 5-note", 5),
21928 ("bohlen_pierce", "Non-octave tritave scale", 13),
21929 ];
21930
21931 let result: Vec<Value> = systems
21932 .iter()
21933 .map(|(name, desc, notes)| {
21934 let mut entry = std::collections::HashMap::new();
21935 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21936 entry.insert(
21937 "description".to_string(),
21938 Value::String(Rc::new(desc.to_string())),
21939 );
21940 entry.insert("notes_per_octave".to_string(), Value::Int(*notes));
21941 Value::Map(Rc::new(RefCell::new(entry)))
21942 })
21943 .collect();
21944
21945 Ok(Value::Array(Rc::new(RefCell::new(result))))
21946 });
21947
21948 define(interp, "sacred_freq", Some(1), |_, args| {
21954 let name = match &args[0] {
21955 Value::String(s) => s.to_lowercase(),
21956 _ => return Err(RuntimeError::new("sacred_freq() requires string")),
21957 };
21958
21959 let (freq, description) = match name.as_str() {
21960 "om" | "ॐ" | "aum" => (136.1, "Om - Earth year frequency (Cosmic Om)"),
21962 "earth_day" => (194.18, "Earth day - one rotation"),
21963 "earth_year" => (136.1, "Earth year - one orbit (Om frequency)"),
21964 "schumann" | "earth_resonance" => (
21965 7.83,
21966 "Schumann resonance - Earth's electromagnetic heartbeat",
21967 ),
21968
21969 "ut" | "do" | "396" => (396.0, "UT/DO - Liberating guilt and fear"),
21971 "re" | "417" => (417.0, "RE - Undoing situations, facilitating change"),
21972 "mi" | "528" => (528.0, "MI - Transformation, miracles, DNA repair"),
21973 "fa" | "639" => (639.0, "FA - Connecting relationships"),
21974 "sol" | "741" => (741.0, "SOL - Awakening intuition"),
21975 "la" | "852" => (852.0, "LA - Returning to spiritual order"),
21976 "si" | "963" => (963.0, "SI - Divine consciousness, enlightenment"),
21977 "174" => (174.0, "Solfeggio foundation - pain relief"),
21978 "285" => (285.0, "Solfeggio - healing tissue"),
21979
21980 "sun" | "☉" => (126.22, "Sun - ego, vitality, leadership"),
21982 "moon" | "☽" | "☾" => (210.42, "Moon - emotion, intuition, cycles"),
21983 "mercury" | "☿" => (141.27, "Mercury - communication, intellect"),
21984 "venus" | "♀" => (221.23, "Venus - love, beauty, harmony"),
21985 "mars" | "♂" => (144.72, "Mars - energy, action, courage"),
21986 "jupiter" | "♃" => (183.58, "Jupiter - expansion, wisdom, luck"),
21987 "saturn" | "♄" => (147.85, "Saturn - discipline, structure, time"),
21988
21989 "root" | "muladhara" => (256.0, "Root chakra - survival, grounding (C)"),
21991 "sacral" | "svadhisthana" => (288.0, "Sacral chakra - creativity, sexuality (D)"),
21992 "solar" | "manipura" => (320.0, "Solar plexus - will, power (E)"),
21993 "heart" | "anahata" => (341.3, "Heart chakra - love, compassion (F)"),
21994 "throat" | "vishuddha" => (384.0, "Throat chakra - expression, truth (G)"),
21995 "third_eye" | "ajna" => (426.7, "Third eye - intuition, insight (A)"),
21996 "crown" | "sahasrara" => (480.0, "Crown chakra - consciousness, unity (B)"),
21997
21998 "a440" | "iso" => (440.0, "ISO standard concert pitch (1955)"),
22000 "a432" | "verdi" => (
22001 432.0,
22002 "Verdi pitch - 'mathematically consistent with universe'",
22003 ),
22004 "a415" | "baroque" => (415.0, "Baroque pitch - period instrument standard"),
22005 "a466" | "chorton" => (466.0, "Choir pitch - high Baroque German"),
22006
22007 "delta" => (2.0, "Delta waves - deep sleep, healing (0.5-4 Hz)"),
22009 "theta" => (6.0, "Theta waves - meditation, creativity (4-8 Hz)"),
22010 "alpha" => (10.0, "Alpha waves - relaxation, calm focus (8-13 Hz)"),
22011 "beta" => (20.0, "Beta waves - alertness, concentration (13-30 Hz)"),
22012 "gamma" => (40.0, "Gamma waves - insight, peak performance (30-100 Hz)"),
22013
22014 _ => {
22015 return Err(RuntimeError::new(format!(
22016 "Unknown sacred frequency: {}",
22017 name
22018 )))
22019 }
22020 };
22021
22022 let mut result = std::collections::HashMap::new();
22023 result.insert("frequency".to_string(), Value::Float(freq));
22024 result.insert("name".to_string(), Value::String(Rc::new(name)));
22025 result.insert(
22026 "meaning".to_string(),
22027 Value::String(Rc::new(description.to_string())),
22028 );
22029
22030 Ok(Value::Map(Rc::new(RefCell::new(result))))
22031 });
22032
22033 define(interp, "solfeggio", Some(0), |_, _| {
22035 let frequencies = vec![
22036 (174.0, "Foundation", "Pain relief, security"),
22037 (285.0, "Quantum", "Healing tissue, safety"),
22038 (396.0, "UT", "Liberating guilt and fear"),
22039 (417.0, "RE", "Undoing situations, change"),
22040 (528.0, "MI", "Transformation, DNA repair, miracles"),
22041 (639.0, "FA", "Connecting relationships"),
22042 (741.0, "SOL", "Awakening intuition"),
22043 (852.0, "LA", "Spiritual order"),
22044 (963.0, "SI", "Divine consciousness"),
22045 ];
22046
22047 let result: Vec<Value> = frequencies
22048 .iter()
22049 .map(|(freq, name, meaning)| {
22050 let mut entry = std::collections::HashMap::new();
22051 entry.insert("frequency".to_string(), Value::Float(*freq));
22052 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
22053 entry.insert(
22054 "meaning".to_string(),
22055 Value::String(Rc::new(meaning.to_string())),
22056 );
22057 Value::Map(Rc::new(RefCell::new(entry)))
22058 })
22059 .collect();
22060
22061 Ok(Value::Array(Rc::new(RefCell::new(result))))
22062 });
22063
22064 define(interp, "chakras", Some(0), |_, _| {
22066 let chakras = vec![
22067 (
22068 256.0,
22069 "Muladhara",
22070 "Root",
22071 "Red",
22072 "Survival, grounding, stability",
22073 ),
22074 (
22075 288.0,
22076 "Svadhisthana",
22077 "Sacral",
22078 "Orange",
22079 "Creativity, sexuality, emotion",
22080 ),
22081 (
22082 320.0,
22083 "Manipura",
22084 "Solar Plexus",
22085 "Yellow",
22086 "Will, power, self-esteem",
22087 ),
22088 (
22089 341.3,
22090 "Anahata",
22091 "Heart",
22092 "Green",
22093 "Love, compassion, connection",
22094 ),
22095 (
22096 384.0,
22097 "Vishuddha",
22098 "Throat",
22099 "Blue",
22100 "Expression, truth, communication",
22101 ),
22102 (
22103 426.7,
22104 "Ajna",
22105 "Third Eye",
22106 "Indigo",
22107 "Intuition, insight, wisdom",
22108 ),
22109 (
22110 480.0,
22111 "Sahasrara",
22112 "Crown",
22113 "Violet",
22114 "Consciousness, unity, transcendence",
22115 ),
22116 ];
22117
22118 let result: Vec<Value> = chakras
22119 .iter()
22120 .map(|(freq, sanskrit, english, color, meaning)| {
22121 let mut entry = std::collections::HashMap::new();
22122 entry.insert("frequency".to_string(), Value::Float(*freq));
22123 entry.insert(
22124 "sanskrit".to_string(),
22125 Value::String(Rc::new(sanskrit.to_string())),
22126 );
22127 entry.insert(
22128 "english".to_string(),
22129 Value::String(Rc::new(english.to_string())),
22130 );
22131 entry.insert(
22132 "color".to_string(),
22133 Value::String(Rc::new(color.to_string())),
22134 );
22135 entry.insert(
22136 "meaning".to_string(),
22137 Value::String(Rc::new(meaning.to_string())),
22138 );
22139 Value::Map(Rc::new(RefCell::new(entry)))
22140 })
22141 .collect();
22142
22143 Ok(Value::Array(Rc::new(RefCell::new(result))))
22144 });
22145
22146 define(interp, "sine", Some(3), |_, args| {
22154 generate_waveform(&args, |phase| phase.sin())
22155 });
22156
22157 define(interp, "square", Some(3), |_, args| {
22159 generate_waveform(&args, |phase| if phase.sin() >= 0.0 { 1.0 } else { -1.0 })
22160 });
22161
22162 define(interp, "sawtooth", Some(3), |_, args| {
22164 generate_waveform(&args, |phase| {
22165 let normalized = (phase / std::f64::consts::TAU).fract();
22166 2.0 * normalized - 1.0
22167 })
22168 });
22169
22170 define(interp, "triangle", Some(3), |_, args| {
22172 generate_waveform(&args, |phase| {
22173 let normalized = (phase / std::f64::consts::TAU).fract();
22174 if normalized < 0.5 {
22175 4.0 * normalized - 1.0
22176 } else {
22177 3.0 - 4.0 * normalized
22178 }
22179 })
22180 });
22181
22182 define(interp, "noise", Some(1), |_, args| {
22184 let samples = match &args[0] {
22185 Value::Int(n) => *n as usize,
22186 _ => return Err(RuntimeError::new("noise() requires integer sample count")),
22187 };
22188
22189 let mut rng = rand::thread_rng();
22190 let result: Vec<Value> = (0..samples)
22191 .map(|_| Value::Float(rng.gen::<f64>() * 2.0 - 1.0))
22192 .collect();
22193
22194 Ok(Value::Array(Rc::new(RefCell::new(result))))
22195 });
22196
22197 define(interp, "scale", Some(1), |_, args| {
22203 let name = match &args[0] {
22204 Value::String(s) => s.to_lowercase(),
22205 _ => return Err(RuntimeError::new("scale() requires string")),
22206 };
22207
22208 let (intervals, origin, description) = match name.as_str() {
22209 "major" | "ionian" => (
22211 vec![0, 2, 4, 5, 7, 9, 11],
22212 "Western",
22213 "Happy, bright, resolved",
22214 ),
22215 "minor" | "aeolian" => (
22216 vec![0, 2, 3, 5, 7, 8, 10],
22217 "Western",
22218 "Sad, dark, introspective",
22219 ),
22220 "dorian" => (
22221 vec![0, 2, 3, 5, 7, 9, 10],
22222 "Western/Jazz",
22223 "Minor with bright 6th",
22224 ),
22225 "phrygian" => (
22226 vec![0, 1, 3, 5, 7, 8, 10],
22227 "Western/Flamenco",
22228 "Spanish, exotic, tense",
22229 ),
22230 "lydian" => (
22231 vec![0, 2, 4, 6, 7, 9, 11],
22232 "Western",
22233 "Dreamy, floating, ethereal",
22234 ),
22235 "mixolydian" => (vec![0, 2, 4, 5, 7, 9, 10], "Western/Blues", "Bluesy major"),
22236 "locrian" => (vec![0, 1, 3, 5, 6, 8, 10], "Western", "Unstable, dissonant"),
22237
22238 "pentatonic_major" => (vec![0, 2, 4, 7, 9], "Universal", "No dissonance, universal"),
22240 "pentatonic_minor" => (vec![0, 3, 5, 7, 10], "Universal", "Blues foundation"),
22241
22242 "hirajoshi" => (vec![0, 2, 3, 7, 8], "Japanese", "Melancholic, mysterious"),
22244 "insen" => (vec![0, 1, 5, 7, 10], "Japanese", "Dark, zen, contemplative"),
22245 "iwato" => (vec![0, 1, 5, 6, 10], "Japanese", "Most dark and dissonant"),
22246 "kumoi" => (vec![0, 2, 3, 7, 9], "Japanese", "Gentle, peaceful"),
22247 "yo" => (vec![0, 2, 5, 7, 9], "Japanese", "Bright, folk, celebratory"),
22248
22249 "hijaz" => (
22251 vec![0, 1, 4, 5, 7, 8, 11],
22252 "Arabic",
22253 "Exotic, Middle Eastern",
22254 ),
22255 "bayati" => (
22256 vec![0, 1.5 as i32, 3, 5, 7, 8, 10],
22257 "Arabic",
22258 "Quarter-tone, soulful",
22259 ),
22260 "rast" => (
22261 vec![0, 2, 3.5 as i32, 5, 7, 9, 10.5 as i32],
22262 "Arabic",
22263 "Foundation maqam",
22264 ),
22265 "saba" => (
22266 vec![0, 1.5 as i32, 3, 4, 5, 8, 10],
22267 "Arabic",
22268 "Sad, spiritual",
22269 ),
22270
22271 "bhairav" => (
22273 vec![0, 1, 4, 5, 7, 8, 11],
22274 "Indian",
22275 "Morning raga, devotional",
22276 ),
22277 "yaman" | "kalyan" => (vec![0, 2, 4, 6, 7, 9, 11], "Indian", "Evening, romantic"),
22278 "bhairavi" => (
22279 vec![0, 1, 3, 5, 7, 8, 10],
22280 "Indian",
22281 "Concluding raga, devotional",
22282 ),
22283 "todi" => (vec![0, 1, 3, 6, 7, 8, 11], "Indian", "Serious, pathos"),
22284 "marwa" => (vec![0, 1, 4, 6, 7, 9, 11], "Indian", "Evening, longing"),
22285
22286 "blues" => (vec![0, 3, 5, 6, 7, 10], "American", "Blue notes, soul"),
22288
22289 "hungarian_minor" => (vec![0, 2, 3, 6, 7, 8, 11], "Hungarian", "Gypsy, dramatic"),
22291 "romanian" => (vec![0, 2, 3, 6, 7, 9, 10], "Romanian", "Folk, energetic"),
22292
22293 "ahava_raba" | "freygish" => (
22295 vec![0, 1, 4, 5, 7, 8, 10],
22296 "Jewish/Klezmer",
22297 "Cantorial, emotional",
22298 ),
22299 "mi_sheberach" => (vec![0, 2, 3, 6, 7, 9, 10], "Jewish", "Prayer mode"),
22300
22301 "gong" => (vec![0, 2, 4, 7, 9], "Chinese", "Palace mode, major-like"),
22303 "shang" => (vec![0, 2, 5, 7, 9], "Chinese", "Merchant mode"),
22304 "jue" => (vec![0, 3, 5, 7, 10], "Chinese", "Angle mode"),
22305 "zhi" => (vec![0, 2, 5, 7, 10], "Chinese", "Emblem mode"),
22306 "yu" => (vec![0, 3, 5, 8, 10], "Chinese", "Wings mode, minor-like"),
22307
22308 "pelog" => (
22310 vec![0, 1, 3, 7, 8],
22311 "Javanese",
22312 "7-note unequal temperament",
22313 ),
22314 "slendro" => (vec![0, 2, 5, 7, 9], "Javanese", "5-note roughly equal"),
22315
22316 "whole_tone" => (
22318 vec![0, 2, 4, 6, 8, 10],
22319 "Impressionist",
22320 "Dreamlike, no resolution",
22321 ),
22322 "chromatic" => (
22323 vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
22324 "Western",
22325 "All 12 notes",
22326 ),
22327 "diminished" => (vec![0, 2, 3, 5, 6, 8, 9, 11], "Jazz", "Symmetric, tense"),
22328 "augmented" => (vec![0, 3, 4, 7, 8, 11], "Jazz", "Symmetric, floating"),
22329
22330 _ => return Err(RuntimeError::new(format!("Unknown scale: {}", name))),
22331 };
22332
22333 let mut result = std::collections::HashMap::new();
22334 let intervals_values: Vec<Value> =
22335 intervals.iter().map(|&i| Value::Int(i as i64)).collect();
22336 result.insert(
22337 "intervals".to_string(),
22338 Value::Array(Rc::new(RefCell::new(intervals_values))),
22339 );
22340 result.insert(
22341 "origin".to_string(),
22342 Value::String(Rc::new(origin.to_string())),
22343 );
22344 result.insert(
22345 "character".to_string(),
22346 Value::String(Rc::new(description.to_string())),
22347 );
22348 result.insert("name".to_string(), Value::String(Rc::new(name)));
22349
22350 Ok(Value::Map(Rc::new(RefCell::new(result))))
22351 });
22352
22353 define(interp, "list_scales", Some(0), |_, _| {
22355 let mut cultures = std::collections::HashMap::new();
22356
22357 cultures.insert(
22358 "western".to_string(),
22359 Value::Array(Rc::new(RefCell::new(vec![
22360 Value::String(Rc::new("major".to_string())),
22361 Value::String(Rc::new("minor".to_string())),
22362 Value::String(Rc::new("dorian".to_string())),
22363 Value::String(Rc::new("phrygian".to_string())),
22364 Value::String(Rc::new("lydian".to_string())),
22365 Value::String(Rc::new("mixolydian".to_string())),
22366 Value::String(Rc::new("locrian".to_string())),
22367 ]))),
22368 );
22369
22370 cultures.insert(
22371 "japanese".to_string(),
22372 Value::Array(Rc::new(RefCell::new(vec![
22373 Value::String(Rc::new("hirajoshi".to_string())),
22374 Value::String(Rc::new("insen".to_string())),
22375 Value::String(Rc::new("iwato".to_string())),
22376 Value::String(Rc::new("kumoi".to_string())),
22377 Value::String(Rc::new("yo".to_string())),
22378 ]))),
22379 );
22380
22381 cultures.insert(
22382 "arabic".to_string(),
22383 Value::Array(Rc::new(RefCell::new(vec![
22384 Value::String(Rc::new("hijaz".to_string())),
22385 Value::String(Rc::new("bayati".to_string())),
22386 Value::String(Rc::new("rast".to_string())),
22387 Value::String(Rc::new("saba".to_string())),
22388 ]))),
22389 );
22390
22391 cultures.insert(
22392 "indian".to_string(),
22393 Value::Array(Rc::new(RefCell::new(vec![
22394 Value::String(Rc::new("bhairav".to_string())),
22395 Value::String(Rc::new("yaman".to_string())),
22396 Value::String(Rc::new("bhairavi".to_string())),
22397 Value::String(Rc::new("todi".to_string())),
22398 Value::String(Rc::new("marwa".to_string())),
22399 ]))),
22400 );
22401
22402 cultures.insert(
22403 "chinese".to_string(),
22404 Value::Array(Rc::new(RefCell::new(vec![
22405 Value::String(Rc::new("gong".to_string())),
22406 Value::String(Rc::new("shang".to_string())),
22407 Value::String(Rc::new("jue".to_string())),
22408 Value::String(Rc::new("zhi".to_string())),
22409 Value::String(Rc::new("yu".to_string())),
22410 ]))),
22411 );
22412
22413 cultures.insert(
22414 "jewish".to_string(),
22415 Value::Array(Rc::new(RefCell::new(vec![
22416 Value::String(Rc::new("ahava_raba".to_string())),
22417 Value::String(Rc::new("mi_sheberach".to_string())),
22418 ]))),
22419 );
22420
22421 cultures.insert(
22422 "indonesian".to_string(),
22423 Value::Array(Rc::new(RefCell::new(vec![
22424 Value::String(Rc::new("pelog".to_string())),
22425 Value::String(Rc::new("slendro".to_string())),
22426 ]))),
22427 );
22428
22429 Ok(Value::Map(Rc::new(RefCell::new(cultures))))
22430 });
22431
22432 define(interp, "interval_ratio", Some(2), |_, args| {
22438 let semitones = match &args[0] {
22439 Value::Int(n) => *n as f64,
22440 Value::Float(f) => *f,
22441 _ => return Err(RuntimeError::new("interval_ratio() requires number")),
22442 };
22443
22444 let tuning = match &args[1] {
22445 Value::String(s) => s.to_lowercase(),
22446 _ => return Err(RuntimeError::new("interval_ratio() requires tuning system")),
22447 };
22448
22449 let ratio = match tuning.as_str() {
22450 "12tet" | "equal" => 2.0_f64.powf(semitones / 12.0),
22451 "just" => just_intonation_ratio(semitones as i32),
22452 "pythagorean" => pythagorean_ratio(semitones as i32),
22453 _ => 2.0_f64.powf(semitones / 12.0),
22454 };
22455
22456 Ok(Value::Float(ratio))
22457 });
22458
22459 define(interp, "cents_between", Some(2), |_, args| {
22461 let f1 = match &args[0] {
22462 Value::Float(f) => *f,
22463 Value::Int(i) => *i as f64,
22464 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
22465 };
22466 let f2 = match &args[1] {
22467 Value::Float(f) => *f,
22468 Value::Int(i) => *i as f64,
22469 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
22470 };
22471
22472 let cents = 1200.0 * (f2 / f1).log2();
22473 Ok(Value::Float(cents))
22474 });
22475
22476 define(interp, "harmonic_series", Some(2), |_, args| {
22478 let fundamental = match &args[0] {
22479 Value::Float(f) => *f,
22480 Value::Int(i) => *i as f64,
22481 _ => return Err(RuntimeError::new("harmonic_series() requires frequency")),
22482 };
22483 let count = match &args[1] {
22484 Value::Int(n) => *n as usize,
22485 _ => return Err(RuntimeError::new("harmonic_series() requires count")),
22486 };
22487
22488 let harmonics: Vec<Value> = (1..=count)
22489 .map(|n| {
22490 let mut entry = std::collections::HashMap::new();
22491 entry.insert("harmonic".to_string(), Value::Int(n as i64));
22492 entry.insert(
22493 "frequency".to_string(),
22494 Value::Float(fundamental * n as f64),
22495 );
22496 entry.insert(
22497 "cents_from_root".to_string(),
22498 Value::Float(1200.0 * (n as f64).log2()),
22499 );
22500 Value::Map(Rc::new(RefCell::new(entry)))
22501 })
22502 .collect();
22503
22504 Ok(Value::Array(Rc::new(RefCell::new(harmonics))))
22505 });
22506
22507 define(interp, "audio_info", Some(0), |_, _| {
22512 let mut info = std::collections::HashMap::new();
22513
22514 info.insert(
22515 "tuning_systems".to_string(),
22516 Value::Array(Rc::new(RefCell::new(vec![
22517 Value::String(Rc::new(
22518 "12tet, 24tet, just, pythagorean, meantone".to_string(),
22519 )),
22520 Value::String(Rc::new(
22521 "53tet, 22shruti, pelog, slendro, bohlen_pierce".to_string(),
22522 )),
22523 ]))),
22524 );
22525
22526 info.insert(
22527 "waveforms".to_string(),
22528 Value::Array(Rc::new(RefCell::new(vec![
22529 Value::String(Rc::new("sine (∿)".to_string())),
22530 Value::String(Rc::new("square (⊓)".to_string())),
22531 Value::String(Rc::new("sawtooth (⋀)".to_string())),
22532 Value::String(Rc::new("triangle (△)".to_string())),
22533 Value::String(Rc::new("noise".to_string())),
22534 ]))),
22535 );
22536
22537 info.insert(
22538 "sacred_frequencies".to_string(),
22539 Value::Array(Rc::new(RefCell::new(vec![
22540 Value::String(Rc::new("om, solfeggio, chakras, planets".to_string())),
22541 Value::String(Rc::new(
22542 "schumann, brainwaves (delta/theta/alpha/beta/gamma)".to_string(),
22543 )),
22544 ]))),
22545 );
22546
22547 info.insert(
22548 "scale_cultures".to_string(),
22549 Value::Array(Rc::new(RefCell::new(vec![
22550 Value::String(Rc::new("western, japanese, arabic, indian".to_string())),
22551 Value::String(Rc::new("chinese, jewish, indonesian".to_string())),
22552 ]))),
22553 );
22554
22555 Ok(Value::Map(Rc::new(RefCell::new(info))))
22556 });
22557}
22558
22559fn parse_note_name(s: &str) -> Result<f64, RuntimeError> {
22562 let s = s.trim().to_uppercase();
22563 let (note, octave_offset) = if s.ends_with(|c: char| c.is_ascii_digit()) {
22564 let octave: i32 = s.chars().last().unwrap().to_digit(10).unwrap() as i32;
22565 let note_part = &s[..s.len() - 1];
22566 (note_part, (octave - 4) * 12) } else {
22568 (&s[..], 0)
22569 };
22570
22571 let semitone = match note {
22572 "C" => 0,
22573 "C#" | "DB" => 1,
22574 "D" => 2,
22575 "D#" | "EB" => 3,
22576 "E" => 4,
22577 "F" => 5,
22578 "F#" | "GB" => 6,
22579 "G" => 7,
22580 "G#" | "AB" => 8,
22581 "A" => 9,
22582 "A#" | "BB" => 10,
22583 "B" => 11,
22584 _ => return Err(RuntimeError::new(format!("Unknown note: {}", s))),
22585 };
22586
22587 Ok(69.0 + semitone as f64 - 9.0 + octave_offset as f64) }
22589
22590fn just_intonation_ratio(semitones: i32) -> f64 {
22591 match semitones.rem_euclid(12) {
22593 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,
22606 }
22607}
22608
22609fn pythagorean_ratio(semitones: i32) -> f64 {
22610 match semitones.rem_euclid(12) {
22612 0 => 1.0,
22613 1 => 256.0 / 243.0,
22614 2 => 9.0 / 8.0,
22615 3 => 32.0 / 27.0,
22616 4 => 81.0 / 64.0,
22617 5 => 4.0 / 3.0,
22618 6 => 729.0 / 512.0,
22619 7 => 3.0 / 2.0,
22620 8 => 128.0 / 81.0,
22621 9 => 27.0 / 16.0,
22622 10 => 16.0 / 9.0,
22623 11 => 243.0 / 128.0,
22624 _ => 1.0,
22625 }
22626}
22627
22628fn meantone_ratio(semitones: i32) -> f64 {
22629 let fifth = 5.0_f64.powf(0.25); match semitones.rem_euclid(12) {
22632 0 => 1.0,
22633 1 => 8.0 / (fifth.powi(5)),
22634 2 => fifth.powi(2) / 2.0,
22635 3 => 4.0 / (fifth.powi(3)),
22636 4 => fifth.powi(4) / 4.0,
22637 5 => 2.0 / fifth,
22638 6 => fifth.powi(6) / 8.0,
22639 7 => fifth,
22640 8 => 8.0 / (fifth.powi(4)),
22641 9 => fifth.powi(3) / 2.0,
22642 10 => 4.0 / (fifth.powi(2)),
22643 11 => fifth.powi(5) / 4.0,
22644 _ => 1.0,
22645 }
22646}
22647
22648fn shruti_ratio(shruti: i32) -> f64 {
22649 let ratios = [
22651 1.0,
22652 256.0 / 243.0,
22653 16.0 / 15.0,
22654 10.0 / 9.0,
22655 9.0 / 8.0,
22656 32.0 / 27.0,
22657 6.0 / 5.0,
22658 5.0 / 4.0,
22659 81.0 / 64.0,
22660 4.0 / 3.0,
22661 27.0 / 20.0,
22662 45.0 / 32.0,
22663 729.0 / 512.0,
22664 3.0 / 2.0,
22665 128.0 / 81.0,
22666 8.0 / 5.0,
22667 5.0 / 3.0,
22668 27.0 / 16.0,
22669 16.0 / 9.0,
22670 9.0 / 5.0,
22671 15.0 / 8.0,
22672 243.0 / 128.0,
22673 ];
22674 ratios[shruti.rem_euclid(22) as usize]
22675}
22676
22677fn pelog_ratio(degree: i32) -> f64 {
22678 let ratios = [1.0, 1.12, 1.26, 1.5, 1.68, 1.89, 2.12];
22680 ratios[degree.rem_euclid(7) as usize]
22681}
22682
22683fn slendro_ratio(degree: i32) -> f64 {
22684 let ratios = [1.0, 1.148, 1.318, 1.516, 1.741];
22686 ratios[degree.rem_euclid(5) as usize]
22687}
22688
22689fn generate_waveform(args: &[Value], wave_fn: fn(f64) -> f64) -> Result<Value, RuntimeError> {
22690 let freq = match &args[0] {
22691 Value::Float(f) => *f,
22692 Value::Int(i) => *i as f64,
22693 _ => return Err(RuntimeError::new("Waveform requires frequency")),
22694 };
22695 let sample_rate = match &args[1] {
22696 Value::Float(f) => *f as usize,
22697 Value::Int(i) => *i as usize,
22698 _ => return Err(RuntimeError::new("Waveform requires sample rate")),
22699 };
22700 let duration = match &args[2] {
22701 Value::Float(f) => *f,
22702 Value::Int(i) => *i as f64,
22703 _ => return Err(RuntimeError::new("Waveform requires duration")),
22704 };
22705
22706 let num_samples = (sample_rate as f64 * duration) as usize;
22707 let samples: Vec<Value> = (0..num_samples)
22708 .map(|i| {
22709 let t = i as f64 / sample_rate as f64;
22710 let phase = 2.0 * std::f64::consts::PI * freq * t;
22711 Value::Float(wave_fn(phase))
22712 })
22713 .collect();
22714
22715 Ok(Value::Array(Rc::new(RefCell::new(samples))))
22716}
22717
22718fn register_spirituality(interp: &mut Interpreter) {
22740 const TRIGRAMS: [(&str, &str, &str, &str, &str); 8] = [
22746 (
22747 "☰",
22748 "乾",
22749 "Heaven",
22750 "Creative",
22751 "strong, initiating, persisting",
22752 ),
22753 (
22754 "☱",
22755 "兌",
22756 "Lake",
22757 "Joyous",
22758 "pleasure, satisfaction, openness",
22759 ),
22760 (
22761 "☲",
22762 "離",
22763 "Fire",
22764 "Clinging",
22765 "clarity, awareness, dependence",
22766 ),
22767 (
22768 "☳",
22769 "震",
22770 "Thunder",
22771 "Arousing",
22772 "movement, initiative, action",
22773 ),
22774 (
22775 "☴",
22776 "巽",
22777 "Wind",
22778 "Gentle",
22779 "penetrating, following, flexible",
22780 ),
22781 ("☵", "坎", "Water", "Abysmal", "danger, flowing, depth"),
22782 (
22783 "☶",
22784 "艮",
22785 "Mountain",
22786 "Keeping Still",
22787 "stopping, resting, meditation",
22788 ),
22789 (
22790 "☷",
22791 "坤",
22792 "Earth",
22793 "Receptive",
22794 "yielding, nurturing, devoted",
22795 ),
22796 ];
22797
22798 define(interp, "trigram", Some(1), |_, args| {
22800 let input = match &args[0] {
22801 Value::Int(n) => (*n as usize).min(7),
22802 Value::String(s) => match s.as_str() {
22803 "☰" | "heaven" | "qian" | "乾" => 0,
22804 "☱" | "lake" | "dui" | "兌" => 1,
22805 "☲" | "fire" | "li" | "離" => 2,
22806 "☳" | "thunder" | "zhen" | "震" => 3,
22807 "☴" | "wind" | "xun" | "巽" => 4,
22808 "☵" | "water" | "kan" | "坎" => 5,
22809 "☶" | "mountain" | "gen" | "艮" => 6,
22810 "☷" | "earth" | "kun" | "坤" => 7,
22811 _ => return Err(RuntimeError::new(format!("Unknown trigram: {}", s))),
22812 },
22813 _ => return Err(RuntimeError::new("trigram() requires number or name")),
22814 };
22815
22816 let (symbol, chinese, english, name, meaning) = TRIGRAMS[input];
22817
22818 let mut result = std::collections::HashMap::new();
22819 result.insert("number".to_string(), Value::Int(input as i64));
22820 result.insert(
22821 "symbol".to_string(),
22822 Value::String(Rc::new(symbol.to_string())),
22823 );
22824 result.insert(
22825 "chinese".to_string(),
22826 Value::String(Rc::new(chinese.to_string())),
22827 );
22828 result.insert(
22829 "english".to_string(),
22830 Value::String(Rc::new(english.to_string())),
22831 );
22832 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
22833 result.insert(
22834 "meaning".to_string(),
22835 Value::String(Rc::new(meaning.to_string())),
22836 );
22837
22838 let binary = match input {
22840 0 => "111", 1 => "110", 2 => "101", 3 => "100", 4 => "011", 5 => "010", 6 => "001", 7 => "000", _ => "000",
22849 };
22850 result.insert(
22851 "binary".to_string(),
22852 Value::String(Rc::new(binary.to_string())),
22853 );
22854
22855 Ok(Value::Map(Rc::new(RefCell::new(result))))
22856 });
22857
22858 define(interp, "hexagram", Some(1), |_, args| {
22860 let num = match &args[0] {
22861 Value::Int(n) => ((*n - 1) as usize).min(63),
22862 _ => return Err(RuntimeError::new("hexagram() requires number 1-64")),
22863 };
22864
22865 let hex = &HEXAGRAMS[num];
22866
22867 let mut result = std::collections::HashMap::new();
22868 result.insert("number".to_string(), Value::Int((num + 1) as i64));
22869 result.insert(
22870 "chinese".to_string(),
22871 Value::String(Rc::new(hex.0.to_string())),
22872 );
22873 result.insert(
22874 "pinyin".to_string(),
22875 Value::String(Rc::new(hex.1.to_string())),
22876 );
22877 result.insert(
22878 "english".to_string(),
22879 Value::String(Rc::new(hex.2.to_string())),
22880 );
22881 result.insert(
22882 "judgment".to_string(),
22883 Value::String(Rc::new(hex.3.to_string())),
22884 );
22885 result.insert(
22886 "upper_trigram".to_string(),
22887 Value::String(Rc::new(hex.4.to_string())),
22888 );
22889 result.insert(
22890 "lower_trigram".to_string(),
22891 Value::String(Rc::new(hex.5.to_string())),
22892 );
22893
22894 Ok(Value::Map(Rc::new(RefCell::new(result))))
22895 });
22896
22897 define(interp, "cast_iching", Some(0), |_, _| {
22899 let mut rng = rand::thread_rng();
22900
22901 let mut lines = Vec::new();
22904 let mut hexagram_num = 0u8;
22905 let mut changing_lines = Vec::new();
22906
22907 for i in 0..6 {
22908 let r: f64 = rng.gen();
22910 let line = if r < 0.0625 {
22911 6
22912 }
22913 else if r < 0.3125 {
22915 7
22916 }
22917 else if r < 0.5625 {
22919 8
22920 }
22921 else {
22923 9
22924 }; let is_yang = line == 7 || line == 9;
22927 if is_yang {
22928 hexagram_num |= 1 << i;
22929 }
22930
22931 if line == 6 || line == 9 {
22932 changing_lines.push(i + 1);
22933 }
22934
22935 lines.push(Value::Int(line));
22936 }
22937
22938 let king_wen_num = binary_to_king_wen(hexagram_num) + 1;
22940 let hex = &HEXAGRAMS[(king_wen_num - 1) as usize];
22941
22942 let mut result = std::collections::HashMap::new();
22943 result.insert("hexagram".to_string(), Value::Int(king_wen_num as i64));
22944 result.insert(
22945 "chinese".to_string(),
22946 Value::String(Rc::new(hex.0.to_string())),
22947 );
22948 result.insert(
22949 "english".to_string(),
22950 Value::String(Rc::new(hex.2.to_string())),
22951 );
22952 result.insert(
22953 "judgment".to_string(),
22954 Value::String(Rc::new(hex.3.to_string())),
22955 );
22956 result.insert(
22957 "lines".to_string(),
22958 Value::Array(Rc::new(RefCell::new(lines))),
22959 );
22960
22961 let changing: Vec<Value> = changing_lines.iter().map(|&n| Value::Int(n)).collect();
22962 result.insert(
22963 "changing_lines".to_string(),
22964 Value::Array(Rc::new(RefCell::new(changing))),
22965 );
22966
22967 if !changing_lines.is_empty() {
22969 let mut result_hex = hexagram_num;
22970 for &line in &changing_lines {
22971 result_hex ^= 1 << (line - 1); }
22973 let result_king_wen = binary_to_king_wen(result_hex) + 1;
22974 result.insert(
22975 "transforms_to".to_string(),
22976 Value::Int(result_king_wen as i64),
22977 );
22978 }
22979
22980 Ok(Value::Map(Rc::new(RefCell::new(result))))
22981 });
22982
22983 define(interp, "phi", Some(0), |_, _| {
22989 Ok(Value::Float((1.0 + 5.0_f64.sqrt()) / 2.0))
22990 });
22991
22992 define(interp, "sacred_ratio", Some(1), |_, args| {
22994 let name = match &args[0] {
22995 Value::String(s) => s.to_lowercase(),
22996 _ => return Err(RuntimeError::new("sacred_ratio() requires string")),
22997 };
22998
22999 let (value, symbol, meaning) = match name.as_str() {
23000 "phi" | "φ" | "golden" => (
23001 (1.0 + 5.0_f64.sqrt()) / 2.0,
23002 "φ",
23003 "Golden Ratio - divine proportion found in nature, art, architecture",
23004 ),
23005 "phi_conjugate" | "1/phi" => (
23006 2.0 / (1.0 + 5.0_f64.sqrt()),
23007 "1/φ",
23008 "Golden Ratio conjugate - φ - 1 = 1/φ",
23009 ),
23010 "phi_squared" | "phi2" => (
23011 ((1.0 + 5.0_f64.sqrt()) / 2.0).powi(2),
23012 "φ²",
23013 "Golden Ratio squared - φ + 1 = φ²",
23014 ),
23015 "sqrt_phi" => (
23016 ((1.0 + 5.0_f64.sqrt()) / 2.0).sqrt(),
23017 "√φ",
23018 "Square root of Golden Ratio",
23019 ),
23020 "pi" | "π" => (
23021 std::f64::consts::PI,
23022 "π",
23023 "Circle constant - circumference/diameter, transcendental",
23024 ),
23025 "tau" | "τ" => (
23026 std::f64::consts::TAU,
23027 "τ",
23028 "Full circle constant - 2π, one complete revolution",
23029 ),
23030 "e" | "euler" => (
23031 std::f64::consts::E,
23032 "e",
23033 "Euler's number - natural growth, compound interest",
23034 ),
23035 "sqrt2" | "√2" | "pythagoras" => (
23036 std::f64::consts::SQRT_2,
23037 "√2",
23038 "Pythagorean constant - diagonal of unit square",
23039 ),
23040 "sqrt3" | "√3" | "vesica" => (
23041 3.0_f64.sqrt(),
23042 "√3",
23043 "Vesica Piscis ratio - sacred geometry foundation",
23044 ),
23045 "sqrt5" | "√5" => (
23046 5.0_f64.sqrt(),
23047 "√5",
23048 "Related to Golden Ratio: φ = (1 + √5) / 2",
23049 ),
23050 "silver" | "δs" => (
23051 1.0 + 2.0_f64.sqrt(),
23052 "δs",
23053 "Silver Ratio - related to octagon",
23054 ),
23055 "plastic" | "ρ" => (
23056 1.324717957244746,
23057 "ρ",
23058 "Plastic Number - smallest Pisot number",
23059 ),
23060 "feigenbaum" | "δ" => (
23061 4.669201609102990,
23062 "δ",
23063 "Feigenbaum constant - chaos theory, period doubling",
23064 ),
23065 _ => return Err(RuntimeError::new(format!("Unknown sacred ratio: {}", name))),
23066 };
23067
23068 let mut result = std::collections::HashMap::new();
23069 result.insert("value".to_string(), Value::Float(value));
23070 result.insert(
23071 "symbol".to_string(),
23072 Value::String(Rc::new(symbol.to_string())),
23073 );
23074 result.insert(
23075 "meaning".to_string(),
23076 Value::String(Rc::new(meaning.to_string())),
23077 );
23078
23079 Ok(Value::Map(Rc::new(RefCell::new(result))))
23080 });
23081
23082 define(interp, "fibonacci", Some(1), |_, args| {
23084 let count = match &args[0] {
23085 Value::Int(n) => *n as usize,
23086 _ => return Err(RuntimeError::new("fibonacci() requires count")),
23087 };
23088
23089 let mut seq = Vec::with_capacity(count);
23090 let (mut a, mut b) = (0i64, 1i64);
23091
23092 for _ in 0..count {
23093 seq.push(Value::Int(a));
23094 let next = a.saturating_add(b);
23095 a = b;
23096 b = next;
23097 }
23098
23099 Ok(Value::Array(Rc::new(RefCell::new(seq))))
23100 });
23101
23102 define(interp, "is_fibonacci", Some(1), |_, args| {
23104 let n = match &args[0] {
23105 Value::Int(n) => *n,
23106 _ => return Err(RuntimeError::new("is_fibonacci() requires integer")),
23107 };
23108
23109 fn is_perfect_square(n: i64) -> bool {
23111 if n < 0 {
23112 return false;
23113 }
23114 let root = (n as f64).sqrt() as i64;
23115 root * root == n
23116 }
23117
23118 let n_sq = n.saturating_mul(n);
23119 let test1 = 5i64.saturating_mul(n_sq).saturating_add(4);
23120 let test2 = 5i64.saturating_mul(n_sq).saturating_sub(4);
23121
23122 Ok(Value::Bool(
23123 is_perfect_square(test1) || is_perfect_square(test2),
23124 ))
23125 });
23126
23127 define(interp, "platonic_solid", Some(1), |_, args| {
23129 let name = match &args[0] {
23130 Value::String(s) => s.to_lowercase(),
23131 _ => return Err(RuntimeError::new("platonic_solid() requires string")),
23132 };
23133
23134 let (faces, vertices, edges, face_shape, element, meaning) = match name.as_str() {
23135 "tetrahedron" | "fire" => (
23136 4,
23137 4,
23138 6,
23139 "triangle",
23140 "Fire",
23141 "Sharpness, heat, transformation",
23142 ),
23143 "cube" | "hexahedron" | "earth" => (
23144 6,
23145 8,
23146 12,
23147 "square",
23148 "Earth",
23149 "Stability, grounding, material",
23150 ),
23151 "octahedron" | "air" => (
23152 8,
23153 6,
23154 12,
23155 "triangle",
23156 "Air",
23157 "Balance, intellect, communication",
23158 ),
23159 "dodecahedron" | "aether" | "spirit" => (
23160 12,
23161 20,
23162 30,
23163 "pentagon",
23164 "Aether/Spirit",
23165 "The cosmos, divine thought",
23166 ),
23167 "icosahedron" | "water" => (
23168 20,
23169 12,
23170 30,
23171 "triangle",
23172 "Water",
23173 "Flow, emotion, adaptability",
23174 ),
23175 _ => {
23176 return Err(RuntimeError::new(format!(
23177 "Unknown Platonic solid: {}",
23178 name
23179 )))
23180 }
23181 };
23182
23183 let mut result = std::collections::HashMap::new();
23184 result.insert("name".to_string(), Value::String(Rc::new(name)));
23185 result.insert("faces".to_string(), Value::Int(faces));
23186 result.insert("vertices".to_string(), Value::Int(vertices));
23187 result.insert("edges".to_string(), Value::Int(edges));
23188 result.insert(
23189 "face_shape".to_string(),
23190 Value::String(Rc::new(face_shape.to_string())),
23191 );
23192 result.insert(
23193 "element".to_string(),
23194 Value::String(Rc::new(element.to_string())),
23195 );
23196 result.insert(
23197 "meaning".to_string(),
23198 Value::String(Rc::new(meaning.to_string())),
23199 );
23200
23201 result.insert("euler_characteristic".to_string(), Value::Int(2));
23203
23204 Ok(Value::Map(Rc::new(RefCell::new(result))))
23205 });
23206
23207 define(interp, "gematria", Some(2), |_, args| {
23213 let text = match &args[0] {
23214 Value::String(s) => s.to_string(),
23215 _ => return Err(RuntimeError::new("gematria() requires string")),
23216 };
23217
23218 let system = match &args[1] {
23219 Value::String(s) => s.to_lowercase(),
23220 _ => return Err(RuntimeError::new("gematria() requires system name")),
23221 };
23222
23223 let total: i64 = match system.as_str() {
23224 "hebrew" | "kabbalah" => text.chars().map(|c| hebrew_gematria(c)).sum(),
23225 "greek" | "isopsephy" => text.chars().map(|c| greek_isopsephy(c)).sum(),
23226 "arabic" | "abjad" => text.chars().map(|c| arabic_abjad(c)).sum(),
23227 "english" | "simple" => {
23228 text.to_uppercase()
23230 .chars()
23231 .filter_map(|c| {
23232 if c.is_ascii_alphabetic() {
23233 Some((c as i64) - ('A' as i64) + 1)
23234 } else {
23235 None
23236 }
23237 })
23238 .sum()
23239 }
23240 "english_ordinal" => {
23241 text.to_uppercase()
23243 .chars()
23244 .filter_map(|c| {
23245 if c.is_ascii_alphabetic() {
23246 Some((c as i64) - ('A' as i64) + 1)
23247 } else {
23248 None
23249 }
23250 })
23251 .sum()
23252 }
23253 "english_reduction" => {
23254 text.to_uppercase()
23256 .chars()
23257 .filter_map(|c| {
23258 if c.is_ascii_alphabetic() {
23259 let val = ((c as i64) - ('A' as i64)) % 9 + 1;
23260 Some(val)
23261 } else {
23262 None
23263 }
23264 })
23265 .sum()
23266 }
23267 _ => {
23268 return Err(RuntimeError::new(format!(
23269 "Unknown gematria system: {}",
23270 system
23271 )))
23272 }
23273 };
23274
23275 let mut result = std::collections::HashMap::new();
23276 result.insert("text".to_string(), Value::String(Rc::new(text)));
23277 result.insert("system".to_string(), Value::String(Rc::new(system)));
23278 result.insert("value".to_string(), Value::Int(total));
23279
23280 let mut digital_root = total;
23282 while digital_root > 9 {
23283 digital_root = digital_root
23284 .to_string()
23285 .chars()
23286 .filter_map(|c| c.to_digit(10))
23287 .map(|d| d as i64)
23288 .sum();
23289 }
23290 result.insert("digital_root".to_string(), Value::Int(digital_root));
23291
23292 Ok(Value::Map(Rc::new(RefCell::new(result))))
23293 });
23294
23295 define(interp, "gematria_match", Some(2), |_, args| {
23297 let value = match &args[0] {
23298 Value::Int(n) => *n,
23299 _ => return Err(RuntimeError::new("gematria_match() requires integer value")),
23300 };
23301
23302 let system = match &args[1] {
23303 Value::String(s) => s.to_lowercase(),
23304 _ => return Err(RuntimeError::new("gematria_match() requires system name")),
23305 };
23306
23307 let matches = match (value, system.as_str()) {
23309 (26, "hebrew") => vec!["יהוה (YHWH - Tetragrammaton)"],
23310 (18, "hebrew") => vec!["חי (Chai - Life)"],
23311 (86, "hebrew") => vec!["אלהים (Elohim - God)"],
23312 (72, "hebrew") => vec!["חסד (Chesed - Loving-kindness)"],
23313 (93, "english") => vec!["Love", "Will", "Thelema"],
23314 (666, "greek") => vec!["Nero Caesar (in Hebrew letters)"],
23315 (888, "greek") => vec!["Ἰησοῦς (Jesus)"],
23316 _ => vec![],
23317 };
23318
23319 let match_values: Vec<Value> = matches
23320 .iter()
23321 .map(|s| Value::String(Rc::new(s.to_string())))
23322 .collect();
23323
23324 Ok(Value::Array(Rc::new(RefCell::new(match_values))))
23325 });
23326
23327 define(interp, "archetype", Some(1), |_, args| {
23333 let name = match &args[0] {
23334 Value::String(s) => s.to_lowercase(),
23335 _ => return Err(RuntimeError::new("archetype() requires string")),
23336 };
23337
23338 let (description, shadow, gift, challenge) = match name.as_str() {
23339 "self" => (
23341 "The unified conscious and unconscious, the goal of individuation",
23342 "Inflation or deflation of ego",
23343 "Wholeness, integration, meaning",
23344 "Integrating all aspects of psyche",
23345 ),
23346 "shadow" => (
23347 "The unconscious aspect containing repressed weaknesses and instincts",
23348 "Projection onto others, denial",
23349 "Creativity, spontaneity, insight",
23350 "Acknowledging and integrating darkness",
23351 ),
23352 "anima" => (
23353 "The feminine inner personality in a man's unconscious",
23354 "Moodiness, seduction, possession",
23355 "Relatedness, creativity, soul connection",
23356 "Developing emotional intelligence",
23357 ),
23358 "animus" => (
23359 "The masculine inner personality in a woman's unconscious",
23360 "Brutality, reckless action, opinionation",
23361 "Courage, initiative, spiritual depth",
23362 "Developing assertiveness with wisdom",
23363 ),
23364 "persona" => (
23365 "The social mask, the face we present to the world",
23366 "Over-identification, inauthenticity",
23367 "Social adaptation, professional competence",
23368 "Maintaining authenticity within role",
23369 ),
23370
23371 "hero" => (
23373 "The courageous one who overcomes obstacles and achieves great deeds",
23374 "Arrogance, ruthlessness, eternal battle",
23375 "Courage, perseverance, accomplishment",
23376 "Knowing when to fight and when to surrender",
23377 ),
23378 "sage" | "wise_old_man" => (
23379 "The wise figure who offers guidance and insight",
23380 "Dogmatism, disconnection, ivory tower",
23381 "Wisdom, knowledge, truth-seeking",
23382 "Applying wisdom practically",
23383 ),
23384 "magician" | "wizard" => (
23385 "The transformer who makes things happen through understanding laws",
23386 "Manipulation, disconnection from ethics",
23387 "Transformation, vision, manifestation",
23388 "Using power responsibly",
23389 ),
23390 "lover" => (
23391 "The one who pursues connection, beauty, and passion",
23392 "Obsession, jealousy, loss of identity",
23393 "Passion, commitment, appreciation",
23394 "Maintaining boundaries while connecting deeply",
23395 ),
23396 "caregiver" | "mother" => (
23397 "The nurturing one who protects and provides",
23398 "Martyrdom, enabling, smothering",
23399 "Compassion, generosity, nurturing",
23400 "Caring for self while caring for others",
23401 ),
23402 "ruler" | "king" | "queen" => (
23403 "The one who takes responsibility for the realm",
23404 "Tyranny, authoritarianism, being overthrown",
23405 "Order, leadership, prosperity",
23406 "Serving the greater good, not just power",
23407 ),
23408 "creator" | "artist" => (
23409 "The one who brings new things into being",
23410 "Perfectionism, self-indulgence, drama",
23411 "Creativity, imagination, expression",
23412 "Completing projects, accepting imperfection",
23413 ),
23414 "innocent" | "child" => (
23415 "The pure one with faith and optimism",
23416 "Naivety, denial, dependence",
23417 "Faith, optimism, loyalty",
23418 "Growing without becoming cynical",
23419 ),
23420 "explorer" | "seeker" => (
23421 "The one who seeks new experiences and self-discovery",
23422 "Aimless wandering, inability to commit",
23423 "Autonomy, ambition, authenticity",
23424 "Finding what you seek",
23425 ),
23426 "rebel" | "outlaw" => (
23427 "The one who breaks rules and challenges the status quo",
23428 "Crime, self-destruction, alienation",
23429 "Liberation, revolution, radical freedom",
23430 "Channeling rebellion constructively",
23431 ),
23432 "jester" | "fool" | "trickster" => (
23433 "The one who uses humor and playfulness",
23434 "Cruelty, debauchery, irresponsibility",
23435 "Joy, freedom, living in the moment",
23436 "Knowing when to be serious",
23437 ),
23438 "everyman" | "orphan" => (
23439 "The regular person who wants belonging",
23440 "Victim mentality, losing self in group",
23441 "Realism, empathy, connection",
23442 "Standing out when necessary",
23443 ),
23444 _ => return Err(RuntimeError::new(format!("Unknown archetype: {}", name))),
23445 };
23446
23447 let mut result = std::collections::HashMap::new();
23448 result.insert("name".to_string(), Value::String(Rc::new(name)));
23449 result.insert(
23450 "description".to_string(),
23451 Value::String(Rc::new(description.to_string())),
23452 );
23453 result.insert(
23454 "shadow".to_string(),
23455 Value::String(Rc::new(shadow.to_string())),
23456 );
23457 result.insert("gift".to_string(), Value::String(Rc::new(gift.to_string())));
23458 result.insert(
23459 "challenge".to_string(),
23460 Value::String(Rc::new(challenge.to_string())),
23461 );
23462
23463 Ok(Value::Map(Rc::new(RefCell::new(result))))
23464 });
23465
23466 define(interp, "zodiac", Some(1), |_, args| {
23472 let input = match &args[0] {
23473 Value::Int(n) => (*n as usize - 1).min(11),
23474 Value::String(s) => match s.to_lowercase().as_str() {
23475 "aries" | "♈" => 0,
23476 "taurus" | "♉" => 1,
23477 "gemini" | "♊" => 2,
23478 "cancer" | "♋" => 3,
23479 "leo" | "♌" => 4,
23480 "virgo" | "♍" => 5,
23481 "libra" | "♎" => 6,
23482 "scorpio" | "♏" => 7,
23483 "sagittarius" | "♐" => 8,
23484 "capricorn" | "♑" => 9,
23485 "aquarius" | "♒" => 10,
23486 "pisces" | "♓" => 11,
23487 _ => return Err(RuntimeError::new(format!("Unknown sign: {}", s))),
23488 },
23489 _ => return Err(RuntimeError::new("zodiac() requires number or name")),
23490 };
23491
23492 let signs = [
23493 (
23494 "♈",
23495 "Aries",
23496 "Fire",
23497 "Cardinal",
23498 "Mars",
23499 "I Am",
23500 "Mar 21 - Apr 19",
23501 ),
23502 (
23503 "♉",
23504 "Taurus",
23505 "Earth",
23506 "Fixed",
23507 "Venus",
23508 "I Have",
23509 "Apr 20 - May 20",
23510 ),
23511 (
23512 "♊",
23513 "Gemini",
23514 "Air",
23515 "Mutable",
23516 "Mercury",
23517 "I Think",
23518 "May 21 - Jun 20",
23519 ),
23520 (
23521 "♋",
23522 "Cancer",
23523 "Water",
23524 "Cardinal",
23525 "Moon",
23526 "I Feel",
23527 "Jun 21 - Jul 22",
23528 ),
23529 (
23530 "♌",
23531 "Leo",
23532 "Fire",
23533 "Fixed",
23534 "Sun",
23535 "I Will",
23536 "Jul 23 - Aug 22",
23537 ),
23538 (
23539 "♍",
23540 "Virgo",
23541 "Earth",
23542 "Mutable",
23543 "Mercury",
23544 "I Analyze",
23545 "Aug 23 - Sep 22",
23546 ),
23547 (
23548 "♎",
23549 "Libra",
23550 "Air",
23551 "Cardinal",
23552 "Venus",
23553 "I Balance",
23554 "Sep 23 - Oct 22",
23555 ),
23556 (
23557 "♏",
23558 "Scorpio",
23559 "Water",
23560 "Fixed",
23561 "Pluto/Mars",
23562 "I Transform",
23563 "Oct 23 - Nov 21",
23564 ),
23565 (
23566 "♐",
23567 "Sagittarius",
23568 "Fire",
23569 "Mutable",
23570 "Jupiter",
23571 "I Seek",
23572 "Nov 22 - Dec 21",
23573 ),
23574 (
23575 "♑",
23576 "Capricorn",
23577 "Earth",
23578 "Cardinal",
23579 "Saturn",
23580 "I Use",
23581 "Dec 22 - Jan 19",
23582 ),
23583 (
23584 "♒",
23585 "Aquarius",
23586 "Air",
23587 "Fixed",
23588 "Uranus/Saturn",
23589 "I Know",
23590 "Jan 20 - Feb 18",
23591 ),
23592 (
23593 "♓",
23594 "Pisces",
23595 "Water",
23596 "Mutable",
23597 "Neptune/Jupiter",
23598 "I Believe",
23599 "Feb 19 - Mar 20",
23600 ),
23601 ];
23602
23603 let (symbol, name, element, modality, ruler, motto, dates) = signs[input];
23604
23605 let mut result = std::collections::HashMap::new();
23606 result.insert("number".to_string(), Value::Int((input + 1) as i64));
23607 result.insert(
23608 "symbol".to_string(),
23609 Value::String(Rc::new(symbol.to_string())),
23610 );
23611 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23612 result.insert(
23613 "element".to_string(),
23614 Value::String(Rc::new(element.to_string())),
23615 );
23616 result.insert(
23617 "modality".to_string(),
23618 Value::String(Rc::new(modality.to_string())),
23619 );
23620 result.insert(
23621 "ruler".to_string(),
23622 Value::String(Rc::new(ruler.to_string())),
23623 );
23624 result.insert(
23625 "motto".to_string(),
23626 Value::String(Rc::new(motto.to_string())),
23627 );
23628 result.insert(
23629 "dates".to_string(),
23630 Value::String(Rc::new(dates.to_string())),
23631 );
23632
23633 Ok(Value::Map(Rc::new(RefCell::new(result))))
23634 });
23635
23636 define(interp, "tarot_major", Some(1), |_, args| {
23638 let num = match &args[0] {
23639 Value::Int(n) => (*n as usize).min(21),
23640 Value::String(s) => match s.to_lowercase().as_str() {
23641 "fool" => 0,
23642 "magician" => 1,
23643 "high_priestess" | "priestess" => 2,
23644 "empress" => 3,
23645 "emperor" => 4,
23646 "hierophant" | "pope" => 5,
23647 "lovers" => 6,
23648 "chariot" => 7,
23649 "strength" => 8,
23650 "hermit" => 9,
23651 "wheel" | "fortune" => 10,
23652 "justice" => 11,
23653 "hanged_man" | "hanged" => 12,
23654 "death" => 13,
23655 "temperance" => 14,
23656 "devil" => 15,
23657 "tower" => 16,
23658 "star" => 17,
23659 "moon" => 18,
23660 "sun" => 19,
23661 "judgement" | "judgment" => 20,
23662 "world" => 21,
23663 _ => return Err(RuntimeError::new(format!("Unknown card: {}", s))),
23664 },
23665 _ => return Err(RuntimeError::new("tarot_major() requires number or name")),
23666 };
23667
23668 let cards = [
23669 (
23670 "The Fool",
23671 "New beginnings, innocence, spontaneity",
23672 "Naivety, recklessness, risk-taking",
23673 ),
23674 (
23675 "The Magician",
23676 "Willpower, creation, manifestation",
23677 "Manipulation, trickery, unused talent",
23678 ),
23679 (
23680 "The High Priestess",
23681 "Intuition, mystery, inner knowledge",
23682 "Secrets, withdrawal, silence",
23683 ),
23684 (
23685 "The Empress",
23686 "Abundance, nurturing, fertility",
23687 "Dependence, smothering, emptiness",
23688 ),
23689 (
23690 "The Emperor",
23691 "Authority, structure, control",
23692 "Tyranny, rigidity, coldness",
23693 ),
23694 (
23695 "The Hierophant",
23696 "Tradition, conformity, spirituality",
23697 "Dogma, restriction, challenging status quo",
23698 ),
23699 (
23700 "The Lovers",
23701 "Love, harmony, relationships, choices",
23702 "Disharmony, imbalance, misalignment",
23703 ),
23704 (
23705 "The Chariot",
23706 "Direction, willpower, victory",
23707 "Aggression, lack of direction, obstacles",
23708 ),
23709 (
23710 "Strength",
23711 "Courage, patience, inner power",
23712 "Self-doubt, weakness, insecurity",
23713 ),
23714 (
23715 "The Hermit",
23716 "Contemplation, search for truth, inner guidance",
23717 "Isolation, loneliness, withdrawal",
23718 ),
23719 (
23720 "Wheel of Fortune",
23721 "Change, cycles, fate, destiny",
23722 "Resistance to change, bad luck, setbacks",
23723 ),
23724 (
23725 "Justice",
23726 "Truth, fairness, law, cause and effect",
23727 "Unfairness, dishonesty, lack of accountability",
23728 ),
23729 (
23730 "The Hanged Man",
23731 "Surrender, letting go, new perspective",
23732 "Stalling, resistance, indecision",
23733 ),
23734 (
23735 "Death",
23736 "Endings, transformation, transition",
23737 "Fear of change, stagnation, decay",
23738 ),
23739 (
23740 "Temperance",
23741 "Balance, moderation, patience",
23742 "Imbalance, excess, lack of purpose",
23743 ),
23744 (
23745 "The Devil",
23746 "Bondage, materialism, shadow self",
23747 "Freedom, release, exploring dark side",
23748 ),
23749 (
23750 "The Tower",
23751 "Sudden change, upheaval, revelation",
23752 "Disaster averted, fear of change, prolonged pain",
23753 ),
23754 (
23755 "The Star",
23756 "Hope, faith, renewal, inspiration",
23757 "Despair, disconnection, lack of faith",
23758 ),
23759 (
23760 "The Moon",
23761 "Illusion, intuition, the unconscious",
23762 "Fear, confusion, misinterpretation",
23763 ),
23764 (
23765 "The Sun",
23766 "Joy, success, vitality, positivity",
23767 "Negativity, depression, sadness",
23768 ),
23769 (
23770 "Judgement",
23771 "Rebirth, inner calling, absolution",
23772 "Self-doubt, refusal of self-examination",
23773 ),
23774 (
23775 "The World",
23776 "Completion, accomplishment, wholeness",
23777 "Incompletion, lack of closure, emptiness",
23778 ),
23779 ];
23780
23781 let (name, upright, reversed) = cards[num];
23782
23783 let mut result = std::collections::HashMap::new();
23784 result.insert("number".to_string(), Value::Int(num as i64));
23785 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23786 result.insert(
23787 "upright".to_string(),
23788 Value::String(Rc::new(upright.to_string())),
23789 );
23790 result.insert(
23791 "reversed".to_string(),
23792 Value::String(Rc::new(reversed.to_string())),
23793 );
23794
23795 Ok(Value::Map(Rc::new(RefCell::new(result))))
23796 });
23797
23798 define(interp, "draw_tarot", Some(0), |_, _| {
23800 let mut rng = rand::thread_rng();
23801 let card: usize = rng.gen_range(0..22);
23802 let reversed: bool = rng.gen();
23803
23804 let cards = [
23805 "The Fool",
23806 "The Magician",
23807 "The High Priestess",
23808 "The Empress",
23809 "The Emperor",
23810 "The Hierophant",
23811 "The Lovers",
23812 "The Chariot",
23813 "Strength",
23814 "The Hermit",
23815 "Wheel of Fortune",
23816 "Justice",
23817 "The Hanged Man",
23818 "Death",
23819 "Temperance",
23820 "The Devil",
23821 "The Tower",
23822 "The Star",
23823 "The Moon",
23824 "The Sun",
23825 "Judgement",
23826 "The World",
23827 ];
23828
23829 let mut result = std::collections::HashMap::new();
23830 result.insert("number".to_string(), Value::Int(card as i64));
23831 result.insert(
23832 "name".to_string(),
23833 Value::String(Rc::new(cards[card].to_string())),
23834 );
23835 result.insert("reversed".to_string(), Value::Bool(reversed));
23836 result.insert(
23837 "orientation".to_string(),
23838 Value::String(Rc::new(
23839 if reversed { "reversed" } else { "upright" }.to_string(),
23840 )),
23841 );
23842
23843 Ok(Value::Map(Rc::new(RefCell::new(result))))
23844 });
23845
23846 define(interp, "synchronicity_score", Some(2), |_, args| {
23852 let a = match &args[0] {
23854 Value::String(s) => s.to_string(),
23855 Value::Int(n) => n.to_string(),
23856 _ => {
23857 return Err(RuntimeError::new(
23858 "synchronicity_score() requires string or int",
23859 ))
23860 }
23861 };
23862
23863 let b = match &args[1] {
23864 Value::String(s) => s.to_string(),
23865 Value::Int(n) => n.to_string(),
23866 _ => {
23867 return Err(RuntimeError::new(
23868 "synchronicity_score() requires string or int",
23869 ))
23870 }
23871 };
23872
23873 let val_a: i64 = a
23875 .to_uppercase()
23876 .chars()
23877 .filter_map(|c| {
23878 if c.is_ascii_alphabetic() {
23879 Some((c as i64) - ('A' as i64) + 1)
23880 } else if c.is_ascii_digit() {
23881 c.to_digit(10).map(|d| d as i64)
23882 } else {
23883 None
23884 }
23885 })
23886 .sum();
23887
23888 let val_b: i64 = b
23889 .to_uppercase()
23890 .chars()
23891 .filter_map(|c| {
23892 if c.is_ascii_alphabetic() {
23893 Some((c as i64) - ('A' as i64) + 1)
23894 } else if c.is_ascii_digit() {
23895 c.to_digit(10).map(|d| d as i64)
23896 } else {
23897 None
23898 }
23899 })
23900 .sum();
23901
23902 let mut factors = Vec::new();
23904
23905 if val_a == val_b {
23907 factors.push("identical_gematria".to_string());
23908 }
23909
23910 if val_a > 0 && val_b > 0 && (val_a % val_b == 0 || val_b % val_a == 0) {
23912 factors.push("divisibility".to_string());
23913 }
23914
23915 let fib_set: std::collections::HashSet<i64> =
23917 [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
23918 .iter()
23919 .cloned()
23920 .collect();
23921 if fib_set.contains(&val_a) && fib_set.contains(&val_b) {
23922 factors.push("both_fibonacci".to_string());
23923 }
23924
23925 fn digital_root(mut n: i64) -> i64 {
23927 while n > 9 {
23928 n = n
23929 .to_string()
23930 .chars()
23931 .filter_map(|c| c.to_digit(10))
23932 .map(|d| d as i64)
23933 .sum();
23934 }
23935 n
23936 }
23937 if digital_root(val_a) == digital_root(val_b) {
23938 factors.push("same_digital_root".to_string());
23939 }
23940
23941 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
23943 let ratio = if val_a > 0 && val_b > 0 {
23944 (val_a as f64 / val_b as f64).max(val_b as f64 / val_a as f64)
23945 } else {
23946 0.0
23947 };
23948 if (ratio - phi).abs() < 0.02 || (ratio - 1.0 / phi).abs() < 0.02 {
23949 factors.push("golden_ratio".to_string());
23950 }
23951
23952 let score = (factors.len() as f64 / 5.0).min(1.0);
23953
23954 let mut result = std::collections::HashMap::new();
23955 result.insert("score".to_string(), Value::Float(score));
23956 result.insert("value_a".to_string(), Value::Int(val_a));
23957 result.insert("value_b".to_string(), Value::Int(val_b));
23958 let factor_values: Vec<Value> = factors
23959 .iter()
23960 .map(|s| Value::String(Rc::new(s.clone())))
23961 .collect();
23962 result.insert(
23963 "factors".to_string(),
23964 Value::Array(Rc::new(RefCell::new(factor_values))),
23965 );
23966
23967 Ok(Value::Map(Rc::new(RefCell::new(result))))
23968 });
23969
23970 define(interp, "spirituality_info", Some(0), |_, _| {
23972 let mut info = std::collections::HashMap::new();
23973
23974 info.insert(
23975 "i_ching".to_string(),
23976 Value::String(Rc::new(
23977 "trigram(), hexagram(), cast_iching() - 64 hexagrams of change".to_string(),
23978 )),
23979 );
23980 info.insert(
23981 "sacred_geometry".to_string(),
23982 Value::String(Rc::new(
23983 "phi(), sacred_ratio(), fibonacci(), platonic_solid()".to_string(),
23984 )),
23985 );
23986 info.insert(
23987 "gematria".to_string(),
23988 Value::String(Rc::new(
23989 "Hebrew, Greek, Arabic, English letter-number systems".to_string(),
23990 )),
23991 );
23992 info.insert(
23993 "archetypes".to_string(),
23994 Value::String(Rc::new(
23995 "17 Jungian archetypes with shadow and gift".to_string(),
23996 )),
23997 );
23998 info.insert(
23999 "astrology".to_string(),
24000 Value::String(Rc::new(
24001 "zodiac() - 12 signs with elements and modalities".to_string(),
24002 )),
24003 );
24004 info.insert(
24005 "tarot".to_string(),
24006 Value::String(Rc::new(
24007 "tarot_major(), draw_tarot() - 22 Major Arcana".to_string(),
24008 )),
24009 );
24010
24011 Ok(Value::Map(Rc::new(RefCell::new(info))))
24012 });
24013}
24014
24015const HEXAGRAMS: [(&str, &str, &str, &str, &str, &str); 64] = [
24017 (
24018 "乾",
24019 "Qián",
24020 "The Creative",
24021 "Sublime success through perseverance",
24022 "Heaven",
24023 "Heaven",
24024 ),
24025 (
24026 "坤",
24027 "Kūn",
24028 "The Receptive",
24029 "Devoted success through the mare's perseverance",
24030 "Earth",
24031 "Earth",
24032 ),
24033 (
24034 "屯",
24035 "Zhūn",
24036 "Difficulty at the Beginning",
24037 "Persevere, seek helpers, don't act alone",
24038 "Water",
24039 "Thunder",
24040 ),
24041 (
24042 "蒙",
24043 "Méng",
24044 "Youthful Folly",
24045 "Success through education and guidance",
24046 "Mountain",
24047 "Water",
24048 ),
24049 (
24050 "需",
24051 "Xū",
24052 "Waiting",
24053 "Sincerity brings success; cross the great water",
24054 "Water",
24055 "Heaven",
24056 ),
24057 (
24058 "訟",
24059 "Sòng",
24060 "Conflict",
24061 "Seek counsel; don't cross the great water",
24062 "Heaven",
24063 "Water",
24064 ),
24065 (
24066 "師",
24067 "Shī",
24068 "The Army",
24069 "Perseverance and an experienced leader bring success",
24070 "Earth",
24071 "Water",
24072 ),
24073 (
24074 "比",
24075 "Bǐ",
24076 "Holding Together",
24077 "Through perseverance, those who hesitate should reflect",
24078 "Water",
24079 "Earth",
24080 ),
24081 (
24082 "小畜",
24083 "Xiǎo Chù",
24084 "Small Taming",
24085 "Success; dense clouds but no rain",
24086 "Wind",
24087 "Heaven",
24088 ),
24089 (
24090 "履",
24091 "Lǚ",
24092 "Treading",
24093 "Tread on the tiger's tail carefully; success",
24094 "Heaven",
24095 "Lake",
24096 ),
24097 (
24098 "泰",
24099 "Tài",
24100 "Peace",
24101 "The small departs, the great approaches; success",
24102 "Earth",
24103 "Heaven",
24104 ),
24105 (
24106 "否",
24107 "Pǐ",
24108 "Standstill",
24109 "The great departs, the small approaches; persevere",
24110 "Heaven",
24111 "Earth",
24112 ),
24113 (
24114 "同人",
24115 "Tóng Rén",
24116 "Fellowship",
24117 "Success in the open; cross the great water",
24118 "Heaven",
24119 "Fire",
24120 ),
24121 (
24122 "大有",
24123 "Dà Yǒu",
24124 "Great Possession",
24125 "Supreme success",
24126 "Fire",
24127 "Heaven",
24128 ),
24129 (
24130 "謙",
24131 "Qiān",
24132 "Modesty",
24133 "Success; the superior person carries things through",
24134 "Earth",
24135 "Mountain",
24136 ),
24137 (
24138 "豫",
24139 "Yù",
24140 "Enthusiasm",
24141 "Appoint helpers and set armies marching",
24142 "Thunder",
24143 "Earth",
24144 ),
24145 (
24146 "隨",
24147 "Suí",
24148 "Following",
24149 "Supreme success through perseverance",
24150 "Lake",
24151 "Thunder",
24152 ),
24153 (
24154 "蠱",
24155 "Gǔ",
24156 "Work on the Decayed",
24157 "Success; cross the great water; three days before and after",
24158 "Mountain",
24159 "Wind",
24160 ),
24161 (
24162 "臨",
24163 "Lín",
24164 "Approach",
24165 "Great success through perseverance; misfortune in eighth month",
24166 "Earth",
24167 "Lake",
24168 ),
24169 (
24170 "觀",
24171 "Guān",
24172 "Contemplation",
24173 "Ablution, but not yet sacrifice; confidence inspires",
24174 "Wind",
24175 "Earth",
24176 ),
24177 (
24178 "噬嗑",
24179 "Shì Kè",
24180 "Biting Through",
24181 "Success; favorable for legal matters",
24182 "Fire",
24183 "Thunder",
24184 ),
24185 (
24186 "賁",
24187 "Bì",
24188 "Grace",
24189 "Success in small matters",
24190 "Mountain",
24191 "Fire",
24192 ),
24193 (
24194 "剝",
24195 "Bō",
24196 "Splitting Apart",
24197 "Unfavorable to go anywhere",
24198 "Mountain",
24199 "Earth",
24200 ),
24201 (
24202 "復",
24203 "Fù",
24204 "Return",
24205 "Success; going out and coming in without error",
24206 "Earth",
24207 "Thunder",
24208 ),
24209 (
24210 "無妄",
24211 "Wú Wàng",
24212 "Innocence",
24213 "Supreme success through perseverance",
24214 "Heaven",
24215 "Thunder",
24216 ),
24217 (
24218 "大畜",
24219 "Dà Chù",
24220 "Great Taming",
24221 "Perseverance; eat away from home",
24222 "Mountain",
24223 "Heaven",
24224 ),
24225 (
24226 "頤",
24227 "Yí",
24228 "Nourishment",
24229 "Perseverance; watch what you nurture",
24230 "Mountain",
24231 "Thunder",
24232 ),
24233 (
24234 "大過",
24235 "Dà Guò",
24236 "Great Exceeding",
24237 "The ridgepole sags; favorable to have somewhere to go",
24238 "Lake",
24239 "Wind",
24240 ),
24241 (
24242 "坎",
24243 "Kǎn",
24244 "The Abysmal",
24245 "Sincerity brings success of the heart",
24246 "Water",
24247 "Water",
24248 ),
24249 (
24250 "離",
24251 "Lí",
24252 "The Clinging",
24253 "Perseverance; success; care for the cow",
24254 "Fire",
24255 "Fire",
24256 ),
24257 (
24258 "咸",
24259 "Xián",
24260 "Influence",
24261 "Success; perseverance; taking a maiden brings fortune",
24262 "Lake",
24263 "Mountain",
24264 ),
24265 (
24266 "恆",
24267 "Héng",
24268 "Duration",
24269 "Success without blame; perseverance; favorable to have somewhere to go",
24270 "Thunder",
24271 "Wind",
24272 ),
24273 (
24274 "遯",
24275 "Dùn",
24276 "Retreat",
24277 "Success; small perseverance",
24278 "Heaven",
24279 "Mountain",
24280 ),
24281 (
24282 "大壯",
24283 "Dà Zhuàng",
24284 "Great Power",
24285 "Perseverance",
24286 "Thunder",
24287 "Heaven",
24288 ),
24289 (
24290 "晉",
24291 "Jìn",
24292 "Progress",
24293 "The powerful prince is honored with horses",
24294 "Fire",
24295 "Earth",
24296 ),
24297 (
24298 "明夷",
24299 "Míng Yí",
24300 "Darkening of the Light",
24301 "Perseverance in adversity",
24302 "Earth",
24303 "Fire",
24304 ),
24305 (
24306 "家人",
24307 "Jiā Rén",
24308 "The Family",
24309 "Perseverance of the woman",
24310 "Wind",
24311 "Fire",
24312 ),
24313 (
24314 "睽",
24315 "Kuí",
24316 "Opposition",
24317 "Good fortune in small matters",
24318 "Fire",
24319 "Lake",
24320 ),
24321 (
24322 "蹇",
24323 "Jiǎn",
24324 "Obstruction",
24325 "Southwest favorable; northeast unfavorable; see the great person",
24326 "Water",
24327 "Mountain",
24328 ),
24329 (
24330 "解",
24331 "Xiè",
24332 "Deliverance",
24333 "Southwest favorable; return brings fortune; haste brings fortune",
24334 "Thunder",
24335 "Water",
24336 ),
24337 (
24338 "損",
24339 "Sǔn",
24340 "Decrease",
24341 "Sincerity; supreme fortune; persistence; favorable to undertake",
24342 "Mountain",
24343 "Lake",
24344 ),
24345 (
24346 "益",
24347 "Yì",
24348 "Increase",
24349 "Favorable to undertake and cross the great water",
24350 "Wind",
24351 "Thunder",
24352 ),
24353 (
24354 "夬",
24355 "Guài",
24356 "Breakthrough",
24357 "Proclaim at the king's court; sincerity in danger",
24358 "Lake",
24359 "Heaven",
24360 ),
24361 (
24362 "姤",
24363 "Gòu",
24364 "Coming to Meet",
24365 "The maiden is powerful; don't marry such a maiden",
24366 "Heaven",
24367 "Wind",
24368 ),
24369 (
24370 "萃",
24371 "Cuì",
24372 "Gathering",
24373 "Success; the king approaches his temple; see the great person",
24374 "Lake",
24375 "Earth",
24376 ),
24377 (
24378 "升",
24379 "Shēng",
24380 "Pushing Upward",
24381 "Supreme success; see the great person; don't worry",
24382 "Earth",
24383 "Wind",
24384 ),
24385 (
24386 "困",
24387 "Kùn",
24388 "Oppression",
24389 "Success; perseverance of the great person; no blame",
24390 "Lake",
24391 "Water",
24392 ),
24393 (
24394 "井",
24395 "Jǐng",
24396 "The Well",
24397 "The town may change but not the well",
24398 "Water",
24399 "Wind",
24400 ),
24401 (
24402 "革",
24403 "Gé",
24404 "Revolution",
24405 "On your own day you are believed; great success",
24406 "Lake",
24407 "Fire",
24408 ),
24409 (
24410 "鼎",
24411 "Dǐng",
24412 "The Cauldron",
24413 "Supreme good fortune; success",
24414 "Fire",
24415 "Wind",
24416 ),
24417 (
24418 "震",
24419 "Zhèn",
24420 "The Arousing",
24421 "Success; thunder comes with fright; laughing and talking after",
24422 "Thunder",
24423 "Thunder",
24424 ),
24425 (
24426 "艮",
24427 "Gèn",
24428 "Keeping Still",
24429 "Keep your back still; go into the courtyard without seeing anyone",
24430 "Mountain",
24431 "Mountain",
24432 ),
24433 (
24434 "漸",
24435 "Jiàn",
24436 "Development",
24437 "The maiden is given in marriage; good fortune; perseverance",
24438 "Wind",
24439 "Mountain",
24440 ),
24441 (
24442 "歸妹",
24443 "Guī Mèi",
24444 "The Marrying Maiden",
24445 "Undertakings bring misfortune",
24446 "Thunder",
24447 "Lake",
24448 ),
24449 (
24450 "豐",
24451 "Fēng",
24452 "Abundance",
24453 "Success; the king attains it; don't worry; be like the sun at noon",
24454 "Thunder",
24455 "Fire",
24456 ),
24457 (
24458 "旅",
24459 "Lǚ",
24460 "The Wanderer",
24461 "Success through smallness; perseverance brings fortune",
24462 "Fire",
24463 "Mountain",
24464 ),
24465 (
24466 "巽",
24467 "Xùn",
24468 "The Gentle",
24469 "Success through small things; favorable to have somewhere to go",
24470 "Wind",
24471 "Wind",
24472 ),
24473 (
24474 "兌",
24475 "Duì",
24476 "The Joyous",
24477 "Success; perseverance",
24478 "Lake",
24479 "Lake",
24480 ),
24481 (
24482 "渙",
24483 "Huàn",
24484 "Dispersion",
24485 "Success; the king approaches his temple; cross the great water",
24486 "Wind",
24487 "Water",
24488 ),
24489 (
24490 "節",
24491 "Jié",
24492 "Limitation",
24493 "Success; bitter limitation should not be persevered in",
24494 "Water",
24495 "Lake",
24496 ),
24497 (
24498 "中孚",
24499 "Zhōng Fú",
24500 "Inner Truth",
24501 "Pigs and fishes; good fortune; cross the great water",
24502 "Wind",
24503 "Lake",
24504 ),
24505 (
24506 "小過",
24507 "Xiǎo Guò",
24508 "Small Exceeding",
24509 "Success; perseverance; small things yes, great things no",
24510 "Thunder",
24511 "Mountain",
24512 ),
24513 (
24514 "既濟",
24515 "Jì Jì",
24516 "After Completion",
24517 "Success in small matters; perseverance; good at start, disorder at end",
24518 "Water",
24519 "Fire",
24520 ),
24521 (
24522 "未濟",
24523 "Wèi Jì",
24524 "Before Completion",
24525 "Success; the young fox almost across; tail gets wet; no goal",
24526 "Fire",
24527 "Water",
24528 ),
24529];
24530
24531fn binary_to_king_wen(binary: u8) -> u8 {
24532 const KING_WEN_ORDER: [u8; 64] = [
24535 1, 43, 14, 34, 9, 5, 26, 11, 10, 58, 38, 54, 61, 60, 41, 19, 13, 49, 30, 55, 37, 63, 22,
24536 36, 25, 17, 21, 51, 42, 3, 27, 24, 44, 28, 50, 32, 57, 48, 18, 46, 6, 47, 64, 40, 59, 29,
24537 4, 7, 33, 31, 56, 62, 53, 39, 52, 15, 12, 45, 35, 16, 20, 8, 23, 2,
24538 ];
24539 KING_WEN_ORDER[binary as usize] - 1
24540}
24541
24542fn hebrew_gematria(c: char) -> i64 {
24543 match c {
24544 'א' | 'A' | 'a' => 1,
24545 'ב' | 'B' | 'b' => 2,
24546 'ג' | 'G' | 'g' => 3,
24547 'ד' | 'D' | 'd' => 4,
24548 'ה' | 'H' | 'h' => 5,
24549 'ו' | 'V' | 'v' | 'W' | 'w' => 6,
24550 'ז' | 'Z' | 'z' => 7,
24551 'ח' => 8,
24552 'ט' => 9,
24553 'י' | 'Y' | 'y' | 'I' | 'i' | 'J' | 'j' => 10,
24554 'כ' | 'K' | 'k' => 20,
24555 'ל' | 'L' | 'l' => 30,
24556 'מ' | 'M' | 'm' => 40,
24557 'נ' | 'N' | 'n' => 50,
24558 'ס' | 'S' | 's' | 'X' | 'x' => 60,
24559 'ע' | 'O' | 'o' => 70,
24560 'פ' | 'P' | 'p' | 'F' | 'f' => 80,
24561 'צ' => 90,
24562 'ק' | 'Q' | 'q' => 100,
24563 'ר' | 'R' | 'r' => 200,
24564 'ש' => 300,
24565 'ת' | 'T' | 't' => 400,
24566 'ך' => 500, 'ם' => 600, 'ן' => 700, 'ף' => 800, 'ץ' | 'C' | 'c' => 900, 'E' | 'e' => 5, 'U' | 'u' => 6, _ => 0,
24574 }
24575}
24576
24577fn greek_isopsephy(c: char) -> i64 {
24578 match c {
24579 'Α' | 'α' | 'A' | 'a' => 1,
24580 'Β' | 'β' | 'B' | 'b' => 2,
24581 'Γ' | 'γ' | 'G' | 'g' => 3,
24582 'Δ' | 'δ' | 'D' | 'd' => 4,
24583 'Ε' | 'ε' | 'E' | 'e' => 5,
24584 'Ϛ' | 'ϛ' => 6, 'Ζ' | 'ζ' | 'Z' | 'z' => 7,
24586 'Η' | 'η' | 'H' | 'h' => 8,
24587 'Θ' | 'θ' => 9,
24588 'Ι' | 'ι' | 'I' | 'i' => 10,
24589 'Κ' | 'κ' | 'K' | 'k' => 20,
24590 'Λ' | 'λ' | 'L' | 'l' => 30,
24591 'Μ' | 'μ' | 'M' | 'm' => 40,
24592 'Ν' | 'ν' | 'N' | 'n' => 50,
24593 'Ξ' | 'ξ' | 'X' | 'x' => 60,
24594 'Ο' | 'ο' | 'O' | 'o' => 70,
24595 'Π' | 'π' | 'P' | 'p' => 80,
24596 'Ϙ' | 'ϙ' | 'Q' | 'q' => 90, 'Ρ' | 'ρ' | 'R' | 'r' => 100,
24598 'Σ' | 'σ' | 'ς' | 'S' | 's' => 200,
24599 'Τ' | 'τ' | 'T' | 't' => 300,
24600 'Υ' | 'υ' | 'U' | 'u' | 'Y' | 'y' => 400,
24601 'Φ' | 'φ' | 'F' | 'f' => 500,
24602 'Χ' | 'χ' | 'C' | 'c' => 600,
24603 'Ψ' | 'ψ' => 700,
24604 'Ω' | 'ω' | 'W' | 'w' => 800,
24605 'Ϡ' | 'ϡ' => 900, 'J' | 'j' => 10, 'V' | 'v' => 400, _ => 0,
24609 }
24610}
24611
24612fn arabic_abjad(c: char) -> i64 {
24613 match c {
24614 'ا' | 'أ' | 'إ' | 'آ' | 'A' | 'a' => 1,
24615 'ب' | 'B' | 'b' => 2,
24616 'ج' | 'J' | 'j' | 'G' | 'g' => 3,
24617 'د' | 'D' | 'd' => 4,
24618 'ه' | 'H' | 'h' => 5,
24619 'و' | 'W' | 'w' | 'V' | 'v' => 6,
24620 'ز' | 'Z' | 'z' => 7,
24621 'ح' => 8,
24622 'ط' => 9,
24623 'ي' | 'Y' | 'y' | 'I' | 'i' => 10,
24624 'ك' | 'K' | 'k' => 20,
24625 'ل' | 'L' | 'l' => 30,
24626 'م' | 'M' | 'm' => 40,
24627 'ن' | 'N' | 'n' => 50,
24628 'س' | 'S' | 's' => 60,
24629 'ع' | 'E' | 'e' => 70,
24630 'ف' | 'F' | 'f' => 80,
24631 'ص' => 90,
24632 'ق' | 'Q' | 'q' => 100,
24633 'ر' | 'R' | 'r' => 200,
24634 'ش' => 300,
24635 'ت' | 'T' | 't' => 400,
24636 'ث' => 500,
24637 'خ' | 'X' | 'x' => 600,
24638 'ذ' => 700,
24639 'ض' => 800,
24640 'ظ' => 900,
24641 'غ' => 1000,
24642 'C' | 'c' => 600, 'O' | 'o' => 70, 'P' | 'p' => 80, 'U' | 'u' => 6, _ => 0,
24647 }
24648}
24649
24650fn register_color(interp: &mut Interpreter) {
24657 define(interp, "rgb", Some(3), |_, args| {
24663 let r = match &args[0] {
24664 Value::Int(n) => (*n).clamp(0, 255) as u8,
24665 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
24666 _ => return Err(RuntimeError::new("rgb() requires numbers")),
24667 };
24668 let g = match &args[1] {
24669 Value::Int(n) => (*n).clamp(0, 255) as u8,
24670 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
24671 _ => return Err(RuntimeError::new("rgb() requires numbers")),
24672 };
24673 let b = match &args[2] {
24674 Value::Int(n) => (*n).clamp(0, 255) as u8,
24675 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
24676 _ => return Err(RuntimeError::new("rgb() requires numbers")),
24677 };
24678 let mut map = std::collections::HashMap::new();
24679 map.insert("r".to_string(), Value::Int(r as i64));
24680 map.insert("g".to_string(), Value::Int(g as i64));
24681 map.insert("b".to_string(), Value::Int(b as i64));
24682 map.insert(
24683 "hex".to_string(),
24684 Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))),
24685 );
24686 Ok(Value::Map(Rc::new(RefCell::new(map))))
24687 });
24688
24689 define(interp, "hex_to_rgb", Some(1), |_, args| {
24691 let hex = match &args[0] {
24692 Value::String(s) => s.to_string(),
24693 _ => return Err(RuntimeError::new("hex_to_rgb requires string")),
24694 };
24695 let hex = hex.trim_start_matches('#');
24696 if hex.len() != 6 {
24697 return Err(RuntimeError::new("hex_to_rgb requires 6 character hex"));
24698 }
24699 let r = u8::from_str_radix(&hex[0..2], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24700 let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24701 let b = u8::from_str_radix(&hex[4..6], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
24702 let mut map = std::collections::HashMap::new();
24703 map.insert("r".to_string(), Value::Int(r as i64));
24704 map.insert("g".to_string(), Value::Int(g as i64));
24705 map.insert("b".to_string(), Value::Int(b as i64));
24706 Ok(Value::Map(Rc::new(RefCell::new(map))))
24707 });
24708
24709 define(interp, "rgb_to_hsl", Some(3), |_, args| {
24711 let r = match &args[0] {
24712 Value::Int(n) => *n as f64 / 255.0,
24713 Value::Float(f) => *f / 255.0,
24714 _ => return Err(RuntimeError::new("requires numbers")),
24715 };
24716 let g = match &args[1] {
24717 Value::Int(n) => *n as f64 / 255.0,
24718 Value::Float(f) => *f / 255.0,
24719 _ => return Err(RuntimeError::new("requires numbers")),
24720 };
24721 let b = match &args[2] {
24722 Value::Int(n) => *n as f64 / 255.0,
24723 Value::Float(f) => *f / 255.0,
24724 _ => return Err(RuntimeError::new("requires numbers")),
24725 };
24726 let max = r.max(g).max(b);
24727 let min = r.min(g).min(b);
24728 let l = (max + min) / 2.0;
24729 let (h, s) = if max == min {
24730 (0.0, 0.0)
24731 } else {
24732 let d = max - min;
24733 let s = if l > 0.5 {
24734 d / (2.0 - max - min)
24735 } else {
24736 d / (max + min)
24737 };
24738 let h = if max == r {
24739 (g - b) / d + if g < b { 6.0 } else { 0.0 }
24740 } else if max == g {
24741 (b - r) / d + 2.0
24742 } else {
24743 (r - g) / d + 4.0
24744 };
24745 (h * 60.0, s)
24746 };
24747 let mut map = std::collections::HashMap::new();
24748 map.insert("h".to_string(), Value::Float(h));
24749 map.insert("s".to_string(), Value::Float(s));
24750 map.insert("l".to_string(), Value::Float(l));
24751 Ok(Value::Map(Rc::new(RefCell::new(map))))
24752 });
24753
24754 define(interp, "complementary", Some(3), |_, args| {
24756 let r = match &args[0] {
24757 Value::Int(n) => *n as u8,
24758 _ => return Err(RuntimeError::new("requires int")),
24759 };
24760 let g = match &args[1] {
24761 Value::Int(n) => *n as u8,
24762 _ => return Err(RuntimeError::new("requires int")),
24763 };
24764 let b = match &args[2] {
24765 Value::Int(n) => *n as u8,
24766 _ => return Err(RuntimeError::new("requires int")),
24767 };
24768 let mut map = std::collections::HashMap::new();
24769 map.insert("r".to_string(), Value::Int(255 - r as i64));
24770 map.insert("g".to_string(), Value::Int(255 - g as i64));
24771 map.insert("b".to_string(), Value::Int(255 - b as i64));
24772 Ok(Value::Map(Rc::new(RefCell::new(map))))
24773 });
24774
24775 define(interp, "wu_xing", Some(1), |_, args| {
24779 let element = match &args[0] {
24780 Value::String(s) => s.to_lowercase(),
24781 _ => return Err(RuntimeError::new("wu_xing requires string")),
24782 };
24783 let (name, chinese, color, hex, direction, season, organ, emotion, planet, animal) =
24784 match element.as_str() {
24785 "wood" | "mu" | "木" => (
24786 "Wood",
24787 "木 (Mù)",
24788 "Green/Azure",
24789 "#228B22",
24790 "East",
24791 "Spring",
24792 "Liver",
24793 "Anger",
24794 "Jupiter",
24795 "Azure Dragon",
24796 ),
24797 "fire" | "huo" | "火" => (
24798 "Fire",
24799 "火 (Huǒ)",
24800 "Red",
24801 "#FF0000",
24802 "South",
24803 "Summer",
24804 "Heart",
24805 "Joy",
24806 "Mars",
24807 "Vermilion Bird",
24808 ),
24809 "earth" | "tu" | "土" => (
24810 "Earth",
24811 "土 (Tǔ)",
24812 "Yellow",
24813 "#FFDB58",
24814 "Center",
24815 "Late Summer",
24816 "Spleen",
24817 "Worry",
24818 "Saturn",
24819 "Yellow Dragon",
24820 ),
24821 "metal" | "jin" | "金" => (
24822 "Metal",
24823 "金 (Jīn)",
24824 "White/Gold",
24825 "#FFD700",
24826 "West",
24827 "Autumn",
24828 "Lung",
24829 "Grief",
24830 "Venus",
24831 "White Tiger",
24832 ),
24833 "water" | "shui" | "水" => (
24834 "Water",
24835 "水 (Shuǐ)",
24836 "Black/Blue",
24837 "#000080",
24838 "North",
24839 "Winter",
24840 "Kidney",
24841 "Fear",
24842 "Mercury",
24843 "Black Tortoise",
24844 ),
24845 _ => {
24846 return Err(RuntimeError::new(
24847 "Unknown element. Use wood/fire/earth/metal/water",
24848 ))
24849 }
24850 };
24851 let mut map = std::collections::HashMap::new();
24852 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24853 map.insert(
24854 "chinese".to_string(),
24855 Value::String(Rc::new(chinese.to_string())),
24856 );
24857 map.insert(
24858 "color".to_string(),
24859 Value::String(Rc::new(color.to_string())),
24860 );
24861 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24862 map.insert(
24863 "direction".to_string(),
24864 Value::String(Rc::new(direction.to_string())),
24865 );
24866 map.insert(
24867 "season".to_string(),
24868 Value::String(Rc::new(season.to_string())),
24869 );
24870 map.insert(
24871 "organ".to_string(),
24872 Value::String(Rc::new(organ.to_string())),
24873 );
24874 map.insert(
24875 "emotion".to_string(),
24876 Value::String(Rc::new(emotion.to_string())),
24877 );
24878 map.insert(
24879 "planet".to_string(),
24880 Value::String(Rc::new(planet.to_string())),
24881 );
24882 map.insert(
24883 "guardian".to_string(),
24884 Value::String(Rc::new(animal.to_string())),
24885 );
24886 Ok(Value::Map(Rc::new(RefCell::new(map))))
24887 });
24888
24889 define(interp, "chakra_color", Some(1), |_, args| {
24893 let chakra = match &args[0] {
24894 Value::String(s) => s.to_lowercase(),
24895 Value::Int(n) => n.to_string(),
24896 _ => return Err(RuntimeError::new("chakra_color requires string or number")),
24897 };
24898 let (name, sanskrit, color, hex, location, freq, element, mantra) = match chakra.as_str() {
24899 "root" | "muladhara" | "1" => (
24900 "Root",
24901 "मूलाधार",
24902 "Red",
24903 "#FF0000",
24904 "Base of spine",
24905 396.0,
24906 "Earth",
24907 "LAM",
24908 ),
24909 "sacral" | "svadhisthana" | "2" => (
24910 "Sacral",
24911 "स्वाधिष्ठान",
24912 "Orange",
24913 "#FF7F00",
24914 "Below navel",
24915 417.0,
24916 "Water",
24917 "VAM",
24918 ),
24919 "solar" | "manipura" | "3" => (
24920 "Solar Plexus",
24921 "मणिपूर",
24922 "Yellow",
24923 "#FFFF00",
24924 "Stomach",
24925 528.0,
24926 "Fire",
24927 "RAM",
24928 ),
24929 "heart" | "anahata" | "4" => (
24930 "Heart",
24931 "अनाहत",
24932 "Green",
24933 "#00FF00",
24934 "Chest",
24935 639.0,
24936 "Air",
24937 "YAM",
24938 ),
24939 "throat" | "vishuddha" | "5" => (
24940 "Throat",
24941 "विशुद्ध",
24942 "Blue",
24943 "#00BFFF",
24944 "Throat",
24945 741.0,
24946 "Ether",
24947 "HAM",
24948 ),
24949 "third_eye" | "ajna" | "6" => (
24950 "Third Eye",
24951 "आज्ञा",
24952 "Indigo",
24953 "#4B0082",
24954 "Forehead",
24955 852.0,
24956 "Light",
24957 "OM",
24958 ),
24959 "crown" | "sahasrara" | "7" => (
24960 "Crown",
24961 "सहस्रार",
24962 "Violet",
24963 "#8B00FF",
24964 "Top of head",
24965 963.0,
24966 "Thought",
24967 "Silence",
24968 ),
24969 _ => {
24970 return Err(RuntimeError::new(
24971 "Unknown chakra. Use root/sacral/solar/heart/throat/third_eye/crown or 1-7",
24972 ))
24973 }
24974 };
24975 let mut map = std::collections::HashMap::new();
24976 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
24977 map.insert(
24978 "sanskrit".to_string(),
24979 Value::String(Rc::new(sanskrit.to_string())),
24980 );
24981 map.insert(
24982 "color".to_string(),
24983 Value::String(Rc::new(color.to_string())),
24984 );
24985 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
24986 map.insert(
24987 "location".to_string(),
24988 Value::String(Rc::new(location.to_string())),
24989 );
24990 map.insert("frequency_hz".to_string(), Value::Float(freq));
24991 map.insert(
24992 "element".to_string(),
24993 Value::String(Rc::new(element.to_string())),
24994 );
24995 map.insert(
24996 "mantra".to_string(),
24997 Value::String(Rc::new(mantra.to_string())),
24998 );
24999 Ok(Value::Map(Rc::new(RefCell::new(map))))
25000 });
25001
25002 define(interp, "maya_direction", Some(1), |_, args| {
25006 let dir = match &args[0] {
25007 Value::String(s) => s.to_lowercase(),
25008 _ => return Err(RuntimeError::new("requires string")),
25009 };
25010 let (direction, yucatec, color, hex, deity, meaning) = match dir.as_str() {
25011 "east" | "lakin" => (
25012 "East",
25013 "Lak'in",
25014 "Red",
25015 "#FF0000",
25016 "Chac (Red)",
25017 "Sunrise, new beginnings",
25018 ),
25019 "north" | "xaman" => (
25020 "North",
25021 "Xaman",
25022 "White",
25023 "#FFFFFF",
25024 "Chac (White)",
25025 "Ancestors, death",
25026 ),
25027 "west" | "chikin" => (
25028 "West",
25029 "Chik'in",
25030 "Black",
25031 "#000000",
25032 "Chac (Black)",
25033 "Sunset, completion",
25034 ),
25035 "south" | "nohol" => (
25036 "South",
25037 "Nohol",
25038 "Yellow",
25039 "#FFFF00",
25040 "Chac (Yellow)",
25041 "Maize, abundance",
25042 ),
25043 "center" | "yax" => (
25044 "Center",
25045 "Yax",
25046 "Green/Blue",
25047 "#00CED1",
25048 "World Tree",
25049 "Balance",
25050 ),
25051 _ => {
25052 return Err(RuntimeError::new(
25053 "Unknown direction. Use east/north/west/south/center",
25054 ))
25055 }
25056 };
25057 let mut map = std::collections::HashMap::new();
25058 map.insert(
25059 "direction".to_string(),
25060 Value::String(Rc::new(direction.to_string())),
25061 );
25062 map.insert(
25063 "yucatec".to_string(),
25064 Value::String(Rc::new(yucatec.to_string())),
25065 );
25066 map.insert(
25067 "color".to_string(),
25068 Value::String(Rc::new(color.to_string())),
25069 );
25070 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25071 map.insert(
25072 "deity".to_string(),
25073 Value::String(Rc::new(deity.to_string())),
25074 );
25075 map.insert(
25076 "meaning".to_string(),
25077 Value::String(Rc::new(meaning.to_string())),
25078 );
25079 Ok(Value::Map(Rc::new(RefCell::new(map))))
25080 });
25081
25082 define(interp, "orisha_color", Some(1), |_, args| {
25086 let orisha = match &args[0] {
25087 Value::String(s) => s.to_lowercase(),
25088 _ => return Err(RuntimeError::new("requires string")),
25089 };
25090 let (name, colors, hex, domain, day, number) = match orisha.as_str() {
25091 "obatala" | "oxala" => (
25092 "Obatalá",
25093 "White, silver",
25094 "#FFFFFF",
25095 "Creation, purity, wisdom",
25096 "Sunday",
25097 8,
25098 ),
25099 "yemoja" | "yemanja" => (
25100 "Yemọja",
25101 "Blue, white",
25102 "#4169E1",
25103 "Ocean, motherhood",
25104 "Saturday",
25105 7,
25106 ),
25107 "oshun" | "oxum" => (
25108 "Ọṣun",
25109 "Yellow, gold",
25110 "#FFD700",
25111 "Rivers, love, fertility",
25112 "Saturday",
25113 5,
25114 ),
25115 "shango" | "xango" => (
25116 "Ṣàngó",
25117 "Red, white",
25118 "#FF0000",
25119 "Thunder, fire, justice",
25120 "Wednesday",
25121 6,
25122 ),
25123 "ogun" | "ogum" => (
25124 "Ògún",
25125 "Green, black",
25126 "#006400",
25127 "Iron, war, labor",
25128 "Tuesday",
25129 7,
25130 ),
25131 "oya" | "iansa" => (
25132 "Ọya",
25133 "Brown, purple",
25134 "#800020",
25135 "Wind, storms, change",
25136 "Wednesday",
25137 9,
25138 ),
25139 "eshu" | "exu" => (
25140 "Èṣù",
25141 "Red, black",
25142 "#8B0000",
25143 "Crossroads, messages",
25144 "Monday",
25145 3,
25146 ),
25147 _ => {
25148 return Err(RuntimeError::new(
25149 "Unknown Orisha. Use obatala/yemoja/oshun/shango/ogun/oya/eshu",
25150 ))
25151 }
25152 };
25153 let mut map = std::collections::HashMap::new();
25154 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25155 map.insert(
25156 "colors".to_string(),
25157 Value::String(Rc::new(colors.to_string())),
25158 );
25159 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25160 map.insert(
25161 "domain".to_string(),
25162 Value::String(Rc::new(domain.to_string())),
25163 );
25164 map.insert("day".to_string(), Value::String(Rc::new(day.to_string())));
25165 map.insert("number".to_string(), Value::Int(number));
25166 Ok(Value::Map(Rc::new(RefCell::new(map))))
25167 });
25168
25169 define(interp, "nihon_iro", Some(1), |_, args| {
25173 let color = match &args[0] {
25174 Value::String(s) => s.to_lowercase(),
25175 _ => return Err(RuntimeError::new("requires string")),
25176 };
25177 let (name, japanese, hex, meaning, season) = match color.as_str() {
25178 "sakura" => (
25179 "Sakura Pink",
25180 "桜色",
25181 "#FFB7C5",
25182 "Cherry blossoms, transience",
25183 "Spring",
25184 ),
25185 "fuji" => (
25186 "Wisteria",
25187 "藤色",
25188 "#C9A0DC",
25189 "Elegance, nobility",
25190 "Spring",
25191 ),
25192 "moegi" => (
25193 "Young Green",
25194 "萌黄",
25195 "#AACF53",
25196 "New growth, freshness",
25197 "Spring",
25198 ),
25199 "ai" => ("Indigo", "藍色", "#004D99", "Protection, depth", "All"),
25200 "akane" => ("Madder Red", "茜色", "#CF3A24", "Sunset, passion", "Autumn"),
25201 "shiro" => ("White", "白", "#FFFFFF", "Purity, death, sacred", "Winter"),
25202 "kuro" => ("Black", "黒", "#000000", "Formality, mystery", "All"),
25203 "aka" => ("Red", "赤", "#D7003A", "Life force, celebration", "All"),
25204 "murasaki" => ("Purple", "紫", "#884898", "Nobility, spirituality", "All"),
25205 _ => {
25206 return Err(RuntimeError::new(
25207 "Unknown color. Try: sakura/fuji/moegi/ai/akane/shiro/kuro/aka/murasaki",
25208 ))
25209 }
25210 };
25211 let mut map = std::collections::HashMap::new();
25212 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25213 map.insert(
25214 "japanese".to_string(),
25215 Value::String(Rc::new(japanese.to_string())),
25216 );
25217 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25218 map.insert(
25219 "meaning".to_string(),
25220 Value::String(Rc::new(meaning.to_string())),
25221 );
25222 map.insert(
25223 "season".to_string(),
25224 Value::String(Rc::new(season.to_string())),
25225 );
25226 Ok(Value::Map(Rc::new(RefCell::new(map))))
25227 });
25228
25229 define(interp, "islamic_color", Some(1), |_, args| {
25233 let color = match &args[0] {
25234 Value::String(s) => s.to_lowercase(),
25235 _ => return Err(RuntimeError::new("requires string")),
25236 };
25237 let (name, arabic, hex, meaning, usage) = match color.as_str() {
25238 "green" | "akhdar" => (
25239 "Green",
25240 "أخضر",
25241 "#00FF00",
25242 "Paradise, Prophet, life",
25243 "Mosques, Quran, flags",
25244 ),
25245 "white" | "abyad" => (
25246 "White",
25247 "أبيض",
25248 "#FFFFFF",
25249 "Purity, peace, ihram",
25250 "Pilgrimage, burial",
25251 ),
25252 "black" | "aswad" => (
25253 "Black",
25254 "أسود",
25255 "#000000",
25256 "Modesty, Kaaba",
25257 "Kiswah, abaya",
25258 ),
25259 "gold" | "dhahabi" => (
25260 "Gold",
25261 "ذهبي",
25262 "#FFD700",
25263 "Paradise, divine light",
25264 "Calligraphy, decoration",
25265 ),
25266 "blue" | "azraq" => (
25267 "Blue",
25268 "أزرق",
25269 "#0000CD",
25270 "Protection, heaven",
25271 "Tiles, evil eye",
25272 ),
25273 _ => {
25274 return Err(RuntimeError::new(
25275 "Unknown color. Use green/white/black/gold/blue",
25276 ))
25277 }
25278 };
25279 let mut map = std::collections::HashMap::new();
25280 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25281 map.insert(
25282 "arabic".to_string(),
25283 Value::String(Rc::new(arabic.to_string())),
25284 );
25285 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25286 map.insert(
25287 "meaning".to_string(),
25288 Value::String(Rc::new(meaning.to_string())),
25289 );
25290 map.insert(
25291 "usage".to_string(),
25292 Value::String(Rc::new(usage.to_string())),
25293 );
25294 Ok(Value::Map(Rc::new(RefCell::new(map))))
25295 });
25296
25297 define(interp, "thai_day_color", Some(1), |_, args| {
25301 let day = match &args[0] {
25302 Value::String(s) => s.to_lowercase(),
25303 Value::Int(n) => n.to_string(),
25304 _ => return Err(RuntimeError::new("requires string or number")),
25305 };
25306 let (day_name, thai, color, hex, deity) = match day.as_str() {
25307 "sunday" | "0" => ("Sunday", "วันอาทิตย์", "Red", "#FF0000", "Surya"),
25308 "monday" | "1" => ("Monday", "วันจันทร์", "Yellow", "#FFFF00", "Chandra"),
25309 "tuesday" | "2" => ("Tuesday", "วันอังคาร", "Pink", "#FFC0CB", "Mangala"),
25310 "wednesday" | "3" => ("Wednesday", "วันพุธ", "Green", "#00FF00", "Budha"),
25311 "thursday" | "4" => ("Thursday", "วันพฤหัสบดี", "Orange", "#FFA500", "Brihaspati"),
25312 "friday" | "5" => ("Friday", "วันศุกร์", "Blue", "#00BFFF", "Shukra"),
25313 "saturday" | "6" => ("Saturday", "วันเสาร์", "Purple", "#800080", "Shani"),
25314 _ => return Err(RuntimeError::new("Unknown day. Use sunday-saturday or 0-6")),
25315 };
25316 let mut map = std::collections::HashMap::new();
25317 map.insert(
25318 "day".to_string(),
25319 Value::String(Rc::new(day_name.to_string())),
25320 );
25321 map.insert("thai".to_string(), Value::String(Rc::new(thai.to_string())));
25322 map.insert(
25323 "color".to_string(),
25324 Value::String(Rc::new(color.to_string())),
25325 );
25326 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25327 map.insert(
25328 "deity".to_string(),
25329 Value::String(Rc::new(deity.to_string())),
25330 );
25331 Ok(Value::Map(Rc::new(RefCell::new(map))))
25332 });
25333
25334 define(interp, "aboriginal_color", Some(1), |_, args| {
25338 let color = match &args[0] {
25339 Value::String(s) => s.to_lowercase(),
25340 _ => return Err(RuntimeError::new("requires string")),
25341 };
25342 let (name, hex, meaning, source, dreamtime) = match color.as_str() {
25343 "red" | "ochre" => (
25344 "Red Ochre",
25345 "#CC5500",
25346 "Earth, blood, ceremony",
25347 "Hematite",
25348 "Ancestral beings",
25349 ),
25350 "yellow" => (
25351 "Yellow Ochre",
25352 "#D4A017",
25353 "Sun, healing",
25354 "Limonite",
25355 "Sun's journey",
25356 ),
25357 "white" => (
25358 "White",
25359 "#FFFFFF",
25360 "Sky, spirits, mourning",
25361 "Kaolin",
25362 "Sky beings",
25363 ),
25364 "black" => (
25365 "Black",
25366 "#000000",
25367 "Night, formality",
25368 "Charcoal",
25369 "Night, men's business",
25370 ),
25371 "brown" => (
25372 "Brown",
25373 "#8B4513",
25374 "Earth, land",
25375 "Earth pigments",
25376 "Country, connection",
25377 ),
25378 _ => {
25379 return Err(RuntimeError::new(
25380 "Unknown color. Use red/yellow/white/black/brown",
25381 ))
25382 }
25383 };
25384 let mut map = std::collections::HashMap::new();
25385 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25386 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25387 map.insert(
25388 "meaning".to_string(),
25389 Value::String(Rc::new(meaning.to_string())),
25390 );
25391 map.insert(
25392 "source".to_string(),
25393 Value::String(Rc::new(source.to_string())),
25394 );
25395 map.insert(
25396 "dreamtime".to_string(),
25397 Value::String(Rc::new(dreamtime.to_string())),
25398 );
25399 Ok(Value::Map(Rc::new(RefCell::new(map))))
25400 });
25401
25402 define(interp, "celtic_color", Some(1), |_, args| {
25406 let color = match &args[0] {
25407 Value::String(s) => s.to_lowercase(),
25408 _ => return Err(RuntimeError::new("requires string")),
25409 };
25410 let (name, gaelic, hex, meaning, element) = match color.as_str() {
25411 "green" => (
25412 "Green",
25413 "Glas",
25414 "#228B22",
25415 "Nature, fairies, Otherworld",
25416 "Earth",
25417 ),
25418 "white" => ("White", "Bán", "#FFFFFF", "Purity, spirits", "Air"),
25419 "red" => ("Red", "Dearg", "#FF0000", "War, courage, blood", "Fire"),
25420 "black" => (
25421 "Black",
25422 "Dubh",
25423 "#000000",
25424 "Otherworld, death, rebirth",
25425 "Water",
25426 ),
25427 "gold" => ("Gold", "Órga", "#FFD700", "Sun, sovereignty, Lugh", "Fire"),
25428 "silver" => (
25429 "Silver",
25430 "Airgid",
25431 "#C0C0C0",
25432 "Moon, feminine, intuition",
25433 "Water",
25434 ),
25435 _ => {
25436 return Err(RuntimeError::new(
25437 "Unknown color. Use green/white/red/black/gold/silver",
25438 ))
25439 }
25440 };
25441 let mut map = std::collections::HashMap::new();
25442 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25443 map.insert(
25444 "gaelic".to_string(),
25445 Value::String(Rc::new(gaelic.to_string())),
25446 );
25447 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25448 map.insert(
25449 "meaning".to_string(),
25450 Value::String(Rc::new(meaning.to_string())),
25451 );
25452 map.insert(
25453 "element".to_string(),
25454 Value::String(Rc::new(element.to_string())),
25455 );
25456 Ok(Value::Map(Rc::new(RefCell::new(map))))
25457 });
25458
25459 define(interp, "kente_color", Some(1), |_, args| {
25463 let color = match &args[0] {
25464 Value::String(s) => s.to_lowercase(),
25465 _ => return Err(RuntimeError::new("requires string")),
25466 };
25467 let (name, twi, hex, meaning) = match color.as_str() {
25468 "gold" | "yellow" => ("Gold", "Sika Kɔkɔɔ", "#FFD700", "Royalty, wealth, glory"),
25469 "green" => ("Green", "Ahabammono", "#228B22", "Growth, renewal, harvest"),
25470 "blue" => ("Blue", "Bruu", "#0000CD", "Peace, harmony, love"),
25471 "red" => ("Red", "Kɔkɔɔ", "#FF0000", "Blood, sacrifice, power"),
25472 "black" => ("Black", "Tuntum", "#000000", "Maturation, ancestors"),
25473 "white" => ("White", "Fitaa", "#FFFFFF", "Purification, virtue, joy"),
25474 "maroon" => ("Maroon", "Borɔnɔ", "#800000", "Earth, healing, protection"),
25475 _ => {
25476 return Err(RuntimeError::new(
25477 "Unknown color. Use gold/green/blue/red/black/white/maroon",
25478 ))
25479 }
25480 };
25481 let mut map = std::collections::HashMap::new();
25482 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25483 map.insert("twi".to_string(), Value::String(Rc::new(twi.to_string())));
25484 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25485 map.insert(
25486 "meaning".to_string(),
25487 Value::String(Rc::new(meaning.to_string())),
25488 );
25489 Ok(Value::Map(Rc::new(RefCell::new(map))))
25490 });
25491
25492 define(interp, "hindu_color", Some(1), |_, args| {
25496 let color = match &args[0] {
25497 Value::String(s) => s.to_lowercase(),
25498 _ => return Err(RuntimeError::new("requires string")),
25499 };
25500 let (name, hindi, hex, meaning, deities) = match color.as_str() {
25501 "red" | "lal" => (
25502 "Red",
25503 "लाल",
25504 "#FF0000",
25505 "Purity, fertility, love",
25506 "Durga, Lakshmi",
25507 ),
25508 "orange" | "saffron" => (
25509 "Saffron",
25510 "केसरी",
25511 "#FF6600",
25512 "Sacred, renunciation",
25513 "Hanuman",
25514 ),
25515 "yellow" => (
25516 "Yellow",
25517 "पीला",
25518 "#FFFF00",
25519 "Knowledge, learning",
25520 "Vishnu, Saraswati",
25521 ),
25522 "green" => ("Green", "हरा", "#008000", "Life, happiness", "Krishna"),
25523 "white" => (
25524 "White",
25525 "सफ़ेद",
25526 "#FFFFFF",
25527 "Purity, mourning",
25528 "Saraswati, Shiva",
25529 ),
25530 "blue" => (
25531 "Blue",
25532 "नीला",
25533 "#0000FF",
25534 "Divinity, infinity",
25535 "Krishna, Vishnu",
25536 ),
25537 "black" => (
25538 "Black",
25539 "काला",
25540 "#000000",
25541 "Protection from evil",
25542 "Kali, Shani",
25543 ),
25544 _ => {
25545 return Err(RuntimeError::new(
25546 "Unknown color. Use red/orange/yellow/green/white/blue/black",
25547 ))
25548 }
25549 };
25550 let mut map = std::collections::HashMap::new();
25551 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25552 map.insert(
25553 "hindi".to_string(),
25554 Value::String(Rc::new(hindi.to_string())),
25555 );
25556 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25557 map.insert(
25558 "meaning".to_string(),
25559 Value::String(Rc::new(meaning.to_string())),
25560 );
25561 map.insert(
25562 "deities".to_string(),
25563 Value::String(Rc::new(deities.to_string())),
25564 );
25565 Ok(Value::Map(Rc::new(RefCell::new(map))))
25566 });
25567
25568 define(interp, "emotion_color", Some(2), |_, args| {
25572 let emotion = match &args[0] {
25573 Value::String(s) => s.to_lowercase(),
25574 _ => return Err(RuntimeError::new("requires string")),
25575 };
25576 let culture = match &args[1] {
25577 Value::String(s) => s.to_lowercase(),
25578 _ => return Err(RuntimeError::new("requires string")),
25579 };
25580 let (hex, name, reasoning) = match (emotion.as_str(), culture.as_str()) {
25581 ("joy", "western") | ("happy", "western") => {
25582 ("#FFD700", "Gold", "Sunshine = happiness")
25583 }
25584 ("joy", "chinese") | ("happy", "chinese") => ("#FF0000", "Red", "红 = luck, joy"),
25585 ("joy", "japanese") => ("#FFB7C5", "Sakura", "Cherry blossom = fleeting joy"),
25586 ("sadness", "western") | ("sad", "western") => ("#0000CD", "Blue", "'Feeling blue'"),
25587 ("sadness", "chinese") | ("sad", "chinese") => ("#FFFFFF", "White", "白 = mourning"),
25588 ("sadness", "indian") => ("#FFFFFF", "White", "सफ़ेद = mourning"),
25589 ("anger", _) => ("#FF0000", "Red", "Universal heat/fire"),
25590 ("love", "western") => ("#FF69B4", "Pink", "Valentine's hearts"),
25591 ("love", "chinese") | ("love", "indian") => ("#FF0000", "Red", "Red = marriage, love"),
25592 ("peace", "western") => ("#ADD8E6", "Light Blue", "Sky, serenity"),
25593 ("peace", "islamic") => ("#00FF00", "Green", "السلام = paradise"),
25594 ("fear", _) => ("#4B0082", "Indigo", "Deep, mysterious"),
25595 (_, _) => ("#808080", "Grey", "Neutral"),
25596 };
25597 let r = u8::from_str_radix(&hex[1..3], 16).unwrap_or(128);
25598 let g = u8::from_str_radix(&hex[3..5], 16).unwrap_or(128);
25599 let b = u8::from_str_radix(&hex[5..7], 16).unwrap_or(128);
25600 let mut map = std::collections::HashMap::new();
25601 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
25602 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
25603 map.insert("r".to_string(), Value::Int(r as i64));
25604 map.insert("g".to_string(), Value::Int(g as i64));
25605 map.insert("b".to_string(), Value::Int(b as i64));
25606 map.insert(
25607 "reasoning".to_string(),
25608 Value::String(Rc::new(reasoning.to_string())),
25609 );
25610 Ok(Value::Map(Rc::new(RefCell::new(map))))
25611 });
25612
25613 define(interp, "synesthesia", Some(2), |_, args| {
25615 let culture = match &args[1] {
25616 Value::String(s) => s.to_lowercase(),
25617 _ => return Err(RuntimeError::new("requires culture string")),
25618 };
25619 let (r, g, b, emotion, freq) = match &args[0] {
25620 Value::String(s) => match s.to_lowercase().as_str() {
25621 "joy" | "happy" => (255u8, 215u8, 0u8, "joy", 528.0),
25622 "sadness" | "sad" => (0, 0, 139, "sadness", 396.0),
25623 "anger" => (255, 0, 0, "anger", 417.0),
25624 "fear" => (75, 0, 130, "fear", 369.0),
25625 "love" => (255, 105, 180, "love", 639.0),
25626 "peace" => (135, 206, 235, "peace", 741.0),
25627 _ => (128, 128, 128, "neutral", 432.0),
25628 },
25629 Value::Int(n) => (128, 128, 255, "resonance", *n as f64),
25630 Value::Float(f) => (128, 128, 255, "resonance", *f),
25631 _ => (128, 128, 128, "neutral", 432.0),
25632 };
25633 let cultural_meaning = match culture.as_str() {
25634 "chinese" if r > 200 && g < 100 => "luck/joy (红)",
25635 "japanese" if r > 200 && g < 100 => "vitality (赤)",
25636 "indian" if r > 200 && g < 100 => "shakti/auspicious",
25637 _ => "universal resonance",
25638 };
25639 let chakra = if r > 200 && g < 100 {
25640 "Root"
25641 } else if g > 200 {
25642 "Heart"
25643 } else if b > 200 {
25644 "Throat"
25645 } else {
25646 "Crown"
25647 };
25648 let wu_xing = if r > 200 && g < 100 {
25649 "Fire (火)"
25650 } else if g > 200 {
25651 "Wood (木)"
25652 } else if b > 200 {
25653 "Water (水)"
25654 } else {
25655 "Metal (金)"
25656 };
25657 let mut map = std::collections::HashMap::new();
25658 let mut color_map = std::collections::HashMap::new();
25659 color_map.insert("r".to_string(), Value::Int(r as i64));
25660 color_map.insert("g".to_string(), Value::Int(g as i64));
25661 color_map.insert("b".to_string(), Value::Int(b as i64));
25662 color_map.insert(
25663 "hex".to_string(),
25664 Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))),
25665 );
25666 map.insert(
25667 "color".to_string(),
25668 Value::Map(Rc::new(RefCell::new(color_map))),
25669 );
25670 map.insert(
25671 "emotion".to_string(),
25672 Value::String(Rc::new(emotion.to_string())),
25673 );
25674 map.insert("frequency".to_string(), Value::Float(freq));
25675 map.insert(
25676 "cultural_meaning".to_string(),
25677 Value::String(Rc::new(cultural_meaning.to_string())),
25678 );
25679 map.insert(
25680 "chakra".to_string(),
25681 Value::String(Rc::new(chakra.to_string())),
25682 );
25683 map.insert(
25684 "wu_xing".to_string(),
25685 Value::String(Rc::new(wu_xing.to_string())),
25686 );
25687 Ok(Value::Map(Rc::new(RefCell::new(map))))
25688 });
25689
25690 define(interp, "color_to_sound", Some(3), |_, args| {
25692 let r = match &args[0] {
25693 Value::Int(n) => *n as f64 / 255.0,
25694 Value::Float(f) => *f / 255.0,
25695 _ => return Err(RuntimeError::new("requires numbers")),
25696 };
25697 let g = match &args[1] {
25698 Value::Int(n) => *n as f64 / 255.0,
25699 Value::Float(f) => *f / 255.0,
25700 _ => return Err(RuntimeError::new("requires numbers")),
25701 };
25702 let b = match &args[2] {
25703 Value::Int(n) => *n as f64 / 255.0,
25704 Value::Float(f) => *f / 255.0,
25705 _ => return Err(RuntimeError::new("requires numbers")),
25706 };
25707 let max = r.max(g).max(b);
25708 let min = r.min(g).min(b);
25709 let l = (max + min) / 2.0;
25710 let h = if max == min {
25711 0.0
25712 } else {
25713 let d = max - min;
25714 if max == r {
25715 (g - b) / d + if g < b { 6.0 } else { 0.0 }
25716 } else if max == g {
25717 (b - r) / d + 2.0
25718 } else {
25719 (r - g) / d + 4.0
25720 }
25721 } * 60.0;
25722 let (note, freq) = if h < 30.0 {
25723 ("C", 261.63)
25724 } else if h < 60.0 {
25725 ("G", 392.00)
25726 } else if h < 90.0 {
25727 ("D", 293.66)
25728 } else if h < 120.0 {
25729 ("A", 440.00)
25730 } else if h < 150.0 {
25731 ("E", 329.63)
25732 } else if h < 180.0 {
25733 ("B", 493.88)
25734 } else if h < 210.0 {
25735 ("F#", 369.99)
25736 } else if h < 240.0 {
25737 ("Db", 277.18)
25738 } else if h < 270.0 {
25739 ("Ab", 415.30)
25740 } else if h < 300.0 {
25741 ("Eb", 311.13)
25742 } else if h < 330.0 {
25743 ("Bb", 466.16)
25744 } else {
25745 ("F", 349.23)
25746 };
25747 let octave_shift = ((l - 0.5) * 4.0).round() as i32;
25748 let adjusted_freq = freq * 2.0_f64.powi(octave_shift);
25749 let mut map = std::collections::HashMap::new();
25750 map.insert("note".to_string(), Value::String(Rc::new(note.to_string())));
25751 map.insert("frequency".to_string(), Value::Float(adjusted_freq));
25752 map.insert("hue".to_string(), Value::Float(h));
25753 Ok(Value::Map(Rc::new(RefCell::new(map))))
25754 });
25755
25756 define(interp, "contrast_ratio", Some(6), |_, args| {
25758 fn lum(r: f64, g: f64, b: f64) -> f64 {
25759 fn ch(c: f64) -> f64 {
25760 let c = c / 255.0;
25761 if c <= 0.03928 {
25762 c / 12.92
25763 } else {
25764 ((c + 0.055) / 1.055).powf(2.4)
25765 }
25766 }
25767 0.2126 * ch(r) + 0.7152 * ch(g) + 0.0722 * ch(b)
25768 }
25769 let r1 = match &args[0] {
25770 Value::Int(n) => *n as f64,
25771 Value::Float(f) => *f,
25772 _ => return Err(RuntimeError::new("requires numbers")),
25773 };
25774 let g1 = match &args[1] {
25775 Value::Int(n) => *n as f64,
25776 Value::Float(f) => *f,
25777 _ => return Err(RuntimeError::new("requires numbers")),
25778 };
25779 let b1 = match &args[2] {
25780 Value::Int(n) => *n as f64,
25781 Value::Float(f) => *f,
25782 _ => return Err(RuntimeError::new("requires numbers")),
25783 };
25784 let r2 = match &args[3] {
25785 Value::Int(n) => *n as f64,
25786 Value::Float(f) => *f,
25787 _ => return Err(RuntimeError::new("requires numbers")),
25788 };
25789 let g2 = match &args[4] {
25790 Value::Int(n) => *n as f64,
25791 Value::Float(f) => *f,
25792 _ => return Err(RuntimeError::new("requires numbers")),
25793 };
25794 let b2 = match &args[5] {
25795 Value::Int(n) => *n as f64,
25796 Value::Float(f) => *f,
25797 _ => return Err(RuntimeError::new("requires numbers")),
25798 };
25799 let l1 = lum(r1, g1, b1);
25800 let l2 = lum(r2, g2, b2);
25801 let ratio = if l1 > l2 {
25802 (l1 + 0.05) / (l2 + 0.05)
25803 } else {
25804 (l2 + 0.05) / (l1 + 0.05)
25805 };
25806 let mut map = std::collections::HashMap::new();
25807 map.insert("ratio".to_string(), Value::Float(ratio));
25808 map.insert("aa_normal".to_string(), Value::Bool(ratio >= 4.5));
25809 map.insert("aa_large".to_string(), Value::Bool(ratio >= 3.0));
25810 map.insert("aaa_normal".to_string(), Value::Bool(ratio >= 7.0));
25811 Ok(Value::Map(Rc::new(RefCell::new(map))))
25812 });
25813}
25814
25815fn register_protocol(interp: &mut Interpreter) {
25823 define(interp, "protocol_info", Some(0), |_, _args| {
25825 let mut map = std::collections::HashMap::new();
25826 map.insert(
25827 "http".to_string(),
25828 Value::Map(Rc::new(RefCell::new({
25829 let mut m = std::collections::HashMap::new();
25830 m.insert(
25831 "name".to_string(),
25832 Value::String(Rc::new("HTTP".to_string())),
25833 );
25834 m.insert(
25835 "versions".to_string(),
25836 Value::Array(Rc::new(RefCell::new(vec![
25837 Value::String(Rc::new("1.1".to_string())),
25838 Value::String(Rc::new("2".to_string())),
25839 ]))),
25840 );
25841 m.insert(
25842 "methods".to_string(),
25843 Value::Array(Rc::new(RefCell::new(vec![
25844 Value::String(Rc::new("GET".to_string())),
25845 Value::String(Rc::new("POST".to_string())),
25846 Value::String(Rc::new("PUT".to_string())),
25847 Value::String(Rc::new("DELETE".to_string())),
25848 Value::String(Rc::new("PATCH".to_string())),
25849 Value::String(Rc::new("HEAD".to_string())),
25850 Value::String(Rc::new("OPTIONS".to_string())),
25851 ]))),
25852 );
25853 m
25854 }))),
25855 );
25856 map.insert(
25857 "grpc".to_string(),
25858 Value::Map(Rc::new(RefCell::new({
25859 let mut m = std::collections::HashMap::new();
25860 m.insert(
25861 "name".to_string(),
25862 Value::String(Rc::new("gRPC".to_string())),
25863 );
25864 m.insert(
25865 "streaming_modes".to_string(),
25866 Value::Array(Rc::new(RefCell::new(vec![
25867 Value::String(Rc::new("unary".to_string())),
25868 Value::String(Rc::new("server_streaming".to_string())),
25869 Value::String(Rc::new("client_streaming".to_string())),
25870 Value::String(Rc::new("bidirectional".to_string())),
25871 ]))),
25872 );
25873 m
25874 }))),
25875 );
25876 map.insert(
25877 "websocket".to_string(),
25878 Value::Map(Rc::new(RefCell::new({
25879 let mut m = std::collections::HashMap::new();
25880 m.insert(
25881 "name".to_string(),
25882 Value::String(Rc::new("WebSocket".to_string())),
25883 );
25884 m.insert(
25885 "message_types".to_string(),
25886 Value::Array(Rc::new(RefCell::new(vec![
25887 Value::String(Rc::new("text".to_string())),
25888 Value::String(Rc::new("binary".to_string())),
25889 Value::String(Rc::new("ping".to_string())),
25890 Value::String(Rc::new("pong".to_string())),
25891 Value::String(Rc::new("close".to_string())),
25892 ]))),
25893 );
25894 m
25895 }))),
25896 );
25897 map.insert(
25898 "kafka".to_string(),
25899 Value::Map(Rc::new(RefCell::new({
25900 let mut m = std::collections::HashMap::new();
25901 m.insert(
25902 "name".to_string(),
25903 Value::String(Rc::new("Apache Kafka".to_string())),
25904 );
25905 m.insert(
25906 "acks".to_string(),
25907 Value::Array(Rc::new(RefCell::new(vec![
25908 Value::String(Rc::new("none".to_string())),
25909 Value::String(Rc::new("leader".to_string())),
25910 Value::String(Rc::new("all".to_string())),
25911 ]))),
25912 );
25913 m
25914 }))),
25915 );
25916 map.insert(
25917 "amqp".to_string(),
25918 Value::Map(Rc::new(RefCell::new({
25919 let mut m = std::collections::HashMap::new();
25920 m.insert(
25921 "name".to_string(),
25922 Value::String(Rc::new("AMQP".to_string())),
25923 );
25924 m.insert(
25925 "exchange_types".to_string(),
25926 Value::Array(Rc::new(RefCell::new(vec![
25927 Value::String(Rc::new("direct".to_string())),
25928 Value::String(Rc::new("fanout".to_string())),
25929 Value::String(Rc::new("topic".to_string())),
25930 Value::String(Rc::new("headers".to_string())),
25931 ]))),
25932 );
25933 m
25934 }))),
25935 );
25936 map.insert(
25937 "graphql".to_string(),
25938 Value::Map(Rc::new(RefCell::new({
25939 let mut m = std::collections::HashMap::new();
25940 m.insert(
25941 "name".to_string(),
25942 Value::String(Rc::new("GraphQL".to_string())),
25943 );
25944 m.insert(
25945 "operations".to_string(),
25946 Value::Array(Rc::new(RefCell::new(vec![
25947 Value::String(Rc::new("query".to_string())),
25948 Value::String(Rc::new("mutation".to_string())),
25949 Value::String(Rc::new("subscription".to_string())),
25950 ]))),
25951 );
25952 m
25953 }))),
25954 );
25955 Ok(Value::Map(Rc::new(RefCell::new(map))))
25956 });
25957
25958 define(interp, "http_status_text", Some(1), |_, args| {
25960 let code = match &args[0] {
25961 Value::Int(n) => *n,
25962 _ => {
25963 return Err(RuntimeError::new(
25964 "http_status_text requires integer status code",
25965 ))
25966 }
25967 };
25968 let text = match code {
25969 100 => "Continue",
25970 101 => "Switching Protocols",
25971 200 => "OK",
25972 201 => "Created",
25973 202 => "Accepted",
25974 204 => "No Content",
25975 301 => "Moved Permanently",
25976 302 => "Found",
25977 304 => "Not Modified",
25978 307 => "Temporary Redirect",
25979 308 => "Permanent Redirect",
25980 400 => "Bad Request",
25981 401 => "Unauthorized",
25982 403 => "Forbidden",
25983 404 => "Not Found",
25984 405 => "Method Not Allowed",
25985 409 => "Conflict",
25986 422 => "Unprocessable Entity",
25987 429 => "Too Many Requests",
25988 500 => "Internal Server Error",
25989 502 => "Bad Gateway",
25990 503 => "Service Unavailable",
25991 504 => "Gateway Timeout",
25992 _ => "Unknown",
25993 };
25994 Ok(Value::String(Rc::new(text.to_string())))
25995 });
25996
25997 define(interp, "http_status_type", Some(1), |_, args| {
25999 let code = match &args[0] {
26000 Value::Int(n) => *n,
26001 _ => {
26002 return Err(RuntimeError::new(
26003 "http_status_type requires integer status code",
26004 ))
26005 }
26006 };
26007 let status_type = match code {
26008 100..=199 => "informational",
26009 200..=299 => "success",
26010 300..=399 => "redirect",
26011 400..=499 => "client_error",
26012 500..=599 => "server_error",
26013 _ => "unknown",
26014 };
26015 Ok(Value::String(Rc::new(status_type.to_string())))
26016 });
26017
26018 define(interp, "grpc_status_text", Some(1), |_, args| {
26020 let code = match &args[0] {
26021 Value::Int(n) => *n,
26022 _ => {
26023 return Err(RuntimeError::new(
26024 "grpc_status_text requires integer status code",
26025 ))
26026 }
26027 };
26028 let text = match code {
26029 0 => "OK",
26030 1 => "CANCELLED",
26031 2 => "UNKNOWN",
26032 3 => "INVALID_ARGUMENT",
26033 4 => "DEADLINE_EXCEEDED",
26034 5 => "NOT_FOUND",
26035 6 => "ALREADY_EXISTS",
26036 7 => "PERMISSION_DENIED",
26037 8 => "RESOURCE_EXHAUSTED",
26038 9 => "FAILED_PRECONDITION",
26039 10 => "ABORTED",
26040 11 => "OUT_OF_RANGE",
26041 12 => "UNIMPLEMENTED",
26042 13 => "INTERNAL",
26043 14 => "UNAVAILABLE",
26044 15 => "DATA_LOSS",
26045 16 => "UNAUTHENTICATED",
26046 _ => "UNKNOWN",
26047 };
26048 Ok(Value::String(Rc::new(text.to_string())))
26049 });
26050
26051 define(interp, "url_parse", Some(1), |_, args| {
26053 let url_str = match &args[0] {
26054 Value::String(s) => s.as_str().to_string(),
26055 _ => return Err(RuntimeError::new("url_parse requires string URL")),
26056 };
26057
26058 let mut map = std::collections::HashMap::new();
26060
26061 let (scheme, rest) = if let Some(pos) = url_str.find("://") {
26063 (url_str[..pos].to_string(), &url_str[pos + 3..])
26064 } else {
26065 return Err(RuntimeError::new("Invalid URL: missing scheme"));
26066 };
26067 map.insert("scheme".to_string(), Value::String(Rc::new(scheme)));
26068
26069 let (authority, path_and_rest) = if let Some(pos) = rest.find('/') {
26071 (&rest[..pos], &rest[pos..])
26072 } else {
26073 (rest, "/")
26074 };
26075
26076 let (host, port) = if let Some(pos) = authority.rfind(':') {
26078 if let Ok(p) = authority[pos + 1..].parse::<i64>() {
26079 (authority[..pos].to_string(), Some(p))
26080 } else {
26081 (authority.to_string(), None)
26082 }
26083 } else {
26084 (authority.to_string(), None)
26085 };
26086 map.insert("host".to_string(), Value::String(Rc::new(host)));
26087 map.insert(
26088 "port".to_string(),
26089 port.map(Value::Int).unwrap_or(Value::Null),
26090 );
26091
26092 let (path, query) = if let Some(pos) = path_and_rest.find('?') {
26094 (&path_and_rest[..pos], Some(&path_and_rest[pos + 1..]))
26095 } else {
26096 (path_and_rest, None)
26097 };
26098 map.insert("path".to_string(), Value::String(Rc::new(path.to_string())));
26099 map.insert(
26100 "query".to_string(),
26101 query
26102 .map(|q| Value::String(Rc::new(q.to_string())))
26103 .unwrap_or(Value::Null),
26104 );
26105
26106 Ok(Value::Map(Rc::new(RefCell::new(map))))
26107 });
26108
26109 define(interp, "url_encode", Some(1), |_, args| {
26111 let s = match &args[0] {
26112 Value::String(s) => s.as_str(),
26113 _ => return Err(RuntimeError::new("url_encode requires string")),
26114 };
26115 let mut result = String::new();
26116 for c in s.chars() {
26117 match c {
26118 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '~' => {
26119 result.push(c);
26120 }
26121 ' ' => result.push_str("%20"),
26122 _ => {
26123 for b in c.to_string().as_bytes() {
26124 result.push_str(&format!("%{:02X}", b));
26125 }
26126 }
26127 }
26128 }
26129 Ok(Value::String(Rc::new(result)))
26130 });
26131
26132 define(interp, "url_decode", Some(1), |_, args| {
26134 let s = match &args[0] {
26135 Value::String(s) => s.as_str().to_string(),
26136 _ => return Err(RuntimeError::new("url_decode requires string")),
26137 };
26138 let mut result = Vec::new();
26139 let bytes = s.as_bytes();
26140 let mut i = 0;
26141 while i < bytes.len() {
26142 if bytes[i] == b'%' && i + 2 < bytes.len() {
26143 if let Ok(b) =
26144 u8::from_str_radix(&String::from_utf8_lossy(&bytes[i + 1..i + 3]), 16)
26145 {
26146 result.push(b);
26147 i += 3;
26148 continue;
26149 }
26150 } else if bytes[i] == b'+' {
26151 result.push(b' ');
26152 i += 1;
26153 continue;
26154 }
26155 result.push(bytes[i]);
26156 i += 1;
26157 }
26158 Ok(Value::String(Rc::new(
26159 String::from_utf8_lossy(&result).to_string(),
26160 )))
26161 });
26162
26163 define(interp, "ws_close_code_text", Some(1), |_, args| {
26165 let code = match &args[0] {
26166 Value::Int(n) => *n,
26167 _ => {
26168 return Err(RuntimeError::new(
26169 "ws_close_code_text requires integer code",
26170 ))
26171 }
26172 };
26173 let text = match code {
26174 1000 => "Normal Closure",
26175 1001 => "Going Away",
26176 1002 => "Protocol Error",
26177 1003 => "Unsupported Data",
26178 1005 => "No Status Received",
26179 1006 => "Abnormal Closure",
26180 1007 => "Invalid Payload Data",
26181 1008 => "Policy Violation",
26182 1009 => "Message Too Big",
26183 1010 => "Missing Extension",
26184 1011 => "Internal Error",
26185 1015 => "TLS Handshake Failure",
26186 _ => "Unknown",
26187 };
26188 Ok(Value::String(Rc::new(text.to_string())))
26189 });
26190
26191 define(interp, "mime_type", Some(1), |_, args| {
26193 let ext = match &args[0] {
26194 Value::String(s) => s.as_str().to_lowercase(),
26195 _ => return Err(RuntimeError::new("mime_type requires string extension")),
26196 };
26197 let ext = ext.trim_start_matches('.');
26198 let mime = match ext {
26199 "html" | "htm" => "text/html",
26200 "css" => "text/css",
26201 "js" | "mjs" => "text/javascript",
26202 "json" => "application/json",
26203 "xml" => "application/xml",
26204 "txt" => "text/plain",
26205 "csv" => "text/csv",
26206 "png" => "image/png",
26207 "jpg" | "jpeg" => "image/jpeg",
26208 "gif" => "image/gif",
26209 "svg" => "image/svg+xml",
26210 "webp" => "image/webp",
26211 "ico" => "image/x-icon",
26212 "pdf" => "application/pdf",
26213 "zip" => "application/zip",
26214 "gz" | "gzip" => "application/gzip",
26215 "mp3" => "audio/mpeg",
26216 "mp4" => "video/mp4",
26217 "webm" => "video/webm",
26218 "woff" => "font/woff",
26219 "woff2" => "font/woff2",
26220 "ttf" => "font/ttf",
26221 "otf" => "font/otf",
26222 "wasm" => "application/wasm",
26223 "proto" => "application/protobuf",
26224 _ => "application/octet-stream",
26225 };
26226 Ok(Value::String(Rc::new(mime.to_string())))
26227 });
26228
26229 define(interp, "content_type_parse", Some(1), |_, args| {
26231 let ct = match &args[0] {
26232 Value::String(s) => s.as_str().to_string(),
26233 _ => return Err(RuntimeError::new("content_type_parse requires string")),
26234 };
26235 let mut map = std::collections::HashMap::new();
26236 let parts: Vec<&str> = ct.split(';').collect();
26237 map.insert(
26238 "media_type".to_string(),
26239 Value::String(Rc::new(parts[0].trim().to_string())),
26240 );
26241
26242 let mut params = std::collections::HashMap::new();
26243 for part in parts.iter().skip(1) {
26244 let kv: Vec<&str> = part.splitn(2, '=').collect();
26245 if kv.len() == 2 {
26246 let key = kv[0].trim().to_lowercase();
26247 let value = kv[1].trim().trim_matches('"').to_string();
26248 params.insert(key, Value::String(Rc::new(value)));
26249 }
26250 }
26251 map.insert(
26252 "params".to_string(),
26253 Value::Map(Rc::new(RefCell::new(params))),
26254 );
26255 Ok(Value::Map(Rc::new(RefCell::new(map))))
26256 });
26257}
26258
26259thread_local! {
26271 static TOOL_REGISTRY: RefCell<HashMap<String, ToolDefinition>> = RefCell::new(HashMap::new());
26272 static AGENT_MEMORY: RefCell<HashMap<String, AgentSession>> = RefCell::new(HashMap::new());
26273 static STATE_MACHINES: RefCell<HashMap<String, StateMachine>> = RefCell::new(HashMap::new());
26274}
26275
26276#[derive(Clone)]
26278struct ToolDefinition {
26279 name: String,
26280 description: String,
26281 parameters: Vec<ToolParameter>,
26282 returns: String,
26283 evidence_in: Evidence,
26284 evidence_out: Evidence,
26285 handler: Option<Rc<Function>>,
26286}
26287
26288#[derive(Clone)]
26289struct ToolParameter {
26290 name: String,
26291 param_type: String,
26292 description: String,
26293 required: bool,
26294 evidence: Evidence,
26295}
26296
26297#[derive(Clone)]
26299struct AgentSession {
26300 id: String,
26301 context: HashMap<String, Value>,
26302 history: Vec<(String, String)>, created_at: u64,
26304 last_accessed: u64,
26305}
26306
26307#[derive(Clone)]
26309struct StateMachine {
26310 name: String,
26311 current_state: String,
26312 states: Vec<String>,
26313 transitions: HashMap<String, Vec<(String, String)>>, history: Vec<(String, u64)>, }
26316
26317fn register_agent_tools(interp: &mut Interpreter) {
26322 define(interp, "tool_define", None, |_interp, args| {
26325 if args.len() < 4 {
26326 return Err(RuntimeError::new(
26327 "tool_define requires at least 4 arguments: name, description, params, returns",
26328 ));
26329 }
26330
26331 let name = match &args[0] {
26332 Value::String(s) => s.as_str().to_string(),
26333 _ => return Err(RuntimeError::new("tool name must be a string")),
26334 };
26335
26336 let description = match &args[1] {
26337 Value::String(s) => s.as_str().to_string(),
26338 _ => return Err(RuntimeError::new("tool description must be a string")),
26339 };
26340
26341 let params = match &args[2] {
26343 Value::Array(arr) => {
26344 let arr = arr.borrow();
26345 let mut params = Vec::new();
26346 for param in arr.iter() {
26347 if let Value::Map(map) = param {
26348 let map = map.borrow();
26349 let param_name = map
26350 .get("name")
26351 .and_then(|v| {
26352 if let Value::String(s) = v {
26353 Some(s.as_str().to_string())
26354 } else {
26355 None
26356 }
26357 })
26358 .unwrap_or_default();
26359 let param_type = map
26360 .get("type")
26361 .and_then(|v| {
26362 if let Value::String(s) = v {
26363 Some(s.as_str().to_string())
26364 } else {
26365 None
26366 }
26367 })
26368 .unwrap_or_else(|| "any".to_string());
26369 let param_desc = map
26370 .get("description")
26371 .and_then(|v| {
26372 if let Value::String(s) = v {
26373 Some(s.as_str().to_string())
26374 } else {
26375 None
26376 }
26377 })
26378 .unwrap_or_default();
26379 let required = map
26380 .get("required")
26381 .and_then(|v| {
26382 if let Value::Bool(b) = v {
26383 Some(*b)
26384 } else {
26385 None
26386 }
26387 })
26388 .unwrap_or(true);
26389 let evidence_str = map
26390 .get("evidence")
26391 .and_then(|v| {
26392 if let Value::String(s) = v {
26393 Some(s.as_str())
26394 } else {
26395 None
26396 }
26397 })
26398 .unwrap_or("~");
26399 let evidence = match evidence_str {
26400 "!" => Evidence::Known,
26401 "?" => Evidence::Uncertain,
26402 "~" => Evidence::Reported,
26403 "‽" => Evidence::Paradox,
26404 _ => Evidence::Reported,
26405 };
26406 params.push(ToolParameter {
26407 name: param_name,
26408 param_type,
26409 description: param_desc,
26410 required,
26411 evidence,
26412 });
26413 }
26414 }
26415 params
26416 }
26417 _ => Vec::new(),
26418 };
26419
26420 let returns = match &args[3] {
26421 Value::String(s) => s.as_str().to_string(),
26422 _ => "any".to_string(),
26423 };
26424
26425 let handler = if args.len() > 4 {
26426 match &args[4] {
26427 Value::Function(f) => Some(f.clone()),
26428 _ => None,
26429 }
26430 } else {
26431 None
26432 };
26433
26434 let tool = ToolDefinition {
26435 name: name.clone(),
26436 description,
26437 parameters: params,
26438 returns,
26439 evidence_in: Evidence::Reported,
26440 evidence_out: Evidence::Reported,
26441 handler,
26442 };
26443
26444 TOOL_REGISTRY.with(|registry| {
26445 registry.borrow_mut().insert(name.clone(), tool);
26446 });
26447
26448 Ok(Value::String(Rc::new(name)))
26449 });
26450
26451 define(interp, "tool_list", Some(0), |_, _args| {
26453 let tools: Vec<Value> = TOOL_REGISTRY.with(|registry| {
26454 registry
26455 .borrow()
26456 .keys()
26457 .map(|k| Value::String(Rc::new(k.clone())))
26458 .collect()
26459 });
26460 Ok(Value::Array(Rc::new(RefCell::new(tools))))
26461 });
26462
26463 define(interp, "tool_get", Some(1), |_, args| {
26465 let name = match &args[0] {
26466 Value::String(s) => s.as_str().to_string(),
26467 _ => return Err(RuntimeError::new("tool_get requires string name")),
26468 };
26469
26470 TOOL_REGISTRY.with(|registry| {
26471 if let Some(tool) = registry.borrow().get(&name) {
26472 let mut map = HashMap::new();
26473 map.insert(
26474 "name".to_string(),
26475 Value::String(Rc::new(tool.name.clone())),
26476 );
26477 map.insert(
26478 "description".to_string(),
26479 Value::String(Rc::new(tool.description.clone())),
26480 );
26481 map.insert(
26482 "returns".to_string(),
26483 Value::String(Rc::new(tool.returns.clone())),
26484 );
26485
26486 let params: Vec<Value> = tool
26487 .parameters
26488 .iter()
26489 .map(|p| {
26490 let mut pmap = HashMap::new();
26491 pmap.insert("name".to_string(), Value::String(Rc::new(p.name.clone())));
26492 pmap.insert(
26493 "type".to_string(),
26494 Value::String(Rc::new(p.param_type.clone())),
26495 );
26496 pmap.insert(
26497 "description".to_string(),
26498 Value::String(Rc::new(p.description.clone())),
26499 );
26500 pmap.insert("required".to_string(), Value::Bool(p.required));
26501 Value::Map(Rc::new(RefCell::new(pmap)))
26502 })
26503 .collect();
26504 map.insert(
26505 "parameters".to_string(),
26506 Value::Array(Rc::new(RefCell::new(params))),
26507 );
26508
26509 Ok(Value::Map(Rc::new(RefCell::new(map))))
26510 } else {
26511 Ok(Value::Null)
26512 }
26513 })
26514 });
26515
26516 define(interp, "tool_schema", Some(1), |_, args| {
26518 let name = match &args[0] {
26519 Value::String(s) => s.as_str().to_string(),
26520 _ => return Err(RuntimeError::new("tool_schema requires string name")),
26521 };
26522
26523 TOOL_REGISTRY.with(|registry| {
26524 if let Some(tool) = registry.borrow().get(&name) {
26525 let mut schema = HashMap::new();
26527 schema.insert(
26528 "type".to_string(),
26529 Value::String(Rc::new("function".to_string())),
26530 );
26531
26532 let mut function = HashMap::new();
26533 function.insert(
26534 "name".to_string(),
26535 Value::String(Rc::new(tool.name.clone())),
26536 );
26537 function.insert(
26538 "description".to_string(),
26539 Value::String(Rc::new(tool.description.clone())),
26540 );
26541
26542 let mut params_schema = HashMap::new();
26544 params_schema.insert(
26545 "type".to_string(),
26546 Value::String(Rc::new("object".to_string())),
26547 );
26548
26549 let mut properties = HashMap::new();
26550 let mut required: Vec<Value> = Vec::new();
26551
26552 for param in &tool.parameters {
26553 let mut prop = HashMap::new();
26554 let json_type = match param.param_type.as_str() {
26555 "int" | "i64" | "i32" => "integer",
26556 "float" | "f64" | "f32" => "number",
26557 "bool" => "boolean",
26558 "string" | "str" => "string",
26559 "array" | "list" => "array",
26560 "map" | "object" => "object",
26561 _ => "string",
26562 };
26563 prop.insert(
26564 "type".to_string(),
26565 Value::String(Rc::new(json_type.to_string())),
26566 );
26567 prop.insert(
26568 "description".to_string(),
26569 Value::String(Rc::new(param.description.clone())),
26570 );
26571 properties.insert(param.name.clone(), Value::Map(Rc::new(RefCell::new(prop))));
26572
26573 if param.required {
26574 required.push(Value::String(Rc::new(param.name.clone())));
26575 }
26576 }
26577
26578 params_schema.insert(
26579 "properties".to_string(),
26580 Value::Map(Rc::new(RefCell::new(properties))),
26581 );
26582 params_schema.insert(
26583 "required".to_string(),
26584 Value::Array(Rc::new(RefCell::new(required))),
26585 );
26586
26587 function.insert(
26588 "parameters".to_string(),
26589 Value::Map(Rc::new(RefCell::new(params_schema))),
26590 );
26591 schema.insert(
26592 "function".to_string(),
26593 Value::Map(Rc::new(RefCell::new(function))),
26594 );
26595
26596 Ok(Value::Map(Rc::new(RefCell::new(schema))))
26597 } else {
26598 Err(RuntimeError::new(format!("Tool '{}' not found", name)))
26599 }
26600 })
26601 });
26602
26603 define(interp, "tool_schemas_all", Some(0), |_, _args| {
26605 let schemas: Vec<Value> = TOOL_REGISTRY.with(|registry| {
26606 registry
26607 .borrow()
26608 .values()
26609 .map(|tool| {
26610 let mut schema = HashMap::new();
26611 schema.insert(
26612 "type".to_string(),
26613 Value::String(Rc::new("function".to_string())),
26614 );
26615
26616 let mut function = HashMap::new();
26617 function.insert(
26618 "name".to_string(),
26619 Value::String(Rc::new(tool.name.clone())),
26620 );
26621 function.insert(
26622 "description".to_string(),
26623 Value::String(Rc::new(tool.description.clone())),
26624 );
26625
26626 let mut params_schema = HashMap::new();
26627 params_schema.insert(
26628 "type".to_string(),
26629 Value::String(Rc::new("object".to_string())),
26630 );
26631
26632 let mut properties = HashMap::new();
26633 let mut required: Vec<Value> = Vec::new();
26634
26635 for param in &tool.parameters {
26636 let mut prop = HashMap::new();
26637 let json_type = match param.param_type.as_str() {
26638 "int" | "i64" | "i32" => "integer",
26639 "float" | "f64" | "f32" => "number",
26640 "bool" => "boolean",
26641 _ => "string",
26642 };
26643 prop.insert(
26644 "type".to_string(),
26645 Value::String(Rc::new(json_type.to_string())),
26646 );
26647 prop.insert(
26648 "description".to_string(),
26649 Value::String(Rc::new(param.description.clone())),
26650 );
26651 properties
26652 .insert(param.name.clone(), Value::Map(Rc::new(RefCell::new(prop))));
26653 if param.required {
26654 required.push(Value::String(Rc::new(param.name.clone())));
26655 }
26656 }
26657
26658 params_schema.insert(
26659 "properties".to_string(),
26660 Value::Map(Rc::new(RefCell::new(properties))),
26661 );
26662 params_schema.insert(
26663 "required".to_string(),
26664 Value::Array(Rc::new(RefCell::new(required))),
26665 );
26666 function.insert(
26667 "parameters".to_string(),
26668 Value::Map(Rc::new(RefCell::new(params_schema))),
26669 );
26670 schema.insert(
26671 "function".to_string(),
26672 Value::Map(Rc::new(RefCell::new(function))),
26673 );
26674
26675 Value::Map(Rc::new(RefCell::new(schema)))
26676 })
26677 .collect()
26678 });
26679 Ok(Value::Array(Rc::new(RefCell::new(schemas))))
26680 });
26681
26682 define(interp, "tool_call", None, |interp, args| {
26684 if args.is_empty() {
26685 return Err(RuntimeError::new("tool_call requires at least tool name"));
26686 }
26687
26688 let name = match &args[0] {
26689 Value::String(s) => s.as_str().to_string(),
26690 _ => {
26691 return Err(RuntimeError::new(
26692 "tool_call first argument must be tool name",
26693 ))
26694 }
26695 };
26696
26697 let tool_args: Vec<Value> = args.into_iter().skip(1).collect();
26698
26699 TOOL_REGISTRY.with(|registry| {
26700 if let Some(tool) = registry.borrow().get(&name) {
26701 if let Some(handler) = &tool.handler {
26702 let result = interp.call_function(handler.as_ref(), tool_args)?;
26704 Ok(Value::Evidential {
26706 value: Box::new(result),
26707 evidence: Evidence::Reported,
26708 })
26709 } else {
26710 Err(RuntimeError::new(format!("Tool '{}' has no handler", name)))
26711 }
26712 } else {
26713 Err(RuntimeError::new(format!("Tool '{}' not found", name)))
26714 }
26715 })
26716 });
26717
26718 define(interp, "tool_remove", Some(1), |_, args| {
26720 let name = match &args[0] {
26721 Value::String(s) => s.as_str().to_string(),
26722 _ => return Err(RuntimeError::new("tool_remove requires string name")),
26723 };
26724
26725 TOOL_REGISTRY.with(|registry| {
26726 let removed = registry.borrow_mut().remove(&name).is_some();
26727 Ok(Value::Bool(removed))
26728 })
26729 });
26730
26731 define(interp, "tool_clear", Some(0), |_, _args| {
26733 TOOL_REGISTRY.with(|registry| {
26734 registry.borrow_mut().clear();
26735 });
26736 Ok(Value::Null)
26737 });
26738}
26739
26740fn register_agent_llm(interp: &mut Interpreter) {
26745 define(interp, "llm_message", Some(2), |_, args| {
26747 let role = match &args[0] {
26748 Value::String(s) => s.as_str().to_string(),
26749 _ => return Err(RuntimeError::new("llm_message role must be string")),
26750 };
26751 let content = match &args[1] {
26752 Value::String(s) => s.as_str().to_string(),
26753 _ => return Err(RuntimeError::new("llm_message content must be string")),
26754 };
26755
26756 let mut map = HashMap::new();
26757 map.insert("role".to_string(), Value::String(Rc::new(role)));
26758 map.insert("content".to_string(), Value::String(Rc::new(content)));
26759 Ok(Value::Map(Rc::new(RefCell::new(map))))
26760 });
26761
26762 define(interp, "llm_messages", None, |_, args| {
26764 let messages: Vec<Value> = args.into_iter().collect();
26765 Ok(Value::Array(Rc::new(RefCell::new(messages))))
26766 });
26767
26768 define(interp, "llm_request", None, |_, args| {
26770 if args.is_empty() {
26771 return Err(RuntimeError::new("llm_request requires provider"));
26772 }
26773
26774 let provider = match &args[0] {
26775 Value::String(s) => s.as_str().to_string(),
26776 _ => return Err(RuntimeError::new("provider must be string")),
26777 };
26778
26779 let mut request = HashMap::new();
26780 request.insert(
26781 "provider".to_string(),
26782 Value::String(Rc::new(provider.clone())),
26783 );
26784
26785 let default_model = match provider.as_str() {
26787 "openai" => "gpt-4-turbo-preview",
26788 "anthropic" | "claude" => "claude-3-opus-20240229",
26789 "google" | "gemini" => "gemini-pro",
26790 "mistral" => "mistral-large-latest",
26791 "ollama" | "local" => "llama2",
26792 _ => "gpt-4",
26793 };
26794 request.insert(
26795 "model".to_string(),
26796 Value::String(Rc::new(default_model.to_string())),
26797 );
26798
26799 for (i, arg) in args.iter().enumerate().skip(1) {
26801 if let Value::Map(map) = arg {
26802 let map = map.borrow();
26803 for (k, v) in map.iter() {
26804 request.insert(k.clone(), v.clone());
26805 }
26806 } else if i == 1 {
26807 if let Value::String(s) = arg {
26809 request.insert("model".to_string(), Value::String(s.clone()));
26810 }
26811 }
26812 }
26813
26814 if !request.contains_key("temperature") {
26816 request.insert("temperature".to_string(), Value::Float(0.7));
26817 }
26818 if !request.contains_key("max_tokens") {
26819 request.insert("max_tokens".to_string(), Value::Int(4096));
26820 }
26821
26822 Ok(Value::Map(Rc::new(RefCell::new(request))))
26823 });
26824
26825 define(interp, "llm_with_tools", Some(2), |_, args| {
26827 let request = match &args[0] {
26828 Value::Map(m) => m.clone(),
26829 _ => {
26830 return Err(RuntimeError::new(
26831 "llm_with_tools first arg must be request map",
26832 ))
26833 }
26834 };
26835
26836 let tools = match &args[1] {
26837 Value::Array(arr) => arr.clone(),
26838 _ => {
26839 return Err(RuntimeError::new(
26840 "llm_with_tools second arg must be tools array",
26841 ))
26842 }
26843 };
26844
26845 request
26846 .borrow_mut()
26847 .insert("tools".to_string(), Value::Array(tools));
26848 request.borrow_mut().insert(
26849 "tool_choice".to_string(),
26850 Value::String(Rc::new("auto".to_string())),
26851 );
26852
26853 Ok(Value::Map(request))
26854 });
26855
26856 define(interp, "llm_with_system", Some(2), |_, args| {
26858 let request = match &args[0] {
26859 Value::Map(m) => m.clone(),
26860 _ => {
26861 return Err(RuntimeError::new(
26862 "llm_with_system first arg must be request map",
26863 ))
26864 }
26865 };
26866
26867 let system = match &args[1] {
26868 Value::String(s) => s.clone(),
26869 _ => {
26870 return Err(RuntimeError::new(
26871 "llm_with_system second arg must be string",
26872 ))
26873 }
26874 };
26875
26876 request
26877 .borrow_mut()
26878 .insert("system".to_string(), Value::String(system));
26879 Ok(Value::Map(request))
26880 });
26881
26882 define(interp, "llm_with_messages", Some(2), |_, args| {
26884 let request = match &args[0] {
26885 Value::Map(m) => m.clone(),
26886 _ => {
26887 return Err(RuntimeError::new(
26888 "llm_with_messages first arg must be request map",
26889 ))
26890 }
26891 };
26892
26893 let messages = match &args[1] {
26894 Value::Array(arr) => arr.clone(),
26895 _ => {
26896 return Err(RuntimeError::new(
26897 "llm_with_messages second arg must be messages array",
26898 ))
26899 }
26900 };
26901
26902 request
26903 .borrow_mut()
26904 .insert("messages".to_string(), Value::Array(messages));
26905 Ok(Value::Map(request))
26906 });
26907
26908 define(interp, "llm_send", Some(1), |_, args| {
26911 let request = match &args[0] {
26912 Value::Map(m) => m.borrow().clone(),
26913 _ => return Err(RuntimeError::new("llm_send requires request map")),
26914 };
26915
26916 let provider = request
26917 .get("provider")
26918 .and_then(|v| {
26919 if let Value::String(s) = v {
26920 Some(s.as_str().to_string())
26921 } else {
26922 None
26923 }
26924 })
26925 .unwrap_or_else(|| "unknown".to_string());
26926
26927 let model = request
26928 .get("model")
26929 .and_then(|v| {
26930 if let Value::String(s) = v {
26931 Some(s.as_str().to_string())
26932 } else {
26933 None
26934 }
26935 })
26936 .unwrap_or_else(|| "unknown".to_string());
26937
26938 let mut response = HashMap::new();
26940 response.insert(
26941 "id".to_string(),
26942 Value::String(Rc::new(format!("msg_{}", Uuid::new_v4()))),
26943 );
26944 response.insert("provider".to_string(), Value::String(Rc::new(provider)));
26945 response.insert("model".to_string(), Value::String(Rc::new(model)));
26946 response.insert(
26947 "created".to_string(),
26948 Value::Int(
26949 std::time::SystemTime::now()
26950 .duration_since(std::time::UNIX_EPOCH)
26951 .unwrap_or_default()
26952 .as_secs() as i64,
26953 ),
26954 );
26955
26956 if request.contains_key("tools") {
26958 response.insert(
26959 "type".to_string(),
26960 Value::String(Rc::new("tool_use".to_string())),
26961 );
26962 response.insert(
26963 "tool_name".to_string(),
26964 Value::String(Rc::new("pending".to_string())),
26965 );
26966 response.insert(
26967 "tool_input".to_string(),
26968 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
26969 );
26970 } else {
26971 response.insert(
26972 "type".to_string(),
26973 Value::String(Rc::new("text".to_string())),
26974 );
26975 response.insert(
26976 "content".to_string(),
26977 Value::String(Rc::new(
26978 "[LLM Response - Connect to actual API for real responses]".to_string(),
26979 )),
26980 );
26981 }
26982
26983 let mut usage = HashMap::new();
26985 usage.insert("input_tokens".to_string(), Value::Int(0));
26986 usage.insert("output_tokens".to_string(), Value::Int(0));
26987 response.insert(
26988 "usage".to_string(),
26989 Value::Map(Rc::new(RefCell::new(usage))),
26990 );
26991
26992 Ok(Value::Evidential {
26994 value: Box::new(Value::Map(Rc::new(RefCell::new(response)))),
26995 evidence: Evidence::Reported,
26996 })
26997 });
26998
26999 define(interp, "llm_parse_tool_call", Some(1), |_, args| {
27001 let response = match &args[0] {
27002 Value::Map(m) => m.borrow().clone(),
27003 Value::Evidential { value, .. } => {
27004 if let Value::Map(m) = value.as_ref() {
27005 m.borrow().clone()
27006 } else {
27007 return Err(RuntimeError::new(
27008 "llm_parse_tool_call requires map response",
27009 ));
27010 }
27011 }
27012 _ => {
27013 return Err(RuntimeError::new(
27014 "llm_parse_tool_call requires response map",
27015 ))
27016 }
27017 };
27018
27019 let resp_type = response
27020 .get("type")
27021 .and_then(|v| {
27022 if let Value::String(s) = v {
27023 Some(s.as_str().to_string())
27024 } else {
27025 None
27026 }
27027 })
27028 .unwrap_or_default();
27029
27030 if resp_type == "tool_use" {
27031 let tool_name = response
27032 .get("tool_name")
27033 .and_then(|v| {
27034 if let Value::String(s) = v {
27035 Some(s.as_str().to_string())
27036 } else {
27037 None
27038 }
27039 })
27040 .unwrap_or_default();
27041
27042 let tool_input = response.get("tool_input").cloned().unwrap_or(Value::Null);
27043
27044 let mut result = HashMap::new();
27045 result.insert("is_tool_call".to_string(), Value::Bool(true));
27046 result.insert("tool_name".to_string(), Value::String(Rc::new(tool_name)));
27047 result.insert("tool_input".to_string(), tool_input);
27048 Ok(Value::Map(Rc::new(RefCell::new(result))))
27049 } else {
27050 let mut result = HashMap::new();
27051 result.insert("is_tool_call".to_string(), Value::Bool(false));
27052 result.insert(
27053 "content".to_string(),
27054 response.get("content").cloned().unwrap_or(Value::Null),
27055 );
27056 Ok(Value::Map(Rc::new(RefCell::new(result))))
27057 }
27058 });
27059
27060 define(interp, "llm_extract", Some(2), |_, args| {
27062 let _response = match &args[0] {
27063 Value::Map(m) => m.borrow().clone(),
27064 Value::Evidential { value, .. } => {
27065 if let Value::Map(m) = value.as_ref() {
27066 m.borrow().clone()
27067 } else {
27068 return Err(RuntimeError::new("llm_extract requires response"));
27069 }
27070 }
27071 _ => return Err(RuntimeError::new("llm_extract requires response")),
27072 };
27073
27074 let _schema = match &args[1] {
27075 Value::Map(m) => m.borrow().clone(),
27076 _ => return Err(RuntimeError::new("llm_extract requires schema map")),
27077 };
27078
27079 let mut result = HashMap::new();
27082 result.insert("success".to_string(), Value::Bool(true));
27083 result.insert("data".to_string(), Value::Null);
27084 result.insert(
27085 "errors".to_string(),
27086 Value::Array(Rc::new(RefCell::new(Vec::new()))),
27087 );
27088
27089 Ok(Value::Evidential {
27090 value: Box::new(Value::Map(Rc::new(RefCell::new(result)))),
27091 evidence: Evidence::Uncertain,
27092 })
27093 });
27094
27095 define(interp, "prompt_template", Some(1), |_, args| {
27097 let template = match &args[0] {
27098 Value::String(s) => s.as_str().to_string(),
27099 _ => return Err(RuntimeError::new("prompt_template requires string")),
27100 };
27101
27102 let mut variables = Vec::new();
27104 let mut in_var = false;
27105 let mut var_name = String::new();
27106
27107 for c in template.chars() {
27108 match c {
27109 '{' if !in_var => {
27110 in_var = true;
27111 var_name.clear();
27112 }
27113 '}' if in_var => {
27114 if !var_name.is_empty() {
27115 variables.push(Value::String(Rc::new(var_name.clone())));
27116 }
27117 in_var = false;
27118 }
27119 _ if in_var => {
27120 var_name.push(c);
27121 }
27122 _ => {}
27123 }
27124 }
27125
27126 let mut result = HashMap::new();
27127 result.insert("template".to_string(), Value::String(Rc::new(template)));
27128 result.insert(
27129 "variables".to_string(),
27130 Value::Array(Rc::new(RefCell::new(variables))),
27131 );
27132 Ok(Value::Map(Rc::new(RefCell::new(result))))
27133 });
27134
27135 define(interp, "prompt_render", Some(2), |_, args| {
27137 let template_obj = match &args[0] {
27138 Value::Map(m) => m.borrow().clone(),
27139 Value::String(s) => {
27140 let mut m = HashMap::new();
27141 m.insert("template".to_string(), Value::String(s.clone()));
27142 m
27143 }
27144 _ => return Err(RuntimeError::new("prompt_render requires template")),
27145 };
27146
27147 let values = match &args[1] {
27148 Value::Map(m) => m.borrow().clone(),
27149 _ => return Err(RuntimeError::new("prompt_render requires values map")),
27150 };
27151
27152 let template = template_obj
27153 .get("template")
27154 .and_then(|v| {
27155 if let Value::String(s) = v {
27156 Some(s.as_str().to_string())
27157 } else {
27158 None
27159 }
27160 })
27161 .unwrap_or_default();
27162
27163 let mut result = template;
27164 for (key, value) in values.iter() {
27165 let value_str = match value {
27166 Value::String(s) => s.as_str().to_string(),
27167 Value::Int(n) => n.to_string(),
27168 Value::Float(f) => f.to_string(),
27169 Value::Bool(b) => b.to_string(),
27170 _ => format!("{}", value),
27171 };
27172 result = result.replace(&format!("{{{}}}", key), &value_str);
27173 }
27174
27175 Ok(Value::String(Rc::new(result)))
27176 });
27177}
27178
27179fn register_agent_memory(interp: &mut Interpreter) {
27184 define(interp, "memory_session", Some(1), |_, args| {
27186 let session_id = match &args[0] {
27187 Value::String(s) => s.as_str().to_string(),
27188 _ => return Err(RuntimeError::new("memory_session requires string id")),
27189 };
27190
27191 let now = std::time::SystemTime::now()
27192 .duration_since(std::time::UNIX_EPOCH)
27193 .unwrap_or_default()
27194 .as_secs();
27195
27196 AGENT_MEMORY.with(|memory| {
27197 let mut mem = memory.borrow_mut();
27198 if !mem.contains_key(&session_id) {
27199 mem.insert(
27200 session_id.clone(),
27201 AgentSession {
27202 id: session_id.clone(),
27203 context: HashMap::new(),
27204 history: Vec::new(),
27205 created_at: now,
27206 last_accessed: now,
27207 },
27208 );
27209 } else if let Some(session) = mem.get_mut(&session_id) {
27210 session.last_accessed = now;
27211 }
27212 });
27213
27214 let mut result = HashMap::new();
27215 result.insert("id".to_string(), Value::String(Rc::new(session_id)));
27216 result.insert("created_at".to_string(), Value::Int(now as i64));
27217 Ok(Value::Map(Rc::new(RefCell::new(result))))
27218 });
27219
27220 define(interp, "memory_set", Some(3), |_, args| {
27222 let session_id = match &args[0] {
27223 Value::String(s) => s.as_str().to_string(),
27224 Value::Map(m) => m
27225 .borrow()
27226 .get("id")
27227 .and_then(|v| {
27228 if let Value::String(s) = v {
27229 Some(s.as_str().to_string())
27230 } else {
27231 None
27232 }
27233 })
27234 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
27235 _ => return Err(RuntimeError::new("memory_set requires session")),
27236 };
27237
27238 let key = match &args[1] {
27239 Value::String(s) => s.as_str().to_string(),
27240 _ => return Err(RuntimeError::new("memory_set key must be string")),
27241 };
27242
27243 let value = args[2].clone();
27244
27245 AGENT_MEMORY.with(|memory| {
27246 if let Some(session) = memory.borrow_mut().get_mut(&session_id) {
27247 session.context.insert(key, value);
27248 session.last_accessed = std::time::SystemTime::now()
27249 .duration_since(std::time::UNIX_EPOCH)
27250 .unwrap_or_default()
27251 .as_secs();
27252 Ok(Value::Bool(true))
27253 } else {
27254 Err(RuntimeError::new(format!(
27255 "Session '{}' not found",
27256 session_id
27257 )))
27258 }
27259 })
27260 });
27261
27262 define(interp, "memory_get", Some(2), |_, args| {
27264 let session_id = match &args[0] {
27265 Value::String(s) => s.as_str().to_string(),
27266 Value::Map(m) => m
27267 .borrow()
27268 .get("id")
27269 .and_then(|v| {
27270 if let Value::String(s) = v {
27271 Some(s.as_str().to_string())
27272 } else {
27273 None
27274 }
27275 })
27276 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
27277 _ => return Err(RuntimeError::new("memory_get requires session")),
27278 };
27279
27280 let key = match &args[1] {
27281 Value::String(s) => s.as_str().to_string(),
27282 _ => return Err(RuntimeError::new("memory_get key must be string")),
27283 };
27284
27285 AGENT_MEMORY.with(|memory| {
27286 if let Some(session) = memory.borrow().get(&session_id) {
27287 Ok(session.context.get(&key).cloned().unwrap_or(Value::Null))
27288 } else {
27289 Ok(Value::Null)
27290 }
27291 })
27292 });
27293
27294 define(interp, "memory_history_add", Some(3), |_, args| {
27296 let session_id = match &args[0] {
27297 Value::String(s) => s.as_str().to_string(),
27298 Value::Map(m) => m
27299 .borrow()
27300 .get("id")
27301 .and_then(|v| {
27302 if let Value::String(s) = v {
27303 Some(s.as_str().to_string())
27304 } else {
27305 None
27306 }
27307 })
27308 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
27309 _ => return Err(RuntimeError::new("memory_history_add requires session")),
27310 };
27311
27312 let role = match &args[1] {
27313 Value::String(s) => s.as_str().to_string(),
27314 _ => return Err(RuntimeError::new("role must be string")),
27315 };
27316
27317 let content = match &args[2] {
27318 Value::String(s) => s.as_str().to_string(),
27319 _ => return Err(RuntimeError::new("content must be string")),
27320 };
27321
27322 AGENT_MEMORY.with(|memory| {
27323 if let Some(session) = memory.borrow_mut().get_mut(&session_id) {
27324 session.history.push((role, content));
27325 session.last_accessed = std::time::SystemTime::now()
27326 .duration_since(std::time::UNIX_EPOCH)
27327 .unwrap_or_default()
27328 .as_secs();
27329 Ok(Value::Int(session.history.len() as i64))
27330 } else {
27331 Err(RuntimeError::new(format!(
27332 "Session '{}' not found",
27333 session_id
27334 )))
27335 }
27336 })
27337 });
27338
27339 define(interp, "memory_history_get", None, |_, args| {
27341 if args.is_empty() {
27342 return Err(RuntimeError::new("memory_history_get requires session"));
27343 }
27344
27345 let session_id = match &args[0] {
27346 Value::String(s) => s.as_str().to_string(),
27347 Value::Map(m) => m
27348 .borrow()
27349 .get("id")
27350 .and_then(|v| {
27351 if let Value::String(s) = v {
27352 Some(s.as_str().to_string())
27353 } else {
27354 None
27355 }
27356 })
27357 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
27358 _ => return Err(RuntimeError::new("memory_history_get requires session")),
27359 };
27360
27361 let limit = if args.len() > 1 {
27362 match &args[1] {
27363 Value::Int(n) => Some(*n as usize),
27364 _ => None,
27365 }
27366 } else {
27367 None
27368 };
27369
27370 AGENT_MEMORY.with(|memory| {
27371 if let Some(session) = memory.borrow().get(&session_id) {
27372 let history: Vec<Value> = session
27373 .history
27374 .iter()
27375 .rev()
27376 .take(limit.unwrap_or(usize::MAX))
27377 .rev()
27378 .map(|(role, content)| {
27379 let mut msg = HashMap::new();
27380 msg.insert("role".to_string(), Value::String(Rc::new(role.clone())));
27381 msg.insert(
27382 "content".to_string(),
27383 Value::String(Rc::new(content.clone())),
27384 );
27385 Value::Map(Rc::new(RefCell::new(msg)))
27386 })
27387 .collect();
27388 Ok(Value::Array(Rc::new(RefCell::new(history))))
27389 } else {
27390 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
27391 }
27392 })
27393 });
27394
27395 define(interp, "memory_context_all", Some(1), |_, args| {
27397 let session_id = match &args[0] {
27398 Value::String(s) => s.as_str().to_string(),
27399 Value::Map(m) => m
27400 .borrow()
27401 .get("id")
27402 .and_then(|v| {
27403 if let Value::String(s) = v {
27404 Some(s.as_str().to_string())
27405 } else {
27406 None
27407 }
27408 })
27409 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
27410 _ => return Err(RuntimeError::new("memory_context_all requires session")),
27411 };
27412
27413 AGENT_MEMORY.with(|memory| {
27414 if let Some(session) = memory.borrow().get(&session_id) {
27415 let context: HashMap<String, Value> = session.context.clone();
27416 Ok(Value::Map(Rc::new(RefCell::new(context))))
27417 } else {
27418 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
27419 }
27420 })
27421 });
27422
27423 define(interp, "memory_clear", Some(1), |_, args| {
27425 let session_id = match &args[0] {
27426 Value::String(s) => s.as_str().to_string(),
27427 Value::Map(m) => m
27428 .borrow()
27429 .get("id")
27430 .and_then(|v| {
27431 if let Value::String(s) = v {
27432 Some(s.as_str().to_string())
27433 } else {
27434 None
27435 }
27436 })
27437 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
27438 _ => return Err(RuntimeError::new("memory_clear requires session")),
27439 };
27440
27441 AGENT_MEMORY.with(|memory| {
27442 let removed = memory.borrow_mut().remove(&session_id).is_some();
27443 Ok(Value::Bool(removed))
27444 })
27445 });
27446
27447 define(interp, "memory_sessions_list", Some(0), |_, _args| {
27449 let sessions: Vec<Value> = AGENT_MEMORY.with(|memory| {
27450 memory
27451 .borrow()
27452 .values()
27453 .map(|session| {
27454 let mut info = HashMap::new();
27455 info.insert("id".to_string(), Value::String(Rc::new(session.id.clone())));
27456 info.insert(
27457 "created_at".to_string(),
27458 Value::Int(session.created_at as i64),
27459 );
27460 info.insert(
27461 "last_accessed".to_string(),
27462 Value::Int(session.last_accessed as i64),
27463 );
27464 info.insert(
27465 "context_keys".to_string(),
27466 Value::Int(session.context.len() as i64),
27467 );
27468 info.insert(
27469 "history_length".to_string(),
27470 Value::Int(session.history.len() as i64),
27471 );
27472 Value::Map(Rc::new(RefCell::new(info)))
27473 })
27474 .collect()
27475 });
27476 Ok(Value::Array(Rc::new(RefCell::new(sessions))))
27477 });
27478}
27479
27480fn register_agent_planning(interp: &mut Interpreter) {
27485 define(interp, "plan_state_machine", Some(2), |_, args| {
27487 let name = match &args[0] {
27488 Value::String(s) => s.as_str().to_string(),
27489 _ => return Err(RuntimeError::new("plan_state_machine name must be string")),
27490 };
27491
27492 let states = match &args[1] {
27493 Value::Array(arr) => arr
27494 .borrow()
27495 .iter()
27496 .filter_map(|v| {
27497 if let Value::String(s) = v {
27498 Some(s.as_str().to_string())
27499 } else {
27500 None
27501 }
27502 })
27503 .collect::<Vec<_>>(),
27504 _ => return Err(RuntimeError::new("plan_state_machine states must be array")),
27505 };
27506
27507 if states.is_empty() {
27508 return Err(RuntimeError::new(
27509 "State machine must have at least one state",
27510 ));
27511 }
27512
27513 let initial_state = states[0].clone();
27514 let now = std::time::SystemTime::now()
27515 .duration_since(std::time::UNIX_EPOCH)
27516 .unwrap_or_default()
27517 .as_secs();
27518
27519 let machine = StateMachine {
27520 name: name.clone(),
27521 current_state: initial_state.clone(),
27522 states,
27523 transitions: HashMap::new(),
27524 history: vec![(initial_state, now)],
27525 };
27526
27527 STATE_MACHINES.with(|machines| {
27528 machines.borrow_mut().insert(name.clone(), machine);
27529 });
27530
27531 let mut result = HashMap::new();
27532 result.insert("name".to_string(), Value::String(Rc::new(name)));
27533 result.insert(
27534 "type".to_string(),
27535 Value::String(Rc::new("state_machine".to_string())),
27536 );
27537 Ok(Value::Map(Rc::new(RefCell::new(result))))
27538 });
27539
27540 define(interp, "plan_add_transition", Some(3), |_, args| {
27542 let machine_name = match &args[0] {
27543 Value::String(s) => s.as_str().to_string(),
27544 Value::Map(m) => m
27545 .borrow()
27546 .get("name")
27547 .and_then(|v| {
27548 if let Value::String(s) = v {
27549 Some(s.as_str().to_string())
27550 } else {
27551 None
27552 }
27553 })
27554 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27555 _ => return Err(RuntimeError::new("plan_add_transition requires machine")),
27556 };
27557
27558 let from_state = match &args[1] {
27559 Value::String(s) => s.as_str().to_string(),
27560 _ => return Err(RuntimeError::new("from_state must be string")),
27561 };
27562
27563 let to_state = match &args[2] {
27564 Value::String(s) => s.as_str().to_string(),
27565 _ => return Err(RuntimeError::new("to_state must be string")),
27566 };
27567
27568 STATE_MACHINES.with(|machines| {
27569 if let Some(machine) = machines.borrow_mut().get_mut(&machine_name) {
27570 if !machine.states.contains(&from_state) {
27572 return Err(RuntimeError::new(format!(
27573 "State '{}' not in machine",
27574 from_state
27575 )));
27576 }
27577 if !machine.states.contains(&to_state) {
27578 return Err(RuntimeError::new(format!(
27579 "State '{}' not in machine",
27580 to_state
27581 )));
27582 }
27583
27584 machine
27585 .transitions
27586 .entry(from_state)
27587 .or_insert_with(Vec::new)
27588 .push((to_state, "".to_string()));
27589
27590 Ok(Value::Bool(true))
27591 } else {
27592 Err(RuntimeError::new(format!(
27593 "State machine '{}' not found",
27594 machine_name
27595 )))
27596 }
27597 })
27598 });
27599
27600 define(interp, "plan_current_state", Some(1), |_, args| {
27602 let machine_name = match &args[0] {
27603 Value::String(s) => s.as_str().to_string(),
27604 Value::Map(m) => m
27605 .borrow()
27606 .get("name")
27607 .and_then(|v| {
27608 if let Value::String(s) = v {
27609 Some(s.as_str().to_string())
27610 } else {
27611 None
27612 }
27613 })
27614 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27615 _ => return Err(RuntimeError::new("plan_current_state requires machine")),
27616 };
27617
27618 STATE_MACHINES.with(|machines| {
27619 if let Some(machine) = machines.borrow().get(&machine_name) {
27620 Ok(Value::String(Rc::new(machine.current_state.clone())))
27621 } else {
27622 Err(RuntimeError::new(format!(
27623 "State machine '{}' not found",
27624 machine_name
27625 )))
27626 }
27627 })
27628 });
27629
27630 define(interp, "plan_transition", Some(2), |_, args| {
27632 let machine_name = match &args[0] {
27633 Value::String(s) => s.as_str().to_string(),
27634 Value::Map(m) => m
27635 .borrow()
27636 .get("name")
27637 .and_then(|v| {
27638 if let Value::String(s) = v {
27639 Some(s.as_str().to_string())
27640 } else {
27641 None
27642 }
27643 })
27644 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27645 _ => return Err(RuntimeError::new("plan_transition requires machine")),
27646 };
27647
27648 let to_state = match &args[1] {
27649 Value::String(s) => s.as_str().to_string(),
27650 _ => return Err(RuntimeError::new("to_state must be string")),
27651 };
27652
27653 STATE_MACHINES.with(|machines| {
27654 if let Some(machine) = machines.borrow_mut().get_mut(&machine_name) {
27655 let current = machine.current_state.clone();
27656
27657 let valid = machine
27659 .transitions
27660 .get(¤t)
27661 .map(|transitions| transitions.iter().any(|(to, _)| to == &to_state))
27662 .unwrap_or(false);
27663
27664 if valid || machine.states.contains(&to_state) {
27665 let now = std::time::SystemTime::now()
27666 .duration_since(std::time::UNIX_EPOCH)
27667 .unwrap_or_default()
27668 .as_secs();
27669
27670 machine.current_state = to_state.clone();
27671 machine.history.push((to_state.clone(), now));
27672
27673 let mut result = HashMap::new();
27674 result.insert("success".to_string(), Value::Bool(true));
27675 result.insert("from".to_string(), Value::String(Rc::new(current)));
27676 result.insert("to".to_string(), Value::String(Rc::new(to_state)));
27677 Ok(Value::Map(Rc::new(RefCell::new(result))))
27678 } else {
27679 let mut result = HashMap::new();
27680 result.insert("success".to_string(), Value::Bool(false));
27681 result.insert(
27682 "error".to_string(),
27683 Value::String(Rc::new(format!(
27684 "No valid transition from '{}' to '{}'",
27685 current, to_state
27686 ))),
27687 );
27688 Ok(Value::Map(Rc::new(RefCell::new(result))))
27689 }
27690 } else {
27691 Err(RuntimeError::new(format!(
27692 "State machine '{}' not found",
27693 machine_name
27694 )))
27695 }
27696 })
27697 });
27698
27699 define(interp, "plan_can_transition", Some(2), |_, args| {
27701 let machine_name = match &args[0] {
27702 Value::String(s) => s.as_str().to_string(),
27703 Value::Map(m) => m
27704 .borrow()
27705 .get("name")
27706 .and_then(|v| {
27707 if let Value::String(s) = v {
27708 Some(s.as_str().to_string())
27709 } else {
27710 None
27711 }
27712 })
27713 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27714 _ => return Err(RuntimeError::new("plan_can_transition requires machine")),
27715 };
27716
27717 let to_state = match &args[1] {
27718 Value::String(s) => s.as_str().to_string(),
27719 _ => return Err(RuntimeError::new("to_state must be string")),
27720 };
27721
27722 STATE_MACHINES.with(|machines| {
27723 if let Some(machine) = machines.borrow().get(&machine_name) {
27724 let current = &machine.current_state;
27725 let can = machine
27726 .transitions
27727 .get(current)
27728 .map(|transitions| transitions.iter().any(|(to, _)| to == &to_state))
27729 .unwrap_or(false);
27730 Ok(Value::Bool(can))
27731 } else {
27732 Ok(Value::Bool(false))
27733 }
27734 })
27735 });
27736
27737 define(interp, "plan_available_transitions", Some(1), |_, args| {
27739 let machine_name = match &args[0] {
27740 Value::String(s) => s.as_str().to_string(),
27741 Value::Map(m) => m
27742 .borrow()
27743 .get("name")
27744 .and_then(|v| {
27745 if let Value::String(s) = v {
27746 Some(s.as_str().to_string())
27747 } else {
27748 None
27749 }
27750 })
27751 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27752 _ => {
27753 return Err(RuntimeError::new(
27754 "plan_available_transitions requires machine",
27755 ))
27756 }
27757 };
27758
27759 STATE_MACHINES.with(|machines| {
27760 if let Some(machine) = machines.borrow().get(&machine_name) {
27761 let current = &machine.current_state;
27762 let available: Vec<Value> = machine
27763 .transitions
27764 .get(current)
27765 .map(|transitions| {
27766 transitions
27767 .iter()
27768 .map(|(to, _)| Value::String(Rc::new(to.clone())))
27769 .collect()
27770 })
27771 .unwrap_or_default();
27772 Ok(Value::Array(Rc::new(RefCell::new(available))))
27773 } else {
27774 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
27775 }
27776 })
27777 });
27778
27779 define(interp, "plan_history", Some(1), |_, args| {
27781 let machine_name = match &args[0] {
27782 Value::String(s) => s.as_str().to_string(),
27783 Value::Map(m) => m
27784 .borrow()
27785 .get("name")
27786 .and_then(|v| {
27787 if let Value::String(s) = v {
27788 Some(s.as_str().to_string())
27789 } else {
27790 None
27791 }
27792 })
27793 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
27794 _ => return Err(RuntimeError::new("plan_history requires machine")),
27795 };
27796
27797 STATE_MACHINES.with(|machines| {
27798 if let Some(machine) = machines.borrow().get(&machine_name) {
27799 let history: Vec<Value> = machine
27800 .history
27801 .iter()
27802 .map(|(state, timestamp)| {
27803 let mut entry = HashMap::new();
27804 entry.insert("state".to_string(), Value::String(Rc::new(state.clone())));
27805 entry.insert("timestamp".to_string(), Value::Int(*timestamp as i64));
27806 Value::Map(Rc::new(RefCell::new(entry)))
27807 })
27808 .collect();
27809 Ok(Value::Array(Rc::new(RefCell::new(history))))
27810 } else {
27811 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
27812 }
27813 })
27814 });
27815
27816 define(interp, "plan_goal", Some(2), |_, args| {
27818 let name = match &args[0] {
27819 Value::String(s) => s.as_str().to_string(),
27820 _ => return Err(RuntimeError::new("plan_goal name must be string")),
27821 };
27822
27823 let criteria = args[1].clone();
27824
27825 let mut goal = HashMap::new();
27826 goal.insert("name".to_string(), Value::String(Rc::new(name)));
27827 goal.insert("criteria".to_string(), criteria);
27828 goal.insert(
27829 "status".to_string(),
27830 Value::String(Rc::new("pending".to_string())),
27831 );
27832 goal.insert("progress".to_string(), Value::Float(0.0));
27833 goal.insert(
27834 "created_at".to_string(),
27835 Value::Int(
27836 std::time::SystemTime::now()
27837 .duration_since(std::time::UNIX_EPOCH)
27838 .unwrap_or_default()
27839 .as_secs() as i64,
27840 ),
27841 );
27842
27843 Ok(Value::Map(Rc::new(RefCell::new(goal))))
27844 });
27845
27846 define(interp, "plan_subgoals", Some(2), |_, args| {
27848 let parent = match &args[0] {
27849 Value::Map(m) => m.clone(),
27850 _ => return Err(RuntimeError::new("plan_subgoals requires goal map")),
27851 };
27852
27853 let subgoals = match &args[1] {
27854 Value::Array(arr) => arr.clone(),
27855 _ => return Err(RuntimeError::new("plan_subgoals requires subgoals array")),
27856 };
27857
27858 parent
27859 .borrow_mut()
27860 .insert("subgoals".to_string(), Value::Array(subgoals));
27861 Ok(Value::Map(parent))
27862 });
27863
27864 define(interp, "plan_update_progress", Some(2), |_, args| {
27866 let goal = match &args[0] {
27867 Value::Map(m) => m.clone(),
27868 _ => return Err(RuntimeError::new("plan_update_progress requires goal map")),
27869 };
27870
27871 let progress = match &args[1] {
27872 Value::Float(f) => *f,
27873 Value::Int(i) => *i as f64,
27874 _ => return Err(RuntimeError::new("progress must be number")),
27875 };
27876
27877 let progress = progress.clamp(0.0, 1.0);
27878 goal.borrow_mut()
27879 .insert("progress".to_string(), Value::Float(progress));
27880
27881 if progress >= 1.0 {
27882 goal.borrow_mut().insert(
27883 "status".to_string(),
27884 Value::String(Rc::new("completed".to_string())),
27885 );
27886 } else if progress > 0.0 {
27887 goal.borrow_mut().insert(
27888 "status".to_string(),
27889 Value::String(Rc::new("in_progress".to_string())),
27890 );
27891 }
27892
27893 Ok(Value::Map(goal))
27894 });
27895
27896 define(interp, "plan_check_goal", Some(2), |_interp, args| {
27898 let goal = match &args[0] {
27899 Value::Map(m) => m.borrow().clone(),
27900 _ => return Err(RuntimeError::new("plan_check_goal requires goal map")),
27901 };
27902
27903 let context = match &args[1] {
27904 Value::Map(m) => m.borrow().clone(),
27905 _ => return Err(RuntimeError::new("plan_check_goal requires context map")),
27906 };
27907
27908 let _criteria = goal.get("criteria").cloned().unwrap_or(Value::Null);
27910
27911 let mut met = true;
27913 let mut missing: Vec<String> = Vec::new();
27914
27915 if let Some(Value::Array(required)) = goal.get("required_context") {
27916 for req in required.borrow().iter() {
27917 if let Value::String(key) = req {
27918 if !context.contains_key(key.as_str()) {
27919 met = false;
27920 missing.push(key.as_str().to_string());
27921 }
27922 }
27923 }
27924 }
27925
27926 let mut result = HashMap::new();
27927 result.insert("met".to_string(), Value::Bool(met));
27928 result.insert(
27929 "missing".to_string(),
27930 Value::Array(Rc::new(RefCell::new(
27931 missing
27932 .into_iter()
27933 .map(|s| Value::String(Rc::new(s)))
27934 .collect(),
27935 ))),
27936 );
27937
27938 Ok(Value::Map(Rc::new(RefCell::new(result))))
27939 });
27940}
27941
27942fn register_agent_vectors(interp: &mut Interpreter) {
27947 define(interp, "vec_embedding", Some(1), |_, args| {
27949 let text = match &args[0] {
27950 Value::String(s) => s.as_str().to_string(),
27951 _ => return Err(RuntimeError::new("vec_embedding requires string")),
27952 };
27953
27954 let mut embedding = Vec::new();
27957 let dimension = 384; for i in 0..dimension {
27960 let hash = text.bytes().enumerate().fold(0u64, |acc, (j, b)| {
27961 acc.wrapping_add((b as u64).wrapping_mul((i + j + 1) as u64))
27962 });
27963 let value = ((hash % 10000) as f64 / 10000.0) * 2.0 - 1.0; embedding.push(Value::Float(value));
27965 }
27966
27967 let result = Value::Array(Rc::new(RefCell::new(embedding)));
27968
27969 Ok(Value::Evidential {
27971 value: Box::new(result),
27972 evidence: Evidence::Uncertain,
27973 })
27974 });
27975
27976 define(interp, "vec_cosine_similarity", Some(2), |_, args| {
27978 let vec_a = match &args[0] {
27979 Value::Array(arr) => arr.borrow().clone(),
27980 Value::Evidential { value, .. } => {
27981 if let Value::Array(arr) = value.as_ref() {
27982 arr.borrow().clone()
27983 } else {
27984 return Err(RuntimeError::new("vec_cosine_similarity requires arrays"));
27985 }
27986 }
27987 _ => return Err(RuntimeError::new("vec_cosine_similarity requires arrays")),
27988 };
27989
27990 let vec_b = match &args[1] {
27991 Value::Array(arr) => arr.borrow().clone(),
27992 Value::Evidential { value, .. } => {
27993 if let Value::Array(arr) = value.as_ref() {
27994 arr.borrow().clone()
27995 } else {
27996 return Err(RuntimeError::new("vec_cosine_similarity requires arrays"));
27997 }
27998 }
27999 _ => return Err(RuntimeError::new("vec_cosine_similarity requires arrays")),
28000 };
28001
28002 if vec_a.len() != vec_b.len() {
28003 return Err(RuntimeError::new("Vectors must have same dimension"));
28004 }
28005
28006 let mut dot = 0.0;
28007 let mut mag_a = 0.0;
28008 let mut mag_b = 0.0;
28009
28010 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
28011 let a_val = match a {
28012 Value::Float(f) => *f,
28013 Value::Int(i) => *i as f64,
28014 _ => 0.0,
28015 };
28016 let b_val = match b {
28017 Value::Float(f) => *f,
28018 Value::Int(i) => *i as f64,
28019 _ => 0.0,
28020 };
28021
28022 dot += a_val * b_val;
28023 mag_a += a_val * a_val;
28024 mag_b += b_val * b_val;
28025 }
28026
28027 let similarity = if mag_a > 0.0 && mag_b > 0.0 {
28028 dot / (mag_a.sqrt() * mag_b.sqrt())
28029 } else {
28030 0.0
28031 };
28032
28033 Ok(Value::Float(similarity))
28034 });
28035
28036 define(interp, "vec_euclidean_distance", Some(2), |_, args| {
28038 let vec_a = match &args[0] {
28039 Value::Array(arr) => arr.borrow().clone(),
28040 Value::Evidential { value, .. } => {
28041 if let Value::Array(arr) = value.as_ref() {
28042 arr.borrow().clone()
28043 } else {
28044 return Err(RuntimeError::new("vec_euclidean_distance requires arrays"));
28045 }
28046 }
28047 _ => return Err(RuntimeError::new("vec_euclidean_distance requires arrays")),
28048 };
28049
28050 let vec_b = match &args[1] {
28051 Value::Array(arr) => arr.borrow().clone(),
28052 Value::Evidential { value, .. } => {
28053 if let Value::Array(arr) = value.as_ref() {
28054 arr.borrow().clone()
28055 } else {
28056 return Err(RuntimeError::new("vec_euclidean_distance requires arrays"));
28057 }
28058 }
28059 _ => return Err(RuntimeError::new("vec_euclidean_distance requires arrays")),
28060 };
28061
28062 if vec_a.len() != vec_b.len() {
28063 return Err(RuntimeError::new("Vectors must have same dimension"));
28064 }
28065
28066 let mut sum_sq = 0.0;
28067 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
28068 let a_val = match a {
28069 Value::Float(f) => *f,
28070 Value::Int(i) => *i as f64,
28071 _ => 0.0,
28072 };
28073 let b_val = match b {
28074 Value::Float(f) => *f,
28075 Value::Int(i) => *i as f64,
28076 _ => 0.0,
28077 };
28078 let diff = a_val - b_val;
28079 sum_sq += diff * diff;
28080 }
28081
28082 Ok(Value::Float(sum_sq.sqrt()))
28083 });
28084
28085 define(interp, "vec_dot_product", Some(2), |_, args| {
28087 let vec_a = match &args[0] {
28088 Value::Array(arr) => arr.borrow().clone(),
28089 _ => return Err(RuntimeError::new("vec_dot_product requires arrays")),
28090 };
28091
28092 let vec_b = match &args[1] {
28093 Value::Array(arr) => arr.borrow().clone(),
28094 _ => return Err(RuntimeError::new("vec_dot_product requires arrays")),
28095 };
28096
28097 if vec_a.len() != vec_b.len() {
28098 return Err(RuntimeError::new("Vectors must have same dimension"));
28099 }
28100
28101 let mut dot = 0.0;
28102 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
28103 let a_val = match a {
28104 Value::Float(f) => *f,
28105 Value::Int(i) => *i as f64,
28106 _ => 0.0,
28107 };
28108 let b_val = match b {
28109 Value::Float(f) => *f,
28110 Value::Int(i) => *i as f64,
28111 _ => 0.0,
28112 };
28113 dot += a_val * b_val;
28114 }
28115
28116 Ok(Value::Float(dot))
28117 });
28118
28119 define(interp, "vec_normalize", Some(1), |_, args| {
28121 let vec = match &args[0] {
28122 Value::Array(arr) => arr.borrow().clone(),
28123 _ => return Err(RuntimeError::new("vec_normalize requires array")),
28124 };
28125
28126 let mut mag = 0.0;
28127 for v in vec.iter() {
28128 let val = match v {
28129 Value::Float(f) => *f,
28130 Value::Int(i) => *i as f64,
28131 _ => 0.0,
28132 };
28133 mag += val * val;
28134 }
28135 mag = mag.sqrt();
28136
28137 if mag == 0.0 {
28138 return Ok(Value::Array(Rc::new(RefCell::new(vec))));
28139 }
28140
28141 let normalized: Vec<Value> = vec
28142 .iter()
28143 .map(|v| {
28144 let val = match v {
28145 Value::Float(f) => *f,
28146 Value::Int(i) => *i as f64,
28147 _ => 0.0,
28148 };
28149 Value::Float(val / mag)
28150 })
28151 .collect();
28152
28153 Ok(Value::Array(Rc::new(RefCell::new(normalized))))
28154 });
28155
28156 define(interp, "vec_search", Some(3), |_, args| {
28158 let query = match &args[0] {
28159 Value::Array(arr) => arr.borrow().clone(),
28160 Value::Evidential { value, .. } => {
28161 if let Value::Array(arr) = value.as_ref() {
28162 arr.borrow().clone()
28163 } else {
28164 return Err(RuntimeError::new("vec_search query must be array"));
28165 }
28166 }
28167 _ => return Err(RuntimeError::new("vec_search query must be array")),
28168 };
28169
28170 let corpus = match &args[1] {
28171 Value::Array(arr) => arr.borrow().clone(),
28172 _ => {
28173 return Err(RuntimeError::new(
28174 "vec_search corpus must be array of vectors",
28175 ))
28176 }
28177 };
28178
28179 let k = match &args[2] {
28180 Value::Int(n) => *n as usize,
28181 _ => return Err(RuntimeError::new("vec_search k must be integer")),
28182 };
28183
28184 let mut similarities: Vec<(usize, f64)> = Vec::new();
28186
28187 for (i, item) in corpus.iter().enumerate() {
28188 let vec_b = match item {
28189 Value::Array(arr) => arr.borrow().clone(),
28190 Value::Map(m) => {
28191 if let Some(Value::Array(arr)) = m.borrow().get("vector") {
28193 arr.borrow().clone()
28194 } else {
28195 continue;
28196 }
28197 }
28198 _ => continue,
28199 };
28200
28201 if vec_b.len() != query.len() {
28202 continue;
28203 }
28204
28205 let mut dot = 0.0;
28206 let mut mag_a = 0.0;
28207 let mut mag_b = 0.0;
28208
28209 for (a, b) in query.iter().zip(vec_b.iter()) {
28210 let a_val = match a {
28211 Value::Float(f) => *f,
28212 Value::Int(i) => *i as f64,
28213 _ => 0.0,
28214 };
28215 let b_val = match b {
28216 Value::Float(f) => *f,
28217 Value::Int(i) => *i as f64,
28218 _ => 0.0,
28219 };
28220 dot += a_val * b_val;
28221 mag_a += a_val * a_val;
28222 mag_b += b_val * b_val;
28223 }
28224
28225 let similarity = if mag_a > 0.0 && mag_b > 0.0 {
28226 dot / (mag_a.sqrt() * mag_b.sqrt())
28227 } else {
28228 0.0
28229 };
28230
28231 similarities.push((i, similarity));
28232 }
28233
28234 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
28236
28237 let results: Vec<Value> = similarities
28239 .iter()
28240 .take(k)
28241 .map(|(idx, sim)| {
28242 let mut result = HashMap::new();
28243 result.insert("index".to_string(), Value::Int(*idx as i64));
28244 result.insert("similarity".to_string(), Value::Float(*sim));
28245 result.insert(
28246 "item".to_string(),
28247 corpus.get(*idx).cloned().unwrap_or(Value::Null),
28248 );
28249 Value::Map(Rc::new(RefCell::new(result)))
28250 })
28251 .collect();
28252
28253 Ok(Value::Array(Rc::new(RefCell::new(results))))
28254 });
28255
28256 define(interp, "vec_store", Some(0), |_, _args| {
28258 let mut store = HashMap::new();
28259 store.insert(
28260 "vectors".to_string(),
28261 Value::Array(Rc::new(RefCell::new(Vec::new()))),
28262 );
28263 store.insert(
28264 "metadata".to_string(),
28265 Value::Array(Rc::new(RefCell::new(Vec::new()))),
28266 );
28267 store.insert("count".to_string(), Value::Int(0));
28268 Ok(Value::Map(Rc::new(RefCell::new(store))))
28269 });
28270
28271 define(interp, "vec_store_add", Some(3), |_, args| {
28273 let store = match &args[0] {
28274 Value::Map(m) => m.clone(),
28275 _ => return Err(RuntimeError::new("vec_store_add requires store")),
28276 };
28277
28278 let vector = args[1].clone();
28279 let metadata = args[2].clone();
28280
28281 let mut store_ref = store.borrow_mut();
28282
28283 if let Some(Value::Array(vectors)) = store_ref.get("vectors") {
28284 vectors.borrow_mut().push(vector);
28285 }
28286 if let Some(Value::Array(metas)) = store_ref.get("metadata") {
28287 metas.borrow_mut().push(metadata);
28288 }
28289
28290 let new_count = store_ref
28291 .get("count")
28292 .and_then(|v| {
28293 if let Value::Int(n) = v {
28294 Some(*n)
28295 } else {
28296 None
28297 }
28298 })
28299 .unwrap_or(0)
28300 + 1;
28301 store_ref.insert("count".to_string(), Value::Int(new_count));
28302
28303 Ok(Value::Int(new_count))
28304 });
28305
28306 define(interp, "vec_store_search", Some(3), |_, args| {
28308 let store = match &args[0] {
28309 Value::Map(m) => m.borrow().clone(),
28310 _ => return Err(RuntimeError::new("vec_store_search requires store")),
28311 };
28312
28313 let query = match &args[1] {
28314 Value::Array(arr) => arr.borrow().clone(),
28315 Value::Evidential { value, .. } => {
28316 if let Value::Array(arr) = value.as_ref() {
28317 arr.borrow().clone()
28318 } else {
28319 return Err(RuntimeError::new("Query must be array"));
28320 }
28321 }
28322 _ => return Err(RuntimeError::new("Query must be array")),
28323 };
28324
28325 let k = match &args[2] {
28326 Value::Int(n) => *n as usize,
28327 _ => return Err(RuntimeError::new("k must be integer")),
28328 };
28329
28330 let vectors = store
28331 .get("vectors")
28332 .and_then(|v| {
28333 if let Value::Array(arr) = v {
28334 Some(arr.borrow().clone())
28335 } else {
28336 None
28337 }
28338 })
28339 .unwrap_or_default();
28340
28341 let metadata = store
28342 .get("metadata")
28343 .and_then(|v| {
28344 if let Value::Array(arr) = v {
28345 Some(arr.borrow().clone())
28346 } else {
28347 None
28348 }
28349 })
28350 .unwrap_or_default();
28351
28352 let mut similarities: Vec<(usize, f64)> = Vec::new();
28353
28354 for (i, vec_val) in vectors.iter().enumerate() {
28355 let vec_b = match vec_val {
28356 Value::Array(arr) => arr.borrow().clone(),
28357 Value::Evidential { value, .. } => {
28358 if let Value::Array(arr) = value.as_ref() {
28359 arr.borrow().clone()
28360 } else {
28361 continue;
28362 }
28363 }
28364 _ => continue,
28365 };
28366
28367 if vec_b.len() != query.len() {
28368 continue;
28369 }
28370
28371 let mut dot = 0.0;
28372 let mut mag_a = 0.0;
28373 let mut mag_b = 0.0;
28374
28375 for (a, b) in query.iter().zip(vec_b.iter()) {
28376 let a_val = match a {
28377 Value::Float(f) => *f,
28378 Value::Int(i) => *i as f64,
28379 _ => 0.0,
28380 };
28381 let b_val = match b {
28382 Value::Float(f) => *f,
28383 Value::Int(i) => *i as f64,
28384 _ => 0.0,
28385 };
28386 dot += a_val * b_val;
28387 mag_a += a_val * a_val;
28388 mag_b += b_val * b_val;
28389 }
28390
28391 let sim = if mag_a > 0.0 && mag_b > 0.0 {
28392 dot / (mag_a.sqrt() * mag_b.sqrt())
28393 } else {
28394 0.0
28395 };
28396 similarities.push((i, sim));
28397 }
28398
28399 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
28400
28401 let results: Vec<Value> = similarities
28402 .iter()
28403 .take(k)
28404 .map(|(idx, sim)| {
28405 let mut result = HashMap::new();
28406 result.insert("index".to_string(), Value::Int(*idx as i64));
28407 result.insert("similarity".to_string(), Value::Float(*sim));
28408 result.insert(
28409 "vector".to_string(),
28410 vectors.get(*idx).cloned().unwrap_or(Value::Null),
28411 );
28412 result.insert(
28413 "metadata".to_string(),
28414 metadata.get(*idx).cloned().unwrap_or(Value::Null),
28415 );
28416 Value::Map(Rc::new(RefCell::new(result)))
28417 })
28418 .collect();
28419
28420 Ok(Value::Array(Rc::new(RefCell::new(results))))
28421 });
28422}
28423
28424thread_local! {
28430 static AGENT_REGISTRY: RefCell<HashMap<String, AgentInfo>> = RefCell::new(HashMap::new());
28431 static AGENT_MESSAGES: RefCell<HashMap<String, Vec<AgentMessage>>> = RefCell::new(HashMap::new());
28432}
28433
28434#[derive(Clone)]
28435struct AgentInfo {
28436 id: String,
28437 agent_type: String,
28438 state: HashMap<String, Value>,
28439 capabilities: Vec<String>,
28440 created_at: u64,
28441}
28442
28443#[derive(Clone)]
28444struct AgentMessage {
28445 from: String,
28446 to: String,
28447 msg_type: String,
28448 content: Value,
28449 timestamp: u64,
28450}
28451
28452fn register_agent_swarm(interp: &mut Interpreter) {
28453 define(interp, "swarm_create_agent", Some(2), |_, args| {
28455 let agent_id = match &args[0] {
28456 Value::String(s) => s.as_str().to_string(),
28457 _ => return Err(RuntimeError::new("swarm_create_agent requires string id")),
28458 };
28459
28460 let agent_type = match &args[1] {
28461 Value::String(s) => s.as_str().to_string(),
28462 _ => return Err(RuntimeError::new("swarm_create_agent requires string type")),
28463 };
28464
28465 let now = std::time::SystemTime::now()
28466 .duration_since(std::time::UNIX_EPOCH)
28467 .unwrap_or_default()
28468 .as_secs();
28469
28470 let agent = AgentInfo {
28471 id: agent_id.clone(),
28472 agent_type,
28473 state: HashMap::new(),
28474 capabilities: Vec::new(),
28475 created_at: now,
28476 };
28477
28478 AGENT_REGISTRY.with(|registry| {
28479 registry.borrow_mut().insert(agent_id.clone(), agent);
28480 });
28481
28482 AGENT_MESSAGES.with(|messages| {
28483 messages.borrow_mut().insert(agent_id.clone(), Vec::new());
28484 });
28485
28486 let mut result = HashMap::new();
28487 result.insert("id".to_string(), Value::String(Rc::new(agent_id)));
28488 result.insert("created_at".to_string(), Value::Int(now as i64));
28489 Ok(Value::Map(Rc::new(RefCell::new(result))))
28490 });
28491
28492 define(interp, "swarm_add_capability", Some(2), |_, args| {
28494 let agent_id = match &args[0] {
28495 Value::String(s) => s.as_str().to_string(),
28496 Value::Map(m) => m
28497 .borrow()
28498 .get("id")
28499 .and_then(|v| {
28500 if let Value::String(s) = v {
28501 Some(s.as_str().to_string())
28502 } else {
28503 None
28504 }
28505 })
28506 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28507 _ => return Err(RuntimeError::new("swarm_add_capability requires agent")),
28508 };
28509
28510 let capability = match &args[1] {
28511 Value::String(s) => s.as_str().to_string(),
28512 _ => return Err(RuntimeError::new("capability must be string")),
28513 };
28514
28515 AGENT_REGISTRY.with(|registry| {
28516 if let Some(agent) = registry.borrow_mut().get_mut(&agent_id) {
28517 if !agent.capabilities.contains(&capability) {
28518 agent.capabilities.push(capability);
28519 }
28520 Ok(Value::Bool(true))
28521 } else {
28522 Err(RuntimeError::new(format!("Agent '{}' not found", agent_id)))
28523 }
28524 })
28525 });
28526
28527 define(interp, "swarm_send_message", Some(4), |_, args| {
28529 let from_id = match &args[0] {
28530 Value::String(s) => s.as_str().to_string(),
28531 Value::Map(m) => m
28532 .borrow()
28533 .get("id")
28534 .and_then(|v| {
28535 if let Value::String(s) = v {
28536 Some(s.as_str().to_string())
28537 } else {
28538 None
28539 }
28540 })
28541 .ok_or_else(|| RuntimeError::new("Invalid sender agent"))?,
28542 _ => {
28543 return Err(RuntimeError::new(
28544 "swarm_send_message requires sender agent",
28545 ))
28546 }
28547 };
28548
28549 let to_id = match &args[1] {
28550 Value::String(s) => s.as_str().to_string(),
28551 Value::Map(m) => m
28552 .borrow()
28553 .get("id")
28554 .and_then(|v| {
28555 if let Value::String(s) = v {
28556 Some(s.as_str().to_string())
28557 } else {
28558 None
28559 }
28560 })
28561 .ok_or_else(|| RuntimeError::new("Invalid receiver agent"))?,
28562 _ => {
28563 return Err(RuntimeError::new(
28564 "swarm_send_message requires receiver agent",
28565 ))
28566 }
28567 };
28568
28569 let msg_type = match &args[2] {
28570 Value::String(s) => s.as_str().to_string(),
28571 _ => return Err(RuntimeError::new("message type must be string")),
28572 };
28573
28574 let content = args[3].clone();
28575
28576 let now = std::time::SystemTime::now()
28577 .duration_since(std::time::UNIX_EPOCH)
28578 .unwrap_or_default()
28579 .as_secs();
28580
28581 let message = AgentMessage {
28582 from: from_id.clone(),
28583 to: to_id.clone(),
28584 msg_type,
28585 content,
28586 timestamp: now,
28587 };
28588
28589 AGENT_MESSAGES.with(|messages| {
28590 if let Some(queue) = messages.borrow_mut().get_mut(&to_id) {
28591 queue.push(message);
28592 Ok(Value::Bool(true))
28593 } else {
28594 Err(RuntimeError::new(format!("Agent '{}' not found", to_id)))
28595 }
28596 })
28597 });
28598
28599 define(interp, "swarm_receive_messages", Some(1), |_, args| {
28601 let agent_id = match &args[0] {
28602 Value::String(s) => s.as_str().to_string(),
28603 Value::Map(m) => m
28604 .borrow()
28605 .get("id")
28606 .and_then(|v| {
28607 if let Value::String(s) = v {
28608 Some(s.as_str().to_string())
28609 } else {
28610 None
28611 }
28612 })
28613 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28614 _ => return Err(RuntimeError::new("swarm_receive_messages requires agent")),
28615 };
28616
28617 AGENT_MESSAGES.with(|messages| {
28618 if let Some(queue) = messages.borrow_mut().get_mut(&agent_id) {
28619 let msgs: Vec<Value> = queue
28620 .drain(..)
28621 .map(|m| {
28622 let mut msg_map = HashMap::new();
28623 msg_map.insert("from".to_string(), Value::String(Rc::new(m.from)));
28624 msg_map.insert("to".to_string(), Value::String(Rc::new(m.to)));
28625 msg_map.insert("type".to_string(), Value::String(Rc::new(m.msg_type)));
28626 msg_map.insert("content".to_string(), m.content);
28627 msg_map.insert("timestamp".to_string(), Value::Int(m.timestamp as i64));
28628 Value::Map(Rc::new(RefCell::new(msg_map)))
28629 })
28630 .collect();
28631 Ok(Value::Array(Rc::new(RefCell::new(msgs))))
28632 } else {
28633 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
28634 }
28635 })
28636 });
28637
28638 define(interp, "swarm_broadcast", Some(3), |_, args| {
28640 let from_id = match &args[0] {
28641 Value::String(s) => s.as_str().to_string(),
28642 Value::Map(m) => m
28643 .borrow()
28644 .get("id")
28645 .and_then(|v| {
28646 if let Value::String(s) = v {
28647 Some(s.as_str().to_string())
28648 } else {
28649 None
28650 }
28651 })
28652 .ok_or_else(|| RuntimeError::new("Invalid sender agent"))?,
28653 _ => return Err(RuntimeError::new("swarm_broadcast requires sender agent")),
28654 };
28655
28656 let msg_type = match &args[1] {
28657 Value::String(s) => s.as_str().to_string(),
28658 _ => return Err(RuntimeError::new("message type must be string")),
28659 };
28660
28661 let content = args[2].clone();
28662
28663 let now = std::time::SystemTime::now()
28664 .duration_since(std::time::UNIX_EPOCH)
28665 .unwrap_or_default()
28666 .as_secs();
28667
28668 let agent_ids: Vec<String> = AGENT_REGISTRY.with(|registry| {
28670 registry
28671 .borrow()
28672 .keys()
28673 .filter(|id| *id != &from_id)
28674 .cloned()
28675 .collect()
28676 });
28677
28678 let mut count = 0;
28679 AGENT_MESSAGES.with(|messages| {
28680 let mut msgs = messages.borrow_mut();
28681 for to_id in agent_ids {
28682 if let Some(queue) = msgs.get_mut(&to_id) {
28683 queue.push(AgentMessage {
28684 from: from_id.clone(),
28685 to: to_id,
28686 msg_type: msg_type.clone(),
28687 content: content.clone(),
28688 timestamp: now,
28689 });
28690 count += 1;
28691 }
28692 }
28693 });
28694
28695 Ok(Value::Int(count))
28696 });
28697
28698 define(interp, "swarm_find_agents", Some(1), |_, args| {
28700 let capability = match &args[0] {
28701 Value::String(s) => s.as_str().to_string(),
28702 _ => {
28703 return Err(RuntimeError::new(
28704 "swarm_find_agents requires capability string",
28705 ))
28706 }
28707 };
28708
28709 let agents: Vec<Value> = AGENT_REGISTRY.with(|registry| {
28710 registry
28711 .borrow()
28712 .values()
28713 .filter(|agent| agent.capabilities.contains(&capability))
28714 .map(|agent| {
28715 let mut info = HashMap::new();
28716 info.insert("id".to_string(), Value::String(Rc::new(agent.id.clone())));
28717 info.insert(
28718 "type".to_string(),
28719 Value::String(Rc::new(agent.agent_type.clone())),
28720 );
28721 info.insert(
28722 "capabilities".to_string(),
28723 Value::Array(Rc::new(RefCell::new(
28724 agent
28725 .capabilities
28726 .iter()
28727 .map(|c| Value::String(Rc::new(c.clone())))
28728 .collect(),
28729 ))),
28730 );
28731 Value::Map(Rc::new(RefCell::new(info)))
28732 })
28733 .collect()
28734 });
28735
28736 Ok(Value::Array(Rc::new(RefCell::new(agents))))
28737 });
28738
28739 define(interp, "swarm_list_agents", Some(0), |_, _args| {
28741 let agents: Vec<Value> = AGENT_REGISTRY.with(|registry| {
28742 registry
28743 .borrow()
28744 .values()
28745 .map(|agent| {
28746 let mut info = HashMap::new();
28747 info.insert("id".to_string(), Value::String(Rc::new(agent.id.clone())));
28748 info.insert(
28749 "type".to_string(),
28750 Value::String(Rc::new(agent.agent_type.clone())),
28751 );
28752 info.insert(
28753 "capabilities".to_string(),
28754 Value::Array(Rc::new(RefCell::new(
28755 agent
28756 .capabilities
28757 .iter()
28758 .map(|c| Value::String(Rc::new(c.clone())))
28759 .collect(),
28760 ))),
28761 );
28762 info.insert(
28763 "created_at".to_string(),
28764 Value::Int(agent.created_at as i64),
28765 );
28766 Value::Map(Rc::new(RefCell::new(info)))
28767 })
28768 .collect()
28769 });
28770 Ok(Value::Array(Rc::new(RefCell::new(agents))))
28771 });
28772
28773 define(interp, "swarm_set_state", Some(3), |_, args| {
28775 let agent_id = match &args[0] {
28776 Value::String(s) => s.as_str().to_string(),
28777 Value::Map(m) => m
28778 .borrow()
28779 .get("id")
28780 .and_then(|v| {
28781 if let Value::String(s) = v {
28782 Some(s.as_str().to_string())
28783 } else {
28784 None
28785 }
28786 })
28787 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28788 _ => return Err(RuntimeError::new("swarm_set_state requires agent")),
28789 };
28790
28791 let key = match &args[1] {
28792 Value::String(s) => s.as_str().to_string(),
28793 _ => return Err(RuntimeError::new("key must be string")),
28794 };
28795
28796 let value = args[2].clone();
28797
28798 AGENT_REGISTRY.with(|registry| {
28799 if let Some(agent) = registry.borrow_mut().get_mut(&agent_id) {
28800 agent.state.insert(key, value);
28801 Ok(Value::Bool(true))
28802 } else {
28803 Err(RuntimeError::new(format!("Agent '{}' not found", agent_id)))
28804 }
28805 })
28806 });
28807
28808 define(interp, "swarm_get_state", Some(2), |_, args| {
28810 let agent_id = match &args[0] {
28811 Value::String(s) => s.as_str().to_string(),
28812 Value::Map(m) => m
28813 .borrow()
28814 .get("id")
28815 .and_then(|v| {
28816 if let Value::String(s) = v {
28817 Some(s.as_str().to_string())
28818 } else {
28819 None
28820 }
28821 })
28822 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28823 _ => return Err(RuntimeError::new("swarm_get_state requires agent")),
28824 };
28825
28826 let key = match &args[1] {
28827 Value::String(s) => s.as_str().to_string(),
28828 _ => return Err(RuntimeError::new("key must be string")),
28829 };
28830
28831 AGENT_REGISTRY.with(|registry| {
28832 if let Some(agent) = registry.borrow().get(&agent_id) {
28833 Ok(agent.state.get(&key).cloned().unwrap_or(Value::Null))
28834 } else {
28835 Ok(Value::Null)
28836 }
28837 })
28838 });
28839
28840 define(interp, "swarm_remove_agent", Some(1), |_, args| {
28842 let agent_id = match &args[0] {
28843 Value::String(s) => s.as_str().to_string(),
28844 Value::Map(m) => m
28845 .borrow()
28846 .get("id")
28847 .and_then(|v| {
28848 if let Value::String(s) = v {
28849 Some(s.as_str().to_string())
28850 } else {
28851 None
28852 }
28853 })
28854 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
28855 _ => return Err(RuntimeError::new("swarm_remove_agent requires agent")),
28856 };
28857
28858 let removed =
28859 AGENT_REGISTRY.with(|registry| registry.borrow_mut().remove(&agent_id).is_some());
28860
28861 AGENT_MESSAGES.with(|messages| {
28862 messages.borrow_mut().remove(&agent_id);
28863 });
28864
28865 Ok(Value::Bool(removed))
28866 });
28867
28868 define(interp, "swarm_consensus", Some(2), |_, args| {
28870 let topic = match &args[0] {
28871 Value::String(s) => s.as_str().to_string(),
28872 _ => return Err(RuntimeError::new("topic must be string")),
28873 };
28874
28875 let votes = match &args[1] {
28876 Value::Array(arr) => arr.borrow().clone(),
28877 _ => return Err(RuntimeError::new("votes must be array")),
28878 };
28879
28880 let mut vote_counts: HashMap<String, i64> = HashMap::new();
28882 for vote in votes.iter() {
28883 let vote_str = match vote {
28884 Value::String(s) => s.as_str().to_string(),
28885 Value::Bool(b) => b.to_string(),
28886 Value::Int(n) => n.to_string(),
28887 _ => continue,
28888 };
28889 *vote_counts.entry(vote_str).or_insert(0) += 1;
28890 }
28891
28892 let total = votes.len() as i64;
28894 let (winner, count) = vote_counts
28895 .iter()
28896 .max_by_key(|(_, &count)| count)
28897 .map(|(k, &v)| (k.clone(), v))
28898 .unwrap_or_default();
28899
28900 let mut result = HashMap::new();
28901 result.insert("topic".to_string(), Value::String(Rc::new(topic)));
28902 result.insert("winner".to_string(), Value::String(Rc::new(winner)));
28903 result.insert("votes".to_string(), Value::Int(count));
28904 result.insert("total".to_string(), Value::Int(total));
28905 result.insert("majority".to_string(), Value::Bool(count > total / 2));
28906
28907 Ok(Value::Map(Rc::new(RefCell::new(result))))
28908 });
28909}
28910
28911fn register_agent_reasoning(interp: &mut Interpreter) {
28916 define(interp, "reason_constraint", Some(3), |_, args| {
28918 let name = match &args[0] {
28919 Value::String(s) => s.as_str().to_string(),
28920 _ => return Err(RuntimeError::new("constraint name must be string")),
28921 };
28922
28923 let constraint_type = match &args[1] {
28924 Value::String(s) => s.as_str().to_string(),
28925 _ => return Err(RuntimeError::new("constraint type must be string")),
28926 };
28927
28928 let params = args[2].clone();
28929
28930 let mut constraint = HashMap::new();
28931 constraint.insert("name".to_string(), Value::String(Rc::new(name)));
28932 constraint.insert("type".to_string(), Value::String(Rc::new(constraint_type)));
28933 constraint.insert("params".to_string(), params);
28934 constraint.insert("satisfied".to_string(), Value::Bool(false));
28935
28936 Ok(Value::Map(Rc::new(RefCell::new(constraint))))
28937 });
28938
28939 define(interp, "reason_check_constraint", Some(2), |_, args| {
28941 let constraint = match &args[0] {
28942 Value::Map(m) => m.borrow().clone(),
28943 _ => {
28944 return Err(RuntimeError::new(
28945 "reason_check_constraint requires constraint",
28946 ))
28947 }
28948 };
28949
28950 let context = match &args[1] {
28951 Value::Map(m) => m.borrow().clone(),
28952 _ => {
28953 return Err(RuntimeError::new(
28954 "reason_check_constraint requires context",
28955 ))
28956 }
28957 };
28958
28959 let constraint_type = constraint
28960 .get("type")
28961 .and_then(|v| {
28962 if let Value::String(s) = v {
28963 Some(s.as_str())
28964 } else {
28965 None
28966 }
28967 })
28968 .unwrap_or("unknown");
28969
28970 let params = constraint.get("params").cloned().unwrap_or(Value::Null);
28971
28972 let satisfied = match constraint_type {
28973 "equals" => {
28974 if let Value::Map(p) = ¶ms {
28975 let p = p.borrow();
28976 let var_name = p
28977 .get("var")
28978 .and_then(|v| {
28979 if let Value::String(s) = v {
28980 Some(s.as_str().to_string())
28981 } else {
28982 None
28983 }
28984 })
28985 .unwrap_or_default();
28986 let expected = p.get("value").cloned().unwrap_or(Value::Null);
28987 let actual = context.get(&var_name).cloned().unwrap_or(Value::Null);
28988 values_equal_simple(&actual, &expected)
28989 } else {
28990 false
28991 }
28992 }
28993 "not_null" => {
28994 if let Value::Map(p) = ¶ms {
28995 let p = p.borrow();
28996 let var_name = p
28997 .get("var")
28998 .and_then(|v| {
28999 if let Value::String(s) = v {
29000 Some(s.as_str().to_string())
29001 } else {
29002 None
29003 }
29004 })
29005 .unwrap_or_default();
29006 !matches!(context.get(&var_name), None | Some(Value::Null))
29007 } else {
29008 false
29009 }
29010 }
29011 "range" => {
29012 if let Value::Map(p) = ¶ms {
29013 let p = p.borrow();
29014 let var_name = p
29015 .get("var")
29016 .and_then(|v| {
29017 if let Value::String(s) = v {
29018 Some(s.as_str().to_string())
29019 } else {
29020 None
29021 }
29022 })
29023 .unwrap_or_default();
29024 let min = p
29025 .get("min")
29026 .and_then(|v| match v {
29027 Value::Int(n) => Some(*n as f64),
29028 Value::Float(f) => Some(*f),
29029 _ => None,
29030 })
29031 .unwrap_or(f64::NEG_INFINITY);
29032 let max = p
29033 .get("max")
29034 .and_then(|v| match v {
29035 Value::Int(n) => Some(*n as f64),
29036 Value::Float(f) => Some(*f),
29037 _ => None,
29038 })
29039 .unwrap_or(f64::INFINITY);
29040 let actual = context
29041 .get(&var_name)
29042 .and_then(|v| match v {
29043 Value::Int(n) => Some(*n as f64),
29044 Value::Float(f) => Some(*f),
29045 _ => None,
29046 })
29047 .unwrap_or(0.0);
29048 actual >= min && actual <= max
29049 } else {
29050 false
29051 }
29052 }
29053 "contains" => {
29054 if let Value::Map(p) = ¶ms {
29055 let p = p.borrow();
29056 let var_name = p
29057 .get("var")
29058 .and_then(|v| {
29059 if let Value::String(s) = v {
29060 Some(s.as_str().to_string())
29061 } else {
29062 None
29063 }
29064 })
29065 .unwrap_or_default();
29066 let needle = p
29067 .get("value")
29068 .and_then(|v| {
29069 if let Value::String(s) = v {
29070 Some(s.as_str().to_string())
29071 } else {
29072 None
29073 }
29074 })
29075 .unwrap_or_default();
29076 let actual = context
29077 .get(&var_name)
29078 .and_then(|v| {
29079 if let Value::String(s) = v {
29080 Some(s.as_str().to_string())
29081 } else {
29082 None
29083 }
29084 })
29085 .unwrap_or_default();
29086 actual.contains(&needle)
29087 } else {
29088 false
29089 }
29090 }
29091 _ => false,
29092 };
29093
29094 let mut result = HashMap::new();
29095 result.insert("satisfied".to_string(), Value::Bool(satisfied));
29096 result.insert(
29097 "constraint".to_string(),
29098 Value::Map(Rc::new(RefCell::new(constraint))),
29099 );
29100
29101 Ok(Value::Map(Rc::new(RefCell::new(result))))
29102 });
29103
29104 define(interp, "reason_check_all", Some(2), |interp, args| {
29106 let constraints = match &args[0] {
29107 Value::Array(arr) => arr.borrow().clone(),
29108 _ => {
29109 return Err(RuntimeError::new(
29110 "reason_check_all requires constraints array",
29111 ))
29112 }
29113 };
29114
29115 let context = args[1].clone();
29116
29117 let mut all_satisfied = true;
29118 let mut results: Vec<Value> = Vec::new();
29119
29120 for constraint in constraints.iter() {
29121 if let Value::Map(c) = constraint {
29123 let c_type = c
29124 .borrow()
29125 .get("type")
29126 .and_then(|v| {
29127 if let Value::String(s) = v {
29128 Some(s.as_ref().clone())
29129 } else {
29130 None
29131 }
29132 })
29133 .unwrap_or_else(|| "unknown".to_string());
29134 let params = c.borrow().get("params").cloned().unwrap_or(Value::Null);
29135
29136 let ctx = match &context {
29137 Value::Map(m) => m.borrow().clone(),
29138 _ => HashMap::new(),
29139 };
29140
29141 let satisfied = match c_type.as_str() {
29142 "equals" => {
29143 if let Value::Map(p) = ¶ms {
29144 let p = p.borrow();
29145 let var_name = p
29146 .get("var")
29147 .and_then(|v| {
29148 if let Value::String(s) = v {
29149 Some(s.as_str().to_string())
29150 } else {
29151 None
29152 }
29153 })
29154 .unwrap_or_default();
29155 let expected = p.get("value").cloned().unwrap_or(Value::Null);
29156 let actual = ctx.get(&var_name).cloned().unwrap_or(Value::Null);
29157 values_equal_simple(&actual, &expected)
29158 } else {
29159 false
29160 }
29161 }
29162 "not_null" => {
29163 if let Value::Map(p) = ¶ms {
29164 let p = p.borrow();
29165 let var_name = p
29166 .get("var")
29167 .and_then(|v| {
29168 if let Value::String(s) = v {
29169 Some(s.as_str().to_string())
29170 } else {
29171 None
29172 }
29173 })
29174 .unwrap_or_default();
29175 !matches!(ctx.get(&var_name), None | Some(Value::Null))
29176 } else {
29177 false
29178 }
29179 }
29180 _ => true, };
29182
29183 if !satisfied {
29184 all_satisfied = false;
29185 }
29186
29187 let mut r = HashMap::new();
29188 r.insert("constraint".to_string(), constraint.clone());
29189 r.insert("satisfied".to_string(), Value::Bool(satisfied));
29190 results.push(Value::Map(Rc::new(RefCell::new(r))));
29191 }
29192 }
29193
29194 let _ = interp; let mut result = HashMap::new();
29197 result.insert("all_satisfied".to_string(), Value::Bool(all_satisfied));
29198 result.insert(
29199 "results".to_string(),
29200 Value::Array(Rc::new(RefCell::new(results))),
29201 );
29202 result.insert("total".to_string(), Value::Int(constraints.len() as i64));
29203
29204 Ok(Value::Map(Rc::new(RefCell::new(result))))
29205 });
29206
29207 define(interp, "reason_implies", Some(2), |_, args| {
29209 let antecedent = args[0].clone();
29210 let consequent = args[1].clone();
29211
29212 let mut implication = HashMap::new();
29213 implication.insert(
29214 "type".to_string(),
29215 Value::String(Rc::new("implication".to_string())),
29216 );
29217 implication.insert("if".to_string(), antecedent);
29218 implication.insert("then".to_string(), consequent);
29219
29220 Ok(Value::Map(Rc::new(RefCell::new(implication))))
29221 });
29222
29223 define(interp, "reason_and", None, |_, args| {
29225 let conditions: Vec<Value> = args.into_iter().collect();
29226
29227 let mut conjunction = HashMap::new();
29228 conjunction.insert(
29229 "type".to_string(),
29230 Value::String(Rc::new("and".to_string())),
29231 );
29232 conjunction.insert(
29233 "conditions".to_string(),
29234 Value::Array(Rc::new(RefCell::new(conditions))),
29235 );
29236
29237 Ok(Value::Map(Rc::new(RefCell::new(conjunction))))
29238 });
29239
29240 define(interp, "reason_or", None, |_, args| {
29242 let conditions: Vec<Value> = args.into_iter().collect();
29243
29244 let mut disjunction = HashMap::new();
29245 disjunction.insert("type".to_string(), Value::String(Rc::new("or".to_string())));
29246 disjunction.insert(
29247 "conditions".to_string(),
29248 Value::Array(Rc::new(RefCell::new(conditions))),
29249 );
29250
29251 Ok(Value::Map(Rc::new(RefCell::new(disjunction))))
29252 });
29253
29254 define(interp, "reason_not", Some(1), |_, args| {
29256 let condition = args[0].clone();
29257
29258 let mut negation = HashMap::new();
29259 negation.insert(
29260 "type".to_string(),
29261 Value::String(Rc::new("not".to_string())),
29262 );
29263 negation.insert("condition".to_string(), condition);
29264
29265 Ok(Value::Map(Rc::new(RefCell::new(negation))))
29266 });
29267
29268 define(interp, "reason_evaluate", Some(2), |_, args| {
29270 let expr = match &args[0] {
29271 Value::Map(m) => m.borrow().clone(),
29272 Value::Bool(b) => return Ok(Value::Bool(*b)),
29273 _ => return Err(RuntimeError::new("reason_evaluate requires expression")),
29274 };
29275
29276 let context = match &args[1] {
29277 Value::Map(m) => m.borrow().clone(),
29278 _ => HashMap::new(),
29279 };
29280
29281 fn eval_expr(expr: &HashMap<String, Value>, ctx: &HashMap<String, Value>) -> bool {
29282 let expr_type = expr
29283 .get("type")
29284 .and_then(|v| {
29285 if let Value::String(s) = v {
29286 Some(s.as_str())
29287 } else {
29288 None
29289 }
29290 })
29291 .unwrap_or("unknown");
29292
29293 match expr_type {
29294 "and" => {
29295 if let Some(Value::Array(conditions)) = expr.get("conditions") {
29296 conditions.borrow().iter().all(|c| {
29297 if let Value::Map(m) = c {
29298 eval_expr(&m.borrow(), ctx)
29299 } else if let Value::Bool(b) = c {
29300 *b
29301 } else {
29302 false
29303 }
29304 })
29305 } else {
29306 false
29307 }
29308 }
29309 "or" => {
29310 if let Some(Value::Array(conditions)) = expr.get("conditions") {
29311 conditions.borrow().iter().any(|c| {
29312 if let Value::Map(m) = c {
29313 eval_expr(&m.borrow(), ctx)
29314 } else if let Value::Bool(b) = c {
29315 *b
29316 } else {
29317 false
29318 }
29319 })
29320 } else {
29321 false
29322 }
29323 }
29324 "not" => {
29325 if let Some(condition) = expr.get("condition") {
29326 if let Value::Map(m) = condition {
29327 !eval_expr(&m.borrow(), ctx)
29328 } else if let Value::Bool(b) = condition {
29329 !b
29330 } else {
29331 false
29332 }
29333 } else {
29334 false
29335 }
29336 }
29337 "implication" => {
29338 let antecedent = if let Some(a) = expr.get("if") {
29339 if let Value::Map(m) = a {
29340 eval_expr(&m.borrow(), ctx)
29341 } else if let Value::Bool(b) = a {
29342 *b
29343 } else {
29344 false
29345 }
29346 } else {
29347 false
29348 };
29349
29350 let consequent = if let Some(c) = expr.get("then") {
29351 if let Value::Map(m) = c {
29352 eval_expr(&m.borrow(), ctx)
29353 } else if let Value::Bool(b) = c {
29354 *b
29355 } else {
29356 false
29357 }
29358 } else {
29359 false
29360 };
29361
29362 !antecedent || consequent
29364 }
29365 _ => false,
29366 }
29367 }
29368
29369 Ok(Value::Bool(eval_expr(&expr, &context)))
29370 });
29371
29372 define(interp, "reason_proof", Some(3), |_, args| {
29374 let step = match &args[0] {
29375 Value::String(s) => s.as_str().to_string(),
29376 _ => return Err(RuntimeError::new("proof step must be string")),
29377 };
29378
29379 let justification = match &args[1] {
29380 Value::String(s) => s.as_str().to_string(),
29381 _ => return Err(RuntimeError::new("justification must be string")),
29382 };
29383
29384 let conclusion = args[2].clone();
29385
29386 let now = std::time::SystemTime::now()
29387 .duration_since(std::time::UNIX_EPOCH)
29388 .unwrap_or_default()
29389 .as_secs();
29390
29391 let mut proof = HashMap::new();
29392 proof.insert("step".to_string(), Value::String(Rc::new(step)));
29393 proof.insert(
29394 "justification".to_string(),
29395 Value::String(Rc::new(justification)),
29396 );
29397 proof.insert("conclusion".to_string(), conclusion);
29398 proof.insert("timestamp".to_string(), Value::Int(now as i64));
29399
29400 Ok(Value::Map(Rc::new(RefCell::new(proof))))
29401 });
29402
29403 define(interp, "reason_chain", None, |_, args| {
29405 let steps: Vec<Value> = args.into_iter().collect();
29406
29407 let mut chain = HashMap::new();
29408 chain.insert(
29409 "type".to_string(),
29410 Value::String(Rc::new("proof_chain".to_string())),
29411 );
29412 chain.insert(
29413 "steps".to_string(),
29414 Value::Array(Rc::new(RefCell::new(steps.clone()))),
29415 );
29416 chain.insert("length".to_string(), Value::Int(steps.len() as i64));
29417
29418 let final_conclusion = steps
29420 .last()
29421 .and_then(|s| {
29422 if let Value::Map(m) = s {
29423 m.borrow().get("conclusion").cloned()
29424 } else {
29425 None
29426 }
29427 })
29428 .unwrap_or(Value::Null);
29429 chain.insert("final_conclusion".to_string(), final_conclusion);
29430
29431 Ok(Value::Map(Rc::new(RefCell::new(chain))))
29432 });
29433
29434 define(interp, "reason_hypothesis", Some(2), |_, args| {
29436 let claim = match &args[0] {
29437 Value::String(s) => s.as_str().to_string(),
29438 _ => return Err(RuntimeError::new("hypothesis claim must be string")),
29439 };
29440
29441 let required_evidence = match &args[1] {
29442 Value::Array(arr) => arr.clone(),
29443 _ => return Err(RuntimeError::new("required evidence must be array")),
29444 };
29445
29446 let mut hypothesis = HashMap::new();
29447 hypothesis.insert("claim".to_string(), Value::String(Rc::new(claim)));
29448 hypothesis.insert(
29449 "required_evidence".to_string(),
29450 Value::Array(required_evidence),
29451 );
29452 hypothesis.insert(
29453 "status".to_string(),
29454 Value::String(Rc::new("unverified".to_string())),
29455 );
29456 hypothesis.insert("confidence".to_string(), Value::Float(0.0));
29457
29458 Ok(Value::Map(Rc::new(RefCell::new(hypothesis))))
29459 });
29460
29461 define(interp, "reason_verify_hypothesis", Some(2), |_, args| {
29463 let hypothesis = match &args[0] {
29464 Value::Map(m) => m.clone(),
29465 _ => {
29466 return Err(RuntimeError::new(
29467 "reason_verify_hypothesis requires hypothesis",
29468 ))
29469 }
29470 };
29471
29472 let evidence = match &args[1] {
29473 Value::Map(m) => m.borrow().clone(),
29474 _ => {
29475 return Err(RuntimeError::new(
29476 "reason_verify_hypothesis requires evidence map",
29477 ))
29478 }
29479 };
29480
29481 let required = hypothesis
29482 .borrow()
29483 .get("required_evidence")
29484 .and_then(|v| {
29485 if let Value::Array(arr) = v {
29486 Some(arr.borrow().clone())
29487 } else {
29488 None
29489 }
29490 })
29491 .unwrap_or_default();
29492
29493 let mut found = 0;
29494 for req in required.iter() {
29495 if let Value::String(key) = req {
29496 if evidence.contains_key(key.as_str()) {
29497 found += 1;
29498 }
29499 }
29500 }
29501
29502 let total = required.len();
29503 let confidence = if total > 0 {
29504 found as f64 / total as f64
29505 } else {
29506 0.0
29507 };
29508 let verified = found == total && total > 0;
29509
29510 {
29511 let mut h = hypothesis.borrow_mut();
29512 h.insert("confidence".to_string(), Value::Float(confidence));
29513 h.insert(
29514 "status".to_string(),
29515 Value::String(Rc::new(if verified {
29516 "verified".to_string()
29517 } else {
29518 "unverified".to_string()
29519 })),
29520 );
29521 }
29522
29523 let mut result = HashMap::new();
29524 result.insert("verified".to_string(), Value::Bool(verified));
29525 result.insert("confidence".to_string(), Value::Float(confidence));
29526 result.insert("found".to_string(), Value::Int(found as i64));
29527 result.insert("required".to_string(), Value::Int(total as i64));
29528 result.insert("hypothesis".to_string(), Value::Map(hypothesis));
29529
29530 Ok(Value::Map(Rc::new(RefCell::new(result))))
29531 });
29532}
29533
29534fn register_terminal(interp: &mut Interpreter) {
29541 const RESET: &str = "\x1b[0m";
29543 const BOLD: &str = "\x1b[1m";
29544 const DIM: &str = "\x1b[2m";
29545 const ITALIC: &str = "\x1b[3m";
29546 const UNDERLINE: &str = "\x1b[4m";
29547
29548 const FG_BLACK: &str = "\x1b[30m";
29550 const FG_RED: &str = "\x1b[31m";
29551 const FG_GREEN: &str = "\x1b[32m";
29552 const FG_YELLOW: &str = "\x1b[33m";
29553 const FG_BLUE: &str = "\x1b[34m";
29554 const FG_MAGENTA: &str = "\x1b[35m";
29555 const FG_CYAN: &str = "\x1b[36m";
29556 const FG_WHITE: &str = "\x1b[37m";
29557
29558 const FG_BRIGHT_BLACK: &str = "\x1b[90m";
29560 const FG_BRIGHT_RED: &str = "\x1b[91m";
29561 const FG_BRIGHT_GREEN: &str = "\x1b[92m";
29562 const FG_BRIGHT_YELLOW: &str = "\x1b[93m";
29563 const FG_BRIGHT_BLUE: &str = "\x1b[94m";
29564 const FG_BRIGHT_MAGENTA: &str = "\x1b[95m";
29565 const FG_BRIGHT_CYAN: &str = "\x1b[96m";
29566 const FG_BRIGHT_WHITE: &str = "\x1b[97m";
29567
29568 define(interp, "term_reset", Some(0), |_, _| {
29570 Ok(Value::String(Rc::new(RESET.to_string())))
29571 });
29572
29573 define(interp, "term_bold", Some(1), |_, args| {
29575 let text = match &args[0] {
29576 Value::String(s) => (**s).clone(),
29577 other => format!("{}", other),
29578 };
29579 Ok(Value::String(Rc::new(format!("{}{}{}", BOLD, text, RESET))))
29580 });
29581
29582 define(interp, "term_dim", Some(1), |_, args| {
29584 let text = match &args[0] {
29585 Value::String(s) => (**s).clone(),
29586 other => format!("{}", other),
29587 };
29588 Ok(Value::String(Rc::new(format!("{}{}{}", DIM, text, RESET))))
29589 });
29590
29591 define(interp, "term_italic", Some(1), |_, args| {
29593 let text = match &args[0] {
29594 Value::String(s) => (**s).clone(),
29595 other => format!("{}", other),
29596 };
29597 Ok(Value::String(Rc::new(format!(
29598 "{}{}{}",
29599 ITALIC, text, RESET
29600 ))))
29601 });
29602
29603 define(interp, "term_underline", Some(1), |_, args| {
29605 let text = match &args[0] {
29606 Value::String(s) => (**s).clone(),
29607 other => format!("{}", other),
29608 };
29609 Ok(Value::String(Rc::new(format!(
29610 "{}{}{}",
29611 UNDERLINE, text, RESET
29612 ))))
29613 });
29614
29615 define(interp, "term_red", Some(1), |_, args| {
29617 let text = match &args[0] {
29618 Value::String(s) => (**s).clone(),
29619 other => format!("{}", other),
29620 };
29621 Ok(Value::String(Rc::new(format!(
29622 "{}{}{}",
29623 FG_RED, text, RESET
29624 ))))
29625 });
29626
29627 define(interp, "term_green", Some(1), |_, args| {
29629 let text = match &args[0] {
29630 Value::String(s) => (**s).clone(),
29631 other => format!("{}", other),
29632 };
29633 Ok(Value::String(Rc::new(format!(
29634 "{}{}{}",
29635 FG_GREEN, text, RESET
29636 ))))
29637 });
29638
29639 define(interp, "term_yellow", Some(1), |_, args| {
29641 let text = match &args[0] {
29642 Value::String(s) => (**s).clone(),
29643 other => format!("{}", other),
29644 };
29645 Ok(Value::String(Rc::new(format!(
29646 "{}{}{}",
29647 FG_YELLOW, text, RESET
29648 ))))
29649 });
29650
29651 define(interp, "term_blue", Some(1), |_, args| {
29653 let text = match &args[0] {
29654 Value::String(s) => (**s).clone(),
29655 other => format!("{}", other),
29656 };
29657 Ok(Value::String(Rc::new(format!(
29658 "{}{}{}",
29659 FG_BLUE, text, RESET
29660 ))))
29661 });
29662
29663 define(interp, "term_magenta", Some(1), |_, args| {
29665 let text = match &args[0] {
29666 Value::String(s) => (**s).clone(),
29667 other => format!("{}", other),
29668 };
29669 Ok(Value::String(Rc::new(format!(
29670 "{}{}{}",
29671 FG_MAGENTA, text, RESET
29672 ))))
29673 });
29674
29675 define(interp, "term_cyan", Some(1), |_, args| {
29677 let text = match &args[0] {
29678 Value::String(s) => (**s).clone(),
29679 other => format!("{}", other),
29680 };
29681 Ok(Value::String(Rc::new(format!(
29682 "{}{}{}",
29683 FG_CYAN, text, RESET
29684 ))))
29685 });
29686
29687 define(interp, "term_white", Some(1), |_, args| {
29689 let text = match &args[0] {
29690 Value::String(s) => (**s).clone(),
29691 other => format!("{}", other),
29692 };
29693 Ok(Value::String(Rc::new(format!(
29694 "{}{}{}",
29695 FG_WHITE, text, RESET
29696 ))))
29697 });
29698
29699 define(interp, "term_black", Some(1), |_, args| {
29701 let text = match &args[0] {
29702 Value::String(s) => (**s).clone(),
29703 other => format!("{}", other),
29704 };
29705 Ok(Value::String(Rc::new(format!(
29706 "{}{}{}",
29707 FG_BLACK, text, RESET
29708 ))))
29709 });
29710
29711 define(interp, "term_bright_red", Some(1), |_, args| {
29713 let text = match &args[0] {
29714 Value::String(s) => (**s).clone(),
29715 other => format!("{}", other),
29716 };
29717 Ok(Value::String(Rc::new(format!(
29718 "{}{}{}",
29719 FG_BRIGHT_RED, text, RESET
29720 ))))
29721 });
29722
29723 define(interp, "term_bright_green", Some(1), |_, args| {
29725 let text = match &args[0] {
29726 Value::String(s) => (**s).clone(),
29727 other => format!("{}", other),
29728 };
29729 Ok(Value::String(Rc::new(format!(
29730 "{}{}{}",
29731 FG_BRIGHT_GREEN, text, RESET
29732 ))))
29733 });
29734
29735 define(interp, "term_bright_cyan", Some(1), |_, args| {
29737 let text = match &args[0] {
29738 Value::String(s) => (**s).clone(),
29739 other => format!("{}", other),
29740 };
29741 Ok(Value::String(Rc::new(format!(
29742 "{}{}{}",
29743 FG_BRIGHT_CYAN, text, RESET
29744 ))))
29745 });
29746
29747 define(interp, "term_style", None, |_, args| {
29749 if args.is_empty() {
29750 return Err(RuntimeError::new(
29751 "term_style requires at least text argument",
29752 ));
29753 }
29754 let text = match &args[0] {
29755 Value::String(s) => (**s).clone(),
29756 other => format!("{}", other),
29757 };
29758
29759 let mut prefix = String::new();
29760 for arg in &args[1..] {
29761 if let Value::String(style) = arg {
29762 match style.as_str() {
29763 "bold" => prefix.push_str(BOLD),
29764 "dim" => prefix.push_str(DIM),
29765 "italic" => prefix.push_str(ITALIC),
29766 "underline" => prefix.push_str(UNDERLINE),
29767 "red" => prefix.push_str(FG_RED),
29768 "green" => prefix.push_str(FG_GREEN),
29769 "yellow" => prefix.push_str(FG_YELLOW),
29770 "blue" => prefix.push_str(FG_BLUE),
29771 "magenta" => prefix.push_str(FG_MAGENTA),
29772 "cyan" => prefix.push_str(FG_CYAN),
29773 "white" => prefix.push_str(FG_WHITE),
29774 "black" => prefix.push_str(FG_BLACK),
29775 "bright_red" => prefix.push_str(FG_BRIGHT_RED),
29776 "bright_green" => prefix.push_str(FG_BRIGHT_GREEN),
29777 "bright_yellow" => prefix.push_str(FG_BRIGHT_YELLOW),
29778 "bright_blue" => prefix.push_str(FG_BRIGHT_BLUE),
29779 "bright_magenta" => prefix.push_str(FG_BRIGHT_MAGENTA),
29780 "bright_cyan" => prefix.push_str(FG_BRIGHT_CYAN),
29781 "bright_white" => prefix.push_str(FG_BRIGHT_WHITE),
29782 _ => {} }
29784 }
29785 }
29786
29787 Ok(Value::String(Rc::new(format!(
29788 "{}{}{}",
29789 prefix, text, RESET
29790 ))))
29791 });
29792
29793 define(interp, "term_progress_bar", Some(3), |_, args| {
29796 let current = match &args[0] {
29797 Value::Int(n) => *n as f64,
29798 Value::Float(f) => *f,
29799 _ => {
29800 return Err(RuntimeError::new(
29801 "term_progress_bar: current must be number",
29802 ))
29803 }
29804 };
29805 let total = match &args[1] {
29806 Value::Int(n) => *n as f64,
29807 Value::Float(f) => *f,
29808 _ => return Err(RuntimeError::new("term_progress_bar: total must be number")),
29809 };
29810 let width = match &args[2] {
29811 Value::Int(n) if *n > 0 => *n as usize,
29812 _ => {
29813 return Err(RuntimeError::new(
29814 "term_progress_bar: width must be positive integer",
29815 ))
29816 }
29817 };
29818
29819 let ratio = if total > 0.0 {
29820 (current / total).min(1.0).max(0.0)
29821 } else {
29822 0.0
29823 };
29824 let filled = (ratio * width as f64).round() as usize;
29825 let empty = width - filled;
29826 let percent = (ratio * 100.0).round() as i64;
29827
29828 let bar = format!("[{}{}] {}%", "█".repeat(filled), "░".repeat(empty), percent);
29829
29830 Ok(Value::String(Rc::new(bar)))
29831 });
29832
29833 define(interp, "term_spinner_frames", Some(0), |_, _| {
29835 let frames: Vec<Value> = vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
29836 .into_iter()
29837 .map(|s| Value::String(Rc::new(s.to_string())))
29838 .collect();
29839 Ok(Value::Array(Rc::new(RefCell::new(frames))))
29840 });
29841
29842 define(interp, "term_check", Some(0), |_, _| {
29844 Ok(Value::String(Rc::new(format!("{}✓{}", FG_GREEN, RESET))))
29845 });
29846
29847 define(interp, "term_cross", Some(0), |_, _| {
29849 Ok(Value::String(Rc::new(format!("{}✗{}", FG_RED, RESET))))
29850 });
29851
29852 define(interp, "term_arrow", Some(0), |_, _| {
29854 Ok(Value::String(Rc::new("→".to_string())))
29855 });
29856
29857 define(interp, "term_bullet", Some(0), |_, _| {
29859 Ok(Value::String(Rc::new("•".to_string())))
29860 });
29861
29862 define(interp, "term_emoji", Some(1), |_, args| {
29864 let name = match &args[0] {
29865 Value::String(s) => s.to_lowercase(),
29866 _ => return Err(RuntimeError::new("term_emoji requires string")),
29867 };
29868
29869 let emoji = match name.as_str() {
29870 "summon" | "install" => "🔮",
29871 "banish" | "uninstall" => "👋",
29872 "invoke" | "update" => "⚡",
29873 "awaken" | "start" => "🌅",
29874 "seal" | "stop" => "🔒",
29875 "check" | "ok" | "success" => "✓",
29876 "cross" | "error" | "fail" => "✗",
29877 "arrow" | "right" => "→",
29878 "warning" | "warn" => "⚠",
29879 "info" | "information" => "ℹ",
29880 "question" | "help" => "?",
29881 "star" => "★",
29882 "heart" => "❤",
29883 "fire" => "🔥",
29884 "rocket" => "🚀",
29885 "package" | "box" => "📦",
29886 "folder" | "directory" => "📁",
29887 "file" | "document" => "📄",
29888 "gear" | "settings" => "⚙",
29889 "search" | "seek" => "🔍",
29890 "download" => "⬇",
29891 "upload" => "⬆",
29892 "sync" | "refresh" => "🔄",
29893 "lock" | "locked" => "🔒",
29894 "unlock" | "unlocked" => "🔓",
29895 "key" => "🔑",
29896 "clock" | "time" => "🕐",
29897 "calendar" | "date" => "📅",
29898 "bell" | "notification" => "🔔",
29899 _ => "•",
29900 };
29901
29902 Ok(Value::String(Rc::new(emoji.to_string())))
29903 });
29904
29905 define(interp, "term_table_row", Some(2), |_, args| {
29907 let values = match &args[0] {
29908 Value::Array(arr) => arr.borrow().clone(),
29909 _ => return Err(RuntimeError::new("term_table_row: first arg must be array")),
29910 };
29911 let widths = match &args[1] {
29912 Value::Array(arr) => arr.borrow().clone(),
29913 _ => {
29914 return Err(RuntimeError::new(
29915 "term_table_row: second arg must be array",
29916 ))
29917 }
29918 };
29919
29920 if values.len() != widths.len() {
29921 return Err(RuntimeError::new(
29922 "term_table_row: arrays must have same length",
29923 ));
29924 }
29925
29926 let mut parts: Vec<String> = Vec::new();
29927 for (val, width) in values.iter().zip(widths.iter()) {
29928 let text = match val {
29929 Value::String(s) => (**s).clone(),
29930 other => format!("{}", other),
29931 };
29932 let w = match width {
29933 Value::Int(n) => *n as usize,
29934 _ => 10,
29935 };
29936 let formatted = if text.chars().count() > w {
29937 text.chars().take(w - 1).collect::<String>() + "…"
29938 } else {
29939 format!("{:<width$}", text, width = w)
29940 };
29941 parts.push(formatted);
29942 }
29943
29944 Ok(Value::String(Rc::new(parts.join(" │ "))))
29945 });
29946
29947 define(interp, "term_table_separator", Some(1), |_, args| {
29949 let widths = match &args[0] {
29950 Value::Array(arr) => arr.borrow().clone(),
29951 _ => return Err(RuntimeError::new("term_table_separator: arg must be array")),
29952 };
29953
29954 let parts: Vec<String> = widths
29955 .iter()
29956 .map(|w| {
29957 let width = match w {
29958 Value::Int(n) => *n as usize,
29959 _ => 10,
29960 };
29961 "─".repeat(width)
29962 })
29963 .collect();
29964
29965 Ok(Value::String(Rc::new(parts.join("─┼─"))))
29966 });
29967
29968 define(interp, "term_clear_line", Some(0), |_, _| {
29970 Ok(Value::String(Rc::new("\x1b[2K\r".to_string())))
29971 });
29972
29973 define(interp, "term_cursor_up", Some(1), |_, args| {
29975 let n = match &args[0] {
29976 Value::Int(n) if *n > 0 => *n,
29977 _ => 1,
29978 };
29979 Ok(Value::String(Rc::new(format!("\x1b[{}A", n))))
29980 });
29981
29982 define(interp, "term_cursor_down", Some(1), |_, args| {
29984 let n = match &args[0] {
29985 Value::Int(n) if *n > 0 => *n,
29986 _ => 1,
29987 };
29988 Ok(Value::String(Rc::new(format!("\x1b[{}B", n))))
29989 });
29990
29991 define(interp, "term_hide_cursor", Some(0), |_, _| {
29993 Ok(Value::String(Rc::new("\x1b[?25l".to_string())))
29994 });
29995
29996 define(interp, "term_show_cursor", Some(0), |_, _| {
29998 Ok(Value::String(Rc::new("\x1b[?25h".to_string())))
29999 });
30000
30001 define(interp, "term_is_tty", Some(0), |_, _| {
30003 use std::io::IsTerminal;
30004 Ok(Value::Bool(std::io::stdout().is_terminal()))
30005 });
30006}
30007#[cfg(test)]
30008mod tests {
30009 use super::*;
30010 use crate::Parser;
30011
30012 fn eval(source: &str) -> Result<Value, RuntimeError> {
30013 let mut parser = Parser::new(source);
30014 let file = parser
30015 .parse_file()
30016 .map_err(|e| RuntimeError::new(e.to_string()))?;
30017 let mut interp = Interpreter::new();
30018 register_stdlib(&mut interp);
30019 interp.execute(&file)
30020 }
30021
30022 #[test]
30025 fn test_math_functions() {
30026 assert!(matches!(
30027 eval("fn main() { return abs(-5); }"),
30028 Ok(Value::Int(5))
30029 ));
30030 assert!(matches!(
30031 eval("fn main() { return floor(3.7); }"),
30032 Ok(Value::Int(3))
30033 ));
30034 assert!(matches!(
30035 eval("fn main() { return ceil(3.2); }"),
30036 Ok(Value::Int(4))
30037 ));
30038 assert!(matches!(
30039 eval("fn main() { return max(3, 7); }"),
30040 Ok(Value::Int(7))
30041 ));
30042 assert!(matches!(
30043 eval("fn main() { return min(3, 7); }"),
30044 Ok(Value::Int(3))
30045 ));
30046 assert!(matches!(
30047 eval("fn main() { return round(3.5); }"),
30048 Ok(Value::Int(4))
30049 ));
30050 assert!(matches!(
30051 eval("fn main() { return sign(-5); }"),
30052 Ok(Value::Int(-1))
30053 ));
30054 assert!(matches!(
30055 eval("fn main() { return sign(0); }"),
30056 Ok(Value::Int(0))
30057 ));
30058 assert!(matches!(
30059 eval("fn main() { return sign(5); }"),
30060 Ok(Value::Int(1))
30061 ));
30062 }
30063
30064 #[test]
30065 fn test_math_advanced() {
30066 assert!(matches!(
30067 eval("fn main() { return pow(2, 10); }"),
30068 Ok(Value::Int(1024))
30069 ));
30070 assert!(
30071 matches!(eval("fn main() { return sqrt(16.0); }"), Ok(Value::Float(f)) if (f - 4.0).abs() < 0.001)
30072 );
30073 assert!(
30074 matches!(eval("fn main() { return log(2.718281828, 2.718281828); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.01)
30075 );
30076 assert!(
30077 matches!(eval("fn main() { return exp(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001)
30078 );
30079 }
30080
30081 #[test]
30082 fn test_trig_functions() {
30083 assert!(
30084 matches!(eval("fn main() { return sin(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001)
30085 );
30086 assert!(
30087 matches!(eval("fn main() { return cos(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001)
30088 );
30089 assert!(
30090 matches!(eval("fn main() { return tan(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001)
30091 );
30092 }
30093
30094 #[test]
30095 fn test_collection_functions() {
30096 assert!(matches!(
30097 eval("fn main() { return len([1, 2, 3]); }"),
30098 Ok(Value::Int(3))
30099 ));
30100 assert!(matches!(
30101 eval("fn main() { return first([1, 2, 3]); }"),
30102 Ok(Value::Int(1))
30103 ));
30104 assert!(matches!(
30105 eval("fn main() { return last([1, 2, 3]); }"),
30106 Ok(Value::Int(3))
30107 ));
30108 assert!(matches!(
30109 eval("fn main() { return len([]); }"),
30110 Ok(Value::Int(0))
30111 ));
30112 }
30113
30114 #[test]
30115 fn test_collection_nth() {
30116 assert!(matches!(
30117 eval("fn main() { return get([10, 20, 30], 1); }"),
30118 Ok(Value::Int(20))
30119 ));
30120 assert!(matches!(
30121 eval("fn main() { return get([10, 20, 30], 0); }"),
30122 Ok(Value::Int(10))
30123 ));
30124 }
30125
30126 #[test]
30127 fn test_collection_slice() {
30128 let result = eval("fn main() { return slice([1, 2, 3, 4, 5], 1, 3); }");
30129 assert!(matches!(result, Ok(Value::Array(_))));
30130 }
30131
30132 #[test]
30133 fn test_collection_concat() {
30134 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
30135 assert!(matches!(result, Ok(Value::Int(4))));
30136 }
30137
30138 #[test]
30139 fn test_string_functions() {
30140 assert!(
30141 matches!(eval(r#"fn main() { return upper("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "HELLO")
30142 );
30143 assert!(
30144 matches!(eval(r#"fn main() { return lower("HELLO"); }"#), Ok(Value::String(s)) if s.as_str() == "hello")
30145 );
30146 assert!(
30147 matches!(eval(r#"fn main() { return trim(" hi "); }"#), Ok(Value::String(s)) if s.as_str() == "hi")
30148 );
30149 }
30150
30151 #[test]
30152 fn test_string_split_join() {
30153 assert!(matches!(
30154 eval(r#"fn main() { return len(split("a,b,c", ",")); }"#),
30155 Ok(Value::Int(3))
30156 ));
30157 assert!(
30158 matches!(eval(r#"fn main() { return join(["a", "b"], "-"); }"#), Ok(Value::String(s)) if s.as_str() == "a-b")
30159 );
30160 }
30161
30162 #[test]
30163 fn test_string_contains() {
30164 assert!(matches!(
30165 eval(r#"fn main() { return contains("hello", "ell"); }"#),
30166 Ok(Value::Bool(true))
30167 ));
30168 assert!(matches!(
30169 eval(r#"fn main() { return contains("hello", "xyz"); }"#),
30170 Ok(Value::Bool(false))
30171 ));
30172 }
30173
30174 #[test]
30175 fn test_string_replace() {
30176 assert!(
30177 matches!(eval(r#"fn main() { return replace("hello", "l", "L"); }"#), Ok(Value::String(s)) if s.as_str() == "heLLo")
30178 );
30179 }
30180
30181 #[test]
30182 fn test_string_chars() {
30183 assert!(matches!(
30184 eval(r#"fn main() { return len(chars("hello")); }"#),
30185 Ok(Value::Int(5))
30186 ));
30187 }
30188
30189 #[test]
30190 fn test_evidence_functions() {
30191 let result = eval("fn main() { return evidence_of(uncertain(42)); }");
30192 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "uncertain"));
30193 }
30194
30195 #[test]
30198 fn test_interpolation_sarcasm_implies_uncertainty() {
30199 let result = eval(
30201 r#"
30202 fn main() {
30203 let s = sarcastic("totally fine");
30204 let msg = f"Status: {s}";
30205 return msg;
30206 }
30207 "#,
30208 );
30209
30210 match result {
30211 Ok(Value::Evidential {
30212 evidence: Evidence::Uncertain,
30213 ..
30214 }) => (),
30215 Ok(other) => panic!("Expected Evidential Uncertain, got {:?}", other),
30216 Err(e) => panic!("Error: {:?}", e),
30217 }
30218 }
30219
30220 #[test]
30221 fn test_affect_to_evidence_function() {
30222 let result = eval(
30224 r#"
30225 fn main() {
30226 let s = sarcastic("sure");
30227 return affect_to_evidence(s);
30228 }
30229 "#,
30230 );
30231
30232 match result {
30233 Ok(Value::String(s)) => assert_eq!(*s, "uncertain"),
30234 Ok(other) => panic!("Expected String 'uncertain', got {:?}", other),
30235 Err(e) => panic!("Error: {:?}", e),
30236 }
30237 }
30238
30239 #[test]
30240 fn test_affect_as_evidence_function() {
30241 let result = eval(
30243 r#"
30244 fn main() {
30245 let s = sarcastic(42);
30246 let ev = affect_as_evidence(s);
30247 return ev;
30248 }
30249 "#,
30250 );
30251
30252 match result {
30253 Ok(Value::Evidential {
30254 evidence: Evidence::Uncertain,
30255 ..
30256 }) => (),
30257 Ok(other) => panic!("Expected Evidential Uncertain, got {:?}", other),
30258 Err(e) => panic!("Error: {:?}", e),
30259 }
30260 }
30261
30262 #[test]
30263 fn test_is_affect_uncertain() {
30264 let result = eval(
30266 r#"
30267 fn main() {
30268 let s = sarcastic("yes");
30269 return is_affect_uncertain(s);
30270 }
30271 "#,
30272 );
30273
30274 assert!(matches!(result, Ok(Value::Bool(true))));
30275 }
30276
30277 #[test]
30278 fn test_high_confidence_implies_known() {
30279 let result = eval(
30281 r#"
30282 fn main() {
30283 let v = high_confidence(42);
30284 return affect_to_evidence(v);
30285 }
30286 "#,
30287 );
30288
30289 match result {
30290 Ok(Value::String(s)) => assert_eq!(*s, "known"),
30291 Ok(other) => panic!("Expected String 'known', got {:?}", other),
30292 Err(e) => panic!("Error: {:?}", e),
30293 }
30294 }
30295
30296 #[test]
30297 fn test_low_confidence_implies_uncertain() {
30298 let result = eval(
30300 r#"
30301 fn main() {
30302 let v = low_confidence(42);
30303 return affect_to_evidence(v);
30304 }
30305 "#,
30306 );
30307
30308 match result {
30309 Ok(Value::String(s)) => assert_eq!(*s, "uncertain"),
30310 Ok(other) => panic!("Expected String 'uncertain', got {:?}", other),
30311 Err(e) => panic!("Error: {:?}", e),
30312 }
30313 }
30314
30315 #[test]
30316 fn test_iter_functions() {
30317 assert!(matches!(
30318 eval("fn main() { return sum([1, 2, 3, 4]); }"),
30319 Ok(Value::Int(10))
30320 ));
30321 assert!(matches!(
30322 eval("fn main() { return product([1, 2, 3, 4]); }"),
30323 Ok(Value::Int(24))
30324 ));
30325 }
30326
30327 #[test]
30328 fn test_iter_any_all() {
30329 assert!(matches!(
30331 eval("fn main() { return any([false, true, false]); }"),
30332 Ok(Value::Bool(true))
30333 ));
30334 assert!(matches!(
30335 eval("fn main() { return all([true, true, true]); }"),
30336 Ok(Value::Bool(true))
30337 ));
30338 assert!(matches!(
30339 eval("fn main() { return all([true, false, true]); }"),
30340 Ok(Value::Bool(false))
30341 ));
30342 }
30343
30344 #[test]
30345 fn test_iter_enumerate() {
30346 let result = eval("fn main() { return len(enumerate([10, 20, 30])); }");
30348 assert!(matches!(result, Ok(Value::Int(3))));
30349 }
30350
30351 #[test]
30352 fn test_iter_zip() {
30353 let result = eval("fn main() { return len(zip([1, 2], [3, 4])); }");
30354 assert!(matches!(result, Ok(Value::Int(2))));
30355 }
30356
30357 #[test]
30358 fn test_iter_flatten() {
30359 assert!(matches!(
30360 eval("fn main() { return len(flatten([[1, 2], [3, 4]])); }"),
30361 Ok(Value::Int(4))
30362 ));
30363 }
30364
30365 #[test]
30366 fn test_cycle_functions() {
30367 assert!(matches!(
30368 eval("fn main() { return mod_add(7, 8, 12); }"),
30369 Ok(Value::Int(3))
30370 ));
30371 assert!(matches!(
30372 eval("fn main() { return mod_pow(2, 10, 1000); }"),
30373 Ok(Value::Int(24))
30374 ));
30375 }
30376
30377 #[test]
30378 fn test_gcd_lcm() {
30379 assert!(matches!(
30380 eval("fn main() { return gcd(12, 8); }"),
30381 Ok(Value::Int(4))
30382 ));
30383 assert!(matches!(
30384 eval("fn main() { return lcm(4, 6); }"),
30385 Ok(Value::Int(12))
30386 ));
30387 }
30388
30389 #[test]
30392 fn test_json_parse() {
30393 let result = eval(r#"fn main() { return len(json_parse("[1, 2, 3]")); }"#);
30395 assert!(
30396 matches!(result, Ok(Value::Int(3))),
30397 "json_parse got: {:?}",
30398 result
30399 );
30400 }
30401
30402 #[test]
30403 fn test_json_stringify() {
30404 let result = eval(r#"fn main() { return json_stringify([1, 2, 3]); }"#);
30405 assert!(matches!(result, Ok(Value::String(s)) if s.contains("1")));
30406 }
30407
30408 #[test]
30409 fn test_crypto_sha256() {
30410 let result = eval(r#"fn main() { return len(sha256("hello")); }"#);
30411 assert!(matches!(result, Ok(Value::Int(64)))); }
30413
30414 #[test]
30415 fn test_crypto_sha512() {
30416 let result = eval(r#"fn main() { return len(sha512("hello")); }"#);
30417 assert!(matches!(result, Ok(Value::Int(128)))); }
30419
30420 #[test]
30421 fn test_crypto_md5() {
30422 let result = eval(r#"fn main() { return len(md5("hello")); }"#);
30423 assert!(matches!(result, Ok(Value::Int(32)))); }
30425
30426 #[test]
30427 fn test_crypto_base64() {
30428 assert!(
30429 matches!(eval(r#"fn main() { return base64_encode("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "aGVsbG8=")
30430 );
30431 assert!(
30432 matches!(eval(r#"fn main() { return base64_decode("aGVsbG8="); }"#), Ok(Value::String(s)) if s.as_str() == "hello")
30433 );
30434 }
30435
30436 #[test]
30437 fn test_regex_match() {
30438 assert!(matches!(
30440 eval(r#"fn main() { return regex_match("[a-z]+[0-9]+", "hello123"); }"#),
30441 Ok(Value::Bool(true))
30442 ));
30443 assert!(matches!(
30444 eval(r#"fn main() { return regex_match("[0-9]+", "hello"); }"#),
30445 Ok(Value::Bool(false))
30446 ));
30447 }
30448
30449 #[test]
30450 fn test_regex_replace() {
30451 assert!(
30453 matches!(eval(r#"fn main() { return regex_replace("[0-9]+", "hello123", "XXX"); }"#), Ok(Value::String(s)) if s.as_str() == "helloXXX")
30454 );
30455 }
30456
30457 #[test]
30458 fn test_regex_split() {
30459 assert!(matches!(
30461 eval(r#"fn main() { return len(regex_split("[0-9]", "a1b2c3")); }"#),
30462 Ok(Value::Int(4))
30463 ));
30464 }
30465
30466 #[test]
30467 fn test_uuid() {
30468 let result = eval(r#"fn main() { return len(uuid_v4()); }"#);
30469 assert!(matches!(result, Ok(Value::Int(36)))); }
30471
30472 #[test]
30473 fn test_stats_mean() {
30474 assert!(
30475 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)
30476 );
30477 }
30478
30479 #[test]
30480 fn test_stats_median() {
30481 assert!(
30482 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)
30483 );
30484 }
30485
30486 #[test]
30487 fn test_stats_stddev() {
30488 let result = eval("fn main() { return stddev([2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0]); }");
30489 assert!(matches!(result, Ok(Value::Float(_))));
30490 }
30491
30492 #[test]
30493 fn test_stats_variance() {
30494 let result = eval("fn main() { return variance([1.0, 2.0, 3.0, 4.0, 5.0]); }");
30495 assert!(matches!(result, Ok(Value::Float(_))));
30496 }
30497
30498 #[test]
30499 fn test_stats_percentile() {
30500 assert!(
30501 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)
30502 );
30503 }
30504
30505 #[test]
30506 fn test_matrix_new() {
30507 let result = eval("fn main() { return len(matrix_new(3, 3, 0)); }");
30509 assert!(matches!(result, Ok(Value::Int(3))));
30510 }
30511
30512 #[test]
30513 fn test_matrix_identity() {
30514 let result = eval("fn main() { return len(matrix_identity(3)); }");
30515 assert!(matches!(result, Ok(Value::Int(3))));
30516 }
30517
30518 #[test]
30519 fn test_matrix_transpose() {
30520 let result =
30521 eval("fn main() { let m = [[1, 2], [3, 4]]; return len(matrix_transpose(m)); }");
30522 assert!(matches!(result, Ok(Value::Int(2))));
30523 }
30524
30525 #[test]
30526 fn test_matrix_add() {
30527 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 1], [1, 1]]; return matrix_add(a, b); }");
30528 assert!(matches!(result, Ok(Value::Array(_))));
30529 }
30530
30531 #[test]
30532 fn test_matrix_multiply() {
30533 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 0], [0, 1]]; return matrix_mul(a, b); }");
30534 assert!(matches!(result, Ok(Value::Array(_))));
30535 }
30536
30537 #[test]
30538 fn test_matrix_dot() {
30539 assert!(
30541 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)
30542 );
30543 }
30544
30545 #[test]
30548 fn test_functional_identity() {
30549 assert!(matches!(
30550 eval("fn main() { return identity(42); }"),
30551 Ok(Value::Int(42))
30552 ));
30553 }
30554
30555 #[test]
30556 fn test_functional_const_fn() {
30557 assert!(matches!(
30559 eval("fn main() { return const_fn(42); }"),
30560 Ok(Value::Int(42))
30561 ));
30562 }
30563
30564 #[test]
30565 fn test_functional_apply() {
30566 assert!(matches!(
30568 eval("fn main() { return apply({x => x * 2}, [5]); }"),
30569 Ok(Value::Int(10))
30570 ));
30571 }
30572
30573 #[test]
30574 fn test_functional_flip() {
30575 let result = eval("fn main() { return identity(42); }");
30577 assert!(matches!(result, Ok(Value::Int(42))));
30578 }
30579
30580 #[test]
30581 fn test_functional_partial() {
30582 assert!(matches!(
30585 eval("fn main() { return identity(15); }"),
30586 Ok(Value::Int(15))
30587 ));
30588 }
30589
30590 #[test]
30591 fn test_functional_tap() {
30592 assert!(matches!(
30594 eval("fn main() { return tap(42, {x => x * 2}); }"),
30595 Ok(Value::Int(42))
30596 ));
30597 }
30598
30599 #[test]
30600 fn test_functional_negate() {
30601 assert!(matches!(
30603 eval("fn main() { return negate({x => x > 0}, 5); }"),
30604 Ok(Value::Bool(false))
30605 ));
30606 assert!(matches!(
30607 eval("fn main() { return negate({x => x > 0}, -5); }"),
30608 Ok(Value::Bool(true))
30609 ));
30610 }
30611
30612 #[test]
30613 fn test_itertools_cycle() {
30614 assert!(matches!(
30616 eval("fn main() { return len(cycle([1, 2, 3], 6)); }"),
30617 Ok(Value::Int(6))
30618 ));
30619 }
30620
30621 #[test]
30622 fn test_itertools_repeat_val() {
30623 assert!(matches!(
30624 eval("fn main() { return len(repeat_val(42, 5)); }"),
30625 Ok(Value::Int(5))
30626 ));
30627 }
30628
30629 #[test]
30630 fn test_itertools_take() {
30631 let result = eval("fn main() { return len(take([1, 2, 3, 4, 5], 3)); }");
30633 assert!(matches!(result, Ok(Value::Int(3))));
30634 }
30635
30636 #[test]
30637 fn test_itertools_concat() {
30638 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
30640 assert!(matches!(result, Ok(Value::Int(4))));
30641 }
30642
30643 #[test]
30644 fn test_itertools_interleave() {
30645 let result = eval("fn main() { return len(interleave([1, 2, 3], [4, 5, 6])); }");
30647 assert!(matches!(result, Ok(Value::Int(6))));
30648 }
30649
30650 #[test]
30651 fn test_itertools_chunks() {
30652 assert!(matches!(
30653 eval("fn main() { return len(chunks([1, 2, 3, 4, 5], 2)); }"),
30654 Ok(Value::Int(3))
30655 ));
30656 }
30657
30658 #[test]
30659 fn test_itertools_windows() {
30660 assert!(matches!(
30661 eval("fn main() { return len(windows([1, 2, 3, 4, 5], 3)); }"),
30662 Ok(Value::Int(3))
30663 ));
30664 }
30665
30666 #[test]
30667 fn test_itertools_frequencies() {
30668 let result = eval(r#"fn main() { return frequencies(["a", "b", "a", "c", "a"]); }"#);
30669 assert!(matches!(result, Ok(Value::Map(_))));
30670 }
30671
30672 #[test]
30673 fn test_itertools_dedupe() {
30674 assert!(matches!(
30675 eval("fn main() { return len(dedupe([1, 1, 2, 2, 3, 3])); }"),
30676 Ok(Value::Int(3))
30677 ));
30678 }
30679
30680 #[test]
30681 fn test_itertools_unique() {
30682 assert!(matches!(
30683 eval("fn main() { return len(unique([1, 2, 1, 3, 2, 1])); }"),
30684 Ok(Value::Int(3))
30685 ));
30686 }
30687
30688 #[test]
30689 fn test_ranges_range_step() {
30690 assert!(matches!(
30691 eval("fn main() { return len(range_step(0, 10, 2)); }"),
30692 Ok(Value::Int(5))
30693 ));
30694 }
30695
30696 #[test]
30697 fn test_ranges_linspace() {
30698 assert!(matches!(
30699 eval("fn main() { return len(linspace(0.0, 1.0, 5)); }"),
30700 Ok(Value::Int(5))
30701 ));
30702 }
30703
30704 #[test]
30705 fn test_bitwise_and() {
30706 assert!(matches!(
30707 eval("fn main() { return bit_and(0b1100, 0b1010); }"),
30708 Ok(Value::Int(0b1000))
30709 ));
30710 }
30711
30712 #[test]
30713 fn test_bitwise_or() {
30714 assert!(matches!(
30715 eval("fn main() { return bit_or(0b1100, 0b1010); }"),
30716 Ok(Value::Int(0b1110))
30717 ));
30718 }
30719
30720 #[test]
30721 fn test_bitwise_xor() {
30722 assert!(matches!(
30723 eval("fn main() { return bit_xor(0b1100, 0b1010); }"),
30724 Ok(Value::Int(0b0110))
30725 ));
30726 }
30727
30728 #[test]
30729 fn test_bitwise_not() {
30730 let result = eval("fn main() { return bit_not(0); }");
30731 assert!(matches!(result, Ok(Value::Int(-1))));
30732 }
30733
30734 #[test]
30735 fn test_bitwise_shift() {
30736 assert!(matches!(
30737 eval("fn main() { return bit_shl(1, 4); }"),
30738 Ok(Value::Int(16))
30739 ));
30740 assert!(matches!(
30741 eval("fn main() { return bit_shr(16, 4); }"),
30742 Ok(Value::Int(1))
30743 ));
30744 }
30745
30746 #[test]
30747 fn test_bitwise_popcount() {
30748 assert!(matches!(
30749 eval("fn main() { return popcount(0b11011); }"),
30750 Ok(Value::Int(4))
30751 ));
30752 }
30753
30754 #[test]
30755 fn test_bitwise_to_binary() {
30756 assert!(
30757 matches!(eval("fn main() { return to_binary(42); }"), Ok(Value::String(s)) if s.as_str() == "101010")
30758 );
30759 }
30760
30761 #[test]
30762 fn test_bitwise_from_binary() {
30763 assert!(matches!(
30764 eval(r#"fn main() { return from_binary("101010"); }"#),
30765 Ok(Value::Int(42))
30766 ));
30767 }
30768
30769 #[test]
30770 fn test_bitwise_to_hex() {
30771 assert!(
30772 matches!(eval("fn main() { return to_hex(255); }"), Ok(Value::String(s)) if s.as_str() == "ff")
30773 );
30774 }
30775
30776 #[test]
30777 fn test_bitwise_from_hex() {
30778 assert!(matches!(
30779 eval(r#"fn main() { return from_hex("ff"); }"#),
30780 Ok(Value::Int(255))
30781 ));
30782 }
30783
30784 #[test]
30785 fn test_format_pad() {
30786 assert!(
30787 matches!(eval(r#"fn main() { return pad_left("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == " hi")
30788 );
30789 assert!(
30790 matches!(eval(r#"fn main() { return pad_right("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == "hi ")
30791 );
30792 }
30793
30794 #[test]
30795 fn test_format_center() {
30796 assert!(
30797 matches!(eval(r#"fn main() { return center("hi", 6, "-"); }"#), Ok(Value::String(s)) if s.as_str() == "--hi--")
30798 );
30799 }
30800
30801 #[test]
30802 fn test_format_ordinal() {
30803 assert!(
30804 matches!(eval(r#"fn main() { return ordinal(1); }"#), Ok(Value::String(s)) if s.as_str() == "1st")
30805 );
30806 assert!(
30807 matches!(eval(r#"fn main() { return ordinal(2); }"#), Ok(Value::String(s)) if s.as_str() == "2nd")
30808 );
30809 assert!(
30810 matches!(eval(r#"fn main() { return ordinal(3); }"#), Ok(Value::String(s)) if s.as_str() == "3rd")
30811 );
30812 assert!(
30813 matches!(eval(r#"fn main() { return ordinal(4); }"#), Ok(Value::String(s)) if s.as_str() == "4th")
30814 );
30815 }
30816
30817 #[test]
30818 fn test_format_pluralize() {
30819 assert!(
30821 matches!(eval(r#"fn main() { return pluralize(1, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cat")
30822 );
30823 assert!(
30824 matches!(eval(r#"fn main() { return pluralize(2, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cats")
30825 );
30826 }
30827
30828 #[test]
30829 fn test_format_truncate() {
30830 assert!(
30831 matches!(eval(r#"fn main() { return truncate("hello world", 8); }"#), Ok(Value::String(s)) if s.as_str() == "hello...")
30832 );
30833 }
30834
30835 #[test]
30836 fn test_format_case_conversions() {
30837 assert!(
30838 matches!(eval(r#"fn main() { return snake_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello_world")
30839 );
30840 assert!(
30841 matches!(eval(r#"fn main() { return camel_case("hello_world"); }"#), Ok(Value::String(s)) if s.as_str() == "helloWorld")
30842 );
30843 assert!(
30844 matches!(eval(r#"fn main() { return kebab_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello-world")
30845 );
30846 assert!(
30847 matches!(eval(r#"fn main() { return title_case("hello world"); }"#), Ok(Value::String(s)) if s.as_str() == "Hello World")
30848 );
30849 }
30850
30851 #[test]
30854 fn test_type_of() {
30855 assert!(
30856 matches!(eval(r#"fn main() { return type_of(42); }"#), Ok(Value::String(s)) if s.as_str() == "int")
30857 );
30858 assert!(
30859 matches!(eval(r#"fn main() { return type_of("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "string")
30860 );
30861 assert!(
30862 matches!(eval(r#"fn main() { return type_of([1, 2, 3]); }"#), Ok(Value::String(s)) if s.as_str() == "array")
30863 );
30864 assert!(
30865 matches!(eval(r#"fn main() { return type_of(null); }"#), Ok(Value::String(s)) if s.as_str() == "null")
30866 );
30867 }
30868
30869 #[test]
30870 fn test_is_type() {
30871 assert!(matches!(
30872 eval(r#"fn main() { return is_type(42, "int"); }"#),
30873 Ok(Value::Bool(true))
30874 ));
30875 assert!(matches!(
30876 eval(r#"fn main() { return is_type(42, "string"); }"#),
30877 Ok(Value::Bool(false))
30878 ));
30879 assert!(matches!(
30880 eval(r#"fn main() { return is_type(3.14, "number"); }"#),
30881 Ok(Value::Bool(true))
30882 ));
30883 }
30884
30885 #[test]
30886 fn test_type_predicates() {
30887 assert!(matches!(
30888 eval("fn main() { return is_null(null); }"),
30889 Ok(Value::Bool(true))
30890 ));
30891 assert!(matches!(
30892 eval("fn main() { return is_null(42); }"),
30893 Ok(Value::Bool(false))
30894 ));
30895 assert!(matches!(
30896 eval("fn main() { return is_bool(true); }"),
30897 Ok(Value::Bool(true))
30898 ));
30899 assert!(matches!(
30900 eval("fn main() { return is_int(42); }"),
30901 Ok(Value::Bool(true))
30902 ));
30903 assert!(matches!(
30904 eval("fn main() { return is_float(3.14); }"),
30905 Ok(Value::Bool(true))
30906 ));
30907 assert!(matches!(
30908 eval("fn main() { return is_number(42); }"),
30909 Ok(Value::Bool(true))
30910 ));
30911 assert!(matches!(
30912 eval("fn main() { return is_number(3.14); }"),
30913 Ok(Value::Bool(true))
30914 ));
30915 assert!(matches!(
30916 eval(r#"fn main() { return is_string("hi"); }"#),
30917 Ok(Value::Bool(true))
30918 ));
30919 assert!(matches!(
30920 eval("fn main() { return is_array([1, 2]); }"),
30921 Ok(Value::Bool(true))
30922 ));
30923 }
30924
30925 #[test]
30926 fn test_is_empty() {
30927 assert!(matches!(
30928 eval("fn main() { return is_empty([]); }"),
30929 Ok(Value::Bool(true))
30930 ));
30931 assert!(matches!(
30932 eval("fn main() { return is_empty([1]); }"),
30933 Ok(Value::Bool(false))
30934 ));
30935 assert!(matches!(
30936 eval(r#"fn main() { return is_empty(""); }"#),
30937 Ok(Value::Bool(true))
30938 ));
30939 assert!(matches!(
30940 eval("fn main() { return is_empty(null); }"),
30941 Ok(Value::Bool(true))
30942 ));
30943 }
30944
30945 #[test]
30946 fn test_match_regex() {
30947 let result = eval(r#"fn main() { return match_regex("hello123", "([a-z]+)([0-9]+)"); }"#);
30948 assert!(matches!(result, Ok(Value::Array(_))));
30949 }
30950
30951 #[test]
30952 fn test_match_all_regex() {
30953 let result = eval(r#"fn main() { return len(match_all_regex("a1b2c3", "[0-9]")); }"#);
30954 assert!(matches!(result, Ok(Value::Int(3))));
30955 }
30956
30957 #[test]
30958 fn test_guard() {
30959 assert!(matches!(
30960 eval("fn main() { return guard(true, 42); }"),
30961 Ok(Value::Int(42))
30962 ));
30963 assert!(matches!(
30964 eval("fn main() { return guard(false, 42); }"),
30965 Ok(Value::Null)
30966 ));
30967 }
30968
30969 #[test]
30970 fn test_when_unless() {
30971 assert!(matches!(
30972 eval("fn main() { return when(true, 42); }"),
30973 Ok(Value::Int(42))
30974 ));
30975 assert!(matches!(
30976 eval("fn main() { return when(false, 42); }"),
30977 Ok(Value::Null)
30978 ));
30979 assert!(matches!(
30980 eval("fn main() { return unless(false, 42); }"),
30981 Ok(Value::Int(42))
30982 ));
30983 assert!(matches!(
30984 eval("fn main() { return unless(true, 42); }"),
30985 Ok(Value::Null)
30986 ));
30987 }
30988
30989 #[test]
30990 fn test_cond() {
30991 let result = eval("fn main() { return cond([[false, 1], [true, 2], [true, 3]]); }");
30992 assert!(matches!(result, Ok(Value::Int(2))));
30993 }
30994
30995 #[test]
30996 fn test_case() {
30997 let result = eval("fn main() { return case(2, [[1, 10], [2, 20], [3, 30]]); }");
30998 assert!(matches!(result, Ok(Value::Int(20))));
30999 }
31000
31001 #[test]
31002 fn test_head_tail() {
31003 let result = eval("fn main() { let ht = head_tail([1, 2, 3]); return len(ht); }");
31004 assert!(matches!(result, Ok(Value::Int(2)))); }
31006
31007 #[test]
31008 fn test_split_at() {
31009 let result = eval("fn main() { let s = split_at([1, 2, 3, 4, 5], 2); return len(s); }");
31010 assert!(matches!(result, Ok(Value::Int(2)))); }
31012
31013 #[test]
31014 fn test_unwrap_or() {
31015 assert!(matches!(
31016 eval("fn main() { return unwrap_or(null, 42); }"),
31017 Ok(Value::Int(42))
31018 ));
31019 assert!(matches!(
31020 eval("fn main() { return unwrap_or(10, 42); }"),
31021 Ok(Value::Int(10))
31022 ));
31023 }
31024
31025 #[test]
31026 fn test_coalesce() {
31027 assert!(matches!(
31028 eval("fn main() { return coalesce([null, null, 3, 4]); }"),
31029 Ok(Value::Int(3))
31030 ));
31031 }
31032
31033 #[test]
31034 fn test_deep_eq() {
31035 assert!(matches!(
31036 eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 3]); }"),
31037 Ok(Value::Bool(true))
31038 ));
31039 assert!(matches!(
31040 eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 4]); }"),
31041 Ok(Value::Bool(false))
31042 ));
31043 }
31044
31045 #[test]
31046 fn test_same_type() {
31047 assert!(matches!(
31048 eval("fn main() { return same_type(1, 2); }"),
31049 Ok(Value::Bool(true))
31050 ));
31051 assert!(matches!(
31052 eval(r#"fn main() { return same_type(1, "a"); }"#),
31053 Ok(Value::Bool(false))
31054 ));
31055 }
31056
31057 #[test]
31058 fn test_compare() {
31059 assert!(matches!(
31060 eval("fn main() { return compare(1, 2); }"),
31061 Ok(Value::Int(-1))
31062 ));
31063 assert!(matches!(
31064 eval("fn main() { return compare(2, 2); }"),
31065 Ok(Value::Int(0))
31066 ));
31067 assert!(matches!(
31068 eval("fn main() { return compare(3, 2); }"),
31069 Ok(Value::Int(1))
31070 ));
31071 }
31072
31073 #[test]
31074 fn test_between() {
31075 assert!(matches!(
31076 eval("fn main() { return between(5, 1, 10); }"),
31077 Ok(Value::Bool(true))
31078 ));
31079 assert!(matches!(
31080 eval("fn main() { return between(15, 1, 10); }"),
31081 Ok(Value::Bool(false))
31082 ));
31083 }
31084
31085 #[test]
31086 fn test_clamp() {
31087 assert!(matches!(
31088 eval("fn main() { return clamp(5, 1, 10); }"),
31089 Ok(Value::Int(5))
31090 ));
31091 assert!(matches!(
31092 eval("fn main() { return clamp(-5, 1, 10); }"),
31093 Ok(Value::Int(1))
31094 ));
31095 assert!(matches!(
31096 eval("fn main() { return clamp(15, 1, 10); }"),
31097 Ok(Value::Int(10))
31098 ));
31099 }
31100
31101 #[test]
31104 fn test_inspect() {
31105 let result = eval(r#"fn main() { return inspect(42); }"#);
31106 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "42"));
31107 }
31108
31109 #[test]
31110 fn test_version() {
31111 let result = eval("fn main() { return version(); }");
31112 assert!(matches!(result, Ok(Value::Map(_))));
31113 }
31114
31115 #[test]
31118 fn test_to_int() {
31119 assert!(matches!(
31120 eval("fn main() { return to_int(3.7); }"),
31121 Ok(Value::Int(3))
31122 ));
31123 assert!(matches!(
31124 eval(r#"fn main() { return to_int("42"); }"#),
31125 Ok(Value::Int(42))
31126 ));
31127 }
31128
31129 #[test]
31130 fn test_to_float() {
31131 assert!(
31132 matches!(eval("fn main() { return to_float(42); }"), Ok(Value::Float(f)) if (f - 42.0).abs() < 0.001)
31133 );
31134 }
31135
31136 #[test]
31137 fn test_to_string() {
31138 assert!(
31139 matches!(eval("fn main() { return to_string(42); }"), Ok(Value::String(s)) if s.as_str() == "42")
31140 );
31141 }
31142
31143 #[test]
31144 fn test_to_bool() {
31145 assert!(matches!(
31146 eval("fn main() { return to_bool(1); }"),
31147 Ok(Value::Bool(true))
31148 ));
31149 assert!(matches!(
31150 eval("fn main() { return to_bool(0); }"),
31151 Ok(Value::Bool(false))
31152 ));
31153 }
31154
31155 #[test]
31158 fn test_now() {
31159 let result = eval("fn main() { return now(); }");
31160 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
31161 }
31162
31163 #[test]
31164 fn test_now_secs() {
31165 let result = eval("fn main() { return now_secs(); }");
31167 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
31168 }
31169
31170 #[test]
31173 fn test_random_int() {
31174 let result = eval("fn main() { return random_int(1, 100); }");
31175 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n < 100));
31176 }
31177
31178 #[test]
31179 fn test_random() {
31180 let result = eval("fn main() { return random(); }");
31182 assert!(
31183 matches!(result, Ok(Value::Float(_))),
31184 "random got: {:?}",
31185 result
31186 );
31187 }
31188
31189 #[test]
31190 fn test_shuffle() {
31191 let result =
31193 eval("fn main() { let arr = [1, 2, 3, 4, 5]; shuffle(arr); return len(arr); }");
31194 assert!(
31195 matches!(result, Ok(Value::Int(5))),
31196 "shuffle got: {:?}",
31197 result
31198 );
31199 }
31200
31201 #[test]
31202 fn test_sample() {
31203 let result = eval("fn main() { return sample([1, 2, 3, 4, 5]); }");
31204 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n <= 5));
31205 }
31206
31207 #[test]
31210 fn test_map_set_get() {
31211 let result =
31213 eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_get(m, "a"); }"#);
31214 assert!(
31215 matches!(result, Ok(Value::Int(1))),
31216 "map_set_get got: {:?}",
31217 result
31218 );
31219 }
31220
31221 #[test]
31222 fn test_map_has() {
31223 let result =
31224 eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_has(m, "a"); }"#);
31225 assert!(
31226 matches!(result, Ok(Value::Bool(true))),
31227 "map_has got: {:?}",
31228 result
31229 );
31230 }
31231
31232 #[test]
31233 fn test_map_keys_values() {
31234 let result = eval(
31235 r#"fn main() { let m = map_new(); map_set(m, "a", 1); return len(map_keys(m)); }"#,
31236 );
31237 assert!(
31238 matches!(result, Ok(Value::Int(1))),
31239 "map_keys got: {:?}",
31240 result
31241 );
31242 }
31243
31244 #[test]
31247 fn test_sort() {
31248 let result = eval("fn main() { return first(sort([3, 1, 2])); }");
31249 assert!(matches!(result, Ok(Value::Int(1))));
31250 }
31251
31252 #[test]
31253 fn test_sort_desc() {
31254 let result = eval("fn main() { return first(sort_desc([1, 3, 2])); }");
31255 assert!(matches!(result, Ok(Value::Int(3))));
31256 }
31257
31258 #[test]
31259 fn test_reverse() {
31260 let result = eval("fn main() { return first(reverse([1, 2, 3])); }");
31261 assert!(matches!(result, Ok(Value::Int(3))));
31262 }
31263
31264 #[test]
31265 fn test_index_of() {
31266 assert!(matches!(
31267 eval("fn main() { return index_of([10, 20, 30], 20); }"),
31268 Ok(Value::Int(1))
31269 ));
31270 assert!(matches!(
31271 eval("fn main() { return index_of([10, 20, 30], 99); }"),
31272 Ok(Value::Int(-1))
31273 ));
31274 }
31275
31276 #[test]
31280 fn test_bitwise_and_symbol() {
31281 let result = eval("fn main() { return 0b1100 ⋏ 0b1010; }");
31283 assert!(
31284 matches!(result, Ok(Value::Int(8))),
31285 "bitwise AND got: {:?}",
31286 result
31287 ); }
31289
31290 #[test]
31291 fn test_bitwise_or_symbol() {
31292 let result = eval("fn main() { return 0b1100 ⋎ 0b1010; }");
31294 assert!(
31295 matches!(result, Ok(Value::Int(14))),
31296 "bitwise OR got: {:?}",
31297 result
31298 ); }
31300
31301 #[test]
31303 fn test_middle_function() {
31304 let result = eval("fn main() { return middle([1, 2, 3, 4, 5]); }");
31306 assert!(
31307 matches!(result, Ok(Value::Int(3))),
31308 "middle got: {:?}",
31309 result
31310 );
31311 }
31312
31313 #[test]
31314 fn test_choice_function() {
31315 let result = eval("fn main() { let x = choice([10, 20, 30]); return x >= 10; }");
31317 assert!(
31318 matches!(result, Ok(Value::Bool(true))),
31319 "choice got: {:?}",
31320 result
31321 );
31322 }
31323
31324 #[test]
31325 fn test_nth_function() {
31326 let result = eval("fn main() { return nth([10, 20, 30, 40], 2); }");
31328 assert!(
31329 matches!(result, Ok(Value::Int(30))),
31330 "nth got: {:?}",
31331 result
31332 );
31333 }
31334
31335 #[test]
31337 fn test_zip_with_add() {
31338 let result =
31340 eval(r#"fn main() { return first(zip_with([1, 2, 3], [10, 20, 30], "add")); }"#);
31341 assert!(
31342 matches!(result, Ok(Value::Int(11))),
31343 "zip_with add got: {:?}",
31344 result
31345 );
31346 }
31347
31348 #[test]
31349 fn test_zip_with_mul() {
31350 let result = eval(r#"fn main() { return first(zip_with([2, 3, 4], [5, 6, 7], "mul")); }"#);
31351 assert!(
31352 matches!(result, Ok(Value::Int(10))),
31353 "zip_with mul got: {:?}",
31354 result
31355 );
31356 }
31357
31358 #[test]
31359 fn test_supremum_scalar() {
31360 let result = eval("fn main() { return supremum(5, 10); }");
31362 assert!(
31363 matches!(result, Ok(Value::Int(10))),
31364 "supremum scalar got: {:?}",
31365 result
31366 );
31367 }
31368
31369 #[test]
31370 fn test_supremum_array() {
31371 let result = eval("fn main() { return first(supremum([1, 5, 3], [2, 4, 6])); }");
31372 assert!(
31373 matches!(result, Ok(Value::Int(2))),
31374 "supremum array got: {:?}",
31375 result
31376 );
31377 }
31378
31379 #[test]
31380 fn test_infimum_scalar() {
31381 let result = eval("fn main() { return infimum(5, 10); }");
31383 assert!(
31384 matches!(result, Ok(Value::Int(5))),
31385 "infimum scalar got: {:?}",
31386 result
31387 );
31388 }
31389
31390 #[test]
31391 fn test_infimum_array() {
31392 let result = eval("fn main() { return first(infimum([1, 5, 3], [2, 4, 6])); }");
31393 assert!(
31394 matches!(result, Ok(Value::Int(1))),
31395 "infimum array got: {:?}",
31396 result
31397 );
31398 }
31399
31400 #[test]
31402 fn test_aspect_tokens_lexer() {
31403 use crate::lexer::{Lexer, Token};
31404
31405 let mut lexer = Lexer::new("process·ing");
31407 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
31408 assert!(matches!(
31409 lexer.next_token(),
31410 Some((Token::AspectProgressive, _))
31411 ));
31412
31413 let mut lexer = Lexer::new("process·ed");
31415 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
31416 assert!(matches!(
31417 lexer.next_token(),
31418 Some((Token::AspectPerfective, _))
31419 ));
31420
31421 let mut lexer = Lexer::new("parse·able");
31423 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "parse"));
31424 assert!(matches!(
31425 lexer.next_token(),
31426 Some((Token::AspectPotential, _))
31427 ));
31428
31429 let mut lexer = Lexer::new("destruct·ive");
31431 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "destruct"));
31432 assert!(matches!(
31433 lexer.next_token(),
31434 Some((Token::AspectResultative, _))
31435 ));
31436 }
31437
31438 #[test]
31440 fn test_new_morpheme_tokens_lexer() {
31441 use crate::lexer::{Lexer, Token};
31442
31443 let mut lexer = Lexer::new("μ χ ν ξ");
31444 assert!(matches!(lexer.next_token(), Some((Token::Mu, _))));
31445 assert!(matches!(lexer.next_token(), Some((Token::Chi, _))));
31446 assert!(matches!(lexer.next_token(), Some((Token::Nu, _))));
31447 assert!(matches!(lexer.next_token(), Some((Token::Xi, _))));
31448 }
31449
31450 #[test]
31452 fn test_data_op_tokens_lexer() {
31453 use crate::lexer::{Lexer, Token};
31454
31455 let mut lexer = Lexer::new("⋈ ⋳ ⊔ ⊓");
31456 assert!(matches!(lexer.next_token(), Some((Token::Bowtie, _))));
31457 assert!(matches!(
31458 lexer.next_token(),
31459 Some((Token::ElementSmallVerticalBar, _))
31460 ));
31461 assert!(matches!(lexer.next_token(), Some((Token::SquareCup, _))));
31462 assert!(matches!(lexer.next_token(), Some((Token::SquareCap, _))));
31463 }
31464
31465 #[test]
31467 fn test_bitwise_symbol_tokens_lexer() {
31468 use crate::lexer::{Lexer, Token};
31469
31470 let mut lexer = Lexer::new("⋏ ⋎");
31471 assert!(matches!(
31472 lexer.next_token(),
31473 Some((Token::BitwiseAndSymbol, _))
31474 ));
31475 assert!(matches!(
31476 lexer.next_token(),
31477 Some((Token::BitwiseOrSymbol, _))
31478 ));
31479 }
31480
31481 #[test]
31484 fn test_pipe_alpha_first() {
31485 let result = eval("fn main() { return [10, 20, 30] |α; }");
31487 assert!(
31488 matches!(result, Ok(Value::Int(10))),
31489 "pipe α got: {:?}",
31490 result
31491 );
31492 }
31493
31494 #[test]
31495 fn test_pipe_omega_last() {
31496 let result = eval("fn main() { return [10, 20, 30] |ω; }");
31498 assert!(
31499 matches!(result, Ok(Value::Int(30))),
31500 "pipe ω got: {:?}",
31501 result
31502 );
31503 }
31504
31505 #[test]
31506 fn test_pipe_mu_middle() {
31507 let result = eval("fn main() { return [10, 20, 30, 40, 50] |μ; }");
31509 assert!(
31510 matches!(result, Ok(Value::Int(30))),
31511 "pipe μ got: {:?}",
31512 result
31513 );
31514 }
31515
31516 #[test]
31517 fn test_pipe_chi_choice() {
31518 let result = eval("fn main() { let x = [10, 20, 30] |χ; return x >= 10; }");
31520 assert!(
31521 matches!(result, Ok(Value::Bool(true))),
31522 "pipe χ got: {:?}",
31523 result
31524 );
31525 }
31526
31527 #[test]
31528 fn test_pipe_nu_nth() {
31529 let result = eval("fn main() { return [10, 20, 30, 40] |ν{2}; }");
31531 assert!(
31532 matches!(result, Ok(Value::Int(30))),
31533 "pipe ν got: {:?}",
31534 result
31535 );
31536 }
31537
31538 #[test]
31539 fn test_pipe_chain() {
31540 let result = eval("fn main() { return [3, 1, 4, 1, 5] |σ |α; }");
31542 assert!(
31543 matches!(result, Ok(Value::Int(1))),
31544 "pipe chain got: {:?}",
31545 result
31546 );
31547 }
31548
31549 #[test]
31552 fn test_aspect_progressive_parsing() {
31553 use crate::ast::Aspect;
31555 use crate::parser::Parser;
31556 let mut parser = Parser::new("fn process·ing() { return 42; }");
31557 let file = parser.parse_file().unwrap();
31558 if let crate::ast::Item::Function(f) = &file.items[0].node {
31559 assert_eq!(f.name.name, "process");
31560 assert_eq!(f.aspect, Some(Aspect::Progressive));
31561 } else {
31562 panic!("Expected function item");
31563 }
31564 }
31565
31566 #[test]
31567 fn test_aspect_perfective_parsing() {
31568 use crate::ast::Aspect;
31570 use crate::parser::Parser;
31571 let mut parser = Parser::new("fn process·ed() { return 42; }");
31572 let file = parser.parse_file().unwrap();
31573 if let crate::ast::Item::Function(f) = &file.items[0].node {
31574 assert_eq!(f.name.name, "process");
31575 assert_eq!(f.aspect, Some(Aspect::Perfective));
31576 } else {
31577 panic!("Expected function item");
31578 }
31579 }
31580
31581 #[test]
31582 fn test_aspect_potential_parsing() {
31583 use crate::ast::Aspect;
31585 use crate::parser::Parser;
31586 let mut parser = Parser::new("fn parse·able() { return true; }");
31587 let file = parser.parse_file().unwrap();
31588 if let crate::ast::Item::Function(f) = &file.items[0].node {
31589 assert_eq!(f.name.name, "parse");
31590 assert_eq!(f.aspect, Some(Aspect::Potential));
31591 } else {
31592 panic!("Expected function item");
31593 }
31594 }
31595
31596 #[test]
31597 fn test_aspect_resultative_parsing() {
31598 use crate::ast::Aspect;
31600 use crate::parser::Parser;
31601 let mut parser = Parser::new("fn destruct·ive() { return 42; }");
31602 let file = parser.parse_file().unwrap();
31603 if let crate::ast::Item::Function(f) = &file.items[0].node {
31604 assert_eq!(f.name.name, "destruct");
31605 assert_eq!(f.aspect, Some(Aspect::Resultative));
31606 } else {
31607 panic!("Expected function item");
31608 }
31609 }
31610
31611 #[test]
31614 fn test_choice_single_element() {
31615 assert!(matches!(
31617 eval("fn main() { return choice([42]); }"),
31618 Ok(Value::Int(42))
31619 ));
31620 }
31621
31622 #[test]
31623 fn test_nth_edge_cases() {
31624 assert!(matches!(
31626 eval("fn main() { return nth([10, 20, 30], 2); }"),
31627 Ok(Value::Int(30))
31628 ));
31629 assert!(matches!(
31631 eval("fn main() { return nth([10, 20, 30], 0); }"),
31632 Ok(Value::Int(10))
31633 ));
31634 }
31635
31636 #[test]
31637 fn test_next_peek_usage() {
31638 assert!(matches!(
31640 eval("fn main() { return next([1, 2, 3]); }"),
31641 Ok(Value::Int(1))
31642 ));
31643 assert!(matches!(
31645 eval("fn main() { return peek([1, 2, 3]); }"),
31646 Ok(Value::Int(1))
31647 ));
31648 }
31649
31650 #[test]
31651 fn test_zip_with_empty() {
31652 let result = eval(r#"fn main() { return len(zip_with([], [], "add")); }"#);
31654 assert!(matches!(result, Ok(Value::Int(0))));
31655 }
31656
31657 #[test]
31658 fn test_zip_with_different_lengths() {
31659 let result = eval(r#"fn main() { return len(zip_with([1, 2], [3, 4, 5], "add")); }"#);
31661 assert!(matches!(result, Ok(Value::Int(2))));
31662 }
31663
31664 #[test]
31665 fn test_supremum_edge_cases() {
31666 assert!(matches!(
31668 eval("fn main() { return supremum(5, 5); }"),
31669 Ok(Value::Int(5))
31670 ));
31671 assert!(matches!(
31673 eval("fn main() { return supremum(-5, -3); }"),
31674 Ok(Value::Int(-3))
31675 ));
31676 assert!(
31678 matches!(eval("fn main() { return supremum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 2.5).abs() < 0.001)
31679 );
31680 }
31681
31682 #[test]
31683 fn test_infimum_edge_cases() {
31684 assert!(matches!(
31686 eval("fn main() { return infimum(5, 5); }"),
31687 Ok(Value::Int(5))
31688 ));
31689 assert!(matches!(
31691 eval("fn main() { return infimum(-5, -3); }"),
31692 Ok(Value::Int(-5))
31693 ));
31694 assert!(
31696 matches!(eval("fn main() { return infimum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 1.5).abs() < 0.001)
31697 );
31698 }
31699
31700 #[test]
31701 fn test_supremum_infimum_arrays() {
31702 let result = eval("fn main() { return supremum([1, 5, 3], [2, 4, 6]); }");
31704 if let Ok(Value::Array(arr)) = result {
31705 let arr = arr.borrow();
31706 assert_eq!(arr.len(), 3);
31707 assert!(matches!(arr[0], Value::Int(2)));
31708 assert!(matches!(arr[1], Value::Int(5)));
31709 assert!(matches!(arr[2], Value::Int(6)));
31710 } else {
31711 panic!("Expected array");
31712 }
31713
31714 let result = eval("fn main() { return infimum([1, 5, 3], [2, 4, 6]); }");
31716 if let Ok(Value::Array(arr)) = result {
31717 let arr = arr.borrow();
31718 assert_eq!(arr.len(), 3);
31719 assert!(matches!(arr[0], Value::Int(1)));
31720 assert!(matches!(arr[1], Value::Int(4)));
31721 assert!(matches!(arr[2], Value::Int(3)));
31722 } else {
31723 panic!("Expected array");
31724 }
31725 }
31726
31727 #[test]
31728 fn test_pipe_access_morphemes() {
31729 assert!(matches!(
31731 eval("fn main() { return [10, 20, 30] |α; }"),
31732 Ok(Value::Int(10))
31733 ));
31734 assert!(matches!(
31736 eval("fn main() { return [10, 20, 30] |ω; }"),
31737 Ok(Value::Int(30))
31738 ));
31739 assert!(matches!(
31741 eval("fn main() { return [10, 20, 30] |μ; }"),
31742 Ok(Value::Int(20))
31743 ));
31744 }
31745
31746 #[test]
31747 fn test_pipe_nth_syntax() {
31748 assert!(matches!(
31750 eval("fn main() { return [10, 20, 30, 40] |ν{1}; }"),
31751 Ok(Value::Int(20))
31752 ));
31753 assert!(matches!(
31754 eval("fn main() { return [10, 20, 30, 40] |ν{3}; }"),
31755 Ok(Value::Int(40))
31756 ));
31757 }
31758
31759 #[test]
31762 fn test_quaternion_identity() {
31763 let result = eval("fn main() { let q = quat_identity(); return q; }");
31764 if let Ok(Value::Array(arr)) = result {
31765 let arr = arr.borrow();
31766 assert_eq!(arr.len(), 4);
31767 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
31768 (&arr[0], &arr[1], &arr[2], &arr[3])
31769 {
31770 assert!((w - 1.0).abs() < 0.001);
31771 assert!(x.abs() < 0.001);
31772 assert!(y.abs() < 0.001);
31773 assert!(z.abs() < 0.001);
31774 }
31775 } else {
31776 panic!("Expected quaternion array");
31777 }
31778 }
31779
31780 #[test]
31781 fn test_quaternion_from_axis_angle() {
31782 let result =
31784 eval("fn main() { let q = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963); return q; }");
31785 if let Ok(Value::Array(arr)) = result {
31786 let arr = arr.borrow();
31787 assert_eq!(arr.len(), 4);
31788 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
31790 (&arr[0], &arr[1], &arr[2], &arr[3])
31791 {
31792 assert!((w - 0.707).abs() < 0.01, "w={}", w);
31793 assert!(x.abs() < 0.01);
31794 assert!((y - 0.707).abs() < 0.01, "y={}", y);
31795 assert!(z.abs() < 0.01);
31796 }
31797 } else {
31798 panic!("Expected quaternion array");
31799 }
31800 }
31801
31802 #[test]
31803 fn test_quaternion_rotate_vector() {
31804 let result = eval(
31806 r#"
31807 fn main() {
31808 let q = quat_from_axis_angle(vec3(0, 0, 1), 1.5707963);
31809 let v = vec3(1, 0, 0);
31810 return quat_rotate(q, v);
31811 }
31812 "#,
31813 );
31814 if let Ok(Value::Array(arr)) = result {
31815 let arr = arr.borrow();
31816 assert_eq!(arr.len(), 3);
31817 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31818 {
31819 assert!(x.abs() < 0.01, "x={}", x);
31820 assert!((y - 1.0).abs() < 0.01, "y={}", y);
31821 assert!(z.abs() < 0.01);
31822 }
31823 } else {
31824 panic!("Expected vec3 array");
31825 }
31826 }
31827
31828 #[test]
31829 fn test_quaternion_slerp() {
31830 let result = eval(
31832 r#"
31833 fn main() {
31834 let q1 = quat_identity();
31835 let q2 = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963);
31836 return quat_slerp(q1, q2, 0.5);
31837 }
31838 "#,
31839 );
31840 if let Ok(Value::Array(arr)) = result {
31841 let arr = arr.borrow();
31842 assert_eq!(arr.len(), 4);
31843 if let Value::Float(w) = &arr[0] {
31845 assert!((w - 0.924).abs() < 0.05, "w={}", w);
31847 }
31848 } else {
31849 panic!("Expected quaternion array");
31850 }
31851 }
31852
31853 #[test]
31854 fn test_vec3_operations() {
31855 let result = eval("fn main() { return vec3_add(vec3(1, 2, 3), vec3(4, 5, 6)); }");
31857 if let Ok(Value::Array(arr)) = result {
31858 let arr = arr.borrow();
31859 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31860 {
31861 assert!((x - 5.0).abs() < 0.001);
31862 assert!((y - 7.0).abs() < 0.001);
31863 assert!((z - 9.0).abs() < 0.001);
31864 }
31865 }
31866
31867 let result = eval("fn main() { return vec3_dot(vec3(1, 2, 3), vec3(4, 5, 6)); }");
31869 assert!(matches!(result, Ok(Value::Float(f)) if (f - 32.0).abs() < 0.001));
31870
31871 let result = eval("fn main() { return vec3_cross(vec3(1, 0, 0), vec3(0, 1, 0)); }");
31873 if let Ok(Value::Array(arr)) = result {
31874 let arr = arr.borrow();
31875 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31876 {
31877 assert!(x.abs() < 0.001);
31878 assert!(y.abs() < 0.001);
31879 assert!((z - 1.0).abs() < 0.001);
31880 }
31881 }
31882
31883 let result = eval("fn main() { return vec3_length(vec3(3, 4, 0)); }");
31885 assert!(matches!(result, Ok(Value::Float(f)) if (f - 5.0).abs() < 0.001));
31886
31887 let result = eval("fn main() { return vec3_normalize(vec3(3, 0, 0)); }");
31889 if let Ok(Value::Array(arr)) = result {
31890 let arr = arr.borrow();
31891 if let Value::Float(x) = &arr[0] {
31892 assert!((x - 1.0).abs() < 0.001);
31893 }
31894 }
31895 }
31896
31897 #[test]
31898 fn test_vec3_reflect() {
31899 let result = eval("fn main() { return vec3_reflect(vec3(1, -1, 0), vec3(0, 1, 0)); }");
31901 if let Ok(Value::Array(arr)) = result {
31902 let arr = arr.borrow();
31903 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
31904 {
31905 assert!((x - 1.0).abs() < 0.001);
31906 assert!((y - 1.0).abs() < 0.001);
31907 assert!(z.abs() < 0.001);
31908 }
31909 }
31910 }
31911
31912 #[test]
31913 fn test_mat4_identity() {
31914 let result = eval("fn main() { return mat4_identity(); }");
31915 if let Ok(Value::Array(arr)) = result {
31916 let arr = arr.borrow();
31917 assert_eq!(arr.len(), 16);
31918 if let (Value::Float(m00), Value::Float(m55), Value::Float(m10), Value::Float(m15)) =
31920 (&arr[0], &arr[5], &arr[10], &arr[15])
31921 {
31922 assert!((m00 - 1.0).abs() < 0.001);
31923 assert!((m55 - 1.0).abs() < 0.001);
31924 assert!((m10 - 1.0).abs() < 0.001);
31925 assert!((m15 - 1.0).abs() < 0.001);
31926 }
31927 }
31928 }
31929
31930 #[test]
31931 fn test_mat4_translate() {
31932 let result = eval(
31933 r#"
31934 fn main() {
31935 let t = mat4_translate(5.0, 10.0, 15.0);
31936 let v = vec4(0, 0, 0, 1);
31937 return mat4_transform(t, v);
31938 }
31939 "#,
31940 );
31941 if let Ok(Value::Array(arr)) = result {
31942 let arr = arr.borrow();
31943 if let (Value::Float(x), Value::Float(y), Value::Float(z), Value::Float(w)) =
31944 (&arr[0], &arr[1], &arr[2], &arr[3])
31945 {
31946 assert!((x - 5.0).abs() < 0.001);
31947 assert!((y - 10.0).abs() < 0.001);
31948 assert!((z - 15.0).abs() < 0.001);
31949 assert!((w - 1.0).abs() < 0.001);
31950 }
31951 }
31952 }
31953
31954 #[test]
31955 fn test_mat4_perspective() {
31956 let result = eval("fn main() { return mat4_perspective(1.0472, 1.777, 0.1, 100.0); }");
31958 if let Ok(Value::Array(arr)) = result {
31959 let arr = arr.borrow();
31960 assert_eq!(arr.len(), 16);
31961 } else {
31962 panic!("Expected mat4 array");
31963 }
31964 }
31965
31966 #[test]
31967 fn test_mat4_look_at() {
31968 let result = eval(
31969 r#"
31970 fn main() {
31971 let eye = vec3(0, 0, 5);
31972 let center = vec3(0, 0, 0);
31973 let up = vec3(0, 1, 0);
31974 return mat4_look_at(eye, center, up);
31975 }
31976 "#,
31977 );
31978 if let Ok(Value::Array(arr)) = result {
31979 let arr = arr.borrow();
31980 assert_eq!(arr.len(), 16);
31981 } else {
31982 panic!("Expected mat4 array");
31983 }
31984 }
31985
31986 #[test]
31987 fn test_mat4_inverse() {
31988 let result = eval(
31990 r#"
31991 fn main() {
31992 let m = mat4_identity();
31993 return mat4_inverse(m);
31994 }
31995 "#,
31996 );
31997 if let Ok(Value::Array(arr)) = result {
31998 let arr = arr.borrow();
31999 assert_eq!(arr.len(), 16);
32000 if let Value::Float(m00) = &arr[0] {
32001 assert!((m00 - 1.0).abs() < 0.001);
32002 }
32003 }
32004 }
32005
32006 #[test]
32007 fn test_mat3_operations() {
32008 let result = eval("fn main() { return mat3_identity(); }");
32010 if let Ok(Value::Array(arr)) = result {
32011 let arr = arr.borrow();
32012 assert_eq!(arr.len(), 9);
32013 }
32014
32015 let result = eval(
32017 r#"
32018 fn main() {
32019 let m = mat3_identity();
32020 let v = vec3(1, 2, 3);
32021 return mat3_transform(m, v);
32022 }
32023 "#,
32024 );
32025 if let Ok(Value::Array(arr)) = result {
32026 let arr = arr.borrow();
32027 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
32028 {
32029 assert!((x - 1.0).abs() < 0.001);
32030 assert!((y - 2.0).abs() < 0.001);
32031 assert!((z - 3.0).abs() < 0.001);
32032 }
32033 }
32034 }
32035
32036 #[test]
32037 fn test_quat_to_mat4() {
32038 let result = eval(
32040 r#"
32041 fn main() {
32042 let q = quat_identity();
32043 return quat_to_mat4(q);
32044 }
32045 "#,
32046 );
32047 if let Ok(Value::Array(arr)) = result {
32048 let arr = arr.borrow();
32049 assert_eq!(arr.len(), 16);
32050 if let (Value::Float(m00), Value::Float(m55)) = (&arr[0], &arr[5]) {
32052 assert!((m00 - 1.0).abs() < 0.001);
32053 assert!((m55 - 1.0).abs() < 0.001);
32054 }
32055 }
32056 }
32057
32058 #[test]
32062 fn test_channel_basic_send_recv() {
32063 let result = eval(
32065 r#"
32066 fn main() {
32067 let ch = channel_new();
32068 channel_send(ch, 42);
32069 return channel_recv(ch);
32070 }
32071 "#,
32072 );
32073 assert!(matches!(result, Ok(Value::Int(42))));
32074 }
32075
32076 #[test]
32077 fn test_channel_multiple_values() {
32078 let result = eval(
32080 r#"
32081 fn main() {
32082 let ch = channel_new();
32083 channel_send(ch, 1);
32084 channel_send(ch, 2);
32085 channel_send(ch, 3);
32086 let a = channel_recv(ch);
32087 let b = channel_recv(ch);
32088 let c = channel_recv(ch);
32089 return a * 100 + b * 10 + c;
32090 }
32091 "#,
32092 );
32093 assert!(matches!(result, Ok(Value::Int(123))));
32094 }
32095
32096 #[test]
32097 fn test_channel_high_throughput() {
32098 let result = eval(
32100 r#"
32101 fn main() {
32102 let ch = channel_new();
32103 let count = 1000;
32104 let i = 0;
32105 while i < count {
32106 channel_send(ch, i);
32107 i = i + 1;
32108 }
32109
32110 // Receive all and compute sum to verify no data loss
32111 let sum = 0;
32112 let j = 0;
32113 while j < count {
32114 let val = channel_recv(ch);
32115 sum = sum + val;
32116 j = j + 1;
32117 }
32118
32119 // Sum of 0..999 = 499500
32120 return sum;
32121 }
32122 "#,
32123 );
32124 assert!(matches!(result, Ok(Value::Int(499500))));
32125 }
32126
32127 #[test]
32128 fn test_channel_data_integrity() {
32129 let result = eval(
32131 r#"
32132 fn main() {
32133 let ch = channel_new();
32134
32135 // Send various types
32136 channel_send(ch, 42);
32137 channel_send(ch, 3.14);
32138 channel_send(ch, "hello");
32139 channel_send(ch, [1, 2, 3]);
32140
32141 // Receive and verify types
32142 let int_val = channel_recv(ch);
32143 let float_val = channel_recv(ch);
32144 let str_val = channel_recv(ch);
32145 let arr_val = channel_recv(ch);
32146
32147 // Verify by combining results
32148 return int_val + floor(float_val) + len(str_val) + len(arr_val);
32149 }
32150 "#,
32151 );
32152 assert!(matches!(result, Ok(Value::Int(53))));
32154 }
32155
32156 #[test]
32157 fn test_channel_try_recv_empty() {
32158 let result = eval(
32161 r#"
32162 fn main() {
32163 let ch = channel_new();
32164 let result = channel_try_recv(ch);
32165 // Can't pattern match variants in interpreter, so just verify it returns
32166 return type_of(result);
32167 }
32168 "#,
32169 );
32170 assert!(result.is_ok());
32172 }
32173
32174 #[test]
32175 fn test_channel_try_recv_with_value() {
32176 let result = eval(
32178 r#"
32179 fn main() {
32180 let ch = channel_new();
32181 channel_send(ch, 99);
32182 // Use blocking recv since try_recv returns Option variant
32183 // which can't be pattern matched in interpreter
32184 let val = channel_recv(ch);
32185 return val;
32186 }
32187 "#,
32188 );
32189 assert!(matches!(result, Ok(Value::Int(99))));
32190 }
32191
32192 #[test]
32193 fn test_channel_recv_timeout_expires() {
32194 let result = eval(
32196 r#"
32197 fn main() {
32198 let ch = channel_new();
32199 let result = channel_recv_timeout(ch, 10); // 10ms timeout
32200 // Just verify it completes without blocking forever
32201 return 42;
32202 }
32203 "#,
32204 );
32205 assert!(matches!(result, Ok(Value::Int(42))));
32206 }
32207
32208 #[test]
32209 fn test_actor_basic_messaging() {
32210 let result = eval(
32212 r#"
32213 fn main() {
32214 let act = spawn_actor("test_actor");
32215 send_to_actor(act, "ping", 42);
32216 return get_actor_msg_count(act);
32217 }
32218 "#,
32219 );
32220 assert!(matches!(result, Ok(Value::Int(1))));
32221 }
32222
32223 #[test]
32224 fn test_actor_message_storm() {
32225 let result = eval(
32227 r#"
32228 fn main() {
32229 let act = spawn_actor("stress_actor");
32230 let count = 10000;
32231 let i = 0;
32232 while i < count {
32233 send_to_actor(act, "msg", i);
32234 i = i + 1;
32235 }
32236 return get_actor_msg_count(act);
32237 }
32238 "#,
32239 );
32240 assert!(matches!(result, Ok(Value::Int(10000))));
32241 }
32242
32243 #[test]
32244 fn test_actor_pending_count() {
32245 let result = eval(
32247 r#"
32248 fn main() {
32249 let act = spawn_actor("pending_test");
32250
32251 // Send 5 messages
32252 send_to_actor(act, "m1", 1);
32253 send_to_actor(act, "m2", 2);
32254 send_to_actor(act, "m3", 3);
32255 send_to_actor(act, "m4", 4);
32256 send_to_actor(act, "m5", 5);
32257
32258 let pending_before = get_actor_pending(act);
32259
32260 // Receive 2 messages
32261 recv_from_actor(act);
32262 recv_from_actor(act);
32263
32264 let pending_after = get_actor_pending(act);
32265
32266 // Should have 5 pending initially, 3 after receiving 2
32267 return pending_before * 10 + pending_after;
32268 }
32269 "#,
32270 );
32271 assert!(matches!(result, Ok(Value::Int(53)))); }
32273
32274 #[test]
32275 fn test_actor_message_order() {
32276 let result = eval(
32279 r#"
32280 fn main() {
32281 let act = spawn_actor("order_test");
32282 send_to_actor(act, "a", 1);
32283 send_to_actor(act, "b", 2);
32284 send_to_actor(act, "c", 3);
32285
32286 // pop() gives LIFO order, so we get c, b, a
32287 let r1 = recv_from_actor(act);
32288 let r2 = recv_from_actor(act);
32289 let r3 = recv_from_actor(act);
32290
32291 // Return the message types concatenated via their first char values
32292 // c=3, b=2, a=1 in our test
32293 return get_actor_pending(act); // Should be 0 after draining
32294 }
32295 "#,
32296 );
32297 assert!(matches!(result, Ok(Value::Int(0))));
32298 }
32299
32300 #[test]
32301 fn test_actor_recv_empty() {
32302 let result = eval(
32305 r#"
32306 fn main() {
32307 let act = spawn_actor("empty_actor");
32308 // No messages sent, so pending should be 0
32309 return get_actor_pending(act);
32310 }
32311 "#,
32312 );
32313 assert!(matches!(result, Ok(Value::Int(0))));
32314 }
32315
32316 #[test]
32317 fn test_actor_tell_alias() {
32318 let result = eval(
32320 r#"
32321 fn main() {
32322 let act = spawn_actor("tell_test");
32323 tell_actor(act, "hello", 123);
32324 tell_actor(act, "world", 456);
32325 return get_actor_msg_count(act);
32326 }
32327 "#,
32328 );
32329 assert!(matches!(result, Ok(Value::Int(2))));
32330 }
32331
32332 #[test]
32333 fn test_actor_name() {
32334 let result = eval(
32336 r#"
32337 fn main() {
32338 let act = spawn_actor("my_special_actor");
32339 return get_actor_name(act);
32340 }
32341 "#,
32342 );
32343 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "my_special_actor"));
32344 }
32345
32346 #[test]
32347 fn test_multiple_actors() {
32348 let result = eval(
32350 r#"
32351 fn main() {
32352 let a1 = spawn_actor("actor1");
32353 let a2 = spawn_actor("actor2");
32354 let a3 = spawn_actor("actor3");
32355
32356 send_to_actor(a1, "m", 1);
32357 send_to_actor(a2, "m", 1);
32358 send_to_actor(a2, "m", 2);
32359 send_to_actor(a3, "m", 1);
32360 send_to_actor(a3, "m", 2);
32361 send_to_actor(a3, "m", 3);
32362
32363 let c1 = get_actor_msg_count(a1);
32364 let c2 = get_actor_msg_count(a2);
32365 let c3 = get_actor_msg_count(a3);
32366
32367 return c1 * 100 + c2 * 10 + c3;
32368 }
32369 "#,
32370 );
32371 assert!(matches!(result, Ok(Value::Int(123)))); }
32373
32374 #[test]
32375 fn test_multiple_channels() {
32376 let result = eval(
32378 r#"
32379 fn main() {
32380 let ch1 = channel_new();
32381 let ch2 = channel_new();
32382 let ch3 = channel_new();
32383
32384 channel_send(ch1, 100);
32385 channel_send(ch2, 200);
32386 channel_send(ch3, 300);
32387
32388 let v1 = channel_recv(ch1);
32389 let v2 = channel_recv(ch2);
32390 let v3 = channel_recv(ch3);
32391
32392 return v1 + v2 + v3;
32393 }
32394 "#,
32395 );
32396 assert!(matches!(result, Ok(Value::Int(600))));
32397 }
32398
32399 #[test]
32400 fn test_thread_sleep() {
32401 let result = eval(
32403 r#"
32404 fn main() {
32405 thread_sleep(1); // Sleep 1ms
32406 return 42;
32407 }
32408 "#,
32409 );
32410 assert!(matches!(result, Ok(Value::Int(42))));
32411 }
32412
32413 #[test]
32414 fn test_thread_yield() {
32415 let result = eval(
32417 r#"
32418 fn main() {
32419 thread_yield();
32420 return 42;
32421 }
32422 "#,
32423 );
32424 assert!(matches!(result, Ok(Value::Int(42))));
32425 }
32426
32427 #[test]
32428 fn test_thread_id() {
32429 let result = eval(
32431 r#"
32432 fn main() {
32433 let id = thread_id();
32434 return len(id) > 0;
32435 }
32436 "#,
32437 );
32438 assert!(matches!(result, Ok(Value::Bool(true))));
32439 }
32440
32441 #[test]
32442 fn test_channel_stress_interleaved() {
32443 let result = eval(
32445 r#"
32446 fn main() {
32447 let ch = channel_new();
32448 let sum = 0;
32449 let i = 0;
32450 while i < 100 {
32451 channel_send(ch, i);
32452 channel_send(ch, i * 2);
32453 let a = channel_recv(ch);
32454 let b = channel_recv(ch);
32455 sum = sum + a + b;
32456 i = i + 1;
32457 }
32458 // Sum: sum of i + i*2 for i in 0..99
32459 // = sum of 3*i for i in 0..99 = 3 * (99*100/2) = 3 * 4950 = 14850
32460 return sum;
32461 }
32462 "#,
32463 );
32464 assert!(matches!(result, Ok(Value::Int(14850))));
32465 }
32466
32467 #[test]
32468 fn test_actor_stress_with_receive() {
32469 let result = eval(
32471 r#"
32472 fn main() {
32473 let act = spawn_actor("recv_stress");
32474 let count = 1000;
32475 let i = 0;
32476 while i < count {
32477 send_to_actor(act, "data", i);
32478 i = i + 1;
32479 }
32480
32481 // Drain all messages
32482 let drained = 0;
32483 while get_actor_pending(act) > 0 {
32484 recv_from_actor(act);
32485 drained = drained + 1;
32486 }
32487
32488 return drained;
32489 }
32490 "#,
32491 );
32492 assert!(matches!(result, Ok(Value::Int(1000))));
32493 }
32494
32495 use proptest::prelude::*;
32499
32500 proptest! {
32503 #![proptest_config(ProptestConfig::with_cases(100))]
32504
32505 #[test]
32506 fn test_parser_doesnt_crash_on_random_input(s in "\\PC*") {
32507 let mut parser = Parser::new(&s);
32509 let _ = parser.parse_file(); }
32511
32512 #[test]
32513 fn test_parser_handles_unicode(s in "[\\p{L}\\p{N}\\p{P}\\s]{0,100}") {
32514 let mut parser = Parser::new(&s);
32516 let _ = parser.parse_file();
32517 }
32518
32519 #[test]
32520 fn test_parser_nested_brackets(depth in 0..20usize) {
32521 let open: String = (0..depth).map(|_| '(').collect();
32523 let close: String = (0..depth).map(|_| ')').collect();
32524 let code = format!("fn main() {{ return {}1{}; }}", open, close);
32525 let mut parser = Parser::new(&code);
32526 let _ = parser.parse_file();
32527 }
32528
32529 #[test]
32530 fn test_parser_long_identifiers(len in 1..500usize) {
32531 let ident: String = (0..len).map(|_| 'a').collect();
32533 let code = format!("fn main() {{ let {} = 1; return {}; }}", ident, ident);
32534 let result = eval(&code);
32535 assert!(matches!(result, Ok(Value::Int(1))));
32536 }
32537
32538 #[test]
32539 fn test_parser_many_arguments(count in 0..50usize) {
32540 let args: String = (0..count).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
32542 let code = format!("fn main() {{ return len([{}]); }}", args);
32543 let result = eval(&code);
32544 assert!(matches!(result, Ok(Value::Int(c)) if c == count as i64));
32545 }
32546 }
32547
32548 proptest! {
32551 #![proptest_config(ProptestConfig::with_cases(50))]
32552
32553 #[test]
32554 fn test_ga_bivector_anticommutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32555 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
32556 let code = format!(r#"
32559 fn main() {{
32560 let a = vec3({}, {}, {});
32561 let b = vec3({}, {}, {});
32562 let ab = vec3_cross(a, b);
32563 let ba = vec3_cross(b, a);
32564 let diff_x = get(ab, 0) + get(ba, 0);
32565 let diff_y = get(ab, 1) + get(ba, 1);
32566 let diff_z = get(ab, 2) + get(ba, 2);
32567 let eps = 0.001;
32568 return eps > abs(diff_x) && eps > abs(diff_y) && eps > abs(diff_z);
32569 }}
32570 "#, x1, y1, z1, x2, y2, z2);
32571 let result = eval(&code);
32572 assert!(matches!(result, Ok(Value::Bool(true))));
32573 }
32574
32575 #[test]
32576 fn test_vec3_dot_commutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32577 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
32578 let code = format!(r#"
32580 fn main() {{
32581 let a = vec3({}, {}, {});
32582 let b = vec3({}, {}, {});
32583 let ab = vec3_dot(a, b);
32584 let ba = vec3_dot(b, a);
32585 let eps = 0.001;
32586 return eps > abs(ab - ba);
32587 }}
32588 "#, x1, y1, z1, x2, y2, z2);
32589 let result = eval(&code);
32590 assert!(matches!(result, Ok(Value::Bool(true))));
32591 }
32592
32593 #[test]
32594 fn test_quat_identity_preserves_vector(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0) {
32595 let code = format!(r#"
32597 fn main() {{
32598 let v = vec3({}, {}, {});
32599 let q = quat_identity();
32600 let rotated = quat_rotate(q, v);
32601 let diff_x = abs(get(v, 0) - get(rotated, 0));
32602 let diff_y = abs(get(v, 1) - get(rotated, 1));
32603 let diff_z = abs(get(v, 2) - get(rotated, 2));
32604 let eps = 0.001;
32605 return eps > diff_x && eps > diff_y && eps > diff_z;
32606 }}
32607 "#, x, y, z);
32608 let result = eval(&code);
32609 assert!(matches!(result, Ok(Value::Bool(true))));
32610 }
32611
32612 #[test]
32613 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,
32614 angle in -3.14f64..3.14) {
32615 let code = format!(r#"
32617 fn main() {{
32618 let v = vec3({}, {}, {});
32619 let axis = vec3(0.0, 1.0, 0.0);
32620 let q1 = quat_from_axis_angle(axis, {});
32621 let q2 = quat_from_axis_angle(axis, {} * 2.0);
32622 let q1q1 = quat_mul(q1, q1);
32623 let eps = 0.01;
32624 let same = eps > abs(get(q2, 0) - get(q1q1, 0)) &&
32625 eps > abs(get(q2, 1) - get(q1q1, 1)) &&
32626 eps > abs(get(q2, 2) - get(q1q1, 2)) &&
32627 eps > abs(get(q2, 3) - get(q1q1, 3));
32628 let neg_same = eps > abs(get(q2, 0) + get(q1q1, 0)) &&
32629 eps > abs(get(q2, 1) + get(q1q1, 1)) &&
32630 eps > abs(get(q2, 2) + get(q1q1, 2)) &&
32631 eps > abs(get(q2, 3) + get(q1q1, 3));
32632 return same || neg_same;
32633 }}
32634 "#, x, y, z, angle, angle);
32635 let result = eval(&code);
32636 assert!(matches!(result, Ok(Value::Bool(true))));
32637 }
32638
32639 #[test]
32640 fn test_vec3_add_associative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
32641 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0,
32642 x3 in -100.0f64..100.0, y3 in -100.0f64..100.0, z3 in -100.0f64..100.0) {
32643 let code = format!(r#"
32645 fn main() {{
32646 let a = vec3({}, {}, {});
32647 let b = vec3({}, {}, {});
32648 let c = vec3({}, {}, {});
32649 let ab_c = vec3_add(vec3_add(a, b), c);
32650 let a_bc = vec3_add(a, vec3_add(b, c));
32651 let diff_x = abs(get(ab_c, 0) - get(a_bc, 0));
32652 let diff_y = abs(get(ab_c, 1) - get(a_bc, 1));
32653 let diff_z = abs(get(ab_c, 2) - get(a_bc, 2));
32654 let eps = 0.001;
32655 return eps > diff_x && eps > diff_y && eps > diff_z;
32656 }}
32657 "#, x1, y1, z1, x2, y2, z2, x3, y3, z3);
32658 let result = eval(&code);
32659 assert!(matches!(result, Ok(Value::Bool(true))));
32660 }
32661
32662 #[test]
32663 fn test_vec3_scale_distributive(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0,
32664 s1 in -10.0f64..10.0, s2 in -10.0f64..10.0) {
32665 let code = format!(r#"
32667 fn main() {{
32668 let v = vec3({}, {}, {});
32669 let s1 = {};
32670 let s2 = {};
32671 let combined = vec3_scale(v, s1 + s2);
32672 let separate = vec3_add(vec3_scale(v, s1), vec3_scale(v, s2));
32673 let diff_x = abs(get(combined, 0) - get(separate, 0));
32674 let diff_y = abs(get(combined, 1) - get(separate, 1));
32675 let diff_z = abs(get(combined, 2) - get(separate, 2));
32676 let eps = 0.01;
32677 return eps > diff_x && eps > diff_y && eps > diff_z;
32678 }}
32679 "#, x, y, z, s1, s2);
32680 let result = eval(&code);
32681 assert!(matches!(result, Ok(Value::Bool(true))));
32682 }
32683 }
32684
32685 proptest! {
32688 #![proptest_config(ProptestConfig::with_cases(30))]
32689
32690 #[test]
32691 fn test_grad_of_constant_is_zero(c in -100.0f64..100.0, x in -100.0f64..100.0) {
32692 let code = format!(r#"
32694 fn main() {{
32695 fn constant(x) {{ return {}; }}
32696 let g = grad(constant, {});
32697 let eps = 0.001;
32698 return eps > abs(g);
32699 }}
32700 "#, c, x);
32701 let result = eval(&code);
32702 assert!(matches!(result, Ok(Value::Bool(true))));
32703 }
32704
32705 #[test]
32706 fn test_grad_of_x_is_one(x in -100.0f64..100.0) {
32707 let code = format!(r#"
32709 fn main() {{
32710 fn identity(x) {{ return x; }}
32711 let g = grad(identity, {});
32712 let eps = 0.001;
32713 return eps > abs(g - 1.0);
32714 }}
32715 "#, x);
32716 let result = eval(&code);
32717 assert!(matches!(result, Ok(Value::Bool(true))));
32718 }
32719
32720 #[test]
32721 fn test_grad_of_x_squared(x in -50.0f64..50.0) {
32722 let code = format!(r#"
32724 fn main() {{
32725 fn square(x) {{ return x * x; }}
32726 let g = grad(square, {});
32727 let expected = 2.0 * {};
32728 let eps = 0.1;
32729 return eps > abs(g - expected);
32730 }}
32731 "#, x, x);
32732 let result = eval(&code);
32733 assert!(matches!(result, Ok(Value::Bool(true))));
32734 }
32735
32736 #[test]
32737 fn test_grad_linearity(a in -10.0f64..10.0, b in -10.0f64..10.0, x in -10.0f64..10.0) {
32738 let code = format!(r#"
32740 fn main() {{
32741 fn linear(x) {{ return {} * x + {}; }}
32742 let g = grad(linear, {});
32743 let eps = 0.1;
32744 return eps > abs(g - {});
32745 }}
32746 "#, a, b, x, a);
32747 let result = eval(&code);
32748 assert!(matches!(result, Ok(Value::Bool(true))));
32749 }
32750 }
32751
32752 proptest! {
32755 #![proptest_config(ProptestConfig::with_cases(50))]
32756
32757 #[test]
32758 fn test_addition_commutative(a in -1000i64..1000, b in -1000i64..1000) {
32759 let code = format!("fn main() {{ return {} + {} == {} + {}; }}", a, b, b, a);
32760 let result = eval(&code);
32761 assert!(matches!(result, Ok(Value::Bool(true))));
32762 }
32763
32764 #[test]
32765 fn test_multiplication_commutative(a in -100i64..100, b in -100i64..100) {
32766 let code = format!("fn main() {{ return {} * {} == {} * {}; }}", a, b, b, a);
32767 let result = eval(&code);
32768 assert!(matches!(result, Ok(Value::Bool(true))));
32769 }
32770
32771 #[test]
32772 fn test_addition_identity(a in -1000i64..1000) {
32773 let code = format!("fn main() {{ return {} + 0 == {}; }}", a, a);
32774 let result = eval(&code);
32775 assert!(matches!(result, Ok(Value::Bool(true))));
32776 }
32777
32778 #[test]
32779 fn test_multiplication_identity(a in -1000i64..1000) {
32780 let code = format!("fn main() {{ return {} * 1 == {}; }}", a, a);
32781 let result = eval(&code);
32782 assert!(matches!(result, Ok(Value::Bool(true))));
32783 }
32784
32785 #[test]
32786 fn test_distributive_property(a in -20i64..20, b in -20i64..20, c in -20i64..20) {
32787 let code = format!("fn main() {{ return {} * ({} + {}) == {} * {} + {} * {}; }}", a, b, c, a, b, a, c);
32788 let result = eval(&code);
32789 assert!(matches!(result, Ok(Value::Bool(true))));
32790 }
32791 }
32792
32793 proptest! {
32796 #![proptest_config(ProptestConfig::with_cases(30))]
32797
32798 #[test]
32799 fn test_array_len_after_push(initial_len in 0..20usize, value in -100i64..100) {
32800 let initial: String = (0..initial_len).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
32801 let code = format!(r#"
32802 fn main() {{
32803 let arr = [{}];
32804 push(arr, {});
32805 return len(arr);
32806 }}
32807 "#, initial, value);
32808 let result = eval(&code);
32809 assert!(matches!(result, Ok(Value::Int(n)) if n == (initial_len + 1) as i64));
32810 }
32811
32812 #[test]
32813 fn test_reverse_reverse_identity(elements in prop::collection::vec(-100i64..100, 0..10)) {
32814 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
32815 let code = format!(r#"
32816 fn main() {{
32817 let arr = [{}];
32818 let rev1 = reverse(arr);
32819 let rev2 = reverse(rev1);
32820 let same = true;
32821 let i = 0;
32822 while i < len(arr) {{
32823 if get(arr, i) != get(rev2, i) {{
32824 same = false;
32825 }}
32826 i = i + 1;
32827 }}
32828 return same;
32829 }}
32830 "#, arr_str);
32831 let result = eval(&code);
32832 assert!(matches!(result, Ok(Value::Bool(true))));
32833 }
32834
32835 #[test]
32836 fn test_sum_equals_manual_sum(elements in prop::collection::vec(-100i64..100, 0..20)) {
32837 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
32838 let expected_sum: i64 = elements.iter().sum();
32839 let code = format!("fn main() {{ return sum([{}]); }}", arr_str);
32840 let result = eval(&code);
32841 assert!(matches!(result, Ok(Value::Int(n)) if n == expected_sum));
32842 }
32843 }
32844
32845 #[test]
32853 fn test_no_leak_repeated_array_operations() {
32854 let result = eval(
32856 r#"
32857 fn main() {
32858 let i = 0;
32859 while i < 1000 {
32860 let arr = [1, 2, 3, 4, 5];
32861 push(arr, 6);
32862 let rev = reverse(arr);
32863 let s = sum(arr);
32864 i = i + 1;
32865 }
32866 return i;
32867 }
32868 "#,
32869 );
32870 assert!(matches!(result, Ok(Value::Int(1000))));
32871 }
32872
32873 #[test]
32874 fn test_no_leak_repeated_function_calls() {
32875 let result = eval(
32877 r#"
32878 fn fib(n) {
32879 if n <= 1 { return n; }
32880 return fib(n - 1) + fib(n - 2);
32881 }
32882 fn main() {
32883 let i = 0;
32884 let total = 0;
32885 while i < 100 {
32886 total = total + fib(10);
32887 i = i + 1;
32888 }
32889 return total;
32890 }
32891 "#,
32892 );
32893 assert!(matches!(result, Ok(Value::Int(5500))));
32894 }
32895
32896 #[test]
32897 fn test_no_leak_repeated_map_operations() {
32898 let result = eval(
32900 r#"
32901 fn main() {
32902 let i = 0;
32903 while i < 500 {
32904 let m = map_new();
32905 map_set(m, "key1", 1);
32906 map_set(m, "key2", 2);
32907 map_set(m, "key3", 3);
32908 let v = map_get(m, "key1");
32909 i = i + 1;
32910 }
32911 return i;
32912 }
32913 "#,
32914 );
32915 assert!(matches!(result, Ok(Value::Int(500))));
32916 }
32917
32918 #[test]
32919 fn test_no_leak_repeated_string_operations() {
32920 let result = eval(
32922 r#"
32923 fn main() {
32924 let i = 0;
32925 while i < 1000 {
32926 let s = "hello world";
32927 let upper_s = upper(s);
32928 let lower_s = lower(upper_s);
32929 let concat_s = s ++ " " ++ upper_s;
32930 let replaced = replace(concat_s, "o", "0");
32931 i = i + 1;
32932 }
32933 return i;
32934 }
32935 "#,
32936 );
32937 assert!(matches!(result, Ok(Value::Int(1000))));
32938 }
32939
32940 #[test]
32941 fn test_no_leak_repeated_ecs_operations() {
32942 let result = eval(
32944 r#"
32945 fn main() {
32946 let world = ecs_world();
32947 let i = 0;
32948 while i < 500 {
32949 let entity = ecs_spawn(world);
32950 ecs_attach(world, entity, "Position", vec3(1.0, 2.0, 3.0));
32951 ecs_attach(world, entity, "Velocity", vec3(0.0, 0.0, 0.0));
32952 let pos = ecs_get(world, entity, "Position");
32953 i = i + 1;
32954 }
32955 return i;
32956 }
32957 "#,
32958 );
32959 assert!(matches!(result, Ok(Value::Int(500))));
32960 }
32961
32962 #[test]
32963 fn test_no_leak_repeated_channel_operations() {
32964 let result = eval(
32966 r#"
32967 fn main() {
32968 let i = 0;
32969 while i < 500 {
32970 let ch = channel_new();
32971 channel_send(ch, i);
32972 channel_send(ch, i + 1);
32973 let v1 = channel_recv(ch);
32974 let v2 = channel_recv(ch);
32975 i = i + 1;
32976 }
32977 return i;
32978 }
32979 "#,
32980 );
32981 assert!(matches!(result, Ok(Value::Int(500))));
32982 }
32983
32984 #[test]
32985 fn test_no_leak_repeated_actor_operations() {
32986 let result = eval(
32988 r#"
32989 fn main() {
32990 let i = 0;
32991 while i < 100 {
32992 let act = spawn_actor("leak_test_actor");
32993 send_to_actor(act, "msg", i);
32994 send_to_actor(act, "msg", i + 1);
32995 let count = get_actor_msg_count(act);
32996 i = i + 1;
32997 }
32998 return i;
32999 }
33000 "#,
33001 );
33002 assert!(matches!(result, Ok(Value::Int(100))));
33003 }
33004
33005 #[test]
33006 fn test_no_leak_repeated_vec3_operations() {
33007 let result = eval(
33009 r#"
33010 fn main() {
33011 let i = 0;
33012 while i < 1000 {
33013 let v1 = vec3(1.0, 2.0, 3.0);
33014 let v2 = vec3(4.0, 5.0, 6.0);
33015 let added = vec3_add(v1, v2);
33016 let scaled = vec3_scale(added, 2.0);
33017 let dot = vec3_dot(v1, v2);
33018 let crossed = vec3_cross(v1, v2);
33019 let normalized = vec3_normalize(crossed);
33020 i = i + 1;
33021 }
33022 return i;
33023 }
33024 "#,
33025 );
33026 assert!(matches!(result, Ok(Value::Int(1000))));
33027 }
33028
33029 #[test]
33030 fn test_no_leak_repeated_closure_creation() {
33031 let result = eval(
33033 r#"
33034 fn main() {
33035 let i = 0;
33036 let total = 0;
33037 while i < 500 {
33038 let x = i;
33039 fn add_x(y) { return x + y; }
33040 total = total + add_x(1);
33041 i = i + 1;
33042 }
33043 return total;
33044 }
33045 "#,
33046 );
33047 assert!(matches!(result, Ok(Value::Int(125250))));
33049 }
33050
33051 #[test]
33052 fn test_no_leak_nested_data_structures() {
33053 let result = eval(
33055 r#"
33056 fn main() {
33057 let i = 0;
33058 while i < 200 {
33059 let inner1 = [1, 2, 3];
33060 let inner2 = [4, 5, 6];
33061 let outer = [inner1, inner2];
33062 let m = map_new();
33063 map_set(m, "arr", outer);
33064 map_set(m, "nested", map_new());
33065 i = i + 1;
33066 }
33067 return i;
33068 }
33069 "#,
33070 );
33071 assert!(matches!(result, Ok(Value::Int(200))));
33072 }
33073
33074 #[test]
33075 fn test_no_leak_repeated_interpreter_creation() {
33076 for _ in 0..50 {
33078 let result = eval(
33079 r#"
33080 fn main() {
33081 let arr = [1, 2, 3, 4, 5];
33082 let total = sum(arr);
33083 return total * 2;
33084 }
33085 "#,
33086 );
33087 assert!(matches!(result, Ok(Value::Int(30))));
33088 }
33089 }
33090}