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::rc::Rc;
52use std::sync::{mpsc, Arc, Mutex};
53use std::thread;
54use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
55
56use base64::{engine::general_purpose, Engine as _};
58use md5::Md5;
59use regex::Regex;
60use sha2::{Digest, Sha256, Sha512};
61use unicode_normalization::UnicodeNormalization;
62use unicode_segmentation::UnicodeSegmentation;
63use uuid::Uuid;
64
65use deunicode::deunicode;
67use icu_casemap::titlecase::TitlecaseOptions;
68use icu_casemap::CaseMapper;
69use icu_collator::{Collator, CollatorOptions};
70use icu_locid::{LanguageIdentifier, Locale};
71use icu_segmenter::{SentenceSegmenter, WordSegmenter};
72use unicode_bidi::BidiInfo;
73use unicode_script::{Script, UnicodeScript};
74use unicode_width::UnicodeWidthStr;
75
76use rust_stemmers::{Algorithm as StemAlgorithm, Stemmer};
78use tiktoken_rs::{cl100k_base, p50k_base, r50k_base};
79use whatlang::{detect, Lang, Script as WhatLangScript};
80
81use rand::Rng;
83
84pub fn register_stdlib(interp: &mut Interpreter) {
86 register_core(interp);
87 register_math(interp);
88 register_collections(interp);
89 register_string(interp);
90 register_evidence(interp);
91 register_affect(interp);
92 register_iter(interp);
93 register_io(interp);
94 register_time(interp);
95 register_random(interp);
96 register_convert(interp);
97 register_cycle(interp);
98 register_simd(interp);
99 register_graphics_math(interp);
100 register_concurrency(interp);
101 register_json(interp);
103 register_fs(interp);
104 register_crypto(interp);
105 register_regex(interp);
106 register_uuid(interp);
107 register_system(interp);
108 register_stats(interp);
109 register_matrix(interp);
110 register_functional(interp);
112 register_benchmark(interp);
113 register_itertools(interp);
114 register_ranges(interp);
115 register_bitwise(interp);
116 register_format(interp);
117 register_pattern(interp);
119 register_devex(interp);
121 register_soa(interp);
123 register_tensor(interp);
124 register_autodiff(interp);
125 register_spatial(interp);
126 register_physics(interp);
127 register_geometric_algebra(interp);
129 register_dimensional(interp);
130 register_ecs(interp);
131 register_polycultural_text(interp);
133 register_text_intelligence(interp);
135 register_hologram(interp);
137 register_experimental_crypto(interp);
138 register_multibase(interp);
140 register_audio(interp);
142 register_spirituality(interp);
144 register_color(interp);
146 register_protocol(interp);
148 register_agent_tools(interp);
150 register_agent_llm(interp);
151 register_agent_memory(interp);
152 register_agent_planning(interp);
153 register_agent_vectors(interp);
154 register_agent_swarm(interp);
156 register_agent_reasoning(interp);
157}
158
159fn define(
161 interp: &mut Interpreter,
162 name: &str,
163 arity: Option<usize>,
164 func: fn(&mut Interpreter, Vec<Value>) -> Result<Value, RuntimeError>,
165) {
166 let builtin = Value::BuiltIn(Rc::new(BuiltInFn {
167 name: name.to_string(),
168 arity,
169 func,
170 }));
171 interp
172 .globals
173 .borrow_mut()
174 .define(name.to_string(), builtin);
175}
176
177fn values_equal_simple(a: &Value, b: &Value) -> bool {
179 match (a, b) {
180 (Value::Int(x), Value::Int(y)) => x == y,
181 (Value::Float(x), Value::Float(y)) => (x - y).abs() < f64::EPSILON,
182 (Value::Int(x), Value::Float(y)) | (Value::Float(y), Value::Int(x)) => {
183 (*x as f64 - y).abs() < f64::EPSILON
184 }
185 (Value::Bool(x), Value::Bool(y)) => x == y,
186 (Value::String(x), Value::String(y)) => x == y,
187 (Value::Char(x), Value::Char(y)) => x == y,
188 (Value::Null, Value::Null) => true,
189 (Value::Empty, Value::Empty) => true,
190 (Value::Infinity, Value::Infinity) => true,
191 _ => false,
192 }
193}
194
195fn register_core(interp: &mut Interpreter) {
200 define(interp, "print", None, |interp, args| {
202 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
203 let line = output.join(" ");
204 print!("{}", line);
205 std::io::stdout().flush().ok();
206 interp.output.push(line);
207 Ok(Value::Null)
208 });
209
210 define(interp, "println", None, |interp, args| {
212 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
213 let line = output.join(" ");
214 println!("{}", line);
215 interp.output.push(line);
216 Ok(Value::Null)
217 });
218
219 define(interp, "dbg", Some(1), |interp, args| {
221 let output = format!("[DEBUG] {:?}", args[0]);
222 println!("{}", output);
223 interp.output.push(output);
224 Ok(args[0].clone())
225 });
226
227 define(interp, "type_of", Some(1), |_, args| {
229 let type_name = match &args[0] {
230 Value::Null => "null",
231 Value::Bool(_) => "bool",
232 Value::Int(_) => "i64",
233 Value::Float(_) => "f64",
234 Value::String(_) => "str",
235 Value::Char(_) => "char",
236 Value::Array(_) => "array",
237 Value::Tuple(_) => "tuple",
238 Value::Struct { name, .. } => name,
239 Value::Variant { enum_name, .. } => enum_name,
240 Value::Function(_) => "fn",
241 Value::BuiltIn(_) => "builtin",
242 Value::Ref(_) => "ref",
243 Value::Infinity => "infinity",
244 Value::Empty => "empty",
245 Value::Evidential { evidence, .. } => match evidence {
246 Evidence::Known => "known",
247 Evidence::Uncertain => "uncertain",
248 Evidence::Reported => "reported",
249 Evidence::Paradox => "paradox",
250 },
251 Value::Affective { .. } => "affective",
252 Value::Map(_) => "map",
253 Value::Set(_) => "set",
254 Value::Channel(_) => "channel",
255 Value::ThreadHandle(_) => "thread",
256 Value::Actor(_) => "actor",
257 Value::Future(_) => "future",
258 };
259 Ok(Value::String(Rc::new(type_name.to_string())))
260 });
261
262 define(interp, "assert", None, |_, args| {
264 if args.is_empty() {
265 return Err(RuntimeError::new("assert() requires at least one argument"));
266 }
267 let condition = match &args[0] {
268 Value::Bool(b) => *b,
269 _ => return Err(RuntimeError::new("assert() condition must be bool")),
270 };
271 if !condition {
272 let msg = if args.len() > 1 {
273 format!("{}", args[1])
274 } else {
275 "assertion failed".to_string()
276 };
277 return Err(RuntimeError::new(format!("Assertion failed: {}", msg)));
278 }
279 Ok(Value::Null)
280 });
281
282 define(interp, "panic", None, |_, args| {
284 let msg = if args.is_empty() {
285 "explicit panic".to_string()
286 } else {
287 args.iter()
288 .map(|v| format!("{}", v))
289 .collect::<Vec<_>>()
290 .join(" ")
291 };
292 Err(RuntimeError::new(format!("PANIC: {}", msg)))
293 });
294
295 define(interp, "todo", None, |_, args| {
297 let msg = if args.is_empty() {
298 "not yet implemented".to_string()
299 } else {
300 format!("{}", args[0])
301 };
302 Err(RuntimeError::new(format!("TODO: {}", msg)))
303 });
304
305 define(interp, "unreachable", None, |_, args| {
307 let msg = if args.is_empty() {
308 "entered unreachable code".to_string()
309 } else {
310 format!("{}", args[0])
311 };
312 Err(RuntimeError::new(format!("UNREACHABLE: {}", msg)))
313 });
314
315 define(interp, "clone", Some(1), |_, args| Ok(deep_clone(&args[0])));
317
318 define(interp, "id", Some(1), |_, args| Ok(args[0].clone()));
320
321 define(interp, "default", Some(1), |_, args| {
323 let type_name = match &args[0] {
324 Value::String(s) => s.as_str(),
325 _ => return Err(RuntimeError::new("default() requires type name string")),
326 };
327 match type_name {
328 "bool" => Ok(Value::Bool(false)),
329 "i64" | "int" => Ok(Value::Int(0)),
330 "f64" | "float" => Ok(Value::Float(0.0)),
331 "str" | "string" => Ok(Value::String(Rc::new(String::new()))),
332 "array" => Ok(Value::Array(Rc::new(RefCell::new(Vec::new())))),
333 _ => Err(RuntimeError::new(format!(
334 "no default for type: {}",
335 type_name
336 ))),
337 }
338 });
339}
340
341fn deep_clone(value: &Value) -> Value {
343 match value {
344 Value::Array(arr) => {
345 let cloned: Vec<Value> = arr.borrow().iter().map(deep_clone).collect();
346 Value::Array(Rc::new(RefCell::new(cloned)))
347 }
348 Value::Struct { name, fields } => {
349 let cloned: HashMap<String, Value> = fields
350 .borrow()
351 .iter()
352 .map(|(k, v)| (k.clone(), deep_clone(v)))
353 .collect();
354 Value::Struct {
355 name: name.clone(),
356 fields: Rc::new(RefCell::new(cloned)),
357 }
358 }
359 Value::Evidential { value, evidence } => Value::Evidential {
360 value: Box::new(deep_clone(value)),
361 evidence: *evidence,
362 },
363 other => other.clone(),
364 }
365}
366
367fn register_math(interp: &mut Interpreter) {
372 define(interp, "abs", Some(1), |_, args| match &args[0] {
374 Value::Int(n) => Ok(Value::Int(n.abs())),
375 Value::Float(n) => Ok(Value::Float(n.abs())),
376 _ => Err(RuntimeError::new("abs() requires number")),
377 });
378
379 define(interp, "neg", Some(1), |_, args| match &args[0] {
380 Value::Int(n) => Ok(Value::Int(-n)),
381 Value::Float(n) => Ok(Value::Float(-n)),
382 _ => Err(RuntimeError::new("neg() requires number")),
383 });
384
385 define(interp, "sqrt", Some(1), |_, args| match &args[0] {
386 Value::Int(n) => Ok(Value::Float((*n as f64).sqrt())),
387 Value::Float(n) => Ok(Value::Float(n.sqrt())),
388 _ => Err(RuntimeError::new("sqrt() requires number")),
389 });
390
391 define(interp, "cbrt", Some(1), |_, args| match &args[0] {
392 Value::Int(n) => Ok(Value::Float((*n as f64).cbrt())),
393 Value::Float(n) => Ok(Value::Float(n.cbrt())),
394 _ => Err(RuntimeError::new("cbrt() requires number")),
395 });
396
397 define(interp, "pow", Some(2), |_, args| {
398 match (&args[0], &args[1]) {
399 (Value::Int(base), Value::Int(exp)) => {
400 if *exp >= 0 {
401 Ok(Value::Int(base.pow(*exp as u32)))
402 } else {
403 Ok(Value::Float((*base as f64).powi(*exp as i32)))
404 }
405 }
406 (Value::Float(base), Value::Int(exp)) => Ok(Value::Float(base.powi(*exp as i32))),
407 (Value::Float(base), Value::Float(exp)) => Ok(Value::Float(base.powf(*exp))),
408 (Value::Int(base), Value::Float(exp)) => Ok(Value::Float((*base as f64).powf(*exp))),
409 _ => Err(RuntimeError::new("pow() requires numbers")),
410 }
411 });
412
413 define(interp, "exp", Some(1), |_, args| match &args[0] {
414 Value::Int(n) => Ok(Value::Float((*n as f64).exp())),
415 Value::Float(n) => Ok(Value::Float(n.exp())),
416 _ => Err(RuntimeError::new("exp() requires number")),
417 });
418
419 define(interp, "ln", Some(1), |_, args| match &args[0] {
420 Value::Int(n) => Ok(Value::Float((*n as f64).ln())),
421 Value::Float(n) => Ok(Value::Float(n.ln())),
422 _ => Err(RuntimeError::new("ln() requires number")),
423 });
424
425 define(interp, "log", Some(2), |_, args| {
426 let (value, base) = match (&args[0], &args[1]) {
427 (Value::Int(v), Value::Int(b)) => (*v as f64, *b as f64),
428 (Value::Float(v), Value::Int(b)) => (*v, *b as f64),
429 (Value::Int(v), Value::Float(b)) => (*v as f64, *b),
430 (Value::Float(v), Value::Float(b)) => (*v, *b),
431 _ => return Err(RuntimeError::new("log() requires numbers")),
432 };
433 Ok(Value::Float(value.log(base)))
434 });
435
436 define(interp, "log10", Some(1), |_, args| match &args[0] {
437 Value::Int(n) => Ok(Value::Float((*n as f64).log10())),
438 Value::Float(n) => Ok(Value::Float(n.log10())),
439 _ => Err(RuntimeError::new("log10() requires number")),
440 });
441
442 define(interp, "log2", Some(1), |_, args| match &args[0] {
443 Value::Int(n) => Ok(Value::Float((*n as f64).log2())),
444 Value::Float(n) => Ok(Value::Float(n.log2())),
445 _ => Err(RuntimeError::new("log2() requires number")),
446 });
447
448 define(interp, "sin", Some(1), |_, args| match &args[0] {
450 Value::Int(n) => Ok(Value::Float((*n as f64).sin())),
451 Value::Float(n) => Ok(Value::Float(n.sin())),
452 _ => Err(RuntimeError::new("sin() requires number")),
453 });
454
455 define(interp, "cos", Some(1), |_, args| match &args[0] {
456 Value::Int(n) => Ok(Value::Float((*n as f64).cos())),
457 Value::Float(n) => Ok(Value::Float(n.cos())),
458 _ => Err(RuntimeError::new("cos() requires number")),
459 });
460
461 define(interp, "tan", Some(1), |_, args| match &args[0] {
462 Value::Int(n) => Ok(Value::Float((*n as f64).tan())),
463 Value::Float(n) => Ok(Value::Float(n.tan())),
464 _ => Err(RuntimeError::new("tan() requires number")),
465 });
466
467 define(interp, "asin", Some(1), |_, args| match &args[0] {
468 Value::Int(n) => Ok(Value::Float((*n as f64).asin())),
469 Value::Float(n) => Ok(Value::Float(n.asin())),
470 _ => Err(RuntimeError::new("asin() requires number")),
471 });
472
473 define(interp, "acos", Some(1), |_, args| match &args[0] {
474 Value::Int(n) => Ok(Value::Float((*n as f64).acos())),
475 Value::Float(n) => Ok(Value::Float(n.acos())),
476 _ => Err(RuntimeError::new("acos() requires number")),
477 });
478
479 define(interp, "atan", Some(1), |_, args| match &args[0] {
480 Value::Int(n) => Ok(Value::Float((*n as f64).atan())),
481 Value::Float(n) => Ok(Value::Float(n.atan())),
482 _ => Err(RuntimeError::new("atan() requires number")),
483 });
484
485 define(interp, "atan2", Some(2), |_, args| {
486 let (y, x) = match (&args[0], &args[1]) {
487 (Value::Int(y), Value::Int(x)) => (*y as f64, *x as f64),
488 (Value::Float(y), Value::Int(x)) => (*y, *x as f64),
489 (Value::Int(y), Value::Float(x)) => (*y as f64, *x),
490 (Value::Float(y), Value::Float(x)) => (*y, *x),
491 _ => return Err(RuntimeError::new("atan2() requires numbers")),
492 };
493 Ok(Value::Float(y.atan2(x)))
494 });
495
496 define(interp, "sinh", Some(1), |_, args| match &args[0] {
498 Value::Int(n) => Ok(Value::Float((*n as f64).sinh())),
499 Value::Float(n) => Ok(Value::Float(n.sinh())),
500 _ => Err(RuntimeError::new("sinh() requires number")),
501 });
502
503 define(interp, "cosh", Some(1), |_, args| match &args[0] {
504 Value::Int(n) => Ok(Value::Float((*n as f64).cosh())),
505 Value::Float(n) => Ok(Value::Float(n.cosh())),
506 _ => Err(RuntimeError::new("cosh() requires number")),
507 });
508
509 define(interp, "tanh", Some(1), |_, args| match &args[0] {
510 Value::Int(n) => Ok(Value::Float((*n as f64).tanh())),
511 Value::Float(n) => Ok(Value::Float(n.tanh())),
512 _ => Err(RuntimeError::new("tanh() requires number")),
513 });
514
515 define(interp, "floor", Some(1), |_, args| match &args[0] {
517 Value::Int(n) => Ok(Value::Int(*n)),
518 Value::Float(n) => Ok(Value::Int(n.floor() as i64)),
519 _ => Err(RuntimeError::new("floor() requires number")),
520 });
521
522 define(interp, "ceil", Some(1), |_, args| match &args[0] {
523 Value::Int(n) => Ok(Value::Int(*n)),
524 Value::Float(n) => Ok(Value::Int(n.ceil() as i64)),
525 _ => Err(RuntimeError::new("ceil() requires number")),
526 });
527
528 define(interp, "round", Some(1), |_, args| match &args[0] {
529 Value::Int(n) => Ok(Value::Int(*n)),
530 Value::Float(n) => Ok(Value::Int(n.round() as i64)),
531 _ => Err(RuntimeError::new("round() requires number")),
532 });
533
534 define(interp, "trunc", Some(1), |_, args| match &args[0] {
535 Value::Int(n) => Ok(Value::Int(*n)),
536 Value::Float(n) => Ok(Value::Int(n.trunc() as i64)),
537 _ => Err(RuntimeError::new("trunc() requires number")),
538 });
539
540 define(interp, "fract", Some(1), |_, args| match &args[0] {
541 Value::Int(_) => Ok(Value::Float(0.0)),
542 Value::Float(n) => Ok(Value::Float(n.fract())),
543 _ => Err(RuntimeError::new("fract() requires number")),
544 });
545
546 define(interp, "min", Some(2), |_, args| {
548 match (&args[0], &args[1]) {
549 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
550 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
551 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).min(*b))),
552 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.min(*b as f64))),
553 _ => Err(RuntimeError::new("min() requires numbers")),
554 }
555 });
556
557 define(interp, "max", Some(2), |_, args| {
558 match (&args[0], &args[1]) {
559 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
560 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
561 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).max(*b))),
562 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.max(*b as f64))),
563 _ => Err(RuntimeError::new("max() requires numbers")),
564 }
565 });
566
567 define(interp, "clamp", Some(3), |_, args| {
568 match (&args[0], &args[1], &args[2]) {
569 (Value::Int(val), Value::Int(min), Value::Int(max)) => {
570 Ok(Value::Int(*val.max(min).min(max)))
571 }
572 (Value::Float(val), Value::Float(min), Value::Float(max)) => {
573 Ok(Value::Float(val.max(*min).min(*max)))
574 }
575 _ => Err(RuntimeError::new("clamp() requires matching number types")),
576 }
577 });
578
579 define(interp, "sign", Some(1), |_, args| match &args[0] {
581 Value::Int(n) => Ok(Value::Int(n.signum())),
582 Value::Float(n) => Ok(Value::Float(if *n > 0.0 {
583 1.0
584 } else if *n < 0.0 {
585 -1.0
586 } else {
587 0.0
588 })),
589 _ => Err(RuntimeError::new("sign() requires number")),
590 });
591
592 define(interp, "PI", Some(0), |_, _| {
594 Ok(Value::Float(std::f64::consts::PI))
595 });
596 define(interp, "E", Some(0), |_, _| {
597 Ok(Value::Float(std::f64::consts::E))
598 });
599 define(interp, "TAU", Some(0), |_, _| {
600 Ok(Value::Float(std::f64::consts::TAU))
601 });
602 define(interp, "PHI", Some(0), |_, _| {
603 Ok(Value::Float(1.618033988749895))
604 }); define(interp, "gcd", Some(2), |_, args| {
608 match (&args[0], &args[1]) {
609 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(gcd(*a, *b))),
610 _ => Err(RuntimeError::new("gcd() requires integers")),
611 }
612 });
613
614 define(interp, "lcm", Some(2), |_, args| {
615 match (&args[0], &args[1]) {
616 (Value::Int(a), Value::Int(b)) => {
617 let g = gcd(*a, *b);
618 Ok(Value::Int((a * b).abs() / g))
619 }
620 _ => Err(RuntimeError::new("lcm() requires integers")),
621 }
622 });
623
624 define(interp, "factorial", Some(1), |_, args| match &args[0] {
626 Value::Int(n) if *n >= 0 => {
627 let mut result: i64 = 1;
628 for i in 2..=(*n as u64) {
629 result = result.saturating_mul(i as i64);
630 }
631 Ok(Value::Int(result))
632 }
633 Value::Int(_) => Err(RuntimeError::new(
634 "factorial() requires non-negative integer",
635 )),
636 _ => Err(RuntimeError::new("factorial() requires integer")),
637 });
638
639 define(interp, "is_nan", Some(1), |_, args| match &args[0] {
641 Value::Float(n) => Ok(Value::Bool(n.is_nan())),
642 Value::Int(_) => Ok(Value::Bool(false)),
643 _ => Err(RuntimeError::new("is_nan() requires number")),
644 });
645
646 define(interp, "is_infinite", Some(1), |_, args| match &args[0] {
647 Value::Float(n) => Ok(Value::Bool(n.is_infinite())),
648 Value::Int(_) => Ok(Value::Bool(false)),
649 Value::Infinity => Ok(Value::Bool(true)),
650 _ => Err(RuntimeError::new("is_infinite() requires number")),
651 });
652
653 define(interp, "is_finite", Some(1), |_, args| match &args[0] {
654 Value::Float(n) => Ok(Value::Bool(n.is_finite())),
655 Value::Int(_) => Ok(Value::Bool(true)),
656 Value::Infinity => Ok(Value::Bool(false)),
657 _ => Err(RuntimeError::new("is_finite() requires number")),
658 });
659
660 define(interp, "is_even", Some(1), |_, args| match &args[0] {
661 Value::Int(n) => Ok(Value::Bool(n % 2 == 0)),
662 _ => Err(RuntimeError::new("is_even() requires integer")),
663 });
664
665 define(interp, "is_odd", Some(1), |_, args| match &args[0] {
666 Value::Int(n) => Ok(Value::Bool(n % 2 != 0)),
667 _ => Err(RuntimeError::new("is_odd() requires integer")),
668 });
669
670 define(interp, "is_prime", Some(1), |_, args| match &args[0] {
671 Value::Int(n) => Ok(Value::Bool(is_prime(*n))),
672 _ => Err(RuntimeError::new("is_prime() requires integer")),
673 });
674}
675
676fn gcd(mut a: i64, mut b: i64) -> i64 {
677 a = a.abs();
678 b = b.abs();
679 while b != 0 {
680 let t = b;
681 b = a % b;
682 a = t;
683 }
684 a
685}
686
687fn is_prime(n: i64) -> bool {
688 if n < 2 {
689 return false;
690 }
691 if n == 2 {
692 return true;
693 }
694 if n % 2 == 0 {
695 return false;
696 }
697 let sqrt = (n as f64).sqrt() as i64;
698 for i in (3..=sqrt).step_by(2) {
699 if n % i == 0 {
700 return false;
701 }
702 }
703 true
704}
705
706fn register_collections(interp: &mut Interpreter) {
711 define(interp, "len", Some(1), |_, args| match &args[0] {
713 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
714 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
715 Value::Tuple(t) => Ok(Value::Int(t.len() as i64)),
716 Value::Map(m) => Ok(Value::Int(m.borrow().len() as i64)),
717 Value::Set(s) => Ok(Value::Int(s.borrow().len() as i64)),
718 _ => Err(RuntimeError::new(
719 "len() requires array, string, tuple, map, or set",
720 )),
721 });
722
723 define(interp, "is_empty", Some(1), |_, args| match &args[0] {
724 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
725 Value::String(s) => Ok(Value::Bool(s.is_empty())),
726 Value::Tuple(t) => Ok(Value::Bool(t.is_empty())),
727 Value::Map(m) => Ok(Value::Bool(m.borrow().is_empty())),
728 Value::Set(s) => Ok(Value::Bool(s.borrow().is_empty())),
729 _ => Err(RuntimeError::new("is_empty() requires collection")),
730 });
731
732 define(interp, "push", Some(2), |_, args| match &args[0] {
734 Value::Array(arr) => {
735 arr.borrow_mut().push(args[1].clone());
736 Ok(Value::Null)
737 }
738 _ => Err(RuntimeError::new("push() requires array")),
739 });
740
741 define(interp, "pop", Some(1), |_, args| match &args[0] {
742 Value::Array(arr) => arr
743 .borrow_mut()
744 .pop()
745 .ok_or_else(|| RuntimeError::new("pop() on empty array")),
746 _ => Err(RuntimeError::new("pop() requires array")),
747 });
748
749 define(interp, "first", Some(1), |_, args| match &args[0] {
750 Value::Array(arr) => arr
751 .borrow()
752 .first()
753 .cloned()
754 .ok_or_else(|| RuntimeError::new("first() on empty array")),
755 Value::Tuple(t) => t
756 .first()
757 .cloned()
758 .ok_or_else(|| RuntimeError::new("first() on empty tuple")),
759 _ => Err(RuntimeError::new("first() requires array or tuple")),
760 });
761
762 define(interp, "last", Some(1), |_, args| match &args[0] {
763 Value::Array(arr) => arr
764 .borrow()
765 .last()
766 .cloned()
767 .ok_or_else(|| RuntimeError::new("last() on empty array")),
768 Value::Tuple(t) => t
769 .last()
770 .cloned()
771 .ok_or_else(|| RuntimeError::new("last() on empty tuple")),
772 _ => Err(RuntimeError::new("last() requires array or tuple")),
773 });
774
775 define(interp, "middle", Some(1), |_, args| match &args[0] {
777 Value::Array(arr) => {
778 let arr = arr.borrow();
779 if arr.is_empty() {
780 return Err(RuntimeError::new("middle() on empty array"));
781 }
782 let mid = arr.len() / 2;
783 Ok(arr[mid].clone())
784 }
785 Value::Tuple(t) => {
786 if t.is_empty() {
787 return Err(RuntimeError::new("middle() on empty tuple"));
788 }
789 let mid = t.len() / 2;
790 Ok(t[mid].clone())
791 }
792 _ => Err(RuntimeError::new("middle() requires array or tuple")),
793 });
794
795 define(interp, "choice", Some(1), |_, args| {
797 use std::time::{SystemTime, UNIX_EPOCH};
798 match &args[0] {
799 Value::Array(arr) => {
800 let arr = arr.borrow();
801 if arr.is_empty() {
802 return Err(RuntimeError::new("choice() on empty array"));
803 }
804 let seed = SystemTime::now()
805 .duration_since(UNIX_EPOCH)
806 .unwrap_or(std::time::Duration::ZERO)
807 .as_nanos() as u64;
808 let idx = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize
809 % arr.len();
810 Ok(arr[idx].clone())
811 }
812 Value::Tuple(t) => {
813 if t.is_empty() {
814 return Err(RuntimeError::new("choice() on empty tuple"));
815 }
816 let seed = SystemTime::now()
817 .duration_since(UNIX_EPOCH)
818 .unwrap_or(std::time::Duration::ZERO)
819 .as_nanos() as u64;
820 let idx =
821 ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % t.len();
822 Ok(t[idx].clone())
823 }
824 _ => Err(RuntimeError::new("choice() requires array or tuple")),
825 }
826 });
827
828 define(interp, "nth", Some(2), |_, args| {
830 let n = match &args[1] {
831 Value::Int(i) => *i,
832 _ => return Err(RuntimeError::new("nth() index must be integer")),
833 };
834 match &args[0] {
835 Value::Array(arr) => {
836 let arr = arr.borrow();
837 if n < 0 || n as usize >= arr.len() {
838 return Err(RuntimeError::new("nth() index out of bounds"));
839 }
840 Ok(arr[n as usize].clone())
841 }
842 Value::Tuple(t) => {
843 if n < 0 || n as usize >= t.len() {
844 return Err(RuntimeError::new("nth() index out of bounds"));
845 }
846 Ok(t[n as usize].clone())
847 }
848 _ => Err(RuntimeError::new("nth() requires array or tuple")),
849 }
850 });
851
852 define(interp, "next", Some(1), |_, args| match &args[0] {
854 Value::Array(arr) => {
855 let mut arr = arr.borrow_mut();
856 if arr.is_empty() {
857 return Err(RuntimeError::new("next() on empty array"));
858 }
859 Ok(arr.remove(0))
860 }
861 _ => Err(RuntimeError::new("next() requires array")),
862 });
863
864 define(interp, "peek", Some(1), |_, args| match &args[0] {
866 Value::Array(arr) => arr
867 .borrow()
868 .first()
869 .cloned()
870 .ok_or_else(|| RuntimeError::new("peek() on empty array")),
871 _ => Err(RuntimeError::new("peek() requires array")),
872 });
873
874 define(interp, "get", Some(2), |_, args| {
875 let index = match &args[1] {
876 Value::Int(i) => *i,
877 _ => return Err(RuntimeError::new("get() index must be integer")),
878 };
879 match &args[0] {
880 Value::Array(arr) => {
881 let arr = arr.borrow();
882 let idx = if index < 0 {
883 arr.len() as i64 + index
884 } else {
885 index
886 } as usize;
887 arr.get(idx)
888 .cloned()
889 .ok_or_else(|| RuntimeError::new("index out of bounds"))
890 }
891 Value::Tuple(t) => {
892 let idx = if index < 0 {
893 t.len() as i64 + index
894 } else {
895 index
896 } as usize;
897 t.get(idx)
898 .cloned()
899 .ok_or_else(|| RuntimeError::new("index out of bounds"))
900 }
901 _ => Err(RuntimeError::new("get() requires array or tuple")),
902 }
903 });
904
905 define(interp, "set", Some(3), |_, args| {
906 let index = match &args[1] {
907 Value::Int(i) => *i as usize,
908 _ => return Err(RuntimeError::new("set() index must be integer")),
909 };
910 match &args[0] {
911 Value::Array(arr) => {
912 let mut arr = arr.borrow_mut();
913 if index >= arr.len() {
914 return Err(RuntimeError::new("index out of bounds"));
915 }
916 arr[index] = args[2].clone();
917 Ok(Value::Null)
918 }
919 _ => Err(RuntimeError::new("set() requires array")),
920 }
921 });
922
923 define(interp, "insert", Some(3), |_, args| {
924 let index = match &args[1] {
925 Value::Int(i) => *i as usize,
926 _ => return Err(RuntimeError::new("insert() index must be integer")),
927 };
928 match &args[0] {
929 Value::Array(arr) => {
930 let mut arr = arr.borrow_mut();
931 if index > arr.len() {
932 return Err(RuntimeError::new("index out of bounds"));
933 }
934 arr.insert(index, args[2].clone());
935 Ok(Value::Null)
936 }
937 _ => Err(RuntimeError::new("insert() requires array")),
938 }
939 });
940
941 define(interp, "remove", Some(2), |_, args| {
942 let index = match &args[1] {
943 Value::Int(i) => *i as usize,
944 _ => return Err(RuntimeError::new("remove() index must be integer")),
945 };
946 match &args[0] {
947 Value::Array(arr) => {
948 let mut arr = arr.borrow_mut();
949 if index >= arr.len() {
950 return Err(RuntimeError::new("index out of bounds"));
951 }
952 Ok(arr.remove(index))
953 }
954 _ => Err(RuntimeError::new("remove() requires array")),
955 }
956 });
957
958 define(interp, "clear", Some(1), |_, args| match &args[0] {
959 Value::Array(arr) => {
960 arr.borrow_mut().clear();
961 Ok(Value::Null)
962 }
963 _ => Err(RuntimeError::new("clear() requires array")),
964 });
965
966 define(interp, "contains", Some(2), |_, args| match &args[0] {
968 Value::Array(arr) => Ok(Value::Bool(
969 arr.borrow().iter().any(|v| values_equal(v, &args[1])),
970 )),
971 Value::String(s) => match &args[1] {
972 Value::String(sub) => Ok(Value::Bool(s.contains(sub.as_str()))),
973 Value::Char(c) => Ok(Value::Bool(s.contains(*c))),
974 _ => Err(RuntimeError::new(
975 "string contains() requires string or char",
976 )),
977 },
978 _ => Err(RuntimeError::new("contains() requires array or string")),
979 });
980
981 define(interp, "index_of", Some(2), |_, args| match &args[0] {
982 Value::Array(arr) => {
983 let idx = arr.borrow().iter().position(|v| values_equal(v, &args[1]));
984 match idx {
985 Some(i) => Ok(Value::Int(i as i64)),
986 None => Ok(Value::Int(-1)),
987 }
988 }
989 Value::String(s) => match &args[1] {
990 Value::String(sub) => match s.find(sub.as_str()) {
991 Some(i) => Ok(Value::Int(i as i64)),
992 None => Ok(Value::Int(-1)),
993 },
994 Value::Char(c) => match s.find(*c) {
995 Some(i) => Ok(Value::Int(i as i64)),
996 None => Ok(Value::Int(-1)),
997 },
998 _ => Err(RuntimeError::new(
999 "string index_of() requires string or char",
1000 )),
1001 },
1002 _ => Err(RuntimeError::new("index_of() requires array or string")),
1003 });
1004
1005 define(interp, "reverse", Some(1), |_, args| match &args[0] {
1007 Value::Array(arr) => {
1008 let mut reversed: Vec<Value> = arr.borrow().clone();
1009 reversed.reverse();
1010 Ok(Value::Array(Rc::new(RefCell::new(reversed))))
1011 }
1012 Value::String(s) => {
1013 let reversed: String = s.chars().rev().collect();
1014 Ok(Value::String(Rc::new(reversed)))
1015 }
1016 _ => Err(RuntimeError::new("reverse() requires array or string")),
1017 });
1018
1019 define(interp, "sort", Some(1), |_, args| match &args[0] {
1020 Value::Array(arr) => {
1021 let mut sorted: Vec<Value> = arr.borrow().clone();
1022 sorted.sort_by(compare_values);
1023 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1024 }
1025 _ => Err(RuntimeError::new("sort() requires array")),
1026 });
1027
1028 define(interp, "sort_desc", Some(1), |_, args| match &args[0] {
1029 Value::Array(arr) => {
1030 let mut sorted: Vec<Value> = arr.borrow().clone();
1031 sorted.sort_by(|a, b| compare_values(b, a));
1032 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1033 }
1034 _ => Err(RuntimeError::new("sort_desc() requires array")),
1035 });
1036
1037 define(interp, "unique", Some(1), |_, args| match &args[0] {
1038 Value::Array(arr) => {
1039 let arr = arr.borrow();
1040 let mut seen = Vec::new();
1041 let unique: Vec<Value> = arr
1042 .iter()
1043 .filter(|v| {
1044 if seen.iter().any(|s| values_equal(s, v)) {
1045 false
1046 } else {
1047 seen.push((*v).clone());
1048 true
1049 }
1050 })
1051 .cloned()
1052 .collect();
1053 Ok(Value::Array(Rc::new(RefCell::new(unique))))
1054 }
1055 _ => Err(RuntimeError::new("unique() requires array")),
1056 });
1057
1058 define(interp, "flatten", Some(1), |_, args| match &args[0] {
1059 Value::Array(arr) => {
1060 let mut flattened = Vec::new();
1061 for item in arr.borrow().iter() {
1062 match item {
1063 Value::Array(inner) => flattened.extend(inner.borrow().clone()),
1064 other => flattened.push(other.clone()),
1065 }
1066 }
1067 Ok(Value::Array(Rc::new(RefCell::new(flattened))))
1068 }
1069 _ => Err(RuntimeError::new("flatten() requires array")),
1070 });
1071
1072 define(interp, "concat", Some(2), |_, args| {
1074 match (&args[0], &args[1]) {
1075 (Value::Array(a), Value::Array(b)) => {
1076 let mut result = a.borrow().clone();
1077 result.extend(b.borrow().clone());
1078 Ok(Value::Array(Rc::new(RefCell::new(result))))
1079 }
1080 (Value::String(a), Value::String(b)) => {
1081 Ok(Value::String(Rc::new(format!("{}{}", a, b))))
1082 }
1083 _ => Err(RuntimeError::new(
1084 "concat() requires two arrays or two strings",
1085 )),
1086 }
1087 });
1088
1089 define(interp, "zip", Some(2), |_, args| {
1090 match (&args[0], &args[1]) {
1091 (Value::Array(a), Value::Array(b)) => {
1092 let a = a.borrow();
1093 let b = b.borrow();
1094 let zipped: Vec<Value> = a
1095 .iter()
1096 .zip(b.iter())
1097 .map(|(x, y)| Value::Tuple(Rc::new(vec![x.clone(), y.clone()])))
1098 .collect();
1099 Ok(Value::Array(Rc::new(RefCell::new(zipped))))
1100 }
1101 _ => Err(RuntimeError::new("zip() requires two arrays")),
1102 }
1103 });
1104
1105 define(interp, "enumerate", Some(1), |_, args| match &args[0] {
1106 Value::Array(arr) => {
1107 let enumerated: Vec<Value> = arr
1108 .borrow()
1109 .iter()
1110 .enumerate()
1111 .map(|(i, v)| Value::Tuple(Rc::new(vec![Value::Int(i as i64), v.clone()])))
1112 .collect();
1113 Ok(Value::Array(Rc::new(RefCell::new(enumerated))))
1114 }
1115 _ => Err(RuntimeError::new("enumerate() requires array")),
1116 });
1117
1118 define(interp, "zip_with", Some(3), |_, args| {
1121 let mode = match &args[2] {
1122 Value::String(s) => s.as_str(),
1123 _ => return Err(RuntimeError::new("zip_with() mode must be string")),
1124 };
1125 match (&args[0], &args[1]) {
1126 (Value::Array(a), Value::Array(b)) => {
1127 let a = a.borrow();
1128 let b = b.borrow();
1129 let result: Result<Vec<Value>, RuntimeError> = a
1130 .iter()
1131 .zip(b.iter())
1132 .map(|(x, y)| match (x, y, mode) {
1133 (Value::Int(a), Value::Int(b), "add") => Ok(Value::Int(a + b)),
1134 (Value::Int(a), Value::Int(b), "sub") => Ok(Value::Int(a - b)),
1135 (Value::Int(a), Value::Int(b), "mul") => Ok(Value::Int(a * b)),
1136 (Value::Float(a), Value::Float(b), "add") => Ok(Value::Float(a + b)),
1137 (Value::Float(a), Value::Float(b), "sub") => Ok(Value::Float(a - b)),
1138 (Value::Float(a), Value::Float(b), "mul") => Ok(Value::Float(a * b)),
1139 (_, _, "pair") => Ok(Value::Tuple(Rc::new(vec![x.clone(), y.clone()]))),
1140 _ => Err(RuntimeError::new("zip_with() incompatible types or mode")),
1141 })
1142 .collect();
1143 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1144 }
1145 _ => Err(RuntimeError::new("zip_with() requires two arrays")),
1146 }
1147 });
1148
1149 define(interp, "supremum", Some(2), |_, args| {
1151 match (&args[0], &args[1]) {
1152 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1153 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1154 (Value::Array(a), Value::Array(b)) => {
1155 let a = a.borrow();
1157 let b = b.borrow();
1158 let result: Result<Vec<Value>, RuntimeError> = a
1159 .iter()
1160 .zip(b.iter())
1161 .map(|(x, y)| match (x, y) {
1162 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1163 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1164 _ => Err(RuntimeError::new("supremum() requires numeric arrays")),
1165 })
1166 .collect();
1167 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1168 }
1169 _ => Err(RuntimeError::new(
1170 "supremum() requires numeric values or arrays",
1171 )),
1172 }
1173 });
1174
1175 define(interp, "infimum", Some(2), |_, args| {
1177 match (&args[0], &args[1]) {
1178 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1179 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1180 (Value::Array(a), Value::Array(b)) => {
1181 let a = a.borrow();
1183 let b = b.borrow();
1184 let result: Result<Vec<Value>, RuntimeError> = a
1185 .iter()
1186 .zip(b.iter())
1187 .map(|(x, y)| match (x, y) {
1188 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1189 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1190 _ => Err(RuntimeError::new("infimum() requires numeric arrays")),
1191 })
1192 .collect();
1193 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1194 }
1195 _ => Err(RuntimeError::new(
1196 "infimum() requires numeric values or arrays",
1197 )),
1198 }
1199 });
1200
1201 define(interp, "slice", Some(3), |_, args| {
1203 let start = match &args[1] {
1204 Value::Int(i) => *i as usize,
1205 _ => return Err(RuntimeError::new("slice() start must be integer")),
1206 };
1207 let end = match &args[2] {
1208 Value::Int(i) => *i as usize,
1209 _ => return Err(RuntimeError::new("slice() end must be integer")),
1210 };
1211 match &args[0] {
1212 Value::Array(arr) => {
1213 let arr = arr.borrow();
1214 let end = end.min(arr.len());
1215 let sliced: Vec<Value> = arr[start..end].to_vec();
1216 Ok(Value::Array(Rc::new(RefCell::new(sliced))))
1217 }
1218 Value::String(s) => {
1219 let chars: Vec<char> = s.chars().collect();
1220 let end = end.min(chars.len());
1221 let sliced: String = chars[start..end].iter().collect();
1222 Ok(Value::String(Rc::new(sliced)))
1223 }
1224 _ => Err(RuntimeError::new("slice() requires array or string")),
1225 }
1226 });
1227
1228 define(interp, "take", Some(2), |_, args| {
1229 let n = match &args[1] {
1230 Value::Int(i) => *i as usize,
1231 _ => return Err(RuntimeError::new("take() n must be integer")),
1232 };
1233 match &args[0] {
1234 Value::Array(arr) => {
1235 let taken: Vec<Value> = arr.borrow().iter().take(n).cloned().collect();
1236 Ok(Value::Array(Rc::new(RefCell::new(taken))))
1237 }
1238 _ => Err(RuntimeError::new("take() requires array")),
1239 }
1240 });
1241
1242 define(interp, "skip", Some(2), |_, args| {
1243 let n = match &args[1] {
1244 Value::Int(i) => *i as usize,
1245 _ => return Err(RuntimeError::new("skip() n must be integer")),
1246 };
1247 match &args[0] {
1248 Value::Array(arr) => {
1249 let skipped: Vec<Value> = arr.borrow().iter().skip(n).cloned().collect();
1250 Ok(Value::Array(Rc::new(RefCell::new(skipped))))
1251 }
1252 _ => Err(RuntimeError::new("skip() requires array")),
1253 }
1254 });
1255
1256 define(interp, "chunk", Some(2), |_, args| {
1257 let size = match &args[1] {
1258 Value::Int(i) if *i > 0 => *i as usize,
1259 _ => return Err(RuntimeError::new("chunk() size must be positive integer")),
1260 };
1261 match &args[0] {
1262 Value::Array(arr) => {
1263 let chunks: Vec<Value> = arr
1264 .borrow()
1265 .chunks(size)
1266 .map(|c| Value::Array(Rc::new(RefCell::new(c.to_vec()))))
1267 .collect();
1268 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
1269 }
1270 _ => Err(RuntimeError::new("chunk() requires array")),
1271 }
1272 });
1273
1274 define(interp, "range", Some(2), |_, args| {
1276 let start = match &args[0] {
1277 Value::Int(n) => *n,
1278 _ => return Err(RuntimeError::new("range() requires integers")),
1279 };
1280 let end = match &args[1] {
1281 Value::Int(n) => *n,
1282 _ => return Err(RuntimeError::new("range() requires integers")),
1283 };
1284 let values: Vec<Value> = (start..end).map(Value::Int).collect();
1285 Ok(Value::Array(Rc::new(RefCell::new(values))))
1286 });
1287
1288 define(interp, "range_inclusive", Some(2), |_, args| {
1289 let start = match &args[0] {
1290 Value::Int(n) => *n,
1291 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1292 };
1293 let end = match &args[1] {
1294 Value::Int(n) => *n,
1295 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1296 };
1297 let values: Vec<Value> = (start..=end).map(Value::Int).collect();
1298 Ok(Value::Array(Rc::new(RefCell::new(values))))
1299 });
1300
1301 define(interp, "repeat", Some(2), |_, args| {
1302 let n = match &args[1] {
1303 Value::Int(i) if *i >= 0 => *i as usize,
1304 _ => {
1305 return Err(RuntimeError::new(
1306 "repeat() count must be non-negative integer",
1307 ))
1308 }
1309 };
1310 let repeated: Vec<Value> = std::iter::repeat(args[0].clone()).take(n).collect();
1311 Ok(Value::Array(Rc::new(RefCell::new(repeated))))
1312 });
1313
1314 define(interp, "map_new", Some(0), |_, _| {
1320 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
1321 });
1322
1323 define(interp, "map_get", Some(2), |_, args| {
1325 let key = match &args[1] {
1326 Value::String(s) => s.to_string(),
1327 _ => return Err(RuntimeError::new("map_get() key must be string")),
1328 };
1329 match &args[0] {
1330 Value::Map(map) => Ok(map.borrow().get(&key).cloned().unwrap_or(Value::Null)),
1331 _ => Err(RuntimeError::new("map_get() requires map")),
1332 }
1333 });
1334
1335 define(interp, "map_set", Some(3), |_, args| {
1337 let key = match &args[1] {
1338 Value::String(s) => s.to_string(),
1339 _ => return Err(RuntimeError::new("map_set() key must be string")),
1340 };
1341 match &args[0] {
1342 Value::Map(map) => {
1343 map.borrow_mut().insert(key, args[2].clone());
1344 Ok(Value::Null)
1345 }
1346 _ => Err(RuntimeError::new("map_set() requires map")),
1347 }
1348 });
1349
1350 define(interp, "map_has", Some(2), |_, args| {
1352 let key = match &args[1] {
1353 Value::String(s) => s.to_string(),
1354 _ => return Err(RuntimeError::new("map_has() key must be string")),
1355 };
1356 match &args[0] {
1357 Value::Map(map) => Ok(Value::Bool(map.borrow().contains_key(&key))),
1358 _ => Err(RuntimeError::new("map_has() requires map")),
1359 }
1360 });
1361
1362 define(interp, "map_remove", Some(2), |_, args| {
1364 let key = match &args[1] {
1365 Value::String(s) => s.to_string(),
1366 _ => return Err(RuntimeError::new("map_remove() key must be string")),
1367 };
1368 match &args[0] {
1369 Value::Map(map) => Ok(map.borrow_mut().remove(&key).unwrap_or(Value::Null)),
1370 _ => Err(RuntimeError::new("map_remove() requires map")),
1371 }
1372 });
1373
1374 define(interp, "map_keys", Some(1), |_, args| match &args[0] {
1376 Value::Map(map) => {
1377 let keys: Vec<Value> = map
1378 .borrow()
1379 .keys()
1380 .map(|k| Value::String(Rc::new(k.clone())))
1381 .collect();
1382 Ok(Value::Array(Rc::new(RefCell::new(keys))))
1383 }
1384 _ => Err(RuntimeError::new("map_keys() requires map")),
1385 });
1386
1387 define(interp, "map_values", Some(1), |_, args| match &args[0] {
1389 Value::Map(map) => {
1390 let values: Vec<Value> = map.borrow().values().cloned().collect();
1391 Ok(Value::Array(Rc::new(RefCell::new(values))))
1392 }
1393 _ => Err(RuntimeError::new("map_values() requires map")),
1394 });
1395
1396 define(interp, "map_len", Some(1), |_, args| match &args[0] {
1398 Value::Map(map) => Ok(Value::Int(map.borrow().len() as i64)),
1399 _ => Err(RuntimeError::new("map_len() requires map")),
1400 });
1401
1402 define(interp, "map_clear", Some(1), |_, args| match &args[0] {
1404 Value::Map(map) => {
1405 map.borrow_mut().clear();
1406 Ok(Value::Null)
1407 }
1408 _ => Err(RuntimeError::new("map_clear() requires map")),
1409 });
1410
1411 define(interp, "set_new", Some(0), |_, _| {
1417 Ok(Value::Set(Rc::new(RefCell::new(
1418 std::collections::HashSet::new(),
1419 ))))
1420 });
1421
1422 define(interp, "set_add", Some(2), |_, args| {
1424 let item = match &args[1] {
1425 Value::String(s) => s.to_string(),
1426 _ => return Err(RuntimeError::new("set_add() item must be string")),
1427 };
1428 match &args[0] {
1429 Value::Set(set) => {
1430 set.borrow_mut().insert(item);
1431 Ok(Value::Null)
1432 }
1433 _ => Err(RuntimeError::new("set_add() requires set")),
1434 }
1435 });
1436
1437 define(interp, "set_has", Some(2), |_, args| {
1439 let item = match &args[1] {
1440 Value::String(s) => s.to_string(),
1441 _ => return Err(RuntimeError::new("set_has() item must be string")),
1442 };
1443 match &args[0] {
1444 Value::Set(set) => Ok(Value::Bool(set.borrow().contains(&item))),
1445 _ => Err(RuntimeError::new("set_has() requires set")),
1446 }
1447 });
1448
1449 define(interp, "set_remove", Some(2), |_, args| {
1451 let item = match &args[1] {
1452 Value::String(s) => s.to_string(),
1453 _ => return Err(RuntimeError::new("set_remove() item must be string")),
1454 };
1455 match &args[0] {
1456 Value::Set(set) => Ok(Value::Bool(set.borrow_mut().remove(&item))),
1457 _ => Err(RuntimeError::new("set_remove() requires set")),
1458 }
1459 });
1460
1461 define(interp, "set_to_array", Some(1), |_, args| match &args[0] {
1463 Value::Set(set) => {
1464 let items: Vec<Value> = set
1465 .borrow()
1466 .iter()
1467 .map(|s| Value::String(Rc::new(s.clone())))
1468 .collect();
1469 Ok(Value::Array(Rc::new(RefCell::new(items))))
1470 }
1471 _ => Err(RuntimeError::new("set_to_array() requires set")),
1472 });
1473
1474 define(interp, "set_len", Some(1), |_, args| match &args[0] {
1476 Value::Set(set) => Ok(Value::Int(set.borrow().len() as i64)),
1477 _ => Err(RuntimeError::new("set_len() requires set")),
1478 });
1479
1480 define(interp, "set_clear", Some(1), |_, args| match &args[0] {
1482 Value::Set(set) => {
1483 set.borrow_mut().clear();
1484 Ok(Value::Null)
1485 }
1486 _ => Err(RuntimeError::new("set_clear() requires set")),
1487 });
1488}
1489
1490fn values_equal(a: &Value, b: &Value) -> bool {
1491 match (a, b) {
1492 (Value::Null, Value::Null) => true,
1493 (Value::Bool(a), Value::Bool(b)) => a == b,
1494 (Value::Int(a), Value::Int(b)) => a == b,
1495 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
1496 (Value::String(a), Value::String(b)) => a == b,
1497 (Value::Char(a), Value::Char(b)) => a == b,
1498 (Value::Array(a), Value::Array(b)) => {
1499 let a = a.borrow();
1500 let b = b.borrow();
1501 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1502 }
1503 (Value::Tuple(a), Value::Tuple(b)) => {
1504 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1505 }
1506 _ => false,
1507 }
1508}
1509
1510fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
1511 match (a, b) {
1512 (Value::Int(a), Value::Int(b)) => a.cmp(b),
1513 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal),
1514 (Value::String(a), Value::String(b)) => a.cmp(b),
1515 (Value::Char(a), Value::Char(b)) => a.cmp(b),
1516 _ => std::cmp::Ordering::Equal,
1517 }
1518}
1519
1520fn register_string(interp: &mut Interpreter) {
1525 define(interp, "chars", Some(1), |_, args| match &args[0] {
1526 Value::String(s) => {
1527 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
1528 Ok(Value::Array(Rc::new(RefCell::new(chars))))
1529 }
1530 _ => Err(RuntimeError::new("chars() requires string")),
1531 });
1532
1533 define(interp, "bytes", Some(1), |_, args| match &args[0] {
1534 Value::String(s) => {
1535 let bytes: Vec<Value> = s.bytes().map(|b| Value::Int(b as i64)).collect();
1536 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
1537 }
1538 _ => Err(RuntimeError::new("bytes() requires string")),
1539 });
1540
1541 define(interp, "split", Some(2), |_, args| {
1542 match (&args[0], &args[1]) {
1543 (Value::String(s), Value::String(sep)) => {
1544 let parts: Vec<Value> = s
1545 .split(sep.as_str())
1546 .map(|p| Value::String(Rc::new(p.to_string())))
1547 .collect();
1548 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1549 }
1550 (Value::String(s), Value::Char(sep)) => {
1551 let parts: Vec<Value> = s
1552 .split(*sep)
1553 .map(|p| Value::String(Rc::new(p.to_string())))
1554 .collect();
1555 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1556 }
1557 _ => Err(RuntimeError::new("split() requires string and separator")),
1558 }
1559 });
1560
1561 define(interp, "join", Some(2), |_, args| {
1562 match (&args[0], &args[1]) {
1563 (Value::Array(arr), Value::String(sep)) => {
1564 let parts: Vec<String> = arr.borrow().iter().map(|v| format!("{}", v)).collect();
1565 Ok(Value::String(Rc::new(parts.join(sep.as_str()))))
1566 }
1567 _ => Err(RuntimeError::new(
1568 "join() requires array and separator string",
1569 )),
1570 }
1571 });
1572
1573 define(interp, "trim", Some(1), |_, args| match &args[0] {
1574 Value::String(s) => Ok(Value::String(Rc::new(s.trim().to_string()))),
1575 _ => Err(RuntimeError::new("trim() requires string")),
1576 });
1577
1578 define(interp, "trim_start", Some(1), |_, args| match &args[0] {
1579 Value::String(s) => Ok(Value::String(Rc::new(s.trim_start().to_string()))),
1580 _ => Err(RuntimeError::new("trim_start() requires string")),
1581 });
1582
1583 define(interp, "trim_end", Some(1), |_, args| match &args[0] {
1584 Value::String(s) => Ok(Value::String(Rc::new(s.trim_end().to_string()))),
1585 _ => Err(RuntimeError::new("trim_end() requires string")),
1586 });
1587
1588 define(interp, "upper", Some(1), |_, args| match &args[0] {
1589 Value::String(s) => Ok(Value::String(Rc::new(s.to_uppercase()))),
1590 Value::Char(c) => Ok(Value::Char(c.to_uppercase().next().unwrap_or(*c))),
1591 _ => Err(RuntimeError::new("upper() requires string or char")),
1592 });
1593
1594 define(interp, "lower", Some(1), |_, args| match &args[0] {
1595 Value::String(s) => Ok(Value::String(Rc::new(s.to_lowercase()))),
1596 Value::Char(c) => Ok(Value::Char(c.to_lowercase().next().unwrap_or(*c))),
1597 _ => Err(RuntimeError::new("lower() requires string or char")),
1598 });
1599
1600 define(interp, "capitalize", Some(1), |_, args| match &args[0] {
1601 Value::String(s) => {
1602 let mut chars = s.chars();
1603 let capitalized = match chars.next() {
1604 None => String::new(),
1605 Some(c) => c.to_uppercase().chain(chars).collect(),
1606 };
1607 Ok(Value::String(Rc::new(capitalized)))
1608 }
1609 _ => Err(RuntimeError::new("capitalize() requires string")),
1610 });
1611
1612 define(interp, "replace", Some(3), |_, args| {
1613 match (&args[0], &args[1], &args[2]) {
1614 (Value::String(s), Value::String(from), Value::String(to)) => Ok(Value::String(
1615 Rc::new(s.replace(from.as_str(), to.as_str())),
1616 )),
1617 _ => Err(RuntimeError::new("replace() requires three strings")),
1618 }
1619 });
1620
1621 define(interp, "starts_with", Some(2), |_, args| {
1622 match (&args[0], &args[1]) {
1623 (Value::String(s), Value::String(prefix)) => {
1624 Ok(Value::Bool(s.starts_with(prefix.as_str())))
1625 }
1626 _ => Err(RuntimeError::new("starts_with() requires two strings")),
1627 }
1628 });
1629
1630 define(interp, "ends_with", Some(2), |_, args| {
1631 match (&args[0], &args[1]) {
1632 (Value::String(s), Value::String(suffix)) => {
1633 Ok(Value::Bool(s.ends_with(suffix.as_str())))
1634 }
1635 _ => Err(RuntimeError::new("ends_with() requires two strings")),
1636 }
1637 });
1638
1639 define(interp, "repeat_str", Some(2), |_, args| {
1640 match (&args[0], &args[1]) {
1641 (Value::String(s), Value::Int(n)) if *n >= 0 => {
1642 Ok(Value::String(Rc::new(s.repeat(*n as usize))))
1643 }
1644 _ => Err(RuntimeError::new(
1645 "repeat_str() requires string and non-negative integer",
1646 )),
1647 }
1648 });
1649
1650 define(interp, "pad_left", Some(3), |_, args| {
1651 match (&args[0], &args[1], &args[2]) {
1652 (Value::String(s), Value::Int(width), Value::Char(c)) => {
1653 let width = *width as usize;
1654 if s.len() >= width {
1655 Ok(Value::String(s.clone()))
1656 } else {
1657 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
1658 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
1659 }
1660 }
1661 _ => Err(RuntimeError::new(
1662 "pad_left() requires string, width, and char",
1663 )),
1664 }
1665 });
1666
1667 define(interp, "pad_right", Some(3), |_, args| {
1668 match (&args[0], &args[1], &args[2]) {
1669 (Value::String(s), Value::Int(width), Value::Char(c)) => {
1670 let width = *width as usize;
1671 if s.len() >= width {
1672 Ok(Value::String(s.clone()))
1673 } else {
1674 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
1675 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
1676 }
1677 }
1678 _ => Err(RuntimeError::new(
1679 "pad_right() requires string, width, and char",
1680 )),
1681 }
1682 });
1683
1684 define(interp, "lines", Some(1), |_, args| match &args[0] {
1685 Value::String(s) => {
1686 let lines: Vec<Value> = s
1687 .lines()
1688 .map(|l| Value::String(Rc::new(l.to_string())))
1689 .collect();
1690 Ok(Value::Array(Rc::new(RefCell::new(lines))))
1691 }
1692 _ => Err(RuntimeError::new("lines() requires string")),
1693 });
1694
1695 define(interp, "words", Some(1), |_, args| match &args[0] {
1696 Value::String(s) => {
1697 let words: Vec<Value> = s
1698 .split_whitespace()
1699 .map(|w| Value::String(Rc::new(w.to_string())))
1700 .collect();
1701 Ok(Value::Array(Rc::new(RefCell::new(words))))
1702 }
1703 _ => Err(RuntimeError::new("words() requires string")),
1704 });
1705
1706 define(interp, "is_alpha", Some(1), |_, args| match &args[0] {
1707 Value::String(s) => Ok(Value::Bool(
1708 !s.is_empty() && s.chars().all(|c| c.is_alphabetic()),
1709 )),
1710 Value::Char(c) => Ok(Value::Bool(c.is_alphabetic())),
1711 _ => Err(RuntimeError::new("is_alpha() requires string or char")),
1712 });
1713
1714 define(interp, "is_digit", Some(1), |_, args| match &args[0] {
1715 Value::String(s) => Ok(Value::Bool(
1716 !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()),
1717 )),
1718 Value::Char(c) => Ok(Value::Bool(c.is_ascii_digit())),
1719 _ => Err(RuntimeError::new("is_digit() requires string or char")),
1720 });
1721
1722 define(interp, "is_alnum", Some(1), |_, args| match &args[0] {
1723 Value::String(s) => Ok(Value::Bool(
1724 !s.is_empty() && s.chars().all(|c| c.is_alphanumeric()),
1725 )),
1726 Value::Char(c) => Ok(Value::Bool(c.is_alphanumeric())),
1727 _ => Err(RuntimeError::new("is_alnum() requires string or char")),
1728 });
1729
1730 define(interp, "is_space", Some(1), |_, args| match &args[0] {
1731 Value::String(s) => Ok(Value::Bool(
1732 !s.is_empty() && s.chars().all(|c| c.is_whitespace()),
1733 )),
1734 Value::Char(c) => Ok(Value::Bool(c.is_whitespace())),
1735 _ => Err(RuntimeError::new("is_space() requires string or char")),
1736 });
1737
1738 define(interp, "find", Some(2), |_, args| {
1744 match (&args[0], &args[1]) {
1745 (Value::String(s), Value::String(sub)) => {
1746 match s.find(sub.as_str()) {
1747 Some(byte_idx) => {
1748 let char_idx = s[..byte_idx].chars().count() as i64;
1750 Ok(Value::Int(char_idx))
1751 }
1752 None => Ok(Value::Int(-1)),
1753 }
1754 }
1755 (Value::String(s), Value::Char(c)) => match s.find(*c) {
1756 Some(byte_idx) => {
1757 let char_idx = s[..byte_idx].chars().count() as i64;
1758 Ok(Value::Int(char_idx))
1759 }
1760 None => Ok(Value::Int(-1)),
1761 },
1762 _ => Err(RuntimeError::new(
1763 "find() requires string and substring/char",
1764 )),
1765 }
1766 });
1767
1768 define(interp, "index_of", Some(2), |_, args| {
1770 match (&args[0], &args[1]) {
1771 (Value::String(s), Value::String(sub)) => match s.find(sub.as_str()) {
1772 Some(byte_idx) => {
1773 let char_idx = s[..byte_idx].chars().count() as i64;
1774 Ok(Value::Int(char_idx))
1775 }
1776 None => Ok(Value::Int(-1)),
1777 },
1778 (Value::String(s), Value::Char(c)) => match s.find(*c) {
1779 Some(byte_idx) => {
1780 let char_idx = s[..byte_idx].chars().count() as i64;
1781 Ok(Value::Int(char_idx))
1782 }
1783 None => Ok(Value::Int(-1)),
1784 },
1785 (Value::Array(arr), search) => {
1786 for (i, v) in arr.borrow().iter().enumerate() {
1788 if values_equal_simple(v, search) {
1789 return Ok(Value::Int(i as i64));
1790 }
1791 }
1792 Ok(Value::Int(-1))
1793 }
1794 _ => Err(RuntimeError::new(
1795 "index_of() requires array/string and element/substring",
1796 )),
1797 }
1798 });
1799
1800 define(interp, "last_index_of", Some(2), |_, args| {
1802 match (&args[0], &args[1]) {
1803 (Value::String(s), Value::String(sub)) => match s.rfind(sub.as_str()) {
1804 Some(byte_idx) => {
1805 let char_idx = s[..byte_idx].chars().count() as i64;
1806 Ok(Value::Int(char_idx))
1807 }
1808 None => Ok(Value::Int(-1)),
1809 },
1810 (Value::String(s), Value::Char(c)) => match s.rfind(*c) {
1811 Some(byte_idx) => {
1812 let char_idx = s[..byte_idx].chars().count() as i64;
1813 Ok(Value::Int(char_idx))
1814 }
1815 None => Ok(Value::Int(-1)),
1816 },
1817 _ => Err(RuntimeError::new(
1818 "last_index_of() requires string and substring/char",
1819 )),
1820 }
1821 });
1822
1823 define(interp, "substring", Some(3), |_, args| {
1825 let s = match &args[0] {
1826 Value::String(s) => (**s).clone(),
1827 _ => {
1828 return Err(RuntimeError::new(
1829 "substring: first argument must be a string",
1830 ))
1831 }
1832 };
1833 let start = match &args[1] {
1834 Value::Int(n) if *n >= 0 => *n as usize,
1835 _ => {
1836 return Err(RuntimeError::new(
1837 "substring: start must be a non-negative integer",
1838 ))
1839 }
1840 };
1841 let end = match &args[2] {
1842 Value::Int(n) if *n >= 0 => *n as usize,
1843 _ => {
1844 return Err(RuntimeError::new(
1845 "substring: end must be a non-negative integer",
1846 ))
1847 }
1848 };
1849 let chars: Vec<char> = s.chars().collect();
1850 let len = chars.len();
1851 let actual_start = start.min(len);
1852 let actual_end = end.min(len);
1853 if actual_start >= actual_end {
1854 return Ok(Value::String(Rc::new(String::new())));
1855 }
1856 let result: String = chars[actual_start..actual_end].iter().collect();
1857 Ok(Value::String(Rc::new(result)))
1858 });
1859
1860 define(interp, "count", Some(2), |_, args| {
1862 match (&args[0], &args[1]) {
1863 (Value::String(s), Value::String(sub)) => {
1864 if sub.is_empty() {
1865 return Err(RuntimeError::new("count: cannot count empty string"));
1866 }
1867 let count = s.matches(sub.as_str()).count() as i64;
1868 Ok(Value::Int(count))
1869 }
1870 (Value::String(s), Value::Char(c)) => {
1871 let count = s.chars().filter(|&ch| ch == *c).count() as i64;
1872 Ok(Value::Int(count))
1873 }
1874 _ => Err(RuntimeError::new(
1875 "count() requires string and substring/char",
1876 )),
1877 }
1878 });
1879
1880 define(interp, "char_at", Some(2), |_, args| {
1882 let s = match &args[0] {
1883 Value::String(s) => (**s).clone(),
1884 _ => {
1885 return Err(RuntimeError::new(
1886 "char_at: first argument must be a string",
1887 ))
1888 }
1889 };
1890 let idx = match &args[1] {
1891 Value::Int(n) => *n,
1892 _ => {
1893 return Err(RuntimeError::new(
1894 "char_at: second argument must be an integer",
1895 ))
1896 }
1897 };
1898 let chars: Vec<char> = s.chars().collect();
1899 let actual_idx = if idx < 0 {
1900 (chars.len() as i64 + idx) as usize
1901 } else {
1902 idx as usize
1903 };
1904 match chars.get(actual_idx) {
1905 Some(c) => Ok(Value::Char(*c)),
1906 None => Ok(Value::Null),
1907 }
1908 });
1909
1910 define(interp, "char_code_at", Some(2), |_, args| {
1912 let s = match &args[0] {
1913 Value::String(s) => (**s).clone(),
1914 _ => {
1915 return Err(RuntimeError::new(
1916 "char_code_at: first argument must be a string",
1917 ))
1918 }
1919 };
1920 let idx = match &args[1] {
1921 Value::Int(n) => *n,
1922 _ => {
1923 return Err(RuntimeError::new(
1924 "char_code_at: second argument must be an integer",
1925 ))
1926 }
1927 };
1928 let chars: Vec<char> = s.chars().collect();
1929 let actual_idx = if idx < 0 {
1930 (chars.len() as i64 + idx) as usize
1931 } else {
1932 idx as usize
1933 };
1934 match chars.get(actual_idx) {
1935 Some(c) => Ok(Value::Int(*c as i64)),
1936 None => Ok(Value::Null),
1937 }
1938 });
1939
1940 define(interp, "from_char_code", Some(1), |_, args| {
1942 let code = match &args[0] {
1943 Value::Int(n) => *n as u32,
1944 _ => {
1945 return Err(RuntimeError::new(
1946 "from_char_code: argument must be an integer",
1947 ))
1948 }
1949 };
1950 match char::from_u32(code) {
1951 Some(c) => Ok(Value::String(Rc::new(c.to_string()))),
1952 None => Err(RuntimeError::new(
1953 "from_char_code: invalid Unicode code point",
1954 )),
1955 }
1956 });
1957
1958 define(interp, "insert", Some(3), |_, args| {
1960 let s = match &args[0] {
1961 Value::String(s) => (**s).clone(),
1962 _ => return Err(RuntimeError::new("insert: first argument must be a string")),
1963 };
1964 let idx = match &args[1] {
1965 Value::Int(n) if *n >= 0 => *n as usize,
1966 _ => {
1967 return Err(RuntimeError::new(
1968 "insert: index must be a non-negative integer",
1969 ))
1970 }
1971 };
1972 let insertion = match &args[2] {
1973 Value::String(s) => (**s).clone(),
1974 _ => return Err(RuntimeError::new("insert: third argument must be a string")),
1975 };
1976 let chars: Vec<char> = s.chars().collect();
1977 let actual_idx = idx.min(chars.len());
1978 let mut result: String = chars[..actual_idx].iter().collect();
1979 result.push_str(&insertion);
1980 result.extend(chars[actual_idx..].iter());
1981 Ok(Value::String(Rc::new(result)))
1982 });
1983
1984 define(interp, "remove", Some(3), |_, args| {
1986 let s = match &args[0] {
1987 Value::String(s) => (**s).clone(),
1988 _ => return Err(RuntimeError::new("remove: first argument must be a string")),
1989 };
1990 let start = match &args[1] {
1991 Value::Int(n) if *n >= 0 => *n as usize,
1992 _ => {
1993 return Err(RuntimeError::new(
1994 "remove: start must be a non-negative integer",
1995 ))
1996 }
1997 };
1998 let len = match &args[2] {
1999 Value::Int(n) if *n >= 0 => *n as usize,
2000 _ => {
2001 return Err(RuntimeError::new(
2002 "remove: length must be a non-negative integer",
2003 ))
2004 }
2005 };
2006 let chars: Vec<char> = s.chars().collect();
2007 let str_len = chars.len();
2008 let actual_start = start.min(str_len);
2009 let actual_end = (start + len).min(str_len);
2010 let mut result: String = chars[..actual_start].iter().collect();
2011 result.extend(chars[actual_end..].iter());
2012 Ok(Value::String(Rc::new(result)))
2013 });
2014
2015 define(interp, "compare", Some(2), |_, args| {
2017 match (&args[0], &args[1]) {
2018 (Value::String(a), Value::String(b)) => {
2019 let result = match a.cmp(b) {
2020 std::cmp::Ordering::Less => -1,
2021 std::cmp::Ordering::Equal => 0,
2022 std::cmp::Ordering::Greater => 1,
2023 };
2024 Ok(Value::Int(result))
2025 }
2026 _ => Err(RuntimeError::new("compare() requires two strings")),
2027 }
2028 });
2029
2030 define(interp, "compare_ignore_case", Some(2), |_, args| {
2032 match (&args[0], &args[1]) {
2033 (Value::String(a), Value::String(b)) => {
2034 let result = match a.to_lowercase().cmp(&b.to_lowercase()) {
2035 std::cmp::Ordering::Less => -1,
2036 std::cmp::Ordering::Equal => 0,
2037 std::cmp::Ordering::Greater => 1,
2038 };
2039 Ok(Value::Int(result))
2040 }
2041 _ => Err(RuntimeError::new(
2042 "compare_ignore_case() requires two strings",
2043 )),
2044 }
2045 });
2046
2047 define(interp, "char_count", Some(1), |_, args| match &args[0] {
2049 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
2050 _ => Err(RuntimeError::new("char_count() requires string")),
2051 });
2052
2053 define(interp, "byte_count", Some(1), |_, args| match &args[0] {
2055 Value::String(s) => Ok(Value::Int(s.len() as i64)),
2056 _ => Err(RuntimeError::new("byte_count() requires string")),
2057 });
2058
2059 define(interp, "is_empty", Some(1), |_, args| match &args[0] {
2061 Value::String(s) => Ok(Value::Bool(s.is_empty())),
2062 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
2063 _ => Err(RuntimeError::new("is_empty() requires string or array")),
2064 });
2065
2066 define(interp, "is_blank", Some(1), |_, args| match &args[0] {
2068 Value::String(s) => Ok(Value::Bool(s.trim().is_empty())),
2069 _ => Err(RuntimeError::new("is_blank() requires string")),
2070 });
2071
2072 define(interp, "nfc", Some(1), |_, args| match &args[0] {
2078 Value::String(s) => Ok(Value::String(Rc::new(s.nfc().collect()))),
2079 _ => Err(RuntimeError::new("nfc() requires string")),
2080 });
2081
2082 define(interp, "nfd", Some(1), |_, args| match &args[0] {
2084 Value::String(s) => Ok(Value::String(Rc::new(s.nfd().collect()))),
2085 _ => Err(RuntimeError::new("nfd() requires string")),
2086 });
2087
2088 define(interp, "nfkc", Some(1), |_, args| match &args[0] {
2090 Value::String(s) => Ok(Value::String(Rc::new(s.nfkc().collect()))),
2091 _ => Err(RuntimeError::new("nfkc() requires string")),
2092 });
2093
2094 define(interp, "nfkd", Some(1), |_, args| match &args[0] {
2096 Value::String(s) => Ok(Value::String(Rc::new(s.nfkd().collect()))),
2097 _ => Err(RuntimeError::new("nfkd() requires string")),
2098 });
2099
2100 define(interp, "is_nfc", Some(1), |_, args| match &args[0] {
2102 Value::String(s) => {
2103 let normalized: String = s.nfc().collect();
2104 Ok(Value::Bool(*s.as_ref() == normalized))
2105 }
2106 _ => Err(RuntimeError::new("is_nfc() requires string")),
2107 });
2108
2109 define(interp, "is_nfd", Some(1), |_, args| match &args[0] {
2111 Value::String(s) => {
2112 let normalized: String = s.nfd().collect();
2113 Ok(Value::Bool(*s.as_ref() == normalized))
2114 }
2115 _ => Err(RuntimeError::new("is_nfd() requires string")),
2116 });
2117
2118 define(interp, "graphemes", Some(1), |_, args| match &args[0] {
2124 Value::String(s) => {
2125 let graphemes: Vec<Value> = s
2126 .graphemes(true)
2127 .map(|g| Value::String(Rc::new(g.to_string())))
2128 .collect();
2129 Ok(Value::Array(Rc::new(RefCell::new(graphemes))))
2130 }
2131 _ => Err(RuntimeError::new("graphemes() requires string")),
2132 });
2133
2134 define(interp, "grapheme_count", Some(1), |_, args| {
2136 match &args[0] {
2137 Value::String(s) => Ok(Value::Int(s.graphemes(true).count() as i64)),
2138 _ => Err(RuntimeError::new("grapheme_count() requires string")),
2139 }
2140 });
2141
2142 define(interp, "grapheme_at", Some(2), |_, args| {
2144 let s = match &args[0] {
2145 Value::String(s) => (**s).clone(),
2146 _ => {
2147 return Err(RuntimeError::new(
2148 "grapheme_at: first argument must be a string",
2149 ))
2150 }
2151 };
2152 let idx = match &args[1] {
2153 Value::Int(n) => *n,
2154 _ => {
2155 return Err(RuntimeError::new(
2156 "grapheme_at: second argument must be an integer",
2157 ))
2158 }
2159 };
2160 let graphemes: Vec<&str> = s.graphemes(true).collect();
2161 let actual_idx = if idx < 0 {
2162 (graphemes.len() as i64 + idx) as usize
2163 } else {
2164 idx as usize
2165 };
2166 match graphemes.get(actual_idx) {
2167 Some(g) => Ok(Value::String(Rc::new(g.to_string()))),
2168 None => Ok(Value::Null),
2169 }
2170 });
2171
2172 define(interp, "grapheme_slice", Some(3), |_, args| {
2174 let s = match &args[0] {
2175 Value::String(s) => (**s).clone(),
2176 _ => {
2177 return Err(RuntimeError::new(
2178 "grapheme_slice: first argument must be a string",
2179 ))
2180 }
2181 };
2182 let start = match &args[1] {
2183 Value::Int(n) if *n >= 0 => *n as usize,
2184 _ => {
2185 return Err(RuntimeError::new(
2186 "grapheme_slice: start must be a non-negative integer",
2187 ))
2188 }
2189 };
2190 let end = match &args[2] {
2191 Value::Int(n) if *n >= 0 => *n as usize,
2192 _ => {
2193 return Err(RuntimeError::new(
2194 "grapheme_slice: end must be a non-negative integer",
2195 ))
2196 }
2197 };
2198 let graphemes: Vec<&str> = s.graphemes(true).collect();
2199 let len = graphemes.len();
2200 let actual_start = start.min(len);
2201 let actual_end = end.min(len);
2202 if actual_start >= actual_end {
2203 return Ok(Value::String(Rc::new(String::new())));
2204 }
2205 let result: String = graphemes[actual_start..actual_end].join("");
2206 Ok(Value::String(Rc::new(result)))
2207 });
2208
2209 define(interp, "grapheme_reverse", Some(1), |_, args| {
2211 match &args[0] {
2212 Value::String(s) => {
2213 let reversed: String = s.graphemes(true).rev().collect();
2214 Ok(Value::String(Rc::new(reversed)))
2215 }
2216 _ => Err(RuntimeError::new("grapheme_reverse() requires string")),
2217 }
2218 });
2219
2220 define(interp, "word_boundaries", Some(1), |_, args| {
2222 match &args[0] {
2223 Value::String(s) => {
2224 let words: Vec<Value> = s
2225 .unicode_words()
2226 .map(|w| Value::String(Rc::new(w.to_string())))
2227 .collect();
2228 Ok(Value::Array(Rc::new(RefCell::new(words))))
2229 }
2230 _ => Err(RuntimeError::new("word_boundaries() requires string")),
2231 }
2232 });
2233
2234 define(interp, "string_builder", Some(0), |_, _| {
2241 Ok(Value::String(Rc::new(String::new())))
2242 });
2243
2244 define(interp, "concat_all", Some(1), |_, args| match &args[0] {
2246 Value::Array(arr) => {
2247 let parts: Vec<String> = arr
2248 .borrow()
2249 .iter()
2250 .map(|v| match v {
2251 Value::String(s) => (**s).clone(),
2252 other => format!("{}", other),
2253 })
2254 .collect();
2255 Ok(Value::String(Rc::new(parts.join(""))))
2256 }
2257 _ => Err(RuntimeError::new("concat_all() requires array")),
2258 });
2259
2260 define(interp, "repeat_join", Some(3), |_, args| {
2262 let s = match &args[0] {
2263 Value::String(s) => (**s).clone(),
2264 _ => {
2265 return Err(RuntimeError::new(
2266 "repeat_join: first argument must be a string",
2267 ))
2268 }
2269 };
2270 let n = match &args[1] {
2271 Value::Int(n) if *n >= 0 => *n as usize,
2272 _ => {
2273 return Err(RuntimeError::new(
2274 "repeat_join: count must be a non-negative integer",
2275 ))
2276 }
2277 };
2278 let sep = match &args[2] {
2279 Value::String(s) => (**s).clone(),
2280 _ => return Err(RuntimeError::new("repeat_join: separator must be a string")),
2281 };
2282 if n == 0 {
2283 return Ok(Value::String(Rc::new(String::new())));
2284 }
2285 let parts: Vec<&str> = std::iter::repeat(s.as_str()).take(n).collect();
2286 Ok(Value::String(Rc::new(parts.join(&sep))))
2287 });
2288}
2289
2290fn register_evidence(interp: &mut Interpreter) {
2295 use crate::interpreter::RuntimeConfidence;
2296
2297 define(interp, "known", Some(1), |_, args| {
2299 Ok(Value::Evidential {
2300 value: Box::new(args[0].clone()),
2301 evidence: Evidence::Known,
2302 })
2303 });
2304
2305 define(interp, "uncertain", Some(1), |_, args| {
2306 Ok(Value::Evidential {
2307 value: Box::new(args[0].clone()),
2308 evidence: Evidence::Uncertain,
2309 })
2310 });
2311
2312 define(interp, "reported", Some(1), |_, args| {
2313 Ok(Value::Evidential {
2314 value: Box::new(args[0].clone()),
2315 evidence: Evidence::Reported,
2316 })
2317 });
2318
2319 define(interp, "paradox", Some(1), |_, args| {
2320 Ok(Value::Evidential {
2321 value: Box::new(args[0].clone()),
2322 evidence: Evidence::Paradox,
2323 })
2324 });
2325
2326 define(interp, "evidence_of", Some(1), |_, args| {
2328 match &args[0] {
2329 Value::Evidential { evidence, .. } => {
2330 let level = match evidence {
2331 Evidence::Known => "known",
2332 Evidence::Uncertain => "uncertain",
2333 Evidence::Reported => "reported",
2334 Evidence::Paradox => "paradox",
2335 };
2336 Ok(Value::String(Rc::new(level.to_string())))
2337 }
2338 _ => Ok(Value::String(Rc::new("known".to_string()))), }
2340 });
2341
2342 define(interp, "is_known", Some(1), |_, args| {
2343 match &args[0] {
2344 Value::Evidential {
2345 evidence: Evidence::Known,
2346 ..
2347 } => Ok(Value::Bool(true)),
2348 Value::Evidential { .. } => Ok(Value::Bool(false)),
2349 _ => Ok(Value::Bool(true)), }
2351 });
2352
2353 define(interp, "is_uncertain", Some(1), |_, args| match &args[0] {
2354 Value::Evidential {
2355 evidence: Evidence::Uncertain,
2356 ..
2357 } => Ok(Value::Bool(true)),
2358 _ => Ok(Value::Bool(false)),
2359 });
2360
2361 define(interp, "is_reported", Some(1), |_, args| match &args[0] {
2362 Value::Evidential {
2363 evidence: Evidence::Reported,
2364 ..
2365 } => Ok(Value::Bool(true)),
2366 _ => Ok(Value::Bool(false)),
2367 });
2368
2369 define(interp, "is_paradox", Some(1), |_, args| match &args[0] {
2370 Value::Evidential {
2371 evidence: Evidence::Paradox,
2372 ..
2373 } => Ok(Value::Bool(true)),
2374 _ => Ok(Value::Bool(false)),
2375 });
2376
2377 define(interp, "strip_evidence", Some(1), |_, args| {
2379 match &args[0] {
2380 Value::Evidential { value, .. } => Ok(*value.clone()),
2381 other => Ok(other.clone()),
2382 }
2383 });
2384
2385 define(interp, "trust", Some(1), |_, args| {
2387 match &args[0] {
2389 Value::Evidential { value, .. } => Ok(Value::Evidential {
2390 value: value.clone(),
2391 evidence: Evidence::Known,
2392 }),
2393 other => Ok(other.clone()),
2394 }
2395 });
2396
2397 define(interp, "verify", Some(2), |_, args| {
2398 let pred_result = match &args[1] {
2400 Value::Bool(b) => *b,
2401 _ => return Err(RuntimeError::new("verify() predicate must be bool")),
2402 };
2403
2404 if pred_result {
2405 match &args[0] {
2406 Value::Evidential { value, .. } => Ok(Value::Evidential {
2407 value: value.clone(),
2408 evidence: Evidence::Known,
2409 }),
2410 other => Ok(other.clone()),
2411 }
2412 } else {
2413 Ok(args[0].clone()) }
2415 });
2416
2417 define(interp, "combine_evidence", Some(2), |_, args| {
2419 let ev1 = match &args[0] {
2420 Value::Evidential { evidence, .. } => *evidence,
2421 _ => Evidence::Known,
2422 };
2423 let ev2 = match &args[1] {
2424 Value::Evidential { evidence, .. } => *evidence,
2425 _ => Evidence::Known,
2426 };
2427
2428 let combined = match (ev1, ev2) {
2430 (Evidence::Paradox, _) | (_, Evidence::Paradox) => Evidence::Paradox,
2431 (Evidence::Reported, _) | (_, Evidence::Reported) => Evidence::Reported,
2432 (Evidence::Uncertain, _) | (_, Evidence::Uncertain) => Evidence::Uncertain,
2433 _ => Evidence::Known,
2434 };
2435
2436 Ok(Value::String(Rc::new(
2437 match combined {
2438 Evidence::Known => "known",
2439 Evidence::Uncertain => "uncertain",
2440 Evidence::Reported => "reported",
2441 Evidence::Paradox => "paradox",
2442 }
2443 .to_string(),
2444 )))
2445 });
2446
2447 define(interp, "affect_to_evidence", Some(1), |_, args| {
2451 match &args[0] {
2452 Value::Affective { affect, .. } => {
2453 if affect.sarcasm {
2455 return Ok(Value::String(Rc::new("uncertain".to_string())));
2456 }
2457 match affect.confidence {
2459 Some(RuntimeConfidence::High) => {
2460 Ok(Value::String(Rc::new("known".to_string())))
2461 }
2462 Some(RuntimeConfidence::Low) => {
2463 Ok(Value::String(Rc::new("uncertain".to_string())))
2464 }
2465 _ => Ok(Value::String(Rc::new("known".to_string()))),
2466 }
2467 }
2468 _ => Ok(Value::String(Rc::new("known".to_string()))),
2469 }
2470 });
2471
2472 define(
2474 interp,
2475 "affect_as_evidence",
2476 Some(1),
2477 |_, args| match &args[0] {
2478 Value::Affective { value, affect } => {
2479 let evidence = if affect.sarcasm {
2480 Evidence::Uncertain
2481 } else {
2482 match affect.confidence {
2483 Some(RuntimeConfidence::High) => Evidence::Known,
2484 Some(RuntimeConfidence::Low) => Evidence::Uncertain,
2485 _ => Evidence::Known,
2486 }
2487 };
2488 Ok(Value::Evidential {
2489 value: value.clone(),
2490 evidence,
2491 })
2492 }
2493 other => Ok(other.clone()),
2494 },
2495 );
2496
2497 define(
2499 interp,
2500 "is_affect_uncertain",
2501 Some(1),
2502 |_, args| match &args[0] {
2503 Value::Affective { affect, .. } => {
2504 let uncertain =
2505 affect.sarcasm || matches!(affect.confidence, Some(RuntimeConfidence::Low));
2506 Ok(Value::Bool(uncertain))
2507 }
2508 _ => Ok(Value::Bool(false)),
2509 },
2510 );
2511
2512 define(interp, "with_affect_evidence", Some(2), |_, args| {
2514 match &args[0] {
2516 Value::Affective { affect, .. } => {
2517 let evidence = if affect.sarcasm {
2518 Evidence::Uncertain
2519 } else {
2520 match affect.confidence {
2521 Some(RuntimeConfidence::High) => Evidence::Known,
2522 Some(RuntimeConfidence::Low) => Evidence::Uncertain,
2523 _ => Evidence::Known,
2524 }
2525 };
2526 Ok(Value::Evidential {
2527 value: Box::new(args[1].clone()),
2528 evidence,
2529 })
2530 }
2531 Value::Evidential { evidence, .. } => {
2532 Ok(Value::Evidential {
2534 value: Box::new(args[1].clone()),
2535 evidence: *evidence,
2536 })
2537 }
2538 _ => Ok(args[1].clone()),
2539 }
2540 });
2541}
2542
2543fn register_affect(interp: &mut Interpreter) {
2548 use crate::interpreter::{
2549 RuntimeAffect, RuntimeConfidence, RuntimeEmotion, RuntimeFormality, RuntimeIntensity,
2550 RuntimeSentiment,
2551 };
2552
2553 define(interp, "positive", Some(1), |_, args| {
2557 Ok(Value::Affective {
2558 value: Box::new(args[0].clone()),
2559 affect: RuntimeAffect {
2560 sentiment: Some(RuntimeSentiment::Positive),
2561 sarcasm: false,
2562 intensity: None,
2563 formality: None,
2564 emotion: None,
2565 confidence: None,
2566 },
2567 })
2568 });
2569
2570 define(interp, "negative", Some(1), |_, args| {
2571 Ok(Value::Affective {
2572 value: Box::new(args[0].clone()),
2573 affect: RuntimeAffect {
2574 sentiment: Some(RuntimeSentiment::Negative),
2575 sarcasm: false,
2576 intensity: None,
2577 formality: None,
2578 emotion: None,
2579 confidence: None,
2580 },
2581 })
2582 });
2583
2584 define(interp, "neutral", Some(1), |_, args| {
2585 Ok(Value::Affective {
2586 value: Box::new(args[0].clone()),
2587 affect: RuntimeAffect {
2588 sentiment: Some(RuntimeSentiment::Neutral),
2589 sarcasm: false,
2590 intensity: None,
2591 formality: None,
2592 emotion: None,
2593 confidence: None,
2594 },
2595 })
2596 });
2597
2598 define(interp, "sarcastic", Some(1), |_, args| {
2600 Ok(Value::Affective {
2601 value: Box::new(args[0].clone()),
2602 affect: RuntimeAffect {
2603 sentiment: None,
2604 sarcasm: true,
2605 intensity: None,
2606 formality: None,
2607 emotion: None,
2608 confidence: None,
2609 },
2610 })
2611 });
2612
2613 define(interp, "intensify", Some(1), |_, args| {
2615 Ok(Value::Affective {
2616 value: Box::new(args[0].clone()),
2617 affect: RuntimeAffect {
2618 sentiment: None,
2619 sarcasm: false,
2620 intensity: Some(RuntimeIntensity::Up),
2621 formality: None,
2622 emotion: None,
2623 confidence: None,
2624 },
2625 })
2626 });
2627
2628 define(interp, "dampen", Some(1), |_, args| {
2629 Ok(Value::Affective {
2630 value: Box::new(args[0].clone()),
2631 affect: RuntimeAffect {
2632 sentiment: None,
2633 sarcasm: false,
2634 intensity: Some(RuntimeIntensity::Down),
2635 formality: None,
2636 emotion: None,
2637 confidence: None,
2638 },
2639 })
2640 });
2641
2642 define(interp, "maximize", Some(1), |_, args| {
2643 Ok(Value::Affective {
2644 value: Box::new(args[0].clone()),
2645 affect: RuntimeAffect {
2646 sentiment: None,
2647 sarcasm: false,
2648 intensity: Some(RuntimeIntensity::Max),
2649 formality: None,
2650 emotion: None,
2651 confidence: None,
2652 },
2653 })
2654 });
2655
2656 define(interp, "formal", Some(1), |_, args| {
2658 Ok(Value::Affective {
2659 value: Box::new(args[0].clone()),
2660 affect: RuntimeAffect {
2661 sentiment: None,
2662 sarcasm: false,
2663 intensity: None,
2664 formality: Some(RuntimeFormality::Formal),
2665 emotion: None,
2666 confidence: None,
2667 },
2668 })
2669 });
2670
2671 define(interp, "informal", Some(1), |_, args| {
2672 Ok(Value::Affective {
2673 value: Box::new(args[0].clone()),
2674 affect: RuntimeAffect {
2675 sentiment: None,
2676 sarcasm: false,
2677 intensity: None,
2678 formality: Some(RuntimeFormality::Informal),
2679 emotion: None,
2680 confidence: None,
2681 },
2682 })
2683 });
2684
2685 define(interp, "joyful", Some(1), |_, args| {
2687 Ok(Value::Affective {
2688 value: Box::new(args[0].clone()),
2689 affect: RuntimeAffect {
2690 sentiment: None,
2691 sarcasm: false,
2692 intensity: None,
2693 formality: None,
2694 emotion: Some(RuntimeEmotion::Joy),
2695 confidence: None,
2696 },
2697 })
2698 });
2699
2700 define(interp, "sad", Some(1), |_, args| {
2701 Ok(Value::Affective {
2702 value: Box::new(args[0].clone()),
2703 affect: RuntimeAffect {
2704 sentiment: None,
2705 sarcasm: false,
2706 intensity: None,
2707 formality: None,
2708 emotion: Some(RuntimeEmotion::Sadness),
2709 confidence: None,
2710 },
2711 })
2712 });
2713
2714 define(interp, "angry", Some(1), |_, args| {
2715 Ok(Value::Affective {
2716 value: Box::new(args[0].clone()),
2717 affect: RuntimeAffect {
2718 sentiment: None,
2719 sarcasm: false,
2720 intensity: None,
2721 formality: None,
2722 emotion: Some(RuntimeEmotion::Anger),
2723 confidence: None,
2724 },
2725 })
2726 });
2727
2728 define(interp, "fearful", Some(1), |_, args| {
2729 Ok(Value::Affective {
2730 value: Box::new(args[0].clone()),
2731 affect: RuntimeAffect {
2732 sentiment: None,
2733 sarcasm: false,
2734 intensity: None,
2735 formality: None,
2736 emotion: Some(RuntimeEmotion::Fear),
2737 confidence: None,
2738 },
2739 })
2740 });
2741
2742 define(interp, "surprised", Some(1), |_, args| {
2743 Ok(Value::Affective {
2744 value: Box::new(args[0].clone()),
2745 affect: RuntimeAffect {
2746 sentiment: None,
2747 sarcasm: false,
2748 intensity: None,
2749 formality: None,
2750 emotion: Some(RuntimeEmotion::Surprise),
2751 confidence: None,
2752 },
2753 })
2754 });
2755
2756 define(interp, "loving", Some(1), |_, args| {
2757 Ok(Value::Affective {
2758 value: Box::new(args[0].clone()),
2759 affect: RuntimeAffect {
2760 sentiment: None,
2761 sarcasm: false,
2762 intensity: None,
2763 formality: None,
2764 emotion: Some(RuntimeEmotion::Love),
2765 confidence: None,
2766 },
2767 })
2768 });
2769
2770 define(interp, "high_confidence", Some(1), |_, args| {
2772 Ok(Value::Affective {
2773 value: Box::new(args[0].clone()),
2774 affect: RuntimeAffect {
2775 sentiment: None,
2776 sarcasm: false,
2777 intensity: None,
2778 formality: None,
2779 emotion: None,
2780 confidence: Some(RuntimeConfidence::High),
2781 },
2782 })
2783 });
2784
2785 define(interp, "medium_confidence", Some(1), |_, args| {
2786 Ok(Value::Affective {
2787 value: Box::new(args[0].clone()),
2788 affect: RuntimeAffect {
2789 sentiment: None,
2790 sarcasm: false,
2791 intensity: None,
2792 formality: None,
2793 emotion: None,
2794 confidence: Some(RuntimeConfidence::Medium),
2795 },
2796 })
2797 });
2798
2799 define(interp, "low_confidence", Some(1), |_, args| {
2800 Ok(Value::Affective {
2801 value: Box::new(args[0].clone()),
2802 affect: RuntimeAffect {
2803 sentiment: None,
2804 sarcasm: false,
2805 intensity: None,
2806 formality: None,
2807 emotion: None,
2808 confidence: Some(RuntimeConfidence::Low),
2809 },
2810 })
2811 });
2812
2813 define(interp, "affect_of", Some(1), |_, args| match &args[0] {
2816 Value::Affective { affect, .. } => {
2817 let mut parts = Vec::new();
2818 if let Some(s) = &affect.sentiment {
2819 parts.push(match s {
2820 RuntimeSentiment::Positive => "positive",
2821 RuntimeSentiment::Negative => "negative",
2822 RuntimeSentiment::Neutral => "neutral",
2823 });
2824 }
2825 if affect.sarcasm {
2826 parts.push("sarcastic");
2827 }
2828 if let Some(i) = &affect.intensity {
2829 parts.push(match i {
2830 RuntimeIntensity::Up => "intensified",
2831 RuntimeIntensity::Down => "dampened",
2832 RuntimeIntensity::Max => "maximized",
2833 });
2834 }
2835 if let Some(f) = &affect.formality {
2836 parts.push(match f {
2837 RuntimeFormality::Formal => "formal",
2838 RuntimeFormality::Informal => "informal",
2839 });
2840 }
2841 if let Some(e) = &affect.emotion {
2842 parts.push(match e {
2843 RuntimeEmotion::Joy => "joyful",
2844 RuntimeEmotion::Sadness => "sad",
2845 RuntimeEmotion::Anger => "angry",
2846 RuntimeEmotion::Fear => "fearful",
2847 RuntimeEmotion::Surprise => "surprised",
2848 RuntimeEmotion::Love => "loving",
2849 });
2850 }
2851 if let Some(c) = &affect.confidence {
2852 parts.push(match c {
2853 RuntimeConfidence::High => "high_confidence",
2854 RuntimeConfidence::Medium => "medium_confidence",
2855 RuntimeConfidence::Low => "low_confidence",
2856 });
2857 }
2858 Ok(Value::String(Rc::new(parts.join(", "))))
2859 }
2860 _ => Ok(Value::String(Rc::new("none".to_string()))),
2861 });
2862
2863 define(interp, "is_sarcastic", Some(1), |_, args| match &args[0] {
2864 Value::Affective { affect, .. } => Ok(Value::Bool(affect.sarcasm)),
2865 _ => Ok(Value::Bool(false)),
2866 });
2867
2868 define(interp, "is_positive", Some(1), |_, args| match &args[0] {
2869 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
2870 affect.sentiment,
2871 Some(RuntimeSentiment::Positive)
2872 ))),
2873 _ => Ok(Value::Bool(false)),
2874 });
2875
2876 define(interp, "is_negative", Some(1), |_, args| match &args[0] {
2877 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
2878 affect.sentiment,
2879 Some(RuntimeSentiment::Negative)
2880 ))),
2881 _ => Ok(Value::Bool(false)),
2882 });
2883
2884 define(interp, "is_formal", Some(1), |_, args| match &args[0] {
2885 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
2886 affect.formality,
2887 Some(RuntimeFormality::Formal)
2888 ))),
2889 _ => Ok(Value::Bool(false)),
2890 });
2891
2892 define(interp, "is_informal", Some(1), |_, args| match &args[0] {
2893 Value::Affective { affect, .. } => Ok(Value::Bool(matches!(
2894 affect.formality,
2895 Some(RuntimeFormality::Informal)
2896 ))),
2897 _ => Ok(Value::Bool(false)),
2898 });
2899
2900 define(interp, "emotion_of", Some(1), |_, args| match &args[0] {
2901 Value::Affective { affect, .. } => {
2902 let emotion_str = match &affect.emotion {
2903 Some(RuntimeEmotion::Joy) => "joy",
2904 Some(RuntimeEmotion::Sadness) => "sadness",
2905 Some(RuntimeEmotion::Anger) => "anger",
2906 Some(RuntimeEmotion::Fear) => "fear",
2907 Some(RuntimeEmotion::Surprise) => "surprise",
2908 Some(RuntimeEmotion::Love) => "love",
2909 None => "none",
2910 };
2911 Ok(Value::String(Rc::new(emotion_str.to_string())))
2912 }
2913 _ => Ok(Value::String(Rc::new("none".to_string()))),
2914 });
2915
2916 define(interp, "confidence_of", Some(1), |_, args| match &args[0] {
2917 Value::Affective { affect, .. } => {
2918 let conf_str = match &affect.confidence {
2919 Some(RuntimeConfidence::High) => "high",
2920 Some(RuntimeConfidence::Medium) => "medium",
2921 Some(RuntimeConfidence::Low) => "low",
2922 None => "none",
2923 };
2924 Ok(Value::String(Rc::new(conf_str.to_string())))
2925 }
2926 _ => Ok(Value::String(Rc::new("none".to_string()))),
2927 });
2928
2929 define(interp, "strip_affect", Some(1), |_, args| match &args[0] {
2931 Value::Affective { value, .. } => Ok(*value.clone()),
2932 other => Ok(other.clone()),
2933 });
2934
2935 define(interp, "with_affect", None, |_, args| {
2937 if args.is_empty() {
2938 return Err(RuntimeError::new(
2939 "with_affect requires at least one argument",
2940 ));
2941 }
2942
2943 let base_value = args[0].clone();
2944 let mut affect = RuntimeAffect {
2945 sentiment: None,
2946 sarcasm: false,
2947 intensity: None,
2948 formality: None,
2949 emotion: None,
2950 confidence: None,
2951 };
2952
2953 for arg in args.iter().skip(1) {
2955 if let Value::String(s) = arg {
2956 match s.as_str() {
2957 "positive" | "⊕" => affect.sentiment = Some(RuntimeSentiment::Positive),
2958 "negative" | "⊖" => affect.sentiment = Some(RuntimeSentiment::Negative),
2959 "neutral" | "⊜" => affect.sentiment = Some(RuntimeSentiment::Neutral),
2960 "sarcastic" | "⸮" => affect.sarcasm = true,
2961 "intensify" | "↑" => affect.intensity = Some(RuntimeIntensity::Up),
2962 "dampen" | "↓" => affect.intensity = Some(RuntimeIntensity::Down),
2963 "maximize" | "⇈" => affect.intensity = Some(RuntimeIntensity::Max),
2964 "formal" | "♔" => affect.formality = Some(RuntimeFormality::Formal),
2965 "informal" | "♟" => affect.formality = Some(RuntimeFormality::Informal),
2966 "joy" | "☺" => affect.emotion = Some(RuntimeEmotion::Joy),
2967 "sadness" | "☹" => affect.emotion = Some(RuntimeEmotion::Sadness),
2968 "anger" | "⚡" => affect.emotion = Some(RuntimeEmotion::Anger),
2969 "fear" | "❄" => affect.emotion = Some(RuntimeEmotion::Fear),
2970 "surprise" | "✦" => affect.emotion = Some(RuntimeEmotion::Surprise),
2971 "love" | "♡" => affect.emotion = Some(RuntimeEmotion::Love),
2972 "high" | "◉" => affect.confidence = Some(RuntimeConfidence::High),
2973 "medium" | "◎" => affect.confidence = Some(RuntimeConfidence::Medium),
2974 "low" | "○" => affect.confidence = Some(RuntimeConfidence::Low),
2975 _ => {}
2976 }
2977 }
2978 }
2979
2980 Ok(Value::Affective {
2981 value: Box::new(base_value),
2982 affect,
2983 })
2984 });
2985}
2986
2987fn register_iter(interp: &mut Interpreter) {
2992 define(interp, "sum", Some(1), |_, args| match &args[0] {
2994 Value::Array(arr) => {
2995 let mut sum_int: i64 = 0;
2996 let mut sum_float: f64 = 0.0;
2997 let mut is_float = false;
2998
2999 for val in arr.borrow().iter() {
3000 match val {
3001 Value::Int(n) => {
3002 if is_float {
3003 sum_float += *n as f64;
3004 } else {
3005 sum_int += n;
3006 }
3007 }
3008 Value::Float(n) => {
3009 if !is_float {
3010 sum_float = sum_int as f64;
3011 is_float = true;
3012 }
3013 sum_float += n;
3014 }
3015 _ => return Err(RuntimeError::new("sum() requires array of numbers")),
3016 }
3017 }
3018
3019 if is_float {
3020 Ok(Value::Float(sum_float))
3021 } else {
3022 Ok(Value::Int(sum_int))
3023 }
3024 }
3025 _ => Err(RuntimeError::new("sum() requires array")),
3026 });
3027
3028 define(interp, "product", Some(1), |_, args| match &args[0] {
3030 Value::Array(arr) => {
3031 let mut prod_int: i64 = 1;
3032 let mut prod_float: f64 = 1.0;
3033 let mut is_float = false;
3034
3035 for val in arr.borrow().iter() {
3036 match val {
3037 Value::Int(n) => {
3038 if is_float {
3039 prod_float *= *n as f64;
3040 } else {
3041 prod_int *= n;
3042 }
3043 }
3044 Value::Float(n) => {
3045 if !is_float {
3046 prod_float = prod_int as f64;
3047 is_float = true;
3048 }
3049 prod_float *= n;
3050 }
3051 _ => return Err(RuntimeError::new("product() requires array of numbers")),
3052 }
3053 }
3054
3055 if is_float {
3056 Ok(Value::Float(prod_float))
3057 } else {
3058 Ok(Value::Int(prod_int))
3059 }
3060 }
3061 _ => Err(RuntimeError::new("product() requires array")),
3062 });
3063
3064 define(interp, "mean", Some(1), |_, args| match &args[0] {
3066 Value::Array(arr) => {
3067 let arr = arr.borrow();
3068 if arr.is_empty() {
3069 return Err(RuntimeError::new("mean() on empty array"));
3070 }
3071
3072 let mut sum: f64 = 0.0;
3073 for val in arr.iter() {
3074 match val {
3075 Value::Int(n) => sum += *n as f64,
3076 Value::Float(n) => sum += n,
3077 _ => return Err(RuntimeError::new("mean() requires array of numbers")),
3078 }
3079 }
3080
3081 Ok(Value::Float(sum / arr.len() as f64))
3082 }
3083 _ => Err(RuntimeError::new("mean() requires array")),
3084 });
3085
3086 define(interp, "median", Some(1), |_, args| match &args[0] {
3088 Value::Array(arr) => {
3089 let arr = arr.borrow();
3090 if arr.is_empty() {
3091 return Err(RuntimeError::new("median() on empty array"));
3092 }
3093
3094 let mut nums: Vec<f64> = Vec::new();
3095 for val in arr.iter() {
3096 match val {
3097 Value::Int(n) => nums.push(*n as f64),
3098 Value::Float(n) => nums.push(*n),
3099 _ => return Err(RuntimeError::new("median() requires array of numbers")),
3100 }
3101 }
3102
3103 nums.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
3104 let mid = nums.len() / 2;
3105
3106 if nums.len() % 2 == 0 {
3107 Ok(Value::Float((nums[mid - 1] + nums[mid]) / 2.0))
3108 } else {
3109 Ok(Value::Float(nums[mid]))
3110 }
3111 }
3112 _ => Err(RuntimeError::new("median() requires array")),
3113 });
3114
3115 define(interp, "min_of", Some(1), |_, args| match &args[0] {
3117 Value::Array(arr) => {
3118 let arr = arr.borrow();
3119 if arr.is_empty() {
3120 return Err(RuntimeError::new("min_of() on empty array"));
3121 }
3122
3123 let mut min = &arr[0];
3124 for val in arr.iter().skip(1) {
3125 if matches!(compare_values(val, min), std::cmp::Ordering::Less) {
3126 min = val;
3127 }
3128 }
3129 Ok(min.clone())
3130 }
3131 _ => Err(RuntimeError::new("min_of() requires array")),
3132 });
3133
3134 define(interp, "max_of", Some(1), |_, args| match &args[0] {
3136 Value::Array(arr) => {
3137 let arr = arr.borrow();
3138 if arr.is_empty() {
3139 return Err(RuntimeError::new("max_of() on empty array"));
3140 }
3141
3142 let mut max = &arr[0];
3143 for val in arr.iter().skip(1) {
3144 if matches!(compare_values(val, max), std::cmp::Ordering::Greater) {
3145 max = val;
3146 }
3147 }
3148 Ok(max.clone())
3149 }
3150 _ => Err(RuntimeError::new("max_of() requires array")),
3151 });
3152
3153 define(interp, "count", Some(1), |_, args| match &args[0] {
3155 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
3156 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
3157 _ => Err(RuntimeError::new("count() requires array or string")),
3158 });
3159
3160 define(interp, "any", Some(1), |_, args| match &args[0] {
3162 Value::Array(arr) => {
3163 for val in arr.borrow().iter() {
3164 if is_truthy(val) {
3165 return Ok(Value::Bool(true));
3166 }
3167 }
3168 Ok(Value::Bool(false))
3169 }
3170 _ => Err(RuntimeError::new("any() requires array")),
3171 });
3172
3173 define(interp, "all", Some(1), |_, args| match &args[0] {
3175 Value::Array(arr) => {
3176 for val in arr.borrow().iter() {
3177 if !is_truthy(val) {
3178 return Ok(Value::Bool(false));
3179 }
3180 }
3181 Ok(Value::Bool(true))
3182 }
3183 _ => Err(RuntimeError::new("all() requires array")),
3184 });
3185
3186 define(interp, "none", Some(1), |_, args| match &args[0] {
3188 Value::Array(arr) => {
3189 for val in arr.borrow().iter() {
3190 if is_truthy(val) {
3191 return Ok(Value::Bool(false));
3192 }
3193 }
3194 Ok(Value::Bool(true))
3195 }
3196 _ => Err(RuntimeError::new("none() requires array")),
3197 });
3198}
3199
3200fn is_truthy(val: &Value) -> bool {
3201 match val {
3202 Value::Null | Value::Empty => false,
3203 Value::Bool(b) => *b,
3204 Value::Int(n) => *n != 0,
3205 Value::Float(n) => *n != 0.0 && !n.is_nan(),
3206 Value::String(s) => !s.is_empty(),
3207 Value::Array(arr) => !arr.borrow().is_empty(),
3208 Value::Evidential { value, .. } => is_truthy(value),
3209 _ => true,
3210 }
3211}
3212
3213fn register_io(interp: &mut Interpreter) {
3218 define(interp, "read_file", Some(1), |_, args| {
3220 match &args[0] {
3221 Value::String(path) => {
3222 match std::fs::read_to_string(path.as_str()) {
3223 Ok(content) => Ok(Value::Evidential {
3224 value: Box::new(Value::String(Rc::new(content))),
3225 evidence: Evidence::Reported, }),
3227 Err(e) => Err(RuntimeError::new(format!("read_file failed: {}", e))),
3228 }
3229 }
3230 _ => Err(RuntimeError::new("read_file() requires path string")),
3231 }
3232 });
3233
3234 define(interp, "write_file", Some(2), |_, args| {
3236 match (&args[0], &args[1]) {
3237 (Value::String(path), Value::String(content)) => {
3238 match std::fs::write(path.as_str(), content.as_str()) {
3239 Ok(_) => Ok(Value::Bool(true)),
3240 Err(e) => Err(RuntimeError::new(format!("write_file failed: {}", e))),
3241 }
3242 }
3243 _ => Err(RuntimeError::new(
3244 "write_file() requires path and content strings",
3245 )),
3246 }
3247 });
3248
3249 define(interp, "append_file", Some(2), |_, args| {
3251 match (&args[0], &args[1]) {
3252 (Value::String(path), Value::String(content)) => {
3253 use std::fs::OpenOptions;
3254 let result = OpenOptions::new()
3255 .create(true)
3256 .append(true)
3257 .open(path.as_str())
3258 .and_then(|mut f| f.write_all(content.as_bytes()));
3259 match result {
3260 Ok(_) => Ok(Value::Bool(true)),
3261 Err(e) => Err(RuntimeError::new(format!("append_file failed: {}", e))),
3262 }
3263 }
3264 _ => Err(RuntimeError::new(
3265 "append_file() requires path and content strings",
3266 )),
3267 }
3268 });
3269
3270 define(interp, "file_exists", Some(1), |_, args| match &args[0] {
3272 Value::String(path) => Ok(Value::Bool(std::path::Path::new(path.as_str()).exists())),
3273 _ => Err(RuntimeError::new("file_exists() requires path string")),
3274 });
3275
3276 define(interp, "read_lines", Some(1), |_, args| match &args[0] {
3278 Value::String(path) => match std::fs::read_to_string(path.as_str()) {
3279 Ok(content) => {
3280 let lines: Vec<Value> = content
3281 .lines()
3282 .map(|l| Value::String(Rc::new(l.to_string())))
3283 .collect();
3284 Ok(Value::Evidential {
3285 value: Box::new(Value::Array(Rc::new(RefCell::new(lines)))),
3286 evidence: Evidence::Reported,
3287 })
3288 }
3289 Err(e) => Err(RuntimeError::new(format!("read_lines failed: {}", e))),
3290 },
3291 _ => Err(RuntimeError::new("read_lines() requires path string")),
3292 });
3293
3294 define(interp, "env", Some(1), |_, args| {
3296 match &args[0] {
3297 Value::String(name) => {
3298 match std::env::var(name.as_str()) {
3299 Ok(value) => Ok(Value::Evidential {
3300 value: Box::new(Value::String(Rc::new(value))),
3301 evidence: Evidence::Reported, }),
3303 Err(_) => Ok(Value::Null),
3304 }
3305 }
3306 _ => Err(RuntimeError::new("env() requires variable name string")),
3307 }
3308 });
3309
3310 define(interp, "env_or", Some(2), |_, args| {
3312 match (&args[0], &args[1]) {
3313 (Value::String(name), default) => match std::env::var(name.as_str()) {
3314 Ok(value) => Ok(Value::Evidential {
3315 value: Box::new(Value::String(Rc::new(value))),
3316 evidence: Evidence::Reported,
3317 }),
3318 Err(_) => Ok(default.clone()),
3319 },
3320 _ => Err(RuntimeError::new("env_or() requires variable name string")),
3321 }
3322 });
3323
3324 define(interp, "cwd", Some(0), |_, _| {
3326 match std::env::current_dir() {
3327 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
3328 Err(e) => Err(RuntimeError::new(format!("cwd() failed: {}", e))),
3329 }
3330 });
3331
3332 define(interp, "args", Some(0), |_, _| {
3334 let args: Vec<Value> = std::env::args()
3335 .map(|a| Value::String(Rc::new(a)))
3336 .collect();
3337 Ok(Value::Array(Rc::new(RefCell::new(args))))
3338 });
3339}
3340
3341fn register_time(interp: &mut Interpreter) {
3346 define(interp, "now", Some(0), |_, _| {
3348 let duration = SystemTime::now()
3349 .duration_since(UNIX_EPOCH)
3350 .unwrap_or(Duration::ZERO);
3351 Ok(Value::Int(duration.as_millis() as i64))
3352 });
3353
3354 define(interp, "now_secs", Some(0), |_, _| {
3356 let duration = SystemTime::now()
3357 .duration_since(UNIX_EPOCH)
3358 .unwrap_or(Duration::ZERO);
3359 Ok(Value::Int(duration.as_secs() as i64))
3360 });
3361
3362 define(interp, "now_micros", Some(0), |_, _| {
3364 let duration = SystemTime::now()
3365 .duration_since(UNIX_EPOCH)
3366 .unwrap_or(Duration::ZERO);
3367 Ok(Value::Int(duration.as_micros() as i64))
3368 });
3369
3370 define(interp, "sleep", Some(1), |_, args| match &args[0] {
3372 Value::Int(ms) if *ms >= 0 => {
3373 std::thread::sleep(Duration::from_millis(*ms as u64));
3374 Ok(Value::Null)
3375 }
3376 _ => Err(RuntimeError::new(
3377 "sleep() requires non-negative integer milliseconds",
3378 )),
3379 });
3380
3381 define(interp, "timer_start", Some(0), |_, _| {
3387 let now = Instant::now();
3388 Ok(Value::Int(now.elapsed().as_nanos() as i64)) });
3391}
3392
3393fn register_random(interp: &mut Interpreter) {
3398 define(interp, "random", Some(0), |_, _| {
3400 use std::time::SystemTime;
3402 let seed = SystemTime::now()
3403 .duration_since(UNIX_EPOCH)
3404 .unwrap_or(Duration::ZERO)
3405 .as_nanos() as u64;
3406 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as f64;
3407 Ok(Value::Float(rand / u32::MAX as f64))
3408 });
3409
3410 define(interp, "random_int", Some(2), |_, args| {
3412 match (&args[0], &args[1]) {
3413 (Value::Int(min), Value::Int(max)) if max > min => {
3414 use std::time::SystemTime;
3415 let seed = SystemTime::now()
3416 .duration_since(UNIX_EPOCH)
3417 .unwrap_or(Duration::ZERO)
3418 .as_nanos() as u64;
3419 let range = (max - min) as u64;
3420 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) % range;
3421 Ok(Value::Int(*min + rand as i64))
3422 }
3423 _ => Err(RuntimeError::new(
3424 "random_int() requires min < max integers",
3425 )),
3426 }
3427 });
3428
3429 define(interp, "shuffle", Some(1), |_, args| match &args[0] {
3431 Value::Array(arr) => {
3432 let mut arr = arr.borrow_mut();
3433 use std::time::SystemTime;
3434 let mut seed = SystemTime::now()
3435 .duration_since(UNIX_EPOCH)
3436 .unwrap_or(Duration::ZERO)
3437 .as_nanos() as u64;
3438
3439 for i in (1..arr.len()).rev() {
3440 seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
3441 let j = ((seed >> 16) as usize) % (i + 1);
3442 arr.swap(i, j);
3443 }
3444 Ok(Value::Null)
3445 }
3446 _ => Err(RuntimeError::new("shuffle() requires array")),
3447 });
3448
3449 define(interp, "sample", Some(1), |_, args| match &args[0] {
3451 Value::Array(arr) => {
3452 let arr = arr.borrow();
3453 if arr.is_empty() {
3454 return Err(RuntimeError::new("sample() on empty array"));
3455 }
3456
3457 use std::time::SystemTime;
3458 let seed = SystemTime::now()
3459 .duration_since(UNIX_EPOCH)
3460 .unwrap_or(Duration::ZERO)
3461 .as_nanos() as u64;
3462 let idx =
3463 ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % arr.len();
3464 Ok(arr[idx].clone())
3465 }
3466 _ => Err(RuntimeError::new("sample() requires array")),
3467 });
3468}
3469
3470fn register_convert(interp: &mut Interpreter) {
3475 define(interp, "to_string", Some(1), |_, args| {
3477 Ok(Value::String(Rc::new(format!("{}", args[0]))))
3478 });
3479
3480 define(interp, "to_int", Some(1), |_, args| match &args[0] {
3482 Value::Int(n) => Ok(Value::Int(*n)),
3483 Value::Float(n) => Ok(Value::Int(*n as i64)),
3484 Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
3485 Value::Char(c) => Ok(Value::Int(*c as i64)),
3486 Value::String(s) => s
3487 .parse::<i64>()
3488 .map(Value::Int)
3489 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as integer", s))),
3490 _ => Err(RuntimeError::new("to_int() cannot convert this type")),
3491 });
3492
3493 define(interp, "to_float", Some(1), |_, args| match &args[0] {
3495 Value::Int(n) => Ok(Value::Float(*n as f64)),
3496 Value::Float(n) => Ok(Value::Float(*n)),
3497 Value::Bool(b) => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
3498 Value::String(s) => s
3499 .parse::<f64>()
3500 .map(Value::Float)
3501 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as float", s))),
3502 _ => Err(RuntimeError::new("to_float() cannot convert this type")),
3503 });
3504
3505 define(interp, "to_bool", Some(1), |_, args| {
3507 Ok(Value::Bool(is_truthy(&args[0])))
3508 });
3509
3510 define(interp, "to_char", Some(1), |_, args| match &args[0] {
3512 Value::Char(c) => Ok(Value::Char(*c)),
3513 Value::Int(n) => char::from_u32(*n as u32)
3514 .map(Value::Char)
3515 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n))),
3516 Value::String(s) => s
3517 .chars()
3518 .next()
3519 .map(Value::Char)
3520 .ok_or_else(|| RuntimeError::new("to_char() on empty string")),
3521 _ => Err(RuntimeError::new("to_char() cannot convert this type")),
3522 });
3523
3524 define(interp, "to_array", Some(1), |_, args| match &args[0] {
3526 Value::Array(arr) => Ok(Value::Array(arr.clone())),
3527 Value::Tuple(t) => Ok(Value::Array(Rc::new(RefCell::new(t.as_ref().clone())))),
3528 Value::String(s) => {
3529 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
3530 Ok(Value::Array(Rc::new(RefCell::new(chars))))
3531 }
3532 _ => Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()])))),
3533 });
3534
3535 define(interp, "to_tuple", Some(1), |_, args| match &args[0] {
3537 Value::Tuple(t) => Ok(Value::Tuple(t.clone())),
3538 Value::Array(arr) => Ok(Value::Tuple(Rc::new(arr.borrow().clone()))),
3539 _ => Ok(Value::Tuple(Rc::new(vec![args[0].clone()]))),
3540 });
3541
3542 define(interp, "char_code", Some(1), |_, args| match &args[0] {
3544 Value::Char(c) => Ok(Value::Int(*c as i64)),
3545 _ => Err(RuntimeError::new("char_code() requires char")),
3546 });
3547
3548 define(interp, "from_char_code", Some(1), |_, args| {
3550 match &args[0] {
3551 Value::Int(n) => char::from_u32(*n as u32)
3552 .map(Value::Char)
3553 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n))),
3554 _ => Err(RuntimeError::new("from_char_code() requires integer")),
3555 }
3556 });
3557
3558 define(interp, "hex", Some(1), |_, args| match &args[0] {
3560 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:x}", n)))),
3561 _ => Err(RuntimeError::new("hex() requires integer")),
3562 });
3563
3564 define(interp, "oct", Some(1), |_, args| match &args[0] {
3566 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:o}", n)))),
3567 _ => Err(RuntimeError::new("oct() requires integer")),
3568 });
3569
3570 define(interp, "bin", Some(1), |_, args| match &args[0] {
3572 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:b}", n)))),
3573 _ => Err(RuntimeError::new("bin() requires integer")),
3574 });
3575
3576 define(interp, "parse_int", Some(2), |_, args| {
3578 match (&args[0], &args[1]) {
3579 (Value::String(s), Value::Int(base)) if *base >= 2 && *base <= 36 => {
3580 i64::from_str_radix(s.trim(), *base as u32)
3581 .map(Value::Int)
3582 .map_err(|_| {
3583 RuntimeError::new(format!("cannot parse '{}' as base-{} integer", s, base))
3584 })
3585 }
3586 _ => Err(RuntimeError::new(
3587 "parse_int() requires string and base 2-36",
3588 )),
3589 }
3590 });
3591}
3592
3593fn register_cycle(interp: &mut Interpreter) {
3599 define(interp, "cycle", Some(2), |_, args| {
3601 match (&args[0], &args[1]) {
3602 (Value::Int(value), Value::Int(modulus)) if *modulus > 0 => {
3603 let normalized = value.rem_euclid(*modulus);
3604 Ok(Value::Tuple(Rc::new(vec![
3605 Value::Int(normalized),
3606 Value::Int(*modulus),
3607 ])))
3608 }
3609 _ => Err(RuntimeError::new(
3610 "cycle() requires value and positive modulus",
3611 )),
3612 }
3613 });
3614
3615 define(interp, "mod_add", Some(3), |_, args| {
3617 match (&args[0], &args[1], &args[2]) {
3618 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
3619 Ok(Value::Int((a + b).rem_euclid(*m)))
3620 }
3621 _ => Err(RuntimeError::new(
3622 "mod_add() requires two integers and positive modulus",
3623 )),
3624 }
3625 });
3626
3627 define(interp, "mod_sub", Some(3), |_, args| {
3629 match (&args[0], &args[1], &args[2]) {
3630 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
3631 Ok(Value::Int((a - b).rem_euclid(*m)))
3632 }
3633 _ => Err(RuntimeError::new(
3634 "mod_sub() requires two integers and positive modulus",
3635 )),
3636 }
3637 });
3638
3639 define(interp, "mod_mul", Some(3), |_, args| {
3641 match (&args[0], &args[1], &args[2]) {
3642 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
3643 Ok(Value::Int((a * b).rem_euclid(*m)))
3644 }
3645 _ => Err(RuntimeError::new(
3646 "mod_mul() requires two integers and positive modulus",
3647 )),
3648 }
3649 });
3650
3651 define(interp, "mod_pow", Some(3), |_, args| {
3653 match (&args[0], &args[1], &args[2]) {
3654 (Value::Int(base), Value::Int(exp), Value::Int(m)) if *m > 0 && *exp >= 0 => {
3655 Ok(Value::Int(mod_pow(*base, *exp as u64, *m)))
3656 }
3657 _ => Err(RuntimeError::new(
3658 "mod_pow() requires base, non-negative exp, and positive modulus",
3659 )),
3660 }
3661 });
3662
3663 define(interp, "mod_inv", Some(2), |_, args| {
3665 match (&args[0], &args[1]) {
3666 (Value::Int(a), Value::Int(m)) if *m > 0 => match mod_inverse(*a, *m) {
3667 Some(inv) => Ok(Value::Int(inv)),
3668 None => Err(RuntimeError::new(format!(
3669 "no modular inverse of {} mod {}",
3670 a, m
3671 ))),
3672 },
3673 _ => Err(RuntimeError::new(
3674 "mod_inv() requires integer and positive modulus",
3675 )),
3676 }
3677 });
3678
3679 define(interp, "octave", Some(1), |_, args| {
3682 match &args[0] {
3683 Value::Int(note) => Ok(Value::Int(note.rem_euclid(12))),
3684 Value::Float(freq) => {
3685 let semitones = 12.0 * (freq / 440.0).log2();
3687 Ok(Value::Float(semitones.rem_euclid(12.0)))
3688 }
3689 _ => Err(RuntimeError::new("octave() requires number")),
3690 }
3691 });
3692
3693 define(interp, "interval", Some(2), |_, args| {
3695 match (&args[0], &args[1]) {
3696 (Value::Int(a), Value::Int(b)) => Ok(Value::Int((b - a).rem_euclid(12))),
3697 _ => Err(RuntimeError::new("interval() requires two integers")),
3698 }
3699 });
3700
3701 define(interp, "cents", Some(1), |_, args| match &args[0] {
3703 Value::Int(semitones) => Ok(Value::Int(*semitones * 100)),
3704 Value::Float(semitones) => Ok(Value::Float(*semitones * 100.0)),
3705 _ => Err(RuntimeError::new("cents() requires number")),
3706 });
3707
3708 define(interp, "freq", Some(1), |_, args| match &args[0] {
3710 Value::Int(midi) => {
3711 let freq = 440.0 * 2.0_f64.powf((*midi as f64 - 69.0) / 12.0);
3712 Ok(Value::Float(freq))
3713 }
3714 _ => Err(RuntimeError::new("freq() requires integer MIDI note")),
3715 });
3716
3717 define(interp, "midi", Some(1), |_, args| match &args[0] {
3719 Value::Float(freq) if *freq > 0.0 => {
3720 let midi = 69.0 + 12.0 * (freq / 440.0).log2();
3721 Ok(Value::Int(midi.round() as i64))
3722 }
3723 Value::Int(freq) if *freq > 0 => {
3724 let midi = 69.0 + 12.0 * (*freq as f64 / 440.0).log2();
3725 Ok(Value::Int(midi.round() as i64))
3726 }
3727 _ => Err(RuntimeError::new("midi() requires positive frequency")),
3728 });
3729}
3730
3731fn mod_pow(mut base: i64, mut exp: u64, modulus: i64) -> i64 {
3733 if modulus == 1 {
3734 return 0;
3735 }
3736 let mut result: i64 = 1;
3737 base = base.rem_euclid(modulus);
3738 while exp > 0 {
3739 if exp % 2 == 1 {
3740 result = (result * base).rem_euclid(modulus);
3741 }
3742 exp /= 2;
3743 base = (base * base).rem_euclid(modulus);
3744 }
3745 result
3746}
3747
3748fn register_simd(interp: &mut Interpreter) {
3754 define(interp, "simd_new", Some(4), |_, args| {
3756 let values: Result<Vec<f64>, _> = args
3757 .iter()
3758 .map(|v| match v {
3759 Value::Float(f) => Ok(*f),
3760 Value::Int(i) => Ok(*i as f64),
3761 _ => Err(RuntimeError::new("simd_new() requires numbers")),
3762 })
3763 .collect();
3764 let values = values?;
3765 Ok(Value::Array(Rc::new(RefCell::new(vec![
3766 Value::Float(values[0]),
3767 Value::Float(values[1]),
3768 Value::Float(values[2]),
3769 Value::Float(values[3]),
3770 ]))))
3771 });
3772
3773 define(interp, "simd_splat", Some(1), |_, args| {
3775 let v = match &args[0] {
3776 Value::Float(f) => *f,
3777 Value::Int(i) => *i as f64,
3778 _ => return Err(RuntimeError::new("simd_splat() requires number")),
3779 };
3780 Ok(Value::Array(Rc::new(RefCell::new(vec![
3781 Value::Float(v),
3782 Value::Float(v),
3783 Value::Float(v),
3784 Value::Float(v),
3785 ]))))
3786 });
3787
3788 define(interp, "simd_add", Some(2), |_, args| {
3790 simd_binary_op(&args[0], &args[1], |a, b| a + b, "simd_add")
3791 });
3792
3793 define(interp, "simd_sub", Some(2), |_, args| {
3795 simd_binary_op(&args[0], &args[1], |a, b| a - b, "simd_sub")
3796 });
3797
3798 define(interp, "simd_mul", Some(2), |_, args| {
3800 simd_binary_op(&args[0], &args[1], |a, b| a * b, "simd_mul")
3801 });
3802
3803 define(interp, "simd_div", Some(2), |_, args| {
3805 simd_binary_op(&args[0], &args[1], |a, b| a / b, "simd_div")
3806 });
3807
3808 define(interp, "simd_dot", Some(2), |_, args| {
3810 let a = extract_simd(&args[0], "simd_dot")?;
3811 let b = extract_simd(&args[1], "simd_dot")?;
3812 let dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
3813 Ok(Value::Float(dot))
3814 });
3815
3816 define(interp, "simd_cross", Some(2), |_, args| {
3818 let a = extract_simd(&args[0], "simd_cross")?;
3819 let b = extract_simd(&args[1], "simd_cross")?;
3820 Ok(Value::Array(Rc::new(RefCell::new(vec![
3821 Value::Float(a[1] * b[2] - a[2] * b[1]),
3822 Value::Float(a[2] * b[0] - a[0] * b[2]),
3823 Value::Float(a[0] * b[1] - a[1] * b[0]),
3824 Value::Float(0.0),
3825 ]))))
3826 });
3827
3828 define(interp, "simd_length", Some(1), |_, args| {
3830 let v = extract_simd(&args[0], "simd_length")?;
3831 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
3832 Ok(Value::Float(len_sq.sqrt()))
3833 });
3834
3835 define(interp, "simd_normalize", Some(1), |_, args| {
3837 let v = extract_simd(&args[0], "simd_normalize")?;
3838 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
3839 let len = len_sq.sqrt();
3840 if len < 1e-10 {
3841 return Ok(Value::Array(Rc::new(RefCell::new(vec![
3842 Value::Float(0.0),
3843 Value::Float(0.0),
3844 Value::Float(0.0),
3845 Value::Float(0.0),
3846 ]))));
3847 }
3848 let inv_len = 1.0 / len;
3849 Ok(Value::Array(Rc::new(RefCell::new(vec![
3850 Value::Float(v[0] * inv_len),
3851 Value::Float(v[1] * inv_len),
3852 Value::Float(v[2] * inv_len),
3853 Value::Float(v[3] * inv_len),
3854 ]))))
3855 });
3856
3857 define(interp, "simd_min", Some(2), |_, args| {
3859 simd_binary_op(&args[0], &args[1], |a, b| a.min(b), "simd_min")
3860 });
3861
3862 define(interp, "simd_max", Some(2), |_, args| {
3864 simd_binary_op(&args[0], &args[1], |a, b| a.max(b), "simd_max")
3865 });
3866
3867 define(interp, "simd_hadd", Some(1), |_, args| {
3869 let v = extract_simd(&args[0], "simd_hadd")?;
3870 Ok(Value::Float(v[0] + v[1] + v[2] + v[3]))
3871 });
3872
3873 define(interp, "simd_extract", Some(2), |_, args| {
3875 let v = extract_simd(&args[0], "simd_extract")?;
3876 let idx = match &args[1] {
3877 Value::Int(i) => *i as usize,
3878 _ => return Err(RuntimeError::new("simd_extract() requires integer index")),
3879 };
3880 if idx > 3 {
3881 return Err(RuntimeError::new("simd_extract() index must be 0-3"));
3882 }
3883 Ok(Value::Float(v[idx]))
3884 });
3885
3886 define(interp, "simd_free", Some(1), |_, _| Ok(Value::Null));
3888
3889 define(interp, "simd_lerp", Some(3), |_, args| {
3891 let a = extract_simd(&args[0], "simd_lerp")?;
3892 let b = extract_simd(&args[1], "simd_lerp")?;
3893 let t = match &args[2] {
3894 Value::Float(f) => *f,
3895 Value::Int(i) => *i as f64,
3896 _ => return Err(RuntimeError::new("simd_lerp() requires float t")),
3897 };
3898 let one_t = 1.0 - t;
3899 Ok(Value::Array(Rc::new(RefCell::new(vec![
3900 Value::Float(a[0] * one_t + b[0] * t),
3901 Value::Float(a[1] * one_t + b[1] * t),
3902 Value::Float(a[2] * one_t + b[2] * t),
3903 Value::Float(a[3] * one_t + b[3] * t),
3904 ]))))
3905 });
3906}
3907
3908fn extract_simd(val: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
3910 match val {
3911 Value::Array(arr) => {
3912 let arr = arr.borrow();
3913 if arr.len() < 4 {
3914 return Err(RuntimeError::new(format!(
3915 "{}() requires 4-element array",
3916 fn_name
3917 )));
3918 }
3919 let mut result = [0.0; 4];
3920 for (i, v) in arr.iter().take(4).enumerate() {
3921 result[i] = match v {
3922 Value::Float(f) => *f,
3923 Value::Int(n) => *n as f64,
3924 _ => {
3925 return Err(RuntimeError::new(format!(
3926 "{}() requires numeric array",
3927 fn_name
3928 )))
3929 }
3930 };
3931 }
3932 Ok(result)
3933 }
3934 _ => Err(RuntimeError::new(format!(
3935 "{}() requires array argument",
3936 fn_name
3937 ))),
3938 }
3939}
3940
3941fn simd_binary_op<F>(a: &Value, b: &Value, op: F, fn_name: &str) -> Result<Value, RuntimeError>
3943where
3944 F: Fn(f64, f64) -> f64,
3945{
3946 let a = extract_simd(a, fn_name)?;
3947 let b = extract_simd(b, fn_name)?;
3948 Ok(Value::Array(Rc::new(RefCell::new(vec![
3949 Value::Float(op(a[0], b[0])),
3950 Value::Float(op(a[1], b[1])),
3951 Value::Float(op(a[2], b[2])),
3952 Value::Float(op(a[3], b[3])),
3953 ]))))
3954}
3955
3956fn register_graphics_math(interp: &mut Interpreter) {
3967 define(interp, "quat_new", Some(4), |_, args| {
3975 let w = extract_number(&args[0], "quat_new")?;
3976 let x = extract_number(&args[1], "quat_new")?;
3977 let y = extract_number(&args[2], "quat_new")?;
3978 let z = extract_number(&args[3], "quat_new")?;
3979 Ok(make_vec4(w, x, y, z))
3980 });
3981
3982 define(interp, "quat_identity", Some(0), |_, _| {
3984 Ok(make_vec4(1.0, 0.0, 0.0, 0.0))
3985 });
3986
3987 define(interp, "quat_from_axis_angle", Some(2), |_, args| {
3989 let axis = extract_vec3(&args[0], "quat_from_axis_angle")?;
3990 let angle = extract_number(&args[1], "quat_from_axis_angle")?;
3991
3992 let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
3994 if len < 1e-10 {
3995 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0)); }
3997 let ax = axis[0] / len;
3998 let ay = axis[1] / len;
3999 let az = axis[2] / len;
4000
4001 let half_angle = angle / 2.0;
4002 let s = half_angle.sin();
4003 let c = half_angle.cos();
4004
4005 Ok(make_vec4(c, ax * s, ay * s, az * s))
4006 });
4007
4008 define(interp, "quat_from_euler", Some(3), |_, args| {
4011 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();
4016 let (sy, cy) = (yaw / 2.0).sin_cos();
4017 let (sr, cr) = (roll / 2.0).sin_cos();
4018
4019 let w = cp * cy * cr + sp * sy * sr;
4021 let x = sp * cy * cr - cp * sy * sr;
4022 let y = cp * sy * cr + sp * cy * sr;
4023 let z = cp * cy * sr - sp * sy * cr;
4024
4025 Ok(make_vec4(w, x, y, z))
4026 });
4027
4028 define(interp, "quat_mul", Some(2), |_, args| {
4030 let q1 = extract_vec4(&args[0], "quat_mul")?;
4031 let q2 = extract_vec4(&args[1], "quat_mul")?;
4032
4033 let w = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
4035 let x = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
4036 let y = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
4037 let z = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
4038
4039 Ok(make_vec4(w, x, y, z))
4040 });
4041
4042 define(interp, "quat_conjugate", Some(1), |_, args| {
4044 let q = extract_vec4(&args[0], "quat_conjugate")?;
4045 Ok(make_vec4(q[0], -q[1], -q[2], -q[3]))
4046 });
4047
4048 define(interp, "quat_inverse", Some(1), |_, args| {
4050 let q = extract_vec4(&args[0], "quat_inverse")?;
4051 let norm_sq = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
4052 if norm_sq < 1e-10 {
4053 return Err(RuntimeError::new(
4054 "quat_inverse: cannot invert zero quaternion",
4055 ));
4056 }
4057 Ok(make_vec4(
4058 q[0] / norm_sq,
4059 -q[1] / norm_sq,
4060 -q[2] / norm_sq,
4061 -q[3] / norm_sq,
4062 ))
4063 });
4064
4065 define(interp, "quat_normalize", Some(1), |_, args| {
4067 let q = extract_vec4(&args[0], "quat_normalize")?;
4068 let len = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]).sqrt();
4069 if len < 1e-10 {
4070 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0));
4071 }
4072 Ok(make_vec4(q[0] / len, q[1] / len, q[2] / len, q[3] / len))
4073 });
4074
4075 define(interp, "quat_rotate", Some(2), |_, args| {
4077 let q = extract_vec4(&args[0], "quat_rotate")?;
4078 let v = extract_vec3(&args[1], "quat_rotate")?;
4079
4080 let qw = q[0];
4082 let qx = q[1];
4083 let qy = q[2];
4084 let qz = q[3];
4085 let vx = v[0];
4086 let vy = v[1];
4087 let vz = v[2];
4088
4089 let tx = 2.0 * (qy * vz - qz * vy);
4091 let ty = 2.0 * (qz * vx - qx * vz);
4092 let tz = 2.0 * (qx * vy - qy * vx);
4093
4094 let rx = vx + qw * tx + (qy * tz - qz * ty);
4096 let ry = vy + qw * ty + (qz * tx - qx * tz);
4097 let rz = vz + qw * tz + (qx * ty - qy * tx);
4098
4099 Ok(make_vec3(rx, ry, rz))
4100 });
4101
4102 define(interp, "quat_slerp", Some(3), |_, args| {
4104 let q1 = extract_vec4(&args[0], "quat_slerp")?;
4105 let mut q2 = extract_vec4(&args[1], "quat_slerp")?;
4106 let t = extract_number(&args[2], "quat_slerp")?;
4107
4108 let mut dot = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
4110
4111 if dot < 0.0 {
4113 q2 = [-q2[0], -q2[1], -q2[2], -q2[3]];
4114 dot = -dot;
4115 }
4116
4117 if dot > 0.9995 {
4119 let w = q1[0] + t * (q2[0] - q1[0]);
4120 let x = q1[1] + t * (q2[1] - q1[1]);
4121 let y = q1[2] + t * (q2[2] - q1[2]);
4122 let z = q1[3] + t * (q2[3] - q1[3]);
4123 let len = (w * w + x * x + y * y + z * z).sqrt();
4124 return Ok(make_vec4(w / len, x / len, y / len, z / len));
4125 }
4126
4127 let theta_0 = dot.acos();
4129 let theta = theta_0 * t;
4130 let sin_theta = theta.sin();
4131 let sin_theta_0 = theta_0.sin();
4132
4133 let s0 = (theta_0 - theta).cos() - dot * sin_theta / sin_theta_0;
4134 let s1 = sin_theta / sin_theta_0;
4135
4136 Ok(make_vec4(
4137 s0 * q1[0] + s1 * q2[0],
4138 s0 * q1[1] + s1 * q2[1],
4139 s0 * q1[2] + s1 * q2[2],
4140 s0 * q1[3] + s1 * q2[3],
4141 ))
4142 });
4143
4144 define(interp, "quat_to_euler", Some(1), |_, args| {
4146 let q = extract_vec4(&args[0], "quat_to_euler")?;
4147 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4148
4149 let sinr_cosp = 2.0 * (w * x + y * z);
4151 let cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
4152 let roll = sinr_cosp.atan2(cosr_cosp);
4153
4154 let sinp = 2.0 * (w * y - z * x);
4156 let pitch = if sinp.abs() >= 1.0 {
4157 std::f64::consts::FRAC_PI_2.copysign(sinp)
4158 } else {
4159 sinp.asin()
4160 };
4161
4162 let siny_cosp = 2.0 * (w * z + x * y);
4164 let cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
4165 let yaw = siny_cosp.atan2(cosy_cosp);
4166
4167 Ok(make_vec3(pitch, yaw, roll))
4168 });
4169
4170 define(interp, "quat_to_mat4", Some(1), |_, args| {
4172 let q = extract_vec4(&args[0], "quat_to_mat4")?;
4173 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4174
4175 let xx = x * x;
4176 let yy = y * y;
4177 let zz = z * z;
4178 let xy = x * y;
4179 let xz = x * z;
4180 let yz = y * z;
4181 let wx = w * x;
4182 let wy = w * y;
4183 let wz = w * z;
4184
4185 Ok(make_mat4([
4187 1.0 - 2.0 * (yy + zz),
4188 2.0 * (xy + wz),
4189 2.0 * (xz - wy),
4190 0.0,
4191 2.0 * (xy - wz),
4192 1.0 - 2.0 * (xx + zz),
4193 2.0 * (yz + wx),
4194 0.0,
4195 2.0 * (xz + wy),
4196 2.0 * (yz - wx),
4197 1.0 - 2.0 * (xx + yy),
4198 0.0,
4199 0.0,
4200 0.0,
4201 0.0,
4202 1.0,
4203 ]))
4204 });
4205
4206 define(interp, "vec2", Some(2), |_, args| {
4212 let x = extract_number(&args[0], "vec2")?;
4213 let y = extract_number(&args[1], "vec2")?;
4214 Ok(make_vec2(x, y))
4215 });
4216
4217 define(interp, "vec3", Some(3), |_, args| {
4219 let x = extract_number(&args[0], "vec3")?;
4220 let y = extract_number(&args[1], "vec3")?;
4221 let z = extract_number(&args[2], "vec3")?;
4222 Ok(make_vec3(x, y, z))
4223 });
4224
4225 define(interp, "vec4", Some(4), |_, args| {
4227 let x = extract_number(&args[0], "vec4")?;
4228 let y = extract_number(&args[1], "vec4")?;
4229 let z = extract_number(&args[2], "vec4")?;
4230 let w = extract_number(&args[3], "vec4")?;
4231 Ok(make_vec4(x, y, z, w))
4232 });
4233
4234 define(interp, "vec3_add", Some(2), |_, args| {
4236 let a = extract_vec3(&args[0], "vec3_add")?;
4237 let b = extract_vec3(&args[1], "vec3_add")?;
4238 Ok(make_vec3(a[0] + b[0], a[1] + b[1], a[2] + b[2]))
4239 });
4240
4241 define(interp, "vec3_sub", Some(2), |_, args| {
4243 let a = extract_vec3(&args[0], "vec3_sub")?;
4244 let b = extract_vec3(&args[1], "vec3_sub")?;
4245 Ok(make_vec3(a[0] - b[0], a[1] - b[1], a[2] - b[2]))
4246 });
4247
4248 define(interp, "vec3_scale", Some(2), |_, args| {
4250 let v = extract_vec3(&args[0], "vec3_scale")?;
4251 let s = extract_number(&args[1], "vec3_scale")?;
4252 Ok(make_vec3(v[0] * s, v[1] * s, v[2] * s))
4253 });
4254
4255 define(interp, "vec3_dot", Some(2), |_, args| {
4257 let a = extract_vec3(&args[0], "vec3_dot")?;
4258 let b = extract_vec3(&args[1], "vec3_dot")?;
4259 Ok(Value::Float(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]))
4260 });
4261
4262 define(interp, "vec3_cross", Some(2), |_, args| {
4264 let a = extract_vec3(&args[0], "vec3_cross")?;
4265 let b = extract_vec3(&args[1], "vec3_cross")?;
4266 Ok(make_vec3(
4267 a[1] * b[2] - a[2] * b[1],
4268 a[2] * b[0] - a[0] * b[2],
4269 a[0] * b[1] - a[1] * b[0],
4270 ))
4271 });
4272
4273 define(interp, "vec3_length", Some(1), |_, args| {
4275 let v = extract_vec3(&args[0], "vec3_length")?;
4276 Ok(Value::Float(
4277 (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt(),
4278 ))
4279 });
4280
4281 define(interp, "vec3_normalize", Some(1), |_, args| {
4283 let v = extract_vec3(&args[0], "vec3_normalize")?;
4284 let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
4285 if len < 1e-10 {
4286 return Ok(make_vec3(0.0, 0.0, 0.0));
4287 }
4288 Ok(make_vec3(v[0] / len, v[1] / len, v[2] / len))
4289 });
4290
4291 define(interp, "vec3_lerp", Some(3), |_, args| {
4293 let a = extract_vec3(&args[0], "vec3_lerp")?;
4294 let b = extract_vec3(&args[1], "vec3_lerp")?;
4295 let t = extract_number(&args[2], "vec3_lerp")?;
4296 Ok(make_vec3(
4297 a[0] + t * (b[0] - a[0]),
4298 a[1] + t * (b[1] - a[1]),
4299 a[2] + t * (b[2] - a[2]),
4300 ))
4301 });
4302
4303 define(interp, "vec3_reflect", Some(2), |_, args| {
4305 let i = extract_vec3(&args[0], "vec3_reflect")?;
4306 let n = extract_vec3(&args[1], "vec3_reflect")?;
4307 let dot = i[0] * n[0] + i[1] * n[1] + i[2] * n[2];
4308 Ok(make_vec3(
4309 i[0] - 2.0 * dot * n[0],
4310 i[1] - 2.0 * dot * n[1],
4311 i[2] - 2.0 * dot * n[2],
4312 ))
4313 });
4314
4315 define(interp, "vec3_refract", Some(3), |_, args| {
4317 let i = extract_vec3(&args[0], "vec3_refract")?;
4318 let n = extract_vec3(&args[1], "vec3_refract")?;
4319 let eta = extract_number(&args[2], "vec3_refract")?;
4320
4321 let dot = i[0] * n[0] + i[1] * n[1] + i[2] * n[2];
4322 let k = 1.0 - eta * eta * (1.0 - dot * dot);
4323
4324 if k < 0.0 {
4325 return Ok(make_vec3(0.0, 0.0, 0.0));
4327 }
4328
4329 let coeff = eta * dot + k.sqrt();
4330 Ok(make_vec3(
4331 eta * i[0] - coeff * n[0],
4332 eta * i[1] - coeff * n[1],
4333 eta * i[2] - coeff * n[2],
4334 ))
4335 });
4336
4337 define(interp, "vec4_dot", Some(2), |_, args| {
4339 let a = extract_vec4(&args[0], "vec4_dot")?;
4340 let b = extract_vec4(&args[1], "vec4_dot")?;
4341 Ok(Value::Float(
4342 a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3],
4343 ))
4344 });
4345
4346 define(interp, "mat4_identity", Some(0), |_, _| {
4352 Ok(make_mat4([
4353 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,
4354 ]))
4355 });
4356
4357 define(interp, "mat4_mul", Some(2), |_, args| {
4359 let a = extract_mat4(&args[0], "mat4_mul")?;
4360 let b = extract_mat4(&args[1], "mat4_mul")?;
4361
4362 let mut result = [0.0f64; 16];
4363 for col in 0..4 {
4364 for row in 0..4 {
4365 let mut sum = 0.0;
4366 for k in 0..4 {
4367 sum += a[k * 4 + row] * b[col * 4 + k];
4368 }
4369 result[col * 4 + row] = sum;
4370 }
4371 }
4372 Ok(make_mat4(result))
4373 });
4374
4375 define(interp, "mat4_transform", Some(2), |_, args| {
4377 let m = extract_mat4(&args[0], "mat4_transform")?;
4378 let v = extract_vec4(&args[1], "mat4_transform")?;
4379
4380 Ok(make_vec4(
4381 m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3],
4382 m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3],
4383 m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3],
4384 m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3],
4385 ))
4386 });
4387
4388 define(interp, "mat4_translate", Some(3), |_, args| {
4390 let tx = extract_number(&args[0], "mat4_translate")?;
4391 let ty = extract_number(&args[1], "mat4_translate")?;
4392 let tz = extract_number(&args[2], "mat4_translate")?;
4393 Ok(make_mat4([
4394 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,
4395 ]))
4396 });
4397
4398 define(interp, "mat4_scale", Some(3), |_, args| {
4400 let sx = extract_number(&args[0], "mat4_scale")?;
4401 let sy = extract_number(&args[1], "mat4_scale")?;
4402 let sz = extract_number(&args[2], "mat4_scale")?;
4403 Ok(make_mat4([
4404 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,
4405 ]))
4406 });
4407
4408 define(interp, "mat4_rotate_x", Some(1), |_, args| {
4410 let angle = extract_number(&args[0], "mat4_rotate_x")?;
4411 let (s, c) = angle.sin_cos();
4412 Ok(make_mat4([
4413 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,
4414 ]))
4415 });
4416
4417 define(interp, "mat4_rotate_y", Some(1), |_, args| {
4419 let angle = extract_number(&args[0], "mat4_rotate_y")?;
4420 let (s, c) = angle.sin_cos();
4421 Ok(make_mat4([
4422 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,
4423 ]))
4424 });
4425
4426 define(interp, "mat4_rotate_z", Some(1), |_, args| {
4428 let angle = extract_number(&args[0], "mat4_rotate_z")?;
4429 let (s, c) = angle.sin_cos();
4430 Ok(make_mat4([
4431 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,
4432 ]))
4433 });
4434
4435 define(interp, "mat4_perspective", Some(4), |_, args| {
4437 let fov_y = extract_number(&args[0], "mat4_perspective")?;
4438 let aspect = extract_number(&args[1], "mat4_perspective")?;
4439 let near = extract_number(&args[2], "mat4_perspective")?;
4440 let far = extract_number(&args[3], "mat4_perspective")?;
4441
4442 let f = 1.0 / (fov_y / 2.0).tan();
4443 let nf = 1.0 / (near - far);
4444
4445 Ok(make_mat4([
4446 f / aspect,
4447 0.0,
4448 0.0,
4449 0.0,
4450 0.0,
4451 f,
4452 0.0,
4453 0.0,
4454 0.0,
4455 0.0,
4456 (far + near) * nf,
4457 -1.0,
4458 0.0,
4459 0.0,
4460 2.0 * far * near * nf,
4461 0.0,
4462 ]))
4463 });
4464
4465 define(interp, "mat4_ortho", Some(6), |_, args| {
4467 let left = extract_number(&args[0], "mat4_ortho")?;
4468 let right = extract_number(&args[1], "mat4_ortho")?;
4469 let bottom = extract_number(&args[2], "mat4_ortho")?;
4470 let top = extract_number(&args[3], "mat4_ortho")?;
4471 let near = extract_number(&args[4], "mat4_ortho")?;
4472 let far = extract_number(&args[5], "mat4_ortho")?;
4473
4474 let lr = 1.0 / (left - right);
4475 let bt = 1.0 / (bottom - top);
4476 let nf = 1.0 / (near - far);
4477
4478 Ok(make_mat4([
4479 -2.0 * lr,
4480 0.0,
4481 0.0,
4482 0.0,
4483 0.0,
4484 -2.0 * bt,
4485 0.0,
4486 0.0,
4487 0.0,
4488 0.0,
4489 2.0 * nf,
4490 0.0,
4491 (left + right) * lr,
4492 (top + bottom) * bt,
4493 (far + near) * nf,
4494 1.0,
4495 ]))
4496 });
4497
4498 define(interp, "mat4_look_at", Some(3), |_, args| {
4500 let eye = extract_vec3(&args[0], "mat4_look_at")?;
4501 let center = extract_vec3(&args[1], "mat4_look_at")?;
4502 let up = extract_vec3(&args[2], "mat4_look_at")?;
4503
4504 let fx = center[0] - eye[0];
4506 let fy = center[1] - eye[1];
4507 let fz = center[2] - eye[2];
4508 let flen = (fx * fx + fy * fy + fz * fz).sqrt();
4509 let (fx, fy, fz) = (fx / flen, fy / flen, fz / flen);
4510
4511 let rx = fy * up[2] - fz * up[1];
4513 let ry = fz * up[0] - fx * up[2];
4514 let rz = fx * up[1] - fy * up[0];
4515 let rlen = (rx * rx + ry * ry + rz * rz).sqrt();
4516 let (rx, ry, rz) = (rx / rlen, ry / rlen, rz / rlen);
4517
4518 let ux = ry * fz - rz * fy;
4520 let uy = rz * fx - rx * fz;
4521 let uz = rx * fy - ry * fx;
4522
4523 Ok(make_mat4([
4524 rx,
4525 ux,
4526 -fx,
4527 0.0,
4528 ry,
4529 uy,
4530 -fy,
4531 0.0,
4532 rz,
4533 uz,
4534 -fz,
4535 0.0,
4536 -(rx * eye[0] + ry * eye[1] + rz * eye[2]),
4537 -(ux * eye[0] + uy * eye[1] + uz * eye[2]),
4538 -(-fx * eye[0] - fy * eye[1] - fz * eye[2]),
4539 1.0,
4540 ]))
4541 });
4542
4543 define(interp, "mat4_inverse", Some(1), |_, args| {
4545 let m = extract_mat4(&args[0], "mat4_inverse")?;
4546
4547 let a00 = m[0];
4549 let a01 = m[1];
4550 let a02 = m[2];
4551 let a03 = m[3];
4552 let a10 = m[4];
4553 let a11 = m[5];
4554 let a12 = m[6];
4555 let a13 = m[7];
4556 let a20 = m[8];
4557 let a21 = m[9];
4558 let a22 = m[10];
4559 let a23 = m[11];
4560 let a30 = m[12];
4561 let a31 = m[13];
4562 let a32 = m[14];
4563 let a33 = m[15];
4564
4565 let b00 = a00 * a11 - a01 * a10;
4566 let b01 = a00 * a12 - a02 * a10;
4567 let b02 = a00 * a13 - a03 * a10;
4568 let b03 = a01 * a12 - a02 * a11;
4569 let b04 = a01 * a13 - a03 * a11;
4570 let b05 = a02 * a13 - a03 * a12;
4571 let b06 = a20 * a31 - a21 * a30;
4572 let b07 = a20 * a32 - a22 * a30;
4573 let b08 = a20 * a33 - a23 * a30;
4574 let b09 = a21 * a32 - a22 * a31;
4575 let b10 = a21 * a33 - a23 * a31;
4576 let b11 = a22 * a33 - a23 * a32;
4577
4578 let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
4579
4580 if det.abs() < 1e-10 {
4581 return Err(RuntimeError::new("mat4_inverse: singular matrix"));
4582 }
4583
4584 let inv_det = 1.0 / det;
4585
4586 Ok(make_mat4([
4587 (a11 * b11 - a12 * b10 + a13 * b09) * inv_det,
4588 (a02 * b10 - a01 * b11 - a03 * b09) * inv_det,
4589 (a31 * b05 - a32 * b04 + a33 * b03) * inv_det,
4590 (a22 * b04 - a21 * b05 - a23 * b03) * inv_det,
4591 (a12 * b08 - a10 * b11 - a13 * b07) * inv_det,
4592 (a00 * b11 - a02 * b08 + a03 * b07) * inv_det,
4593 (a32 * b02 - a30 * b05 - a33 * b01) * inv_det,
4594 (a20 * b05 - a22 * b02 + a23 * b01) * inv_det,
4595 (a10 * b10 - a11 * b08 + a13 * b06) * inv_det,
4596 (a01 * b08 - a00 * b10 - a03 * b06) * inv_det,
4597 (a30 * b04 - a31 * b02 + a33 * b00) * inv_det,
4598 (a21 * b02 - a20 * b04 - a23 * b00) * inv_det,
4599 (a11 * b07 - a10 * b09 - a12 * b06) * inv_det,
4600 (a00 * b09 - a01 * b07 + a02 * b06) * inv_det,
4601 (a31 * b01 - a30 * b03 - a32 * b00) * inv_det,
4602 (a20 * b03 - a21 * b01 + a22 * b00) * inv_det,
4603 ]))
4604 });
4605
4606 define(interp, "mat4_transpose", Some(1), |_, args| {
4608 let m = extract_mat4(&args[0], "mat4_transpose")?;
4609 Ok(make_mat4([
4610 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],
4611 m[11], m[15],
4612 ]))
4613 });
4614
4615 define(interp, "mat3_identity", Some(0), |_, _| {
4621 Ok(make_mat3([1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]))
4622 });
4623
4624 define(interp, "mat3_from_mat4", Some(1), |_, args| {
4626 let m = extract_mat4(&args[0], "mat3_from_mat4")?;
4627 Ok(make_mat3([
4628 m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10],
4629 ]))
4630 });
4631
4632 define(interp, "mat3_mul", Some(2), |_, args| {
4634 let a = extract_mat3(&args[0], "mat3_mul")?;
4635 let b = extract_mat3(&args[1], "mat3_mul")?;
4636
4637 let mut result = [0.0f64; 9];
4638 for col in 0..3 {
4639 for row in 0..3 {
4640 let mut sum = 0.0;
4641 for k in 0..3 {
4642 sum += a[k * 3 + row] * b[col * 3 + k];
4643 }
4644 result[col * 3 + row] = sum;
4645 }
4646 }
4647 Ok(make_mat3(result))
4648 });
4649
4650 define(interp, "mat3_transform", Some(2), |_, args| {
4652 let m = extract_mat3(&args[0], "mat3_transform")?;
4653 let v = extract_vec3(&args[1], "mat3_transform")?;
4654
4655 Ok(make_vec3(
4656 m[0] * v[0] + m[3] * v[1] + m[6] * v[2],
4657 m[1] * v[0] + m[4] * v[1] + m[7] * v[2],
4658 m[2] * v[0] + m[5] * v[1] + m[8] * v[2],
4659 ))
4660 });
4661
4662 define(interp, "mat3_inverse", Some(1), |_, args| {
4664 let m = extract_mat3(&args[0], "mat3_inverse")?;
4665
4666 let det = m[0] * (m[4] * m[8] - m[5] * m[7]) - m[3] * (m[1] * m[8] - m[2] * m[7])
4667 + m[6] * (m[1] * m[5] - m[2] * m[4]);
4668
4669 if det.abs() < 1e-10 {
4670 return Err(RuntimeError::new("mat3_inverse: singular matrix"));
4671 }
4672
4673 let inv_det = 1.0 / det;
4674
4675 Ok(make_mat3([
4676 (m[4] * m[8] - m[5] * m[7]) * inv_det,
4677 (m[2] * m[7] - m[1] * m[8]) * inv_det,
4678 (m[1] * m[5] - m[2] * m[4]) * inv_det,
4679 (m[5] * m[6] - m[3] * m[8]) * inv_det,
4680 (m[0] * m[8] - m[2] * m[6]) * inv_det,
4681 (m[2] * m[3] - m[0] * m[5]) * inv_det,
4682 (m[3] * m[7] - m[4] * m[6]) * inv_det,
4683 (m[1] * m[6] - m[0] * m[7]) * inv_det,
4684 (m[0] * m[4] - m[1] * m[3]) * inv_det,
4685 ]))
4686 });
4687
4688 define(interp, "mat3_transpose", Some(1), |_, args| {
4690 let m = extract_mat3(&args[0], "mat3_transpose")?;
4691 Ok(make_mat3([
4692 m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8],
4693 ]))
4694 });
4695}
4696
4697fn extract_number(v: &Value, fn_name: &str) -> Result<f64, RuntimeError> {
4699 match v {
4700 Value::Float(f) => Ok(*f),
4701 Value::Int(i) => Ok(*i as f64),
4702 _ => Err(RuntimeError::new(format!(
4703 "{}() requires number argument",
4704 fn_name
4705 ))),
4706 }
4707}
4708
4709fn extract_vec2(v: &Value, fn_name: &str) -> Result<[f64; 2], RuntimeError> {
4710 match v {
4711 Value::Array(arr) => {
4712 let arr = arr.borrow();
4713 if arr.len() < 2 {
4714 return Err(RuntimeError::new(format!(
4715 "{}() requires vec2 (2 elements)",
4716 fn_name
4717 )));
4718 }
4719 Ok([
4720 extract_number(&arr[0], fn_name)?,
4721 extract_number(&arr[1], fn_name)?,
4722 ])
4723 }
4724 _ => Err(RuntimeError::new(format!(
4725 "{}() requires vec2 array",
4726 fn_name
4727 ))),
4728 }
4729}
4730
4731fn extract_vec3(v: &Value, fn_name: &str) -> Result<[f64; 3], RuntimeError> {
4732 match v {
4733 Value::Array(arr) => {
4734 let arr = arr.borrow();
4735 if arr.len() < 3 {
4736 return Err(RuntimeError::new(format!(
4737 "{}() requires vec3 (3 elements)",
4738 fn_name
4739 )));
4740 }
4741 Ok([
4742 extract_number(&arr[0], fn_name)?,
4743 extract_number(&arr[1], fn_name)?,
4744 extract_number(&arr[2], fn_name)?,
4745 ])
4746 }
4747 _ => Err(RuntimeError::new(format!(
4748 "{}() requires vec3 array",
4749 fn_name
4750 ))),
4751 }
4752}
4753
4754fn extract_vec4(v: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
4755 match v {
4756 Value::Array(arr) => {
4757 let arr = arr.borrow();
4758 if arr.len() < 4 {
4759 return Err(RuntimeError::new(format!(
4760 "{}() requires vec4 (4 elements)",
4761 fn_name
4762 )));
4763 }
4764 Ok([
4765 extract_number(&arr[0], fn_name)?,
4766 extract_number(&arr[1], fn_name)?,
4767 extract_number(&arr[2], fn_name)?,
4768 extract_number(&arr[3], fn_name)?,
4769 ])
4770 }
4771 _ => Err(RuntimeError::new(format!(
4772 "{}() requires vec4 array",
4773 fn_name
4774 ))),
4775 }
4776}
4777
4778fn extract_mat3(v: &Value, fn_name: &str) -> Result<[f64; 9], RuntimeError> {
4779 match v {
4780 Value::Array(arr) => {
4781 let arr = arr.borrow();
4782 if arr.len() < 9 {
4783 return Err(RuntimeError::new(format!(
4784 "{}() requires mat3 (9 elements)",
4785 fn_name
4786 )));
4787 }
4788 let mut result = [0.0f64; 9];
4789 for i in 0..9 {
4790 result[i] = extract_number(&arr[i], fn_name)?;
4791 }
4792 Ok(result)
4793 }
4794 _ => Err(RuntimeError::new(format!(
4795 "{}() requires mat3 array",
4796 fn_name
4797 ))),
4798 }
4799}
4800
4801fn extract_mat4(v: &Value, fn_name: &str) -> Result<[f64; 16], RuntimeError> {
4802 match v {
4803 Value::Array(arr) => {
4804 let arr = arr.borrow();
4805 if arr.len() < 16 {
4806 return Err(RuntimeError::new(format!(
4807 "{}() requires mat4 (16 elements)",
4808 fn_name
4809 )));
4810 }
4811 let mut result = [0.0f64; 16];
4812 for i in 0..16 {
4813 result[i] = extract_number(&arr[i], fn_name)?;
4814 }
4815 Ok(result)
4816 }
4817 _ => Err(RuntimeError::new(format!(
4818 "{}() requires mat4 array",
4819 fn_name
4820 ))),
4821 }
4822}
4823
4824fn make_vec2(x: f64, y: f64) -> Value {
4825 Value::Array(Rc::new(RefCell::new(vec![
4826 Value::Float(x),
4827 Value::Float(y),
4828 ])))
4829}
4830
4831fn make_vec3(x: f64, y: f64, z: f64) -> Value {
4832 Value::Array(Rc::new(RefCell::new(vec![
4833 Value::Float(x),
4834 Value::Float(y),
4835 Value::Float(z),
4836 ])))
4837}
4838
4839fn make_vec3_arr(v: [f64; 3]) -> Value {
4841 make_vec3(v[0], v[1], v[2])
4842}
4843
4844fn make_vec4(x: f64, y: f64, z: f64, w: f64) -> Value {
4845 Value::Array(Rc::new(RefCell::new(vec![
4846 Value::Float(x),
4847 Value::Float(y),
4848 Value::Float(z),
4849 Value::Float(w),
4850 ])))
4851}
4852
4853fn make_mat3(m: [f64; 9]) -> Value {
4854 Value::Array(Rc::new(RefCell::new(
4855 m.iter().map(|&v| Value::Float(v)).collect(),
4856 )))
4857}
4858
4859fn make_mat4(m: [f64; 16]) -> Value {
4860 Value::Array(Rc::new(RefCell::new(
4861 m.iter().map(|&v| Value::Float(v)).collect(),
4862 )))
4863}
4864
4865fn register_concurrency(interp: &mut Interpreter) {
4882 define(interp, "channel_new", Some(0), |_, _| {
4886 let (sender, receiver) = mpsc::channel();
4887 let inner = ChannelInner {
4888 sender: Mutex::new(sender),
4889 receiver: Mutex::new(receiver),
4890 };
4891 Ok(Value::Channel(Arc::new(inner)))
4892 });
4893
4894 define(interp, "channel_send", Some(2), |_, args| {
4896 let channel = match &args[0] {
4897 Value::Channel(ch) => ch.clone(),
4898 _ => {
4899 return Err(RuntimeError::new(
4900 "channel_send() requires channel as first argument",
4901 ))
4902 }
4903 };
4904 let value = args[1].clone();
4905
4906 let sender = channel
4907 .sender
4908 .lock()
4909 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
4910 sender
4911 .send(value)
4912 .map_err(|_| RuntimeError::new("channel_send() failed: receiver dropped"))?;
4913
4914 Ok(Value::Null)
4915 });
4916
4917 define(interp, "channel_recv", Some(1), |_, args| {
4919 let channel = match &args[0] {
4920 Value::Channel(ch) => ch.clone(),
4921 _ => {
4922 return Err(RuntimeError::new(
4923 "channel_recv() requires channel argument",
4924 ))
4925 }
4926 };
4927
4928 let receiver = channel
4929 .receiver
4930 .lock()
4931 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
4932 match receiver.recv() {
4933 Ok(value) => Ok(value),
4934 Err(_) => Err(RuntimeError::new("channel_recv() failed: sender dropped")),
4935 }
4936 });
4937
4938 define(interp, "channel_try_recv", Some(1), |_, args| {
4940 let channel = match &args[0] {
4941 Value::Channel(ch) => ch.clone(),
4942 _ => {
4943 return Err(RuntimeError::new(
4944 "channel_try_recv() requires channel argument",
4945 ))
4946 }
4947 };
4948
4949 let receiver = channel
4950 .receiver
4951 .lock()
4952 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
4953 match receiver.try_recv() {
4954 Ok(value) => {
4955 Ok(Value::Variant {
4957 enum_name: "Option".to_string(),
4958 variant_name: "Some".to_string(),
4959 fields: Some(Rc::new(vec![value])),
4960 })
4961 }
4962 Err(mpsc::TryRecvError::Empty) => {
4963 Ok(Value::Variant {
4965 enum_name: "Option".to_string(),
4966 variant_name: "None".to_string(),
4967 fields: None,
4968 })
4969 }
4970 Err(mpsc::TryRecvError::Disconnected) => Err(RuntimeError::new(
4971 "channel_try_recv() failed: sender dropped",
4972 )),
4973 }
4974 });
4975
4976 define(interp, "channel_recv_timeout", Some(2), |_, args| {
4978 let channel = match &args[0] {
4979 Value::Channel(ch) => ch.clone(),
4980 _ => {
4981 return Err(RuntimeError::new(
4982 "channel_recv_timeout() requires a channel as first argument.\n\
4983 Create a channel with channel_new():\n\
4984 let ch = channel_new();\n\
4985 channel_send(ch, value);\n\
4986 let result = channel_recv_timeout(ch, 1000); // 1 second timeout",
4987 ))
4988 }
4989 };
4990 let timeout_ms = match &args[1] {
4991 Value::Int(ms) => *ms as u64,
4992 _ => {
4993 return Err(RuntimeError::new(
4994 "channel_recv_timeout() requires timeout in milliseconds (integer).\n\
4995 Example: channel_recv_timeout(ch, 1000) // Wait up to 1 second",
4996 ))
4997 }
4998 };
4999
5000 let receiver = channel
5001 .receiver
5002 .lock()
5003 .map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
5004 match receiver.recv_timeout(std::time::Duration::from_millis(timeout_ms)) {
5005 Ok(value) => Ok(Value::Variant {
5006 enum_name: "Option".to_string(),
5007 variant_name: "Some".to_string(),
5008 fields: Some(Rc::new(vec![value])),
5009 }),
5010 Err(mpsc::RecvTimeoutError::Timeout) => Ok(Value::Variant {
5011 enum_name: "Option".to_string(),
5012 variant_name: "None".to_string(),
5013 fields: None,
5014 }),
5015 Err(mpsc::RecvTimeoutError::Disconnected) => Err(RuntimeError::new(
5016 "channel_recv_timeout() failed: sender dropped",
5017 )),
5018 }
5019 });
5020
5021 define(interp, "thread_spawn_detached", Some(0), |_, _| {
5029 thread::spawn(|| {
5032 });
5034 Ok(Value::Null)
5035 });
5036
5037 define(interp, "thread_join", Some(1), |_, args| {
5040 match &args[0] {
5041 Value::ThreadHandle(h) => {
5042 let mut guard = h
5043 .lock()
5044 .map_err(|_| RuntimeError::new("thread handle mutex poisoned"))?;
5045 if let Some(handle) = guard.take() {
5046 match handle.join() {
5047 Ok(v) => Ok(v),
5048 Err(_) => Err(RuntimeError::new("thread panicked")),
5049 }
5050 } else {
5051 Err(RuntimeError::new("thread already joined"))
5052 }
5053 }
5054 _ => Ok(args[0].clone()),
5056 }
5057 });
5058
5059 define(interp, "thread_sleep", Some(1), |_, args| {
5061 let ms = match &args[0] {
5062 Value::Int(ms) => *ms as u64,
5063 Value::Float(ms) => *ms as u64,
5064 _ => {
5065 return Err(RuntimeError::new(
5066 "thread_sleep() requires integer milliseconds",
5067 ))
5068 }
5069 };
5070
5071 thread::sleep(std::time::Duration::from_millis(ms));
5072 Ok(Value::Null)
5073 });
5074
5075 define(interp, "thread_yield", Some(0), |_, _| {
5077 thread::yield_now();
5078 Ok(Value::Null)
5079 });
5080
5081 define(interp, "thread_id", Some(0), |_, _| {
5083 let id = thread::current().id();
5084 Ok(Value::String(Rc::new(format!("{:?}", id))))
5085 });
5086
5087 define(interp, "spawn_actor", Some(1), |_, args| {
5094 let name = match &args[0] {
5095 Value::String(s) => s.to_string(),
5096 _ => return Err(RuntimeError::new("actor_spawn() requires string name")),
5097 };
5098
5099 let inner = ActorInner {
5100 name,
5101 message_queue: Mutex::new(Vec::new()),
5102 message_count: std::sync::atomic::AtomicUsize::new(0),
5103 };
5104
5105 Ok(Value::Actor(Arc::new(inner)))
5106 });
5107
5108 define(interp, "send_to_actor", Some(3), |_, args| {
5111 let actor = match &args[0] {
5112 Value::Actor(a) => a.clone(),
5113 _ => {
5114 return Err(RuntimeError::new(
5115 "actor_send() requires actor as first argument",
5116 ))
5117 }
5118 };
5119 let msg_type = match &args[1] {
5120 Value::String(s) => s.to_string(),
5121 _ => {
5122 return Err(RuntimeError::new(
5123 "actor_send() requires string message type",
5124 ))
5125 }
5126 };
5127 let msg_data = format!("{}", args[2]);
5128
5129 let mut queue = actor
5130 .message_queue
5131 .lock()
5132 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
5133 queue.push((msg_type, msg_data));
5134 actor
5135 .message_count
5136 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
5137
5138 Ok(Value::Null)
5139 });
5140
5141 define(interp, "tell_actor", Some(3), |_, args| {
5143 let actor = match &args[0] {
5144 Value::Actor(a) => a.clone(),
5145 _ => {
5146 return Err(RuntimeError::new(
5147 "actor_tell() requires actor as first argument",
5148 ))
5149 }
5150 };
5151 let msg_type = match &args[1] {
5152 Value::String(s) => s.to_string(),
5153 _ => {
5154 return Err(RuntimeError::new(
5155 "actor_tell() requires string message type",
5156 ))
5157 }
5158 };
5159 let msg_data = format!("{}", args[2]);
5160
5161 let mut queue = actor
5162 .message_queue
5163 .lock()
5164 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
5165 queue.push((msg_type, msg_data));
5166 actor
5167 .message_count
5168 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
5169
5170 Ok(Value::Null)
5171 });
5172
5173 define(interp, "recv_from_actor", Some(1), |_, args| {
5176 let actor = match &args[0] {
5177 Value::Actor(a) => a.clone(),
5178 _ => return Err(RuntimeError::new("actor_recv() requires actor argument")),
5179 };
5180
5181 let mut queue = actor
5182 .message_queue
5183 .lock()
5184 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
5185 match queue.pop() {
5186 Some((msg_type, msg_data)) => {
5187 Ok(Value::Variant {
5189 enum_name: "Option".to_string(),
5190 variant_name: "Some".to_string(),
5191 fields: Some(Rc::new(vec![Value::Tuple(Rc::new(vec![
5192 Value::String(Rc::new(msg_type)),
5193 Value::String(Rc::new(msg_data)),
5194 ]))])),
5195 })
5196 }
5197 None => Ok(Value::Variant {
5198 enum_name: "Option".to_string(),
5199 variant_name: "None".to_string(),
5200 fields: None,
5201 }),
5202 }
5203 });
5204
5205 define(interp, "get_actor_msg_count", Some(1), |_, args| {
5207 let a = match &args[0] {
5208 Value::Actor(a) => a.clone(),
5209 _ => {
5210 return Err(RuntimeError::new(
5211 "get_actor_msg_count() requires actor argument",
5212 ))
5213 }
5214 };
5215
5216 let count = a.message_count.load(std::sync::atomic::Ordering::SeqCst);
5217 Ok(Value::Int(count as i64))
5218 });
5219
5220 define(interp, "get_actor_name", Some(1), |_, args| {
5222 let a = match &args[0] {
5223 Value::Actor(a) => a.clone(),
5224 _ => {
5225 return Err(RuntimeError::new(
5226 "get_actor_name() requires actor argument",
5227 ))
5228 }
5229 };
5230
5231 Ok(Value::String(Rc::new(a.name.clone())))
5232 });
5233
5234 define(interp, "get_actor_pending", Some(1), |_, args| {
5236 let a = match &args[0] {
5237 Value::Actor(a) => a.clone(),
5238 _ => {
5239 return Err(RuntimeError::new(
5240 "get_actor_pending() requires actor argument",
5241 ))
5242 }
5243 };
5244
5245 let queue = a
5246 .message_queue
5247 .lock()
5248 .map_err(|_| RuntimeError::new("actor queue poisoned"))?;
5249 Ok(Value::Int(queue.len() as i64))
5250 });
5251
5252 define(interp, "mutex_new", Some(1), |_, args| {
5256 let value = args[0].clone();
5257 let mut map = std::collections::HashMap::new();
5259 map.insert("__mutex_value".to_string(), value);
5260 map.insert("__mutex_locked".to_string(), Value::Bool(false));
5261 Ok(Value::Map(Rc::new(RefCell::new(map))))
5262 });
5263
5264 define(interp, "mutex_lock", Some(1), |_, args| {
5266 let mutex = match &args[0] {
5267 Value::Map(m) => m.clone(),
5268 _ => return Err(RuntimeError::new("mutex_lock() requires mutex")),
5269 };
5270
5271 let mut map = mutex.borrow_mut();
5272 map.insert("__mutex_locked".to_string(), Value::Bool(true));
5274
5275 match map.get("__mutex_value") {
5276 Some(v) => Ok(v.clone()),
5277 None => Err(RuntimeError::new("invalid mutex")),
5278 }
5279 });
5280
5281 define(interp, "mutex_unlock", Some(2), |_, args| {
5283 let mutex = match &args[0] {
5284 Value::Map(m) => m.clone(),
5285 _ => return Err(RuntimeError::new("mutex_unlock() requires mutex")),
5286 };
5287 let new_value = args[1].clone();
5288
5289 let mut map = mutex.borrow_mut();
5290 map.insert("__mutex_value".to_string(), new_value);
5291 map.insert("__mutex_locked".to_string(), Value::Bool(false));
5292
5293 Ok(Value::Null)
5294 });
5295
5296 define(interp, "atomic_new", Some(1), |_, args| {
5298 let value = match &args[0] {
5299 Value::Int(i) => *i,
5300 _ => return Err(RuntimeError::new("atomic_new() requires integer")),
5301 };
5302
5303 let mut map = std::collections::HashMap::new();
5305 map.insert("__atomic_value".to_string(), Value::Int(value));
5306 Ok(Value::Map(Rc::new(RefCell::new(map))))
5307 });
5308
5309 define(interp, "atomic_load", Some(1), |_, args| {
5311 let atomic = match &args[0] {
5312 Value::Map(m) => m.clone(),
5313 _ => return Err(RuntimeError::new("atomic_load() requires atomic")),
5314 };
5315
5316 let map = atomic.borrow();
5317 match map.get("__atomic_value") {
5318 Some(v) => Ok(v.clone()),
5319 None => Err(RuntimeError::new("invalid atomic")),
5320 }
5321 });
5322
5323 define(interp, "atomic_store", Some(2), |_, args| {
5325 let atomic = match &args[0] {
5326 Value::Map(m) => m.clone(),
5327 _ => return Err(RuntimeError::new("atomic_store() requires atomic")),
5328 };
5329 let value = match &args[1] {
5330 Value::Int(i) => *i,
5331 _ => return Err(RuntimeError::new("atomic_store() requires integer value")),
5332 };
5333
5334 let mut map = atomic.borrow_mut();
5335 map.insert("__atomic_value".to_string(), Value::Int(value));
5336 Ok(Value::Null)
5337 });
5338
5339 define(interp, "atomic_add", Some(2), |_, args| {
5341 let atomic = match &args[0] {
5342 Value::Map(m) => m.clone(),
5343 _ => return Err(RuntimeError::new("atomic_add() requires atomic")),
5344 };
5345 let delta = match &args[1] {
5346 Value::Int(i) => *i,
5347 _ => return Err(RuntimeError::new("atomic_add() requires integer delta")),
5348 };
5349
5350 let mut map = atomic.borrow_mut();
5351 let old = match map.get("__atomic_value") {
5352 Some(Value::Int(i)) => *i,
5353 _ => return Err(RuntimeError::new("invalid atomic")),
5354 };
5355 map.insert("__atomic_value".to_string(), Value::Int(old + delta));
5356 Ok(Value::Int(old))
5357 });
5358
5359 define(interp, "atomic_cas", Some(3), |_, args| {
5361 let atomic = match &args[0] {
5362 Value::Map(m) => m.clone(),
5363 _ => return Err(RuntimeError::new("atomic_cas() requires atomic")),
5364 };
5365 let expected = match &args[1] {
5366 Value::Int(i) => *i,
5367 _ => return Err(RuntimeError::new("atomic_cas() requires integer expected")),
5368 };
5369 let new_value = match &args[2] {
5370 Value::Int(i) => *i,
5371 _ => return Err(RuntimeError::new("atomic_cas() requires integer new value")),
5372 };
5373
5374 let mut map = atomic.borrow_mut();
5375 let current = match map.get("__atomic_value") {
5376 Some(Value::Int(i)) => *i,
5377 _ => return Err(RuntimeError::new("invalid atomic")),
5378 };
5379
5380 if current == expected {
5381 map.insert("__atomic_value".to_string(), Value::Int(new_value));
5382 Ok(Value::Bool(true))
5383 } else {
5384 Ok(Value::Bool(false))
5385 }
5386 });
5387
5388 define(interp, "parallel_map", Some(2), |_, args| {
5392 let arr = match &args[0] {
5393 Value::Array(a) => a.borrow().clone(),
5394 _ => return Err(RuntimeError::new("parallel_map() requires array")),
5395 };
5396 let _func = args[1].clone();
5397
5398 Ok(Value::Array(Rc::new(RefCell::new(arr))))
5401 });
5402
5403 define(interp, "parallel_for", Some(3), |_, args| {
5405 let start = match &args[0] {
5406 Value::Int(i) => *i,
5407 _ => return Err(RuntimeError::new("parallel_for() requires integer start")),
5408 };
5409 let end = match &args[1] {
5410 Value::Int(i) => *i,
5411 _ => return Err(RuntimeError::new("parallel_for() requires integer end")),
5412 };
5413 let _func = args[2].clone();
5414
5415 let range: Vec<Value> = (start..end).map(|i| Value::Int(i)).collect();
5418 Ok(Value::Array(Rc::new(RefCell::new(range))))
5419 });
5420
5421 define(interp, "async_sleep", Some(1), |interp, args| {
5439 let ms = match &args[0] {
5440 Value::Int(ms) => *ms as u64,
5441 Value::Float(ms) => *ms as u64,
5442 _ => {
5443 return Err(RuntimeError::new(
5444 "async_sleep() requires integer milliseconds",
5445 ))
5446 }
5447 };
5448
5449 Ok(interp.make_future_timer(std::time::Duration::from_millis(ms)))
5450 });
5451
5452 define(interp, "future_ready", Some(1), |interp, args| {
5454 Ok(interp.make_future_immediate(args[0].clone()))
5455 });
5456
5457 define(interp, "future_pending", Some(0), |_, _| {
5459 Ok(Value::Future(Rc::new(RefCell::new(
5460 crate::interpreter::FutureInner {
5461 state: crate::interpreter::FutureState::Pending,
5462 computation: None,
5463 complete_at: None,
5464 },
5465 ))))
5466 });
5467
5468 define(interp, "is_future", Some(1), |_, args| {
5470 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
5471 });
5472
5473 define(interp, "is_ready", Some(1), |_, args| {
5475 match &args[0] {
5476 Value::Future(fut) => {
5477 let f = fut.borrow();
5478 Ok(Value::Bool(matches!(
5479 f.state,
5480 crate::interpreter::FutureState::Ready(_)
5481 )))
5482 }
5483 _ => Ok(Value::Bool(true)), }
5485 });
5486
5487 define(interp, "join_futures", Some(1), |_, args| {
5489 let futures = match &args[0] {
5490 Value::Array(arr) => {
5491 let arr = arr.borrow();
5492 let mut futs = Vec::new();
5493 for v in arr.iter() {
5494 match v {
5495 Value::Future(f) => futs.push(f.clone()),
5496 _ => {
5497 return Err(RuntimeError::new(
5498 "join_futures() requires array of futures",
5499 ))
5500 }
5501 }
5502 }
5503 futs
5504 }
5505 _ => {
5506 return Err(RuntimeError::new(
5507 "join_futures() requires array of futures",
5508 ))
5509 }
5510 };
5511
5512 Ok(Value::Future(Rc::new(RefCell::new(
5513 crate::interpreter::FutureInner {
5514 state: crate::interpreter::FutureState::Pending,
5515 computation: Some(crate::interpreter::FutureComputation::Join(futures)),
5516 complete_at: None,
5517 },
5518 ))))
5519 });
5520
5521 define(interp, "race_futures", Some(1), |_, args| {
5523 let futures = match &args[0] {
5524 Value::Array(arr) => {
5525 let arr = arr.borrow();
5526 let mut futs = Vec::new();
5527 for v in arr.iter() {
5528 match v {
5529 Value::Future(f) => futs.push(f.clone()),
5530 _ => {
5531 return Err(RuntimeError::new(
5532 "race_futures() requires array of futures",
5533 ))
5534 }
5535 }
5536 }
5537 futs
5538 }
5539 _ => {
5540 return Err(RuntimeError::new(
5541 "race_futures() requires array of futures",
5542 ))
5543 }
5544 };
5545
5546 Ok(Value::Future(Rc::new(RefCell::new(
5547 crate::interpreter::FutureInner {
5548 state: crate::interpreter::FutureState::Pending,
5549 computation: Some(crate::interpreter::FutureComputation::Race(futures)),
5550 complete_at: None,
5551 },
5552 ))))
5553 });
5554
5555 define(interp, "poll_future", Some(1), |_, args| {
5557 match &args[0] {
5558 Value::Future(fut) => {
5559 let f = fut.borrow();
5560 match &f.state {
5561 crate::interpreter::FutureState::Ready(v) => Ok(Value::Variant {
5562 enum_name: "Option".to_string(),
5563 variant_name: "Some".to_string(),
5564 fields: Some(Rc::new(vec![(**v).clone()])),
5565 }),
5566 _ => Ok(Value::Variant {
5567 enum_name: "Option".to_string(),
5568 variant_name: "None".to_string(),
5569 fields: None,
5570 }),
5571 }
5572 }
5573 other => Ok(Value::Variant {
5575 enum_name: "Option".to_string(),
5576 variant_name: "Some".to_string(),
5577 fields: Some(Rc::new(vec![other.clone()])),
5578 }),
5579 }
5580 });
5581}
5582
5583fn register_json(interp: &mut Interpreter) {
5588 define(interp, "json_parse", Some(1), |_, args| {
5590 let json_str = match &args[0] {
5591 Value::String(s) => s.as_str(),
5592 _ => return Err(RuntimeError::new("json_parse() requires string argument")),
5593 };
5594
5595 fn json_to_value(json: &serde_json::Value) -> Value {
5596 match json {
5597 serde_json::Value::Null => Value::Null,
5598 serde_json::Value::Bool(b) => Value::Bool(*b),
5599 serde_json::Value::Number(n) => {
5600 if let Some(i) = n.as_i64() {
5601 Value::Int(i)
5602 } else if let Some(f) = n.as_f64() {
5603 Value::Float(f)
5604 } else {
5605 Value::Null
5606 }
5607 }
5608 serde_json::Value::String(s) => Value::String(Rc::new(s.clone())),
5609 serde_json::Value::Array(arr) => {
5610 let values: Vec<Value> = arr.iter().map(json_to_value).collect();
5611 Value::Array(Rc::new(RefCell::new(values)))
5612 }
5613 serde_json::Value::Object(obj) => {
5614 let mut map = HashMap::new();
5615 for (k, v) in obj {
5616 map.insert(k.clone(), json_to_value(v));
5617 }
5618 Value::Map(Rc::new(RefCell::new(map)))
5619 }
5620 }
5621 }
5622
5623 match serde_json::from_str(json_str) {
5624 Ok(json) => Ok(json_to_value(&json)),
5625 Err(e) => Err(RuntimeError::new(format!("JSON parse error: {}", e))),
5626 }
5627 });
5628
5629 define(interp, "json_stringify", Some(1), |_, args| {
5631 fn value_to_json(val: &Value) -> serde_json::Value {
5632 match val {
5633 Value::Null => serde_json::Value::Null,
5634 Value::Bool(b) => serde_json::Value::Bool(*b),
5635 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
5636 Value::Float(f) => serde_json::Number::from_f64(*f)
5637 .map(serde_json::Value::Number)
5638 .unwrap_or(serde_json::Value::Null),
5639 Value::String(s) => serde_json::Value::String(s.to_string()),
5640 Value::Array(arr) => {
5641 let arr = arr.borrow();
5642 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
5643 }
5644 Value::Tuple(t) => serde_json::Value::Array(t.iter().map(value_to_json).collect()),
5645 Value::Map(map) => {
5646 let map = map.borrow();
5647 let obj: serde_json::Map<String, serde_json::Value> = map
5648 .iter()
5649 .map(|(k, v)| (k.clone(), value_to_json(v)))
5650 .collect();
5651 serde_json::Value::Object(obj)
5652 }
5653 Value::Struct { fields, .. } => {
5654 let fields = fields.borrow();
5655 let obj: serde_json::Map<String, serde_json::Value> = fields
5656 .iter()
5657 .map(|(k, v)| (k.clone(), value_to_json(v)))
5658 .collect();
5659 serde_json::Value::Object(obj)
5660 }
5661 _ => serde_json::Value::String(format!("{}", val)),
5662 }
5663 }
5664
5665 let json = value_to_json(&args[0]);
5666 Ok(Value::String(Rc::new(json.to_string())))
5667 });
5668
5669 define(interp, "json_pretty", Some(1), |_, args| {
5671 fn value_to_json(val: &Value) -> serde_json::Value {
5672 match val {
5673 Value::Null => serde_json::Value::Null,
5674 Value::Bool(b) => serde_json::Value::Bool(*b),
5675 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
5676 Value::Float(f) => serde_json::Number::from_f64(*f)
5677 .map(serde_json::Value::Number)
5678 .unwrap_or(serde_json::Value::Null),
5679 Value::String(s) => serde_json::Value::String(s.to_string()),
5680 Value::Array(arr) => {
5681 let arr = arr.borrow();
5682 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
5683 }
5684 Value::Map(map) => {
5685 let map = map.borrow();
5686 let obj: serde_json::Map<String, serde_json::Value> = map
5687 .iter()
5688 .map(|(k, v)| (k.clone(), value_to_json(v)))
5689 .collect();
5690 serde_json::Value::Object(obj)
5691 }
5692 _ => serde_json::Value::String(format!("{}", val)),
5693 }
5694 }
5695
5696 let json = value_to_json(&args[0]);
5697 match serde_json::to_string_pretty(&json) {
5698 Ok(s) => Ok(Value::String(Rc::new(s))),
5699 Err(e) => Err(RuntimeError::new(format!("JSON stringify error: {}", e))),
5700 }
5701 });
5702
5703 define(interp, "json_get", Some(2), |_, args| {
5705 let path = match &args[1] {
5706 Value::String(s) => s.to_string(),
5707 _ => return Err(RuntimeError::new("json_get() requires string path")),
5708 };
5709
5710 let mut current = args[0].clone();
5711 for key in path.split('.') {
5712 current = match ¤t {
5713 Value::Map(map) => {
5714 let map = map.borrow();
5715 map.get(key).cloned().unwrap_or(Value::Null)
5716 }
5717 Value::Array(arr) => {
5718 if let Ok(idx) = key.parse::<usize>() {
5719 let arr = arr.borrow();
5720 arr.get(idx).cloned().unwrap_or(Value::Null)
5721 } else {
5722 Value::Null
5723 }
5724 }
5725 _ => Value::Null,
5726 };
5727 }
5728 Ok(current)
5729 });
5730
5731 define(interp, "json_set", Some(3), |_, args| {
5733 let path = match &args[1] {
5734 Value::String(s) => s.to_string(),
5735 _ => return Err(RuntimeError::new("json_set() requires string path")),
5736 };
5737 let new_value = args[2].clone();
5738
5739 match &args[0] {
5741 Value::Map(map) => {
5742 let mut map = map.borrow_mut();
5743 map.insert(path, new_value);
5744 Ok(Value::Map(Rc::new(RefCell::new(map.clone()))))
5745 }
5746 _ => Err(RuntimeError::new("json_set() requires map/object")),
5747 }
5748 });
5749}
5750
5751fn register_fs(interp: &mut Interpreter) {
5756 define(interp, "fs_read", Some(1), |_, args| {
5758 let path = match &args[0] {
5759 Value::String(s) => s.to_string(),
5760 _ => return Err(RuntimeError::new("fs_read() requires string path")),
5761 };
5762
5763 match std::fs::read_to_string(&path) {
5764 Ok(content) => Ok(Value::String(Rc::new(content))),
5765 Err(e) => Err(RuntimeError::new(format!("fs_read() error: {}", e))),
5766 }
5767 });
5768
5769 define(interp, "fs_read_bytes", Some(1), |_, args| {
5771 let path = match &args[0] {
5772 Value::String(s) => s.to_string(),
5773 _ => return Err(RuntimeError::new("fs_read_bytes() requires string path")),
5774 };
5775
5776 match std::fs::read(&path) {
5777 Ok(bytes) => {
5778 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
5779 Ok(Value::Array(Rc::new(RefCell::new(values))))
5780 }
5781 Err(e) => Err(RuntimeError::new(format!("fs_read_bytes() error: {}", e))),
5782 }
5783 });
5784
5785 define(interp, "fs_write", Some(2), |_, args| {
5787 let path = match &args[0] {
5788 Value::String(s) => s.to_string(),
5789 _ => return Err(RuntimeError::new("fs_write() requires string path")),
5790 };
5791 let content = format!("{}", args[1]);
5792
5793 match std::fs::write(&path, content) {
5794 Ok(()) => Ok(Value::Null),
5795 Err(e) => Err(RuntimeError::new(format!("fs_write() error: {}", e))),
5796 }
5797 });
5798
5799 define(interp, "fs_append", Some(2), |_, args| {
5801 let path = match &args[0] {
5802 Value::String(s) => s.to_string(),
5803 _ => return Err(RuntimeError::new("fs_append() requires string path")),
5804 };
5805 let content = format!("{}", args[1]);
5806
5807 use std::fs::OpenOptions;
5808 match OpenOptions::new().append(true).create(true).open(&path) {
5809 Ok(mut file) => {
5810 use std::io::Write;
5811 match file.write_all(content.as_bytes()) {
5812 Ok(()) => Ok(Value::Null),
5813 Err(e) => Err(RuntimeError::new(format!("fs_append() write error: {}", e))),
5814 }
5815 }
5816 Err(e) => Err(RuntimeError::new(format!("fs_append() error: {}", e))),
5817 }
5818 });
5819
5820 define(interp, "fs_exists", Some(1), |_, args| {
5822 let path = match &args[0] {
5823 Value::String(s) => s.to_string(),
5824 _ => return Err(RuntimeError::new("fs_exists() requires string path")),
5825 };
5826 Ok(Value::Bool(std::path::Path::new(&path).exists()))
5827 });
5828
5829 define(interp, "fs_is_file", Some(1), |_, args| {
5831 let path = match &args[0] {
5832 Value::String(s) => s.to_string(),
5833 _ => return Err(RuntimeError::new("fs_is_file() requires string path")),
5834 };
5835 Ok(Value::Bool(std::path::Path::new(&path).is_file()))
5836 });
5837
5838 define(interp, "fs_is_dir", Some(1), |_, args| {
5840 let path = match &args[0] {
5841 Value::String(s) => s.to_string(),
5842 _ => return Err(RuntimeError::new("fs_is_dir() requires string path")),
5843 };
5844 Ok(Value::Bool(std::path::Path::new(&path).is_dir()))
5845 });
5846
5847 define(interp, "fs_mkdir", Some(1), |_, args| {
5849 let path = match &args[0] {
5850 Value::String(s) => s.to_string(),
5851 _ => return Err(RuntimeError::new("fs_mkdir() requires string path")),
5852 };
5853
5854 match std::fs::create_dir_all(&path) {
5855 Ok(()) => Ok(Value::Null),
5856 Err(e) => Err(RuntimeError::new(format!("fs_mkdir() error: {}", e))),
5857 }
5858 });
5859
5860 define(interp, "fs_remove", Some(1), |_, args| {
5862 let path = match &args[0] {
5863 Value::String(s) => s.to_string(),
5864 _ => return Err(RuntimeError::new("fs_remove() requires string path")),
5865 };
5866
5867 let p = std::path::Path::new(&path);
5868 let result = if p.is_dir() {
5869 std::fs::remove_dir_all(&path)
5870 } else {
5871 std::fs::remove_file(&path)
5872 };
5873
5874 match result {
5875 Ok(()) => Ok(Value::Null),
5876 Err(e) => Err(RuntimeError::new(format!("fs_remove() error: {}", e))),
5877 }
5878 });
5879
5880 define(interp, "fs_list", Some(1), |_, args| {
5882 let path = match &args[0] {
5883 Value::String(s) => s.to_string(),
5884 _ => return Err(RuntimeError::new("fs_list() requires string path")),
5885 };
5886
5887 match std::fs::read_dir(&path) {
5888 Ok(entries) => {
5889 let mut files = Vec::new();
5890 for entry in entries.flatten() {
5891 if let Some(name) = entry.file_name().to_str() {
5892 files.push(Value::String(Rc::new(name.to_string())));
5893 }
5894 }
5895 Ok(Value::Array(Rc::new(RefCell::new(files))))
5896 }
5897 Err(e) => Err(RuntimeError::new(format!("fs_list() error: {}", e))),
5898 }
5899 });
5900
5901 define(interp, "fs_copy", Some(2), |_, args| {
5903 let src = match &args[0] {
5904 Value::String(s) => s.to_string(),
5905 _ => return Err(RuntimeError::new("fs_copy() requires string source path")),
5906 };
5907 let dst = match &args[1] {
5908 Value::String(s) => s.to_string(),
5909 _ => {
5910 return Err(RuntimeError::new(
5911 "fs_copy() requires string destination path",
5912 ))
5913 }
5914 };
5915
5916 match std::fs::copy(&src, &dst) {
5917 Ok(bytes) => Ok(Value::Int(bytes as i64)),
5918 Err(e) => Err(RuntimeError::new(format!("fs_copy() error: {}", e))),
5919 }
5920 });
5921
5922 define(interp, "fs_rename", Some(2), |_, args| {
5924 let src = match &args[0] {
5925 Value::String(s) => s.to_string(),
5926 _ => return Err(RuntimeError::new("fs_rename() requires string source path")),
5927 };
5928 let dst = match &args[1] {
5929 Value::String(s) => s.to_string(),
5930 _ => {
5931 return Err(RuntimeError::new(
5932 "fs_rename() requires string destination path",
5933 ))
5934 }
5935 };
5936
5937 match std::fs::rename(&src, &dst) {
5938 Ok(()) => Ok(Value::Null),
5939 Err(e) => Err(RuntimeError::new(format!("fs_rename() error: {}", e))),
5940 }
5941 });
5942
5943 define(interp, "fs_size", Some(1), |_, args| {
5945 let path = match &args[0] {
5946 Value::String(s) => s.to_string(),
5947 _ => return Err(RuntimeError::new("fs_size() requires string path")),
5948 };
5949
5950 match std::fs::metadata(&path) {
5951 Ok(meta) => Ok(Value::Int(meta.len() as i64)),
5952 Err(e) => Err(RuntimeError::new(format!("fs_size() error: {}", e))),
5953 }
5954 });
5955
5956 define(interp, "path_join", None, |_, args| {
5958 let mut path = std::path::PathBuf::new();
5959 for arg in &args {
5960 match arg {
5961 Value::String(s) => path.push(s.as_str()),
5962 Value::Array(arr) => {
5963 for v in arr.borrow().iter() {
5964 if let Value::String(s) = v {
5965 path.push(s.as_str());
5966 }
5967 }
5968 }
5969 _ => {}
5970 }
5971 }
5972 Ok(Value::String(Rc::new(path.to_string_lossy().to_string())))
5973 });
5974
5975 define(interp, "path_parent", Some(1), |_, args| {
5977 let path = match &args[0] {
5978 Value::String(s) => s.to_string(),
5979 _ => return Err(RuntimeError::new("path_parent() requires string path")),
5980 };
5981
5982 let p = std::path::Path::new(&path);
5983 match p.parent() {
5984 Some(parent) => Ok(Value::String(Rc::new(parent.to_string_lossy().to_string()))),
5985 None => Ok(Value::Null),
5986 }
5987 });
5988
5989 define(interp, "path_filename", Some(1), |_, args| {
5991 let path = match &args[0] {
5992 Value::String(s) => s.to_string(),
5993 _ => return Err(RuntimeError::new("path_filename() requires string path")),
5994 };
5995
5996 let p = std::path::Path::new(&path);
5997 match p.file_name() {
5998 Some(name) => Ok(Value::String(Rc::new(name.to_string_lossy().to_string()))),
5999 None => Ok(Value::Null),
6000 }
6001 });
6002
6003 define(interp, "path_extension", Some(1), |_, args| {
6005 let path = match &args[0] {
6006 Value::String(s) => s.to_string(),
6007 _ => return Err(RuntimeError::new("path_extension() requires string path")),
6008 };
6009
6010 let p = std::path::Path::new(&path);
6011 match p.extension() {
6012 Some(ext) => Ok(Value::String(Rc::new(ext.to_string_lossy().to_string()))),
6013 None => Ok(Value::Null),
6014 }
6015 });
6016}
6017
6018fn register_crypto(interp: &mut Interpreter) {
6050 fn extract_bytes(v: &Value, fn_name: &str) -> Result<Vec<u8>, RuntimeError> {
6052 match v {
6053 Value::String(s) => Ok(s.as_bytes().to_vec()),
6054 Value::Array(arr) => {
6055 let arr = arr.borrow();
6056 Ok(arr
6057 .iter()
6058 .filter_map(|v| {
6059 if let Value::Int(n) = v {
6060 Some(*n as u8)
6061 } else {
6062 None
6063 }
6064 })
6065 .collect())
6066 }
6067 _ => Err(RuntimeError::new(format!(
6068 "{}() requires string or byte array",
6069 fn_name
6070 ))),
6071 }
6072 }
6073
6074 fn bytes_to_array(bytes: &[u8]) -> Value {
6075 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
6076 Value::Array(Rc::new(RefCell::new(values)))
6077 }
6078
6079 define(interp, "sha256", Some(1), |_, args| {
6085 let data = extract_bytes(&args[0], "sha256")?;
6086 let mut hasher = Sha256::new();
6087 hasher.update(&data);
6088 let result = hasher.finalize();
6089 Ok(Value::String(Rc::new(
6090 result.iter().map(|b| format!("{:02x}", b)).collect(),
6091 )))
6092 });
6093
6094 define(interp, "sha512", Some(1), |_, args| {
6096 let data = extract_bytes(&args[0], "sha512")?;
6097 let mut hasher = Sha512::new();
6098 hasher.update(&data);
6099 let result = hasher.finalize();
6100 Ok(Value::String(Rc::new(
6101 result.iter().map(|b| format!("{:02x}", b)).collect(),
6102 )))
6103 });
6104
6105 define(interp, "sha3_256", Some(1), |_, args| {
6107 use sha3::{Digest as Sha3Digest, Sha3_256};
6108 let data = extract_bytes(&args[0], "sha3_256")?;
6109 let mut hasher = Sha3_256::new();
6110 hasher.update(&data);
6111 let result = hasher.finalize();
6112 Ok(Value::String(Rc::new(
6113 result.iter().map(|b| format!("{:02x}", b)).collect(),
6114 )))
6115 });
6116
6117 define(interp, "sha3_512", Some(1), |_, args| {
6119 use sha3::{Digest as Sha3Digest, Sha3_512};
6120 let data = extract_bytes(&args[0], "sha3_512")?;
6121 let mut hasher = Sha3_512::new();
6122 hasher.update(&data);
6123 let result = hasher.finalize();
6124 Ok(Value::String(Rc::new(
6125 result.iter().map(|b| format!("{:02x}", b)).collect(),
6126 )))
6127 });
6128
6129 define(interp, "blake3", Some(1), |_, args| {
6131 let data = extract_bytes(&args[0], "blake3")?;
6132 let hash = blake3::hash(&data);
6133 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
6134 });
6135
6136 define(interp, "blake3_keyed", Some(2), |_, args| {
6138 let key = extract_bytes(&args[0], "blake3_keyed")?;
6139 let data = extract_bytes(&args[1], "blake3_keyed")?;
6140 if key.len() != 32 {
6141 return Err(RuntimeError::new("blake3_keyed() requires 32-byte key"));
6142 }
6143 let mut key_arr = [0u8; 32];
6144 key_arr.copy_from_slice(&key);
6145 let hash = blake3::keyed_hash(&key_arr, &data);
6146 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
6147 });
6148
6149 define(interp, "md5", Some(1), |_, args| {
6151 let data = extract_bytes(&args[0], "md5")?;
6152 let mut hasher = Md5::new();
6153 hasher.update(&data);
6154 let result = hasher.finalize();
6155 Ok(Value::String(Rc::new(
6156 result.iter().map(|b| format!("{:02x}", b)).collect(),
6157 )))
6158 });
6159
6160 define(interp, "aes_gcm_encrypt", Some(2), |_, args| {
6166 use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
6167 use rand::RngCore;
6168
6169 let key = extract_bytes(&args[0], "aes_gcm_encrypt")?;
6170 let plaintext = extract_bytes(&args[1], "aes_gcm_encrypt")?;
6171
6172 if key.len() != 32 {
6173 return Err(RuntimeError::new("aes_gcm_encrypt() requires 32-byte key"));
6174 }
6175
6176 let cipher = Aes256Gcm::new_from_slice(&key)
6177 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
6178
6179 let mut nonce_bytes = [0u8; 12];
6180 rand::thread_rng().fill_bytes(&mut nonce_bytes);
6181 let nonce = Nonce::from_slice(&nonce_bytes);
6182
6183 let ciphertext = cipher
6184 .encrypt(nonce, plaintext.as_ref())
6185 .map_err(|e| RuntimeError::new(format!("AES encryption error: {}", e)))?;
6186
6187 let mut result = HashMap::new();
6188 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
6189 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
6190 Ok(Value::Map(Rc::new(RefCell::new(result))))
6191 });
6192
6193 define(interp, "aes_gcm_decrypt", Some(3), |_, args| {
6195 use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
6196
6197 let key = extract_bytes(&args[0], "aes_gcm_decrypt")?;
6198 let ciphertext = extract_bytes(&args[1], "aes_gcm_decrypt")?;
6199 let nonce_bytes = extract_bytes(&args[2], "aes_gcm_decrypt")?;
6200
6201 if key.len() != 32 {
6202 return Err(RuntimeError::new("aes_gcm_decrypt() requires 32-byte key"));
6203 }
6204 if nonce_bytes.len() != 12 {
6205 return Err(RuntimeError::new(
6206 "aes_gcm_decrypt() requires 12-byte nonce",
6207 ));
6208 }
6209
6210 let cipher = Aes256Gcm::new_from_slice(&key)
6211 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
6212 let nonce = Nonce::from_slice(&nonce_bytes);
6213
6214 let plaintext = cipher
6215 .decrypt(nonce, ciphertext.as_ref())
6216 .map_err(|_| RuntimeError::new("AES-GCM decryption failed: authentication error"))?;
6217
6218 match String::from_utf8(plaintext.clone()) {
6219 Ok(s) => Ok(Value::String(Rc::new(s))),
6220 Err(_) => Ok(bytes_to_array(&plaintext)),
6221 }
6222 });
6223
6224 define(interp, "chacha20_encrypt", Some(2), |_, args| {
6226 use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
6227 use rand::RngCore;
6228
6229 let key = extract_bytes(&args[0], "chacha20_encrypt")?;
6230 let plaintext = extract_bytes(&args[1], "chacha20_encrypt")?;
6231
6232 if key.len() != 32 {
6233 return Err(RuntimeError::new("chacha20_encrypt() requires 32-byte key"));
6234 }
6235
6236 let cipher = ChaCha20Poly1305::new_from_slice(&key)
6237 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
6238
6239 let mut nonce_bytes = [0u8; 12];
6240 rand::thread_rng().fill_bytes(&mut nonce_bytes);
6241 let nonce = Nonce::from_slice(&nonce_bytes);
6242
6243 let ciphertext = cipher
6244 .encrypt(nonce, plaintext.as_ref())
6245 .map_err(|e| RuntimeError::new(format!("ChaCha20 encryption error: {}", e)))?;
6246
6247 let mut result = HashMap::new();
6248 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
6249 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
6250 Ok(Value::Map(Rc::new(RefCell::new(result))))
6251 });
6252
6253 define(interp, "chacha20_decrypt", Some(3), |_, args| {
6255 use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, KeyInit, Nonce};
6256
6257 let key = extract_bytes(&args[0], "chacha20_decrypt")?;
6258 let ciphertext = extract_bytes(&args[1], "chacha20_decrypt")?;
6259 let nonce_bytes = extract_bytes(&args[2], "chacha20_decrypt")?;
6260
6261 if key.len() != 32 {
6262 return Err(RuntimeError::new("chacha20_decrypt() requires 32-byte key"));
6263 }
6264 if nonce_bytes.len() != 12 {
6265 return Err(RuntimeError::new(
6266 "chacha20_decrypt() requires 12-byte nonce",
6267 ));
6268 }
6269
6270 let cipher = ChaCha20Poly1305::new_from_slice(&key)
6271 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
6272 let nonce = Nonce::from_slice(&nonce_bytes);
6273
6274 let plaintext = cipher
6275 .decrypt(nonce, ciphertext.as_ref())
6276 .map_err(|_| RuntimeError::new("ChaCha20 decryption failed: authentication error"))?;
6277
6278 match String::from_utf8(plaintext.clone()) {
6279 Ok(s) => Ok(Value::String(Rc::new(s))),
6280 Err(_) => Ok(bytes_to_array(&plaintext)),
6281 }
6282 });
6283
6284 define(interp, "ed25519_keygen", Some(0), |_, _| {
6290 use ed25519_dalek::SigningKey;
6291 use rand::rngs::OsRng;
6292
6293 let signing_key = SigningKey::generate(&mut OsRng);
6294 let verifying_key = signing_key.verifying_key();
6295
6296 let mut result = HashMap::new();
6297 result.insert(
6298 "private_key".to_string(),
6299 Value::String(Rc::new(
6300 signing_key
6301 .to_bytes()
6302 .iter()
6303 .map(|b| format!("{:02x}", b))
6304 .collect(),
6305 )),
6306 );
6307 result.insert(
6308 "public_key".to_string(),
6309 Value::String(Rc::new(
6310 verifying_key
6311 .to_bytes()
6312 .iter()
6313 .map(|b| format!("{:02x}", b))
6314 .collect(),
6315 )),
6316 );
6317 Ok(Value::Map(Rc::new(RefCell::new(result))))
6318 });
6319
6320 define(interp, "ed25519_sign", Some(2), |_, args| {
6322 use ed25519_dalek::{Signer, SigningKey};
6323
6324 let private_key_hex = match &args[0] {
6325 Value::String(s) => s.to_string(),
6326 _ => return Err(RuntimeError::new("ed25519_sign() requires hex private key")),
6327 };
6328 let message = extract_bytes(&args[1], "ed25519_sign")?;
6329
6330 let key_bytes: Vec<u8> = (0..private_key_hex.len())
6331 .step_by(2)
6332 .map(|i| u8::from_str_radix(&private_key_hex[i..i + 2], 16))
6333 .collect::<Result<Vec<_>, _>>()
6334 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
6335
6336 if key_bytes.len() != 32 {
6337 return Err(RuntimeError::new(
6338 "ed25519_sign() requires 32-byte private key",
6339 ));
6340 }
6341
6342 let mut key_arr = [0u8; 32];
6343 key_arr.copy_from_slice(&key_bytes);
6344 let signing_key = SigningKey::from_bytes(&key_arr);
6345 let signature = signing_key.sign(&message);
6346
6347 Ok(Value::String(Rc::new(
6348 signature
6349 .to_bytes()
6350 .iter()
6351 .map(|b| format!("{:02x}", b))
6352 .collect(),
6353 )))
6354 });
6355
6356 define(interp, "ed25519_verify", Some(3), |_, args| {
6358 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
6359
6360 let public_key_hex = match &args[0] {
6361 Value::String(s) => s.to_string(),
6362 _ => {
6363 return Err(RuntimeError::new(
6364 "ed25519_verify() requires hex public key",
6365 ))
6366 }
6367 };
6368 let message = extract_bytes(&args[1], "ed25519_verify")?;
6369 let signature_hex = match &args[2] {
6370 Value::String(s) => s.to_string(),
6371 _ => return Err(RuntimeError::new("ed25519_verify() requires hex signature")),
6372 };
6373
6374 let key_bytes: Vec<u8> = (0..public_key_hex.len())
6375 .step_by(2)
6376 .map(|i| u8::from_str_radix(&public_key_hex[i..i + 2], 16))
6377 .collect::<Result<Vec<_>, _>>()
6378 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
6379 let sig_bytes: Vec<u8> = (0..signature_hex.len())
6380 .step_by(2)
6381 .map(|i| u8::from_str_radix(&signature_hex[i..i + 2], 16))
6382 .collect::<Result<Vec<_>, _>>()
6383 .map_err(|_| RuntimeError::new("Invalid signature hex"))?;
6384
6385 if key_bytes.len() != 32 {
6386 return Err(RuntimeError::new(
6387 "ed25519_verify() requires 32-byte public key",
6388 ));
6389 }
6390 if sig_bytes.len() != 64 {
6391 return Err(RuntimeError::new(
6392 "ed25519_verify() requires 64-byte signature",
6393 ));
6394 }
6395
6396 let mut key_arr = [0u8; 32];
6397 key_arr.copy_from_slice(&key_bytes);
6398 let mut sig_arr = [0u8; 64];
6399 sig_arr.copy_from_slice(&sig_bytes);
6400
6401 let verifying_key = VerifyingKey::from_bytes(&key_arr)
6402 .map_err(|e| RuntimeError::new(format!("Invalid public key: {}", e)))?;
6403 let signature = Signature::from_bytes(&sig_arr);
6404
6405 match verifying_key.verify(&message, &signature) {
6406 Ok(_) => Ok(Value::Bool(true)),
6407 Err(_) => Ok(Value::Bool(false)),
6408 }
6409 });
6410
6411 define(interp, "x25519_keygen", Some(0), |_, _| {
6413 use rand::rngs::OsRng;
6414 use x25519_dalek::{PublicKey, StaticSecret};
6415
6416 let secret = StaticSecret::random_from_rng(OsRng);
6417 let public = PublicKey::from(&secret);
6418
6419 let mut result = HashMap::new();
6420 result.insert(
6421 "private_key".to_string(),
6422 Value::String(Rc::new(
6423 secret
6424 .as_bytes()
6425 .iter()
6426 .map(|b| format!("{:02x}", b))
6427 .collect(),
6428 )),
6429 );
6430 result.insert(
6431 "public_key".to_string(),
6432 Value::String(Rc::new(
6433 public
6434 .as_bytes()
6435 .iter()
6436 .map(|b| format!("{:02x}", b))
6437 .collect(),
6438 )),
6439 );
6440 Ok(Value::Map(Rc::new(RefCell::new(result))))
6441 });
6442
6443 define(interp, "x25519_exchange", Some(2), |_, args| {
6445 use x25519_dalek::{PublicKey, StaticSecret};
6446
6447 let my_private_hex = match &args[0] {
6448 Value::String(s) => s.to_string(),
6449 _ => {
6450 return Err(RuntimeError::new(
6451 "x25519_exchange() requires hex private key",
6452 ))
6453 }
6454 };
6455 let their_public_hex = match &args[1] {
6456 Value::String(s) => s.to_string(),
6457 _ => {
6458 return Err(RuntimeError::new(
6459 "x25519_exchange() requires hex public key",
6460 ))
6461 }
6462 };
6463
6464 let my_private_bytes: Vec<u8> = (0..my_private_hex.len())
6465 .step_by(2)
6466 .map(|i| u8::from_str_radix(&my_private_hex[i..i + 2], 16))
6467 .collect::<Result<Vec<_>, _>>()
6468 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
6469 let their_public_bytes: Vec<u8> = (0..their_public_hex.len())
6470 .step_by(2)
6471 .map(|i| u8::from_str_radix(&their_public_hex[i..i + 2], 16))
6472 .collect::<Result<Vec<_>, _>>()
6473 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
6474
6475 if my_private_bytes.len() != 32 || their_public_bytes.len() != 32 {
6476 return Err(RuntimeError::new("x25519_exchange() requires 32-byte keys"));
6477 }
6478
6479 let mut priv_arr = [0u8; 32];
6480 priv_arr.copy_from_slice(&my_private_bytes);
6481 let mut pub_arr = [0u8; 32];
6482 pub_arr.copy_from_slice(&their_public_bytes);
6483
6484 let my_secret = StaticSecret::from(priv_arr);
6485 let their_public = PublicKey::from(pub_arr);
6486 let shared_secret = my_secret.diffie_hellman(&their_public);
6487
6488 Ok(Value::String(Rc::new(
6489 shared_secret
6490 .as_bytes()
6491 .iter()
6492 .map(|b| format!("{:02x}", b))
6493 .collect(),
6494 )))
6495 });
6496
6497 define(interp, "argon2_hash", Some(1), |_, args| {
6503 use argon2::{
6504 password_hash::{PasswordHasher, SaltString},
6505 Argon2,
6506 };
6507 use rand::rngs::OsRng;
6508
6509 let password = extract_bytes(&args[0], "argon2_hash")?;
6510 let salt = SaltString::generate(&mut OsRng);
6511 let argon2 = Argon2::default();
6512
6513 let hash = argon2
6514 .hash_password(&password, &salt)
6515 .map_err(|e| RuntimeError::new(format!("Argon2 error: {}", e)))?;
6516
6517 let mut result = HashMap::new();
6518 result.insert("hash".to_string(), Value::String(Rc::new(hash.to_string())));
6519 result.insert("salt".to_string(), Value::String(Rc::new(salt.to_string())));
6520 Ok(Value::Map(Rc::new(RefCell::new(result))))
6521 });
6522
6523 define(interp, "argon2_verify", Some(2), |_, args| {
6525 use argon2::{Argon2, PasswordHash, PasswordVerifier};
6526
6527 let password = extract_bytes(&args[0], "argon2_verify")?;
6528 let hash_str = match &args[1] {
6529 Value::String(s) => s.to_string(),
6530 _ => return Err(RuntimeError::new("argon2_verify() requires hash string")),
6531 };
6532
6533 let parsed_hash = PasswordHash::new(&hash_str)
6534 .map_err(|e| RuntimeError::new(format!("Invalid hash: {}", e)))?;
6535
6536 match Argon2::default().verify_password(&password, &parsed_hash) {
6537 Ok(_) => Ok(Value::Bool(true)),
6538 Err(_) => Ok(Value::Bool(false)),
6539 }
6540 });
6541
6542 define(interp, "hkdf_expand", Some(3), |_, args| {
6544 use hkdf::Hkdf;
6545
6546 let ikm = extract_bytes(&args[0], "hkdf_expand")?;
6547 let salt = extract_bytes(&args[1], "hkdf_expand")?;
6548 let info = extract_bytes(&args[2], "hkdf_expand")?;
6549
6550 let hk = Hkdf::<Sha256>::new(Some(&salt), &ikm);
6551 let mut okm = [0u8; 32];
6552 hk.expand(&info, &mut okm)
6553 .map_err(|e| RuntimeError::new(format!("HKDF error: {}", e)))?;
6554
6555 Ok(Value::String(Rc::new(
6556 okm.iter().map(|b| format!("{:02x}", b)).collect(),
6557 )))
6558 });
6559
6560 define(interp, "pbkdf2_derive", Some(3), |_, args| {
6562 let password = extract_bytes(&args[0], "pbkdf2_derive")?;
6563 let salt = extract_bytes(&args[1], "pbkdf2_derive")?;
6564 let iterations = match &args[2] {
6565 Value::Int(n) => *n as u32,
6566 _ => {
6567 return Err(RuntimeError::new(
6568 "pbkdf2_derive() requires integer iterations",
6569 ))
6570 }
6571 };
6572
6573 let mut key = [0u8; 32];
6574 pbkdf2::pbkdf2_hmac::<Sha256>(&password, &salt, iterations, &mut key);
6575 Ok(Value::String(Rc::new(
6576 key.iter().map(|b| format!("{:02x}", b)).collect(),
6577 )))
6578 });
6579
6580 define(interp, "hmac_sha256", Some(2), |_, args| {
6586 use hmac::{Hmac, Mac};
6587 type HmacSha256 = Hmac<Sha256>;
6588
6589 let key = extract_bytes(&args[0], "hmac_sha256")?;
6590 let message = extract_bytes(&args[1], "hmac_sha256")?;
6591
6592 let mut mac = HmacSha256::new_from_slice(&key)
6593 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
6594 mac.update(&message);
6595 let result = mac.finalize();
6596 Ok(Value::String(Rc::new(
6597 result
6598 .into_bytes()
6599 .iter()
6600 .map(|b| format!("{:02x}", b))
6601 .collect(),
6602 )))
6603 });
6604
6605 define(interp, "hmac_sha512", Some(2), |_, args| {
6607 use hmac::{Hmac, Mac};
6608 type HmacSha512 = Hmac<Sha512>;
6609
6610 let key = extract_bytes(&args[0], "hmac_sha512")?;
6611 let message = extract_bytes(&args[1], "hmac_sha512")?;
6612
6613 let mut mac = HmacSha512::new_from_slice(&key)
6614 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
6615 mac.update(&message);
6616 let result = mac.finalize();
6617 Ok(Value::String(Rc::new(
6618 result
6619 .into_bytes()
6620 .iter()
6621 .map(|b| format!("{:02x}", b))
6622 .collect(),
6623 )))
6624 });
6625
6626 define(interp, "hmac_verify", Some(3), |_, args| {
6628 use hmac::{Hmac, Mac};
6629 type HmacSha256 = Hmac<Sha256>;
6630
6631 let key = extract_bytes(&args[0], "hmac_verify")?;
6632 let message = extract_bytes(&args[1], "hmac_verify")?;
6633 let expected_hex = match &args[2] {
6634 Value::String(s) => s.to_string(),
6635 _ => return Err(RuntimeError::new("hmac_verify() requires hex MAC")),
6636 };
6637
6638 let expected: Vec<u8> = (0..expected_hex.len())
6639 .step_by(2)
6640 .map(|i| u8::from_str_radix(&expected_hex[i..i + 2], 16))
6641 .collect::<Result<Vec<_>, _>>()
6642 .map_err(|_| RuntimeError::new("Invalid MAC hex"))?;
6643
6644 let mut mac = HmacSha256::new_from_slice(&key)
6645 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
6646 mac.update(&message);
6647
6648 match mac.verify_slice(&expected) {
6649 Ok(_) => Ok(Value::Bool(true)),
6650 Err(_) => Ok(Value::Bool(false)),
6651 }
6652 });
6653
6654 define(interp, "secure_random_bytes", Some(1), |_, args| {
6660 use rand::RngCore;
6661
6662 let length = match &args[0] {
6663 Value::Int(n) => *n as usize,
6664 _ => {
6665 return Err(RuntimeError::new(
6666 "secure_random_bytes() requires integer length",
6667 ))
6668 }
6669 };
6670
6671 if length > 1024 * 1024 {
6672 return Err(RuntimeError::new("secure_random_bytes() max 1MB"));
6673 }
6674
6675 let mut bytes = vec![0u8; length];
6676 rand::thread_rng().fill_bytes(&mut bytes);
6677 Ok(bytes_to_array(&bytes))
6678 });
6679
6680 define(interp, "secure_random_hex", Some(1), |_, args| {
6682 use rand::RngCore;
6683
6684 let byte_length = match &args[0] {
6685 Value::Int(n) => *n as usize,
6686 _ => {
6687 return Err(RuntimeError::new(
6688 "secure_random_hex() requires integer length",
6689 ))
6690 }
6691 };
6692
6693 if byte_length > 1024 * 1024 {
6694 return Err(RuntimeError::new("secure_random_hex() max 1MB"));
6695 }
6696
6697 let mut bytes = vec![0u8; byte_length];
6698 rand::thread_rng().fill_bytes(&mut bytes);
6699 Ok(Value::String(Rc::new(
6700 bytes.iter().map(|b| format!("{:02x}", b)).collect(),
6701 )))
6702 });
6703
6704 define(interp, "generate_key", Some(1), |_, args| {
6706 use rand::RngCore;
6707
6708 let bits = match &args[0] {
6709 Value::Int(n) => *n as usize,
6710 _ => return Err(RuntimeError::new("generate_key() requires bit length")),
6711 };
6712
6713 if bits % 8 != 0 {
6714 return Err(RuntimeError::new(
6715 "generate_key() bit length must be multiple of 8",
6716 ));
6717 }
6718 if bits > 512 {
6719 return Err(RuntimeError::new("generate_key() max 512 bits"));
6720 }
6721
6722 let bytes = bits / 8;
6723 let mut key = vec![0u8; bytes];
6724 rand::thread_rng().fill_bytes(&mut key);
6725 Ok(Value::String(Rc::new(
6726 key.iter().map(|b| format!("{:02x}", b)).collect(),
6727 )))
6728 });
6729
6730 define(interp, "base64_encode", Some(1), |_, args| {
6736 let data = extract_bytes(&args[0], "base64_encode")?;
6737 Ok(Value::String(Rc::new(
6738 general_purpose::STANDARD.encode(&data),
6739 )))
6740 });
6741
6742 define(interp, "base64_decode", Some(1), |_, args| {
6744 let encoded = match &args[0] {
6745 Value::String(s) => s.to_string(),
6746 _ => return Err(RuntimeError::new("base64_decode() requires string")),
6747 };
6748
6749 match general_purpose::STANDARD.decode(&encoded) {
6750 Ok(bytes) => match String::from_utf8(bytes.clone()) {
6751 Ok(s) => Ok(Value::String(Rc::new(s))),
6752 Err(_) => Ok(bytes_to_array(&bytes)),
6753 },
6754 Err(e) => Err(RuntimeError::new(format!("base64_decode() error: {}", e))),
6755 }
6756 });
6757
6758 define(interp, "hex_encode", Some(1), |_, args| {
6760 let data = extract_bytes(&args[0], "hex_encode")?;
6761 Ok(Value::String(Rc::new(
6762 data.iter().map(|b| format!("{:02x}", b)).collect(),
6763 )))
6764 });
6765
6766 define(interp, "hex_decode", Some(1), |_, args| {
6768 let hex_str = match &args[0] {
6769 Value::String(s) => s.to_string(),
6770 _ => return Err(RuntimeError::new("hex_decode() requires string")),
6771 };
6772
6773 let hex_str = hex_str.trim();
6774 if hex_str.len() % 2 != 0 {
6775 return Err(RuntimeError::new(
6776 "hex_decode() requires even-length hex string",
6777 ));
6778 }
6779
6780 let bytes: Vec<Value> = (0..hex_str.len())
6781 .step_by(2)
6782 .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).map(|b| Value::Int(b as i64)))
6783 .collect::<Result<Vec<_>, _>>()
6784 .map_err(|_| RuntimeError::new("hex_decode() invalid hex"))?;
6785 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
6786 });
6787
6788 define(interp, "constant_time_eq", Some(2), |_, args| {
6794 let a = extract_bytes(&args[0], "constant_time_eq")?;
6795 let b = extract_bytes(&args[1], "constant_time_eq")?;
6796
6797 if a.len() != b.len() {
6798 return Ok(Value::Bool(false));
6799 }
6800
6801 let mut result = 0u8;
6802 for (x, y) in a.iter().zip(b.iter()) {
6803 result |= x ^ y;
6804 }
6805 Ok(Value::Bool(result == 0))
6806 });
6807
6808 define(interp, "crypto_info", Some(0), |_, _| {
6814 let mut info = HashMap::new();
6815 info.insert(
6816 "version".to_string(),
6817 Value::String(Rc::new("2.0".to_string())),
6818 );
6819 info.insert(
6820 "phase".to_string(),
6821 Value::String(Rc::new("Evidential Cryptography".to_string())),
6822 );
6823
6824 let capabilities = vec![
6825 "sha256",
6826 "sha512",
6827 "sha3_256",
6828 "sha3_512",
6829 "blake3",
6830 "md5",
6831 "aes_gcm_encrypt",
6832 "aes_gcm_decrypt",
6833 "chacha20_encrypt",
6834 "chacha20_decrypt",
6835 "ed25519_keygen",
6836 "ed25519_sign",
6837 "ed25519_verify",
6838 "x25519_keygen",
6839 "x25519_exchange",
6840 "argon2_hash",
6841 "argon2_verify",
6842 "hkdf_expand",
6843 "pbkdf2_derive",
6844 "hmac_sha256",
6845 "hmac_sha512",
6846 "hmac_verify",
6847 "secure_random_bytes",
6848 "secure_random_hex",
6849 "generate_key",
6850 "base64_encode",
6851 "base64_decode",
6852 "hex_encode",
6853 "hex_decode",
6854 "constant_time_eq",
6855 ];
6856 let cap_values: Vec<Value> = capabilities
6857 .iter()
6858 .map(|s| Value::String(Rc::new(s.to_string())))
6859 .collect();
6860 info.insert(
6861 "functions".to_string(),
6862 Value::Array(Rc::new(RefCell::new(cap_values))),
6863 );
6864
6865 Ok(Value::Map(Rc::new(RefCell::new(info))))
6866 });
6867}
6868
6869fn register_regex(interp: &mut Interpreter) {
6874 define(interp, "regex_match", Some(2), |_, args| {
6876 let pattern = match &args[0] {
6877 Value::String(s) => s.to_string(),
6878 _ => return Err(RuntimeError::new("regex_match() requires string pattern")),
6879 };
6880 let text = match &args[1] {
6881 Value::String(s) => s.to_string(),
6882 _ => return Err(RuntimeError::new("regex_match() requires string text")),
6883 };
6884
6885 match Regex::new(&pattern) {
6886 Ok(re) => Ok(Value::Bool(re.is_match(&text))),
6887 Err(e) => Err(RuntimeError::new(format!(
6888 "regex_match() invalid pattern: {}",
6889 e
6890 ))),
6891 }
6892 });
6893
6894 define(interp, "regex_find", Some(2), |_, args| {
6896 let pattern = match &args[0] {
6897 Value::String(s) => s.to_string(),
6898 _ => return Err(RuntimeError::new("regex_find() requires string pattern")),
6899 };
6900 let text = match &args[1] {
6901 Value::String(s) => s.to_string(),
6902 _ => return Err(RuntimeError::new("regex_find() requires string text")),
6903 };
6904
6905 match Regex::new(&pattern) {
6906 Ok(re) => match re.find(&text) {
6907 Some(m) => Ok(Value::String(Rc::new(m.as_str().to_string()))),
6908 None => Ok(Value::Null),
6909 },
6910 Err(e) => Err(RuntimeError::new(format!(
6911 "regex_find() invalid pattern: {}",
6912 e
6913 ))),
6914 }
6915 });
6916
6917 define(interp, "regex_find_all", Some(2), |_, args| {
6919 let pattern = match &args[0] {
6920 Value::String(s) => s.to_string(),
6921 _ => {
6922 return Err(RuntimeError::new(
6923 "regex_find_all() requires string pattern",
6924 ))
6925 }
6926 };
6927 let text = match &args[1] {
6928 Value::String(s) => s.to_string(),
6929 _ => return Err(RuntimeError::new("regex_find_all() requires string text")),
6930 };
6931
6932 match Regex::new(&pattern) {
6933 Ok(re) => {
6934 let matches: Vec<Value> = re
6935 .find_iter(&text)
6936 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
6937 .collect();
6938 Ok(Value::Array(Rc::new(RefCell::new(matches))))
6939 }
6940 Err(e) => Err(RuntimeError::new(format!(
6941 "regex_find_all() invalid pattern: {}",
6942 e
6943 ))),
6944 }
6945 });
6946
6947 define(interp, "regex_replace", Some(3), |_, args| {
6949 let pattern = match &args[0] {
6950 Value::String(s) => s.to_string(),
6951 _ => return Err(RuntimeError::new("regex_replace() requires string pattern")),
6952 };
6953 let text = match &args[1] {
6954 Value::String(s) => s.to_string(),
6955 _ => return Err(RuntimeError::new("regex_replace() requires string text")),
6956 };
6957 let replacement = match &args[2] {
6958 Value::String(s) => s.to_string(),
6959 _ => {
6960 return Err(RuntimeError::new(
6961 "regex_replace() requires string replacement",
6962 ))
6963 }
6964 };
6965
6966 match Regex::new(&pattern) {
6967 Ok(re) => {
6968 let result = re.replace(&text, replacement.as_str());
6969 Ok(Value::String(Rc::new(result.to_string())))
6970 }
6971 Err(e) => Err(RuntimeError::new(format!(
6972 "regex_replace() invalid pattern: {}",
6973 e
6974 ))),
6975 }
6976 });
6977
6978 define(interp, "regex_replace_all", Some(3), |_, args| {
6980 let pattern = match &args[0] {
6981 Value::String(s) => s.to_string(),
6982 _ => {
6983 return Err(RuntimeError::new(
6984 "regex_replace_all() requires string pattern",
6985 ))
6986 }
6987 };
6988 let text = match &args[1] {
6989 Value::String(s) => s.to_string(),
6990 _ => {
6991 return Err(RuntimeError::new(
6992 "regex_replace_all() requires string text",
6993 ))
6994 }
6995 };
6996 let replacement = match &args[2] {
6997 Value::String(s) => s.to_string(),
6998 _ => {
6999 return Err(RuntimeError::new(
7000 "regex_replace_all() requires string replacement",
7001 ))
7002 }
7003 };
7004
7005 match Regex::new(&pattern) {
7006 Ok(re) => {
7007 let result = re.replace_all(&text, replacement.as_str());
7008 Ok(Value::String(Rc::new(result.to_string())))
7009 }
7010 Err(e) => Err(RuntimeError::new(format!(
7011 "regex_replace_all() invalid pattern: {}",
7012 e
7013 ))),
7014 }
7015 });
7016
7017 define(interp, "regex_split", Some(2), |_, args| {
7019 let pattern = match &args[0] {
7020 Value::String(s) => s.to_string(),
7021 _ => return Err(RuntimeError::new("regex_split() requires string pattern")),
7022 };
7023 let text = match &args[1] {
7024 Value::String(s) => s.to_string(),
7025 _ => return Err(RuntimeError::new("regex_split() requires string text")),
7026 };
7027
7028 match Regex::new(&pattern) {
7029 Ok(re) => {
7030 let parts: Vec<Value> = re
7031 .split(&text)
7032 .map(|s| Value::String(Rc::new(s.to_string())))
7033 .collect();
7034 Ok(Value::Array(Rc::new(RefCell::new(parts))))
7035 }
7036 Err(e) => Err(RuntimeError::new(format!(
7037 "regex_split() invalid pattern: {}",
7038 e
7039 ))),
7040 }
7041 });
7042
7043 define(interp, "regex_captures", Some(2), |_, args| {
7045 let pattern = match &args[0] {
7046 Value::String(s) => s.to_string(),
7047 _ => {
7048 return Err(RuntimeError::new(
7049 "regex_captures() requires string pattern",
7050 ))
7051 }
7052 };
7053 let text = match &args[1] {
7054 Value::String(s) => s.to_string(),
7055 _ => return Err(RuntimeError::new("regex_captures() requires string text")),
7056 };
7057
7058 match Regex::new(&pattern) {
7059 Ok(re) => match re.captures(&text) {
7060 Some(caps) => {
7061 let captures: Vec<Value> = caps
7062 .iter()
7063 .map(|m| {
7064 m.map(|m| Value::String(Rc::new(m.as_str().to_string())))
7065 .unwrap_or(Value::Null)
7066 })
7067 .collect();
7068 Ok(Value::Array(Rc::new(RefCell::new(captures))))
7069 }
7070 None => Ok(Value::Null),
7071 },
7072 Err(e) => Err(RuntimeError::new(format!(
7073 "regex_captures() invalid pattern: {}",
7074 e
7075 ))),
7076 }
7077 });
7078}
7079
7080fn register_uuid(interp: &mut Interpreter) {
7085 define(interp, "uuid_v4", Some(0), |_, _| {
7087 let id = Uuid::new_v4();
7088 Ok(Value::String(Rc::new(id.to_string())))
7089 });
7090
7091 define(interp, "uuid_nil", Some(0), |_, _| {
7093 Ok(Value::String(Rc::new(Uuid::nil().to_string())))
7094 });
7095
7096 define(interp, "uuid_parse", Some(1), |_, args| {
7098 let s = match &args[0] {
7099 Value::String(s) => s.to_string(),
7100 _ => return Err(RuntimeError::new("uuid_parse() requires string")),
7101 };
7102
7103 match Uuid::parse_str(&s) {
7104 Ok(id) => Ok(Value::String(Rc::new(id.to_string()))),
7105 Err(e) => Err(RuntimeError::new(format!("uuid_parse() error: {}", e))),
7106 }
7107 });
7108
7109 define(interp, "uuid_is_valid", Some(1), |_, args| {
7111 let s = match &args[0] {
7112 Value::String(s) => s.to_string(),
7113 _ => return Ok(Value::Bool(false)),
7114 };
7115 Ok(Value::Bool(Uuid::parse_str(&s).is_ok()))
7116 });
7117}
7118
7119fn register_system(interp: &mut Interpreter) {
7124 define(interp, "env_get", Some(1), |_, args| {
7126 let key = match &args[0] {
7127 Value::String(s) => s.to_string(),
7128 _ => return Err(RuntimeError::new("env_get() requires string key")),
7129 };
7130
7131 match std::env::var(&key) {
7132 Ok(val) => Ok(Value::String(Rc::new(val))),
7133 Err(_) => Ok(Value::Null),
7134 }
7135 });
7136
7137 define(interp, "env_set", Some(2), |_, args| {
7139 let key = match &args[0] {
7140 Value::String(s) => s.to_string(),
7141 _ => return Err(RuntimeError::new("env_set() requires string key")),
7142 };
7143 let val = match &args[1] {
7144 Value::String(s) => s.to_string(),
7145 _ => format!("{}", args[1]),
7146 };
7147
7148 std::env::set_var(&key, &val);
7149 Ok(Value::Null)
7150 });
7151
7152 define(interp, "env_remove", Some(1), |_, args| {
7154 let key = match &args[0] {
7155 Value::String(s) => s.to_string(),
7156 _ => return Err(RuntimeError::new("env_remove() requires string key")),
7157 };
7158
7159 std::env::remove_var(&key);
7160 Ok(Value::Null)
7161 });
7162
7163 define(interp, "env_vars", Some(0), |_, _| {
7165 let mut map = HashMap::new();
7166 for (key, val) in std::env::vars() {
7167 map.insert(key, Value::String(Rc::new(val)));
7168 }
7169 Ok(Value::Map(Rc::new(RefCell::new(map))))
7170 });
7171
7172 define(interp, "args", Some(0), |_, _| {
7174 let args: Vec<Value> = std::env::args()
7175 .map(|s| Value::String(Rc::new(s)))
7176 .collect();
7177 Ok(Value::Array(Rc::new(RefCell::new(args))))
7178 });
7179
7180 define(interp, "cwd", Some(0), |_, _| {
7182 match std::env::current_dir() {
7183 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
7184 Err(e) => Err(RuntimeError::new(format!("cwd() error: {}", e))),
7185 }
7186 });
7187
7188 define(interp, "chdir", Some(1), |_, args| {
7190 let path = match &args[0] {
7191 Value::String(s) => s.to_string(),
7192 _ => return Err(RuntimeError::new("chdir() requires string path")),
7193 };
7194
7195 match std::env::set_current_dir(&path) {
7196 Ok(()) => Ok(Value::Null),
7197 Err(e) => Err(RuntimeError::new(format!("chdir() error: {}", e))),
7198 }
7199 });
7200
7201 define(interp, "hostname", Some(0), |_, _| {
7203 match std::fs::read_to_string("/etc/hostname") {
7205 Ok(name) => Ok(Value::String(Rc::new(name.trim().to_string()))),
7206 Err(_) => Ok(Value::String(Rc::new("unknown".to_string()))),
7207 }
7208 });
7209
7210 define(interp, "pid", Some(0), |_, _| {
7212 Ok(Value::Int(std::process::id() as i64))
7213 });
7214
7215 define(interp, "exit", Some(1), |_, args| {
7217 let code = match &args[0] {
7218 Value::Int(n) => *n as i32,
7219 _ => 0,
7220 };
7221 std::process::exit(code);
7222 });
7223
7224 define(interp, "shell", Some(1), |_, args| {
7226 let cmd = match &args[0] {
7227 Value::String(s) => s.to_string(),
7228 _ => return Err(RuntimeError::new("shell() requires string command")),
7229 };
7230
7231 match std::process::Command::new("sh")
7232 .arg("-c")
7233 .arg(&cmd)
7234 .output()
7235 {
7236 Ok(output) => {
7237 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
7238 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
7239 let code = output.status.code().unwrap_or(-1);
7240
7241 let mut result = HashMap::new();
7242 result.insert("stdout".to_string(), Value::String(Rc::new(stdout)));
7243 result.insert("stderr".to_string(), Value::String(Rc::new(stderr)));
7244 result.insert("code".to_string(), Value::Int(code as i64));
7245 result.insert("success".to_string(), Value::Bool(output.status.success()));
7246
7247 Ok(Value::Map(Rc::new(RefCell::new(result))))
7248 }
7249 Err(e) => Err(RuntimeError::new(format!("shell() error: {}", e))),
7250 }
7251 });
7252
7253 define(interp, "platform", Some(0), |_, _| {
7255 Ok(Value::String(Rc::new(std::env::consts::OS.to_string())))
7256 });
7257
7258 define(interp, "arch", Some(0), |_, _| {
7260 Ok(Value::String(Rc::new(std::env::consts::ARCH.to_string())))
7261 });
7262}
7263
7264fn register_stats(interp: &mut Interpreter) {
7269 fn extract_numbers(val: &Value) -> Result<Vec<f64>, RuntimeError> {
7271 match val {
7272 Value::Array(arr) => {
7273 let arr = arr.borrow();
7274 let mut nums = Vec::new();
7275 for v in arr.iter() {
7276 match v {
7277 Value::Int(n) => nums.push(*n as f64),
7278 Value::Float(f) => nums.push(*f),
7279 _ => {
7280 return Err(RuntimeError::new("stats functions require numeric array"))
7281 }
7282 }
7283 }
7284 Ok(nums)
7285 }
7286 _ => Err(RuntimeError::new("stats functions require array")),
7287 }
7288 }
7289
7290 define(interp, "mean", Some(1), |_, args| {
7292 let nums = extract_numbers(&args[0])?;
7293 if nums.is_empty() {
7294 return Ok(Value::Float(0.0));
7295 }
7296 let sum: f64 = nums.iter().sum();
7297 Ok(Value::Float(sum / nums.len() as f64))
7298 });
7299
7300 define(interp, "median", Some(1), |_, args| {
7302 let mut nums = extract_numbers(&args[0])?;
7303 if nums.is_empty() {
7304 return Ok(Value::Float(0.0));
7305 }
7306 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
7307 let len = nums.len();
7308 if len % 2 == 0 {
7309 Ok(Value::Float((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
7310 } else {
7311 Ok(Value::Float(nums[len / 2]))
7312 }
7313 });
7314
7315 define(interp, "mode", Some(1), |_, args| {
7317 let nums = extract_numbers(&args[0])?;
7318 if nums.is_empty() {
7319 return Ok(Value::Null);
7320 }
7321
7322 let mut counts: HashMap<String, usize> = HashMap::new();
7323 for n in &nums {
7324 let key = format!("{:.10}", n);
7325 *counts.entry(key).or_insert(0) += 1;
7326 }
7327
7328 let max_count = counts.values().max().unwrap_or(&0);
7329 for n in &nums {
7330 let key = format!("{:.10}", n);
7331 if counts.get(&key) == Some(max_count) {
7332 return Ok(Value::Float(*n));
7333 }
7334 }
7335 Ok(Value::Null)
7336 });
7337
7338 define(interp, "variance", Some(1), |_, args| {
7340 let nums = extract_numbers(&args[0])?;
7341 if nums.is_empty() {
7342 return Ok(Value::Float(0.0));
7343 }
7344 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
7345 let variance: f64 =
7346 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
7347 Ok(Value::Float(variance))
7348 });
7349
7350 define(interp, "stddev", Some(1), |_, args| {
7352 let nums = extract_numbers(&args[0])?;
7353 if nums.is_empty() {
7354 return Ok(Value::Float(0.0));
7355 }
7356 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
7357 let variance: f64 =
7358 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
7359 Ok(Value::Float(variance.sqrt()))
7360 });
7361
7362 define(interp, "percentile", Some(2), |_, args| {
7364 let mut nums = extract_numbers(&args[0])?;
7365 let p = match &args[1] {
7366 Value::Int(n) => *n as f64,
7367 Value::Float(f) => *f,
7368 _ => {
7369 return Err(RuntimeError::new(
7370 "percentile() requires numeric percentile",
7371 ))
7372 }
7373 };
7374
7375 if nums.is_empty() {
7376 return Ok(Value::Float(0.0));
7377 }
7378
7379 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
7380 let idx = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
7381 Ok(Value::Float(nums[idx.min(nums.len() - 1)]))
7382 });
7383
7384 define(interp, "correlation", Some(2), |_, args| {
7386 let x = extract_numbers(&args[0])?;
7387 let y = extract_numbers(&args[1])?;
7388
7389 if x.len() != y.len() || x.is_empty() {
7390 return Err(RuntimeError::new(
7391 "correlation() requires equal-length non-empty arrays",
7392 ));
7393 }
7394
7395 let n = x.len() as f64;
7396 let mean_x: f64 = x.iter().sum::<f64>() / n;
7397 let mean_y: f64 = y.iter().sum::<f64>() / n;
7398
7399 let mut cov = 0.0;
7400 let mut var_x = 0.0;
7401 let mut var_y = 0.0;
7402
7403 for i in 0..x.len() {
7404 let dx = x[i] - mean_x;
7405 let dy = y[i] - mean_y;
7406 cov += dx * dy;
7407 var_x += dx * dx;
7408 var_y += dy * dy;
7409 }
7410
7411 if var_x == 0.0 || var_y == 0.0 {
7412 return Ok(Value::Float(0.0));
7413 }
7414
7415 Ok(Value::Float(cov / (var_x.sqrt() * var_y.sqrt())))
7416 });
7417
7418 define(interp, "range", Some(1), |_, args| {
7420 let nums = extract_numbers(&args[0])?;
7421 if nums.is_empty() {
7422 return Ok(Value::Float(0.0));
7423 }
7424 let min = nums.iter().cloned().fold(f64::INFINITY, f64::min);
7425 let max = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
7426 Ok(Value::Float(max - min))
7427 });
7428
7429 define(interp, "zscore", Some(1), |_, args| {
7431 let nums = extract_numbers(&args[0])?;
7432 if nums.is_empty() {
7433 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
7434 }
7435
7436 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
7437 let variance: f64 =
7438 nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
7439 let stddev = variance.sqrt();
7440
7441 if stddev == 0.0 {
7442 let zeros: Vec<Value> = nums.iter().map(|_| Value::Float(0.0)).collect();
7443 return Ok(Value::Array(Rc::new(RefCell::new(zeros))));
7444 }
7445
7446 let zscores: Vec<Value> = nums
7447 .iter()
7448 .map(|x| Value::Float((x - mean) / stddev))
7449 .collect();
7450 Ok(Value::Array(Rc::new(RefCell::new(zscores))))
7451 });
7452}
7453
7454fn register_matrix(interp: &mut Interpreter) {
7459 fn extract_matrix(val: &Value) -> Result<Vec<Vec<f64>>, RuntimeError> {
7461 match val {
7462 Value::Array(arr) => {
7463 let arr = arr.borrow();
7464 let mut matrix = Vec::new();
7465 for row in arr.iter() {
7466 match row {
7467 Value::Array(row_arr) => {
7468 let row_arr = row_arr.borrow();
7469 let mut row_vec = Vec::new();
7470 for v in row_arr.iter() {
7471 match v {
7472 Value::Int(n) => row_vec.push(*n as f64),
7473 Value::Float(f) => row_vec.push(*f),
7474 _ => {
7475 return Err(RuntimeError::new(
7476 "matrix requires numeric values",
7477 ))
7478 }
7479 }
7480 }
7481 matrix.push(row_vec);
7482 }
7483 _ => return Err(RuntimeError::new("matrix requires 2D array")),
7484 }
7485 }
7486 Ok(matrix)
7487 }
7488 _ => Err(RuntimeError::new("matrix requires array")),
7489 }
7490 }
7491
7492 fn matrix_to_value(m: Vec<Vec<f64>>) -> Value {
7493 let rows: Vec<Value> = m
7494 .into_iter()
7495 .map(|row| {
7496 let cols: Vec<Value> = row.into_iter().map(Value::Float).collect();
7497 Value::Array(Rc::new(RefCell::new(cols)))
7498 })
7499 .collect();
7500 Value::Array(Rc::new(RefCell::new(rows)))
7501 }
7502
7503 define(interp, "matrix_new", Some(3), |_, args| {
7505 let rows = match &args[0] {
7506 Value::Int(n) => *n as usize,
7507 _ => return Err(RuntimeError::new("matrix_new() requires integer rows")),
7508 };
7509 let cols = match &args[1] {
7510 Value::Int(n) => *n as usize,
7511 _ => return Err(RuntimeError::new("matrix_new() requires integer cols")),
7512 };
7513 let fill = match &args[2] {
7514 Value::Int(n) => *n as f64,
7515 Value::Float(f) => *f,
7516 _ => 0.0,
7517 };
7518
7519 let matrix = vec![vec![fill; cols]; rows];
7520 Ok(matrix_to_value(matrix))
7521 });
7522
7523 define(interp, "matrix_identity", Some(1), |_, args| {
7525 let size = match &args[0] {
7526 Value::Int(n) => *n as usize,
7527 _ => return Err(RuntimeError::new("matrix_identity() requires integer size")),
7528 };
7529
7530 let mut matrix = vec![vec![0.0; size]; size];
7531 for i in 0..size {
7532 matrix[i][i] = 1.0;
7533 }
7534 Ok(matrix_to_value(matrix))
7535 });
7536
7537 define(interp, "matrix_add", Some(2), |_, args| {
7539 let a = extract_matrix(&args[0])?;
7540 let b = extract_matrix(&args[1])?;
7541
7542 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
7543 return Err(RuntimeError::new(
7544 "matrix_add() requires same-size matrices",
7545 ));
7546 }
7547
7548 let result: Vec<Vec<f64>> = a
7549 .iter()
7550 .zip(b.iter())
7551 .map(|(row_a, row_b)| row_a.iter().zip(row_b.iter()).map(|(x, y)| x + y).collect())
7552 .collect();
7553
7554 Ok(matrix_to_value(result))
7555 });
7556
7557 define(interp, "matrix_sub", Some(2), |_, args| {
7559 let a = extract_matrix(&args[0])?;
7560 let b = extract_matrix(&args[1])?;
7561
7562 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
7563 return Err(RuntimeError::new(
7564 "matrix_sub() requires same-size matrices",
7565 ));
7566 }
7567
7568 let result: Vec<Vec<f64>> = a
7569 .iter()
7570 .zip(b.iter())
7571 .map(|(row_a, row_b)| row_a.iter().zip(row_b.iter()).map(|(x, y)| x - y).collect())
7572 .collect();
7573
7574 Ok(matrix_to_value(result))
7575 });
7576
7577 define(interp, "matrix_mul", Some(2), |_, args| {
7579 let a = extract_matrix(&args[0])?;
7580 let b = extract_matrix(&args[1])?;
7581
7582 if a.is_empty() || b.is_empty() || a[0].len() != b.len() {
7583 return Err(RuntimeError::new(
7584 "matrix_mul() requires compatible matrices (a.cols == b.rows)",
7585 ));
7586 }
7587
7588 let rows = a.len();
7589 let cols = b[0].len();
7590 let inner = b.len();
7591
7592 let mut result = vec![vec![0.0; cols]; rows];
7593 for i in 0..rows {
7594 for j in 0..cols {
7595 for k in 0..inner {
7596 result[i][j] += a[i][k] * b[k][j];
7597 }
7598 }
7599 }
7600
7601 Ok(matrix_to_value(result))
7602 });
7603
7604 define(interp, "matrix_scale", Some(2), |_, args| {
7606 let m = extract_matrix(&args[0])?;
7607 let scale = match &args[1] {
7608 Value::Int(n) => *n as f64,
7609 Value::Float(f) => *f,
7610 _ => return Err(RuntimeError::new("matrix_scale() requires numeric scalar")),
7611 };
7612
7613 let result: Vec<Vec<f64>> = m
7614 .iter()
7615 .map(|row| row.iter().map(|x| x * scale).collect())
7616 .collect();
7617
7618 Ok(matrix_to_value(result))
7619 });
7620
7621 define(interp, "matrix_transpose", Some(1), |_, args| {
7623 let m = extract_matrix(&args[0])?;
7624 if m.is_empty() {
7625 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
7626 }
7627
7628 let rows = m.len();
7629 let cols = m[0].len();
7630 let mut result = vec![vec![0.0; rows]; cols];
7631
7632 for i in 0..rows {
7633 for j in 0..cols {
7634 result[j][i] = m[i][j];
7635 }
7636 }
7637
7638 Ok(matrix_to_value(result))
7639 });
7640
7641 define(interp, "matrix_det", Some(1), |_, args| {
7643 let m = extract_matrix(&args[0])?;
7644
7645 if m.is_empty() || m.len() != m[0].len() {
7646 return Err(RuntimeError::new("matrix_det() requires square matrix"));
7647 }
7648
7649 let n = m.len();
7650 match n {
7651 1 => Ok(Value::Float(m[0][0])),
7652 2 => Ok(Value::Float(m[0][0] * m[1][1] - m[0][1] * m[1][0])),
7653 3 => {
7654 let det = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
7655 - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
7656 + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
7657 Ok(Value::Float(det))
7658 }
7659 _ => Err(RuntimeError::new(
7660 "matrix_det() only supports up to 3x3 matrices",
7661 )),
7662 }
7663 });
7664
7665 define(interp, "matrix_trace", Some(1), |_, args| {
7667 let m = extract_matrix(&args[0])?;
7668
7669 let size = m.len().min(if m.is_empty() { 0 } else { m[0].len() });
7670 let trace: f64 = (0..size).map(|i| m[i][i]).sum();
7671
7672 Ok(Value::Float(trace))
7673 });
7674
7675 define(interp, "matrix_dot", Some(2), |_, args| {
7677 fn extract_vector(val: &Value) -> Result<Vec<f64>, RuntimeError> {
7678 match val {
7679 Value::Array(arr) => {
7680 let arr = arr.borrow();
7681 let mut vec = Vec::new();
7682 for v in arr.iter() {
7683 match v {
7684 Value::Int(n) => vec.push(*n as f64),
7685 Value::Float(f) => vec.push(*f),
7686 _ => {
7687 return Err(RuntimeError::new(
7688 "dot product requires numeric vectors",
7689 ))
7690 }
7691 }
7692 }
7693 Ok(vec)
7694 }
7695 _ => Err(RuntimeError::new("dot product requires arrays")),
7696 }
7697 }
7698
7699 let a = extract_vector(&args[0])?;
7700 let b = extract_vector(&args[1])?;
7701
7702 if a.len() != b.len() {
7703 return Err(RuntimeError::new(
7704 "matrix_dot() requires same-length vectors",
7705 ));
7706 }
7707
7708 let dot: f64 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
7709 Ok(Value::Float(dot))
7710 });
7711}
7712
7713fn mod_inverse(a: i64, m: i64) -> Option<i64> {
7715 let (mut old_r, mut r) = (a, m);
7716 let (mut old_s, mut s) = (1i64, 0i64);
7717
7718 while r != 0 {
7719 let q = old_r / r;
7720 (old_r, r) = (r, old_r - q * r);
7721 (old_s, s) = (s, old_s - q * s);
7722 }
7723
7724 if old_r != 1 {
7725 None } else {
7727 Some(old_s.rem_euclid(m))
7728 }
7729}
7730
7731fn register_functional(interp: &mut Interpreter) {
7737 define(interp, "identity", Some(1), |_, args| Ok(args[0].clone()));
7739
7740 define(interp, "const_fn", Some(1), |_, args| Ok(args[0].clone()));
7742
7743 define(interp, "apply", Some(2), |interp, args| {
7745 let func = match &args[0] {
7746 Value::Function(f) => f.clone(),
7747 _ => {
7748 return Err(RuntimeError::new(
7749 "apply: first argument must be a function",
7750 ))
7751 }
7752 };
7753 let fn_args = match &args[1] {
7754 Value::Array(arr) => arr.borrow().clone(),
7755 _ => return Err(RuntimeError::new("apply: second argument must be an array")),
7756 };
7757 interp.call_function(&func, fn_args)
7758 });
7759
7760 define(interp, "flip", Some(3), |interp, args| {
7762 let func = match &args[0] {
7763 Value::Function(f) => f.clone(),
7764 _ => return Err(RuntimeError::new("flip: first argument must be a function")),
7765 };
7766 let flipped_args = vec![args[2].clone(), args[1].clone()];
7767 interp.call_function(&func, flipped_args)
7768 });
7769
7770 define(interp, "tap", Some(2), |interp, args| {
7772 let val = args[0].clone();
7773 let func = match &args[1] {
7774 Value::Function(f) => f.clone(),
7775 _ => return Err(RuntimeError::new("tap: second argument must be a function")),
7776 };
7777 let _ = interp.call_function(&func, vec![val.clone()]);
7778 Ok(val)
7779 });
7780
7781 define(interp, "thunk", Some(1), |_, args| {
7783 Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()]))))
7784 });
7785
7786 define(interp, "force", Some(1), |interp, args| match &args[0] {
7788 Value::Array(arr) => {
7789 let arr = arr.borrow();
7790 if arr.len() == 1 {
7791 if let Value::Function(f) = &arr[0] {
7792 return interp.call_function(f, vec![]);
7793 }
7794 }
7795 Ok(arr.get(0).cloned().unwrap_or(Value::Null))
7796 }
7797 v => Ok(v.clone()),
7798 });
7799
7800 define(interp, "negate", Some(2), |interp, args| {
7802 let func = match &args[0] {
7803 Value::Function(f) => f.clone(),
7804 _ => {
7805 return Err(RuntimeError::new(
7806 "negate: first argument must be a function",
7807 ))
7808 }
7809 };
7810 let result = interp.call_function(&func, vec![args[1].clone()])?;
7811 Ok(Value::Bool(!is_truthy(&result)))
7812 });
7813
7814 define(interp, "complement", Some(2), |interp, args| {
7816 let func = match &args[0] {
7817 Value::Function(f) => f.clone(),
7818 _ => {
7819 return Err(RuntimeError::new(
7820 "complement: first argument must be a function",
7821 ))
7822 }
7823 };
7824 let result = interp.call_function(&func, vec![args[1].clone()])?;
7825 Ok(Value::Bool(!is_truthy(&result)))
7826 });
7827
7828 define(interp, "partial", None, |interp, args| {
7830 if args.len() < 2 {
7831 return Err(RuntimeError::new(
7832 "partial: requires at least function and one argument",
7833 ));
7834 }
7835 let func = match &args[0] {
7836 Value::Function(f) => f.clone(),
7837 _ => {
7838 return Err(RuntimeError::new(
7839 "partial: first argument must be a function",
7840 ))
7841 }
7842 };
7843 let partial_args: Vec<Value> = args[1..].to_vec();
7844 interp.call_function(&func, partial_args)
7845 });
7846
7847 define(interp, "juxt", None, |interp, args| {
7849 if args.len() < 2 {
7850 return Err(RuntimeError::new("juxt: requires functions and a value"));
7851 }
7852 let val = args.last().unwrap().clone();
7853 let results: Result<Vec<Value>, _> = args[..args.len() - 1]
7854 .iter()
7855 .map(|f| match f {
7856 Value::Function(func) => interp.call_function(func, vec![val.clone()]),
7857 _ => Err(RuntimeError::new(
7858 "juxt: all but last argument must be functions",
7859 )),
7860 })
7861 .collect();
7862 Ok(Value::Array(Rc::new(RefCell::new(results?))))
7863 });
7864}
7865
7866fn register_benchmark(interp: &mut Interpreter) {
7868 define(interp, "bench", Some(2), |interp, args| {
7870 let func = match &args[0] {
7871 Value::Function(f) => f.clone(),
7872 _ => {
7873 return Err(RuntimeError::new(
7874 "bench: first argument must be a function",
7875 ))
7876 }
7877 };
7878 let iterations = match &args[1] {
7879 Value::Int(n) => *n as usize,
7880 _ => {
7881 return Err(RuntimeError::new(
7882 "bench: second argument must be an integer",
7883 ))
7884 }
7885 };
7886
7887 let start = std::time::Instant::now();
7888 for _ in 0..iterations {
7889 let _ = interp.call_function(&func, vec![])?;
7890 }
7891 let elapsed = start.elapsed();
7892 let avg_ms = elapsed.as_secs_f64() * 1000.0 / iterations as f64;
7893 Ok(Value::Float(avg_ms))
7894 });
7895
7896 define(interp, "time_it", Some(1), |interp, args| {
7898 let func = match &args[0] {
7899 Value::Function(f) => f.clone(),
7900 _ => return Err(RuntimeError::new("time_it: argument must be a function")),
7901 };
7902
7903 let start = std::time::Instant::now();
7904 let result = interp.call_function(&func, vec![])?;
7905 let elapsed_ms = start.elapsed().as_secs_f64() * 1000.0;
7906
7907 Ok(Value::Tuple(Rc::new(vec![
7908 result,
7909 Value::Float(elapsed_ms),
7910 ])))
7911 });
7912
7913 define(interp, "stopwatch_start", Some(0), |_, _| {
7915 let elapsed = std::time::SystemTime::now()
7916 .duration_since(std::time::UNIX_EPOCH)
7917 .unwrap_or_default();
7918 Ok(Value::Float(elapsed.as_secs_f64() * 1000.0))
7919 });
7920
7921 define(interp, "stopwatch_elapsed", Some(1), |_, args| {
7923 let start_ms = match &args[0] {
7924 Value::Float(f) => *f,
7925 Value::Int(n) => *n as f64,
7926 _ => {
7927 return Err(RuntimeError::new(
7928 "stopwatch_elapsed: argument must be a number",
7929 ))
7930 }
7931 };
7932 let now = std::time::SystemTime::now()
7933 .duration_since(std::time::UNIX_EPOCH)
7934 .unwrap_or_default();
7935 let now_ms = now.as_secs_f64() * 1000.0;
7936 Ok(Value::Float(now_ms - start_ms))
7937 });
7938
7939 define(interp, "compare_bench", Some(3), |interp, args| {
7941 let func1 = match &args[0] {
7942 Value::Function(f) => f.clone(),
7943 _ => {
7944 return Err(RuntimeError::new(
7945 "compare_bench: first argument must be a function",
7946 ))
7947 }
7948 };
7949 let func2 = match &args[1] {
7950 Value::Function(f) => f.clone(),
7951 _ => {
7952 return Err(RuntimeError::new(
7953 "compare_bench: second argument must be a function",
7954 ))
7955 }
7956 };
7957 let iterations = match &args[2] {
7958 Value::Int(n) => *n as usize,
7959 _ => {
7960 return Err(RuntimeError::new(
7961 "compare_bench: third argument must be an integer",
7962 ))
7963 }
7964 };
7965
7966 let start1 = std::time::Instant::now();
7967 for _ in 0..iterations {
7968 let _ = interp.call_function(&func1, vec![])?;
7969 }
7970 let time1 = start1.elapsed().as_secs_f64();
7971
7972 let start2 = std::time::Instant::now();
7973 for _ in 0..iterations {
7974 let _ = interp.call_function(&func2, vec![])?;
7975 }
7976 let time2 = start2.elapsed().as_secs_f64();
7977
7978 let mut results = std::collections::HashMap::new();
7979 results.insert("time1_ms".to_string(), Value::Float(time1 * 1000.0));
7980 results.insert("time2_ms".to_string(), Value::Float(time2 * 1000.0));
7981 results.insert("speedup".to_string(), Value::Float(time1 / time2));
7982 results.insert("iterations".to_string(), Value::Int(iterations as i64));
7983
7984 Ok(Value::Struct {
7985 name: "BenchResult".to_string(),
7986 fields: Rc::new(RefCell::new(results)),
7987 })
7988 });
7989
7990 define(interp, "memory_usage", Some(0), |_, _| Ok(Value::Int(0)));
7992}
7993
7994fn register_itertools(interp: &mut Interpreter) {
7996 define(interp, "cycle", Some(2), |_, args| {
7998 let arr = match &args[0] {
7999 Value::Array(a) => a.borrow().clone(),
8000 _ => return Err(RuntimeError::new("cycle: first argument must be an array")),
8001 };
8002 let n = match &args[1] {
8003 Value::Int(n) => *n as usize,
8004 _ => {
8005 return Err(RuntimeError::new(
8006 "cycle: second argument must be an integer",
8007 ))
8008 }
8009 };
8010
8011 if arr.is_empty() {
8012 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
8013 }
8014
8015 let result: Vec<Value> = (0..n).map(|i| arr[i % arr.len()].clone()).collect();
8016 Ok(Value::Array(Rc::new(RefCell::new(result))))
8017 });
8018
8019 define(interp, "repeat_val", Some(2), |_, args| {
8021 let val = args[0].clone();
8022 let n = match &args[1] {
8023 Value::Int(n) => *n as usize,
8024 _ => {
8025 return Err(RuntimeError::new(
8026 "repeat_val: second argument must be an integer",
8027 ))
8028 }
8029 };
8030
8031 let result: Vec<Value> = std::iter::repeat(val).take(n).collect();
8032 Ok(Value::Array(Rc::new(RefCell::new(result))))
8033 });
8034
8035 define(interp, "take_while", Some(2), |interp, args| {
8037 let arr = match &args[0] {
8038 Value::Array(a) => a.borrow().clone(),
8039 _ => {
8040 return Err(RuntimeError::new(
8041 "take_while: first argument must be an array",
8042 ))
8043 }
8044 };
8045 let pred = match &args[1] {
8046 Value::Function(f) => f.clone(),
8047 _ => {
8048 return Err(RuntimeError::new(
8049 "take_while: second argument must be a function",
8050 ))
8051 }
8052 };
8053
8054 let mut result = Vec::new();
8055 for item in arr {
8056 let keep = interp.call_function(&pred, vec![item.clone()])?;
8057 if is_truthy(&keep) {
8058 result.push(item);
8059 } else {
8060 break;
8061 }
8062 }
8063 Ok(Value::Array(Rc::new(RefCell::new(result))))
8064 });
8065
8066 define(interp, "drop_while", Some(2), |interp, args| {
8068 let arr = match &args[0] {
8069 Value::Array(a) => a.borrow().clone(),
8070 _ => {
8071 return Err(RuntimeError::new(
8072 "drop_while: first argument must be an array",
8073 ))
8074 }
8075 };
8076 let pred = match &args[1] {
8077 Value::Function(f) => f.clone(),
8078 _ => {
8079 return Err(RuntimeError::new(
8080 "drop_while: second argument must be a function",
8081 ))
8082 }
8083 };
8084
8085 let mut dropping = true;
8086 let mut result = Vec::new();
8087 for item in arr {
8088 if dropping {
8089 let drop = interp.call_function(&pred, vec![item.clone()])?;
8090 if !is_truthy(&drop) {
8091 dropping = false;
8092 result.push(item);
8093 }
8094 } else {
8095 result.push(item);
8096 }
8097 }
8098 Ok(Value::Array(Rc::new(RefCell::new(result))))
8099 });
8100
8101 define(interp, "group_by", Some(2), |interp, args| {
8103 let arr = match &args[0] {
8104 Value::Array(a) => a.borrow().clone(),
8105 _ => {
8106 return Err(RuntimeError::new(
8107 "group_by: first argument must be an array",
8108 ))
8109 }
8110 };
8111 let key_fn = match &args[1] {
8112 Value::Function(f) => f.clone(),
8113 _ => {
8114 return Err(RuntimeError::new(
8115 "group_by: second argument must be a function",
8116 ))
8117 }
8118 };
8119
8120 let mut groups: Vec<Value> = Vec::new();
8121 let mut current_group: Vec<Value> = Vec::new();
8122 let mut current_key: Option<Value> = None;
8123
8124 for item in arr {
8125 let key = interp.call_function(&key_fn, vec![item.clone()])?;
8126 match ¤t_key {
8127 Some(k) if value_eq(k, &key) => {
8128 current_group.push(item);
8129 }
8130 _ => {
8131 if !current_group.is_empty() {
8132 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
8133 }
8134 current_group = vec![item];
8135 current_key = Some(key);
8136 }
8137 }
8138 }
8139 if !current_group.is_empty() {
8140 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
8141 }
8142
8143 Ok(Value::Array(Rc::new(RefCell::new(groups))))
8144 });
8145
8146 define(interp, "partition", Some(2), |interp, args| {
8148 let arr = match &args[0] {
8149 Value::Array(a) => a.borrow().clone(),
8150 _ => {
8151 return Err(RuntimeError::new(
8152 "partition: first argument must be an array",
8153 ))
8154 }
8155 };
8156 let pred = match &args[1] {
8157 Value::Function(f) => f.clone(),
8158 _ => {
8159 return Err(RuntimeError::new(
8160 "partition: second argument must be a function",
8161 ))
8162 }
8163 };
8164
8165 let mut true_items = Vec::new();
8166 let mut false_items = Vec::new();
8167
8168 for item in arr {
8169 let result = interp.call_function(&pred, vec![item.clone()])?;
8170 if is_truthy(&result) {
8171 true_items.push(item);
8172 } else {
8173 false_items.push(item);
8174 }
8175 }
8176
8177 Ok(Value::Tuple(Rc::new(vec![
8178 Value::Array(Rc::new(RefCell::new(true_items))),
8179 Value::Array(Rc::new(RefCell::new(false_items))),
8180 ])))
8181 });
8182
8183 define(interp, "interleave", Some(2), |_, args| {
8185 let arr1 = match &args[0] {
8186 Value::Array(a) => a.borrow().clone(),
8187 _ => {
8188 return Err(RuntimeError::new(
8189 "interleave: first argument must be an array",
8190 ))
8191 }
8192 };
8193 let arr2 = match &args[1] {
8194 Value::Array(a) => a.borrow().clone(),
8195 _ => {
8196 return Err(RuntimeError::new(
8197 "interleave: second argument must be an array",
8198 ))
8199 }
8200 };
8201
8202 let mut result = Vec::new();
8203 let mut i1 = arr1.into_iter();
8204 let mut i2 = arr2.into_iter();
8205
8206 loop {
8207 match (i1.next(), i2.next()) {
8208 (Some(a), Some(b)) => {
8209 result.push(a);
8210 result.push(b);
8211 }
8212 (Some(a), None) => {
8213 result.push(a);
8214 result.extend(i1);
8215 break;
8216 }
8217 (None, Some(b)) => {
8218 result.push(b);
8219 result.extend(i2);
8220 break;
8221 }
8222 (None, None) => break,
8223 }
8224 }
8225
8226 Ok(Value::Array(Rc::new(RefCell::new(result))))
8227 });
8228
8229 define(interp, "chunks", Some(2), |_, args| {
8231 let arr = match &args[0] {
8232 Value::Array(a) => a.borrow().clone(),
8233 _ => return Err(RuntimeError::new("chunks: first argument must be an array")),
8234 };
8235 let size = match &args[1] {
8236 Value::Int(n) if *n > 0 => *n as usize,
8237 _ => {
8238 return Err(RuntimeError::new(
8239 "chunks: second argument must be a positive integer",
8240 ))
8241 }
8242 };
8243
8244 let chunks: Vec<Value> = arr
8245 .chunks(size)
8246 .map(|chunk| Value::Array(Rc::new(RefCell::new(chunk.to_vec()))))
8247 .collect();
8248
8249 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
8250 });
8251
8252 define(interp, "windows", Some(2), |_, args| {
8254 let arr = match &args[0] {
8255 Value::Array(a) => a.borrow().clone(),
8256 _ => {
8257 return Err(RuntimeError::new(
8258 "windows: first argument must be an array",
8259 ))
8260 }
8261 };
8262 let size = match &args[1] {
8263 Value::Int(n) if *n > 0 => *n as usize,
8264 _ => {
8265 return Err(RuntimeError::new(
8266 "windows: second argument must be a positive integer",
8267 ))
8268 }
8269 };
8270
8271 if arr.len() < size {
8272 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
8273 }
8274
8275 let windows: Vec<Value> = arr
8276 .windows(size)
8277 .map(|window| Value::Array(Rc::new(RefCell::new(window.to_vec()))))
8278 .collect();
8279
8280 Ok(Value::Array(Rc::new(RefCell::new(windows))))
8281 });
8282
8283 define(interp, "scan", Some(3), |interp, args| {
8285 let arr = match &args[0] {
8286 Value::Array(a) => a.borrow().clone(),
8287 _ => return Err(RuntimeError::new("scan: first argument must be an array")),
8288 };
8289 let init = args[1].clone();
8290 let func = match &args[2] {
8291 Value::Function(f) => f.clone(),
8292 _ => return Err(RuntimeError::new("scan: third argument must be a function")),
8293 };
8294
8295 let mut results = vec![init.clone()];
8296 let mut acc = init;
8297
8298 for item in arr {
8299 acc = interp.call_function(&func, vec![acc, item])?;
8300 results.push(acc.clone());
8301 }
8302
8303 Ok(Value::Array(Rc::new(RefCell::new(results))))
8304 });
8305
8306 define(interp, "frequencies", Some(1), |_, args| {
8308 let arr = match &args[0] {
8309 Value::Array(a) => a.borrow().clone(),
8310 _ => return Err(RuntimeError::new("frequencies: argument must be an array")),
8311 };
8312
8313 let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
8314 for item in &arr {
8315 let key = format!("{}", item);
8316 *counts.entry(key).or_insert(0) += 1;
8317 }
8318
8319 let result: std::collections::HashMap<String, Value> = counts
8320 .into_iter()
8321 .map(|(k, v)| (k, Value::Int(v)))
8322 .collect();
8323
8324 Ok(Value::Map(Rc::new(RefCell::new(result))))
8325 });
8326
8327 define(interp, "dedupe", Some(1), |_, args| {
8329 let arr = match &args[0] {
8330 Value::Array(a) => a.borrow().clone(),
8331 _ => return Err(RuntimeError::new("dedupe: argument must be an array")),
8332 };
8333
8334 let mut result = Vec::new();
8335 let mut prev: Option<Value> = None;
8336
8337 for item in arr {
8338 match &prev {
8339 Some(p) if value_eq(p, &item) => continue,
8340 _ => {
8341 result.push(item.clone());
8342 prev = Some(item);
8343 }
8344 }
8345 }
8346
8347 Ok(Value::Array(Rc::new(RefCell::new(result))))
8348 });
8349
8350 define(interp, "unique", Some(1), |_, args| {
8352 let arr = match &args[0] {
8353 Value::Array(a) => a.borrow().clone(),
8354 _ => return Err(RuntimeError::new("unique: argument must be an array")),
8355 };
8356
8357 let mut seen = std::collections::HashSet::new();
8358 let mut result = Vec::new();
8359
8360 for item in arr {
8361 let key = format!("{}", item);
8362 if seen.insert(key) {
8363 result.push(item);
8364 }
8365 }
8366
8367 Ok(Value::Array(Rc::new(RefCell::new(result))))
8368 });
8369}
8370
8371fn register_ranges(interp: &mut Interpreter) {
8373 define(interp, "range_step", Some(3), |_, args| {
8375 let start = match &args[0] {
8376 Value::Int(n) => *n,
8377 Value::Float(f) => *f as i64,
8378 _ => return Err(RuntimeError::new("range_step: start must be a number")),
8379 };
8380 let end = match &args[1] {
8381 Value::Int(n) => *n,
8382 Value::Float(f) => *f as i64,
8383 _ => return Err(RuntimeError::new("range_step: end must be a number")),
8384 };
8385 let step = match &args[2] {
8386 Value::Int(n) if *n != 0 => *n,
8387 Value::Float(f) if *f != 0.0 => *f as i64,
8388 _ => {
8389 return Err(RuntimeError::new(
8390 "range_step: step must be a non-zero number",
8391 ))
8392 }
8393 };
8394
8395 let mut result = Vec::new();
8396 if step > 0 {
8397 let mut i = start;
8398 while i < end {
8399 result.push(Value::Int(i));
8400 i += step;
8401 }
8402 } else {
8403 let mut i = start;
8404 while i > end {
8405 result.push(Value::Int(i));
8406 i += step;
8407 }
8408 }
8409
8410 Ok(Value::Array(Rc::new(RefCell::new(result))))
8411 });
8412
8413 define(interp, "linspace", Some(3), |_, args| {
8415 let start = match &args[0] {
8416 Value::Int(n) => *n as f64,
8417 Value::Float(f) => *f,
8418 _ => return Err(RuntimeError::new("linspace: start must be a number")),
8419 };
8420 let end = match &args[1] {
8421 Value::Int(n) => *n as f64,
8422 Value::Float(f) => *f,
8423 _ => return Err(RuntimeError::new("linspace: end must be a number")),
8424 };
8425 let n = match &args[2] {
8426 Value::Int(n) if *n > 0 => *n as usize,
8427 _ => {
8428 return Err(RuntimeError::new(
8429 "linspace: count must be a positive integer",
8430 ))
8431 }
8432 };
8433
8434 if n == 1 {
8435 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
8436 start,
8437 )]))));
8438 }
8439
8440 let step = (end - start) / (n - 1) as f64;
8441 let result: Vec<Value> = (0..n)
8442 .map(|i| Value::Float(start + step * i as f64))
8443 .collect();
8444
8445 Ok(Value::Array(Rc::new(RefCell::new(result))))
8446 });
8447
8448 define(interp, "logspace", Some(3), |_, args| {
8450 let start_exp = match &args[0] {
8451 Value::Int(n) => *n as f64,
8452 Value::Float(f) => *f,
8453 _ => {
8454 return Err(RuntimeError::new(
8455 "logspace: start exponent must be a number",
8456 ))
8457 }
8458 };
8459 let end_exp = match &args[1] {
8460 Value::Int(n) => *n as f64,
8461 Value::Float(f) => *f,
8462 _ => return Err(RuntimeError::new("logspace: end exponent must be a number")),
8463 };
8464 let n = match &args[2] {
8465 Value::Int(n) if *n > 0 => *n as usize,
8466 _ => {
8467 return Err(RuntimeError::new(
8468 "logspace: count must be a positive integer",
8469 ))
8470 }
8471 };
8472
8473 if n == 1 {
8474 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
8475 10f64.powf(start_exp),
8476 )]))));
8477 }
8478
8479 let step = (end_exp - start_exp) / (n - 1) as f64;
8480 let result: Vec<Value> = (0..n)
8481 .map(|i| Value::Float(10f64.powf(start_exp + step * i as f64)))
8482 .collect();
8483
8484 Ok(Value::Array(Rc::new(RefCell::new(result))))
8485 });
8486
8487 define(interp, "arange", Some(3), |_, args| {
8489 let start = match &args[0] {
8490 Value::Int(n) => *n as f64,
8491 Value::Float(f) => *f,
8492 _ => return Err(RuntimeError::new("arange: start must be a number")),
8493 };
8494 let stop = match &args[1] {
8495 Value::Int(n) => *n as f64,
8496 Value::Float(f) => *f,
8497 _ => return Err(RuntimeError::new("arange: stop must be a number")),
8498 };
8499 let step = match &args[2] {
8500 Value::Int(n) if *n != 0 => *n as f64,
8501 Value::Float(f) if *f != 0.0 => *f,
8502 _ => return Err(RuntimeError::new("arange: step must be a non-zero number")),
8503 };
8504
8505 let mut result = Vec::new();
8506 if step > 0.0 {
8507 let mut x = start;
8508 while x < stop {
8509 result.push(Value::Float(x));
8510 x += step;
8511 }
8512 } else {
8513 let mut x = start;
8514 while x > stop {
8515 result.push(Value::Float(x));
8516 x += step;
8517 }
8518 }
8519
8520 Ok(Value::Array(Rc::new(RefCell::new(result))))
8521 });
8522
8523 define(interp, "geomspace", Some(3), |_, args| {
8525 let start = match &args[0] {
8526 Value::Int(n) if *n > 0 => *n as f64,
8527 Value::Float(f) if *f > 0.0 => *f,
8528 _ => {
8529 return Err(RuntimeError::new(
8530 "geomspace: start must be a positive number",
8531 ))
8532 }
8533 };
8534 let end = match &args[1] {
8535 Value::Int(n) if *n > 0 => *n as f64,
8536 Value::Float(f) if *f > 0.0 => *f,
8537 _ => {
8538 return Err(RuntimeError::new(
8539 "geomspace: end must be a positive number",
8540 ))
8541 }
8542 };
8543 let n = match &args[2] {
8544 Value::Int(n) if *n > 0 => *n as usize,
8545 _ => {
8546 return Err(RuntimeError::new(
8547 "geomspace: count must be a positive integer",
8548 ))
8549 }
8550 };
8551
8552 if n == 1 {
8553 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(
8554 start,
8555 )]))));
8556 }
8557
8558 let ratio = (end / start).powf(1.0 / (n - 1) as f64);
8559 let result: Vec<Value> = (0..n)
8560 .map(|i| Value::Float(start * ratio.powi(i as i32)))
8561 .collect();
8562
8563 Ok(Value::Array(Rc::new(RefCell::new(result))))
8564 });
8565}
8566
8567fn register_bitwise(interp: &mut Interpreter) {
8569 define(interp, "bit_and", Some(2), |_, args| {
8570 let a = match &args[0] {
8571 Value::Int(n) => *n,
8572 _ => return Err(RuntimeError::new("bit_and: arguments must be integers")),
8573 };
8574 let b = match &args[1] {
8575 Value::Int(n) => *n,
8576 _ => return Err(RuntimeError::new("bit_and: arguments must be integers")),
8577 };
8578 Ok(Value::Int(a & b))
8579 });
8580
8581 define(interp, "bit_or", Some(2), |_, args| {
8582 let a = match &args[0] {
8583 Value::Int(n) => *n,
8584 _ => return Err(RuntimeError::new("bit_or: arguments must be integers")),
8585 };
8586 let b = match &args[1] {
8587 Value::Int(n) => *n,
8588 _ => return Err(RuntimeError::new("bit_or: arguments must be integers")),
8589 };
8590 Ok(Value::Int(a | b))
8591 });
8592
8593 define(interp, "bit_xor", Some(2), |_, args| {
8594 let a = match &args[0] {
8595 Value::Int(n) => *n,
8596 _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")),
8597 };
8598 let b = match &args[1] {
8599 Value::Int(n) => *n,
8600 _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")),
8601 };
8602 Ok(Value::Int(a ^ b))
8603 });
8604
8605 define(interp, "bit_not", Some(1), |_, args| {
8606 let a = match &args[0] {
8607 Value::Int(n) => *n,
8608 _ => return Err(RuntimeError::new("bit_not: argument must be an integer")),
8609 };
8610 Ok(Value::Int(!a))
8611 });
8612
8613 define(interp, "bit_shl", Some(2), |_, args| {
8614 let a = match &args[0] {
8615 Value::Int(n) => *n,
8616 _ => {
8617 return Err(RuntimeError::new(
8618 "bit_shl: first argument must be an integer",
8619 ))
8620 }
8621 };
8622 let b = match &args[1] {
8623 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
8624 _ => return Err(RuntimeError::new("bit_shl: shift amount must be 0-63")),
8625 };
8626 Ok(Value::Int(a << b))
8627 });
8628
8629 define(interp, "bit_shr", Some(2), |_, args| {
8630 let a = match &args[0] {
8631 Value::Int(n) => *n,
8632 _ => {
8633 return Err(RuntimeError::new(
8634 "bit_shr: first argument must be an integer",
8635 ))
8636 }
8637 };
8638 let b = match &args[1] {
8639 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
8640 _ => return Err(RuntimeError::new("bit_shr: shift amount must be 0-63")),
8641 };
8642 Ok(Value::Int(a >> b))
8643 });
8644
8645 define(interp, "popcount", Some(1), |_, args| {
8646 let a = match &args[0] {
8647 Value::Int(n) => *n,
8648 _ => return Err(RuntimeError::new("popcount: argument must be an integer")),
8649 };
8650 Ok(Value::Int(a.count_ones() as i64))
8651 });
8652
8653 define(interp, "leading_zeros", Some(1), |_, args| {
8654 let a = match &args[0] {
8655 Value::Int(n) => *n,
8656 _ => {
8657 return Err(RuntimeError::new(
8658 "leading_zeros: argument must be an integer",
8659 ))
8660 }
8661 };
8662 Ok(Value::Int(a.leading_zeros() as i64))
8663 });
8664
8665 define(interp, "trailing_zeros", Some(1), |_, args| {
8666 let a = match &args[0] {
8667 Value::Int(n) => *n,
8668 _ => {
8669 return Err(RuntimeError::new(
8670 "trailing_zeros: argument must be an integer",
8671 ))
8672 }
8673 };
8674 Ok(Value::Int(a.trailing_zeros() as i64))
8675 });
8676
8677 define(interp, "bit_test", Some(2), |_, args| {
8678 let a = match &args[0] {
8679 Value::Int(n) => *n,
8680 _ => {
8681 return Err(RuntimeError::new(
8682 "bit_test: first argument must be an integer",
8683 ))
8684 }
8685 };
8686 let pos = match &args[1] {
8687 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
8688 _ => return Err(RuntimeError::new("bit_test: position must be 0-63")),
8689 };
8690 Ok(Value::Bool((a >> pos) & 1 == 1))
8691 });
8692
8693 define(interp, "bit_set", Some(2), |_, args| {
8694 let a = match &args[0] {
8695 Value::Int(n) => *n,
8696 _ => {
8697 return Err(RuntimeError::new(
8698 "bit_set: first argument must be an integer",
8699 ))
8700 }
8701 };
8702 let pos = match &args[1] {
8703 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
8704 _ => return Err(RuntimeError::new("bit_set: position must be 0-63")),
8705 };
8706 Ok(Value::Int(a | (1 << pos)))
8707 });
8708
8709 define(interp, "bit_clear", Some(2), |_, args| {
8710 let a = match &args[0] {
8711 Value::Int(n) => *n,
8712 _ => {
8713 return Err(RuntimeError::new(
8714 "bit_clear: first argument must be an integer",
8715 ))
8716 }
8717 };
8718 let pos = match &args[1] {
8719 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
8720 _ => return Err(RuntimeError::new("bit_clear: position must be 0-63")),
8721 };
8722 Ok(Value::Int(a & !(1 << pos)))
8723 });
8724
8725 define(interp, "bit_toggle", Some(2), |_, args| {
8726 let a = match &args[0] {
8727 Value::Int(n) => *n,
8728 _ => {
8729 return Err(RuntimeError::new(
8730 "bit_toggle: first argument must be an integer",
8731 ))
8732 }
8733 };
8734 let pos = match &args[1] {
8735 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
8736 _ => return Err(RuntimeError::new("bit_toggle: position must be 0-63")),
8737 };
8738 Ok(Value::Int(a ^ (1 << pos)))
8739 });
8740
8741 define(interp, "to_binary", Some(1), |_, args| {
8742 let a = match &args[0] {
8743 Value::Int(n) => *n,
8744 _ => return Err(RuntimeError::new("to_binary: argument must be an integer")),
8745 };
8746 Ok(Value::String(Rc::new(format!("{:b}", a))))
8747 });
8748
8749 define(interp, "from_binary", Some(1), |_, args| {
8750 let s = match &args[0] {
8751 Value::String(s) => (**s).clone(),
8752 _ => return Err(RuntimeError::new("from_binary: argument must be a string")),
8753 };
8754 match i64::from_str_radix(&s, 2) {
8755 Ok(n) => Ok(Value::Int(n)),
8756 Err(_) => Err(RuntimeError::new("from_binary: invalid binary string")),
8757 }
8758 });
8759
8760 define(interp, "to_hex", Some(1), |_, args| {
8761 let a = match &args[0] {
8762 Value::Int(n) => *n,
8763 _ => return Err(RuntimeError::new("to_hex: argument must be an integer")),
8764 };
8765 Ok(Value::String(Rc::new(format!("{:x}", a))))
8766 });
8767
8768 define(interp, "from_hex", Some(1), |_, args| {
8769 let s = match &args[0] {
8770 Value::String(s) => s.trim_start_matches("0x").to_string(),
8771 _ => return Err(RuntimeError::new("from_hex: argument must be a string")),
8772 };
8773 match i64::from_str_radix(&s, 16) {
8774 Ok(n) => Ok(Value::Int(n)),
8775 Err(_) => Err(RuntimeError::new("from_hex: invalid hex string")),
8776 }
8777 });
8778
8779 define(interp, "to_octal", Some(1), |_, args| {
8780 let a = match &args[0] {
8781 Value::Int(n) => *n,
8782 _ => return Err(RuntimeError::new("to_octal: argument must be an integer")),
8783 };
8784 Ok(Value::String(Rc::new(format!("{:o}", a))))
8785 });
8786
8787 define(interp, "from_octal", Some(1), |_, args| {
8788 let s = match &args[0] {
8789 Value::String(s) => s.trim_start_matches("0o").to_string(),
8790 _ => return Err(RuntimeError::new("from_octal: argument must be a string")),
8791 };
8792 match i64::from_str_radix(&s, 8) {
8793 Ok(n) => Ok(Value::Int(n)),
8794 Err(_) => Err(RuntimeError::new("from_octal: invalid octal string")),
8795 }
8796 });
8797}
8798
8799fn register_format(interp: &mut Interpreter) {
8801 define(interp, "format", None, |_, args| {
8803 if args.is_empty() {
8804 return Err(RuntimeError::new(
8805 "format: requires at least a format string",
8806 ));
8807 }
8808 let template = match &args[0] {
8809 Value::String(s) => (**s).clone(),
8810 _ => return Err(RuntimeError::new("format: first argument must be a string")),
8811 };
8812 let mut result = template;
8813 for arg in &args[1..] {
8814 if let Some(pos) = result.find("{}") {
8815 result = format!("{}{}{}", &result[..pos], arg, &result[pos + 2..]);
8816 }
8817 }
8818 Ok(Value::String(Rc::new(result)))
8819 });
8820
8821 define(interp, "pad_left", Some(3), |_, args| {
8823 let s = match &args[0] {
8824 Value::String(s) => (**s).clone(),
8825 _ => {
8826 return Err(RuntimeError::new(
8827 "pad_left: first argument must be a string",
8828 ))
8829 }
8830 };
8831 let width = match &args[1] {
8832 Value::Int(n) if *n >= 0 => *n as usize,
8833 _ => {
8834 return Err(RuntimeError::new(
8835 "pad_left: width must be a non-negative integer",
8836 ))
8837 }
8838 };
8839 let pad_char = match &args[2] {
8840 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
8841 Value::Char(c) => *c,
8842 _ => {
8843 return Err(RuntimeError::new(
8844 "pad_left: pad character must be a non-empty string or char",
8845 ))
8846 }
8847 };
8848 let char_count = s.chars().count();
8849 if char_count >= width {
8850 return Ok(Value::String(Rc::new(s)));
8851 }
8852 let padding: String = std::iter::repeat(pad_char)
8853 .take(width - char_count)
8854 .collect();
8855 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
8856 });
8857
8858 define(interp, "pad_right", Some(3), |_, args| {
8860 let s = match &args[0] {
8861 Value::String(s) => (**s).clone(),
8862 _ => {
8863 return Err(RuntimeError::new(
8864 "pad_right: first argument must be a string",
8865 ))
8866 }
8867 };
8868 let width = match &args[1] {
8869 Value::Int(n) if *n >= 0 => *n as usize,
8870 _ => {
8871 return Err(RuntimeError::new(
8872 "pad_right: width must be a non-negative integer",
8873 ))
8874 }
8875 };
8876 let pad_char = match &args[2] {
8877 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
8878 Value::Char(c) => *c,
8879 _ => {
8880 return Err(RuntimeError::new(
8881 "pad_right: pad character must be a non-empty string or char",
8882 ))
8883 }
8884 };
8885 let char_count = s.chars().count();
8886 if char_count >= width {
8887 return Ok(Value::String(Rc::new(s)));
8888 }
8889 let padding: String = std::iter::repeat(pad_char)
8890 .take(width - char_count)
8891 .collect();
8892 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
8893 });
8894
8895 define(interp, "center", Some(3), |_, args| {
8897 let s = match &args[0] {
8898 Value::String(s) => (**s).clone(),
8899 _ => return Err(RuntimeError::new("center: first argument must be a string")),
8900 };
8901 let width = match &args[1] {
8902 Value::Int(n) if *n >= 0 => *n as usize,
8903 _ => {
8904 return Err(RuntimeError::new(
8905 "center: width must be a non-negative integer",
8906 ))
8907 }
8908 };
8909 let pad_char = match &args[2] {
8910 Value::String(s) if !s.is_empty() => s.chars().next().unwrap(),
8911 Value::Char(c) => *c,
8912 _ => {
8913 return Err(RuntimeError::new(
8914 "center: pad character must be a non-empty string or char",
8915 ))
8916 }
8917 };
8918 let char_count = s.chars().count();
8919 if char_count >= width {
8920 return Ok(Value::String(Rc::new(s)));
8921 }
8922 let total_padding = width - char_count;
8923 let left_padding = total_padding / 2;
8924 let right_padding = total_padding - left_padding;
8925 let left: String = std::iter::repeat(pad_char).take(left_padding).collect();
8926 let right: String = std::iter::repeat(pad_char).take(right_padding).collect();
8927 Ok(Value::String(Rc::new(format!("{}{}{}", left, s, right))))
8928 });
8929
8930 define(interp, "number_format", Some(1), |_, args| {
8932 let n = match &args[0] {
8933 Value::Int(n) => *n,
8934 Value::Float(f) => *f as i64,
8935 _ => {
8936 return Err(RuntimeError::new(
8937 "number_format: argument must be a number",
8938 ))
8939 }
8940 };
8941 let s = n.abs().to_string();
8942 let mut result = String::new();
8943 for (i, c) in s.chars().rev().enumerate() {
8944 if i > 0 && i % 3 == 0 {
8945 result.push(',');
8946 }
8947 result.push(c);
8948 }
8949 let formatted: String = result.chars().rev().collect();
8950 if n < 0 {
8951 Ok(Value::String(Rc::new(format!("-{}", formatted))))
8952 } else {
8953 Ok(Value::String(Rc::new(formatted)))
8954 }
8955 });
8956
8957 define(interp, "ordinal", Some(1), |_, args| {
8959 let n = match &args[0] {
8960 Value::Int(n) => *n,
8961 _ => return Err(RuntimeError::new("ordinal: argument must be an integer")),
8962 };
8963 let suffix = match (n % 10, n % 100) {
8964 (1, 11) => "th",
8965 (2, 12) => "th",
8966 (3, 13) => "th",
8967 (1, _) => "st",
8968 (2, _) => "nd",
8969 (3, _) => "rd",
8970 _ => "th",
8971 };
8972 Ok(Value::String(Rc::new(format!("{}{}", n, suffix))))
8973 });
8974
8975 define(interp, "pluralize", Some(3), |_, args| {
8977 let count = match &args[0] {
8978 Value::Int(n) => *n,
8979 _ => {
8980 return Err(RuntimeError::new(
8981 "pluralize: first argument must be an integer",
8982 ))
8983 }
8984 };
8985 let singular = match &args[1] {
8986 Value::String(s) => s.clone(),
8987 _ => {
8988 return Err(RuntimeError::new(
8989 "pluralize: second argument must be a string",
8990 ))
8991 }
8992 };
8993 let plural = match &args[2] {
8994 Value::String(s) => s.clone(),
8995 _ => {
8996 return Err(RuntimeError::new(
8997 "pluralize: third argument must be a string",
8998 ))
8999 }
9000 };
9001 if count == 1 || count == -1 {
9002 Ok(Value::String(singular))
9003 } else {
9004 Ok(Value::String(plural))
9005 }
9006 });
9007
9008 define(interp, "truncate", Some(2), |_, args| {
9010 let s = match &args[0] {
9011 Value::String(s) => (**s).clone(),
9012 _ => {
9013 return Err(RuntimeError::new(
9014 "truncate: first argument must be a string",
9015 ))
9016 }
9017 };
9018 let max_len = match &args[1] {
9019 Value::Int(n) if *n >= 0 => *n as usize,
9020 _ => {
9021 return Err(RuntimeError::new(
9022 "truncate: max length must be a non-negative integer",
9023 ))
9024 }
9025 };
9026 let char_count = s.chars().count();
9027 if char_count <= max_len {
9028 return Ok(Value::String(Rc::new(s)));
9029 }
9030 if max_len <= 3 {
9031 return Ok(Value::String(Rc::new(s.chars().take(max_len).collect())));
9032 }
9033 let truncated: String = s.chars().take(max_len - 3).collect();
9034 Ok(Value::String(Rc::new(format!("{}...", truncated))))
9035 });
9036
9037 define(interp, "word_wrap", Some(2), |_, args| {
9039 let s = match &args[0] {
9040 Value::String(s) => (**s).clone(),
9041 _ => {
9042 return Err(RuntimeError::new(
9043 "word_wrap: first argument must be a string",
9044 ))
9045 }
9046 };
9047 let width = match &args[1] {
9048 Value::Int(n) if *n > 0 => *n as usize,
9049 _ => {
9050 return Err(RuntimeError::new(
9051 "word_wrap: width must be a positive integer",
9052 ))
9053 }
9054 };
9055 let mut result = String::new();
9056 let mut line_len = 0;
9057 for word in s.split_whitespace() {
9058 if line_len > 0 && line_len + 1 + word.len() > width {
9059 result.push('\n');
9060 line_len = 0;
9061 } else if line_len > 0 {
9062 result.push(' ');
9063 line_len += 1;
9064 }
9065 result.push_str(word);
9066 line_len += word.len();
9067 }
9068 Ok(Value::String(Rc::new(result)))
9069 });
9070
9071 define(interp, "snake_case", Some(1), |_, args| {
9073 let s = match &args[0] {
9074 Value::String(s) => (**s).clone(),
9075 _ => return Err(RuntimeError::new("snake_case: argument must be a string")),
9076 };
9077 let mut result = String::new();
9078 for (i, c) in s.chars().enumerate() {
9079 if c.is_uppercase() {
9080 if i > 0 {
9081 result.push('_');
9082 }
9083 result.push(c.to_lowercase().next().unwrap());
9084 } else if c == ' ' || c == '-' {
9085 result.push('_');
9086 } else {
9087 result.push(c);
9088 }
9089 }
9090 Ok(Value::String(Rc::new(result)))
9091 });
9092
9093 define(interp, "camel_case", Some(1), |_, args| {
9095 let s = match &args[0] {
9096 Value::String(s) => (**s).clone(),
9097 _ => return Err(RuntimeError::new("camel_case: argument must be a string")),
9098 };
9099 let mut result = String::new();
9100 let mut capitalize_next = false;
9101 for (i, c) in s.chars().enumerate() {
9102 if c == '_' || c == '-' || c == ' ' {
9103 capitalize_next = true;
9104 } else if capitalize_next {
9105 result.push(c.to_uppercase().next().unwrap());
9106 capitalize_next = false;
9107 } else if i == 0 {
9108 result.push(c.to_lowercase().next().unwrap());
9109 } else {
9110 result.push(c);
9111 }
9112 }
9113 Ok(Value::String(Rc::new(result)))
9114 });
9115
9116 define(interp, "kebab_case", Some(1), |_, args| {
9118 let s = match &args[0] {
9119 Value::String(s) => (**s).clone(),
9120 _ => return Err(RuntimeError::new("kebab_case: argument must be a string")),
9121 };
9122 let mut result = String::new();
9123 for (i, c) in s.chars().enumerate() {
9124 if c.is_uppercase() {
9125 if i > 0 {
9126 result.push('-');
9127 }
9128 result.push(c.to_lowercase().next().unwrap());
9129 } else if c == '_' || c == ' ' {
9130 result.push('-');
9131 } else {
9132 result.push(c);
9133 }
9134 }
9135 Ok(Value::String(Rc::new(result)))
9136 });
9137
9138 define(interp, "title_case", Some(1), |_, args| {
9140 let s = match &args[0] {
9141 Value::String(s) => (**s).clone(),
9142 _ => return Err(RuntimeError::new("title_case: argument must be a string")),
9143 };
9144 let result: String = s
9145 .split_whitespace()
9146 .map(|word| {
9147 let mut chars = word.chars();
9148 match chars.next() {
9149 None => String::new(),
9150 Some(first) => {
9151 first.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase()
9152 }
9153 }
9154 })
9155 .collect::<Vec<_>>()
9156 .join(" ");
9157 Ok(Value::String(Rc::new(result)))
9158 });
9159}
9160
9161fn register_pattern(interp: &mut Interpreter) {
9169 define(interp, "type_of", Some(1), |_, args| {
9173 let type_name = match &args[0] {
9174 Value::Null => "null",
9175 Value::Bool(_) => "bool",
9176 Value::Int(_) => "int",
9177 Value::Float(_) => "float",
9178 Value::String(_) => "string",
9179 Value::Char(_) => "char",
9180 Value::Array(_) => "array",
9181 Value::Tuple(_) => "tuple",
9182 Value::Map(_) => "map",
9183 Value::Set(_) => "set",
9184 Value::Struct { name, .. } => {
9185 return Ok(Value::String(Rc::new(format!("struct:{}", name))))
9186 }
9187 Value::Variant {
9188 enum_name,
9189 variant_name,
9190 ..
9191 } => {
9192 return Ok(Value::String(Rc::new(format!(
9193 "{}::{}",
9194 enum_name, variant_name
9195 ))))
9196 }
9197 Value::Function(_) => "function",
9198 Value::BuiltIn(_) => "builtin",
9199 Value::Ref(_) => "ref",
9200 Value::Infinity => "infinity",
9201 Value::Empty => "empty",
9202 Value::Evidential { .. } => "evidential",
9203 Value::Affective { .. } => "affective",
9204 Value::Channel(_) => "channel",
9205 Value::ThreadHandle(_) => "thread",
9206 Value::Actor(_) => "actor",
9207 Value::Future(_) => "future",
9208 };
9209 Ok(Value::String(Rc::new(type_name.to_string())))
9210 });
9211
9212 define(interp, "is_type", Some(2), |_, args| {
9214 let type_name = match &args[1] {
9215 Value::String(s) => s.to_lowercase(),
9216 _ => {
9217 return Err(RuntimeError::new(
9218 "is_type: second argument must be type name string",
9219 ))
9220 }
9221 };
9222 let matches = match (&args[0], type_name.as_str()) {
9223 (Value::Null, "null") => true,
9224 (Value::Bool(_), "bool") => true,
9225 (Value::Int(_), "int") | (Value::Int(_), "integer") => true,
9226 (Value::Float(_), "float") | (Value::Float(_), "number") => true,
9227 (Value::Int(_), "number") => true,
9228 (Value::String(_), "string") => true,
9229 (Value::Array(_), "array") | (Value::Array(_), "list") => true,
9230 (Value::Tuple(_), "tuple") => true,
9231 (Value::Map(_), "map") | (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
9232 (Value::Set(_), "set") => true,
9233 (Value::Function(_), "function") | (Value::Function(_), "fn") => true,
9234 (Value::BuiltIn(_), "function") | (Value::BuiltIn(_), "builtin") => true,
9235 (Value::Struct { name, .. }, t) => t == "struct" || t == &name.to_lowercase(),
9236 (Value::Variant { enum_name, .. }, t) => {
9237 t == "variant" || t == "enum" || t == &enum_name.to_lowercase()
9238 }
9239 (Value::Channel(_), "channel") => true,
9240 (Value::ThreadHandle(_), "thread") => true,
9241 (Value::Actor(_), "actor") => true,
9242 (Value::Future(_), "future") => true,
9243 _ => false,
9244 };
9245 Ok(Value::Bool(matches))
9246 });
9247
9248 define(interp, "is_null", Some(1), |_, args| {
9250 Ok(Value::Bool(matches!(&args[0], Value::Null)))
9251 });
9252 define(interp, "is_bool", Some(1), |_, args| {
9253 Ok(Value::Bool(matches!(&args[0], Value::Bool(_))))
9254 });
9255 define(interp, "is_int", Some(1), |_, args| {
9256 Ok(Value::Bool(matches!(&args[0], Value::Int(_))))
9257 });
9258 define(interp, "is_float", Some(1), |_, args| {
9259 Ok(Value::Bool(matches!(&args[0], Value::Float(_))))
9260 });
9261 define(interp, "is_number", Some(1), |_, args| {
9262 Ok(Value::Bool(matches!(
9263 &args[0],
9264 Value::Int(_) | Value::Float(_)
9265 )))
9266 });
9267 define(interp, "is_string", Some(1), |_, args| {
9268 Ok(Value::Bool(matches!(&args[0], Value::String(_))))
9269 });
9270 define(interp, "is_array", Some(1), |_, args| {
9271 Ok(Value::Bool(matches!(&args[0], Value::Array(_))))
9272 });
9273 define(interp, "is_tuple", Some(1), |_, args| {
9274 Ok(Value::Bool(matches!(&args[0], Value::Tuple(_))))
9275 });
9276 define(interp, "is_map", Some(1), |_, args| {
9277 Ok(Value::Bool(matches!(&args[0], Value::Map(_))))
9278 });
9279 define(interp, "is_set", Some(1), |_, args| {
9280 Ok(Value::Bool(matches!(&args[0], Value::Set(_))))
9281 });
9282 define(interp, "is_function", Some(1), |_, args| {
9283 Ok(Value::Bool(matches!(
9284 &args[0],
9285 Value::Function(_) | Value::BuiltIn(_)
9286 )))
9287 });
9288 define(interp, "is_struct", Some(1), |_, args| {
9289 Ok(Value::Bool(matches!(&args[0], Value::Struct { .. })))
9290 });
9291 define(interp, "is_variant", Some(1), |_, args| {
9292 Ok(Value::Bool(matches!(&args[0], Value::Variant { .. })))
9293 });
9294 define(interp, "is_future", Some(1), |_, args| {
9295 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
9296 });
9297 define(interp, "is_channel", Some(1), |_, args| {
9298 Ok(Value::Bool(matches!(&args[0], Value::Channel(_))))
9299 });
9300
9301 define(interp, "is_empty", Some(1), |_, args| {
9303 let empty = match &args[0] {
9304 Value::Null => true,
9305 Value::String(s) => s.is_empty(),
9306 Value::Array(a) => a.borrow().is_empty(),
9307 Value::Tuple(t) => t.is_empty(),
9308 Value::Map(m) => m.borrow().is_empty(),
9309 Value::Set(s) => s.borrow().is_empty(),
9310 _ => false,
9311 };
9312 Ok(Value::Bool(empty))
9313 });
9314
9315 define(interp, "match_regex", Some(2), |_, args| {
9319 let text = match &args[0] {
9320 Value::String(s) => (**s).clone(),
9321 _ => {
9322 return Err(RuntimeError::new(
9323 "match_regex: first argument must be a string",
9324 ))
9325 }
9326 };
9327 let pattern = match &args[1] {
9328 Value::String(s) => (**s).clone(),
9329 _ => {
9330 return Err(RuntimeError::new(
9331 "match_regex: second argument must be a regex pattern string",
9332 ))
9333 }
9334 };
9335
9336 let re = match Regex::new(&pattern) {
9337 Ok(r) => r,
9338 Err(e) => {
9339 return Err(RuntimeError::new(format!(
9340 "match_regex: invalid regex: {}",
9341 e
9342 )))
9343 }
9344 };
9345
9346 match re.captures(&text) {
9347 Some(caps) => {
9348 let mut captures: Vec<Value> = Vec::new();
9349 for i in 0..caps.len() {
9350 if let Some(m) = caps.get(i) {
9351 captures.push(Value::String(Rc::new(m.as_str().to_string())));
9352 } else {
9353 captures.push(Value::Null);
9354 }
9355 }
9356 Ok(Value::Array(Rc::new(RefCell::new(captures))))
9357 }
9358 None => Ok(Value::Null),
9359 }
9360 });
9361
9362 define(interp, "match_all_regex", Some(2), |_, args| {
9364 let text = match &args[0] {
9365 Value::String(s) => (**s).clone(),
9366 _ => {
9367 return Err(RuntimeError::new(
9368 "match_all_regex: first argument must be a string",
9369 ))
9370 }
9371 };
9372 let pattern = match &args[1] {
9373 Value::String(s) => (**s).clone(),
9374 _ => {
9375 return Err(RuntimeError::new(
9376 "match_all_regex: second argument must be a regex pattern string",
9377 ))
9378 }
9379 };
9380
9381 let re = match Regex::new(&pattern) {
9382 Ok(r) => r,
9383 Err(e) => {
9384 return Err(RuntimeError::new(format!(
9385 "match_all_regex: invalid regex: {}",
9386 e
9387 )))
9388 }
9389 };
9390
9391 let matches: Vec<Value> = re
9392 .find_iter(&text)
9393 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
9394 .collect();
9395 Ok(Value::Array(Rc::new(RefCell::new(matches))))
9396 });
9397
9398 define(interp, "capture_named", Some(2), |_, args| {
9400 let text = match &args[0] {
9401 Value::String(s) => (**s).clone(),
9402 _ => {
9403 return Err(RuntimeError::new(
9404 "capture_named: first argument must be a string",
9405 ))
9406 }
9407 };
9408 let pattern = match &args[1] {
9409 Value::String(s) => (**s).clone(),
9410 _ => {
9411 return Err(RuntimeError::new(
9412 "capture_named: second argument must be a regex pattern string",
9413 ))
9414 }
9415 };
9416
9417 let re = match Regex::new(&pattern) {
9418 Ok(r) => r,
9419 Err(e) => {
9420 return Err(RuntimeError::new(format!(
9421 "capture_named: invalid regex: {}",
9422 e
9423 )))
9424 }
9425 };
9426
9427 match re.captures(&text) {
9428 Some(caps) => {
9429 let mut result: HashMap<String, Value> = HashMap::new();
9430 for name in re.capture_names().flatten() {
9431 if let Some(m) = caps.name(name) {
9432 result.insert(
9433 name.to_string(),
9434 Value::String(Rc::new(m.as_str().to_string())),
9435 );
9436 }
9437 }
9438 Ok(Value::Map(Rc::new(RefCell::new(result))))
9439 }
9440 None => Ok(Value::Null),
9441 }
9442 });
9443
9444 define(interp, "match_struct", Some(2), |_, args| {
9448 let expected_name = match &args[1] {
9449 Value::String(s) => (**s).clone(),
9450 _ => {
9451 return Err(RuntimeError::new(
9452 "match_struct: second argument must be struct name string",
9453 ))
9454 }
9455 };
9456 match &args[0] {
9457 Value::Struct { name, .. } => Ok(Value::Bool(name == &expected_name)),
9458 _ => Ok(Value::Bool(false)),
9459 }
9460 });
9461
9462 define(interp, "match_variant", Some(3), |_, args| {
9464 let expected_enum = match &args[1] {
9465 Value::String(s) => (**s).clone(),
9466 _ => {
9467 return Err(RuntimeError::new(
9468 "match_variant: second argument must be enum name string",
9469 ))
9470 }
9471 };
9472 let expected_variant = match &args[2] {
9473 Value::String(s) => (**s).clone(),
9474 _ => {
9475 return Err(RuntimeError::new(
9476 "match_variant: third argument must be variant name string",
9477 ))
9478 }
9479 };
9480 match &args[0] {
9481 Value::Variant {
9482 enum_name,
9483 variant_name,
9484 ..
9485 } => Ok(Value::Bool(
9486 enum_name == &expected_enum && variant_name == &expected_variant,
9487 )),
9488 _ => Ok(Value::Bool(false)),
9489 }
9490 });
9491
9492 define(interp, "get_field", Some(2), |_, args| {
9494 let field_name = match &args[1] {
9495 Value::String(s) => (**s).clone(),
9496 _ => {
9497 return Err(RuntimeError::new(
9498 "get_field: second argument must be field name string",
9499 ))
9500 }
9501 };
9502 match &args[0] {
9503 Value::Struct { fields, .. } => Ok(fields
9504 .borrow()
9505 .get(&field_name)
9506 .cloned()
9507 .unwrap_or(Value::Null)),
9508 Value::Map(m) => Ok(m.borrow().get(&field_name).cloned().unwrap_or(Value::Null)),
9509 _ => Ok(Value::Null),
9510 }
9511 });
9512
9513 define(interp, "has_field", Some(2), |_, args| {
9515 let field_name = match &args[1] {
9516 Value::String(s) => (**s).clone(),
9517 _ => {
9518 return Err(RuntimeError::new(
9519 "has_field: second argument must be field name string",
9520 ))
9521 }
9522 };
9523 match &args[0] {
9524 Value::Struct { fields, .. } => {
9525 Ok(Value::Bool(fields.borrow().contains_key(&field_name)))
9526 }
9527 Value::Map(m) => Ok(Value::Bool(m.borrow().contains_key(&field_name))),
9528 _ => Ok(Value::Bool(false)),
9529 }
9530 });
9531
9532 define(interp, "get_fields", Some(1), |_, args| {
9534 let fields: Vec<Value> = match &args[0] {
9535 Value::Struct { fields, .. } => fields
9536 .borrow()
9537 .keys()
9538 .map(|k| Value::String(Rc::new(k.clone())))
9539 .collect(),
9540 Value::Map(m) => m
9541 .borrow()
9542 .keys()
9543 .map(|k| Value::String(Rc::new(k.clone())))
9544 .collect(),
9545 _ => {
9546 return Err(RuntimeError::new(
9547 "get_fields: argument must be struct or map",
9548 ))
9549 }
9550 };
9551 Ok(Value::Array(Rc::new(RefCell::new(fields))))
9552 });
9553
9554 define(interp, "struct_name", Some(1), |_, args| match &args[0] {
9556 Value::Struct { name, .. } => Ok(Value::String(Rc::new(name.clone()))),
9557 _ => Ok(Value::Null),
9558 });
9559
9560 define(interp, "variant_name", Some(1), |_, args| match &args[0] {
9562 Value::Variant { variant_name, .. } => Ok(Value::String(Rc::new(variant_name.clone()))),
9563 _ => Ok(Value::Null),
9564 });
9565
9566 define(interp, "variant_data", Some(1), |_, args| match &args[0] {
9568 Value::Variant { fields, .. } => match fields {
9569 Some(f) => Ok(Value::Array(Rc::new(RefCell::new((**f).clone())))),
9570 None => Ok(Value::Null),
9571 },
9572 _ => Ok(Value::Null),
9573 });
9574
9575 define(interp, "guard", Some(2), |_, args| {
9579 if is_truthy(&args[0]) {
9580 Ok(args[1].clone())
9581 } else {
9582 Ok(Value::Null)
9583 }
9584 });
9585
9586 define(interp, "when", Some(2), |interp, args| {
9588 if is_truthy(&args[0]) {
9589 match &args[1] {
9590 Value::Function(f) => interp.call_function(f, vec![]),
9591 other => Ok(other.clone()),
9592 }
9593 } else {
9594 Ok(Value::Null)
9595 }
9596 });
9597
9598 define(interp, "unless", Some(2), |interp, args| {
9600 if !is_truthy(&args[0]) {
9601 match &args[1] {
9602 Value::Function(f) => interp.call_function(f, vec![]),
9603 other => Ok(other.clone()),
9604 }
9605 } else {
9606 Ok(Value::Null)
9607 }
9608 });
9609
9610 define(interp, "cond", Some(1), |interp, args| {
9613 let clauses = match &args[0] {
9614 Value::Array(a) => a.borrow().clone(),
9615 _ => {
9616 return Err(RuntimeError::new(
9617 "cond: argument must be array of [condition, value] pairs",
9618 ))
9619 }
9620 };
9621
9622 for clause in clauses {
9623 let pair = match &clause {
9624 Value::Array(a) => a.borrow().clone(),
9625 Value::Tuple(t) => (**t).clone(),
9626 _ => {
9627 return Err(RuntimeError::new(
9628 "cond: each clause must be [condition, value] pair",
9629 ))
9630 }
9631 };
9632 if pair.len() != 2 {
9633 return Err(RuntimeError::new(
9634 "cond: each clause must have exactly 2 elements",
9635 ));
9636 }
9637
9638 if is_truthy(&pair[0]) {
9639 return match &pair[1] {
9640 Value::Function(f) => interp.call_function(f, vec![]),
9641 other => Ok(other.clone()),
9642 };
9643 }
9644 }
9645 Ok(Value::Null)
9646 });
9647
9648 define(interp, "case", Some(2), |interp, args| {
9651 let value = &args[0];
9652 let clauses = match &args[1] {
9653 Value::Array(a) => a.borrow().clone(),
9654 _ => {
9655 return Err(RuntimeError::new(
9656 "case: second argument must be array of [pattern, result] pairs",
9657 ))
9658 }
9659 };
9660
9661 for clause in clauses {
9662 let pair = match &clause {
9663 Value::Array(a) => a.borrow().clone(),
9664 Value::Tuple(t) => (**t).clone(),
9665 _ => {
9666 return Err(RuntimeError::new(
9667 "case: each clause must be [pattern, result] pair",
9668 ))
9669 }
9670 };
9671 if pair.len() != 2 {
9672 return Err(RuntimeError::new(
9673 "case: each clause must have exactly 2 elements",
9674 ));
9675 }
9676
9677 if value_eq(value, &pair[0]) {
9678 return match &pair[1] {
9679 Value::Function(f) => interp.call_function(f, vec![value.clone()]),
9680 other => Ok(other.clone()),
9681 };
9682 }
9683 }
9684 Ok(Value::Null)
9685 });
9686
9687 define(interp, "destructure_array", Some(2), |_, args| {
9691 let arr = match &args[0] {
9692 Value::Array(a) => a.borrow().clone(),
9693 Value::Tuple(t) => (**t).clone(),
9694 _ => {
9695 return Err(RuntimeError::new(
9696 "destructure_array: first argument must be array or tuple",
9697 ))
9698 }
9699 };
9700 let indices = match &args[1] {
9701 Value::Array(a) => a.borrow().clone(),
9702 _ => {
9703 return Err(RuntimeError::new(
9704 "destructure_array: second argument must be array of indices",
9705 ))
9706 }
9707 };
9708
9709 let mut result = Vec::new();
9710 for idx in indices {
9711 match idx {
9712 Value::Int(i) => {
9713 let i = if i < 0 { arr.len() as i64 + i } else { i } as usize;
9714 result.push(arr.get(i).cloned().unwrap_or(Value::Null));
9715 }
9716 _ => result.push(Value::Null),
9717 }
9718 }
9719 Ok(Value::Array(Rc::new(RefCell::new(result))))
9720 });
9721
9722 define(interp, "destructure_map", Some(2), |_, args| {
9724 let map = match &args[0] {
9725 Value::Map(m) => m.borrow().clone(),
9726 Value::Struct { fields, .. } => fields.borrow().clone(),
9727 _ => {
9728 return Err(RuntimeError::new(
9729 "destructure_map: first argument must be map or struct",
9730 ))
9731 }
9732 };
9733 let keys = match &args[1] {
9734 Value::Array(a) => a.borrow().clone(),
9735 _ => {
9736 return Err(RuntimeError::new(
9737 "destructure_map: second argument must be array of keys",
9738 ))
9739 }
9740 };
9741
9742 let mut result = Vec::new();
9743 for key in keys {
9744 match key {
9745 Value::String(k) => {
9746 result.push(map.get(&*k).cloned().unwrap_or(Value::Null));
9747 }
9748 _ => result.push(Value::Null),
9749 }
9750 }
9751 Ok(Value::Array(Rc::new(RefCell::new(result))))
9752 });
9753
9754 define(interp, "head_tail", Some(1), |_, args| {
9756 let arr = match &args[0] {
9757 Value::Array(a) => a.borrow().clone(),
9758 _ => return Err(RuntimeError::new("head_tail: argument must be array")),
9759 };
9760
9761 if arr.is_empty() {
9762 Ok(Value::Tuple(Rc::new(vec![
9763 Value::Null,
9764 Value::Array(Rc::new(RefCell::new(vec![]))),
9765 ])))
9766 } else {
9767 let head = arr[0].clone();
9768 let tail = arr[1..].to_vec();
9769 Ok(Value::Tuple(Rc::new(vec![
9770 head,
9771 Value::Array(Rc::new(RefCell::new(tail))),
9772 ])))
9773 }
9774 });
9775
9776 define(interp, "init_last", Some(1), |_, args| {
9778 let arr = match &args[0] {
9779 Value::Array(a) => a.borrow().clone(),
9780 _ => return Err(RuntimeError::new("init_last: argument must be array")),
9781 };
9782
9783 if arr.is_empty() {
9784 Ok(Value::Tuple(Rc::new(vec![
9785 Value::Array(Rc::new(RefCell::new(vec![]))),
9786 Value::Null,
9787 ])))
9788 } else {
9789 let last = arr[arr.len() - 1].clone();
9790 let init = arr[..arr.len() - 1].to_vec();
9791 Ok(Value::Tuple(Rc::new(vec![
9792 Value::Array(Rc::new(RefCell::new(init))),
9793 last,
9794 ])))
9795 }
9796 });
9797
9798 define(interp, "split_at", Some(2), |_, args| {
9800 let arr = match &args[0] {
9801 Value::Array(a) => a.borrow().clone(),
9802 _ => return Err(RuntimeError::new("split_at: first argument must be array")),
9803 };
9804 let idx = match &args[1] {
9805 Value::Int(i) => *i as usize,
9806 _ => {
9807 return Err(RuntimeError::new(
9808 "split_at: second argument must be integer",
9809 ))
9810 }
9811 };
9812
9813 let idx = idx.min(arr.len());
9814 let left = arr[..idx].to_vec();
9815 let right = arr[idx..].to_vec();
9816 Ok(Value::Tuple(Rc::new(vec![
9817 Value::Array(Rc::new(RefCell::new(left))),
9818 Value::Array(Rc::new(RefCell::new(right))),
9819 ])))
9820 });
9821
9822 define(interp, "unwrap_or", Some(2), |_, args| {
9826 if matches!(&args[0], Value::Null) {
9827 Ok(args[1].clone())
9828 } else {
9829 Ok(args[0].clone())
9830 }
9831 });
9832
9833 define(interp, "unwrap_or_else", Some(2), |interp, args| {
9835 if matches!(&args[0], Value::Null) {
9836 match &args[1] {
9837 Value::Function(f) => interp.call_function(f, vec![]),
9838 other => Ok(other.clone()),
9839 }
9840 } else {
9841 Ok(args[0].clone())
9842 }
9843 });
9844
9845 define(interp, "map_or", Some(3), |interp, args| {
9847 if matches!(&args[0], Value::Null) {
9848 Ok(args[1].clone())
9849 } else {
9850 match &args[2] {
9851 Value::Function(f) => interp.call_function(f, vec![args[0].clone()]),
9852 _ => Err(RuntimeError::new(
9853 "map_or: third argument must be a function",
9854 )),
9855 }
9856 }
9857 });
9858
9859 define(interp, "coalesce", Some(1), |_, args| {
9861 let values = match &args[0] {
9862 Value::Array(a) => a.borrow().clone(),
9863 _ => return Err(RuntimeError::new("coalesce: argument must be array")),
9864 };
9865
9866 for v in values {
9867 if !matches!(v, Value::Null) {
9868 return Ok(v);
9869 }
9870 }
9871 Ok(Value::Null)
9872 });
9873
9874 define(interp, "deep_eq", Some(2), |_, args| {
9878 Ok(Value::Bool(deep_value_eq(&args[0], &args[1])))
9879 });
9880
9881 define(interp, "same_type", Some(2), |_, args| {
9883 let same = match (&args[0], &args[1]) {
9884 (Value::Null, Value::Null) => true,
9885 (Value::Bool(_), Value::Bool(_)) => true,
9886 (Value::Int(_), Value::Int(_)) => true,
9887 (Value::Float(_), Value::Float(_)) => true,
9888 (Value::String(_), Value::String(_)) => true,
9889 (Value::Array(_), Value::Array(_)) => true,
9890 (Value::Tuple(_), Value::Tuple(_)) => true,
9891 (Value::Map(_), Value::Map(_)) => true,
9892 (Value::Set(_), Value::Set(_)) => true,
9893 (Value::Function(_), Value::Function(_)) => true,
9894 (Value::BuiltIn(_), Value::BuiltIn(_)) => true,
9895 (Value::Struct { name: n1, .. }, Value::Struct { name: n2, .. }) => n1 == n2,
9896 (Value::Variant { enum_name: e1, .. }, Value::Variant { enum_name: e2, .. }) => {
9897 e1 == e2
9898 }
9899 _ => false,
9900 };
9901 Ok(Value::Bool(same))
9902 });
9903
9904 define(interp, "compare", Some(2), |_, args| {
9906 let cmp = match (&args[0], &args[1]) {
9907 (Value::Int(a), Value::Int(b)) => a.cmp(b),
9908 (Value::Float(a), Value::Float(b)) => {
9909 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
9910 }
9911 (Value::Int(a), Value::Float(b)) => (*a as f64)
9912 .partial_cmp(b)
9913 .unwrap_or(std::cmp::Ordering::Equal),
9914 (Value::Float(a), Value::Int(b)) => a
9915 .partial_cmp(&(*b as f64))
9916 .unwrap_or(std::cmp::Ordering::Equal),
9917 (Value::String(a), Value::String(b)) => a.cmp(b),
9918 _ => {
9919 return Err(RuntimeError::new(
9920 "compare: can only compare numbers or strings",
9921 ))
9922 }
9923 };
9924 Ok(Value::Int(match cmp {
9925 std::cmp::Ordering::Less => -1,
9926 std::cmp::Ordering::Equal => 0,
9927 std::cmp::Ordering::Greater => 1,
9928 }))
9929 });
9930
9931 define(interp, "between", Some(3), |_, args| {
9933 let in_range = match (&args[0], &args[1], &args[2]) {
9934 (Value::Int(v), Value::Int(min), Value::Int(max)) => v >= min && v <= max,
9935 (Value::Float(v), Value::Float(min), Value::Float(max)) => v >= min && v <= max,
9936 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
9937 (*v as f64) >= (*min as f64) && (*v as f64) <= *max
9938 }
9939 (Value::Int(v), Value::Float(min), Value::Int(max)) => {
9940 (*v as f64) >= *min && (*v as f64) <= (*max as f64)
9941 }
9942 (Value::Float(v), Value::Int(min), Value::Int(max)) => {
9943 *v >= (*min as f64) && *v <= (*max as f64)
9944 }
9945 (Value::String(v), Value::String(min), Value::String(max)) => v >= min && v <= max,
9946 _ => {
9947 return Err(RuntimeError::new(
9948 "between: arguments must be comparable (numbers or strings)",
9949 ))
9950 }
9951 };
9952 Ok(Value::Bool(in_range))
9953 });
9954
9955 define(interp, "clamp", Some(3), |_, args| {
9957 match (&args[0], &args[1], &args[2]) {
9958 (Value::Int(v), Value::Int(min), Value::Int(max)) => {
9959 Ok(Value::Int((*v).max(*min).min(*max)))
9960 }
9961 (Value::Float(v), Value::Float(min), Value::Float(max)) => {
9962 Ok(Value::Float(v.max(*min).min(*max)))
9963 }
9964 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
9965 Ok(Value::Float((*v as f64).max(*min as f64).min(*max)))
9966 }
9967 _ => Err(RuntimeError::new("clamp: arguments must be numbers")),
9968 }
9969 });
9970}
9971
9972fn deep_value_eq(a: &Value, b: &Value) -> bool {
9974 match (a, b) {
9975 (Value::Null, Value::Null) => true,
9976 (Value::Bool(a), Value::Bool(b)) => a == b,
9977 (Value::Int(a), Value::Int(b)) => a == b,
9978 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
9979 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
9980 (*a as f64 - b).abs() < f64::EPSILON
9981 }
9982 (Value::String(a), Value::String(b)) => a == b,
9983 (Value::Array(a), Value::Array(b)) => {
9984 let a = a.borrow();
9985 let b = b.borrow();
9986 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
9987 }
9988 (Value::Tuple(a), Value::Tuple(b)) => {
9989 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
9990 }
9991 (Value::Map(a), Value::Map(b)) => {
9992 let a = a.borrow();
9993 let b = b.borrow();
9994 a.len() == b.len()
9995 && a.iter()
9996 .all(|(k, v)| b.get(k).map_or(false, |bv| deep_value_eq(v, bv)))
9997 }
9998 (Value::Set(a), Value::Set(b)) => {
9999 let a = a.borrow();
10000 let b = b.borrow();
10001 a.len() == b.len() && a.iter().all(|k| b.contains(k))
10002 }
10003 (
10004 Value::Struct {
10005 name: n1,
10006 fields: f1,
10007 },
10008 Value::Struct {
10009 name: n2,
10010 fields: f2,
10011 },
10012 ) => {
10013 let f1 = f1.borrow();
10014 let f2 = f2.borrow();
10015 n1 == n2
10016 && f1.len() == f2.len()
10017 && f1
10018 .iter()
10019 .all(|(k, v)| f2.get(k).map_or(false, |v2| deep_value_eq(v, v2)))
10020 }
10021 (
10022 Value::Variant {
10023 enum_name: e1,
10024 variant_name: v1,
10025 fields: d1,
10026 },
10027 Value::Variant {
10028 enum_name: e2,
10029 variant_name: v2,
10030 fields: d2,
10031 },
10032 ) => {
10033 if e1 != e2 || v1 != v2 {
10034 return false;
10035 }
10036 match (d1, d2) {
10037 (Some(f1), Some(f2)) => {
10038 f1.len() == f2.len()
10039 && f1.iter().zip(f2.iter()).all(|(x, y)| deep_value_eq(x, y))
10040 }
10041 (None, None) => true,
10042 _ => false,
10043 }
10044 }
10045 _ => false,
10046 }
10047}
10048
10049fn value_eq(a: &Value, b: &Value) -> bool {
10051 match (a, b) {
10052 (Value::Null, Value::Null) => true,
10053 (Value::Bool(a), Value::Bool(b)) => a == b,
10054 (Value::Int(a), Value::Int(b)) => a == b,
10055 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
10056 (Value::String(a), Value::String(b)) => a == b,
10057 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
10058 (*a as f64 - b).abs() < f64::EPSILON
10059 }
10060 _ => false,
10061 }
10062}
10063
10064fn register_devex(interp: &mut Interpreter) {
10072 define(interp, "debug", Some(1), |_, args| {
10076 let type_name = match &args[0] {
10077 Value::Null => "null".to_string(),
10078 Value::Bool(_) => "bool".to_string(),
10079 Value::Int(_) => "int".to_string(),
10080 Value::Float(_) => "float".to_string(),
10081 Value::String(_) => "string".to_string(),
10082 Value::Char(_) => "char".to_string(),
10083 Value::Array(a) => format!("array[{}]", a.borrow().len()),
10084 Value::Tuple(t) => format!("tuple[{}]", t.len()),
10085 Value::Map(m) => format!("map[{}]", m.borrow().len()),
10086 Value::Set(s) => format!("set[{}]", s.borrow().len()),
10087 Value::Struct { name, fields } => format!("struct {}[{}]", name, fields.borrow().len()),
10088 Value::Variant {
10089 enum_name,
10090 variant_name,
10091 ..
10092 } => format!("{}::{}", enum_name, variant_name),
10093 Value::Function(_) => "function".to_string(),
10094 Value::BuiltIn(_) => "builtin".to_string(),
10095 Value::Ref(_) => "ref".to_string(),
10096 Value::Infinity => "infinity".to_string(),
10097 Value::Empty => "empty".to_string(),
10098 Value::Evidential { evidence, .. } => format!("evidential[{:?}]", evidence),
10099 Value::Affective { affect, .. } => format!("affective[sarcasm={}]", affect.sarcasm),
10100 Value::Channel(_) => "channel".to_string(),
10101 Value::ThreadHandle(_) => "thread".to_string(),
10102 Value::Actor(_) => "actor".to_string(),
10103 Value::Future(_) => "future".to_string(),
10104 };
10105 let value_repr = format_value_debug(&args[0]);
10106 println!("[DEBUG] {}: {}", type_name, value_repr);
10107 Ok(args[0].clone())
10108 });
10109
10110 define(interp, "inspect", Some(1), |_, args| {
10112 Ok(Value::String(Rc::new(format_value_debug(&args[0]))))
10113 });
10114
10115 define(interp, "dbg", Some(1), |_, args| {
10117 println!("{}", format_value_debug(&args[0]));
10118 Ok(args[0].clone())
10119 });
10120
10121 define(interp, "trace", Some(2), |_, args| {
10123 let label = match &args[0] {
10124 Value::String(s) => (**s).clone(),
10125 _ => format_value_debug(&args[0]),
10126 };
10127 println!("[TRACE] {}: {}", label, format_value_debug(&args[1]));
10128 Ok(args[1].clone())
10129 });
10130
10131 define(interp, "pp", Some(1), |_, args| {
10133 println!("{}", pretty_print_value(&args[0], 0));
10134 Ok(Value::Null)
10135 });
10136
10137 define(interp, "assert_eq", Some(2), |_, args| {
10141 if deep_value_eq(&args[0], &args[1]) {
10142 Ok(Value::Bool(true))
10143 } else {
10144 Err(RuntimeError::new(format!(
10145 "Assertion failed: expected {} to equal {}",
10146 format_value_debug(&args[0]),
10147 format_value_debug(&args[1])
10148 )))
10149 }
10150 });
10151
10152 define(interp, "assert_ne", Some(2), |_, args| {
10154 if !deep_value_eq(&args[0], &args[1]) {
10155 Ok(Value::Bool(true))
10156 } else {
10157 Err(RuntimeError::new(format!(
10158 "Assertion failed: expected {} to not equal {}",
10159 format_value_debug(&args[0]),
10160 format_value_debug(&args[1])
10161 )))
10162 }
10163 });
10164
10165 define(interp, "assert_lt", Some(2), |_, args| {
10167 let cmp = devex_compare(&args[0], &args[1])?;
10168 if cmp < 0 {
10169 Ok(Value::Bool(true))
10170 } else {
10171 Err(RuntimeError::new(format!(
10172 "Assertion failed: expected {} < {}",
10173 format_value_debug(&args[0]),
10174 format_value_debug(&args[1])
10175 )))
10176 }
10177 });
10178
10179 define(interp, "assert_le", Some(2), |_, args| {
10181 let cmp = devex_compare(&args[0], &args[1])?;
10182 if cmp <= 0 {
10183 Ok(Value::Bool(true))
10184 } else {
10185 Err(RuntimeError::new(format!(
10186 "Assertion failed: expected {} <= {}",
10187 format_value_debug(&args[0]),
10188 format_value_debug(&args[1])
10189 )))
10190 }
10191 });
10192
10193 define(interp, "assert_gt", Some(2), |_, args| {
10195 let cmp = devex_compare(&args[0], &args[1])?;
10196 if cmp > 0 {
10197 Ok(Value::Bool(true))
10198 } else {
10199 Err(RuntimeError::new(format!(
10200 "Assertion failed: expected {} > {}",
10201 format_value_debug(&args[0]),
10202 format_value_debug(&args[1])
10203 )))
10204 }
10205 });
10206
10207 define(interp, "assert_ge", Some(2), |_, args| {
10209 let cmp = devex_compare(&args[0], &args[1])?;
10210 if cmp >= 0 {
10211 Ok(Value::Bool(true))
10212 } else {
10213 Err(RuntimeError::new(format!(
10214 "Assertion failed: expected {} >= {}",
10215 format_value_debug(&args[0]),
10216 format_value_debug(&args[1])
10217 )))
10218 }
10219 });
10220
10221 define(interp, "assert_true", Some(1), |_, args| {
10223 if is_truthy(&args[0]) {
10224 Ok(Value::Bool(true))
10225 } else {
10226 Err(RuntimeError::new(format!(
10227 "Assertion failed: expected {} to be truthy",
10228 format_value_debug(&args[0])
10229 )))
10230 }
10231 });
10232
10233 define(interp, "assert_false", Some(1), |_, args| {
10235 if !is_truthy(&args[0]) {
10236 Ok(Value::Bool(true))
10237 } else {
10238 Err(RuntimeError::new(format!(
10239 "Assertion failed: expected {} to be falsy",
10240 format_value_debug(&args[0])
10241 )))
10242 }
10243 });
10244
10245 define(interp, "assert_null", Some(1), |_, args| {
10247 if matches!(&args[0], Value::Null) {
10248 Ok(Value::Bool(true))
10249 } else {
10250 Err(RuntimeError::new(format!(
10251 "Assertion failed: expected null, got {}",
10252 format_value_debug(&args[0])
10253 )))
10254 }
10255 });
10256
10257 define(interp, "assert_not_null", Some(1), |_, args| {
10259 if !matches!(&args[0], Value::Null) {
10260 Ok(Value::Bool(true))
10261 } else {
10262 Err(RuntimeError::new(
10263 "Assertion failed: expected non-null value, got null",
10264 ))
10265 }
10266 });
10267
10268 define(interp, "assert_type", Some(2), |_, args| {
10270 let expected = match &args[1] {
10271 Value::String(s) => s.to_lowercase(),
10272 _ => {
10273 return Err(RuntimeError::new(
10274 "assert_type: second argument must be type name string",
10275 ))
10276 }
10277 };
10278 let actual = get_type_name(&args[0]).to_lowercase();
10279 if actual == expected || matches_type_alias(&args[0], &expected) {
10280 Ok(Value::Bool(true))
10281 } else {
10282 Err(RuntimeError::new(format!(
10283 "Assertion failed: expected type '{}', got '{}'",
10284 expected, actual
10285 )))
10286 }
10287 });
10288
10289 define(interp, "assert_contains", Some(2), |_, args| {
10291 let contains = match &args[0] {
10292 Value::Array(a) => a.borrow().iter().any(|v| deep_value_eq(v, &args[1])),
10293 Value::String(s) => {
10294 if let Value::String(sub) = &args[1] {
10295 s.contains(&**sub)
10296 } else {
10297 false
10298 }
10299 }
10300 Value::Map(m) => {
10301 if let Value::String(k) = &args[1] {
10302 m.borrow().contains_key(&**k)
10303 } else {
10304 false
10305 }
10306 }
10307 Value::Set(s) => {
10308 if let Value::String(k) = &args[1] {
10309 s.borrow().contains(&**k)
10310 } else {
10311 false
10312 }
10313 }
10314 _ => false,
10315 };
10316 if contains {
10317 Ok(Value::Bool(true))
10318 } else {
10319 Err(RuntimeError::new(format!(
10320 "Assertion failed: {} does not contain {}",
10321 format_value_debug(&args[0]),
10322 format_value_debug(&args[1])
10323 )))
10324 }
10325 });
10326
10327 define(interp, "assert_len", Some(2), |_, args| {
10329 let expected = match &args[1] {
10330 Value::Int(n) => *n as usize,
10331 _ => {
10332 return Err(RuntimeError::new(
10333 "assert_len: second argument must be integer",
10334 ))
10335 }
10336 };
10337 let actual = match &args[0] {
10338 Value::String(s) => s.len(),
10339 Value::Array(a) => a.borrow().len(),
10340 Value::Tuple(t) => t.len(),
10341 Value::Map(m) => m.borrow().len(),
10342 Value::Set(s) => s.borrow().len(),
10343 _ => {
10344 return Err(RuntimeError::new(
10345 "assert_len: first argument must be a collection",
10346 ))
10347 }
10348 };
10349 if actual == expected {
10350 Ok(Value::Bool(true))
10351 } else {
10352 Err(RuntimeError::new(format!(
10353 "Assertion failed: expected length {}, got {}",
10354 expected, actual
10355 )))
10356 }
10357 });
10358
10359 define(interp, "assert_match", Some(2), |_, args| {
10361 let text = match &args[0] {
10362 Value::String(s) => (**s).clone(),
10363 _ => {
10364 return Err(RuntimeError::new(
10365 "assert_match: first argument must be string",
10366 ))
10367 }
10368 };
10369 let pattern = match &args[1] {
10370 Value::String(s) => (**s).clone(),
10371 _ => {
10372 return Err(RuntimeError::new(
10373 "assert_match: second argument must be regex pattern",
10374 ))
10375 }
10376 };
10377 let re =
10378 Regex::new(&pattern).map_err(|e| RuntimeError::new(format!("Invalid regex: {}", e)))?;
10379 if re.is_match(&text) {
10380 Ok(Value::Bool(true))
10381 } else {
10382 Err(RuntimeError::new(format!(
10383 "Assertion failed: '{}' does not match pattern '{}'",
10384 text, pattern
10385 )))
10386 }
10387 });
10388
10389 define(interp, "test", Some(2), |interp, args| {
10393 let name = match &args[0] {
10394 Value::String(s) => (**s).clone(),
10395 _ => {
10396 return Err(RuntimeError::new(
10397 "test: first argument must be test name string",
10398 ))
10399 }
10400 };
10401 let func = match &args[1] {
10402 Value::Function(f) => f.clone(),
10403 _ => {
10404 return Err(RuntimeError::new(
10405 "test: second argument must be test function",
10406 ))
10407 }
10408 };
10409
10410 let start = Instant::now();
10411 let result = interp.call_function(&func, vec![]);
10412 let elapsed = start.elapsed();
10413
10414 match result {
10415 Ok(_) => {
10416 println!("✓ {} ({:.2}ms)", name, elapsed.as_secs_f64() * 1000.0);
10417 Ok(Value::Bool(true))
10418 }
10419 Err(e) => {
10420 println!(
10421 "✗ {} ({:.2}ms): {}",
10422 name,
10423 elapsed.as_secs_f64() * 1000.0,
10424 e
10425 );
10426 Ok(Value::Bool(false))
10427 }
10428 }
10429 });
10430
10431 define(interp, "skip", Some(1), |_, args| {
10433 let reason = match &args[0] {
10434 Value::String(s) => (**s).clone(),
10435 _ => "skipped".to_string(),
10436 };
10437 println!("⊘ {}", reason);
10438 Ok(Value::Null)
10439 });
10440
10441 define(interp, "profile", Some(1), |interp, args| {
10445 let func = match &args[0] {
10446 Value::Function(f) => f.clone(),
10447 _ => return Err(RuntimeError::new("profile: argument must be function")),
10448 };
10449
10450 let start = Instant::now();
10451 let result = interp.call_function(&func, vec![])?;
10452 let elapsed = start.elapsed();
10453
10454 let mut timing = HashMap::new();
10455 timing.insert(
10456 "ms".to_string(),
10457 Value::Float(elapsed.as_secs_f64() * 1000.0),
10458 );
10459 timing.insert("us".to_string(), Value::Float(elapsed.as_micros() as f64));
10460 timing.insert("ns".to_string(), Value::Int(elapsed.as_nanos() as i64));
10461
10462 Ok(Value::Tuple(Rc::new(vec![
10463 result,
10464 Value::Map(Rc::new(RefCell::new(timing))),
10465 ])))
10466 });
10467
10468 define(interp, "measure", Some(2), |interp, args| {
10470 let func = match &args[0] {
10471 Value::Function(f) => f.clone(),
10472 _ => {
10473 return Err(RuntimeError::new(
10474 "measure: first argument must be function",
10475 ))
10476 }
10477 };
10478 let iterations = match &args[1] {
10479 Value::Int(n) => *n as usize,
10480 _ => {
10481 return Err(RuntimeError::new(
10482 "measure: second argument must be iteration count",
10483 ))
10484 }
10485 };
10486
10487 let mut times: Vec<f64> = Vec::new();
10488 let mut last_result = Value::Null;
10489
10490 for _ in 0..iterations {
10491 let start = Instant::now();
10492 last_result = interp.call_function(&func, vec![])?;
10493 times.push(start.elapsed().as_secs_f64() * 1000.0);
10494 }
10495
10496 let sum: f64 = times.iter().sum();
10497 let avg = sum / iterations as f64;
10498 let min = times.iter().cloned().fold(f64::INFINITY, f64::min);
10499 let max = times.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
10500
10501 let variance: f64 =
10502 times.iter().map(|t| (t - avg).powi(2)).sum::<f64>() / iterations as f64;
10503 let stddev = variance.sqrt();
10504
10505 let mut stats = HashMap::new();
10506 stats.insert("iterations".to_string(), Value::Int(iterations as i64));
10507 stats.insert("total_ms".to_string(), Value::Float(sum));
10508 stats.insert("avg_ms".to_string(), Value::Float(avg));
10509 stats.insert("min_ms".to_string(), Value::Float(min));
10510 stats.insert("max_ms".to_string(), Value::Float(max));
10511 stats.insert("stddev_ms".to_string(), Value::Float(stddev));
10512
10513 Ok(Value::Tuple(Rc::new(vec![
10514 last_result,
10515 Value::Map(Rc::new(RefCell::new(stats))),
10516 ])))
10517 });
10518
10519 define(interp, "help", Some(1), |_, args| {
10523 let name = match &args[0] {
10524 Value::String(s) => (**s).clone(),
10525 Value::BuiltIn(f) => f.name.clone(),
10526 _ => {
10527 return Err(RuntimeError::new(
10528 "help: argument must be function name or builtin",
10529 ))
10530 }
10531 };
10532
10533 let doc = get_function_doc(&name);
10535 Ok(Value::String(Rc::new(doc)))
10536 });
10537
10538 define(interp, "list_builtins", Some(0), |_, _| {
10540 let categories = vec![
10541 "Core: print, println, assert, panic, len, type_of",
10542 "Math: abs, floor, ceil, round, sqrt, pow, log, sin, cos, tan",
10543 "Collections: map, filter, reduce, zip, flatten, first, last, sort, reverse",
10544 "Strings: upper, lower, trim, split, join, contains, replace, format",
10545 "IO: read_file, write_file, file_exists, read_line",
10546 "Time: now, sleep, timestamp, format_time",
10547 "JSON: json_parse, json_stringify",
10548 "Crypto: sha256, sha512, md5, base64_encode, base64_decode",
10549 "Regex: regex_match, regex_replace, regex_split",
10550 "Pattern: type_of, is_type, match_regex, match_struct, guard, when",
10551 "DevEx: debug, inspect, trace, assert_eq, assert_ne, test, profile",
10552 ];
10553 let values: Vec<Value> = categories
10554 .iter()
10555 .map(|s| Value::String(Rc::new(s.to_string())))
10556 .collect();
10557 Ok(Value::Array(Rc::new(RefCell::new(values))))
10558 });
10559
10560 define(interp, "todo", Some(0), |_, _| {
10564 Err(RuntimeError::new("not yet implemented"))
10565 });
10566
10567 define(interp, "unreachable", Some(0), |_, _| {
10569 Err(RuntimeError::new("reached unreachable code"))
10570 });
10571
10572 define(interp, "unimplemented", Some(1), |_, args| {
10574 let msg = match &args[0] {
10575 Value::String(s) => (**s).clone(),
10576 _ => "unimplemented".to_string(),
10577 };
10578 Err(RuntimeError::new(format!("unimplemented: {}", msg)))
10579 });
10580
10581 define(interp, "deprecated", Some(2), |_, args| {
10583 let msg = match &args[0] {
10584 Value::String(s) => (**s).clone(),
10585 _ => "deprecated".to_string(),
10586 };
10587 eprintln!("[DEPRECATED] {}", msg);
10588 Ok(args[1].clone())
10589 });
10590
10591 define(interp, "version", Some(0), |_, _| {
10593 let mut info = HashMap::new();
10594 info.insert(
10595 "sigil".to_string(),
10596 Value::String(Rc::new("0.1.0".to_string())),
10597 );
10598 info.insert(
10599 "stdlib".to_string(),
10600 Value::String(Rc::new("7.0".to_string())),
10601 );
10602 info.insert(
10603 "phase".to_string(),
10604 Value::String(Rc::new("Phase 7 - DevEx".to_string())),
10605 );
10606 Ok(Value::Map(Rc::new(RefCell::new(info))))
10607 });
10608}
10609
10610fn format_value_debug(value: &Value) -> String {
10612 match value {
10613 Value::Null => "null".to_string(),
10614 Value::Bool(b) => b.to_string(),
10615 Value::Int(n) => n.to_string(),
10616 Value::Float(f) => format!("{:.6}", f),
10617 Value::String(s) => format!("\"{}\"", s),
10618 Value::Char(c) => format!("'{}'", c),
10619 Value::Array(a) => {
10620 let items: Vec<String> = a.borrow().iter().take(10).map(format_value_debug).collect();
10621 if a.borrow().len() > 10 {
10622 format!(
10623 "[{}, ... ({} more)]",
10624 items.join(", "),
10625 a.borrow().len() - 10
10626 )
10627 } else {
10628 format!("[{}]", items.join(", "))
10629 }
10630 }
10631 Value::Tuple(t) => {
10632 let items: Vec<String> = t.iter().map(format_value_debug).collect();
10633 format!("({})", items.join(", "))
10634 }
10635 Value::Map(m) => {
10636 let items: Vec<String> = m
10637 .borrow()
10638 .iter()
10639 .take(5)
10640 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
10641 .collect();
10642 if m.borrow().len() > 5 {
10643 format!(
10644 "{{{}, ... ({} more)}}",
10645 items.join(", "),
10646 m.borrow().len() - 5
10647 )
10648 } else {
10649 format!("{{{}}}", items.join(", "))
10650 }
10651 }
10652 Value::Set(s) => {
10653 let items: Vec<String> = s.borrow().iter().take(5).cloned().collect();
10654 if s.borrow().len() > 5 {
10655 format!(
10656 "#{{{}, ... ({} more)}}",
10657 items.join(", "),
10658 s.borrow().len() - 5
10659 )
10660 } else {
10661 format!("#{{{}}}", items.join(", "))
10662 }
10663 }
10664 Value::Struct { name, fields } => {
10665 let items: Vec<String> = fields
10666 .borrow()
10667 .iter()
10668 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
10669 .collect();
10670 format!("{} {{{}}}", name, items.join(", "))
10671 }
10672 Value::Variant {
10673 enum_name,
10674 variant_name,
10675 fields,
10676 } => match fields {
10677 Some(f) => {
10678 let items: Vec<String> = f.iter().map(format_value_debug).collect();
10679 format!("{}::{}({})", enum_name, variant_name, items.join(", "))
10680 }
10681 None => format!("{}::{}", enum_name, variant_name),
10682 },
10683 Value::Function(_) => "<function>".to_string(),
10684 Value::BuiltIn(f) => format!("<builtin:{}>", f.name),
10685 Value::Ref(r) => format!("&{}", format_value_debug(&r.borrow())),
10686 Value::Infinity => "∞".to_string(),
10687 Value::Empty => "∅".to_string(),
10688 Value::Evidential { value, evidence } => {
10689 format!("{:?}({})", evidence, format_value_debug(value))
10690 }
10691 Value::Affective { value, affect } => {
10692 let mut markers = Vec::new();
10693 if let Some(s) = &affect.sentiment {
10694 markers.push(format!("{:?}", s));
10695 }
10696 if affect.sarcasm {
10697 markers.push("sarcasm".to_string());
10698 }
10699 if let Some(i) = &affect.intensity {
10700 markers.push(format!("{:?}", i));
10701 }
10702 if let Some(f) = &affect.formality {
10703 markers.push(format!("{:?}", f));
10704 }
10705 if let Some(e) = &affect.emotion {
10706 markers.push(format!("{:?}", e));
10707 }
10708 if let Some(c) = &affect.confidence {
10709 markers.push(format!("{:?}", c));
10710 }
10711 format!("{}[{}]", format_value_debug(value), markers.join(","))
10712 }
10713 Value::Channel(_) => "<channel>".to_string(),
10714 Value::ThreadHandle(_) => "<thread>".to_string(),
10715 Value::Actor(_) => "<actor>".to_string(),
10716 Value::Future(_) => "<future>".to_string(),
10717 }
10718}
10719
10720fn pretty_print_value(value: &Value, indent: usize) -> String {
10722 let prefix = " ".repeat(indent);
10723 match value {
10724 Value::Array(a) => {
10725 if a.borrow().is_empty() {
10726 "[]".to_string()
10727 } else {
10728 let items: Vec<String> = a
10729 .borrow()
10730 .iter()
10731 .map(|v| {
10732 format!(
10733 "{}{}",
10734 " ".repeat(indent + 1),
10735 pretty_print_value(v, indent + 1)
10736 )
10737 })
10738 .collect();
10739 format!("[\n{}\n{}]", items.join(",\n"), prefix)
10740 }
10741 }
10742 Value::Map(m) => {
10743 if m.borrow().is_empty() {
10744 "{}".to_string()
10745 } else {
10746 let items: Vec<String> = m
10747 .borrow()
10748 .iter()
10749 .map(|(k, v)| {
10750 format!(
10751 "{}\"{}\": {}",
10752 " ".repeat(indent + 1),
10753 k,
10754 pretty_print_value(v, indent + 1)
10755 )
10756 })
10757 .collect();
10758 format!("{{\n{}\n{}}}", items.join(",\n"), prefix)
10759 }
10760 }
10761 Value::Struct { name, fields } => {
10762 if fields.borrow().is_empty() {
10763 format!("{} {{}}", name)
10764 } else {
10765 let items: Vec<String> = fields
10766 .borrow()
10767 .iter()
10768 .map(|(k, v)| {
10769 format!(
10770 "{}{}: {}",
10771 " ".repeat(indent + 1),
10772 k,
10773 pretty_print_value(v, indent + 1)
10774 )
10775 })
10776 .collect();
10777 format!("{} {{\n{}\n{}}}", name, items.join(",\n"), prefix)
10778 }
10779 }
10780 _ => format_value_debug(value),
10781 }
10782}
10783
10784fn devex_compare(a: &Value, b: &Value) -> Result<i64, RuntimeError> {
10786 match (a, b) {
10787 (Value::Int(a), Value::Int(b)) => Ok(if a < b {
10788 -1
10789 } else if a > b {
10790 1
10791 } else {
10792 0
10793 }),
10794 (Value::Float(a), Value::Float(b)) => Ok(if a < b {
10795 -1
10796 } else if a > b {
10797 1
10798 } else {
10799 0
10800 }),
10801 (Value::Int(a), Value::Float(b)) => {
10802 let a = *a as f64;
10803 Ok(if a < *b {
10804 -1
10805 } else if a > *b {
10806 1
10807 } else {
10808 0
10809 })
10810 }
10811 (Value::Float(a), Value::Int(b)) => {
10812 let b = *b as f64;
10813 Ok(if *a < b {
10814 -1
10815 } else if *a > b {
10816 1
10817 } else {
10818 0
10819 })
10820 }
10821 (Value::String(a), Value::String(b)) => Ok(if a < b {
10822 -1
10823 } else if a > b {
10824 1
10825 } else {
10826 0
10827 }),
10828 _ => Err(RuntimeError::new("cannot compare these types")),
10829 }
10830}
10831
10832fn get_type_name(value: &Value) -> String {
10834 match value {
10835 Value::Null => "null".to_string(),
10836 Value::Bool(_) => "bool".to_string(),
10837 Value::Int(_) => "int".to_string(),
10838 Value::Float(_) => "float".to_string(),
10839 Value::String(_) => "string".to_string(),
10840 Value::Char(_) => "char".to_string(),
10841 Value::Array(_) => "array".to_string(),
10842 Value::Tuple(_) => "tuple".to_string(),
10843 Value::Map(_) => "map".to_string(),
10844 Value::Set(_) => "set".to_string(),
10845 Value::Struct { name, .. } => name.clone(),
10846 Value::Variant { enum_name, .. } => enum_name.clone(),
10847 Value::Function(_) => "function".to_string(),
10848 Value::BuiltIn(_) => "builtin".to_string(),
10849 Value::Ref(_) => "ref".to_string(),
10850 Value::Infinity => "infinity".to_string(),
10851 Value::Empty => "empty".to_string(),
10852 Value::Evidential { .. } => "evidential".to_string(),
10853 Value::Affective { .. } => "affective".to_string(),
10854 Value::Channel(_) => "channel".to_string(),
10855 Value::ThreadHandle(_) => "thread".to_string(),
10856 Value::Actor(_) => "actor".to_string(),
10857 Value::Future(_) => "future".to_string(),
10858 }
10859}
10860
10861fn matches_type_alias(value: &Value, type_name: &str) -> bool {
10863 match (value, type_name) {
10864 (Value::Int(_), "number") | (Value::Float(_), "number") => true,
10865 (Value::Int(_), "integer") => true,
10866 (Value::Array(_), "list") => true,
10867 (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
10868 (Value::Function(_), "fn") | (Value::BuiltIn(_), "fn") => true,
10869 (Value::BuiltIn(_), "function") => true,
10870 _ => false,
10871 }
10872}
10873
10874fn get_function_doc(name: &str) -> String {
10876 match name {
10877 "print" => "print(value) - Print value to stdout".to_string(),
10878 "println" => "println(value) - Print value with newline".to_string(),
10879 "len" => "len(collection) - Get length of string, array, map, or set".to_string(),
10880 "type_of" => "type_of(value) - Get type name as string".to_string(),
10881 "assert" => "assert(condition) - Assert condition is truthy, panic if false".to_string(),
10882 "assert_eq" => "assert_eq(a, b) - Assert two values are deeply equal".to_string(),
10883 "debug" => "debug(value) - Print value with type info and return it".to_string(),
10884 "map" => "map(array, fn) - Apply function to each element".to_string(),
10885 "filter" => "filter(array, fn) - Keep elements where predicate is true".to_string(),
10886 "reduce" => "reduce(array, init, fn) - Fold array with function".to_string(),
10887 "range" => "range(start, end) - Create array of integers from start to end".to_string(),
10888 "sum" => "sum(array) - Sum all numeric elements".to_string(),
10889 "product" => "product(array) - Multiply all numeric elements".to_string(),
10890 "sort" => "sort(array) - Sort array in ascending order".to_string(),
10891 "reverse" => "reverse(array) - Reverse array order".to_string(),
10892 "join" => "join(array, sep) - Join array elements with separator".to_string(),
10893 "split" => "split(string, sep) - Split string by separator".to_string(),
10894 "trim" => "trim(string) - Remove leading/trailing whitespace".to_string(),
10895 "upper" => "upper(string) - Convert to uppercase".to_string(),
10896 "lower" => "lower(string) - Convert to lowercase".to_string(),
10897 _ => format!("No documentation available for '{}'", name),
10898 }
10899}
10900
10901fn register_soa(interp: &mut Interpreter) {
10913 define(interp, "aos_to_soa", Some(2), |_, args| {
10916 let arr = match &args[0] {
10917 Value::Array(arr) => arr.borrow().clone(),
10918 _ => {
10919 return Err(RuntimeError::new(
10920 "aos_to_soa: first argument must be array",
10921 ))
10922 }
10923 };
10924 let keys = match &args[1] {
10925 Value::Array(keys) => keys.borrow().clone(),
10926 _ => {
10927 return Err(RuntimeError::new(
10928 "aos_to_soa: second argument must be array of keys",
10929 ))
10930 }
10931 };
10932
10933 if arr.is_empty() {
10934 let mut result = HashMap::new();
10936 for key in &keys {
10937 if let Value::String(k) = key {
10938 result.insert((**k).clone(), Value::Array(Rc::new(RefCell::new(vec![]))));
10939 }
10940 }
10941 return Ok(Value::Map(Rc::new(RefCell::new(result))));
10942 }
10943
10944 let key_names: Vec<String> = keys
10946 .iter()
10947 .filter_map(|k| {
10948 if let Value::String(s) = k {
10949 Some((**s).clone())
10950 } else {
10951 None
10952 }
10953 })
10954 .collect();
10955
10956 let mut soa: HashMap<String, Vec<Value>> = HashMap::new();
10958 for key in &key_names {
10959 soa.insert(key.clone(), Vec::with_capacity(arr.len()));
10960 }
10961
10962 for item in &arr {
10964 match item {
10965 Value::Map(map) => {
10966 let map = map.borrow();
10967 for key in &key_names {
10968 let val = map.get(key).cloned().unwrap_or(Value::Null);
10969 soa.get_mut(key).unwrap().push(val);
10970 }
10971 }
10972 Value::Struct { fields, .. } => {
10973 let fields = fields.borrow();
10974 for key in &key_names {
10975 let val = fields.get(key).cloned().unwrap_or(Value::Null);
10976 soa.get_mut(key).unwrap().push(val);
10977 }
10978 }
10979 _ => {
10980 return Err(RuntimeError::new(
10981 "aos_to_soa: array must contain structs or maps",
10982 ))
10983 }
10984 }
10985 }
10986
10987 let result: HashMap<String, Value> = soa
10989 .into_iter()
10990 .map(|(k, v)| (k, Value::Array(Rc::new(RefCell::new(v)))))
10991 .collect();
10992
10993 Ok(Value::Map(Rc::new(RefCell::new(result))))
10994 });
10995
10996 define(interp, "soa_to_aos", Some(1), |_, args| {
10999 let soa = match &args[0] {
11000 Value::Map(map) => map.borrow().clone(),
11001 _ => return Err(RuntimeError::new("soa_to_aos: argument must be map")),
11002 };
11003
11004 if soa.is_empty() {
11005 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
11006 }
11007
11008 let len = soa
11010 .values()
11011 .next()
11012 .and_then(|v| {
11013 if let Value::Array(arr) = v {
11014 Some(arr.borrow().len())
11015 } else {
11016 None
11017 }
11018 })
11019 .unwrap_or(0);
11020
11021 let mut aos: Vec<Value> = Vec::with_capacity(len);
11023 for i in 0..len {
11024 let mut fields = HashMap::new();
11025 for (key, value) in &soa {
11026 if let Value::Array(arr) = value {
11027 let arr = arr.borrow();
11028 if i < arr.len() {
11029 fields.insert(key.clone(), arr[i].clone());
11030 }
11031 }
11032 }
11033 aos.push(Value::Map(Rc::new(RefCell::new(fields))));
11034 }
11035
11036 Ok(Value::Array(Rc::new(RefCell::new(aos))))
11037 });
11038
11039 define(interp, "soa_map", Some(3), |interp, args| {
11042 let mut soa = match &args[0] {
11043 Value::Map(map) => map.borrow().clone(),
11044 _ => return Err(RuntimeError::new("soa_map: first argument must be SoA map")),
11045 };
11046 let key = match &args[1] {
11047 Value::String(s) => (**s).clone(),
11048 _ => {
11049 return Err(RuntimeError::new(
11050 "soa_map: second argument must be key string",
11051 ))
11052 }
11053 };
11054 let func = match &args[2] {
11055 Value::Function(f) => f.clone(),
11056 _ => {
11057 return Err(RuntimeError::new(
11058 "soa_map: third argument must be a function",
11059 ))
11060 }
11061 };
11062
11063 let arr = soa
11065 .get(&key)
11066 .ok_or_else(|| RuntimeError::new(format!("soa_map: key '{}' not found", key)))?;
11067
11068 let arr_vals = match arr {
11069 Value::Array(a) => a.borrow().clone(),
11070 _ => return Err(RuntimeError::new("soa_map: key must map to array")),
11071 };
11072
11073 let results: Vec<Value> = arr_vals
11075 .iter()
11076 .map(|val| interp.call_function(&func, vec![val.clone()]))
11077 .collect::<Result<_, _>>()?;
11078
11079 soa.insert(key, Value::Array(Rc::new(RefCell::new(results))));
11081
11082 Ok(Value::Map(Rc::new(RefCell::new(soa))))
11083 });
11084
11085 define(interp, "soa_zip", Some(3), |interp, args| {
11088 let soa = match &args[0] {
11089 Value::Map(map) => map.borrow().clone(),
11090 _ => return Err(RuntimeError::new("soa_zip: first argument must be SoA map")),
11091 };
11092 let keys = match &args[1] {
11093 Value::Array(keys) => keys.borrow().clone(),
11094 _ => {
11095 return Err(RuntimeError::new(
11096 "soa_zip: second argument must be array of keys",
11097 ))
11098 }
11099 };
11100 let func = match &args[2] {
11101 Value::Function(f) => f.clone(),
11102 _ => {
11103 return Err(RuntimeError::new(
11104 "soa_zip: third argument must be a function",
11105 ))
11106 }
11107 };
11108
11109 let arrays: Vec<Vec<Value>> = keys
11111 .iter()
11112 .filter_map(|k| {
11113 if let Value::String(s) = k {
11114 if let Some(Value::Array(arr)) = soa.get(&**s) {
11115 return Some(arr.borrow().clone());
11116 }
11117 }
11118 None
11119 })
11120 .collect();
11121
11122 if arrays.is_empty() {
11123 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
11124 }
11125
11126 let len = arrays[0].len();
11127
11128 let results: Vec<Value> = (0..len)
11130 .map(|i| {
11131 let fn_args: Vec<Value> = arrays
11132 .iter()
11133 .filter_map(|arr| arr.get(i).cloned())
11134 .collect();
11135 interp.call_function(&func, fn_args)
11136 })
11137 .collect::<Result<_, _>>()?;
11138
11139 Ok(Value::Array(Rc::new(RefCell::new(results))))
11140 });
11141
11142 define(interp, "interleave", None, |_, args| {
11145 if args.is_empty() {
11146 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
11147 }
11148
11149 let arrays: Vec<Vec<Value>> = args
11150 .iter()
11151 .filter_map(|arg| {
11152 if let Value::Array(arr) = arg {
11153 Some(arr.borrow().clone())
11154 } else {
11155 None
11156 }
11157 })
11158 .collect();
11159
11160 if arrays.is_empty() {
11161 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
11162 }
11163
11164 let len = arrays[0].len();
11165 let stride = arrays.len();
11166 let mut result = Vec::with_capacity(len * stride);
11167
11168 for i in 0..len {
11169 for arr in &arrays {
11170 if let Some(val) = arr.get(i) {
11171 result.push(val.clone());
11172 }
11173 }
11174 }
11175
11176 Ok(Value::Array(Rc::new(RefCell::new(result))))
11177 });
11178
11179 define(interp, "deinterleave", Some(2), |_, args| {
11182 let arr = match &args[0] {
11183 Value::Array(arr) => arr.borrow().clone(),
11184 _ => {
11185 return Err(RuntimeError::new(
11186 "deinterleave: first argument must be array",
11187 ))
11188 }
11189 };
11190 let stride = match &args[1] {
11191 Value::Int(n) => *n as usize,
11192 _ => {
11193 return Err(RuntimeError::new(
11194 "deinterleave: second argument must be integer stride",
11195 ))
11196 }
11197 };
11198
11199 if stride == 0 {
11200 return Err(RuntimeError::new("deinterleave: stride must be > 0"));
11201 }
11202
11203 let mut result: Vec<Vec<Value>> = (0..stride).map(|_| Vec::new()).collect();
11204
11205 for (i, val) in arr.iter().enumerate() {
11206 result[i % stride].push(val.clone());
11207 }
11208
11209 Ok(Value::Array(Rc::new(RefCell::new(
11210 result
11211 .into_iter()
11212 .map(|v| Value::Array(Rc::new(RefCell::new(v))))
11213 .collect(),
11214 ))))
11215 });
11216}
11217
11218fn register_tensor(interp: &mut Interpreter) {
11224 define(interp, "outer_product", Some(2), |_, args| {
11227 let a = match &args[0] {
11228 Value::Array(arr) => arr.borrow().clone(),
11229 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
11230 };
11231 let b = match &args[1] {
11232 Value::Array(arr) => arr.borrow().clone(),
11233 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
11234 };
11235
11236 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
11238 for ai in &a {
11239 for bi in &b {
11240 let product = match (ai, bi) {
11241 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
11242 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
11243 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
11244 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
11245 _ => return Err(RuntimeError::new("outer_product: elements must be numeric")),
11246 };
11247 result.push(product);
11248 }
11249 }
11250
11251 Ok(Value::Array(Rc::new(RefCell::new(result))))
11252 });
11253
11254 define(interp, "tensor_contract", Some(4), |_, args| {
11257 let a = match &args[0] {
11258 Value::Array(arr) => arr.borrow().clone(),
11259 _ => {
11260 return Err(RuntimeError::new(
11261 "tensor_contract: first argument must be array",
11262 ))
11263 }
11264 };
11265 let b = match &args[1] {
11266 Value::Array(arr) => arr.borrow().clone(),
11267 _ => {
11268 return Err(RuntimeError::new(
11269 "tensor_contract: second argument must be array",
11270 ))
11271 }
11272 };
11273 let _axis_a = match &args[2] {
11274 Value::Int(n) => *n as usize,
11275 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
11276 };
11277 let _axis_b = match &args[3] {
11278 Value::Int(n) => *n as usize,
11279 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
11280 };
11281
11282 if a.len() != b.len() {
11284 return Err(RuntimeError::new(
11285 "tensor_contract: vectors must have same length for contraction",
11286 ));
11287 }
11288
11289 let mut sum = 0.0f64;
11290 for (ai, bi) in a.iter().zip(b.iter()) {
11291 let product = match (ai, bi) {
11292 (Value::Float(x), Value::Float(y)) => x * y,
11293 (Value::Int(x), Value::Int(y)) => (*x as f64) * (*y as f64),
11294 (Value::Float(x), Value::Int(y)) => x * (*y as f64),
11295 (Value::Int(x), Value::Float(y)) => (*x as f64) * y,
11296 _ => {
11297 return Err(RuntimeError::new(
11298 "tensor_contract: elements must be numeric",
11299 ))
11300 }
11301 };
11302 sum += product;
11303 }
11304
11305 Ok(Value::Float(sum))
11306 });
11307
11308 define(interp, "kronecker_product", Some(2), |_, args| {
11311 let a = match &args[0] {
11312 Value::Array(arr) => arr.borrow().clone(),
11313 _ => {
11314 return Err(RuntimeError::new(
11315 "kronecker_product: arguments must be arrays",
11316 ))
11317 }
11318 };
11319 let b = match &args[1] {
11320 Value::Array(arr) => arr.borrow().clone(),
11321 _ => {
11322 return Err(RuntimeError::new(
11323 "kronecker_product: arguments must be arrays",
11324 ))
11325 }
11326 };
11327
11328 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
11330 for ai in &a {
11331 for bi in &b {
11332 let product = match (ai, bi) {
11333 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
11334 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
11335 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
11336 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
11337 _ => {
11338 return Err(RuntimeError::new(
11339 "kronecker_product: elements must be numeric",
11340 ))
11341 }
11342 };
11343 result.push(product);
11344 }
11345 }
11346
11347 Ok(Value::Array(Rc::new(RefCell::new(result))))
11348 });
11349
11350 define(interp, "hadamard_product", Some(2), |_, args| {
11352 let a = match &args[0] {
11353 Value::Array(arr) => arr.borrow().clone(),
11354 _ => {
11355 return Err(RuntimeError::new(
11356 "hadamard_product: arguments must be arrays",
11357 ))
11358 }
11359 };
11360 let b = match &args[1] {
11361 Value::Array(arr) => arr.borrow().clone(),
11362 _ => {
11363 return Err(RuntimeError::new(
11364 "hadamard_product: arguments must be arrays",
11365 ))
11366 }
11367 };
11368
11369 if a.len() != b.len() {
11370 return Err(RuntimeError::new(
11371 "hadamard_product: arrays must have same length",
11372 ));
11373 }
11374
11375 let result: Vec<Value> = a
11376 .iter()
11377 .zip(b.iter())
11378 .map(|(ai, bi)| match (ai, bi) {
11379 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
11380 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
11381 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * (*y as f64))),
11382 (Value::Int(x), Value::Float(y)) => Ok(Value::Float((*x as f64) * y)),
11383 _ => Err(RuntimeError::new(
11384 "hadamard_product: elements must be numeric",
11385 )),
11386 })
11387 .collect::<Result<_, _>>()?;
11388
11389 Ok(Value::Array(Rc::new(RefCell::new(result))))
11390 });
11391
11392 define(interp, "trace", Some(2), |_, args| {
11394 let arr = match &args[0] {
11395 Value::Array(arr) => arr.borrow().clone(),
11396 _ => return Err(RuntimeError::new("trace: first argument must be array")),
11397 };
11398 let size = match &args[1] {
11399 Value::Int(n) => *n as usize,
11400 _ => {
11401 return Err(RuntimeError::new(
11402 "trace: second argument must be matrix size",
11403 ))
11404 }
11405 };
11406
11407 let mut sum = 0.0f64;
11408 for i in 0..size {
11409 let idx = i * size + i;
11410 if idx < arr.len() {
11411 sum += match &arr[idx] {
11412 Value::Float(f) => *f,
11413 Value::Int(n) => *n as f64,
11414 _ => return Err(RuntimeError::new("trace: elements must be numeric")),
11415 };
11416 }
11417 }
11418
11419 Ok(Value::Float(sum))
11420 });
11421}
11422
11423fn register_autodiff(interp: &mut Interpreter) {
11469 define(interp, "grad", None, |interp, args| {
11472 if args.len() < 2 {
11473 return Err(RuntimeError::new(
11474 "grad() requires function and point arguments.\n\
11475 Usage: grad(f, x) or grad(f, x, step_size)\n\
11476 Example:\n\
11477 fn f(x) { return x * x; }\n\
11478 let derivative = grad(f, 3.0); // Returns 6.0",
11479 ));
11480 }
11481
11482 let func = match &args[0] {
11483 Value::Function(f) => f.clone(),
11484 _ => {
11485 return Err(RuntimeError::new(
11486 "grad() first argument must be a function.\n\
11487 Got non-function value. Define a function first:\n\
11488 fn my_func(x) { return x * x; }\n\
11489 grad(my_func, 2.0)",
11490 ))
11491 }
11492 };
11493 let x = match &args[1] {
11494 Value::Float(f) => *f,
11495 Value::Int(n) => *n as f64,
11496 Value::Array(arr) => {
11497 let arr = arr.borrow().clone();
11499 let h = if args.len() > 2 {
11500 match &args[2] {
11501 Value::Float(f) => *f,
11502 Value::Int(n) => *n as f64,
11503 _ => 1e-7,
11504 }
11505 } else {
11506 1e-7
11507 };
11508
11509 let mut gradient = Vec::with_capacity(arr.len());
11510 for (i, xi) in arr.iter().enumerate() {
11511 let xi_val = match xi {
11512 Value::Float(f) => *f,
11513 Value::Int(n) => *n as f64,
11514 _ => continue,
11515 };
11516
11517 let mut x_plus = arr.clone();
11519 let mut x_minus = arr.clone();
11520 x_plus[i] = Value::Float(xi_val + h);
11521 x_minus[i] = Value::Float(xi_val - h);
11522
11523 let f_plus = interp
11524 .call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
11525 let f_minus = interp
11526 .call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
11527
11528 let grad_i = match (f_plus, f_minus) {
11529 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
11530 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
11531 _ => return Err(RuntimeError::new("grad: function must return numeric")),
11532 };
11533
11534 gradient.push(Value::Float(grad_i));
11535 }
11536
11537 return Ok(Value::Array(Rc::new(RefCell::new(gradient))));
11538 }
11539 _ => return Err(RuntimeError::new("grad: x must be numeric or array")),
11540 };
11541
11542 let h = if args.len() > 2 {
11543 match &args[2] {
11544 Value::Float(f) => *f,
11545 Value::Int(n) => *n as f64,
11546 _ => 1e-7,
11547 }
11548 } else {
11549 1e-7
11550 };
11551
11552 let f_plus = interp.call_function(&func, vec![Value::Float(x + h)])?;
11554 let f_minus = interp.call_function(&func, vec![Value::Float(x - h)])?;
11555
11556 let derivative = match (f_plus, f_minus) {
11557 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
11558 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
11559 _ => return Err(RuntimeError::new("grad: function must return numeric")),
11560 };
11561
11562 Ok(Value::Float(derivative))
11563 });
11564
11565 define(interp, "jacobian", Some(2), |interp, args| {
11567 let func = match &args[0] {
11568 Value::Function(f) => f.clone(),
11569 _ => {
11570 return Err(RuntimeError::new(
11571 "jacobian: first argument must be a function",
11572 ))
11573 }
11574 };
11575 let x = match &args[1] {
11576 Value::Array(arr) => arr.borrow().clone(),
11577 _ => return Err(RuntimeError::new("jacobian: second argument must be array")),
11578 };
11579
11580 let h = 1e-7;
11581 let n = x.len();
11582
11583 let f_x =
11585 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x.clone())))])?;
11586 let m = match &f_x {
11587 Value::Array(arr) => arr.borrow().len(),
11588 _ => 1,
11589 };
11590
11591 let mut jacobian: Vec<Value> = Vec::with_capacity(m * n);
11593
11594 for j in 0..n {
11595 let xj = match &x[j] {
11596 Value::Float(f) => *f,
11597 Value::Int(i) => *i as f64,
11598 _ => continue,
11599 };
11600
11601 let mut x_plus = x.clone();
11602 let mut x_minus = x.clone();
11603 x_plus[j] = Value::Float(xj + h);
11604 x_minus[j] = Value::Float(xj - h);
11605
11606 let f_plus =
11607 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
11608 let f_minus =
11609 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
11610
11611 match (&f_plus, &f_minus) {
11613 (Value::Array(fp), Value::Array(fm)) => {
11614 let fp = fp.borrow();
11615 let fm = fm.borrow();
11616 for i in 0..m {
11617 let dfi_dxj = match (&fp[i], &fm[i]) {
11618 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
11619 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
11620 _ => 0.0,
11621 };
11622 jacobian.push(Value::Float(dfi_dxj));
11623 }
11624 }
11625 (Value::Float(fp), Value::Float(fm)) => {
11626 jacobian.push(Value::Float((fp - fm) / (2.0 * h)));
11627 }
11628 _ => {
11629 return Err(RuntimeError::new(
11630 "jacobian: function must return array or numeric",
11631 ))
11632 }
11633 }
11634 }
11635
11636 Ok(Value::Array(Rc::new(RefCell::new(jacobian))))
11637 });
11638
11639 define(interp, "hessian", Some(2), |interp, args| {
11641 let func = match &args[0] {
11642 Value::Function(f) => f.clone(),
11643 _ => {
11644 return Err(RuntimeError::new(
11645 "hessian: first argument must be a function",
11646 ))
11647 }
11648 };
11649 let x = match &args[1] {
11650 Value::Array(arr) => arr.borrow().clone(),
11651 _ => return Err(RuntimeError::new("hessian: second argument must be array")),
11652 };
11653
11654 let h = 1e-5; let n = x.len();
11656
11657 let mut hessian: Vec<Value> = Vec::with_capacity(n * n);
11658
11659 for i in 0..n {
11660 for j in 0..n {
11661 let xi = match &x[i] {
11662 Value::Float(f) => *f,
11663 Value::Int(k) => *k as f64,
11664 _ => continue,
11665 };
11666 let xj = match &x[j] {
11667 Value::Float(f) => *f,
11668 Value::Int(k) => *k as f64,
11669 _ => continue,
11670 };
11671
11672 let mut x_pp = x.clone();
11674 let mut x_pm = x.clone();
11675 let mut x_mp = x.clone();
11676 let mut x_mm = x.clone();
11677
11678 x_pp[i] = Value::Float(xi + h);
11679 x_pp[j] = Value::Float(if i == j { xi + 2.0 * h } else { xj + h });
11680 x_pm[i] = Value::Float(xi + h);
11681 x_pm[j] = Value::Float(if i == j { xi } else { xj - h });
11682 x_mp[i] = Value::Float(xi - h);
11683 x_mp[j] = Value::Float(if i == j { xi } else { xj + h });
11684 x_mm[i] = Value::Float(xi - h);
11685 x_mm[j] = Value::Float(if i == j { xi - 2.0 * h } else { xj - h });
11686
11687 let f_pp =
11688 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pp)))])?;
11689 let f_pm =
11690 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pm)))])?;
11691 let f_mp =
11692 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mp)))])?;
11693 let f_mm =
11694 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mm)))])?;
11695
11696 let d2f = match (f_pp, f_pm, f_mp, f_mm) {
11697 (
11698 Value::Float(fpp),
11699 Value::Float(fpm),
11700 Value::Float(fmp),
11701 Value::Float(fmm),
11702 ) => (fpp - fpm - fmp + fmm) / (4.0 * h * h),
11703 _ => 0.0,
11704 };
11705
11706 hessian.push(Value::Float(d2f));
11707 }
11708 }
11709
11710 Ok(Value::Array(Rc::new(RefCell::new(hessian))))
11711 });
11712
11713 define(interp, "divergence", Some(2), |interp, args| {
11715 let func = match &args[0] {
11716 Value::Function(f) => f.clone(),
11717 _ => {
11718 return Err(RuntimeError::new(
11719 "divergence: first argument must be a function",
11720 ))
11721 }
11722 };
11723 let x = match &args[1] {
11724 Value::Array(arr) => arr.borrow().clone(),
11725 _ => {
11726 return Err(RuntimeError::new(
11727 "divergence: second argument must be array",
11728 ))
11729 }
11730 };
11731
11732 let h = 1e-7;
11733 let mut div = 0.0f64;
11734
11735 for (i, xi) in x.iter().enumerate() {
11736 let xi_val = match xi {
11737 Value::Float(f) => *f,
11738 Value::Int(n) => *n as f64,
11739 _ => continue,
11740 };
11741
11742 let mut x_plus = x.clone();
11743 let mut x_minus = x.clone();
11744 x_plus[i] = Value::Float(xi_val + h);
11745 x_minus[i] = Value::Float(xi_val - h);
11746
11747 let f_plus =
11748 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
11749 let f_minus =
11750 interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
11751
11752 let df_i = match (&f_plus, &f_minus) {
11754 (Value::Array(fp), Value::Array(fm)) => {
11755 let fp = fp.borrow();
11756 let fm = fm.borrow();
11757 if i < fp.len() && i < fm.len() {
11758 match (&fp[i], &fm[i]) {
11759 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
11760 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
11761 _ => 0.0,
11762 }
11763 } else {
11764 0.0
11765 }
11766 }
11767 _ => 0.0,
11768 };
11769
11770 div += df_i;
11771 }
11772
11773 Ok(Value::Float(div))
11774 });
11775}
11776
11777fn register_spatial(interp: &mut Interpreter) {
11783 define(interp, "spatial_hash_new", Some(1), |_, args| {
11785 let cell_size = match &args[0] {
11786 Value::Float(f) => *f,
11787 Value::Int(n) => *n as f64,
11788 _ => {
11789 return Err(RuntimeError::new(
11790 "spatial_hash_new: cell_size must be numeric",
11791 ))
11792 }
11793 };
11794
11795 let mut config = HashMap::new();
11796 config.insert("cell_size".to_string(), Value::Float(cell_size));
11797 config.insert(
11798 "buckets".to_string(),
11799 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
11800 );
11801
11802 Ok(Value::Map(Rc::new(RefCell::new(config))))
11803 });
11804
11805 define(interp, "spatial_hash_insert", Some(3), |_, args| {
11807 let hash = match &args[0] {
11808 Value::Map(map) => map.clone(),
11809 _ => {
11810 return Err(RuntimeError::new(
11811 "spatial_hash_insert: first argument must be spatial hash",
11812 ))
11813 }
11814 };
11815 let id = args[1].clone();
11816 let pos = match &args[2] {
11817 Value::Array(arr) => arr.borrow().clone(),
11818 _ => {
11819 return Err(RuntimeError::new(
11820 "spatial_hash_insert: position must be array",
11821 ))
11822 }
11823 };
11824
11825 let cell_size = {
11826 let h = hash.borrow();
11827 match h.get("cell_size") {
11828 Some(Value::Float(f)) => *f,
11829 _ => 1.0,
11830 }
11831 };
11832
11833 let key = pos
11835 .iter()
11836 .filter_map(|v| match v {
11837 Value::Float(f) => Some((*f / cell_size).floor() as i64),
11838 Value::Int(n) => Some(*n / (cell_size as i64)),
11839 _ => None,
11840 })
11841 .map(|n| n.to_string())
11842 .collect::<Vec<_>>()
11843 .join(",");
11844
11845 {
11847 let mut h = hash.borrow_mut();
11848 let buckets = h
11849 .entry("buckets".to_string())
11850 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
11851
11852 if let Value::Map(buckets_map) = buckets {
11853 let mut bm = buckets_map.borrow_mut();
11854 let bucket = bm
11855 .entry(key)
11856 .or_insert_with(|| Value::Array(Rc::new(RefCell::new(vec![]))));
11857
11858 if let Value::Array(arr) = bucket {
11859 arr.borrow_mut().push(id);
11860 }
11861 }
11862 }
11863
11864 Ok(Value::Map(hash))
11865 });
11866
11867 define(interp, "spatial_hash_query", Some(3), |_, args| {
11869 let hash = match &args[0] {
11870 Value::Map(map) => map.borrow().clone(),
11871 _ => {
11872 return Err(RuntimeError::new(
11873 "spatial_hash_query: first argument must be spatial hash",
11874 ))
11875 }
11876 };
11877 let pos = match &args[1] {
11878 Value::Array(arr) => arr.borrow().clone(),
11879 _ => {
11880 return Err(RuntimeError::new(
11881 "spatial_hash_query: position must be array",
11882 ))
11883 }
11884 };
11885 let radius = match &args[2] {
11886 Value::Float(f) => *f,
11887 Value::Int(n) => *n as f64,
11888 _ => {
11889 return Err(RuntimeError::new(
11890 "spatial_hash_query: radius must be numeric",
11891 ))
11892 }
11893 };
11894
11895 let cell_size = match hash.get("cell_size") {
11896 Some(Value::Float(f)) => *f,
11897 _ => 1.0,
11898 };
11899
11900 let center: Vec<i64> = pos
11902 .iter()
11903 .filter_map(|v| match v {
11904 Value::Float(f) => Some((*f / cell_size).floor() as i64),
11905 Value::Int(n) => Some(*n / (cell_size as i64)),
11906 _ => None,
11907 })
11908 .collect();
11909
11910 let cells_to_check = (radius / cell_size).ceil() as i64;
11912
11913 let mut results: Vec<Value> = Vec::new();
11914
11915 if let Some(Value::Map(buckets)) = hash.get("buckets") {
11916 let buckets = buckets.borrow();
11917
11918 if center.len() >= 2 {
11920 for dx in -cells_to_check..=cells_to_check {
11921 for dy in -cells_to_check..=cells_to_check {
11922 let key = format!("{},{}", center[0] + dx, center[1] + dy);
11923 if let Some(Value::Array(bucket)) = buckets.get(&key) {
11924 for item in bucket.borrow().iter() {
11925 results.push(item.clone());
11928 }
11929 }
11930 }
11931 }
11932 }
11933 }
11934
11935 Ok(Value::Array(Rc::new(RefCell::new(results))))
11936 });
11937
11938 define(interp, "aabb_new", Some(2), |_, args| {
11940 let min = match &args[0] {
11941 Value::Array(arr) => arr.borrow().clone(),
11942 _ => return Err(RuntimeError::new("aabb_new: min must be array")),
11943 };
11944 let max = match &args[1] {
11945 Value::Array(arr) => arr.borrow().clone(),
11946 _ => return Err(RuntimeError::new("aabb_new: max must be array")),
11947 };
11948
11949 let mut aabb = HashMap::new();
11950 aabb.insert("min".to_string(), Value::Array(Rc::new(RefCell::new(min))));
11951 aabb.insert("max".to_string(), Value::Array(Rc::new(RefCell::new(max))));
11952
11953 Ok(Value::Map(Rc::new(RefCell::new(aabb))))
11954 });
11955
11956 define(interp, "aabb_intersects", Some(2), |_, args| {
11958 let a = match &args[0] {
11959 Value::Map(map) => map.borrow().clone(),
11960 _ => {
11961 return Err(RuntimeError::new(
11962 "aabb_intersects: arguments must be AABBs",
11963 ))
11964 }
11965 };
11966 let b = match &args[1] {
11967 Value::Map(map) => map.borrow().clone(),
11968 _ => {
11969 return Err(RuntimeError::new(
11970 "aabb_intersects: arguments must be AABBs",
11971 ))
11972 }
11973 };
11974
11975 let a_min = extract_vec_from_map(&a, "min")?;
11976 let a_max = extract_vec_from_map(&a, "max")?;
11977 let b_min = extract_vec_from_map(&b, "min")?;
11978 let b_max = extract_vec_from_map(&b, "max")?;
11979
11980 for i in 0..a_min
11982 .len()
11983 .min(a_max.len())
11984 .min(b_min.len())
11985 .min(b_max.len())
11986 {
11987 if a_max[i] < b_min[i] || b_max[i] < a_min[i] {
11988 return Ok(Value::Bool(false));
11989 }
11990 }
11991
11992 Ok(Value::Bool(true))
11993 });
11994
11995 define(interp, "aabb_contains", Some(2), |_, args| {
11997 let aabb = match &args[0] {
11998 Value::Map(map) => map.borrow().clone(),
11999 _ => {
12000 return Err(RuntimeError::new(
12001 "aabb_contains: first argument must be AABB",
12002 ))
12003 }
12004 };
12005 let point = match &args[1] {
12006 Value::Array(arr) => arr.borrow().clone(),
12007 _ => {
12008 return Err(RuntimeError::new(
12009 "aabb_contains: second argument must be point array",
12010 ))
12011 }
12012 };
12013
12014 let min = extract_vec_from_map(&aabb, "min")?;
12015 let max = extract_vec_from_map(&aabb, "max")?;
12016
12017 for (i, p) in point.iter().enumerate() {
12018 let p_val = match p {
12019 Value::Float(f) => *f,
12020 Value::Int(n) => *n as f64,
12021 _ => continue,
12022 };
12023
12024 if i < min.len() && p_val < min[i] {
12025 return Ok(Value::Bool(false));
12026 }
12027 if i < max.len() && p_val > max[i] {
12028 return Ok(Value::Bool(false));
12029 }
12030 }
12031
12032 Ok(Value::Bool(true))
12033 });
12034}
12035
12036fn extract_vec_from_map(map: &HashMap<String, Value>, key: &str) -> Result<Vec<f64>, RuntimeError> {
12038 match map.get(key) {
12039 Some(Value::Array(arr)) => arr
12040 .borrow()
12041 .iter()
12042 .map(|v| match v {
12043 Value::Float(f) => Ok(*f),
12044 Value::Int(n) => Ok(*n as f64),
12045 _ => Err(RuntimeError::new("Expected numeric value")),
12046 })
12047 .collect(),
12048 _ => Err(RuntimeError::new(format!(
12049 "Missing or invalid '{}' in AABB",
12050 key
12051 ))),
12052 }
12053}
12054
12055fn register_physics(interp: &mut Interpreter) {
12061 define(interp, "verlet_integrate", Some(4), |_, args| {
12064 let pos = extract_vec3(&args[0], "verlet_integrate")?;
12065 let prev = extract_vec3(&args[1], "verlet_integrate")?;
12066 let accel = extract_vec3(&args[2], "verlet_integrate")?;
12067 let dt = match &args[3] {
12068 Value::Float(f) => *f,
12069 Value::Int(n) => *n as f64,
12070 _ => return Err(RuntimeError::new("verlet_integrate: dt must be numeric")),
12071 };
12072
12073 let dt2 = dt * dt;
12074 let new_pos = [
12075 pos[0] + (pos[0] - prev[0]) + accel[0] * dt2,
12076 pos[1] + (pos[1] - prev[1]) + accel[1] * dt2,
12077 pos[2] + (pos[2] - prev[2]) + accel[2] * dt2,
12078 ];
12079
12080 Ok(make_vec3_arr(new_pos))
12081 });
12082
12083 define(interp, "spring_force", Some(4), |_, args| {
12085 let p1 = extract_vec3(&args[0], "spring_force")?;
12086 let p2 = extract_vec3(&args[1], "spring_force")?;
12087 let rest_length = match &args[2] {
12088 Value::Float(f) => *f,
12089 Value::Int(n) => *n as f64,
12090 _ => {
12091 return Err(RuntimeError::new(
12092 "spring_force: rest_length must be numeric",
12093 ))
12094 }
12095 };
12096 let stiffness = match &args[3] {
12097 Value::Float(f) => *f,
12098 Value::Int(n) => *n as f64,
12099 _ => return Err(RuntimeError::new("spring_force: stiffness must be numeric")),
12100 };
12101
12102 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
12103 let length = (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]).sqrt();
12104
12105 if length < 1e-10 {
12106 return Ok(make_vec3_arr([0.0, 0.0, 0.0]));
12107 }
12108
12109 let displacement = length - rest_length;
12110 let force_mag = stiffness * displacement;
12111 let normalized = [delta[0] / length, delta[1] / length, delta[2] / length];
12112
12113 Ok(make_vec3_arr([
12114 normalized[0] * force_mag,
12115 normalized[1] * force_mag,
12116 normalized[2] * force_mag,
12117 ]))
12118 });
12119
12120 define(interp, "distance_constraint", Some(3), |_, args| {
12123 let p1 = extract_vec3(&args[0], "distance_constraint")?;
12124 let p2 = extract_vec3(&args[1], "distance_constraint")?;
12125 let target = match &args[2] {
12126 Value::Float(f) => *f,
12127 Value::Int(n) => *n as f64,
12128 _ => {
12129 return Err(RuntimeError::new(
12130 "distance_constraint: target must be numeric",
12131 ))
12132 }
12133 };
12134
12135 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
12136 let length = (delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]).sqrt();
12137
12138 if length < 1e-10 {
12139 return Ok(Value::Tuple(Rc::new(vec![
12140 make_vec3_arr(p1),
12141 make_vec3_arr(p2),
12142 ])));
12143 }
12144
12145 let correction = (length - target) / length * 0.5;
12146 let corr_vec = [
12147 delta[0] * correction,
12148 delta[1] * correction,
12149 delta[2] * correction,
12150 ];
12151
12152 let new_p1 = [
12153 p1[0] + corr_vec[0],
12154 p1[1] + corr_vec[1],
12155 p1[2] + corr_vec[2],
12156 ];
12157 let new_p2 = [
12158 p2[0] - corr_vec[0],
12159 p2[1] - corr_vec[1],
12160 p2[2] - corr_vec[2],
12161 ];
12162
12163 Ok(Value::Tuple(Rc::new(vec![
12164 make_vec3_arr(new_p1),
12165 make_vec3_arr(new_p2),
12166 ])))
12167 });
12168
12169 define(interp, "solve_constraints", Some(3), |_, args| {
12172 let mut points = match &args[0] {
12173 Value::Array(arr) => arr.borrow().clone(),
12174 _ => {
12175 return Err(RuntimeError::new(
12176 "solve_constraints: first argument must be array of points",
12177 ))
12178 }
12179 };
12180 let constraints = match &args[1] {
12181 Value::Array(arr) => arr.borrow().clone(),
12182 _ => {
12183 return Err(RuntimeError::new(
12184 "solve_constraints: second argument must be array of constraints",
12185 ))
12186 }
12187 };
12188 let iterations = match &args[2] {
12189 Value::Int(n) => *n as usize,
12190 _ => {
12191 return Err(RuntimeError::new(
12192 "solve_constraints: iterations must be integer",
12193 ))
12194 }
12195 };
12196
12197 for _ in 0..iterations {
12198 for constraint in &constraints {
12199 match constraint {
12200 Value::Map(c) => {
12201 let c = c.borrow();
12202 let constraint_type = c
12203 .get("type")
12204 .and_then(|v| {
12205 if let Value::String(s) = v {
12206 Some((**s).clone())
12207 } else {
12208 None
12209 }
12210 })
12211 .unwrap_or_default();
12212
12213 match constraint_type.as_str() {
12214 "distance" => {
12215 let indices = match c.get("indices") {
12216 Some(Value::Array(arr)) => arr.borrow().clone(),
12217 _ => continue,
12218 };
12219 let target = match c.get("distance") {
12220 Some(Value::Float(f)) => *f,
12221 Some(Value::Int(n)) => *n as f64,
12222 _ => continue,
12223 };
12224
12225 if indices.len() >= 2 {
12226 let i1 = match &indices[0] {
12227 Value::Int(n) => *n as usize,
12228 _ => continue,
12229 };
12230 let i2 = match &indices[1] {
12231 Value::Int(n) => *n as usize,
12232 _ => continue,
12233 };
12234
12235 if i1 < points.len() && i2 < points.len() {
12236 let p1 = extract_vec3(&points[i1], "solve")?;
12238 let p2 = extract_vec3(&points[i2], "solve")?;
12239
12240 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
12241 let length = (delta[0] * delta[0]
12242 + delta[1] * delta[1]
12243 + delta[2] * delta[2])
12244 .sqrt();
12245
12246 if length > 1e-10 {
12247 let correction = (length - target) / length * 0.5;
12248 let corr_vec = [
12249 delta[0] * correction,
12250 delta[1] * correction,
12251 delta[2] * correction,
12252 ];
12253
12254 points[i1] = make_vec3_arr([
12255 p1[0] + corr_vec[0],
12256 p1[1] + corr_vec[1],
12257 p1[2] + corr_vec[2],
12258 ]);
12259 points[i2] = make_vec3_arr([
12260 p2[0] - corr_vec[0],
12261 p2[1] - corr_vec[1],
12262 p2[2] - corr_vec[2],
12263 ]);
12264 }
12265 }
12266 }
12267 }
12268 _ => {}
12269 }
12270 }
12271 _ => continue,
12272 }
12273 }
12274 }
12275
12276 Ok(Value::Array(Rc::new(RefCell::new(points))))
12277 });
12278
12279 define(interp, "ray_sphere_intersect", Some(4), |_, args| {
12282 let origin = extract_vec3(&args[0], "ray_sphere_intersect")?;
12283 let dir = extract_vec3(&args[1], "ray_sphere_intersect")?;
12284 let center = extract_vec3(&args[2], "ray_sphere_intersect")?;
12285 let radius = match &args[3] {
12286 Value::Float(f) => *f,
12287 Value::Int(n) => *n as f64,
12288 _ => {
12289 return Err(RuntimeError::new(
12290 "ray_sphere_intersect: radius must be numeric",
12291 ))
12292 }
12293 };
12294
12295 let oc = [
12296 origin[0] - center[0],
12297 origin[1] - center[1],
12298 origin[2] - center[2],
12299 ];
12300
12301 let a = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2];
12302 let b = 2.0 * (oc[0] * dir[0] + oc[1] * dir[1] + oc[2] * dir[2]);
12303 let c = oc[0] * oc[0] + oc[1] * oc[1] + oc[2] * oc[2] - radius * radius;
12304
12305 let discriminant = b * b - 4.0 * a * c;
12306
12307 if discriminant < 0.0 {
12308 Ok(Value::Float(-1.0))
12309 } else {
12310 let t = (-b - discriminant.sqrt()) / (2.0 * a);
12311 if t > 0.0 {
12312 Ok(Value::Float(t))
12313 } else {
12314 let t2 = (-b + discriminant.sqrt()) / (2.0 * a);
12315 if t2 > 0.0 {
12316 Ok(Value::Float(t2))
12317 } else {
12318 Ok(Value::Float(-1.0))
12319 }
12320 }
12321 }
12322 });
12323
12324 define(interp, "ray_plane_intersect", Some(4), |_, args| {
12326 let origin = extract_vec3(&args[0], "ray_plane_intersect")?;
12327 let dir = extract_vec3(&args[1], "ray_plane_intersect")?;
12328 let plane_pt = extract_vec3(&args[2], "ray_plane_intersect")?;
12329 let normal = extract_vec3(&args[3], "ray_plane_intersect")?;
12330
12331 let denom = dir[0] * normal[0] + dir[1] * normal[1] + dir[2] * normal[2];
12332
12333 if denom.abs() < 1e-10 {
12334 return Ok(Value::Float(-1.0)); }
12336
12337 let diff = [
12338 plane_pt[0] - origin[0],
12339 plane_pt[1] - origin[1],
12340 plane_pt[2] - origin[2],
12341 ];
12342 let t = (diff[0] * normal[0] + diff[1] * normal[1] + diff[2] * normal[2]) / denom;
12343
12344 if t > 0.0 {
12345 Ok(Value::Float(t))
12346 } else {
12347 Ok(Value::Float(-1.0))
12348 }
12349 });
12350}
12351
12352fn register_geometric_algebra(interp: &mut Interpreter) {
12414 fn make_multivector(components: [f64; 8]) -> Value {
12416 let mut mv = HashMap::new();
12417 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(
12426 "_type".to_string(),
12427 Value::String(Rc::new("multivector".to_string())),
12428 );
12429 Value::Map(Rc::new(RefCell::new(mv)))
12430 }
12431
12432 fn extract_multivector(v: &Value, fn_name: &str) -> Result<[f64; 8], RuntimeError> {
12433 match v {
12434 Value::Map(map) => {
12435 let map = map.borrow();
12436 let get_component = |key: &str| -> f64 {
12437 match map.get(key) {
12438 Some(Value::Float(f)) => *f,
12439 Some(Value::Int(n)) => *n as f64,
12440 _ => 0.0,
12441 }
12442 };
12443 Ok([
12444 get_component("s"),
12445 get_component("e1"),
12446 get_component("e2"),
12447 get_component("e3"),
12448 get_component("e12"),
12449 get_component("e23"),
12450 get_component("e31"),
12451 get_component("e123"),
12452 ])
12453 }
12454 _ => Err(RuntimeError::new(format!(
12455 "{}: expected multivector",
12456 fn_name
12457 ))),
12458 }
12459 }
12460
12461 define(interp, "mv_new", Some(8), |_, args| {
12463 let mut components = [0.0f64; 8];
12464 for (i, arg) in args.iter().enumerate().take(8) {
12465 components[i] = match arg {
12466 Value::Float(f) => *f,
12467 Value::Int(n) => *n as f64,
12468 _ => 0.0,
12469 };
12470 }
12471 Ok(make_multivector(components))
12472 });
12473
12474 define(interp, "mv_scalar", Some(1), |_, args| {
12476 let s = match &args[0] {
12477 Value::Float(f) => *f,
12478 Value::Int(n) => *n as f64,
12479 _ => return Err(RuntimeError::new("mv_scalar: expected number")),
12480 };
12481 Ok(make_multivector([s, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))
12482 });
12483
12484 define(interp, "mv_vector", Some(3), |_, args| {
12486 let x = match &args[0] {
12487 Value::Float(f) => *f,
12488 Value::Int(n) => *n as f64,
12489 _ => 0.0,
12490 };
12491 let y = match &args[1] {
12492 Value::Float(f) => *f,
12493 Value::Int(n) => *n as f64,
12494 _ => 0.0,
12495 };
12496 let z = match &args[2] {
12497 Value::Float(f) => *f,
12498 Value::Int(n) => *n as f64,
12499 _ => 0.0,
12500 };
12501 Ok(make_multivector([0.0, x, y, z, 0.0, 0.0, 0.0, 0.0]))
12502 });
12503
12504 define(interp, "mv_bivector", Some(3), |_, args| {
12506 let xy = match &args[0] {
12507 Value::Float(f) => *f,
12508 Value::Int(n) => *n as f64,
12509 _ => 0.0,
12510 };
12511 let yz = match &args[1] {
12512 Value::Float(f) => *f,
12513 Value::Int(n) => *n as f64,
12514 _ => 0.0,
12515 };
12516 let zx = match &args[2] {
12517 Value::Float(f) => *f,
12518 Value::Int(n) => *n as f64,
12519 _ => 0.0,
12520 };
12521 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, xy, yz, zx, 0.0]))
12522 });
12523
12524 define(interp, "mv_trivector", Some(1), |_, args| {
12526 let xyz = match &args[0] {
12527 Value::Float(f) => *f,
12528 Value::Int(n) => *n as f64,
12529 _ => 0.0,
12530 };
12531 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, xyz]))
12532 });
12533
12534 define(interp, "mv_add", Some(2), |_, args| {
12536 let a = extract_multivector(&args[0], "mv_add")?;
12537 let b = extract_multivector(&args[1], "mv_add")?;
12538 Ok(make_multivector([
12539 a[0] + b[0],
12540 a[1] + b[1],
12541 a[2] + b[2],
12542 a[3] + b[3],
12543 a[4] + b[4],
12544 a[5] + b[5],
12545 a[6] + b[6],
12546 a[7] + b[7],
12547 ]))
12548 });
12549
12550 define(interp, "mv_sub", Some(2), |_, args| {
12552 let a = extract_multivector(&args[0], "mv_sub")?;
12553 let b = extract_multivector(&args[1], "mv_sub")?;
12554 Ok(make_multivector([
12555 a[0] - b[0],
12556 a[1] - b[1],
12557 a[2] - b[2],
12558 a[3] - b[3],
12559 a[4] - b[4],
12560 a[5] - b[5],
12561 a[6] - b[6],
12562 a[7] - b[7],
12563 ]))
12564 });
12565
12566 define(interp, "mv_scale", Some(2), |_, args| {
12568 let a = extract_multivector(&args[0], "mv_scale")?;
12569 let s = match &args[1] {
12570 Value::Float(f) => *f,
12571 Value::Int(n) => *n as f64,
12572 _ => {
12573 return Err(RuntimeError::new(
12574 "mv_scale: second argument must be number",
12575 ))
12576 }
12577 };
12578 Ok(make_multivector([
12579 a[0] * s,
12580 a[1] * s,
12581 a[2] * s,
12582 a[3] * s,
12583 a[4] * s,
12584 a[5] * s,
12585 a[6] * s,
12586 a[7] * s,
12587 ]))
12588 });
12589
12590 define(interp, "mv_geometric", Some(2), |_, args| {
12593 let a = extract_multivector(&args[0], "mv_geometric")?;
12594 let b = extract_multivector(&args[1], "mv_geometric")?;
12595
12596 let mut r = [0.0f64; 8];
12599
12600 r[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
12602 - a[4] * b[4]
12603 - a[5] * b[5]
12604 - a[6] * b[6]
12605 - a[7] * b[7];
12606
12607 r[1] = a[0] * b[1] + a[1] * b[0] - a[2] * b[4] + a[3] * b[6] + a[4] * b[2]
12609 - a[5] * b[7]
12610 - a[6] * b[3]
12611 - a[7] * b[5];
12612
12613 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]
12615 - a[6] * b[7]
12616 - a[7] * b[6];
12617
12618 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]
12620 + a[6] * b[1]
12621 - a[7] * b[4];
12622
12623 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]
12625 - a[6] * b[5]
12626 + a[7] * b[3];
12627
12628 r[5] = a[0] * b[5] + a[1] * b[7] + a[2] * b[3] - a[3] * b[2] - a[4] * b[6]
12630 + a[5] * b[0]
12631 + a[6] * b[4]
12632 + a[7] * b[1];
12633
12634 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]
12636 + a[6] * b[0]
12637 + a[7] * b[2];
12638
12639 r[7] = a[0] * b[7]
12641 + a[1] * b[5]
12642 + a[2] * b[6]
12643 + a[3] * b[4]
12644 + a[4] * b[3]
12645 + a[5] * b[1]
12646 + a[6] * b[2]
12647 + a[7] * b[0];
12648
12649 Ok(make_multivector(r))
12650 });
12651
12652 define(interp, "mv_wedge", Some(2), |_, args| {
12655 let a = extract_multivector(&args[0], "mv_wedge")?;
12656 let b = extract_multivector(&args[1], "mv_wedge")?;
12657
12658 let mut r = [0.0f64; 8];
12659
12660 r[0] = a[0] * b[0];
12662
12663 r[1] = a[0] * b[1] + a[1] * b[0];
12665 r[2] = a[0] * b[2] + a[2] * b[0];
12666 r[3] = a[0] * b[3] + a[3] * b[0];
12667
12668 r[4] = a[0] * b[4] + a[1] * b[2] - a[2] * b[1] + a[4] * b[0];
12670 r[5] = a[0] * b[5] + a[2] * b[3] - a[3] * b[2] + a[5] * b[0];
12671 r[6] = a[0] * b[6] + a[3] * b[1] - a[1] * b[3] + a[6] * b[0];
12672
12673 r[7] = a[0] * b[7] + a[7] * b[0] + a[1] * b[5] + a[2] * b[6] + a[3] * b[4]
12675 - a[4] * b[3]
12676 - a[5] * b[1]
12677 - a[6] * b[2];
12678
12679 Ok(make_multivector(r))
12680 });
12681
12682 define(interp, "mv_inner", Some(2), |_, args| {
12685 let a = extract_multivector(&args[0], "mv_inner")?;
12686 let b = extract_multivector(&args[1], "mv_inner")?;
12687
12688 let mut r = [0.0f64; 8];
12689
12690 r[0] = a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
12693 - a[4] * b[4]
12694 - a[5] * b[5]
12695 - a[6] * b[6]
12696 - a[7] * b[7];
12697
12698 r[1] = a[4] * b[2] - a[6] * b[3] - a[5] * b[7];
12700 r[2] = -a[4] * b[1] + a[5] * b[3] - a[6] * b[7];
12701 r[3] = a[6] * b[1] - a[5] * b[2] - a[4] * b[7];
12702
12703 r[4] = a[7] * b[3];
12705 r[5] = a[7] * b[1];
12706 r[6] = a[7] * b[2];
12707
12708 Ok(make_multivector(r))
12709 });
12710
12711 define(interp, "mv_reverse", Some(1), |_, args| {
12714 let a = extract_multivector(&args[0], "mv_reverse")?;
12715 Ok(make_multivector([
12717 a[0], a[1], a[2], a[3], -a[4], -a[5], -a[6], -a[7],
12718 ]))
12719 });
12720
12721 define(interp, "mv_dual", Some(1), |_, args| {
12724 let a = extract_multivector(&args[0], "mv_dual")?;
12725 Ok(make_multivector([
12728 -a[7], -a[5], -a[6], -a[4], a[3], a[1], a[2], a[0], ]))
12737 });
12738
12739 define(interp, "mv_magnitude", Some(1), |_, args| {
12741 let a = extract_multivector(&args[0], "mv_magnitude")?;
12742 let mag_sq = a[0] * a[0]
12743 + a[1] * a[1]
12744 + a[2] * a[2]
12745 + a[3] * a[3]
12746 + a[4] * a[4]
12747 + a[5] * a[5]
12748 + a[6] * a[6]
12749 + a[7] * a[7];
12750 Ok(Value::Float(mag_sq.sqrt()))
12751 });
12752
12753 define(interp, "mv_normalize", Some(1), |_, args| {
12755 let a = extract_multivector(&args[0], "mv_normalize")?;
12756 let mag = (a[0] * a[0]
12757 + a[1] * a[1]
12758 + a[2] * a[2]
12759 + a[3] * a[3]
12760 + a[4] * a[4]
12761 + a[5] * a[5]
12762 + a[6] * a[6]
12763 + a[7] * a[7])
12764 .sqrt();
12765 if mag < 1e-10 {
12766 return Ok(make_multivector([0.0; 8]));
12767 }
12768 Ok(make_multivector([
12769 a[0] / mag,
12770 a[1] / mag,
12771 a[2] / mag,
12772 a[3] / mag,
12773 a[4] / mag,
12774 a[5] / mag,
12775 a[6] / mag,
12776 a[7] / mag,
12777 ]))
12778 });
12779
12780 define(interp, "rotor_from_axis_angle", Some(2), |_, args| {
12783 let axis = extract_vec3(&args[0], "rotor_from_axis_angle")?;
12784 let angle = match &args[1] {
12785 Value::Float(f) => *f,
12786 Value::Int(n) => *n as f64,
12787 _ => {
12788 return Err(RuntimeError::new(
12789 "rotor_from_axis_angle: angle must be number",
12790 ))
12791 }
12792 };
12793
12794 let len = (axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]).sqrt();
12796 if len < 1e-10 {
12797 return Ok(make_multivector([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]));
12799 }
12800 let (nx, ny, nz) = (axis[0] / len, axis[1] / len, axis[2] / len);
12801
12802 let half_angle = angle / 2.0;
12803 let (s, c) = half_angle.sin_cos();
12804
12805 Ok(make_multivector([
12808 c, 0.0,
12810 0.0,
12811 0.0, -s * nz, -s * nx, -s * ny, 0.0, ]))
12817 });
12818
12819 define(interp, "rotor_apply", Some(2), |_, args| {
12822 let r = extract_multivector(&args[0], "rotor_apply")?;
12823 let v = extract_vec3(&args[1], "rotor_apply")?;
12824
12825 let v_mv = [0.0, v[0], v[1], v[2], 0.0, 0.0, 0.0, 0.0];
12827
12828 let r_rev = [r[0], r[1], r[2], r[3], -r[4], -r[5], -r[6], -r[7]];
12830
12831 let mut rv = [0.0f64; 8];
12833 rv[0] = r[0] * v_mv[0] + r[1] * v_mv[1] + r[2] * v_mv[2] + r[3] * v_mv[3]
12834 - r[4] * v_mv[4]
12835 - r[5] * v_mv[5]
12836 - r[6] * v_mv[6]
12837 - r[7] * v_mv[7];
12838 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]
12839 - r[5] * v_mv[7]
12840 - r[6] * v_mv[3]
12841 - r[7] * v_mv[5];
12842 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]
12843 + r[5] * v_mv[3]
12844 - r[6] * v_mv[7]
12845 - r[7] * v_mv[6];
12846 rv[3] = r[0] * v_mv[3] - r[1] * v_mv[6] + r[2] * v_mv[5] + r[3] * v_mv[0]
12847 - r[4] * v_mv[7]
12848 - r[5] * v_mv[2]
12849 + r[6] * v_mv[1]
12850 - r[7] * v_mv[4];
12851 rv[4] = r[0] * v_mv[4] + r[1] * v_mv[2] - r[2] * v_mv[1]
12852 + r[3] * v_mv[7]
12853 + r[4] * v_mv[0]
12854 + r[5] * v_mv[6]
12855 - r[6] * v_mv[5]
12856 + r[7] * v_mv[3];
12857 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]
12858 + r[5] * v_mv[0]
12859 + r[6] * v_mv[4]
12860 + r[7] * v_mv[1];
12861 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]
12862 - r[5] * v_mv[4]
12863 + r[6] * v_mv[0]
12864 + r[7] * v_mv[2];
12865 rv[7] = r[0] * v_mv[7]
12866 + r[1] * v_mv[5]
12867 + r[2] * v_mv[6]
12868 + r[3] * v_mv[4]
12869 + r[4] * v_mv[3]
12870 + r[5] * v_mv[1]
12871 + r[6] * v_mv[2]
12872 + r[7] * v_mv[0];
12873
12874 let mut result = [0.0f64; 8];
12876 result[1] = rv[0] * r_rev[1] + rv[1] * r_rev[0] - rv[2] * r_rev[4]
12877 + rv[3] * r_rev[6]
12878 + rv[4] * r_rev[2]
12879 - rv[5] * r_rev[7]
12880 - rv[6] * r_rev[3]
12881 - rv[7] * r_rev[5];
12882 result[2] = rv[0] * r_rev[2] + rv[1] * r_rev[4] + rv[2] * r_rev[0]
12883 - rv[3] * r_rev[5]
12884 - rv[4] * r_rev[1]
12885 + rv[5] * r_rev[3]
12886 - rv[6] * r_rev[7]
12887 - rv[7] * r_rev[6];
12888 result[3] = rv[0] * r_rev[3] - rv[1] * r_rev[6] + rv[2] * r_rev[5] + rv[3] * r_rev[0]
12889 - rv[4] * r_rev[7]
12890 - rv[5] * r_rev[2]
12891 + rv[6] * r_rev[1]
12892 - rv[7] * r_rev[4];
12893
12894 Ok(make_vec3(result[1], result[2], result[3]))
12896 });
12897
12898 define(interp, "rotor_compose", Some(2), |_, args| {
12900 let a = extract_multivector(&args[0], "rotor_compose")?;
12901 let b = extract_multivector(&args[1], "rotor_compose")?;
12902
12903 let mut r = [0.0f64; 8];
12905 r[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
12906 - a[4] * b[4]
12907 - a[5] * b[5]
12908 - a[6] * b[6]
12909 - a[7] * b[7];
12910 r[1] = a[0] * b[1] + a[1] * b[0] - a[2] * b[4] + a[3] * b[6] + a[4] * b[2]
12911 - a[5] * b[7]
12912 - a[6] * b[3]
12913 - a[7] * b[5];
12914 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]
12915 - a[6] * b[7]
12916 - a[7] * b[6];
12917 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]
12918 + a[6] * b[1]
12919 - a[7] * b[4];
12920 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]
12921 - a[6] * b[5]
12922 + a[7] * b[3];
12923 r[5] = a[0] * b[5] + a[1] * b[7] + a[2] * b[3] - a[3] * b[2] - a[4] * b[6]
12924 + a[5] * b[0]
12925 + a[6] * b[4]
12926 + a[7] * b[1];
12927 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]
12928 + a[6] * b[0]
12929 + a[7] * b[2];
12930 r[7] = a[0] * b[7]
12931 + a[1] * b[5]
12932 + a[2] * b[6]
12933 + a[3] * b[4]
12934 + a[4] * b[3]
12935 + a[5] * b[1]
12936 + a[6] * b[2]
12937 + a[7] * b[0];
12938
12939 Ok(make_multivector(r))
12940 });
12941
12942 define(interp, "mv_reflect", Some(2), |_, args| {
12945 let v = extract_vec3(&args[0], "mv_reflect")?;
12946 let n = extract_vec3(&args[1], "mv_reflect")?;
12947
12948 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
12950 if len < 1e-10 {
12951 return Ok(make_vec3(v[0], v[1], v[2]));
12952 }
12953 let (nx, ny, nz) = (n[0] / len, n[1] / len, n[2] / len);
12954
12955 let dot = v[0] * nx + v[1] * ny + v[2] * nz;
12957 Ok(make_vec3(
12958 v[0] - 2.0 * dot * nx,
12959 v[1] - 2.0 * dot * ny,
12960 v[2] - 2.0 * dot * nz,
12961 ))
12962 });
12963
12964 define(interp, "mv_project", Some(2), |_, args| {
12966 let v = extract_vec3(&args[0], "mv_project")?;
12967 let n = extract_vec3(&args[1], "mv_project")?;
12968
12969 let len = (n[0] * n[0] + n[1] * n[1] + n[2] * n[2]).sqrt();
12970 if len < 1e-10 {
12971 return Ok(make_vec3(v[0], v[1], v[2]));
12972 }
12973 let (nx, ny, nz) = (n[0] / len, n[1] / len, n[2] / len);
12974
12975 let dot = v[0] * nx + v[1] * ny + v[2] * nz;
12977 Ok(make_vec3(v[0] - dot * nx, v[1] - dot * ny, v[2] - dot * nz))
12978 });
12979
12980 define(interp, "mv_grade", Some(2), |_, args| {
12982 let a = extract_multivector(&args[0], "mv_grade")?;
12983 let k = match &args[1] {
12984 Value::Int(n) => *n,
12985 _ => return Err(RuntimeError::new("mv_grade: grade must be integer")),
12986 };
12987
12988 match k {
12989 0 => Ok(make_multivector([a[0], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])),
12990 1 => Ok(make_multivector([
12991 0.0, a[1], a[2], a[3], 0.0, 0.0, 0.0, 0.0,
12992 ])),
12993 2 => Ok(make_multivector([
12994 0.0, 0.0, 0.0, 0.0, a[4], a[5], a[6], 0.0,
12995 ])),
12996 3 => Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, a[7]])),
12997 _ => Ok(make_multivector([0.0; 8])),
12998 }
12999 });
13000}
13001
13002fn register_dimensional(interp: &mut Interpreter) {
13009 fn make_quantity(value: f64, units: [i32; 7]) -> Value {
13012 let mut q = HashMap::new();
13013 q.insert("value".to_string(), Value::Float(value));
13014 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(
13022 "_type".to_string(),
13023 Value::String(Rc::new("quantity".to_string())),
13024 );
13025 Value::Map(Rc::new(RefCell::new(q)))
13026 }
13027
13028 fn extract_quantity(v: &Value, fn_name: &str) -> Result<(f64, [i32; 7]), RuntimeError> {
13029 match v {
13030 Value::Map(map) => {
13031 let map = map.borrow();
13032 let value = match map.get("value") {
13033 Some(Value::Float(f)) => *f,
13034 Some(Value::Int(n)) => *n as f64,
13035 _ => return Err(RuntimeError::new(format!("{}: missing value", fn_name))),
13036 };
13037 let get_exp = |key: &str| -> i32 {
13038 match map.get(key) {
13039 Some(Value::Int(n)) => *n as i32,
13040 _ => 0,
13041 }
13042 };
13043 Ok((
13044 value,
13045 [
13046 get_exp("m"),
13047 get_exp("kg"),
13048 get_exp("s"),
13049 get_exp("A"),
13050 get_exp("K"),
13051 get_exp("mol"),
13052 get_exp("cd"),
13053 ],
13054 ))
13055 }
13056 Value::Float(f) => Ok((*f, [0; 7])),
13057 Value::Int(n) => Ok((*n as f64, [0; 7])),
13058 _ => Err(RuntimeError::new(format!(
13059 "{}: expected quantity or number",
13060 fn_name
13061 ))),
13062 }
13063 }
13064
13065 fn units_to_string(units: [i32; 7]) -> String {
13066 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
13067 let mut parts = Vec::new();
13068 for (i, &exp) in units.iter().enumerate() {
13069 if exp == 1 {
13070 parts.push(names[i].to_string());
13071 } else if exp != 0 {
13072 parts.push(format!("{}^{}", names[i], exp));
13073 }
13074 }
13075 if parts.is_empty() {
13076 "dimensionless".to_string()
13077 } else {
13078 parts.join("·")
13079 }
13080 }
13081
13082 define(interp, "qty", Some(2), |_, args| {
13085 let value = match &args[0] {
13086 Value::Float(f) => *f,
13087 Value::Int(n) => *n as f64,
13088 _ => return Err(RuntimeError::new("qty: first argument must be number")),
13089 };
13090 let unit_str = match &args[1] {
13091 Value::String(s) => s.to_string(),
13092 _ => {
13093 return Err(RuntimeError::new(
13094 "qty: second argument must be unit string",
13095 ))
13096 }
13097 };
13098
13099 let mut units = [0i32; 7];
13101 let in_denominator = unit_str.contains('/');
13104
13105 for part in unit_str.split(|c: char| c == '*' || c == '·' || c == ' ') {
13107 let part = part.trim();
13108 if part.is_empty() {
13109 continue;
13110 }
13111
13112 let (base, exp) = if let Some(idx) = part.find('^') {
13113 let (b, e) = part.split_at(idx);
13114 (b, e[1..].parse::<i32>().unwrap_or(1))
13115 } else if part.contains('/') {
13116 continue;
13118 } else {
13119 (part, 1)
13120 };
13121
13122 let sign = if in_denominator { -1 } else { 1 };
13123 match base {
13124 "m" | "meter" | "meters" => units[0] += exp * sign,
13125 "kg" | "kilogram" | "kilograms" => units[1] += exp * sign,
13126 "s" | "sec" | "second" | "seconds" => units[2] += exp * sign,
13127 "A" | "amp" | "ampere" | "amperes" => units[3] += exp * sign,
13128 "K" | "kelvin" => units[4] += exp * sign,
13129 "mol" | "mole" | "moles" => units[5] += exp * sign,
13130 "cd" | "candela" => units[6] += exp * sign,
13131 "N" | "newton" | "newtons" => {
13133 units[1] += sign;
13134 units[0] += sign;
13135 units[2] -= 2 * sign;
13136 }
13137 "J" | "joule" | "joules" => {
13138 units[1] += sign;
13139 units[0] += 2 * sign;
13140 units[2] -= 2 * sign;
13141 }
13142 "W" | "watt" | "watts" => {
13143 units[1] += sign;
13144 units[0] += 2 * sign;
13145 units[2] -= 3 * sign;
13146 }
13147 "Pa" | "pascal" | "pascals" => {
13148 units[1] += sign;
13149 units[0] -= sign;
13150 units[2] -= 2 * sign;
13151 }
13152 "Hz" | "hertz" => {
13153 units[2] -= sign;
13154 }
13155 "C" | "coulomb" | "coulombs" => {
13156 units[3] += sign;
13157 units[2] += sign;
13158 }
13159 "V" | "volt" | "volts" => {
13160 units[1] += sign;
13161 units[0] += 2 * sign;
13162 units[2] -= 3 * sign;
13163 units[3] -= sign;
13164 }
13165 "Ω" | "ohm" | "ohms" => {
13166 units[1] += sign;
13167 units[0] += 2 * sign;
13168 units[2] -= 3 * sign;
13169 units[3] -= 2 * sign;
13170 }
13171 _ => {}
13172 }
13173 }
13174
13175 Ok(make_quantity(value, units))
13176 });
13177
13178 define(interp, "qty_add", Some(2), |_, args| {
13180 let (val_a, units_a) = extract_quantity(&args[0], "qty_add")?;
13181 let (val_b, units_b) = extract_quantity(&args[1], "qty_add")?;
13182
13183 if units_a != units_b {
13184 return Err(RuntimeError::new(format!(
13185 "qty_add: unit mismatch: {} vs {}",
13186 units_to_string(units_a),
13187 units_to_string(units_b)
13188 )));
13189 }
13190
13191 Ok(make_quantity(val_a + val_b, units_a))
13192 });
13193
13194 define(interp, "qty_sub", Some(2), |_, args| {
13196 let (val_a, units_a) = extract_quantity(&args[0], "qty_sub")?;
13197 let (val_b, units_b) = extract_quantity(&args[1], "qty_sub")?;
13198
13199 if units_a != units_b {
13200 return Err(RuntimeError::new(format!(
13201 "qty_sub: unit mismatch: {} vs {}",
13202 units_to_string(units_a),
13203 units_to_string(units_b)
13204 )));
13205 }
13206
13207 Ok(make_quantity(val_a - val_b, units_a))
13208 });
13209
13210 define(interp, "qty_mul", Some(2), |_, args| {
13212 let (val_a, units_a) = extract_quantity(&args[0], "qty_mul")?;
13213 let (val_b, units_b) = extract_quantity(&args[1], "qty_mul")?;
13214
13215 let mut result_units = [0i32; 7];
13216 for i in 0..7 {
13217 result_units[i] = units_a[i] + units_b[i];
13218 }
13219
13220 Ok(make_quantity(val_a * val_b, result_units))
13221 });
13222
13223 define(interp, "qty_div", Some(2), |_, args| {
13225 let (val_a, units_a) = extract_quantity(&args[0], "qty_div")?;
13226 let (val_b, units_b) = extract_quantity(&args[1], "qty_div")?;
13227
13228 if val_b.abs() < 1e-15 {
13229 return Err(RuntimeError::new("qty_div: division by zero"));
13230 }
13231
13232 let mut result_units = [0i32; 7];
13233 for i in 0..7 {
13234 result_units[i] = units_a[i] - units_b[i];
13235 }
13236
13237 Ok(make_quantity(val_a / val_b, result_units))
13238 });
13239
13240 define(interp, "qty_pow", Some(2), |_, args| {
13242 let (val, units) = extract_quantity(&args[0], "qty_pow")?;
13243 let n = match &args[1] {
13244 Value::Int(n) => *n as i32,
13245 Value::Float(f) => *f as i32,
13246 _ => return Err(RuntimeError::new("qty_pow: exponent must be number")),
13247 };
13248
13249 let mut result_units = [0i32; 7];
13250 for i in 0..7 {
13251 result_units[i] = units[i] * n;
13252 }
13253
13254 Ok(make_quantity(val.powi(n), result_units))
13255 });
13256
13257 define(interp, "qty_sqrt", Some(1), |_, args| {
13259 let (val, units) = extract_quantity(&args[0], "qty_sqrt")?;
13260
13261 for (i, &exp) in units.iter().enumerate() {
13263 if exp % 2 != 0 {
13264 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
13265 return Err(RuntimeError::new(format!(
13266 "qty_sqrt: cannot take sqrt of {} (odd exponent on {})",
13267 units_to_string(units),
13268 names[i]
13269 )));
13270 }
13271 }
13272
13273 let mut result_units = [0i32; 7];
13274 for i in 0..7 {
13275 result_units[i] = units[i] / 2;
13276 }
13277
13278 Ok(make_quantity(val.sqrt(), result_units))
13279 });
13280
13281 define(interp, "qty_value", Some(1), |_, args| {
13283 let (val, _) = extract_quantity(&args[0], "qty_value")?;
13284 Ok(Value::Float(val))
13285 });
13286
13287 define(interp, "qty_units", Some(1), |_, args| {
13289 let (_, units) = extract_quantity(&args[0], "qty_units")?;
13290 Ok(Value::String(Rc::new(units_to_string(units))))
13291 });
13292
13293 define(interp, "qty_convert", Some(2), |_, args| {
13296 let (val, units) = extract_quantity(&args[0], "qty_convert")?;
13297 let _target = match &args[1] {
13298 Value::String(s) => s.to_string(),
13299 _ => return Err(RuntimeError::new("qty_convert: target must be unit string")),
13300 };
13301
13302 Ok(make_quantity(val, units))
13305 });
13306
13307 define(interp, "qty_check", Some(2), |_, args| {
13309 let (_, units) = extract_quantity(&args[0], "qty_check")?;
13310 let expected = match &args[1] {
13311 Value::String(s) => s.to_string(),
13312 _ => return Err(RuntimeError::new("qty_check: expected string")),
13313 };
13314
13315 let actual_str = units_to_string(units);
13317 Ok(Value::Bool(
13318 actual_str.contains(&expected) || expected.contains(&actual_str),
13319 ))
13320 });
13321
13322 define(interp, "c_light", Some(0), |_, _| {
13325 Ok(make_quantity(299792458.0, [1, 0, -1, 0, 0, 0, 0])) });
13327
13328 define(interp, "G_gravity", Some(0), |_, _| {
13330 Ok(make_quantity(6.67430e-11, [3, -1, -2, 0, 0, 0, 0])) });
13332
13333 define(interp, "h_planck", Some(0), |_, _| {
13335 Ok(make_quantity(6.62607015e-34, [2, 1, -1, 0, 0, 0, 0])) });
13337
13338 define(interp, "e_charge", Some(0), |_, _| {
13340 Ok(make_quantity(1.602176634e-19, [0, 0, 1, 1, 0, 0, 0])) });
13342
13343 define(interp, "k_boltzmann", Some(0), |_, _| {
13345 Ok(make_quantity(1.380649e-23, [2, 1, -2, 0, -1, 0, 0])) });
13347}
13348
13349fn register_ecs(interp: &mut Interpreter) {
13429 define(interp, "ecs_world", Some(0), |_, _| {
13431 let mut world = HashMap::new();
13432 world.insert(
13433 "_type".to_string(),
13434 Value::String(Rc::new("ecs_world".to_string())),
13435 );
13436 world.insert("next_id".to_string(), Value::Int(0));
13437 world.insert(
13438 "entities".to_string(),
13439 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
13440 );
13441 world.insert(
13442 "components".to_string(),
13443 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
13444 );
13445 Ok(Value::Map(Rc::new(RefCell::new(world))))
13446 });
13447
13448 define(interp, "ecs_spawn", Some(1), |_, args| {
13450 let world = match &args[0] {
13451 Value::Map(m) => m.clone(),
13452 _ => return Err(RuntimeError::new("ecs_spawn: expected world")),
13453 };
13454
13455 let mut world_ref = world.borrow_mut();
13456 let id = match world_ref.get("next_id") {
13457 Some(Value::Int(n)) => *n,
13458 _ => 0,
13459 };
13460
13461 world_ref.insert("next_id".to_string(), Value::Int(id + 1));
13463
13464 if let Some(Value::Map(entities)) = world_ref.get("entities") {
13466 entities
13467 .borrow_mut()
13468 .insert(id.to_string(), Value::Bool(true));
13469 }
13470
13471 Ok(Value::Int(id))
13472 });
13473
13474 define(interp, "ecs_despawn", Some(2), |_, args| {
13476 let world = match &args[0] {
13477 Value::Map(m) => m.clone(),
13478 _ => return Err(RuntimeError::new("ecs_despawn: expected world")),
13479 };
13480 let id = match &args[1] {
13481 Value::Int(n) => *n,
13482 _ => return Err(RuntimeError::new("ecs_despawn: expected entity id")),
13483 };
13484
13485 let world_ref = world.borrow();
13486
13487 if let Some(Value::Map(entities)) = world_ref.get("entities") {
13489 entities.borrow_mut().remove(&id.to_string());
13490 }
13491
13492 if let Some(Value::Map(components)) = world_ref.get("components") {
13494 let comps = components.borrow();
13495 for (_, comp_storage) in comps.iter() {
13496 if let Value::Map(storage) = comp_storage {
13497 storage.borrow_mut().remove(&id.to_string());
13498 }
13499 }
13500 }
13501
13502 Ok(Value::Bool(true))
13503 });
13504
13505 define(interp, "ecs_attach", Some(4), |_, args| {
13507 let world = match &args[0] {
13508 Value::Map(m) => m.clone(),
13509 _ => {
13510 return Err(RuntimeError::new(
13511 "ecs_attach() expects a world as first argument.\n\
13512 Usage: ecs_attach(world, entity_id, component_name, data)\n\
13513 Example:\n\
13514 let world = ecs_world();\n\
13515 let e = ecs_spawn(world);\n\
13516 ecs_attach(world, e, \"Position\", vec3(0, 0, 0));",
13517 ))
13518 }
13519 };
13520 let id = match &args[1] {
13521 Value::Int(n) => *n,
13522 _ => {
13523 return Err(RuntimeError::new(
13524 "ecs_attach() expects an entity ID (integer) as second argument.\n\
13525 Entity IDs are returned by ecs_spawn().\n\
13526 Example:\n\
13527 let entity = ecs_spawn(world); // Returns 0, 1, 2...\n\
13528 ecs_attach(world, entity, \"Health\", 100);",
13529 ))
13530 }
13531 };
13532 let comp_name = match &args[2] {
13533 Value::String(s) => s.to_string(),
13534 _ => {
13535 return Err(RuntimeError::new(
13536 "ecs_attach() expects a string component name as third argument.\n\
13537 Common component names: \"Position\", \"Velocity\", \"Health\", \"Sprite\"\n\
13538 Example: ecs_attach(world, entity, \"Position\", vec3(0, 0, 0));",
13539 ))
13540 }
13541 };
13542 let data = args[3].clone();
13543
13544 let world_ref = world.borrow();
13545
13546 if let Some(Value::Map(components)) = world_ref.get("components") {
13548 let mut comps = components.borrow_mut();
13549
13550 let storage = comps
13551 .entry(comp_name.clone())
13552 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
13553
13554 if let Value::Map(storage_map) = storage {
13555 storage_map.borrow_mut().insert(id.to_string(), data);
13556 }
13557 }
13558
13559 Ok(Value::Bool(true))
13560 });
13561
13562 define(interp, "ecs_get", Some(3), |_, args| {
13564 let world = match &args[0] {
13565 Value::Map(m) => m.clone(),
13566 _ => return Err(RuntimeError::new("ecs_get: expected world")),
13567 };
13568 let id = match &args[1] {
13569 Value::Int(n) => *n,
13570 _ => return Err(RuntimeError::new("ecs_get: expected entity id")),
13571 };
13572 let comp_name = match &args[2] {
13573 Value::String(s) => s.to_string(),
13574 _ => return Err(RuntimeError::new("ecs_get: expected component name")),
13575 };
13576
13577 let world_ref = world.borrow();
13578
13579 if let Some(Value::Map(components)) = world_ref.get("components") {
13580 let comps = components.borrow();
13581 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
13582 if let Some(data) = storage.borrow().get(&id.to_string()) {
13583 return Ok(data.clone());
13584 }
13585 }
13586 }
13587
13588 Ok(Value::Null)
13589 });
13590
13591 define(interp, "ecs_has", Some(3), |_, args| {
13593 let world = match &args[0] {
13594 Value::Map(m) => m.clone(),
13595 _ => return Err(RuntimeError::new("ecs_has: expected world")),
13596 };
13597 let id = match &args[1] {
13598 Value::Int(n) => *n,
13599 _ => return Err(RuntimeError::new("ecs_has: expected entity id")),
13600 };
13601 let comp_name = match &args[2] {
13602 Value::String(s) => s.to_string(),
13603 _ => return Err(RuntimeError::new("ecs_has: expected component name")),
13604 };
13605
13606 let world_ref = world.borrow();
13607
13608 if let Some(Value::Map(components)) = world_ref.get("components") {
13609 let comps = components.borrow();
13610 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
13611 return Ok(Value::Bool(storage.borrow().contains_key(&id.to_string())));
13612 }
13613 }
13614
13615 Ok(Value::Bool(false))
13616 });
13617
13618 define(interp, "ecs_remove", Some(3), |_, args| {
13620 let world = match &args[0] {
13621 Value::Map(m) => m.clone(),
13622 _ => return Err(RuntimeError::new("ecs_remove: expected world")),
13623 };
13624 let id = match &args[1] {
13625 Value::Int(n) => *n,
13626 _ => return Err(RuntimeError::new("ecs_remove: expected entity id")),
13627 };
13628 let comp_name = match &args[2] {
13629 Value::String(s) => s.to_string(),
13630 _ => return Err(RuntimeError::new("ecs_remove: expected component name")),
13631 };
13632
13633 let world_ref = world.borrow();
13634
13635 if let Some(Value::Map(components)) = world_ref.get("components") {
13636 let comps = components.borrow();
13637 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
13638 storage.borrow_mut().remove(&id.to_string());
13639 return Ok(Value::Bool(true));
13640 }
13641 }
13642
13643 Ok(Value::Bool(false))
13644 });
13645
13646 define(interp, "ecs_query", None, |_, args| {
13649 if args.is_empty() {
13650 return Err(RuntimeError::new(
13651 "ecs_query: expected at least world argument",
13652 ));
13653 }
13654
13655 let world = match &args[0] {
13656 Value::Map(m) => m.clone(),
13657 _ => return Err(RuntimeError::new("ecs_query: expected world")),
13658 };
13659
13660 let comp_names: Vec<String> = args[1..]
13661 .iter()
13662 .filter_map(|a| match a {
13663 Value::String(s) => Some(s.to_string()),
13664 _ => None,
13665 })
13666 .collect();
13667
13668 if comp_names.is_empty() {
13669 let world_ref = world.borrow();
13671 if let Some(Value::Map(entities)) = world_ref.get("entities") {
13672 let result: Vec<Value> = entities
13673 .borrow()
13674 .keys()
13675 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
13676 .collect();
13677 return Ok(Value::Array(Rc::new(RefCell::new(result))));
13678 }
13679 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13680 }
13681
13682 let world_ref = world.borrow();
13683 let mut result_ids: Option<Vec<String>> = None;
13684
13685 if let Some(Value::Map(components)) = world_ref.get("components") {
13686 let comps = components.borrow();
13687
13688 for comp_name in &comp_names {
13689 if let Some(Value::Map(storage)) = comps.get(comp_name) {
13690 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
13691
13692 result_ids = Some(match result_ids {
13693 None => keys,
13694 Some(existing) => {
13695 existing.into_iter().filter(|k| keys.contains(k)).collect()
13696 }
13697 });
13698 } else {
13699 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13701 }
13702 }
13703 }
13704
13705 let result: Vec<Value> = result_ids
13706 .unwrap_or_default()
13707 .iter()
13708 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
13709 .collect();
13710
13711 Ok(Value::Array(Rc::new(RefCell::new(result))))
13712 });
13713
13714 define(interp, "ecs_query_with", Some(3), |interp, args| {
13717 let world = match &args[0] {
13718 Value::Map(m) => m.clone(),
13719 _ => return Err(RuntimeError::new("ecs_query_with: expected world")),
13720 };
13721 let comp_names: Vec<String> = match &args[1] {
13722 Value::Array(arr) => arr
13723 .borrow()
13724 .iter()
13725 .filter_map(|v| match v {
13726 Value::String(s) => Some(s.to_string()),
13727 _ => None,
13728 })
13729 .collect(),
13730 _ => {
13731 return Err(RuntimeError::new(
13732 "ecs_query_with: expected array of component names",
13733 ))
13734 }
13735 };
13736 let callback = match &args[2] {
13737 Value::Function(f) => f.clone(),
13738 _ => {
13739 return Err(RuntimeError::new(
13740 "ecs_query_with: expected callback function",
13741 ))
13742 }
13743 };
13744
13745 let mut callback_data: Vec<(i64, HashMap<String, Value>)> = Vec::new();
13747
13748 {
13749 let world_ref = world.borrow();
13750 let mut result_ids: Option<Vec<String>> = None;
13751
13752 if let Some(Value::Map(components)) = world_ref.get("components") {
13753 let comps = components.borrow();
13754
13755 for comp_name in &comp_names {
13756 if let Some(Value::Map(storage)) = comps.get(comp_name) {
13757 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
13758 result_ids = Some(match result_ids {
13759 None => keys,
13760 Some(existing) => {
13761 existing.into_iter().filter(|k| keys.contains(k)).collect()
13762 }
13763 });
13764 } else {
13765 result_ids = Some(vec![]);
13766 break;
13767 }
13768 }
13769
13770 for id_str in result_ids.unwrap_or_default() {
13772 if let Ok(id) = id_str.parse::<i64>() {
13773 let mut entity_comps = HashMap::new();
13774 for comp_name in &comp_names {
13775 if let Some(Value::Map(storage)) = comps.get(comp_name) {
13776 if let Some(data) = storage.borrow().get(&id_str) {
13777 entity_comps.insert(comp_name.clone(), data.clone());
13778 }
13779 }
13780 }
13781 callback_data.push((id, entity_comps));
13782 }
13783 }
13784 }
13785 } for (id, entity_comps) in callback_data {
13789 let callback_args = vec![
13790 Value::Int(id),
13791 Value::Map(Rc::new(RefCell::new(entity_comps))),
13792 ];
13793 interp.call_function(&callback, callback_args)?;
13794 }
13795
13796 Ok(Value::Null)
13797 });
13798
13799 define(interp, "ecs_count", Some(1), |_, args| {
13801 let world = match &args[0] {
13802 Value::Map(m) => m.clone(),
13803 _ => return Err(RuntimeError::new("ecs_count: expected world")),
13804 };
13805
13806 let world_ref = world.borrow();
13807 if let Some(Value::Map(entities)) = world_ref.get("entities") {
13808 return Ok(Value::Int(entities.borrow().len() as i64));
13809 }
13810
13811 Ok(Value::Int(0))
13812 });
13813
13814 define(interp, "ecs_alive", Some(2), |_, args| {
13816 let world = match &args[0] {
13817 Value::Map(m) => m.clone(),
13818 _ => return Err(RuntimeError::new("ecs_alive: expected world")),
13819 };
13820 let id = match &args[1] {
13821 Value::Int(n) => *n,
13822 _ => return Err(RuntimeError::new("ecs_alive: expected entity id")),
13823 };
13824
13825 let world_ref = world.borrow();
13826 if let Some(Value::Map(entities)) = world_ref.get("entities") {
13827 return Ok(Value::Bool(entities.borrow().contains_key(&id.to_string())));
13828 }
13829
13830 Ok(Value::Bool(false))
13831 });
13832}
13833
13834fn register_polycultural_text(interp: &mut Interpreter) {
13854 define(interp, "script", Some(1), |_, args| {
13864 match &args[0] {
13865 Value::String(s) => {
13866 let mut script_counts: HashMap<String, usize> = HashMap::new();
13868 for c in s.chars() {
13869 if !c.is_whitespace() && !c.is_ascii_punctuation() {
13870 let script = c.script();
13871 let name = format!("{:?}", script);
13872 *script_counts.entry(name).or_insert(0) += 1;
13873 }
13874 }
13875 let dominant = script_counts
13877 .into_iter()
13878 .max_by_key(|(_, count)| *count)
13879 .map(|(name, _)| name)
13880 .unwrap_or_else(|| "Unknown".to_string());
13881 Ok(Value::String(Rc::new(dominant)))
13882 }
13883 _ => Err(RuntimeError::new("script() requires string")),
13884 }
13885 });
13886
13887 define(interp, "scripts", Some(1), |_, args| match &args[0] {
13889 Value::String(s) => {
13890 let mut scripts: Vec<String> = s
13891 .chars()
13892 .filter(|c| !c.is_whitespace() && !c.is_ascii_punctuation())
13893 .map(|c| format!("{:?}", c.script()))
13894 .collect();
13895 scripts.sort();
13896 scripts.dedup();
13897 let values: Vec<Value> = scripts
13898 .into_iter()
13899 .map(|s| Value::String(Rc::new(s)))
13900 .collect();
13901 Ok(Value::Array(Rc::new(RefCell::new(values))))
13902 }
13903 _ => Err(RuntimeError::new("scripts() requires string")),
13904 });
13905
13906 define(interp, "is_script", Some(2), |_, args| {
13908 match (&args[0], &args[1]) {
13909 (Value::String(s), Value::String(script_name)) => {
13910 let target = script_name.to_lowercase();
13911 let mut matching = 0usize;
13912 let mut total = 0usize;
13913 for c in s.chars() {
13914 if !c.is_whitespace() && !c.is_ascii_punctuation() {
13915 total += 1;
13916 let script_str = format!("{:?}", c.script()).to_lowercase();
13917 if script_str == target {
13918 matching += 1;
13919 }
13920 }
13921 }
13922 let ratio = if total > 0 {
13923 matching as f64 / total as f64
13924 } else {
13925 0.0
13926 };
13927 Ok(Value::Bool(ratio > 0.5))
13928 }
13929 _ => Err(RuntimeError::new(
13930 "is_script() requires string and script name",
13931 )),
13932 }
13933 });
13934
13935 define(interp, "is_latin", Some(1), |_, args| match &args[0] {
13937 Value::String(s) => {
13938 let is_latin = s
13939 .chars()
13940 .filter(|c| !c.is_whitespace())
13941 .all(|c| matches!(c.script(), Script::Latin | Script::Common));
13942 Ok(Value::Bool(is_latin && !s.is_empty()))
13943 }
13944 _ => Err(RuntimeError::new("is_latin() requires string")),
13945 });
13946
13947 define(interp, "is_cjk", Some(1), |_, args| match &args[0] {
13948 Value::String(s) => {
13949 let has_cjk = s.chars().any(|c| {
13950 matches!(
13951 c.script(),
13952 Script::Han
13953 | Script::Hiragana
13954 | Script::Katakana
13955 | Script::Hangul
13956 | Script::Bopomofo
13957 )
13958 });
13959 Ok(Value::Bool(has_cjk))
13960 }
13961 _ => Err(RuntimeError::new("is_cjk() requires string")),
13962 });
13963
13964 define(interp, "is_arabic", Some(1), |_, args| match &args[0] {
13965 Value::String(s) => {
13966 let has_arabic = s.chars().any(|c| matches!(c.script(), Script::Arabic));
13967 Ok(Value::Bool(has_arabic))
13968 }
13969 _ => Err(RuntimeError::new("is_arabic() requires string")),
13970 });
13971
13972 define(interp, "is_hebrew", Some(1), |_, args| match &args[0] {
13973 Value::String(s) => {
13974 let has_hebrew = s.chars().any(|c| matches!(c.script(), Script::Hebrew));
13975 Ok(Value::Bool(has_hebrew))
13976 }
13977 _ => Err(RuntimeError::new("is_hebrew() requires string")),
13978 });
13979
13980 define(interp, "is_cyrillic", Some(1), |_, args| match &args[0] {
13981 Value::String(s) => {
13982 let has_cyrillic = s.chars().any(|c| matches!(c.script(), Script::Cyrillic));
13983 Ok(Value::Bool(has_cyrillic))
13984 }
13985 _ => Err(RuntimeError::new("is_cyrillic() requires string")),
13986 });
13987
13988 define(interp, "is_greek", Some(1), |_, args| match &args[0] {
13989 Value::String(s) => {
13990 let has_greek = s.chars().any(|c| matches!(c.script(), Script::Greek));
13991 Ok(Value::Bool(has_greek))
13992 }
13993 _ => Err(RuntimeError::new("is_greek() requires string")),
13994 });
13995
13996 define(interp, "is_devanagari", Some(1), |_, args| match &args[0] {
13997 Value::String(s) => {
13998 let has_devanagari = s.chars().any(|c| matches!(c.script(), Script::Devanagari));
13999 Ok(Value::Bool(has_devanagari))
14000 }
14001 _ => Err(RuntimeError::new("is_devanagari() requires string")),
14002 });
14003
14004 define(interp, "is_thai", Some(1), |_, args| match &args[0] {
14005 Value::String(s) => {
14006 let has_thai = s.chars().any(|c| matches!(c.script(), Script::Thai));
14007 Ok(Value::Bool(has_thai))
14008 }
14009 _ => Err(RuntimeError::new("is_thai() requires string")),
14010 });
14011
14012 define(interp, "is_hangul", Some(1), |_, args| match &args[0] {
14013 Value::String(s) => {
14014 let has_hangul = s.chars().any(|c| matches!(c.script(), Script::Hangul));
14015 Ok(Value::Bool(has_hangul))
14016 }
14017 _ => Err(RuntimeError::new("is_hangul() requires string")),
14018 });
14019
14020 define(interp, "is_hiragana", Some(1), |_, args| match &args[0] {
14021 Value::String(s) => {
14022 let has_hiragana = s.chars().any(|c| matches!(c.script(), Script::Hiragana));
14023 Ok(Value::Bool(has_hiragana))
14024 }
14025 _ => Err(RuntimeError::new("is_hiragana() requires string")),
14026 });
14027
14028 define(interp, "is_katakana", Some(1), |_, args| match &args[0] {
14029 Value::String(s) => {
14030 let has_katakana = s.chars().any(|c| matches!(c.script(), Script::Katakana));
14031 Ok(Value::Bool(has_katakana))
14032 }
14033 _ => Err(RuntimeError::new("is_katakana() requires string")),
14034 });
14035
14036 define(interp, "char_script", Some(1), |_, args| match &args[0] {
14038 Value::Char(c) => {
14039 let script = format!("{:?}", c.script());
14040 Ok(Value::String(Rc::new(script)))
14041 }
14042 Value::String(s) if s.chars().count() == 1 => {
14043 let c = s.chars().next().unwrap();
14044 let script = format!("{:?}", c.script());
14045 Ok(Value::String(Rc::new(script)))
14046 }
14047 _ => Err(RuntimeError::new("char_script() requires single character")),
14048 });
14049
14050 define(interp, "text_direction", Some(1), |_, args| {
14060 match &args[0] {
14061 Value::String(s) => {
14062 let bidi_info = BidiInfo::new(s, None);
14063 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
14065 let direction = if has_rtl { "rtl" } else { "ltr" };
14066 Ok(Value::String(Rc::new(direction.to_string())))
14067 }
14068 _ => Err(RuntimeError::new("text_direction() requires string")),
14069 }
14070 });
14071
14072 define(interp, "is_rtl", Some(1), |_, args| match &args[0] {
14074 Value::String(s) => {
14075 let bidi_info = BidiInfo::new(s, None);
14076 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
14077 Ok(Value::Bool(has_rtl))
14078 }
14079 _ => Err(RuntimeError::new("is_rtl() requires string")),
14080 });
14081
14082 define(interp, "is_ltr", Some(1), |_, args| match &args[0] {
14084 Value::String(s) => {
14085 let bidi_info = BidiInfo::new(s, None);
14086 let is_ltr = bidi_info.paragraphs.iter().all(|p| !p.level.is_rtl());
14087 Ok(Value::Bool(is_ltr))
14088 }
14089 _ => Err(RuntimeError::new("is_ltr() requires string")),
14090 });
14091
14092 define(interp, "is_bidi", Some(1), |_, args| {
14094 match &args[0] {
14095 Value::String(s) => {
14096 let has_rtl = s.chars().any(|c| {
14098 matches!(
14099 c.script(),
14100 Script::Arabic | Script::Hebrew | Script::Syriac | Script::Thaana
14101 )
14102 });
14103 let has_ltr = s.chars().any(|c| {
14104 matches!(c.script(), Script::Latin | Script::Greek | Script::Cyrillic)
14105 });
14106 Ok(Value::Bool(has_rtl && has_ltr))
14107 }
14108 _ => Err(RuntimeError::new("is_bidi() requires string")),
14109 }
14110 });
14111
14112 define(interp, "bidi_reorder", Some(1), |_, args| match &args[0] {
14114 Value::String(s) => {
14115 let bidi_info = BidiInfo::new(s, None);
14116 let mut result = String::new();
14117 for para in &bidi_info.paragraphs {
14118 let line = para.range.clone();
14119 let reordered = bidi_info.reorder_line(para, line);
14120 result.push_str(&reordered);
14121 }
14122 Ok(Value::String(Rc::new(result)))
14123 }
14124 _ => Err(RuntimeError::new("bidi_reorder() requires string")),
14125 });
14126
14127 define(interp, "display_width", Some(1), |_, args| match &args[0] {
14137 Value::String(s) => {
14138 let width = UnicodeWidthStr::width(s.as_str());
14139 Ok(Value::Int(width as i64))
14140 }
14141 _ => Err(RuntimeError::new("display_width() requires string")),
14142 });
14143
14144 define(interp, "is_fullwidth", Some(1), |_, args| {
14146 match &args[0] {
14147 Value::String(s) => {
14148 let char_count = s.chars().count();
14149 let display_width = UnicodeWidthStr::width(s.as_str());
14150 Ok(Value::Bool(display_width > char_count))
14152 }
14153 _ => Err(RuntimeError::new("is_fullwidth() requires string")),
14154 }
14155 });
14156
14157 define(interp, "pad_display", Some(3), |_, args| {
14159 match (&args[0], &args[1], &args[2]) {
14160 (Value::String(s), Value::Int(target_width), Value::String(align)) => {
14161 let current_width = UnicodeWidthStr::width(s.as_str());
14162 let target = *target_width as usize;
14163 if current_width >= target {
14164 return Ok(Value::String(s.clone()));
14165 }
14166 let padding = target - current_width;
14167 let result = match align.as_str() {
14168 "left" => format!("{}{}", s, " ".repeat(padding)),
14169 "right" => format!("{}{}", " ".repeat(padding), s),
14170 "center" => {
14171 let left = padding / 2;
14172 let right = padding - left;
14173 format!("{}{}{}", " ".repeat(left), s, " ".repeat(right))
14174 }
14175 _ => {
14176 return Err(RuntimeError::new(
14177 "pad_display: align must be 'left', 'right', or 'center'",
14178 ))
14179 }
14180 };
14181 Ok(Value::String(Rc::new(result)))
14182 }
14183 _ => Err(RuntimeError::new(
14184 "pad_display() requires string, width, and alignment",
14185 )),
14186 }
14187 });
14188
14189 define(interp, "transliterate", Some(1), |_, args| match &args[0] {
14199 Value::String(s) => {
14200 let ascii = deunicode(s);
14201 Ok(Value::String(Rc::new(ascii)))
14202 }
14203 _ => Err(RuntimeError::new("transliterate() requires string")),
14204 });
14205
14206 define(interp, "to_ascii", Some(1), |_, args| match &args[0] {
14208 Value::String(s) => {
14209 let ascii = deunicode(s);
14210 Ok(Value::String(Rc::new(ascii)))
14211 }
14212 _ => Err(RuntimeError::new("to_ascii() requires string")),
14213 });
14214
14215 define(interp, "slugify", Some(1), |_, args| {
14217 match &args[0] {
14218 Value::String(s) => {
14219 let ascii = deunicode(s);
14220 let slug: String = ascii
14221 .to_lowercase()
14222 .chars()
14223 .map(|c| if c.is_alphanumeric() { c } else { '-' })
14224 .collect();
14225 let mut result = String::new();
14227 let mut last_was_dash = true; for c in slug.chars() {
14229 if c == '-' {
14230 if !last_was_dash {
14231 result.push(c);
14232 last_was_dash = true;
14233 }
14234 } else {
14235 result.push(c);
14236 last_was_dash = false;
14237 }
14238 }
14239 if result.ends_with('-') {
14241 result.pop();
14242 }
14243 Ok(Value::String(Rc::new(result)))
14244 }
14245 _ => Err(RuntimeError::new("slugify() requires string")),
14246 }
14247 });
14248
14249 define(interp, "strip_diacritics", Some(1), |_, args| {
14259 match &args[0] {
14260 Value::String(s) => {
14261 let decomposed: String = s.nfd().collect();
14263 let stripped: String = decomposed
14265 .chars()
14266 .filter(|c| {
14267 let code = *c as u32;
14271 !(0x0300..=0x036F).contains(&code)
14273 && !(0x1AB0..=0x1AFF).contains(&code)
14274 && !(0x1DC0..=0x1DFF).contains(&code)
14275 && !(0x20D0..=0x20FF).contains(&code)
14276 && !(0xFE20..=0xFE2F).contains(&code)
14277 })
14278 .collect();
14279 Ok(Value::String(Rc::new(stripped)))
14280 }
14281 _ => Err(RuntimeError::new("strip_diacritics() requires string")),
14282 }
14283 });
14284
14285 define(interp, "has_diacritics", Some(1), |_, args| {
14287 match &args[0] {
14288 Value::String(s) => {
14289 let decomposed: String = s.nfd().collect();
14290 let has_marks = decomposed.chars().any(|c| {
14291 let code = c as u32;
14292 (0x0300..=0x036F).contains(&code)
14293 || (0x1AB0..=0x1AFF).contains(&code)
14294 || (0x1DC0..=0x1DFF).contains(&code)
14295 || (0x20D0..=0x20FF).contains(&code)
14296 || (0xFE20..=0xFE2F).contains(&code)
14297 });
14298 Ok(Value::Bool(has_marks))
14299 }
14300 _ => Err(RuntimeError::new("has_diacritics() requires string")),
14301 }
14302 });
14303
14304 define(interp, "normalize_accents", Some(2), |_, args| {
14306 match (&args[0], &args[1]) {
14307 (Value::String(s), Value::String(form)) => {
14308 let result = match form.as_str() {
14309 "composed" | "nfc" => s.nfc().collect(),
14310 "decomposed" | "nfd" => s.nfd().collect(),
14311 _ => {
14312 return Err(RuntimeError::new(
14313 "normalize_accents: form must be 'composed' or 'decomposed'",
14314 ))
14315 }
14316 };
14317 Ok(Value::String(Rc::new(result)))
14318 }
14319 _ => Err(RuntimeError::new(
14320 "normalize_accents() requires string and form",
14321 )),
14322 }
14323 });
14324
14325 define(interp, "upper_locale", Some(2), |_, args| {
14337 match (&args[0], &args[1]) {
14338 (Value::String(s), Value::String(locale_str)) => {
14339 let case_mapper = CaseMapper::new();
14340 let langid: LanguageIdentifier =
14341 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
14342 let result = case_mapper.uppercase_to_string(s, &langid);
14343 Ok(Value::String(Rc::new(result)))
14344 }
14345 _ => Err(RuntimeError::new(
14346 "upper_locale() requires string and locale",
14347 )),
14348 }
14349 });
14350
14351 define(interp, "lower_locale", Some(2), |_, args| {
14353 match (&args[0], &args[1]) {
14354 (Value::String(s), Value::String(locale_str)) => {
14355 let case_mapper = CaseMapper::new();
14356 let langid: LanguageIdentifier =
14357 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
14358 let result = case_mapper.lowercase_to_string(s, &langid);
14359 Ok(Value::String(Rc::new(result)))
14360 }
14361 _ => Err(RuntimeError::new(
14362 "lower_locale() requires string and locale",
14363 )),
14364 }
14365 });
14366
14367 define(interp, "titlecase_locale", Some(2), |_, args| {
14369 match (&args[0], &args[1]) {
14370 (Value::String(s), Value::String(locale_str)) => {
14371 let case_mapper = CaseMapper::new();
14372 let langid: LanguageIdentifier =
14373 locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
14374 let options = TitlecaseOptions::default();
14375 let result = case_mapper
14376 .titlecase_segment_with_only_case_data_to_string(s, &langid, options);
14377 Ok(Value::String(Rc::new(result)))
14378 }
14379 _ => Err(RuntimeError::new(
14380 "titlecase_locale() requires string and locale",
14381 )),
14382 }
14383 });
14384
14385 define(interp, "case_fold", Some(1), |_, args| match &args[0] {
14387 Value::String(s) => {
14388 let case_mapper = CaseMapper::new();
14389 let result = case_mapper.fold_string(s);
14390 Ok(Value::String(Rc::new(result)))
14391 }
14392 _ => Err(RuntimeError::new("case_fold() requires string")),
14393 });
14394
14395 define(interp, "case_insensitive_eq", Some(2), |_, args| {
14397 match (&args[0], &args[1]) {
14398 (Value::String(a), Value::String(b)) => {
14399 let case_mapper = CaseMapper::new();
14400 let folded_a = case_mapper.fold_string(a);
14401 let folded_b = case_mapper.fold_string(b);
14402 Ok(Value::Bool(folded_a == folded_b))
14403 }
14404 _ => Err(RuntimeError::new(
14405 "case_insensitive_eq() requires two strings",
14406 )),
14407 }
14408 });
14409
14410 define(interp, "compare_locale", Some(3), |_, args| {
14422 match (&args[0], &args[1], &args[2]) {
14423 (Value::String(a), Value::String(b), Value::String(locale_str)) => {
14424 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
14425 let options = CollatorOptions::new();
14426 let collator = Collator::try_new(&locale.into(), options)
14427 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
14428 let result = match collator.compare(a, b) {
14429 std::cmp::Ordering::Less => -1,
14430 std::cmp::Ordering::Equal => 0,
14431 std::cmp::Ordering::Greater => 1,
14432 };
14433 Ok(Value::Int(result))
14434 }
14435 _ => Err(RuntimeError::new(
14436 "compare_locale() requires two strings and locale",
14437 )),
14438 }
14439 });
14440
14441 define(interp, "sort_locale", Some(2), |_, args| {
14443 match (&args[0], &args[1]) {
14444 (Value::Array(arr), Value::String(locale_str)) => {
14445 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
14446 let options = CollatorOptions::new();
14447 let collator = Collator::try_new(&locale.into(), options)
14448 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
14449
14450 let mut items: Vec<(String, Value)> = arr
14451 .borrow()
14452 .iter()
14453 .map(|v| {
14454 let s = match v {
14455 Value::String(s) => (**s).clone(),
14456 _ => format!("{}", v),
14457 };
14458 (s, v.clone())
14459 })
14460 .collect();
14461
14462 items.sort_by(|(a, _), (b, _)| collator.compare(a, b));
14463
14464 let sorted: Vec<Value> = items.into_iter().map(|(_, v)| v).collect();
14465 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
14466 }
14467 _ => Err(RuntimeError::new("sort_locale() requires array and locale")),
14468 }
14469 });
14470
14471 define(interp, "sentences", Some(1), |_, args| match &args[0] {
14483 Value::String(s) => {
14484 let segmenter = SentenceSegmenter::new();
14485 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
14486 let mut sentences = Vec::new();
14487 let mut start = 0;
14488 for end in breakpoints {
14489 let sentence = s[start..end].trim();
14490 if !sentence.is_empty() {
14491 sentences.push(Value::String(Rc::new(sentence.to_string())));
14492 }
14493 start = end;
14494 }
14495 Ok(Value::Array(Rc::new(RefCell::new(sentences))))
14496 }
14497 _ => Err(RuntimeError::new("sentences() requires string")),
14498 });
14499
14500 define(interp, "sentence_count", Some(1), |_, args| {
14502 match &args[0] {
14503 Value::String(s) => {
14504 let segmenter = SentenceSegmenter::new();
14505 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
14506 let count = breakpoints.len().saturating_sub(1);
14508 Ok(Value::Int(count as i64))
14509 }
14510 _ => Err(RuntimeError::new("sentence_count() requires string")),
14511 }
14512 });
14513
14514 define(interp, "words_icu", Some(1), |_, args| {
14516 match &args[0] {
14517 Value::String(s) => {
14518 let segmenter = WordSegmenter::new_auto();
14519 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
14520 let mut words = Vec::new();
14521 let mut start = 0;
14522 for end in breakpoints {
14523 let word = &s[start..end];
14524 if !word.trim().is_empty() {
14526 words.push(Value::String(Rc::new(word.to_string())));
14527 }
14528 start = end;
14529 }
14530 Ok(Value::Array(Rc::new(RefCell::new(words))))
14531 }
14532 _ => Err(RuntimeError::new("words_icu() requires string")),
14533 }
14534 });
14535
14536 define(interp, "word_count_icu", Some(1), |_, args| {
14538 match &args[0] {
14539 Value::String(s) => {
14540 let segmenter = WordSegmenter::new_auto();
14541 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
14542 let mut count = 0;
14543 let mut start = 0;
14544 for end in breakpoints {
14545 let word = &s[start..end];
14546 if !word.trim().is_empty() && word.chars().any(|c| c.is_alphanumeric()) {
14547 count += 1;
14548 }
14549 start = end;
14550 }
14551 Ok(Value::Int(count))
14552 }
14553 _ => Err(RuntimeError::new("word_count_icu() requires string")),
14554 }
14555 });
14556
14557 define(interp, "is_emoji", Some(1), |_, args| {
14563 match &args[0] {
14564 Value::String(s) => {
14565 let has_emoji = s.chars().any(|c| {
14566 let code = c as u32;
14567 (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) });
14582 Ok(Value::Bool(has_emoji))
14583 }
14584 _ => Err(RuntimeError::new("is_emoji() requires string")),
14585 }
14586 });
14587
14588 define(interp, "extract_emoji", Some(1), |_, args| match &args[0] {
14590 Value::String(s) => {
14591 let emoji: Vec<Value> = s
14592 .graphemes(true)
14593 .filter(|g| {
14594 g.chars().any(|c| {
14595 let code = c as u32;
14596 (0x1F600..=0x1F64F).contains(&code)
14597 || (0x1F300..=0x1F5FF).contains(&code)
14598 || (0x1F680..=0x1F6FF).contains(&code)
14599 || (0x1F1E0..=0x1F1FF).contains(&code)
14600 || (0x2600..=0x26FF).contains(&code)
14601 || (0x2700..=0x27BF).contains(&code)
14602 || (0x1F900..=0x1F9FF).contains(&code)
14603 || (0x1FA00..=0x1FA6F).contains(&code)
14604 || (0x1FA70..=0x1FAFF).contains(&code)
14605 })
14606 })
14607 .map(|g| Value::String(Rc::new(g.to_string())))
14608 .collect();
14609 Ok(Value::Array(Rc::new(RefCell::new(emoji))))
14610 }
14611 _ => Err(RuntimeError::new("extract_emoji() requires string")),
14612 });
14613
14614 define(interp, "strip_emoji", Some(1), |_, args| match &args[0] {
14616 Value::String(s) => {
14617 let stripped: String = s
14618 .graphemes(true)
14619 .filter(|g| {
14620 !g.chars().any(|c| {
14621 let code = c as u32;
14622 (0x1F600..=0x1F64F).contains(&code)
14623 || (0x1F300..=0x1F5FF).contains(&code)
14624 || (0x1F680..=0x1F6FF).contains(&code)
14625 || (0x1F1E0..=0x1F1FF).contains(&code)
14626 || (0x2600..=0x26FF).contains(&code)
14627 || (0x2700..=0x27BF).contains(&code)
14628 || (0x1F900..=0x1F9FF).contains(&code)
14629 || (0x1FA00..=0x1FA6F).contains(&code)
14630 || (0x1FA70..=0x1FAFF).contains(&code)
14631 })
14632 })
14633 .collect();
14634 Ok(Value::String(Rc::new(stripped)))
14635 }
14636 _ => Err(RuntimeError::new("strip_emoji() requires string")),
14637 });
14638
14639 define(interp, "script_runs", Some(1), |_, args| {
14645 match &args[0] {
14646 Value::String(s) => {
14647 let mut runs: Vec<Value> = Vec::new();
14648 let mut current_run = String::new();
14649 let mut current_script: Option<Script> = None;
14650
14651 for c in s.chars() {
14652 let script = c.script();
14653 if script != Script::Common && script != Script::Inherited {
14655 if let Some(curr) = current_script {
14656 if script != curr {
14657 if !current_run.is_empty() {
14659 runs.push(Value::String(Rc::new(current_run.clone())));
14660 current_run.clear();
14661 }
14662 current_script = Some(script);
14663 }
14664 } else {
14665 current_script = Some(script);
14666 }
14667 }
14668 current_run.push(c);
14669 }
14670
14671 if !current_run.is_empty() {
14673 runs.push(Value::String(Rc::new(current_run)));
14674 }
14675
14676 Ok(Value::Array(Rc::new(RefCell::new(runs))))
14677 }
14678 _ => Err(RuntimeError::new("script_runs() requires string")),
14679 }
14680 });
14681
14682 define(interp, "script_ratio", Some(1), |_, args| {
14684 match &args[0] {
14685 Value::String(s) => {
14686 let mut script_counts: HashMap<String, usize> = HashMap::new();
14687 let mut total = 0usize;
14688
14689 for c in s.chars() {
14690 if !c.is_whitespace() && c != ' ' {
14691 let script = format!("{:?}", c.script());
14692 *script_counts.entry(script).or_insert(0) += 1;
14693 total += 1;
14694 }
14695 }
14696
14697 let mut result = HashMap::new();
14699 for (script, count) in script_counts {
14700 let ratio = if total > 0 {
14701 count as f64 / total as f64
14702 } else {
14703 0.0
14704 };
14705 result.insert(script, Value::Float(ratio));
14706 }
14707
14708 let map = Rc::new(RefCell::new(result));
14709 Ok(Value::Map(map))
14710 }
14711 _ => Err(RuntimeError::new("script_ratio() requires string")),
14712 }
14713 });
14714
14715 define(interp, "locale_name", Some(1), |_, args| {
14721 match &args[0] {
14722 Value::String(locale_str) => {
14723 Ok(Value::String(locale_str.clone()))
14726 }
14727 _ => Err(RuntimeError::new("locale_name() requires string")),
14728 }
14729 });
14730
14731 define(interp, "supported_locales", Some(0), |_, _| {
14733 let locales = vec![
14735 "ar", "bg", "ca", "cs", "da", "de", "el", "en", "es", "et", "fi", "fr", "he", "hi",
14736 "hr", "hu", "id", "it", "ja", "ko", "lt", "lv", "ms", "nb", "nl", "pl", "pt", "ro",
14737 "ru", "sk", "sl", "sr", "sv", "th", "tr", "uk", "vi", "zh",
14738 ];
14739 let values: Vec<Value> = locales
14740 .into_iter()
14741 .map(|s| Value::String(Rc::new(s.to_string())))
14742 .collect();
14743 Ok(Value::Array(Rc::new(RefCell::new(values))))
14744 });
14745}
14746
14747fn register_text_intelligence(interp: &mut Interpreter) {
14752 define(interp, "levenshtein", Some(2), |_, args| {
14758 match (&args[0], &args[1]) {
14759 (Value::String(a), Value::String(b)) => {
14760 let distance = strsim::levenshtein(a, b);
14761 Ok(Value::Int(distance as i64))
14762 }
14763 _ => Err(RuntimeError::new("levenshtein() requires two strings")),
14764 }
14765 });
14766
14767 define(
14769 interp,
14770 "levenshtein_normalized",
14771 Some(2),
14772 |_, args| match (&args[0], &args[1]) {
14773 (Value::String(a), Value::String(b)) => {
14774 let distance = strsim::normalized_levenshtein(a, b);
14775 Ok(Value::Float(distance))
14776 }
14777 _ => Err(RuntimeError::new(
14778 "levenshtein_normalized() requires two strings",
14779 )),
14780 },
14781 );
14782
14783 define(interp, "jaro", Some(2), |_, args| {
14785 match (&args[0], &args[1]) {
14786 (Value::String(a), Value::String(b)) => {
14787 let sim = strsim::jaro(a, b);
14788 Ok(Value::Float(sim))
14789 }
14790 _ => Err(RuntimeError::new("jaro() requires two strings")),
14791 }
14792 });
14793
14794 define(interp, "jaro_winkler", Some(2), |_, args| {
14796 match (&args[0], &args[1]) {
14797 (Value::String(a), Value::String(b)) => {
14798 let sim = strsim::jaro_winkler(a, b);
14799 Ok(Value::Float(sim))
14800 }
14801 _ => Err(RuntimeError::new("jaro_winkler() requires two strings")),
14802 }
14803 });
14804
14805 define(interp, "sorensen_dice", Some(2), |_, args| {
14807 match (&args[0], &args[1]) {
14808 (Value::String(a), Value::String(b)) => {
14809 let sim = strsim::sorensen_dice(a, b);
14810 Ok(Value::Float(sim))
14811 }
14812 _ => Err(RuntimeError::new("sorensen_dice() requires two strings")),
14813 }
14814 });
14815
14816 define(interp, "damerau_levenshtein", Some(2), |_, args| {
14818 match (&args[0], &args[1]) {
14819 (Value::String(a), Value::String(b)) => {
14820 let distance = strsim::damerau_levenshtein(a, b);
14821 Ok(Value::Int(distance as i64))
14822 }
14823 _ => Err(RuntimeError::new(
14824 "damerau_levenshtein() requires two strings",
14825 )),
14826 }
14827 });
14828
14829 define(interp, "osa_distance", Some(2), |_, args| {
14831 match (&args[0], &args[1]) {
14832 (Value::String(a), Value::String(b)) => {
14833 let distance = strsim::osa_distance(a, b);
14834 Ok(Value::Int(distance as i64))
14835 }
14836 _ => Err(RuntimeError::new("osa_distance() requires two strings")),
14837 }
14838 });
14839
14840 define(interp, "fuzzy_match", Some(3), |_, args| {
14842 match (&args[0], &args[1], &args[2]) {
14843 (Value::String(a), Value::String(b), Value::Float(threshold)) => {
14844 let sim = strsim::jaro_winkler(a, b);
14845 Ok(Value::Bool(sim >= *threshold))
14846 }
14847 (Value::String(a), Value::String(b), Value::Int(threshold)) => {
14848 let sim = strsim::jaro_winkler(a, b);
14849 Ok(Value::Bool(sim >= *threshold as f64))
14850 }
14851 _ => Err(RuntimeError::new(
14852 "fuzzy_match() requires two strings and threshold",
14853 )),
14854 }
14855 });
14856
14857 define(interp, "fuzzy_search", Some(3), |_, args| {
14859 match (&args[0], &args[1], &args[2]) {
14860 (Value::String(query), Value::Array(items), Value::Int(limit)) => {
14861 let items_ref = items.borrow();
14862 let mut scores: Vec<(f64, &str)> = items_ref
14863 .iter()
14864 .filter_map(|v| {
14865 if let Value::String(s) = v {
14866 Some((strsim::jaro_winkler(query, s), s.as_str()))
14867 } else {
14868 None
14869 }
14870 })
14871 .collect();
14872 scores.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
14873 let results: Vec<Value> = scores
14874 .into_iter()
14875 .take(*limit as usize)
14876 .map(|(_, s)| Value::String(Rc::new(s.to_string())))
14877 .collect();
14878 Ok(Value::Array(Rc::new(RefCell::new(results))))
14879 }
14880 _ => Err(RuntimeError::new(
14881 "fuzzy_search() requires query string, array, and limit",
14882 )),
14883 }
14884 });
14885
14886 define(interp, "soundex", Some(1), |_, args| match &args[0] {
14892 Value::String(s) => {
14893 let code = compute_soundex(s);
14894 Ok(Value::String(Rc::new(code)))
14895 }
14896 _ => Err(RuntimeError::new("soundex() requires string")),
14897 });
14898
14899 define(interp, "soundex_match", Some(2), |_, args| {
14901 match (&args[0], &args[1]) {
14902 (Value::String(a), Value::String(b)) => {
14903 let code_a = compute_soundex(a);
14904 let code_b = compute_soundex(b);
14905 Ok(Value::Bool(code_a == code_b))
14906 }
14907 _ => Err(RuntimeError::new("soundex_match() requires two strings")),
14908 }
14909 });
14910
14911 define(interp, "metaphone", Some(1), |_, args| match &args[0] {
14913 Value::String(s) => {
14914 let code = compute_metaphone(s);
14915 Ok(Value::String(Rc::new(code)))
14916 }
14917 _ => Err(RuntimeError::new("metaphone() requires string")),
14918 });
14919
14920 define(interp, "metaphone_match", Some(2), |_, args| {
14922 match (&args[0], &args[1]) {
14923 (Value::String(a), Value::String(b)) => {
14924 let code_a = compute_metaphone(a);
14925 let code_b = compute_metaphone(b);
14926 Ok(Value::Bool(code_a == code_b))
14927 }
14928 _ => Err(RuntimeError::new("metaphone_match() requires two strings")),
14929 }
14930 });
14931
14932 define(interp, "cologne_phonetic", Some(1), |_, args| {
14934 match &args[0] {
14935 Value::String(s) => {
14936 let code = compute_cologne(s);
14937 Ok(Value::String(Rc::new(code)))
14938 }
14939 _ => Err(RuntimeError::new("cologne_phonetic() requires string")),
14940 }
14941 });
14942
14943 define(interp, "detect_language", Some(1), |_, args| {
14949 match &args[0] {
14950 Value::String(s) => {
14951 if let Some(info) = detect(s) {
14952 let lang_code = match info.lang() {
14953 Lang::Eng => "en",
14954 Lang::Spa => "es",
14955 Lang::Fra => "fr",
14956 Lang::Deu => "de",
14957 Lang::Ita => "it",
14958 Lang::Por => "pt",
14959 Lang::Rus => "ru",
14960 Lang::Ara => "ar",
14961 Lang::Hin => "hi",
14962 Lang::Cmn => "zh",
14963 Lang::Jpn => "ja",
14964 Lang::Kor => "ko",
14965 Lang::Nld => "nl",
14966 Lang::Swe => "sv",
14967 Lang::Tur => "tr",
14968 Lang::Pol => "pl",
14969 Lang::Ukr => "uk",
14970 Lang::Ces => "cs",
14971 Lang::Dan => "da",
14972 Lang::Fin => "fi",
14973 Lang::Ell => "el",
14974 Lang::Heb => "he",
14975 Lang::Hun => "hu",
14976 Lang::Ind => "id",
14977 Lang::Nob => "no",
14978 Lang::Ron => "ro",
14979 Lang::Slk => "sk",
14980 Lang::Tha => "th",
14981 Lang::Vie => "vi",
14982 _ => "unknown",
14983 };
14984 Ok(Value::String(Rc::new(lang_code.to_string())))
14985 } else {
14986 Ok(Value::String(Rc::new("unknown".to_string())))
14987 }
14988 }
14989 _ => Err(RuntimeError::new("detect_language() requires string")),
14990 }
14991 });
14992
14993 define(
14995 interp,
14996 "detect_language_confidence",
14997 Some(1),
14998 |_, args| match &args[0] {
14999 Value::String(s) => {
15000 if let Some(info) = detect(s) {
15001 let lang_code = match info.lang() {
15002 Lang::Eng => "en",
15003 Lang::Spa => "es",
15004 Lang::Fra => "fr",
15005 Lang::Deu => "de",
15006 Lang::Ita => "it",
15007 Lang::Por => "pt",
15008 Lang::Rus => "ru",
15009 Lang::Ara => "ar",
15010 Lang::Cmn => "zh",
15011 Lang::Jpn => "ja",
15012 _ => "unknown",
15013 };
15014 let confidence = info.confidence();
15015 let mut map = HashMap::new();
15016 map.insert(
15017 "lang".to_string(),
15018 Value::String(Rc::new(lang_code.to_string())),
15019 );
15020 map.insert("confidence".to_string(), Value::Float(confidence as f64));
15021 Ok(Value::Map(Rc::new(RefCell::new(map))))
15022 } else {
15023 let mut map = HashMap::new();
15024 map.insert(
15025 "lang".to_string(),
15026 Value::String(Rc::new("unknown".to_string())),
15027 );
15028 map.insert("confidence".to_string(), Value::Float(0.0));
15029 Ok(Value::Map(Rc::new(RefCell::new(map))))
15030 }
15031 }
15032 _ => Err(RuntimeError::new(
15033 "detect_language_confidence() requires string",
15034 )),
15035 },
15036 );
15037
15038 define(
15040 interp,
15041 "detect_script_whatlang",
15042 Some(1),
15043 |_, args| match &args[0] {
15044 Value::String(s) => {
15045 if let Some(info) = detect(s) {
15046 let script_name = match info.script() {
15047 WhatLangScript::Latin => "Latin",
15048 WhatLangScript::Cyrillic => "Cyrillic",
15049 WhatLangScript::Arabic => "Arabic",
15050 WhatLangScript::Devanagari => "Devanagari",
15051 WhatLangScript::Ethiopic => "Ethiopic",
15052 WhatLangScript::Georgian => "Georgian",
15053 WhatLangScript::Greek => "Greek",
15054 WhatLangScript::Gujarati => "Gujarati",
15055 WhatLangScript::Gurmukhi => "Gurmukhi",
15056 WhatLangScript::Hangul => "Hangul",
15057 WhatLangScript::Hebrew => "Hebrew",
15058 WhatLangScript::Hiragana => "Hiragana",
15059 WhatLangScript::Kannada => "Kannada",
15060 WhatLangScript::Katakana => "Katakana",
15061 WhatLangScript::Khmer => "Khmer",
15062 WhatLangScript::Malayalam => "Malayalam",
15063 WhatLangScript::Mandarin => "Mandarin",
15064 WhatLangScript::Myanmar => "Myanmar",
15065 WhatLangScript::Oriya => "Oriya",
15066 WhatLangScript::Sinhala => "Sinhala",
15067 WhatLangScript::Tamil => "Tamil",
15068 WhatLangScript::Telugu => "Telugu",
15069 WhatLangScript::Thai => "Thai",
15070 WhatLangScript::Bengali => "Bengali",
15071 WhatLangScript::Armenian => "Armenian",
15072 };
15073 Ok(Value::String(Rc::new(script_name.to_string())))
15074 } else {
15075 Ok(Value::String(Rc::new("Unknown".to_string())))
15076 }
15077 }
15078 _ => Err(RuntimeError::new(
15079 "detect_script_whatlang() requires string",
15080 )),
15081 },
15082 );
15083
15084 define(interp, "is_language", Some(2), |_, args| {
15086 match (&args[0], &args[1]) {
15087 (Value::String(s), Value::String(lang)) => {
15088 if let Some(info) = detect(s) {
15089 let detected = match info.lang() {
15090 Lang::Eng => "en",
15091 Lang::Spa => "es",
15092 Lang::Fra => "fr",
15093 Lang::Deu => "de",
15094 Lang::Ita => "it",
15095 Lang::Por => "pt",
15096 Lang::Rus => "ru",
15097 _ => "unknown",
15098 };
15099 Ok(Value::Bool(detected == lang.as_str()))
15100 } else {
15101 Ok(Value::Bool(false))
15102 }
15103 }
15104 _ => Err(RuntimeError::new(
15105 "is_language() requires string and language code",
15106 )),
15107 }
15108 });
15109
15110 define(interp, "token_count", Some(1), |_, args| match &args[0] {
15116 Value::String(s) => {
15117 if let Ok(bpe) = cl100k_base() {
15118 let tokens = bpe.encode_with_special_tokens(s);
15119 Ok(Value::Int(tokens.len() as i64))
15120 } else {
15121 Err(RuntimeError::new("Failed to initialize tokenizer"))
15122 }
15123 }
15124 _ => Err(RuntimeError::new("token_count() requires string")),
15125 });
15126
15127 define(interp, "token_count_model", Some(2), |_, args| {
15129 match (&args[0], &args[1]) {
15130 (Value::String(s), Value::String(model)) => {
15131 let bpe_result = match model.as_str() {
15132 "gpt4" | "gpt-4" | "claude" | "cl100k" => cl100k_base(),
15133 "gpt3" | "gpt-3" | "p50k" => p50k_base(),
15134 "codex" | "r50k" => r50k_base(),
15135 _ => cl100k_base(), };
15137 if let Ok(bpe) = bpe_result {
15138 let tokens = bpe.encode_with_special_tokens(s);
15139 Ok(Value::Int(tokens.len() as i64))
15140 } else {
15141 Err(RuntimeError::new("Failed to initialize tokenizer"))
15142 }
15143 }
15144 _ => Err(RuntimeError::new(
15145 "token_count_model() requires string and model name",
15146 )),
15147 }
15148 });
15149
15150 define(interp, "tokenize_ids", Some(1), |_, args| match &args[0] {
15152 Value::String(s) => {
15153 if let Ok(bpe) = cl100k_base() {
15154 let tokens = bpe.encode_with_special_tokens(s);
15155 let values: Vec<Value> = tokens.into_iter().map(|t| Value::Int(t as i64)).collect();
15156 Ok(Value::Array(Rc::new(RefCell::new(values))))
15157 } else {
15158 Err(RuntimeError::new("Failed to initialize tokenizer"))
15159 }
15160 }
15161 _ => Err(RuntimeError::new("tokenize_ids() requires string")),
15162 });
15163
15164 define(interp, "truncate_tokens", Some(2), |_, args| {
15166 match (&args[0], &args[1]) {
15167 (Value::String(s), Value::Int(max_tokens)) => {
15168 if let Ok(bpe) = cl100k_base() {
15169 let tokens = bpe.encode_with_special_tokens(s);
15170 if tokens.len() <= *max_tokens as usize {
15171 Ok(Value::String(s.clone()))
15172 } else {
15173 let truncated: Vec<usize> =
15174 tokens.into_iter().take(*max_tokens as usize).collect();
15175 if let Ok(decoded) = bpe.decode(truncated) {
15176 Ok(Value::String(Rc::new(decoded)))
15177 } else {
15178 Err(RuntimeError::new("Failed to decode tokens"))
15179 }
15180 }
15181 } else {
15182 Err(RuntimeError::new("Failed to initialize tokenizer"))
15183 }
15184 }
15185 _ => Err(RuntimeError::new(
15186 "truncate_tokens() requires string and max tokens",
15187 )),
15188 }
15189 });
15190
15191 define(interp, "estimate_cost", Some(3), |_, args| {
15193 match (&args[0], &args[1], &args[2]) {
15194 (Value::String(s), Value::Float(input_cost), Value::Float(output_cost)) => {
15195 if let Ok(bpe) = cl100k_base() {
15196 let tokens = bpe.encode_with_special_tokens(s);
15197 let count = tokens.len() as f64;
15198 let input_total = (count / 1000.0) * input_cost;
15200 let output_total = (count / 1000.0) * output_cost;
15201 let mut map = HashMap::new();
15202 map.insert("tokens".to_string(), Value::Int(tokens.len() as i64));
15203 map.insert("input_cost".to_string(), Value::Float(input_total));
15204 map.insert("output_cost".to_string(), Value::Float(output_total));
15205 Ok(Value::Map(Rc::new(RefCell::new(map))))
15206 } else {
15207 Err(RuntimeError::new("Failed to initialize tokenizer"))
15208 }
15209 }
15210 _ => Err(RuntimeError::new(
15211 "estimate_cost() requires string, input cost, output cost",
15212 )),
15213 }
15214 });
15215
15216 define(interp, "stem", Some(1), |_, args| match &args[0] {
15222 Value::String(s) => {
15223 let stemmer = Stemmer::create(StemAlgorithm::English);
15224 let stemmed = stemmer.stem(s);
15225 Ok(Value::String(Rc::new(stemmed.to_string())))
15226 }
15227 _ => Err(RuntimeError::new("stem() requires string")),
15228 });
15229
15230 define(interp, "stem_language", Some(2), |_, args| {
15232 match (&args[0], &args[1]) {
15233 (Value::String(s), Value::String(lang)) => {
15234 let algorithm = match lang.as_str() {
15235 "en" | "english" => StemAlgorithm::English,
15236 "fr" | "french" => StemAlgorithm::French,
15237 "de" | "german" => StemAlgorithm::German,
15238 "es" | "spanish" => StemAlgorithm::Spanish,
15239 "it" | "italian" => StemAlgorithm::Italian,
15240 "pt" | "portuguese" => StemAlgorithm::Portuguese,
15241 "nl" | "dutch" => StemAlgorithm::Dutch,
15242 "sv" | "swedish" => StemAlgorithm::Swedish,
15243 "no" | "norwegian" => StemAlgorithm::Norwegian,
15244 "da" | "danish" => StemAlgorithm::Danish,
15245 "fi" | "finnish" => StemAlgorithm::Finnish,
15246 "ru" | "russian" => StemAlgorithm::Russian,
15247 "ro" | "romanian" => StemAlgorithm::Romanian,
15248 "hu" | "hungarian" => StemAlgorithm::Hungarian,
15249 "tr" | "turkish" => StemAlgorithm::Turkish,
15250 "ar" | "arabic" => StemAlgorithm::Arabic,
15251 _ => StemAlgorithm::English,
15252 };
15253 let stemmer = Stemmer::create(algorithm);
15254 let stemmed = stemmer.stem(s);
15255 Ok(Value::String(Rc::new(stemmed.to_string())))
15256 }
15257 _ => Err(RuntimeError::new(
15258 "stem_language() requires string and language code",
15259 )),
15260 }
15261 });
15262
15263 define(interp, "stem_all", Some(1), |_, args| match &args[0] {
15265 Value::Array(arr) => {
15266 let stemmer = Stemmer::create(StemAlgorithm::English);
15267 let arr_ref = arr.borrow();
15268 let results: Vec<Value> = arr_ref
15269 .iter()
15270 .filter_map(|v| {
15271 if let Value::String(s) = v {
15272 Some(Value::String(Rc::new(stemmer.stem(s).to_string())))
15273 } else {
15274 None
15275 }
15276 })
15277 .collect();
15278 Ok(Value::Array(Rc::new(RefCell::new(results))))
15279 }
15280 _ => Err(RuntimeError::new("stem_all() requires array of strings")),
15281 });
15282
15283 define(interp, "is_stopword", Some(1), |_, args| match &args[0] {
15289 Value::String(s) => {
15290 let word = s.to_lowercase();
15291 let stopwords = get_stopwords("en");
15292 Ok(Value::Bool(stopwords.contains(&word.as_str())))
15293 }
15294 _ => Err(RuntimeError::new("is_stopword() requires string")),
15295 });
15296
15297 define(interp, "is_stopword_language", Some(2), |_, args| {
15299 match (&args[0], &args[1]) {
15300 (Value::String(s), Value::String(lang)) => {
15301 let word = s.to_lowercase();
15302 let stopwords = get_stopwords(lang);
15303 Ok(Value::Bool(stopwords.contains(&word.as_str())))
15304 }
15305 _ => Err(RuntimeError::new(
15306 "is_stopword_language() requires string and language",
15307 )),
15308 }
15309 });
15310
15311 define(interp, "remove_stopwords", Some(1), |_, args| {
15313 match &args[0] {
15314 Value::Array(arr) => {
15315 let stopwords = get_stopwords("en");
15316 let arr_ref = arr.borrow();
15317 let results: Vec<Value> = arr_ref
15318 .iter()
15319 .filter(|v| {
15320 if let Value::String(s) = v {
15321 !stopwords.contains(&s.to_lowercase().as_str())
15322 } else {
15323 true
15324 }
15325 })
15326 .cloned()
15327 .collect();
15328 Ok(Value::Array(Rc::new(RefCell::new(results))))
15329 }
15330 _ => Err(RuntimeError::new(
15331 "remove_stopwords() requires array of strings",
15332 )),
15333 }
15334 });
15335
15336 define(
15338 interp,
15339 "remove_stopwords_text",
15340 Some(1),
15341 |_, args| match &args[0] {
15342 Value::String(s) => {
15343 let stopwords = get_stopwords("en");
15344 let words: Vec<&str> = s
15345 .split_whitespace()
15346 .filter(|w| !stopwords.contains(&w.to_lowercase().as_str()))
15347 .collect();
15348 Ok(Value::String(Rc::new(words.join(" "))))
15349 }
15350 _ => Err(RuntimeError::new("remove_stopwords_text() requires string")),
15351 },
15352 );
15353
15354 define(
15356 interp,
15357 "get_stopwords_list",
15358 Some(1),
15359 |_, args| match &args[0] {
15360 Value::String(lang) => {
15361 let stopwords = get_stopwords(lang);
15362 let values: Vec<Value> = stopwords
15363 .iter()
15364 .map(|s| Value::String(Rc::new(s.to_string())))
15365 .collect();
15366 Ok(Value::Array(Rc::new(RefCell::new(values))))
15367 }
15368 _ => Err(RuntimeError::new(
15369 "get_stopwords_list() requires language code",
15370 )),
15371 },
15372 );
15373
15374 define(interp, "ngrams", Some(2), |_, args| {
15380 match (&args[0], &args[1]) {
15381 (Value::String(s), Value::Int(n)) => {
15382 let words: Vec<&str> = s.split_whitespace().collect();
15383 let n = *n as usize;
15384 if n == 0 || n > words.len() {
15385 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15386 }
15387 let ngrams: Vec<Value> = words
15388 .windows(n)
15389 .map(|w| Value::String(Rc::new(w.join(" "))))
15390 .collect();
15391 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
15392 }
15393 _ => Err(RuntimeError::new("ngrams() requires string and n")),
15394 }
15395 });
15396
15397 define(interp, "char_ngrams", Some(2), |_, args| {
15399 match (&args[0], &args[1]) {
15400 (Value::String(s), Value::Int(n)) => {
15401 let chars: Vec<char> = s.chars().collect();
15402 let n = *n as usize;
15403 if n == 0 || n > chars.len() {
15404 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15405 }
15406 let ngrams: Vec<Value> = chars
15407 .windows(n)
15408 .map(|w| Value::String(Rc::new(w.iter().collect())))
15409 .collect();
15410 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
15411 }
15412 _ => Err(RuntimeError::new("char_ngrams() requires string and n")),
15413 }
15414 });
15415
15416 define(interp, "shingles", Some(2), |_, args| {
15418 match (&args[0], &args[1]) {
15419 (Value::String(s), Value::Int(n)) => {
15420 let words: Vec<&str> = s.split_whitespace().collect();
15421 let n = *n as usize;
15422 if n == 0 || n > words.len() {
15423 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
15424 }
15425 let mut seen = std::collections::HashSet::new();
15426 let shingles: Vec<Value> = words
15427 .windows(n)
15428 .filter_map(|w| {
15429 let s = w.join(" ");
15430 if seen.insert(s.clone()) {
15431 Some(Value::String(Rc::new(s)))
15432 } else {
15433 None
15434 }
15435 })
15436 .collect();
15437 Ok(Value::Array(Rc::new(RefCell::new(shingles))))
15438 }
15439 _ => Err(RuntimeError::new("shingles() requires string and n")),
15440 }
15441 });
15442
15443 define(interp, "jaccard_similarity", Some(2), |_, args| {
15445 match (&args[0], &args[1]) {
15446 (Value::Array(a), Value::Array(b)) => {
15447 let a_ref = a.borrow();
15448 let b_ref = b.borrow();
15449 let set_a: std::collections::HashSet<String> = a_ref
15450 .iter()
15451 .filter_map(|v| {
15452 if let Value::String(s) = v {
15453 Some(s.to_string())
15454 } else {
15455 None
15456 }
15457 })
15458 .collect();
15459 let set_b: std::collections::HashSet<String> = b_ref
15460 .iter()
15461 .filter_map(|v| {
15462 if let Value::String(s) = v {
15463 Some(s.to_string())
15464 } else {
15465 None
15466 }
15467 })
15468 .collect();
15469 let intersection = set_a.intersection(&set_b).count();
15470 let union = set_a.union(&set_b).count();
15471 if union == 0 {
15472 Ok(Value::Float(0.0))
15473 } else {
15474 Ok(Value::Float(intersection as f64 / union as f64))
15475 }
15476 }
15477 _ => Err(RuntimeError::new(
15478 "jaccard_similarity() requires two arrays",
15479 )),
15480 }
15481 });
15482
15483 define(interp, "minhash_signature", Some(2), |_, args| {
15485 match (&args[0], &args[1]) {
15486 (Value::Array(arr), Value::Int(num_hashes)) => {
15487 let arr_ref = arr.borrow();
15488 let items: std::collections::HashSet<String> = arr_ref
15489 .iter()
15490 .filter_map(|v| {
15491 if let Value::String(s) = v {
15492 Some(s.to_string())
15493 } else {
15494 None
15495 }
15496 })
15497 .collect();
15498
15499 let mut signature: Vec<Value> = Vec::with_capacity(*num_hashes as usize);
15501 for i in 0..*num_hashes {
15502 let mut min_hash: u64 = u64::MAX;
15503 for item in &items {
15504 let hash = compute_hash(item, i as u64);
15505 if hash < min_hash {
15506 min_hash = hash;
15507 }
15508 }
15509 signature.push(Value::Int(min_hash as i64));
15510 }
15511 Ok(Value::Array(Rc::new(RefCell::new(signature))))
15512 }
15513 _ => Err(RuntimeError::new(
15514 "minhash_signature() requires array and num_hashes",
15515 )),
15516 }
15517 });
15518
15519 define(interp, "preprocess_text", Some(1), |_, args| {
15525 match &args[0] {
15526 Value::String(s) => {
15527 let lower = s.to_lowercase();
15529 let clean: String = lower
15531 .chars()
15532 .filter(|c| c.is_alphanumeric() || c.is_whitespace())
15533 .collect();
15534 let normalized: String = clean.split_whitespace().collect::<Vec<_>>().join(" ");
15536 Ok(Value::String(Rc::new(normalized)))
15537 }
15538 _ => Err(RuntimeError::new("preprocess_text() requires string")),
15539 }
15540 });
15541
15542 define(interp, "tokenize_words", Some(1), |_, args| {
15544 match &args[0] {
15545 Value::String(s) => {
15546 let words: Vec<Value> = s
15547 .split_whitespace()
15548 .map(|w| Value::String(Rc::new(w.to_string())))
15549 .collect();
15550 Ok(Value::Array(Rc::new(RefCell::new(words))))
15551 }
15552 _ => Err(RuntimeError::new("tokenize_words() requires string")),
15553 }
15554 });
15555
15556 define(interp, "extract_keywords", Some(1), |_, args| {
15558 match &args[0] {
15559 Value::String(s) => {
15560 let stopwords = get_stopwords("en");
15561 let words: Vec<Value> = s
15562 .split_whitespace()
15563 .filter(|w| {
15564 let lower = w.to_lowercase();
15565 !stopwords.contains(&lower.as_str()) && lower.len() > 2
15566 })
15567 .map(|w| Value::String(Rc::new(w.to_lowercase())))
15568 .collect();
15569 Ok(Value::Array(Rc::new(RefCell::new(words))))
15570 }
15571 _ => Err(RuntimeError::new("extract_keywords() requires string")),
15572 }
15573 });
15574
15575 define(interp, "word_frequency", Some(1), |_, args| {
15577 match &args[0] {
15578 Value::String(s) => {
15579 let mut freq: HashMap<String, i64> = HashMap::new();
15580 for word in s.split_whitespace() {
15581 let lower = word.to_lowercase();
15582 *freq.entry(lower).or_insert(0) += 1;
15583 }
15584 let map: HashMap<String, Value> =
15585 freq.into_iter().map(|(k, v)| (k, Value::Int(v))).collect();
15586 Ok(Value::Map(Rc::new(RefCell::new(map))))
15587 }
15588 _ => Err(RuntimeError::new("word_frequency() requires string")),
15589 }
15590 });
15591
15592 define(interp, "sentiment_words", Some(1), |_, args| {
15598 match &args[0] {
15599 Value::String(s) => {
15600 let positive = vec![
15601 "good",
15602 "great",
15603 "excellent",
15604 "amazing",
15605 "wonderful",
15606 "fantastic",
15607 "love",
15608 "happy",
15609 "joy",
15610 "beautiful",
15611 "awesome",
15612 "perfect",
15613 "best",
15614 "brilliant",
15615 "delightful",
15616 "pleasant",
15617 "positive",
15618 ];
15619 let negative = vec![
15620 "bad",
15621 "terrible",
15622 "awful",
15623 "horrible",
15624 "hate",
15625 "sad",
15626 "angry",
15627 "worst",
15628 "poor",
15629 "negative",
15630 "disappointing",
15631 "ugly",
15632 "disgusting",
15633 "painful",
15634 "miserable",
15635 "annoying",
15636 ];
15637
15638 let lower = s.to_lowercase();
15639 let words: Vec<&str> = lower.split_whitespace().collect();
15640 let pos_count: i64 = words.iter().filter(|w| positive.contains(w)).count() as i64;
15641 let neg_count: i64 = words.iter().filter(|w| negative.contains(w)).count() as i64;
15642
15643 let mut map = HashMap::new();
15644 map.insert("positive".to_string(), Value::Int(pos_count));
15645 map.insert("negative".to_string(), Value::Int(neg_count));
15646 map.insert("total".to_string(), Value::Int(words.len() as i64));
15647
15648 let score = if pos_count + neg_count > 0 {
15649 (pos_count - neg_count) as f64 / (pos_count + neg_count) as f64
15650 } else {
15651 0.0
15652 };
15653 map.insert("score".to_string(), Value::Float(score));
15654
15655 Ok(Value::Map(Rc::new(RefCell::new(map))))
15656 }
15657 _ => Err(RuntimeError::new("sentiment_words() requires string")),
15658 }
15659 });
15660
15661 define(interp, "has_question", Some(1), |_, args| match &args[0] {
15663 Value::String(s) => {
15664 let has_q_mark = s.contains('?');
15665 let lower = s.to_lowercase();
15666 let question_words = [
15667 "what", "where", "when", "why", "how", "who", "which", "whose", "whom",
15668 ];
15669 let starts_with_q = question_words.iter().any(|w| lower.starts_with(w));
15670 Ok(Value::Bool(has_q_mark || starts_with_q))
15671 }
15672 _ => Err(RuntimeError::new("has_question() requires string")),
15673 });
15674
15675 define(interp, "has_exclamation", Some(1), |_, args| {
15677 match &args[0] {
15678 Value::String(s) => Ok(Value::Bool(s.contains('!'))),
15679 _ => Err(RuntimeError::new("has_exclamation() requires string")),
15680 }
15681 });
15682
15683 define(interp, "text_formality", Some(1), |_, args| {
15685 match &args[0] {
15686 Value::String(s) => {
15687 let lower = s.to_lowercase();
15688 let informal_markers = vec![
15689 "gonna", "wanna", "gotta", "kinda", "sorta", "dunno", "yeah", "yep", "nope",
15690 "ok", "lol", "omg", "btw", "u", "ur", "r", "y", "2", "4",
15691 ];
15692 let formal_markers = vec![
15693 "therefore",
15694 "furthermore",
15695 "moreover",
15696 "consequently",
15697 "nevertheless",
15698 "however",
15699 "whereas",
15700 "hereby",
15701 "respectfully",
15702 "sincerely",
15703 "accordingly",
15704 ];
15705
15706 let words: Vec<&str> = lower.split_whitespace().collect();
15707 let informal_count = words
15708 .iter()
15709 .filter(|w| informal_markers.contains(w))
15710 .count();
15711 let formal_count = words.iter().filter(|w| formal_markers.contains(w)).count();
15712
15713 let score = if informal_count + formal_count > 0 {
15714 formal_count as f64 / (informal_count + formal_count) as f64
15715 } else {
15716 0.5 };
15718
15719 Ok(Value::Float(score))
15720 }
15721 _ => Err(RuntimeError::new("text_formality() requires string")),
15722 }
15723 });
15724
15725 define(interp, "sentiment_vader", Some(1), |_, args| {
15731 match &args[0] {
15732 Value::String(s) => {
15733 let result = compute_vader_sentiment(s);
15734 let mut map = HashMap::new();
15735 map.insert("positive".to_string(), Value::Float(result.0));
15736 map.insert("negative".to_string(), Value::Float(result.1));
15737 map.insert("neutral".to_string(), Value::Float(result.2));
15738 map.insert("compound".to_string(), Value::Float(result.3));
15739 Ok(Value::Map(Rc::new(RefCell::new(map))))
15740 }
15741 _ => Err(RuntimeError::new("sentiment_vader() requires string")),
15742 }
15743 });
15744
15745 define(interp, "emotion_detect", Some(1), |_, args| {
15747 match &args[0] {
15748 Value::String(s) => {
15749 let emotions = compute_emotions(s);
15750 let map: HashMap<String, Value> = emotions
15751 .into_iter()
15752 .map(|(k, v)| (k, Value::Float(v)))
15753 .collect();
15754 Ok(Value::Map(Rc::new(RefCell::new(map))))
15755 }
15756 _ => Err(RuntimeError::new("emotion_detect() requires string")),
15757 }
15758 });
15759
15760 define(interp, "intensity_score", Some(1), |_, args| {
15762 match &args[0] {
15763 Value::String(s) => {
15764 let score = compute_intensity(s);
15765 Ok(Value::Float(score))
15766 }
15767 _ => Err(RuntimeError::new("intensity_score() requires string")),
15768 }
15769 });
15770
15771 define(interp, "detect_sarcasm", Some(1), |_, args| {
15777 match &args[0] {
15778 Value::String(s) => {
15779 let result = compute_sarcasm_score(s);
15780 let mut map = HashMap::new();
15781 map.insert("score".to_string(), Value::Float(result.0));
15782 map.insert("confidence".to_string(), Value::Float(result.1));
15783 let markers: Vec<Value> = result
15784 .2
15785 .into_iter()
15786 .map(|m| Value::String(Rc::new(m)))
15787 .collect();
15788 map.insert(
15789 "markers".to_string(),
15790 Value::Array(Rc::new(RefCell::new(markers))),
15791 );
15792 Ok(Value::Map(Rc::new(RefCell::new(map))))
15793 }
15794 _ => Err(RuntimeError::new("detect_sarcasm() requires string")),
15795 }
15796 });
15797
15798 define(interp, "is_sarcastic", Some(1), |_, args| match &args[0] {
15800 Value::String(s) => {
15801 let result = compute_sarcasm_score(s);
15802 Ok(Value::Bool(result.0 > 0.5))
15803 }
15804 _ => Err(RuntimeError::new("is_sarcastic() requires string")),
15805 });
15806
15807 define(interp, "detect_irony", Some(1), |_, args| match &args[0] {
15809 Value::String(s) => {
15810 let score = compute_irony_score(s);
15811 Ok(Value::Float(score))
15812 }
15813 _ => Err(RuntimeError::new("detect_irony() requires string")),
15814 });
15815
15816 define(interp, "extract_emails", Some(1), |_, args| {
15822 match &args[0] {
15823 Value::String(s) => {
15824 let re = Regex::new(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}").unwrap();
15825 let emails: Vec<Value> = re
15826 .find_iter(s)
15827 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
15828 .collect();
15829 Ok(Value::Array(Rc::new(RefCell::new(emails))))
15830 }
15831 _ => Err(RuntimeError::new("extract_emails() requires string")),
15832 }
15833 });
15834
15835 define(interp, "extract_urls", Some(1), |_, args| match &args[0] {
15837 Value::String(s) => {
15838 let re = Regex::new(r"https?://[^\s<>\[\]{}|\\^]+").unwrap();
15839 let urls: Vec<Value> = re
15840 .find_iter(s)
15841 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
15842 .collect();
15843 Ok(Value::Array(Rc::new(RefCell::new(urls))))
15844 }
15845 _ => Err(RuntimeError::new("extract_urls() requires string")),
15846 });
15847
15848 define(
15850 interp,
15851 "extract_phone_numbers",
15852 Some(1),
15853 |_, args| match &args[0] {
15854 Value::String(s) => {
15855 let re =
15856 Regex::new(r"(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}")
15857 .unwrap();
15858 let phones: Vec<Value> = re
15859 .find_iter(s)
15860 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
15861 .collect();
15862 Ok(Value::Array(Rc::new(RefCell::new(phones))))
15863 }
15864 _ => Err(RuntimeError::new("extract_phone_numbers() requires string")),
15865 },
15866 );
15867
15868 define(interp, "extract_dates", Some(1), |_, args| {
15870 match &args[0] {
15871 Value::String(s) => {
15872 let patterns = vec![
15874 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}",
15878 r"\d{1,2}\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{4}",
15879 ];
15880 let mut dates = Vec::new();
15881 for pattern in patterns {
15882 if let Ok(re) = Regex::new(pattern) {
15883 for m in re.find_iter(s) {
15884 dates.push(Value::String(Rc::new(m.as_str().to_string())));
15885 }
15886 }
15887 }
15888 Ok(Value::Array(Rc::new(RefCell::new(dates))))
15889 }
15890 _ => Err(RuntimeError::new("extract_dates() requires string")),
15891 }
15892 });
15893
15894 define(interp, "extract_money", Some(1), |_, args| match &args[0] {
15896 Value::String(s) => {
15897 let re = Regex::new(r"[$€£¥]\s*\d+(?:,\d{3})*(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:dollars?|euros?|pounds?|USD|EUR|GBP)").unwrap();
15898 let money: Vec<Value> = re
15899 .find_iter(s)
15900 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
15901 .collect();
15902 Ok(Value::Array(Rc::new(RefCell::new(money))))
15903 }
15904 _ => Err(RuntimeError::new("extract_money() requires string")),
15905 });
15906
15907 define(interp, "extract_hashtags", Some(1), |_, args| {
15909 match &args[0] {
15910 Value::String(s) => {
15911 let re = Regex::new(r"#\w+").unwrap();
15912 let tags: Vec<Value> = re
15913 .find_iter(s)
15914 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
15915 .collect();
15916 Ok(Value::Array(Rc::new(RefCell::new(tags))))
15917 }
15918 _ => Err(RuntimeError::new("extract_hashtags() requires string")),
15919 }
15920 });
15921
15922 define(interp, "extract_mentions", Some(1), |_, args| {
15924 match &args[0] {
15925 Value::String(s) => {
15926 let re = Regex::new(r"@\w+").unwrap();
15927 let mentions: Vec<Value> = re
15928 .find_iter(s)
15929 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
15930 .collect();
15931 Ok(Value::Array(Rc::new(RefCell::new(mentions))))
15932 }
15933 _ => Err(RuntimeError::new("extract_mentions() requires string")),
15934 }
15935 });
15936
15937 define(interp, "extract_numbers", Some(1), |_, args| {
15939 match &args[0] {
15940 Value::String(s) => {
15941 let re = Regex::new(r"-?\d+(?:,\d{3})*(?:\.\d+)?").unwrap();
15942 let numbers: Vec<Value> = re
15943 .find_iter(s)
15944 .filter_map(|m| {
15945 let num_str = m.as_str().replace(",", "");
15946 if let Ok(n) = num_str.parse::<f64>() {
15947 Some(Value::Float(n))
15948 } else {
15949 None
15950 }
15951 })
15952 .collect();
15953 Ok(Value::Array(Rc::new(RefCell::new(numbers))))
15954 }
15955 _ => Err(RuntimeError::new("extract_numbers() requires string")),
15956 }
15957 });
15958
15959 define(interp, "extract_entities", Some(1), |_, args| {
15961 match &args[0] {
15962 Value::String(s) => {
15963 let re = Regex::new(r"(?:[.!?]\s+)?([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)").unwrap();
15965 let mut entities = std::collections::HashSet::new();
15966 for cap in re.captures_iter(s) {
15967 if let Some(m) = cap.get(1) {
15968 let entity = m.as_str().to_string();
15969 let starters = [
15971 "The", "A", "An", "This", "That", "It", "I", "We", "They", "He", "She",
15972 ];
15973 if !starters.contains(&entity.as_str()) {
15974 entities.insert(entity);
15975 }
15976 }
15977 }
15978 let results: Vec<Value> = entities
15979 .into_iter()
15980 .map(|e| Value::String(Rc::new(e)))
15981 .collect();
15982 Ok(Value::Array(Rc::new(RefCell::new(results))))
15983 }
15984 _ => Err(RuntimeError::new("extract_entities() requires string")),
15985 }
15986 });
15987
15988 define(interp, "text_hash_vector", Some(2), |_, args| {
15994 match (&args[0], &args[1]) {
15995 (Value::String(s), Value::Int(dims)) => {
15996 let dims = *dims as usize;
15997 let mut vector = vec![0.0f64; dims];
15998
15999 for word in s.to_lowercase().split_whitespace() {
16001 let hash = compute_hash(word, 0);
16002 let idx = (hash as usize) % dims;
16003 vector[idx] += 1.0;
16004 }
16005
16006 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
16008 if magnitude > 0.0 {
16009 for v in vector.iter_mut() {
16010 *v /= magnitude;
16011 }
16012 }
16013
16014 let values: Vec<Value> = vector.into_iter().map(Value::Float).collect();
16015 Ok(Value::Array(Rc::new(RefCell::new(values))))
16016 }
16017 _ => Err(RuntimeError::new(
16018 "text_hash_vector() requires string and dimensions",
16019 )),
16020 }
16021 });
16022
16023 define(interp, "text_fingerprint", Some(1), |_, args| {
16025 match &args[0] {
16026 Value::String(s) => {
16027 let lower = s.to_lowercase();
16029 let words: Vec<&str> = lower.split_whitespace().collect();
16030
16031 let mut fp: u64 = 0;
16032 for (i, word) in words.iter().enumerate() {
16033 let h = compute_hash(word, i as u64);
16034 fp ^= h.rotate_left((i % 64) as u32);
16035 }
16036
16037 Ok(Value::String(Rc::new(format!("{:016x}", fp))))
16038 }
16039 _ => Err(RuntimeError::new("text_fingerprint() requires string")),
16040 }
16041 });
16042
16043 define(interp, "cosine_similarity", Some(2), |_, args| {
16045 match (&args[0], &args[1]) {
16046 (Value::Array(a), Value::Array(b)) => {
16047 let a_ref = a.borrow();
16048 let b_ref = b.borrow();
16049
16050 if a_ref.len() != b_ref.len() {
16051 return Err(RuntimeError::new("Vectors must have same length"));
16052 }
16053
16054 let mut dot = 0.0;
16055 let mut mag_a = 0.0;
16056 let mut mag_b = 0.0;
16057
16058 for (va, vb) in a_ref.iter().zip(b_ref.iter()) {
16059 let fa = match va {
16060 Value::Float(f) => *f,
16061 Value::Int(i) => *i as f64,
16062 _ => continue,
16063 };
16064 let fb = match vb {
16065 Value::Float(f) => *f,
16066 Value::Int(i) => *i as f64,
16067 _ => continue,
16068 };
16069 dot += fa * fb;
16070 mag_a += fa * fa;
16071 mag_b += fb * fb;
16072 }
16073
16074 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
16075 if denom == 0.0 {
16076 Ok(Value::Float(0.0))
16077 } else {
16078 Ok(Value::Float(dot / denom))
16079 }
16080 }
16081 _ => Err(RuntimeError::new("cosine_similarity() requires two arrays")),
16082 }
16083 });
16084
16085 define(interp, "text_similarity_embedding", Some(2), |_, args| {
16087 match (&args[0], &args[1]) {
16088 (Value::String(a), Value::String(b)) => {
16089 let dims = 128;
16090
16091 let vec_a = create_hash_vector(a, dims);
16093 let vec_b = create_hash_vector(b, dims);
16094
16095 let mut dot = 0.0;
16097 let mut mag_a = 0.0;
16098 let mut mag_b = 0.0;
16099
16100 for i in 0..dims {
16101 dot += vec_a[i] * vec_b[i];
16102 mag_a += vec_a[i] * vec_a[i];
16103 mag_b += vec_b[i] * vec_b[i];
16104 }
16105
16106 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
16107 if denom == 0.0 {
16108 Ok(Value::Float(0.0))
16109 } else {
16110 Ok(Value::Float(dot / denom))
16111 }
16112 }
16113 _ => Err(RuntimeError::new(
16114 "text_similarity_embedding() requires two strings",
16115 )),
16116 }
16117 });
16118
16119 define(
16125 interp,
16126 "flesch_reading_ease",
16127 Some(1),
16128 |_, args| match &args[0] {
16129 Value::String(s) => {
16130 let (words, sentences, syllables) = count_text_stats(s);
16131 if words == 0 || sentences == 0 {
16132 return Ok(Value::Float(0.0));
16133 }
16134 let score = 206.835
16135 - 1.015 * (words as f64 / sentences as f64)
16136 - 84.6 * (syllables as f64 / words as f64);
16137 Ok(Value::Float(score.max(0.0).min(100.0)))
16138 }
16139 _ => Err(RuntimeError::new("flesch_reading_ease() requires string")),
16140 },
16141 );
16142
16143 define(
16145 interp,
16146 "flesch_kincaid_grade",
16147 Some(1),
16148 |_, args| match &args[0] {
16149 Value::String(s) => {
16150 let (words, sentences, syllables) = count_text_stats(s);
16151 if words == 0 || sentences == 0 {
16152 return Ok(Value::Float(0.0));
16153 }
16154 let grade = 0.39 * (words as f64 / sentences as f64)
16155 + 11.8 * (syllables as f64 / words as f64)
16156 - 15.59;
16157 Ok(Value::Float(grade.max(0.0)))
16158 }
16159 _ => Err(RuntimeError::new("flesch_kincaid_grade() requires string")),
16160 },
16161 );
16162
16163 define(
16165 interp,
16166 "automated_readability_index",
16167 Some(1),
16168 |_, args| match &args[0] {
16169 Value::String(s) => {
16170 let chars: usize = s.chars().filter(|c| c.is_alphanumeric()).count();
16171 let words: usize = s.split_whitespace().count();
16172 let sentences: usize = s
16173 .matches(|c| c == '.' || c == '!' || c == '?')
16174 .count()
16175 .max(1);
16176
16177 if words == 0 {
16178 return Ok(Value::Float(0.0));
16179 }
16180
16181 let ari = 4.71 * (chars as f64 / words as f64)
16182 + 0.5 * (words as f64 / sentences as f64)
16183 - 21.43;
16184 Ok(Value::Float(ari.max(0.0)))
16185 }
16186 _ => Err(RuntimeError::new(
16187 "automated_readability_index() requires string",
16188 )),
16189 },
16190 );
16191
16192 define(interp, "reading_time", Some(1), |_, args| {
16194 match &args[0] {
16195 Value::String(s) => {
16196 let words = s.split_whitespace().count();
16197 let minutes = words as f64 / 200.0; Ok(Value::Float(minutes))
16199 }
16200 _ => Err(RuntimeError::new("reading_time() requires string")),
16201 }
16202 });
16203
16204 define(interp, "speaking_time", Some(1), |_, args| {
16206 match &args[0] {
16207 Value::String(s) => {
16208 let words = s.split_whitespace().count();
16209 let minutes = words as f64 / 150.0; Ok(Value::Float(minutes))
16211 }
16212 _ => Err(RuntimeError::new("speaking_time() requires string")),
16213 }
16214 });
16215}
16216
16217fn compute_vader_sentiment(s: &str) -> (f64, f64, f64, f64) {
16223 let positive_words: Vec<(&str, f64)> = vec![
16225 ("love", 3.0),
16226 ("loved", 3.0),
16227 ("loving", 3.0),
16228 ("excellent", 3.0),
16229 ("amazing", 3.0),
16230 ("fantastic", 3.0),
16231 ("wonderful", 3.0),
16232 ("great", 2.5),
16233 ("awesome", 2.5),
16234 ("brilliant", 2.5),
16235 ("superb", 2.5),
16236 ("good", 2.0),
16237 ("nice", 2.0),
16238 ("pleasant", 2.0),
16239 ("happy", 2.0),
16240 ("like", 1.5),
16241 ("enjoy", 1.5),
16242 ("fine", 1.5),
16243 ("okay", 1.0),
16244 ("best", 3.0),
16245 ("perfect", 3.0),
16246 ("beautiful", 2.5),
16247 ("delightful", 2.5),
16248 ("excited", 2.5),
16249 ("thrilled", 3.0),
16250 ("glad", 2.0),
16251 ("pleased", 2.0),
16252 ];
16253
16254 let negative_words: Vec<(&str, f64)> = vec![
16255 ("hate", 3.0),
16256 ("hated", 3.0),
16257 ("hating", 3.0),
16258 ("terrible", 3.0),
16259 ("horrible", 3.0),
16260 ("awful", 3.0),
16261 ("disgusting", 3.0),
16262 ("bad", 2.5),
16263 ("poor", 2.5),
16264 ("worst", 3.0),
16265 ("pathetic", 2.5),
16266 ("sad", 2.0),
16267 ("angry", 2.5),
16268 ("upset", 2.0),
16269 ("disappointed", 2.0),
16270 ("dislike", 1.5),
16271 ("annoying", 2.0),
16272 ("boring", 1.5),
16273 ("mediocre", 1.0),
16274 ("ugly", 2.5),
16275 ("stupid", 2.5),
16276 ("dumb", 2.0),
16277 ("useless", 2.5),
16278 ("painful", 2.5),
16279 ("miserable", 3.0),
16280 ("depressing", 2.5),
16281 ("frustrating", 2.0),
16282 ];
16283
16284 let boosters = vec![
16286 "very",
16287 "really",
16288 "extremely",
16289 "absolutely",
16290 "incredibly",
16291 "totally",
16292 "so",
16293 ];
16294 let dampeners = vec![
16295 "somewhat", "slightly", "a bit", "kind of", "sort of", "barely",
16296 ];
16297
16298 let lower = s.to_lowercase();
16299 let words: Vec<&str> = lower.split_whitespace().collect();
16300
16301 let mut pos_score = 0.0;
16302 let mut neg_score = 0.0;
16303 let mut word_count = 0;
16304
16305 for (i, word) in words.iter().enumerate() {
16306 let mut modifier = 1.0;
16307
16308 if i > 0 {
16310 if boosters.contains(&words[i - 1]) {
16311 modifier = 1.5;
16312 } else if dampeners.iter().any(|d| words[i - 1].contains(d)) {
16313 modifier = 0.5;
16314 }
16315 }
16316
16317 let negated = i > 0
16319 && [
16320 "not",
16321 "no",
16322 "never",
16323 "neither",
16324 "don't",
16325 "doesn't",
16326 "didn't",
16327 "won't",
16328 "wouldn't",
16329 "couldn't",
16330 "shouldn't",
16331 ]
16332 .contains(&words[i - 1]);
16333
16334 if let Some((_, score)) = positive_words.iter().find(|(w, _)| w == word) {
16335 if negated {
16336 neg_score += score * modifier;
16337 } else {
16338 pos_score += score * modifier;
16339 }
16340 word_count += 1;
16341 } else if let Some((_, score)) = negative_words.iter().find(|(w, _)| w == word) {
16342 if negated {
16343 pos_score += score * modifier * 0.5; } else {
16345 neg_score += score * modifier;
16346 }
16347 word_count += 1;
16348 }
16349 }
16350
16351 let total = pos_score + neg_score;
16353 let (pos_norm, neg_norm) = if total > 0.0 {
16354 (pos_score / total, neg_score / total)
16355 } else {
16356 (0.0, 0.0)
16357 };
16358
16359 let neutral = 1.0 - pos_norm - neg_norm;
16360
16361 let compound = if word_count > 0 {
16363 ((pos_score - neg_score) / (word_count as f64 * 3.0))
16364 .max(-1.0)
16365 .min(1.0)
16366 } else {
16367 0.0
16368 };
16369
16370 (pos_norm, neg_norm, neutral.max(0.0), compound)
16371}
16372
16373fn compute_emotions(s: &str) -> HashMap<String, f64> {
16375 let emotion_words: Vec<(&str, &str)> = vec![
16376 ("happy", "joy"),
16378 ("joyful", "joy"),
16379 ("delighted", "joy"),
16380 ("cheerful", "joy"),
16381 ("excited", "joy"),
16382 ("thrilled", "joy"),
16383 ("ecstatic", "joy"),
16384 ("elated", "joy"),
16385 ("sad", "sadness"),
16387 ("unhappy", "sadness"),
16388 ("depressed", "sadness"),
16389 ("miserable", "sadness"),
16390 ("gloomy", "sadness"),
16391 ("heartbroken", "sadness"),
16392 ("sorrowful", "sadness"),
16393 ("melancholy", "sadness"),
16394 ("angry", "anger"),
16396 ("furious", "anger"),
16397 ("enraged", "anger"),
16398 ("irritated", "anger"),
16399 ("annoyed", "anger"),
16400 ("outraged", "anger"),
16401 ("livid", "anger"),
16402 ("mad", "anger"),
16403 ("afraid", "fear"),
16405 ("scared", "fear"),
16406 ("terrified", "fear"),
16407 ("frightened", "fear"),
16408 ("anxious", "fear"),
16409 ("worried", "fear"),
16410 ("nervous", "fear"),
16411 ("panicked", "fear"),
16412 ("surprised", "surprise"),
16414 ("amazed", "surprise"),
16415 ("astonished", "surprise"),
16416 ("shocked", "surprise"),
16417 ("stunned", "surprise"),
16418 ("startled", "surprise"),
16419 ("bewildered", "surprise"),
16420 ("disgusted", "disgust"),
16422 ("revolted", "disgust"),
16423 ("repulsed", "disgust"),
16424 ("sickened", "disgust"),
16425 ("nauseated", "disgust"),
16426 ("appalled", "disgust"),
16427 ("trust", "trust"),
16429 ("confident", "trust"),
16430 ("secure", "trust"),
16431 ("reliable", "trust"),
16432 ("faithful", "trust"),
16433 ("loyal", "trust"),
16434 ("eager", "anticipation"),
16436 ("hopeful", "anticipation"),
16437 ("expectant", "anticipation"),
16438 ("looking forward", "anticipation"),
16439 ("excited", "anticipation"),
16440 ];
16441
16442 let lower = s.to_lowercase();
16443 let mut counts: HashMap<String, f64> = HashMap::new();
16444
16445 for (word, emotion) in emotion_words {
16446 if lower.contains(word) {
16447 *counts.entry(emotion.to_string()).or_insert(0.0) += 1.0;
16448 }
16449 }
16450
16451 let total: f64 = counts.values().sum();
16453 if total > 0.0 {
16454 for v in counts.values_mut() {
16455 *v /= total;
16456 }
16457 }
16458
16459 counts
16460}
16461
16462fn compute_intensity(s: &str) -> f64 {
16464 let intensifiers = vec![
16465 ("very", 1.5),
16466 ("really", 1.5),
16467 ("extremely", 2.0),
16468 ("incredibly", 2.0),
16469 ("absolutely", 2.0),
16470 ("totally", 1.5),
16471 ("completely", 1.5),
16472 ("utterly", 2.0),
16473 ("so", 1.3),
16474 ("such", 1.3),
16475 ("quite", 1.2),
16476 ("rather", 1.1),
16477 ];
16478
16479 let exclamation_boost = 0.5;
16480 let caps_boost = 0.3;
16481
16482 let lower = s.to_lowercase();
16483 let mut score = 1.0;
16484
16485 for (word, boost) in intensifiers {
16486 if lower.contains(word) {
16487 score *= boost;
16488 }
16489 }
16490
16491 let exclamations = s.matches('!').count();
16493 score += exclamations as f64 * exclamation_boost;
16494
16495 let caps_words = s
16497 .split_whitespace()
16498 .filter(|w| w.len() > 2 && w.chars().all(|c| c.is_uppercase()))
16499 .count();
16500 score += caps_words as f64 * caps_boost;
16501
16502 score.min(5.0)
16503}
16504
16505fn compute_sarcasm_score(s: &str) -> (f64, f64, Vec<String>) {
16507 let mut markers = Vec::new();
16508 let mut score: f64 = 0.0;
16509
16510 let lower = s.to_lowercase();
16511
16512 let explicit = vec![
16514 "/s",
16515 "not!",
16516 "yeah right",
16517 "sure thing",
16518 "oh really",
16519 "oh great",
16520 "wow, just wow",
16521 "thanks a lot",
16522 "how wonderful",
16523 "isn't that special",
16524 "clearly",
16525 "obviously",
16526 "shocking",
16527 "no way",
16528 "what a surprise",
16529 ];
16530
16531 for marker in &explicit {
16532 if lower.contains(marker) {
16533 markers.push(format!("explicit: {}", marker));
16534 score += 0.4;
16535 }
16536 }
16537
16538 let hyperbolic = vec![
16540 "best thing ever",
16541 "worst thing ever",
16542 "literally dying",
16543 "absolutely perfect",
16544 "world's greatest",
16545 "totally awesome",
16546 "so much fun",
16547 "couldn't be happier",
16548 ];
16549
16550 for h in &hyperbolic {
16551 if lower.contains(h) {
16552 markers.push(format!("hyperbole: {}", h));
16553 score += 0.3;
16554 }
16555 }
16556
16557 let has_positive = ["great", "wonderful", "amazing", "love", "best", "awesome"]
16559 .iter()
16560 .any(|w| lower.contains(w));
16561 let has_negative_context = ["but", "however", "although", "except", "unfortunately"]
16562 .iter()
16563 .any(|w| lower.contains(w));
16564
16565 if has_positive && has_negative_context {
16566 markers.push("positive-negative contrast".to_string());
16567 score += 0.25;
16568 }
16569
16570 let quote_pattern = Regex::new(r#"["'](\w+)["']"#).unwrap();
16572 for cap in quote_pattern.captures_iter(s) {
16573 if let Some(m) = cap.get(1) {
16574 let word = m.as_str().to_lowercase();
16575 if [
16576 "great",
16577 "wonderful",
16578 "helpful",
16579 "useful",
16580 "smart",
16581 "genius",
16582 "brilliant",
16583 ]
16584 .contains(&word.as_str())
16585 {
16586 markers.push(format!("air quotes: \"{}\"", word));
16587 score += 0.35;
16588 }
16589 }
16590 }
16591
16592 if s.contains("...") || s.contains("!!!") || s.contains("???") {
16594 markers.push("excessive punctuation".to_string());
16595 score += 0.15;
16596 }
16597
16598 let confidence = if markers.is_empty() {
16600 0.0
16601 } else {
16602 (markers.len() as f64 * 0.25).min(1.0)
16603 };
16604
16605 (score.min(1.0), confidence, markers)
16606}
16607
16608fn compute_irony_score(s: &str) -> f64 {
16610 let mut score: f64 = 0.0;
16611 let lower = s.to_lowercase();
16612
16613 let irony_phrases = vec![
16615 "of course",
16616 "as expected",
16617 "naturally",
16618 "predictably",
16619 "who would have thought",
16620 "surprise surprise",
16621 "go figure",
16622 "typical",
16623 "as usual",
16624 "yet again",
16625 "once again",
16626 ];
16627
16628 for phrase in irony_phrases {
16629 if lower.contains(phrase) {
16630 score += 0.2;
16631 }
16632 }
16633
16634 if lower.contains("but") || lower.contains("yet") || lower.contains("however") {
16636 score += 0.1;
16637 }
16638
16639 if s.contains('?')
16641 && (lower.starts_with("isn't")
16642 || lower.starts_with("aren't")
16643 || lower.starts_with("doesn't")
16644 || lower.starts_with("don't")
16645 || lower.contains("right?")
16646 || lower.contains("isn't it"))
16647 {
16648 score += 0.25;
16649 }
16650
16651 score.min(1.0)
16652}
16653
16654fn create_hash_vector(s: &str, dims: usize) -> Vec<f64> {
16656 let mut vector = vec![0.0f64; dims];
16657
16658 for word in s.to_lowercase().split_whitespace() {
16659 let hash = compute_hash(word, 0);
16660 let idx = (hash as usize) % dims;
16661 vector[idx] += 1.0;
16662 }
16663
16664 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
16666 if magnitude > 0.0 {
16667 for v in vector.iter_mut() {
16668 *v /= magnitude;
16669 }
16670 }
16671
16672 vector
16673}
16674
16675fn count_text_stats(s: &str) -> (usize, usize, usize) {
16677 let words: Vec<&str> = s.split_whitespace().collect();
16678 let word_count = words.len();
16679 let sentence_count = s
16680 .matches(|c| c == '.' || c == '!' || c == '?')
16681 .count()
16682 .max(1);
16683
16684 let mut syllable_count = 0;
16685 for word in &words {
16686 syllable_count += count_syllables(word);
16687 }
16688
16689 (word_count, sentence_count, syllable_count)
16690}
16691
16692fn count_syllables(word: &str) -> usize {
16694 let word = word.to_lowercase();
16695 let vowels = ['a', 'e', 'i', 'o', 'u', 'y'];
16696 let mut count = 0;
16697 let mut prev_was_vowel = false;
16698
16699 for c in word.chars() {
16700 let is_vowel = vowels.contains(&c);
16701 if is_vowel && !prev_was_vowel {
16702 count += 1;
16703 }
16704 prev_was_vowel = is_vowel;
16705 }
16706
16707 if word.ends_with('e') && count > 1 {
16709 count -= 1;
16710 }
16711
16712 count.max(1)
16713}
16714
16715fn compute_soundex(s: &str) -> String {
16717 if s.is_empty() {
16718 return "0000".to_string();
16719 }
16720
16721 let s = s.to_uppercase();
16722 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
16723
16724 if chars.is_empty() {
16725 return "0000".to_string();
16726 }
16727
16728 let first = chars[0];
16729 let mut code = String::new();
16730 code.push(first);
16731
16732 let get_code = |c: char| -> char {
16733 match c {
16734 'B' | 'F' | 'P' | 'V' => '1',
16735 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => '2',
16736 'D' | 'T' => '3',
16737 'L' => '4',
16738 'M' | 'N' => '5',
16739 'R' => '6',
16740 _ => '0',
16741 }
16742 };
16743
16744 let mut prev_code = get_code(first);
16745
16746 for &c in chars.iter().skip(1) {
16747 let curr_code = get_code(c);
16748 if curr_code != '0' && curr_code != prev_code {
16749 code.push(curr_code);
16750 if code.len() == 4 {
16751 break;
16752 }
16753 }
16754 prev_code = curr_code;
16755 }
16756
16757 while code.len() < 4 {
16758 code.push('0');
16759 }
16760
16761 code
16762}
16763
16764fn compute_metaphone(s: &str) -> String {
16766 let s = s.to_uppercase();
16767 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
16768
16769 if chars.is_empty() {
16770 return String::new();
16771 }
16772
16773 let mut result = String::new();
16774 let mut i = 0;
16775
16776 if chars.len() >= 2 {
16778 let prefix: String = chars[0..2].iter().collect();
16779 if ["KN", "GN", "PN", "AE", "WR"].contains(&prefix.as_str()) {
16780 i = 1;
16781 }
16782 }
16783
16784 while i < chars.len() && result.len() < 6 {
16785 let c = chars[i];
16786 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
16787 let next = chars.get(i + 1).copied();
16788
16789 let code = match c {
16790 'A' | 'E' | 'I' | 'O' | 'U' => {
16791 if i == 0 {
16792 Some(c)
16793 } else {
16794 None
16795 }
16796 }
16797 'B' => {
16798 if prev != Some('M') || i == chars.len() - 1 {
16799 Some('B')
16800 } else {
16801 None
16802 }
16803 }
16804 'C' => {
16805 if next == Some('H') {
16806 Some('X')
16807 } else if matches!(next, Some('I') | Some('E') | Some('Y')) {
16808 Some('S')
16809 } else {
16810 Some('K')
16811 }
16812 }
16813 'D' => {
16814 if next == Some('G')
16815 && matches!(chars.get(i + 2), Some('E') | Some('I') | Some('Y'))
16816 {
16817 Some('J')
16818 } else {
16819 Some('T')
16820 }
16821 }
16822 'F' => Some('F'),
16823 'G' => {
16824 if next == Some('H')
16825 && !matches!(
16826 chars.get(i + 2),
16827 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
16828 )
16829 {
16830 None
16831 } else if matches!(next, Some('N') | Some('E') | Some('I') | Some('Y')) {
16832 Some('J')
16833 } else {
16834 Some('K')
16835 }
16836 }
16837 'H' => {
16838 if matches!(
16839 prev,
16840 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
16841 ) {
16842 None
16843 } else if matches!(
16844 next,
16845 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
16846 ) {
16847 Some('H')
16848 } else {
16849 None
16850 }
16851 }
16852 'J' => Some('J'),
16853 'K' => {
16854 if prev != Some('C') {
16855 Some('K')
16856 } else {
16857 None
16858 }
16859 }
16860 'L' => Some('L'),
16861 'M' => Some('M'),
16862 'N' => Some('N'),
16863 'P' => {
16864 if next == Some('H') {
16865 Some('F')
16866 } else {
16867 Some('P')
16868 }
16869 }
16870 'Q' => Some('K'),
16871 'R' => Some('R'),
16872 'S' => {
16873 if next == Some('H') {
16874 Some('X')
16875 } else {
16876 Some('S')
16877 }
16878 }
16879 'T' => {
16880 if next == Some('H') {
16881 Some('0') } else if next == Some('I') && matches!(chars.get(i + 2), Some('O') | Some('A')) {
16883 Some('X')
16884 } else {
16885 Some('T')
16886 }
16887 }
16888 'V' => Some('F'),
16889 'W' | 'Y' => {
16890 if matches!(
16891 next,
16892 Some('A') | Some('E') | Some('I') | Some('O') | Some('U')
16893 ) {
16894 Some(c)
16895 } else {
16896 None
16897 }
16898 }
16899 'X' => {
16900 result.push('K');
16901 Some('S')
16902 }
16903 'Z' => Some('S'),
16904 _ => None,
16905 };
16906
16907 if let Some(ch) = code {
16908 result.push(ch);
16909 }
16910
16911 if next == Some(c) {
16913 i += 1;
16914 }
16915 i += 1;
16916 }
16917
16918 result
16919}
16920
16921fn compute_cologne(s: &str) -> String {
16923 let s = s.to_uppercase();
16924 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
16925
16926 if chars.is_empty() {
16927 return String::new();
16928 }
16929
16930 let mut result = String::new();
16931
16932 for (i, &c) in chars.iter().enumerate() {
16933 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
16934 let next = chars.get(i + 1).copied();
16935
16936 let code = match c {
16937 'A' | 'E' | 'I' | 'O' | 'U' | 'J' | 'Y' => '0',
16938 'H' => continue,
16939 'B' | 'P' => '1',
16940 'D' | 'T' => {
16941 if matches!(next, Some('C') | Some('S') | Some('Z')) {
16942 '8'
16943 } else {
16944 '2'
16945 }
16946 }
16947 'F' | 'V' | 'W' => '3',
16948 'G' | 'K' | 'Q' => '4',
16949 'C' => {
16950 if i == 0 {
16951 if matches!(
16952 next,
16953 Some('A')
16954 | Some('H')
16955 | Some('K')
16956 | Some('L')
16957 | Some('O')
16958 | Some('Q')
16959 | Some('R')
16960 | Some('U')
16961 | Some('X')
16962 ) {
16963 '4'
16964 } else {
16965 '8'
16966 }
16967 } else if matches!(prev, Some('S') | Some('Z')) {
16968 '8'
16969 } else if matches!(
16970 next,
16971 Some('A')
16972 | Some('H')
16973 | Some('K')
16974 | Some('O')
16975 | Some('Q')
16976 | Some('U')
16977 | Some('X')
16978 ) {
16979 '4'
16980 } else {
16981 '8'
16982 }
16983 }
16984 'X' => {
16985 if matches!(prev, Some('C') | Some('K') | Some('Q')) {
16986 '8'
16987 } else {
16988 result.push('4');
16989 '8'
16990 }
16991 }
16992 'L' => '5',
16993 'M' | 'N' => '6',
16994 'R' => '7',
16995 'S' | 'Z' => '8',
16996 _ => continue,
16997 };
16998
16999 result.push(code);
17000 }
17001
17002 let mut deduped = String::new();
17004 let mut prev = None;
17005 for c in result.chars() {
17006 if prev != Some(c) {
17007 deduped.push(c);
17008 }
17009 prev = Some(c);
17010 }
17011
17012 let trimmed: String = deduped.trim_start_matches('0').to_string();
17014 if trimmed.is_empty() {
17015 "0".to_string()
17016 } else {
17017 trimmed
17018 }
17019}
17020
17021fn get_stopwords(lang: &str) -> Vec<&'static str> {
17023 match lang {
17024 "en" | "english" => vec![
17025 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with",
17026 "by", "from", "as", "is", "was", "are", "were", "been", "be", "have", "has", "had",
17027 "do", "does", "did", "will", "would", "could", "should", "may", "might", "must",
17028 "shall", "can", "need", "it", "its", "this", "that", "these", "those", "i", "you",
17029 "he", "she", "we", "they", "me", "him", "her", "us", "them", "my", "your", "his",
17030 "her", "our", "their", "what", "which", "who", "whom", "whose", "when", "where", "why",
17031 "how", "all", "each", "every", "both", "few", "more", "most", "other", "some", "such",
17032 "no", "nor", "not", "only", "own", "same", "so", "than", "too", "very", "just", "also",
17033 "now",
17034 ],
17035 "de" | "german" => vec![
17036 "der", "die", "das", "den", "dem", "des", "ein", "eine", "einer", "einem", "einen",
17037 "und", "oder", "aber", "in", "auf", "an", "zu", "für", "von", "mit", "bei", "als",
17038 "ist", "war", "sind", "waren", "sein", "haben", "hat", "hatte", "werden", "wird",
17039 "wurde", "kann", "können", "muss", "müssen", "soll", "sollen", "will", "wollen", "es",
17040 "sie", "er", "wir", "ihr", "ich", "du", "man", "sich", "nicht", "auch", "nur", "noch",
17041 "schon", "mehr", "sehr", "so",
17042 ],
17043 "fr" | "french" => vec![
17044 "le", "la", "les", "un", "une", "des", "et", "ou", "mais", "dans", "sur", "à", "de",
17045 "pour", "par", "avec", "ce", "cette", "ces", "est", "sont", "était", "être", "avoir",
17046 "a", "ont", "avait", "je", "tu", "il", "elle", "nous", "vous", "ils", "elles", "on",
17047 "ne", "pas", "plus", "moins", "très", "aussi", "que", "qui",
17048 ],
17049 "es" | "spanish" => vec![
17050 "el", "la", "los", "las", "un", "una", "unos", "unas", "y", "o", "pero", "en", "de",
17051 "a", "para", "por", "con", "es", "son", "era", "ser", "estar", "tiene", "tienen", "yo",
17052 "tú", "él", "ella", "nosotros", "ustedes", "ellos", "ellas", "no", "sí", "muy", "más",
17053 "menos", "también", "que", "quien", "cual", "como", "cuando",
17054 ],
17055 "it" | "italian" => vec![
17056 "il", "lo", "la", "i", "gli", "le", "un", "uno", "una", "e", "o", "ma", "in", "di",
17057 "a", "da", "per", "con", "su", "tra", "fra", "è", "sono", "era", "erano", "essere",
17058 "avere", "ha", "hanno", "io", "tu", "lui", "lei", "noi", "voi", "loro", "mi", "ti",
17059 "ci", "non", "più", "molto", "anche", "come", "che", "chi", "quale", "questo",
17060 "quello", "quando", "dove", "perché", "se", "però",
17061 ],
17062 "pt" | "portuguese" => vec![
17063 "o", "a", "os", "as", "um", "uma", "uns", "umas", "e", "ou", "mas", "em", "de", "para",
17064 "por", "com", "sem", "sob", "sobre", "é", "são", "era", "eram", "ser", "estar", "ter",
17065 "tem", "têm", "eu", "tu", "ele", "ela", "nós", "vós", "eles", "elas", "me", "te",
17066 "não", "mais", "muito", "também", "como", "que", "quem", "qual", "este", "esse",
17067 "aquele", "quando", "onde", "porque", "se", "já",
17068 ],
17069 "nl" | "dutch" => vec![
17070 "de", "het", "een", "en", "of", "maar", "in", "op", "aan", "van", "voor", "met", "bij",
17071 "naar", "om", "te", "tot", "uit", "over", "is", "zijn", "was", "waren", "worden",
17072 "wordt", "werd", "hebben", "ik", "je", "jij", "hij", "zij", "wij", "jullie", "ze",
17073 "mij", "jou", "niet", "geen", "meer", "ook", "als", "dat", "die", "wat", "wie", "dit",
17074 "deze", "wanneer", "waar", "waarom", "hoe", "dan", "nog",
17075 ],
17076 "ru" | "russian" => vec![
17077 "и",
17078 "в",
17079 "на",
17080 "с",
17081 "к",
17082 "по",
17083 "за",
17084 "из",
17085 "у",
17086 "о",
17087 "от",
17088 "до",
17089 "для",
17090 "при",
17091 "без",
17092 "под",
17093 "над",
17094 "между",
17095 "через",
17096 "после",
17097 "это",
17098 "то",
17099 "что",
17100 "как",
17101 "так",
17102 "но",
17103 "а",
17104 "или",
17105 "если",
17106 "же",
17107 "я",
17108 "ты",
17109 "он",
17110 "она",
17111 "мы",
17112 "вы",
17113 "они",
17114 "его",
17115 "её",
17116 "их",
17117 "не",
17118 "ни",
17119 "да",
17120 "нет",
17121 "был",
17122 "была",
17123 "были",
17124 "быть",
17125 "есть",
17126 "все",
17127 "всё",
17128 "весь",
17129 "этот",
17130 "тот",
17131 "который",
17132 "когда",
17133 "где",
17134 ],
17135 "ar" | "arabic" => vec![
17136 "في",
17137 "من",
17138 "إلى",
17139 "على",
17140 "عن",
17141 "مع",
17142 "هذا",
17143 "هذه",
17144 "ذلك",
17145 "تلك",
17146 "التي",
17147 "الذي",
17148 "اللذان",
17149 "اللتان",
17150 "الذين",
17151 "اللاتي",
17152 "اللواتي",
17153 "هو",
17154 "هي",
17155 "هم",
17156 "هن",
17157 "أنا",
17158 "أنت",
17159 "نحن",
17160 "أنتم",
17161 "أنتن",
17162 "كان",
17163 "كانت",
17164 "كانوا",
17165 "يكون",
17166 "تكون",
17167 "ليس",
17168 "ليست",
17169 "ليسوا",
17170 "و",
17171 "أو",
17172 "ثم",
17173 "لكن",
17174 "بل",
17175 "إن",
17176 "أن",
17177 "لأن",
17178 "كي",
17179 "حتى",
17180 "ما",
17181 "لا",
17182 "قد",
17183 "كل",
17184 "بعض",
17185 "غير",
17186 "أي",
17187 "كيف",
17188 "متى",
17189 "أين",
17190 ],
17191 "zh" | "chinese" => vec![
17192 "的", "了", "是", "在", "有", "和", "与", "或", "但", "而", "我", "你", "他", "她",
17193 "它", "我们", "你们", "他们", "她们", "这", "那", "这个", "那个", "这些", "那些",
17194 "什么", "哪", "哪个", "不", "没", "没有", "很", "也", "都", "就", "才", "只", "还",
17195 "把", "被", "给", "从", "到", "为", "以", "因为", "所以", "如果", "会", "能", "可以",
17196 "要", "想", "应该", "必须", "可能", "一", "个",
17197 ],
17198 "ja" | "japanese" => vec![
17199 "の",
17200 "に",
17201 "は",
17202 "を",
17203 "た",
17204 "が",
17205 "で",
17206 "て",
17207 "と",
17208 "し",
17209 "れ",
17210 "さ",
17211 "ある",
17212 "いる",
17213 "も",
17214 "する",
17215 "から",
17216 "な",
17217 "こと",
17218 "として",
17219 "い",
17220 "や",
17221 "など",
17222 "なっ",
17223 "ない",
17224 "この",
17225 "ため",
17226 "その",
17227 "あっ",
17228 "よう",
17229 "また",
17230 "もの",
17231 "という",
17232 "あり",
17233 "まで",
17234 "られ",
17235 "なる",
17236 "へ",
17237 "か",
17238 "だ",
17239 "これ",
17240 "によって",
17241 "により",
17242 "おり",
17243 "より",
17244 "による",
17245 "ず",
17246 "なり",
17247 "られる",
17248 "において",
17249 ],
17250 "ko" | "korean" => vec![
17251 "이",
17252 "그",
17253 "저",
17254 "것",
17255 "수",
17256 "등",
17257 "들",
17258 "및",
17259 "에",
17260 "의",
17261 "가",
17262 "을",
17263 "를",
17264 "은",
17265 "는",
17266 "로",
17267 "으로",
17268 "와",
17269 "과",
17270 "도",
17271 "에서",
17272 "까지",
17273 "부터",
17274 "만",
17275 "뿐",
17276 "처럼",
17277 "같이",
17278 "보다",
17279 "하다",
17280 "있다",
17281 "되다",
17282 "없다",
17283 "않다",
17284 "이다",
17285 "아니다",
17286 "나",
17287 "너",
17288 "우리",
17289 "그들",
17290 "이것",
17291 "그것",
17292 "저것",
17293 "무엇",
17294 "어디",
17295 "언제",
17296 "왜",
17297 "어떻게",
17298 "누구",
17299 "어느",
17300 "모든",
17301 "각",
17302 ],
17303 "hi" | "hindi" => vec![
17304 "का",
17305 "के",
17306 "की",
17307 "में",
17308 "है",
17309 "हैं",
17310 "को",
17311 "से",
17312 "पर",
17313 "था",
17314 "थे",
17315 "थी",
17316 "और",
17317 "या",
17318 "लेकिन",
17319 "अगर",
17320 "तो",
17321 "भी",
17322 "ही",
17323 "यह",
17324 "वह",
17325 "इस",
17326 "उस",
17327 "ये",
17328 "वे",
17329 "जो",
17330 "कि",
17331 "क्या",
17332 "कैसे",
17333 "मैं",
17334 "तुम",
17335 "आप",
17336 "हम",
17337 "वे",
17338 "उन्हें",
17339 "उनके",
17340 "अपने",
17341 "नहीं",
17342 "न",
17343 "कुछ",
17344 "कोई",
17345 "सब",
17346 "बहुत",
17347 "कम",
17348 "ज्यादा",
17349 "होना",
17350 "करना",
17351 "जाना",
17352 "आना",
17353 "देना",
17354 "लेना",
17355 "रहना",
17356 "सकना",
17357 ],
17358 "tr" | "turkish" => vec![
17359 "bir",
17360 "ve",
17361 "bu",
17362 "da",
17363 "de",
17364 "için",
17365 "ile",
17366 "mi",
17367 "ne",
17368 "o",
17369 "var",
17370 "ben",
17371 "sen",
17372 "biz",
17373 "siz",
17374 "onlar",
17375 "ki",
17376 "ama",
17377 "çok",
17378 "daha",
17379 "gibi",
17380 "kadar",
17381 "sonra",
17382 "şey",
17383 "kendi",
17384 "bütün",
17385 "her",
17386 "bazı",
17387 "olan",
17388 "olarak",
17389 "değil",
17390 "ya",
17391 "hem",
17392 "veya",
17393 "ancak",
17394 "ise",
17395 "göre",
17396 "rağmen",
17397 "dolayı",
17398 "üzere",
17399 "karşı",
17400 "arasında",
17401 "olan",
17402 "oldu",
17403 "olur",
17404 "olmak",
17405 "etmek",
17406 "yapmak",
17407 "demek",
17408 ],
17409 "pl" | "polish" => vec![
17410 "i",
17411 "w",
17412 "z",
17413 "na",
17414 "do",
17415 "o",
17416 "że",
17417 "to",
17418 "nie",
17419 "się",
17420 "jest",
17421 "tak",
17422 "jak",
17423 "ale",
17424 "po",
17425 "co",
17426 "czy",
17427 "lub",
17428 "oraz",
17429 "ja",
17430 "ty",
17431 "on",
17432 "ona",
17433 "my",
17434 "wy",
17435 "oni",
17436 "one",
17437 "pan",
17438 "pani",
17439 "ten",
17440 "ta",
17441 "te",
17442 "tego",
17443 "tej",
17444 "tym",
17445 "tych",
17446 "który",
17447 "która",
17448 "być",
17449 "mieć",
17450 "móc",
17451 "musieć",
17452 "chcieć",
17453 "wiedzieć",
17454 "mówić",
17455 "bardzo",
17456 "tylko",
17457 "już",
17458 "jeszcze",
17459 "też",
17460 "więc",
17461 "jednak",
17462 ],
17463 "sv" | "swedish" => vec![
17464 "och", "i", "att", "det", "som", "en", "på", "är", "av", "för", "med", "till", "den",
17465 "har", "de", "inte", "om", "ett", "men", "jag", "du", "han", "hon", "vi", "ni", "de",
17466 "dem", "sig", "sin", "var", "från", "eller", "när", "kan", "ska", "så", "än", "nu",
17467 "också", "bara", "mycket", "mer", "andra", "detta", "sedan", "hade", "varit", "skulle",
17468 "vara", "bli", "blev", "blir", "göra",
17469 ],
17470 _ => vec![
17471 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
17472 ],
17473 }
17474}
17475
17476fn compute_hash(s: &str, seed: u64) -> u64 {
17478 let mut hash: u64 = seed.wrapping_mul(0x517cc1b727220a95);
17479 for b in s.bytes() {
17480 hash = hash.wrapping_mul(31).wrapping_add(b as u64);
17481 }
17482 hash
17483}
17484
17485fn register_hologram(interp: &mut Interpreter) {
17505 use crate::interpreter::{
17506 RuntimeAffect, RuntimeConfidence, RuntimeEmotion, RuntimeFormality, RuntimeIntensity,
17507 RuntimeSentiment,
17508 };
17509
17510 define(interp, "emotional_hologram", Some(1), |_, args| {
17513 let affect = match &args[0] {
17514 Value::Affective { affect, .. } => affect.clone(),
17515 _ => RuntimeAffect {
17516 sentiment: None,
17517 sarcasm: false,
17518 intensity: None,
17519 formality: None,
17520 emotion: None,
17521 confidence: None,
17522 },
17523 };
17524
17525 let mut hologram = std::collections::HashMap::new();
17526
17527 let valence = match affect.sentiment {
17529 Some(RuntimeSentiment::Positive) => 1.0,
17530 Some(RuntimeSentiment::Negative) => -1.0,
17531 Some(RuntimeSentiment::Neutral) | None => 0.0,
17532 };
17533 hologram.insert("valence".to_string(), Value::Float(valence));
17534
17535 let arousal = match affect.intensity {
17537 Some(RuntimeIntensity::Down) => 0.25,
17538 None => 0.5,
17539 Some(RuntimeIntensity::Up) => 0.75,
17540 Some(RuntimeIntensity::Max) => 1.0,
17541 };
17542 hologram.insert("arousal".to_string(), Value::Float(arousal));
17543
17544 let dominance = match affect.formality {
17546 Some(RuntimeFormality::Informal) => 0.25,
17547 None => 0.5,
17548 Some(RuntimeFormality::Formal) => 0.85,
17549 };
17550 hologram.insert("dominance".to_string(), Value::Float(dominance));
17551
17552 let authenticity = if affect.sarcasm { -0.9 } else { 0.9 };
17554 hologram.insert("authenticity".to_string(), Value::Float(authenticity));
17555
17556 let certainty = match affect.confidence {
17558 Some(RuntimeConfidence::Low) => 0.2,
17559 None | Some(RuntimeConfidence::Medium) => 0.5,
17560 Some(RuntimeConfidence::High) => 0.9,
17561 };
17562 hologram.insert("certainty".to_string(), Value::Float(certainty));
17563
17564 let emotion_index = match affect.emotion {
17566 Some(RuntimeEmotion::Joy) => 0,
17567 Some(RuntimeEmotion::Sadness) => 1,
17568 Some(RuntimeEmotion::Anger) => 2,
17569 Some(RuntimeEmotion::Fear) => 3,
17570 Some(RuntimeEmotion::Surprise) => 4,
17571 Some(RuntimeEmotion::Love) => 5,
17572 None => -1,
17573 };
17574 hologram.insert("emotion_index".to_string(), Value::Int(emotion_index));
17575
17576 let emotion_name = match affect.emotion {
17578 Some(RuntimeEmotion::Joy) => "joy",
17579 Some(RuntimeEmotion::Sadness) => "sadness",
17580 Some(RuntimeEmotion::Anger) => "anger",
17581 Some(RuntimeEmotion::Fear) => "fear",
17582 Some(RuntimeEmotion::Surprise) => "surprise",
17583 Some(RuntimeEmotion::Love) => "love",
17584 None => "none",
17585 };
17586 hologram.insert(
17587 "emotion".to_string(),
17588 Value::String(Rc::new(emotion_name.to_string())),
17589 );
17590
17591 Ok(Value::Map(Rc::new(RefCell::new(hologram))))
17592 });
17593
17594 define(interp, "emotional_distance", Some(2), |interp, args| {
17596 let h1 = get_hologram_values(&args[0], interp)?;
17598 let h2 = get_hologram_values(&args[1], interp)?;
17599
17600 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();
17607
17608 Ok(Value::Float(dist))
17609 });
17610
17611 define(interp, "emotional_similarity", Some(2), |interp, args| {
17613 let h1 = get_hologram_values(&args[0], interp)?;
17614 let h2 = get_hologram_values(&args[1], interp)?;
17615
17616 let dot = h1.0 * h2.0 + h1.1 * h2.1 + h1.2 * h2.2 + h1.3 * h2.3 + h1.4 * h2.4;
17617 let mag1 =
17618 (h1.0.powi(2) + h1.1.powi(2) + h1.2.powi(2) + h1.3.powi(2) + h1.4.powi(2)).sqrt();
17619 let mag2 =
17620 (h2.0.powi(2) + h2.1.powi(2) + h2.2.powi(2) + h2.3.powi(2) + h2.4.powi(2)).sqrt();
17621
17622 let similarity = if mag1 > 0.0 && mag2 > 0.0 {
17623 (dot / (mag1 * mag2) + 1.0) / 2.0 } else {
17625 0.5
17626 };
17627
17628 Ok(Value::Float(similarity))
17629 });
17630
17631 define(interp, "emotional_dissonance", Some(1), |_, args| {
17634 let affect = match &args[0] {
17635 Value::Affective { affect, .. } => affect.clone(),
17636 _ => return Ok(Value::Float(0.0)),
17637 };
17638
17639 let mut dissonance: f64 = 0.0;
17640
17641 if matches!(affect.sentiment, Some(RuntimeSentiment::Positive)) && affect.sarcasm {
17643 dissonance += 0.4;
17644 }
17645
17646 if matches!(affect.sentiment, Some(RuntimeSentiment::Negative)) && affect.sarcasm {
17648 dissonance += 0.1;
17649 }
17650
17651 if matches!(affect.confidence, Some(RuntimeConfidence::High))
17653 && matches!(affect.intensity, Some(RuntimeIntensity::Down))
17654 {
17655 dissonance += 0.2;
17656 }
17657
17658 if matches!(affect.formality, Some(RuntimeFormality::Formal)) {
17660 if matches!(
17661 affect.emotion,
17662 Some(RuntimeEmotion::Anger) | Some(RuntimeEmotion::Fear)
17663 ) {
17664 dissonance += 0.3;
17665 }
17666 }
17667
17668 if matches!(
17670 affect.emotion,
17671 Some(RuntimeEmotion::Joy) | Some(RuntimeEmotion::Love)
17672 ) && affect.sarcasm
17673 {
17674 dissonance += 0.3;
17675 }
17676
17677 Ok(Value::Float(dissonance.min(1.0)))
17678 });
17679
17680 define(interp, "emotional_fingerprint", Some(1), |interp, args| {
17683 let h = get_hologram_values(&args[0], interp)?;
17684
17685 let repr = format!(
17687 "hologram:v{:.4}:a{:.4}:d{:.4}:auth{:.4}:c{:.4}",
17688 h.0, h.1, h.2, h.3, h.4
17689 );
17690
17691 let hash = blake3::hash(repr.as_bytes());
17693 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
17694 });
17695
17696 define(interp, "emotional_morph", Some(3), |interp, args| {
17699 let h1 = get_hologram_values(&args[0], interp)?;
17700 let h2 = get_hologram_values(&args[1], interp)?;
17701 let t = match &args[2] {
17702 Value::Float(f) => f.max(0.0).min(1.0),
17703 Value::Int(i) => (*i as f64).max(0.0).min(1.0),
17704 _ => {
17705 return Err(RuntimeError::new(
17706 "emotional_morph() requires numeric t value",
17707 ))
17708 }
17709 };
17710
17711 let mut result = std::collections::HashMap::new();
17712 result.insert(
17713 "valence".to_string(),
17714 Value::Float(h1.0 + (h2.0 - h1.0) * t),
17715 );
17716 result.insert(
17717 "arousal".to_string(),
17718 Value::Float(h1.1 + (h2.1 - h1.1) * t),
17719 );
17720 result.insert(
17721 "dominance".to_string(),
17722 Value::Float(h1.2 + (h2.2 - h1.2) * t),
17723 );
17724 result.insert(
17725 "authenticity".to_string(),
17726 Value::Float(h1.3 + (h2.3 - h1.3) * t),
17727 );
17728 result.insert(
17729 "certainty".to_string(),
17730 Value::Float(h1.4 + (h2.4 - h1.4) * t),
17731 );
17732
17733 Ok(Value::Map(Rc::new(RefCell::new(result))))
17734 });
17735
17736 define(interp, "cultural_emotion", Some(2), |_, args| {
17742 let emotion = match &args[0] {
17743 Value::Affective { affect, .. } => affect.emotion.clone(),
17744 Value::String(s) => match s.as_str() {
17745 "joy" => Some(RuntimeEmotion::Joy),
17746 "sadness" => Some(RuntimeEmotion::Sadness),
17747 "anger" => Some(RuntimeEmotion::Anger),
17748 "fear" => Some(RuntimeEmotion::Fear),
17749 "surprise" => Some(RuntimeEmotion::Surprise),
17750 "love" => Some(RuntimeEmotion::Love),
17751 _ => None,
17752 },
17753 _ => None,
17754 };
17755
17756 let culture = match &args[1] {
17757 Value::String(s) => s.to_lowercase(),
17758 _ => {
17759 return Err(RuntimeError::new(
17760 "cultural_emotion() requires string culture",
17761 ))
17762 }
17763 };
17764
17765 let result = match (emotion, culture.as_str()) {
17766 (Some(RuntimeEmotion::Joy), "japanese" | "ja") => create_cultural_entry(
17768 "木漏れ日",
17769 "komorebi",
17770 "sunlight filtering through leaves - peaceful joy",
17771 ),
17772 (Some(RuntimeEmotion::Sadness), "japanese" | "ja") => create_cultural_entry(
17773 "物の哀れ",
17774 "mono no aware",
17775 "the pathos of things - bittersweet awareness of impermanence",
17776 ),
17777 (Some(RuntimeEmotion::Love), "japanese" | "ja") => create_cultural_entry(
17778 "甘え",
17779 "amae",
17780 "indulgent dependence on another's benevolence",
17781 ),
17782 (Some(RuntimeEmotion::Fear), "japanese" | "ja") => create_cultural_entry(
17783 "空気を読む",
17784 "kuuki wo yomu",
17785 "anxiety about reading the room",
17786 ),
17787
17788 (Some(RuntimeEmotion::Sadness), "portuguese" | "pt") => create_cultural_entry(
17790 "saudade",
17791 "saudade",
17792 "melancholic longing for something or someone absent",
17793 ),
17794 (Some(RuntimeEmotion::Joy), "portuguese" | "pt") => {
17795 create_cultural_entry("alegria", "alegria", "exuberant collective joy")
17796 }
17797
17798 (Some(RuntimeEmotion::Joy), "german" | "de") => create_cultural_entry(
17800 "Schadenfreude",
17801 "schadenfreude",
17802 "pleasure derived from another's misfortune",
17803 ),
17804 (Some(RuntimeEmotion::Sadness), "german" | "de") => create_cultural_entry(
17805 "Weltschmerz",
17806 "weltschmerz",
17807 "world-weariness, melancholy about the world's state",
17808 ),
17809 (Some(RuntimeEmotion::Fear), "german" | "de") => create_cultural_entry(
17810 "Torschlusspanik",
17811 "torschlusspanik",
17812 "fear of diminishing opportunities with age",
17813 ),
17814
17815 (Some(RuntimeEmotion::Joy), "danish" | "da") => {
17817 create_cultural_entry("hygge", "hygge", "cozy contentment and conviviality")
17818 }
17819
17820 (Some(RuntimeEmotion::Joy), "arabic" | "ar") => {
17822 create_cultural_entry("طرب", "tarab", "musical ecstasy, enchantment through art")
17823 }
17824 (Some(RuntimeEmotion::Love), "arabic" | "ar") => {
17825 create_cultural_entry("هوى", "hawa", "passionate, sometimes irrational love")
17826 }
17827
17828 (Some(RuntimeEmotion::Sadness), "korean" | "ko") => create_cultural_entry(
17830 "한",
17831 "han",
17832 "collective grief and resentment from historical suffering",
17833 ),
17834 (Some(RuntimeEmotion::Joy), "korean" | "ko") => create_cultural_entry(
17835 "정",
17836 "jeong",
17837 "deep affection and attachment formed over time",
17838 ),
17839
17840 (Some(RuntimeEmotion::Sadness), "russian" | "ru") => {
17842 create_cultural_entry("тоска", "toska", "spiritual anguish without specific cause")
17843 }
17844
17845 (Some(RuntimeEmotion::Love), "hindi" | "hi") => {
17847 create_cultural_entry("विरह", "viraha", "longing for an absent beloved")
17848 }
17849
17850 (Some(RuntimeEmotion::Anger), "finnish" | "fi") => {
17852 create_cultural_entry("sisu", "sisu", "stoic determination and grit in adversity")
17853 }
17854
17855 (Some(e), _) => {
17857 let name = match e {
17858 RuntimeEmotion::Joy => "joy",
17859 RuntimeEmotion::Sadness => "sadness",
17860 RuntimeEmotion::Anger => "anger",
17861 RuntimeEmotion::Fear => "fear",
17862 RuntimeEmotion::Surprise => "surprise",
17863 RuntimeEmotion::Love => "love",
17864 };
17865 create_cultural_entry(name, name, "universal emotion")
17866 }
17867 (None, _) => create_cultural_entry("none", "none", "no emotion"),
17868 };
17869
17870 Ok(result)
17871 });
17872
17873 define(interp, "list_cultural_emotions", Some(1), |_, args| {
17875 let culture = match &args[0] {
17876 Value::String(s) => s.to_lowercase(),
17877 _ => {
17878 return Err(RuntimeError::new(
17879 "list_cultural_emotions() requires string culture",
17880 ))
17881 }
17882 };
17883
17884 let emotions: Vec<(&str, &str, &str)> = match culture.as_str() {
17885 "japanese" | "ja" => vec![
17886 ("木漏れ日", "komorebi", "sunlight through leaves"),
17887 ("物の哀れ", "mono no aware", "pathos of things"),
17888 ("甘え", "amae", "indulgent dependence"),
17889 ("侘寂", "wabi-sabi", "beauty in imperfection"),
17890 ("生きがい", "ikigai", "reason for being"),
17891 ],
17892 "german" | "de" => vec![
17893 ("Schadenfreude", "schadenfreude", "joy at misfortune"),
17894 ("Weltschmerz", "weltschmerz", "world-weariness"),
17895 ("Torschlusspanik", "torschlusspanik", "gate-closing panic"),
17896 ("Sehnsucht", "sehnsucht", "deep longing"),
17897 ("Wanderlust", "wanderlust", "desire to travel"),
17898 ],
17899 "portuguese" | "pt" => vec![
17900 ("saudade", "saudade", "melancholic longing"),
17901 ("alegria", "alegria", "exuberant joy"),
17902 ("desabafar", "desabafar", "emotional unburdening"),
17903 ],
17904 "danish" | "da" => vec![("hygge", "hygge", "cozy contentment")],
17905 "korean" | "ko" => vec![
17906 ("한", "han", "collective grief"),
17907 ("정", "jeong", "deep affection"),
17908 ("눈치", "nunchi", "situational awareness"),
17909 ],
17910 "arabic" | "ar" => vec![
17911 ("طرب", "tarab", "musical ecstasy"),
17912 ("هوى", "hawa", "passionate love"),
17913 ("صبر", "sabr", "patient perseverance"),
17914 ],
17915 "russian" | "ru" => vec![
17916 ("тоска", "toska", "spiritual anguish"),
17917 ("пошлость", "poshlost", "spiritual vulgarity"),
17918 ],
17919 "finnish" | "fi" => vec![("sisu", "sisu", "stoic determination")],
17920 "hindi" | "hi" => vec![
17921 ("विरह", "viraha", "longing for beloved"),
17922 ("जुगाड़", "jugaad", "creative improvisation"),
17923 ],
17924 _ => vec![
17925 ("joy", "joy", "universal happiness"),
17926 ("sadness", "sadness", "universal sorrow"),
17927 ("anger", "anger", "universal frustration"),
17928 ("fear", "fear", "universal anxiety"),
17929 ("surprise", "surprise", "universal amazement"),
17930 ("love", "love", "universal affection"),
17931 ],
17932 };
17933
17934 let result: Vec<Value> = emotions
17935 .iter()
17936 .map(|(native, romanized, meaning)| create_cultural_entry(native, romanized, meaning))
17937 .collect();
17938
17939 Ok(Value::Array(Rc::new(RefCell::new(result))))
17940 });
17941
17942 define(interp, "hologram_info", Some(0), |_, _| {
17944 let mut info = std::collections::HashMap::new();
17945
17946 info.insert(
17947 "dimensions".to_string(),
17948 Value::Array(Rc::new(RefCell::new(vec![
17949 Value::String(Rc::new("valence".to_string())),
17950 Value::String(Rc::new("arousal".to_string())),
17951 Value::String(Rc::new("dominance".to_string())),
17952 Value::String(Rc::new("authenticity".to_string())),
17953 Value::String(Rc::new("certainty".to_string())),
17954 Value::String(Rc::new("emotion_index".to_string())),
17955 ]))),
17956 );
17957
17958 info.insert(
17959 "supported_cultures".to_string(),
17960 Value::Array(Rc::new(RefCell::new(vec![
17961 Value::String(Rc::new("japanese".to_string())),
17962 Value::String(Rc::new("german".to_string())),
17963 Value::String(Rc::new("portuguese".to_string())),
17964 Value::String(Rc::new("danish".to_string())),
17965 Value::String(Rc::new("korean".to_string())),
17966 Value::String(Rc::new("arabic".to_string())),
17967 Value::String(Rc::new("russian".to_string())),
17968 Value::String(Rc::new("finnish".to_string())),
17969 Value::String(Rc::new("hindi".to_string())),
17970 ]))),
17971 );
17972
17973 let funcs = vec![
17974 "emotional_hologram",
17975 "emotional_distance",
17976 "emotional_similarity",
17977 "emotional_dissonance",
17978 "emotional_fingerprint",
17979 "emotional_morph",
17980 "cultural_emotion",
17981 "list_cultural_emotions",
17982 "hologram_info",
17983 ];
17984 let func_values: Vec<Value> = funcs
17985 .iter()
17986 .map(|s| Value::String(Rc::new(s.to_string())))
17987 .collect();
17988 info.insert(
17989 "functions".to_string(),
17990 Value::Array(Rc::new(RefCell::new(func_values))),
17991 );
17992
17993 Ok(Value::Map(Rc::new(RefCell::new(info))))
17994 });
17995}
17996
17997fn get_hologram_values(
17999 val: &Value,
18000 _interp: &mut Interpreter,
18001) -> Result<(f64, f64, f64, f64, f64), RuntimeError> {
18002 use crate::interpreter::{
18003 RuntimeAffect, RuntimeConfidence, RuntimeFormality, RuntimeIntensity, RuntimeSentiment,
18004 };
18005
18006 let affect = match val {
18007 Value::Affective { affect, .. } => affect.clone(),
18008 Value::Map(m) => {
18009 let map = m.borrow();
18011 let v = extract_float(&map, "valence").unwrap_or(0.0);
18012 let a = extract_float(&map, "arousal").unwrap_or(0.5);
18013 let d = extract_float(&map, "dominance").unwrap_or(0.5);
18014 let auth = extract_float(&map, "authenticity").unwrap_or(0.9);
18015 let c = extract_float(&map, "certainty").unwrap_or(0.5);
18016 return Ok((v, a, d, auth, c));
18017 }
18018 _ => RuntimeAffect {
18019 sentiment: None,
18020 sarcasm: false,
18021 intensity: None,
18022 formality: None,
18023 emotion: None,
18024 confidence: None,
18025 },
18026 };
18027
18028 let v = match affect.sentiment {
18029 Some(RuntimeSentiment::Positive) => 1.0,
18030 Some(RuntimeSentiment::Negative) => -1.0,
18031 _ => 0.0,
18032 };
18033 let a = match affect.intensity {
18034 Some(RuntimeIntensity::Down) => 0.25,
18035 Some(RuntimeIntensity::Up) => 0.75,
18036 Some(RuntimeIntensity::Max) => 1.0,
18037 None => 0.5,
18038 };
18039 let d = match affect.formality {
18040 Some(RuntimeFormality::Informal) => 0.25,
18041 Some(RuntimeFormality::Formal) => 0.85,
18042 None => 0.5,
18043 };
18044 let auth = if affect.sarcasm { -0.9 } else { 0.9 };
18045 let c = match affect.confidence {
18046 Some(RuntimeConfidence::Low) => 0.2,
18047 Some(RuntimeConfidence::High) => 0.9,
18048 _ => 0.5,
18049 };
18050
18051 Ok((v, a, d, auth, c))
18052}
18053
18054fn extract_float(map: &std::collections::HashMap<String, Value>, key: &str) -> Option<f64> {
18055 match map.get(key) {
18056 Some(Value::Float(f)) => Some(*f),
18057 Some(Value::Int(i)) => Some(*i as f64),
18058 _ => None,
18059 }
18060}
18061
18062fn create_cultural_entry(native: &str, romanized: &str, meaning: &str) -> Value {
18063 let mut entry = std::collections::HashMap::new();
18064 entry.insert(
18065 "native".to_string(),
18066 Value::String(Rc::new(native.to_string())),
18067 );
18068 entry.insert(
18069 "romanized".to_string(),
18070 Value::String(Rc::new(romanized.to_string())),
18071 );
18072 entry.insert(
18073 "meaning".to_string(),
18074 Value::String(Rc::new(meaning.to_string())),
18075 );
18076 Value::Map(Rc::new(RefCell::new(entry)))
18077}
18078
18079fn register_experimental_crypto(interp: &mut Interpreter) {
18084 define(interp, "commit", Some(1), |_, args| {
18090 let value_str = match &args[0] {
18091 Value::String(s) => s.to_string(),
18092 other => format!("{:?}", other),
18093 };
18094
18095 let mut nonce = [0u8; 32];
18097 getrandom::getrandom(&mut nonce)
18098 .map_err(|e| RuntimeError::new(format!("commit() random failed: {}", e)))?;
18099 let nonce_hex = hex::encode(&nonce);
18100
18101 let commitment_input = format!("{}:{}", value_str, nonce_hex);
18103 let commitment = blake3::hash(commitment_input.as_bytes());
18104
18105 let mut result = std::collections::HashMap::new();
18106 result.insert(
18107 "commitment".to_string(),
18108 Value::String(Rc::new(commitment.to_hex().to_string())),
18109 );
18110 result.insert("nonce".to_string(), Value::String(Rc::new(nonce_hex)));
18111 result.insert("value".to_string(), args[0].clone());
18112
18113 Ok(Value::Map(Rc::new(RefCell::new(result))))
18114 });
18115
18116 define(interp, "verify_commitment", Some(3), |_, args| {
18118 let commitment = match &args[0] {
18119 Value::String(s) => s.to_string(),
18120 _ => {
18121 return Err(RuntimeError::new(
18122 "verify_commitment() requires string commitment",
18123 ))
18124 }
18125 };
18126 let value_str = match &args[1] {
18127 Value::String(s) => s.to_string(),
18128 other => format!("{:?}", other),
18129 };
18130 let nonce = match &args[2] {
18131 Value::String(s) => s.to_string(),
18132 _ => {
18133 return Err(RuntimeError::new(
18134 "verify_commitment() requires string nonce",
18135 ))
18136 }
18137 };
18138
18139 let commitment_input = format!("{}:{}", value_str, nonce);
18141 let computed = blake3::hash(commitment_input.as_bytes());
18142
18143 Ok(Value::Bool(computed.to_hex().to_string() == commitment))
18144 });
18145
18146 define(interp, "secret_split", Some(3), |_, args| {
18152 let secret = match &args[0] {
18153 Value::String(s) => s.as_bytes().to_vec(),
18154 Value::Array(arr) => {
18155 let borrowed = arr.borrow();
18156 borrowed
18157 .iter()
18158 .filter_map(|v| {
18159 if let Value::Int(i) = v {
18160 Some(*i as u8)
18161 } else {
18162 None
18163 }
18164 })
18165 .collect()
18166 }
18167 _ => {
18168 return Err(RuntimeError::new(
18169 "secret_split() requires string or byte array",
18170 ))
18171 }
18172 };
18173
18174 let threshold = match &args[1] {
18175 Value::Int(n) => *n as usize,
18176 _ => {
18177 return Err(RuntimeError::new(
18178 "secret_split() requires integer threshold",
18179 ))
18180 }
18181 };
18182
18183 let num_shares = match &args[2] {
18184 Value::Int(n) => *n as usize,
18185 _ => {
18186 return Err(RuntimeError::new(
18187 "secret_split() requires integer num_shares",
18188 ))
18189 }
18190 };
18191
18192 if threshold < 2 {
18193 return Err(RuntimeError::new("secret_split() threshold must be >= 2"));
18194 }
18195 if num_shares < threshold {
18196 return Err(RuntimeError::new(
18197 "secret_split() num_shares must be >= threshold",
18198 ));
18199 }
18200 if num_shares > 255 {
18201 return Err(RuntimeError::new("secret_split() max 255 shares"));
18202 }
18203
18204 let mut rng = rand::thread_rng();
18207 let mut shares: Vec<Vec<u8>> = (0..num_shares)
18208 .map(|_| Vec::with_capacity(secret.len() + 1))
18209 .collect();
18210
18211 for (i, share) in shares.iter_mut().enumerate() {
18213 share.push((i + 1) as u8);
18214 }
18215
18216 for &byte in &secret {
18218 let mut coefficients: Vec<u8> = vec![byte];
18221 for _ in 1..threshold {
18222 coefficients.push(rng.gen());
18223 }
18224
18225 for (i, share) in shares.iter_mut().enumerate() {
18227 let x = (i + 1) as u8;
18228 let y = eval_polynomial_gf256(&coefficients, x);
18229 share.push(y);
18230 }
18231 }
18232
18233 let share_values: Vec<Value> = shares
18235 .iter()
18236 .map(|share| {
18237 let hex = hex::encode(share);
18238 Value::String(Rc::new(hex))
18239 })
18240 .collect();
18241
18242 let mut result = std::collections::HashMap::new();
18243 result.insert(
18244 "shares".to_string(),
18245 Value::Array(Rc::new(RefCell::new(share_values))),
18246 );
18247 result.insert("threshold".to_string(), Value::Int(threshold as i64));
18248 result.insert("total".to_string(), Value::Int(num_shares as i64));
18249
18250 Ok(Value::Map(Rc::new(RefCell::new(result))))
18251 });
18252
18253 define(interp, "secret_recover", Some(1), |_, args| {
18255 let shares: Vec<Vec<u8>> = match &args[0] {
18256 Value::Array(arr) => {
18257 let borrowed = arr.borrow();
18258 borrowed
18259 .iter()
18260 .filter_map(|v| {
18261 if let Value::String(s) = v {
18262 hex::decode(s.as_str()).ok()
18263 } else {
18264 None
18265 }
18266 })
18267 .collect()
18268 }
18269 _ => {
18270 return Err(RuntimeError::new(
18271 "secret_recover() requires array of share strings",
18272 ))
18273 }
18274 };
18275
18276 if shares.is_empty() {
18277 return Err(RuntimeError::new(
18278 "secret_recover() requires at least one share",
18279 ));
18280 }
18281
18282 let share_len = shares[0].len();
18283 if share_len < 2 {
18284 return Err(RuntimeError::new("secret_recover() invalid share format"));
18285 }
18286
18287 let mut secret = Vec::with_capacity(share_len - 1);
18289
18290 for byte_idx in 1..share_len {
18291 let points: Vec<(u8, u8)> = shares
18293 .iter()
18294 .map(|share| (share[0], share[byte_idx]))
18295 .collect();
18296
18297 let recovered_byte = lagrange_interpolate_gf256(&points, 0);
18299 secret.push(recovered_byte);
18300 }
18301
18302 match String::from_utf8(secret.clone()) {
18304 Ok(s) => Ok(Value::String(Rc::new(s))),
18305 Err(_) => {
18306 let byte_values: Vec<Value> =
18308 secret.iter().map(|&b| Value::Int(b as i64)).collect();
18309 Ok(Value::Array(Rc::new(RefCell::new(byte_values))))
18310 }
18311 }
18312 });
18313
18314 define(interp, "council_split", Some(2), |_, args| {
18320 let secret = match &args[0] {
18321 Value::String(s) => s.as_bytes().to_vec(),
18322 _ => return Err(RuntimeError::new("council_split() requires string secret")),
18323 };
18324
18325 let num_elders = match &args[1] {
18326 Value::Int(n) => *n as usize,
18327 _ => {
18328 return Err(RuntimeError::new(
18329 "council_split() requires integer num_elders",
18330 ))
18331 }
18332 };
18333
18334 if num_elders < 3 {
18335 return Err(RuntimeError::new(
18336 "council_split() requires at least 3 elders",
18337 ));
18338 }
18339
18340 let threshold = (num_elders / 2) + 1;
18342
18343 let mut rng = rand::thread_rng();
18345 let mut shares: Vec<Vec<u8>> = (0..num_elders)
18346 .map(|_| Vec::with_capacity(secret.len() + 1))
18347 .collect();
18348
18349 for (i, share) in shares.iter_mut().enumerate() {
18350 share.push((i + 1) as u8);
18351 }
18352
18353 for &byte in &secret {
18354 let mut coefficients: Vec<u8> = vec![byte];
18355 for _ in 1..threshold {
18356 coefficients.push(rng.gen());
18357 }
18358
18359 for (i, share) in shares.iter_mut().enumerate() {
18360 let x = (i + 1) as u8;
18361 let y = eval_polynomial_gf256(&coefficients, x);
18362 share.push(y);
18363 }
18364 }
18365
18366 let share_values: Vec<Value> = shares
18367 .iter()
18368 .map(|share| Value::String(Rc::new(hex::encode(share))))
18369 .collect();
18370
18371 let mut result = std::collections::HashMap::new();
18372 result.insert(
18373 "shares".to_string(),
18374 Value::Array(Rc::new(RefCell::new(share_values))),
18375 );
18376 result.insert("threshold".to_string(), Value::Int(threshold as i64));
18377 result.insert("total".to_string(), Value::Int(num_elders as i64));
18378 result.insert(
18379 "model".to_string(),
18380 Value::String(Rc::new("ubuntu".to_string())),
18381 );
18382 result.insert(
18383 "philosophy".to_string(),
18384 Value::String(Rc::new(
18385 "I am because we are - majority consensus required".to_string(),
18386 )),
18387 );
18388
18389 Ok(Value::Map(Rc::new(RefCell::new(result))))
18390 });
18391
18392 define(interp, "witness_chain", Some(2), |_, args| {
18395 let statement = match &args[0] {
18396 Value::String(s) => s.to_string(),
18397 _ => {
18398 return Err(RuntimeError::new(
18399 "witness_chain() requires string statement",
18400 ))
18401 }
18402 };
18403
18404 let witnesses: Vec<String> = match &args[1] {
18405 Value::Array(arr) => {
18406 let borrowed = arr.borrow();
18407 borrowed
18408 .iter()
18409 .filter_map(|v| {
18410 if let Value::String(s) = v {
18411 Some(s.to_string())
18412 } else {
18413 None
18414 }
18415 })
18416 .collect()
18417 }
18418 _ => {
18419 return Err(RuntimeError::new(
18420 "witness_chain() requires array of witness names",
18421 ))
18422 }
18423 };
18424
18425 if witnesses.is_empty() {
18426 return Err(RuntimeError::new(
18427 "witness_chain() requires at least one witness",
18428 ));
18429 }
18430
18431 let mut chain = Vec::new();
18433 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
18434
18435 for (i, witness) in witnesses.iter().enumerate() {
18436 let attestation = format!("{}:attests:{}", witness, prev_hash);
18437 let hash = blake3::hash(attestation.as_bytes()).to_hex().to_string();
18438
18439 let mut link = std::collections::HashMap::new();
18440 link.insert(
18441 "witness".to_string(),
18442 Value::String(Rc::new(witness.clone())),
18443 );
18444 link.insert("position".to_string(), Value::Int((i + 1) as i64));
18445 link.insert(
18446 "attests_to".to_string(),
18447 Value::String(Rc::new(prev_hash.clone())),
18448 );
18449 link.insert(
18450 "signature".to_string(),
18451 Value::String(Rc::new(hash.clone())),
18452 );
18453
18454 chain.push(Value::Map(Rc::new(RefCell::new(link))));
18455 prev_hash = hash;
18456 }
18457
18458 let mut result = std::collections::HashMap::new();
18459 result.insert("statement".to_string(), Value::String(Rc::new(statement)));
18460 result.insert(
18461 "chain".to_string(),
18462 Value::Array(Rc::new(RefCell::new(chain))),
18463 );
18464 result.insert("final_seal".to_string(), Value::String(Rc::new(prev_hash)));
18465 result.insert(
18466 "model".to_string(),
18467 Value::String(Rc::new("isnad".to_string())),
18468 );
18469 result.insert(
18470 "philosophy".to_string(),
18471 Value::String(Rc::new(
18472 "Chain of reliable transmitters - each witness validates the previous".to_string(),
18473 )),
18474 );
18475
18476 Ok(Value::Map(Rc::new(RefCell::new(result))))
18477 });
18478
18479 define(interp, "verify_witness_chain", Some(1), |_, args| {
18481 let chain_map = match &args[0] {
18482 Value::Map(m) => m.borrow(),
18483 _ => {
18484 return Err(RuntimeError::new(
18485 "verify_witness_chain() requires chain map",
18486 ))
18487 }
18488 };
18489
18490 let statement = match chain_map.get("statement") {
18491 Some(Value::String(s)) => s.to_string(),
18492 _ => {
18493 return Err(RuntimeError::new(
18494 "verify_witness_chain() invalid chain format",
18495 ))
18496 }
18497 };
18498
18499 let chain = match chain_map.get("chain") {
18500 Some(Value::Array(arr)) => arr.borrow().clone(),
18501 _ => {
18502 return Err(RuntimeError::new(
18503 "verify_witness_chain() invalid chain format",
18504 ))
18505 }
18506 };
18507
18508 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
18509
18510 for link_val in chain.iter() {
18511 if let Value::Map(link_map) = link_val {
18512 let link = link_map.borrow();
18513 let witness = match link.get("witness") {
18514 Some(Value::String(s)) => s.to_string(),
18515 _ => return Ok(Value::Bool(false)),
18516 };
18517 let attests_to = match link.get("attests_to") {
18518 Some(Value::String(s)) => s.to_string(),
18519 _ => return Ok(Value::Bool(false)),
18520 };
18521 let signature = match link.get("signature") {
18522 Some(Value::String(s)) => s.to_string(),
18523 _ => return Ok(Value::Bool(false)),
18524 };
18525
18526 if attests_to != prev_hash {
18528 return Ok(Value::Bool(false));
18529 }
18530
18531 let expected = format!("{}:attests:{}", witness, prev_hash);
18532 let computed = blake3::hash(expected.as_bytes()).to_hex().to_string();
18533
18534 if computed != signature {
18535 return Ok(Value::Bool(false));
18536 }
18537
18538 prev_hash = signature;
18539 } else {
18540 return Ok(Value::Bool(false));
18541 }
18542 }
18543
18544 Ok(Value::Bool(true))
18545 });
18546
18547 define(interp, "experimental_crypto_info", Some(0), |_, _| {
18549 let mut info = std::collections::HashMap::new();
18550
18551 info.insert(
18552 "commitment_functions".to_string(),
18553 Value::Array(Rc::new(RefCell::new(vec![
18554 Value::String(Rc::new("commit".to_string())),
18555 Value::String(Rc::new("verify_commitment".to_string())),
18556 ]))),
18557 );
18558
18559 info.insert(
18560 "threshold_functions".to_string(),
18561 Value::Array(Rc::new(RefCell::new(vec![
18562 Value::String(Rc::new("secret_split".to_string())),
18563 Value::String(Rc::new("secret_recover".to_string())),
18564 ]))),
18565 );
18566
18567 info.insert(
18568 "cultural_ceremonies".to_string(),
18569 Value::Array(Rc::new(RefCell::new(vec![
18570 Value::String(Rc::new(
18571 "council_split (Ubuntu - African consensus)".to_string(),
18572 )),
18573 Value::String(Rc::new(
18574 "witness_chain (Isnad - Islamic transmission)".to_string(),
18575 )),
18576 ]))),
18577 );
18578
18579 Ok(Value::Map(Rc::new(RefCell::new(info))))
18580 });
18581}
18582
18583fn eval_polynomial_gf256(coefficients: &[u8], x: u8) -> u8 {
18585 let mut result: u8 = 0;
18586 let mut x_power: u8 = 1;
18587
18588 for &coef in coefficients {
18589 result ^= gf256_mul(coef, x_power);
18590 x_power = gf256_mul(x_power, x);
18591 }
18592
18593 result
18594}
18595
18596fn lagrange_interpolate_gf256(points: &[(u8, u8)], _x: u8) -> u8 {
18598 let mut result: u8 = 0;
18599
18600 for (i, &(xi, yi)) in points.iter().enumerate() {
18601 let mut numerator: u8 = 1;
18602 let mut denominator: u8 = 1;
18603
18604 for (j, &(xj, _)) in points.iter().enumerate() {
18605 if i != j {
18606 numerator = gf256_mul(numerator, xj);
18608 denominator = gf256_mul(denominator, xi ^ xj);
18610 }
18611 }
18612
18613 let term = gf256_mul(yi, gf256_mul(numerator, gf256_inv(denominator)));
18615 result ^= term;
18616 }
18617
18618 result
18619}
18620
18621fn gf256_mul(mut a: u8, mut b: u8) -> u8 {
18623 let mut result: u8 = 0;
18624 let modulus: u16 = 0x11b; while b != 0 {
18627 if b & 1 != 0 {
18628 result ^= a;
18629 }
18630 let high_bit = (a & 0x80) != 0;
18631 a <<= 1;
18632 if high_bit {
18633 a ^= (modulus & 0xff) as u8;
18634 }
18635 b >>= 1;
18636 }
18637
18638 result
18639}
18640
18641fn gf256_inv(a: u8) -> u8 {
18643 if a == 0 {
18644 return 0;
18645 }
18646
18647 let mut result = a;
18649 for _ in 0..6 {
18650 result = gf256_mul(result, result);
18651 result = gf256_mul(result, a);
18652 }
18653 gf256_mul(result, result)
18654}
18655
18656fn register_multibase(interp: &mut Interpreter) {
18675 define(interp, "to_vigesimal", Some(1), |_, args| {
18679 let n = match &args[0] {
18680 Value::Int(n) => *n,
18681 _ => return Err(RuntimeError::new("to_vigesimal() requires integer")),
18682 };
18683
18684 let result = to_base_string(n.unsigned_abs(), 20, false);
18685 let prefix = if n < 0 { "-0v" } else { "0v" };
18686 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
18687 });
18688
18689 define(interp, "from_vigesimal", Some(1), |_, args| {
18690 let s = match &args[0] {
18691 Value::String(s) => s.to_string(),
18692 _ => return Err(RuntimeError::new("from_vigesimal() requires string")),
18693 };
18694
18695 let (negative, clean) = parse_base_prefix(&s, "0v");
18696 let value = from_base_string(&clean, 20)?;
18697 Ok(Value::Int(if negative {
18698 -(value as i64)
18699 } else {
18700 value as i64
18701 }))
18702 });
18703
18704 define(interp, "to_sexagesimal", Some(1), |_, args| {
18708 let n = match &args[0] {
18709 Value::Int(n) => *n,
18710 _ => return Err(RuntimeError::new("to_sexagesimal() requires integer")),
18711 };
18712
18713 let negative = n < 0;
18714 let mut value = n.unsigned_abs();
18715 let mut parts = Vec::new();
18716
18717 if value == 0 {
18718 parts.push("0".to_string());
18719 } else {
18720 while value > 0 {
18721 parts.push(format!("{}", value % 60));
18722 value /= 60;
18723 }
18724 parts.reverse();
18725 }
18726
18727 let prefix = if negative { "-0s" } else { "0s" };
18728 Ok(Value::String(Rc::new(format!(
18729 "{}[{}]",
18730 prefix,
18731 parts.join(":")
18732 ))))
18733 });
18734
18735 define(interp, "from_sexagesimal", Some(1), |_, args| {
18736 let s = match &args[0] {
18737 Value::String(s) => s.to_string(),
18738 _ => return Err(RuntimeError::new("from_sexagesimal() requires string")),
18739 };
18740
18741 let negative = s.starts_with('-');
18742 let clean = s
18743 .trim_start_matches('-')
18744 .trim_start_matches("0s")
18745 .trim_start_matches('[')
18746 .trim_end_matches(']');
18747
18748 let mut result: i64 = 0;
18749 for part in clean.split(':') {
18750 let digit: i64 = part
18751 .trim()
18752 .parse()
18753 .map_err(|_| RuntimeError::new(format!("Invalid sexagesimal digit: {}", part)))?;
18754 if digit < 0 || digit >= 60 {
18755 return Err(RuntimeError::new(format!(
18756 "Sexagesimal digit out of range: {}",
18757 digit
18758 )));
18759 }
18760 result = result * 60 + digit;
18761 }
18762
18763 Ok(Value::Int(if negative { -result } else { result }))
18764 });
18765
18766 define(interp, "to_duodecimal", Some(1), |_, args| {
18770 let n = match &args[0] {
18771 Value::Int(n) => *n,
18772 _ => return Err(RuntimeError::new("to_duodecimal() requires integer")),
18773 };
18774
18775 let result = to_base_string_custom(n.unsigned_abs(), 12, "0123456789XE");
18776 let prefix = if n < 0 { "-0z" } else { "0z" };
18777 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
18778 });
18779
18780 define(interp, "from_duodecimal", Some(1), |_, args| {
18781 let s = match &args[0] {
18782 Value::String(s) => s.to_string(),
18783 _ => return Err(RuntimeError::new("from_duodecimal() requires string")),
18784 };
18785
18786 let (negative, clean) = parse_base_prefix(&s, "0z");
18787 let value = from_base_string_custom(&clean.to_uppercase(), "0123456789XE")?;
18788 Ok(Value::Int(if negative {
18789 -(value as i64)
18790 } else {
18791 value as i64
18792 }))
18793 });
18794
18795 define(interp, "to_base", Some(2), |_, args| {
18798 let n = match &args[0] {
18799 Value::Int(n) => *n,
18800 _ => return Err(RuntimeError::new("to_base() requires integer")),
18801 };
18802 let base = match &args[1] {
18803 Value::Int(b) => *b as u64,
18804 _ => return Err(RuntimeError::new("to_base() requires integer base")),
18805 };
18806
18807 if base < 2 || base > 36 {
18808 return Err(RuntimeError::new("to_base() base must be 2-36"));
18809 }
18810
18811 let result = to_base_string(n.unsigned_abs(), base, false);
18812 let prefix = if n < 0 { "-" } else { "" };
18813 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
18814 });
18815
18816 define(interp, "from_base", Some(2), |_, args| {
18817 let s = match &args[0] {
18818 Value::String(s) => s.to_string(),
18819 _ => return Err(RuntimeError::new("from_base() requires string")),
18820 };
18821 let base = match &args[1] {
18822 Value::Int(b) => *b as u64,
18823 _ => return Err(RuntimeError::new("from_base() requires integer base")),
18824 };
18825
18826 if base < 2 || base > 36 {
18827 return Err(RuntimeError::new("from_base() base must be 2-36"));
18828 }
18829
18830 let negative = s.starts_with('-');
18831 let clean = s.trim_start_matches('-');
18832 let value = from_base_string(clean, base)?;
18833 Ok(Value::Int(if negative {
18834 -(value as i64)
18835 } else {
18836 value as i64
18837 }))
18838 });
18839
18840 const BASE58_ALPHABET: &str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
18845
18846 define(interp, "base58_encode", Some(1), |_, args| {
18847 let bytes: Vec<u8> = match &args[0] {
18848 Value::String(s) => s.as_bytes().to_vec(),
18849 Value::Array(arr) => arr
18850 .borrow()
18851 .iter()
18852 .filter_map(|v| {
18853 if let Value::Int(i) = v {
18854 Some(*i as u8)
18855 } else {
18856 None
18857 }
18858 })
18859 .collect(),
18860 _ => {
18861 return Err(RuntimeError::new(
18862 "base58_encode() requires string or byte array",
18863 ))
18864 }
18865 };
18866
18867 let leading_zeros = bytes.iter().take_while(|&&b| b == 0).count();
18869
18870 let mut result = Vec::new();
18872 let mut num: Vec<u8> = bytes.clone();
18873
18874 while !num.is_empty() && !num.iter().all(|&b| b == 0) {
18875 let mut remainder = 0u32;
18876 let mut new_num = Vec::new();
18877
18878 for &byte in &num {
18879 let acc = (remainder << 8) + byte as u32;
18880 let digit = acc / 58;
18881 remainder = acc % 58;
18882
18883 if !new_num.is_empty() || digit > 0 {
18884 new_num.push(digit as u8);
18885 }
18886 }
18887
18888 result.push(BASE58_ALPHABET.chars().nth(remainder as usize).unwrap());
18889 num = new_num;
18890 }
18891
18892 for _ in 0..leading_zeros {
18894 result.push('1');
18895 }
18896
18897 result.reverse();
18898 Ok(Value::String(Rc::new(result.into_iter().collect())))
18899 });
18900
18901 define(interp, "base58_decode", Some(1), |_, args| {
18902 let s = match &args[0] {
18903 Value::String(s) => s.to_string(),
18904 _ => return Err(RuntimeError::new("base58_decode() requires string")),
18905 };
18906
18907 let leading_ones = s.chars().take_while(|&c| c == '1').count();
18909
18910 let mut num: Vec<u8> = Vec::new();
18912
18913 for c in s.chars() {
18914 let digit = BASE58_ALPHABET
18915 .find(c)
18916 .ok_or_else(|| RuntimeError::new(format!("Invalid base58 character: {}", c)))?;
18917
18918 let mut carry = digit as u32;
18919 for byte in num.iter_mut().rev() {
18920 let acc = (*byte as u32) * 58 + carry;
18921 *byte = (acc & 0xff) as u8;
18922 carry = acc >> 8;
18923 }
18924
18925 while carry > 0 {
18926 num.insert(0, (carry & 0xff) as u8);
18927 carry >>= 8;
18928 }
18929 }
18930
18931 let mut result = vec![0u8; leading_ones];
18933 result.extend(num);
18934
18935 Ok(Value::String(Rc::new(hex::encode(&result))))
18937 });
18938
18939 const BASE32_ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
18943
18944 define(interp, "base32_encode", Some(1), |_, args| {
18945 let bytes: Vec<u8> = match &args[0] {
18946 Value::String(s) => s.as_bytes().to_vec(),
18947 Value::Array(arr) => arr
18948 .borrow()
18949 .iter()
18950 .filter_map(|v| {
18951 if let Value::Int(i) = v {
18952 Some(*i as u8)
18953 } else {
18954 None
18955 }
18956 })
18957 .collect(),
18958 _ => {
18959 return Err(RuntimeError::new(
18960 "base32_encode() requires string or byte array",
18961 ))
18962 }
18963 };
18964
18965 let mut result = String::new();
18966 let mut buffer: u64 = 0;
18967 let mut bits = 0;
18968
18969 for byte in bytes {
18970 buffer = (buffer << 8) | byte as u64;
18971 bits += 8;
18972
18973 while bits >= 5 {
18974 bits -= 5;
18975 let idx = ((buffer >> bits) & 0x1f) as usize;
18976 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
18977 }
18978 }
18979
18980 if bits > 0 {
18981 let idx = ((buffer << (5 - bits)) & 0x1f) as usize;
18982 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
18983 }
18984
18985 while result.len() % 8 != 0 {
18987 result.push('=');
18988 }
18989
18990 Ok(Value::String(Rc::new(result)))
18991 });
18992
18993 define(interp, "base32_decode", Some(1), |_, args| {
18994 let s = match &args[0] {
18995 Value::String(s) => s.to_uppercase().replace('=', ""),
18996 _ => return Err(RuntimeError::new("base32_decode() requires string")),
18997 };
18998
18999 let mut result = Vec::new();
19000 let mut buffer: u64 = 0;
19001 let mut bits = 0;
19002
19003 for c in s.chars() {
19004 let digit = BASE32_ALPHABET
19005 .find(c)
19006 .ok_or_else(|| RuntimeError::new(format!("Invalid base32 character: {}", c)))?;
19007
19008 buffer = (buffer << 5) | digit as u64;
19009 bits += 5;
19010
19011 if bits >= 8 {
19012 bits -= 8;
19013 result.push((buffer >> bits) as u8);
19014 buffer &= (1 << bits) - 1;
19015 }
19016 }
19017
19018 Ok(Value::String(Rc::new(hex::encode(&result))))
19019 });
19020
19021 define(interp, "sacred_numbers", Some(1), |_, args| {
19025 let culture = match &args[0] {
19026 Value::String(s) => s.to_lowercase(),
19027 _ => {
19028 return Err(RuntimeError::new(
19029 "sacred_numbers() requires string culture",
19030 ))
19031 }
19032 };
19033
19034 let numbers: Vec<(i64, &str)> = match culture.as_str() {
19035 "mayan" | "maya" => vec![
19036 (13, "Sacred cycle - Tzolkin calendar"),
19037 (20, "Base of vigesimal system - human digits"),
19038 (52, "Calendar round - 52 years"),
19039 (260, "Tzolkin sacred calendar days"),
19040 (365, "Haab solar calendar days"),
19041 (400, "Baktun - 20×20 years"),
19042 ],
19043 "babylonian" | "mesopotamian" => vec![
19044 (12, "Months, hours - celestial division"),
19045 (60, "Sexagesimal base - minutes, seconds, degrees"),
19046 (360, "Circle degrees - 6×60"),
19047 (3600, "Sar - 60×60, large count"),
19048 (7, "Planets visible to naked eye"),
19049 ],
19050 "chinese" | "zh" => vec![
19051 (8, "八 (bā) - prosperity, wealth (sounds like 發)"),
19052 (9, "九 (jiǔ) - longevity (sounds like 久)"),
19053 (6, "六 (liù) - smooth, flowing"),
19054 (2, "二 (èr) - pairs, harmony"),
19055 (4, "四 (sì) - AVOID - sounds like death (死)"),
19056 (5, "五 (wǔ) - five elements"),
19057 (12, "十二 - zodiac animals"),
19058 ],
19059 "japanese" | "ja" => vec![
19060 (7, "七 (nana) - lucky, seven gods of fortune"),
19061 (8, "八 (hachi) - prosperity, expansion"),
19062 (3, "三 (san) - completeness"),
19063 (5, "五 (go) - five elements"),
19064 (4, "四 (shi) - AVOID - sounds like death (死)"),
19065 (9, "九 (ku) - AVOID - sounds like suffering (苦)"),
19066 ],
19067 "hebrew" | "jewish" => vec![
19068 (7, "Shabbat, creation days, menorah branches"),
19069 (18, "Chai (חי) - life - gematria of ח(8) + י(10)"),
19070 (40, "Transformation - flood, Sinai, wilderness"),
19071 (12, "Tribes of Israel"),
19072 (613, "Mitzvot - commandments"),
19073 (26, "Gematria of YHWH"),
19074 ],
19075 "islamic" | "arabic" | "ar" => vec![
19076 (5, "Pillars of Islam, daily prayers"),
19077 (7, "Heavens, circumambulation of Kaaba"),
19078 (40, "Age of prophethood, days of repentance"),
19079 (99, "Names of Allah"),
19080 (786, "Abjad value of Bismillah"),
19081 ],
19082 "hindu" | "indian" | "hi" => vec![
19083 (108, "Sacred beads, Upanishads, sun's distance"),
19084 (7, "Chakras (main), rishis, sacred rivers"),
19085 (3, "Trimurti - Brahma, Vishnu, Shiva"),
19086 (4, "Vedas, yugas, varnas"),
19087 (9, "Planets (navagraha), durga forms"),
19088 (1008, "Names of Vishnu"),
19089 ],
19090 "greek" | "pythagorean" => vec![
19091 (1, "Monad - unity, source"),
19092 (2, "Dyad - duality, diversity"),
19093 (3, "Triad - harmony, completion"),
19094 (4, "Tetrad - solidity, earth"),
19095 (7, "Heptad - perfection"),
19096 (10, "Decad - tetractys, divine"),
19097 (12, "Olympian gods"),
19098 ],
19099 "celtic" | "irish" => vec![
19100 (3, "Triple goddess, triquetra"),
19101 (5, "Elements including spirit"),
19102 (9, "Triple threes - sacred completion"),
19103 (13, "Lunar months"),
19104 (17, "St. Patrick's Day"),
19105 (20, "Vigesimal counting"),
19106 ],
19107 _ => vec![
19108 (1, "Unity"),
19109 (7, "Widely considered lucky"),
19110 (12, "Dozen - practical division"),
19111 (13, "Often considered unlucky in West"),
19112 ],
19113 };
19114
19115 let result: Vec<Value> = numbers
19116 .iter()
19117 .map(|(n, meaning)| {
19118 let mut entry = std::collections::HashMap::new();
19119 entry.insert("number".to_string(), Value::Int(*n));
19120 entry.insert(
19121 "meaning".to_string(),
19122 Value::String(Rc::new(meaning.to_string())),
19123 );
19124 Value::Map(Rc::new(RefCell::new(entry)))
19125 })
19126 .collect();
19127
19128 Ok(Value::Array(Rc::new(RefCell::new(result))))
19129 });
19130
19131 define(interp, "is_sacred", Some(2), |_, args| {
19133 let n = match &args[0] {
19134 Value::Int(n) => *n,
19135 _ => return Err(RuntimeError::new("is_sacred() requires integer")),
19136 };
19137 let culture = match &args[1] {
19138 Value::String(s) => s.to_lowercase(),
19139 _ => return Err(RuntimeError::new("is_sacred() requires string culture")),
19140 };
19141
19142 let sacred = match culture.as_str() {
19143 "mayan" | "maya" => vec![13, 20, 52, 260, 365, 400],
19144 "babylonian" | "mesopotamian" => vec![12, 60, 360, 3600, 7],
19145 "chinese" | "zh" => vec![8, 9, 6, 2, 5, 12],
19146 "japanese" | "ja" => vec![7, 8, 3, 5],
19147 "hebrew" | "jewish" => vec![7, 18, 40, 12, 613, 26],
19148 "islamic" | "arabic" | "ar" => vec![5, 7, 40, 99, 786],
19149 "hindu" | "indian" | "hi" => vec![108, 7, 3, 4, 9, 1008],
19150 "greek" | "pythagorean" => vec![1, 2, 3, 4, 7, 10, 12],
19151 "celtic" | "irish" => vec![3, 5, 9, 13, 17, 20],
19152 _ => vec![7, 12],
19153 };
19154
19155 Ok(Value::Bool(sacred.contains(&n)))
19156 });
19157
19158 define(interp, "is_unlucky", Some(2), |_, args| {
19160 let n = match &args[0] {
19161 Value::Int(n) => *n,
19162 _ => return Err(RuntimeError::new("is_unlucky() requires integer")),
19163 };
19164 let culture = match &args[1] {
19165 Value::String(s) => s.to_lowercase(),
19166 _ => return Err(RuntimeError::new("is_unlucky() requires string culture")),
19167 };
19168
19169 let unlucky = match culture.as_str() {
19170 "chinese" | "zh" => vec![4], "japanese" | "ja" => vec![4, 9], "western" | "en" => vec![13], "italian" | "it" => vec![17], _ => vec![],
19175 };
19176
19177 Ok(Value::Bool(unlucky.contains(&n)))
19178 });
19179
19180 define(interp, "number_meaning", Some(2), |_, args| {
19182 let n = match &args[0] {
19183 Value::Int(n) => *n,
19184 _ => return Err(RuntimeError::new("number_meaning() requires integer")),
19185 };
19186 let culture = match &args[1] {
19187 Value::String(s) => s.to_lowercase(),
19188 _ => {
19189 return Err(RuntimeError::new(
19190 "number_meaning() requires string culture",
19191 ))
19192 }
19193 };
19194
19195 let meaning = match (n, culture.as_str()) {
19196 (8, "chinese" | "zh") => "八 (bā) - Most auspicious, sounds like 發 (prosperity)",
19198 (4, "chinese" | "zh") => "四 (sì) - Unlucky, sounds like 死 (death)",
19199 (9, "chinese" | "zh") => "九 (jiǔ) - Longevity, sounds like 久 (long-lasting)",
19200 (6, "chinese" | "zh") => "六 (liù) - Smooth and well-off",
19201 (2, "chinese" | "zh") => "二 (èr) - Good things come in pairs",
19202
19203 (7, "japanese" | "ja") => "七 (nana) - Lucky, seven gods of fortune",
19205 (8, "japanese" | "ja") => "八 (hachi) - Lucky, represents spreading prosperity",
19206 (4, "japanese" | "ja") => "四 (shi) - Unlucky, homophone of death",
19207 (9, "japanese" | "ja") => "九 (ku) - Unlucky, homophone of suffering",
19208
19209 (18, "hebrew" | "jewish") => "Chai (חי) - Life itself, most auspicious",
19211 (7, "hebrew" | "jewish") => "Divine completion - Shabbat, menorah, creation",
19212 (40, "hebrew" | "jewish") => "Transformation - flood, Sinai, wilderness years",
19213 (613, "hebrew" | "jewish") => "Taryag - total number of mitzvot (commandments)",
19214
19215 (13, "mayan" | "maya") => "Sacred Tzolkin cycle, celestial layers",
19217 (20, "mayan" | "maya") => "Vigesimal base - fingers and toes, uinal",
19218 (260, "mayan" | "maya") => "Tzolkin calendar - 13 × 20 sacred days",
19219
19220 (60, "babylonian" | "mesopotamian") => {
19222 "Sexagesimal base - divisible by 2,3,4,5,6,10,12,15,20,30"
19223 }
19224 (360, "babylonian" | "mesopotamian") => "Circle degrees - celestial observation",
19225
19226 (108, "hindu" | "indian" | "hi") => {
19228 "Sacred completeness - mala beads, Upanishads, sun ratio"
19229 }
19230 (3, "hindu" | "indian" | "hi") => "Trimurti - Brahma, Vishnu, Shiva",
19231 (9, "hindu" | "indian" | "hi") => "Navagraha (9 planets), Durga's 9 forms",
19232
19233 (99, "islamic" | "arabic" | "ar") => "Asma ul-Husna - 99 names of Allah",
19235 (5, "islamic" | "arabic" | "ar") => "Five pillars of Islam",
19236 (786, "islamic" | "arabic" | "ar") => "Abjad numerology of Bismillah",
19237
19238 (10, "greek" | "pythagorean") => "Tetractys - 1+2+3+4, perfect number",
19240 (7, "greek" | "pythagorean") => "Heptad - virgin number, Athena's number",
19241
19242 _ => "No specific cultural meaning recorded",
19243 };
19244
19245 let mut result = std::collections::HashMap::new();
19246 result.insert("number".to_string(), Value::Int(n));
19247 result.insert("culture".to_string(), Value::String(Rc::new(culture)));
19248 result.insert(
19249 "meaning".to_string(),
19250 Value::String(Rc::new(meaning.to_string())),
19251 );
19252
19253 Ok(Value::Map(Rc::new(RefCell::new(result))))
19254 });
19255
19256 define(interp, "to_babylonian_time", Some(1), |_, args| {
19260 let seconds = match &args[0] {
19261 Value::Int(n) => *n,
19262 Value::Float(f) => *f as i64,
19263 _ => return Err(RuntimeError::new("to_babylonian_time() requires number")),
19264 };
19265
19266 let hours = seconds / 3600;
19267 let mins = (seconds % 3600) / 60;
19268 let secs = seconds % 60;
19269
19270 Ok(Value::String(Rc::new(format!(
19271 "0s[{}:{}:{}]",
19272 hours, mins, secs
19273 ))))
19274 });
19275
19276 define(interp, "from_babylonian_time", Some(1), |_, args| {
19278 let s = match &args[0] {
19279 Value::String(s) => s.to_string(),
19280 _ => return Err(RuntimeError::new("from_babylonian_time() requires string")),
19281 };
19282
19283 let clean = s
19284 .trim_start_matches("0s")
19285 .trim_start_matches('[')
19286 .trim_end_matches(']');
19287
19288 let parts: Vec<i64> = clean
19289 .split(':')
19290 .map(|p| p.trim().parse::<i64>().unwrap_or(0))
19291 .collect();
19292
19293 let seconds = match parts.len() {
19294 1 => parts[0],
19295 2 => parts[0] * 60 + parts[1],
19296 3 => parts[0] * 3600 + parts[1] * 60 + parts[2],
19297 _ => return Err(RuntimeError::new("Invalid Babylonian time format")),
19298 };
19299
19300 Ok(Value::Int(seconds))
19301 });
19302
19303 define(interp, "vigesimal_shares", Some(3), |_, args| {
19307 let secret = match &args[0] {
19308 Value::String(s) => s.as_bytes().to_vec(),
19309 _ => {
19310 return Err(RuntimeError::new(
19311 "vigesimal_shares() requires string secret",
19312 ))
19313 }
19314 };
19315
19316 let threshold = match &args[1] {
19317 Value::Int(n) => *n as usize,
19318 _ => {
19319 return Err(RuntimeError::new(
19320 "vigesimal_shares() requires integer threshold",
19321 ))
19322 }
19323 };
19324
19325 let num_shares = match &args[2] {
19326 Value::Int(n) => *n as usize,
19327 _ => {
19328 return Err(RuntimeError::new(
19329 "vigesimal_shares() requires integer num_shares",
19330 ))
19331 }
19332 };
19333
19334 if threshold < 2 || num_shares < threshold || num_shares > 20 {
19335 return Err(RuntimeError::new(
19336 "vigesimal_shares() requires 2 <= threshold <= num_shares <= 20 (Mayan limit)",
19337 ));
19338 }
19339
19340 let mut rng = rand::thread_rng();
19342 let mut shares: Vec<Vec<u8>> = (0..num_shares)
19343 .map(|_| Vec::with_capacity(secret.len() + 1))
19344 .collect();
19345
19346 for (i, share) in shares.iter_mut().enumerate() {
19347 share.push((i + 1) as u8);
19348 }
19349
19350 for &byte in &secret {
19351 let mut coefficients: Vec<u8> = vec![byte];
19352 for _ in 1..threshold {
19353 coefficients.push(rng.gen());
19354 }
19355
19356 for (i, share) in shares.iter_mut().enumerate() {
19357 let x = (i + 1) as u8;
19358 let y = eval_polynomial_gf256(&coefficients, x);
19359 share.push(y);
19360 }
19361 }
19362
19363 let share_values: Vec<Value> = shares
19365 .iter()
19366 .enumerate()
19367 .map(|(i, share)| {
19368 let mut entry = std::collections::HashMap::new();
19369
19370 let mut vig_parts: Vec<String> = Vec::new();
19372 for &byte in share {
19373 vig_parts.push(to_base_string(byte as u64, 20, true));
19374 }
19375
19376 entry.insert("index".to_string(), Value::Int((i + 1) as i64));
19377 entry.insert(
19378 "vigesimal".to_string(),
19379 Value::String(Rc::new(format!("0v{}", vig_parts.join(".")))),
19380 );
19381 entry.insert(
19382 "hex".to_string(),
19383 Value::String(Rc::new(hex::encode(share))),
19384 );
19385
19386 Value::Map(Rc::new(RefCell::new(entry)))
19387 })
19388 .collect();
19389
19390 let mut result = std::collections::HashMap::new();
19391 result.insert(
19392 "shares".to_string(),
19393 Value::Array(Rc::new(RefCell::new(share_values))),
19394 );
19395 result.insert("threshold".to_string(), Value::Int(threshold as i64));
19396 result.insert("total".to_string(), Value::Int(num_shares as i64));
19397 result.insert(
19398 "base".to_string(),
19399 Value::String(Rc::new("vigesimal (Mayan base-20)".to_string())),
19400 );
19401
19402 Ok(Value::Map(Rc::new(RefCell::new(result))))
19403 });
19404
19405 define(interp, "multibase_info", Some(0), |_, _| {
19407 let mut info = std::collections::HashMap::new();
19408
19409 let bases = vec![
19410 ("binary", 2, "0b", "Modern computing"),
19411 ("octal", 8, "0o", "Unix, historical computing"),
19412 ("decimal", 10, "", "Indo-Arabic global standard"),
19413 (
19414 "duodecimal",
19415 12,
19416 "0z",
19417 "Dozen system - time, music, measurement",
19418 ),
19419 ("hexadecimal", 16, "0x", "Computing, colors, addresses"),
19420 (
19421 "vigesimal",
19422 20,
19423 "0v",
19424 "Mayan, Celtic, Basque - human digits",
19425 ),
19426 (
19427 "sexagesimal",
19428 60,
19429 "0s",
19430 "Babylonian - time, angles, astronomy",
19431 ),
19432 ];
19433
19434 let base_list: Vec<Value> = bases
19435 .iter()
19436 .map(|(name, base, prefix, desc)| {
19437 let mut entry = std::collections::HashMap::new();
19438 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
19439 entry.insert("base".to_string(), Value::Int(*base as i64));
19440 entry.insert(
19441 "prefix".to_string(),
19442 Value::String(Rc::new(prefix.to_string())),
19443 );
19444 entry.insert(
19445 "origin".to_string(),
19446 Value::String(Rc::new(desc.to_string())),
19447 );
19448 Value::Map(Rc::new(RefCell::new(entry)))
19449 })
19450 .collect();
19451
19452 info.insert(
19453 "numeral_systems".to_string(),
19454 Value::Array(Rc::new(RefCell::new(base_list))),
19455 );
19456
19457 let encodings = vec!["base58 (Bitcoin)", "base32 (RFC 4648)", "base64 (standard)"];
19458 let enc_list: Vec<Value> = encodings
19459 .iter()
19460 .map(|s| Value::String(Rc::new(s.to_string())))
19461 .collect();
19462 info.insert(
19463 "special_encodings".to_string(),
19464 Value::Array(Rc::new(RefCell::new(enc_list))),
19465 );
19466
19467 let cultures = vec![
19468 "mayan",
19469 "babylonian",
19470 "chinese",
19471 "japanese",
19472 "hebrew",
19473 "islamic",
19474 "hindu",
19475 "greek",
19476 "celtic",
19477 ];
19478 let cult_list: Vec<Value> = cultures
19479 .iter()
19480 .map(|s| Value::String(Rc::new(s.to_string())))
19481 .collect();
19482 info.insert(
19483 "supported_cultures".to_string(),
19484 Value::Array(Rc::new(RefCell::new(cult_list))),
19485 );
19486
19487 Ok(Value::Map(Rc::new(RefCell::new(info))))
19488 });
19489}
19490
19491fn to_base_string(mut n: u64, base: u64, pad_to_two: bool) -> String {
19494 const DIGITS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
19495
19496 if n == 0 {
19497 return if pad_to_two {
19498 "00".to_string()
19499 } else {
19500 "0".to_string()
19501 };
19502 }
19503
19504 let mut result = Vec::new();
19505 while n > 0 {
19506 result.push(DIGITS[(n % base) as usize] as char);
19507 n /= base;
19508 }
19509
19510 if pad_to_two && result.len() < 2 {
19511 result.push('0');
19512 }
19513
19514 result.reverse();
19515 result.into_iter().collect()
19516}
19517
19518fn to_base_string_custom(mut n: u64, base: u64, digits: &str) -> String {
19519 if n == 0 {
19520 return digits.chars().next().unwrap().to_string();
19521 }
19522
19523 let mut result = Vec::new();
19524 let digit_chars: Vec<char> = digits.chars().collect();
19525
19526 while n > 0 {
19527 result.push(digit_chars[(n % base) as usize]);
19528 n /= base;
19529 }
19530
19531 result.reverse();
19532 result.into_iter().collect()
19533}
19534
19535fn from_base_string(s: &str, base: u64) -> Result<u64, RuntimeError> {
19536 let mut result: u64 = 0;
19537
19538 for c in s.chars() {
19539 let digit = match c {
19540 '0'..='9' => c as u64 - '0' as u64,
19541 'A'..='Z' => c as u64 - 'A' as u64 + 10,
19542 'a'..='z' => c as u64 - 'a' as u64 + 10,
19543 _ => return Err(RuntimeError::new(format!("Invalid digit: {}", c))),
19544 };
19545
19546 if digit >= base {
19547 return Err(RuntimeError::new(format!(
19548 "Digit {} out of range for base {}",
19549 c, base
19550 )));
19551 }
19552
19553 result = result
19554 .checked_mul(base)
19555 .and_then(|r| r.checked_add(digit))
19556 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
19557 }
19558
19559 Ok(result)
19560}
19561
19562fn from_base_string_custom(s: &str, digits: &str) -> Result<u64, RuntimeError> {
19563 let base = digits.len() as u64;
19564 let mut result: u64 = 0;
19565
19566 for c in s.chars() {
19567 let digit = digits
19568 .find(c)
19569 .ok_or_else(|| RuntimeError::new(format!("Invalid digit: {}", c)))?
19570 as u64;
19571
19572 result = result
19573 .checked_mul(base)
19574 .and_then(|r| r.checked_add(digit))
19575 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
19576 }
19577
19578 Ok(result)
19579}
19580
19581fn parse_base_prefix(s: &str, prefix: &str) -> (bool, String) {
19582 let negative = s.starts_with('-');
19583 let clean = s
19584 .trim_start_matches('-')
19585 .trim_start_matches(prefix)
19586 .to_string();
19587 (negative, clean)
19588}
19589
19590fn register_audio(interp: &mut Interpreter) {
19618 define(interp, "tune", Some(3), |_, args| {
19625 let note = match &args[0] {
19626 Value::Int(n) => *n as f64, Value::Float(f) => *f, Value::String(s) => parse_note_name(s)?,
19629 _ => return Err(RuntimeError::new("tune() requires note number or name")),
19630 };
19631
19632 let system = match &args[1] {
19633 Value::String(s) => s.to_lowercase(),
19634 _ => return Err(RuntimeError::new("tune() requires tuning system name")),
19635 };
19636
19637 let root_freq = match &args[2] {
19638 Value::Float(f) => *f,
19639 Value::Int(i) => *i as f64,
19640 _ => return Err(RuntimeError::new("tune() requires root frequency")),
19641 };
19642
19643 let freq = match system.as_str() {
19644 "12tet" | "equal" | "western" => {
19645 root_freq * 2.0_f64.powf((note - 69.0) / 12.0)
19647 }
19648 "24tet" | "quarter" | "arabic" | "maqam" => {
19649 root_freq * 2.0_f64.powf((note - 69.0) / 24.0)
19651 }
19652 "just" | "pure" => {
19653 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
19655 let octave = ((note - 69.0) / 12.0).floor();
19656 let ratio = just_intonation_ratio(interval as i32);
19657 root_freq * ratio * 2.0_f64.powf(octave)
19658 }
19659 "pythagorean" => {
19660 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
19662 let octave = ((note - 69.0) / 12.0).floor();
19663 let ratio = pythagorean_ratio(interval as i32);
19664 root_freq * ratio * 2.0_f64.powf(octave)
19665 }
19666 "meantone" | "quarter_comma" => {
19667 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
19669 let octave = ((note - 69.0) / 12.0).floor();
19670 let ratio = meantone_ratio(interval as i32);
19671 root_freq * ratio * 2.0_f64.powf(octave)
19672 }
19673 "53tet" | "turkish" | "persian" | "comma" => {
19674 root_freq * 2.0_f64.powf((note - 69.0) / 53.0)
19676 }
19677 "22shruti" | "shruti" | "indian" => {
19678 let shruti = (note - 69.0) % 22.0;
19680 let octave = ((note - 69.0) / 22.0).floor();
19681 let ratio = shruti_ratio(shruti as i32);
19682 root_freq * ratio * 2.0_f64.powf(octave)
19683 }
19684 "gamelan_pelog" | "pelog" => {
19685 let degree = ((note - 69.0) % 7.0 + 7.0) % 7.0;
19687 let octave = ((note - 69.0) / 7.0).floor();
19688 let ratio = pelog_ratio(degree as i32);
19689 root_freq * ratio * 2.0_f64.powf(octave)
19690 }
19691 "gamelan_slendro" | "slendro" => {
19692 let degree = ((note - 69.0) % 5.0 + 5.0) % 5.0;
19694 let octave = ((note - 69.0) / 5.0).floor();
19695 let ratio = slendro_ratio(degree as i32);
19696 root_freq * ratio * 2.0_f64.powf(octave)
19697 }
19698 "bohlen_pierce" | "bp" => {
19699 root_freq * 3.0_f64.powf((note - 69.0) / 13.0)
19701 }
19702 _ => {
19703 return Err(RuntimeError::new(format!(
19704 "Unknown tuning system: {}",
19705 system
19706 )))
19707 }
19708 };
19709
19710 Ok(Value::Float(freq))
19711 });
19712
19713 define(interp, "tuning_info", Some(1), |_, args| {
19715 let system = match &args[0] {
19716 Value::String(s) => s.to_lowercase(),
19717 _ => return Err(RuntimeError::new("tuning_info() requires string")),
19718 };
19719
19720 let (name, notes_per_octave, origin, description) = match system.as_str() {
19721 "12tet" | "equal" | "western" => (
19722 "12-TET", 12, "Western (18th century)",
19723 "Equal temperament - every semitone is exactly 2^(1/12). Universal but slightly impure."
19724 ),
19725 "24tet" | "quarter" | "arabic" | "maqam" => (
19726 "24-TET", 24, "Arabic/Turkish",
19727 "Quarter-tone system for maqam music. Enables neutral seconds and other microtones."
19728 ),
19729 "just" | "pure" => (
19730 "Just Intonation", 12, "Ancient (Ptolemy)",
19731 "Pure frequency ratios (3:2 fifth, 5:4 third). Beatless intervals but limited modulation."
19732 ),
19733 "pythagorean" => (
19734 "Pythagorean", 12, "Ancient Greece",
19735 "Built entirely from perfect fifths (3:2). Pure fifths but harsh thirds."
19736 ),
19737 "meantone" | "quarter_comma" => (
19738 "Quarter-Comma Meantone", 12, "Renaissance Europe",
19739 "Tempered fifths for pure major thirds. Beautiful in limited keys."
19740 ),
19741 "53tet" | "turkish" | "persian" | "comma" => (
19742 "53-TET", 53, "Turkish/Persian",
19743 "53 notes per octave. Closely approximates just intonation and allows maqam/dastgah."
19744 ),
19745 "22shruti" | "shruti" | "indian" => (
19746 "22-Shruti", 22, "Indian Classical",
19747 "Ancient Indian system. Each shruti is a 'microtone' - the smallest perceptible interval."
19748 ),
19749 "gamelan_pelog" | "pelog" => (
19750 "Pelog", 7, "Javanese Gamelan",
19751 "Heptatonic scale with unequal steps. Each gamelan has unique tuning - instruments are married."
19752 ),
19753 "gamelan_slendro" | "slendro" => (
19754 "Slendro", 5, "Javanese Gamelan",
19755 "Pentatonic scale, roughly equal steps (~240 cents). Each ensemble uniquely tuned."
19756 ),
19757 "bohlen_pierce" | "bp" => (
19758 "Bohlen-Pierce", 13, "Modern (1970s)",
19759 "Non-octave scale based on 3:1 tritave. Alien but mathematically beautiful."
19760 ),
19761 _ => return Err(RuntimeError::new(format!("Unknown tuning system: {}", system))),
19762 };
19763
19764 let mut info = std::collections::HashMap::new();
19765 info.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
19766 info.insert("notes_per_octave".to_string(), Value::Int(notes_per_octave));
19767 info.insert(
19768 "origin".to_string(),
19769 Value::String(Rc::new(origin.to_string())),
19770 );
19771 info.insert(
19772 "description".to_string(),
19773 Value::String(Rc::new(description.to_string())),
19774 );
19775
19776 Ok(Value::Map(Rc::new(RefCell::new(info))))
19777 });
19778
19779 define(interp, "list_tuning_systems", Some(0), |_, _| {
19781 let systems = vec![
19782 ("12tet", "Western equal temperament", 12),
19783 ("24tet", "Arabic/Turkish quarter-tones", 24),
19784 ("just", "Pure ratio just intonation", 12),
19785 ("pythagorean", "Ancient Greek pure fifths", 12),
19786 ("meantone", "Renaissance quarter-comma", 12),
19787 ("53tet", "Turkish/Persian comma system", 53),
19788 ("22shruti", "Indian microtonal", 22),
19789 ("pelog", "Javanese gamelan 7-note", 7),
19790 ("slendro", "Javanese gamelan 5-note", 5),
19791 ("bohlen_pierce", "Non-octave tritave scale", 13),
19792 ];
19793
19794 let result: Vec<Value> = systems
19795 .iter()
19796 .map(|(name, desc, notes)| {
19797 let mut entry = std::collections::HashMap::new();
19798 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
19799 entry.insert(
19800 "description".to_string(),
19801 Value::String(Rc::new(desc.to_string())),
19802 );
19803 entry.insert("notes_per_octave".to_string(), Value::Int(*notes));
19804 Value::Map(Rc::new(RefCell::new(entry)))
19805 })
19806 .collect();
19807
19808 Ok(Value::Array(Rc::new(RefCell::new(result))))
19809 });
19810
19811 define(interp, "sacred_freq", Some(1), |_, args| {
19817 let name = match &args[0] {
19818 Value::String(s) => s.to_lowercase(),
19819 _ => return Err(RuntimeError::new("sacred_freq() requires string")),
19820 };
19821
19822 let (freq, description) = match name.as_str() {
19823 "om" | "ॐ" | "aum" => (136.1, "Om - Earth year frequency (Cosmic Om)"),
19825 "earth_day" => (194.18, "Earth day - one rotation"),
19826 "earth_year" => (136.1, "Earth year - one orbit (Om frequency)"),
19827 "schumann" | "earth_resonance" => (
19828 7.83,
19829 "Schumann resonance - Earth's electromagnetic heartbeat",
19830 ),
19831
19832 "ut" | "do" | "396" => (396.0, "UT/DO - Liberating guilt and fear"),
19834 "re" | "417" => (417.0, "RE - Undoing situations, facilitating change"),
19835 "mi" | "528" => (528.0, "MI - Transformation, miracles, DNA repair"),
19836 "fa" | "639" => (639.0, "FA - Connecting relationships"),
19837 "sol" | "741" => (741.0, "SOL - Awakening intuition"),
19838 "la" | "852" => (852.0, "LA - Returning to spiritual order"),
19839 "si" | "963" => (963.0, "SI - Divine consciousness, enlightenment"),
19840 "174" => (174.0, "Solfeggio foundation - pain relief"),
19841 "285" => (285.0, "Solfeggio - healing tissue"),
19842
19843 "sun" | "☉" => (126.22, "Sun - ego, vitality, leadership"),
19845 "moon" | "☽" | "☾" => (210.42, "Moon - emotion, intuition, cycles"),
19846 "mercury" | "☿" => (141.27, "Mercury - communication, intellect"),
19847 "venus" | "♀" => (221.23, "Venus - love, beauty, harmony"),
19848 "mars" | "♂" => (144.72, "Mars - energy, action, courage"),
19849 "jupiter" | "♃" => (183.58, "Jupiter - expansion, wisdom, luck"),
19850 "saturn" | "♄" => (147.85, "Saturn - discipline, structure, time"),
19851
19852 "root" | "muladhara" => (256.0, "Root chakra - survival, grounding (C)"),
19854 "sacral" | "svadhisthana" => (288.0, "Sacral chakra - creativity, sexuality (D)"),
19855 "solar" | "manipura" => (320.0, "Solar plexus - will, power (E)"),
19856 "heart" | "anahata" => (341.3, "Heart chakra - love, compassion (F)"),
19857 "throat" | "vishuddha" => (384.0, "Throat chakra - expression, truth (G)"),
19858 "third_eye" | "ajna" => (426.7, "Third eye - intuition, insight (A)"),
19859 "crown" | "sahasrara" => (480.0, "Crown chakra - consciousness, unity (B)"),
19860
19861 "a440" | "iso" => (440.0, "ISO standard concert pitch (1955)"),
19863 "a432" | "verdi" => (
19864 432.0,
19865 "Verdi pitch - 'mathematically consistent with universe'",
19866 ),
19867 "a415" | "baroque" => (415.0, "Baroque pitch - period instrument standard"),
19868 "a466" | "chorton" => (466.0, "Choir pitch - high Baroque German"),
19869
19870 "delta" => (2.0, "Delta waves - deep sleep, healing (0.5-4 Hz)"),
19872 "theta" => (6.0, "Theta waves - meditation, creativity (4-8 Hz)"),
19873 "alpha" => (10.0, "Alpha waves - relaxation, calm focus (8-13 Hz)"),
19874 "beta" => (20.0, "Beta waves - alertness, concentration (13-30 Hz)"),
19875 "gamma" => (40.0, "Gamma waves - insight, peak performance (30-100 Hz)"),
19876
19877 _ => {
19878 return Err(RuntimeError::new(format!(
19879 "Unknown sacred frequency: {}",
19880 name
19881 )))
19882 }
19883 };
19884
19885 let mut result = std::collections::HashMap::new();
19886 result.insert("frequency".to_string(), Value::Float(freq));
19887 result.insert("name".to_string(), Value::String(Rc::new(name)));
19888 result.insert(
19889 "meaning".to_string(),
19890 Value::String(Rc::new(description.to_string())),
19891 );
19892
19893 Ok(Value::Map(Rc::new(RefCell::new(result))))
19894 });
19895
19896 define(interp, "solfeggio", Some(0), |_, _| {
19898 let frequencies = vec![
19899 (174.0, "Foundation", "Pain relief, security"),
19900 (285.0, "Quantum", "Healing tissue, safety"),
19901 (396.0, "UT", "Liberating guilt and fear"),
19902 (417.0, "RE", "Undoing situations, change"),
19903 (528.0, "MI", "Transformation, DNA repair, miracles"),
19904 (639.0, "FA", "Connecting relationships"),
19905 (741.0, "SOL", "Awakening intuition"),
19906 (852.0, "LA", "Spiritual order"),
19907 (963.0, "SI", "Divine consciousness"),
19908 ];
19909
19910 let result: Vec<Value> = frequencies
19911 .iter()
19912 .map(|(freq, name, meaning)| {
19913 let mut entry = std::collections::HashMap::new();
19914 entry.insert("frequency".to_string(), Value::Float(*freq));
19915 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
19916 entry.insert(
19917 "meaning".to_string(),
19918 Value::String(Rc::new(meaning.to_string())),
19919 );
19920 Value::Map(Rc::new(RefCell::new(entry)))
19921 })
19922 .collect();
19923
19924 Ok(Value::Array(Rc::new(RefCell::new(result))))
19925 });
19926
19927 define(interp, "chakras", Some(0), |_, _| {
19929 let chakras = vec![
19930 (
19931 256.0,
19932 "Muladhara",
19933 "Root",
19934 "Red",
19935 "Survival, grounding, stability",
19936 ),
19937 (
19938 288.0,
19939 "Svadhisthana",
19940 "Sacral",
19941 "Orange",
19942 "Creativity, sexuality, emotion",
19943 ),
19944 (
19945 320.0,
19946 "Manipura",
19947 "Solar Plexus",
19948 "Yellow",
19949 "Will, power, self-esteem",
19950 ),
19951 (
19952 341.3,
19953 "Anahata",
19954 "Heart",
19955 "Green",
19956 "Love, compassion, connection",
19957 ),
19958 (
19959 384.0,
19960 "Vishuddha",
19961 "Throat",
19962 "Blue",
19963 "Expression, truth, communication",
19964 ),
19965 (
19966 426.7,
19967 "Ajna",
19968 "Third Eye",
19969 "Indigo",
19970 "Intuition, insight, wisdom",
19971 ),
19972 (
19973 480.0,
19974 "Sahasrara",
19975 "Crown",
19976 "Violet",
19977 "Consciousness, unity, transcendence",
19978 ),
19979 ];
19980
19981 let result: Vec<Value> = chakras
19982 .iter()
19983 .map(|(freq, sanskrit, english, color, meaning)| {
19984 let mut entry = std::collections::HashMap::new();
19985 entry.insert("frequency".to_string(), Value::Float(*freq));
19986 entry.insert(
19987 "sanskrit".to_string(),
19988 Value::String(Rc::new(sanskrit.to_string())),
19989 );
19990 entry.insert(
19991 "english".to_string(),
19992 Value::String(Rc::new(english.to_string())),
19993 );
19994 entry.insert(
19995 "color".to_string(),
19996 Value::String(Rc::new(color.to_string())),
19997 );
19998 entry.insert(
19999 "meaning".to_string(),
20000 Value::String(Rc::new(meaning.to_string())),
20001 );
20002 Value::Map(Rc::new(RefCell::new(entry)))
20003 })
20004 .collect();
20005
20006 Ok(Value::Array(Rc::new(RefCell::new(result))))
20007 });
20008
20009 define(interp, "sine", Some(3), |_, args| {
20017 generate_waveform(&args, |phase| phase.sin())
20018 });
20019
20020 define(interp, "square", Some(3), |_, args| {
20022 generate_waveform(&args, |phase| if phase.sin() >= 0.0 { 1.0 } else { -1.0 })
20023 });
20024
20025 define(interp, "sawtooth", Some(3), |_, args| {
20027 generate_waveform(&args, |phase| {
20028 let normalized = (phase / std::f64::consts::TAU).fract();
20029 2.0 * normalized - 1.0
20030 })
20031 });
20032
20033 define(interp, "triangle", Some(3), |_, args| {
20035 generate_waveform(&args, |phase| {
20036 let normalized = (phase / std::f64::consts::TAU).fract();
20037 if normalized < 0.5 {
20038 4.0 * normalized - 1.0
20039 } else {
20040 3.0 - 4.0 * normalized
20041 }
20042 })
20043 });
20044
20045 define(interp, "noise", Some(1), |_, args| {
20047 let samples = match &args[0] {
20048 Value::Int(n) => *n as usize,
20049 _ => return Err(RuntimeError::new("noise() requires integer sample count")),
20050 };
20051
20052 let mut rng = rand::thread_rng();
20053 let result: Vec<Value> = (0..samples)
20054 .map(|_| Value::Float(rng.gen::<f64>() * 2.0 - 1.0))
20055 .collect();
20056
20057 Ok(Value::Array(Rc::new(RefCell::new(result))))
20058 });
20059
20060 define(interp, "scale", Some(1), |_, args| {
20066 let name = match &args[0] {
20067 Value::String(s) => s.to_lowercase(),
20068 _ => return Err(RuntimeError::new("scale() requires string")),
20069 };
20070
20071 let (intervals, origin, description) = match name.as_str() {
20072 "major" | "ionian" => (
20074 vec![0, 2, 4, 5, 7, 9, 11],
20075 "Western",
20076 "Happy, bright, resolved",
20077 ),
20078 "minor" | "aeolian" => (
20079 vec![0, 2, 3, 5, 7, 8, 10],
20080 "Western",
20081 "Sad, dark, introspective",
20082 ),
20083 "dorian" => (
20084 vec![0, 2, 3, 5, 7, 9, 10],
20085 "Western/Jazz",
20086 "Minor with bright 6th",
20087 ),
20088 "phrygian" => (
20089 vec![0, 1, 3, 5, 7, 8, 10],
20090 "Western/Flamenco",
20091 "Spanish, exotic, tense",
20092 ),
20093 "lydian" => (
20094 vec![0, 2, 4, 6, 7, 9, 11],
20095 "Western",
20096 "Dreamy, floating, ethereal",
20097 ),
20098 "mixolydian" => (vec![0, 2, 4, 5, 7, 9, 10], "Western/Blues", "Bluesy major"),
20099 "locrian" => (vec![0, 1, 3, 5, 6, 8, 10], "Western", "Unstable, dissonant"),
20100
20101 "pentatonic_major" => (vec![0, 2, 4, 7, 9], "Universal", "No dissonance, universal"),
20103 "pentatonic_minor" => (vec![0, 3, 5, 7, 10], "Universal", "Blues foundation"),
20104
20105 "hirajoshi" => (vec![0, 2, 3, 7, 8], "Japanese", "Melancholic, mysterious"),
20107 "insen" => (vec![0, 1, 5, 7, 10], "Japanese", "Dark, zen, contemplative"),
20108 "iwato" => (vec![0, 1, 5, 6, 10], "Japanese", "Most dark and dissonant"),
20109 "kumoi" => (vec![0, 2, 3, 7, 9], "Japanese", "Gentle, peaceful"),
20110 "yo" => (vec![0, 2, 5, 7, 9], "Japanese", "Bright, folk, celebratory"),
20111
20112 "hijaz" => (
20114 vec![0, 1, 4, 5, 7, 8, 11],
20115 "Arabic",
20116 "Exotic, Middle Eastern",
20117 ),
20118 "bayati" => (
20119 vec![0, 1.5 as i32, 3, 5, 7, 8, 10],
20120 "Arabic",
20121 "Quarter-tone, soulful",
20122 ),
20123 "rast" => (
20124 vec![0, 2, 3.5 as i32, 5, 7, 9, 10.5 as i32],
20125 "Arabic",
20126 "Foundation maqam",
20127 ),
20128 "saba" => (
20129 vec![0, 1.5 as i32, 3, 4, 5, 8, 10],
20130 "Arabic",
20131 "Sad, spiritual",
20132 ),
20133
20134 "bhairav" => (
20136 vec![0, 1, 4, 5, 7, 8, 11],
20137 "Indian",
20138 "Morning raga, devotional",
20139 ),
20140 "yaman" | "kalyan" => (vec![0, 2, 4, 6, 7, 9, 11], "Indian", "Evening, romantic"),
20141 "bhairavi" => (
20142 vec![0, 1, 3, 5, 7, 8, 10],
20143 "Indian",
20144 "Concluding raga, devotional",
20145 ),
20146 "todi" => (vec![0, 1, 3, 6, 7, 8, 11], "Indian", "Serious, pathos"),
20147 "marwa" => (vec![0, 1, 4, 6, 7, 9, 11], "Indian", "Evening, longing"),
20148
20149 "blues" => (vec![0, 3, 5, 6, 7, 10], "American", "Blue notes, soul"),
20151
20152 "hungarian_minor" => (vec![0, 2, 3, 6, 7, 8, 11], "Hungarian", "Gypsy, dramatic"),
20154 "romanian" => (vec![0, 2, 3, 6, 7, 9, 10], "Romanian", "Folk, energetic"),
20155
20156 "ahava_raba" | "freygish" => (
20158 vec![0, 1, 4, 5, 7, 8, 10],
20159 "Jewish/Klezmer",
20160 "Cantorial, emotional",
20161 ),
20162 "mi_sheberach" => (vec![0, 2, 3, 6, 7, 9, 10], "Jewish", "Prayer mode"),
20163
20164 "gong" => (vec![0, 2, 4, 7, 9], "Chinese", "Palace mode, major-like"),
20166 "shang" => (vec![0, 2, 5, 7, 9], "Chinese", "Merchant mode"),
20167 "jue" => (vec![0, 3, 5, 7, 10], "Chinese", "Angle mode"),
20168 "zhi" => (vec![0, 2, 5, 7, 10], "Chinese", "Emblem mode"),
20169 "yu" => (vec![0, 3, 5, 8, 10], "Chinese", "Wings mode, minor-like"),
20170
20171 "pelog" => (
20173 vec![0, 1, 3, 7, 8],
20174 "Javanese",
20175 "7-note unequal temperament",
20176 ),
20177 "slendro" => (vec![0, 2, 5, 7, 9], "Javanese", "5-note roughly equal"),
20178
20179 "whole_tone" => (
20181 vec![0, 2, 4, 6, 8, 10],
20182 "Impressionist",
20183 "Dreamlike, no resolution",
20184 ),
20185 "chromatic" => (
20186 vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
20187 "Western",
20188 "All 12 notes",
20189 ),
20190 "diminished" => (vec![0, 2, 3, 5, 6, 8, 9, 11], "Jazz", "Symmetric, tense"),
20191 "augmented" => (vec![0, 3, 4, 7, 8, 11], "Jazz", "Symmetric, floating"),
20192
20193 _ => return Err(RuntimeError::new(format!("Unknown scale: {}", name))),
20194 };
20195
20196 let mut result = std::collections::HashMap::new();
20197 let intervals_values: Vec<Value> =
20198 intervals.iter().map(|&i| Value::Int(i as i64)).collect();
20199 result.insert(
20200 "intervals".to_string(),
20201 Value::Array(Rc::new(RefCell::new(intervals_values))),
20202 );
20203 result.insert(
20204 "origin".to_string(),
20205 Value::String(Rc::new(origin.to_string())),
20206 );
20207 result.insert(
20208 "character".to_string(),
20209 Value::String(Rc::new(description.to_string())),
20210 );
20211 result.insert("name".to_string(), Value::String(Rc::new(name)));
20212
20213 Ok(Value::Map(Rc::new(RefCell::new(result))))
20214 });
20215
20216 define(interp, "list_scales", Some(0), |_, _| {
20218 let mut cultures = std::collections::HashMap::new();
20219
20220 cultures.insert(
20221 "western".to_string(),
20222 Value::Array(Rc::new(RefCell::new(vec![
20223 Value::String(Rc::new("major".to_string())),
20224 Value::String(Rc::new("minor".to_string())),
20225 Value::String(Rc::new("dorian".to_string())),
20226 Value::String(Rc::new("phrygian".to_string())),
20227 Value::String(Rc::new("lydian".to_string())),
20228 Value::String(Rc::new("mixolydian".to_string())),
20229 Value::String(Rc::new("locrian".to_string())),
20230 ]))),
20231 );
20232
20233 cultures.insert(
20234 "japanese".to_string(),
20235 Value::Array(Rc::new(RefCell::new(vec![
20236 Value::String(Rc::new("hirajoshi".to_string())),
20237 Value::String(Rc::new("insen".to_string())),
20238 Value::String(Rc::new("iwato".to_string())),
20239 Value::String(Rc::new("kumoi".to_string())),
20240 Value::String(Rc::new("yo".to_string())),
20241 ]))),
20242 );
20243
20244 cultures.insert(
20245 "arabic".to_string(),
20246 Value::Array(Rc::new(RefCell::new(vec![
20247 Value::String(Rc::new("hijaz".to_string())),
20248 Value::String(Rc::new("bayati".to_string())),
20249 Value::String(Rc::new("rast".to_string())),
20250 Value::String(Rc::new("saba".to_string())),
20251 ]))),
20252 );
20253
20254 cultures.insert(
20255 "indian".to_string(),
20256 Value::Array(Rc::new(RefCell::new(vec![
20257 Value::String(Rc::new("bhairav".to_string())),
20258 Value::String(Rc::new("yaman".to_string())),
20259 Value::String(Rc::new("bhairavi".to_string())),
20260 Value::String(Rc::new("todi".to_string())),
20261 Value::String(Rc::new("marwa".to_string())),
20262 ]))),
20263 );
20264
20265 cultures.insert(
20266 "chinese".to_string(),
20267 Value::Array(Rc::new(RefCell::new(vec![
20268 Value::String(Rc::new("gong".to_string())),
20269 Value::String(Rc::new("shang".to_string())),
20270 Value::String(Rc::new("jue".to_string())),
20271 Value::String(Rc::new("zhi".to_string())),
20272 Value::String(Rc::new("yu".to_string())),
20273 ]))),
20274 );
20275
20276 cultures.insert(
20277 "jewish".to_string(),
20278 Value::Array(Rc::new(RefCell::new(vec![
20279 Value::String(Rc::new("ahava_raba".to_string())),
20280 Value::String(Rc::new("mi_sheberach".to_string())),
20281 ]))),
20282 );
20283
20284 cultures.insert(
20285 "indonesian".to_string(),
20286 Value::Array(Rc::new(RefCell::new(vec![
20287 Value::String(Rc::new("pelog".to_string())),
20288 Value::String(Rc::new("slendro".to_string())),
20289 ]))),
20290 );
20291
20292 Ok(Value::Map(Rc::new(RefCell::new(cultures))))
20293 });
20294
20295 define(interp, "interval_ratio", Some(2), |_, args| {
20301 let semitones = match &args[0] {
20302 Value::Int(n) => *n as f64,
20303 Value::Float(f) => *f,
20304 _ => return Err(RuntimeError::new("interval_ratio() requires number")),
20305 };
20306
20307 let tuning = match &args[1] {
20308 Value::String(s) => s.to_lowercase(),
20309 _ => return Err(RuntimeError::new("interval_ratio() requires tuning system")),
20310 };
20311
20312 let ratio = match tuning.as_str() {
20313 "12tet" | "equal" => 2.0_f64.powf(semitones / 12.0),
20314 "just" => just_intonation_ratio(semitones as i32),
20315 "pythagorean" => pythagorean_ratio(semitones as i32),
20316 _ => 2.0_f64.powf(semitones / 12.0),
20317 };
20318
20319 Ok(Value::Float(ratio))
20320 });
20321
20322 define(interp, "cents_between", Some(2), |_, args| {
20324 let f1 = match &args[0] {
20325 Value::Float(f) => *f,
20326 Value::Int(i) => *i as f64,
20327 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
20328 };
20329 let f2 = match &args[1] {
20330 Value::Float(f) => *f,
20331 Value::Int(i) => *i as f64,
20332 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
20333 };
20334
20335 let cents = 1200.0 * (f2 / f1).log2();
20336 Ok(Value::Float(cents))
20337 });
20338
20339 define(interp, "harmonic_series", Some(2), |_, args| {
20341 let fundamental = match &args[0] {
20342 Value::Float(f) => *f,
20343 Value::Int(i) => *i as f64,
20344 _ => return Err(RuntimeError::new("harmonic_series() requires frequency")),
20345 };
20346 let count = match &args[1] {
20347 Value::Int(n) => *n as usize,
20348 _ => return Err(RuntimeError::new("harmonic_series() requires count")),
20349 };
20350
20351 let harmonics: Vec<Value> = (1..=count)
20352 .map(|n| {
20353 let mut entry = std::collections::HashMap::new();
20354 entry.insert("harmonic".to_string(), Value::Int(n as i64));
20355 entry.insert(
20356 "frequency".to_string(),
20357 Value::Float(fundamental * n as f64),
20358 );
20359 entry.insert(
20360 "cents_from_root".to_string(),
20361 Value::Float(1200.0 * (n as f64).log2()),
20362 );
20363 Value::Map(Rc::new(RefCell::new(entry)))
20364 })
20365 .collect();
20366
20367 Ok(Value::Array(Rc::new(RefCell::new(harmonics))))
20368 });
20369
20370 define(interp, "audio_info", Some(0), |_, _| {
20375 let mut info = std::collections::HashMap::new();
20376
20377 info.insert(
20378 "tuning_systems".to_string(),
20379 Value::Array(Rc::new(RefCell::new(vec![
20380 Value::String(Rc::new(
20381 "12tet, 24tet, just, pythagorean, meantone".to_string(),
20382 )),
20383 Value::String(Rc::new(
20384 "53tet, 22shruti, pelog, slendro, bohlen_pierce".to_string(),
20385 )),
20386 ]))),
20387 );
20388
20389 info.insert(
20390 "waveforms".to_string(),
20391 Value::Array(Rc::new(RefCell::new(vec![
20392 Value::String(Rc::new("sine (∿)".to_string())),
20393 Value::String(Rc::new("square (⊓)".to_string())),
20394 Value::String(Rc::new("sawtooth (⋀)".to_string())),
20395 Value::String(Rc::new("triangle (△)".to_string())),
20396 Value::String(Rc::new("noise".to_string())),
20397 ]))),
20398 );
20399
20400 info.insert(
20401 "sacred_frequencies".to_string(),
20402 Value::Array(Rc::new(RefCell::new(vec![
20403 Value::String(Rc::new("om, solfeggio, chakras, planets".to_string())),
20404 Value::String(Rc::new(
20405 "schumann, brainwaves (delta/theta/alpha/beta/gamma)".to_string(),
20406 )),
20407 ]))),
20408 );
20409
20410 info.insert(
20411 "scale_cultures".to_string(),
20412 Value::Array(Rc::new(RefCell::new(vec![
20413 Value::String(Rc::new("western, japanese, arabic, indian".to_string())),
20414 Value::String(Rc::new("chinese, jewish, indonesian".to_string())),
20415 ]))),
20416 );
20417
20418 Ok(Value::Map(Rc::new(RefCell::new(info))))
20419 });
20420}
20421
20422fn parse_note_name(s: &str) -> Result<f64, RuntimeError> {
20425 let s = s.trim().to_uppercase();
20426 let (note, octave_offset) = if s.ends_with(|c: char| c.is_ascii_digit()) {
20427 let octave: i32 = s.chars().last().unwrap().to_digit(10).unwrap() as i32;
20428 let note_part = &s[..s.len() - 1];
20429 (note_part, (octave - 4) * 12) } else {
20431 (&s[..], 0)
20432 };
20433
20434 let semitone = match note {
20435 "C" => 0,
20436 "C#" | "DB" => 1,
20437 "D" => 2,
20438 "D#" | "EB" => 3,
20439 "E" => 4,
20440 "F" => 5,
20441 "F#" | "GB" => 6,
20442 "G" => 7,
20443 "G#" | "AB" => 8,
20444 "A" => 9,
20445 "A#" | "BB" => 10,
20446 "B" => 11,
20447 _ => return Err(RuntimeError::new(format!("Unknown note: {}", s))),
20448 };
20449
20450 Ok(69.0 + semitone as f64 - 9.0 + octave_offset as f64) }
20452
20453fn just_intonation_ratio(semitones: i32) -> f64 {
20454 match semitones.rem_euclid(12) {
20456 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,
20469 }
20470}
20471
20472fn pythagorean_ratio(semitones: i32) -> f64 {
20473 match semitones.rem_euclid(12) {
20475 0 => 1.0,
20476 1 => 256.0 / 243.0,
20477 2 => 9.0 / 8.0,
20478 3 => 32.0 / 27.0,
20479 4 => 81.0 / 64.0,
20480 5 => 4.0 / 3.0,
20481 6 => 729.0 / 512.0,
20482 7 => 3.0 / 2.0,
20483 8 => 128.0 / 81.0,
20484 9 => 27.0 / 16.0,
20485 10 => 16.0 / 9.0,
20486 11 => 243.0 / 128.0,
20487 _ => 1.0,
20488 }
20489}
20490
20491fn meantone_ratio(semitones: i32) -> f64 {
20492 let fifth = 5.0_f64.powf(0.25); match semitones.rem_euclid(12) {
20495 0 => 1.0,
20496 1 => 8.0 / (fifth.powi(5)),
20497 2 => fifth.powi(2) / 2.0,
20498 3 => 4.0 / (fifth.powi(3)),
20499 4 => fifth.powi(4) / 4.0,
20500 5 => 2.0 / fifth,
20501 6 => fifth.powi(6) / 8.0,
20502 7 => fifth,
20503 8 => 8.0 / (fifth.powi(4)),
20504 9 => fifth.powi(3) / 2.0,
20505 10 => 4.0 / (fifth.powi(2)),
20506 11 => fifth.powi(5) / 4.0,
20507 _ => 1.0,
20508 }
20509}
20510
20511fn shruti_ratio(shruti: i32) -> f64 {
20512 let ratios = [
20514 1.0,
20515 256.0 / 243.0,
20516 16.0 / 15.0,
20517 10.0 / 9.0,
20518 9.0 / 8.0,
20519 32.0 / 27.0,
20520 6.0 / 5.0,
20521 5.0 / 4.0,
20522 81.0 / 64.0,
20523 4.0 / 3.0,
20524 27.0 / 20.0,
20525 45.0 / 32.0,
20526 729.0 / 512.0,
20527 3.0 / 2.0,
20528 128.0 / 81.0,
20529 8.0 / 5.0,
20530 5.0 / 3.0,
20531 27.0 / 16.0,
20532 16.0 / 9.0,
20533 9.0 / 5.0,
20534 15.0 / 8.0,
20535 243.0 / 128.0,
20536 ];
20537 ratios[shruti.rem_euclid(22) as usize]
20538}
20539
20540fn pelog_ratio(degree: i32) -> f64 {
20541 let ratios = [1.0, 1.12, 1.26, 1.5, 1.68, 1.89, 2.12];
20543 ratios[degree.rem_euclid(7) as usize]
20544}
20545
20546fn slendro_ratio(degree: i32) -> f64 {
20547 let ratios = [1.0, 1.148, 1.318, 1.516, 1.741];
20549 ratios[degree.rem_euclid(5) as usize]
20550}
20551
20552fn generate_waveform(args: &[Value], wave_fn: fn(f64) -> f64) -> Result<Value, RuntimeError> {
20553 let freq = match &args[0] {
20554 Value::Float(f) => *f,
20555 Value::Int(i) => *i as f64,
20556 _ => return Err(RuntimeError::new("Waveform requires frequency")),
20557 };
20558 let sample_rate = match &args[1] {
20559 Value::Float(f) => *f as usize,
20560 Value::Int(i) => *i as usize,
20561 _ => return Err(RuntimeError::new("Waveform requires sample rate")),
20562 };
20563 let duration = match &args[2] {
20564 Value::Float(f) => *f,
20565 Value::Int(i) => *i as f64,
20566 _ => return Err(RuntimeError::new("Waveform requires duration")),
20567 };
20568
20569 let num_samples = (sample_rate as f64 * duration) as usize;
20570 let samples: Vec<Value> = (0..num_samples)
20571 .map(|i| {
20572 let t = i as f64 / sample_rate as f64;
20573 let phase = 2.0 * std::f64::consts::PI * freq * t;
20574 Value::Float(wave_fn(phase))
20575 })
20576 .collect();
20577
20578 Ok(Value::Array(Rc::new(RefCell::new(samples))))
20579}
20580
20581fn register_spirituality(interp: &mut Interpreter) {
20603 const TRIGRAMS: [(&str, &str, &str, &str, &str); 8] = [
20609 (
20610 "☰",
20611 "乾",
20612 "Heaven",
20613 "Creative",
20614 "strong, initiating, persisting",
20615 ),
20616 (
20617 "☱",
20618 "兌",
20619 "Lake",
20620 "Joyous",
20621 "pleasure, satisfaction, openness",
20622 ),
20623 (
20624 "☲",
20625 "離",
20626 "Fire",
20627 "Clinging",
20628 "clarity, awareness, dependence",
20629 ),
20630 (
20631 "☳",
20632 "震",
20633 "Thunder",
20634 "Arousing",
20635 "movement, initiative, action",
20636 ),
20637 (
20638 "☴",
20639 "巽",
20640 "Wind",
20641 "Gentle",
20642 "penetrating, following, flexible",
20643 ),
20644 ("☵", "坎", "Water", "Abysmal", "danger, flowing, depth"),
20645 (
20646 "☶",
20647 "艮",
20648 "Mountain",
20649 "Keeping Still",
20650 "stopping, resting, meditation",
20651 ),
20652 (
20653 "☷",
20654 "坤",
20655 "Earth",
20656 "Receptive",
20657 "yielding, nurturing, devoted",
20658 ),
20659 ];
20660
20661 define(interp, "trigram", Some(1), |_, args| {
20663 let input = match &args[0] {
20664 Value::Int(n) => (*n as usize).min(7),
20665 Value::String(s) => match s.as_str() {
20666 "☰" | "heaven" | "qian" | "乾" => 0,
20667 "☱" | "lake" | "dui" | "兌" => 1,
20668 "☲" | "fire" | "li" | "離" => 2,
20669 "☳" | "thunder" | "zhen" | "震" => 3,
20670 "☴" | "wind" | "xun" | "巽" => 4,
20671 "☵" | "water" | "kan" | "坎" => 5,
20672 "☶" | "mountain" | "gen" | "艮" => 6,
20673 "☷" | "earth" | "kun" | "坤" => 7,
20674 _ => return Err(RuntimeError::new(format!("Unknown trigram: {}", s))),
20675 },
20676 _ => return Err(RuntimeError::new("trigram() requires number or name")),
20677 };
20678
20679 let (symbol, chinese, english, name, meaning) = TRIGRAMS[input];
20680
20681 let mut result = std::collections::HashMap::new();
20682 result.insert("number".to_string(), Value::Int(input as i64));
20683 result.insert(
20684 "symbol".to_string(),
20685 Value::String(Rc::new(symbol.to_string())),
20686 );
20687 result.insert(
20688 "chinese".to_string(),
20689 Value::String(Rc::new(chinese.to_string())),
20690 );
20691 result.insert(
20692 "english".to_string(),
20693 Value::String(Rc::new(english.to_string())),
20694 );
20695 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
20696 result.insert(
20697 "meaning".to_string(),
20698 Value::String(Rc::new(meaning.to_string())),
20699 );
20700
20701 let binary = match input {
20703 0 => "111", 1 => "110", 2 => "101", 3 => "100", 4 => "011", 5 => "010", 6 => "001", 7 => "000", _ => "000",
20712 };
20713 result.insert(
20714 "binary".to_string(),
20715 Value::String(Rc::new(binary.to_string())),
20716 );
20717
20718 Ok(Value::Map(Rc::new(RefCell::new(result))))
20719 });
20720
20721 define(interp, "hexagram", Some(1), |_, args| {
20723 let num = match &args[0] {
20724 Value::Int(n) => ((*n - 1) as usize).min(63),
20725 _ => return Err(RuntimeError::new("hexagram() requires number 1-64")),
20726 };
20727
20728 let hex = &HEXAGRAMS[num];
20729
20730 let mut result = std::collections::HashMap::new();
20731 result.insert("number".to_string(), Value::Int((num + 1) as i64));
20732 result.insert(
20733 "chinese".to_string(),
20734 Value::String(Rc::new(hex.0.to_string())),
20735 );
20736 result.insert(
20737 "pinyin".to_string(),
20738 Value::String(Rc::new(hex.1.to_string())),
20739 );
20740 result.insert(
20741 "english".to_string(),
20742 Value::String(Rc::new(hex.2.to_string())),
20743 );
20744 result.insert(
20745 "judgment".to_string(),
20746 Value::String(Rc::new(hex.3.to_string())),
20747 );
20748 result.insert(
20749 "upper_trigram".to_string(),
20750 Value::String(Rc::new(hex.4.to_string())),
20751 );
20752 result.insert(
20753 "lower_trigram".to_string(),
20754 Value::String(Rc::new(hex.5.to_string())),
20755 );
20756
20757 Ok(Value::Map(Rc::new(RefCell::new(result))))
20758 });
20759
20760 define(interp, "cast_iching", Some(0), |_, _| {
20762 let mut rng = rand::thread_rng();
20763
20764 let mut lines = Vec::new();
20767 let mut hexagram_num = 0u8;
20768 let mut changing_lines = Vec::new();
20769
20770 for i in 0..6 {
20771 let r: f64 = rng.gen();
20773 let line = if r < 0.0625 {
20774 6
20775 }
20776 else if r < 0.3125 {
20778 7
20779 }
20780 else if r < 0.5625 {
20782 8
20783 }
20784 else {
20786 9
20787 }; let is_yang = line == 7 || line == 9;
20790 if is_yang {
20791 hexagram_num |= 1 << i;
20792 }
20793
20794 if line == 6 || line == 9 {
20795 changing_lines.push(i + 1);
20796 }
20797
20798 lines.push(Value::Int(line));
20799 }
20800
20801 let king_wen_num = binary_to_king_wen(hexagram_num) + 1;
20803 let hex = &HEXAGRAMS[(king_wen_num - 1) as usize];
20804
20805 let mut result = std::collections::HashMap::new();
20806 result.insert("hexagram".to_string(), Value::Int(king_wen_num as i64));
20807 result.insert(
20808 "chinese".to_string(),
20809 Value::String(Rc::new(hex.0.to_string())),
20810 );
20811 result.insert(
20812 "english".to_string(),
20813 Value::String(Rc::new(hex.2.to_string())),
20814 );
20815 result.insert(
20816 "judgment".to_string(),
20817 Value::String(Rc::new(hex.3.to_string())),
20818 );
20819 result.insert(
20820 "lines".to_string(),
20821 Value::Array(Rc::new(RefCell::new(lines))),
20822 );
20823
20824 let changing: Vec<Value> = changing_lines.iter().map(|&n| Value::Int(n)).collect();
20825 result.insert(
20826 "changing_lines".to_string(),
20827 Value::Array(Rc::new(RefCell::new(changing))),
20828 );
20829
20830 if !changing_lines.is_empty() {
20832 let mut result_hex = hexagram_num;
20833 for &line in &changing_lines {
20834 result_hex ^= 1 << (line - 1); }
20836 let result_king_wen = binary_to_king_wen(result_hex) + 1;
20837 result.insert(
20838 "transforms_to".to_string(),
20839 Value::Int(result_king_wen as i64),
20840 );
20841 }
20842
20843 Ok(Value::Map(Rc::new(RefCell::new(result))))
20844 });
20845
20846 define(interp, "phi", Some(0), |_, _| {
20852 Ok(Value::Float((1.0 + 5.0_f64.sqrt()) / 2.0))
20853 });
20854
20855 define(interp, "sacred_ratio", Some(1), |_, args| {
20857 let name = match &args[0] {
20858 Value::String(s) => s.to_lowercase(),
20859 _ => return Err(RuntimeError::new("sacred_ratio() requires string")),
20860 };
20861
20862 let (value, symbol, meaning) = match name.as_str() {
20863 "phi" | "φ" | "golden" => (
20864 (1.0 + 5.0_f64.sqrt()) / 2.0,
20865 "φ",
20866 "Golden Ratio - divine proportion found in nature, art, architecture",
20867 ),
20868 "phi_conjugate" | "1/phi" => (
20869 2.0 / (1.0 + 5.0_f64.sqrt()),
20870 "1/φ",
20871 "Golden Ratio conjugate - φ - 1 = 1/φ",
20872 ),
20873 "phi_squared" | "phi2" => (
20874 ((1.0 + 5.0_f64.sqrt()) / 2.0).powi(2),
20875 "φ²",
20876 "Golden Ratio squared - φ + 1 = φ²",
20877 ),
20878 "sqrt_phi" => (
20879 ((1.0 + 5.0_f64.sqrt()) / 2.0).sqrt(),
20880 "√φ",
20881 "Square root of Golden Ratio",
20882 ),
20883 "pi" | "π" => (
20884 std::f64::consts::PI,
20885 "π",
20886 "Circle constant - circumference/diameter, transcendental",
20887 ),
20888 "tau" | "τ" => (
20889 std::f64::consts::TAU,
20890 "τ",
20891 "Full circle constant - 2π, one complete revolution",
20892 ),
20893 "e" | "euler" => (
20894 std::f64::consts::E,
20895 "e",
20896 "Euler's number - natural growth, compound interest",
20897 ),
20898 "sqrt2" | "√2" | "pythagoras" => (
20899 std::f64::consts::SQRT_2,
20900 "√2",
20901 "Pythagorean constant - diagonal of unit square",
20902 ),
20903 "sqrt3" | "√3" | "vesica" => (
20904 3.0_f64.sqrt(),
20905 "√3",
20906 "Vesica Piscis ratio - sacred geometry foundation",
20907 ),
20908 "sqrt5" | "√5" => (
20909 5.0_f64.sqrt(),
20910 "√5",
20911 "Related to Golden Ratio: φ = (1 + √5) / 2",
20912 ),
20913 "silver" | "δs" => (
20914 1.0 + 2.0_f64.sqrt(),
20915 "δs",
20916 "Silver Ratio - related to octagon",
20917 ),
20918 "plastic" | "ρ" => (
20919 1.324717957244746,
20920 "ρ",
20921 "Plastic Number - smallest Pisot number",
20922 ),
20923 "feigenbaum" | "δ" => (
20924 4.669201609102990,
20925 "δ",
20926 "Feigenbaum constant - chaos theory, period doubling",
20927 ),
20928 _ => return Err(RuntimeError::new(format!("Unknown sacred ratio: {}", name))),
20929 };
20930
20931 let mut result = std::collections::HashMap::new();
20932 result.insert("value".to_string(), Value::Float(value));
20933 result.insert(
20934 "symbol".to_string(),
20935 Value::String(Rc::new(symbol.to_string())),
20936 );
20937 result.insert(
20938 "meaning".to_string(),
20939 Value::String(Rc::new(meaning.to_string())),
20940 );
20941
20942 Ok(Value::Map(Rc::new(RefCell::new(result))))
20943 });
20944
20945 define(interp, "fibonacci", Some(1), |_, args| {
20947 let count = match &args[0] {
20948 Value::Int(n) => *n as usize,
20949 _ => return Err(RuntimeError::new("fibonacci() requires count")),
20950 };
20951
20952 let mut seq = Vec::with_capacity(count);
20953 let (mut a, mut b) = (0i64, 1i64);
20954
20955 for _ in 0..count {
20956 seq.push(Value::Int(a));
20957 let next = a.saturating_add(b);
20958 a = b;
20959 b = next;
20960 }
20961
20962 Ok(Value::Array(Rc::new(RefCell::new(seq))))
20963 });
20964
20965 define(interp, "is_fibonacci", Some(1), |_, args| {
20967 let n = match &args[0] {
20968 Value::Int(n) => *n,
20969 _ => return Err(RuntimeError::new("is_fibonacci() requires integer")),
20970 };
20971
20972 fn is_perfect_square(n: i64) -> bool {
20974 if n < 0 {
20975 return false;
20976 }
20977 let root = (n as f64).sqrt() as i64;
20978 root * root == n
20979 }
20980
20981 let n_sq = n.saturating_mul(n);
20982 let test1 = 5i64.saturating_mul(n_sq).saturating_add(4);
20983 let test2 = 5i64.saturating_mul(n_sq).saturating_sub(4);
20984
20985 Ok(Value::Bool(
20986 is_perfect_square(test1) || is_perfect_square(test2),
20987 ))
20988 });
20989
20990 define(interp, "platonic_solid", Some(1), |_, args| {
20992 let name = match &args[0] {
20993 Value::String(s) => s.to_lowercase(),
20994 _ => return Err(RuntimeError::new("platonic_solid() requires string")),
20995 };
20996
20997 let (faces, vertices, edges, face_shape, element, meaning) = match name.as_str() {
20998 "tetrahedron" | "fire" => (
20999 4,
21000 4,
21001 6,
21002 "triangle",
21003 "Fire",
21004 "Sharpness, heat, transformation",
21005 ),
21006 "cube" | "hexahedron" | "earth" => (
21007 6,
21008 8,
21009 12,
21010 "square",
21011 "Earth",
21012 "Stability, grounding, material",
21013 ),
21014 "octahedron" | "air" => (
21015 8,
21016 6,
21017 12,
21018 "triangle",
21019 "Air",
21020 "Balance, intellect, communication",
21021 ),
21022 "dodecahedron" | "aether" | "spirit" => (
21023 12,
21024 20,
21025 30,
21026 "pentagon",
21027 "Aether/Spirit",
21028 "The cosmos, divine thought",
21029 ),
21030 "icosahedron" | "water" => (
21031 20,
21032 12,
21033 30,
21034 "triangle",
21035 "Water",
21036 "Flow, emotion, adaptability",
21037 ),
21038 _ => {
21039 return Err(RuntimeError::new(format!(
21040 "Unknown Platonic solid: {}",
21041 name
21042 )))
21043 }
21044 };
21045
21046 let mut result = std::collections::HashMap::new();
21047 result.insert("name".to_string(), Value::String(Rc::new(name)));
21048 result.insert("faces".to_string(), Value::Int(faces));
21049 result.insert("vertices".to_string(), Value::Int(vertices));
21050 result.insert("edges".to_string(), Value::Int(edges));
21051 result.insert(
21052 "face_shape".to_string(),
21053 Value::String(Rc::new(face_shape.to_string())),
21054 );
21055 result.insert(
21056 "element".to_string(),
21057 Value::String(Rc::new(element.to_string())),
21058 );
21059 result.insert(
21060 "meaning".to_string(),
21061 Value::String(Rc::new(meaning.to_string())),
21062 );
21063
21064 result.insert("euler_characteristic".to_string(), Value::Int(2));
21066
21067 Ok(Value::Map(Rc::new(RefCell::new(result))))
21068 });
21069
21070 define(interp, "gematria", Some(2), |_, args| {
21076 let text = match &args[0] {
21077 Value::String(s) => s.to_string(),
21078 _ => return Err(RuntimeError::new("gematria() requires string")),
21079 };
21080
21081 let system = match &args[1] {
21082 Value::String(s) => s.to_lowercase(),
21083 _ => return Err(RuntimeError::new("gematria() requires system name")),
21084 };
21085
21086 let total: i64 = match system.as_str() {
21087 "hebrew" | "kabbalah" => text.chars().map(|c| hebrew_gematria(c)).sum(),
21088 "greek" | "isopsephy" => text.chars().map(|c| greek_isopsephy(c)).sum(),
21089 "arabic" | "abjad" => text.chars().map(|c| arabic_abjad(c)).sum(),
21090 "english" | "simple" => {
21091 text.to_uppercase()
21093 .chars()
21094 .filter_map(|c| {
21095 if c.is_ascii_alphabetic() {
21096 Some((c as i64) - ('A' as i64) + 1)
21097 } else {
21098 None
21099 }
21100 })
21101 .sum()
21102 }
21103 "english_ordinal" => {
21104 text.to_uppercase()
21106 .chars()
21107 .filter_map(|c| {
21108 if c.is_ascii_alphabetic() {
21109 Some((c as i64) - ('A' as i64) + 1)
21110 } else {
21111 None
21112 }
21113 })
21114 .sum()
21115 }
21116 "english_reduction" => {
21117 text.to_uppercase()
21119 .chars()
21120 .filter_map(|c| {
21121 if c.is_ascii_alphabetic() {
21122 let val = ((c as i64) - ('A' as i64)) % 9 + 1;
21123 Some(val)
21124 } else {
21125 None
21126 }
21127 })
21128 .sum()
21129 }
21130 _ => {
21131 return Err(RuntimeError::new(format!(
21132 "Unknown gematria system: {}",
21133 system
21134 )))
21135 }
21136 };
21137
21138 let mut result = std::collections::HashMap::new();
21139 result.insert("text".to_string(), Value::String(Rc::new(text)));
21140 result.insert("system".to_string(), Value::String(Rc::new(system)));
21141 result.insert("value".to_string(), Value::Int(total));
21142
21143 let mut digital_root = total;
21145 while digital_root > 9 {
21146 digital_root = digital_root
21147 .to_string()
21148 .chars()
21149 .filter_map(|c| c.to_digit(10))
21150 .map(|d| d as i64)
21151 .sum();
21152 }
21153 result.insert("digital_root".to_string(), Value::Int(digital_root));
21154
21155 Ok(Value::Map(Rc::new(RefCell::new(result))))
21156 });
21157
21158 define(interp, "gematria_match", Some(2), |_, args| {
21160 let value = match &args[0] {
21161 Value::Int(n) => *n,
21162 _ => return Err(RuntimeError::new("gematria_match() requires integer value")),
21163 };
21164
21165 let system = match &args[1] {
21166 Value::String(s) => s.to_lowercase(),
21167 _ => return Err(RuntimeError::new("gematria_match() requires system name")),
21168 };
21169
21170 let matches = match (value, system.as_str()) {
21172 (26, "hebrew") => vec!["יהוה (YHWH - Tetragrammaton)"],
21173 (18, "hebrew") => vec!["חי (Chai - Life)"],
21174 (86, "hebrew") => vec!["אלהים (Elohim - God)"],
21175 (72, "hebrew") => vec!["חסד (Chesed - Loving-kindness)"],
21176 (93, "english") => vec!["Love", "Will", "Thelema"],
21177 (666, "greek") => vec!["Nero Caesar (in Hebrew letters)"],
21178 (888, "greek") => vec!["Ἰησοῦς (Jesus)"],
21179 _ => vec![],
21180 };
21181
21182 let match_values: Vec<Value> = matches
21183 .iter()
21184 .map(|s| Value::String(Rc::new(s.to_string())))
21185 .collect();
21186
21187 Ok(Value::Array(Rc::new(RefCell::new(match_values))))
21188 });
21189
21190 define(interp, "archetype", Some(1), |_, args| {
21196 let name = match &args[0] {
21197 Value::String(s) => s.to_lowercase(),
21198 _ => return Err(RuntimeError::new("archetype() requires string")),
21199 };
21200
21201 let (description, shadow, gift, challenge) = match name.as_str() {
21202 "self" => (
21204 "The unified conscious and unconscious, the goal of individuation",
21205 "Inflation or deflation of ego",
21206 "Wholeness, integration, meaning",
21207 "Integrating all aspects of psyche",
21208 ),
21209 "shadow" => (
21210 "The unconscious aspect containing repressed weaknesses and instincts",
21211 "Projection onto others, denial",
21212 "Creativity, spontaneity, insight",
21213 "Acknowledging and integrating darkness",
21214 ),
21215 "anima" => (
21216 "The feminine inner personality in a man's unconscious",
21217 "Moodiness, seduction, possession",
21218 "Relatedness, creativity, soul connection",
21219 "Developing emotional intelligence",
21220 ),
21221 "animus" => (
21222 "The masculine inner personality in a woman's unconscious",
21223 "Brutality, reckless action, opinionation",
21224 "Courage, initiative, spiritual depth",
21225 "Developing assertiveness with wisdom",
21226 ),
21227 "persona" => (
21228 "The social mask, the face we present to the world",
21229 "Over-identification, inauthenticity",
21230 "Social adaptation, professional competence",
21231 "Maintaining authenticity within role",
21232 ),
21233
21234 "hero" => (
21236 "The courageous one who overcomes obstacles and achieves great deeds",
21237 "Arrogance, ruthlessness, eternal battle",
21238 "Courage, perseverance, accomplishment",
21239 "Knowing when to fight and when to surrender",
21240 ),
21241 "sage" | "wise_old_man" => (
21242 "The wise figure who offers guidance and insight",
21243 "Dogmatism, disconnection, ivory tower",
21244 "Wisdom, knowledge, truth-seeking",
21245 "Applying wisdom practically",
21246 ),
21247 "magician" | "wizard" => (
21248 "The transformer who makes things happen through understanding laws",
21249 "Manipulation, disconnection from ethics",
21250 "Transformation, vision, manifestation",
21251 "Using power responsibly",
21252 ),
21253 "lover" => (
21254 "The one who pursues connection, beauty, and passion",
21255 "Obsession, jealousy, loss of identity",
21256 "Passion, commitment, appreciation",
21257 "Maintaining boundaries while connecting deeply",
21258 ),
21259 "caregiver" | "mother" => (
21260 "The nurturing one who protects and provides",
21261 "Martyrdom, enabling, smothering",
21262 "Compassion, generosity, nurturing",
21263 "Caring for self while caring for others",
21264 ),
21265 "ruler" | "king" | "queen" => (
21266 "The one who takes responsibility for the realm",
21267 "Tyranny, authoritarianism, being overthrown",
21268 "Order, leadership, prosperity",
21269 "Serving the greater good, not just power",
21270 ),
21271 "creator" | "artist" => (
21272 "The one who brings new things into being",
21273 "Perfectionism, self-indulgence, drama",
21274 "Creativity, imagination, expression",
21275 "Completing projects, accepting imperfection",
21276 ),
21277 "innocent" | "child" => (
21278 "The pure one with faith and optimism",
21279 "Naivety, denial, dependence",
21280 "Faith, optimism, loyalty",
21281 "Growing without becoming cynical",
21282 ),
21283 "explorer" | "seeker" => (
21284 "The one who seeks new experiences and self-discovery",
21285 "Aimless wandering, inability to commit",
21286 "Autonomy, ambition, authenticity",
21287 "Finding what you seek",
21288 ),
21289 "rebel" | "outlaw" => (
21290 "The one who breaks rules and challenges the status quo",
21291 "Crime, self-destruction, alienation",
21292 "Liberation, revolution, radical freedom",
21293 "Channeling rebellion constructively",
21294 ),
21295 "jester" | "fool" | "trickster" => (
21296 "The one who uses humor and playfulness",
21297 "Cruelty, debauchery, irresponsibility",
21298 "Joy, freedom, living in the moment",
21299 "Knowing when to be serious",
21300 ),
21301 "everyman" | "orphan" => (
21302 "The regular person who wants belonging",
21303 "Victim mentality, losing self in group",
21304 "Realism, empathy, connection",
21305 "Standing out when necessary",
21306 ),
21307 _ => return Err(RuntimeError::new(format!("Unknown archetype: {}", name))),
21308 };
21309
21310 let mut result = std::collections::HashMap::new();
21311 result.insert("name".to_string(), Value::String(Rc::new(name)));
21312 result.insert(
21313 "description".to_string(),
21314 Value::String(Rc::new(description.to_string())),
21315 );
21316 result.insert(
21317 "shadow".to_string(),
21318 Value::String(Rc::new(shadow.to_string())),
21319 );
21320 result.insert("gift".to_string(), Value::String(Rc::new(gift.to_string())));
21321 result.insert(
21322 "challenge".to_string(),
21323 Value::String(Rc::new(challenge.to_string())),
21324 );
21325
21326 Ok(Value::Map(Rc::new(RefCell::new(result))))
21327 });
21328
21329 define(interp, "zodiac", Some(1), |_, args| {
21335 let input = match &args[0] {
21336 Value::Int(n) => (*n as usize - 1).min(11),
21337 Value::String(s) => match s.to_lowercase().as_str() {
21338 "aries" | "♈" => 0,
21339 "taurus" | "♉" => 1,
21340 "gemini" | "♊" => 2,
21341 "cancer" | "♋" => 3,
21342 "leo" | "♌" => 4,
21343 "virgo" | "♍" => 5,
21344 "libra" | "♎" => 6,
21345 "scorpio" | "♏" => 7,
21346 "sagittarius" | "♐" => 8,
21347 "capricorn" | "♑" => 9,
21348 "aquarius" | "♒" => 10,
21349 "pisces" | "♓" => 11,
21350 _ => return Err(RuntimeError::new(format!("Unknown sign: {}", s))),
21351 },
21352 _ => return Err(RuntimeError::new("zodiac() requires number or name")),
21353 };
21354
21355 let signs = [
21356 (
21357 "♈",
21358 "Aries",
21359 "Fire",
21360 "Cardinal",
21361 "Mars",
21362 "I Am",
21363 "Mar 21 - Apr 19",
21364 ),
21365 (
21366 "♉",
21367 "Taurus",
21368 "Earth",
21369 "Fixed",
21370 "Venus",
21371 "I Have",
21372 "Apr 20 - May 20",
21373 ),
21374 (
21375 "♊",
21376 "Gemini",
21377 "Air",
21378 "Mutable",
21379 "Mercury",
21380 "I Think",
21381 "May 21 - Jun 20",
21382 ),
21383 (
21384 "♋",
21385 "Cancer",
21386 "Water",
21387 "Cardinal",
21388 "Moon",
21389 "I Feel",
21390 "Jun 21 - Jul 22",
21391 ),
21392 (
21393 "♌",
21394 "Leo",
21395 "Fire",
21396 "Fixed",
21397 "Sun",
21398 "I Will",
21399 "Jul 23 - Aug 22",
21400 ),
21401 (
21402 "♍",
21403 "Virgo",
21404 "Earth",
21405 "Mutable",
21406 "Mercury",
21407 "I Analyze",
21408 "Aug 23 - Sep 22",
21409 ),
21410 (
21411 "♎",
21412 "Libra",
21413 "Air",
21414 "Cardinal",
21415 "Venus",
21416 "I Balance",
21417 "Sep 23 - Oct 22",
21418 ),
21419 (
21420 "♏",
21421 "Scorpio",
21422 "Water",
21423 "Fixed",
21424 "Pluto/Mars",
21425 "I Transform",
21426 "Oct 23 - Nov 21",
21427 ),
21428 (
21429 "♐",
21430 "Sagittarius",
21431 "Fire",
21432 "Mutable",
21433 "Jupiter",
21434 "I Seek",
21435 "Nov 22 - Dec 21",
21436 ),
21437 (
21438 "♑",
21439 "Capricorn",
21440 "Earth",
21441 "Cardinal",
21442 "Saturn",
21443 "I Use",
21444 "Dec 22 - Jan 19",
21445 ),
21446 (
21447 "♒",
21448 "Aquarius",
21449 "Air",
21450 "Fixed",
21451 "Uranus/Saturn",
21452 "I Know",
21453 "Jan 20 - Feb 18",
21454 ),
21455 (
21456 "♓",
21457 "Pisces",
21458 "Water",
21459 "Mutable",
21460 "Neptune/Jupiter",
21461 "I Believe",
21462 "Feb 19 - Mar 20",
21463 ),
21464 ];
21465
21466 let (symbol, name, element, modality, ruler, motto, dates) = signs[input];
21467
21468 let mut result = std::collections::HashMap::new();
21469 result.insert("number".to_string(), Value::Int((input + 1) as i64));
21470 result.insert(
21471 "symbol".to_string(),
21472 Value::String(Rc::new(symbol.to_string())),
21473 );
21474 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21475 result.insert(
21476 "element".to_string(),
21477 Value::String(Rc::new(element.to_string())),
21478 );
21479 result.insert(
21480 "modality".to_string(),
21481 Value::String(Rc::new(modality.to_string())),
21482 );
21483 result.insert(
21484 "ruler".to_string(),
21485 Value::String(Rc::new(ruler.to_string())),
21486 );
21487 result.insert(
21488 "motto".to_string(),
21489 Value::String(Rc::new(motto.to_string())),
21490 );
21491 result.insert(
21492 "dates".to_string(),
21493 Value::String(Rc::new(dates.to_string())),
21494 );
21495
21496 Ok(Value::Map(Rc::new(RefCell::new(result))))
21497 });
21498
21499 define(interp, "tarot_major", Some(1), |_, args| {
21501 let num = match &args[0] {
21502 Value::Int(n) => (*n as usize).min(21),
21503 Value::String(s) => match s.to_lowercase().as_str() {
21504 "fool" => 0,
21505 "magician" => 1,
21506 "high_priestess" | "priestess" => 2,
21507 "empress" => 3,
21508 "emperor" => 4,
21509 "hierophant" | "pope" => 5,
21510 "lovers" => 6,
21511 "chariot" => 7,
21512 "strength" => 8,
21513 "hermit" => 9,
21514 "wheel" | "fortune" => 10,
21515 "justice" => 11,
21516 "hanged_man" | "hanged" => 12,
21517 "death" => 13,
21518 "temperance" => 14,
21519 "devil" => 15,
21520 "tower" => 16,
21521 "star" => 17,
21522 "moon" => 18,
21523 "sun" => 19,
21524 "judgement" | "judgment" => 20,
21525 "world" => 21,
21526 _ => return Err(RuntimeError::new(format!("Unknown card: {}", s))),
21527 },
21528 _ => return Err(RuntimeError::new("tarot_major() requires number or name")),
21529 };
21530
21531 let cards = [
21532 (
21533 "The Fool",
21534 "New beginnings, innocence, spontaneity",
21535 "Naivety, recklessness, risk-taking",
21536 ),
21537 (
21538 "The Magician",
21539 "Willpower, creation, manifestation",
21540 "Manipulation, trickery, unused talent",
21541 ),
21542 (
21543 "The High Priestess",
21544 "Intuition, mystery, inner knowledge",
21545 "Secrets, withdrawal, silence",
21546 ),
21547 (
21548 "The Empress",
21549 "Abundance, nurturing, fertility",
21550 "Dependence, smothering, emptiness",
21551 ),
21552 (
21553 "The Emperor",
21554 "Authority, structure, control",
21555 "Tyranny, rigidity, coldness",
21556 ),
21557 (
21558 "The Hierophant",
21559 "Tradition, conformity, spirituality",
21560 "Dogma, restriction, challenging status quo",
21561 ),
21562 (
21563 "The Lovers",
21564 "Love, harmony, relationships, choices",
21565 "Disharmony, imbalance, misalignment",
21566 ),
21567 (
21568 "The Chariot",
21569 "Direction, willpower, victory",
21570 "Aggression, lack of direction, obstacles",
21571 ),
21572 (
21573 "Strength",
21574 "Courage, patience, inner power",
21575 "Self-doubt, weakness, insecurity",
21576 ),
21577 (
21578 "The Hermit",
21579 "Contemplation, search for truth, inner guidance",
21580 "Isolation, loneliness, withdrawal",
21581 ),
21582 (
21583 "Wheel of Fortune",
21584 "Change, cycles, fate, destiny",
21585 "Resistance to change, bad luck, setbacks",
21586 ),
21587 (
21588 "Justice",
21589 "Truth, fairness, law, cause and effect",
21590 "Unfairness, dishonesty, lack of accountability",
21591 ),
21592 (
21593 "The Hanged Man",
21594 "Surrender, letting go, new perspective",
21595 "Stalling, resistance, indecision",
21596 ),
21597 (
21598 "Death",
21599 "Endings, transformation, transition",
21600 "Fear of change, stagnation, decay",
21601 ),
21602 (
21603 "Temperance",
21604 "Balance, moderation, patience",
21605 "Imbalance, excess, lack of purpose",
21606 ),
21607 (
21608 "The Devil",
21609 "Bondage, materialism, shadow self",
21610 "Freedom, release, exploring dark side",
21611 ),
21612 (
21613 "The Tower",
21614 "Sudden change, upheaval, revelation",
21615 "Disaster averted, fear of change, prolonged pain",
21616 ),
21617 (
21618 "The Star",
21619 "Hope, faith, renewal, inspiration",
21620 "Despair, disconnection, lack of faith",
21621 ),
21622 (
21623 "The Moon",
21624 "Illusion, intuition, the unconscious",
21625 "Fear, confusion, misinterpretation",
21626 ),
21627 (
21628 "The Sun",
21629 "Joy, success, vitality, positivity",
21630 "Negativity, depression, sadness",
21631 ),
21632 (
21633 "Judgement",
21634 "Rebirth, inner calling, absolution",
21635 "Self-doubt, refusal of self-examination",
21636 ),
21637 (
21638 "The World",
21639 "Completion, accomplishment, wholeness",
21640 "Incompletion, lack of closure, emptiness",
21641 ),
21642 ];
21643
21644 let (name, upright, reversed) = cards[num];
21645
21646 let mut result = std::collections::HashMap::new();
21647 result.insert("number".to_string(), Value::Int(num as i64));
21648 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
21649 result.insert(
21650 "upright".to_string(),
21651 Value::String(Rc::new(upright.to_string())),
21652 );
21653 result.insert(
21654 "reversed".to_string(),
21655 Value::String(Rc::new(reversed.to_string())),
21656 );
21657
21658 Ok(Value::Map(Rc::new(RefCell::new(result))))
21659 });
21660
21661 define(interp, "draw_tarot", Some(0), |_, _| {
21663 let mut rng = rand::thread_rng();
21664 let card: usize = rng.gen_range(0..22);
21665 let reversed: bool = rng.gen();
21666
21667 let cards = [
21668 "The Fool",
21669 "The Magician",
21670 "The High Priestess",
21671 "The Empress",
21672 "The Emperor",
21673 "The Hierophant",
21674 "The Lovers",
21675 "The Chariot",
21676 "Strength",
21677 "The Hermit",
21678 "Wheel of Fortune",
21679 "Justice",
21680 "The Hanged Man",
21681 "Death",
21682 "Temperance",
21683 "The Devil",
21684 "The Tower",
21685 "The Star",
21686 "The Moon",
21687 "The Sun",
21688 "Judgement",
21689 "The World",
21690 ];
21691
21692 let mut result = std::collections::HashMap::new();
21693 result.insert("number".to_string(), Value::Int(card as i64));
21694 result.insert(
21695 "name".to_string(),
21696 Value::String(Rc::new(cards[card].to_string())),
21697 );
21698 result.insert("reversed".to_string(), Value::Bool(reversed));
21699 result.insert(
21700 "orientation".to_string(),
21701 Value::String(Rc::new(
21702 if reversed { "reversed" } else { "upright" }.to_string(),
21703 )),
21704 );
21705
21706 Ok(Value::Map(Rc::new(RefCell::new(result))))
21707 });
21708
21709 define(interp, "synchronicity_score", Some(2), |_, args| {
21715 let a = match &args[0] {
21717 Value::String(s) => s.to_string(),
21718 Value::Int(n) => n.to_string(),
21719 _ => {
21720 return Err(RuntimeError::new(
21721 "synchronicity_score() requires string or int",
21722 ))
21723 }
21724 };
21725
21726 let b = match &args[1] {
21727 Value::String(s) => s.to_string(),
21728 Value::Int(n) => n.to_string(),
21729 _ => {
21730 return Err(RuntimeError::new(
21731 "synchronicity_score() requires string or int",
21732 ))
21733 }
21734 };
21735
21736 let val_a: i64 = a
21738 .to_uppercase()
21739 .chars()
21740 .filter_map(|c| {
21741 if c.is_ascii_alphabetic() {
21742 Some((c as i64) - ('A' as i64) + 1)
21743 } else if c.is_ascii_digit() {
21744 c.to_digit(10).map(|d| d as i64)
21745 } else {
21746 None
21747 }
21748 })
21749 .sum();
21750
21751 let val_b: i64 = b
21752 .to_uppercase()
21753 .chars()
21754 .filter_map(|c| {
21755 if c.is_ascii_alphabetic() {
21756 Some((c as i64) - ('A' as i64) + 1)
21757 } else if c.is_ascii_digit() {
21758 c.to_digit(10).map(|d| d as i64)
21759 } else {
21760 None
21761 }
21762 })
21763 .sum();
21764
21765 let mut factors = Vec::new();
21767
21768 if val_a == val_b {
21770 factors.push("identical_gematria".to_string());
21771 }
21772
21773 if val_a > 0 && val_b > 0 && (val_a % val_b == 0 || val_b % val_a == 0) {
21775 factors.push("divisibility".to_string());
21776 }
21777
21778 let fib_set: std::collections::HashSet<i64> =
21780 [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
21781 .iter()
21782 .cloned()
21783 .collect();
21784 if fib_set.contains(&val_a) && fib_set.contains(&val_b) {
21785 factors.push("both_fibonacci".to_string());
21786 }
21787
21788 fn digital_root(mut n: i64) -> i64 {
21790 while n > 9 {
21791 n = n
21792 .to_string()
21793 .chars()
21794 .filter_map(|c| c.to_digit(10))
21795 .map(|d| d as i64)
21796 .sum();
21797 }
21798 n
21799 }
21800 if digital_root(val_a) == digital_root(val_b) {
21801 factors.push("same_digital_root".to_string());
21802 }
21803
21804 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
21806 let ratio = if val_a > 0 && val_b > 0 {
21807 (val_a as f64 / val_b as f64).max(val_b as f64 / val_a as f64)
21808 } else {
21809 0.0
21810 };
21811 if (ratio - phi).abs() < 0.02 || (ratio - 1.0 / phi).abs() < 0.02 {
21812 factors.push("golden_ratio".to_string());
21813 }
21814
21815 let score = (factors.len() as f64 / 5.0).min(1.0);
21816
21817 let mut result = std::collections::HashMap::new();
21818 result.insert("score".to_string(), Value::Float(score));
21819 result.insert("value_a".to_string(), Value::Int(val_a));
21820 result.insert("value_b".to_string(), Value::Int(val_b));
21821 let factor_values: Vec<Value> = factors
21822 .iter()
21823 .map(|s| Value::String(Rc::new(s.clone())))
21824 .collect();
21825 result.insert(
21826 "factors".to_string(),
21827 Value::Array(Rc::new(RefCell::new(factor_values))),
21828 );
21829
21830 Ok(Value::Map(Rc::new(RefCell::new(result))))
21831 });
21832
21833 define(interp, "spirituality_info", Some(0), |_, _| {
21835 let mut info = std::collections::HashMap::new();
21836
21837 info.insert(
21838 "i_ching".to_string(),
21839 Value::String(Rc::new(
21840 "trigram(), hexagram(), cast_iching() - 64 hexagrams of change".to_string(),
21841 )),
21842 );
21843 info.insert(
21844 "sacred_geometry".to_string(),
21845 Value::String(Rc::new(
21846 "phi(), sacred_ratio(), fibonacci(), platonic_solid()".to_string(),
21847 )),
21848 );
21849 info.insert(
21850 "gematria".to_string(),
21851 Value::String(Rc::new(
21852 "Hebrew, Greek, Arabic, English letter-number systems".to_string(),
21853 )),
21854 );
21855 info.insert(
21856 "archetypes".to_string(),
21857 Value::String(Rc::new(
21858 "17 Jungian archetypes with shadow and gift".to_string(),
21859 )),
21860 );
21861 info.insert(
21862 "astrology".to_string(),
21863 Value::String(Rc::new(
21864 "zodiac() - 12 signs with elements and modalities".to_string(),
21865 )),
21866 );
21867 info.insert(
21868 "tarot".to_string(),
21869 Value::String(Rc::new(
21870 "tarot_major(), draw_tarot() - 22 Major Arcana".to_string(),
21871 )),
21872 );
21873
21874 Ok(Value::Map(Rc::new(RefCell::new(info))))
21875 });
21876}
21877
21878const HEXAGRAMS: [(&str, &str, &str, &str, &str, &str); 64] = [
21880 (
21881 "乾",
21882 "Qián",
21883 "The Creative",
21884 "Sublime success through perseverance",
21885 "Heaven",
21886 "Heaven",
21887 ),
21888 (
21889 "坤",
21890 "Kūn",
21891 "The Receptive",
21892 "Devoted success through the mare's perseverance",
21893 "Earth",
21894 "Earth",
21895 ),
21896 (
21897 "屯",
21898 "Zhūn",
21899 "Difficulty at the Beginning",
21900 "Persevere, seek helpers, don't act alone",
21901 "Water",
21902 "Thunder",
21903 ),
21904 (
21905 "蒙",
21906 "Méng",
21907 "Youthful Folly",
21908 "Success through education and guidance",
21909 "Mountain",
21910 "Water",
21911 ),
21912 (
21913 "需",
21914 "Xū",
21915 "Waiting",
21916 "Sincerity brings success; cross the great water",
21917 "Water",
21918 "Heaven",
21919 ),
21920 (
21921 "訟",
21922 "Sòng",
21923 "Conflict",
21924 "Seek counsel; don't cross the great water",
21925 "Heaven",
21926 "Water",
21927 ),
21928 (
21929 "師",
21930 "Shī",
21931 "The Army",
21932 "Perseverance and an experienced leader bring success",
21933 "Earth",
21934 "Water",
21935 ),
21936 (
21937 "比",
21938 "Bǐ",
21939 "Holding Together",
21940 "Through perseverance, those who hesitate should reflect",
21941 "Water",
21942 "Earth",
21943 ),
21944 (
21945 "小畜",
21946 "Xiǎo Chù",
21947 "Small Taming",
21948 "Success; dense clouds but no rain",
21949 "Wind",
21950 "Heaven",
21951 ),
21952 (
21953 "履",
21954 "Lǚ",
21955 "Treading",
21956 "Tread on the tiger's tail carefully; success",
21957 "Heaven",
21958 "Lake",
21959 ),
21960 (
21961 "泰",
21962 "Tài",
21963 "Peace",
21964 "The small departs, the great approaches; success",
21965 "Earth",
21966 "Heaven",
21967 ),
21968 (
21969 "否",
21970 "Pǐ",
21971 "Standstill",
21972 "The great departs, the small approaches; persevere",
21973 "Heaven",
21974 "Earth",
21975 ),
21976 (
21977 "同人",
21978 "Tóng Rén",
21979 "Fellowship",
21980 "Success in the open; cross the great water",
21981 "Heaven",
21982 "Fire",
21983 ),
21984 (
21985 "大有",
21986 "Dà Yǒu",
21987 "Great Possession",
21988 "Supreme success",
21989 "Fire",
21990 "Heaven",
21991 ),
21992 (
21993 "謙",
21994 "Qiān",
21995 "Modesty",
21996 "Success; the superior person carries things through",
21997 "Earth",
21998 "Mountain",
21999 ),
22000 (
22001 "豫",
22002 "Yù",
22003 "Enthusiasm",
22004 "Appoint helpers and set armies marching",
22005 "Thunder",
22006 "Earth",
22007 ),
22008 (
22009 "隨",
22010 "Suí",
22011 "Following",
22012 "Supreme success through perseverance",
22013 "Lake",
22014 "Thunder",
22015 ),
22016 (
22017 "蠱",
22018 "Gǔ",
22019 "Work on the Decayed",
22020 "Success; cross the great water; three days before and after",
22021 "Mountain",
22022 "Wind",
22023 ),
22024 (
22025 "臨",
22026 "Lín",
22027 "Approach",
22028 "Great success through perseverance; misfortune in eighth month",
22029 "Earth",
22030 "Lake",
22031 ),
22032 (
22033 "觀",
22034 "Guān",
22035 "Contemplation",
22036 "Ablution, but not yet sacrifice; confidence inspires",
22037 "Wind",
22038 "Earth",
22039 ),
22040 (
22041 "噬嗑",
22042 "Shì Kè",
22043 "Biting Through",
22044 "Success; favorable for legal matters",
22045 "Fire",
22046 "Thunder",
22047 ),
22048 (
22049 "賁",
22050 "Bì",
22051 "Grace",
22052 "Success in small matters",
22053 "Mountain",
22054 "Fire",
22055 ),
22056 (
22057 "剝",
22058 "Bō",
22059 "Splitting Apart",
22060 "Unfavorable to go anywhere",
22061 "Mountain",
22062 "Earth",
22063 ),
22064 (
22065 "復",
22066 "Fù",
22067 "Return",
22068 "Success; going out and coming in without error",
22069 "Earth",
22070 "Thunder",
22071 ),
22072 (
22073 "無妄",
22074 "Wú Wàng",
22075 "Innocence",
22076 "Supreme success through perseverance",
22077 "Heaven",
22078 "Thunder",
22079 ),
22080 (
22081 "大畜",
22082 "Dà Chù",
22083 "Great Taming",
22084 "Perseverance; eat away from home",
22085 "Mountain",
22086 "Heaven",
22087 ),
22088 (
22089 "頤",
22090 "Yí",
22091 "Nourishment",
22092 "Perseverance; watch what you nurture",
22093 "Mountain",
22094 "Thunder",
22095 ),
22096 (
22097 "大過",
22098 "Dà Guò",
22099 "Great Exceeding",
22100 "The ridgepole sags; favorable to have somewhere to go",
22101 "Lake",
22102 "Wind",
22103 ),
22104 (
22105 "坎",
22106 "Kǎn",
22107 "The Abysmal",
22108 "Sincerity brings success of the heart",
22109 "Water",
22110 "Water",
22111 ),
22112 (
22113 "離",
22114 "Lí",
22115 "The Clinging",
22116 "Perseverance; success; care for the cow",
22117 "Fire",
22118 "Fire",
22119 ),
22120 (
22121 "咸",
22122 "Xián",
22123 "Influence",
22124 "Success; perseverance; taking a maiden brings fortune",
22125 "Lake",
22126 "Mountain",
22127 ),
22128 (
22129 "恆",
22130 "Héng",
22131 "Duration",
22132 "Success without blame; perseverance; favorable to have somewhere to go",
22133 "Thunder",
22134 "Wind",
22135 ),
22136 (
22137 "遯",
22138 "Dùn",
22139 "Retreat",
22140 "Success; small perseverance",
22141 "Heaven",
22142 "Mountain",
22143 ),
22144 (
22145 "大壯",
22146 "Dà Zhuàng",
22147 "Great Power",
22148 "Perseverance",
22149 "Thunder",
22150 "Heaven",
22151 ),
22152 (
22153 "晉",
22154 "Jìn",
22155 "Progress",
22156 "The powerful prince is honored with horses",
22157 "Fire",
22158 "Earth",
22159 ),
22160 (
22161 "明夷",
22162 "Míng Yí",
22163 "Darkening of the Light",
22164 "Perseverance in adversity",
22165 "Earth",
22166 "Fire",
22167 ),
22168 (
22169 "家人",
22170 "Jiā Rén",
22171 "The Family",
22172 "Perseverance of the woman",
22173 "Wind",
22174 "Fire",
22175 ),
22176 (
22177 "睽",
22178 "Kuí",
22179 "Opposition",
22180 "Good fortune in small matters",
22181 "Fire",
22182 "Lake",
22183 ),
22184 (
22185 "蹇",
22186 "Jiǎn",
22187 "Obstruction",
22188 "Southwest favorable; northeast unfavorable; see the great person",
22189 "Water",
22190 "Mountain",
22191 ),
22192 (
22193 "解",
22194 "Xiè",
22195 "Deliverance",
22196 "Southwest favorable; return brings fortune; haste brings fortune",
22197 "Thunder",
22198 "Water",
22199 ),
22200 (
22201 "損",
22202 "Sǔn",
22203 "Decrease",
22204 "Sincerity; supreme fortune; persistence; favorable to undertake",
22205 "Mountain",
22206 "Lake",
22207 ),
22208 (
22209 "益",
22210 "Yì",
22211 "Increase",
22212 "Favorable to undertake and cross the great water",
22213 "Wind",
22214 "Thunder",
22215 ),
22216 (
22217 "夬",
22218 "Guài",
22219 "Breakthrough",
22220 "Proclaim at the king's court; sincerity in danger",
22221 "Lake",
22222 "Heaven",
22223 ),
22224 (
22225 "姤",
22226 "Gòu",
22227 "Coming to Meet",
22228 "The maiden is powerful; don't marry such a maiden",
22229 "Heaven",
22230 "Wind",
22231 ),
22232 (
22233 "萃",
22234 "Cuì",
22235 "Gathering",
22236 "Success; the king approaches his temple; see the great person",
22237 "Lake",
22238 "Earth",
22239 ),
22240 (
22241 "升",
22242 "Shēng",
22243 "Pushing Upward",
22244 "Supreme success; see the great person; don't worry",
22245 "Earth",
22246 "Wind",
22247 ),
22248 (
22249 "困",
22250 "Kùn",
22251 "Oppression",
22252 "Success; perseverance of the great person; no blame",
22253 "Lake",
22254 "Water",
22255 ),
22256 (
22257 "井",
22258 "Jǐng",
22259 "The Well",
22260 "The town may change but not the well",
22261 "Water",
22262 "Wind",
22263 ),
22264 (
22265 "革",
22266 "Gé",
22267 "Revolution",
22268 "On your own day you are believed; great success",
22269 "Lake",
22270 "Fire",
22271 ),
22272 (
22273 "鼎",
22274 "Dǐng",
22275 "The Cauldron",
22276 "Supreme good fortune; success",
22277 "Fire",
22278 "Wind",
22279 ),
22280 (
22281 "震",
22282 "Zhèn",
22283 "The Arousing",
22284 "Success; thunder comes with fright; laughing and talking after",
22285 "Thunder",
22286 "Thunder",
22287 ),
22288 (
22289 "艮",
22290 "Gèn",
22291 "Keeping Still",
22292 "Keep your back still; go into the courtyard without seeing anyone",
22293 "Mountain",
22294 "Mountain",
22295 ),
22296 (
22297 "漸",
22298 "Jiàn",
22299 "Development",
22300 "The maiden is given in marriage; good fortune; perseverance",
22301 "Wind",
22302 "Mountain",
22303 ),
22304 (
22305 "歸妹",
22306 "Guī Mèi",
22307 "The Marrying Maiden",
22308 "Undertakings bring misfortune",
22309 "Thunder",
22310 "Lake",
22311 ),
22312 (
22313 "豐",
22314 "Fēng",
22315 "Abundance",
22316 "Success; the king attains it; don't worry; be like the sun at noon",
22317 "Thunder",
22318 "Fire",
22319 ),
22320 (
22321 "旅",
22322 "Lǚ",
22323 "The Wanderer",
22324 "Success through smallness; perseverance brings fortune",
22325 "Fire",
22326 "Mountain",
22327 ),
22328 (
22329 "巽",
22330 "Xùn",
22331 "The Gentle",
22332 "Success through small things; favorable to have somewhere to go",
22333 "Wind",
22334 "Wind",
22335 ),
22336 (
22337 "兌",
22338 "Duì",
22339 "The Joyous",
22340 "Success; perseverance",
22341 "Lake",
22342 "Lake",
22343 ),
22344 (
22345 "渙",
22346 "Huàn",
22347 "Dispersion",
22348 "Success; the king approaches his temple; cross the great water",
22349 "Wind",
22350 "Water",
22351 ),
22352 (
22353 "節",
22354 "Jié",
22355 "Limitation",
22356 "Success; bitter limitation should not be persevered in",
22357 "Water",
22358 "Lake",
22359 ),
22360 (
22361 "中孚",
22362 "Zhōng Fú",
22363 "Inner Truth",
22364 "Pigs and fishes; good fortune; cross the great water",
22365 "Wind",
22366 "Lake",
22367 ),
22368 (
22369 "小過",
22370 "Xiǎo Guò",
22371 "Small Exceeding",
22372 "Success; perseverance; small things yes, great things no",
22373 "Thunder",
22374 "Mountain",
22375 ),
22376 (
22377 "既濟",
22378 "Jì Jì",
22379 "After Completion",
22380 "Success in small matters; perseverance; good at start, disorder at end",
22381 "Water",
22382 "Fire",
22383 ),
22384 (
22385 "未濟",
22386 "Wèi Jì",
22387 "Before Completion",
22388 "Success; the young fox almost across; tail gets wet; no goal",
22389 "Fire",
22390 "Water",
22391 ),
22392];
22393
22394fn binary_to_king_wen(binary: u8) -> u8 {
22395 const KING_WEN_ORDER: [u8; 64] = [
22398 1, 43, 14, 34, 9, 5, 26, 11, 10, 58, 38, 54, 61, 60, 41, 19, 13, 49, 30, 55, 37, 63, 22,
22399 36, 25, 17, 21, 51, 42, 3, 27, 24, 44, 28, 50, 32, 57, 48, 18, 46, 6, 47, 64, 40, 59, 29,
22400 4, 7, 33, 31, 56, 62, 53, 39, 52, 15, 12, 45, 35, 16, 20, 8, 23, 2,
22401 ];
22402 KING_WEN_ORDER[binary as usize] - 1
22403}
22404
22405fn hebrew_gematria(c: char) -> i64 {
22406 match c {
22407 'א' | 'A' | 'a' => 1,
22408 'ב' | 'B' | 'b' => 2,
22409 'ג' | 'G' | 'g' => 3,
22410 'ד' | 'D' | 'd' => 4,
22411 'ה' | 'H' | 'h' => 5,
22412 'ו' | 'V' | 'v' | 'W' | 'w' => 6,
22413 'ז' | 'Z' | 'z' => 7,
22414 'ח' => 8,
22415 'ט' => 9,
22416 'י' | 'Y' | 'y' | 'I' | 'i' | 'J' | 'j' => 10,
22417 'כ' | 'K' | 'k' => 20,
22418 'ל' | 'L' | 'l' => 30,
22419 'מ' | 'M' | 'm' => 40,
22420 'נ' | 'N' | 'n' => 50,
22421 'ס' | 'S' | 's' | 'X' | 'x' => 60,
22422 'ע' | 'O' | 'o' => 70,
22423 'פ' | 'P' | 'p' | 'F' | 'f' => 80,
22424 'צ' => 90,
22425 'ק' | 'Q' | 'q' => 100,
22426 'ר' | 'R' | 'r' => 200,
22427 'ש' => 300,
22428 'ת' | 'T' | 't' => 400,
22429 'ך' => 500, 'ם' => 600, 'ן' => 700, 'ף' => 800, 'ץ' | 'C' | 'c' => 900, 'E' | 'e' => 5, 'U' | 'u' => 6, _ => 0,
22437 }
22438}
22439
22440fn greek_isopsephy(c: char) -> i64 {
22441 match c {
22442 'Α' | 'α' | 'A' | 'a' => 1,
22443 'Β' | 'β' | 'B' | 'b' => 2,
22444 'Γ' | 'γ' | 'G' | 'g' => 3,
22445 'Δ' | 'δ' | 'D' | 'd' => 4,
22446 'Ε' | 'ε' | 'E' | 'e' => 5,
22447 'Ϛ' | 'ϛ' => 6, 'Ζ' | 'ζ' | 'Z' | 'z' => 7,
22449 'Η' | 'η' | 'H' | 'h' => 8,
22450 'Θ' | 'θ' => 9,
22451 'Ι' | 'ι' | 'I' | 'i' => 10,
22452 'Κ' | 'κ' | 'K' | 'k' => 20,
22453 'Λ' | 'λ' | 'L' | 'l' => 30,
22454 'Μ' | 'μ' | 'M' | 'm' => 40,
22455 'Ν' | 'ν' | 'N' | 'n' => 50,
22456 'Ξ' | 'ξ' | 'X' | 'x' => 60,
22457 'Ο' | 'ο' | 'O' | 'o' => 70,
22458 'Π' | 'π' | 'P' | 'p' => 80,
22459 'Ϙ' | 'ϙ' | 'Q' | 'q' => 90, 'Ρ' | 'ρ' | 'R' | 'r' => 100,
22461 'Σ' | 'σ' | 'ς' | 'S' | 's' => 200,
22462 'Τ' | 'τ' | 'T' | 't' => 300,
22463 'Υ' | 'υ' | 'U' | 'u' | 'Y' | 'y' => 400,
22464 'Φ' | 'φ' | 'F' | 'f' => 500,
22465 'Χ' | 'χ' | 'C' | 'c' => 600,
22466 'Ψ' | 'ψ' => 700,
22467 'Ω' | 'ω' | 'W' | 'w' => 800,
22468 'Ϡ' | 'ϡ' => 900, 'J' | 'j' => 10, 'V' | 'v' => 400, _ => 0,
22472 }
22473}
22474
22475fn arabic_abjad(c: char) -> i64 {
22476 match c {
22477 'ا' | 'أ' | 'إ' | 'آ' | 'A' | 'a' => 1,
22478 'ب' | 'B' | 'b' => 2,
22479 'ج' | 'J' | 'j' | 'G' | 'g' => 3,
22480 'د' | 'D' | 'd' => 4,
22481 'ه' | 'H' | 'h' => 5,
22482 'و' | 'W' | 'w' | 'V' | 'v' => 6,
22483 'ز' | 'Z' | 'z' => 7,
22484 'ح' => 8,
22485 'ط' => 9,
22486 'ي' | 'Y' | 'y' | 'I' | 'i' => 10,
22487 'ك' | 'K' | 'k' => 20,
22488 'ل' | 'L' | 'l' => 30,
22489 'م' | 'M' | 'm' => 40,
22490 'ن' | 'N' | 'n' => 50,
22491 'س' | 'S' | 's' => 60,
22492 'ع' | 'E' | 'e' => 70,
22493 'ف' | 'F' | 'f' => 80,
22494 'ص' => 90,
22495 'ق' | 'Q' | 'q' => 100,
22496 'ر' | 'R' | 'r' => 200,
22497 'ش' => 300,
22498 'ت' | 'T' | 't' => 400,
22499 'ث' => 500,
22500 'خ' | 'X' | 'x' => 600,
22501 'ذ' => 700,
22502 'ض' => 800,
22503 'ظ' => 900,
22504 'غ' => 1000,
22505 'C' | 'c' => 600, 'O' | 'o' => 70, 'P' | 'p' => 80, 'U' | 'u' => 6, _ => 0,
22510 }
22511}
22512
22513fn register_color(interp: &mut Interpreter) {
22520 define(interp, "rgb", Some(3), |_, args| {
22526 let r = match &args[0] {
22527 Value::Int(n) => (*n).clamp(0, 255) as u8,
22528 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
22529 _ => return Err(RuntimeError::new("rgb() requires numbers")),
22530 };
22531 let g = match &args[1] {
22532 Value::Int(n) => (*n).clamp(0, 255) as u8,
22533 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
22534 _ => return Err(RuntimeError::new("rgb() requires numbers")),
22535 };
22536 let b = match &args[2] {
22537 Value::Int(n) => (*n).clamp(0, 255) as u8,
22538 Value::Float(f) => (*f as i64).clamp(0, 255) as u8,
22539 _ => return Err(RuntimeError::new("rgb() requires numbers")),
22540 };
22541 let mut map = std::collections::HashMap::new();
22542 map.insert("r".to_string(), Value::Int(r as i64));
22543 map.insert("g".to_string(), Value::Int(g as i64));
22544 map.insert("b".to_string(), Value::Int(b as i64));
22545 map.insert(
22546 "hex".to_string(),
22547 Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))),
22548 );
22549 Ok(Value::Map(Rc::new(RefCell::new(map))))
22550 });
22551
22552 define(interp, "hex_to_rgb", Some(1), |_, args| {
22554 let hex = match &args[0] {
22555 Value::String(s) => s.to_string(),
22556 _ => return Err(RuntimeError::new("hex_to_rgb requires string")),
22557 };
22558 let hex = hex.trim_start_matches('#');
22559 if hex.len() != 6 {
22560 return Err(RuntimeError::new("hex_to_rgb requires 6 character hex"));
22561 }
22562 let r = u8::from_str_radix(&hex[0..2], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
22563 let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
22564 let b = u8::from_str_radix(&hex[4..6], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
22565 let mut map = std::collections::HashMap::new();
22566 map.insert("r".to_string(), Value::Int(r as i64));
22567 map.insert("g".to_string(), Value::Int(g as i64));
22568 map.insert("b".to_string(), Value::Int(b as i64));
22569 Ok(Value::Map(Rc::new(RefCell::new(map))))
22570 });
22571
22572 define(interp, "rgb_to_hsl", Some(3), |_, args| {
22574 let r = match &args[0] {
22575 Value::Int(n) => *n as f64 / 255.0,
22576 Value::Float(f) => *f / 255.0,
22577 _ => return Err(RuntimeError::new("requires numbers")),
22578 };
22579 let g = match &args[1] {
22580 Value::Int(n) => *n as f64 / 255.0,
22581 Value::Float(f) => *f / 255.0,
22582 _ => return Err(RuntimeError::new("requires numbers")),
22583 };
22584 let b = match &args[2] {
22585 Value::Int(n) => *n as f64 / 255.0,
22586 Value::Float(f) => *f / 255.0,
22587 _ => return Err(RuntimeError::new("requires numbers")),
22588 };
22589 let max = r.max(g).max(b);
22590 let min = r.min(g).min(b);
22591 let l = (max + min) / 2.0;
22592 let (h, s) = if max == min {
22593 (0.0, 0.0)
22594 } else {
22595 let d = max - min;
22596 let s = if l > 0.5 {
22597 d / (2.0 - max - min)
22598 } else {
22599 d / (max + min)
22600 };
22601 let h = if max == r {
22602 (g - b) / d + if g < b { 6.0 } else { 0.0 }
22603 } else if max == g {
22604 (b - r) / d + 2.0
22605 } else {
22606 (r - g) / d + 4.0
22607 };
22608 (h * 60.0, s)
22609 };
22610 let mut map = std::collections::HashMap::new();
22611 map.insert("h".to_string(), Value::Float(h));
22612 map.insert("s".to_string(), Value::Float(s));
22613 map.insert("l".to_string(), Value::Float(l));
22614 Ok(Value::Map(Rc::new(RefCell::new(map))))
22615 });
22616
22617 define(interp, "complementary", Some(3), |_, args| {
22619 let r = match &args[0] {
22620 Value::Int(n) => *n as u8,
22621 _ => return Err(RuntimeError::new("requires int")),
22622 };
22623 let g = match &args[1] {
22624 Value::Int(n) => *n as u8,
22625 _ => return Err(RuntimeError::new("requires int")),
22626 };
22627 let b = match &args[2] {
22628 Value::Int(n) => *n as u8,
22629 _ => return Err(RuntimeError::new("requires int")),
22630 };
22631 let mut map = std::collections::HashMap::new();
22632 map.insert("r".to_string(), Value::Int(255 - r as i64));
22633 map.insert("g".to_string(), Value::Int(255 - g as i64));
22634 map.insert("b".to_string(), Value::Int(255 - b as i64));
22635 Ok(Value::Map(Rc::new(RefCell::new(map))))
22636 });
22637
22638 define(interp, "wu_xing", Some(1), |_, args| {
22642 let element = match &args[0] {
22643 Value::String(s) => s.to_lowercase(),
22644 _ => return Err(RuntimeError::new("wu_xing requires string")),
22645 };
22646 let (name, chinese, color, hex, direction, season, organ, emotion, planet, animal) =
22647 match element.as_str() {
22648 "wood" | "mu" | "木" => (
22649 "Wood",
22650 "木 (Mù)",
22651 "Green/Azure",
22652 "#228B22",
22653 "East",
22654 "Spring",
22655 "Liver",
22656 "Anger",
22657 "Jupiter",
22658 "Azure Dragon",
22659 ),
22660 "fire" | "huo" | "火" => (
22661 "Fire",
22662 "火 (Huǒ)",
22663 "Red",
22664 "#FF0000",
22665 "South",
22666 "Summer",
22667 "Heart",
22668 "Joy",
22669 "Mars",
22670 "Vermilion Bird",
22671 ),
22672 "earth" | "tu" | "土" => (
22673 "Earth",
22674 "土 (Tǔ)",
22675 "Yellow",
22676 "#FFDB58",
22677 "Center",
22678 "Late Summer",
22679 "Spleen",
22680 "Worry",
22681 "Saturn",
22682 "Yellow Dragon",
22683 ),
22684 "metal" | "jin" | "金" => (
22685 "Metal",
22686 "金 (Jīn)",
22687 "White/Gold",
22688 "#FFD700",
22689 "West",
22690 "Autumn",
22691 "Lung",
22692 "Grief",
22693 "Venus",
22694 "White Tiger",
22695 ),
22696 "water" | "shui" | "水" => (
22697 "Water",
22698 "水 (Shuǐ)",
22699 "Black/Blue",
22700 "#000080",
22701 "North",
22702 "Winter",
22703 "Kidney",
22704 "Fear",
22705 "Mercury",
22706 "Black Tortoise",
22707 ),
22708 _ => {
22709 return Err(RuntimeError::new(
22710 "Unknown element. Use wood/fire/earth/metal/water",
22711 ))
22712 }
22713 };
22714 let mut map = std::collections::HashMap::new();
22715 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
22716 map.insert(
22717 "chinese".to_string(),
22718 Value::String(Rc::new(chinese.to_string())),
22719 );
22720 map.insert(
22721 "color".to_string(),
22722 Value::String(Rc::new(color.to_string())),
22723 );
22724 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
22725 map.insert(
22726 "direction".to_string(),
22727 Value::String(Rc::new(direction.to_string())),
22728 );
22729 map.insert(
22730 "season".to_string(),
22731 Value::String(Rc::new(season.to_string())),
22732 );
22733 map.insert(
22734 "organ".to_string(),
22735 Value::String(Rc::new(organ.to_string())),
22736 );
22737 map.insert(
22738 "emotion".to_string(),
22739 Value::String(Rc::new(emotion.to_string())),
22740 );
22741 map.insert(
22742 "planet".to_string(),
22743 Value::String(Rc::new(planet.to_string())),
22744 );
22745 map.insert(
22746 "guardian".to_string(),
22747 Value::String(Rc::new(animal.to_string())),
22748 );
22749 Ok(Value::Map(Rc::new(RefCell::new(map))))
22750 });
22751
22752 define(interp, "chakra_color", Some(1), |_, args| {
22756 let chakra = match &args[0] {
22757 Value::String(s) => s.to_lowercase(),
22758 Value::Int(n) => n.to_string(),
22759 _ => return Err(RuntimeError::new("chakra_color requires string or number")),
22760 };
22761 let (name, sanskrit, color, hex, location, freq, element, mantra) = match chakra.as_str() {
22762 "root" | "muladhara" | "1" => (
22763 "Root",
22764 "मूलाधार",
22765 "Red",
22766 "#FF0000",
22767 "Base of spine",
22768 396.0,
22769 "Earth",
22770 "LAM",
22771 ),
22772 "sacral" | "svadhisthana" | "2" => (
22773 "Sacral",
22774 "स्वाधिष्ठान",
22775 "Orange",
22776 "#FF7F00",
22777 "Below navel",
22778 417.0,
22779 "Water",
22780 "VAM",
22781 ),
22782 "solar" | "manipura" | "3" => (
22783 "Solar Plexus",
22784 "मणिपूर",
22785 "Yellow",
22786 "#FFFF00",
22787 "Stomach",
22788 528.0,
22789 "Fire",
22790 "RAM",
22791 ),
22792 "heart" | "anahata" | "4" => (
22793 "Heart",
22794 "अनाहत",
22795 "Green",
22796 "#00FF00",
22797 "Chest",
22798 639.0,
22799 "Air",
22800 "YAM",
22801 ),
22802 "throat" | "vishuddha" | "5" => (
22803 "Throat",
22804 "विशुद्ध",
22805 "Blue",
22806 "#00BFFF",
22807 "Throat",
22808 741.0,
22809 "Ether",
22810 "HAM",
22811 ),
22812 "third_eye" | "ajna" | "6" => (
22813 "Third Eye",
22814 "आज्ञा",
22815 "Indigo",
22816 "#4B0082",
22817 "Forehead",
22818 852.0,
22819 "Light",
22820 "OM",
22821 ),
22822 "crown" | "sahasrara" | "7" => (
22823 "Crown",
22824 "सहस्रार",
22825 "Violet",
22826 "#8B00FF",
22827 "Top of head",
22828 963.0,
22829 "Thought",
22830 "Silence",
22831 ),
22832 _ => {
22833 return Err(RuntimeError::new(
22834 "Unknown chakra. Use root/sacral/solar/heart/throat/third_eye/crown or 1-7",
22835 ))
22836 }
22837 };
22838 let mut map = std::collections::HashMap::new();
22839 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
22840 map.insert(
22841 "sanskrit".to_string(),
22842 Value::String(Rc::new(sanskrit.to_string())),
22843 );
22844 map.insert(
22845 "color".to_string(),
22846 Value::String(Rc::new(color.to_string())),
22847 );
22848 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
22849 map.insert(
22850 "location".to_string(),
22851 Value::String(Rc::new(location.to_string())),
22852 );
22853 map.insert("frequency_hz".to_string(), Value::Float(freq));
22854 map.insert(
22855 "element".to_string(),
22856 Value::String(Rc::new(element.to_string())),
22857 );
22858 map.insert(
22859 "mantra".to_string(),
22860 Value::String(Rc::new(mantra.to_string())),
22861 );
22862 Ok(Value::Map(Rc::new(RefCell::new(map))))
22863 });
22864
22865 define(interp, "maya_direction", Some(1), |_, args| {
22869 let dir = match &args[0] {
22870 Value::String(s) => s.to_lowercase(),
22871 _ => return Err(RuntimeError::new("requires string")),
22872 };
22873 let (direction, yucatec, color, hex, deity, meaning) = match dir.as_str() {
22874 "east" | "lakin" => (
22875 "East",
22876 "Lak'in",
22877 "Red",
22878 "#FF0000",
22879 "Chac (Red)",
22880 "Sunrise, new beginnings",
22881 ),
22882 "north" | "xaman" => (
22883 "North",
22884 "Xaman",
22885 "White",
22886 "#FFFFFF",
22887 "Chac (White)",
22888 "Ancestors, death",
22889 ),
22890 "west" | "chikin" => (
22891 "West",
22892 "Chik'in",
22893 "Black",
22894 "#000000",
22895 "Chac (Black)",
22896 "Sunset, completion",
22897 ),
22898 "south" | "nohol" => (
22899 "South",
22900 "Nohol",
22901 "Yellow",
22902 "#FFFF00",
22903 "Chac (Yellow)",
22904 "Maize, abundance",
22905 ),
22906 "center" | "yax" => (
22907 "Center",
22908 "Yax",
22909 "Green/Blue",
22910 "#00CED1",
22911 "World Tree",
22912 "Balance",
22913 ),
22914 _ => {
22915 return Err(RuntimeError::new(
22916 "Unknown direction. Use east/north/west/south/center",
22917 ))
22918 }
22919 };
22920 let mut map = std::collections::HashMap::new();
22921 map.insert(
22922 "direction".to_string(),
22923 Value::String(Rc::new(direction.to_string())),
22924 );
22925 map.insert(
22926 "yucatec".to_string(),
22927 Value::String(Rc::new(yucatec.to_string())),
22928 );
22929 map.insert(
22930 "color".to_string(),
22931 Value::String(Rc::new(color.to_string())),
22932 );
22933 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
22934 map.insert(
22935 "deity".to_string(),
22936 Value::String(Rc::new(deity.to_string())),
22937 );
22938 map.insert(
22939 "meaning".to_string(),
22940 Value::String(Rc::new(meaning.to_string())),
22941 );
22942 Ok(Value::Map(Rc::new(RefCell::new(map))))
22943 });
22944
22945 define(interp, "orisha_color", Some(1), |_, args| {
22949 let orisha = match &args[0] {
22950 Value::String(s) => s.to_lowercase(),
22951 _ => return Err(RuntimeError::new("requires string")),
22952 };
22953 let (name, colors, hex, domain, day, number) = match orisha.as_str() {
22954 "obatala" | "oxala" => (
22955 "Obatalá",
22956 "White, silver",
22957 "#FFFFFF",
22958 "Creation, purity, wisdom",
22959 "Sunday",
22960 8,
22961 ),
22962 "yemoja" | "yemanja" => (
22963 "Yemọja",
22964 "Blue, white",
22965 "#4169E1",
22966 "Ocean, motherhood",
22967 "Saturday",
22968 7,
22969 ),
22970 "oshun" | "oxum" => (
22971 "Ọṣun",
22972 "Yellow, gold",
22973 "#FFD700",
22974 "Rivers, love, fertility",
22975 "Saturday",
22976 5,
22977 ),
22978 "shango" | "xango" => (
22979 "Ṣàngó",
22980 "Red, white",
22981 "#FF0000",
22982 "Thunder, fire, justice",
22983 "Wednesday",
22984 6,
22985 ),
22986 "ogun" | "ogum" => (
22987 "Ògún",
22988 "Green, black",
22989 "#006400",
22990 "Iron, war, labor",
22991 "Tuesday",
22992 7,
22993 ),
22994 "oya" | "iansa" => (
22995 "Ọya",
22996 "Brown, purple",
22997 "#800020",
22998 "Wind, storms, change",
22999 "Wednesday",
23000 9,
23001 ),
23002 "eshu" | "exu" => (
23003 "Èṣù",
23004 "Red, black",
23005 "#8B0000",
23006 "Crossroads, messages",
23007 "Monday",
23008 3,
23009 ),
23010 _ => {
23011 return Err(RuntimeError::new(
23012 "Unknown Orisha. Use obatala/yemoja/oshun/shango/ogun/oya/eshu",
23013 ))
23014 }
23015 };
23016 let mut map = std::collections::HashMap::new();
23017 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23018 map.insert(
23019 "colors".to_string(),
23020 Value::String(Rc::new(colors.to_string())),
23021 );
23022 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23023 map.insert(
23024 "domain".to_string(),
23025 Value::String(Rc::new(domain.to_string())),
23026 );
23027 map.insert("day".to_string(), Value::String(Rc::new(day.to_string())));
23028 map.insert("number".to_string(), Value::Int(number));
23029 Ok(Value::Map(Rc::new(RefCell::new(map))))
23030 });
23031
23032 define(interp, "nihon_iro", Some(1), |_, args| {
23036 let color = match &args[0] {
23037 Value::String(s) => s.to_lowercase(),
23038 _ => return Err(RuntimeError::new("requires string")),
23039 };
23040 let (name, japanese, hex, meaning, season) = match color.as_str() {
23041 "sakura" => (
23042 "Sakura Pink",
23043 "桜色",
23044 "#FFB7C5",
23045 "Cherry blossoms, transience",
23046 "Spring",
23047 ),
23048 "fuji" => (
23049 "Wisteria",
23050 "藤色",
23051 "#C9A0DC",
23052 "Elegance, nobility",
23053 "Spring",
23054 ),
23055 "moegi" => (
23056 "Young Green",
23057 "萌黄",
23058 "#AACF53",
23059 "New growth, freshness",
23060 "Spring",
23061 ),
23062 "ai" => ("Indigo", "藍色", "#004D99", "Protection, depth", "All"),
23063 "akane" => ("Madder Red", "茜色", "#CF3A24", "Sunset, passion", "Autumn"),
23064 "shiro" => ("White", "白", "#FFFFFF", "Purity, death, sacred", "Winter"),
23065 "kuro" => ("Black", "黒", "#000000", "Formality, mystery", "All"),
23066 "aka" => ("Red", "赤", "#D7003A", "Life force, celebration", "All"),
23067 "murasaki" => ("Purple", "紫", "#884898", "Nobility, spirituality", "All"),
23068 _ => {
23069 return Err(RuntimeError::new(
23070 "Unknown color. Try: sakura/fuji/moegi/ai/akane/shiro/kuro/aka/murasaki",
23071 ))
23072 }
23073 };
23074 let mut map = std::collections::HashMap::new();
23075 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23076 map.insert(
23077 "japanese".to_string(),
23078 Value::String(Rc::new(japanese.to_string())),
23079 );
23080 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23081 map.insert(
23082 "meaning".to_string(),
23083 Value::String(Rc::new(meaning.to_string())),
23084 );
23085 map.insert(
23086 "season".to_string(),
23087 Value::String(Rc::new(season.to_string())),
23088 );
23089 Ok(Value::Map(Rc::new(RefCell::new(map))))
23090 });
23091
23092 define(interp, "islamic_color", Some(1), |_, args| {
23096 let color = match &args[0] {
23097 Value::String(s) => s.to_lowercase(),
23098 _ => return Err(RuntimeError::new("requires string")),
23099 };
23100 let (name, arabic, hex, meaning, usage) = match color.as_str() {
23101 "green" | "akhdar" => (
23102 "Green",
23103 "أخضر",
23104 "#00FF00",
23105 "Paradise, Prophet, life",
23106 "Mosques, Quran, flags",
23107 ),
23108 "white" | "abyad" => (
23109 "White",
23110 "أبيض",
23111 "#FFFFFF",
23112 "Purity, peace, ihram",
23113 "Pilgrimage, burial",
23114 ),
23115 "black" | "aswad" => (
23116 "Black",
23117 "أسود",
23118 "#000000",
23119 "Modesty, Kaaba",
23120 "Kiswah, abaya",
23121 ),
23122 "gold" | "dhahabi" => (
23123 "Gold",
23124 "ذهبي",
23125 "#FFD700",
23126 "Paradise, divine light",
23127 "Calligraphy, decoration",
23128 ),
23129 "blue" | "azraq" => (
23130 "Blue",
23131 "أزرق",
23132 "#0000CD",
23133 "Protection, heaven",
23134 "Tiles, evil eye",
23135 ),
23136 _ => {
23137 return Err(RuntimeError::new(
23138 "Unknown color. Use green/white/black/gold/blue",
23139 ))
23140 }
23141 };
23142 let mut map = std::collections::HashMap::new();
23143 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23144 map.insert(
23145 "arabic".to_string(),
23146 Value::String(Rc::new(arabic.to_string())),
23147 );
23148 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23149 map.insert(
23150 "meaning".to_string(),
23151 Value::String(Rc::new(meaning.to_string())),
23152 );
23153 map.insert(
23154 "usage".to_string(),
23155 Value::String(Rc::new(usage.to_string())),
23156 );
23157 Ok(Value::Map(Rc::new(RefCell::new(map))))
23158 });
23159
23160 define(interp, "thai_day_color", Some(1), |_, args| {
23164 let day = match &args[0] {
23165 Value::String(s) => s.to_lowercase(),
23166 Value::Int(n) => n.to_string(),
23167 _ => return Err(RuntimeError::new("requires string or number")),
23168 };
23169 let (day_name, thai, color, hex, deity) = match day.as_str() {
23170 "sunday" | "0" => ("Sunday", "วันอาทิตย์", "Red", "#FF0000", "Surya"),
23171 "monday" | "1" => ("Monday", "วันจันทร์", "Yellow", "#FFFF00", "Chandra"),
23172 "tuesday" | "2" => ("Tuesday", "วันอังคาร", "Pink", "#FFC0CB", "Mangala"),
23173 "wednesday" | "3" => ("Wednesday", "วันพุธ", "Green", "#00FF00", "Budha"),
23174 "thursday" | "4" => ("Thursday", "วันพฤหัสบดี", "Orange", "#FFA500", "Brihaspati"),
23175 "friday" | "5" => ("Friday", "วันศุกร์", "Blue", "#00BFFF", "Shukra"),
23176 "saturday" | "6" => ("Saturday", "วันเสาร์", "Purple", "#800080", "Shani"),
23177 _ => return Err(RuntimeError::new("Unknown day. Use sunday-saturday or 0-6")),
23178 };
23179 let mut map = std::collections::HashMap::new();
23180 map.insert(
23181 "day".to_string(),
23182 Value::String(Rc::new(day_name.to_string())),
23183 );
23184 map.insert("thai".to_string(), Value::String(Rc::new(thai.to_string())));
23185 map.insert(
23186 "color".to_string(),
23187 Value::String(Rc::new(color.to_string())),
23188 );
23189 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23190 map.insert(
23191 "deity".to_string(),
23192 Value::String(Rc::new(deity.to_string())),
23193 );
23194 Ok(Value::Map(Rc::new(RefCell::new(map))))
23195 });
23196
23197 define(interp, "aboriginal_color", Some(1), |_, args| {
23201 let color = match &args[0] {
23202 Value::String(s) => s.to_lowercase(),
23203 _ => return Err(RuntimeError::new("requires string")),
23204 };
23205 let (name, hex, meaning, source, dreamtime) = match color.as_str() {
23206 "red" | "ochre" => (
23207 "Red Ochre",
23208 "#CC5500",
23209 "Earth, blood, ceremony",
23210 "Hematite",
23211 "Ancestral beings",
23212 ),
23213 "yellow" => (
23214 "Yellow Ochre",
23215 "#D4A017",
23216 "Sun, healing",
23217 "Limonite",
23218 "Sun's journey",
23219 ),
23220 "white" => (
23221 "White",
23222 "#FFFFFF",
23223 "Sky, spirits, mourning",
23224 "Kaolin",
23225 "Sky beings",
23226 ),
23227 "black" => (
23228 "Black",
23229 "#000000",
23230 "Night, formality",
23231 "Charcoal",
23232 "Night, men's business",
23233 ),
23234 "brown" => (
23235 "Brown",
23236 "#8B4513",
23237 "Earth, land",
23238 "Earth pigments",
23239 "Country, connection",
23240 ),
23241 _ => {
23242 return Err(RuntimeError::new(
23243 "Unknown color. Use red/yellow/white/black/brown",
23244 ))
23245 }
23246 };
23247 let mut map = std::collections::HashMap::new();
23248 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23249 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23250 map.insert(
23251 "meaning".to_string(),
23252 Value::String(Rc::new(meaning.to_string())),
23253 );
23254 map.insert(
23255 "source".to_string(),
23256 Value::String(Rc::new(source.to_string())),
23257 );
23258 map.insert(
23259 "dreamtime".to_string(),
23260 Value::String(Rc::new(dreamtime.to_string())),
23261 );
23262 Ok(Value::Map(Rc::new(RefCell::new(map))))
23263 });
23264
23265 define(interp, "celtic_color", Some(1), |_, args| {
23269 let color = match &args[0] {
23270 Value::String(s) => s.to_lowercase(),
23271 _ => return Err(RuntimeError::new("requires string")),
23272 };
23273 let (name, gaelic, hex, meaning, element) = match color.as_str() {
23274 "green" => (
23275 "Green",
23276 "Glas",
23277 "#228B22",
23278 "Nature, fairies, Otherworld",
23279 "Earth",
23280 ),
23281 "white" => ("White", "Bán", "#FFFFFF", "Purity, spirits", "Air"),
23282 "red" => ("Red", "Dearg", "#FF0000", "War, courage, blood", "Fire"),
23283 "black" => (
23284 "Black",
23285 "Dubh",
23286 "#000000",
23287 "Otherworld, death, rebirth",
23288 "Water",
23289 ),
23290 "gold" => ("Gold", "Órga", "#FFD700", "Sun, sovereignty, Lugh", "Fire"),
23291 "silver" => (
23292 "Silver",
23293 "Airgid",
23294 "#C0C0C0",
23295 "Moon, feminine, intuition",
23296 "Water",
23297 ),
23298 _ => {
23299 return Err(RuntimeError::new(
23300 "Unknown color. Use green/white/red/black/gold/silver",
23301 ))
23302 }
23303 };
23304 let mut map = std::collections::HashMap::new();
23305 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23306 map.insert(
23307 "gaelic".to_string(),
23308 Value::String(Rc::new(gaelic.to_string())),
23309 );
23310 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23311 map.insert(
23312 "meaning".to_string(),
23313 Value::String(Rc::new(meaning.to_string())),
23314 );
23315 map.insert(
23316 "element".to_string(),
23317 Value::String(Rc::new(element.to_string())),
23318 );
23319 Ok(Value::Map(Rc::new(RefCell::new(map))))
23320 });
23321
23322 define(interp, "kente_color", Some(1), |_, args| {
23326 let color = match &args[0] {
23327 Value::String(s) => s.to_lowercase(),
23328 _ => return Err(RuntimeError::new("requires string")),
23329 };
23330 let (name, twi, hex, meaning) = match color.as_str() {
23331 "gold" | "yellow" => ("Gold", "Sika Kɔkɔɔ", "#FFD700", "Royalty, wealth, glory"),
23332 "green" => ("Green", "Ahabammono", "#228B22", "Growth, renewal, harvest"),
23333 "blue" => ("Blue", "Bruu", "#0000CD", "Peace, harmony, love"),
23334 "red" => ("Red", "Kɔkɔɔ", "#FF0000", "Blood, sacrifice, power"),
23335 "black" => ("Black", "Tuntum", "#000000", "Maturation, ancestors"),
23336 "white" => ("White", "Fitaa", "#FFFFFF", "Purification, virtue, joy"),
23337 "maroon" => ("Maroon", "Borɔnɔ", "#800000", "Earth, healing, protection"),
23338 _ => {
23339 return Err(RuntimeError::new(
23340 "Unknown color. Use gold/green/blue/red/black/white/maroon",
23341 ))
23342 }
23343 };
23344 let mut map = std::collections::HashMap::new();
23345 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23346 map.insert("twi".to_string(), Value::String(Rc::new(twi.to_string())));
23347 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23348 map.insert(
23349 "meaning".to_string(),
23350 Value::String(Rc::new(meaning.to_string())),
23351 );
23352 Ok(Value::Map(Rc::new(RefCell::new(map))))
23353 });
23354
23355 define(interp, "hindu_color", Some(1), |_, args| {
23359 let color = match &args[0] {
23360 Value::String(s) => s.to_lowercase(),
23361 _ => return Err(RuntimeError::new("requires string")),
23362 };
23363 let (name, hindi, hex, meaning, deities) = match color.as_str() {
23364 "red" | "lal" => (
23365 "Red",
23366 "लाल",
23367 "#FF0000",
23368 "Purity, fertility, love",
23369 "Durga, Lakshmi",
23370 ),
23371 "orange" | "saffron" => (
23372 "Saffron",
23373 "केसरी",
23374 "#FF6600",
23375 "Sacred, renunciation",
23376 "Hanuman",
23377 ),
23378 "yellow" => (
23379 "Yellow",
23380 "पीला",
23381 "#FFFF00",
23382 "Knowledge, learning",
23383 "Vishnu, Saraswati",
23384 ),
23385 "green" => ("Green", "हरा", "#008000", "Life, happiness", "Krishna"),
23386 "white" => (
23387 "White",
23388 "सफ़ेद",
23389 "#FFFFFF",
23390 "Purity, mourning",
23391 "Saraswati, Shiva",
23392 ),
23393 "blue" => (
23394 "Blue",
23395 "नीला",
23396 "#0000FF",
23397 "Divinity, infinity",
23398 "Krishna, Vishnu",
23399 ),
23400 "black" => (
23401 "Black",
23402 "काला",
23403 "#000000",
23404 "Protection from evil",
23405 "Kali, Shani",
23406 ),
23407 _ => {
23408 return Err(RuntimeError::new(
23409 "Unknown color. Use red/orange/yellow/green/white/blue/black",
23410 ))
23411 }
23412 };
23413 let mut map = std::collections::HashMap::new();
23414 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23415 map.insert(
23416 "hindi".to_string(),
23417 Value::String(Rc::new(hindi.to_string())),
23418 );
23419 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23420 map.insert(
23421 "meaning".to_string(),
23422 Value::String(Rc::new(meaning.to_string())),
23423 );
23424 map.insert(
23425 "deities".to_string(),
23426 Value::String(Rc::new(deities.to_string())),
23427 );
23428 Ok(Value::Map(Rc::new(RefCell::new(map))))
23429 });
23430
23431 define(interp, "emotion_color", Some(2), |_, args| {
23435 let emotion = match &args[0] {
23436 Value::String(s) => s.to_lowercase(),
23437 _ => return Err(RuntimeError::new("requires string")),
23438 };
23439 let culture = match &args[1] {
23440 Value::String(s) => s.to_lowercase(),
23441 _ => return Err(RuntimeError::new("requires string")),
23442 };
23443 let (hex, name, reasoning) = match (emotion.as_str(), culture.as_str()) {
23444 ("joy", "western") | ("happy", "western") => {
23445 ("#FFD700", "Gold", "Sunshine = happiness")
23446 }
23447 ("joy", "chinese") | ("happy", "chinese") => ("#FF0000", "Red", "红 = luck, joy"),
23448 ("joy", "japanese") => ("#FFB7C5", "Sakura", "Cherry blossom = fleeting joy"),
23449 ("sadness", "western") | ("sad", "western") => ("#0000CD", "Blue", "'Feeling blue'"),
23450 ("sadness", "chinese") | ("sad", "chinese") => ("#FFFFFF", "White", "白 = mourning"),
23451 ("sadness", "indian") => ("#FFFFFF", "White", "सफ़ेद = mourning"),
23452 ("anger", _) => ("#FF0000", "Red", "Universal heat/fire"),
23453 ("love", "western") => ("#FF69B4", "Pink", "Valentine's hearts"),
23454 ("love", "chinese") | ("love", "indian") => ("#FF0000", "Red", "Red = marriage, love"),
23455 ("peace", "western") => ("#ADD8E6", "Light Blue", "Sky, serenity"),
23456 ("peace", "islamic") => ("#00FF00", "Green", "السلام = paradise"),
23457 ("fear", _) => ("#4B0082", "Indigo", "Deep, mysterious"),
23458 (_, _) => ("#808080", "Grey", "Neutral"),
23459 };
23460 let r = u8::from_str_radix(&hex[1..3], 16).unwrap_or(128);
23461 let g = u8::from_str_radix(&hex[3..5], 16).unwrap_or(128);
23462 let b = u8::from_str_radix(&hex[5..7], 16).unwrap_or(128);
23463 let mut map = std::collections::HashMap::new();
23464 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
23465 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
23466 map.insert("r".to_string(), Value::Int(r as i64));
23467 map.insert("g".to_string(), Value::Int(g as i64));
23468 map.insert("b".to_string(), Value::Int(b as i64));
23469 map.insert(
23470 "reasoning".to_string(),
23471 Value::String(Rc::new(reasoning.to_string())),
23472 );
23473 Ok(Value::Map(Rc::new(RefCell::new(map))))
23474 });
23475
23476 define(interp, "synesthesia", Some(2), |_, args| {
23478 let culture = match &args[1] {
23479 Value::String(s) => s.to_lowercase(),
23480 _ => return Err(RuntimeError::new("requires culture string")),
23481 };
23482 let (r, g, b, emotion, freq) = match &args[0] {
23483 Value::String(s) => match s.to_lowercase().as_str() {
23484 "joy" | "happy" => (255u8, 215u8, 0u8, "joy", 528.0),
23485 "sadness" | "sad" => (0, 0, 139, "sadness", 396.0),
23486 "anger" => (255, 0, 0, "anger", 417.0),
23487 "fear" => (75, 0, 130, "fear", 369.0),
23488 "love" => (255, 105, 180, "love", 639.0),
23489 "peace" => (135, 206, 235, "peace", 741.0),
23490 _ => (128, 128, 128, "neutral", 432.0),
23491 },
23492 Value::Int(n) => (128, 128, 255, "resonance", *n as f64),
23493 Value::Float(f) => (128, 128, 255, "resonance", *f),
23494 _ => (128, 128, 128, "neutral", 432.0),
23495 };
23496 let cultural_meaning = match culture.as_str() {
23497 "chinese" if r > 200 && g < 100 => "luck/joy (红)",
23498 "japanese" if r > 200 && g < 100 => "vitality (赤)",
23499 "indian" if r > 200 && g < 100 => "shakti/auspicious",
23500 _ => "universal resonance",
23501 };
23502 let chakra = if r > 200 && g < 100 {
23503 "Root"
23504 } else if g > 200 {
23505 "Heart"
23506 } else if b > 200 {
23507 "Throat"
23508 } else {
23509 "Crown"
23510 };
23511 let wu_xing = if r > 200 && g < 100 {
23512 "Fire (火)"
23513 } else if g > 200 {
23514 "Wood (木)"
23515 } else if b > 200 {
23516 "Water (水)"
23517 } else {
23518 "Metal (金)"
23519 };
23520 let mut map = std::collections::HashMap::new();
23521 let mut color_map = std::collections::HashMap::new();
23522 color_map.insert("r".to_string(), Value::Int(r as i64));
23523 color_map.insert("g".to_string(), Value::Int(g as i64));
23524 color_map.insert("b".to_string(), Value::Int(b as i64));
23525 color_map.insert(
23526 "hex".to_string(),
23527 Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))),
23528 );
23529 map.insert(
23530 "color".to_string(),
23531 Value::Map(Rc::new(RefCell::new(color_map))),
23532 );
23533 map.insert(
23534 "emotion".to_string(),
23535 Value::String(Rc::new(emotion.to_string())),
23536 );
23537 map.insert("frequency".to_string(), Value::Float(freq));
23538 map.insert(
23539 "cultural_meaning".to_string(),
23540 Value::String(Rc::new(cultural_meaning.to_string())),
23541 );
23542 map.insert(
23543 "chakra".to_string(),
23544 Value::String(Rc::new(chakra.to_string())),
23545 );
23546 map.insert(
23547 "wu_xing".to_string(),
23548 Value::String(Rc::new(wu_xing.to_string())),
23549 );
23550 Ok(Value::Map(Rc::new(RefCell::new(map))))
23551 });
23552
23553 define(interp, "color_to_sound", Some(3), |_, args| {
23555 let r = match &args[0] {
23556 Value::Int(n) => *n as f64 / 255.0,
23557 Value::Float(f) => *f / 255.0,
23558 _ => return Err(RuntimeError::new("requires numbers")),
23559 };
23560 let g = match &args[1] {
23561 Value::Int(n) => *n as f64 / 255.0,
23562 Value::Float(f) => *f / 255.0,
23563 _ => return Err(RuntimeError::new("requires numbers")),
23564 };
23565 let b = match &args[2] {
23566 Value::Int(n) => *n as f64 / 255.0,
23567 Value::Float(f) => *f / 255.0,
23568 _ => return Err(RuntimeError::new("requires numbers")),
23569 };
23570 let max = r.max(g).max(b);
23571 let min = r.min(g).min(b);
23572 let l = (max + min) / 2.0;
23573 let h = if max == min {
23574 0.0
23575 } else {
23576 let d = max - min;
23577 if max == r {
23578 (g - b) / d + if g < b { 6.0 } else { 0.0 }
23579 } else if max == g {
23580 (b - r) / d + 2.0
23581 } else {
23582 (r - g) / d + 4.0
23583 }
23584 } * 60.0;
23585 let (note, freq) = if h < 30.0 {
23586 ("C", 261.63)
23587 } else if h < 60.0 {
23588 ("G", 392.00)
23589 } else if h < 90.0 {
23590 ("D", 293.66)
23591 } else if h < 120.0 {
23592 ("A", 440.00)
23593 } else if h < 150.0 {
23594 ("E", 329.63)
23595 } else if h < 180.0 {
23596 ("B", 493.88)
23597 } else if h < 210.0 {
23598 ("F#", 369.99)
23599 } else if h < 240.0 {
23600 ("Db", 277.18)
23601 } else if h < 270.0 {
23602 ("Ab", 415.30)
23603 } else if h < 300.0 {
23604 ("Eb", 311.13)
23605 } else if h < 330.0 {
23606 ("Bb", 466.16)
23607 } else {
23608 ("F", 349.23)
23609 };
23610 let octave_shift = ((l - 0.5) * 4.0).round() as i32;
23611 let adjusted_freq = freq * 2.0_f64.powi(octave_shift);
23612 let mut map = std::collections::HashMap::new();
23613 map.insert("note".to_string(), Value::String(Rc::new(note.to_string())));
23614 map.insert("frequency".to_string(), Value::Float(adjusted_freq));
23615 map.insert("hue".to_string(), Value::Float(h));
23616 Ok(Value::Map(Rc::new(RefCell::new(map))))
23617 });
23618
23619 define(interp, "contrast_ratio", Some(6), |_, args| {
23621 fn lum(r: f64, g: f64, b: f64) -> f64 {
23622 fn ch(c: f64) -> f64 {
23623 let c = c / 255.0;
23624 if c <= 0.03928 {
23625 c / 12.92
23626 } else {
23627 ((c + 0.055) / 1.055).powf(2.4)
23628 }
23629 }
23630 0.2126 * ch(r) + 0.7152 * ch(g) + 0.0722 * ch(b)
23631 }
23632 let r1 = match &args[0] {
23633 Value::Int(n) => *n as f64,
23634 Value::Float(f) => *f,
23635 _ => return Err(RuntimeError::new("requires numbers")),
23636 };
23637 let g1 = match &args[1] {
23638 Value::Int(n) => *n as f64,
23639 Value::Float(f) => *f,
23640 _ => return Err(RuntimeError::new("requires numbers")),
23641 };
23642 let b1 = match &args[2] {
23643 Value::Int(n) => *n as f64,
23644 Value::Float(f) => *f,
23645 _ => return Err(RuntimeError::new("requires numbers")),
23646 };
23647 let r2 = match &args[3] {
23648 Value::Int(n) => *n as f64,
23649 Value::Float(f) => *f,
23650 _ => return Err(RuntimeError::new("requires numbers")),
23651 };
23652 let g2 = match &args[4] {
23653 Value::Int(n) => *n as f64,
23654 Value::Float(f) => *f,
23655 _ => return Err(RuntimeError::new("requires numbers")),
23656 };
23657 let b2 = match &args[5] {
23658 Value::Int(n) => *n as f64,
23659 Value::Float(f) => *f,
23660 _ => return Err(RuntimeError::new("requires numbers")),
23661 };
23662 let l1 = lum(r1, g1, b1);
23663 let l2 = lum(r2, g2, b2);
23664 let ratio = if l1 > l2 {
23665 (l1 + 0.05) / (l2 + 0.05)
23666 } else {
23667 (l2 + 0.05) / (l1 + 0.05)
23668 };
23669 let mut map = std::collections::HashMap::new();
23670 map.insert("ratio".to_string(), Value::Float(ratio));
23671 map.insert("aa_normal".to_string(), Value::Bool(ratio >= 4.5));
23672 map.insert("aa_large".to_string(), Value::Bool(ratio >= 3.0));
23673 map.insert("aaa_normal".to_string(), Value::Bool(ratio >= 7.0));
23674 Ok(Value::Map(Rc::new(RefCell::new(map))))
23675 });
23676}
23677
23678fn register_protocol(interp: &mut Interpreter) {
23686 define(interp, "protocol_info", Some(0), |_, _args| {
23688 let mut map = std::collections::HashMap::new();
23689 map.insert(
23690 "http".to_string(),
23691 Value::Map(Rc::new(RefCell::new({
23692 let mut m = std::collections::HashMap::new();
23693 m.insert(
23694 "name".to_string(),
23695 Value::String(Rc::new("HTTP".to_string())),
23696 );
23697 m.insert(
23698 "versions".to_string(),
23699 Value::Array(Rc::new(RefCell::new(vec![
23700 Value::String(Rc::new("1.1".to_string())),
23701 Value::String(Rc::new("2".to_string())),
23702 ]))),
23703 );
23704 m.insert(
23705 "methods".to_string(),
23706 Value::Array(Rc::new(RefCell::new(vec![
23707 Value::String(Rc::new("GET".to_string())),
23708 Value::String(Rc::new("POST".to_string())),
23709 Value::String(Rc::new("PUT".to_string())),
23710 Value::String(Rc::new("DELETE".to_string())),
23711 Value::String(Rc::new("PATCH".to_string())),
23712 Value::String(Rc::new("HEAD".to_string())),
23713 Value::String(Rc::new("OPTIONS".to_string())),
23714 ]))),
23715 );
23716 m
23717 }))),
23718 );
23719 map.insert(
23720 "grpc".to_string(),
23721 Value::Map(Rc::new(RefCell::new({
23722 let mut m = std::collections::HashMap::new();
23723 m.insert(
23724 "name".to_string(),
23725 Value::String(Rc::new("gRPC".to_string())),
23726 );
23727 m.insert(
23728 "streaming_modes".to_string(),
23729 Value::Array(Rc::new(RefCell::new(vec![
23730 Value::String(Rc::new("unary".to_string())),
23731 Value::String(Rc::new("server_streaming".to_string())),
23732 Value::String(Rc::new("client_streaming".to_string())),
23733 Value::String(Rc::new("bidirectional".to_string())),
23734 ]))),
23735 );
23736 m
23737 }))),
23738 );
23739 map.insert(
23740 "websocket".to_string(),
23741 Value::Map(Rc::new(RefCell::new({
23742 let mut m = std::collections::HashMap::new();
23743 m.insert(
23744 "name".to_string(),
23745 Value::String(Rc::new("WebSocket".to_string())),
23746 );
23747 m.insert(
23748 "message_types".to_string(),
23749 Value::Array(Rc::new(RefCell::new(vec![
23750 Value::String(Rc::new("text".to_string())),
23751 Value::String(Rc::new("binary".to_string())),
23752 Value::String(Rc::new("ping".to_string())),
23753 Value::String(Rc::new("pong".to_string())),
23754 Value::String(Rc::new("close".to_string())),
23755 ]))),
23756 );
23757 m
23758 }))),
23759 );
23760 map.insert(
23761 "kafka".to_string(),
23762 Value::Map(Rc::new(RefCell::new({
23763 let mut m = std::collections::HashMap::new();
23764 m.insert(
23765 "name".to_string(),
23766 Value::String(Rc::new("Apache Kafka".to_string())),
23767 );
23768 m.insert(
23769 "acks".to_string(),
23770 Value::Array(Rc::new(RefCell::new(vec![
23771 Value::String(Rc::new("none".to_string())),
23772 Value::String(Rc::new("leader".to_string())),
23773 Value::String(Rc::new("all".to_string())),
23774 ]))),
23775 );
23776 m
23777 }))),
23778 );
23779 map.insert(
23780 "amqp".to_string(),
23781 Value::Map(Rc::new(RefCell::new({
23782 let mut m = std::collections::HashMap::new();
23783 m.insert(
23784 "name".to_string(),
23785 Value::String(Rc::new("AMQP".to_string())),
23786 );
23787 m.insert(
23788 "exchange_types".to_string(),
23789 Value::Array(Rc::new(RefCell::new(vec![
23790 Value::String(Rc::new("direct".to_string())),
23791 Value::String(Rc::new("fanout".to_string())),
23792 Value::String(Rc::new("topic".to_string())),
23793 Value::String(Rc::new("headers".to_string())),
23794 ]))),
23795 );
23796 m
23797 }))),
23798 );
23799 map.insert(
23800 "graphql".to_string(),
23801 Value::Map(Rc::new(RefCell::new({
23802 let mut m = std::collections::HashMap::new();
23803 m.insert(
23804 "name".to_string(),
23805 Value::String(Rc::new("GraphQL".to_string())),
23806 );
23807 m.insert(
23808 "operations".to_string(),
23809 Value::Array(Rc::new(RefCell::new(vec![
23810 Value::String(Rc::new("query".to_string())),
23811 Value::String(Rc::new("mutation".to_string())),
23812 Value::String(Rc::new("subscription".to_string())),
23813 ]))),
23814 );
23815 m
23816 }))),
23817 );
23818 Ok(Value::Map(Rc::new(RefCell::new(map))))
23819 });
23820
23821 define(interp, "http_status_text", Some(1), |_, args| {
23823 let code = match &args[0] {
23824 Value::Int(n) => *n,
23825 _ => {
23826 return Err(RuntimeError::new(
23827 "http_status_text requires integer status code",
23828 ))
23829 }
23830 };
23831 let text = match code {
23832 100 => "Continue",
23833 101 => "Switching Protocols",
23834 200 => "OK",
23835 201 => "Created",
23836 202 => "Accepted",
23837 204 => "No Content",
23838 301 => "Moved Permanently",
23839 302 => "Found",
23840 304 => "Not Modified",
23841 307 => "Temporary Redirect",
23842 308 => "Permanent Redirect",
23843 400 => "Bad Request",
23844 401 => "Unauthorized",
23845 403 => "Forbidden",
23846 404 => "Not Found",
23847 405 => "Method Not Allowed",
23848 409 => "Conflict",
23849 422 => "Unprocessable Entity",
23850 429 => "Too Many Requests",
23851 500 => "Internal Server Error",
23852 502 => "Bad Gateway",
23853 503 => "Service Unavailable",
23854 504 => "Gateway Timeout",
23855 _ => "Unknown",
23856 };
23857 Ok(Value::String(Rc::new(text.to_string())))
23858 });
23859
23860 define(interp, "http_status_type", Some(1), |_, args| {
23862 let code = match &args[0] {
23863 Value::Int(n) => *n,
23864 _ => {
23865 return Err(RuntimeError::new(
23866 "http_status_type requires integer status code",
23867 ))
23868 }
23869 };
23870 let status_type = match code {
23871 100..=199 => "informational",
23872 200..=299 => "success",
23873 300..=399 => "redirect",
23874 400..=499 => "client_error",
23875 500..=599 => "server_error",
23876 _ => "unknown",
23877 };
23878 Ok(Value::String(Rc::new(status_type.to_string())))
23879 });
23880
23881 define(interp, "grpc_status_text", Some(1), |_, args| {
23883 let code = match &args[0] {
23884 Value::Int(n) => *n,
23885 _ => {
23886 return Err(RuntimeError::new(
23887 "grpc_status_text requires integer status code",
23888 ))
23889 }
23890 };
23891 let text = match code {
23892 0 => "OK",
23893 1 => "CANCELLED",
23894 2 => "UNKNOWN",
23895 3 => "INVALID_ARGUMENT",
23896 4 => "DEADLINE_EXCEEDED",
23897 5 => "NOT_FOUND",
23898 6 => "ALREADY_EXISTS",
23899 7 => "PERMISSION_DENIED",
23900 8 => "RESOURCE_EXHAUSTED",
23901 9 => "FAILED_PRECONDITION",
23902 10 => "ABORTED",
23903 11 => "OUT_OF_RANGE",
23904 12 => "UNIMPLEMENTED",
23905 13 => "INTERNAL",
23906 14 => "UNAVAILABLE",
23907 15 => "DATA_LOSS",
23908 16 => "UNAUTHENTICATED",
23909 _ => "UNKNOWN",
23910 };
23911 Ok(Value::String(Rc::new(text.to_string())))
23912 });
23913
23914 define(interp, "url_parse", Some(1), |_, args| {
23916 let url_str = match &args[0] {
23917 Value::String(s) => s.as_str().to_string(),
23918 _ => return Err(RuntimeError::new("url_parse requires string URL")),
23919 };
23920
23921 let mut map = std::collections::HashMap::new();
23923
23924 let (scheme, rest) = if let Some(pos) = url_str.find("://") {
23926 (url_str[..pos].to_string(), &url_str[pos + 3..])
23927 } else {
23928 return Err(RuntimeError::new("Invalid URL: missing scheme"));
23929 };
23930 map.insert("scheme".to_string(), Value::String(Rc::new(scheme)));
23931
23932 let (authority, path_and_rest) = if let Some(pos) = rest.find('/') {
23934 (&rest[..pos], &rest[pos..])
23935 } else {
23936 (rest, "/")
23937 };
23938
23939 let (host, port) = if let Some(pos) = authority.rfind(':') {
23941 if let Ok(p) = authority[pos + 1..].parse::<i64>() {
23942 (authority[..pos].to_string(), Some(p))
23943 } else {
23944 (authority.to_string(), None)
23945 }
23946 } else {
23947 (authority.to_string(), None)
23948 };
23949 map.insert("host".to_string(), Value::String(Rc::new(host)));
23950 map.insert(
23951 "port".to_string(),
23952 port.map(Value::Int).unwrap_or(Value::Null),
23953 );
23954
23955 let (path, query) = if let Some(pos) = path_and_rest.find('?') {
23957 (&path_and_rest[..pos], Some(&path_and_rest[pos + 1..]))
23958 } else {
23959 (path_and_rest, None)
23960 };
23961 map.insert("path".to_string(), Value::String(Rc::new(path.to_string())));
23962 map.insert(
23963 "query".to_string(),
23964 query
23965 .map(|q| Value::String(Rc::new(q.to_string())))
23966 .unwrap_or(Value::Null),
23967 );
23968
23969 Ok(Value::Map(Rc::new(RefCell::new(map))))
23970 });
23971
23972 define(interp, "url_encode", Some(1), |_, args| {
23974 let s = match &args[0] {
23975 Value::String(s) => s.as_str(),
23976 _ => return Err(RuntimeError::new("url_encode requires string")),
23977 };
23978 let mut result = String::new();
23979 for c in s.chars() {
23980 match c {
23981 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '~' => {
23982 result.push(c);
23983 }
23984 ' ' => result.push_str("%20"),
23985 _ => {
23986 for b in c.to_string().as_bytes() {
23987 result.push_str(&format!("%{:02X}", b));
23988 }
23989 }
23990 }
23991 }
23992 Ok(Value::String(Rc::new(result)))
23993 });
23994
23995 define(interp, "url_decode", Some(1), |_, args| {
23997 let s = match &args[0] {
23998 Value::String(s) => s.as_str().to_string(),
23999 _ => return Err(RuntimeError::new("url_decode requires string")),
24000 };
24001 let mut result = Vec::new();
24002 let bytes = s.as_bytes();
24003 let mut i = 0;
24004 while i < bytes.len() {
24005 if bytes[i] == b'%' && i + 2 < bytes.len() {
24006 if let Ok(b) =
24007 u8::from_str_radix(&String::from_utf8_lossy(&bytes[i + 1..i + 3]), 16)
24008 {
24009 result.push(b);
24010 i += 3;
24011 continue;
24012 }
24013 } else if bytes[i] == b'+' {
24014 result.push(b' ');
24015 i += 1;
24016 continue;
24017 }
24018 result.push(bytes[i]);
24019 i += 1;
24020 }
24021 Ok(Value::String(Rc::new(
24022 String::from_utf8_lossy(&result).to_string(),
24023 )))
24024 });
24025
24026 define(interp, "ws_close_code_text", Some(1), |_, args| {
24028 let code = match &args[0] {
24029 Value::Int(n) => *n,
24030 _ => {
24031 return Err(RuntimeError::new(
24032 "ws_close_code_text requires integer code",
24033 ))
24034 }
24035 };
24036 let text = match code {
24037 1000 => "Normal Closure",
24038 1001 => "Going Away",
24039 1002 => "Protocol Error",
24040 1003 => "Unsupported Data",
24041 1005 => "No Status Received",
24042 1006 => "Abnormal Closure",
24043 1007 => "Invalid Payload Data",
24044 1008 => "Policy Violation",
24045 1009 => "Message Too Big",
24046 1010 => "Missing Extension",
24047 1011 => "Internal Error",
24048 1015 => "TLS Handshake Failure",
24049 _ => "Unknown",
24050 };
24051 Ok(Value::String(Rc::new(text.to_string())))
24052 });
24053
24054 define(interp, "mime_type", Some(1), |_, args| {
24056 let ext = match &args[0] {
24057 Value::String(s) => s.as_str().to_lowercase(),
24058 _ => return Err(RuntimeError::new("mime_type requires string extension")),
24059 };
24060 let ext = ext.trim_start_matches('.');
24061 let mime = match ext {
24062 "html" | "htm" => "text/html",
24063 "css" => "text/css",
24064 "js" | "mjs" => "text/javascript",
24065 "json" => "application/json",
24066 "xml" => "application/xml",
24067 "txt" => "text/plain",
24068 "csv" => "text/csv",
24069 "png" => "image/png",
24070 "jpg" | "jpeg" => "image/jpeg",
24071 "gif" => "image/gif",
24072 "svg" => "image/svg+xml",
24073 "webp" => "image/webp",
24074 "ico" => "image/x-icon",
24075 "pdf" => "application/pdf",
24076 "zip" => "application/zip",
24077 "gz" | "gzip" => "application/gzip",
24078 "mp3" => "audio/mpeg",
24079 "mp4" => "video/mp4",
24080 "webm" => "video/webm",
24081 "woff" => "font/woff",
24082 "woff2" => "font/woff2",
24083 "ttf" => "font/ttf",
24084 "otf" => "font/otf",
24085 "wasm" => "application/wasm",
24086 "proto" => "application/protobuf",
24087 _ => "application/octet-stream",
24088 };
24089 Ok(Value::String(Rc::new(mime.to_string())))
24090 });
24091
24092 define(interp, "content_type_parse", Some(1), |_, args| {
24094 let ct = match &args[0] {
24095 Value::String(s) => s.as_str().to_string(),
24096 _ => return Err(RuntimeError::new("content_type_parse requires string")),
24097 };
24098 let mut map = std::collections::HashMap::new();
24099 let parts: Vec<&str> = ct.split(';').collect();
24100 map.insert(
24101 "media_type".to_string(),
24102 Value::String(Rc::new(parts[0].trim().to_string())),
24103 );
24104
24105 let mut params = std::collections::HashMap::new();
24106 for part in parts.iter().skip(1) {
24107 let kv: Vec<&str> = part.splitn(2, '=').collect();
24108 if kv.len() == 2 {
24109 let key = kv[0].trim().to_lowercase();
24110 let value = kv[1].trim().trim_matches('"').to_string();
24111 params.insert(key, Value::String(Rc::new(value)));
24112 }
24113 }
24114 map.insert(
24115 "params".to_string(),
24116 Value::Map(Rc::new(RefCell::new(params))),
24117 );
24118 Ok(Value::Map(Rc::new(RefCell::new(map))))
24119 });
24120}
24121
24122thread_local! {
24134 static TOOL_REGISTRY: RefCell<HashMap<String, ToolDefinition>> = RefCell::new(HashMap::new());
24135 static AGENT_MEMORY: RefCell<HashMap<String, AgentSession>> = RefCell::new(HashMap::new());
24136 static STATE_MACHINES: RefCell<HashMap<String, StateMachine>> = RefCell::new(HashMap::new());
24137}
24138
24139#[derive(Clone)]
24141struct ToolDefinition {
24142 name: String,
24143 description: String,
24144 parameters: Vec<ToolParameter>,
24145 returns: String,
24146 evidence_in: Evidence,
24147 evidence_out: Evidence,
24148 handler: Option<Rc<Function>>,
24149}
24150
24151#[derive(Clone)]
24152struct ToolParameter {
24153 name: String,
24154 param_type: String,
24155 description: String,
24156 required: bool,
24157 evidence: Evidence,
24158}
24159
24160#[derive(Clone)]
24162struct AgentSession {
24163 id: String,
24164 context: HashMap<String, Value>,
24165 history: Vec<(String, String)>, created_at: u64,
24167 last_accessed: u64,
24168}
24169
24170#[derive(Clone)]
24172struct StateMachine {
24173 name: String,
24174 current_state: String,
24175 states: Vec<String>,
24176 transitions: HashMap<String, Vec<(String, String)>>, history: Vec<(String, u64)>, }
24179
24180fn register_agent_tools(interp: &mut Interpreter) {
24185 define(interp, "tool_define", None, |_interp, args| {
24188 if args.len() < 4 {
24189 return Err(RuntimeError::new(
24190 "tool_define requires at least 4 arguments: name, description, params, returns",
24191 ));
24192 }
24193
24194 let name = match &args[0] {
24195 Value::String(s) => s.as_str().to_string(),
24196 _ => return Err(RuntimeError::new("tool name must be a string")),
24197 };
24198
24199 let description = match &args[1] {
24200 Value::String(s) => s.as_str().to_string(),
24201 _ => return Err(RuntimeError::new("tool description must be a string")),
24202 };
24203
24204 let params = match &args[2] {
24206 Value::Array(arr) => {
24207 let arr = arr.borrow();
24208 let mut params = Vec::new();
24209 for param in arr.iter() {
24210 if let Value::Map(map) = param {
24211 let map = map.borrow();
24212 let param_name = map
24213 .get("name")
24214 .and_then(|v| {
24215 if let Value::String(s) = v {
24216 Some(s.as_str().to_string())
24217 } else {
24218 None
24219 }
24220 })
24221 .unwrap_or_default();
24222 let param_type = map
24223 .get("type")
24224 .and_then(|v| {
24225 if let Value::String(s) = v {
24226 Some(s.as_str().to_string())
24227 } else {
24228 None
24229 }
24230 })
24231 .unwrap_or_else(|| "any".to_string());
24232 let param_desc = map
24233 .get("description")
24234 .and_then(|v| {
24235 if let Value::String(s) = v {
24236 Some(s.as_str().to_string())
24237 } else {
24238 None
24239 }
24240 })
24241 .unwrap_or_default();
24242 let required = map
24243 .get("required")
24244 .and_then(|v| {
24245 if let Value::Bool(b) = v {
24246 Some(*b)
24247 } else {
24248 None
24249 }
24250 })
24251 .unwrap_or(true);
24252 let evidence_str = map
24253 .get("evidence")
24254 .and_then(|v| {
24255 if let Value::String(s) = v {
24256 Some(s.as_str())
24257 } else {
24258 None
24259 }
24260 })
24261 .unwrap_or("~");
24262 let evidence = match evidence_str {
24263 "!" => Evidence::Known,
24264 "?" => Evidence::Uncertain,
24265 "~" => Evidence::Reported,
24266 "‽" => Evidence::Paradox,
24267 _ => Evidence::Reported,
24268 };
24269 params.push(ToolParameter {
24270 name: param_name,
24271 param_type,
24272 description: param_desc,
24273 required,
24274 evidence,
24275 });
24276 }
24277 }
24278 params
24279 }
24280 _ => Vec::new(),
24281 };
24282
24283 let returns = match &args[3] {
24284 Value::String(s) => s.as_str().to_string(),
24285 _ => "any".to_string(),
24286 };
24287
24288 let handler = if args.len() > 4 {
24289 match &args[4] {
24290 Value::Function(f) => Some(f.clone()),
24291 _ => None,
24292 }
24293 } else {
24294 None
24295 };
24296
24297 let tool = ToolDefinition {
24298 name: name.clone(),
24299 description,
24300 parameters: params,
24301 returns,
24302 evidence_in: Evidence::Reported,
24303 evidence_out: Evidence::Reported,
24304 handler,
24305 };
24306
24307 TOOL_REGISTRY.with(|registry| {
24308 registry.borrow_mut().insert(name.clone(), tool);
24309 });
24310
24311 Ok(Value::String(Rc::new(name)))
24312 });
24313
24314 define(interp, "tool_list", Some(0), |_, _args| {
24316 let tools: Vec<Value> = TOOL_REGISTRY.with(|registry| {
24317 registry
24318 .borrow()
24319 .keys()
24320 .map(|k| Value::String(Rc::new(k.clone())))
24321 .collect()
24322 });
24323 Ok(Value::Array(Rc::new(RefCell::new(tools))))
24324 });
24325
24326 define(interp, "tool_get", Some(1), |_, args| {
24328 let name = match &args[0] {
24329 Value::String(s) => s.as_str().to_string(),
24330 _ => return Err(RuntimeError::new("tool_get requires string name")),
24331 };
24332
24333 TOOL_REGISTRY.with(|registry| {
24334 if let Some(tool) = registry.borrow().get(&name) {
24335 let mut map = HashMap::new();
24336 map.insert(
24337 "name".to_string(),
24338 Value::String(Rc::new(tool.name.clone())),
24339 );
24340 map.insert(
24341 "description".to_string(),
24342 Value::String(Rc::new(tool.description.clone())),
24343 );
24344 map.insert(
24345 "returns".to_string(),
24346 Value::String(Rc::new(tool.returns.clone())),
24347 );
24348
24349 let params: Vec<Value> = tool
24350 .parameters
24351 .iter()
24352 .map(|p| {
24353 let mut pmap = HashMap::new();
24354 pmap.insert("name".to_string(), Value::String(Rc::new(p.name.clone())));
24355 pmap.insert(
24356 "type".to_string(),
24357 Value::String(Rc::new(p.param_type.clone())),
24358 );
24359 pmap.insert(
24360 "description".to_string(),
24361 Value::String(Rc::new(p.description.clone())),
24362 );
24363 pmap.insert("required".to_string(), Value::Bool(p.required));
24364 Value::Map(Rc::new(RefCell::new(pmap)))
24365 })
24366 .collect();
24367 map.insert(
24368 "parameters".to_string(),
24369 Value::Array(Rc::new(RefCell::new(params))),
24370 );
24371
24372 Ok(Value::Map(Rc::new(RefCell::new(map))))
24373 } else {
24374 Ok(Value::Null)
24375 }
24376 })
24377 });
24378
24379 define(interp, "tool_schema", Some(1), |_, args| {
24381 let name = match &args[0] {
24382 Value::String(s) => s.as_str().to_string(),
24383 _ => return Err(RuntimeError::new("tool_schema requires string name")),
24384 };
24385
24386 TOOL_REGISTRY.with(|registry| {
24387 if let Some(tool) = registry.borrow().get(&name) {
24388 let mut schema = HashMap::new();
24390 schema.insert(
24391 "type".to_string(),
24392 Value::String(Rc::new("function".to_string())),
24393 );
24394
24395 let mut function = HashMap::new();
24396 function.insert(
24397 "name".to_string(),
24398 Value::String(Rc::new(tool.name.clone())),
24399 );
24400 function.insert(
24401 "description".to_string(),
24402 Value::String(Rc::new(tool.description.clone())),
24403 );
24404
24405 let mut params_schema = HashMap::new();
24407 params_schema.insert(
24408 "type".to_string(),
24409 Value::String(Rc::new("object".to_string())),
24410 );
24411
24412 let mut properties = HashMap::new();
24413 let mut required: Vec<Value> = Vec::new();
24414
24415 for param in &tool.parameters {
24416 let mut prop = HashMap::new();
24417 let json_type = match param.param_type.as_str() {
24418 "int" | "i64" | "i32" => "integer",
24419 "float" | "f64" | "f32" => "number",
24420 "bool" => "boolean",
24421 "string" | "str" => "string",
24422 "array" | "list" => "array",
24423 "map" | "object" => "object",
24424 _ => "string",
24425 };
24426 prop.insert(
24427 "type".to_string(),
24428 Value::String(Rc::new(json_type.to_string())),
24429 );
24430 prop.insert(
24431 "description".to_string(),
24432 Value::String(Rc::new(param.description.clone())),
24433 );
24434 properties.insert(param.name.clone(), Value::Map(Rc::new(RefCell::new(prop))));
24435
24436 if param.required {
24437 required.push(Value::String(Rc::new(param.name.clone())));
24438 }
24439 }
24440
24441 params_schema.insert(
24442 "properties".to_string(),
24443 Value::Map(Rc::new(RefCell::new(properties))),
24444 );
24445 params_schema.insert(
24446 "required".to_string(),
24447 Value::Array(Rc::new(RefCell::new(required))),
24448 );
24449
24450 function.insert(
24451 "parameters".to_string(),
24452 Value::Map(Rc::new(RefCell::new(params_schema))),
24453 );
24454 schema.insert(
24455 "function".to_string(),
24456 Value::Map(Rc::new(RefCell::new(function))),
24457 );
24458
24459 Ok(Value::Map(Rc::new(RefCell::new(schema))))
24460 } else {
24461 Err(RuntimeError::new(format!("Tool '{}' not found", name)))
24462 }
24463 })
24464 });
24465
24466 define(interp, "tool_schemas_all", Some(0), |_, _args| {
24468 let schemas: Vec<Value> = TOOL_REGISTRY.with(|registry| {
24469 registry
24470 .borrow()
24471 .values()
24472 .map(|tool| {
24473 let mut schema = HashMap::new();
24474 schema.insert(
24475 "type".to_string(),
24476 Value::String(Rc::new("function".to_string())),
24477 );
24478
24479 let mut function = HashMap::new();
24480 function.insert(
24481 "name".to_string(),
24482 Value::String(Rc::new(tool.name.clone())),
24483 );
24484 function.insert(
24485 "description".to_string(),
24486 Value::String(Rc::new(tool.description.clone())),
24487 );
24488
24489 let mut params_schema = HashMap::new();
24490 params_schema.insert(
24491 "type".to_string(),
24492 Value::String(Rc::new("object".to_string())),
24493 );
24494
24495 let mut properties = HashMap::new();
24496 let mut required: Vec<Value> = Vec::new();
24497
24498 for param in &tool.parameters {
24499 let mut prop = HashMap::new();
24500 let json_type = match param.param_type.as_str() {
24501 "int" | "i64" | "i32" => "integer",
24502 "float" | "f64" | "f32" => "number",
24503 "bool" => "boolean",
24504 _ => "string",
24505 };
24506 prop.insert(
24507 "type".to_string(),
24508 Value::String(Rc::new(json_type.to_string())),
24509 );
24510 prop.insert(
24511 "description".to_string(),
24512 Value::String(Rc::new(param.description.clone())),
24513 );
24514 properties
24515 .insert(param.name.clone(), Value::Map(Rc::new(RefCell::new(prop))));
24516 if param.required {
24517 required.push(Value::String(Rc::new(param.name.clone())));
24518 }
24519 }
24520
24521 params_schema.insert(
24522 "properties".to_string(),
24523 Value::Map(Rc::new(RefCell::new(properties))),
24524 );
24525 params_schema.insert(
24526 "required".to_string(),
24527 Value::Array(Rc::new(RefCell::new(required))),
24528 );
24529 function.insert(
24530 "parameters".to_string(),
24531 Value::Map(Rc::new(RefCell::new(params_schema))),
24532 );
24533 schema.insert(
24534 "function".to_string(),
24535 Value::Map(Rc::new(RefCell::new(function))),
24536 );
24537
24538 Value::Map(Rc::new(RefCell::new(schema)))
24539 })
24540 .collect()
24541 });
24542 Ok(Value::Array(Rc::new(RefCell::new(schemas))))
24543 });
24544
24545 define(interp, "tool_call", None, |interp, args| {
24547 if args.is_empty() {
24548 return Err(RuntimeError::new("tool_call requires at least tool name"));
24549 }
24550
24551 let name = match &args[0] {
24552 Value::String(s) => s.as_str().to_string(),
24553 _ => {
24554 return Err(RuntimeError::new(
24555 "tool_call first argument must be tool name",
24556 ))
24557 }
24558 };
24559
24560 let tool_args: Vec<Value> = args.into_iter().skip(1).collect();
24561
24562 TOOL_REGISTRY.with(|registry| {
24563 if let Some(tool) = registry.borrow().get(&name) {
24564 if let Some(handler) = &tool.handler {
24565 let result = interp.call_function(handler.as_ref(), tool_args)?;
24567 Ok(Value::Evidential {
24569 value: Box::new(result),
24570 evidence: Evidence::Reported,
24571 })
24572 } else {
24573 Err(RuntimeError::new(format!("Tool '{}' has no handler", name)))
24574 }
24575 } else {
24576 Err(RuntimeError::new(format!("Tool '{}' not found", name)))
24577 }
24578 })
24579 });
24580
24581 define(interp, "tool_remove", Some(1), |_, args| {
24583 let name = match &args[0] {
24584 Value::String(s) => s.as_str().to_string(),
24585 _ => return Err(RuntimeError::new("tool_remove requires string name")),
24586 };
24587
24588 TOOL_REGISTRY.with(|registry| {
24589 let removed = registry.borrow_mut().remove(&name).is_some();
24590 Ok(Value::Bool(removed))
24591 })
24592 });
24593
24594 define(interp, "tool_clear", Some(0), |_, _args| {
24596 TOOL_REGISTRY.with(|registry| {
24597 registry.borrow_mut().clear();
24598 });
24599 Ok(Value::Null)
24600 });
24601}
24602
24603fn register_agent_llm(interp: &mut Interpreter) {
24608 define(interp, "llm_message", Some(2), |_, args| {
24610 let role = match &args[0] {
24611 Value::String(s) => s.as_str().to_string(),
24612 _ => return Err(RuntimeError::new("llm_message role must be string")),
24613 };
24614 let content = match &args[1] {
24615 Value::String(s) => s.as_str().to_string(),
24616 _ => return Err(RuntimeError::new("llm_message content must be string")),
24617 };
24618
24619 let mut map = HashMap::new();
24620 map.insert("role".to_string(), Value::String(Rc::new(role)));
24621 map.insert("content".to_string(), Value::String(Rc::new(content)));
24622 Ok(Value::Map(Rc::new(RefCell::new(map))))
24623 });
24624
24625 define(interp, "llm_messages", None, |_, args| {
24627 let messages: Vec<Value> = args.into_iter().collect();
24628 Ok(Value::Array(Rc::new(RefCell::new(messages))))
24629 });
24630
24631 define(interp, "llm_request", None, |_, args| {
24633 if args.is_empty() {
24634 return Err(RuntimeError::new("llm_request requires provider"));
24635 }
24636
24637 let provider = match &args[0] {
24638 Value::String(s) => s.as_str().to_string(),
24639 _ => return Err(RuntimeError::new("provider must be string")),
24640 };
24641
24642 let mut request = HashMap::new();
24643 request.insert(
24644 "provider".to_string(),
24645 Value::String(Rc::new(provider.clone())),
24646 );
24647
24648 let default_model = match provider.as_str() {
24650 "openai" => "gpt-4-turbo-preview",
24651 "anthropic" | "claude" => "claude-3-opus-20240229",
24652 "google" | "gemini" => "gemini-pro",
24653 "mistral" => "mistral-large-latest",
24654 "ollama" | "local" => "llama2",
24655 _ => "gpt-4",
24656 };
24657 request.insert(
24658 "model".to_string(),
24659 Value::String(Rc::new(default_model.to_string())),
24660 );
24661
24662 for (i, arg) in args.iter().enumerate().skip(1) {
24664 if let Value::Map(map) = arg {
24665 let map = map.borrow();
24666 for (k, v) in map.iter() {
24667 request.insert(k.clone(), v.clone());
24668 }
24669 } else if i == 1 {
24670 if let Value::String(s) = arg {
24672 request.insert("model".to_string(), Value::String(s.clone()));
24673 }
24674 }
24675 }
24676
24677 if !request.contains_key("temperature") {
24679 request.insert("temperature".to_string(), Value::Float(0.7));
24680 }
24681 if !request.contains_key("max_tokens") {
24682 request.insert("max_tokens".to_string(), Value::Int(4096));
24683 }
24684
24685 Ok(Value::Map(Rc::new(RefCell::new(request))))
24686 });
24687
24688 define(interp, "llm_with_tools", Some(2), |_, args| {
24690 let request = match &args[0] {
24691 Value::Map(m) => m.clone(),
24692 _ => {
24693 return Err(RuntimeError::new(
24694 "llm_with_tools first arg must be request map",
24695 ))
24696 }
24697 };
24698
24699 let tools = match &args[1] {
24700 Value::Array(arr) => arr.clone(),
24701 _ => {
24702 return Err(RuntimeError::new(
24703 "llm_with_tools second arg must be tools array",
24704 ))
24705 }
24706 };
24707
24708 request
24709 .borrow_mut()
24710 .insert("tools".to_string(), Value::Array(tools));
24711 request.borrow_mut().insert(
24712 "tool_choice".to_string(),
24713 Value::String(Rc::new("auto".to_string())),
24714 );
24715
24716 Ok(Value::Map(request))
24717 });
24718
24719 define(interp, "llm_with_system", Some(2), |_, args| {
24721 let request = match &args[0] {
24722 Value::Map(m) => m.clone(),
24723 _ => {
24724 return Err(RuntimeError::new(
24725 "llm_with_system first arg must be request map",
24726 ))
24727 }
24728 };
24729
24730 let system = match &args[1] {
24731 Value::String(s) => s.clone(),
24732 _ => {
24733 return Err(RuntimeError::new(
24734 "llm_with_system second arg must be string",
24735 ))
24736 }
24737 };
24738
24739 request
24740 .borrow_mut()
24741 .insert("system".to_string(), Value::String(system));
24742 Ok(Value::Map(request))
24743 });
24744
24745 define(interp, "llm_with_messages", Some(2), |_, args| {
24747 let request = match &args[0] {
24748 Value::Map(m) => m.clone(),
24749 _ => {
24750 return Err(RuntimeError::new(
24751 "llm_with_messages first arg must be request map",
24752 ))
24753 }
24754 };
24755
24756 let messages = match &args[1] {
24757 Value::Array(arr) => arr.clone(),
24758 _ => {
24759 return Err(RuntimeError::new(
24760 "llm_with_messages second arg must be messages array",
24761 ))
24762 }
24763 };
24764
24765 request
24766 .borrow_mut()
24767 .insert("messages".to_string(), Value::Array(messages));
24768 Ok(Value::Map(request))
24769 });
24770
24771 define(interp, "llm_send", Some(1), |_, args| {
24774 let request = match &args[0] {
24775 Value::Map(m) => m.borrow().clone(),
24776 _ => return Err(RuntimeError::new("llm_send requires request map")),
24777 };
24778
24779 let provider = request
24780 .get("provider")
24781 .and_then(|v| {
24782 if let Value::String(s) = v {
24783 Some(s.as_str().to_string())
24784 } else {
24785 None
24786 }
24787 })
24788 .unwrap_or_else(|| "unknown".to_string());
24789
24790 let model = request
24791 .get("model")
24792 .and_then(|v| {
24793 if let Value::String(s) = v {
24794 Some(s.as_str().to_string())
24795 } else {
24796 None
24797 }
24798 })
24799 .unwrap_or_else(|| "unknown".to_string());
24800
24801 let mut response = HashMap::new();
24803 response.insert(
24804 "id".to_string(),
24805 Value::String(Rc::new(format!("msg_{}", Uuid::new_v4()))),
24806 );
24807 response.insert("provider".to_string(), Value::String(Rc::new(provider)));
24808 response.insert("model".to_string(), Value::String(Rc::new(model)));
24809 response.insert(
24810 "created".to_string(),
24811 Value::Int(
24812 std::time::SystemTime::now()
24813 .duration_since(std::time::UNIX_EPOCH)
24814 .unwrap_or_default()
24815 .as_secs() as i64,
24816 ),
24817 );
24818
24819 if request.contains_key("tools") {
24821 response.insert(
24822 "type".to_string(),
24823 Value::String(Rc::new("tool_use".to_string())),
24824 );
24825 response.insert(
24826 "tool_name".to_string(),
24827 Value::String(Rc::new("pending".to_string())),
24828 );
24829 response.insert(
24830 "tool_input".to_string(),
24831 Value::Map(Rc::new(RefCell::new(HashMap::new()))),
24832 );
24833 } else {
24834 response.insert(
24835 "type".to_string(),
24836 Value::String(Rc::new("text".to_string())),
24837 );
24838 response.insert(
24839 "content".to_string(),
24840 Value::String(Rc::new(
24841 "[LLM Response - Connect to actual API for real responses]".to_string(),
24842 )),
24843 );
24844 }
24845
24846 let mut usage = HashMap::new();
24848 usage.insert("input_tokens".to_string(), Value::Int(0));
24849 usage.insert("output_tokens".to_string(), Value::Int(0));
24850 response.insert(
24851 "usage".to_string(),
24852 Value::Map(Rc::new(RefCell::new(usage))),
24853 );
24854
24855 Ok(Value::Evidential {
24857 value: Box::new(Value::Map(Rc::new(RefCell::new(response)))),
24858 evidence: Evidence::Reported,
24859 })
24860 });
24861
24862 define(interp, "llm_parse_tool_call", Some(1), |_, args| {
24864 let response = match &args[0] {
24865 Value::Map(m) => m.borrow().clone(),
24866 Value::Evidential { value, .. } => {
24867 if let Value::Map(m) = value.as_ref() {
24868 m.borrow().clone()
24869 } else {
24870 return Err(RuntimeError::new(
24871 "llm_parse_tool_call requires map response",
24872 ));
24873 }
24874 }
24875 _ => {
24876 return Err(RuntimeError::new(
24877 "llm_parse_tool_call requires response map",
24878 ))
24879 }
24880 };
24881
24882 let resp_type = response
24883 .get("type")
24884 .and_then(|v| {
24885 if let Value::String(s) = v {
24886 Some(s.as_str().to_string())
24887 } else {
24888 None
24889 }
24890 })
24891 .unwrap_or_default();
24892
24893 if resp_type == "tool_use" {
24894 let tool_name = response
24895 .get("tool_name")
24896 .and_then(|v| {
24897 if let Value::String(s) = v {
24898 Some(s.as_str().to_string())
24899 } else {
24900 None
24901 }
24902 })
24903 .unwrap_or_default();
24904
24905 let tool_input = response.get("tool_input").cloned().unwrap_or(Value::Null);
24906
24907 let mut result = HashMap::new();
24908 result.insert("is_tool_call".to_string(), Value::Bool(true));
24909 result.insert("tool_name".to_string(), Value::String(Rc::new(tool_name)));
24910 result.insert("tool_input".to_string(), tool_input);
24911 Ok(Value::Map(Rc::new(RefCell::new(result))))
24912 } else {
24913 let mut result = HashMap::new();
24914 result.insert("is_tool_call".to_string(), Value::Bool(false));
24915 result.insert(
24916 "content".to_string(),
24917 response.get("content").cloned().unwrap_or(Value::Null),
24918 );
24919 Ok(Value::Map(Rc::new(RefCell::new(result))))
24920 }
24921 });
24922
24923 define(interp, "llm_extract", Some(2), |_, args| {
24925 let _response = match &args[0] {
24926 Value::Map(m) => m.borrow().clone(),
24927 Value::Evidential { value, .. } => {
24928 if let Value::Map(m) = value.as_ref() {
24929 m.borrow().clone()
24930 } else {
24931 return Err(RuntimeError::new("llm_extract requires response"));
24932 }
24933 }
24934 _ => return Err(RuntimeError::new("llm_extract requires response")),
24935 };
24936
24937 let _schema = match &args[1] {
24938 Value::Map(m) => m.borrow().clone(),
24939 _ => return Err(RuntimeError::new("llm_extract requires schema map")),
24940 };
24941
24942 let mut result = HashMap::new();
24945 result.insert("success".to_string(), Value::Bool(true));
24946 result.insert("data".to_string(), Value::Null);
24947 result.insert(
24948 "errors".to_string(),
24949 Value::Array(Rc::new(RefCell::new(Vec::new()))),
24950 );
24951
24952 Ok(Value::Evidential {
24953 value: Box::new(Value::Map(Rc::new(RefCell::new(result)))),
24954 evidence: Evidence::Uncertain,
24955 })
24956 });
24957
24958 define(interp, "prompt_template", Some(1), |_, args| {
24960 let template = match &args[0] {
24961 Value::String(s) => s.as_str().to_string(),
24962 _ => return Err(RuntimeError::new("prompt_template requires string")),
24963 };
24964
24965 let mut variables = Vec::new();
24967 let mut in_var = false;
24968 let mut var_name = String::new();
24969
24970 for c in template.chars() {
24971 match c {
24972 '{' if !in_var => {
24973 in_var = true;
24974 var_name.clear();
24975 }
24976 '}' if in_var => {
24977 if !var_name.is_empty() {
24978 variables.push(Value::String(Rc::new(var_name.clone())));
24979 }
24980 in_var = false;
24981 }
24982 _ if in_var => {
24983 var_name.push(c);
24984 }
24985 _ => {}
24986 }
24987 }
24988
24989 let mut result = HashMap::new();
24990 result.insert("template".to_string(), Value::String(Rc::new(template)));
24991 result.insert(
24992 "variables".to_string(),
24993 Value::Array(Rc::new(RefCell::new(variables))),
24994 );
24995 Ok(Value::Map(Rc::new(RefCell::new(result))))
24996 });
24997
24998 define(interp, "prompt_render", Some(2), |_, args| {
25000 let template_obj = match &args[0] {
25001 Value::Map(m) => m.borrow().clone(),
25002 Value::String(s) => {
25003 let mut m = HashMap::new();
25004 m.insert("template".to_string(), Value::String(s.clone()));
25005 m
25006 }
25007 _ => return Err(RuntimeError::new("prompt_render requires template")),
25008 };
25009
25010 let values = match &args[1] {
25011 Value::Map(m) => m.borrow().clone(),
25012 _ => return Err(RuntimeError::new("prompt_render requires values map")),
25013 };
25014
25015 let template = template_obj
25016 .get("template")
25017 .and_then(|v| {
25018 if let Value::String(s) = v {
25019 Some(s.as_str().to_string())
25020 } else {
25021 None
25022 }
25023 })
25024 .unwrap_or_default();
25025
25026 let mut result = template;
25027 for (key, value) in values.iter() {
25028 let value_str = match value {
25029 Value::String(s) => s.as_str().to_string(),
25030 Value::Int(n) => n.to_string(),
25031 Value::Float(f) => f.to_string(),
25032 Value::Bool(b) => b.to_string(),
25033 _ => format!("{}", value),
25034 };
25035 result = result.replace(&format!("{{{}}}", key), &value_str);
25036 }
25037
25038 Ok(Value::String(Rc::new(result)))
25039 });
25040}
25041
25042fn register_agent_memory(interp: &mut Interpreter) {
25047 define(interp, "memory_session", Some(1), |_, args| {
25049 let session_id = match &args[0] {
25050 Value::String(s) => s.as_str().to_string(),
25051 _ => return Err(RuntimeError::new("memory_session requires string id")),
25052 };
25053
25054 let now = std::time::SystemTime::now()
25055 .duration_since(std::time::UNIX_EPOCH)
25056 .unwrap_or_default()
25057 .as_secs();
25058
25059 AGENT_MEMORY.with(|memory| {
25060 let mut mem = memory.borrow_mut();
25061 if !mem.contains_key(&session_id) {
25062 mem.insert(
25063 session_id.clone(),
25064 AgentSession {
25065 id: session_id.clone(),
25066 context: HashMap::new(),
25067 history: Vec::new(),
25068 created_at: now,
25069 last_accessed: now,
25070 },
25071 );
25072 } else if let Some(session) = mem.get_mut(&session_id) {
25073 session.last_accessed = now;
25074 }
25075 });
25076
25077 let mut result = HashMap::new();
25078 result.insert("id".to_string(), Value::String(Rc::new(session_id)));
25079 result.insert("created_at".to_string(), Value::Int(now as i64));
25080 Ok(Value::Map(Rc::new(RefCell::new(result))))
25081 });
25082
25083 define(interp, "memory_set", Some(3), |_, args| {
25085 let session_id = match &args[0] {
25086 Value::String(s) => s.as_str().to_string(),
25087 Value::Map(m) => m
25088 .borrow()
25089 .get("id")
25090 .and_then(|v| {
25091 if let Value::String(s) = v {
25092 Some(s.as_str().to_string())
25093 } else {
25094 None
25095 }
25096 })
25097 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
25098 _ => return Err(RuntimeError::new("memory_set requires session")),
25099 };
25100
25101 let key = match &args[1] {
25102 Value::String(s) => s.as_str().to_string(),
25103 _ => return Err(RuntimeError::new("memory_set key must be string")),
25104 };
25105
25106 let value = args[2].clone();
25107
25108 AGENT_MEMORY.with(|memory| {
25109 if let Some(session) = memory.borrow_mut().get_mut(&session_id) {
25110 session.context.insert(key, value);
25111 session.last_accessed = std::time::SystemTime::now()
25112 .duration_since(std::time::UNIX_EPOCH)
25113 .unwrap_or_default()
25114 .as_secs();
25115 Ok(Value::Bool(true))
25116 } else {
25117 Err(RuntimeError::new(format!(
25118 "Session '{}' not found",
25119 session_id
25120 )))
25121 }
25122 })
25123 });
25124
25125 define(interp, "memory_get", Some(2), |_, args| {
25127 let session_id = match &args[0] {
25128 Value::String(s) => s.as_str().to_string(),
25129 Value::Map(m) => m
25130 .borrow()
25131 .get("id")
25132 .and_then(|v| {
25133 if let Value::String(s) = v {
25134 Some(s.as_str().to_string())
25135 } else {
25136 None
25137 }
25138 })
25139 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
25140 _ => return Err(RuntimeError::new("memory_get requires session")),
25141 };
25142
25143 let key = match &args[1] {
25144 Value::String(s) => s.as_str().to_string(),
25145 _ => return Err(RuntimeError::new("memory_get key must be string")),
25146 };
25147
25148 AGENT_MEMORY.with(|memory| {
25149 if let Some(session) = memory.borrow().get(&session_id) {
25150 Ok(session.context.get(&key).cloned().unwrap_or(Value::Null))
25151 } else {
25152 Ok(Value::Null)
25153 }
25154 })
25155 });
25156
25157 define(interp, "memory_history_add", Some(3), |_, args| {
25159 let session_id = match &args[0] {
25160 Value::String(s) => s.as_str().to_string(),
25161 Value::Map(m) => m
25162 .borrow()
25163 .get("id")
25164 .and_then(|v| {
25165 if let Value::String(s) = v {
25166 Some(s.as_str().to_string())
25167 } else {
25168 None
25169 }
25170 })
25171 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
25172 _ => return Err(RuntimeError::new("memory_history_add requires session")),
25173 };
25174
25175 let role = match &args[1] {
25176 Value::String(s) => s.as_str().to_string(),
25177 _ => return Err(RuntimeError::new("role must be string")),
25178 };
25179
25180 let content = match &args[2] {
25181 Value::String(s) => s.as_str().to_string(),
25182 _ => return Err(RuntimeError::new("content must be string")),
25183 };
25184
25185 AGENT_MEMORY.with(|memory| {
25186 if let Some(session) = memory.borrow_mut().get_mut(&session_id) {
25187 session.history.push((role, content));
25188 session.last_accessed = std::time::SystemTime::now()
25189 .duration_since(std::time::UNIX_EPOCH)
25190 .unwrap_or_default()
25191 .as_secs();
25192 Ok(Value::Int(session.history.len() as i64))
25193 } else {
25194 Err(RuntimeError::new(format!(
25195 "Session '{}' not found",
25196 session_id
25197 )))
25198 }
25199 })
25200 });
25201
25202 define(interp, "memory_history_get", None, |_, args| {
25204 if args.is_empty() {
25205 return Err(RuntimeError::new("memory_history_get requires session"));
25206 }
25207
25208 let session_id = match &args[0] {
25209 Value::String(s) => s.as_str().to_string(),
25210 Value::Map(m) => m
25211 .borrow()
25212 .get("id")
25213 .and_then(|v| {
25214 if let Value::String(s) = v {
25215 Some(s.as_str().to_string())
25216 } else {
25217 None
25218 }
25219 })
25220 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
25221 _ => return Err(RuntimeError::new("memory_history_get requires session")),
25222 };
25223
25224 let limit = if args.len() > 1 {
25225 match &args[1] {
25226 Value::Int(n) => Some(*n as usize),
25227 _ => None,
25228 }
25229 } else {
25230 None
25231 };
25232
25233 AGENT_MEMORY.with(|memory| {
25234 if let Some(session) = memory.borrow().get(&session_id) {
25235 let history: Vec<Value> = session
25236 .history
25237 .iter()
25238 .rev()
25239 .take(limit.unwrap_or(usize::MAX))
25240 .rev()
25241 .map(|(role, content)| {
25242 let mut msg = HashMap::new();
25243 msg.insert("role".to_string(), Value::String(Rc::new(role.clone())));
25244 msg.insert(
25245 "content".to_string(),
25246 Value::String(Rc::new(content.clone())),
25247 );
25248 Value::Map(Rc::new(RefCell::new(msg)))
25249 })
25250 .collect();
25251 Ok(Value::Array(Rc::new(RefCell::new(history))))
25252 } else {
25253 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
25254 }
25255 })
25256 });
25257
25258 define(interp, "memory_context_all", Some(1), |_, args| {
25260 let session_id = match &args[0] {
25261 Value::String(s) => s.as_str().to_string(),
25262 Value::Map(m) => m
25263 .borrow()
25264 .get("id")
25265 .and_then(|v| {
25266 if let Value::String(s) = v {
25267 Some(s.as_str().to_string())
25268 } else {
25269 None
25270 }
25271 })
25272 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
25273 _ => return Err(RuntimeError::new("memory_context_all requires session")),
25274 };
25275
25276 AGENT_MEMORY.with(|memory| {
25277 if let Some(session) = memory.borrow().get(&session_id) {
25278 let context: HashMap<String, Value> = session.context.clone();
25279 Ok(Value::Map(Rc::new(RefCell::new(context))))
25280 } else {
25281 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
25282 }
25283 })
25284 });
25285
25286 define(interp, "memory_clear", Some(1), |_, args| {
25288 let session_id = match &args[0] {
25289 Value::String(s) => s.as_str().to_string(),
25290 Value::Map(m) => m
25291 .borrow()
25292 .get("id")
25293 .and_then(|v| {
25294 if let Value::String(s) = v {
25295 Some(s.as_str().to_string())
25296 } else {
25297 None
25298 }
25299 })
25300 .ok_or_else(|| RuntimeError::new("Invalid session"))?,
25301 _ => return Err(RuntimeError::new("memory_clear requires session")),
25302 };
25303
25304 AGENT_MEMORY.with(|memory| {
25305 let removed = memory.borrow_mut().remove(&session_id).is_some();
25306 Ok(Value::Bool(removed))
25307 })
25308 });
25309
25310 define(interp, "memory_sessions_list", Some(0), |_, _args| {
25312 let sessions: Vec<Value> = AGENT_MEMORY.with(|memory| {
25313 memory
25314 .borrow()
25315 .values()
25316 .map(|session| {
25317 let mut info = HashMap::new();
25318 info.insert("id".to_string(), Value::String(Rc::new(session.id.clone())));
25319 info.insert(
25320 "created_at".to_string(),
25321 Value::Int(session.created_at as i64),
25322 );
25323 info.insert(
25324 "last_accessed".to_string(),
25325 Value::Int(session.last_accessed as i64),
25326 );
25327 info.insert(
25328 "context_keys".to_string(),
25329 Value::Int(session.context.len() as i64),
25330 );
25331 info.insert(
25332 "history_length".to_string(),
25333 Value::Int(session.history.len() as i64),
25334 );
25335 Value::Map(Rc::new(RefCell::new(info)))
25336 })
25337 .collect()
25338 });
25339 Ok(Value::Array(Rc::new(RefCell::new(sessions))))
25340 });
25341}
25342
25343fn register_agent_planning(interp: &mut Interpreter) {
25348 define(interp, "plan_state_machine", Some(2), |_, args| {
25350 let name = match &args[0] {
25351 Value::String(s) => s.as_str().to_string(),
25352 _ => return Err(RuntimeError::new("plan_state_machine name must be string")),
25353 };
25354
25355 let states = match &args[1] {
25356 Value::Array(arr) => arr
25357 .borrow()
25358 .iter()
25359 .filter_map(|v| {
25360 if let Value::String(s) = v {
25361 Some(s.as_str().to_string())
25362 } else {
25363 None
25364 }
25365 })
25366 .collect::<Vec<_>>(),
25367 _ => return Err(RuntimeError::new("plan_state_machine states must be array")),
25368 };
25369
25370 if states.is_empty() {
25371 return Err(RuntimeError::new(
25372 "State machine must have at least one state",
25373 ));
25374 }
25375
25376 let initial_state = states[0].clone();
25377 let now = std::time::SystemTime::now()
25378 .duration_since(std::time::UNIX_EPOCH)
25379 .unwrap_or_default()
25380 .as_secs();
25381
25382 let machine = StateMachine {
25383 name: name.clone(),
25384 current_state: initial_state.clone(),
25385 states,
25386 transitions: HashMap::new(),
25387 history: vec![(initial_state, now)],
25388 };
25389
25390 STATE_MACHINES.with(|machines| {
25391 machines.borrow_mut().insert(name.clone(), machine);
25392 });
25393
25394 let mut result = HashMap::new();
25395 result.insert("name".to_string(), Value::String(Rc::new(name)));
25396 result.insert(
25397 "type".to_string(),
25398 Value::String(Rc::new("state_machine".to_string())),
25399 );
25400 Ok(Value::Map(Rc::new(RefCell::new(result))))
25401 });
25402
25403 define(interp, "plan_add_transition", Some(3), |_, args| {
25405 let machine_name = match &args[0] {
25406 Value::String(s) => s.as_str().to_string(),
25407 Value::Map(m) => m
25408 .borrow()
25409 .get("name")
25410 .and_then(|v| {
25411 if let Value::String(s) = v {
25412 Some(s.as_str().to_string())
25413 } else {
25414 None
25415 }
25416 })
25417 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
25418 _ => return Err(RuntimeError::new("plan_add_transition requires machine")),
25419 };
25420
25421 let from_state = match &args[1] {
25422 Value::String(s) => s.as_str().to_string(),
25423 _ => return Err(RuntimeError::new("from_state must be string")),
25424 };
25425
25426 let to_state = match &args[2] {
25427 Value::String(s) => s.as_str().to_string(),
25428 _ => return Err(RuntimeError::new("to_state must be string")),
25429 };
25430
25431 STATE_MACHINES.with(|machines| {
25432 if let Some(machine) = machines.borrow_mut().get_mut(&machine_name) {
25433 if !machine.states.contains(&from_state) {
25435 return Err(RuntimeError::new(format!(
25436 "State '{}' not in machine",
25437 from_state
25438 )));
25439 }
25440 if !machine.states.contains(&to_state) {
25441 return Err(RuntimeError::new(format!(
25442 "State '{}' not in machine",
25443 to_state
25444 )));
25445 }
25446
25447 machine
25448 .transitions
25449 .entry(from_state)
25450 .or_insert_with(Vec::new)
25451 .push((to_state, "".to_string()));
25452
25453 Ok(Value::Bool(true))
25454 } else {
25455 Err(RuntimeError::new(format!(
25456 "State machine '{}' not found",
25457 machine_name
25458 )))
25459 }
25460 })
25461 });
25462
25463 define(interp, "plan_current_state", Some(1), |_, args| {
25465 let machine_name = match &args[0] {
25466 Value::String(s) => s.as_str().to_string(),
25467 Value::Map(m) => m
25468 .borrow()
25469 .get("name")
25470 .and_then(|v| {
25471 if let Value::String(s) = v {
25472 Some(s.as_str().to_string())
25473 } else {
25474 None
25475 }
25476 })
25477 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
25478 _ => return Err(RuntimeError::new("plan_current_state requires machine")),
25479 };
25480
25481 STATE_MACHINES.with(|machines| {
25482 if let Some(machine) = machines.borrow().get(&machine_name) {
25483 Ok(Value::String(Rc::new(machine.current_state.clone())))
25484 } else {
25485 Err(RuntimeError::new(format!(
25486 "State machine '{}' not found",
25487 machine_name
25488 )))
25489 }
25490 })
25491 });
25492
25493 define(interp, "plan_transition", Some(2), |_, args| {
25495 let machine_name = match &args[0] {
25496 Value::String(s) => s.as_str().to_string(),
25497 Value::Map(m) => m
25498 .borrow()
25499 .get("name")
25500 .and_then(|v| {
25501 if let Value::String(s) = v {
25502 Some(s.as_str().to_string())
25503 } else {
25504 None
25505 }
25506 })
25507 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
25508 _ => return Err(RuntimeError::new("plan_transition requires machine")),
25509 };
25510
25511 let to_state = match &args[1] {
25512 Value::String(s) => s.as_str().to_string(),
25513 _ => return Err(RuntimeError::new("to_state must be string")),
25514 };
25515
25516 STATE_MACHINES.with(|machines| {
25517 if let Some(machine) = machines.borrow_mut().get_mut(&machine_name) {
25518 let current = machine.current_state.clone();
25519
25520 let valid = machine
25522 .transitions
25523 .get(¤t)
25524 .map(|transitions| transitions.iter().any(|(to, _)| to == &to_state))
25525 .unwrap_or(false);
25526
25527 if valid || machine.states.contains(&to_state) {
25528 let now = std::time::SystemTime::now()
25529 .duration_since(std::time::UNIX_EPOCH)
25530 .unwrap_or_default()
25531 .as_secs();
25532
25533 machine.current_state = to_state.clone();
25534 machine.history.push((to_state.clone(), now));
25535
25536 let mut result = HashMap::new();
25537 result.insert("success".to_string(), Value::Bool(true));
25538 result.insert("from".to_string(), Value::String(Rc::new(current)));
25539 result.insert("to".to_string(), Value::String(Rc::new(to_state)));
25540 Ok(Value::Map(Rc::new(RefCell::new(result))))
25541 } else {
25542 let mut result = HashMap::new();
25543 result.insert("success".to_string(), Value::Bool(false));
25544 result.insert(
25545 "error".to_string(),
25546 Value::String(Rc::new(format!(
25547 "No valid transition from '{}' to '{}'",
25548 current, to_state
25549 ))),
25550 );
25551 Ok(Value::Map(Rc::new(RefCell::new(result))))
25552 }
25553 } else {
25554 Err(RuntimeError::new(format!(
25555 "State machine '{}' not found",
25556 machine_name
25557 )))
25558 }
25559 })
25560 });
25561
25562 define(interp, "plan_can_transition", Some(2), |_, args| {
25564 let machine_name = match &args[0] {
25565 Value::String(s) => s.as_str().to_string(),
25566 Value::Map(m) => m
25567 .borrow()
25568 .get("name")
25569 .and_then(|v| {
25570 if let Value::String(s) = v {
25571 Some(s.as_str().to_string())
25572 } else {
25573 None
25574 }
25575 })
25576 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
25577 _ => return Err(RuntimeError::new("plan_can_transition requires machine")),
25578 };
25579
25580 let to_state = match &args[1] {
25581 Value::String(s) => s.as_str().to_string(),
25582 _ => return Err(RuntimeError::new("to_state must be string")),
25583 };
25584
25585 STATE_MACHINES.with(|machines| {
25586 if let Some(machine) = machines.borrow().get(&machine_name) {
25587 let current = &machine.current_state;
25588 let can = machine
25589 .transitions
25590 .get(current)
25591 .map(|transitions| transitions.iter().any(|(to, _)| to == &to_state))
25592 .unwrap_or(false);
25593 Ok(Value::Bool(can))
25594 } else {
25595 Ok(Value::Bool(false))
25596 }
25597 })
25598 });
25599
25600 define(interp, "plan_available_transitions", Some(1), |_, args| {
25602 let machine_name = match &args[0] {
25603 Value::String(s) => s.as_str().to_string(),
25604 Value::Map(m) => m
25605 .borrow()
25606 .get("name")
25607 .and_then(|v| {
25608 if let Value::String(s) = v {
25609 Some(s.as_str().to_string())
25610 } else {
25611 None
25612 }
25613 })
25614 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
25615 _ => {
25616 return Err(RuntimeError::new(
25617 "plan_available_transitions requires machine",
25618 ))
25619 }
25620 };
25621
25622 STATE_MACHINES.with(|machines| {
25623 if let Some(machine) = machines.borrow().get(&machine_name) {
25624 let current = &machine.current_state;
25625 let available: Vec<Value> = machine
25626 .transitions
25627 .get(current)
25628 .map(|transitions| {
25629 transitions
25630 .iter()
25631 .map(|(to, _)| Value::String(Rc::new(to.clone())))
25632 .collect()
25633 })
25634 .unwrap_or_default();
25635 Ok(Value::Array(Rc::new(RefCell::new(available))))
25636 } else {
25637 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
25638 }
25639 })
25640 });
25641
25642 define(interp, "plan_history", Some(1), |_, args| {
25644 let machine_name = match &args[0] {
25645 Value::String(s) => s.as_str().to_string(),
25646 Value::Map(m) => m
25647 .borrow()
25648 .get("name")
25649 .and_then(|v| {
25650 if let Value::String(s) = v {
25651 Some(s.as_str().to_string())
25652 } else {
25653 None
25654 }
25655 })
25656 .ok_or_else(|| RuntimeError::new("Invalid state machine"))?,
25657 _ => return Err(RuntimeError::new("plan_history requires machine")),
25658 };
25659
25660 STATE_MACHINES.with(|machines| {
25661 if let Some(machine) = machines.borrow().get(&machine_name) {
25662 let history: Vec<Value> = machine
25663 .history
25664 .iter()
25665 .map(|(state, timestamp)| {
25666 let mut entry = HashMap::new();
25667 entry.insert("state".to_string(), Value::String(Rc::new(state.clone())));
25668 entry.insert("timestamp".to_string(), Value::Int(*timestamp as i64));
25669 Value::Map(Rc::new(RefCell::new(entry)))
25670 })
25671 .collect();
25672 Ok(Value::Array(Rc::new(RefCell::new(history))))
25673 } else {
25674 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
25675 }
25676 })
25677 });
25678
25679 define(interp, "plan_goal", Some(2), |_, args| {
25681 let name = match &args[0] {
25682 Value::String(s) => s.as_str().to_string(),
25683 _ => return Err(RuntimeError::new("plan_goal name must be string")),
25684 };
25685
25686 let criteria = args[1].clone();
25687
25688 let mut goal = HashMap::new();
25689 goal.insert("name".to_string(), Value::String(Rc::new(name)));
25690 goal.insert("criteria".to_string(), criteria);
25691 goal.insert(
25692 "status".to_string(),
25693 Value::String(Rc::new("pending".to_string())),
25694 );
25695 goal.insert("progress".to_string(), Value::Float(0.0));
25696 goal.insert(
25697 "created_at".to_string(),
25698 Value::Int(
25699 std::time::SystemTime::now()
25700 .duration_since(std::time::UNIX_EPOCH)
25701 .unwrap_or_default()
25702 .as_secs() as i64,
25703 ),
25704 );
25705
25706 Ok(Value::Map(Rc::new(RefCell::new(goal))))
25707 });
25708
25709 define(interp, "plan_subgoals", Some(2), |_, args| {
25711 let parent = match &args[0] {
25712 Value::Map(m) => m.clone(),
25713 _ => return Err(RuntimeError::new("plan_subgoals requires goal map")),
25714 };
25715
25716 let subgoals = match &args[1] {
25717 Value::Array(arr) => arr.clone(),
25718 _ => return Err(RuntimeError::new("plan_subgoals requires subgoals array")),
25719 };
25720
25721 parent
25722 .borrow_mut()
25723 .insert("subgoals".to_string(), Value::Array(subgoals));
25724 Ok(Value::Map(parent))
25725 });
25726
25727 define(interp, "plan_update_progress", Some(2), |_, args| {
25729 let goal = match &args[0] {
25730 Value::Map(m) => m.clone(),
25731 _ => return Err(RuntimeError::new("plan_update_progress requires goal map")),
25732 };
25733
25734 let progress = match &args[1] {
25735 Value::Float(f) => *f,
25736 Value::Int(i) => *i as f64,
25737 _ => return Err(RuntimeError::new("progress must be number")),
25738 };
25739
25740 let progress = progress.clamp(0.0, 1.0);
25741 goal.borrow_mut()
25742 .insert("progress".to_string(), Value::Float(progress));
25743
25744 if progress >= 1.0 {
25745 goal.borrow_mut().insert(
25746 "status".to_string(),
25747 Value::String(Rc::new("completed".to_string())),
25748 );
25749 } else if progress > 0.0 {
25750 goal.borrow_mut().insert(
25751 "status".to_string(),
25752 Value::String(Rc::new("in_progress".to_string())),
25753 );
25754 }
25755
25756 Ok(Value::Map(goal))
25757 });
25758
25759 define(interp, "plan_check_goal", Some(2), |_interp, args| {
25761 let goal = match &args[0] {
25762 Value::Map(m) => m.borrow().clone(),
25763 _ => return Err(RuntimeError::new("plan_check_goal requires goal map")),
25764 };
25765
25766 let context = match &args[1] {
25767 Value::Map(m) => m.borrow().clone(),
25768 _ => return Err(RuntimeError::new("plan_check_goal requires context map")),
25769 };
25770
25771 let _criteria = goal.get("criteria").cloned().unwrap_or(Value::Null);
25773
25774 let mut met = true;
25776 let mut missing: Vec<String> = Vec::new();
25777
25778 if let Some(Value::Array(required)) = goal.get("required_context") {
25779 for req in required.borrow().iter() {
25780 if let Value::String(key) = req {
25781 if !context.contains_key(key.as_str()) {
25782 met = false;
25783 missing.push(key.as_str().to_string());
25784 }
25785 }
25786 }
25787 }
25788
25789 let mut result = HashMap::new();
25790 result.insert("met".to_string(), Value::Bool(met));
25791 result.insert(
25792 "missing".to_string(),
25793 Value::Array(Rc::new(RefCell::new(
25794 missing
25795 .into_iter()
25796 .map(|s| Value::String(Rc::new(s)))
25797 .collect(),
25798 ))),
25799 );
25800
25801 Ok(Value::Map(Rc::new(RefCell::new(result))))
25802 });
25803}
25804
25805fn register_agent_vectors(interp: &mut Interpreter) {
25810 define(interp, "vec_embedding", Some(1), |_, args| {
25812 let text = match &args[0] {
25813 Value::String(s) => s.as_str().to_string(),
25814 _ => return Err(RuntimeError::new("vec_embedding requires string")),
25815 };
25816
25817 let mut embedding = Vec::new();
25820 let dimension = 384; for i in 0..dimension {
25823 let hash = text.bytes().enumerate().fold(0u64, |acc, (j, b)| {
25824 acc.wrapping_add((b as u64).wrapping_mul((i + j + 1) as u64))
25825 });
25826 let value = ((hash % 10000) as f64 / 10000.0) * 2.0 - 1.0; embedding.push(Value::Float(value));
25828 }
25829
25830 let result = Value::Array(Rc::new(RefCell::new(embedding)));
25831
25832 Ok(Value::Evidential {
25834 value: Box::new(result),
25835 evidence: Evidence::Uncertain,
25836 })
25837 });
25838
25839 define(interp, "vec_cosine_similarity", Some(2), |_, args| {
25841 let vec_a = match &args[0] {
25842 Value::Array(arr) => arr.borrow().clone(),
25843 Value::Evidential { value, .. } => {
25844 if let Value::Array(arr) = value.as_ref() {
25845 arr.borrow().clone()
25846 } else {
25847 return Err(RuntimeError::new("vec_cosine_similarity requires arrays"));
25848 }
25849 }
25850 _ => return Err(RuntimeError::new("vec_cosine_similarity requires arrays")),
25851 };
25852
25853 let vec_b = match &args[1] {
25854 Value::Array(arr) => arr.borrow().clone(),
25855 Value::Evidential { value, .. } => {
25856 if let Value::Array(arr) = value.as_ref() {
25857 arr.borrow().clone()
25858 } else {
25859 return Err(RuntimeError::new("vec_cosine_similarity requires arrays"));
25860 }
25861 }
25862 _ => return Err(RuntimeError::new("vec_cosine_similarity requires arrays")),
25863 };
25864
25865 if vec_a.len() != vec_b.len() {
25866 return Err(RuntimeError::new("Vectors must have same dimension"));
25867 }
25868
25869 let mut dot = 0.0;
25870 let mut mag_a = 0.0;
25871 let mut mag_b = 0.0;
25872
25873 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
25874 let a_val = match a {
25875 Value::Float(f) => *f,
25876 Value::Int(i) => *i as f64,
25877 _ => 0.0,
25878 };
25879 let b_val = match b {
25880 Value::Float(f) => *f,
25881 Value::Int(i) => *i as f64,
25882 _ => 0.0,
25883 };
25884
25885 dot += a_val * b_val;
25886 mag_a += a_val * a_val;
25887 mag_b += b_val * b_val;
25888 }
25889
25890 let similarity = if mag_a > 0.0 && mag_b > 0.0 {
25891 dot / (mag_a.sqrt() * mag_b.sqrt())
25892 } else {
25893 0.0
25894 };
25895
25896 Ok(Value::Float(similarity))
25897 });
25898
25899 define(interp, "vec_euclidean_distance", Some(2), |_, args| {
25901 let vec_a = match &args[0] {
25902 Value::Array(arr) => arr.borrow().clone(),
25903 Value::Evidential { value, .. } => {
25904 if let Value::Array(arr) = value.as_ref() {
25905 arr.borrow().clone()
25906 } else {
25907 return Err(RuntimeError::new("vec_euclidean_distance requires arrays"));
25908 }
25909 }
25910 _ => return Err(RuntimeError::new("vec_euclidean_distance requires arrays")),
25911 };
25912
25913 let vec_b = match &args[1] {
25914 Value::Array(arr) => arr.borrow().clone(),
25915 Value::Evidential { value, .. } => {
25916 if let Value::Array(arr) = value.as_ref() {
25917 arr.borrow().clone()
25918 } else {
25919 return Err(RuntimeError::new("vec_euclidean_distance requires arrays"));
25920 }
25921 }
25922 _ => return Err(RuntimeError::new("vec_euclidean_distance requires arrays")),
25923 };
25924
25925 if vec_a.len() != vec_b.len() {
25926 return Err(RuntimeError::new("Vectors must have same dimension"));
25927 }
25928
25929 let mut sum_sq = 0.0;
25930 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
25931 let a_val = match a {
25932 Value::Float(f) => *f,
25933 Value::Int(i) => *i as f64,
25934 _ => 0.0,
25935 };
25936 let b_val = match b {
25937 Value::Float(f) => *f,
25938 Value::Int(i) => *i as f64,
25939 _ => 0.0,
25940 };
25941 let diff = a_val - b_val;
25942 sum_sq += diff * diff;
25943 }
25944
25945 Ok(Value::Float(sum_sq.sqrt()))
25946 });
25947
25948 define(interp, "vec_dot_product", Some(2), |_, args| {
25950 let vec_a = match &args[0] {
25951 Value::Array(arr) => arr.borrow().clone(),
25952 _ => return Err(RuntimeError::new("vec_dot_product requires arrays")),
25953 };
25954
25955 let vec_b = match &args[1] {
25956 Value::Array(arr) => arr.borrow().clone(),
25957 _ => return Err(RuntimeError::new("vec_dot_product requires arrays")),
25958 };
25959
25960 if vec_a.len() != vec_b.len() {
25961 return Err(RuntimeError::new("Vectors must have same dimension"));
25962 }
25963
25964 let mut dot = 0.0;
25965 for (a, b) in vec_a.iter().zip(vec_b.iter()) {
25966 let a_val = match a {
25967 Value::Float(f) => *f,
25968 Value::Int(i) => *i as f64,
25969 _ => 0.0,
25970 };
25971 let b_val = match b {
25972 Value::Float(f) => *f,
25973 Value::Int(i) => *i as f64,
25974 _ => 0.0,
25975 };
25976 dot += a_val * b_val;
25977 }
25978
25979 Ok(Value::Float(dot))
25980 });
25981
25982 define(interp, "vec_normalize", Some(1), |_, args| {
25984 let vec = match &args[0] {
25985 Value::Array(arr) => arr.borrow().clone(),
25986 _ => return Err(RuntimeError::new("vec_normalize requires array")),
25987 };
25988
25989 let mut mag = 0.0;
25990 for v in vec.iter() {
25991 let val = match v {
25992 Value::Float(f) => *f,
25993 Value::Int(i) => *i as f64,
25994 _ => 0.0,
25995 };
25996 mag += val * val;
25997 }
25998 mag = mag.sqrt();
25999
26000 if mag == 0.0 {
26001 return Ok(Value::Array(Rc::new(RefCell::new(vec))));
26002 }
26003
26004 let normalized: Vec<Value> = vec
26005 .iter()
26006 .map(|v| {
26007 let val = match v {
26008 Value::Float(f) => *f,
26009 Value::Int(i) => *i as f64,
26010 _ => 0.0,
26011 };
26012 Value::Float(val / mag)
26013 })
26014 .collect();
26015
26016 Ok(Value::Array(Rc::new(RefCell::new(normalized))))
26017 });
26018
26019 define(interp, "vec_search", Some(3), |_, args| {
26021 let query = match &args[0] {
26022 Value::Array(arr) => arr.borrow().clone(),
26023 Value::Evidential { value, .. } => {
26024 if let Value::Array(arr) = value.as_ref() {
26025 arr.borrow().clone()
26026 } else {
26027 return Err(RuntimeError::new("vec_search query must be array"));
26028 }
26029 }
26030 _ => return Err(RuntimeError::new("vec_search query must be array")),
26031 };
26032
26033 let corpus = match &args[1] {
26034 Value::Array(arr) => arr.borrow().clone(),
26035 _ => {
26036 return Err(RuntimeError::new(
26037 "vec_search corpus must be array of vectors",
26038 ))
26039 }
26040 };
26041
26042 let k = match &args[2] {
26043 Value::Int(n) => *n as usize,
26044 _ => return Err(RuntimeError::new("vec_search k must be integer")),
26045 };
26046
26047 let mut similarities: Vec<(usize, f64)> = Vec::new();
26049
26050 for (i, item) in corpus.iter().enumerate() {
26051 let vec_b = match item {
26052 Value::Array(arr) => arr.borrow().clone(),
26053 Value::Map(m) => {
26054 if let Some(Value::Array(arr)) = m.borrow().get("vector") {
26056 arr.borrow().clone()
26057 } else {
26058 continue;
26059 }
26060 }
26061 _ => continue,
26062 };
26063
26064 if vec_b.len() != query.len() {
26065 continue;
26066 }
26067
26068 let mut dot = 0.0;
26069 let mut mag_a = 0.0;
26070 let mut mag_b = 0.0;
26071
26072 for (a, b) in query.iter().zip(vec_b.iter()) {
26073 let a_val = match a {
26074 Value::Float(f) => *f,
26075 Value::Int(i) => *i as f64,
26076 _ => 0.0,
26077 };
26078 let b_val = match b {
26079 Value::Float(f) => *f,
26080 Value::Int(i) => *i as f64,
26081 _ => 0.0,
26082 };
26083 dot += a_val * b_val;
26084 mag_a += a_val * a_val;
26085 mag_b += b_val * b_val;
26086 }
26087
26088 let similarity = if mag_a > 0.0 && mag_b > 0.0 {
26089 dot / (mag_a.sqrt() * mag_b.sqrt())
26090 } else {
26091 0.0
26092 };
26093
26094 similarities.push((i, similarity));
26095 }
26096
26097 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
26099
26100 let results: Vec<Value> = similarities
26102 .iter()
26103 .take(k)
26104 .map(|(idx, sim)| {
26105 let mut result = HashMap::new();
26106 result.insert("index".to_string(), Value::Int(*idx as i64));
26107 result.insert("similarity".to_string(), Value::Float(*sim));
26108 result.insert(
26109 "item".to_string(),
26110 corpus.get(*idx).cloned().unwrap_or(Value::Null),
26111 );
26112 Value::Map(Rc::new(RefCell::new(result)))
26113 })
26114 .collect();
26115
26116 Ok(Value::Array(Rc::new(RefCell::new(results))))
26117 });
26118
26119 define(interp, "vec_store", Some(0), |_, _args| {
26121 let mut store = HashMap::new();
26122 store.insert(
26123 "vectors".to_string(),
26124 Value::Array(Rc::new(RefCell::new(Vec::new()))),
26125 );
26126 store.insert(
26127 "metadata".to_string(),
26128 Value::Array(Rc::new(RefCell::new(Vec::new()))),
26129 );
26130 store.insert("count".to_string(), Value::Int(0));
26131 Ok(Value::Map(Rc::new(RefCell::new(store))))
26132 });
26133
26134 define(interp, "vec_store_add", Some(3), |_, args| {
26136 let store = match &args[0] {
26137 Value::Map(m) => m.clone(),
26138 _ => return Err(RuntimeError::new("vec_store_add requires store")),
26139 };
26140
26141 let vector = args[1].clone();
26142 let metadata = args[2].clone();
26143
26144 let mut store_ref = store.borrow_mut();
26145
26146 if let Some(Value::Array(vectors)) = store_ref.get("vectors") {
26147 vectors.borrow_mut().push(vector);
26148 }
26149 if let Some(Value::Array(metas)) = store_ref.get("metadata") {
26150 metas.borrow_mut().push(metadata);
26151 }
26152
26153 let new_count = store_ref
26154 .get("count")
26155 .and_then(|v| {
26156 if let Value::Int(n) = v {
26157 Some(*n)
26158 } else {
26159 None
26160 }
26161 })
26162 .unwrap_or(0)
26163 + 1;
26164 store_ref.insert("count".to_string(), Value::Int(new_count));
26165
26166 Ok(Value::Int(new_count))
26167 });
26168
26169 define(interp, "vec_store_search", Some(3), |_, args| {
26171 let store = match &args[0] {
26172 Value::Map(m) => m.borrow().clone(),
26173 _ => return Err(RuntimeError::new("vec_store_search requires store")),
26174 };
26175
26176 let query = match &args[1] {
26177 Value::Array(arr) => arr.borrow().clone(),
26178 Value::Evidential { value, .. } => {
26179 if let Value::Array(arr) = value.as_ref() {
26180 arr.borrow().clone()
26181 } else {
26182 return Err(RuntimeError::new("Query must be array"));
26183 }
26184 }
26185 _ => return Err(RuntimeError::new("Query must be array")),
26186 };
26187
26188 let k = match &args[2] {
26189 Value::Int(n) => *n as usize,
26190 _ => return Err(RuntimeError::new("k must be integer")),
26191 };
26192
26193 let vectors = store
26194 .get("vectors")
26195 .and_then(|v| {
26196 if let Value::Array(arr) = v {
26197 Some(arr.borrow().clone())
26198 } else {
26199 None
26200 }
26201 })
26202 .unwrap_or_default();
26203
26204 let metadata = store
26205 .get("metadata")
26206 .and_then(|v| {
26207 if let Value::Array(arr) = v {
26208 Some(arr.borrow().clone())
26209 } else {
26210 None
26211 }
26212 })
26213 .unwrap_or_default();
26214
26215 let mut similarities: Vec<(usize, f64)> = Vec::new();
26216
26217 for (i, vec_val) in vectors.iter().enumerate() {
26218 let vec_b = match vec_val {
26219 Value::Array(arr) => arr.borrow().clone(),
26220 Value::Evidential { value, .. } => {
26221 if let Value::Array(arr) = value.as_ref() {
26222 arr.borrow().clone()
26223 } else {
26224 continue;
26225 }
26226 }
26227 _ => continue,
26228 };
26229
26230 if vec_b.len() != query.len() {
26231 continue;
26232 }
26233
26234 let mut dot = 0.0;
26235 let mut mag_a = 0.0;
26236 let mut mag_b = 0.0;
26237
26238 for (a, b) in query.iter().zip(vec_b.iter()) {
26239 let a_val = match a {
26240 Value::Float(f) => *f,
26241 Value::Int(i) => *i as f64,
26242 _ => 0.0,
26243 };
26244 let b_val = match b {
26245 Value::Float(f) => *f,
26246 Value::Int(i) => *i as f64,
26247 _ => 0.0,
26248 };
26249 dot += a_val * b_val;
26250 mag_a += a_val * a_val;
26251 mag_b += b_val * b_val;
26252 }
26253
26254 let sim = if mag_a > 0.0 && mag_b > 0.0 {
26255 dot / (mag_a.sqrt() * mag_b.sqrt())
26256 } else {
26257 0.0
26258 };
26259 similarities.push((i, sim));
26260 }
26261
26262 similarities.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
26263
26264 let results: Vec<Value> = similarities
26265 .iter()
26266 .take(k)
26267 .map(|(idx, sim)| {
26268 let mut result = HashMap::new();
26269 result.insert("index".to_string(), Value::Int(*idx as i64));
26270 result.insert("similarity".to_string(), Value::Float(*sim));
26271 result.insert(
26272 "vector".to_string(),
26273 vectors.get(*idx).cloned().unwrap_or(Value::Null),
26274 );
26275 result.insert(
26276 "metadata".to_string(),
26277 metadata.get(*idx).cloned().unwrap_or(Value::Null),
26278 );
26279 Value::Map(Rc::new(RefCell::new(result)))
26280 })
26281 .collect();
26282
26283 Ok(Value::Array(Rc::new(RefCell::new(results))))
26284 });
26285}
26286
26287thread_local! {
26293 static AGENT_REGISTRY: RefCell<HashMap<String, AgentInfo>> = RefCell::new(HashMap::new());
26294 static AGENT_MESSAGES: RefCell<HashMap<String, Vec<AgentMessage>>> = RefCell::new(HashMap::new());
26295}
26296
26297#[derive(Clone)]
26298struct AgentInfo {
26299 id: String,
26300 agent_type: String,
26301 state: HashMap<String, Value>,
26302 capabilities: Vec<String>,
26303 created_at: u64,
26304}
26305
26306#[derive(Clone)]
26307struct AgentMessage {
26308 from: String,
26309 to: String,
26310 msg_type: String,
26311 content: Value,
26312 timestamp: u64,
26313}
26314
26315fn register_agent_swarm(interp: &mut Interpreter) {
26316 define(interp, "swarm_create_agent", Some(2), |_, args| {
26318 let agent_id = match &args[0] {
26319 Value::String(s) => s.as_str().to_string(),
26320 _ => return Err(RuntimeError::new("swarm_create_agent requires string id")),
26321 };
26322
26323 let agent_type = match &args[1] {
26324 Value::String(s) => s.as_str().to_string(),
26325 _ => return Err(RuntimeError::new("swarm_create_agent requires string type")),
26326 };
26327
26328 let now = std::time::SystemTime::now()
26329 .duration_since(std::time::UNIX_EPOCH)
26330 .unwrap_or_default()
26331 .as_secs();
26332
26333 let agent = AgentInfo {
26334 id: agent_id.clone(),
26335 agent_type,
26336 state: HashMap::new(),
26337 capabilities: Vec::new(),
26338 created_at: now,
26339 };
26340
26341 AGENT_REGISTRY.with(|registry| {
26342 registry.borrow_mut().insert(agent_id.clone(), agent);
26343 });
26344
26345 AGENT_MESSAGES.with(|messages| {
26346 messages.borrow_mut().insert(agent_id.clone(), Vec::new());
26347 });
26348
26349 let mut result = HashMap::new();
26350 result.insert("id".to_string(), Value::String(Rc::new(agent_id)));
26351 result.insert("created_at".to_string(), Value::Int(now as i64));
26352 Ok(Value::Map(Rc::new(RefCell::new(result))))
26353 });
26354
26355 define(interp, "swarm_add_capability", Some(2), |_, args| {
26357 let agent_id = match &args[0] {
26358 Value::String(s) => s.as_str().to_string(),
26359 Value::Map(m) => m
26360 .borrow()
26361 .get("id")
26362 .and_then(|v| {
26363 if let Value::String(s) = v {
26364 Some(s.as_str().to_string())
26365 } else {
26366 None
26367 }
26368 })
26369 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
26370 _ => return Err(RuntimeError::new("swarm_add_capability requires agent")),
26371 };
26372
26373 let capability = match &args[1] {
26374 Value::String(s) => s.as_str().to_string(),
26375 _ => return Err(RuntimeError::new("capability must be string")),
26376 };
26377
26378 AGENT_REGISTRY.with(|registry| {
26379 if let Some(agent) = registry.borrow_mut().get_mut(&agent_id) {
26380 if !agent.capabilities.contains(&capability) {
26381 agent.capabilities.push(capability);
26382 }
26383 Ok(Value::Bool(true))
26384 } else {
26385 Err(RuntimeError::new(format!("Agent '{}' not found", agent_id)))
26386 }
26387 })
26388 });
26389
26390 define(interp, "swarm_send_message", Some(4), |_, args| {
26392 let from_id = match &args[0] {
26393 Value::String(s) => s.as_str().to_string(),
26394 Value::Map(m) => m
26395 .borrow()
26396 .get("id")
26397 .and_then(|v| {
26398 if let Value::String(s) = v {
26399 Some(s.as_str().to_string())
26400 } else {
26401 None
26402 }
26403 })
26404 .ok_or_else(|| RuntimeError::new("Invalid sender agent"))?,
26405 _ => {
26406 return Err(RuntimeError::new(
26407 "swarm_send_message requires sender agent",
26408 ))
26409 }
26410 };
26411
26412 let to_id = match &args[1] {
26413 Value::String(s) => s.as_str().to_string(),
26414 Value::Map(m) => m
26415 .borrow()
26416 .get("id")
26417 .and_then(|v| {
26418 if let Value::String(s) = v {
26419 Some(s.as_str().to_string())
26420 } else {
26421 None
26422 }
26423 })
26424 .ok_or_else(|| RuntimeError::new("Invalid receiver agent"))?,
26425 _ => {
26426 return Err(RuntimeError::new(
26427 "swarm_send_message requires receiver agent",
26428 ))
26429 }
26430 };
26431
26432 let msg_type = match &args[2] {
26433 Value::String(s) => s.as_str().to_string(),
26434 _ => return Err(RuntimeError::new("message type must be string")),
26435 };
26436
26437 let content = args[3].clone();
26438
26439 let now = std::time::SystemTime::now()
26440 .duration_since(std::time::UNIX_EPOCH)
26441 .unwrap_or_default()
26442 .as_secs();
26443
26444 let message = AgentMessage {
26445 from: from_id.clone(),
26446 to: to_id.clone(),
26447 msg_type,
26448 content,
26449 timestamp: now,
26450 };
26451
26452 AGENT_MESSAGES.with(|messages| {
26453 if let Some(queue) = messages.borrow_mut().get_mut(&to_id) {
26454 queue.push(message);
26455 Ok(Value::Bool(true))
26456 } else {
26457 Err(RuntimeError::new(format!("Agent '{}' not found", to_id)))
26458 }
26459 })
26460 });
26461
26462 define(interp, "swarm_receive_messages", Some(1), |_, args| {
26464 let agent_id = match &args[0] {
26465 Value::String(s) => s.as_str().to_string(),
26466 Value::Map(m) => m
26467 .borrow()
26468 .get("id")
26469 .and_then(|v| {
26470 if let Value::String(s) = v {
26471 Some(s.as_str().to_string())
26472 } else {
26473 None
26474 }
26475 })
26476 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
26477 _ => return Err(RuntimeError::new("swarm_receive_messages requires agent")),
26478 };
26479
26480 AGENT_MESSAGES.with(|messages| {
26481 if let Some(queue) = messages.borrow_mut().get_mut(&agent_id) {
26482 let msgs: Vec<Value> = queue
26483 .drain(..)
26484 .map(|m| {
26485 let mut msg_map = HashMap::new();
26486 msg_map.insert("from".to_string(), Value::String(Rc::new(m.from)));
26487 msg_map.insert("to".to_string(), Value::String(Rc::new(m.to)));
26488 msg_map.insert("type".to_string(), Value::String(Rc::new(m.msg_type)));
26489 msg_map.insert("content".to_string(), m.content);
26490 msg_map.insert("timestamp".to_string(), Value::Int(m.timestamp as i64));
26491 Value::Map(Rc::new(RefCell::new(msg_map)))
26492 })
26493 .collect();
26494 Ok(Value::Array(Rc::new(RefCell::new(msgs))))
26495 } else {
26496 Ok(Value::Array(Rc::new(RefCell::new(Vec::new()))))
26497 }
26498 })
26499 });
26500
26501 define(interp, "swarm_broadcast", Some(3), |_, args| {
26503 let from_id = match &args[0] {
26504 Value::String(s) => s.as_str().to_string(),
26505 Value::Map(m) => m
26506 .borrow()
26507 .get("id")
26508 .and_then(|v| {
26509 if let Value::String(s) = v {
26510 Some(s.as_str().to_string())
26511 } else {
26512 None
26513 }
26514 })
26515 .ok_or_else(|| RuntimeError::new("Invalid sender agent"))?,
26516 _ => return Err(RuntimeError::new("swarm_broadcast requires sender agent")),
26517 };
26518
26519 let msg_type = match &args[1] {
26520 Value::String(s) => s.as_str().to_string(),
26521 _ => return Err(RuntimeError::new("message type must be string")),
26522 };
26523
26524 let content = args[2].clone();
26525
26526 let now = std::time::SystemTime::now()
26527 .duration_since(std::time::UNIX_EPOCH)
26528 .unwrap_or_default()
26529 .as_secs();
26530
26531 let agent_ids: Vec<String> = AGENT_REGISTRY.with(|registry| {
26533 registry
26534 .borrow()
26535 .keys()
26536 .filter(|id| *id != &from_id)
26537 .cloned()
26538 .collect()
26539 });
26540
26541 let mut count = 0;
26542 AGENT_MESSAGES.with(|messages| {
26543 let mut msgs = messages.borrow_mut();
26544 for to_id in agent_ids {
26545 if let Some(queue) = msgs.get_mut(&to_id) {
26546 queue.push(AgentMessage {
26547 from: from_id.clone(),
26548 to: to_id,
26549 msg_type: msg_type.clone(),
26550 content: content.clone(),
26551 timestamp: now,
26552 });
26553 count += 1;
26554 }
26555 }
26556 });
26557
26558 Ok(Value::Int(count))
26559 });
26560
26561 define(interp, "swarm_find_agents", Some(1), |_, args| {
26563 let capability = match &args[0] {
26564 Value::String(s) => s.as_str().to_string(),
26565 _ => {
26566 return Err(RuntimeError::new(
26567 "swarm_find_agents requires capability string",
26568 ))
26569 }
26570 };
26571
26572 let agents: Vec<Value> = AGENT_REGISTRY.with(|registry| {
26573 registry
26574 .borrow()
26575 .values()
26576 .filter(|agent| agent.capabilities.contains(&capability))
26577 .map(|agent| {
26578 let mut info = HashMap::new();
26579 info.insert("id".to_string(), Value::String(Rc::new(agent.id.clone())));
26580 info.insert(
26581 "type".to_string(),
26582 Value::String(Rc::new(agent.agent_type.clone())),
26583 );
26584 info.insert(
26585 "capabilities".to_string(),
26586 Value::Array(Rc::new(RefCell::new(
26587 agent
26588 .capabilities
26589 .iter()
26590 .map(|c| Value::String(Rc::new(c.clone())))
26591 .collect(),
26592 ))),
26593 );
26594 Value::Map(Rc::new(RefCell::new(info)))
26595 })
26596 .collect()
26597 });
26598
26599 Ok(Value::Array(Rc::new(RefCell::new(agents))))
26600 });
26601
26602 define(interp, "swarm_list_agents", Some(0), |_, _args| {
26604 let agents: Vec<Value> = AGENT_REGISTRY.with(|registry| {
26605 registry
26606 .borrow()
26607 .values()
26608 .map(|agent| {
26609 let mut info = HashMap::new();
26610 info.insert("id".to_string(), Value::String(Rc::new(agent.id.clone())));
26611 info.insert(
26612 "type".to_string(),
26613 Value::String(Rc::new(agent.agent_type.clone())),
26614 );
26615 info.insert(
26616 "capabilities".to_string(),
26617 Value::Array(Rc::new(RefCell::new(
26618 agent
26619 .capabilities
26620 .iter()
26621 .map(|c| Value::String(Rc::new(c.clone())))
26622 .collect(),
26623 ))),
26624 );
26625 info.insert(
26626 "created_at".to_string(),
26627 Value::Int(agent.created_at as i64),
26628 );
26629 Value::Map(Rc::new(RefCell::new(info)))
26630 })
26631 .collect()
26632 });
26633 Ok(Value::Array(Rc::new(RefCell::new(agents))))
26634 });
26635
26636 define(interp, "swarm_set_state", Some(3), |_, args| {
26638 let agent_id = match &args[0] {
26639 Value::String(s) => s.as_str().to_string(),
26640 Value::Map(m) => m
26641 .borrow()
26642 .get("id")
26643 .and_then(|v| {
26644 if let Value::String(s) = v {
26645 Some(s.as_str().to_string())
26646 } else {
26647 None
26648 }
26649 })
26650 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
26651 _ => return Err(RuntimeError::new("swarm_set_state requires agent")),
26652 };
26653
26654 let key = match &args[1] {
26655 Value::String(s) => s.as_str().to_string(),
26656 _ => return Err(RuntimeError::new("key must be string")),
26657 };
26658
26659 let value = args[2].clone();
26660
26661 AGENT_REGISTRY.with(|registry| {
26662 if let Some(agent) = registry.borrow_mut().get_mut(&agent_id) {
26663 agent.state.insert(key, value);
26664 Ok(Value::Bool(true))
26665 } else {
26666 Err(RuntimeError::new(format!("Agent '{}' not found", agent_id)))
26667 }
26668 })
26669 });
26670
26671 define(interp, "swarm_get_state", Some(2), |_, args| {
26673 let agent_id = match &args[0] {
26674 Value::String(s) => s.as_str().to_string(),
26675 Value::Map(m) => m
26676 .borrow()
26677 .get("id")
26678 .and_then(|v| {
26679 if let Value::String(s) = v {
26680 Some(s.as_str().to_string())
26681 } else {
26682 None
26683 }
26684 })
26685 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
26686 _ => return Err(RuntimeError::new("swarm_get_state requires agent")),
26687 };
26688
26689 let key = match &args[1] {
26690 Value::String(s) => s.as_str().to_string(),
26691 _ => return Err(RuntimeError::new("key must be string")),
26692 };
26693
26694 AGENT_REGISTRY.with(|registry| {
26695 if let Some(agent) = registry.borrow().get(&agent_id) {
26696 Ok(agent.state.get(&key).cloned().unwrap_or(Value::Null))
26697 } else {
26698 Ok(Value::Null)
26699 }
26700 })
26701 });
26702
26703 define(interp, "swarm_remove_agent", Some(1), |_, args| {
26705 let agent_id = match &args[0] {
26706 Value::String(s) => s.as_str().to_string(),
26707 Value::Map(m) => m
26708 .borrow()
26709 .get("id")
26710 .and_then(|v| {
26711 if let Value::String(s) = v {
26712 Some(s.as_str().to_string())
26713 } else {
26714 None
26715 }
26716 })
26717 .ok_or_else(|| RuntimeError::new("Invalid agent"))?,
26718 _ => return Err(RuntimeError::new("swarm_remove_agent requires agent")),
26719 };
26720
26721 let removed =
26722 AGENT_REGISTRY.with(|registry| registry.borrow_mut().remove(&agent_id).is_some());
26723
26724 AGENT_MESSAGES.with(|messages| {
26725 messages.borrow_mut().remove(&agent_id);
26726 });
26727
26728 Ok(Value::Bool(removed))
26729 });
26730
26731 define(interp, "swarm_consensus", Some(2), |_, args| {
26733 let topic = match &args[0] {
26734 Value::String(s) => s.as_str().to_string(),
26735 _ => return Err(RuntimeError::new("topic must be string")),
26736 };
26737
26738 let votes = match &args[1] {
26739 Value::Array(arr) => arr.borrow().clone(),
26740 _ => return Err(RuntimeError::new("votes must be array")),
26741 };
26742
26743 let mut vote_counts: HashMap<String, i64> = HashMap::new();
26745 for vote in votes.iter() {
26746 let vote_str = match vote {
26747 Value::String(s) => s.as_str().to_string(),
26748 Value::Bool(b) => b.to_string(),
26749 Value::Int(n) => n.to_string(),
26750 _ => continue,
26751 };
26752 *vote_counts.entry(vote_str).or_insert(0) += 1;
26753 }
26754
26755 let total = votes.len() as i64;
26757 let (winner, count) = vote_counts
26758 .iter()
26759 .max_by_key(|(_, &count)| count)
26760 .map(|(k, &v)| (k.clone(), v))
26761 .unwrap_or_default();
26762
26763 let mut result = HashMap::new();
26764 result.insert("topic".to_string(), Value::String(Rc::new(topic)));
26765 result.insert("winner".to_string(), Value::String(Rc::new(winner)));
26766 result.insert("votes".to_string(), Value::Int(count));
26767 result.insert("total".to_string(), Value::Int(total));
26768 result.insert("majority".to_string(), Value::Bool(count > total / 2));
26769
26770 Ok(Value::Map(Rc::new(RefCell::new(result))))
26771 });
26772}
26773
26774fn register_agent_reasoning(interp: &mut Interpreter) {
26779 define(interp, "reason_constraint", Some(3), |_, args| {
26781 let name = match &args[0] {
26782 Value::String(s) => s.as_str().to_string(),
26783 _ => return Err(RuntimeError::new("constraint name must be string")),
26784 };
26785
26786 let constraint_type = match &args[1] {
26787 Value::String(s) => s.as_str().to_string(),
26788 _ => return Err(RuntimeError::new("constraint type must be string")),
26789 };
26790
26791 let params = args[2].clone();
26792
26793 let mut constraint = HashMap::new();
26794 constraint.insert("name".to_string(), Value::String(Rc::new(name)));
26795 constraint.insert("type".to_string(), Value::String(Rc::new(constraint_type)));
26796 constraint.insert("params".to_string(), params);
26797 constraint.insert("satisfied".to_string(), Value::Bool(false));
26798
26799 Ok(Value::Map(Rc::new(RefCell::new(constraint))))
26800 });
26801
26802 define(interp, "reason_check_constraint", Some(2), |_, args| {
26804 let constraint = match &args[0] {
26805 Value::Map(m) => m.borrow().clone(),
26806 _ => {
26807 return Err(RuntimeError::new(
26808 "reason_check_constraint requires constraint",
26809 ))
26810 }
26811 };
26812
26813 let context = match &args[1] {
26814 Value::Map(m) => m.borrow().clone(),
26815 _ => {
26816 return Err(RuntimeError::new(
26817 "reason_check_constraint requires context",
26818 ))
26819 }
26820 };
26821
26822 let constraint_type = constraint
26823 .get("type")
26824 .and_then(|v| {
26825 if let Value::String(s) = v {
26826 Some(s.as_str())
26827 } else {
26828 None
26829 }
26830 })
26831 .unwrap_or("unknown");
26832
26833 let params = constraint.get("params").cloned().unwrap_or(Value::Null);
26834
26835 let satisfied = match constraint_type {
26836 "equals" => {
26837 if let Value::Map(p) = ¶ms {
26838 let p = p.borrow();
26839 let var_name = p
26840 .get("var")
26841 .and_then(|v| {
26842 if let Value::String(s) = v {
26843 Some(s.as_str().to_string())
26844 } else {
26845 None
26846 }
26847 })
26848 .unwrap_or_default();
26849 let expected = p.get("value").cloned().unwrap_or(Value::Null);
26850 let actual = context.get(&var_name).cloned().unwrap_or(Value::Null);
26851 values_equal_simple(&actual, &expected)
26852 } else {
26853 false
26854 }
26855 }
26856 "not_null" => {
26857 if let Value::Map(p) = ¶ms {
26858 let p = p.borrow();
26859 let var_name = p
26860 .get("var")
26861 .and_then(|v| {
26862 if let Value::String(s) = v {
26863 Some(s.as_str().to_string())
26864 } else {
26865 None
26866 }
26867 })
26868 .unwrap_or_default();
26869 !matches!(context.get(&var_name), None | Some(Value::Null))
26870 } else {
26871 false
26872 }
26873 }
26874 "range" => {
26875 if let Value::Map(p) = ¶ms {
26876 let p = p.borrow();
26877 let var_name = p
26878 .get("var")
26879 .and_then(|v| {
26880 if let Value::String(s) = v {
26881 Some(s.as_str().to_string())
26882 } else {
26883 None
26884 }
26885 })
26886 .unwrap_or_default();
26887 let min = p
26888 .get("min")
26889 .and_then(|v| match v {
26890 Value::Int(n) => Some(*n as f64),
26891 Value::Float(f) => Some(*f),
26892 _ => None,
26893 })
26894 .unwrap_or(f64::NEG_INFINITY);
26895 let max = p
26896 .get("max")
26897 .and_then(|v| match v {
26898 Value::Int(n) => Some(*n as f64),
26899 Value::Float(f) => Some(*f),
26900 _ => None,
26901 })
26902 .unwrap_or(f64::INFINITY);
26903 let actual = context
26904 .get(&var_name)
26905 .and_then(|v| match v {
26906 Value::Int(n) => Some(*n as f64),
26907 Value::Float(f) => Some(*f),
26908 _ => None,
26909 })
26910 .unwrap_or(0.0);
26911 actual >= min && actual <= max
26912 } else {
26913 false
26914 }
26915 }
26916 "contains" => {
26917 if let Value::Map(p) = ¶ms {
26918 let p = p.borrow();
26919 let var_name = p
26920 .get("var")
26921 .and_then(|v| {
26922 if let Value::String(s) = v {
26923 Some(s.as_str().to_string())
26924 } else {
26925 None
26926 }
26927 })
26928 .unwrap_or_default();
26929 let needle = p
26930 .get("value")
26931 .and_then(|v| {
26932 if let Value::String(s) = v {
26933 Some(s.as_str().to_string())
26934 } else {
26935 None
26936 }
26937 })
26938 .unwrap_or_default();
26939 let actual = context
26940 .get(&var_name)
26941 .and_then(|v| {
26942 if let Value::String(s) = v {
26943 Some(s.as_str().to_string())
26944 } else {
26945 None
26946 }
26947 })
26948 .unwrap_or_default();
26949 actual.contains(&needle)
26950 } else {
26951 false
26952 }
26953 }
26954 _ => false,
26955 };
26956
26957 let mut result = HashMap::new();
26958 result.insert("satisfied".to_string(), Value::Bool(satisfied));
26959 result.insert(
26960 "constraint".to_string(),
26961 Value::Map(Rc::new(RefCell::new(constraint))),
26962 );
26963
26964 Ok(Value::Map(Rc::new(RefCell::new(result))))
26965 });
26966
26967 define(interp, "reason_check_all", Some(2), |interp, args| {
26969 let constraints = match &args[0] {
26970 Value::Array(arr) => arr.borrow().clone(),
26971 _ => {
26972 return Err(RuntimeError::new(
26973 "reason_check_all requires constraints array",
26974 ))
26975 }
26976 };
26977
26978 let context = args[1].clone();
26979
26980 let mut all_satisfied = true;
26981 let mut results: Vec<Value> = Vec::new();
26982
26983 for constraint in constraints.iter() {
26984 if let Value::Map(c) = constraint {
26986 let c_type = c
26987 .borrow()
26988 .get("type")
26989 .and_then(|v| {
26990 if let Value::String(s) = v {
26991 Some(s.as_ref().clone())
26992 } else {
26993 None
26994 }
26995 })
26996 .unwrap_or_else(|| "unknown".to_string());
26997 let params = c.borrow().get("params").cloned().unwrap_or(Value::Null);
26998
26999 let ctx = match &context {
27000 Value::Map(m) => m.borrow().clone(),
27001 _ => HashMap::new(),
27002 };
27003
27004 let satisfied = match c_type.as_str() {
27005 "equals" => {
27006 if let Value::Map(p) = ¶ms {
27007 let p = p.borrow();
27008 let var_name = p
27009 .get("var")
27010 .and_then(|v| {
27011 if let Value::String(s) = v {
27012 Some(s.as_str().to_string())
27013 } else {
27014 None
27015 }
27016 })
27017 .unwrap_or_default();
27018 let expected = p.get("value").cloned().unwrap_or(Value::Null);
27019 let actual = ctx.get(&var_name).cloned().unwrap_or(Value::Null);
27020 values_equal_simple(&actual, &expected)
27021 } else {
27022 false
27023 }
27024 }
27025 "not_null" => {
27026 if let Value::Map(p) = ¶ms {
27027 let p = p.borrow();
27028 let var_name = p
27029 .get("var")
27030 .and_then(|v| {
27031 if let Value::String(s) = v {
27032 Some(s.as_str().to_string())
27033 } else {
27034 None
27035 }
27036 })
27037 .unwrap_or_default();
27038 !matches!(ctx.get(&var_name), None | Some(Value::Null))
27039 } else {
27040 false
27041 }
27042 }
27043 _ => true, };
27045
27046 if !satisfied {
27047 all_satisfied = false;
27048 }
27049
27050 let mut r = HashMap::new();
27051 r.insert("constraint".to_string(), constraint.clone());
27052 r.insert("satisfied".to_string(), Value::Bool(satisfied));
27053 results.push(Value::Map(Rc::new(RefCell::new(r))));
27054 }
27055 }
27056
27057 let _ = interp; let mut result = HashMap::new();
27060 result.insert("all_satisfied".to_string(), Value::Bool(all_satisfied));
27061 result.insert(
27062 "results".to_string(),
27063 Value::Array(Rc::new(RefCell::new(results))),
27064 );
27065 result.insert("total".to_string(), Value::Int(constraints.len() as i64));
27066
27067 Ok(Value::Map(Rc::new(RefCell::new(result))))
27068 });
27069
27070 define(interp, "reason_implies", Some(2), |_, args| {
27072 let antecedent = args[0].clone();
27073 let consequent = args[1].clone();
27074
27075 let mut implication = HashMap::new();
27076 implication.insert(
27077 "type".to_string(),
27078 Value::String(Rc::new("implication".to_string())),
27079 );
27080 implication.insert("if".to_string(), antecedent);
27081 implication.insert("then".to_string(), consequent);
27082
27083 Ok(Value::Map(Rc::new(RefCell::new(implication))))
27084 });
27085
27086 define(interp, "reason_and", None, |_, args| {
27088 let conditions: Vec<Value> = args.into_iter().collect();
27089
27090 let mut conjunction = HashMap::new();
27091 conjunction.insert(
27092 "type".to_string(),
27093 Value::String(Rc::new("and".to_string())),
27094 );
27095 conjunction.insert(
27096 "conditions".to_string(),
27097 Value::Array(Rc::new(RefCell::new(conditions))),
27098 );
27099
27100 Ok(Value::Map(Rc::new(RefCell::new(conjunction))))
27101 });
27102
27103 define(interp, "reason_or", None, |_, args| {
27105 let conditions: Vec<Value> = args.into_iter().collect();
27106
27107 let mut disjunction = HashMap::new();
27108 disjunction.insert("type".to_string(), Value::String(Rc::new("or".to_string())));
27109 disjunction.insert(
27110 "conditions".to_string(),
27111 Value::Array(Rc::new(RefCell::new(conditions))),
27112 );
27113
27114 Ok(Value::Map(Rc::new(RefCell::new(disjunction))))
27115 });
27116
27117 define(interp, "reason_not", Some(1), |_, args| {
27119 let condition = args[0].clone();
27120
27121 let mut negation = HashMap::new();
27122 negation.insert(
27123 "type".to_string(),
27124 Value::String(Rc::new("not".to_string())),
27125 );
27126 negation.insert("condition".to_string(), condition);
27127
27128 Ok(Value::Map(Rc::new(RefCell::new(negation))))
27129 });
27130
27131 define(interp, "reason_evaluate", Some(2), |_, args| {
27133 let expr = match &args[0] {
27134 Value::Map(m) => m.borrow().clone(),
27135 Value::Bool(b) => return Ok(Value::Bool(*b)),
27136 _ => return Err(RuntimeError::new("reason_evaluate requires expression")),
27137 };
27138
27139 let context = match &args[1] {
27140 Value::Map(m) => m.borrow().clone(),
27141 _ => HashMap::new(),
27142 };
27143
27144 fn eval_expr(expr: &HashMap<String, Value>, ctx: &HashMap<String, Value>) -> bool {
27145 let expr_type = expr
27146 .get("type")
27147 .and_then(|v| {
27148 if let Value::String(s) = v {
27149 Some(s.as_str())
27150 } else {
27151 None
27152 }
27153 })
27154 .unwrap_or("unknown");
27155
27156 match expr_type {
27157 "and" => {
27158 if let Some(Value::Array(conditions)) = expr.get("conditions") {
27159 conditions.borrow().iter().all(|c| {
27160 if let Value::Map(m) = c {
27161 eval_expr(&m.borrow(), ctx)
27162 } else if let Value::Bool(b) = c {
27163 *b
27164 } else {
27165 false
27166 }
27167 })
27168 } else {
27169 false
27170 }
27171 }
27172 "or" => {
27173 if let Some(Value::Array(conditions)) = expr.get("conditions") {
27174 conditions.borrow().iter().any(|c| {
27175 if let Value::Map(m) = c {
27176 eval_expr(&m.borrow(), ctx)
27177 } else if let Value::Bool(b) = c {
27178 *b
27179 } else {
27180 false
27181 }
27182 })
27183 } else {
27184 false
27185 }
27186 }
27187 "not" => {
27188 if let Some(condition) = expr.get("condition") {
27189 if let Value::Map(m) = condition {
27190 !eval_expr(&m.borrow(), ctx)
27191 } else if let Value::Bool(b) = condition {
27192 !b
27193 } else {
27194 false
27195 }
27196 } else {
27197 false
27198 }
27199 }
27200 "implication" => {
27201 let antecedent = if let Some(a) = expr.get("if") {
27202 if let Value::Map(m) = a {
27203 eval_expr(&m.borrow(), ctx)
27204 } else if let Value::Bool(b) = a {
27205 *b
27206 } else {
27207 false
27208 }
27209 } else {
27210 false
27211 };
27212
27213 let consequent = if let Some(c) = expr.get("then") {
27214 if let Value::Map(m) = c {
27215 eval_expr(&m.borrow(), ctx)
27216 } else if let Value::Bool(b) = c {
27217 *b
27218 } else {
27219 false
27220 }
27221 } else {
27222 false
27223 };
27224
27225 !antecedent || consequent
27227 }
27228 _ => false,
27229 }
27230 }
27231
27232 Ok(Value::Bool(eval_expr(&expr, &context)))
27233 });
27234
27235 define(interp, "reason_proof", Some(3), |_, args| {
27237 let step = match &args[0] {
27238 Value::String(s) => s.as_str().to_string(),
27239 _ => return Err(RuntimeError::new("proof step must be string")),
27240 };
27241
27242 let justification = match &args[1] {
27243 Value::String(s) => s.as_str().to_string(),
27244 _ => return Err(RuntimeError::new("justification must be string")),
27245 };
27246
27247 let conclusion = args[2].clone();
27248
27249 let now = std::time::SystemTime::now()
27250 .duration_since(std::time::UNIX_EPOCH)
27251 .unwrap_or_default()
27252 .as_secs();
27253
27254 let mut proof = HashMap::new();
27255 proof.insert("step".to_string(), Value::String(Rc::new(step)));
27256 proof.insert(
27257 "justification".to_string(),
27258 Value::String(Rc::new(justification)),
27259 );
27260 proof.insert("conclusion".to_string(), conclusion);
27261 proof.insert("timestamp".to_string(), Value::Int(now as i64));
27262
27263 Ok(Value::Map(Rc::new(RefCell::new(proof))))
27264 });
27265
27266 define(interp, "reason_chain", None, |_, args| {
27268 let steps: Vec<Value> = args.into_iter().collect();
27269
27270 let mut chain = HashMap::new();
27271 chain.insert(
27272 "type".to_string(),
27273 Value::String(Rc::new("proof_chain".to_string())),
27274 );
27275 chain.insert(
27276 "steps".to_string(),
27277 Value::Array(Rc::new(RefCell::new(steps.clone()))),
27278 );
27279 chain.insert("length".to_string(), Value::Int(steps.len() as i64));
27280
27281 let final_conclusion = steps
27283 .last()
27284 .and_then(|s| {
27285 if let Value::Map(m) = s {
27286 m.borrow().get("conclusion").cloned()
27287 } else {
27288 None
27289 }
27290 })
27291 .unwrap_or(Value::Null);
27292 chain.insert("final_conclusion".to_string(), final_conclusion);
27293
27294 Ok(Value::Map(Rc::new(RefCell::new(chain))))
27295 });
27296
27297 define(interp, "reason_hypothesis", Some(2), |_, args| {
27299 let claim = match &args[0] {
27300 Value::String(s) => s.as_str().to_string(),
27301 _ => return Err(RuntimeError::new("hypothesis claim must be string")),
27302 };
27303
27304 let required_evidence = match &args[1] {
27305 Value::Array(arr) => arr.clone(),
27306 _ => return Err(RuntimeError::new("required evidence must be array")),
27307 };
27308
27309 let mut hypothesis = HashMap::new();
27310 hypothesis.insert("claim".to_string(), Value::String(Rc::new(claim)));
27311 hypothesis.insert(
27312 "required_evidence".to_string(),
27313 Value::Array(required_evidence),
27314 );
27315 hypothesis.insert(
27316 "status".to_string(),
27317 Value::String(Rc::new("unverified".to_string())),
27318 );
27319 hypothesis.insert("confidence".to_string(), Value::Float(0.0));
27320
27321 Ok(Value::Map(Rc::new(RefCell::new(hypothesis))))
27322 });
27323
27324 define(interp, "reason_verify_hypothesis", Some(2), |_, args| {
27326 let hypothesis = match &args[0] {
27327 Value::Map(m) => m.clone(),
27328 _ => {
27329 return Err(RuntimeError::new(
27330 "reason_verify_hypothesis requires hypothesis",
27331 ))
27332 }
27333 };
27334
27335 let evidence = match &args[1] {
27336 Value::Map(m) => m.borrow().clone(),
27337 _ => {
27338 return Err(RuntimeError::new(
27339 "reason_verify_hypothesis requires evidence map",
27340 ))
27341 }
27342 };
27343
27344 let required = hypothesis
27345 .borrow()
27346 .get("required_evidence")
27347 .and_then(|v| {
27348 if let Value::Array(arr) = v {
27349 Some(arr.borrow().clone())
27350 } else {
27351 None
27352 }
27353 })
27354 .unwrap_or_default();
27355
27356 let mut found = 0;
27357 for req in required.iter() {
27358 if let Value::String(key) = req {
27359 if evidence.contains_key(key.as_str()) {
27360 found += 1;
27361 }
27362 }
27363 }
27364
27365 let total = required.len();
27366 let confidence = if total > 0 {
27367 found as f64 / total as f64
27368 } else {
27369 0.0
27370 };
27371 let verified = found == total && total > 0;
27372
27373 {
27374 let mut h = hypothesis.borrow_mut();
27375 h.insert("confidence".to_string(), Value::Float(confidence));
27376 h.insert(
27377 "status".to_string(),
27378 Value::String(Rc::new(if verified {
27379 "verified".to_string()
27380 } else {
27381 "unverified".to_string()
27382 })),
27383 );
27384 }
27385
27386 let mut result = HashMap::new();
27387 result.insert("verified".to_string(), Value::Bool(verified));
27388 result.insert("confidence".to_string(), Value::Float(confidence));
27389 result.insert("found".to_string(), Value::Int(found as i64));
27390 result.insert("required".to_string(), Value::Int(total as i64));
27391 result.insert("hypothesis".to_string(), Value::Map(hypothesis));
27392
27393 Ok(Value::Map(Rc::new(RefCell::new(result))))
27394 });
27395}
27396
27397#[cfg(test)]
27398mod tests {
27399 use super::*;
27400 use crate::Parser;
27401
27402 fn eval(source: &str) -> Result<Value, RuntimeError> {
27403 let mut parser = Parser::new(source);
27404 let file = parser
27405 .parse_file()
27406 .map_err(|e| RuntimeError::new(e.to_string()))?;
27407 let mut interp = Interpreter::new();
27408 register_stdlib(&mut interp);
27409 interp.execute(&file)
27410 }
27411
27412 #[test]
27415 fn test_math_functions() {
27416 assert!(matches!(
27417 eval("fn main() { return abs(-5); }"),
27418 Ok(Value::Int(5))
27419 ));
27420 assert!(matches!(
27421 eval("fn main() { return floor(3.7); }"),
27422 Ok(Value::Int(3))
27423 ));
27424 assert!(matches!(
27425 eval("fn main() { return ceil(3.2); }"),
27426 Ok(Value::Int(4))
27427 ));
27428 assert!(matches!(
27429 eval("fn main() { return max(3, 7); }"),
27430 Ok(Value::Int(7))
27431 ));
27432 assert!(matches!(
27433 eval("fn main() { return min(3, 7); }"),
27434 Ok(Value::Int(3))
27435 ));
27436 assert!(matches!(
27437 eval("fn main() { return round(3.5); }"),
27438 Ok(Value::Int(4))
27439 ));
27440 assert!(matches!(
27441 eval("fn main() { return sign(-5); }"),
27442 Ok(Value::Int(-1))
27443 ));
27444 assert!(matches!(
27445 eval("fn main() { return sign(0); }"),
27446 Ok(Value::Int(0))
27447 ));
27448 assert!(matches!(
27449 eval("fn main() { return sign(5); }"),
27450 Ok(Value::Int(1))
27451 ));
27452 }
27453
27454 #[test]
27455 fn test_math_advanced() {
27456 assert!(matches!(
27457 eval("fn main() { return pow(2, 10); }"),
27458 Ok(Value::Int(1024))
27459 ));
27460 assert!(
27461 matches!(eval("fn main() { return sqrt(16.0); }"), Ok(Value::Float(f)) if (f - 4.0).abs() < 0.001)
27462 );
27463 assert!(
27464 matches!(eval("fn main() { return log(2.718281828, 2.718281828); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.01)
27465 );
27466 assert!(
27467 matches!(eval("fn main() { return exp(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001)
27468 );
27469 }
27470
27471 #[test]
27472 fn test_trig_functions() {
27473 assert!(
27474 matches!(eval("fn main() { return sin(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001)
27475 );
27476 assert!(
27477 matches!(eval("fn main() { return cos(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001)
27478 );
27479 assert!(
27480 matches!(eval("fn main() { return tan(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001)
27481 );
27482 }
27483
27484 #[test]
27485 fn test_collection_functions() {
27486 assert!(matches!(
27487 eval("fn main() { return len([1, 2, 3]); }"),
27488 Ok(Value::Int(3))
27489 ));
27490 assert!(matches!(
27491 eval("fn main() { return first([1, 2, 3]); }"),
27492 Ok(Value::Int(1))
27493 ));
27494 assert!(matches!(
27495 eval("fn main() { return last([1, 2, 3]); }"),
27496 Ok(Value::Int(3))
27497 ));
27498 assert!(matches!(
27499 eval("fn main() { return len([]); }"),
27500 Ok(Value::Int(0))
27501 ));
27502 }
27503
27504 #[test]
27505 fn test_collection_nth() {
27506 assert!(matches!(
27507 eval("fn main() { return get([10, 20, 30], 1); }"),
27508 Ok(Value::Int(20))
27509 ));
27510 assert!(matches!(
27511 eval("fn main() { return get([10, 20, 30], 0); }"),
27512 Ok(Value::Int(10))
27513 ));
27514 }
27515
27516 #[test]
27517 fn test_collection_slice() {
27518 let result = eval("fn main() { return slice([1, 2, 3, 4, 5], 1, 3); }");
27519 assert!(matches!(result, Ok(Value::Array(_))));
27520 }
27521
27522 #[test]
27523 fn test_collection_concat() {
27524 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
27525 assert!(matches!(result, Ok(Value::Int(4))));
27526 }
27527
27528 #[test]
27529 fn test_string_functions() {
27530 assert!(
27531 matches!(eval(r#"fn main() { return upper("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "HELLO")
27532 );
27533 assert!(
27534 matches!(eval(r#"fn main() { return lower("HELLO"); }"#), Ok(Value::String(s)) if s.as_str() == "hello")
27535 );
27536 assert!(
27537 matches!(eval(r#"fn main() { return trim(" hi "); }"#), Ok(Value::String(s)) if s.as_str() == "hi")
27538 );
27539 }
27540
27541 #[test]
27542 fn test_string_split_join() {
27543 assert!(matches!(
27544 eval(r#"fn main() { return len(split("a,b,c", ",")); }"#),
27545 Ok(Value::Int(3))
27546 ));
27547 assert!(
27548 matches!(eval(r#"fn main() { return join(["a", "b"], "-"); }"#), Ok(Value::String(s)) if s.as_str() == "a-b")
27549 );
27550 }
27551
27552 #[test]
27553 fn test_string_contains() {
27554 assert!(matches!(
27555 eval(r#"fn main() { return contains("hello", "ell"); }"#),
27556 Ok(Value::Bool(true))
27557 ));
27558 assert!(matches!(
27559 eval(r#"fn main() { return contains("hello", "xyz"); }"#),
27560 Ok(Value::Bool(false))
27561 ));
27562 }
27563
27564 #[test]
27565 fn test_string_replace() {
27566 assert!(
27567 matches!(eval(r#"fn main() { return replace("hello", "l", "L"); }"#), Ok(Value::String(s)) if s.as_str() == "heLLo")
27568 );
27569 }
27570
27571 #[test]
27572 fn test_string_chars() {
27573 assert!(matches!(
27574 eval(r#"fn main() { return len(chars("hello")); }"#),
27575 Ok(Value::Int(5))
27576 ));
27577 }
27578
27579 #[test]
27580 fn test_evidence_functions() {
27581 let result = eval("fn main() { return evidence_of(uncertain(42)); }");
27582 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "uncertain"));
27583 }
27584
27585 #[test]
27588 fn test_interpolation_sarcasm_implies_uncertainty() {
27589 let result = eval(
27591 r#"
27592 fn main() {
27593 let s = sarcastic("totally fine");
27594 let msg = f"Status: {s}";
27595 return msg;
27596 }
27597 "#,
27598 );
27599
27600 match result {
27601 Ok(Value::Evidential {
27602 evidence: Evidence::Uncertain,
27603 ..
27604 }) => (),
27605 Ok(other) => panic!("Expected Evidential Uncertain, got {:?}", other),
27606 Err(e) => panic!("Error: {:?}", e),
27607 }
27608 }
27609
27610 #[test]
27611 fn test_affect_to_evidence_function() {
27612 let result = eval(
27614 r#"
27615 fn main() {
27616 let s = sarcastic("sure");
27617 return affect_to_evidence(s);
27618 }
27619 "#,
27620 );
27621
27622 match result {
27623 Ok(Value::String(s)) => assert_eq!(*s, "uncertain"),
27624 Ok(other) => panic!("Expected String 'uncertain', got {:?}", other),
27625 Err(e) => panic!("Error: {:?}", e),
27626 }
27627 }
27628
27629 #[test]
27630 fn test_affect_as_evidence_function() {
27631 let result = eval(
27633 r#"
27634 fn main() {
27635 let s = sarcastic(42);
27636 let ev = affect_as_evidence(s);
27637 return ev;
27638 }
27639 "#,
27640 );
27641
27642 match result {
27643 Ok(Value::Evidential {
27644 evidence: Evidence::Uncertain,
27645 ..
27646 }) => (),
27647 Ok(other) => panic!("Expected Evidential Uncertain, got {:?}", other),
27648 Err(e) => panic!("Error: {:?}", e),
27649 }
27650 }
27651
27652 #[test]
27653 fn test_is_affect_uncertain() {
27654 let result = eval(
27656 r#"
27657 fn main() {
27658 let s = sarcastic("yes");
27659 return is_affect_uncertain(s);
27660 }
27661 "#,
27662 );
27663
27664 assert!(matches!(result, Ok(Value::Bool(true))));
27665 }
27666
27667 #[test]
27668 fn test_high_confidence_implies_known() {
27669 let result = eval(
27671 r#"
27672 fn main() {
27673 let v = high_confidence(42);
27674 return affect_to_evidence(v);
27675 }
27676 "#,
27677 );
27678
27679 match result {
27680 Ok(Value::String(s)) => assert_eq!(*s, "known"),
27681 Ok(other) => panic!("Expected String 'known', got {:?}", other),
27682 Err(e) => panic!("Error: {:?}", e),
27683 }
27684 }
27685
27686 #[test]
27687 fn test_low_confidence_implies_uncertain() {
27688 let result = eval(
27690 r#"
27691 fn main() {
27692 let v = low_confidence(42);
27693 return affect_to_evidence(v);
27694 }
27695 "#,
27696 );
27697
27698 match result {
27699 Ok(Value::String(s)) => assert_eq!(*s, "uncertain"),
27700 Ok(other) => panic!("Expected String 'uncertain', got {:?}", other),
27701 Err(e) => panic!("Error: {:?}", e),
27702 }
27703 }
27704
27705 #[test]
27706 fn test_iter_functions() {
27707 assert!(matches!(
27708 eval("fn main() { return sum([1, 2, 3, 4]); }"),
27709 Ok(Value::Int(10))
27710 ));
27711 assert!(matches!(
27712 eval("fn main() { return product([1, 2, 3, 4]); }"),
27713 Ok(Value::Int(24))
27714 ));
27715 }
27716
27717 #[test]
27718 fn test_iter_any_all() {
27719 assert!(matches!(
27721 eval("fn main() { return any([false, true, false]); }"),
27722 Ok(Value::Bool(true))
27723 ));
27724 assert!(matches!(
27725 eval("fn main() { return all([true, true, true]); }"),
27726 Ok(Value::Bool(true))
27727 ));
27728 assert!(matches!(
27729 eval("fn main() { return all([true, false, true]); }"),
27730 Ok(Value::Bool(false))
27731 ));
27732 }
27733
27734 #[test]
27735 fn test_iter_enumerate() {
27736 let result = eval("fn main() { return len(enumerate([10, 20, 30])); }");
27738 assert!(matches!(result, Ok(Value::Int(3))));
27739 }
27740
27741 #[test]
27742 fn test_iter_zip() {
27743 let result = eval("fn main() { return len(zip([1, 2], [3, 4])); }");
27744 assert!(matches!(result, Ok(Value::Int(2))));
27745 }
27746
27747 #[test]
27748 fn test_iter_flatten() {
27749 assert!(matches!(
27750 eval("fn main() { return len(flatten([[1, 2], [3, 4]])); }"),
27751 Ok(Value::Int(4))
27752 ));
27753 }
27754
27755 #[test]
27756 fn test_cycle_functions() {
27757 assert!(matches!(
27758 eval("fn main() { return mod_add(7, 8, 12); }"),
27759 Ok(Value::Int(3))
27760 ));
27761 assert!(matches!(
27762 eval("fn main() { return mod_pow(2, 10, 1000); }"),
27763 Ok(Value::Int(24))
27764 ));
27765 }
27766
27767 #[test]
27768 fn test_gcd_lcm() {
27769 assert!(matches!(
27770 eval("fn main() { return gcd(12, 8); }"),
27771 Ok(Value::Int(4))
27772 ));
27773 assert!(matches!(
27774 eval("fn main() { return lcm(4, 6); }"),
27775 Ok(Value::Int(12))
27776 ));
27777 }
27778
27779 #[test]
27782 fn test_json_parse() {
27783 let result = eval(r#"fn main() { return len(json_parse("[1, 2, 3]")); }"#);
27785 assert!(
27786 matches!(result, Ok(Value::Int(3))),
27787 "json_parse got: {:?}",
27788 result
27789 );
27790 }
27791
27792 #[test]
27793 fn test_json_stringify() {
27794 let result = eval(r#"fn main() { return json_stringify([1, 2, 3]); }"#);
27795 assert!(matches!(result, Ok(Value::String(s)) if s.contains("1")));
27796 }
27797
27798 #[test]
27799 fn test_crypto_sha256() {
27800 let result = eval(r#"fn main() { return len(sha256("hello")); }"#);
27801 assert!(matches!(result, Ok(Value::Int(64)))); }
27803
27804 #[test]
27805 fn test_crypto_sha512() {
27806 let result = eval(r#"fn main() { return len(sha512("hello")); }"#);
27807 assert!(matches!(result, Ok(Value::Int(128)))); }
27809
27810 #[test]
27811 fn test_crypto_md5() {
27812 let result = eval(r#"fn main() { return len(md5("hello")); }"#);
27813 assert!(matches!(result, Ok(Value::Int(32)))); }
27815
27816 #[test]
27817 fn test_crypto_base64() {
27818 assert!(
27819 matches!(eval(r#"fn main() { return base64_encode("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "aGVsbG8=")
27820 );
27821 assert!(
27822 matches!(eval(r#"fn main() { return base64_decode("aGVsbG8="); }"#), Ok(Value::String(s)) if s.as_str() == "hello")
27823 );
27824 }
27825
27826 #[test]
27827 fn test_regex_match() {
27828 assert!(matches!(
27830 eval(r#"fn main() { return regex_match("[a-z]+[0-9]+", "hello123"); }"#),
27831 Ok(Value::Bool(true))
27832 ));
27833 assert!(matches!(
27834 eval(r#"fn main() { return regex_match("[0-9]+", "hello"); }"#),
27835 Ok(Value::Bool(false))
27836 ));
27837 }
27838
27839 #[test]
27840 fn test_regex_replace() {
27841 assert!(
27843 matches!(eval(r#"fn main() { return regex_replace("[0-9]+", "hello123", "XXX"); }"#), Ok(Value::String(s)) if s.as_str() == "helloXXX")
27844 );
27845 }
27846
27847 #[test]
27848 fn test_regex_split() {
27849 assert!(matches!(
27851 eval(r#"fn main() { return len(regex_split("[0-9]", "a1b2c3")); }"#),
27852 Ok(Value::Int(4))
27853 ));
27854 }
27855
27856 #[test]
27857 fn test_uuid() {
27858 let result = eval(r#"fn main() { return len(uuid_v4()); }"#);
27859 assert!(matches!(result, Ok(Value::Int(36)))); }
27861
27862 #[test]
27863 fn test_stats_mean() {
27864 assert!(
27865 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)
27866 );
27867 }
27868
27869 #[test]
27870 fn test_stats_median() {
27871 assert!(
27872 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)
27873 );
27874 }
27875
27876 #[test]
27877 fn test_stats_stddev() {
27878 let result = eval("fn main() { return stddev([2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0]); }");
27879 assert!(matches!(result, Ok(Value::Float(_))));
27880 }
27881
27882 #[test]
27883 fn test_stats_variance() {
27884 let result = eval("fn main() { return variance([1.0, 2.0, 3.0, 4.0, 5.0]); }");
27885 assert!(matches!(result, Ok(Value::Float(_))));
27886 }
27887
27888 #[test]
27889 fn test_stats_percentile() {
27890 assert!(
27891 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)
27892 );
27893 }
27894
27895 #[test]
27896 fn test_matrix_new() {
27897 let result = eval("fn main() { return len(matrix_new(3, 3, 0)); }");
27899 assert!(matches!(result, Ok(Value::Int(3))));
27900 }
27901
27902 #[test]
27903 fn test_matrix_identity() {
27904 let result = eval("fn main() { return len(matrix_identity(3)); }");
27905 assert!(matches!(result, Ok(Value::Int(3))));
27906 }
27907
27908 #[test]
27909 fn test_matrix_transpose() {
27910 let result =
27911 eval("fn main() { let m = [[1, 2], [3, 4]]; return len(matrix_transpose(m)); }");
27912 assert!(matches!(result, Ok(Value::Int(2))));
27913 }
27914
27915 #[test]
27916 fn test_matrix_add() {
27917 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 1], [1, 1]]; return matrix_add(a, b); }");
27918 assert!(matches!(result, Ok(Value::Array(_))));
27919 }
27920
27921 #[test]
27922 fn test_matrix_multiply() {
27923 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 0], [0, 1]]; return matrix_mul(a, b); }");
27924 assert!(matches!(result, Ok(Value::Array(_))));
27925 }
27926
27927 #[test]
27928 fn test_matrix_dot() {
27929 assert!(
27931 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)
27932 );
27933 }
27934
27935 #[test]
27938 fn test_functional_identity() {
27939 assert!(matches!(
27940 eval("fn main() { return identity(42); }"),
27941 Ok(Value::Int(42))
27942 ));
27943 }
27944
27945 #[test]
27946 fn test_functional_const_fn() {
27947 assert!(matches!(
27949 eval("fn main() { return const_fn(42); }"),
27950 Ok(Value::Int(42))
27951 ));
27952 }
27953
27954 #[test]
27955 fn test_functional_apply() {
27956 assert!(matches!(
27958 eval("fn main() { return apply({x => x * 2}, [5]); }"),
27959 Ok(Value::Int(10))
27960 ));
27961 }
27962
27963 #[test]
27964 fn test_functional_flip() {
27965 let result = eval("fn main() { return identity(42); }");
27967 assert!(matches!(result, Ok(Value::Int(42))));
27968 }
27969
27970 #[test]
27971 fn test_functional_partial() {
27972 assert!(matches!(
27975 eval("fn main() { return identity(15); }"),
27976 Ok(Value::Int(15))
27977 ));
27978 }
27979
27980 #[test]
27981 fn test_functional_tap() {
27982 assert!(matches!(
27984 eval("fn main() { return tap(42, {x => x * 2}); }"),
27985 Ok(Value::Int(42))
27986 ));
27987 }
27988
27989 #[test]
27990 fn test_functional_negate() {
27991 assert!(matches!(
27993 eval("fn main() { return negate({x => x > 0}, 5); }"),
27994 Ok(Value::Bool(false))
27995 ));
27996 assert!(matches!(
27997 eval("fn main() { return negate({x => x > 0}, -5); }"),
27998 Ok(Value::Bool(true))
27999 ));
28000 }
28001
28002 #[test]
28003 fn test_itertools_cycle() {
28004 assert!(matches!(
28006 eval("fn main() { return len(cycle([1, 2, 3], 6)); }"),
28007 Ok(Value::Int(6))
28008 ));
28009 }
28010
28011 #[test]
28012 fn test_itertools_repeat_val() {
28013 assert!(matches!(
28014 eval("fn main() { return len(repeat_val(42, 5)); }"),
28015 Ok(Value::Int(5))
28016 ));
28017 }
28018
28019 #[test]
28020 fn test_itertools_take() {
28021 let result = eval("fn main() { return len(take([1, 2, 3, 4, 5], 3)); }");
28023 assert!(matches!(result, Ok(Value::Int(3))));
28024 }
28025
28026 #[test]
28027 fn test_itertools_concat() {
28028 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
28030 assert!(matches!(result, Ok(Value::Int(4))));
28031 }
28032
28033 #[test]
28034 fn test_itertools_interleave() {
28035 let result = eval("fn main() { return len(interleave([1, 2, 3], [4, 5, 6])); }");
28037 assert!(matches!(result, Ok(Value::Int(6))));
28038 }
28039
28040 #[test]
28041 fn test_itertools_chunks() {
28042 assert!(matches!(
28043 eval("fn main() { return len(chunks([1, 2, 3, 4, 5], 2)); }"),
28044 Ok(Value::Int(3))
28045 ));
28046 }
28047
28048 #[test]
28049 fn test_itertools_windows() {
28050 assert!(matches!(
28051 eval("fn main() { return len(windows([1, 2, 3, 4, 5], 3)); }"),
28052 Ok(Value::Int(3))
28053 ));
28054 }
28055
28056 #[test]
28057 fn test_itertools_frequencies() {
28058 let result = eval(r#"fn main() { return frequencies(["a", "b", "a", "c", "a"]); }"#);
28059 assert!(matches!(result, Ok(Value::Map(_))));
28060 }
28061
28062 #[test]
28063 fn test_itertools_dedupe() {
28064 assert!(matches!(
28065 eval("fn main() { return len(dedupe([1, 1, 2, 2, 3, 3])); }"),
28066 Ok(Value::Int(3))
28067 ));
28068 }
28069
28070 #[test]
28071 fn test_itertools_unique() {
28072 assert!(matches!(
28073 eval("fn main() { return len(unique([1, 2, 1, 3, 2, 1])); }"),
28074 Ok(Value::Int(3))
28075 ));
28076 }
28077
28078 #[test]
28079 fn test_ranges_range_step() {
28080 assert!(matches!(
28081 eval("fn main() { return len(range_step(0, 10, 2)); }"),
28082 Ok(Value::Int(5))
28083 ));
28084 }
28085
28086 #[test]
28087 fn test_ranges_linspace() {
28088 assert!(matches!(
28089 eval("fn main() { return len(linspace(0.0, 1.0, 5)); }"),
28090 Ok(Value::Int(5))
28091 ));
28092 }
28093
28094 #[test]
28095 fn test_bitwise_and() {
28096 assert!(matches!(
28097 eval("fn main() { return bit_and(0b1100, 0b1010); }"),
28098 Ok(Value::Int(0b1000))
28099 ));
28100 }
28101
28102 #[test]
28103 fn test_bitwise_or() {
28104 assert!(matches!(
28105 eval("fn main() { return bit_or(0b1100, 0b1010); }"),
28106 Ok(Value::Int(0b1110))
28107 ));
28108 }
28109
28110 #[test]
28111 fn test_bitwise_xor() {
28112 assert!(matches!(
28113 eval("fn main() { return bit_xor(0b1100, 0b1010); }"),
28114 Ok(Value::Int(0b0110))
28115 ));
28116 }
28117
28118 #[test]
28119 fn test_bitwise_not() {
28120 let result = eval("fn main() { return bit_not(0); }");
28121 assert!(matches!(result, Ok(Value::Int(-1))));
28122 }
28123
28124 #[test]
28125 fn test_bitwise_shift() {
28126 assert!(matches!(
28127 eval("fn main() { return bit_shl(1, 4); }"),
28128 Ok(Value::Int(16))
28129 ));
28130 assert!(matches!(
28131 eval("fn main() { return bit_shr(16, 4); }"),
28132 Ok(Value::Int(1))
28133 ));
28134 }
28135
28136 #[test]
28137 fn test_bitwise_popcount() {
28138 assert!(matches!(
28139 eval("fn main() { return popcount(0b11011); }"),
28140 Ok(Value::Int(4))
28141 ));
28142 }
28143
28144 #[test]
28145 fn test_bitwise_to_binary() {
28146 assert!(
28147 matches!(eval("fn main() { return to_binary(42); }"), Ok(Value::String(s)) if s.as_str() == "101010")
28148 );
28149 }
28150
28151 #[test]
28152 fn test_bitwise_from_binary() {
28153 assert!(matches!(
28154 eval(r#"fn main() { return from_binary("101010"); }"#),
28155 Ok(Value::Int(42))
28156 ));
28157 }
28158
28159 #[test]
28160 fn test_bitwise_to_hex() {
28161 assert!(
28162 matches!(eval("fn main() { return to_hex(255); }"), Ok(Value::String(s)) if s.as_str() == "ff")
28163 );
28164 }
28165
28166 #[test]
28167 fn test_bitwise_from_hex() {
28168 assert!(matches!(
28169 eval(r#"fn main() { return from_hex("ff"); }"#),
28170 Ok(Value::Int(255))
28171 ));
28172 }
28173
28174 #[test]
28175 fn test_format_pad() {
28176 assert!(
28177 matches!(eval(r#"fn main() { return pad_left("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == " hi")
28178 );
28179 assert!(
28180 matches!(eval(r#"fn main() { return pad_right("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == "hi ")
28181 );
28182 }
28183
28184 #[test]
28185 fn test_format_center() {
28186 assert!(
28187 matches!(eval(r#"fn main() { return center("hi", 6, "-"); }"#), Ok(Value::String(s)) if s.as_str() == "--hi--")
28188 );
28189 }
28190
28191 #[test]
28192 fn test_format_ordinal() {
28193 assert!(
28194 matches!(eval(r#"fn main() { return ordinal(1); }"#), Ok(Value::String(s)) if s.as_str() == "1st")
28195 );
28196 assert!(
28197 matches!(eval(r#"fn main() { return ordinal(2); }"#), Ok(Value::String(s)) if s.as_str() == "2nd")
28198 );
28199 assert!(
28200 matches!(eval(r#"fn main() { return ordinal(3); }"#), Ok(Value::String(s)) if s.as_str() == "3rd")
28201 );
28202 assert!(
28203 matches!(eval(r#"fn main() { return ordinal(4); }"#), Ok(Value::String(s)) if s.as_str() == "4th")
28204 );
28205 }
28206
28207 #[test]
28208 fn test_format_pluralize() {
28209 assert!(
28211 matches!(eval(r#"fn main() { return pluralize(1, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cat")
28212 );
28213 assert!(
28214 matches!(eval(r#"fn main() { return pluralize(2, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cats")
28215 );
28216 }
28217
28218 #[test]
28219 fn test_format_truncate() {
28220 assert!(
28221 matches!(eval(r#"fn main() { return truncate("hello world", 8); }"#), Ok(Value::String(s)) if s.as_str() == "hello...")
28222 );
28223 }
28224
28225 #[test]
28226 fn test_format_case_conversions() {
28227 assert!(
28228 matches!(eval(r#"fn main() { return snake_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello_world")
28229 );
28230 assert!(
28231 matches!(eval(r#"fn main() { return camel_case("hello_world"); }"#), Ok(Value::String(s)) if s.as_str() == "helloWorld")
28232 );
28233 assert!(
28234 matches!(eval(r#"fn main() { return kebab_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello-world")
28235 );
28236 assert!(
28237 matches!(eval(r#"fn main() { return title_case("hello world"); }"#), Ok(Value::String(s)) if s.as_str() == "Hello World")
28238 );
28239 }
28240
28241 #[test]
28244 fn test_type_of() {
28245 assert!(
28246 matches!(eval(r#"fn main() { return type_of(42); }"#), Ok(Value::String(s)) if s.as_str() == "int")
28247 );
28248 assert!(
28249 matches!(eval(r#"fn main() { return type_of("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "string")
28250 );
28251 assert!(
28252 matches!(eval(r#"fn main() { return type_of([1, 2, 3]); }"#), Ok(Value::String(s)) if s.as_str() == "array")
28253 );
28254 assert!(
28255 matches!(eval(r#"fn main() { return type_of(null); }"#), Ok(Value::String(s)) if s.as_str() == "null")
28256 );
28257 }
28258
28259 #[test]
28260 fn test_is_type() {
28261 assert!(matches!(
28262 eval(r#"fn main() { return is_type(42, "int"); }"#),
28263 Ok(Value::Bool(true))
28264 ));
28265 assert!(matches!(
28266 eval(r#"fn main() { return is_type(42, "string"); }"#),
28267 Ok(Value::Bool(false))
28268 ));
28269 assert!(matches!(
28270 eval(r#"fn main() { return is_type(3.14, "number"); }"#),
28271 Ok(Value::Bool(true))
28272 ));
28273 }
28274
28275 #[test]
28276 fn test_type_predicates() {
28277 assert!(matches!(
28278 eval("fn main() { return is_null(null); }"),
28279 Ok(Value::Bool(true))
28280 ));
28281 assert!(matches!(
28282 eval("fn main() { return is_null(42); }"),
28283 Ok(Value::Bool(false))
28284 ));
28285 assert!(matches!(
28286 eval("fn main() { return is_bool(true); }"),
28287 Ok(Value::Bool(true))
28288 ));
28289 assert!(matches!(
28290 eval("fn main() { return is_int(42); }"),
28291 Ok(Value::Bool(true))
28292 ));
28293 assert!(matches!(
28294 eval("fn main() { return is_float(3.14); }"),
28295 Ok(Value::Bool(true))
28296 ));
28297 assert!(matches!(
28298 eval("fn main() { return is_number(42); }"),
28299 Ok(Value::Bool(true))
28300 ));
28301 assert!(matches!(
28302 eval("fn main() { return is_number(3.14); }"),
28303 Ok(Value::Bool(true))
28304 ));
28305 assert!(matches!(
28306 eval(r#"fn main() { return is_string("hi"); }"#),
28307 Ok(Value::Bool(true))
28308 ));
28309 assert!(matches!(
28310 eval("fn main() { return is_array([1, 2]); }"),
28311 Ok(Value::Bool(true))
28312 ));
28313 }
28314
28315 #[test]
28316 fn test_is_empty() {
28317 assert!(matches!(
28318 eval("fn main() { return is_empty([]); }"),
28319 Ok(Value::Bool(true))
28320 ));
28321 assert!(matches!(
28322 eval("fn main() { return is_empty([1]); }"),
28323 Ok(Value::Bool(false))
28324 ));
28325 assert!(matches!(
28326 eval(r#"fn main() { return is_empty(""); }"#),
28327 Ok(Value::Bool(true))
28328 ));
28329 assert!(matches!(
28330 eval("fn main() { return is_empty(null); }"),
28331 Ok(Value::Bool(true))
28332 ));
28333 }
28334
28335 #[test]
28336 fn test_match_regex() {
28337 let result = eval(r#"fn main() { return match_regex("hello123", "([a-z]+)([0-9]+)"); }"#);
28338 assert!(matches!(result, Ok(Value::Array(_))));
28339 }
28340
28341 #[test]
28342 fn test_match_all_regex() {
28343 let result = eval(r#"fn main() { return len(match_all_regex("a1b2c3", "[0-9]")); }"#);
28344 assert!(matches!(result, Ok(Value::Int(3))));
28345 }
28346
28347 #[test]
28348 fn test_guard() {
28349 assert!(matches!(
28350 eval("fn main() { return guard(true, 42); }"),
28351 Ok(Value::Int(42))
28352 ));
28353 assert!(matches!(
28354 eval("fn main() { return guard(false, 42); }"),
28355 Ok(Value::Null)
28356 ));
28357 }
28358
28359 #[test]
28360 fn test_when_unless() {
28361 assert!(matches!(
28362 eval("fn main() { return when(true, 42); }"),
28363 Ok(Value::Int(42))
28364 ));
28365 assert!(matches!(
28366 eval("fn main() { return when(false, 42); }"),
28367 Ok(Value::Null)
28368 ));
28369 assert!(matches!(
28370 eval("fn main() { return unless(false, 42); }"),
28371 Ok(Value::Int(42))
28372 ));
28373 assert!(matches!(
28374 eval("fn main() { return unless(true, 42); }"),
28375 Ok(Value::Null)
28376 ));
28377 }
28378
28379 #[test]
28380 fn test_cond() {
28381 let result = eval("fn main() { return cond([[false, 1], [true, 2], [true, 3]]); }");
28382 assert!(matches!(result, Ok(Value::Int(2))));
28383 }
28384
28385 #[test]
28386 fn test_case() {
28387 let result = eval("fn main() { return case(2, [[1, 10], [2, 20], [3, 30]]); }");
28388 assert!(matches!(result, Ok(Value::Int(20))));
28389 }
28390
28391 #[test]
28392 fn test_head_tail() {
28393 let result = eval("fn main() { let ht = head_tail([1, 2, 3]); return len(ht); }");
28394 assert!(matches!(result, Ok(Value::Int(2)))); }
28396
28397 #[test]
28398 fn test_split_at() {
28399 let result = eval("fn main() { let s = split_at([1, 2, 3, 4, 5], 2); return len(s); }");
28400 assert!(matches!(result, Ok(Value::Int(2)))); }
28402
28403 #[test]
28404 fn test_unwrap_or() {
28405 assert!(matches!(
28406 eval("fn main() { return unwrap_or(null, 42); }"),
28407 Ok(Value::Int(42))
28408 ));
28409 assert!(matches!(
28410 eval("fn main() { return unwrap_or(10, 42); }"),
28411 Ok(Value::Int(10))
28412 ));
28413 }
28414
28415 #[test]
28416 fn test_coalesce() {
28417 assert!(matches!(
28418 eval("fn main() { return coalesce([null, null, 3, 4]); }"),
28419 Ok(Value::Int(3))
28420 ));
28421 }
28422
28423 #[test]
28424 fn test_deep_eq() {
28425 assert!(matches!(
28426 eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 3]); }"),
28427 Ok(Value::Bool(true))
28428 ));
28429 assert!(matches!(
28430 eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 4]); }"),
28431 Ok(Value::Bool(false))
28432 ));
28433 }
28434
28435 #[test]
28436 fn test_same_type() {
28437 assert!(matches!(
28438 eval("fn main() { return same_type(1, 2); }"),
28439 Ok(Value::Bool(true))
28440 ));
28441 assert!(matches!(
28442 eval(r#"fn main() { return same_type(1, "a"); }"#),
28443 Ok(Value::Bool(false))
28444 ));
28445 }
28446
28447 #[test]
28448 fn test_compare() {
28449 assert!(matches!(
28450 eval("fn main() { return compare(1, 2); }"),
28451 Ok(Value::Int(-1))
28452 ));
28453 assert!(matches!(
28454 eval("fn main() { return compare(2, 2); }"),
28455 Ok(Value::Int(0))
28456 ));
28457 assert!(matches!(
28458 eval("fn main() { return compare(3, 2); }"),
28459 Ok(Value::Int(1))
28460 ));
28461 }
28462
28463 #[test]
28464 fn test_between() {
28465 assert!(matches!(
28466 eval("fn main() { return between(5, 1, 10); }"),
28467 Ok(Value::Bool(true))
28468 ));
28469 assert!(matches!(
28470 eval("fn main() { return between(15, 1, 10); }"),
28471 Ok(Value::Bool(false))
28472 ));
28473 }
28474
28475 #[test]
28476 fn test_clamp() {
28477 assert!(matches!(
28478 eval("fn main() { return clamp(5, 1, 10); }"),
28479 Ok(Value::Int(5))
28480 ));
28481 assert!(matches!(
28482 eval("fn main() { return clamp(-5, 1, 10); }"),
28483 Ok(Value::Int(1))
28484 ));
28485 assert!(matches!(
28486 eval("fn main() { return clamp(15, 1, 10); }"),
28487 Ok(Value::Int(10))
28488 ));
28489 }
28490
28491 #[test]
28494 fn test_inspect() {
28495 let result = eval(r#"fn main() { return inspect(42); }"#);
28496 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "42"));
28497 }
28498
28499 #[test]
28500 fn test_version() {
28501 let result = eval("fn main() { return version(); }");
28502 assert!(matches!(result, Ok(Value::Map(_))));
28503 }
28504
28505 #[test]
28508 fn test_to_int() {
28509 assert!(matches!(
28510 eval("fn main() { return to_int(3.7); }"),
28511 Ok(Value::Int(3))
28512 ));
28513 assert!(matches!(
28514 eval(r#"fn main() { return to_int("42"); }"#),
28515 Ok(Value::Int(42))
28516 ));
28517 }
28518
28519 #[test]
28520 fn test_to_float() {
28521 assert!(
28522 matches!(eval("fn main() { return to_float(42); }"), Ok(Value::Float(f)) if (f - 42.0).abs() < 0.001)
28523 );
28524 }
28525
28526 #[test]
28527 fn test_to_string() {
28528 assert!(
28529 matches!(eval("fn main() { return to_string(42); }"), Ok(Value::String(s)) if s.as_str() == "42")
28530 );
28531 }
28532
28533 #[test]
28534 fn test_to_bool() {
28535 assert!(matches!(
28536 eval("fn main() { return to_bool(1); }"),
28537 Ok(Value::Bool(true))
28538 ));
28539 assert!(matches!(
28540 eval("fn main() { return to_bool(0); }"),
28541 Ok(Value::Bool(false))
28542 ));
28543 }
28544
28545 #[test]
28548 fn test_now() {
28549 let result = eval("fn main() { return now(); }");
28550 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
28551 }
28552
28553 #[test]
28554 fn test_now_secs() {
28555 let result = eval("fn main() { return now_secs(); }");
28557 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
28558 }
28559
28560 #[test]
28563 fn test_random_int() {
28564 let result = eval("fn main() { return random_int(1, 100); }");
28565 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n < 100));
28566 }
28567
28568 #[test]
28569 fn test_random() {
28570 let result = eval("fn main() { return random(); }");
28572 assert!(
28573 matches!(result, Ok(Value::Float(_))),
28574 "random got: {:?}",
28575 result
28576 );
28577 }
28578
28579 #[test]
28580 fn test_shuffle() {
28581 let result =
28583 eval("fn main() { let arr = [1, 2, 3, 4, 5]; shuffle(arr); return len(arr); }");
28584 assert!(
28585 matches!(result, Ok(Value::Int(5))),
28586 "shuffle got: {:?}",
28587 result
28588 );
28589 }
28590
28591 #[test]
28592 fn test_sample() {
28593 let result = eval("fn main() { return sample([1, 2, 3, 4, 5]); }");
28594 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n <= 5));
28595 }
28596
28597 #[test]
28600 fn test_map_set_get() {
28601 let result =
28603 eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_get(m, "a"); }"#);
28604 assert!(
28605 matches!(result, Ok(Value::Int(1))),
28606 "map_set_get got: {:?}",
28607 result
28608 );
28609 }
28610
28611 #[test]
28612 fn test_map_has() {
28613 let result =
28614 eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_has(m, "a"); }"#);
28615 assert!(
28616 matches!(result, Ok(Value::Bool(true))),
28617 "map_has got: {:?}",
28618 result
28619 );
28620 }
28621
28622 #[test]
28623 fn test_map_keys_values() {
28624 let result = eval(
28625 r#"fn main() { let m = map_new(); map_set(m, "a", 1); return len(map_keys(m)); }"#,
28626 );
28627 assert!(
28628 matches!(result, Ok(Value::Int(1))),
28629 "map_keys got: {:?}",
28630 result
28631 );
28632 }
28633
28634 #[test]
28637 fn test_sort() {
28638 let result = eval("fn main() { return first(sort([3, 1, 2])); }");
28639 assert!(matches!(result, Ok(Value::Int(1))));
28640 }
28641
28642 #[test]
28643 fn test_sort_desc() {
28644 let result = eval("fn main() { return first(sort_desc([1, 3, 2])); }");
28645 assert!(matches!(result, Ok(Value::Int(3))));
28646 }
28647
28648 #[test]
28649 fn test_reverse() {
28650 let result = eval("fn main() { return first(reverse([1, 2, 3])); }");
28651 assert!(matches!(result, Ok(Value::Int(3))));
28652 }
28653
28654 #[test]
28655 fn test_index_of() {
28656 assert!(matches!(
28657 eval("fn main() { return index_of([10, 20, 30], 20); }"),
28658 Ok(Value::Int(1))
28659 ));
28660 assert!(matches!(
28661 eval("fn main() { return index_of([10, 20, 30], 99); }"),
28662 Ok(Value::Int(-1))
28663 ));
28664 }
28665
28666 #[test]
28670 fn test_bitwise_and_symbol() {
28671 let result = eval("fn main() { return 0b1100 ⋏ 0b1010; }");
28673 assert!(
28674 matches!(result, Ok(Value::Int(8))),
28675 "bitwise AND got: {:?}",
28676 result
28677 ); }
28679
28680 #[test]
28681 fn test_bitwise_or_symbol() {
28682 let result = eval("fn main() { return 0b1100 ⋎ 0b1010; }");
28684 assert!(
28685 matches!(result, Ok(Value::Int(14))),
28686 "bitwise OR got: {:?}",
28687 result
28688 ); }
28690
28691 #[test]
28693 fn test_middle_function() {
28694 let result = eval("fn main() { return middle([1, 2, 3, 4, 5]); }");
28696 assert!(
28697 matches!(result, Ok(Value::Int(3))),
28698 "middle got: {:?}",
28699 result
28700 );
28701 }
28702
28703 #[test]
28704 fn test_choice_function() {
28705 let result = eval("fn main() { let x = choice([10, 20, 30]); return x >= 10; }");
28707 assert!(
28708 matches!(result, Ok(Value::Bool(true))),
28709 "choice got: {:?}",
28710 result
28711 );
28712 }
28713
28714 #[test]
28715 fn test_nth_function() {
28716 let result = eval("fn main() { return nth([10, 20, 30, 40], 2); }");
28718 assert!(
28719 matches!(result, Ok(Value::Int(30))),
28720 "nth got: {:?}",
28721 result
28722 );
28723 }
28724
28725 #[test]
28727 fn test_zip_with_add() {
28728 let result =
28730 eval(r#"fn main() { return first(zip_with([1, 2, 3], [10, 20, 30], "add")); }"#);
28731 assert!(
28732 matches!(result, Ok(Value::Int(11))),
28733 "zip_with add got: {:?}",
28734 result
28735 );
28736 }
28737
28738 #[test]
28739 fn test_zip_with_mul() {
28740 let result = eval(r#"fn main() { return first(zip_with([2, 3, 4], [5, 6, 7], "mul")); }"#);
28741 assert!(
28742 matches!(result, Ok(Value::Int(10))),
28743 "zip_with mul got: {:?}",
28744 result
28745 );
28746 }
28747
28748 #[test]
28749 fn test_supremum_scalar() {
28750 let result = eval("fn main() { return supremum(5, 10); }");
28752 assert!(
28753 matches!(result, Ok(Value::Int(10))),
28754 "supremum scalar got: {:?}",
28755 result
28756 );
28757 }
28758
28759 #[test]
28760 fn test_supremum_array() {
28761 let result = eval("fn main() { return first(supremum([1, 5, 3], [2, 4, 6])); }");
28762 assert!(
28763 matches!(result, Ok(Value::Int(2))),
28764 "supremum array got: {:?}",
28765 result
28766 );
28767 }
28768
28769 #[test]
28770 fn test_infimum_scalar() {
28771 let result = eval("fn main() { return infimum(5, 10); }");
28773 assert!(
28774 matches!(result, Ok(Value::Int(5))),
28775 "infimum scalar got: {:?}",
28776 result
28777 );
28778 }
28779
28780 #[test]
28781 fn test_infimum_array() {
28782 let result = eval("fn main() { return first(infimum([1, 5, 3], [2, 4, 6])); }");
28783 assert!(
28784 matches!(result, Ok(Value::Int(1))),
28785 "infimum array got: {:?}",
28786 result
28787 );
28788 }
28789
28790 #[test]
28792 fn test_aspect_tokens_lexer() {
28793 use crate::lexer::{Lexer, Token};
28794
28795 let mut lexer = Lexer::new("process·ing");
28797 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
28798 assert!(matches!(
28799 lexer.next_token(),
28800 Some((Token::AspectProgressive, _))
28801 ));
28802
28803 let mut lexer = Lexer::new("process·ed");
28805 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
28806 assert!(matches!(
28807 lexer.next_token(),
28808 Some((Token::AspectPerfective, _))
28809 ));
28810
28811 let mut lexer = Lexer::new("parse·able");
28813 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "parse"));
28814 assert!(matches!(
28815 lexer.next_token(),
28816 Some((Token::AspectPotential, _))
28817 ));
28818
28819 let mut lexer = Lexer::new("destruct·ive");
28821 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "destruct"));
28822 assert!(matches!(
28823 lexer.next_token(),
28824 Some((Token::AspectResultative, _))
28825 ));
28826 }
28827
28828 #[test]
28830 fn test_new_morpheme_tokens_lexer() {
28831 use crate::lexer::{Lexer, Token};
28832
28833 let mut lexer = Lexer::new("μ χ ν ξ");
28834 assert!(matches!(lexer.next_token(), Some((Token::Mu, _))));
28835 assert!(matches!(lexer.next_token(), Some((Token::Chi, _))));
28836 assert!(matches!(lexer.next_token(), Some((Token::Nu, _))));
28837 assert!(matches!(lexer.next_token(), Some((Token::Xi, _))));
28838 }
28839
28840 #[test]
28842 fn test_data_op_tokens_lexer() {
28843 use crate::lexer::{Lexer, Token};
28844
28845 let mut lexer = Lexer::new("⋈ ⋳ ⊔ ⊓");
28846 assert!(matches!(lexer.next_token(), Some((Token::Bowtie, _))));
28847 assert!(matches!(
28848 lexer.next_token(),
28849 Some((Token::ElementSmallVerticalBar, _))
28850 ));
28851 assert!(matches!(lexer.next_token(), Some((Token::SquareCup, _))));
28852 assert!(matches!(lexer.next_token(), Some((Token::SquareCap, _))));
28853 }
28854
28855 #[test]
28857 fn test_bitwise_symbol_tokens_lexer() {
28858 use crate::lexer::{Lexer, Token};
28859
28860 let mut lexer = Lexer::new("⋏ ⋎");
28861 assert!(matches!(
28862 lexer.next_token(),
28863 Some((Token::BitwiseAndSymbol, _))
28864 ));
28865 assert!(matches!(
28866 lexer.next_token(),
28867 Some((Token::BitwiseOrSymbol, _))
28868 ));
28869 }
28870
28871 #[test]
28874 fn test_pipe_alpha_first() {
28875 let result = eval("fn main() { return [10, 20, 30] |α; }");
28877 assert!(
28878 matches!(result, Ok(Value::Int(10))),
28879 "pipe α got: {:?}",
28880 result
28881 );
28882 }
28883
28884 #[test]
28885 fn test_pipe_omega_last() {
28886 let result = eval("fn main() { return [10, 20, 30] |ω; }");
28888 assert!(
28889 matches!(result, Ok(Value::Int(30))),
28890 "pipe ω got: {:?}",
28891 result
28892 );
28893 }
28894
28895 #[test]
28896 fn test_pipe_mu_middle() {
28897 let result = eval("fn main() { return [10, 20, 30, 40, 50] |μ; }");
28899 assert!(
28900 matches!(result, Ok(Value::Int(30))),
28901 "pipe μ got: {:?}",
28902 result
28903 );
28904 }
28905
28906 #[test]
28907 fn test_pipe_chi_choice() {
28908 let result = eval("fn main() { let x = [10, 20, 30] |χ; return x >= 10; }");
28910 assert!(
28911 matches!(result, Ok(Value::Bool(true))),
28912 "pipe χ got: {:?}",
28913 result
28914 );
28915 }
28916
28917 #[test]
28918 fn test_pipe_nu_nth() {
28919 let result = eval("fn main() { return [10, 20, 30, 40] |ν{2}; }");
28921 assert!(
28922 matches!(result, Ok(Value::Int(30))),
28923 "pipe ν got: {:?}",
28924 result
28925 );
28926 }
28927
28928 #[test]
28929 fn test_pipe_chain() {
28930 let result = eval("fn main() { return [3, 1, 4, 1, 5] |σ |α; }");
28932 assert!(
28933 matches!(result, Ok(Value::Int(1))),
28934 "pipe chain got: {:?}",
28935 result
28936 );
28937 }
28938
28939 #[test]
28942 fn test_aspect_progressive_parsing() {
28943 use crate::ast::Aspect;
28945 use crate::parser::Parser;
28946 let mut parser = Parser::new("fn process·ing() { return 42; }");
28947 let file = parser.parse_file().unwrap();
28948 if let crate::ast::Item::Function(f) = &file.items[0].node {
28949 assert_eq!(f.name.name, "process");
28950 assert_eq!(f.aspect, Some(Aspect::Progressive));
28951 } else {
28952 panic!("Expected function item");
28953 }
28954 }
28955
28956 #[test]
28957 fn test_aspect_perfective_parsing() {
28958 use crate::ast::Aspect;
28960 use crate::parser::Parser;
28961 let mut parser = Parser::new("fn process·ed() { return 42; }");
28962 let file = parser.parse_file().unwrap();
28963 if let crate::ast::Item::Function(f) = &file.items[0].node {
28964 assert_eq!(f.name.name, "process");
28965 assert_eq!(f.aspect, Some(Aspect::Perfective));
28966 } else {
28967 panic!("Expected function item");
28968 }
28969 }
28970
28971 #[test]
28972 fn test_aspect_potential_parsing() {
28973 use crate::ast::Aspect;
28975 use crate::parser::Parser;
28976 let mut parser = Parser::new("fn parse·able() { return true; }");
28977 let file = parser.parse_file().unwrap();
28978 if let crate::ast::Item::Function(f) = &file.items[0].node {
28979 assert_eq!(f.name.name, "parse");
28980 assert_eq!(f.aspect, Some(Aspect::Potential));
28981 } else {
28982 panic!("Expected function item");
28983 }
28984 }
28985
28986 #[test]
28987 fn test_aspect_resultative_parsing() {
28988 use crate::ast::Aspect;
28990 use crate::parser::Parser;
28991 let mut parser = Parser::new("fn destruct·ive() { return 42; }");
28992 let file = parser.parse_file().unwrap();
28993 if let crate::ast::Item::Function(f) = &file.items[0].node {
28994 assert_eq!(f.name.name, "destruct");
28995 assert_eq!(f.aspect, Some(Aspect::Resultative));
28996 } else {
28997 panic!("Expected function item");
28998 }
28999 }
29000
29001 #[test]
29004 fn test_choice_single_element() {
29005 assert!(matches!(
29007 eval("fn main() { return choice([42]); }"),
29008 Ok(Value::Int(42))
29009 ));
29010 }
29011
29012 #[test]
29013 fn test_nth_edge_cases() {
29014 assert!(matches!(
29016 eval("fn main() { return nth([10, 20, 30], 2); }"),
29017 Ok(Value::Int(30))
29018 ));
29019 assert!(matches!(
29021 eval("fn main() { return nth([10, 20, 30], 0); }"),
29022 Ok(Value::Int(10))
29023 ));
29024 }
29025
29026 #[test]
29027 fn test_next_peek_usage() {
29028 assert!(matches!(
29030 eval("fn main() { return next([1, 2, 3]); }"),
29031 Ok(Value::Int(1))
29032 ));
29033 assert!(matches!(
29035 eval("fn main() { return peek([1, 2, 3]); }"),
29036 Ok(Value::Int(1))
29037 ));
29038 }
29039
29040 #[test]
29041 fn test_zip_with_empty() {
29042 let result = eval(r#"fn main() { return len(zip_with([], [], "add")); }"#);
29044 assert!(matches!(result, Ok(Value::Int(0))));
29045 }
29046
29047 #[test]
29048 fn test_zip_with_different_lengths() {
29049 let result = eval(r#"fn main() { return len(zip_with([1, 2], [3, 4, 5], "add")); }"#);
29051 assert!(matches!(result, Ok(Value::Int(2))));
29052 }
29053
29054 #[test]
29055 fn test_supremum_edge_cases() {
29056 assert!(matches!(
29058 eval("fn main() { return supremum(5, 5); }"),
29059 Ok(Value::Int(5))
29060 ));
29061 assert!(matches!(
29063 eval("fn main() { return supremum(-5, -3); }"),
29064 Ok(Value::Int(-3))
29065 ));
29066 assert!(
29068 matches!(eval("fn main() { return supremum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 2.5).abs() < 0.001)
29069 );
29070 }
29071
29072 #[test]
29073 fn test_infimum_edge_cases() {
29074 assert!(matches!(
29076 eval("fn main() { return infimum(5, 5); }"),
29077 Ok(Value::Int(5))
29078 ));
29079 assert!(matches!(
29081 eval("fn main() { return infimum(-5, -3); }"),
29082 Ok(Value::Int(-5))
29083 ));
29084 assert!(
29086 matches!(eval("fn main() { return infimum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 1.5).abs() < 0.001)
29087 );
29088 }
29089
29090 #[test]
29091 fn test_supremum_infimum_arrays() {
29092 let result = eval("fn main() { return supremum([1, 5, 3], [2, 4, 6]); }");
29094 if let Ok(Value::Array(arr)) = result {
29095 let arr = arr.borrow();
29096 assert_eq!(arr.len(), 3);
29097 assert!(matches!(arr[0], Value::Int(2)));
29098 assert!(matches!(arr[1], Value::Int(5)));
29099 assert!(matches!(arr[2], Value::Int(6)));
29100 } else {
29101 panic!("Expected array");
29102 }
29103
29104 let result = eval("fn main() { return infimum([1, 5, 3], [2, 4, 6]); }");
29106 if let Ok(Value::Array(arr)) = result {
29107 let arr = arr.borrow();
29108 assert_eq!(arr.len(), 3);
29109 assert!(matches!(arr[0], Value::Int(1)));
29110 assert!(matches!(arr[1], Value::Int(4)));
29111 assert!(matches!(arr[2], Value::Int(3)));
29112 } else {
29113 panic!("Expected array");
29114 }
29115 }
29116
29117 #[test]
29118 fn test_pipe_access_morphemes() {
29119 assert!(matches!(
29121 eval("fn main() { return [10, 20, 30] |α; }"),
29122 Ok(Value::Int(10))
29123 ));
29124 assert!(matches!(
29126 eval("fn main() { return [10, 20, 30] |ω; }"),
29127 Ok(Value::Int(30))
29128 ));
29129 assert!(matches!(
29131 eval("fn main() { return [10, 20, 30] |μ; }"),
29132 Ok(Value::Int(20))
29133 ));
29134 }
29135
29136 #[test]
29137 fn test_pipe_nth_syntax() {
29138 assert!(matches!(
29140 eval("fn main() { return [10, 20, 30, 40] |ν{1}; }"),
29141 Ok(Value::Int(20))
29142 ));
29143 assert!(matches!(
29144 eval("fn main() { return [10, 20, 30, 40] |ν{3}; }"),
29145 Ok(Value::Int(40))
29146 ));
29147 }
29148
29149 #[test]
29152 fn test_quaternion_identity() {
29153 let result = eval("fn main() { let q = quat_identity(); return q; }");
29154 if let Ok(Value::Array(arr)) = result {
29155 let arr = arr.borrow();
29156 assert_eq!(arr.len(), 4);
29157 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
29158 (&arr[0], &arr[1], &arr[2], &arr[3])
29159 {
29160 assert!((w - 1.0).abs() < 0.001);
29161 assert!(x.abs() < 0.001);
29162 assert!(y.abs() < 0.001);
29163 assert!(z.abs() < 0.001);
29164 }
29165 } else {
29166 panic!("Expected quaternion array");
29167 }
29168 }
29169
29170 #[test]
29171 fn test_quaternion_from_axis_angle() {
29172 let result =
29174 eval("fn main() { let q = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963); return q; }");
29175 if let Ok(Value::Array(arr)) = result {
29176 let arr = arr.borrow();
29177 assert_eq!(arr.len(), 4);
29178 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
29180 (&arr[0], &arr[1], &arr[2], &arr[3])
29181 {
29182 assert!((w - 0.707).abs() < 0.01, "w={}", w);
29183 assert!(x.abs() < 0.01);
29184 assert!((y - 0.707).abs() < 0.01, "y={}", y);
29185 assert!(z.abs() < 0.01);
29186 }
29187 } else {
29188 panic!("Expected quaternion array");
29189 }
29190 }
29191
29192 #[test]
29193 fn test_quaternion_rotate_vector() {
29194 let result = eval(
29196 r#"
29197 fn main() {
29198 let q = quat_from_axis_angle(vec3(0, 0, 1), 1.5707963);
29199 let v = vec3(1, 0, 0);
29200 return quat_rotate(q, v);
29201 }
29202 "#,
29203 );
29204 if let Ok(Value::Array(arr)) = result {
29205 let arr = arr.borrow();
29206 assert_eq!(arr.len(), 3);
29207 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
29208 {
29209 assert!(x.abs() < 0.01, "x={}", x);
29210 assert!((y - 1.0).abs() < 0.01, "y={}", y);
29211 assert!(z.abs() < 0.01);
29212 }
29213 } else {
29214 panic!("Expected vec3 array");
29215 }
29216 }
29217
29218 #[test]
29219 fn test_quaternion_slerp() {
29220 let result = eval(
29222 r#"
29223 fn main() {
29224 let q1 = quat_identity();
29225 let q2 = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963);
29226 return quat_slerp(q1, q2, 0.5);
29227 }
29228 "#,
29229 );
29230 if let Ok(Value::Array(arr)) = result {
29231 let arr = arr.borrow();
29232 assert_eq!(arr.len(), 4);
29233 if let Value::Float(w) = &arr[0] {
29235 assert!((w - 0.924).abs() < 0.05, "w={}", w);
29237 }
29238 } else {
29239 panic!("Expected quaternion array");
29240 }
29241 }
29242
29243 #[test]
29244 fn test_vec3_operations() {
29245 let result = eval("fn main() { return vec3_add(vec3(1, 2, 3), vec3(4, 5, 6)); }");
29247 if let Ok(Value::Array(arr)) = result {
29248 let arr = arr.borrow();
29249 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
29250 {
29251 assert!((x - 5.0).abs() < 0.001);
29252 assert!((y - 7.0).abs() < 0.001);
29253 assert!((z - 9.0).abs() < 0.001);
29254 }
29255 }
29256
29257 let result = eval("fn main() { return vec3_dot(vec3(1, 2, 3), vec3(4, 5, 6)); }");
29259 assert!(matches!(result, Ok(Value::Float(f)) if (f - 32.0).abs() < 0.001));
29260
29261 let result = eval("fn main() { return vec3_cross(vec3(1, 0, 0), vec3(0, 1, 0)); }");
29263 if let Ok(Value::Array(arr)) = result {
29264 let arr = arr.borrow();
29265 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
29266 {
29267 assert!(x.abs() < 0.001);
29268 assert!(y.abs() < 0.001);
29269 assert!((z - 1.0).abs() < 0.001);
29270 }
29271 }
29272
29273 let result = eval("fn main() { return vec3_length(vec3(3, 4, 0)); }");
29275 assert!(matches!(result, Ok(Value::Float(f)) if (f - 5.0).abs() < 0.001));
29276
29277 let result = eval("fn main() { return vec3_normalize(vec3(3, 0, 0)); }");
29279 if let Ok(Value::Array(arr)) = result {
29280 let arr = arr.borrow();
29281 if let Value::Float(x) = &arr[0] {
29282 assert!((x - 1.0).abs() < 0.001);
29283 }
29284 }
29285 }
29286
29287 #[test]
29288 fn test_vec3_reflect() {
29289 let result = eval("fn main() { return vec3_reflect(vec3(1, -1, 0), vec3(0, 1, 0)); }");
29291 if let Ok(Value::Array(arr)) = result {
29292 let arr = arr.borrow();
29293 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
29294 {
29295 assert!((x - 1.0).abs() < 0.001);
29296 assert!((y - 1.0).abs() < 0.001);
29297 assert!(z.abs() < 0.001);
29298 }
29299 }
29300 }
29301
29302 #[test]
29303 fn test_mat4_identity() {
29304 let result = eval("fn main() { return mat4_identity(); }");
29305 if let Ok(Value::Array(arr)) = result {
29306 let arr = arr.borrow();
29307 assert_eq!(arr.len(), 16);
29308 if let (Value::Float(m00), Value::Float(m55), Value::Float(m10), Value::Float(m15)) =
29310 (&arr[0], &arr[5], &arr[10], &arr[15])
29311 {
29312 assert!((m00 - 1.0).abs() < 0.001);
29313 assert!((m55 - 1.0).abs() < 0.001);
29314 assert!((m10 - 1.0).abs() < 0.001);
29315 assert!((m15 - 1.0).abs() < 0.001);
29316 }
29317 }
29318 }
29319
29320 #[test]
29321 fn test_mat4_translate() {
29322 let result = eval(
29323 r#"
29324 fn main() {
29325 let t = mat4_translate(5.0, 10.0, 15.0);
29326 let v = vec4(0, 0, 0, 1);
29327 return mat4_transform(t, v);
29328 }
29329 "#,
29330 );
29331 if let Ok(Value::Array(arr)) = result {
29332 let arr = arr.borrow();
29333 if let (Value::Float(x), Value::Float(y), Value::Float(z), Value::Float(w)) =
29334 (&arr[0], &arr[1], &arr[2], &arr[3])
29335 {
29336 assert!((x - 5.0).abs() < 0.001);
29337 assert!((y - 10.0).abs() < 0.001);
29338 assert!((z - 15.0).abs() < 0.001);
29339 assert!((w - 1.0).abs() < 0.001);
29340 }
29341 }
29342 }
29343
29344 #[test]
29345 fn test_mat4_perspective() {
29346 let result = eval("fn main() { return mat4_perspective(1.0472, 1.777, 0.1, 100.0); }");
29348 if let Ok(Value::Array(arr)) = result {
29349 let arr = arr.borrow();
29350 assert_eq!(arr.len(), 16);
29351 } else {
29352 panic!("Expected mat4 array");
29353 }
29354 }
29355
29356 #[test]
29357 fn test_mat4_look_at() {
29358 let result = eval(
29359 r#"
29360 fn main() {
29361 let eye = vec3(0, 0, 5);
29362 let center = vec3(0, 0, 0);
29363 let up = vec3(0, 1, 0);
29364 return mat4_look_at(eye, center, up);
29365 }
29366 "#,
29367 );
29368 if let Ok(Value::Array(arr)) = result {
29369 let arr = arr.borrow();
29370 assert_eq!(arr.len(), 16);
29371 } else {
29372 panic!("Expected mat4 array");
29373 }
29374 }
29375
29376 #[test]
29377 fn test_mat4_inverse() {
29378 let result = eval(
29380 r#"
29381 fn main() {
29382 let m = mat4_identity();
29383 return mat4_inverse(m);
29384 }
29385 "#,
29386 );
29387 if let Ok(Value::Array(arr)) = result {
29388 let arr = arr.borrow();
29389 assert_eq!(arr.len(), 16);
29390 if let Value::Float(m00) = &arr[0] {
29391 assert!((m00 - 1.0).abs() < 0.001);
29392 }
29393 }
29394 }
29395
29396 #[test]
29397 fn test_mat3_operations() {
29398 let result = eval("fn main() { return mat3_identity(); }");
29400 if let Ok(Value::Array(arr)) = result {
29401 let arr = arr.borrow();
29402 assert_eq!(arr.len(), 9);
29403 }
29404
29405 let result = eval(
29407 r#"
29408 fn main() {
29409 let m = mat3_identity();
29410 let v = vec3(1, 2, 3);
29411 return mat3_transform(m, v);
29412 }
29413 "#,
29414 );
29415 if let Ok(Value::Array(arr)) = result {
29416 let arr = arr.borrow();
29417 if let (Value::Float(x), Value::Float(y), Value::Float(z)) = (&arr[0], &arr[1], &arr[2])
29418 {
29419 assert!((x - 1.0).abs() < 0.001);
29420 assert!((y - 2.0).abs() < 0.001);
29421 assert!((z - 3.0).abs() < 0.001);
29422 }
29423 }
29424 }
29425
29426 #[test]
29427 fn test_quat_to_mat4() {
29428 let result = eval(
29430 r#"
29431 fn main() {
29432 let q = quat_identity();
29433 return quat_to_mat4(q);
29434 }
29435 "#,
29436 );
29437 if let Ok(Value::Array(arr)) = result {
29438 let arr = arr.borrow();
29439 assert_eq!(arr.len(), 16);
29440 if let (Value::Float(m00), Value::Float(m55)) = (&arr[0], &arr[5]) {
29442 assert!((m00 - 1.0).abs() < 0.001);
29443 assert!((m55 - 1.0).abs() < 0.001);
29444 }
29445 }
29446 }
29447
29448 #[test]
29452 fn test_channel_basic_send_recv() {
29453 let result = eval(
29455 r#"
29456 fn main() {
29457 let ch = channel_new();
29458 channel_send(ch, 42);
29459 return channel_recv(ch);
29460 }
29461 "#,
29462 );
29463 assert!(matches!(result, Ok(Value::Int(42))));
29464 }
29465
29466 #[test]
29467 fn test_channel_multiple_values() {
29468 let result = eval(
29470 r#"
29471 fn main() {
29472 let ch = channel_new();
29473 channel_send(ch, 1);
29474 channel_send(ch, 2);
29475 channel_send(ch, 3);
29476 let a = channel_recv(ch);
29477 let b = channel_recv(ch);
29478 let c = channel_recv(ch);
29479 return a * 100 + b * 10 + c;
29480 }
29481 "#,
29482 );
29483 assert!(matches!(result, Ok(Value::Int(123))));
29484 }
29485
29486 #[test]
29487 fn test_channel_high_throughput() {
29488 let result = eval(
29490 r#"
29491 fn main() {
29492 let ch = channel_new();
29493 let count = 1000;
29494 let i = 0;
29495 while i < count {
29496 channel_send(ch, i);
29497 i = i + 1;
29498 }
29499
29500 // Receive all and compute sum to verify no data loss
29501 let sum = 0;
29502 let j = 0;
29503 while j < count {
29504 let val = channel_recv(ch);
29505 sum = sum + val;
29506 j = j + 1;
29507 }
29508
29509 // Sum of 0..999 = 499500
29510 return sum;
29511 }
29512 "#,
29513 );
29514 assert!(matches!(result, Ok(Value::Int(499500))));
29515 }
29516
29517 #[test]
29518 fn test_channel_data_integrity() {
29519 let result = eval(
29521 r#"
29522 fn main() {
29523 let ch = channel_new();
29524
29525 // Send various types
29526 channel_send(ch, 42);
29527 channel_send(ch, 3.14);
29528 channel_send(ch, "hello");
29529 channel_send(ch, [1, 2, 3]);
29530
29531 // Receive and verify types
29532 let int_val = channel_recv(ch);
29533 let float_val = channel_recv(ch);
29534 let str_val = channel_recv(ch);
29535 let arr_val = channel_recv(ch);
29536
29537 // Verify by combining results
29538 return int_val + floor(float_val) + len(str_val) + len(arr_val);
29539 }
29540 "#,
29541 );
29542 assert!(matches!(result, Ok(Value::Int(53))));
29544 }
29545
29546 #[test]
29547 fn test_channel_try_recv_empty() {
29548 let result = eval(
29551 r#"
29552 fn main() {
29553 let ch = channel_new();
29554 let result = channel_try_recv(ch);
29555 // Can't pattern match variants in interpreter, so just verify it returns
29556 return type_of(result);
29557 }
29558 "#,
29559 );
29560 assert!(result.is_ok());
29562 }
29563
29564 #[test]
29565 fn test_channel_try_recv_with_value() {
29566 let result = eval(
29568 r#"
29569 fn main() {
29570 let ch = channel_new();
29571 channel_send(ch, 99);
29572 // Use blocking recv since try_recv returns Option variant
29573 // which can't be pattern matched in interpreter
29574 let val = channel_recv(ch);
29575 return val;
29576 }
29577 "#,
29578 );
29579 assert!(matches!(result, Ok(Value::Int(99))));
29580 }
29581
29582 #[test]
29583 fn test_channel_recv_timeout_expires() {
29584 let result = eval(
29586 r#"
29587 fn main() {
29588 let ch = channel_new();
29589 let result = channel_recv_timeout(ch, 10); // 10ms timeout
29590 // Just verify it completes without blocking forever
29591 return 42;
29592 }
29593 "#,
29594 );
29595 assert!(matches!(result, Ok(Value::Int(42))));
29596 }
29597
29598 #[test]
29599 fn test_actor_basic_messaging() {
29600 let result = eval(
29602 r#"
29603 fn main() {
29604 let act = spawn_actor("test_actor");
29605 send_to_actor(act, "ping", 42);
29606 return get_actor_msg_count(act);
29607 }
29608 "#,
29609 );
29610 assert!(matches!(result, Ok(Value::Int(1))));
29611 }
29612
29613 #[test]
29614 fn test_actor_message_storm() {
29615 let result = eval(
29617 r#"
29618 fn main() {
29619 let act = spawn_actor("stress_actor");
29620 let count = 10000;
29621 let i = 0;
29622 while i < count {
29623 send_to_actor(act, "msg", i);
29624 i = i + 1;
29625 }
29626 return get_actor_msg_count(act);
29627 }
29628 "#,
29629 );
29630 assert!(matches!(result, Ok(Value::Int(10000))));
29631 }
29632
29633 #[test]
29634 fn test_actor_pending_count() {
29635 let result = eval(
29637 r#"
29638 fn main() {
29639 let act = spawn_actor("pending_test");
29640
29641 // Send 5 messages
29642 send_to_actor(act, "m1", 1);
29643 send_to_actor(act, "m2", 2);
29644 send_to_actor(act, "m3", 3);
29645 send_to_actor(act, "m4", 4);
29646 send_to_actor(act, "m5", 5);
29647
29648 let pending_before = get_actor_pending(act);
29649
29650 // Receive 2 messages
29651 recv_from_actor(act);
29652 recv_from_actor(act);
29653
29654 let pending_after = get_actor_pending(act);
29655
29656 // Should have 5 pending initially, 3 after receiving 2
29657 return pending_before * 10 + pending_after;
29658 }
29659 "#,
29660 );
29661 assert!(matches!(result, Ok(Value::Int(53)))); }
29663
29664 #[test]
29665 fn test_actor_message_order() {
29666 let result = eval(
29669 r#"
29670 fn main() {
29671 let act = spawn_actor("order_test");
29672 send_to_actor(act, "a", 1);
29673 send_to_actor(act, "b", 2);
29674 send_to_actor(act, "c", 3);
29675
29676 // pop() gives LIFO order, so we get c, b, a
29677 let r1 = recv_from_actor(act);
29678 let r2 = recv_from_actor(act);
29679 let r3 = recv_from_actor(act);
29680
29681 // Return the message types concatenated via their first char values
29682 // c=3, b=2, a=1 in our test
29683 return get_actor_pending(act); // Should be 0 after draining
29684 }
29685 "#,
29686 );
29687 assert!(matches!(result, Ok(Value::Int(0))));
29688 }
29689
29690 #[test]
29691 fn test_actor_recv_empty() {
29692 let result = eval(
29695 r#"
29696 fn main() {
29697 let act = spawn_actor("empty_actor");
29698 // No messages sent, so pending should be 0
29699 return get_actor_pending(act);
29700 }
29701 "#,
29702 );
29703 assert!(matches!(result, Ok(Value::Int(0))));
29704 }
29705
29706 #[test]
29707 fn test_actor_tell_alias() {
29708 let result = eval(
29710 r#"
29711 fn main() {
29712 let act = spawn_actor("tell_test");
29713 tell_actor(act, "hello", 123);
29714 tell_actor(act, "world", 456);
29715 return get_actor_msg_count(act);
29716 }
29717 "#,
29718 );
29719 assert!(matches!(result, Ok(Value::Int(2))));
29720 }
29721
29722 #[test]
29723 fn test_actor_name() {
29724 let result = eval(
29726 r#"
29727 fn main() {
29728 let act = spawn_actor("my_special_actor");
29729 return get_actor_name(act);
29730 }
29731 "#,
29732 );
29733 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "my_special_actor"));
29734 }
29735
29736 #[test]
29737 fn test_multiple_actors() {
29738 let result = eval(
29740 r#"
29741 fn main() {
29742 let a1 = spawn_actor("actor1");
29743 let a2 = spawn_actor("actor2");
29744 let a3 = spawn_actor("actor3");
29745
29746 send_to_actor(a1, "m", 1);
29747 send_to_actor(a2, "m", 1);
29748 send_to_actor(a2, "m", 2);
29749 send_to_actor(a3, "m", 1);
29750 send_to_actor(a3, "m", 2);
29751 send_to_actor(a3, "m", 3);
29752
29753 let c1 = get_actor_msg_count(a1);
29754 let c2 = get_actor_msg_count(a2);
29755 let c3 = get_actor_msg_count(a3);
29756
29757 return c1 * 100 + c2 * 10 + c3;
29758 }
29759 "#,
29760 );
29761 assert!(matches!(result, Ok(Value::Int(123)))); }
29763
29764 #[test]
29765 fn test_multiple_channels() {
29766 let result = eval(
29768 r#"
29769 fn main() {
29770 let ch1 = channel_new();
29771 let ch2 = channel_new();
29772 let ch3 = channel_new();
29773
29774 channel_send(ch1, 100);
29775 channel_send(ch2, 200);
29776 channel_send(ch3, 300);
29777
29778 let v1 = channel_recv(ch1);
29779 let v2 = channel_recv(ch2);
29780 let v3 = channel_recv(ch3);
29781
29782 return v1 + v2 + v3;
29783 }
29784 "#,
29785 );
29786 assert!(matches!(result, Ok(Value::Int(600))));
29787 }
29788
29789 #[test]
29790 fn test_thread_sleep() {
29791 let result = eval(
29793 r#"
29794 fn main() {
29795 thread_sleep(1); // Sleep 1ms
29796 return 42;
29797 }
29798 "#,
29799 );
29800 assert!(matches!(result, Ok(Value::Int(42))));
29801 }
29802
29803 #[test]
29804 fn test_thread_yield() {
29805 let result = eval(
29807 r#"
29808 fn main() {
29809 thread_yield();
29810 return 42;
29811 }
29812 "#,
29813 );
29814 assert!(matches!(result, Ok(Value::Int(42))));
29815 }
29816
29817 #[test]
29818 fn test_thread_id() {
29819 let result = eval(
29821 r#"
29822 fn main() {
29823 let id = thread_id();
29824 return len(id) > 0;
29825 }
29826 "#,
29827 );
29828 assert!(matches!(result, Ok(Value::Bool(true))));
29829 }
29830
29831 #[test]
29832 fn test_channel_stress_interleaved() {
29833 let result = eval(
29835 r#"
29836 fn main() {
29837 let ch = channel_new();
29838 let sum = 0;
29839 let i = 0;
29840 while i < 100 {
29841 channel_send(ch, i);
29842 channel_send(ch, i * 2);
29843 let a = channel_recv(ch);
29844 let b = channel_recv(ch);
29845 sum = sum + a + b;
29846 i = i + 1;
29847 }
29848 // Sum: sum of i + i*2 for i in 0..99
29849 // = sum of 3*i for i in 0..99 = 3 * (99*100/2) = 3 * 4950 = 14850
29850 return sum;
29851 }
29852 "#,
29853 );
29854 assert!(matches!(result, Ok(Value::Int(14850))));
29855 }
29856
29857 #[test]
29858 fn test_actor_stress_with_receive() {
29859 let result = eval(
29861 r#"
29862 fn main() {
29863 let act = spawn_actor("recv_stress");
29864 let count = 1000;
29865 let i = 0;
29866 while i < count {
29867 send_to_actor(act, "data", i);
29868 i = i + 1;
29869 }
29870
29871 // Drain all messages
29872 let drained = 0;
29873 while get_actor_pending(act) > 0 {
29874 recv_from_actor(act);
29875 drained = drained + 1;
29876 }
29877
29878 return drained;
29879 }
29880 "#,
29881 );
29882 assert!(matches!(result, Ok(Value::Int(1000))));
29883 }
29884
29885 use proptest::prelude::*;
29889
29890 proptest! {
29893 #![proptest_config(ProptestConfig::with_cases(100))]
29894
29895 #[test]
29896 fn test_parser_doesnt_crash_on_random_input(s in "\\PC*") {
29897 let mut parser = Parser::new(&s);
29899 let _ = parser.parse_file(); }
29901
29902 #[test]
29903 fn test_parser_handles_unicode(s in "[\\p{L}\\p{N}\\p{P}\\s]{0,100}") {
29904 let mut parser = Parser::new(&s);
29906 let _ = parser.parse_file();
29907 }
29908
29909 #[test]
29910 fn test_parser_nested_brackets(depth in 0..20usize) {
29911 let open: String = (0..depth).map(|_| '(').collect();
29913 let close: String = (0..depth).map(|_| ')').collect();
29914 let code = format!("fn main() {{ return {}1{}; }}", open, close);
29915 let mut parser = Parser::new(&code);
29916 let _ = parser.parse_file();
29917 }
29918
29919 #[test]
29920 fn test_parser_long_identifiers(len in 1..500usize) {
29921 let ident: String = (0..len).map(|_| 'a').collect();
29923 let code = format!("fn main() {{ let {} = 1; return {}; }}", ident, ident);
29924 let result = eval(&code);
29925 assert!(matches!(result, Ok(Value::Int(1))));
29926 }
29927
29928 #[test]
29929 fn test_parser_many_arguments(count in 0..50usize) {
29930 let args: String = (0..count).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
29932 let code = format!("fn main() {{ return len([{}]); }}", args);
29933 let result = eval(&code);
29934 assert!(matches!(result, Ok(Value::Int(c)) if c == count as i64));
29935 }
29936 }
29937
29938 proptest! {
29941 #![proptest_config(ProptestConfig::with_cases(50))]
29942
29943 #[test]
29944 fn test_ga_bivector_anticommutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
29945 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
29946 let code = format!(r#"
29949 fn main() {{
29950 let a = vec3({}, {}, {});
29951 let b = vec3({}, {}, {});
29952 let ab = vec3_cross(a, b);
29953 let ba = vec3_cross(b, a);
29954 let diff_x = get(ab, 0) + get(ba, 0);
29955 let diff_y = get(ab, 1) + get(ba, 1);
29956 let diff_z = get(ab, 2) + get(ba, 2);
29957 let eps = 0.001;
29958 return eps > abs(diff_x) && eps > abs(diff_y) && eps > abs(diff_z);
29959 }}
29960 "#, x1, y1, z1, x2, y2, z2);
29961 let result = eval(&code);
29962 assert!(matches!(result, Ok(Value::Bool(true))));
29963 }
29964
29965 #[test]
29966 fn test_vec3_dot_commutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
29967 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
29968 let code = format!(r#"
29970 fn main() {{
29971 let a = vec3({}, {}, {});
29972 let b = vec3({}, {}, {});
29973 let ab = vec3_dot(a, b);
29974 let ba = vec3_dot(b, a);
29975 let eps = 0.001;
29976 return eps > abs(ab - ba);
29977 }}
29978 "#, x1, y1, z1, x2, y2, z2);
29979 let result = eval(&code);
29980 assert!(matches!(result, Ok(Value::Bool(true))));
29981 }
29982
29983 #[test]
29984 fn test_quat_identity_preserves_vector(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0) {
29985 let code = format!(r#"
29987 fn main() {{
29988 let v = vec3({}, {}, {});
29989 let q = quat_identity();
29990 let rotated = quat_rotate(q, v);
29991 let diff_x = abs(get(v, 0) - get(rotated, 0));
29992 let diff_y = abs(get(v, 1) - get(rotated, 1));
29993 let diff_z = abs(get(v, 2) - get(rotated, 2));
29994 let eps = 0.001;
29995 return eps > diff_x && eps > diff_y && eps > diff_z;
29996 }}
29997 "#, x, y, z);
29998 let result = eval(&code);
29999 assert!(matches!(result, Ok(Value::Bool(true))));
30000 }
30001
30002 #[test]
30003 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,
30004 angle in -3.14f64..3.14) {
30005 let code = format!(r#"
30007 fn main() {{
30008 let v = vec3({}, {}, {});
30009 let axis = vec3(0.0, 1.0, 0.0);
30010 let q1 = quat_from_axis_angle(axis, {});
30011 let q2 = quat_from_axis_angle(axis, {} * 2.0);
30012 let q1q1 = quat_mul(q1, q1);
30013 let eps = 0.01;
30014 let same = eps > abs(get(q2, 0) - get(q1q1, 0)) &&
30015 eps > abs(get(q2, 1) - get(q1q1, 1)) &&
30016 eps > abs(get(q2, 2) - get(q1q1, 2)) &&
30017 eps > abs(get(q2, 3) - get(q1q1, 3));
30018 let neg_same = eps > abs(get(q2, 0) + get(q1q1, 0)) &&
30019 eps > abs(get(q2, 1) + get(q1q1, 1)) &&
30020 eps > abs(get(q2, 2) + get(q1q1, 2)) &&
30021 eps > abs(get(q2, 3) + get(q1q1, 3));
30022 return same || neg_same;
30023 }}
30024 "#, x, y, z, angle, angle);
30025 let result = eval(&code);
30026 assert!(matches!(result, Ok(Value::Bool(true))));
30027 }
30028
30029 #[test]
30030 fn test_vec3_add_associative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
30031 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0,
30032 x3 in -100.0f64..100.0, y3 in -100.0f64..100.0, z3 in -100.0f64..100.0) {
30033 let code = format!(r#"
30035 fn main() {{
30036 let a = vec3({}, {}, {});
30037 let b = vec3({}, {}, {});
30038 let c = vec3({}, {}, {});
30039 let ab_c = vec3_add(vec3_add(a, b), c);
30040 let a_bc = vec3_add(a, vec3_add(b, c));
30041 let diff_x = abs(get(ab_c, 0) - get(a_bc, 0));
30042 let diff_y = abs(get(ab_c, 1) - get(a_bc, 1));
30043 let diff_z = abs(get(ab_c, 2) - get(a_bc, 2));
30044 let eps = 0.001;
30045 return eps > diff_x && eps > diff_y && eps > diff_z;
30046 }}
30047 "#, x1, y1, z1, x2, y2, z2, x3, y3, z3);
30048 let result = eval(&code);
30049 assert!(matches!(result, Ok(Value::Bool(true))));
30050 }
30051
30052 #[test]
30053 fn test_vec3_scale_distributive(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0,
30054 s1 in -10.0f64..10.0, s2 in -10.0f64..10.0) {
30055 let code = format!(r#"
30057 fn main() {{
30058 let v = vec3({}, {}, {});
30059 let s1 = {};
30060 let s2 = {};
30061 let combined = vec3_scale(v, s1 + s2);
30062 let separate = vec3_add(vec3_scale(v, s1), vec3_scale(v, s2));
30063 let diff_x = abs(get(combined, 0) - get(separate, 0));
30064 let diff_y = abs(get(combined, 1) - get(separate, 1));
30065 let diff_z = abs(get(combined, 2) - get(separate, 2));
30066 let eps = 0.01;
30067 return eps > diff_x && eps > diff_y && eps > diff_z;
30068 }}
30069 "#, x, y, z, s1, s2);
30070 let result = eval(&code);
30071 assert!(matches!(result, Ok(Value::Bool(true))));
30072 }
30073 }
30074
30075 proptest! {
30078 #![proptest_config(ProptestConfig::with_cases(30))]
30079
30080 #[test]
30081 fn test_grad_of_constant_is_zero(c in -100.0f64..100.0, x in -100.0f64..100.0) {
30082 let code = format!(r#"
30084 fn main() {{
30085 fn constant(x) {{ return {}; }}
30086 let g = grad(constant, {});
30087 let eps = 0.001;
30088 return eps > abs(g);
30089 }}
30090 "#, c, x);
30091 let result = eval(&code);
30092 assert!(matches!(result, Ok(Value::Bool(true))));
30093 }
30094
30095 #[test]
30096 fn test_grad_of_x_is_one(x in -100.0f64..100.0) {
30097 let code = format!(r#"
30099 fn main() {{
30100 fn identity(x) {{ return x; }}
30101 let g = grad(identity, {});
30102 let eps = 0.001;
30103 return eps > abs(g - 1.0);
30104 }}
30105 "#, x);
30106 let result = eval(&code);
30107 assert!(matches!(result, Ok(Value::Bool(true))));
30108 }
30109
30110 #[test]
30111 fn test_grad_of_x_squared(x in -50.0f64..50.0) {
30112 let code = format!(r#"
30114 fn main() {{
30115 fn square(x) {{ return x * x; }}
30116 let g = grad(square, {});
30117 let expected = 2.0 * {};
30118 let eps = 0.1;
30119 return eps > abs(g - expected);
30120 }}
30121 "#, x, x);
30122 let result = eval(&code);
30123 assert!(matches!(result, Ok(Value::Bool(true))));
30124 }
30125
30126 #[test]
30127 fn test_grad_linearity(a in -10.0f64..10.0, b in -10.0f64..10.0, x in -10.0f64..10.0) {
30128 let code = format!(r#"
30130 fn main() {{
30131 fn linear(x) {{ return {} * x + {}; }}
30132 let g = grad(linear, {});
30133 let eps = 0.1;
30134 return eps > abs(g - {});
30135 }}
30136 "#, a, b, x, a);
30137 let result = eval(&code);
30138 assert!(matches!(result, Ok(Value::Bool(true))));
30139 }
30140 }
30141
30142 proptest! {
30145 #![proptest_config(ProptestConfig::with_cases(50))]
30146
30147 #[test]
30148 fn test_addition_commutative(a in -1000i64..1000, b in -1000i64..1000) {
30149 let code = format!("fn main() {{ return {} + {} == {} + {}; }}", a, b, b, a);
30150 let result = eval(&code);
30151 assert!(matches!(result, Ok(Value::Bool(true))));
30152 }
30153
30154 #[test]
30155 fn test_multiplication_commutative(a in -100i64..100, b in -100i64..100) {
30156 let code = format!("fn main() {{ return {} * {} == {} * {}; }}", a, b, b, a);
30157 let result = eval(&code);
30158 assert!(matches!(result, Ok(Value::Bool(true))));
30159 }
30160
30161 #[test]
30162 fn test_addition_identity(a in -1000i64..1000) {
30163 let code = format!("fn main() {{ return {} + 0 == {}; }}", a, a);
30164 let result = eval(&code);
30165 assert!(matches!(result, Ok(Value::Bool(true))));
30166 }
30167
30168 #[test]
30169 fn test_multiplication_identity(a in -1000i64..1000) {
30170 let code = format!("fn main() {{ return {} * 1 == {}; }}", a, a);
30171 let result = eval(&code);
30172 assert!(matches!(result, Ok(Value::Bool(true))));
30173 }
30174
30175 #[test]
30176 fn test_distributive_property(a in -20i64..20, b in -20i64..20, c in -20i64..20) {
30177 let code = format!("fn main() {{ return {} * ({} + {}) == {} * {} + {} * {}; }}", a, b, c, a, b, a, c);
30178 let result = eval(&code);
30179 assert!(matches!(result, Ok(Value::Bool(true))));
30180 }
30181 }
30182
30183 proptest! {
30186 #![proptest_config(ProptestConfig::with_cases(30))]
30187
30188 #[test]
30189 fn test_array_len_after_push(initial_len in 0..20usize, value in -100i64..100) {
30190 let initial: String = (0..initial_len).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
30191 let code = format!(r#"
30192 fn main() {{
30193 let arr = [{}];
30194 push(arr, {});
30195 return len(arr);
30196 }}
30197 "#, initial, value);
30198 let result = eval(&code);
30199 assert!(matches!(result, Ok(Value::Int(n)) if n == (initial_len + 1) as i64));
30200 }
30201
30202 #[test]
30203 fn test_reverse_reverse_identity(elements in prop::collection::vec(-100i64..100, 0..10)) {
30204 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
30205 let code = format!(r#"
30206 fn main() {{
30207 let arr = [{}];
30208 let rev1 = reverse(arr);
30209 let rev2 = reverse(rev1);
30210 let same = true;
30211 let i = 0;
30212 while i < len(arr) {{
30213 if get(arr, i) != get(rev2, i) {{
30214 same = false;
30215 }}
30216 i = i + 1;
30217 }}
30218 return same;
30219 }}
30220 "#, arr_str);
30221 let result = eval(&code);
30222 assert!(matches!(result, Ok(Value::Bool(true))));
30223 }
30224
30225 #[test]
30226 fn test_sum_equals_manual_sum(elements in prop::collection::vec(-100i64..100, 0..20)) {
30227 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
30228 let expected_sum: i64 = elements.iter().sum();
30229 let code = format!("fn main() {{ return sum([{}]); }}", arr_str);
30230 let result = eval(&code);
30231 assert!(matches!(result, Ok(Value::Int(n)) if n == expected_sum));
30232 }
30233 }
30234
30235 #[test]
30243 fn test_no_leak_repeated_array_operations() {
30244 let result = eval(
30246 r#"
30247 fn main() {
30248 let i = 0;
30249 while i < 1000 {
30250 let arr = [1, 2, 3, 4, 5];
30251 push(arr, 6);
30252 let rev = reverse(arr);
30253 let s = sum(arr);
30254 i = i + 1;
30255 }
30256 return i;
30257 }
30258 "#,
30259 );
30260 assert!(matches!(result, Ok(Value::Int(1000))));
30261 }
30262
30263 #[test]
30264 fn test_no_leak_repeated_function_calls() {
30265 let result = eval(
30267 r#"
30268 fn fib(n) {
30269 if n <= 1 { return n; }
30270 return fib(n - 1) + fib(n - 2);
30271 }
30272 fn main() {
30273 let i = 0;
30274 let total = 0;
30275 while i < 100 {
30276 total = total + fib(10);
30277 i = i + 1;
30278 }
30279 return total;
30280 }
30281 "#,
30282 );
30283 assert!(matches!(result, Ok(Value::Int(5500))));
30284 }
30285
30286 #[test]
30287 fn test_no_leak_repeated_map_operations() {
30288 let result = eval(
30290 r#"
30291 fn main() {
30292 let i = 0;
30293 while i < 500 {
30294 let m = map_new();
30295 map_set(m, "key1", 1);
30296 map_set(m, "key2", 2);
30297 map_set(m, "key3", 3);
30298 let v = map_get(m, "key1");
30299 i = i + 1;
30300 }
30301 return i;
30302 }
30303 "#,
30304 );
30305 assert!(matches!(result, Ok(Value::Int(500))));
30306 }
30307
30308 #[test]
30309 fn test_no_leak_repeated_string_operations() {
30310 let result = eval(
30312 r#"
30313 fn main() {
30314 let i = 0;
30315 while i < 1000 {
30316 let s = "hello world";
30317 let upper_s = upper(s);
30318 let lower_s = lower(upper_s);
30319 let concat_s = s ++ " " ++ upper_s;
30320 let replaced = replace(concat_s, "o", "0");
30321 i = i + 1;
30322 }
30323 return i;
30324 }
30325 "#,
30326 );
30327 assert!(matches!(result, Ok(Value::Int(1000))));
30328 }
30329
30330 #[test]
30331 fn test_no_leak_repeated_ecs_operations() {
30332 let result = eval(
30334 r#"
30335 fn main() {
30336 let world = ecs_world();
30337 let i = 0;
30338 while i < 500 {
30339 let entity = ecs_spawn(world);
30340 ecs_attach(world, entity, "Position", vec3(1.0, 2.0, 3.0));
30341 ecs_attach(world, entity, "Velocity", vec3(0.0, 0.0, 0.0));
30342 let pos = ecs_get(world, entity, "Position");
30343 i = i + 1;
30344 }
30345 return i;
30346 }
30347 "#,
30348 );
30349 assert!(matches!(result, Ok(Value::Int(500))));
30350 }
30351
30352 #[test]
30353 fn test_no_leak_repeated_channel_operations() {
30354 let result = eval(
30356 r#"
30357 fn main() {
30358 let i = 0;
30359 while i < 500 {
30360 let ch = channel_new();
30361 channel_send(ch, i);
30362 channel_send(ch, i + 1);
30363 let v1 = channel_recv(ch);
30364 let v2 = channel_recv(ch);
30365 i = i + 1;
30366 }
30367 return i;
30368 }
30369 "#,
30370 );
30371 assert!(matches!(result, Ok(Value::Int(500))));
30372 }
30373
30374 #[test]
30375 fn test_no_leak_repeated_actor_operations() {
30376 let result = eval(
30378 r#"
30379 fn main() {
30380 let i = 0;
30381 while i < 100 {
30382 let act = spawn_actor("leak_test_actor");
30383 send_to_actor(act, "msg", i);
30384 send_to_actor(act, "msg", i + 1);
30385 let count = get_actor_msg_count(act);
30386 i = i + 1;
30387 }
30388 return i;
30389 }
30390 "#,
30391 );
30392 assert!(matches!(result, Ok(Value::Int(100))));
30393 }
30394
30395 #[test]
30396 fn test_no_leak_repeated_vec3_operations() {
30397 let result = eval(
30399 r#"
30400 fn main() {
30401 let i = 0;
30402 while i < 1000 {
30403 let v1 = vec3(1.0, 2.0, 3.0);
30404 let v2 = vec3(4.0, 5.0, 6.0);
30405 let added = vec3_add(v1, v2);
30406 let scaled = vec3_scale(added, 2.0);
30407 let dot = vec3_dot(v1, v2);
30408 let crossed = vec3_cross(v1, v2);
30409 let normalized = vec3_normalize(crossed);
30410 i = i + 1;
30411 }
30412 return i;
30413 }
30414 "#,
30415 );
30416 assert!(matches!(result, Ok(Value::Int(1000))));
30417 }
30418
30419 #[test]
30420 fn test_no_leak_repeated_closure_creation() {
30421 let result = eval(
30423 r#"
30424 fn main() {
30425 let i = 0;
30426 let total = 0;
30427 while i < 500 {
30428 let x = i;
30429 fn add_x(y) { return x + y; }
30430 total = total + add_x(1);
30431 i = i + 1;
30432 }
30433 return total;
30434 }
30435 "#,
30436 );
30437 assert!(matches!(result, Ok(Value::Int(125250))));
30439 }
30440
30441 #[test]
30442 fn test_no_leak_nested_data_structures() {
30443 let result = eval(
30445 r#"
30446 fn main() {
30447 let i = 0;
30448 while i < 200 {
30449 let inner1 = [1, 2, 3];
30450 let inner2 = [4, 5, 6];
30451 let outer = [inner1, inner2];
30452 let m = map_new();
30453 map_set(m, "arr", outer);
30454 map_set(m, "nested", map_new());
30455 i = i + 1;
30456 }
30457 return i;
30458 }
30459 "#,
30460 );
30461 assert!(matches!(result, Ok(Value::Int(200))));
30462 }
30463
30464 #[test]
30465 fn test_no_leak_repeated_interpreter_creation() {
30466 for _ in 0..50 {
30468 let result = eval(
30469 r#"
30470 fn main() {
30471 let arr = [1, 2, 3, 4, 5];
30472 let total = sum(arr);
30473 return total * 2;
30474 }
30475 "#,
30476 );
30477 assert!(matches!(result, Ok(Value::Int(30))));
30478 }
30479 }
30480}