1use crate::interpreter::{Interpreter, Value, Evidence, RuntimeError, BuiltInFn, ChannelInner, ActorInner};
46use std::rc::Rc;
47use std::cell::RefCell;
48use std::collections::HashMap;
49use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH};
50use std::io::Write;
51use std::sync::{Arc, Mutex, mpsc};
52use std::thread;
53
54use sha2::{Sha256, Sha512, Digest};
56use md5::Md5;
57use base64::{Engine as _, engine::general_purpose};
58use regex::Regex;
59use uuid::Uuid;
60use unicode_normalization::UnicodeNormalization;
61use unicode_segmentation::UnicodeSegmentation;
62
63use unicode_script::{Script, UnicodeScript};
65use unicode_bidi::BidiInfo;
66use unicode_width::UnicodeWidthStr;
67use deunicode::deunicode;
68use icu_collator::{Collator, CollatorOptions};
69use icu_locid::{Locale, LanguageIdentifier};
70use icu_casemap::CaseMapper;
71use icu_casemap::titlecase::TitlecaseOptions;
72use icu_segmenter::{SentenceSegmenter, WordSegmenter};
73
74use whatlang::{detect, Lang, Script as WhatLangScript};
76use rust_stemmers::{Algorithm as StemAlgorithm, Stemmer};
77use tiktoken_rs::{cl100k_base, p50k_base, r50k_base};
78
79use rand::Rng;
81
82pub fn register_stdlib(interp: &mut Interpreter) {
84 register_core(interp);
85 register_math(interp);
86 register_collections(interp);
87 register_string(interp);
88 register_evidence(interp);
89 register_affect(interp);
90 register_iter(interp);
91 register_io(interp);
92 register_time(interp);
93 register_random(interp);
94 register_convert(interp);
95 register_cycle(interp);
96 register_simd(interp);
97 register_graphics_math(interp);
98 register_concurrency(interp);
99 register_json(interp);
101 register_fs(interp);
102 register_crypto(interp);
103 register_regex(interp);
104 register_uuid(interp);
105 register_system(interp);
106 register_stats(interp);
107 register_matrix(interp);
108 register_functional(interp);
110 register_benchmark(interp);
111 register_itertools(interp);
112 register_ranges(interp);
113 register_bitwise(interp);
114 register_format(interp);
115 register_pattern(interp);
117 register_devex(interp);
119 register_soa(interp);
121 register_tensor(interp);
122 register_autodiff(interp);
123 register_spatial(interp);
124 register_physics(interp);
125 register_geometric_algebra(interp);
127 register_dimensional(interp);
128 register_ecs(interp);
129 register_polycultural_text(interp);
131 register_text_intelligence(interp);
133 register_hologram(interp);
135 register_experimental_crypto(interp);
136 register_multibase(interp);
138 register_audio(interp);
140 register_spirituality(interp);
142 register_color(interp);
144 register_protocol(interp);
146}
147
148fn define(
150 interp: &mut Interpreter,
151 name: &str,
152 arity: Option<usize>,
153 func: fn(&mut Interpreter, Vec<Value>) -> Result<Value, RuntimeError>,
154) {
155 let builtin = Value::BuiltIn(Rc::new(BuiltInFn {
156 name: name.to_string(),
157 arity,
158 func,
159 }));
160 interp.globals.borrow_mut().define(name.to_string(), builtin);
161}
162
163fn values_equal_simple(a: &Value, b: &Value) -> bool {
165 match (a, b) {
166 (Value::Int(x), Value::Int(y)) => x == y,
167 (Value::Float(x), Value::Float(y)) => (x - y).abs() < f64::EPSILON,
168 (Value::Int(x), Value::Float(y)) | (Value::Float(y), Value::Int(x)) => (*x as f64 - y).abs() < f64::EPSILON,
169 (Value::Bool(x), Value::Bool(y)) => x == y,
170 (Value::String(x), Value::String(y)) => x == y,
171 (Value::Char(x), Value::Char(y)) => x == y,
172 (Value::Null, Value::Null) => true,
173 (Value::Empty, Value::Empty) => true,
174 (Value::Infinity, Value::Infinity) => true,
175 _ => false,
176 }
177}
178
179fn register_core(interp: &mut Interpreter) {
184 define(interp, "print", None, |interp, args| {
186 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
187 let line = output.join(" ");
188 print!("{}", line);
189 std::io::stdout().flush().ok();
190 interp.output.push(line);
191 Ok(Value::Null)
192 });
193
194 define(interp, "println", None, |interp, args| {
196 let output: Vec<String> = args.iter().map(|v| format!("{}", v)).collect();
197 let line = output.join(" ");
198 println!("{}", line);
199 interp.output.push(line);
200 Ok(Value::Null)
201 });
202
203 define(interp, "dbg", Some(1), |interp, args| {
205 let output = format!("[DEBUG] {:?}", args[0]);
206 println!("{}", output);
207 interp.output.push(output);
208 Ok(args[0].clone())
209 });
210
211 define(interp, "type_of", Some(1), |_, args| {
213 let type_name = match &args[0] {
214 Value::Null => "null",
215 Value::Bool(_) => "bool",
216 Value::Int(_) => "i64",
217 Value::Float(_) => "f64",
218 Value::String(_) => "str",
219 Value::Char(_) => "char",
220 Value::Array(_) => "array",
221 Value::Tuple(_) => "tuple",
222 Value::Struct { name, .. } => name,
223 Value::Variant { enum_name, .. } => enum_name,
224 Value::Function(_) => "fn",
225 Value::BuiltIn(_) => "builtin",
226 Value::Ref(_) => "ref",
227 Value::Infinity => "infinity",
228 Value::Empty => "empty",
229 Value::Evidential { evidence, .. } => match evidence {
230 Evidence::Known => "known",
231 Evidence::Uncertain => "uncertain",
232 Evidence::Reported => "reported",
233 Evidence::Paradox => "paradox",
234 },
235 Value::Affective { .. } => "affective",
236 Value::Map(_) => "map",
237 Value::Set(_) => "set",
238 Value::Channel(_) => "channel",
239 Value::ThreadHandle(_) => "thread",
240 Value::Actor(_) => "actor",
241 Value::Future(_) => "future",
242 };
243 Ok(Value::String(Rc::new(type_name.to_string())))
244 });
245
246 define(interp, "assert", None, |_, args| {
248 if args.is_empty() {
249 return Err(RuntimeError::new("assert() requires at least one argument"));
250 }
251 let condition = match &args[0] {
252 Value::Bool(b) => *b,
253 _ => return Err(RuntimeError::new("assert() condition must be bool")),
254 };
255 if !condition {
256 let msg = if args.len() > 1 {
257 format!("{}", args[1])
258 } else {
259 "assertion failed".to_string()
260 };
261 return Err(RuntimeError::new(format!("Assertion failed: {}", msg)));
262 }
263 Ok(Value::Null)
264 });
265
266 define(interp, "panic", None, |_, args| {
268 let msg = if args.is_empty() {
269 "explicit panic".to_string()
270 } else {
271 args.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(" ")
272 };
273 Err(RuntimeError::new(format!("PANIC: {}", msg)))
274 });
275
276 define(interp, "todo", None, |_, args| {
278 let msg = if args.is_empty() {
279 "not yet implemented".to_string()
280 } else {
281 format!("{}", args[0])
282 };
283 Err(RuntimeError::new(format!("TODO: {}", msg)))
284 });
285
286 define(interp, "unreachable", None, |_, args| {
288 let msg = if args.is_empty() {
289 "entered unreachable code".to_string()
290 } else {
291 format!("{}", args[0])
292 };
293 Err(RuntimeError::new(format!("UNREACHABLE: {}", msg)))
294 });
295
296 define(interp, "clone", Some(1), |_, args| {
298 Ok(deep_clone(&args[0]))
299 });
300
301 define(interp, "id", Some(1), |_, args| {
303 Ok(args[0].clone())
304 });
305
306 define(interp, "default", Some(1), |_, args| {
308 let type_name = match &args[0] {
309 Value::String(s) => s.as_str(),
310 _ => return Err(RuntimeError::new("default() requires type name string")),
311 };
312 match type_name {
313 "bool" => Ok(Value::Bool(false)),
314 "i64" | "int" => Ok(Value::Int(0)),
315 "f64" | "float" => Ok(Value::Float(0.0)),
316 "str" | "string" => Ok(Value::String(Rc::new(String::new()))),
317 "array" => Ok(Value::Array(Rc::new(RefCell::new(Vec::new())))),
318 _ => Err(RuntimeError::new(format!("no default for type: {}", type_name))),
319 }
320 });
321}
322
323fn deep_clone(value: &Value) -> Value {
325 match value {
326 Value::Array(arr) => {
327 let cloned: Vec<Value> = arr.borrow().iter().map(deep_clone).collect();
328 Value::Array(Rc::new(RefCell::new(cloned)))
329 }
330 Value::Struct { name, fields } => {
331 let cloned: HashMap<String, Value> = fields.borrow()
332 .iter()
333 .map(|(k, v)| (k.clone(), deep_clone(v)))
334 .collect();
335 Value::Struct {
336 name: name.clone(),
337 fields: Rc::new(RefCell::new(cloned)),
338 }
339 }
340 Value::Evidential { value, evidence } => Value::Evidential {
341 value: Box::new(deep_clone(value)),
342 evidence: *evidence,
343 },
344 other => other.clone(),
345 }
346}
347
348fn register_math(interp: &mut Interpreter) {
353 define(interp, "abs", Some(1), |_, args| {
355 match &args[0] {
356 Value::Int(n) => Ok(Value::Int(n.abs())),
357 Value::Float(n) => Ok(Value::Float(n.abs())),
358 _ => Err(RuntimeError::new("abs() requires number")),
359 }
360 });
361
362 define(interp, "neg", Some(1), |_, args| {
363 match &args[0] {
364 Value::Int(n) => Ok(Value::Int(-n)),
365 Value::Float(n) => Ok(Value::Float(-n)),
366 _ => Err(RuntimeError::new("neg() requires number")),
367 }
368 });
369
370 define(interp, "sqrt", Some(1), |_, args| {
371 match &args[0] {
372 Value::Int(n) => Ok(Value::Float((*n as f64).sqrt())),
373 Value::Float(n) => Ok(Value::Float(n.sqrt())),
374 _ => Err(RuntimeError::new("sqrt() requires number")),
375 }
376 });
377
378 define(interp, "cbrt", Some(1), |_, args| {
379 match &args[0] {
380 Value::Int(n) => Ok(Value::Float((*n as f64).cbrt())),
381 Value::Float(n) => Ok(Value::Float(n.cbrt())),
382 _ => Err(RuntimeError::new("cbrt() requires number")),
383 }
384 });
385
386 define(interp, "pow", Some(2), |_, args| {
387 match (&args[0], &args[1]) {
388 (Value::Int(base), Value::Int(exp)) => {
389 if *exp >= 0 {
390 Ok(Value::Int(base.pow(*exp as u32)))
391 } else {
392 Ok(Value::Float((*base as f64).powi(*exp as i32)))
393 }
394 }
395 (Value::Float(base), Value::Int(exp)) => Ok(Value::Float(base.powi(*exp as i32))),
396 (Value::Float(base), Value::Float(exp)) => Ok(Value::Float(base.powf(*exp))),
397 (Value::Int(base), Value::Float(exp)) => Ok(Value::Float((*base as f64).powf(*exp))),
398 _ => Err(RuntimeError::new("pow() requires numbers")),
399 }
400 });
401
402 define(interp, "exp", Some(1), |_, args| {
403 match &args[0] {
404 Value::Int(n) => Ok(Value::Float((*n as f64).exp())),
405 Value::Float(n) => Ok(Value::Float(n.exp())),
406 _ => Err(RuntimeError::new("exp() requires number")),
407 }
408 });
409
410 define(interp, "ln", Some(1), |_, args| {
411 match &args[0] {
412 Value::Int(n) => Ok(Value::Float((*n as f64).ln())),
413 Value::Float(n) => Ok(Value::Float(n.ln())),
414 _ => Err(RuntimeError::new("ln() requires number")),
415 }
416 });
417
418 define(interp, "log", Some(2), |_, args| {
419 let (value, base) = match (&args[0], &args[1]) {
420 (Value::Int(v), Value::Int(b)) => (*v as f64, *b as f64),
421 (Value::Float(v), Value::Int(b)) => (*v, *b as f64),
422 (Value::Int(v), Value::Float(b)) => (*v as f64, *b),
423 (Value::Float(v), Value::Float(b)) => (*v, *b),
424 _ => return Err(RuntimeError::new("log() requires numbers")),
425 };
426 Ok(Value::Float(value.log(base)))
427 });
428
429 define(interp, "log10", Some(1), |_, args| {
430 match &args[0] {
431 Value::Int(n) => Ok(Value::Float((*n as f64).log10())),
432 Value::Float(n) => Ok(Value::Float(n.log10())),
433 _ => Err(RuntimeError::new("log10() requires number")),
434 }
435 });
436
437 define(interp, "log2", Some(1), |_, args| {
438 match &args[0] {
439 Value::Int(n) => Ok(Value::Float((*n as f64).log2())),
440 Value::Float(n) => Ok(Value::Float(n.log2())),
441 _ => Err(RuntimeError::new("log2() requires number")),
442 }
443 });
444
445 define(interp, "sin", Some(1), |_, args| {
447 match &args[0] {
448 Value::Int(n) => Ok(Value::Float((*n as f64).sin())),
449 Value::Float(n) => Ok(Value::Float(n.sin())),
450 _ => Err(RuntimeError::new("sin() requires number")),
451 }
452 });
453
454 define(interp, "cos", Some(1), |_, args| {
455 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
462 define(interp, "tan", Some(1), |_, args| {
463 match &args[0] {
464 Value::Int(n) => Ok(Value::Float((*n as f64).tan())),
465 Value::Float(n) => Ok(Value::Float(n.tan())),
466 _ => Err(RuntimeError::new("tan() requires number")),
467 }
468 });
469
470 define(interp, "asin", Some(1), |_, args| {
471 match &args[0] {
472 Value::Int(n) => Ok(Value::Float((*n as f64).asin())),
473 Value::Float(n) => Ok(Value::Float(n.asin())),
474 _ => Err(RuntimeError::new("asin() requires number")),
475 }
476 });
477
478 define(interp, "acos", Some(1), |_, args| {
479 match &args[0] {
480 Value::Int(n) => Ok(Value::Float((*n as f64).acos())),
481 Value::Float(n) => Ok(Value::Float(n.acos())),
482 _ => Err(RuntimeError::new("acos() requires number")),
483 }
484 });
485
486 define(interp, "atan", Some(1), |_, args| {
487 match &args[0] {
488 Value::Int(n) => Ok(Value::Float((*n as f64).atan())),
489 Value::Float(n) => Ok(Value::Float(n.atan())),
490 _ => Err(RuntimeError::new("atan() requires number")),
491 }
492 });
493
494 define(interp, "atan2", Some(2), |_, args| {
495 let (y, x) = match (&args[0], &args[1]) {
496 (Value::Int(y), Value::Int(x)) => (*y as f64, *x as f64),
497 (Value::Float(y), Value::Int(x)) => (*y, *x as f64),
498 (Value::Int(y), Value::Float(x)) => (*y as f64, *x),
499 (Value::Float(y), Value::Float(x)) => (*y, *x),
500 _ => return Err(RuntimeError::new("atan2() requires numbers")),
501 };
502 Ok(Value::Float(y.atan2(x)))
503 });
504
505 define(interp, "sinh", Some(1), |_, args| {
507 match &args[0] {
508 Value::Int(n) => Ok(Value::Float((*n as f64).sinh())),
509 Value::Float(n) => Ok(Value::Float(n.sinh())),
510 _ => Err(RuntimeError::new("sinh() requires number")),
511 }
512 });
513
514 define(interp, "cosh", Some(1), |_, args| {
515 match &args[0] {
516 Value::Int(n) => Ok(Value::Float((*n as f64).cosh())),
517 Value::Float(n) => Ok(Value::Float(n.cosh())),
518 _ => Err(RuntimeError::new("cosh() requires number")),
519 }
520 });
521
522 define(interp, "tanh", Some(1), |_, args| {
523 match &args[0] {
524 Value::Int(n) => Ok(Value::Float((*n as f64).tanh())),
525 Value::Float(n) => Ok(Value::Float(n.tanh())),
526 _ => Err(RuntimeError::new("tanh() requires number")),
527 }
528 });
529
530 define(interp, "floor", Some(1), |_, args| {
532 match &args[0] {
533 Value::Int(n) => Ok(Value::Int(*n)),
534 Value::Float(n) => Ok(Value::Int(n.floor() as i64)),
535 _ => Err(RuntimeError::new("floor() requires number")),
536 }
537 });
538
539 define(interp, "ceil", Some(1), |_, args| {
540 match &args[0] {
541 Value::Int(n) => Ok(Value::Int(*n)),
542 Value::Float(n) => Ok(Value::Int(n.ceil() as i64)),
543 _ => Err(RuntimeError::new("ceil() requires number")),
544 }
545 });
546
547 define(interp, "round", Some(1), |_, args| {
548 match &args[0] {
549 Value::Int(n) => Ok(Value::Int(*n)),
550 Value::Float(n) => Ok(Value::Int(n.round() as i64)),
551 _ => Err(RuntimeError::new("round() requires number")),
552 }
553 });
554
555 define(interp, "trunc", Some(1), |_, args| {
556 match &args[0] {
557 Value::Int(n) => Ok(Value::Int(*n)),
558 Value::Float(n) => Ok(Value::Int(n.trunc() as i64)),
559 _ => Err(RuntimeError::new("trunc() requires number")),
560 }
561 });
562
563 define(interp, "fract", Some(1), |_, args| {
564 match &args[0] {
565 Value::Int(_) => Ok(Value::Float(0.0)),
566 Value::Float(n) => Ok(Value::Float(n.fract())),
567 _ => Err(RuntimeError::new("fract() requires number")),
568 }
569 });
570
571 define(interp, "min", Some(2), |_, args| {
573 match (&args[0], &args[1]) {
574 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
575 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
576 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).min(*b))),
577 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.min(*b as f64))),
578 _ => Err(RuntimeError::new("min() requires numbers")),
579 }
580 });
581
582 define(interp, "max", Some(2), |_, args| {
583 match (&args[0], &args[1]) {
584 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
585 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
586 (Value::Int(a), Value::Float(b)) => Ok(Value::Float((*a as f64).max(*b))),
587 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a.max(*b as f64))),
588 _ => Err(RuntimeError::new("max() requires numbers")),
589 }
590 });
591
592 define(interp, "clamp", Some(3), |_, args| {
593 match (&args[0], &args[1], &args[2]) {
594 (Value::Int(val), Value::Int(min), Value::Int(max)) => {
595 Ok(Value::Int(*val.max(min).min(max)))
596 }
597 (Value::Float(val), Value::Float(min), Value::Float(max)) => {
598 Ok(Value::Float(val.max(*min).min(*max)))
599 }
600 _ => Err(RuntimeError::new("clamp() requires matching number types")),
601 }
602 });
603
604 define(interp, "sign", Some(1), |_, args| {
606 match &args[0] {
607 Value::Int(n) => Ok(Value::Int(n.signum())),
608 Value::Float(n) => Ok(Value::Float(if *n > 0.0 { 1.0 } else if *n < 0.0 { -1.0 } else { 0.0 })),
609 _ => Err(RuntimeError::new("sign() requires number")),
610 }
611 });
612
613 define(interp, "PI", Some(0), |_, _| Ok(Value::Float(std::f64::consts::PI)));
615 define(interp, "E", Some(0), |_, _| Ok(Value::Float(std::f64::consts::E)));
616 define(interp, "TAU", Some(0), |_, _| Ok(Value::Float(std::f64::consts::TAU)));
617 define(interp, "PHI", Some(0), |_, _| Ok(Value::Float(1.618033988749895))); define(interp, "gcd", Some(2), |_, args| {
621 match (&args[0], &args[1]) {
622 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(gcd(*a, *b))),
623 _ => Err(RuntimeError::new("gcd() requires integers")),
624 }
625 });
626
627 define(interp, "lcm", Some(2), |_, args| {
628 match (&args[0], &args[1]) {
629 (Value::Int(a), Value::Int(b)) => {
630 let g = gcd(*a, *b);
631 Ok(Value::Int((a * b).abs() / g))
632 }
633 _ => Err(RuntimeError::new("lcm() requires integers")),
634 }
635 });
636
637 define(interp, "factorial", Some(1), |_, args| {
639 match &args[0] {
640 Value::Int(n) if *n >= 0 => {
641 let mut result: i64 = 1;
642 for i in 2..=(*n as u64) {
643 result = result.saturating_mul(i as i64);
644 }
645 Ok(Value::Int(result))
646 }
647 Value::Int(_) => Err(RuntimeError::new("factorial() requires non-negative integer")),
648 _ => Err(RuntimeError::new("factorial() requires integer")),
649 }
650 });
651
652 define(interp, "is_nan", Some(1), |_, args| {
654 match &args[0] {
655 Value::Float(n) => Ok(Value::Bool(n.is_nan())),
656 Value::Int(_) => Ok(Value::Bool(false)),
657 _ => Err(RuntimeError::new("is_nan() requires number")),
658 }
659 });
660
661 define(interp, "is_infinite", Some(1), |_, args| {
662 match &args[0] {
663 Value::Float(n) => Ok(Value::Bool(n.is_infinite())),
664 Value::Int(_) => Ok(Value::Bool(false)),
665 Value::Infinity => Ok(Value::Bool(true)),
666 _ => Err(RuntimeError::new("is_infinite() requires number")),
667 }
668 });
669
670 define(interp, "is_finite", Some(1), |_, args| {
671 match &args[0] {
672 Value::Float(n) => Ok(Value::Bool(n.is_finite())),
673 Value::Int(_) => Ok(Value::Bool(true)),
674 Value::Infinity => Ok(Value::Bool(false)),
675 _ => Err(RuntimeError::new("is_finite() requires number")),
676 }
677 });
678
679 define(interp, "is_even", Some(1), |_, args| {
680 match &args[0] {
681 Value::Int(n) => Ok(Value::Bool(n % 2 == 0)),
682 _ => Err(RuntimeError::new("is_even() requires integer")),
683 }
684 });
685
686 define(interp, "is_odd", Some(1), |_, args| {
687 match &args[0] {
688 Value::Int(n) => Ok(Value::Bool(n % 2 != 0)),
689 _ => Err(RuntimeError::new("is_odd() requires integer")),
690 }
691 });
692
693 define(interp, "is_prime", Some(1), |_, args| {
694 match &args[0] {
695 Value::Int(n) => Ok(Value::Bool(is_prime(*n))),
696 _ => Err(RuntimeError::new("is_prime() requires integer")),
697 }
698 });
699}
700
701fn gcd(mut a: i64, mut b: i64) -> i64 {
702 a = a.abs();
703 b = b.abs();
704 while b != 0 {
705 let t = b;
706 b = a % b;
707 a = t;
708 }
709 a
710}
711
712fn is_prime(n: i64) -> bool {
713 if n < 2 { return false; }
714 if n == 2 { return true; }
715 if n % 2 == 0 { return false; }
716 let sqrt = (n as f64).sqrt() as i64;
717 for i in (3..=sqrt).step_by(2) {
718 if n % i == 0 { return false; }
719 }
720 true
721}
722
723fn register_collections(interp: &mut Interpreter) {
728 define(interp, "len", Some(1), |_, args| {
730 match &args[0] {
731 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
732 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
733 Value::Tuple(t) => Ok(Value::Int(t.len() as i64)),
734 Value::Map(m) => Ok(Value::Int(m.borrow().len() as i64)),
735 Value::Set(s) => Ok(Value::Int(s.borrow().len() as i64)),
736 _ => Err(RuntimeError::new("len() requires array, string, tuple, map, or set")),
737 }
738 });
739
740 define(interp, "is_empty", Some(1), |_, args| {
741 match &args[0] {
742 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
743 Value::String(s) => Ok(Value::Bool(s.is_empty())),
744 Value::Tuple(t) => Ok(Value::Bool(t.is_empty())),
745 Value::Map(m) => Ok(Value::Bool(m.borrow().is_empty())),
746 Value::Set(s) => Ok(Value::Bool(s.borrow().is_empty())),
747 _ => Err(RuntimeError::new("is_empty() requires collection")),
748 }
749 });
750
751 define(interp, "push", Some(2), |_, args| {
753 match &args[0] {
754 Value::Array(arr) => {
755 arr.borrow_mut().push(args[1].clone());
756 Ok(Value::Null)
757 }
758 _ => Err(RuntimeError::new("push() requires array")),
759 }
760 });
761
762 define(interp, "pop", Some(1), |_, args| {
763 match &args[0] {
764 Value::Array(arr) => {
765 arr.borrow_mut().pop()
766 .ok_or_else(|| RuntimeError::new("pop() on empty array"))
767 }
768 _ => Err(RuntimeError::new("pop() requires array")),
769 }
770 });
771
772 define(interp, "first", Some(1), |_, args| {
773 match &args[0] {
774 Value::Array(arr) => {
775 arr.borrow().first().cloned()
776 .ok_or_else(|| RuntimeError::new("first() on empty array"))
777 }
778 Value::Tuple(t) => {
779 t.first().cloned()
780 .ok_or_else(|| RuntimeError::new("first() on empty tuple"))
781 }
782 _ => Err(RuntimeError::new("first() requires array or tuple")),
783 }
784 });
785
786 define(interp, "last", Some(1), |_, args| {
787 match &args[0] {
788 Value::Array(arr) => {
789 arr.borrow().last().cloned()
790 .ok_or_else(|| RuntimeError::new("last() on empty array"))
791 }
792 Value::Tuple(t) => {
793 t.last().cloned()
794 .ok_or_else(|| RuntimeError::new("last() on empty tuple"))
795 }
796 _ => Err(RuntimeError::new("last() requires array or tuple")),
797 }
798 });
799
800 define(interp, "middle", Some(1), |_, args| {
802 match &args[0] {
803 Value::Array(arr) => {
804 let arr = arr.borrow();
805 if arr.is_empty() {
806 return Err(RuntimeError::new("middle() on empty array"));
807 }
808 let mid = arr.len() / 2;
809 Ok(arr[mid].clone())
810 }
811 Value::Tuple(t) => {
812 if t.is_empty() {
813 return Err(RuntimeError::new("middle() on empty tuple"));
814 }
815 let mid = t.len() / 2;
816 Ok(t[mid].clone())
817 }
818 _ => Err(RuntimeError::new("middle() requires array or tuple")),
819 }
820 });
821
822 define(interp, "choice", Some(1), |_, args| {
824 use std::time::{SystemTime, UNIX_EPOCH};
825 match &args[0] {
826 Value::Array(arr) => {
827 let arr = arr.borrow();
828 if arr.is_empty() {
829 return Err(RuntimeError::new("choice() on empty array"));
830 }
831 let seed = SystemTime::now()
832 .duration_since(UNIX_EPOCH)
833 .unwrap_or(std::time::Duration::ZERO)
834 .as_nanos() as u64;
835 let idx = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % arr.len();
836 Ok(arr[idx].clone())
837 }
838 Value::Tuple(t) => {
839 if t.is_empty() {
840 return Err(RuntimeError::new("choice() on empty tuple"));
841 }
842 let seed = SystemTime::now()
843 .duration_since(UNIX_EPOCH)
844 .unwrap_or(std::time::Duration::ZERO)
845 .as_nanos() as u64;
846 let idx = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % t.len();
847 Ok(t[idx].clone())
848 }
849 _ => Err(RuntimeError::new("choice() requires array or tuple")),
850 }
851 });
852
853 define(interp, "nth", Some(2), |_, args| {
855 let n = match &args[1] {
856 Value::Int(i) => *i,
857 _ => return Err(RuntimeError::new("nth() index must be integer")),
858 };
859 match &args[0] {
860 Value::Array(arr) => {
861 let arr = arr.borrow();
862 if n < 0 || n as usize >= arr.len() {
863 return Err(RuntimeError::new("nth() index out of bounds"));
864 }
865 Ok(arr[n as usize].clone())
866 }
867 Value::Tuple(t) => {
868 if n < 0 || n as usize >= t.len() {
869 return Err(RuntimeError::new("nth() index out of bounds"));
870 }
871 Ok(t[n as usize].clone())
872 }
873 _ => Err(RuntimeError::new("nth() requires array or tuple")),
874 }
875 });
876
877 define(interp, "next", Some(1), |_, args| {
879 match &args[0] {
880 Value::Array(arr) => {
881 let mut arr = arr.borrow_mut();
882 if arr.is_empty() {
883 return Err(RuntimeError::new("next() on empty array"));
884 }
885 Ok(arr.remove(0))
886 }
887 _ => Err(RuntimeError::new("next() requires array")),
888 }
889 });
890
891 define(interp, "peek", Some(1), |_, args| {
893 match &args[0] {
894 Value::Array(arr) => {
895 arr.borrow().first().cloned()
896 .ok_or_else(|| RuntimeError::new("peek() on empty array"))
897 }
898 _ => Err(RuntimeError::new("peek() requires array")),
899 }
900 });
901
902 define(interp, "get", Some(2), |_, args| {
903 let index = match &args[1] {
904 Value::Int(i) => *i,
905 _ => return Err(RuntimeError::new("get() index must be integer")),
906 };
907 match &args[0] {
908 Value::Array(arr) => {
909 let arr = arr.borrow();
910 let idx = if index < 0 { arr.len() as i64 + index } else { index } as usize;
911 arr.get(idx).cloned()
912 .ok_or_else(|| RuntimeError::new("index out of bounds"))
913 }
914 Value::Tuple(t) => {
915 let idx = if index < 0 { t.len() as i64 + index } else { index } as usize;
916 t.get(idx).cloned()
917 .ok_or_else(|| RuntimeError::new("index out of bounds"))
918 }
919 _ => Err(RuntimeError::new("get() requires array or tuple")),
920 }
921 });
922
923 define(interp, "set", Some(3), |_, args| {
924 let index = match &args[1] {
925 Value::Int(i) => *i as usize,
926 _ => return Err(RuntimeError::new("set() 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[index] = args[2].clone();
935 Ok(Value::Null)
936 }
937 _ => Err(RuntimeError::new("set() requires array")),
938 }
939 });
940
941 define(interp, "insert", Some(3), |_, args| {
942 let index = match &args[1] {
943 Value::Int(i) => *i as usize,
944 _ => return Err(RuntimeError::new("insert() 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 arr.insert(index, args[2].clone());
953 Ok(Value::Null)
954 }
955 _ => Err(RuntimeError::new("insert() requires array")),
956 }
957 });
958
959 define(interp, "remove", Some(2), |_, args| {
960 let index = match &args[1] {
961 Value::Int(i) => *i as usize,
962 _ => return Err(RuntimeError::new("remove() index must be integer")),
963 };
964 match &args[0] {
965 Value::Array(arr) => {
966 let mut arr = arr.borrow_mut();
967 if index >= arr.len() {
968 return Err(RuntimeError::new("index out of bounds"));
969 }
970 Ok(arr.remove(index))
971 }
972 _ => Err(RuntimeError::new("remove() requires array")),
973 }
974 });
975
976 define(interp, "clear", Some(1), |_, args| {
977 match &args[0] {
978 Value::Array(arr) => {
979 arr.borrow_mut().clear();
980 Ok(Value::Null)
981 }
982 _ => Err(RuntimeError::new("clear() requires array")),
983 }
984 });
985
986 define(interp, "contains", Some(2), |_, args| {
988 match &args[0] {
989 Value::Array(arr) => {
990 Ok(Value::Bool(arr.borrow().iter().any(|v| values_equal(v, &args[1]))))
991 }
992 Value::String(s) => {
993 match &args[1] {
994 Value::String(sub) => Ok(Value::Bool(s.contains(sub.as_str()))),
995 Value::Char(c) => Ok(Value::Bool(s.contains(*c))),
996 _ => Err(RuntimeError::new("string contains() requires string or char")),
997 }
998 }
999 _ => Err(RuntimeError::new("contains() requires array or string")),
1000 }
1001 });
1002
1003 define(interp, "index_of", Some(2), |_, args| {
1004 match &args[0] {
1005 Value::Array(arr) => {
1006 let idx = arr.borrow().iter().position(|v| values_equal(v, &args[1]));
1007 match idx {
1008 Some(i) => Ok(Value::Int(i as i64)),
1009 None => Ok(Value::Int(-1)),
1010 }
1011 }
1012 Value::String(s) => {
1013 match &args[1] {
1014 Value::String(sub) => {
1015 match s.find(sub.as_str()) {
1016 Some(i) => Ok(Value::Int(i as i64)),
1017 None => Ok(Value::Int(-1)),
1018 }
1019 }
1020 Value::Char(c) => {
1021 match s.find(*c) {
1022 Some(i) => Ok(Value::Int(i as i64)),
1023 None => Ok(Value::Int(-1)),
1024 }
1025 }
1026 _ => Err(RuntimeError::new("string index_of() requires string or char")),
1027 }
1028 }
1029 _ => Err(RuntimeError::new("index_of() requires array or string")),
1030 }
1031 });
1032
1033 define(interp, "reverse", Some(1), |_, args| {
1035 match &args[0] {
1036 Value::Array(arr) => {
1037 let mut reversed: Vec<Value> = arr.borrow().clone();
1038 reversed.reverse();
1039 Ok(Value::Array(Rc::new(RefCell::new(reversed))))
1040 }
1041 Value::String(s) => {
1042 let reversed: String = s.chars().rev().collect();
1043 Ok(Value::String(Rc::new(reversed)))
1044 }
1045 _ => Err(RuntimeError::new("reverse() requires array or string")),
1046 }
1047 });
1048
1049 define(interp, "sort", Some(1), |_, args| {
1050 match &args[0] {
1051 Value::Array(arr) => {
1052 let mut sorted: Vec<Value> = arr.borrow().clone();
1053 sorted.sort_by(compare_values);
1054 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1055 }
1056 _ => Err(RuntimeError::new("sort() requires array")),
1057 }
1058 });
1059
1060 define(interp, "sort_desc", Some(1), |_, args| {
1061 match &args[0] {
1062 Value::Array(arr) => {
1063 let mut sorted: Vec<Value> = arr.borrow().clone();
1064 sorted.sort_by(|a, b| compare_values(b, a));
1065 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
1066 }
1067 _ => Err(RuntimeError::new("sort_desc() requires array")),
1068 }
1069 });
1070
1071 define(interp, "unique", Some(1), |_, args| {
1072 match &args[0] {
1073 Value::Array(arr) => {
1074 let arr = arr.borrow();
1075 let mut seen = Vec::new();
1076 let unique: Vec<Value> = arr.iter()
1077 .filter(|v| {
1078 if seen.iter().any(|s| values_equal(s, v)) {
1079 false
1080 } else {
1081 seen.push((*v).clone());
1082 true
1083 }
1084 })
1085 .cloned()
1086 .collect();
1087 Ok(Value::Array(Rc::new(RefCell::new(unique))))
1088 }
1089 _ => Err(RuntimeError::new("unique() requires array")),
1090 }
1091 });
1092
1093 define(interp, "flatten", Some(1), |_, args| {
1094 match &args[0] {
1095 Value::Array(arr) => {
1096 let mut flattened = Vec::new();
1097 for item in arr.borrow().iter() {
1098 match item {
1099 Value::Array(inner) => flattened.extend(inner.borrow().clone()),
1100 other => flattened.push(other.clone()),
1101 }
1102 }
1103 Ok(Value::Array(Rc::new(RefCell::new(flattened))))
1104 }
1105 _ => Err(RuntimeError::new("flatten() requires array")),
1106 }
1107 });
1108
1109 define(interp, "concat", Some(2), |_, args| {
1111 match (&args[0], &args[1]) {
1112 (Value::Array(a), Value::Array(b)) => {
1113 let mut result = a.borrow().clone();
1114 result.extend(b.borrow().clone());
1115 Ok(Value::Array(Rc::new(RefCell::new(result))))
1116 }
1117 (Value::String(a), Value::String(b)) => {
1118 Ok(Value::String(Rc::new(format!("{}{}", a, b))))
1119 }
1120 _ => Err(RuntimeError::new("concat() requires two arrays or two strings")),
1121 }
1122 });
1123
1124 define(interp, "zip", Some(2), |_, args| {
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 zipped: Vec<Value> = a.iter().zip(b.iter())
1130 .map(|(x, y)| Value::Tuple(Rc::new(vec![x.clone(), y.clone()])))
1131 .collect();
1132 Ok(Value::Array(Rc::new(RefCell::new(zipped))))
1133 }
1134 _ => Err(RuntimeError::new("zip() requires two arrays")),
1135 }
1136 });
1137
1138 define(interp, "enumerate", Some(1), |_, args| {
1139 match &args[0] {
1140 Value::Array(arr) => {
1141 let enumerated: Vec<Value> = arr.borrow().iter()
1142 .enumerate()
1143 .map(|(i, v)| Value::Tuple(Rc::new(vec![Value::Int(i as i64), v.clone()])))
1144 .collect();
1145 Ok(Value::Array(Rc::new(RefCell::new(enumerated))))
1146 }
1147 _ => Err(RuntimeError::new("enumerate() requires array")),
1148 }
1149 });
1150
1151 define(interp, "zip_with", Some(3), |_, args| {
1154 let mode = match &args[2] {
1155 Value::String(s) => s.as_str(),
1156 _ => return Err(RuntimeError::new("zip_with() mode must be string")),
1157 };
1158 match (&args[0], &args[1]) {
1159 (Value::Array(a), Value::Array(b)) => {
1160 let a = a.borrow();
1161 let b = b.borrow();
1162 let result: Result<Vec<Value>, RuntimeError> = a.iter().zip(b.iter())
1163 .map(|(x, y)| {
1164 match (x, y, mode) {
1165 (Value::Int(a), Value::Int(b), "add") => Ok(Value::Int(a + b)),
1166 (Value::Int(a), Value::Int(b), "sub") => Ok(Value::Int(a - b)),
1167 (Value::Int(a), Value::Int(b), "mul") => Ok(Value::Int(a * b)),
1168 (Value::Float(a), Value::Float(b), "add") => Ok(Value::Float(a + b)),
1169 (Value::Float(a), Value::Float(b), "sub") => Ok(Value::Float(a - b)),
1170 (Value::Float(a), Value::Float(b), "mul") => Ok(Value::Float(a * b)),
1171 (_, _, "pair") => Ok(Value::Tuple(Rc::new(vec![x.clone(), y.clone()]))),
1172 _ => Err(RuntimeError::new("zip_with() incompatible types or mode")),
1173 }
1174 })
1175 .collect();
1176 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1177 }
1178 _ => Err(RuntimeError::new("zip_with() requires two arrays")),
1179 }
1180 });
1181
1182 define(interp, "supremum", Some(2), |_, args| {
1184 match (&args[0], &args[1]) {
1185 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1186 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1187 (Value::Array(a), Value::Array(b)) => {
1188 let a = a.borrow();
1190 let b = b.borrow();
1191 let result: Result<Vec<Value>, RuntimeError> = a.iter().zip(b.iter())
1192 .map(|(x, y)| match (x, y) {
1193 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.max(b))),
1194 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.max(*b))),
1195 _ => Err(RuntimeError::new("supremum() requires numeric arrays")),
1196 })
1197 .collect();
1198 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1199 }
1200 _ => Err(RuntimeError::new("supremum() requires numeric values or arrays")),
1201 }
1202 });
1203
1204 define(interp, "infimum", Some(2), |_, args| {
1206 match (&args[0], &args[1]) {
1207 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1208 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1209 (Value::Array(a), Value::Array(b)) => {
1210 let a = a.borrow();
1212 let b = b.borrow();
1213 let result: Result<Vec<Value>, RuntimeError> = a.iter().zip(b.iter())
1214 .map(|(x, y)| match (x, y) {
1215 (Value::Int(a), Value::Int(b)) => Ok(Value::Int(*a.min(b))),
1216 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a.min(*b))),
1217 _ => Err(RuntimeError::new("infimum() requires numeric arrays")),
1218 })
1219 .collect();
1220 Ok(Value::Array(Rc::new(RefCell::new(result?))))
1221 }
1222 _ => Err(RuntimeError::new("infimum() requires numeric values or arrays")),
1223 }
1224 });
1225
1226 define(interp, "slice", Some(3), |_, args| {
1228 let start = match &args[1] {
1229 Value::Int(i) => *i as usize,
1230 _ => return Err(RuntimeError::new("slice() start must be integer")),
1231 };
1232 let end = match &args[2] {
1233 Value::Int(i) => *i as usize,
1234 _ => return Err(RuntimeError::new("slice() end must be integer")),
1235 };
1236 match &args[0] {
1237 Value::Array(arr) => {
1238 let arr = arr.borrow();
1239 let end = end.min(arr.len());
1240 let sliced: Vec<Value> = arr[start..end].to_vec();
1241 Ok(Value::Array(Rc::new(RefCell::new(sliced))))
1242 }
1243 Value::String(s) => {
1244 let chars: Vec<char> = s.chars().collect();
1245 let end = end.min(chars.len());
1246 let sliced: String = chars[start..end].iter().collect();
1247 Ok(Value::String(Rc::new(sliced)))
1248 }
1249 _ => Err(RuntimeError::new("slice() requires array or string")),
1250 }
1251 });
1252
1253 define(interp, "take", Some(2), |_, args| {
1254 let n = match &args[1] {
1255 Value::Int(i) => *i as usize,
1256 _ => return Err(RuntimeError::new("take() n must be integer")),
1257 };
1258 match &args[0] {
1259 Value::Array(arr) => {
1260 let taken: Vec<Value> = arr.borrow().iter().take(n).cloned().collect();
1261 Ok(Value::Array(Rc::new(RefCell::new(taken))))
1262 }
1263 _ => Err(RuntimeError::new("take() requires array")),
1264 }
1265 });
1266
1267 define(interp, "skip", Some(2), |_, args| {
1268 let n = match &args[1] {
1269 Value::Int(i) => *i as usize,
1270 _ => return Err(RuntimeError::new("skip() n must be integer")),
1271 };
1272 match &args[0] {
1273 Value::Array(arr) => {
1274 let skipped: Vec<Value> = arr.borrow().iter().skip(n).cloned().collect();
1275 Ok(Value::Array(Rc::new(RefCell::new(skipped))))
1276 }
1277 _ => Err(RuntimeError::new("skip() requires array")),
1278 }
1279 });
1280
1281 define(interp, "chunk", Some(2), |_, args| {
1282 let size = match &args[1] {
1283 Value::Int(i) if *i > 0 => *i as usize,
1284 _ => return Err(RuntimeError::new("chunk() size must be positive integer")),
1285 };
1286 match &args[0] {
1287 Value::Array(arr) => {
1288 let chunks: Vec<Value> = arr.borrow()
1289 .chunks(size)
1290 .map(|c| Value::Array(Rc::new(RefCell::new(c.to_vec()))))
1291 .collect();
1292 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
1293 }
1294 _ => Err(RuntimeError::new("chunk() requires array")),
1295 }
1296 });
1297
1298 define(interp, "range", Some(2), |_, args| {
1300 let start = match &args[0] {
1301 Value::Int(n) => *n,
1302 _ => return Err(RuntimeError::new("range() requires integers")),
1303 };
1304 let end = match &args[1] {
1305 Value::Int(n) => *n,
1306 _ => return Err(RuntimeError::new("range() requires integers")),
1307 };
1308 let values: Vec<Value> = (start..end).map(Value::Int).collect();
1309 Ok(Value::Array(Rc::new(RefCell::new(values))))
1310 });
1311
1312 define(interp, "range_inclusive", Some(2), |_, args| {
1313 let start = match &args[0] {
1314 Value::Int(n) => *n,
1315 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1316 };
1317 let end = match &args[1] {
1318 Value::Int(n) => *n,
1319 _ => return Err(RuntimeError::new("range_inclusive() requires integers")),
1320 };
1321 let values: Vec<Value> = (start..=end).map(Value::Int).collect();
1322 Ok(Value::Array(Rc::new(RefCell::new(values))))
1323 });
1324
1325 define(interp, "repeat", Some(2), |_, args| {
1326 let n = match &args[1] {
1327 Value::Int(i) if *i >= 0 => *i as usize,
1328 _ => return Err(RuntimeError::new("repeat() count must be non-negative integer")),
1329 };
1330 let repeated: Vec<Value> = std::iter::repeat(args[0].clone()).take(n).collect();
1331 Ok(Value::Array(Rc::new(RefCell::new(repeated))))
1332 });
1333
1334 define(interp, "map_new", Some(0), |_, _| {
1340 Ok(Value::Map(Rc::new(RefCell::new(HashMap::new()))))
1341 });
1342
1343 define(interp, "map_get", Some(2), |_, args| {
1345 let key = match &args[1] {
1346 Value::String(s) => s.to_string(),
1347 _ => return Err(RuntimeError::new("map_get() key must be string")),
1348 };
1349 match &args[0] {
1350 Value::Map(map) => {
1351 Ok(map.borrow().get(&key).cloned().unwrap_or(Value::Null))
1352 }
1353 _ => Err(RuntimeError::new("map_get() requires map")),
1354 }
1355 });
1356
1357 define(interp, "map_set", Some(3), |_, args| {
1359 let key = match &args[1] {
1360 Value::String(s) => s.to_string(),
1361 _ => return Err(RuntimeError::new("map_set() key must be string")),
1362 };
1363 match &args[0] {
1364 Value::Map(map) => {
1365 map.borrow_mut().insert(key, args[2].clone());
1366 Ok(Value::Null)
1367 }
1368 _ => Err(RuntimeError::new("map_set() requires map")),
1369 }
1370 });
1371
1372 define(interp, "map_has", Some(2), |_, args| {
1374 let key = match &args[1] {
1375 Value::String(s) => s.to_string(),
1376 _ => return Err(RuntimeError::new("map_has() key must be string")),
1377 };
1378 match &args[0] {
1379 Value::Map(map) => {
1380 Ok(Value::Bool(map.borrow().contains_key(&key)))
1381 }
1382 _ => Err(RuntimeError::new("map_has() requires map")),
1383 }
1384 });
1385
1386 define(interp, "map_remove", Some(2), |_, args| {
1388 let key = match &args[1] {
1389 Value::String(s) => s.to_string(),
1390 _ => return Err(RuntimeError::new("map_remove() key must be string")),
1391 };
1392 match &args[0] {
1393 Value::Map(map) => {
1394 Ok(map.borrow_mut().remove(&key).unwrap_or(Value::Null))
1395 }
1396 _ => Err(RuntimeError::new("map_remove() requires map")),
1397 }
1398 });
1399
1400 define(interp, "map_keys", Some(1), |_, args| {
1402 match &args[0] {
1403 Value::Map(map) => {
1404 let keys: Vec<Value> = map.borrow().keys()
1405 .map(|k| Value::String(Rc::new(k.clone())))
1406 .collect();
1407 Ok(Value::Array(Rc::new(RefCell::new(keys))))
1408 }
1409 _ => Err(RuntimeError::new("map_keys() requires map")),
1410 }
1411 });
1412
1413 define(interp, "map_values", Some(1), |_, args| {
1415 match &args[0] {
1416 Value::Map(map) => {
1417 let values: Vec<Value> = map.borrow().values().cloned().collect();
1418 Ok(Value::Array(Rc::new(RefCell::new(values))))
1419 }
1420 _ => Err(RuntimeError::new("map_values() requires map")),
1421 }
1422 });
1423
1424 define(interp, "map_len", Some(1), |_, args| {
1426 match &args[0] {
1427 Value::Map(map) => Ok(Value::Int(map.borrow().len() as i64)),
1428 _ => Err(RuntimeError::new("map_len() requires map")),
1429 }
1430 });
1431
1432 define(interp, "map_clear", Some(1), |_, args| {
1434 match &args[0] {
1435 Value::Map(map) => {
1436 map.borrow_mut().clear();
1437 Ok(Value::Null)
1438 }
1439 _ => Err(RuntimeError::new("map_clear() requires map")),
1440 }
1441 });
1442
1443 define(interp, "set_new", Some(0), |_, _| {
1449 Ok(Value::Set(Rc::new(RefCell::new(std::collections::HashSet::new()))))
1450 });
1451
1452 define(interp, "set_add", Some(2), |_, args| {
1454 let item = match &args[1] {
1455 Value::String(s) => s.to_string(),
1456 _ => return Err(RuntimeError::new("set_add() item must be string")),
1457 };
1458 match &args[0] {
1459 Value::Set(set) => {
1460 set.borrow_mut().insert(item);
1461 Ok(Value::Null)
1462 }
1463 _ => Err(RuntimeError::new("set_add() requires set")),
1464 }
1465 });
1466
1467 define(interp, "set_has", Some(2), |_, args| {
1469 let item = match &args[1] {
1470 Value::String(s) => s.to_string(),
1471 _ => return Err(RuntimeError::new("set_has() item must be string")),
1472 };
1473 match &args[0] {
1474 Value::Set(set) => {
1475 Ok(Value::Bool(set.borrow().contains(&item)))
1476 }
1477 _ => Err(RuntimeError::new("set_has() requires set")),
1478 }
1479 });
1480
1481 define(interp, "set_remove", Some(2), |_, args| {
1483 let item = match &args[1] {
1484 Value::String(s) => s.to_string(),
1485 _ => return Err(RuntimeError::new("set_remove() item must be string")),
1486 };
1487 match &args[0] {
1488 Value::Set(set) => {
1489 Ok(Value::Bool(set.borrow_mut().remove(&item)))
1490 }
1491 _ => Err(RuntimeError::new("set_remove() requires set")),
1492 }
1493 });
1494
1495 define(interp, "set_to_array", Some(1), |_, args| {
1497 match &args[0] {
1498 Value::Set(set) => {
1499 let items: Vec<Value> = set.borrow().iter()
1500 .map(|s| Value::String(Rc::new(s.clone())))
1501 .collect();
1502 Ok(Value::Array(Rc::new(RefCell::new(items))))
1503 }
1504 _ => Err(RuntimeError::new("set_to_array() requires set")),
1505 }
1506 });
1507
1508 define(interp, "set_len", Some(1), |_, args| {
1510 match &args[0] {
1511 Value::Set(set) => Ok(Value::Int(set.borrow().len() as i64)),
1512 _ => Err(RuntimeError::new("set_len() requires set")),
1513 }
1514 });
1515
1516 define(interp, "set_clear", Some(1), |_, args| {
1518 match &args[0] {
1519 Value::Set(set) => {
1520 set.borrow_mut().clear();
1521 Ok(Value::Null)
1522 }
1523 _ => Err(RuntimeError::new("set_clear() requires set")),
1524 }
1525 });
1526}
1527
1528fn values_equal(a: &Value, b: &Value) -> bool {
1529 match (a, b) {
1530 (Value::Null, Value::Null) => true,
1531 (Value::Bool(a), Value::Bool(b)) => a == b,
1532 (Value::Int(a), Value::Int(b)) => a == b,
1533 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
1534 (Value::String(a), Value::String(b)) => a == b,
1535 (Value::Char(a), Value::Char(b)) => a == b,
1536 (Value::Array(a), Value::Array(b)) => {
1537 let a = a.borrow();
1538 let b = b.borrow();
1539 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1540 }
1541 (Value::Tuple(a), Value::Tuple(b)) => {
1542 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
1543 }
1544 _ => false,
1545 }
1546}
1547
1548fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
1549 match (a, b) {
1550 (Value::Int(a), Value::Int(b)) => a.cmp(b),
1551 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal),
1552 (Value::String(a), Value::String(b)) => a.cmp(b),
1553 (Value::Char(a), Value::Char(b)) => a.cmp(b),
1554 _ => std::cmp::Ordering::Equal,
1555 }
1556}
1557
1558fn register_string(interp: &mut Interpreter) {
1563 define(interp, "chars", Some(1), |_, args| {
1564 match &args[0] {
1565 Value::String(s) => {
1566 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
1567 Ok(Value::Array(Rc::new(RefCell::new(chars))))
1568 }
1569 _ => Err(RuntimeError::new("chars() requires string")),
1570 }
1571 });
1572
1573 define(interp, "bytes", Some(1), |_, args| {
1574 match &args[0] {
1575 Value::String(s) => {
1576 let bytes: Vec<Value> = s.bytes().map(|b| Value::Int(b as i64)).collect();
1577 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
1578 }
1579 _ => Err(RuntimeError::new("bytes() requires string")),
1580 }
1581 });
1582
1583 define(interp, "split", Some(2), |_, args| {
1584 match (&args[0], &args[1]) {
1585 (Value::String(s), Value::String(sep)) => {
1586 let parts: Vec<Value> = s.split(sep.as_str())
1587 .map(|p| Value::String(Rc::new(p.to_string())))
1588 .collect();
1589 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1590 }
1591 (Value::String(s), Value::Char(sep)) => {
1592 let parts: Vec<Value> = s.split(*sep)
1593 .map(|p| Value::String(Rc::new(p.to_string())))
1594 .collect();
1595 Ok(Value::Array(Rc::new(RefCell::new(parts))))
1596 }
1597 _ => Err(RuntimeError::new("split() requires string and separator")),
1598 }
1599 });
1600
1601 define(interp, "join", Some(2), |_, args| {
1602 match (&args[0], &args[1]) {
1603 (Value::Array(arr), Value::String(sep)) => {
1604 let parts: Vec<String> = arr.borrow().iter()
1605 .map(|v| format!("{}", v))
1606 .collect();
1607 Ok(Value::String(Rc::new(parts.join(sep.as_str()))))
1608 }
1609 _ => Err(RuntimeError::new("join() requires array and separator string")),
1610 }
1611 });
1612
1613 define(interp, "trim", Some(1), |_, args| {
1614 match &args[0] {
1615 Value::String(s) => Ok(Value::String(Rc::new(s.trim().to_string()))),
1616 _ => Err(RuntimeError::new("trim() requires string")),
1617 }
1618 });
1619
1620 define(interp, "trim_start", Some(1), |_, args| {
1621 match &args[0] {
1622 Value::String(s) => Ok(Value::String(Rc::new(s.trim_start().to_string()))),
1623 _ => Err(RuntimeError::new("trim_start() requires string")),
1624 }
1625 });
1626
1627 define(interp, "trim_end", Some(1), |_, args| {
1628 match &args[0] {
1629 Value::String(s) => Ok(Value::String(Rc::new(s.trim_end().to_string()))),
1630 _ => Err(RuntimeError::new("trim_end() requires string")),
1631 }
1632 });
1633
1634 define(interp, "upper", Some(1), |_, args| {
1635 match &args[0] {
1636 Value::String(s) => Ok(Value::String(Rc::new(s.to_uppercase()))),
1637 Value::Char(c) => Ok(Value::Char(c.to_uppercase().next().unwrap_or(*c))),
1638 _ => Err(RuntimeError::new("upper() requires string or char")),
1639 }
1640 });
1641
1642 define(interp, "lower", Some(1), |_, args| {
1643 match &args[0] {
1644 Value::String(s) => Ok(Value::String(Rc::new(s.to_lowercase()))),
1645 Value::Char(c) => Ok(Value::Char(c.to_lowercase().next().unwrap_or(*c))),
1646 _ => Err(RuntimeError::new("lower() requires string or char")),
1647 }
1648 });
1649
1650 define(interp, "capitalize", Some(1), |_, args| {
1651 match &args[0] {
1652 Value::String(s) => {
1653 let mut chars = s.chars();
1654 let capitalized = match chars.next() {
1655 None => String::new(),
1656 Some(c) => c.to_uppercase().chain(chars).collect(),
1657 };
1658 Ok(Value::String(Rc::new(capitalized)))
1659 }
1660 _ => Err(RuntimeError::new("capitalize() requires string")),
1661 }
1662 });
1663
1664 define(interp, "replace", Some(3), |_, args| {
1665 match (&args[0], &args[1], &args[2]) {
1666 (Value::String(s), Value::String(from), Value::String(to)) => {
1667 Ok(Value::String(Rc::new(s.replace(from.as_str(), to.as_str()))))
1668 }
1669 _ => Err(RuntimeError::new("replace() requires three strings")),
1670 }
1671 });
1672
1673 define(interp, "starts_with", Some(2), |_, args| {
1674 match (&args[0], &args[1]) {
1675 (Value::String(s), Value::String(prefix)) => {
1676 Ok(Value::Bool(s.starts_with(prefix.as_str())))
1677 }
1678 _ => Err(RuntimeError::new("starts_with() requires two strings")),
1679 }
1680 });
1681
1682 define(interp, "ends_with", Some(2), |_, args| {
1683 match (&args[0], &args[1]) {
1684 (Value::String(s), Value::String(suffix)) => {
1685 Ok(Value::Bool(s.ends_with(suffix.as_str())))
1686 }
1687 _ => Err(RuntimeError::new("ends_with() requires two strings")),
1688 }
1689 });
1690
1691 define(interp, "repeat_str", Some(2), |_, args| {
1692 match (&args[0], &args[1]) {
1693 (Value::String(s), Value::Int(n)) if *n >= 0 => {
1694 Ok(Value::String(Rc::new(s.repeat(*n as usize))))
1695 }
1696 _ => Err(RuntimeError::new("repeat_str() requires string and non-negative integer")),
1697 }
1698 });
1699
1700 define(interp, "pad_left", Some(3), |_, args| {
1701 match (&args[0], &args[1], &args[2]) {
1702 (Value::String(s), Value::Int(width), Value::Char(c)) => {
1703 let width = *width as usize;
1704 if s.len() >= width {
1705 Ok(Value::String(s.clone()))
1706 } else {
1707 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
1708 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
1709 }
1710 }
1711 _ => Err(RuntimeError::new("pad_left() requires string, width, and char")),
1712 }
1713 });
1714
1715 define(interp, "pad_right", Some(3), |_, args| {
1716 match (&args[0], &args[1], &args[2]) {
1717 (Value::String(s), Value::Int(width), Value::Char(c)) => {
1718 let width = *width as usize;
1719 if s.len() >= width {
1720 Ok(Value::String(s.clone()))
1721 } else {
1722 let padding: String = std::iter::repeat(*c).take(width - s.len()).collect();
1723 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
1724 }
1725 }
1726 _ => Err(RuntimeError::new("pad_right() requires string, width, and char")),
1727 }
1728 });
1729
1730 define(interp, "lines", Some(1), |_, args| {
1731 match &args[0] {
1732 Value::String(s) => {
1733 let lines: Vec<Value> = s.lines()
1734 .map(|l| Value::String(Rc::new(l.to_string())))
1735 .collect();
1736 Ok(Value::Array(Rc::new(RefCell::new(lines))))
1737 }
1738 _ => Err(RuntimeError::new("lines() requires string")),
1739 }
1740 });
1741
1742 define(interp, "words", Some(1), |_, args| {
1743 match &args[0] {
1744 Value::String(s) => {
1745 let words: Vec<Value> = s.split_whitespace()
1746 .map(|w| Value::String(Rc::new(w.to_string())))
1747 .collect();
1748 Ok(Value::Array(Rc::new(RefCell::new(words))))
1749 }
1750 _ => Err(RuntimeError::new("words() requires string")),
1751 }
1752 });
1753
1754 define(interp, "is_alpha", Some(1), |_, args| {
1755 match &args[0] {
1756 Value::String(s) => Ok(Value::Bool(!s.is_empty() && s.chars().all(|c| c.is_alphabetic()))),
1757 Value::Char(c) => Ok(Value::Bool(c.is_alphabetic())),
1758 _ => Err(RuntimeError::new("is_alpha() requires string or char")),
1759 }
1760 });
1761
1762 define(interp, "is_digit", Some(1), |_, args| {
1763 match &args[0] {
1764 Value::String(s) => Ok(Value::Bool(!s.is_empty() && s.chars().all(|c| c.is_ascii_digit()))),
1765 Value::Char(c) => Ok(Value::Bool(c.is_ascii_digit())),
1766 _ => Err(RuntimeError::new("is_digit() requires string or char")),
1767 }
1768 });
1769
1770 define(interp, "is_alnum", Some(1), |_, args| {
1771 match &args[0] {
1772 Value::String(s) => Ok(Value::Bool(!s.is_empty() && s.chars().all(|c| c.is_alphanumeric()))),
1773 Value::Char(c) => Ok(Value::Bool(c.is_alphanumeric())),
1774 _ => Err(RuntimeError::new("is_alnum() requires string or char")),
1775 }
1776 });
1777
1778 define(interp, "is_space", Some(1), |_, args| {
1779 match &args[0] {
1780 Value::String(s) => Ok(Value::Bool(!s.is_empty() && s.chars().all(|c| c.is_whitespace()))),
1781 Value::Char(c) => Ok(Value::Bool(c.is_whitespace())),
1782 _ => Err(RuntimeError::new("is_space() requires string or char")),
1783 }
1784 });
1785
1786 define(interp, "find", Some(2), |_, args| {
1792 match (&args[0], &args[1]) {
1793 (Value::String(s), Value::String(sub)) => {
1794 match s.find(sub.as_str()) {
1795 Some(byte_idx) => {
1796 let char_idx = s[..byte_idx].chars().count() as i64;
1798 Ok(Value::Int(char_idx))
1799 }
1800 None => Ok(Value::Int(-1)),
1801 }
1802 }
1803 (Value::String(s), Value::Char(c)) => {
1804 match s.find(*c) {
1805 Some(byte_idx) => {
1806 let char_idx = s[..byte_idx].chars().count() as i64;
1807 Ok(Value::Int(char_idx))
1808 }
1809 None => Ok(Value::Int(-1)),
1810 }
1811 }
1812 _ => Err(RuntimeError::new("find() requires string and substring/char")),
1813 }
1814 });
1815
1816 define(interp, "index_of", Some(2), |_, args| {
1818 match (&args[0], &args[1]) {
1819 (Value::String(s), Value::String(sub)) => {
1820 match s.find(sub.as_str()) {
1821 Some(byte_idx) => {
1822 let char_idx = s[..byte_idx].chars().count() as i64;
1823 Ok(Value::Int(char_idx))
1824 }
1825 None => Ok(Value::Int(-1)),
1826 }
1827 }
1828 (Value::String(s), Value::Char(c)) => {
1829 match s.find(*c) {
1830 Some(byte_idx) => {
1831 let char_idx = s[..byte_idx].chars().count() as i64;
1832 Ok(Value::Int(char_idx))
1833 }
1834 None => Ok(Value::Int(-1)),
1835 }
1836 }
1837 (Value::Array(arr), search) => {
1838 for (i, v) in arr.borrow().iter().enumerate() {
1840 if values_equal_simple(v, search) {
1841 return Ok(Value::Int(i as i64));
1842 }
1843 }
1844 Ok(Value::Int(-1))
1845 }
1846 _ => Err(RuntimeError::new("index_of() requires array/string and element/substring")),
1847 }
1848 });
1849
1850 define(interp, "last_index_of", Some(2), |_, args| {
1852 match (&args[0], &args[1]) {
1853 (Value::String(s), Value::String(sub)) => {
1854 match s.rfind(sub.as_str()) {
1855 Some(byte_idx) => {
1856 let char_idx = s[..byte_idx].chars().count() as i64;
1857 Ok(Value::Int(char_idx))
1858 }
1859 None => Ok(Value::Int(-1)),
1860 }
1861 }
1862 (Value::String(s), Value::Char(c)) => {
1863 match s.rfind(*c) {
1864 Some(byte_idx) => {
1865 let char_idx = s[..byte_idx].chars().count() as i64;
1866 Ok(Value::Int(char_idx))
1867 }
1868 None => Ok(Value::Int(-1)),
1869 }
1870 }
1871 _ => Err(RuntimeError::new("last_index_of() requires string and substring/char")),
1872 }
1873 });
1874
1875 define(interp, "substring", Some(3), |_, args| {
1877 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("substring: first argument must be a string")) };
1878 let start = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("substring: start must be a non-negative integer")) };
1879 let end = match &args[2] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("substring: end must be a non-negative integer")) };
1880 let chars: Vec<char> = s.chars().collect();
1881 let len = chars.len();
1882 let actual_start = start.min(len);
1883 let actual_end = end.min(len);
1884 if actual_start >= actual_end {
1885 return Ok(Value::String(Rc::new(String::new())));
1886 }
1887 let result: String = chars[actual_start..actual_end].iter().collect();
1888 Ok(Value::String(Rc::new(result)))
1889 });
1890
1891 define(interp, "count", Some(2), |_, args| {
1893 match (&args[0], &args[1]) {
1894 (Value::String(s), Value::String(sub)) => {
1895 if sub.is_empty() {
1896 return Err(RuntimeError::new("count: cannot count empty string"));
1897 }
1898 let count = s.matches(sub.as_str()).count() as i64;
1899 Ok(Value::Int(count))
1900 }
1901 (Value::String(s), Value::Char(c)) => {
1902 let count = s.chars().filter(|&ch| ch == *c).count() as i64;
1903 Ok(Value::Int(count))
1904 }
1905 _ => Err(RuntimeError::new("count() requires string and substring/char")),
1906 }
1907 });
1908
1909 define(interp, "char_at", Some(2), |_, args| {
1911 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("char_at: first argument must be a string")) };
1912 let idx = match &args[1] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("char_at: second argument must be an integer")) };
1913 let chars: Vec<char> = s.chars().collect();
1914 let actual_idx = if idx < 0 {
1915 (chars.len() as i64 + idx) as usize
1916 } else {
1917 idx as usize
1918 };
1919 match chars.get(actual_idx) {
1920 Some(c) => Ok(Value::Char(*c)),
1921 None => Ok(Value::Null),
1922 }
1923 });
1924
1925 define(interp, "char_code_at", Some(2), |_, args| {
1927 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("char_code_at: first argument must be a string")) };
1928 let idx = match &args[1] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("char_code_at: second argument must be an integer")) };
1929 let chars: Vec<char> = s.chars().collect();
1930 let actual_idx = if idx < 0 {
1931 (chars.len() as i64 + idx) as usize
1932 } else {
1933 idx as usize
1934 };
1935 match chars.get(actual_idx) {
1936 Some(c) => Ok(Value::Int(*c as i64)),
1937 None => Ok(Value::Null),
1938 }
1939 });
1940
1941 define(interp, "from_char_code", Some(1), |_, args| {
1943 let code = match &args[0] { Value::Int(n) => *n as u32, _ => return Err(RuntimeError::new("from_char_code: argument must be an integer")) };
1944 match char::from_u32(code) {
1945 Some(c) => Ok(Value::String(Rc::new(c.to_string()))),
1946 None => Err(RuntimeError::new("from_char_code: invalid Unicode code point")),
1947 }
1948 });
1949
1950 define(interp, "insert", Some(3), |_, args| {
1952 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("insert: first argument must be a string")) };
1953 let idx = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("insert: index must be a non-negative integer")) };
1954 let insertion = match &args[2] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("insert: third argument must be a string")) };
1955 let chars: Vec<char> = s.chars().collect();
1956 let actual_idx = idx.min(chars.len());
1957 let mut result: String = chars[..actual_idx].iter().collect();
1958 result.push_str(&insertion);
1959 result.extend(chars[actual_idx..].iter());
1960 Ok(Value::String(Rc::new(result)))
1961 });
1962
1963 define(interp, "remove", Some(3), |_, args| {
1965 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("remove: first argument must be a string")) };
1966 let start = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("remove: start must be a non-negative integer")) };
1967 let len = match &args[2] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("remove: length must be a non-negative integer")) };
1968 let chars: Vec<char> = s.chars().collect();
1969 let str_len = chars.len();
1970 let actual_start = start.min(str_len);
1971 let actual_end = (start + len).min(str_len);
1972 let mut result: String = chars[..actual_start].iter().collect();
1973 result.extend(chars[actual_end..].iter());
1974 Ok(Value::String(Rc::new(result)))
1975 });
1976
1977 define(interp, "compare", Some(2), |_, args| {
1979 match (&args[0], &args[1]) {
1980 (Value::String(a), Value::String(b)) => {
1981 let result = match a.cmp(b) {
1982 std::cmp::Ordering::Less => -1,
1983 std::cmp::Ordering::Equal => 0,
1984 std::cmp::Ordering::Greater => 1,
1985 };
1986 Ok(Value::Int(result))
1987 }
1988 _ => Err(RuntimeError::new("compare() requires two strings")),
1989 }
1990 });
1991
1992 define(interp, "compare_ignore_case", Some(2), |_, args| {
1994 match (&args[0], &args[1]) {
1995 (Value::String(a), Value::String(b)) => {
1996 let result = match a.to_lowercase().cmp(&b.to_lowercase()) {
1997 std::cmp::Ordering::Less => -1,
1998 std::cmp::Ordering::Equal => 0,
1999 std::cmp::Ordering::Greater => 1,
2000 };
2001 Ok(Value::Int(result))
2002 }
2003 _ => Err(RuntimeError::new("compare_ignore_case() requires two strings")),
2004 }
2005 });
2006
2007 define(interp, "char_count", Some(1), |_, args| {
2009 match &args[0] {
2010 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
2011 _ => Err(RuntimeError::new("char_count() requires string")),
2012 }
2013 });
2014
2015 define(interp, "byte_count", Some(1), |_, args| {
2017 match &args[0] {
2018 Value::String(s) => Ok(Value::Int(s.len() as i64)),
2019 _ => Err(RuntimeError::new("byte_count() requires string")),
2020 }
2021 });
2022
2023 define(interp, "is_empty", Some(1), |_, args| {
2025 match &args[0] {
2026 Value::String(s) => Ok(Value::Bool(s.is_empty())),
2027 Value::Array(arr) => Ok(Value::Bool(arr.borrow().is_empty())),
2028 _ => Err(RuntimeError::new("is_empty() requires string or array")),
2029 }
2030 });
2031
2032 define(interp, "is_blank", Some(1), |_, args| {
2034 match &args[0] {
2035 Value::String(s) => Ok(Value::Bool(s.trim().is_empty())),
2036 _ => Err(RuntimeError::new("is_blank() requires string")),
2037 }
2038 });
2039
2040 define(interp, "nfc", Some(1), |_, args| {
2046 match &args[0] {
2047 Value::String(s) => Ok(Value::String(Rc::new(s.nfc().collect()))),
2048 _ => Err(RuntimeError::new("nfc() requires string")),
2049 }
2050 });
2051
2052 define(interp, "nfd", Some(1), |_, args| {
2054 match &args[0] {
2055 Value::String(s) => Ok(Value::String(Rc::new(s.nfd().collect()))),
2056 _ => Err(RuntimeError::new("nfd() requires string")),
2057 }
2058 });
2059
2060 define(interp, "nfkc", Some(1), |_, args| {
2062 match &args[0] {
2063 Value::String(s) => Ok(Value::String(Rc::new(s.nfkc().collect()))),
2064 _ => Err(RuntimeError::new("nfkc() requires string")),
2065 }
2066 });
2067
2068 define(interp, "nfkd", Some(1), |_, args| {
2070 match &args[0] {
2071 Value::String(s) => Ok(Value::String(Rc::new(s.nfkd().collect()))),
2072 _ => Err(RuntimeError::new("nfkd() requires string")),
2073 }
2074 });
2075
2076 define(interp, "is_nfc", Some(1), |_, args| {
2078 match &args[0] {
2079 Value::String(s) => {
2080 let normalized: String = s.nfc().collect();
2081 Ok(Value::Bool(*s.as_ref() == normalized))
2082 }
2083 _ => Err(RuntimeError::new("is_nfc() requires string")),
2084 }
2085 });
2086
2087 define(interp, "is_nfd", Some(1), |_, args| {
2089 match &args[0] {
2090 Value::String(s) => {
2091 let normalized: String = s.nfd().collect();
2092 Ok(Value::Bool(*s.as_ref() == normalized))
2093 }
2094 _ => Err(RuntimeError::new("is_nfd() requires string")),
2095 }
2096 });
2097
2098 define(interp, "graphemes", Some(1), |_, args| {
2104 match &args[0] {
2105 Value::String(s) => {
2106 let graphemes: Vec<Value> = s.graphemes(true)
2107 .map(|g| Value::String(Rc::new(g.to_string())))
2108 .collect();
2109 Ok(Value::Array(Rc::new(RefCell::new(graphemes))))
2110 }
2111 _ => Err(RuntimeError::new("graphemes() requires string")),
2112 }
2113 });
2114
2115 define(interp, "grapheme_count", Some(1), |_, args| {
2117 match &args[0] {
2118 Value::String(s) => Ok(Value::Int(s.graphemes(true).count() as i64)),
2119 _ => Err(RuntimeError::new("grapheme_count() requires string")),
2120 }
2121 });
2122
2123 define(interp, "grapheme_at", Some(2), |_, args| {
2125 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("grapheme_at: first argument must be a string")) };
2126 let idx = match &args[1] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("grapheme_at: second argument must be an integer")) };
2127 let graphemes: Vec<&str> = s.graphemes(true).collect();
2128 let actual_idx = if idx < 0 {
2129 (graphemes.len() as i64 + idx) as usize
2130 } else {
2131 idx as usize
2132 };
2133 match graphemes.get(actual_idx) {
2134 Some(g) => Ok(Value::String(Rc::new(g.to_string()))),
2135 None => Ok(Value::Null),
2136 }
2137 });
2138
2139 define(interp, "grapheme_slice", Some(3), |_, args| {
2141 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("grapheme_slice: first argument must be a string")) };
2142 let start = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("grapheme_slice: start must be a non-negative integer")) };
2143 let end = match &args[2] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("grapheme_slice: end must be a non-negative integer")) };
2144 let graphemes: Vec<&str> = s.graphemes(true).collect();
2145 let len = graphemes.len();
2146 let actual_start = start.min(len);
2147 let actual_end = end.min(len);
2148 if actual_start >= actual_end {
2149 return Ok(Value::String(Rc::new(String::new())));
2150 }
2151 let result: String = graphemes[actual_start..actual_end].join("");
2152 Ok(Value::String(Rc::new(result)))
2153 });
2154
2155 define(interp, "grapheme_reverse", Some(1), |_, args| {
2157 match &args[0] {
2158 Value::String(s) => {
2159 let reversed: String = s.graphemes(true).rev().collect();
2160 Ok(Value::String(Rc::new(reversed)))
2161 }
2162 _ => Err(RuntimeError::new("grapheme_reverse() requires string")),
2163 }
2164 });
2165
2166 define(interp, "word_boundaries", Some(1), |_, args| {
2168 match &args[0] {
2169 Value::String(s) => {
2170 let words: Vec<Value> = s.unicode_words()
2171 .map(|w| Value::String(Rc::new(w.to_string())))
2172 .collect();
2173 Ok(Value::Array(Rc::new(RefCell::new(words))))
2174 }
2175 _ => Err(RuntimeError::new("word_boundaries() requires string")),
2176 }
2177 });
2178
2179 define(interp, "string_builder", Some(0), |_, _| {
2186 Ok(Value::String(Rc::new(String::new())))
2187 });
2188
2189 define(interp, "concat_all", Some(1), |_, args| {
2191 match &args[0] {
2192 Value::Array(arr) => {
2193 let parts: Vec<String> = arr.borrow().iter()
2194 .map(|v| match v {
2195 Value::String(s) => (**s).clone(),
2196 other => format!("{}", other),
2197 })
2198 .collect();
2199 Ok(Value::String(Rc::new(parts.join(""))))
2200 }
2201 _ => Err(RuntimeError::new("concat_all() requires array")),
2202 }
2203 });
2204
2205 define(interp, "repeat_join", Some(3), |_, args| {
2207 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("repeat_join: first argument must be a string")) };
2208 let n = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("repeat_join: count must be a non-negative integer")) };
2209 let sep = match &args[2] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("repeat_join: separator must be a string")) };
2210 if n == 0 {
2211 return Ok(Value::String(Rc::new(String::new())));
2212 }
2213 let parts: Vec<&str> = std::iter::repeat(s.as_str()).take(n).collect();
2214 Ok(Value::String(Rc::new(parts.join(&sep))))
2215 });
2216}
2217
2218fn register_evidence(interp: &mut Interpreter) {
2223 define(interp, "known", Some(1), |_, args| {
2225 Ok(Value::Evidential {
2226 value: Box::new(args[0].clone()),
2227 evidence: Evidence::Known,
2228 })
2229 });
2230
2231 define(interp, "uncertain", Some(1), |_, args| {
2232 Ok(Value::Evidential {
2233 value: Box::new(args[0].clone()),
2234 evidence: Evidence::Uncertain,
2235 })
2236 });
2237
2238 define(interp, "reported", Some(1), |_, args| {
2239 Ok(Value::Evidential {
2240 value: Box::new(args[0].clone()),
2241 evidence: Evidence::Reported,
2242 })
2243 });
2244
2245 define(interp, "paradox", Some(1), |_, args| {
2246 Ok(Value::Evidential {
2247 value: Box::new(args[0].clone()),
2248 evidence: Evidence::Paradox,
2249 })
2250 });
2251
2252 define(interp, "evidence_of", Some(1), |_, args| {
2254 match &args[0] {
2255 Value::Evidential { evidence, .. } => {
2256 let level = match evidence {
2257 Evidence::Known => "known",
2258 Evidence::Uncertain => "uncertain",
2259 Evidence::Reported => "reported",
2260 Evidence::Paradox => "paradox",
2261 };
2262 Ok(Value::String(Rc::new(level.to_string())))
2263 }
2264 _ => Ok(Value::String(Rc::new("known".to_string()))), }
2266 });
2267
2268 define(interp, "is_known", Some(1), |_, args| {
2269 match &args[0] {
2270 Value::Evidential { evidence: Evidence::Known, .. } => Ok(Value::Bool(true)),
2271 Value::Evidential { .. } => Ok(Value::Bool(false)),
2272 _ => Ok(Value::Bool(true)), }
2274 });
2275
2276 define(interp, "is_uncertain", Some(1), |_, args| {
2277 match &args[0] {
2278 Value::Evidential { evidence: Evidence::Uncertain, .. } => Ok(Value::Bool(true)),
2279 _ => Ok(Value::Bool(false)),
2280 }
2281 });
2282
2283 define(interp, "is_reported", Some(1), |_, args| {
2284 match &args[0] {
2285 Value::Evidential { evidence: Evidence::Reported, .. } => Ok(Value::Bool(true)),
2286 _ => Ok(Value::Bool(false)),
2287 }
2288 });
2289
2290 define(interp, "is_paradox", Some(1), |_, args| {
2291 match &args[0] {
2292 Value::Evidential { evidence: Evidence::Paradox, .. } => Ok(Value::Bool(true)),
2293 _ => Ok(Value::Bool(false)),
2294 }
2295 });
2296
2297 define(interp, "strip_evidence", Some(1), |_, args| {
2299 match &args[0] {
2300 Value::Evidential { value, .. } => Ok(*value.clone()),
2301 other => Ok(other.clone()),
2302 }
2303 });
2304
2305 define(interp, "trust", Some(1), |_, args| {
2307 match &args[0] {
2309 Value::Evidential { value, .. } => {
2310 Ok(Value::Evidential {
2311 value: value.clone(),
2312 evidence: Evidence::Known,
2313 })
2314 }
2315 other => Ok(other.clone()),
2316 }
2317 });
2318
2319 define(interp, "verify", Some(2), |_, args| {
2320 let pred_result = match &args[1] {
2322 Value::Bool(b) => *b,
2323 _ => return Err(RuntimeError::new("verify() predicate must be bool")),
2324 };
2325
2326 if pred_result {
2327 match &args[0] {
2328 Value::Evidential { value, .. } => {
2329 Ok(Value::Evidential {
2330 value: value.clone(),
2331 evidence: Evidence::Known,
2332 })
2333 }
2334 other => Ok(other.clone()),
2335 }
2336 } else {
2337 Ok(args[0].clone()) }
2339 });
2340
2341 define(interp, "combine_evidence", Some(2), |_, args| {
2343 let ev1 = match &args[0] {
2344 Value::Evidential { evidence, .. } => *evidence,
2345 _ => Evidence::Known,
2346 };
2347 let ev2 = match &args[1] {
2348 Value::Evidential { evidence, .. } => *evidence,
2349 _ => Evidence::Known,
2350 };
2351
2352 let combined = match (ev1, ev2) {
2354 (Evidence::Paradox, _) | (_, Evidence::Paradox) => Evidence::Paradox,
2355 (Evidence::Reported, _) | (_, Evidence::Reported) => Evidence::Reported,
2356 (Evidence::Uncertain, _) | (_, Evidence::Uncertain) => Evidence::Uncertain,
2357 _ => Evidence::Known,
2358 };
2359
2360 Ok(Value::String(Rc::new(match combined {
2361 Evidence::Known => "known",
2362 Evidence::Uncertain => "uncertain",
2363 Evidence::Reported => "reported",
2364 Evidence::Paradox => "paradox",
2365 }.to_string())))
2366 });
2367}
2368
2369fn register_affect(interp: &mut Interpreter) {
2374 use crate::interpreter::{RuntimeAffect, RuntimeSentiment, RuntimeIntensity,
2375 RuntimeFormality, RuntimeEmotion, RuntimeConfidence};
2376
2377 define(interp, "positive", Some(1), |_, args| {
2381 Ok(Value::Affective {
2382 value: Box::new(args[0].clone()),
2383 affect: RuntimeAffect {
2384 sentiment: Some(RuntimeSentiment::Positive),
2385 sarcasm: false,
2386 intensity: None,
2387 formality: None,
2388 emotion: None,
2389 confidence: None,
2390 },
2391 })
2392 });
2393
2394 define(interp, "negative", Some(1), |_, args| {
2395 Ok(Value::Affective {
2396 value: Box::new(args[0].clone()),
2397 affect: RuntimeAffect {
2398 sentiment: Some(RuntimeSentiment::Negative),
2399 sarcasm: false,
2400 intensity: None,
2401 formality: None,
2402 emotion: None,
2403 confidence: None,
2404 },
2405 })
2406 });
2407
2408 define(interp, "neutral", Some(1), |_, args| {
2409 Ok(Value::Affective {
2410 value: Box::new(args[0].clone()),
2411 affect: RuntimeAffect {
2412 sentiment: Some(RuntimeSentiment::Neutral),
2413 sarcasm: false,
2414 intensity: None,
2415 formality: None,
2416 emotion: None,
2417 confidence: None,
2418 },
2419 })
2420 });
2421
2422 define(interp, "sarcastic", Some(1), |_, args| {
2424 Ok(Value::Affective {
2425 value: Box::new(args[0].clone()),
2426 affect: RuntimeAffect {
2427 sentiment: None,
2428 sarcasm: true,
2429 intensity: None,
2430 formality: None,
2431 emotion: None,
2432 confidence: None,
2433 },
2434 })
2435 });
2436
2437 define(interp, "intensify", Some(1), |_, args| {
2439 Ok(Value::Affective {
2440 value: Box::new(args[0].clone()),
2441 affect: RuntimeAffect {
2442 sentiment: None,
2443 sarcasm: false,
2444 intensity: Some(RuntimeIntensity::Up),
2445 formality: None,
2446 emotion: None,
2447 confidence: None,
2448 },
2449 })
2450 });
2451
2452 define(interp, "dampen", Some(1), |_, args| {
2453 Ok(Value::Affective {
2454 value: Box::new(args[0].clone()),
2455 affect: RuntimeAffect {
2456 sentiment: None,
2457 sarcasm: false,
2458 intensity: Some(RuntimeIntensity::Down),
2459 formality: None,
2460 emotion: None,
2461 confidence: None,
2462 },
2463 })
2464 });
2465
2466 define(interp, "maximize", Some(1), |_, args| {
2467 Ok(Value::Affective {
2468 value: Box::new(args[0].clone()),
2469 affect: RuntimeAffect {
2470 sentiment: None,
2471 sarcasm: false,
2472 intensity: Some(RuntimeIntensity::Max),
2473 formality: None,
2474 emotion: None,
2475 confidence: None,
2476 },
2477 })
2478 });
2479
2480 define(interp, "formal", Some(1), |_, args| {
2482 Ok(Value::Affective {
2483 value: Box::new(args[0].clone()),
2484 affect: RuntimeAffect {
2485 sentiment: None,
2486 sarcasm: false,
2487 intensity: None,
2488 formality: Some(RuntimeFormality::Formal),
2489 emotion: None,
2490 confidence: None,
2491 },
2492 })
2493 });
2494
2495 define(interp, "informal", Some(1), |_, args| {
2496 Ok(Value::Affective {
2497 value: Box::new(args[0].clone()),
2498 affect: RuntimeAffect {
2499 sentiment: None,
2500 sarcasm: false,
2501 intensity: None,
2502 formality: Some(RuntimeFormality::Informal),
2503 emotion: None,
2504 confidence: None,
2505 },
2506 })
2507 });
2508
2509 define(interp, "joyful", Some(1), |_, args| {
2511 Ok(Value::Affective {
2512 value: Box::new(args[0].clone()),
2513 affect: RuntimeAffect {
2514 sentiment: None,
2515 sarcasm: false,
2516 intensity: None,
2517 formality: None,
2518 emotion: Some(RuntimeEmotion::Joy),
2519 confidence: None,
2520 },
2521 })
2522 });
2523
2524 define(interp, "sad", Some(1), |_, args| {
2525 Ok(Value::Affective {
2526 value: Box::new(args[0].clone()),
2527 affect: RuntimeAffect {
2528 sentiment: None,
2529 sarcasm: false,
2530 intensity: None,
2531 formality: None,
2532 emotion: Some(RuntimeEmotion::Sadness),
2533 confidence: None,
2534 },
2535 })
2536 });
2537
2538 define(interp, "angry", Some(1), |_, args| {
2539 Ok(Value::Affective {
2540 value: Box::new(args[0].clone()),
2541 affect: RuntimeAffect {
2542 sentiment: None,
2543 sarcasm: false,
2544 intensity: None,
2545 formality: None,
2546 emotion: Some(RuntimeEmotion::Anger),
2547 confidence: None,
2548 },
2549 })
2550 });
2551
2552 define(interp, "fearful", Some(1), |_, args| {
2553 Ok(Value::Affective {
2554 value: Box::new(args[0].clone()),
2555 affect: RuntimeAffect {
2556 sentiment: None,
2557 sarcasm: false,
2558 intensity: None,
2559 formality: None,
2560 emotion: Some(RuntimeEmotion::Fear),
2561 confidence: None,
2562 },
2563 })
2564 });
2565
2566 define(interp, "surprised", Some(1), |_, args| {
2567 Ok(Value::Affective {
2568 value: Box::new(args[0].clone()),
2569 affect: RuntimeAffect {
2570 sentiment: None,
2571 sarcasm: false,
2572 intensity: None,
2573 formality: None,
2574 emotion: Some(RuntimeEmotion::Surprise),
2575 confidence: None,
2576 },
2577 })
2578 });
2579
2580 define(interp, "loving", Some(1), |_, args| {
2581 Ok(Value::Affective {
2582 value: Box::new(args[0].clone()),
2583 affect: RuntimeAffect {
2584 sentiment: None,
2585 sarcasm: false,
2586 intensity: None,
2587 formality: None,
2588 emotion: Some(RuntimeEmotion::Love),
2589 confidence: None,
2590 },
2591 })
2592 });
2593
2594 define(interp, "high_confidence", Some(1), |_, args| {
2596 Ok(Value::Affective {
2597 value: Box::new(args[0].clone()),
2598 affect: RuntimeAffect {
2599 sentiment: None,
2600 sarcasm: false,
2601 intensity: None,
2602 formality: None,
2603 emotion: None,
2604 confidence: Some(RuntimeConfidence::High),
2605 },
2606 })
2607 });
2608
2609 define(interp, "medium_confidence", Some(1), |_, args| {
2610 Ok(Value::Affective {
2611 value: Box::new(args[0].clone()),
2612 affect: RuntimeAffect {
2613 sentiment: None,
2614 sarcasm: false,
2615 intensity: None,
2616 formality: None,
2617 emotion: None,
2618 confidence: Some(RuntimeConfidence::Medium),
2619 },
2620 })
2621 });
2622
2623 define(interp, "low_confidence", Some(1), |_, args| {
2624 Ok(Value::Affective {
2625 value: Box::new(args[0].clone()),
2626 affect: RuntimeAffect {
2627 sentiment: None,
2628 sarcasm: false,
2629 intensity: None,
2630 formality: None,
2631 emotion: None,
2632 confidence: Some(RuntimeConfidence::Low),
2633 },
2634 })
2635 });
2636
2637 define(interp, "affect_of", Some(1), |_, args| {
2640 match &args[0] {
2641 Value::Affective { affect, .. } => {
2642 let mut parts = Vec::new();
2643 if let Some(s) = &affect.sentiment {
2644 parts.push(match s {
2645 RuntimeSentiment::Positive => "positive",
2646 RuntimeSentiment::Negative => "negative",
2647 RuntimeSentiment::Neutral => "neutral",
2648 });
2649 }
2650 if affect.sarcasm {
2651 parts.push("sarcastic");
2652 }
2653 if let Some(i) = &affect.intensity {
2654 parts.push(match i {
2655 RuntimeIntensity::Up => "intensified",
2656 RuntimeIntensity::Down => "dampened",
2657 RuntimeIntensity::Max => "maximized",
2658 });
2659 }
2660 if let Some(f) = &affect.formality {
2661 parts.push(match f {
2662 RuntimeFormality::Formal => "formal",
2663 RuntimeFormality::Informal => "informal",
2664 });
2665 }
2666 if let Some(e) = &affect.emotion {
2667 parts.push(match e {
2668 RuntimeEmotion::Joy => "joyful",
2669 RuntimeEmotion::Sadness => "sad",
2670 RuntimeEmotion::Anger => "angry",
2671 RuntimeEmotion::Fear => "fearful",
2672 RuntimeEmotion::Surprise => "surprised",
2673 RuntimeEmotion::Love => "loving",
2674 });
2675 }
2676 if let Some(c) = &affect.confidence {
2677 parts.push(match c {
2678 RuntimeConfidence::High => "high_confidence",
2679 RuntimeConfidence::Medium => "medium_confidence",
2680 RuntimeConfidence::Low => "low_confidence",
2681 });
2682 }
2683 Ok(Value::String(Rc::new(parts.join(", "))))
2684 }
2685 _ => Ok(Value::String(Rc::new("none".to_string()))),
2686 }
2687 });
2688
2689 define(interp, "is_sarcastic", Some(1), |_, args| {
2690 match &args[0] {
2691 Value::Affective { affect, .. } => Ok(Value::Bool(affect.sarcasm)),
2692 _ => Ok(Value::Bool(false)),
2693 }
2694 });
2695
2696 define(interp, "is_positive", Some(1), |_, args| {
2697 match &args[0] {
2698 Value::Affective { affect, .. } => {
2699 Ok(Value::Bool(matches!(affect.sentiment, Some(RuntimeSentiment::Positive))))
2700 }
2701 _ => Ok(Value::Bool(false)),
2702 }
2703 });
2704
2705 define(interp, "is_negative", Some(1), |_, args| {
2706 match &args[0] {
2707 Value::Affective { affect, .. } => {
2708 Ok(Value::Bool(matches!(affect.sentiment, Some(RuntimeSentiment::Negative))))
2709 }
2710 _ => Ok(Value::Bool(false)),
2711 }
2712 });
2713
2714 define(interp, "is_formal", Some(1), |_, args| {
2715 match &args[0] {
2716 Value::Affective { affect, .. } => {
2717 Ok(Value::Bool(matches!(affect.formality, Some(RuntimeFormality::Formal))))
2718 }
2719 _ => Ok(Value::Bool(false)),
2720 }
2721 });
2722
2723 define(interp, "is_informal", Some(1), |_, args| {
2724 match &args[0] {
2725 Value::Affective { affect, .. } => {
2726 Ok(Value::Bool(matches!(affect.formality, Some(RuntimeFormality::Informal))))
2727 }
2728 _ => Ok(Value::Bool(false)),
2729 }
2730 });
2731
2732 define(interp, "emotion_of", Some(1), |_, args| {
2733 match &args[0] {
2734 Value::Affective { affect, .. } => {
2735 let emotion_str = match &affect.emotion {
2736 Some(RuntimeEmotion::Joy) => "joy",
2737 Some(RuntimeEmotion::Sadness) => "sadness",
2738 Some(RuntimeEmotion::Anger) => "anger",
2739 Some(RuntimeEmotion::Fear) => "fear",
2740 Some(RuntimeEmotion::Surprise) => "surprise",
2741 Some(RuntimeEmotion::Love) => "love",
2742 None => "none",
2743 };
2744 Ok(Value::String(Rc::new(emotion_str.to_string())))
2745 }
2746 _ => Ok(Value::String(Rc::new("none".to_string()))),
2747 }
2748 });
2749
2750 define(interp, "confidence_of", Some(1), |_, args| {
2751 match &args[0] {
2752 Value::Affective { affect, .. } => {
2753 let conf_str = match &affect.confidence {
2754 Some(RuntimeConfidence::High) => "high",
2755 Some(RuntimeConfidence::Medium) => "medium",
2756 Some(RuntimeConfidence::Low) => "low",
2757 None => "none",
2758 };
2759 Ok(Value::String(Rc::new(conf_str.to_string())))
2760 }
2761 _ => Ok(Value::String(Rc::new("none".to_string()))),
2762 }
2763 });
2764
2765 define(interp, "strip_affect", Some(1), |_, args| {
2767 match &args[0] {
2768 Value::Affective { value, .. } => Ok(*value.clone()),
2769 other => Ok(other.clone()),
2770 }
2771 });
2772
2773 define(interp, "with_affect", None, |_, args| {
2775 if args.is_empty() {
2776 return Err(RuntimeError::new("with_affect requires at least one argument"));
2777 }
2778
2779 let base_value = args[0].clone();
2780 let mut affect = RuntimeAffect {
2781 sentiment: None,
2782 sarcasm: false,
2783 intensity: None,
2784 formality: None,
2785 emotion: None,
2786 confidence: None,
2787 };
2788
2789 for arg in args.iter().skip(1) {
2791 if let Value::String(s) = arg {
2792 match s.as_str() {
2793 "positive" | "⊕" => affect.sentiment = Some(RuntimeSentiment::Positive),
2794 "negative" | "⊖" => affect.sentiment = Some(RuntimeSentiment::Negative),
2795 "neutral" | "⊜" => affect.sentiment = Some(RuntimeSentiment::Neutral),
2796 "sarcastic" | "⸮" => affect.sarcasm = true,
2797 "intensify" | "↑" => affect.intensity = Some(RuntimeIntensity::Up),
2798 "dampen" | "↓" => affect.intensity = Some(RuntimeIntensity::Down),
2799 "maximize" | "⇈" => affect.intensity = Some(RuntimeIntensity::Max),
2800 "formal" | "♔" => affect.formality = Some(RuntimeFormality::Formal),
2801 "informal" | "♟" => affect.formality = Some(RuntimeFormality::Informal),
2802 "joy" | "☺" => affect.emotion = Some(RuntimeEmotion::Joy),
2803 "sadness" | "☹" => affect.emotion = Some(RuntimeEmotion::Sadness),
2804 "anger" | "⚡" => affect.emotion = Some(RuntimeEmotion::Anger),
2805 "fear" | "❄" => affect.emotion = Some(RuntimeEmotion::Fear),
2806 "surprise" | "✦" => affect.emotion = Some(RuntimeEmotion::Surprise),
2807 "love" | "♡" => affect.emotion = Some(RuntimeEmotion::Love),
2808 "high" | "◉" => affect.confidence = Some(RuntimeConfidence::High),
2809 "medium" | "◎" => affect.confidence = Some(RuntimeConfidence::Medium),
2810 "low" | "○" => affect.confidence = Some(RuntimeConfidence::Low),
2811 _ => {}
2812 }
2813 }
2814 }
2815
2816 Ok(Value::Affective {
2817 value: Box::new(base_value),
2818 affect,
2819 })
2820 });
2821}
2822
2823fn register_iter(interp: &mut Interpreter) {
2828 define(interp, "sum", Some(1), |_, args| {
2830 match &args[0] {
2831 Value::Array(arr) => {
2832 let mut sum_int: i64 = 0;
2833 let mut sum_float: f64 = 0.0;
2834 let mut is_float = false;
2835
2836 for val in arr.borrow().iter() {
2837 match val {
2838 Value::Int(n) => {
2839 if is_float {
2840 sum_float += *n as f64;
2841 } else {
2842 sum_int += n;
2843 }
2844 }
2845 Value::Float(n) => {
2846 if !is_float {
2847 sum_float = sum_int as f64;
2848 is_float = true;
2849 }
2850 sum_float += n;
2851 }
2852 _ => return Err(RuntimeError::new("sum() requires array of numbers")),
2853 }
2854 }
2855
2856 if is_float {
2857 Ok(Value::Float(sum_float))
2858 } else {
2859 Ok(Value::Int(sum_int))
2860 }
2861 }
2862 _ => Err(RuntimeError::new("sum() requires array")),
2863 }
2864 });
2865
2866 define(interp, "product", Some(1), |_, args| {
2868 match &args[0] {
2869 Value::Array(arr) => {
2870 let mut prod_int: i64 = 1;
2871 let mut prod_float: f64 = 1.0;
2872 let mut is_float = false;
2873
2874 for val in arr.borrow().iter() {
2875 match val {
2876 Value::Int(n) => {
2877 if is_float {
2878 prod_float *= *n as f64;
2879 } else {
2880 prod_int *= n;
2881 }
2882 }
2883 Value::Float(n) => {
2884 if !is_float {
2885 prod_float = prod_int as f64;
2886 is_float = true;
2887 }
2888 prod_float *= n;
2889 }
2890 _ => return Err(RuntimeError::new("product() requires array of numbers")),
2891 }
2892 }
2893
2894 if is_float {
2895 Ok(Value::Float(prod_float))
2896 } else {
2897 Ok(Value::Int(prod_int))
2898 }
2899 }
2900 _ => Err(RuntimeError::new("product() requires array")),
2901 }
2902 });
2903
2904 define(interp, "mean", Some(1), |_, args| {
2906 match &args[0] {
2907 Value::Array(arr) => {
2908 let arr = arr.borrow();
2909 if arr.is_empty() {
2910 return Err(RuntimeError::new("mean() on empty array"));
2911 }
2912
2913 let mut sum: f64 = 0.0;
2914 for val in arr.iter() {
2915 match val {
2916 Value::Int(n) => sum += *n as f64,
2917 Value::Float(n) => sum += n,
2918 _ => return Err(RuntimeError::new("mean() requires array of numbers")),
2919 }
2920 }
2921
2922 Ok(Value::Float(sum / arr.len() as f64))
2923 }
2924 _ => Err(RuntimeError::new("mean() requires array")),
2925 }
2926 });
2927
2928 define(interp, "median", Some(1), |_, args| {
2930 match &args[0] {
2931 Value::Array(arr) => {
2932 let arr = arr.borrow();
2933 if arr.is_empty() {
2934 return Err(RuntimeError::new("median() on empty array"));
2935 }
2936
2937 let mut nums: Vec<f64> = Vec::new();
2938 for val in arr.iter() {
2939 match val {
2940 Value::Int(n) => nums.push(*n as f64),
2941 Value::Float(n) => nums.push(*n),
2942 _ => return Err(RuntimeError::new("median() requires array of numbers")),
2943 }
2944 }
2945
2946 nums.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
2947 let mid = nums.len() / 2;
2948
2949 if nums.len() % 2 == 0 {
2950 Ok(Value::Float((nums[mid - 1] + nums[mid]) / 2.0))
2951 } else {
2952 Ok(Value::Float(nums[mid]))
2953 }
2954 }
2955 _ => Err(RuntimeError::new("median() requires array")),
2956 }
2957 });
2958
2959 define(interp, "min_of", Some(1), |_, args| {
2961 match &args[0] {
2962 Value::Array(arr) => {
2963 let arr = arr.borrow();
2964 if arr.is_empty() {
2965 return Err(RuntimeError::new("min_of() on empty array"));
2966 }
2967
2968 let mut min = &arr[0];
2969 for val in arr.iter().skip(1) {
2970 if matches!(compare_values(val, min), std::cmp::Ordering::Less) {
2971 min = val;
2972 }
2973 }
2974 Ok(min.clone())
2975 }
2976 _ => Err(RuntimeError::new("min_of() requires array")),
2977 }
2978 });
2979
2980 define(interp, "max_of", Some(1), |_, args| {
2982 match &args[0] {
2983 Value::Array(arr) => {
2984 let arr = arr.borrow();
2985 if arr.is_empty() {
2986 return Err(RuntimeError::new("max_of() on empty array"));
2987 }
2988
2989 let mut max = &arr[0];
2990 for val in arr.iter().skip(1) {
2991 if matches!(compare_values(val, max), std::cmp::Ordering::Greater) {
2992 max = val;
2993 }
2994 }
2995 Ok(max.clone())
2996 }
2997 _ => Err(RuntimeError::new("max_of() requires array")),
2998 }
2999 });
3000
3001 define(interp, "count", Some(1), |_, args| {
3003 match &args[0] {
3004 Value::Array(arr) => Ok(Value::Int(arr.borrow().len() as i64)),
3005 Value::String(s) => Ok(Value::Int(s.chars().count() as i64)),
3006 _ => Err(RuntimeError::new("count() requires array or string")),
3007 }
3008 });
3009
3010 define(interp, "any", Some(1), |_, args| {
3012 match &args[0] {
3013 Value::Array(arr) => {
3014 for val in arr.borrow().iter() {
3015 if is_truthy(val) {
3016 return Ok(Value::Bool(true));
3017 }
3018 }
3019 Ok(Value::Bool(false))
3020 }
3021 _ => Err(RuntimeError::new("any() requires array")),
3022 }
3023 });
3024
3025 define(interp, "all", Some(1), |_, args| {
3027 match &args[0] {
3028 Value::Array(arr) => {
3029 for val in arr.borrow().iter() {
3030 if !is_truthy(val) {
3031 return Ok(Value::Bool(false));
3032 }
3033 }
3034 Ok(Value::Bool(true))
3035 }
3036 _ => Err(RuntimeError::new("all() requires array")),
3037 }
3038 });
3039
3040 define(interp, "none", Some(1), |_, args| {
3042 match &args[0] {
3043 Value::Array(arr) => {
3044 for val in arr.borrow().iter() {
3045 if is_truthy(val) {
3046 return Ok(Value::Bool(false));
3047 }
3048 }
3049 Ok(Value::Bool(true))
3050 }
3051 _ => Err(RuntimeError::new("none() requires array")),
3052 }
3053 });
3054}
3055
3056fn is_truthy(val: &Value) -> bool {
3057 match val {
3058 Value::Null | Value::Empty => false,
3059 Value::Bool(b) => *b,
3060 Value::Int(n) => *n != 0,
3061 Value::Float(n) => *n != 0.0 && !n.is_nan(),
3062 Value::String(s) => !s.is_empty(),
3063 Value::Array(arr) => !arr.borrow().is_empty(),
3064 Value::Evidential { value, .. } => is_truthy(value),
3065 _ => true,
3066 }
3067}
3068
3069fn register_io(interp: &mut Interpreter) {
3074 define(interp, "read_file", Some(1), |_, args| {
3076 match &args[0] {
3077 Value::String(path) => {
3078 match std::fs::read_to_string(path.as_str()) {
3079 Ok(content) => Ok(Value::Evidential {
3080 value: Box::new(Value::String(Rc::new(content))),
3081 evidence: Evidence::Reported, }),
3083 Err(e) => Err(RuntimeError::new(format!("read_file failed: {}", e))),
3084 }
3085 }
3086 _ => Err(RuntimeError::new("read_file() requires path string")),
3087 }
3088 });
3089
3090 define(interp, "write_file", Some(2), |_, args| {
3092 match (&args[0], &args[1]) {
3093 (Value::String(path), Value::String(content)) => {
3094 match std::fs::write(path.as_str(), content.as_str()) {
3095 Ok(_) => Ok(Value::Bool(true)),
3096 Err(e) => Err(RuntimeError::new(format!("write_file failed: {}", e))),
3097 }
3098 }
3099 _ => Err(RuntimeError::new("write_file() requires path and content strings")),
3100 }
3101 });
3102
3103 define(interp, "append_file", Some(2), |_, args| {
3105 match (&args[0], &args[1]) {
3106 (Value::String(path), Value::String(content)) => {
3107 use std::fs::OpenOptions;
3108 let result = OpenOptions::new()
3109 .create(true)
3110 .append(true)
3111 .open(path.as_str())
3112 .and_then(|mut f| f.write_all(content.as_bytes()));
3113 match result {
3114 Ok(_) => Ok(Value::Bool(true)),
3115 Err(e) => Err(RuntimeError::new(format!("append_file failed: {}", e))),
3116 }
3117 }
3118 _ => Err(RuntimeError::new("append_file() requires path and content strings")),
3119 }
3120 });
3121
3122 define(interp, "file_exists", Some(1), |_, args| {
3124 match &args[0] {
3125 Value::String(path) => Ok(Value::Bool(std::path::Path::new(path.as_str()).exists())),
3126 _ => Err(RuntimeError::new("file_exists() requires path string")),
3127 }
3128 });
3129
3130 define(interp, "read_lines", Some(1), |_, args| {
3132 match &args[0] {
3133 Value::String(path) => {
3134 match std::fs::read_to_string(path.as_str()) {
3135 Ok(content) => {
3136 let lines: Vec<Value> = content.lines()
3137 .map(|l| Value::String(Rc::new(l.to_string())))
3138 .collect();
3139 Ok(Value::Evidential {
3140 value: Box::new(Value::Array(Rc::new(RefCell::new(lines)))),
3141 evidence: Evidence::Reported,
3142 })
3143 }
3144 Err(e) => Err(RuntimeError::new(format!("read_lines failed: {}", e))),
3145 }
3146 }
3147 _ => Err(RuntimeError::new("read_lines() requires path string")),
3148 }
3149 });
3150
3151 define(interp, "env", Some(1), |_, args| {
3153 match &args[0] {
3154 Value::String(name) => {
3155 match std::env::var(name.as_str()) {
3156 Ok(value) => Ok(Value::Evidential {
3157 value: Box::new(Value::String(Rc::new(value))),
3158 evidence: Evidence::Reported, }),
3160 Err(_) => Ok(Value::Null),
3161 }
3162 }
3163 _ => Err(RuntimeError::new("env() requires variable name string")),
3164 }
3165 });
3166
3167 define(interp, "env_or", Some(2), |_, args| {
3169 match (&args[0], &args[1]) {
3170 (Value::String(name), default) => {
3171 match std::env::var(name.as_str()) {
3172 Ok(value) => Ok(Value::Evidential {
3173 value: Box::new(Value::String(Rc::new(value))),
3174 evidence: Evidence::Reported,
3175 }),
3176 Err(_) => Ok(default.clone()),
3177 }
3178 }
3179 _ => Err(RuntimeError::new("env_or() requires variable name string")),
3180 }
3181 });
3182
3183 define(interp, "cwd", Some(0), |_, _| {
3185 match std::env::current_dir() {
3186 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
3187 Err(e) => Err(RuntimeError::new(format!("cwd() failed: {}", e))),
3188 }
3189 });
3190
3191 define(interp, "args", Some(0), |_, _| {
3193 let args: Vec<Value> = std::env::args()
3194 .map(|a| Value::String(Rc::new(a)))
3195 .collect();
3196 Ok(Value::Array(Rc::new(RefCell::new(args))))
3197 });
3198}
3199
3200fn register_time(interp: &mut Interpreter) {
3205 define(interp, "now", Some(0), |_, _| {
3207 let duration = SystemTime::now()
3208 .duration_since(UNIX_EPOCH)
3209 .unwrap_or(Duration::ZERO);
3210 Ok(Value::Int(duration.as_millis() as i64))
3211 });
3212
3213 define(interp, "now_secs", Some(0), |_, _| {
3215 let duration = SystemTime::now()
3216 .duration_since(UNIX_EPOCH)
3217 .unwrap_or(Duration::ZERO);
3218 Ok(Value::Int(duration.as_secs() as i64))
3219 });
3220
3221 define(interp, "now_micros", Some(0), |_, _| {
3223 let duration = SystemTime::now()
3224 .duration_since(UNIX_EPOCH)
3225 .unwrap_or(Duration::ZERO);
3226 Ok(Value::Int(duration.as_micros() as i64))
3227 });
3228
3229 define(interp, "sleep", Some(1), |_, args| {
3231 match &args[0] {
3232 Value::Int(ms) if *ms >= 0 => {
3233 std::thread::sleep(Duration::from_millis(*ms as u64));
3234 Ok(Value::Null)
3235 }
3236 _ => Err(RuntimeError::new("sleep() requires non-negative integer milliseconds")),
3237 }
3238 });
3239
3240 define(interp, "timer_start", Some(0), |_, _| {
3246 let now = Instant::now();
3247 Ok(Value::Int(now.elapsed().as_nanos() as i64)) });
3250}
3251
3252fn register_random(interp: &mut Interpreter) {
3257 define(interp, "random", Some(0), |_, _| {
3259 use std::time::SystemTime;
3261 let seed = SystemTime::now()
3262 .duration_since(UNIX_EPOCH)
3263 .unwrap_or(Duration::ZERO)
3264 .as_nanos() as u64;
3265 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as f64;
3266 Ok(Value::Float(rand / u32::MAX as f64))
3267 });
3268
3269 define(interp, "random_int", Some(2), |_, args| {
3271 match (&args[0], &args[1]) {
3272 (Value::Int(min), Value::Int(max)) if max > min => {
3273 use std::time::SystemTime;
3274 let seed = SystemTime::now()
3275 .duration_since(UNIX_EPOCH)
3276 .unwrap_or(Duration::ZERO)
3277 .as_nanos() as u64;
3278 let range = (max - min) as u64;
3279 let rand = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) % range;
3280 Ok(Value::Int(*min + rand as i64))
3281 }
3282 _ => Err(RuntimeError::new("random_int() requires min < max integers")),
3283 }
3284 });
3285
3286 define(interp, "shuffle", Some(1), |_, args| {
3288 match &args[0] {
3289 Value::Array(arr) => {
3290 let mut arr = arr.borrow_mut();
3291 use std::time::SystemTime;
3292 let mut seed = SystemTime::now()
3293 .duration_since(UNIX_EPOCH)
3294 .unwrap_or(Duration::ZERO)
3295 .as_nanos() as u64;
3296
3297 for i in (1..arr.len()).rev() {
3298 seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
3299 let j = ((seed >> 16) as usize) % (i + 1);
3300 arr.swap(i, j);
3301 }
3302 Ok(Value::Null)
3303 }
3304 _ => Err(RuntimeError::new("shuffle() requires array")),
3305 }
3306 });
3307
3308 define(interp, "sample", Some(1), |_, args| {
3310 match &args[0] {
3311 Value::Array(arr) => {
3312 let arr = arr.borrow();
3313 if arr.is_empty() {
3314 return Err(RuntimeError::new("sample() on empty array"));
3315 }
3316
3317 use std::time::SystemTime;
3318 let seed = SystemTime::now()
3319 .duration_since(UNIX_EPOCH)
3320 .unwrap_or(Duration::ZERO)
3321 .as_nanos() as u64;
3322 let idx = ((seed.wrapping_mul(1103515245).wrapping_add(12345)) >> 16) as usize % arr.len();
3323 Ok(arr[idx].clone())
3324 }
3325 _ => Err(RuntimeError::new("sample() requires array")),
3326 }
3327 });
3328}
3329
3330fn register_convert(interp: &mut Interpreter) {
3335 define(interp, "to_string", Some(1), |_, args| {
3337 Ok(Value::String(Rc::new(format!("{}", args[0]))))
3338 });
3339
3340 define(interp, "to_int", Some(1), |_, args| {
3342 match &args[0] {
3343 Value::Int(n) => Ok(Value::Int(*n)),
3344 Value::Float(n) => Ok(Value::Int(*n as i64)),
3345 Value::Bool(b) => Ok(Value::Int(if *b { 1 } else { 0 })),
3346 Value::Char(c) => Ok(Value::Int(*c as i64)),
3347 Value::String(s) => {
3348 s.parse::<i64>()
3349 .map(Value::Int)
3350 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as integer", s)))
3351 }
3352 _ => Err(RuntimeError::new("to_int() cannot convert this type")),
3353 }
3354 });
3355
3356 define(interp, "to_float", Some(1), |_, args| {
3358 match &args[0] {
3359 Value::Int(n) => Ok(Value::Float(*n as f64)),
3360 Value::Float(n) => Ok(Value::Float(*n)),
3361 Value::Bool(b) => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
3362 Value::String(s) => {
3363 s.parse::<f64>()
3364 .map(Value::Float)
3365 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as float", s)))
3366 }
3367 _ => Err(RuntimeError::new("to_float() cannot convert this type")),
3368 }
3369 });
3370
3371 define(interp, "to_bool", Some(1), |_, args| {
3373 Ok(Value::Bool(is_truthy(&args[0])))
3374 });
3375
3376 define(interp, "to_char", Some(1), |_, args| {
3378 match &args[0] {
3379 Value::Char(c) => Ok(Value::Char(*c)),
3380 Value::Int(n) => {
3381 char::from_u32(*n as u32)
3382 .map(Value::Char)
3383 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n)))
3384 }
3385 Value::String(s) => {
3386 s.chars().next()
3387 .map(Value::Char)
3388 .ok_or_else(|| RuntimeError::new("to_char() on empty string"))
3389 }
3390 _ => Err(RuntimeError::new("to_char() cannot convert this type")),
3391 }
3392 });
3393
3394 define(interp, "to_array", Some(1), |_, args| {
3396 match &args[0] {
3397 Value::Array(arr) => Ok(Value::Array(arr.clone())),
3398 Value::Tuple(t) => Ok(Value::Array(Rc::new(RefCell::new(t.as_ref().clone())))),
3399 Value::String(s) => {
3400 let chars: Vec<Value> = s.chars().map(Value::Char).collect();
3401 Ok(Value::Array(Rc::new(RefCell::new(chars))))
3402 }
3403 _ => Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()])))),
3404 }
3405 });
3406
3407 define(interp, "to_tuple", Some(1), |_, args| {
3409 match &args[0] {
3410 Value::Tuple(t) => Ok(Value::Tuple(t.clone())),
3411 Value::Array(arr) => Ok(Value::Tuple(Rc::new(arr.borrow().clone()))),
3412 _ => Ok(Value::Tuple(Rc::new(vec![args[0].clone()]))),
3413 }
3414 });
3415
3416 define(interp, "char_code", Some(1), |_, args| {
3418 match &args[0] {
3419 Value::Char(c) => Ok(Value::Int(*c as i64)),
3420 _ => Err(RuntimeError::new("char_code() requires char")),
3421 }
3422 });
3423
3424 define(interp, "from_char_code", Some(1), |_, args| {
3426 match &args[0] {
3427 Value::Int(n) => {
3428 char::from_u32(*n as u32)
3429 .map(Value::Char)
3430 .ok_or_else(|| RuntimeError::new(format!("invalid char code: {}", n)))
3431 }
3432 _ => Err(RuntimeError::new("from_char_code() requires integer")),
3433 }
3434 });
3435
3436 define(interp, "hex", Some(1), |_, args| {
3438 match &args[0] {
3439 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:x}", n)))),
3440 _ => Err(RuntimeError::new("hex() requires integer")),
3441 }
3442 });
3443
3444 define(interp, "oct", Some(1), |_, args| {
3446 match &args[0] {
3447 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:o}", n)))),
3448 _ => Err(RuntimeError::new("oct() requires integer")),
3449 }
3450 });
3451
3452 define(interp, "bin", Some(1), |_, args| {
3454 match &args[0] {
3455 Value::Int(n) => Ok(Value::String(Rc::new(format!("{:b}", n)))),
3456 _ => Err(RuntimeError::new("bin() requires integer")),
3457 }
3458 });
3459
3460 define(interp, "parse_int", Some(2), |_, args| {
3462 match (&args[0], &args[1]) {
3463 (Value::String(s), Value::Int(base)) if *base >= 2 && *base <= 36 => {
3464 i64::from_str_radix(s.trim(), *base as u32)
3465 .map(Value::Int)
3466 .map_err(|_| RuntimeError::new(format!("cannot parse '{}' as base-{} integer", s, base)))
3467 }
3468 _ => Err(RuntimeError::new("parse_int() requires string and base 2-36")),
3469 }
3470 });
3471}
3472
3473fn register_cycle(interp: &mut Interpreter) {
3479 define(interp, "cycle", Some(2), |_, args| {
3481 match (&args[0], &args[1]) {
3482 (Value::Int(value), Value::Int(modulus)) if *modulus > 0 => {
3483 let normalized = value.rem_euclid(*modulus);
3484 Ok(Value::Tuple(Rc::new(vec![
3485 Value::Int(normalized),
3486 Value::Int(*modulus),
3487 ])))
3488 }
3489 _ => Err(RuntimeError::new("cycle() requires value and positive modulus")),
3490 }
3491 });
3492
3493 define(interp, "mod_add", Some(3), |_, args| {
3495 match (&args[0], &args[1], &args[2]) {
3496 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
3497 Ok(Value::Int((a + b).rem_euclid(*m)))
3498 }
3499 _ => Err(RuntimeError::new("mod_add() requires two integers and positive modulus")),
3500 }
3501 });
3502
3503 define(interp, "mod_sub", Some(3), |_, args| {
3505 match (&args[0], &args[1], &args[2]) {
3506 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
3507 Ok(Value::Int((a - b).rem_euclid(*m)))
3508 }
3509 _ => Err(RuntimeError::new("mod_sub() requires two integers and positive modulus")),
3510 }
3511 });
3512
3513 define(interp, "mod_mul", Some(3), |_, args| {
3515 match (&args[0], &args[1], &args[2]) {
3516 (Value::Int(a), Value::Int(b), Value::Int(m)) if *m > 0 => {
3517 Ok(Value::Int((a * b).rem_euclid(*m)))
3518 }
3519 _ => Err(RuntimeError::new("mod_mul() requires two integers and positive modulus")),
3520 }
3521 });
3522
3523 define(interp, "mod_pow", Some(3), |_, args| {
3525 match (&args[0], &args[1], &args[2]) {
3526 (Value::Int(base), Value::Int(exp), Value::Int(m)) if *m > 0 && *exp >= 0 => {
3527 Ok(Value::Int(mod_pow(*base, *exp as u64, *m)))
3528 }
3529 _ => Err(RuntimeError::new("mod_pow() requires base, non-negative exp, and positive modulus")),
3530 }
3531 });
3532
3533 define(interp, "mod_inv", Some(2), |_, args| {
3535 match (&args[0], &args[1]) {
3536 (Value::Int(a), Value::Int(m)) if *m > 0 => {
3537 match mod_inverse(*a, *m) {
3538 Some(inv) => Ok(Value::Int(inv)),
3539 None => Err(RuntimeError::new(format!("no modular inverse of {} mod {}", a, m))),
3540 }
3541 }
3542 _ => Err(RuntimeError::new("mod_inv() requires integer and positive modulus")),
3543 }
3544 });
3545
3546 define(interp, "octave", Some(1), |_, args| {
3549 match &args[0] {
3550 Value::Int(note) => Ok(Value::Int(note.rem_euclid(12))),
3551 Value::Float(freq) => {
3552 let semitones = 12.0 * (freq / 440.0).log2();
3554 Ok(Value::Float(semitones.rem_euclid(12.0)))
3555 }
3556 _ => Err(RuntimeError::new("octave() requires number")),
3557 }
3558 });
3559
3560 define(interp, "interval", Some(2), |_, args| {
3562 match (&args[0], &args[1]) {
3563 (Value::Int(a), Value::Int(b)) => {
3564 Ok(Value::Int((b - a).rem_euclid(12)))
3565 }
3566 _ => Err(RuntimeError::new("interval() requires two integers")),
3567 }
3568 });
3569
3570 define(interp, "cents", Some(1), |_, args| {
3572 match &args[0] {
3573 Value::Int(semitones) => Ok(Value::Int(*semitones * 100)),
3574 Value::Float(semitones) => Ok(Value::Float(*semitones * 100.0)),
3575 _ => Err(RuntimeError::new("cents() requires number")),
3576 }
3577 });
3578
3579 define(interp, "freq", Some(1), |_, args| {
3581 match &args[0] {
3582 Value::Int(midi) => {
3583 let freq = 440.0 * 2.0_f64.powf((*midi as f64 - 69.0) / 12.0);
3584 Ok(Value::Float(freq))
3585 }
3586 _ => Err(RuntimeError::new("freq() requires integer MIDI note")),
3587 }
3588 });
3589
3590 define(interp, "midi", Some(1), |_, args| {
3592 match &args[0] {
3593 Value::Float(freq) if *freq > 0.0 => {
3594 let midi = 69.0 + 12.0 * (freq / 440.0).log2();
3595 Ok(Value::Int(midi.round() as i64))
3596 }
3597 Value::Int(freq) if *freq > 0 => {
3598 let midi = 69.0 + 12.0 * (*freq as f64 / 440.0).log2();
3599 Ok(Value::Int(midi.round() as i64))
3600 }
3601 _ => Err(RuntimeError::new("midi() requires positive frequency")),
3602 }
3603 });
3604}
3605
3606fn mod_pow(mut base: i64, mut exp: u64, modulus: i64) -> i64 {
3608 if modulus == 1 { return 0; }
3609 let mut result: i64 = 1;
3610 base = base.rem_euclid(modulus);
3611 while exp > 0 {
3612 if exp % 2 == 1 {
3613 result = (result * base).rem_euclid(modulus);
3614 }
3615 exp /= 2;
3616 base = (base * base).rem_euclid(modulus);
3617 }
3618 result
3619}
3620
3621fn register_simd(interp: &mut Interpreter) {
3627 define(interp, "simd_new", Some(4), |_, args| {
3629 let values: Result<Vec<f64>, _> = args.iter().map(|v| match v {
3630 Value::Float(f) => Ok(*f),
3631 Value::Int(i) => Ok(*i as f64),
3632 _ => Err(RuntimeError::new("simd_new() requires numbers")),
3633 }).collect();
3634 let values = values?;
3635 Ok(Value::Array(Rc::new(RefCell::new(vec![
3636 Value::Float(values[0]),
3637 Value::Float(values[1]),
3638 Value::Float(values[2]),
3639 Value::Float(values[3]),
3640 ]))))
3641 });
3642
3643 define(interp, "simd_splat", Some(1), |_, args| {
3645 let v = match &args[0] {
3646 Value::Float(f) => *f,
3647 Value::Int(i) => *i as f64,
3648 _ => return Err(RuntimeError::new("simd_splat() requires number")),
3649 };
3650 Ok(Value::Array(Rc::new(RefCell::new(vec![
3651 Value::Float(v), Value::Float(v), Value::Float(v), Value::Float(v),
3652 ]))))
3653 });
3654
3655 define(interp, "simd_add", Some(2), |_, args| {
3657 simd_binary_op(&args[0], &args[1], |a, b| a + b, "simd_add")
3658 });
3659
3660 define(interp, "simd_sub", Some(2), |_, args| {
3662 simd_binary_op(&args[0], &args[1], |a, b| a - b, "simd_sub")
3663 });
3664
3665 define(interp, "simd_mul", Some(2), |_, args| {
3667 simd_binary_op(&args[0], &args[1], |a, b| a * b, "simd_mul")
3668 });
3669
3670 define(interp, "simd_div", Some(2), |_, args| {
3672 simd_binary_op(&args[0], &args[1], |a, b| a / b, "simd_div")
3673 });
3674
3675 define(interp, "simd_dot", Some(2), |_, args| {
3677 let a = extract_simd(&args[0], "simd_dot")?;
3678 let b = extract_simd(&args[1], "simd_dot")?;
3679 let dot = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
3680 Ok(Value::Float(dot))
3681 });
3682
3683 define(interp, "simd_cross", Some(2), |_, args| {
3685 let a = extract_simd(&args[0], "simd_cross")?;
3686 let b = extract_simd(&args[1], "simd_cross")?;
3687 Ok(Value::Array(Rc::new(RefCell::new(vec![
3688 Value::Float(a[1] * b[2] - a[2] * b[1]),
3689 Value::Float(a[2] * b[0] - a[0] * b[2]),
3690 Value::Float(a[0] * b[1] - a[1] * b[0]),
3691 Value::Float(0.0),
3692 ]))))
3693 });
3694
3695 define(interp, "simd_length", Some(1), |_, args| {
3697 let v = extract_simd(&args[0], "simd_length")?;
3698 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
3699 Ok(Value::Float(len_sq.sqrt()))
3700 });
3701
3702 define(interp, "simd_normalize", Some(1), |_, args| {
3704 let v = extract_simd(&args[0], "simd_normalize")?;
3705 let len_sq = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
3706 let len = len_sq.sqrt();
3707 if len < 1e-10 {
3708 return Ok(Value::Array(Rc::new(RefCell::new(vec![
3709 Value::Float(0.0), Value::Float(0.0), Value::Float(0.0), Value::Float(0.0),
3710 ]))));
3711 }
3712 let inv_len = 1.0 / len;
3713 Ok(Value::Array(Rc::new(RefCell::new(vec![
3714 Value::Float(v[0] * inv_len),
3715 Value::Float(v[1] * inv_len),
3716 Value::Float(v[2] * inv_len),
3717 Value::Float(v[3] * inv_len),
3718 ]))))
3719 });
3720
3721 define(interp, "simd_min", Some(2), |_, args| {
3723 simd_binary_op(&args[0], &args[1], |a, b| a.min(b), "simd_min")
3724 });
3725
3726 define(interp, "simd_max", Some(2), |_, args| {
3728 simd_binary_op(&args[0], &args[1], |a, b| a.max(b), "simd_max")
3729 });
3730
3731 define(interp, "simd_hadd", Some(1), |_, args| {
3733 let v = extract_simd(&args[0], "simd_hadd")?;
3734 Ok(Value::Float(v[0] + v[1] + v[2] + v[3]))
3735 });
3736
3737 define(interp, "simd_extract", Some(2), |_, args| {
3739 let v = extract_simd(&args[0], "simd_extract")?;
3740 let idx = match &args[1] {
3741 Value::Int(i) => *i as usize,
3742 _ => return Err(RuntimeError::new("simd_extract() requires integer index")),
3743 };
3744 if idx > 3 {
3745 return Err(RuntimeError::new("simd_extract() index must be 0-3"));
3746 }
3747 Ok(Value::Float(v[idx]))
3748 });
3749
3750 define(interp, "simd_free", Some(1), |_, _| {
3752 Ok(Value::Null)
3753 });
3754
3755 define(interp, "simd_lerp", Some(3), |_, args| {
3757 let a = extract_simd(&args[0], "simd_lerp")?;
3758 let b = extract_simd(&args[1], "simd_lerp")?;
3759 let t = match &args[2] {
3760 Value::Float(f) => *f,
3761 Value::Int(i) => *i as f64,
3762 _ => return Err(RuntimeError::new("simd_lerp() requires float t")),
3763 };
3764 let one_t = 1.0 - t;
3765 Ok(Value::Array(Rc::new(RefCell::new(vec![
3766 Value::Float(a[0] * one_t + b[0] * t),
3767 Value::Float(a[1] * one_t + b[1] * t),
3768 Value::Float(a[2] * one_t + b[2] * t),
3769 Value::Float(a[3] * one_t + b[3] * t),
3770 ]))))
3771 });
3772}
3773
3774fn extract_simd(val: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
3776 match val {
3777 Value::Array(arr) => {
3778 let arr = arr.borrow();
3779 if arr.len() < 4 {
3780 return Err(RuntimeError::new(format!("{}() requires 4-element array", fn_name)));
3781 }
3782 let mut result = [0.0; 4];
3783 for (i, v) in arr.iter().take(4).enumerate() {
3784 result[i] = match v {
3785 Value::Float(f) => *f,
3786 Value::Int(n) => *n as f64,
3787 _ => return Err(RuntimeError::new(format!("{}() requires numeric array", fn_name))),
3788 };
3789 }
3790 Ok(result)
3791 }
3792 _ => Err(RuntimeError::new(format!("{}() requires array argument", fn_name))),
3793 }
3794}
3795
3796fn simd_binary_op<F>(a: &Value, b: &Value, op: F, fn_name: &str) -> Result<Value, RuntimeError>
3798where
3799 F: Fn(f64, f64) -> f64,
3800{
3801 let a = extract_simd(a, fn_name)?;
3802 let b = extract_simd(b, fn_name)?;
3803 Ok(Value::Array(Rc::new(RefCell::new(vec![
3804 Value::Float(op(a[0], b[0])),
3805 Value::Float(op(a[1], b[1])),
3806 Value::Float(op(a[2], b[2])),
3807 Value::Float(op(a[3], b[3])),
3808 ]))))
3809}
3810
3811fn register_graphics_math(interp: &mut Interpreter) {
3822 define(interp, "quat_new", Some(4), |_, args| {
3830 let w = extract_number(&args[0], "quat_new")?;
3831 let x = extract_number(&args[1], "quat_new")?;
3832 let y = extract_number(&args[2], "quat_new")?;
3833 let z = extract_number(&args[3], "quat_new")?;
3834 Ok(make_vec4(w, x, y, z))
3835 });
3836
3837 define(interp, "quat_identity", Some(0), |_, _| {
3839 Ok(make_vec4(1.0, 0.0, 0.0, 0.0))
3840 });
3841
3842 define(interp, "quat_from_axis_angle", Some(2), |_, args| {
3844 let axis = extract_vec3(&args[0], "quat_from_axis_angle")?;
3845 let angle = extract_number(&args[1], "quat_from_axis_angle")?;
3846
3847 let len = (axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]).sqrt();
3849 if len < 1e-10 {
3850 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0)); }
3852 let ax = axis[0] / len;
3853 let ay = axis[1] / len;
3854 let az = axis[2] / len;
3855
3856 let half_angle = angle / 2.0;
3857 let s = half_angle.sin();
3858 let c = half_angle.cos();
3859
3860 Ok(make_vec4(c, ax * s, ay * s, az * s))
3861 });
3862
3863 define(interp, "quat_from_euler", Some(3), |_, args| {
3866 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();
3871 let (sy, cy) = (yaw / 2.0).sin_cos();
3872 let (sr, cr) = (roll / 2.0).sin_cos();
3873
3874 let w = cp * cy * cr + sp * sy * sr;
3876 let x = sp * cy * cr - cp * sy * sr;
3877 let y = cp * sy * cr + sp * cy * sr;
3878 let z = cp * cy * sr - sp * sy * cr;
3879
3880 Ok(make_vec4(w, x, y, z))
3881 });
3882
3883 define(interp, "quat_mul", Some(2), |_, args| {
3885 let q1 = extract_vec4(&args[0], "quat_mul")?;
3886 let q2 = extract_vec4(&args[1], "quat_mul")?;
3887
3888 let w = q1[0]*q2[0] - q1[1]*q2[1] - q1[2]*q2[2] - q1[3]*q2[3];
3890 let x = q1[0]*q2[1] + q1[1]*q2[0] + q1[2]*q2[3] - q1[3]*q2[2];
3891 let y = q1[0]*q2[2] - q1[1]*q2[3] + q1[2]*q2[0] + q1[3]*q2[1];
3892 let z = q1[0]*q2[3] + q1[1]*q2[2] - q1[2]*q2[1] + q1[3]*q2[0];
3893
3894 Ok(make_vec4(w, x, y, z))
3895 });
3896
3897 define(interp, "quat_conjugate", Some(1), |_, args| {
3899 let q = extract_vec4(&args[0], "quat_conjugate")?;
3900 Ok(make_vec4(q[0], -q[1], -q[2], -q[3]))
3901 });
3902
3903 define(interp, "quat_inverse", Some(1), |_, args| {
3905 let q = extract_vec4(&args[0], "quat_inverse")?;
3906 let norm_sq = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
3907 if norm_sq < 1e-10 {
3908 return Err(RuntimeError::new("quat_inverse: cannot invert zero quaternion"));
3909 }
3910 Ok(make_vec4(q[0]/norm_sq, -q[1]/norm_sq, -q[2]/norm_sq, -q[3]/norm_sq))
3911 });
3912
3913 define(interp, "quat_normalize", Some(1), |_, args| {
3915 let q = extract_vec4(&args[0], "quat_normalize")?;
3916 let len = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]).sqrt();
3917 if len < 1e-10 {
3918 return Ok(make_vec4(1.0, 0.0, 0.0, 0.0));
3919 }
3920 Ok(make_vec4(q[0]/len, q[1]/len, q[2]/len, q[3]/len))
3921 });
3922
3923 define(interp, "quat_rotate", Some(2), |_, args| {
3925 let q = extract_vec4(&args[0], "quat_rotate")?;
3926 let v = extract_vec3(&args[1], "quat_rotate")?;
3927
3928 let qw = q[0]; let qx = q[1]; let qy = q[2]; let qz = q[3];
3930 let vx = v[0]; let vy = v[1]; let vz = v[2];
3931
3932 let tx = 2.0 * (qy * vz - qz * vy);
3934 let ty = 2.0 * (qz * vx - qx * vz);
3935 let tz = 2.0 * (qx * vy - qy * vx);
3936
3937 let rx = vx + qw * tx + (qy * tz - qz * ty);
3939 let ry = vy + qw * ty + (qz * tx - qx * tz);
3940 let rz = vz + qw * tz + (qx * ty - qy * tx);
3941
3942 Ok(make_vec3(rx, ry, rz))
3943 });
3944
3945 define(interp, "quat_slerp", Some(3), |_, args| {
3947 let q1 = extract_vec4(&args[0], "quat_slerp")?;
3948 let mut q2 = extract_vec4(&args[1], "quat_slerp")?;
3949 let t = extract_number(&args[2], "quat_slerp")?;
3950
3951 let mut dot = q1[0]*q2[0] + q1[1]*q2[1] + q1[2]*q2[2] + q1[3]*q2[3];
3953
3954 if dot < 0.0 {
3956 q2 = [-q2[0], -q2[1], -q2[2], -q2[3]];
3957 dot = -dot;
3958 }
3959
3960 if dot > 0.9995 {
3962 let w = q1[0] + t * (q2[0] - q1[0]);
3963 let x = q1[1] + t * (q2[1] - q1[1]);
3964 let y = q1[2] + t * (q2[2] - q1[2]);
3965 let z = q1[3] + t * (q2[3] - q1[3]);
3966 let len = (w*w + x*x + y*y + z*z).sqrt();
3967 return Ok(make_vec4(w/len, x/len, y/len, z/len));
3968 }
3969
3970 let theta_0 = dot.acos();
3972 let theta = theta_0 * t;
3973 let sin_theta = theta.sin();
3974 let sin_theta_0 = theta_0.sin();
3975
3976 let s0 = (theta_0 - theta).cos() - dot * sin_theta / sin_theta_0;
3977 let s1 = sin_theta / sin_theta_0;
3978
3979 Ok(make_vec4(
3980 s0 * q1[0] + s1 * q2[0],
3981 s0 * q1[1] + s1 * q2[1],
3982 s0 * q1[2] + s1 * q2[2],
3983 s0 * q1[3] + s1 * q2[3],
3984 ))
3985 });
3986
3987 define(interp, "quat_to_euler", Some(1), |_, args| {
3989 let q = extract_vec4(&args[0], "quat_to_euler")?;
3990 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
3991
3992 let sinr_cosp = 2.0 * (w * x + y * z);
3994 let cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
3995 let roll = sinr_cosp.atan2(cosr_cosp);
3996
3997 let sinp = 2.0 * (w * y - z * x);
3999 let pitch = if sinp.abs() >= 1.0 {
4000 std::f64::consts::FRAC_PI_2.copysign(sinp)
4001 } else {
4002 sinp.asin()
4003 };
4004
4005 let siny_cosp = 2.0 * (w * z + x * y);
4007 let cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
4008 let yaw = siny_cosp.atan2(cosy_cosp);
4009
4010 Ok(make_vec3(pitch, yaw, roll))
4011 });
4012
4013 define(interp, "quat_to_mat4", Some(1), |_, args| {
4015 let q = extract_vec4(&args[0], "quat_to_mat4")?;
4016 let (w, x, y, z) = (q[0], q[1], q[2], q[3]);
4017
4018 let xx = x * x; let yy = y * y; let zz = z * z;
4019 let xy = x * y; let xz = x * z; let yz = y * z;
4020 let wx = w * x; let wy = w * y; let wz = w * z;
4021
4022 Ok(make_mat4([
4024 1.0 - 2.0*(yy + zz), 2.0*(xy + wz), 2.0*(xz - wy), 0.0,
4025 2.0*(xy - wz), 1.0 - 2.0*(xx + zz), 2.0*(yz + wx), 0.0,
4026 2.0*(xz + wy), 2.0*(yz - wx), 1.0 - 2.0*(xx + yy), 0.0,
4027 0.0, 0.0, 0.0, 1.0,
4028 ]))
4029 });
4030
4031 define(interp, "vec2", Some(2), |_, args| {
4037 let x = extract_number(&args[0], "vec2")?;
4038 let y = extract_number(&args[1], "vec2")?;
4039 Ok(make_vec2(x, y))
4040 });
4041
4042 define(interp, "vec3", Some(3), |_, args| {
4044 let x = extract_number(&args[0], "vec3")?;
4045 let y = extract_number(&args[1], "vec3")?;
4046 let z = extract_number(&args[2], "vec3")?;
4047 Ok(make_vec3(x, y, z))
4048 });
4049
4050 define(interp, "vec4", Some(4), |_, args| {
4052 let x = extract_number(&args[0], "vec4")?;
4053 let y = extract_number(&args[1], "vec4")?;
4054 let z = extract_number(&args[2], "vec4")?;
4055 let w = extract_number(&args[3], "vec4")?;
4056 Ok(make_vec4(x, y, z, w))
4057 });
4058
4059 define(interp, "vec3_add", Some(2), |_, args| {
4061 let a = extract_vec3(&args[0], "vec3_add")?;
4062 let b = extract_vec3(&args[1], "vec3_add")?;
4063 Ok(make_vec3(a[0]+b[0], a[1]+b[1], a[2]+b[2]))
4064 });
4065
4066 define(interp, "vec3_sub", Some(2), |_, args| {
4068 let a = extract_vec3(&args[0], "vec3_sub")?;
4069 let b = extract_vec3(&args[1], "vec3_sub")?;
4070 Ok(make_vec3(a[0]-b[0], a[1]-b[1], a[2]-b[2]))
4071 });
4072
4073 define(interp, "vec3_scale", Some(2), |_, args| {
4075 let v = extract_vec3(&args[0], "vec3_scale")?;
4076 let s = extract_number(&args[1], "vec3_scale")?;
4077 Ok(make_vec3(v[0]*s, v[1]*s, v[2]*s))
4078 });
4079
4080 define(interp, "vec3_dot", Some(2), |_, args| {
4082 let a = extract_vec3(&args[0], "vec3_dot")?;
4083 let b = extract_vec3(&args[1], "vec3_dot")?;
4084 Ok(Value::Float(a[0]*b[0] + a[1]*b[1] + a[2]*b[2]))
4085 });
4086
4087 define(interp, "vec3_cross", Some(2), |_, args| {
4089 let a = extract_vec3(&args[0], "vec3_cross")?;
4090 let b = extract_vec3(&args[1], "vec3_cross")?;
4091 Ok(make_vec3(
4092 a[1]*b[2] - a[2]*b[1],
4093 a[2]*b[0] - a[0]*b[2],
4094 a[0]*b[1] - a[1]*b[0],
4095 ))
4096 });
4097
4098 define(interp, "vec3_length", Some(1), |_, args| {
4100 let v = extract_vec3(&args[0], "vec3_length")?;
4101 Ok(Value::Float((v[0]*v[0] + v[1]*v[1] + v[2]*v[2]).sqrt()))
4102 });
4103
4104 define(interp, "vec3_normalize", Some(1), |_, args| {
4106 let v = extract_vec3(&args[0], "vec3_normalize")?;
4107 let len = (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]).sqrt();
4108 if len < 1e-10 {
4109 return Ok(make_vec3(0.0, 0.0, 0.0));
4110 }
4111 Ok(make_vec3(v[0]/len, v[1]/len, v[2]/len))
4112 });
4113
4114 define(interp, "vec3_lerp", Some(3), |_, args| {
4116 let a = extract_vec3(&args[0], "vec3_lerp")?;
4117 let b = extract_vec3(&args[1], "vec3_lerp")?;
4118 let t = extract_number(&args[2], "vec3_lerp")?;
4119 Ok(make_vec3(
4120 a[0] + t * (b[0] - a[0]),
4121 a[1] + t * (b[1] - a[1]),
4122 a[2] + t * (b[2] - a[2]),
4123 ))
4124 });
4125
4126 define(interp, "vec3_reflect", Some(2), |_, args| {
4128 let i = extract_vec3(&args[0], "vec3_reflect")?;
4129 let n = extract_vec3(&args[1], "vec3_reflect")?;
4130 let dot = i[0]*n[0] + i[1]*n[1] + i[2]*n[2];
4131 Ok(make_vec3(
4132 i[0] - 2.0 * dot * n[0],
4133 i[1] - 2.0 * dot * n[1],
4134 i[2] - 2.0 * dot * n[2],
4135 ))
4136 });
4137
4138 define(interp, "vec3_refract", Some(3), |_, args| {
4140 let i = extract_vec3(&args[0], "vec3_refract")?;
4141 let n = extract_vec3(&args[1], "vec3_refract")?;
4142 let eta = extract_number(&args[2], "vec3_refract")?;
4143
4144 let dot = i[0]*n[0] + i[1]*n[1] + i[2]*n[2];
4145 let k = 1.0 - eta * eta * (1.0 - dot * dot);
4146
4147 if k < 0.0 {
4148 return Ok(make_vec3(0.0, 0.0, 0.0));
4150 }
4151
4152 let coeff = eta * dot + k.sqrt();
4153 Ok(make_vec3(
4154 eta * i[0] - coeff * n[0],
4155 eta * i[1] - coeff * n[1],
4156 eta * i[2] - coeff * n[2],
4157 ))
4158 });
4159
4160 define(interp, "vec4_dot", Some(2), |_, args| {
4162 let a = extract_vec4(&args[0], "vec4_dot")?;
4163 let b = extract_vec4(&args[1], "vec4_dot")?;
4164 Ok(Value::Float(a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]))
4165 });
4166
4167 define(interp, "mat4_identity", Some(0), |_, _| {
4173 Ok(make_mat4([
4174 1.0, 0.0, 0.0, 0.0,
4175 0.0, 1.0, 0.0, 0.0,
4176 0.0, 0.0, 1.0, 0.0,
4177 0.0, 0.0, 0.0, 1.0,
4178 ]))
4179 });
4180
4181 define(interp, "mat4_mul", Some(2), |_, args| {
4183 let a = extract_mat4(&args[0], "mat4_mul")?;
4184 let b = extract_mat4(&args[1], "mat4_mul")?;
4185
4186 let mut result = [0.0f64; 16];
4187 for col in 0..4 {
4188 for row in 0..4 {
4189 let mut sum = 0.0;
4190 for k in 0..4 {
4191 sum += a[k * 4 + row] * b[col * 4 + k];
4192 }
4193 result[col * 4 + row] = sum;
4194 }
4195 }
4196 Ok(make_mat4(result))
4197 });
4198
4199 define(interp, "mat4_transform", Some(2), |_, args| {
4201 let m = extract_mat4(&args[0], "mat4_transform")?;
4202 let v = extract_vec4(&args[1], "mat4_transform")?;
4203
4204 Ok(make_vec4(
4205 m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3],
4206 m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3],
4207 m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3],
4208 m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3],
4209 ))
4210 });
4211
4212 define(interp, "mat4_translate", Some(3), |_, args| {
4214 let tx = extract_number(&args[0], "mat4_translate")?;
4215 let ty = extract_number(&args[1], "mat4_translate")?;
4216 let tz = extract_number(&args[2], "mat4_translate")?;
4217 Ok(make_mat4([
4218 1.0, 0.0, 0.0, 0.0,
4219 0.0, 1.0, 0.0, 0.0,
4220 0.0, 0.0, 1.0, 0.0,
4221 tx, ty, tz, 1.0,
4222 ]))
4223 });
4224
4225 define(interp, "mat4_scale", Some(3), |_, args| {
4227 let sx = extract_number(&args[0], "mat4_scale")?;
4228 let sy = extract_number(&args[1], "mat4_scale")?;
4229 let sz = extract_number(&args[2], "mat4_scale")?;
4230 Ok(make_mat4([
4231 sx, 0.0, 0.0, 0.0,
4232 0.0, sy, 0.0, 0.0,
4233 0.0, 0.0, sz, 0.0,
4234 0.0, 0.0, 0.0, 1.0,
4235 ]))
4236 });
4237
4238 define(interp, "mat4_rotate_x", Some(1), |_, args| {
4240 let angle = extract_number(&args[0], "mat4_rotate_x")?;
4241 let (s, c) = angle.sin_cos();
4242 Ok(make_mat4([
4243 1.0, 0.0, 0.0, 0.0,
4244 0.0, c, s, 0.0,
4245 0.0, -s, c, 0.0,
4246 0.0, 0.0, 0.0, 1.0,
4247 ]))
4248 });
4249
4250 define(interp, "mat4_rotate_y", Some(1), |_, args| {
4252 let angle = extract_number(&args[0], "mat4_rotate_y")?;
4253 let (s, c) = angle.sin_cos();
4254 Ok(make_mat4([
4255 c, 0.0, -s, 0.0,
4256 0.0, 1.0, 0.0, 0.0,
4257 s, 0.0, c, 0.0,
4258 0.0, 0.0, 0.0, 1.0,
4259 ]))
4260 });
4261
4262 define(interp, "mat4_rotate_z", Some(1), |_, args| {
4264 let angle = extract_number(&args[0], "mat4_rotate_z")?;
4265 let (s, c) = angle.sin_cos();
4266 Ok(make_mat4([
4267 c, s, 0.0, 0.0,
4268 -s, c, 0.0, 0.0,
4269 0.0, 0.0, 1.0, 0.0,
4270 0.0, 0.0, 0.0, 1.0,
4271 ]))
4272 });
4273
4274 define(interp, "mat4_perspective", Some(4), |_, args| {
4276 let fov_y = extract_number(&args[0], "mat4_perspective")?;
4277 let aspect = extract_number(&args[1], "mat4_perspective")?;
4278 let near = extract_number(&args[2], "mat4_perspective")?;
4279 let far = extract_number(&args[3], "mat4_perspective")?;
4280
4281 let f = 1.0 / (fov_y / 2.0).tan();
4282 let nf = 1.0 / (near - far);
4283
4284 Ok(make_mat4([
4285 f / aspect, 0.0, 0.0, 0.0,
4286 0.0, f, 0.0, 0.0,
4287 0.0, 0.0, (far + near) * nf, -1.0,
4288 0.0, 0.0, 2.0 * far * near * nf, 0.0,
4289 ]))
4290 });
4291
4292 define(interp, "mat4_ortho", Some(6), |_, args| {
4294 let left = extract_number(&args[0], "mat4_ortho")?;
4295 let right = extract_number(&args[1], "mat4_ortho")?;
4296 let bottom = extract_number(&args[2], "mat4_ortho")?;
4297 let top = extract_number(&args[3], "mat4_ortho")?;
4298 let near = extract_number(&args[4], "mat4_ortho")?;
4299 let far = extract_number(&args[5], "mat4_ortho")?;
4300
4301 let lr = 1.0 / (left - right);
4302 let bt = 1.0 / (bottom - top);
4303 let nf = 1.0 / (near - far);
4304
4305 Ok(make_mat4([
4306 -2.0 * lr, 0.0, 0.0, 0.0,
4307 0.0, -2.0 * bt, 0.0, 0.0,
4308 0.0, 0.0, 2.0 * nf, 0.0,
4309 (left + right) * lr, (top + bottom) * bt, (far + near) * nf, 1.0,
4310 ]))
4311 });
4312
4313 define(interp, "mat4_look_at", Some(3), |_, args| {
4315 let eye = extract_vec3(&args[0], "mat4_look_at")?;
4316 let center = extract_vec3(&args[1], "mat4_look_at")?;
4317 let up = extract_vec3(&args[2], "mat4_look_at")?;
4318
4319 let fx = center[0] - eye[0];
4321 let fy = center[1] - eye[1];
4322 let fz = center[2] - eye[2];
4323 let flen = (fx*fx + fy*fy + fz*fz).sqrt();
4324 let (fx, fy, fz) = (fx/flen, fy/flen, fz/flen);
4325
4326 let rx = fy * up[2] - fz * up[1];
4328 let ry = fz * up[0] - fx * up[2];
4329 let rz = fx * up[1] - fy * up[0];
4330 let rlen = (rx*rx + ry*ry + rz*rz).sqrt();
4331 let (rx, ry, rz) = (rx/rlen, ry/rlen, rz/rlen);
4332
4333 let ux = ry * fz - rz * fy;
4335 let uy = rz * fx - rx * fz;
4336 let uz = rx * fy - ry * fx;
4337
4338 Ok(make_mat4([
4339 rx, ux, -fx, 0.0,
4340 ry, uy, -fy, 0.0,
4341 rz, uz, -fz, 0.0,
4342 -(rx*eye[0] + ry*eye[1] + rz*eye[2]),
4343 -(ux*eye[0] + uy*eye[1] + uz*eye[2]),
4344 -(-fx*eye[0] - fy*eye[1] - fz*eye[2]),
4345 1.0,
4346 ]))
4347 });
4348
4349 define(interp, "mat4_inverse", Some(1), |_, args| {
4351 let m = extract_mat4(&args[0], "mat4_inverse")?;
4352
4353 let a00 = m[0]; let a01 = m[1]; let a02 = m[2]; let a03 = m[3];
4355 let a10 = m[4]; let a11 = m[5]; let a12 = m[6]; let a13 = m[7];
4356 let a20 = m[8]; let a21 = m[9]; let a22 = m[10]; let a23 = m[11];
4357 let a30 = m[12]; let a31 = m[13]; let a32 = m[14]; let a33 = m[15];
4358
4359 let b00 = a00 * a11 - a01 * a10;
4360 let b01 = a00 * a12 - a02 * a10;
4361 let b02 = a00 * a13 - a03 * a10;
4362 let b03 = a01 * a12 - a02 * a11;
4363 let b04 = a01 * a13 - a03 * a11;
4364 let b05 = a02 * a13 - a03 * a12;
4365 let b06 = a20 * a31 - a21 * a30;
4366 let b07 = a20 * a32 - a22 * a30;
4367 let b08 = a20 * a33 - a23 * a30;
4368 let b09 = a21 * a32 - a22 * a31;
4369 let b10 = a21 * a33 - a23 * a31;
4370 let b11 = a22 * a33 - a23 * a32;
4371
4372 let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
4373
4374 if det.abs() < 1e-10 {
4375 return Err(RuntimeError::new("mat4_inverse: singular matrix"));
4376 }
4377
4378 let inv_det = 1.0 / det;
4379
4380 Ok(make_mat4([
4381 (a11 * b11 - a12 * b10 + a13 * b09) * inv_det,
4382 (a02 * b10 - a01 * b11 - a03 * b09) * inv_det,
4383 (a31 * b05 - a32 * b04 + a33 * b03) * inv_det,
4384 (a22 * b04 - a21 * b05 - a23 * b03) * inv_det,
4385 (a12 * b08 - a10 * b11 - a13 * b07) * inv_det,
4386 (a00 * b11 - a02 * b08 + a03 * b07) * inv_det,
4387 (a32 * b02 - a30 * b05 - a33 * b01) * inv_det,
4388 (a20 * b05 - a22 * b02 + a23 * b01) * inv_det,
4389 (a10 * b10 - a11 * b08 + a13 * b06) * inv_det,
4390 (a01 * b08 - a00 * b10 - a03 * b06) * inv_det,
4391 (a30 * b04 - a31 * b02 + a33 * b00) * inv_det,
4392 (a21 * b02 - a20 * b04 - a23 * b00) * inv_det,
4393 (a11 * b07 - a10 * b09 - a12 * b06) * inv_det,
4394 (a00 * b09 - a01 * b07 + a02 * b06) * inv_det,
4395 (a31 * b01 - a30 * b03 - a32 * b00) * inv_det,
4396 (a20 * b03 - a21 * b01 + a22 * b00) * inv_det,
4397 ]))
4398 });
4399
4400 define(interp, "mat4_transpose", Some(1), |_, args| {
4402 let m = extract_mat4(&args[0], "mat4_transpose")?;
4403 Ok(make_mat4([
4404 m[0], m[4], m[8], m[12],
4405 m[1], m[5], m[9], m[13],
4406 m[2], m[6], m[10], m[14],
4407 m[3], m[7], m[11], m[15],
4408 ]))
4409 });
4410
4411 define(interp, "mat3_identity", Some(0), |_, _| {
4417 Ok(make_mat3([
4418 1.0, 0.0, 0.0,
4419 0.0, 1.0, 0.0,
4420 0.0, 0.0, 1.0,
4421 ]))
4422 });
4423
4424 define(interp, "mat3_from_mat4", Some(1), |_, args| {
4426 let m = extract_mat4(&args[0], "mat3_from_mat4")?;
4427 Ok(make_mat3([
4428 m[0], m[1], m[2],
4429 m[4], m[5], m[6],
4430 m[8], m[9], m[10],
4431 ]))
4432 });
4433
4434 define(interp, "mat3_mul", Some(2), |_, args| {
4436 let a = extract_mat3(&args[0], "mat3_mul")?;
4437 let b = extract_mat3(&args[1], "mat3_mul")?;
4438
4439 let mut result = [0.0f64; 9];
4440 for col in 0..3 {
4441 for row in 0..3 {
4442 let mut sum = 0.0;
4443 for k in 0..3 {
4444 sum += a[k * 3 + row] * b[col * 3 + k];
4445 }
4446 result[col * 3 + row] = sum;
4447 }
4448 }
4449 Ok(make_mat3(result))
4450 });
4451
4452 define(interp, "mat3_transform", Some(2), |_, args| {
4454 let m = extract_mat3(&args[0], "mat3_transform")?;
4455 let v = extract_vec3(&args[1], "mat3_transform")?;
4456
4457 Ok(make_vec3(
4458 m[0]*v[0] + m[3]*v[1] + m[6]*v[2],
4459 m[1]*v[0] + m[4]*v[1] + m[7]*v[2],
4460 m[2]*v[0] + m[5]*v[1] + m[8]*v[2],
4461 ))
4462 });
4463
4464 define(interp, "mat3_inverse", Some(1), |_, args| {
4466 let m = extract_mat3(&args[0], "mat3_inverse")?;
4467
4468 let det = m[0] * (m[4] * m[8] - m[5] * m[7])
4469 - m[3] * (m[1] * m[8] - m[2] * m[7])
4470 + m[6] * (m[1] * m[5] - m[2] * m[4]);
4471
4472 if det.abs() < 1e-10 {
4473 return Err(RuntimeError::new("mat3_inverse: singular matrix"));
4474 }
4475
4476 let inv_det = 1.0 / det;
4477
4478 Ok(make_mat3([
4479 (m[4] * m[8] - m[5] * m[7]) * inv_det,
4480 (m[2] * m[7] - m[1] * m[8]) * inv_det,
4481 (m[1] * m[5] - m[2] * m[4]) * inv_det,
4482 (m[5] * m[6] - m[3] * m[8]) * inv_det,
4483 (m[0] * m[8] - m[2] * m[6]) * inv_det,
4484 (m[2] * m[3] - m[0] * m[5]) * inv_det,
4485 (m[3] * m[7] - m[4] * m[6]) * inv_det,
4486 (m[1] * m[6] - m[0] * m[7]) * inv_det,
4487 (m[0] * m[4] - m[1] * m[3]) * inv_det,
4488 ]))
4489 });
4490
4491 define(interp, "mat3_transpose", Some(1), |_, args| {
4493 let m = extract_mat3(&args[0], "mat3_transpose")?;
4494 Ok(make_mat3([
4495 m[0], m[3], m[6],
4496 m[1], m[4], m[7],
4497 m[2], m[5], m[8],
4498 ]))
4499 });
4500}
4501
4502fn extract_number(v: &Value, fn_name: &str) -> Result<f64, RuntimeError> {
4504 match v {
4505 Value::Float(f) => Ok(*f),
4506 Value::Int(i) => Ok(*i as f64),
4507 _ => Err(RuntimeError::new(format!("{}() requires number argument", fn_name))),
4508 }
4509}
4510
4511fn extract_vec2(v: &Value, fn_name: &str) -> Result<[f64; 2], RuntimeError> {
4512 match v {
4513 Value::Array(arr) => {
4514 let arr = arr.borrow();
4515 if arr.len() < 2 {
4516 return Err(RuntimeError::new(format!("{}() requires vec2 (2 elements)", fn_name)));
4517 }
4518 Ok([
4519 extract_number(&arr[0], fn_name)?,
4520 extract_number(&arr[1], fn_name)?,
4521 ])
4522 }
4523 _ => Err(RuntimeError::new(format!("{}() requires vec2 array", fn_name))),
4524 }
4525}
4526
4527fn extract_vec3(v: &Value, fn_name: &str) -> Result<[f64; 3], RuntimeError> {
4528 match v {
4529 Value::Array(arr) => {
4530 let arr = arr.borrow();
4531 if arr.len() < 3 {
4532 return Err(RuntimeError::new(format!("{}() requires vec3 (3 elements)", fn_name)));
4533 }
4534 Ok([
4535 extract_number(&arr[0], fn_name)?,
4536 extract_number(&arr[1], fn_name)?,
4537 extract_number(&arr[2], fn_name)?,
4538 ])
4539 }
4540 _ => Err(RuntimeError::new(format!("{}() requires vec3 array", fn_name))),
4541 }
4542}
4543
4544fn extract_vec4(v: &Value, fn_name: &str) -> Result<[f64; 4], RuntimeError> {
4545 match v {
4546 Value::Array(arr) => {
4547 let arr = arr.borrow();
4548 if arr.len() < 4 {
4549 return Err(RuntimeError::new(format!("{}() requires vec4 (4 elements)", fn_name)));
4550 }
4551 Ok([
4552 extract_number(&arr[0], fn_name)?,
4553 extract_number(&arr[1], fn_name)?,
4554 extract_number(&arr[2], fn_name)?,
4555 extract_number(&arr[3], fn_name)?,
4556 ])
4557 }
4558 _ => Err(RuntimeError::new(format!("{}() requires vec4 array", fn_name))),
4559 }
4560}
4561
4562fn extract_mat3(v: &Value, fn_name: &str) -> Result<[f64; 9], RuntimeError> {
4563 match v {
4564 Value::Array(arr) => {
4565 let arr = arr.borrow();
4566 if arr.len() < 9 {
4567 return Err(RuntimeError::new(format!("{}() requires mat3 (9 elements)", fn_name)));
4568 }
4569 let mut result = [0.0f64; 9];
4570 for i in 0..9 {
4571 result[i] = extract_number(&arr[i], fn_name)?;
4572 }
4573 Ok(result)
4574 }
4575 _ => Err(RuntimeError::new(format!("{}() requires mat3 array", fn_name))),
4576 }
4577}
4578
4579fn extract_mat4(v: &Value, fn_name: &str) -> Result<[f64; 16], RuntimeError> {
4580 match v {
4581 Value::Array(arr) => {
4582 let arr = arr.borrow();
4583 if arr.len() < 16 {
4584 return Err(RuntimeError::new(format!("{}() requires mat4 (16 elements)", fn_name)));
4585 }
4586 let mut result = [0.0f64; 16];
4587 for i in 0..16 {
4588 result[i] = extract_number(&arr[i], fn_name)?;
4589 }
4590 Ok(result)
4591 }
4592 _ => Err(RuntimeError::new(format!("{}() requires mat4 array", fn_name))),
4593 }
4594}
4595
4596fn make_vec2(x: f64, y: f64) -> Value {
4597 Value::Array(Rc::new(RefCell::new(vec![
4598 Value::Float(x), Value::Float(y),
4599 ])))
4600}
4601
4602fn make_vec3(x: f64, y: f64, z: f64) -> Value {
4603 Value::Array(Rc::new(RefCell::new(vec![
4604 Value::Float(x), Value::Float(y), Value::Float(z),
4605 ])))
4606}
4607
4608fn make_vec3_arr(v: [f64; 3]) -> Value {
4610 make_vec3(v[0], v[1], v[2])
4611}
4612
4613fn make_vec4(x: f64, y: f64, z: f64, w: f64) -> Value {
4614 Value::Array(Rc::new(RefCell::new(vec![
4615 Value::Float(x), Value::Float(y), Value::Float(z), Value::Float(w),
4616 ])))
4617}
4618
4619fn make_mat3(m: [f64; 9]) -> Value {
4620 Value::Array(Rc::new(RefCell::new(
4621 m.iter().map(|&v| Value::Float(v)).collect()
4622 )))
4623}
4624
4625fn make_mat4(m: [f64; 16]) -> Value {
4626 Value::Array(Rc::new(RefCell::new(
4627 m.iter().map(|&v| Value::Float(v)).collect()
4628 )))
4629}
4630
4631fn register_concurrency(interp: &mut Interpreter) {
4648 define(interp, "channel_new", Some(0), |_, _| {
4652 let (sender, receiver) = mpsc::channel();
4653 let inner = ChannelInner {
4654 sender: Mutex::new(sender),
4655 receiver: Mutex::new(receiver),
4656 };
4657 Ok(Value::Channel(Arc::new(inner)))
4658 });
4659
4660 define(interp, "channel_send", Some(2), |_, args| {
4662 let channel = match &args[0] {
4663 Value::Channel(ch) => ch.clone(),
4664 _ => return Err(RuntimeError::new("channel_send() requires channel as first argument")),
4665 };
4666 let value = args[1].clone();
4667
4668 let sender = channel.sender.lock().map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
4669 sender.send(value).map_err(|_| RuntimeError::new("channel_send() failed: receiver dropped"))?;
4670
4671 Ok(Value::Null)
4672 });
4673
4674 define(interp, "channel_recv", Some(1), |_, args| {
4676 let channel = match &args[0] {
4677 Value::Channel(ch) => ch.clone(),
4678 _ => return Err(RuntimeError::new("channel_recv() requires channel argument")),
4679 };
4680
4681 let receiver = channel.receiver.lock().map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
4682 match receiver.recv() {
4683 Ok(value) => Ok(value),
4684 Err(_) => Err(RuntimeError::new("channel_recv() failed: sender dropped")),
4685 }
4686 });
4687
4688 define(interp, "channel_try_recv", Some(1), |_, args| {
4690 let channel = match &args[0] {
4691 Value::Channel(ch) => ch.clone(),
4692 _ => return Err(RuntimeError::new("channel_try_recv() requires channel argument")),
4693 };
4694
4695 let receiver = channel.receiver.lock().map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
4696 match receiver.try_recv() {
4697 Ok(value) => {
4698 Ok(Value::Variant {
4700 enum_name: "Option".to_string(),
4701 variant_name: "Some".to_string(),
4702 fields: Some(Rc::new(vec![value])),
4703 })
4704 }
4705 Err(mpsc::TryRecvError::Empty) => {
4706 Ok(Value::Variant {
4708 enum_name: "Option".to_string(),
4709 variant_name: "None".to_string(),
4710 fields: None,
4711 })
4712 }
4713 Err(mpsc::TryRecvError::Disconnected) => {
4714 Err(RuntimeError::new("channel_try_recv() failed: sender dropped"))
4715 }
4716 }
4717 });
4718
4719 define(interp, "channel_recv_timeout", Some(2), |_, args| {
4721 let channel = match &args[0] {
4722 Value::Channel(ch) => ch.clone(),
4723 _ => return Err(RuntimeError::new(
4724 "channel_recv_timeout() requires a channel as first argument.\n\
4725 Create a channel with channel_new():\n\
4726 let ch = channel_new();\n\
4727 channel_send(ch, value);\n\
4728 let result = channel_recv_timeout(ch, 1000); // 1 second timeout"
4729 )),
4730 };
4731 let timeout_ms = match &args[1] {
4732 Value::Int(ms) => *ms as u64,
4733 _ => return Err(RuntimeError::new(
4734 "channel_recv_timeout() requires timeout in milliseconds (integer).\n\
4735 Example: channel_recv_timeout(ch, 1000) // Wait up to 1 second"
4736 )),
4737 };
4738
4739 let receiver = channel.receiver.lock().map_err(|_| RuntimeError::new("channel mutex poisoned"))?;
4740 match receiver.recv_timeout(std::time::Duration::from_millis(timeout_ms)) {
4741 Ok(value) => Ok(Value::Variant {
4742 enum_name: "Option".to_string(),
4743 variant_name: "Some".to_string(),
4744 fields: Some(Rc::new(vec![value])),
4745 }),
4746 Err(mpsc::RecvTimeoutError::Timeout) => Ok(Value::Variant {
4747 enum_name: "Option".to_string(),
4748 variant_name: "None".to_string(),
4749 fields: None,
4750 }),
4751 Err(mpsc::RecvTimeoutError::Disconnected) => {
4752 Err(RuntimeError::new("channel_recv_timeout() failed: sender dropped"))
4753 }
4754 }
4755 });
4756
4757 define(interp, "thread_spawn_detached", Some(0), |_, _| {
4765 thread::spawn(|| {
4768 });
4770 Ok(Value::Null)
4771 });
4772
4773 define(interp, "thread_join", Some(1), |_, args| {
4776 match &args[0] {
4777 Value::ThreadHandle(h) => {
4778 let mut guard = h.lock().map_err(|_| RuntimeError::new("thread handle mutex poisoned"))?;
4779 if let Some(handle) = guard.take() {
4780 match handle.join() {
4781 Ok(v) => Ok(v),
4782 Err(_) => Err(RuntimeError::new("thread panicked")),
4783 }
4784 } else {
4785 Err(RuntimeError::new("thread already joined"))
4786 }
4787 }
4788 _ => Ok(args[0].clone()),
4790 }
4791 });
4792
4793 define(interp, "thread_sleep", Some(1), |_, args| {
4795 let ms = match &args[0] {
4796 Value::Int(ms) => *ms as u64,
4797 Value::Float(ms) => *ms as u64,
4798 _ => return Err(RuntimeError::new("thread_sleep() requires integer milliseconds")),
4799 };
4800
4801 thread::sleep(std::time::Duration::from_millis(ms));
4802 Ok(Value::Null)
4803 });
4804
4805 define(interp, "thread_yield", Some(0), |_, _| {
4807 thread::yield_now();
4808 Ok(Value::Null)
4809 });
4810
4811 define(interp, "thread_id", Some(0), |_, _| {
4813 let id = thread::current().id();
4814 Ok(Value::String(Rc::new(format!("{:?}", id))))
4815 });
4816
4817 define(interp, "spawn_actor", Some(1), |_, args| {
4824 let name = match &args[0] {
4825 Value::String(s) => s.to_string(),
4826 _ => return Err(RuntimeError::new("actor_spawn() requires string name")),
4827 };
4828
4829 let inner = ActorInner {
4830 name,
4831 message_queue: Mutex::new(Vec::new()),
4832 message_count: std::sync::atomic::AtomicUsize::new(0),
4833 };
4834
4835 Ok(Value::Actor(Arc::new(inner)))
4836 });
4837
4838 define(interp, "send_to_actor", Some(3), |_, args| {
4841 let actor = match &args[0] {
4842 Value::Actor(a) => a.clone(),
4843 _ => return Err(RuntimeError::new("actor_send() requires actor as first argument")),
4844 };
4845 let msg_type = match &args[1] {
4846 Value::String(s) => s.to_string(),
4847 _ => return Err(RuntimeError::new("actor_send() requires string message type")),
4848 };
4849 let msg_data = format!("{}", args[2]);
4850
4851 let mut queue = actor.message_queue.lock().map_err(|_| RuntimeError::new("actor queue poisoned"))?;
4852 queue.push((msg_type, msg_data));
4853 actor.message_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
4854
4855 Ok(Value::Null)
4856 });
4857
4858 define(interp, "tell_actor", Some(3), |_, args| {
4860 let actor = match &args[0] {
4861 Value::Actor(a) => a.clone(),
4862 _ => return Err(RuntimeError::new("actor_tell() requires actor as first argument")),
4863 };
4864 let msg_type = match &args[1] {
4865 Value::String(s) => s.to_string(),
4866 _ => return Err(RuntimeError::new("actor_tell() requires string message type")),
4867 };
4868 let msg_data = format!("{}", args[2]);
4869
4870 let mut queue = actor.message_queue.lock().map_err(|_| RuntimeError::new("actor queue poisoned"))?;
4871 queue.push((msg_type, msg_data));
4872 actor.message_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
4873
4874 Ok(Value::Null)
4875 });
4876
4877 define(interp, "recv_from_actor", Some(1), |_, args| {
4880 let actor = match &args[0] {
4881 Value::Actor(a) => a.clone(),
4882 _ => return Err(RuntimeError::new("actor_recv() requires actor argument")),
4883 };
4884
4885 let mut queue = actor.message_queue.lock().map_err(|_| RuntimeError::new("actor queue poisoned"))?;
4886 match queue.pop() {
4887 Some((msg_type, msg_data)) => {
4888 Ok(Value::Variant {
4890 enum_name: "Option".to_string(),
4891 variant_name: "Some".to_string(),
4892 fields: Some(Rc::new(vec![
4893 Value::Tuple(Rc::new(vec![
4894 Value::String(Rc::new(msg_type)),
4895 Value::String(Rc::new(msg_data)),
4896 ]))
4897 ])),
4898 })
4899 }
4900 None => Ok(Value::Variant {
4901 enum_name: "Option".to_string(),
4902 variant_name: "None".to_string(),
4903 fields: None,
4904 }),
4905 }
4906 });
4907
4908 define(interp, "get_actor_msg_count", Some(1), |_, args| {
4910 let a = match &args[0] {
4911 Value::Actor(a) => a.clone(),
4912 _ => return Err(RuntimeError::new("get_actor_msg_count() requires actor argument")),
4913 };
4914
4915 let count = a.message_count.load(std::sync::atomic::Ordering::SeqCst);
4916 Ok(Value::Int(count as i64))
4917 });
4918
4919 define(interp, "get_actor_name", Some(1), |_, args| {
4921 let a = match &args[0] {
4922 Value::Actor(a) => a.clone(),
4923 _ => return Err(RuntimeError::new("get_actor_name() requires actor argument")),
4924 };
4925
4926 Ok(Value::String(Rc::new(a.name.clone())))
4927 });
4928
4929 define(interp, "get_actor_pending", Some(1), |_, args| {
4931 let a = match &args[0] {
4932 Value::Actor(a) => a.clone(),
4933 _ => return Err(RuntimeError::new("get_actor_pending() requires actor argument")),
4934 };
4935
4936 let queue = a.message_queue.lock().map_err(|_| RuntimeError::new("actor queue poisoned"))?;
4937 Ok(Value::Int(queue.len() as i64))
4938 });
4939
4940 define(interp, "mutex_new", Some(1), |_, args| {
4944 let value = args[0].clone();
4945 let mut map = std::collections::HashMap::new();
4947 map.insert("__mutex_value".to_string(), value);
4948 map.insert("__mutex_locked".to_string(), Value::Bool(false));
4949 Ok(Value::Map(Rc::new(RefCell::new(map))))
4950 });
4951
4952 define(interp, "mutex_lock", Some(1), |_, args| {
4954 let mutex = match &args[0] {
4955 Value::Map(m) => m.clone(),
4956 _ => return Err(RuntimeError::new("mutex_lock() requires mutex")),
4957 };
4958
4959 let mut map = mutex.borrow_mut();
4960 map.insert("__mutex_locked".to_string(), Value::Bool(true));
4962
4963 match map.get("__mutex_value") {
4964 Some(v) => Ok(v.clone()),
4965 None => Err(RuntimeError::new("invalid mutex")),
4966 }
4967 });
4968
4969 define(interp, "mutex_unlock", Some(2), |_, args| {
4971 let mutex = match &args[0] {
4972 Value::Map(m) => m.clone(),
4973 _ => return Err(RuntimeError::new("mutex_unlock() requires mutex")),
4974 };
4975 let new_value = args[1].clone();
4976
4977 let mut map = mutex.borrow_mut();
4978 map.insert("__mutex_value".to_string(), new_value);
4979 map.insert("__mutex_locked".to_string(), Value::Bool(false));
4980
4981 Ok(Value::Null)
4982 });
4983
4984 define(interp, "atomic_new", Some(1), |_, args| {
4986 let value = match &args[0] {
4987 Value::Int(i) => *i,
4988 _ => return Err(RuntimeError::new("atomic_new() requires integer")),
4989 };
4990
4991 let mut map = std::collections::HashMap::new();
4993 map.insert("__atomic_value".to_string(), Value::Int(value));
4994 Ok(Value::Map(Rc::new(RefCell::new(map))))
4995 });
4996
4997 define(interp, "atomic_load", Some(1), |_, args| {
4999 let atomic = match &args[0] {
5000 Value::Map(m) => m.clone(),
5001 _ => return Err(RuntimeError::new("atomic_load() requires atomic")),
5002 };
5003
5004 let map = atomic.borrow();
5005 match map.get("__atomic_value") {
5006 Some(v) => Ok(v.clone()),
5007 None => Err(RuntimeError::new("invalid atomic")),
5008 }
5009 });
5010
5011 define(interp, "atomic_store", Some(2), |_, args| {
5013 let atomic = match &args[0] {
5014 Value::Map(m) => m.clone(),
5015 _ => return Err(RuntimeError::new("atomic_store() requires atomic")),
5016 };
5017 let value = match &args[1] {
5018 Value::Int(i) => *i,
5019 _ => return Err(RuntimeError::new("atomic_store() requires integer value")),
5020 };
5021
5022 let mut map = atomic.borrow_mut();
5023 map.insert("__atomic_value".to_string(), Value::Int(value));
5024 Ok(Value::Null)
5025 });
5026
5027 define(interp, "atomic_add", Some(2), |_, args| {
5029 let atomic = match &args[0] {
5030 Value::Map(m) => m.clone(),
5031 _ => return Err(RuntimeError::new("atomic_add() requires atomic")),
5032 };
5033 let delta = match &args[1] {
5034 Value::Int(i) => *i,
5035 _ => return Err(RuntimeError::new("atomic_add() requires integer delta")),
5036 };
5037
5038 let mut map = atomic.borrow_mut();
5039 let old = match map.get("__atomic_value") {
5040 Some(Value::Int(i)) => *i,
5041 _ => return Err(RuntimeError::new("invalid atomic")),
5042 };
5043 map.insert("__atomic_value".to_string(), Value::Int(old + delta));
5044 Ok(Value::Int(old))
5045 });
5046
5047 define(interp, "atomic_cas", Some(3), |_, args| {
5049 let atomic = match &args[0] {
5050 Value::Map(m) => m.clone(),
5051 _ => return Err(RuntimeError::new("atomic_cas() requires atomic")),
5052 };
5053 let expected = match &args[1] {
5054 Value::Int(i) => *i,
5055 _ => return Err(RuntimeError::new("atomic_cas() requires integer expected")),
5056 };
5057 let new_value = match &args[2] {
5058 Value::Int(i) => *i,
5059 _ => return Err(RuntimeError::new("atomic_cas() requires integer new value")),
5060 };
5061
5062 let mut map = atomic.borrow_mut();
5063 let current = match map.get("__atomic_value") {
5064 Some(Value::Int(i)) => *i,
5065 _ => return Err(RuntimeError::new("invalid atomic")),
5066 };
5067
5068 if current == expected {
5069 map.insert("__atomic_value".to_string(), Value::Int(new_value));
5070 Ok(Value::Bool(true))
5071 } else {
5072 Ok(Value::Bool(false))
5073 }
5074 });
5075
5076 define(interp, "parallel_map", Some(2), |_, args| {
5080 let arr = match &args[0] {
5081 Value::Array(a) => a.borrow().clone(),
5082 _ => return Err(RuntimeError::new("parallel_map() requires array")),
5083 };
5084 let _func = args[1].clone();
5085
5086 Ok(Value::Array(Rc::new(RefCell::new(arr))))
5089 });
5090
5091 define(interp, "parallel_for", Some(3), |_, args| {
5093 let start = match &args[0] {
5094 Value::Int(i) => *i,
5095 _ => return Err(RuntimeError::new("parallel_for() requires integer start")),
5096 };
5097 let end = match &args[1] {
5098 Value::Int(i) => *i,
5099 _ => return Err(RuntimeError::new("parallel_for() requires integer end")),
5100 };
5101 let _func = args[2].clone();
5102
5103 let range: Vec<Value> = (start..end).map(|i| Value::Int(i)).collect();
5106 Ok(Value::Array(Rc::new(RefCell::new(range))))
5107 });
5108
5109 define(interp, "async_sleep", Some(1), |interp, args| {
5127 let ms = match &args[0] {
5128 Value::Int(ms) => *ms as u64,
5129 Value::Float(ms) => *ms as u64,
5130 _ => return Err(RuntimeError::new("async_sleep() requires integer milliseconds")),
5131 };
5132
5133 Ok(interp.make_future_timer(std::time::Duration::from_millis(ms)))
5134 });
5135
5136 define(interp, "future_ready", Some(1), |interp, args| {
5138 Ok(interp.make_future_immediate(args[0].clone()))
5139 });
5140
5141 define(interp, "future_pending", Some(0), |_, _| {
5143 Ok(Value::Future(Rc::new(RefCell::new(crate::interpreter::FutureInner {
5144 state: crate::interpreter::FutureState::Pending,
5145 computation: None,
5146 complete_at: None,
5147 }))))
5148 });
5149
5150 define(interp, "is_future", Some(1), |_, args| {
5152 Ok(Value::Bool(matches!(&args[0], Value::Future(_))))
5153 });
5154
5155 define(interp, "is_ready", Some(1), |_, args| {
5157 match &args[0] {
5158 Value::Future(fut) => {
5159 let f = fut.borrow();
5160 Ok(Value::Bool(matches!(f.state, crate::interpreter::FutureState::Ready(_))))
5161 }
5162 _ => Ok(Value::Bool(true)), }
5164 });
5165
5166 define(interp, "join_futures", Some(1), |_, args| {
5168 let futures = match &args[0] {
5169 Value::Array(arr) => {
5170 let arr = arr.borrow();
5171 let mut futs = Vec::new();
5172 for v in arr.iter() {
5173 match v {
5174 Value::Future(f) => futs.push(f.clone()),
5175 _ => return Err(RuntimeError::new("join_futures() requires array of futures")),
5176 }
5177 }
5178 futs
5179 }
5180 _ => return Err(RuntimeError::new("join_futures() requires array of futures")),
5181 };
5182
5183 Ok(Value::Future(Rc::new(RefCell::new(crate::interpreter::FutureInner {
5184 state: crate::interpreter::FutureState::Pending,
5185 computation: Some(crate::interpreter::FutureComputation::Join(futures)),
5186 complete_at: None,
5187 }))))
5188 });
5189
5190 define(interp, "race_futures", Some(1), |_, args| {
5192 let futures = match &args[0] {
5193 Value::Array(arr) => {
5194 let arr = arr.borrow();
5195 let mut futs = Vec::new();
5196 for v in arr.iter() {
5197 match v {
5198 Value::Future(f) => futs.push(f.clone()),
5199 _ => return Err(RuntimeError::new("race_futures() requires array of futures")),
5200 }
5201 }
5202 futs
5203 }
5204 _ => return Err(RuntimeError::new("race_futures() requires array of futures")),
5205 };
5206
5207 Ok(Value::Future(Rc::new(RefCell::new(crate::interpreter::FutureInner {
5208 state: crate::interpreter::FutureState::Pending,
5209 computation: Some(crate::interpreter::FutureComputation::Race(futures)),
5210 complete_at: None,
5211 }))))
5212 });
5213
5214 define(interp, "poll_future", Some(1), |_, args| {
5216 match &args[0] {
5217 Value::Future(fut) => {
5218 let f = fut.borrow();
5219 match &f.state {
5220 crate::interpreter::FutureState::Ready(v) => {
5221 Ok(Value::Variant {
5222 enum_name: "Option".to_string(),
5223 variant_name: "Some".to_string(),
5224 fields: Some(Rc::new(vec![(**v).clone()])),
5225 })
5226 }
5227 _ => {
5228 Ok(Value::Variant {
5229 enum_name: "Option".to_string(),
5230 variant_name: "None".to_string(),
5231 fields: None,
5232 })
5233 }
5234 }
5235 }
5236 other => Ok(Value::Variant {
5238 enum_name: "Option".to_string(),
5239 variant_name: "Some".to_string(),
5240 fields: Some(Rc::new(vec![other.clone()])),
5241 }),
5242 }
5243 });
5244}
5245
5246fn register_json(interp: &mut Interpreter) {
5251 define(interp, "json_parse", Some(1), |_, args| {
5253 let json_str = match &args[0] {
5254 Value::String(s) => s.as_str(),
5255 _ => return Err(RuntimeError::new("json_parse() requires string argument")),
5256 };
5257
5258 fn json_to_value(json: &serde_json::Value) -> Value {
5259 match json {
5260 serde_json::Value::Null => Value::Null,
5261 serde_json::Value::Bool(b) => Value::Bool(*b),
5262 serde_json::Value::Number(n) => {
5263 if let Some(i) = n.as_i64() {
5264 Value::Int(i)
5265 } else if let Some(f) = n.as_f64() {
5266 Value::Float(f)
5267 } else {
5268 Value::Null
5269 }
5270 }
5271 serde_json::Value::String(s) => Value::String(Rc::new(s.clone())),
5272 serde_json::Value::Array(arr) => {
5273 let values: Vec<Value> = arr.iter().map(json_to_value).collect();
5274 Value::Array(Rc::new(RefCell::new(values)))
5275 }
5276 serde_json::Value::Object(obj) => {
5277 let mut map = HashMap::new();
5278 for (k, v) in obj {
5279 map.insert(k.clone(), json_to_value(v));
5280 }
5281 Value::Map(Rc::new(RefCell::new(map)))
5282 }
5283 }
5284 }
5285
5286 match serde_json::from_str(json_str) {
5287 Ok(json) => Ok(json_to_value(&json)),
5288 Err(e) => Err(RuntimeError::new(format!("JSON parse error: {}", e))),
5289 }
5290 });
5291
5292 define(interp, "json_stringify", Some(1), |_, args| {
5294 fn value_to_json(val: &Value) -> serde_json::Value {
5295 match val {
5296 Value::Null => serde_json::Value::Null,
5297 Value::Bool(b) => serde_json::Value::Bool(*b),
5298 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
5299 Value::Float(f) => {
5300 serde_json::Number::from_f64(*f)
5301 .map(serde_json::Value::Number)
5302 .unwrap_or(serde_json::Value::Null)
5303 }
5304 Value::String(s) => serde_json::Value::String(s.to_string()),
5305 Value::Array(arr) => {
5306 let arr = arr.borrow();
5307 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
5308 }
5309 Value::Tuple(t) => {
5310 serde_json::Value::Array(t.iter().map(value_to_json).collect())
5311 }
5312 Value::Map(map) => {
5313 let map = map.borrow();
5314 let obj: serde_json::Map<String, serde_json::Value> = map
5315 .iter()
5316 .map(|(k, v)| (k.clone(), value_to_json(v)))
5317 .collect();
5318 serde_json::Value::Object(obj)
5319 }
5320 Value::Struct { fields, .. } => {
5321 let fields = fields.borrow();
5322 let obj: serde_json::Map<String, serde_json::Value> = fields
5323 .iter()
5324 .map(|(k, v)| (k.clone(), value_to_json(v)))
5325 .collect();
5326 serde_json::Value::Object(obj)
5327 }
5328 _ => serde_json::Value::String(format!("{}", val)),
5329 }
5330 }
5331
5332 let json = value_to_json(&args[0]);
5333 Ok(Value::String(Rc::new(json.to_string())))
5334 });
5335
5336 define(interp, "json_pretty", Some(1), |_, args| {
5338 fn value_to_json(val: &Value) -> serde_json::Value {
5339 match val {
5340 Value::Null => serde_json::Value::Null,
5341 Value::Bool(b) => serde_json::Value::Bool(*b),
5342 Value::Int(n) => serde_json::Value::Number(serde_json::Number::from(*n)),
5343 Value::Float(f) => {
5344 serde_json::Number::from_f64(*f)
5345 .map(serde_json::Value::Number)
5346 .unwrap_or(serde_json::Value::Null)
5347 }
5348 Value::String(s) => serde_json::Value::String(s.to_string()),
5349 Value::Array(arr) => {
5350 let arr = arr.borrow();
5351 serde_json::Value::Array(arr.iter().map(value_to_json).collect())
5352 }
5353 Value::Map(map) => {
5354 let map = map.borrow();
5355 let obj: serde_json::Map<String, serde_json::Value> = map
5356 .iter()
5357 .map(|(k, v)| (k.clone(), value_to_json(v)))
5358 .collect();
5359 serde_json::Value::Object(obj)
5360 }
5361 _ => serde_json::Value::String(format!("{}", val)),
5362 }
5363 }
5364
5365 let json = value_to_json(&args[0]);
5366 match serde_json::to_string_pretty(&json) {
5367 Ok(s) => Ok(Value::String(Rc::new(s))),
5368 Err(e) => Err(RuntimeError::new(format!("JSON stringify error: {}", e))),
5369 }
5370 });
5371
5372 define(interp, "json_get", Some(2), |_, args| {
5374 let path = match &args[1] {
5375 Value::String(s) => s.to_string(),
5376 _ => return Err(RuntimeError::new("json_get() requires string path")),
5377 };
5378
5379 let mut current = args[0].clone();
5380 for key in path.split('.') {
5381 current = match ¤t {
5382 Value::Map(map) => {
5383 let map = map.borrow();
5384 map.get(key).cloned().unwrap_or(Value::Null)
5385 }
5386 Value::Array(arr) => {
5387 if let Ok(idx) = key.parse::<usize>() {
5388 let arr = arr.borrow();
5389 arr.get(idx).cloned().unwrap_or(Value::Null)
5390 } else {
5391 Value::Null
5392 }
5393 }
5394 _ => Value::Null,
5395 };
5396 }
5397 Ok(current)
5398 });
5399
5400 define(interp, "json_set", Some(3), |_, args| {
5402 let path = match &args[1] {
5403 Value::String(s) => s.to_string(),
5404 _ => return Err(RuntimeError::new("json_set() requires string path")),
5405 };
5406 let new_value = args[2].clone();
5407
5408 match &args[0] {
5410 Value::Map(map) => {
5411 let mut map = map.borrow_mut();
5412 map.insert(path, new_value);
5413 Ok(Value::Map(Rc::new(RefCell::new(map.clone()))))
5414 }
5415 _ => Err(RuntimeError::new("json_set() requires map/object")),
5416 }
5417 });
5418}
5419
5420fn register_fs(interp: &mut Interpreter) {
5425 define(interp, "fs_read", Some(1), |_, args| {
5427 let path = match &args[0] {
5428 Value::String(s) => s.to_string(),
5429 _ => return Err(RuntimeError::new("fs_read() requires string path")),
5430 };
5431
5432 match std::fs::read_to_string(&path) {
5433 Ok(content) => Ok(Value::String(Rc::new(content))),
5434 Err(e) => Err(RuntimeError::new(format!("fs_read() error: {}", e))),
5435 }
5436 });
5437
5438 define(interp, "fs_read_bytes", Some(1), |_, args| {
5440 let path = match &args[0] {
5441 Value::String(s) => s.to_string(),
5442 _ => return Err(RuntimeError::new("fs_read_bytes() requires string path")),
5443 };
5444
5445 match std::fs::read(&path) {
5446 Ok(bytes) => {
5447 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
5448 Ok(Value::Array(Rc::new(RefCell::new(values))))
5449 }
5450 Err(e) => Err(RuntimeError::new(format!("fs_read_bytes() error: {}", e))),
5451 }
5452 });
5453
5454 define(interp, "fs_write", Some(2), |_, args| {
5456 let path = match &args[0] {
5457 Value::String(s) => s.to_string(),
5458 _ => return Err(RuntimeError::new("fs_write() requires string path")),
5459 };
5460 let content = format!("{}", args[1]);
5461
5462 match std::fs::write(&path, content) {
5463 Ok(()) => Ok(Value::Null),
5464 Err(e) => Err(RuntimeError::new(format!("fs_write() error: {}", e))),
5465 }
5466 });
5467
5468 define(interp, "fs_append", Some(2), |_, args| {
5470 let path = match &args[0] {
5471 Value::String(s) => s.to_string(),
5472 _ => return Err(RuntimeError::new("fs_append() requires string path")),
5473 };
5474 let content = format!("{}", args[1]);
5475
5476 use std::fs::OpenOptions;
5477 match OpenOptions::new().append(true).create(true).open(&path) {
5478 Ok(mut file) => {
5479 use std::io::Write;
5480 match file.write_all(content.as_bytes()) {
5481 Ok(()) => Ok(Value::Null),
5482 Err(e) => Err(RuntimeError::new(format!("fs_append() write error: {}", e))),
5483 }
5484 }
5485 Err(e) => Err(RuntimeError::new(format!("fs_append() error: {}", e))),
5486 }
5487 });
5488
5489 define(interp, "fs_exists", Some(1), |_, args| {
5491 let path = match &args[0] {
5492 Value::String(s) => s.to_string(),
5493 _ => return Err(RuntimeError::new("fs_exists() requires string path")),
5494 };
5495 Ok(Value::Bool(std::path::Path::new(&path).exists()))
5496 });
5497
5498 define(interp, "fs_is_file", Some(1), |_, args| {
5500 let path = match &args[0] {
5501 Value::String(s) => s.to_string(),
5502 _ => return Err(RuntimeError::new("fs_is_file() requires string path")),
5503 };
5504 Ok(Value::Bool(std::path::Path::new(&path).is_file()))
5505 });
5506
5507 define(interp, "fs_is_dir", Some(1), |_, args| {
5509 let path = match &args[0] {
5510 Value::String(s) => s.to_string(),
5511 _ => return Err(RuntimeError::new("fs_is_dir() requires string path")),
5512 };
5513 Ok(Value::Bool(std::path::Path::new(&path).is_dir()))
5514 });
5515
5516 define(interp, "fs_mkdir", Some(1), |_, args| {
5518 let path = match &args[0] {
5519 Value::String(s) => s.to_string(),
5520 _ => return Err(RuntimeError::new("fs_mkdir() requires string path")),
5521 };
5522
5523 match std::fs::create_dir_all(&path) {
5524 Ok(()) => Ok(Value::Null),
5525 Err(e) => Err(RuntimeError::new(format!("fs_mkdir() error: {}", e))),
5526 }
5527 });
5528
5529 define(interp, "fs_remove", Some(1), |_, args| {
5531 let path = match &args[0] {
5532 Value::String(s) => s.to_string(),
5533 _ => return Err(RuntimeError::new("fs_remove() requires string path")),
5534 };
5535
5536 let p = std::path::Path::new(&path);
5537 let result = if p.is_dir() {
5538 std::fs::remove_dir_all(&path)
5539 } else {
5540 std::fs::remove_file(&path)
5541 };
5542
5543 match result {
5544 Ok(()) => Ok(Value::Null),
5545 Err(e) => Err(RuntimeError::new(format!("fs_remove() error: {}", e))),
5546 }
5547 });
5548
5549 define(interp, "fs_list", Some(1), |_, args| {
5551 let path = match &args[0] {
5552 Value::String(s) => s.to_string(),
5553 _ => return Err(RuntimeError::new("fs_list() requires string path")),
5554 };
5555
5556 match std::fs::read_dir(&path) {
5557 Ok(entries) => {
5558 let mut files = Vec::new();
5559 for entry in entries.flatten() {
5560 if let Some(name) = entry.file_name().to_str() {
5561 files.push(Value::String(Rc::new(name.to_string())));
5562 }
5563 }
5564 Ok(Value::Array(Rc::new(RefCell::new(files))))
5565 }
5566 Err(e) => Err(RuntimeError::new(format!("fs_list() error: {}", e))),
5567 }
5568 });
5569
5570 define(interp, "fs_copy", Some(2), |_, args| {
5572 let src = match &args[0] {
5573 Value::String(s) => s.to_string(),
5574 _ => return Err(RuntimeError::new("fs_copy() requires string source path")),
5575 };
5576 let dst = match &args[1] {
5577 Value::String(s) => s.to_string(),
5578 _ => return Err(RuntimeError::new("fs_copy() requires string destination path")),
5579 };
5580
5581 match std::fs::copy(&src, &dst) {
5582 Ok(bytes) => Ok(Value::Int(bytes as i64)),
5583 Err(e) => Err(RuntimeError::new(format!("fs_copy() error: {}", e))),
5584 }
5585 });
5586
5587 define(interp, "fs_rename", Some(2), |_, args| {
5589 let src = match &args[0] {
5590 Value::String(s) => s.to_string(),
5591 _ => return Err(RuntimeError::new("fs_rename() requires string source path")),
5592 };
5593 let dst = match &args[1] {
5594 Value::String(s) => s.to_string(),
5595 _ => return Err(RuntimeError::new("fs_rename() requires string destination path")),
5596 };
5597
5598 match std::fs::rename(&src, &dst) {
5599 Ok(()) => Ok(Value::Null),
5600 Err(e) => Err(RuntimeError::new(format!("fs_rename() error: {}", e))),
5601 }
5602 });
5603
5604 define(interp, "fs_size", Some(1), |_, args| {
5606 let path = match &args[0] {
5607 Value::String(s) => s.to_string(),
5608 _ => return Err(RuntimeError::new("fs_size() requires string path")),
5609 };
5610
5611 match std::fs::metadata(&path) {
5612 Ok(meta) => Ok(Value::Int(meta.len() as i64)),
5613 Err(e) => Err(RuntimeError::new(format!("fs_size() error: {}", e))),
5614 }
5615 });
5616
5617 define(interp, "path_join", None, |_, args| {
5619 let mut path = std::path::PathBuf::new();
5620 for arg in &args {
5621 match arg {
5622 Value::String(s) => path.push(s.as_str()),
5623 Value::Array(arr) => {
5624 for v in arr.borrow().iter() {
5625 if let Value::String(s) = v {
5626 path.push(s.as_str());
5627 }
5628 }
5629 }
5630 _ => {}
5631 }
5632 }
5633 Ok(Value::String(Rc::new(path.to_string_lossy().to_string())))
5634 });
5635
5636 define(interp, "path_parent", Some(1), |_, args| {
5638 let path = match &args[0] {
5639 Value::String(s) => s.to_string(),
5640 _ => return Err(RuntimeError::new("path_parent() requires string path")),
5641 };
5642
5643 let p = std::path::Path::new(&path);
5644 match p.parent() {
5645 Some(parent) => Ok(Value::String(Rc::new(parent.to_string_lossy().to_string()))),
5646 None => Ok(Value::Null),
5647 }
5648 });
5649
5650 define(interp, "path_filename", Some(1), |_, args| {
5652 let path = match &args[0] {
5653 Value::String(s) => s.to_string(),
5654 _ => return Err(RuntimeError::new("path_filename() requires string path")),
5655 };
5656
5657 let p = std::path::Path::new(&path);
5658 match p.file_name() {
5659 Some(name) => Ok(Value::String(Rc::new(name.to_string_lossy().to_string()))),
5660 None => Ok(Value::Null),
5661 }
5662 });
5663
5664 define(interp, "path_extension", Some(1), |_, args| {
5666 let path = match &args[0] {
5667 Value::String(s) => s.to_string(),
5668 _ => return Err(RuntimeError::new("path_extension() requires string path")),
5669 };
5670
5671 let p = std::path::Path::new(&path);
5672 match p.extension() {
5673 Some(ext) => Ok(Value::String(Rc::new(ext.to_string_lossy().to_string()))),
5674 None => Ok(Value::Null),
5675 }
5676 });
5677}
5678
5679fn register_crypto(interp: &mut Interpreter) {
5711 fn extract_bytes(v: &Value, fn_name: &str) -> Result<Vec<u8>, RuntimeError> {
5713 match v {
5714 Value::String(s) => Ok(s.as_bytes().to_vec()),
5715 Value::Array(arr) => {
5716 let arr = arr.borrow();
5717 Ok(arr.iter().filter_map(|v| {
5718 if let Value::Int(n) = v { Some(*n as u8) } else { None }
5719 }).collect())
5720 }
5721 _ => Err(RuntimeError::new(format!("{}() requires string or byte array", fn_name))),
5722 }
5723 }
5724
5725 fn bytes_to_array(bytes: &[u8]) -> Value {
5726 let values: Vec<Value> = bytes.iter().map(|b| Value::Int(*b as i64)).collect();
5727 Value::Array(Rc::new(RefCell::new(values)))
5728 }
5729
5730 define(interp, "sha256", Some(1), |_, args| {
5736 let data = extract_bytes(&args[0], "sha256")?;
5737 let mut hasher = Sha256::new();
5738 hasher.update(&data);
5739 let result = hasher.finalize();
5740 Ok(Value::String(Rc::new(result.iter().map(|b| format!("{:02x}", b)).collect())))
5741 });
5742
5743 define(interp, "sha512", Some(1), |_, args| {
5745 let data = extract_bytes(&args[0], "sha512")?;
5746 let mut hasher = Sha512::new();
5747 hasher.update(&data);
5748 let result = hasher.finalize();
5749 Ok(Value::String(Rc::new(result.iter().map(|b| format!("{:02x}", b)).collect())))
5750 });
5751
5752 define(interp, "sha3_256", Some(1), |_, args| {
5754 use sha3::{Sha3_256, Digest as Sha3Digest};
5755 let data = extract_bytes(&args[0], "sha3_256")?;
5756 let mut hasher = Sha3_256::new();
5757 hasher.update(&data);
5758 let result = hasher.finalize();
5759 Ok(Value::String(Rc::new(result.iter().map(|b| format!("{:02x}", b)).collect())))
5760 });
5761
5762 define(interp, "sha3_512", Some(1), |_, args| {
5764 use sha3::{Sha3_512, Digest as Sha3Digest};
5765 let data = extract_bytes(&args[0], "sha3_512")?;
5766 let mut hasher = Sha3_512::new();
5767 hasher.update(&data);
5768 let result = hasher.finalize();
5769 Ok(Value::String(Rc::new(result.iter().map(|b| format!("{:02x}", b)).collect())))
5770 });
5771
5772 define(interp, "blake3", Some(1), |_, args| {
5774 let data = extract_bytes(&args[0], "blake3")?;
5775 let hash = blake3::hash(&data);
5776 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
5777 });
5778
5779 define(interp, "blake3_keyed", Some(2), |_, args| {
5781 let key = extract_bytes(&args[0], "blake3_keyed")?;
5782 let data = extract_bytes(&args[1], "blake3_keyed")?;
5783 if key.len() != 32 {
5784 return Err(RuntimeError::new("blake3_keyed() requires 32-byte key"));
5785 }
5786 let mut key_arr = [0u8; 32];
5787 key_arr.copy_from_slice(&key);
5788 let hash = blake3::keyed_hash(&key_arr, &data);
5789 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
5790 });
5791
5792 define(interp, "md5", Some(1), |_, args| {
5794 let data = extract_bytes(&args[0], "md5")?;
5795 let mut hasher = Md5::new();
5796 hasher.update(&data);
5797 let result = hasher.finalize();
5798 Ok(Value::String(Rc::new(result.iter().map(|b| format!("{:02x}", b)).collect())))
5799 });
5800
5801 define(interp, "aes_gcm_encrypt", Some(2), |_, args| {
5807 use aes_gcm::{Aes256Gcm, KeyInit, aead::Aead, Nonce};
5808 use rand::RngCore;
5809
5810 let key = extract_bytes(&args[0], "aes_gcm_encrypt")?;
5811 let plaintext = extract_bytes(&args[1], "aes_gcm_encrypt")?;
5812
5813 if key.len() != 32 {
5814 return Err(RuntimeError::new("aes_gcm_encrypt() requires 32-byte key"));
5815 }
5816
5817 let cipher = Aes256Gcm::new_from_slice(&key)
5818 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
5819
5820 let mut nonce_bytes = [0u8; 12];
5821 rand::thread_rng().fill_bytes(&mut nonce_bytes);
5822 let nonce = Nonce::from_slice(&nonce_bytes);
5823
5824 let ciphertext = cipher.encrypt(nonce, plaintext.as_ref())
5825 .map_err(|e| RuntimeError::new(format!("AES encryption error: {}", e)))?;
5826
5827 let mut result = HashMap::new();
5828 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
5829 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
5830 Ok(Value::Map(Rc::new(RefCell::new(result))))
5831 });
5832
5833 define(interp, "aes_gcm_decrypt", Some(3), |_, args| {
5835 use aes_gcm::{Aes256Gcm, KeyInit, aead::Aead, Nonce};
5836
5837 let key = extract_bytes(&args[0], "aes_gcm_decrypt")?;
5838 let ciphertext = extract_bytes(&args[1], "aes_gcm_decrypt")?;
5839 let nonce_bytes = extract_bytes(&args[2], "aes_gcm_decrypt")?;
5840
5841 if key.len() != 32 { return Err(RuntimeError::new("aes_gcm_decrypt() requires 32-byte key")); }
5842 if nonce_bytes.len() != 12 { return Err(RuntimeError::new("aes_gcm_decrypt() requires 12-byte nonce")); }
5843
5844 let cipher = Aes256Gcm::new_from_slice(&key)
5845 .map_err(|e| RuntimeError::new(format!("AES key error: {}", e)))?;
5846 let nonce = Nonce::from_slice(&nonce_bytes);
5847
5848 let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())
5849 .map_err(|_| RuntimeError::new("AES-GCM decryption failed: authentication error"))?;
5850
5851 match String::from_utf8(plaintext.clone()) {
5852 Ok(s) => Ok(Value::String(Rc::new(s))),
5853 Err(_) => Ok(bytes_to_array(&plaintext)),
5854 }
5855 });
5856
5857 define(interp, "chacha20_encrypt", Some(2), |_, args| {
5859 use chacha20poly1305::{ChaCha20Poly1305, KeyInit, aead::Aead, Nonce};
5860 use rand::RngCore;
5861
5862 let key = extract_bytes(&args[0], "chacha20_encrypt")?;
5863 let plaintext = extract_bytes(&args[1], "chacha20_encrypt")?;
5864
5865 if key.len() != 32 { return Err(RuntimeError::new("chacha20_encrypt() requires 32-byte key")); }
5866
5867 let cipher = ChaCha20Poly1305::new_from_slice(&key)
5868 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
5869
5870 let mut nonce_bytes = [0u8; 12];
5871 rand::thread_rng().fill_bytes(&mut nonce_bytes);
5872 let nonce = Nonce::from_slice(&nonce_bytes);
5873
5874 let ciphertext = cipher.encrypt(nonce, plaintext.as_ref())
5875 .map_err(|e| RuntimeError::new(format!("ChaCha20 encryption error: {}", e)))?;
5876
5877 let mut result = HashMap::new();
5878 result.insert("ciphertext".to_string(), bytes_to_array(&ciphertext));
5879 result.insert("nonce".to_string(), bytes_to_array(&nonce_bytes));
5880 Ok(Value::Map(Rc::new(RefCell::new(result))))
5881 });
5882
5883 define(interp, "chacha20_decrypt", Some(3), |_, args| {
5885 use chacha20poly1305::{ChaCha20Poly1305, KeyInit, aead::Aead, Nonce};
5886
5887 let key = extract_bytes(&args[0], "chacha20_decrypt")?;
5888 let ciphertext = extract_bytes(&args[1], "chacha20_decrypt")?;
5889 let nonce_bytes = extract_bytes(&args[2], "chacha20_decrypt")?;
5890
5891 if key.len() != 32 { return Err(RuntimeError::new("chacha20_decrypt() requires 32-byte key")); }
5892 if nonce_bytes.len() != 12 { return Err(RuntimeError::new("chacha20_decrypt() requires 12-byte nonce")); }
5893
5894 let cipher = ChaCha20Poly1305::new_from_slice(&key)
5895 .map_err(|e| RuntimeError::new(format!("ChaCha20 key error: {}", e)))?;
5896 let nonce = Nonce::from_slice(&nonce_bytes);
5897
5898 let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())
5899 .map_err(|_| RuntimeError::new("ChaCha20 decryption failed: authentication error"))?;
5900
5901 match String::from_utf8(plaintext.clone()) {
5902 Ok(s) => Ok(Value::String(Rc::new(s))),
5903 Err(_) => Ok(bytes_to_array(&plaintext)),
5904 }
5905 });
5906
5907 define(interp, "ed25519_keygen", Some(0), |_, _| {
5913 use ed25519_dalek::SigningKey;
5914 use rand::rngs::OsRng;
5915
5916 let signing_key = SigningKey::generate(&mut OsRng);
5917 let verifying_key = signing_key.verifying_key();
5918
5919 let mut result = HashMap::new();
5920 result.insert("private_key".to_string(),
5921 Value::String(Rc::new(signing_key.to_bytes().iter().map(|b| format!("{:02x}", b)).collect())));
5922 result.insert("public_key".to_string(),
5923 Value::String(Rc::new(verifying_key.to_bytes().iter().map(|b| format!("{:02x}", b)).collect())));
5924 Ok(Value::Map(Rc::new(RefCell::new(result))))
5925 });
5926
5927 define(interp, "ed25519_sign", Some(2), |_, args| {
5929 use ed25519_dalek::{SigningKey, Signer};
5930
5931 let private_key_hex = match &args[0] {
5932 Value::String(s) => s.to_string(),
5933 _ => return Err(RuntimeError::new("ed25519_sign() requires hex private key")),
5934 };
5935 let message = extract_bytes(&args[1], "ed25519_sign")?;
5936
5937 let key_bytes: Vec<u8> = (0..private_key_hex.len())
5938 .step_by(2)
5939 .map(|i| u8::from_str_radix(&private_key_hex[i..i+2], 16))
5940 .collect::<Result<Vec<_>, _>>()
5941 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
5942
5943 if key_bytes.len() != 32 { return Err(RuntimeError::new("ed25519_sign() requires 32-byte private key")); }
5944
5945 let mut key_arr = [0u8; 32];
5946 key_arr.copy_from_slice(&key_bytes);
5947 let signing_key = SigningKey::from_bytes(&key_arr);
5948 let signature = signing_key.sign(&message);
5949
5950 Ok(Value::String(Rc::new(signature.to_bytes().iter().map(|b| format!("{:02x}", b)).collect())))
5951 });
5952
5953 define(interp, "ed25519_verify", Some(3), |_, args| {
5955 use ed25519_dalek::{VerifyingKey, Verifier, Signature};
5956
5957 let public_key_hex = match &args[0] {
5958 Value::String(s) => s.to_string(),
5959 _ => return Err(RuntimeError::new("ed25519_verify() requires hex public key")),
5960 };
5961 let message = extract_bytes(&args[1], "ed25519_verify")?;
5962 let signature_hex = match &args[2] {
5963 Value::String(s) => s.to_string(),
5964 _ => return Err(RuntimeError::new("ed25519_verify() requires hex signature")),
5965 };
5966
5967 let key_bytes: Vec<u8> = (0..public_key_hex.len())
5968 .step_by(2)
5969 .map(|i| u8::from_str_radix(&public_key_hex[i..i+2], 16))
5970 .collect::<Result<Vec<_>, _>>()
5971 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
5972 let sig_bytes: Vec<u8> = (0..signature_hex.len())
5973 .step_by(2)
5974 .map(|i| u8::from_str_radix(&signature_hex[i..i+2], 16))
5975 .collect::<Result<Vec<_>, _>>()
5976 .map_err(|_| RuntimeError::new("Invalid signature hex"))?;
5977
5978 if key_bytes.len() != 32 { return Err(RuntimeError::new("ed25519_verify() requires 32-byte public key")); }
5979 if sig_bytes.len() != 64 { return Err(RuntimeError::new("ed25519_verify() requires 64-byte signature")); }
5980
5981 let mut key_arr = [0u8; 32];
5982 key_arr.copy_from_slice(&key_bytes);
5983 let mut sig_arr = [0u8; 64];
5984 sig_arr.copy_from_slice(&sig_bytes);
5985
5986 let verifying_key = VerifyingKey::from_bytes(&key_arr)
5987 .map_err(|e| RuntimeError::new(format!("Invalid public key: {}", e)))?;
5988 let signature = Signature::from_bytes(&sig_arr);
5989
5990 match verifying_key.verify(&message, &signature) {
5991 Ok(_) => Ok(Value::Bool(true)),
5992 Err(_) => Ok(Value::Bool(false)),
5993 }
5994 });
5995
5996 define(interp, "x25519_keygen", Some(0), |_, _| {
5998 use x25519_dalek::{StaticSecret, PublicKey};
5999 use rand::rngs::OsRng;
6000
6001 let secret = StaticSecret::random_from_rng(OsRng);
6002 let public = PublicKey::from(&secret);
6003
6004 let mut result = HashMap::new();
6005 result.insert("private_key".to_string(),
6006 Value::String(Rc::new(secret.as_bytes().iter().map(|b| format!("{:02x}", b)).collect())));
6007 result.insert("public_key".to_string(),
6008 Value::String(Rc::new(public.as_bytes().iter().map(|b| format!("{:02x}", b)).collect())));
6009 Ok(Value::Map(Rc::new(RefCell::new(result))))
6010 });
6011
6012 define(interp, "x25519_exchange", Some(2), |_, args| {
6014 use x25519_dalek::{StaticSecret, PublicKey};
6015
6016 let my_private_hex = match &args[0] {
6017 Value::String(s) => s.to_string(),
6018 _ => return Err(RuntimeError::new("x25519_exchange() requires hex private key")),
6019 };
6020 let their_public_hex = match &args[1] {
6021 Value::String(s) => s.to_string(),
6022 _ => return Err(RuntimeError::new("x25519_exchange() requires hex public key")),
6023 };
6024
6025 let my_private_bytes: Vec<u8> = (0..my_private_hex.len())
6026 .step_by(2)
6027 .map(|i| u8::from_str_radix(&my_private_hex[i..i+2], 16))
6028 .collect::<Result<Vec<_>, _>>()
6029 .map_err(|_| RuntimeError::new("Invalid private key hex"))?;
6030 let their_public_bytes: Vec<u8> = (0..their_public_hex.len())
6031 .step_by(2)
6032 .map(|i| u8::from_str_radix(&their_public_hex[i..i+2], 16))
6033 .collect::<Result<Vec<_>, _>>()
6034 .map_err(|_| RuntimeError::new("Invalid public key hex"))?;
6035
6036 if my_private_bytes.len() != 32 || their_public_bytes.len() != 32 {
6037 return Err(RuntimeError::new("x25519_exchange() requires 32-byte keys"));
6038 }
6039
6040 let mut priv_arr = [0u8; 32];
6041 priv_arr.copy_from_slice(&my_private_bytes);
6042 let mut pub_arr = [0u8; 32];
6043 pub_arr.copy_from_slice(&their_public_bytes);
6044
6045 let my_secret = StaticSecret::from(priv_arr);
6046 let their_public = PublicKey::from(pub_arr);
6047 let shared_secret = my_secret.diffie_hellman(&their_public);
6048
6049 Ok(Value::String(Rc::new(shared_secret.as_bytes().iter().map(|b| format!("{:02x}", b)).collect())))
6050 });
6051
6052 define(interp, "argon2_hash", Some(1), |_, args| {
6058 use argon2::{Argon2, password_hash::{SaltString, PasswordHasher}};
6059 use rand::rngs::OsRng;
6060
6061 let password = extract_bytes(&args[0], "argon2_hash")?;
6062 let salt = SaltString::generate(&mut OsRng);
6063 let argon2 = Argon2::default();
6064
6065 let hash = argon2.hash_password(&password, &salt)
6066 .map_err(|e| RuntimeError::new(format!("Argon2 error: {}", e)))?;
6067
6068 let mut result = HashMap::new();
6069 result.insert("hash".to_string(), Value::String(Rc::new(hash.to_string())));
6070 result.insert("salt".to_string(), Value::String(Rc::new(salt.to_string())));
6071 Ok(Value::Map(Rc::new(RefCell::new(result))))
6072 });
6073
6074 define(interp, "argon2_verify", Some(2), |_, args| {
6076 use argon2::{Argon2, PasswordHash, PasswordVerifier};
6077
6078 let password = extract_bytes(&args[0], "argon2_verify")?;
6079 let hash_str = match &args[1] {
6080 Value::String(s) => s.to_string(),
6081 _ => return Err(RuntimeError::new("argon2_verify() requires hash string")),
6082 };
6083
6084 let parsed_hash = PasswordHash::new(&hash_str)
6085 .map_err(|e| RuntimeError::new(format!("Invalid hash: {}", e)))?;
6086
6087 match Argon2::default().verify_password(&password, &parsed_hash) {
6088 Ok(_) => Ok(Value::Bool(true)),
6089 Err(_) => Ok(Value::Bool(false)),
6090 }
6091 });
6092
6093 define(interp, "hkdf_expand", Some(3), |_, args| {
6095 use hkdf::Hkdf;
6096
6097 let ikm = extract_bytes(&args[0], "hkdf_expand")?;
6098 let salt = extract_bytes(&args[1], "hkdf_expand")?;
6099 let info = extract_bytes(&args[2], "hkdf_expand")?;
6100
6101 let hk = Hkdf::<Sha256>::new(Some(&salt), &ikm);
6102 let mut okm = [0u8; 32];
6103 hk.expand(&info, &mut okm)
6104 .map_err(|e| RuntimeError::new(format!("HKDF error: {}", e)))?;
6105
6106 Ok(Value::String(Rc::new(okm.iter().map(|b| format!("{:02x}", b)).collect())))
6107 });
6108
6109 define(interp, "pbkdf2_derive", Some(3), |_, args| {
6111 let password = extract_bytes(&args[0], "pbkdf2_derive")?;
6112 let salt = extract_bytes(&args[1], "pbkdf2_derive")?;
6113 let iterations = match &args[2] {
6114 Value::Int(n) => *n as u32,
6115 _ => return Err(RuntimeError::new("pbkdf2_derive() requires integer iterations")),
6116 };
6117
6118 let mut key = [0u8; 32];
6119 pbkdf2::pbkdf2_hmac::<Sha256>(&password, &salt, iterations, &mut key);
6120 Ok(Value::String(Rc::new(key.iter().map(|b| format!("{:02x}", b)).collect())))
6121 });
6122
6123 define(interp, "hmac_sha256", Some(2), |_, args| {
6129 use hmac::{Hmac, Mac};
6130 type HmacSha256 = Hmac<Sha256>;
6131
6132 let key = extract_bytes(&args[0], "hmac_sha256")?;
6133 let message = extract_bytes(&args[1], "hmac_sha256")?;
6134
6135 let mut mac = HmacSha256::new_from_slice(&key)
6136 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
6137 mac.update(&message);
6138 let result = mac.finalize();
6139 Ok(Value::String(Rc::new(result.into_bytes().iter().map(|b| format!("{:02x}", b)).collect())))
6140 });
6141
6142 define(interp, "hmac_sha512", Some(2), |_, args| {
6144 use hmac::{Hmac, Mac};
6145 type HmacSha512 = Hmac<Sha512>;
6146
6147 let key = extract_bytes(&args[0], "hmac_sha512")?;
6148 let message = extract_bytes(&args[1], "hmac_sha512")?;
6149
6150 let mut mac = HmacSha512::new_from_slice(&key)
6151 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
6152 mac.update(&message);
6153 let result = mac.finalize();
6154 Ok(Value::String(Rc::new(result.into_bytes().iter().map(|b| format!("{:02x}", b)).collect())))
6155 });
6156
6157 define(interp, "hmac_verify", Some(3), |_, args| {
6159 use hmac::{Hmac, Mac};
6160 type HmacSha256 = Hmac<Sha256>;
6161
6162 let key = extract_bytes(&args[0], "hmac_verify")?;
6163 let message = extract_bytes(&args[1], "hmac_verify")?;
6164 let expected_hex = match &args[2] {
6165 Value::String(s) => s.to_string(),
6166 _ => return Err(RuntimeError::new("hmac_verify() requires hex MAC")),
6167 };
6168
6169 let expected: Vec<u8> = (0..expected_hex.len())
6170 .step_by(2)
6171 .map(|i| u8::from_str_radix(&expected_hex[i..i+2], 16))
6172 .collect::<Result<Vec<_>, _>>()
6173 .map_err(|_| RuntimeError::new("Invalid MAC hex"))?;
6174
6175 let mut mac = HmacSha256::new_from_slice(&key)
6176 .map_err(|e| RuntimeError::new(format!("HMAC key error: {}", e)))?;
6177 mac.update(&message);
6178
6179 match mac.verify_slice(&expected) {
6180 Ok(_) => Ok(Value::Bool(true)),
6181 Err(_) => Ok(Value::Bool(false)),
6182 }
6183 });
6184
6185 define(interp, "secure_random_bytes", Some(1), |_, args| {
6191 use rand::RngCore;
6192
6193 let length = match &args[0] {
6194 Value::Int(n) => *n as usize,
6195 _ => return Err(RuntimeError::new("secure_random_bytes() requires integer length")),
6196 };
6197
6198 if length > 1024 * 1024 { return Err(RuntimeError::new("secure_random_bytes() max 1MB")); }
6199
6200 let mut bytes = vec![0u8; length];
6201 rand::thread_rng().fill_bytes(&mut bytes);
6202 Ok(bytes_to_array(&bytes))
6203 });
6204
6205 define(interp, "secure_random_hex", Some(1), |_, args| {
6207 use rand::RngCore;
6208
6209 let byte_length = match &args[0] {
6210 Value::Int(n) => *n as usize,
6211 _ => return Err(RuntimeError::new("secure_random_hex() requires integer length")),
6212 };
6213
6214 if byte_length > 1024 * 1024 { return Err(RuntimeError::new("secure_random_hex() max 1MB")); }
6215
6216 let mut bytes = vec![0u8; byte_length];
6217 rand::thread_rng().fill_bytes(&mut bytes);
6218 Ok(Value::String(Rc::new(bytes.iter().map(|b| format!("{:02x}", b)).collect())))
6219 });
6220
6221 define(interp, "generate_key", Some(1), |_, args| {
6223 use rand::RngCore;
6224
6225 let bits = match &args[0] {
6226 Value::Int(n) => *n as usize,
6227 _ => return Err(RuntimeError::new("generate_key() requires bit length")),
6228 };
6229
6230 if bits % 8 != 0 { return Err(RuntimeError::new("generate_key() bit length must be multiple of 8")); }
6231 if bits > 512 { return Err(RuntimeError::new("generate_key() max 512 bits")); }
6232
6233 let bytes = bits / 8;
6234 let mut key = vec![0u8; bytes];
6235 rand::thread_rng().fill_bytes(&mut key);
6236 Ok(Value::String(Rc::new(key.iter().map(|b| format!("{:02x}", b)).collect())))
6237 });
6238
6239 define(interp, "base64_encode", Some(1), |_, args| {
6245 let data = extract_bytes(&args[0], "base64_encode")?;
6246 Ok(Value::String(Rc::new(general_purpose::STANDARD.encode(&data))))
6247 });
6248
6249 define(interp, "base64_decode", Some(1), |_, args| {
6251 let encoded = match &args[0] {
6252 Value::String(s) => s.to_string(),
6253 _ => return Err(RuntimeError::new("base64_decode() requires string")),
6254 };
6255
6256 match general_purpose::STANDARD.decode(&encoded) {
6257 Ok(bytes) => {
6258 match String::from_utf8(bytes.clone()) {
6259 Ok(s) => Ok(Value::String(Rc::new(s))),
6260 Err(_) => Ok(bytes_to_array(&bytes)),
6261 }
6262 }
6263 Err(e) => Err(RuntimeError::new(format!("base64_decode() error: {}", e))),
6264 }
6265 });
6266
6267 define(interp, "hex_encode", Some(1), |_, args| {
6269 let data = extract_bytes(&args[0], "hex_encode")?;
6270 Ok(Value::String(Rc::new(data.iter().map(|b| format!("{:02x}", b)).collect())))
6271 });
6272
6273 define(interp, "hex_decode", Some(1), |_, args| {
6275 let hex_str = match &args[0] {
6276 Value::String(s) => s.to_string(),
6277 _ => return Err(RuntimeError::new("hex_decode() requires string")),
6278 };
6279
6280 let hex_str = hex_str.trim();
6281 if hex_str.len() % 2 != 0 { return Err(RuntimeError::new("hex_decode() requires even-length hex string")); }
6282
6283 let bytes: Vec<Value> = (0..hex_str.len())
6284 .step_by(2)
6285 .map(|i| u8::from_str_radix(&hex_str[i..i+2], 16).map(|b| Value::Int(b as i64)))
6286 .collect::<Result<Vec<_>, _>>()
6287 .map_err(|_| RuntimeError::new("hex_decode() invalid hex"))?;
6288 Ok(Value::Array(Rc::new(RefCell::new(bytes))))
6289 });
6290
6291 define(interp, "constant_time_eq", Some(2), |_, args| {
6297 let a = extract_bytes(&args[0], "constant_time_eq")?;
6298 let b = extract_bytes(&args[1], "constant_time_eq")?;
6299
6300 if a.len() != b.len() { return Ok(Value::Bool(false)); }
6301
6302 let mut result = 0u8;
6303 for (x, y) in a.iter().zip(b.iter()) { result |= x ^ y; }
6304 Ok(Value::Bool(result == 0))
6305 });
6306
6307 define(interp, "crypto_info", Some(0), |_, _| {
6313 let mut info = HashMap::new();
6314 info.insert("version".to_string(), Value::String(Rc::new("2.0".to_string())));
6315 info.insert("phase".to_string(), Value::String(Rc::new("Evidential Cryptography".to_string())));
6316
6317 let capabilities = vec![
6318 "sha256", "sha512", "sha3_256", "sha3_512", "blake3", "md5",
6319 "aes_gcm_encrypt", "aes_gcm_decrypt", "chacha20_encrypt", "chacha20_decrypt",
6320 "ed25519_keygen", "ed25519_sign", "ed25519_verify",
6321 "x25519_keygen", "x25519_exchange",
6322 "argon2_hash", "argon2_verify", "hkdf_expand", "pbkdf2_derive",
6323 "hmac_sha256", "hmac_sha512", "hmac_verify",
6324 "secure_random_bytes", "secure_random_hex", "generate_key",
6325 "base64_encode", "base64_decode", "hex_encode", "hex_decode",
6326 "constant_time_eq"
6327 ];
6328 let cap_values: Vec<Value> = capabilities.iter().map(|s| Value::String(Rc::new(s.to_string()))).collect();
6329 info.insert("functions".to_string(), Value::Array(Rc::new(RefCell::new(cap_values))));
6330
6331 Ok(Value::Map(Rc::new(RefCell::new(info))))
6332 });
6333}
6334
6335fn register_regex(interp: &mut Interpreter) {
6340 define(interp, "regex_match", Some(2), |_, args| {
6342 let pattern = match &args[0] {
6343 Value::String(s) => s.to_string(),
6344 _ => return Err(RuntimeError::new("regex_match() requires string pattern")),
6345 };
6346 let text = match &args[1] {
6347 Value::String(s) => s.to_string(),
6348 _ => return Err(RuntimeError::new("regex_match() requires string text")),
6349 };
6350
6351 match Regex::new(&pattern) {
6352 Ok(re) => Ok(Value::Bool(re.is_match(&text))),
6353 Err(e) => Err(RuntimeError::new(format!("regex_match() invalid pattern: {}", e))),
6354 }
6355 });
6356
6357 define(interp, "regex_find", Some(2), |_, args| {
6359 let pattern = match &args[0] {
6360 Value::String(s) => s.to_string(),
6361 _ => return Err(RuntimeError::new("regex_find() requires string pattern")),
6362 };
6363 let text = match &args[1] {
6364 Value::String(s) => s.to_string(),
6365 _ => return Err(RuntimeError::new("regex_find() requires string text")),
6366 };
6367
6368 match Regex::new(&pattern) {
6369 Ok(re) => {
6370 match re.find(&text) {
6371 Some(m) => Ok(Value::String(Rc::new(m.as_str().to_string()))),
6372 None => Ok(Value::Null),
6373 }
6374 }
6375 Err(e) => Err(RuntimeError::new(format!("regex_find() invalid pattern: {}", e))),
6376 }
6377 });
6378
6379 define(interp, "regex_find_all", Some(2), |_, args| {
6381 let pattern = match &args[0] {
6382 Value::String(s) => s.to_string(),
6383 _ => return Err(RuntimeError::new("regex_find_all() requires string pattern")),
6384 };
6385 let text = match &args[1] {
6386 Value::String(s) => s.to_string(),
6387 _ => return Err(RuntimeError::new("regex_find_all() requires string text")),
6388 };
6389
6390 match Regex::new(&pattern) {
6391 Ok(re) => {
6392 let matches: Vec<Value> = re.find_iter(&text)
6393 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
6394 .collect();
6395 Ok(Value::Array(Rc::new(RefCell::new(matches))))
6396 }
6397 Err(e) => Err(RuntimeError::new(format!("regex_find_all() invalid pattern: {}", e))),
6398 }
6399 });
6400
6401 define(interp, "regex_replace", Some(3), |_, args| {
6403 let pattern = match &args[0] {
6404 Value::String(s) => s.to_string(),
6405 _ => return Err(RuntimeError::new("regex_replace() requires string pattern")),
6406 };
6407 let text = match &args[1] {
6408 Value::String(s) => s.to_string(),
6409 _ => return Err(RuntimeError::new("regex_replace() requires string text")),
6410 };
6411 let replacement = match &args[2] {
6412 Value::String(s) => s.to_string(),
6413 _ => return Err(RuntimeError::new("regex_replace() requires string replacement")),
6414 };
6415
6416 match Regex::new(&pattern) {
6417 Ok(re) => {
6418 let result = re.replace(&text, replacement.as_str());
6419 Ok(Value::String(Rc::new(result.to_string())))
6420 }
6421 Err(e) => Err(RuntimeError::new(format!("regex_replace() invalid pattern: {}", e))),
6422 }
6423 });
6424
6425 define(interp, "regex_replace_all", Some(3), |_, args| {
6427 let pattern = match &args[0] {
6428 Value::String(s) => s.to_string(),
6429 _ => return Err(RuntimeError::new("regex_replace_all() requires string pattern")),
6430 };
6431 let text = match &args[1] {
6432 Value::String(s) => s.to_string(),
6433 _ => return Err(RuntimeError::new("regex_replace_all() requires string text")),
6434 };
6435 let replacement = match &args[2] {
6436 Value::String(s) => s.to_string(),
6437 _ => return Err(RuntimeError::new("regex_replace_all() requires string replacement")),
6438 };
6439
6440 match Regex::new(&pattern) {
6441 Ok(re) => {
6442 let result = re.replace_all(&text, replacement.as_str());
6443 Ok(Value::String(Rc::new(result.to_string())))
6444 }
6445 Err(e) => Err(RuntimeError::new(format!("regex_replace_all() invalid pattern: {}", e))),
6446 }
6447 });
6448
6449 define(interp, "regex_split", Some(2), |_, args| {
6451 let pattern = match &args[0] {
6452 Value::String(s) => s.to_string(),
6453 _ => return Err(RuntimeError::new("regex_split() requires string pattern")),
6454 };
6455 let text = match &args[1] {
6456 Value::String(s) => s.to_string(),
6457 _ => return Err(RuntimeError::new("regex_split() requires string text")),
6458 };
6459
6460 match Regex::new(&pattern) {
6461 Ok(re) => {
6462 let parts: Vec<Value> = re.split(&text)
6463 .map(|s| Value::String(Rc::new(s.to_string())))
6464 .collect();
6465 Ok(Value::Array(Rc::new(RefCell::new(parts))))
6466 }
6467 Err(e) => Err(RuntimeError::new(format!("regex_split() invalid pattern: {}", e))),
6468 }
6469 });
6470
6471 define(interp, "regex_captures", Some(2), |_, args| {
6473 let pattern = match &args[0] {
6474 Value::String(s) => s.to_string(),
6475 _ => return Err(RuntimeError::new("regex_captures() requires string pattern")),
6476 };
6477 let text = match &args[1] {
6478 Value::String(s) => s.to_string(),
6479 _ => return Err(RuntimeError::new("regex_captures() requires string text")),
6480 };
6481
6482 match Regex::new(&pattern) {
6483 Ok(re) => {
6484 match re.captures(&text) {
6485 Some(caps) => {
6486 let captures: Vec<Value> = caps.iter()
6487 .map(|m| {
6488 m.map(|m| Value::String(Rc::new(m.as_str().to_string())))
6489 .unwrap_or(Value::Null)
6490 })
6491 .collect();
6492 Ok(Value::Array(Rc::new(RefCell::new(captures))))
6493 }
6494 None => Ok(Value::Null),
6495 }
6496 }
6497 Err(e) => Err(RuntimeError::new(format!("regex_captures() invalid pattern: {}", e))),
6498 }
6499 });
6500}
6501
6502fn register_uuid(interp: &mut Interpreter) {
6507 define(interp, "uuid_v4", Some(0), |_, _| {
6509 let id = Uuid::new_v4();
6510 Ok(Value::String(Rc::new(id.to_string())))
6511 });
6512
6513 define(interp, "uuid_nil", Some(0), |_, _| {
6515 Ok(Value::String(Rc::new(Uuid::nil().to_string())))
6516 });
6517
6518 define(interp, "uuid_parse", Some(1), |_, args| {
6520 let s = match &args[0] {
6521 Value::String(s) => s.to_string(),
6522 _ => return Err(RuntimeError::new("uuid_parse() requires string")),
6523 };
6524
6525 match Uuid::parse_str(&s) {
6526 Ok(id) => Ok(Value::String(Rc::new(id.to_string()))),
6527 Err(e) => Err(RuntimeError::new(format!("uuid_parse() error: {}", e))),
6528 }
6529 });
6530
6531 define(interp, "uuid_is_valid", Some(1), |_, args| {
6533 let s = match &args[0] {
6534 Value::String(s) => s.to_string(),
6535 _ => return Ok(Value::Bool(false)),
6536 };
6537 Ok(Value::Bool(Uuid::parse_str(&s).is_ok()))
6538 });
6539}
6540
6541fn register_system(interp: &mut Interpreter) {
6546 define(interp, "env_get", Some(1), |_, args| {
6548 let key = match &args[0] {
6549 Value::String(s) => s.to_string(),
6550 _ => return Err(RuntimeError::new("env_get() requires string key")),
6551 };
6552
6553 match std::env::var(&key) {
6554 Ok(val) => Ok(Value::String(Rc::new(val))),
6555 Err(_) => Ok(Value::Null),
6556 }
6557 });
6558
6559 define(interp, "env_set", Some(2), |_, args| {
6561 let key = match &args[0] {
6562 Value::String(s) => s.to_string(),
6563 _ => return Err(RuntimeError::new("env_set() requires string key")),
6564 };
6565 let val = match &args[1] {
6566 Value::String(s) => s.to_string(),
6567 _ => format!("{}", args[1]),
6568 };
6569
6570 std::env::set_var(&key, &val);
6571 Ok(Value::Null)
6572 });
6573
6574 define(interp, "env_remove", Some(1), |_, args| {
6576 let key = match &args[0] {
6577 Value::String(s) => s.to_string(),
6578 _ => return Err(RuntimeError::new("env_remove() requires string key")),
6579 };
6580
6581 std::env::remove_var(&key);
6582 Ok(Value::Null)
6583 });
6584
6585 define(interp, "env_vars", Some(0), |_, _| {
6587 let mut map = HashMap::new();
6588 for (key, val) in std::env::vars() {
6589 map.insert(key, Value::String(Rc::new(val)));
6590 }
6591 Ok(Value::Map(Rc::new(RefCell::new(map))))
6592 });
6593
6594 define(interp, "args", Some(0), |_, _| {
6596 let args: Vec<Value> = std::env::args()
6597 .map(|s| Value::String(Rc::new(s)))
6598 .collect();
6599 Ok(Value::Array(Rc::new(RefCell::new(args))))
6600 });
6601
6602 define(interp, "cwd", Some(0), |_, _| {
6604 match std::env::current_dir() {
6605 Ok(path) => Ok(Value::String(Rc::new(path.to_string_lossy().to_string()))),
6606 Err(e) => Err(RuntimeError::new(format!("cwd() error: {}", e))),
6607 }
6608 });
6609
6610 define(interp, "chdir", Some(1), |_, args| {
6612 let path = match &args[0] {
6613 Value::String(s) => s.to_string(),
6614 _ => return Err(RuntimeError::new("chdir() requires string path")),
6615 };
6616
6617 match std::env::set_current_dir(&path) {
6618 Ok(()) => Ok(Value::Null),
6619 Err(e) => Err(RuntimeError::new(format!("chdir() error: {}", e))),
6620 }
6621 });
6622
6623 define(interp, "hostname", Some(0), |_, _| {
6625 match std::fs::read_to_string("/etc/hostname") {
6627 Ok(name) => Ok(Value::String(Rc::new(name.trim().to_string()))),
6628 Err(_) => Ok(Value::String(Rc::new("unknown".to_string()))),
6629 }
6630 });
6631
6632 define(interp, "pid", Some(0), |_, _| {
6634 Ok(Value::Int(std::process::id() as i64))
6635 });
6636
6637 define(interp, "exit", Some(1), |_, args| {
6639 let code = match &args[0] {
6640 Value::Int(n) => *n as i32,
6641 _ => 0,
6642 };
6643 std::process::exit(code);
6644 });
6645
6646 define(interp, "shell", Some(1), |_, args| {
6648 let cmd = match &args[0] {
6649 Value::String(s) => s.to_string(),
6650 _ => return Err(RuntimeError::new("shell() requires string command")),
6651 };
6652
6653 match std::process::Command::new("sh")
6654 .arg("-c")
6655 .arg(&cmd)
6656 .output()
6657 {
6658 Ok(output) => {
6659 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
6660 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
6661 let code = output.status.code().unwrap_or(-1);
6662
6663 let mut result = HashMap::new();
6664 result.insert("stdout".to_string(), Value::String(Rc::new(stdout)));
6665 result.insert("stderr".to_string(), Value::String(Rc::new(stderr)));
6666 result.insert("code".to_string(), Value::Int(code as i64));
6667 result.insert("success".to_string(), Value::Bool(output.status.success()));
6668
6669 Ok(Value::Map(Rc::new(RefCell::new(result))))
6670 }
6671 Err(e) => Err(RuntimeError::new(format!("shell() error: {}", e))),
6672 }
6673 });
6674
6675 define(interp, "platform", Some(0), |_, _| {
6677 Ok(Value::String(Rc::new(std::env::consts::OS.to_string())))
6678 });
6679
6680 define(interp, "arch", Some(0), |_, _| {
6682 Ok(Value::String(Rc::new(std::env::consts::ARCH.to_string())))
6683 });
6684}
6685
6686fn register_stats(interp: &mut Interpreter) {
6691 fn extract_numbers(val: &Value) -> Result<Vec<f64>, RuntimeError> {
6693 match val {
6694 Value::Array(arr) => {
6695 let arr = arr.borrow();
6696 let mut nums = Vec::new();
6697 for v in arr.iter() {
6698 match v {
6699 Value::Int(n) => nums.push(*n as f64),
6700 Value::Float(f) => nums.push(*f),
6701 _ => return Err(RuntimeError::new("stats functions require numeric array")),
6702 }
6703 }
6704 Ok(nums)
6705 }
6706 _ => Err(RuntimeError::new("stats functions require array")),
6707 }
6708 }
6709
6710 define(interp, "mean", Some(1), |_, args| {
6712 let nums = extract_numbers(&args[0])?;
6713 if nums.is_empty() {
6714 return Ok(Value::Float(0.0));
6715 }
6716 let sum: f64 = nums.iter().sum();
6717 Ok(Value::Float(sum / nums.len() as f64))
6718 });
6719
6720 define(interp, "median", Some(1), |_, args| {
6722 let mut nums = extract_numbers(&args[0])?;
6723 if nums.is_empty() {
6724 return Ok(Value::Float(0.0));
6725 }
6726 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
6727 let len = nums.len();
6728 if len % 2 == 0 {
6729 Ok(Value::Float((nums[len/2 - 1] + nums[len/2]) / 2.0))
6730 } else {
6731 Ok(Value::Float(nums[len/2]))
6732 }
6733 });
6734
6735 define(interp, "mode", Some(1), |_, args| {
6737 let nums = extract_numbers(&args[0])?;
6738 if nums.is_empty() {
6739 return Ok(Value::Null);
6740 }
6741
6742 let mut counts: HashMap<String, usize> = HashMap::new();
6743 for n in &nums {
6744 let key = format!("{:.10}", n);
6745 *counts.entry(key).or_insert(0) += 1;
6746 }
6747
6748 let max_count = counts.values().max().unwrap_or(&0);
6749 for n in &nums {
6750 let key = format!("{:.10}", n);
6751 if counts.get(&key) == Some(max_count) {
6752 return Ok(Value::Float(*n));
6753 }
6754 }
6755 Ok(Value::Null)
6756 });
6757
6758 define(interp, "variance", Some(1), |_, args| {
6760 let nums = extract_numbers(&args[0])?;
6761 if nums.is_empty() {
6762 return Ok(Value::Float(0.0));
6763 }
6764 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
6765 let variance: f64 = nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
6766 Ok(Value::Float(variance))
6767 });
6768
6769 define(interp, "stddev", Some(1), |_, args| {
6771 let nums = extract_numbers(&args[0])?;
6772 if nums.is_empty() {
6773 return Ok(Value::Float(0.0));
6774 }
6775 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
6776 let variance: f64 = nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
6777 Ok(Value::Float(variance.sqrt()))
6778 });
6779
6780 define(interp, "percentile", Some(2), |_, args| {
6782 let mut nums = extract_numbers(&args[0])?;
6783 let p = match &args[1] {
6784 Value::Int(n) => *n as f64,
6785 Value::Float(f) => *f,
6786 _ => return Err(RuntimeError::new("percentile() requires numeric percentile")),
6787 };
6788
6789 if nums.is_empty() {
6790 return Ok(Value::Float(0.0));
6791 }
6792
6793 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
6794 let idx = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
6795 Ok(Value::Float(nums[idx.min(nums.len() - 1)]))
6796 });
6797
6798 define(interp, "correlation", Some(2), |_, args| {
6800 let x = extract_numbers(&args[0])?;
6801 let y = extract_numbers(&args[1])?;
6802
6803 if x.len() != y.len() || x.is_empty() {
6804 return Err(RuntimeError::new("correlation() requires equal-length non-empty arrays"));
6805 }
6806
6807 let n = x.len() as f64;
6808 let mean_x: f64 = x.iter().sum::<f64>() / n;
6809 let mean_y: f64 = y.iter().sum::<f64>() / n;
6810
6811 let mut cov = 0.0;
6812 let mut var_x = 0.0;
6813 let mut var_y = 0.0;
6814
6815 for i in 0..x.len() {
6816 let dx = x[i] - mean_x;
6817 let dy = y[i] - mean_y;
6818 cov += dx * dy;
6819 var_x += dx * dx;
6820 var_y += dy * dy;
6821 }
6822
6823 if var_x == 0.0 || var_y == 0.0 {
6824 return Ok(Value::Float(0.0));
6825 }
6826
6827 Ok(Value::Float(cov / (var_x.sqrt() * var_y.sqrt())))
6828 });
6829
6830 define(interp, "range", Some(1), |_, args| {
6832 let nums = extract_numbers(&args[0])?;
6833 if nums.is_empty() {
6834 return Ok(Value::Float(0.0));
6835 }
6836 let min = nums.iter().cloned().fold(f64::INFINITY, f64::min);
6837 let max = nums.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
6838 Ok(Value::Float(max - min))
6839 });
6840
6841 define(interp, "zscore", Some(1), |_, args| {
6843 let nums = extract_numbers(&args[0])?;
6844 if nums.is_empty() {
6845 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
6846 }
6847
6848 let mean: f64 = nums.iter().sum::<f64>() / nums.len() as f64;
6849 let variance: f64 = nums.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / nums.len() as f64;
6850 let stddev = variance.sqrt();
6851
6852 if stddev == 0.0 {
6853 let zeros: Vec<Value> = nums.iter().map(|_| Value::Float(0.0)).collect();
6854 return Ok(Value::Array(Rc::new(RefCell::new(zeros))));
6855 }
6856
6857 let zscores: Vec<Value> = nums.iter()
6858 .map(|x| Value::Float((x - mean) / stddev))
6859 .collect();
6860 Ok(Value::Array(Rc::new(RefCell::new(zscores))))
6861 });
6862}
6863
6864fn register_matrix(interp: &mut Interpreter) {
6869 fn extract_matrix(val: &Value) -> Result<Vec<Vec<f64>>, RuntimeError> {
6871 match val {
6872 Value::Array(arr) => {
6873 let arr = arr.borrow();
6874 let mut matrix = Vec::new();
6875 for row in arr.iter() {
6876 match row {
6877 Value::Array(row_arr) => {
6878 let row_arr = row_arr.borrow();
6879 let mut row_vec = Vec::new();
6880 for v in row_arr.iter() {
6881 match v {
6882 Value::Int(n) => row_vec.push(*n as f64),
6883 Value::Float(f) => row_vec.push(*f),
6884 _ => return Err(RuntimeError::new("matrix requires numeric values")),
6885 }
6886 }
6887 matrix.push(row_vec);
6888 }
6889 _ => return Err(RuntimeError::new("matrix requires 2D array")),
6890 }
6891 }
6892 Ok(matrix)
6893 }
6894 _ => Err(RuntimeError::new("matrix requires array")),
6895 }
6896 }
6897
6898 fn matrix_to_value(m: Vec<Vec<f64>>) -> Value {
6899 let rows: Vec<Value> = m.into_iter()
6900 .map(|row| {
6901 let cols: Vec<Value> = row.into_iter().map(Value::Float).collect();
6902 Value::Array(Rc::new(RefCell::new(cols)))
6903 })
6904 .collect();
6905 Value::Array(Rc::new(RefCell::new(rows)))
6906 }
6907
6908 define(interp, "matrix_new", Some(3), |_, args| {
6910 let rows = match &args[0] {
6911 Value::Int(n) => *n as usize,
6912 _ => return Err(RuntimeError::new("matrix_new() requires integer rows")),
6913 };
6914 let cols = match &args[1] {
6915 Value::Int(n) => *n as usize,
6916 _ => return Err(RuntimeError::new("matrix_new() requires integer cols")),
6917 };
6918 let fill = match &args[2] {
6919 Value::Int(n) => *n as f64,
6920 Value::Float(f) => *f,
6921 _ => 0.0,
6922 };
6923
6924 let matrix = vec![vec![fill; cols]; rows];
6925 Ok(matrix_to_value(matrix))
6926 });
6927
6928 define(interp, "matrix_identity", Some(1), |_, args| {
6930 let size = match &args[0] {
6931 Value::Int(n) => *n as usize,
6932 _ => return Err(RuntimeError::new("matrix_identity() requires integer size")),
6933 };
6934
6935 let mut matrix = vec![vec![0.0; size]; size];
6936 for i in 0..size {
6937 matrix[i][i] = 1.0;
6938 }
6939 Ok(matrix_to_value(matrix))
6940 });
6941
6942 define(interp, "matrix_add", Some(2), |_, args| {
6944 let a = extract_matrix(&args[0])?;
6945 let b = extract_matrix(&args[1])?;
6946
6947 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
6948 return Err(RuntimeError::new("matrix_add() requires same-size matrices"));
6949 }
6950
6951 let result: Vec<Vec<f64>> = a.iter().zip(b.iter())
6952 .map(|(row_a, row_b)| {
6953 row_a.iter().zip(row_b.iter()).map(|(x, y)| x + y).collect()
6954 })
6955 .collect();
6956
6957 Ok(matrix_to_value(result))
6958 });
6959
6960 define(interp, "matrix_sub", Some(2), |_, args| {
6962 let a = extract_matrix(&args[0])?;
6963 let b = extract_matrix(&args[1])?;
6964
6965 if a.len() != b.len() || a.is_empty() || a[0].len() != b[0].len() {
6966 return Err(RuntimeError::new("matrix_sub() requires same-size matrices"));
6967 }
6968
6969 let result: Vec<Vec<f64>> = a.iter().zip(b.iter())
6970 .map(|(row_a, row_b)| {
6971 row_a.iter().zip(row_b.iter()).map(|(x, y)| x - y).collect()
6972 })
6973 .collect();
6974
6975 Ok(matrix_to_value(result))
6976 });
6977
6978 define(interp, "matrix_mul", Some(2), |_, args| {
6980 let a = extract_matrix(&args[0])?;
6981 let b = extract_matrix(&args[1])?;
6982
6983 if a.is_empty() || b.is_empty() || a[0].len() != b.len() {
6984 return Err(RuntimeError::new("matrix_mul() requires compatible matrices (a.cols == b.rows)"));
6985 }
6986
6987 let rows = a.len();
6988 let cols = b[0].len();
6989 let inner = b.len();
6990
6991 let mut result = vec![vec![0.0; cols]; rows];
6992 for i in 0..rows {
6993 for j in 0..cols {
6994 for k in 0..inner {
6995 result[i][j] += a[i][k] * b[k][j];
6996 }
6997 }
6998 }
6999
7000 Ok(matrix_to_value(result))
7001 });
7002
7003 define(interp, "matrix_scale", Some(2), |_, args| {
7005 let m = extract_matrix(&args[0])?;
7006 let scale = match &args[1] {
7007 Value::Int(n) => *n as f64,
7008 Value::Float(f) => *f,
7009 _ => return Err(RuntimeError::new("matrix_scale() requires numeric scalar")),
7010 };
7011
7012 let result: Vec<Vec<f64>> = m.iter()
7013 .map(|row| row.iter().map(|x| x * scale).collect())
7014 .collect();
7015
7016 Ok(matrix_to_value(result))
7017 });
7018
7019 define(interp, "matrix_transpose", Some(1), |_, args| {
7021 let m = extract_matrix(&args[0])?;
7022 if m.is_empty() {
7023 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
7024 }
7025
7026 let rows = m.len();
7027 let cols = m[0].len();
7028 let mut result = vec![vec![0.0; rows]; cols];
7029
7030 for i in 0..rows {
7031 for j in 0..cols {
7032 result[j][i] = m[i][j];
7033 }
7034 }
7035
7036 Ok(matrix_to_value(result))
7037 });
7038
7039 define(interp, "matrix_det", Some(1), |_, args| {
7041 let m = extract_matrix(&args[0])?;
7042
7043 if m.is_empty() || m.len() != m[0].len() {
7044 return Err(RuntimeError::new("matrix_det() requires square matrix"));
7045 }
7046
7047 let n = m.len();
7048 match n {
7049 1 => Ok(Value::Float(m[0][0])),
7050 2 => Ok(Value::Float(m[0][0] * m[1][1] - m[0][1] * m[1][0])),
7051 3 => {
7052 let det = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
7053 - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
7054 + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
7055 Ok(Value::Float(det))
7056 }
7057 _ => Err(RuntimeError::new("matrix_det() only supports up to 3x3 matrices")),
7058 }
7059 });
7060
7061 define(interp, "matrix_trace", Some(1), |_, args| {
7063 let m = extract_matrix(&args[0])?;
7064
7065 let size = m.len().min(if m.is_empty() { 0 } else { m[0].len() });
7066 let trace: f64 = (0..size).map(|i| m[i][i]).sum();
7067
7068 Ok(Value::Float(trace))
7069 });
7070
7071 define(interp, "matrix_dot", Some(2), |_, args| {
7073 fn extract_vector(val: &Value) -> Result<Vec<f64>, RuntimeError> {
7074 match val {
7075 Value::Array(arr) => {
7076 let arr = arr.borrow();
7077 let mut vec = Vec::new();
7078 for v in arr.iter() {
7079 match v {
7080 Value::Int(n) => vec.push(*n as f64),
7081 Value::Float(f) => vec.push(*f),
7082 _ => return Err(RuntimeError::new("dot product requires numeric vectors")),
7083 }
7084 }
7085 Ok(vec)
7086 }
7087 _ => Err(RuntimeError::new("dot product requires arrays")),
7088 }
7089 }
7090
7091 let a = extract_vector(&args[0])?;
7092 let b = extract_vector(&args[1])?;
7093
7094 if a.len() != b.len() {
7095 return Err(RuntimeError::new("matrix_dot() requires same-length vectors"));
7096 }
7097
7098 let dot: f64 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
7099 Ok(Value::Float(dot))
7100 });
7101}
7102
7103fn mod_inverse(a: i64, m: i64) -> Option<i64> {
7105 let (mut old_r, mut r) = (a, m);
7106 let (mut old_s, mut s) = (1i64, 0i64);
7107
7108 while r != 0 {
7109 let q = old_r / r;
7110 (old_r, r) = (r, old_r - q * r);
7111 (old_s, s) = (s, old_s - q * s);
7112 }
7113
7114 if old_r != 1 {
7115 None } else {
7117 Some(old_s.rem_euclid(m))
7118 }
7119}
7120
7121fn register_functional(interp: &mut Interpreter) {
7127 define(interp, "identity", Some(1), |_, args| {
7129 Ok(args[0].clone())
7130 });
7131
7132 define(interp, "const_fn", Some(1), |_, args| {
7134 Ok(args[0].clone())
7135 });
7136
7137 define(interp, "apply", Some(2), |interp, args| {
7139 let func = match &args[0] {
7140 Value::Function(f) => f.clone(),
7141 _ => return Err(RuntimeError::new("apply: first argument must be a function")),
7142 };
7143 let fn_args = match &args[1] {
7144 Value::Array(arr) => arr.borrow().clone(),
7145 _ => return Err(RuntimeError::new("apply: second argument must be an array")),
7146 };
7147 interp.call_function(&func, fn_args)
7148 });
7149
7150 define(interp, "flip", Some(3), |interp, args| {
7152 let func = match &args[0] {
7153 Value::Function(f) => f.clone(),
7154 _ => return Err(RuntimeError::new("flip: first argument must be a function")),
7155 };
7156 let flipped_args = vec![args[2].clone(), args[1].clone()];
7157 interp.call_function(&func, flipped_args)
7158 });
7159
7160 define(interp, "tap", Some(2), |interp, args| {
7162 let val = args[0].clone();
7163 let func = match &args[1] {
7164 Value::Function(f) => f.clone(),
7165 _ => return Err(RuntimeError::new("tap: second argument must be a function")),
7166 };
7167 let _ = interp.call_function(&func, vec![val.clone()]);
7168 Ok(val)
7169 });
7170
7171 define(interp, "thunk", Some(1), |_, args| {
7173 Ok(Value::Array(Rc::new(RefCell::new(vec![args[0].clone()]))))
7174 });
7175
7176 define(interp, "force", Some(1), |interp, args| {
7178 match &args[0] {
7179 Value::Array(arr) => {
7180 let arr = arr.borrow();
7181 if arr.len() == 1 {
7182 if let Value::Function(f) = &arr[0] {
7183 return interp.call_function(f, vec![]);
7184 }
7185 }
7186 Ok(arr.get(0).cloned().unwrap_or(Value::Null))
7187 }
7188 v => Ok(v.clone()),
7189 }
7190 });
7191
7192 define(interp, "negate", Some(2), |interp, args| {
7194 let func = match &args[0] {
7195 Value::Function(f) => f.clone(),
7196 _ => return Err(RuntimeError::new("negate: first argument must be a function")),
7197 };
7198 let result = interp.call_function(&func, vec![args[1].clone()])?;
7199 Ok(Value::Bool(!is_truthy(&result)))
7200 });
7201
7202 define(interp, "complement", Some(2), |interp, args| {
7204 let func = match &args[0] {
7205 Value::Function(f) => f.clone(),
7206 _ => return Err(RuntimeError::new("complement: first argument must be a function")),
7207 };
7208 let result = interp.call_function(&func, vec![args[1].clone()])?;
7209 Ok(Value::Bool(!is_truthy(&result)))
7210 });
7211
7212 define(interp, "partial", None, |interp, args| {
7214 if args.len() < 2 {
7215 return Err(RuntimeError::new("partial: requires at least function and one argument"));
7216 }
7217 let func = match &args[0] {
7218 Value::Function(f) => f.clone(),
7219 _ => return Err(RuntimeError::new("partial: first argument must be a function")),
7220 };
7221 let partial_args: Vec<Value> = args[1..].to_vec();
7222 interp.call_function(&func, partial_args)
7223 });
7224
7225 define(interp, "juxt", None, |interp, args| {
7227 if args.len() < 2 {
7228 return Err(RuntimeError::new("juxt: requires functions and a value"));
7229 }
7230 let val = args.last().unwrap().clone();
7231 let results: Result<Vec<Value>, _> = args[..args.len()-1].iter().map(|f| {
7232 match f {
7233 Value::Function(func) => interp.call_function(func, vec![val.clone()]),
7234 _ => Err(RuntimeError::new("juxt: all but last argument must be functions")),
7235 }
7236 }).collect();
7237 Ok(Value::Array(Rc::new(RefCell::new(results?))))
7238 });
7239}
7240
7241fn register_benchmark(interp: &mut Interpreter) {
7243 define(interp, "bench", Some(2), |interp, args| {
7245 let func = match &args[0] {
7246 Value::Function(f) => f.clone(),
7247 _ => return Err(RuntimeError::new("bench: first argument must be a function")),
7248 };
7249 let iterations = match &args[1] {
7250 Value::Int(n) => *n as usize,
7251 _ => return Err(RuntimeError::new("bench: second argument must be an integer")),
7252 };
7253
7254 let start = std::time::Instant::now();
7255 for _ in 0..iterations {
7256 let _ = interp.call_function(&func, vec![])?;
7257 }
7258 let elapsed = start.elapsed();
7259 let avg_ms = elapsed.as_secs_f64() * 1000.0 / iterations as f64;
7260 Ok(Value::Float(avg_ms))
7261 });
7262
7263 define(interp, "time_it", Some(1), |interp, args| {
7265 let func = match &args[0] {
7266 Value::Function(f) => f.clone(),
7267 _ => return Err(RuntimeError::new("time_it: argument must be a function")),
7268 };
7269
7270 let start = std::time::Instant::now();
7271 let result = interp.call_function(&func, vec![])?;
7272 let elapsed_ms = start.elapsed().as_secs_f64() * 1000.0;
7273
7274 Ok(Value::Tuple(Rc::new(vec![result, Value::Float(elapsed_ms)])))
7275 });
7276
7277 define(interp, "stopwatch_start", Some(0), |_, _| {
7279 let elapsed = std::time::SystemTime::now()
7280 .duration_since(std::time::UNIX_EPOCH)
7281 .unwrap_or_default();
7282 Ok(Value::Float(elapsed.as_secs_f64() * 1000.0))
7283 });
7284
7285 define(interp, "stopwatch_elapsed", Some(1), |_, args| {
7287 let start_ms = match &args[0] {
7288 Value::Float(f) => *f,
7289 Value::Int(n) => *n as f64,
7290 _ => return Err(RuntimeError::new("stopwatch_elapsed: argument must be a number")),
7291 };
7292 let now = std::time::SystemTime::now()
7293 .duration_since(std::time::UNIX_EPOCH)
7294 .unwrap_or_default();
7295 let now_ms = now.as_secs_f64() * 1000.0;
7296 Ok(Value::Float(now_ms - start_ms))
7297 });
7298
7299 define(interp, "compare_bench", Some(3), |interp, args| {
7301 let func1 = match &args[0] {
7302 Value::Function(f) => f.clone(),
7303 _ => return Err(RuntimeError::new("compare_bench: first argument must be a function")),
7304 };
7305 let func2 = match &args[1] {
7306 Value::Function(f) => f.clone(),
7307 _ => return Err(RuntimeError::new("compare_bench: second argument must be a function")),
7308 };
7309 let iterations = match &args[2] {
7310 Value::Int(n) => *n as usize,
7311 _ => return Err(RuntimeError::new("compare_bench: third argument must be an integer")),
7312 };
7313
7314 let start1 = std::time::Instant::now();
7315 for _ in 0..iterations {
7316 let _ = interp.call_function(&func1, vec![])?;
7317 }
7318 let time1 = start1.elapsed().as_secs_f64();
7319
7320 let start2 = std::time::Instant::now();
7321 for _ in 0..iterations {
7322 let _ = interp.call_function(&func2, vec![])?;
7323 }
7324 let time2 = start2.elapsed().as_secs_f64();
7325
7326 let mut results = std::collections::HashMap::new();
7327 results.insert("time1_ms".to_string(), Value::Float(time1 * 1000.0));
7328 results.insert("time2_ms".to_string(), Value::Float(time2 * 1000.0));
7329 results.insert("speedup".to_string(), Value::Float(time1 / time2));
7330 results.insert("iterations".to_string(), Value::Int(iterations as i64));
7331
7332 Ok(Value::Struct { name: "BenchResult".to_string(), fields: Rc::new(RefCell::new(results)) })
7333 });
7334
7335 define(interp, "memory_usage", Some(0), |_, _| {
7337 Ok(Value::Int(0))
7338 });
7339}
7340
7341fn register_itertools(interp: &mut Interpreter) {
7343 define(interp, "cycle", Some(2), |_, args| {
7345 let arr = match &args[0] {
7346 Value::Array(a) => a.borrow().clone(),
7347 _ => return Err(RuntimeError::new("cycle: first argument must be an array")),
7348 };
7349 let n = match &args[1] {
7350 Value::Int(n) => *n as usize,
7351 _ => return Err(RuntimeError::new("cycle: second argument must be an integer")),
7352 };
7353
7354 if arr.is_empty() {
7355 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
7356 }
7357
7358 let result: Vec<Value> = (0..n).map(|i| arr[i % arr.len()].clone()).collect();
7359 Ok(Value::Array(Rc::new(RefCell::new(result))))
7360 });
7361
7362 define(interp, "repeat_val", Some(2), |_, args| {
7364 let val = args[0].clone();
7365 let n = match &args[1] {
7366 Value::Int(n) => *n as usize,
7367 _ => return Err(RuntimeError::new("repeat_val: second argument must be an integer")),
7368 };
7369
7370 let result: Vec<Value> = std::iter::repeat(val).take(n).collect();
7371 Ok(Value::Array(Rc::new(RefCell::new(result))))
7372 });
7373
7374 define(interp, "take_while", Some(2), |interp, args| {
7376 let arr = match &args[0] {
7377 Value::Array(a) => a.borrow().clone(),
7378 _ => return Err(RuntimeError::new("take_while: first argument must be an array")),
7379 };
7380 let pred = match &args[1] {
7381 Value::Function(f) => f.clone(),
7382 _ => return Err(RuntimeError::new("take_while: second argument must be a function")),
7383 };
7384
7385 let mut result = Vec::new();
7386 for item in arr {
7387 let keep = interp.call_function(&pred, vec![item.clone()])?;
7388 if is_truthy(&keep) {
7389 result.push(item);
7390 } else {
7391 break;
7392 }
7393 }
7394 Ok(Value::Array(Rc::new(RefCell::new(result))))
7395 });
7396
7397 define(interp, "drop_while", Some(2), |interp, args| {
7399 let arr = match &args[0] {
7400 Value::Array(a) => a.borrow().clone(),
7401 _ => return Err(RuntimeError::new("drop_while: first argument must be an array")),
7402 };
7403 let pred = match &args[1] {
7404 Value::Function(f) => f.clone(),
7405 _ => return Err(RuntimeError::new("drop_while: second argument must be a function")),
7406 };
7407
7408 let mut dropping = true;
7409 let mut result = Vec::new();
7410 for item in arr {
7411 if dropping {
7412 let drop = interp.call_function(&pred, vec![item.clone()])?;
7413 if !is_truthy(&drop) {
7414 dropping = false;
7415 result.push(item);
7416 }
7417 } else {
7418 result.push(item);
7419 }
7420 }
7421 Ok(Value::Array(Rc::new(RefCell::new(result))))
7422 });
7423
7424 define(interp, "group_by", Some(2), |interp, args| {
7426 let arr = match &args[0] {
7427 Value::Array(a) => a.borrow().clone(),
7428 _ => return Err(RuntimeError::new("group_by: first argument must be an array")),
7429 };
7430 let key_fn = match &args[1] {
7431 Value::Function(f) => f.clone(),
7432 _ => return Err(RuntimeError::new("group_by: second argument must be a function")),
7433 };
7434
7435 let mut groups: Vec<Value> = Vec::new();
7436 let mut current_group: Vec<Value> = Vec::new();
7437 let mut current_key: Option<Value> = None;
7438
7439 for item in arr {
7440 let key = interp.call_function(&key_fn, vec![item.clone()])?;
7441 match ¤t_key {
7442 Some(k) if value_eq(k, &key) => {
7443 current_group.push(item);
7444 }
7445 _ => {
7446 if !current_group.is_empty() {
7447 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
7448 }
7449 current_group = vec![item];
7450 current_key = Some(key);
7451 }
7452 }
7453 }
7454 if !current_group.is_empty() {
7455 groups.push(Value::Array(Rc::new(RefCell::new(current_group))));
7456 }
7457
7458 Ok(Value::Array(Rc::new(RefCell::new(groups))))
7459 });
7460
7461 define(interp, "partition", Some(2), |interp, args| {
7463 let arr = match &args[0] {
7464 Value::Array(a) => a.borrow().clone(),
7465 _ => return Err(RuntimeError::new("partition: first argument must be an array")),
7466 };
7467 let pred = match &args[1] {
7468 Value::Function(f) => f.clone(),
7469 _ => return Err(RuntimeError::new("partition: second argument must be a function")),
7470 };
7471
7472 let mut true_items = Vec::new();
7473 let mut false_items = Vec::new();
7474
7475 for item in arr {
7476 let result = interp.call_function(&pred, vec![item.clone()])?;
7477 if is_truthy(&result) {
7478 true_items.push(item);
7479 } else {
7480 false_items.push(item);
7481 }
7482 }
7483
7484 Ok(Value::Tuple(Rc::new(vec![
7485 Value::Array(Rc::new(RefCell::new(true_items))),
7486 Value::Array(Rc::new(RefCell::new(false_items))),
7487 ])))
7488 });
7489
7490 define(interp, "interleave", Some(2), |_, args| {
7492 let arr1 = match &args[0] {
7493 Value::Array(a) => a.borrow().clone(),
7494 _ => return Err(RuntimeError::new("interleave: first argument must be an array")),
7495 };
7496 let arr2 = match &args[1] {
7497 Value::Array(a) => a.borrow().clone(),
7498 _ => return Err(RuntimeError::new("interleave: second argument must be an array")),
7499 };
7500
7501 let mut result = Vec::new();
7502 let mut i1 = arr1.into_iter();
7503 let mut i2 = arr2.into_iter();
7504
7505 loop {
7506 match (i1.next(), i2.next()) {
7507 (Some(a), Some(b)) => {
7508 result.push(a);
7509 result.push(b);
7510 }
7511 (Some(a), None) => {
7512 result.push(a);
7513 result.extend(i1);
7514 break;
7515 }
7516 (None, Some(b)) => {
7517 result.push(b);
7518 result.extend(i2);
7519 break;
7520 }
7521 (None, None) => break,
7522 }
7523 }
7524
7525 Ok(Value::Array(Rc::new(RefCell::new(result))))
7526 });
7527
7528 define(interp, "chunks", Some(2), |_, args| {
7530 let arr = match &args[0] {
7531 Value::Array(a) => a.borrow().clone(),
7532 _ => return Err(RuntimeError::new("chunks: first argument must be an array")),
7533 };
7534 let size = match &args[1] {
7535 Value::Int(n) if *n > 0 => *n as usize,
7536 _ => return Err(RuntimeError::new("chunks: second argument must be a positive integer")),
7537 };
7538
7539 let chunks: Vec<Value> = arr.chunks(size)
7540 .map(|chunk| Value::Array(Rc::new(RefCell::new(chunk.to_vec()))))
7541 .collect();
7542
7543 Ok(Value::Array(Rc::new(RefCell::new(chunks))))
7544 });
7545
7546 define(interp, "windows", Some(2), |_, args| {
7548 let arr = match &args[0] {
7549 Value::Array(a) => a.borrow().clone(),
7550 _ => return Err(RuntimeError::new("windows: first argument must be an array")),
7551 };
7552 let size = match &args[1] {
7553 Value::Int(n) if *n > 0 => *n as usize,
7554 _ => return Err(RuntimeError::new("windows: second argument must be a positive integer")),
7555 };
7556
7557 if arr.len() < size {
7558 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
7559 }
7560
7561 let windows: Vec<Value> = arr.windows(size)
7562 .map(|window| Value::Array(Rc::new(RefCell::new(window.to_vec()))))
7563 .collect();
7564
7565 Ok(Value::Array(Rc::new(RefCell::new(windows))))
7566 });
7567
7568 define(interp, "scan", Some(3), |interp, args| {
7570 let arr = match &args[0] {
7571 Value::Array(a) => a.borrow().clone(),
7572 _ => return Err(RuntimeError::new("scan: first argument must be an array")),
7573 };
7574 let init = args[1].clone();
7575 let func = match &args[2] {
7576 Value::Function(f) => f.clone(),
7577 _ => return Err(RuntimeError::new("scan: third argument must be a function")),
7578 };
7579
7580 let mut results = vec![init.clone()];
7581 let mut acc = init;
7582
7583 for item in arr {
7584 acc = interp.call_function(&func, vec![acc, item])?;
7585 results.push(acc.clone());
7586 }
7587
7588 Ok(Value::Array(Rc::new(RefCell::new(results))))
7589 });
7590
7591 define(interp, "frequencies", Some(1), |_, args| {
7593 let arr = match &args[0] {
7594 Value::Array(a) => a.borrow().clone(),
7595 _ => return Err(RuntimeError::new("frequencies: argument must be an array")),
7596 };
7597
7598 let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
7599 for item in &arr {
7600 let key = format!("{}", item);
7601 *counts.entry(key).or_insert(0) += 1;
7602 }
7603
7604 let result: std::collections::HashMap<String, Value> = counts.into_iter()
7605 .map(|(k, v)| (k, Value::Int(v)))
7606 .collect();
7607
7608 Ok(Value::Map(Rc::new(RefCell::new(result))))
7609 });
7610
7611 define(interp, "dedupe", Some(1), |_, args| {
7613 let arr = match &args[0] {
7614 Value::Array(a) => a.borrow().clone(),
7615 _ => return Err(RuntimeError::new("dedupe: argument must be an array")),
7616 };
7617
7618 let mut result = Vec::new();
7619 let mut prev: Option<Value> = None;
7620
7621 for item in arr {
7622 match &prev {
7623 Some(p) if value_eq(p, &item) => continue,
7624 _ => {
7625 result.push(item.clone());
7626 prev = Some(item);
7627 }
7628 }
7629 }
7630
7631 Ok(Value::Array(Rc::new(RefCell::new(result))))
7632 });
7633
7634 define(interp, "unique", Some(1), |_, args| {
7636 let arr = match &args[0] {
7637 Value::Array(a) => a.borrow().clone(),
7638 _ => return Err(RuntimeError::new("unique: argument must be an array")),
7639 };
7640
7641 let mut seen = std::collections::HashSet::new();
7642 let mut result = Vec::new();
7643
7644 for item in arr {
7645 let key = format!("{}", item);
7646 if seen.insert(key) {
7647 result.push(item);
7648 }
7649 }
7650
7651 Ok(Value::Array(Rc::new(RefCell::new(result))))
7652 });
7653}
7654
7655fn register_ranges(interp: &mut Interpreter) {
7657 define(interp, "range_step", Some(3), |_, args| {
7659 let start = match &args[0] {
7660 Value::Int(n) => *n,
7661 Value::Float(f) => *f as i64,
7662 _ => return Err(RuntimeError::new("range_step: start must be a number")),
7663 };
7664 let end = match &args[1] {
7665 Value::Int(n) => *n,
7666 Value::Float(f) => *f as i64,
7667 _ => return Err(RuntimeError::new("range_step: end must be a number")),
7668 };
7669 let step = match &args[2] {
7670 Value::Int(n) if *n != 0 => *n,
7671 Value::Float(f) if *f != 0.0 => *f as i64,
7672 _ => return Err(RuntimeError::new("range_step: step must be a non-zero number")),
7673 };
7674
7675 let mut result = Vec::new();
7676 if step > 0 {
7677 let mut i = start;
7678 while i < end {
7679 result.push(Value::Int(i));
7680 i += step;
7681 }
7682 } else {
7683 let mut i = start;
7684 while i > end {
7685 result.push(Value::Int(i));
7686 i += step;
7687 }
7688 }
7689
7690 Ok(Value::Array(Rc::new(RefCell::new(result))))
7691 });
7692
7693 define(interp, "linspace", Some(3), |_, args| {
7695 let start = match &args[0] {
7696 Value::Int(n) => *n as f64,
7697 Value::Float(f) => *f,
7698 _ => return Err(RuntimeError::new("linspace: start must be a number")),
7699 };
7700 let end = match &args[1] {
7701 Value::Int(n) => *n as f64,
7702 Value::Float(f) => *f,
7703 _ => return Err(RuntimeError::new("linspace: end must be a number")),
7704 };
7705 let n = match &args[2] {
7706 Value::Int(n) if *n > 0 => *n as usize,
7707 _ => return Err(RuntimeError::new("linspace: count must be a positive integer")),
7708 };
7709
7710 if n == 1 {
7711 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(start)]))));
7712 }
7713
7714 let step = (end - start) / (n - 1) as f64;
7715 let result: Vec<Value> = (0..n)
7716 .map(|i| Value::Float(start + step * i as f64))
7717 .collect();
7718
7719 Ok(Value::Array(Rc::new(RefCell::new(result))))
7720 });
7721
7722 define(interp, "logspace", Some(3), |_, args| {
7724 let start_exp = match &args[0] {
7725 Value::Int(n) => *n as f64,
7726 Value::Float(f) => *f,
7727 _ => return Err(RuntimeError::new("logspace: start exponent must be a number")),
7728 };
7729 let end_exp = match &args[1] {
7730 Value::Int(n) => *n as f64,
7731 Value::Float(f) => *f,
7732 _ => return Err(RuntimeError::new("logspace: end exponent must be a number")),
7733 };
7734 let n = match &args[2] {
7735 Value::Int(n) if *n > 0 => *n as usize,
7736 _ => return Err(RuntimeError::new("logspace: count must be a positive integer")),
7737 };
7738
7739 if n == 1 {
7740 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(10f64.powf(start_exp))]))));
7741 }
7742
7743 let step = (end_exp - start_exp) / (n - 1) as f64;
7744 let result: Vec<Value> = (0..n)
7745 .map(|i| Value::Float(10f64.powf(start_exp + step * i as f64)))
7746 .collect();
7747
7748 Ok(Value::Array(Rc::new(RefCell::new(result))))
7749 });
7750
7751 define(interp, "arange", Some(3), |_, args| {
7753 let start = match &args[0] {
7754 Value::Int(n) => *n as f64,
7755 Value::Float(f) => *f,
7756 _ => return Err(RuntimeError::new("arange: start must be a number")),
7757 };
7758 let stop = match &args[1] {
7759 Value::Int(n) => *n as f64,
7760 Value::Float(f) => *f,
7761 _ => return Err(RuntimeError::new("arange: stop must be a number")),
7762 };
7763 let step = match &args[2] {
7764 Value::Int(n) if *n != 0 => *n as f64,
7765 Value::Float(f) if *f != 0.0 => *f,
7766 _ => return Err(RuntimeError::new("arange: step must be a non-zero number")),
7767 };
7768
7769 let mut result = Vec::new();
7770 if step > 0.0 {
7771 let mut x = start;
7772 while x < stop {
7773 result.push(Value::Float(x));
7774 x += step;
7775 }
7776 } else {
7777 let mut x = start;
7778 while x > stop {
7779 result.push(Value::Float(x));
7780 x += step;
7781 }
7782 }
7783
7784 Ok(Value::Array(Rc::new(RefCell::new(result))))
7785 });
7786
7787 define(interp, "geomspace", Some(3), |_, args| {
7789 let start = match &args[0] {
7790 Value::Int(n) if *n > 0 => *n as f64,
7791 Value::Float(f) if *f > 0.0 => *f,
7792 _ => return Err(RuntimeError::new("geomspace: start must be a positive number")),
7793 };
7794 let end = match &args[1] {
7795 Value::Int(n) if *n > 0 => *n as f64,
7796 Value::Float(f) if *f > 0.0 => *f,
7797 _ => return Err(RuntimeError::new("geomspace: end must be a positive number")),
7798 };
7799 let n = match &args[2] {
7800 Value::Int(n) if *n > 0 => *n as usize,
7801 _ => return Err(RuntimeError::new("geomspace: count must be a positive integer")),
7802 };
7803
7804 if n == 1 {
7805 return Ok(Value::Array(Rc::new(RefCell::new(vec![Value::Float(start)]))));
7806 }
7807
7808 let ratio = (end / start).powf(1.0 / (n - 1) as f64);
7809 let result: Vec<Value> = (0..n)
7810 .map(|i| Value::Float(start * ratio.powi(i as i32)))
7811 .collect();
7812
7813 Ok(Value::Array(Rc::new(RefCell::new(result))))
7814 });
7815}
7816
7817fn register_bitwise(interp: &mut Interpreter) {
7819 define(interp, "bit_and", Some(2), |_, args| {
7820 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_and: arguments must be integers")) };
7821 let b = match &args[1] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_and: arguments must be integers")) };
7822 Ok(Value::Int(a & b))
7823 });
7824
7825 define(interp, "bit_or", Some(2), |_, args| {
7826 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_or: arguments must be integers")) };
7827 let b = match &args[1] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_or: arguments must be integers")) };
7828 Ok(Value::Int(a | b))
7829 });
7830
7831 define(interp, "bit_xor", Some(2), |_, args| {
7832 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")) };
7833 let b = match &args[1] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_xor: arguments must be integers")) };
7834 Ok(Value::Int(a ^ b))
7835 });
7836
7837 define(interp, "bit_not", Some(1), |_, args| {
7838 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_not: argument must be an integer")) };
7839 Ok(Value::Int(!a))
7840 });
7841
7842 define(interp, "bit_shl", Some(2), |_, args| {
7843 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_shl: first argument must be an integer")) };
7844 let b = match &args[1] {
7845 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
7846 _ => return Err(RuntimeError::new("bit_shl: shift amount must be 0-63")),
7847 };
7848 Ok(Value::Int(a << b))
7849 });
7850
7851 define(interp, "bit_shr", Some(2), |_, args| {
7852 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_shr: first argument must be an integer")) };
7853 let b = match &args[1] {
7854 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
7855 _ => return Err(RuntimeError::new("bit_shr: shift amount must be 0-63")),
7856 };
7857 Ok(Value::Int(a >> b))
7858 });
7859
7860 define(interp, "popcount", Some(1), |_, args| {
7861 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("popcount: argument must be an integer")) };
7862 Ok(Value::Int(a.count_ones() as i64))
7863 });
7864
7865 define(interp, "leading_zeros", Some(1), |_, args| {
7866 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("leading_zeros: argument must be an integer")) };
7867 Ok(Value::Int(a.leading_zeros() as i64))
7868 });
7869
7870 define(interp, "trailing_zeros", Some(1), |_, args| {
7871 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("trailing_zeros: argument must be an integer")) };
7872 Ok(Value::Int(a.trailing_zeros() as i64))
7873 });
7874
7875 define(interp, "bit_test", Some(2), |_, args| {
7876 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_test: first argument must be an integer")) };
7877 let pos = match &args[1] {
7878 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
7879 _ => return Err(RuntimeError::new("bit_test: position must be 0-63")),
7880 };
7881 Ok(Value::Bool((a >> pos) & 1 == 1))
7882 });
7883
7884 define(interp, "bit_set", Some(2), |_, args| {
7885 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_set: first argument must be an integer")) };
7886 let pos = match &args[1] {
7887 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
7888 _ => return Err(RuntimeError::new("bit_set: position must be 0-63")),
7889 };
7890 Ok(Value::Int(a | (1 << pos)))
7891 });
7892
7893 define(interp, "bit_clear", Some(2), |_, args| {
7894 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_clear: first argument must be an integer")) };
7895 let pos = match &args[1] {
7896 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
7897 _ => return Err(RuntimeError::new("bit_clear: position must be 0-63")),
7898 };
7899 Ok(Value::Int(a & !(1 << pos)))
7900 });
7901
7902 define(interp, "bit_toggle", Some(2), |_, args| {
7903 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("bit_toggle: first argument must be an integer")) };
7904 let pos = match &args[1] {
7905 Value::Int(n) if *n >= 0 && *n < 64 => *n as u32,
7906 _ => return Err(RuntimeError::new("bit_toggle: position must be 0-63")),
7907 };
7908 Ok(Value::Int(a ^ (1 << pos)))
7909 });
7910
7911 define(interp, "to_binary", Some(1), |_, args| {
7912 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("to_binary: argument must be an integer")) };
7913 Ok(Value::String(Rc::new(format!("{:b}", a))))
7914 });
7915
7916 define(interp, "from_binary", Some(1), |_, args| {
7917 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("from_binary: argument must be a string")) };
7918 match i64::from_str_radix(&s, 2) {
7919 Ok(n) => Ok(Value::Int(n)),
7920 Err(_) => Err(RuntimeError::new("from_binary: invalid binary string")),
7921 }
7922 });
7923
7924 define(interp, "to_hex", Some(1), |_, args| {
7925 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("to_hex: argument must be an integer")) };
7926 Ok(Value::String(Rc::new(format!("{:x}", a))))
7927 });
7928
7929 define(interp, "from_hex", Some(1), |_, args| {
7930 let s = match &args[0] { Value::String(s) => s.trim_start_matches("0x").to_string(), _ => return Err(RuntimeError::new("from_hex: argument must be a string")) };
7931 match i64::from_str_radix(&s, 16) {
7932 Ok(n) => Ok(Value::Int(n)),
7933 Err(_) => Err(RuntimeError::new("from_hex: invalid hex string")),
7934 }
7935 });
7936
7937 define(interp, "to_octal", Some(1), |_, args| {
7938 let a = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("to_octal: argument must be an integer")) };
7939 Ok(Value::String(Rc::new(format!("{:o}", a))))
7940 });
7941
7942 define(interp, "from_octal", Some(1), |_, args| {
7943 let s = match &args[0] { Value::String(s) => s.trim_start_matches("0o").to_string(), _ => return Err(RuntimeError::new("from_octal: argument must be a string")) };
7944 match i64::from_str_radix(&s, 8) {
7945 Ok(n) => Ok(Value::Int(n)),
7946 Err(_) => Err(RuntimeError::new("from_octal: invalid octal string")),
7947 }
7948 });
7949}
7950
7951fn register_format(interp: &mut Interpreter) {
7953 define(interp, "format", None, |_, args| {
7955 if args.is_empty() {
7956 return Err(RuntimeError::new("format: requires at least a format string"));
7957 }
7958 let template = match &args[0] {
7959 Value::String(s) => (**s).clone(),
7960 _ => return Err(RuntimeError::new("format: first argument must be a string")),
7961 };
7962 let mut result = template;
7963 for arg in &args[1..] {
7964 if let Some(pos) = result.find("{}") {
7965 result = format!("{}{}{}", &result[..pos], arg, &result[pos+2..]);
7966 }
7967 }
7968 Ok(Value::String(Rc::new(result)))
7969 });
7970
7971 define(interp, "pad_left", Some(3), |_, args| {
7973 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("pad_left: first argument must be a string")) };
7974 let width = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("pad_left: width must be a non-negative integer")) };
7975 let pad_char = match &args[2] { Value::String(s) if !s.is_empty() => s.chars().next().unwrap(), Value::Char(c) => *c, _ => return Err(RuntimeError::new("pad_left: pad character must be a non-empty string or char")) };
7976 let char_count = s.chars().count();
7977 if char_count >= width { return Ok(Value::String(Rc::new(s))); }
7978 let padding: String = std::iter::repeat(pad_char).take(width - char_count).collect();
7979 Ok(Value::String(Rc::new(format!("{}{}", padding, s))))
7980 });
7981
7982 define(interp, "pad_right", Some(3), |_, args| {
7984 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("pad_right: first argument must be a string")) };
7985 let width = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("pad_right: width must be a non-negative integer")) };
7986 let pad_char = match &args[2] { Value::String(s) if !s.is_empty() => s.chars().next().unwrap(), Value::Char(c) => *c, _ => return Err(RuntimeError::new("pad_right: pad character must be a non-empty string or char")) };
7987 let char_count = s.chars().count();
7988 if char_count >= width { return Ok(Value::String(Rc::new(s))); }
7989 let padding: String = std::iter::repeat(pad_char).take(width - char_count).collect();
7990 Ok(Value::String(Rc::new(format!("{}{}", s, padding))))
7991 });
7992
7993 define(interp, "center", Some(3), |_, args| {
7995 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("center: first argument must be a string")) };
7996 let width = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("center: width must be a non-negative integer")) };
7997 let pad_char = match &args[2] { Value::String(s) if !s.is_empty() => s.chars().next().unwrap(), Value::Char(c) => *c, _ => return Err(RuntimeError::new("center: pad character must be a non-empty string or char")) };
7998 let char_count = s.chars().count();
7999 if char_count >= width { return Ok(Value::String(Rc::new(s))); }
8000 let total_padding = width - char_count;
8001 let left_padding = total_padding / 2;
8002 let right_padding = total_padding - left_padding;
8003 let left: String = std::iter::repeat(pad_char).take(left_padding).collect();
8004 let right: String = std::iter::repeat(pad_char).take(right_padding).collect();
8005 Ok(Value::String(Rc::new(format!("{}{}{}", left, s, right))))
8006 });
8007
8008 define(interp, "number_format", Some(1), |_, args| {
8010 let n = match &args[0] { Value::Int(n) => *n, Value::Float(f) => *f as i64, _ => return Err(RuntimeError::new("number_format: argument must be a number")) };
8011 let s = n.abs().to_string();
8012 let mut result = String::new();
8013 for (i, c) in s.chars().rev().enumerate() {
8014 if i > 0 && i % 3 == 0 { result.push(','); }
8015 result.push(c);
8016 }
8017 let formatted: String = result.chars().rev().collect();
8018 if n < 0 { Ok(Value::String(Rc::new(format!("-{}", formatted)))) } else { Ok(Value::String(Rc::new(formatted))) }
8019 });
8020
8021 define(interp, "ordinal", Some(1), |_, args| {
8023 let n = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("ordinal: argument must be an integer")) };
8024 let suffix = match (n % 10, n % 100) { (1, 11) => "th", (2, 12) => "th", (3, 13) => "th", (1, _) => "st", (2, _) => "nd", (3, _) => "rd", _ => "th" };
8025 Ok(Value::String(Rc::new(format!("{}{}", n, suffix))))
8026 });
8027
8028 define(interp, "pluralize", Some(3), |_, args| {
8030 let count = match &args[0] { Value::Int(n) => *n, _ => return Err(RuntimeError::new("pluralize: first argument must be an integer")) };
8031 let singular = match &args[1] { Value::String(s) => s.clone(), _ => return Err(RuntimeError::new("pluralize: second argument must be a string")) };
8032 let plural = match &args[2] { Value::String(s) => s.clone(), _ => return Err(RuntimeError::new("pluralize: third argument must be a string")) };
8033 if count == 1 || count == -1 { Ok(Value::String(singular)) } else { Ok(Value::String(plural)) }
8034 });
8035
8036 define(interp, "truncate", Some(2), |_, args| {
8038 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("truncate: first argument must be a string")) };
8039 let max_len = match &args[1] { Value::Int(n) if *n >= 0 => *n as usize, _ => return Err(RuntimeError::new("truncate: max length must be a non-negative integer")) };
8040 let char_count = s.chars().count();
8041 if char_count <= max_len { return Ok(Value::String(Rc::new(s))); }
8042 if max_len <= 3 { return Ok(Value::String(Rc::new(s.chars().take(max_len).collect()))); }
8043 let truncated: String = s.chars().take(max_len - 3).collect();
8044 Ok(Value::String(Rc::new(format!("{}...", truncated))))
8045 });
8046
8047 define(interp, "word_wrap", Some(2), |_, args| {
8049 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("word_wrap: first argument must be a string")) };
8050 let width = match &args[1] { Value::Int(n) if *n > 0 => *n as usize, _ => return Err(RuntimeError::new("word_wrap: width must be a positive integer")) };
8051 let mut result = String::new();
8052 let mut line_len = 0;
8053 for word in s.split_whitespace() {
8054 if line_len > 0 && line_len + 1 + word.len() > width { result.push('\n'); line_len = 0; }
8055 else if line_len > 0 { result.push(' '); line_len += 1; }
8056 result.push_str(word);
8057 line_len += word.len();
8058 }
8059 Ok(Value::String(Rc::new(result)))
8060 });
8061
8062 define(interp, "snake_case", Some(1), |_, args| {
8064 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("snake_case: argument must be a string")) };
8065 let mut result = String::new();
8066 for (i, c) in s.chars().enumerate() {
8067 if c.is_uppercase() { if i > 0 { result.push('_'); } result.push(c.to_lowercase().next().unwrap()); }
8068 else if c == ' ' || c == '-' { result.push('_'); }
8069 else { result.push(c); }
8070 }
8071 Ok(Value::String(Rc::new(result)))
8072 });
8073
8074 define(interp, "camel_case", Some(1), |_, args| {
8076 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("camel_case: argument must be a string")) };
8077 let mut result = String::new();
8078 let mut capitalize_next = false;
8079 for (i, c) in s.chars().enumerate() {
8080 if c == '_' || c == '-' || c == ' ' { capitalize_next = true; }
8081 else if capitalize_next { result.push(c.to_uppercase().next().unwrap()); capitalize_next = false; }
8082 else if i == 0 { result.push(c.to_lowercase().next().unwrap()); }
8083 else { result.push(c); }
8084 }
8085 Ok(Value::String(Rc::new(result)))
8086 });
8087
8088 define(interp, "kebab_case", Some(1), |_, args| {
8090 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("kebab_case: argument must be a string")) };
8091 let mut result = String::new();
8092 for (i, c) in s.chars().enumerate() {
8093 if c.is_uppercase() { if i > 0 { result.push('-'); } result.push(c.to_lowercase().next().unwrap()); }
8094 else if c == '_' || c == ' ' { result.push('-'); }
8095 else { result.push(c); }
8096 }
8097 Ok(Value::String(Rc::new(result)))
8098 });
8099
8100 define(interp, "title_case", Some(1), |_, args| {
8102 let s = match &args[0] { Value::String(s) => (**s).clone(), _ => return Err(RuntimeError::new("title_case: argument must be a string")) };
8103 let result: String = s.split_whitespace()
8104 .map(|word| {
8105 let mut chars = word.chars();
8106 match chars.next() { None => String::new(), Some(first) => first.to_uppercase().collect::<String>() + &chars.as_str().to_lowercase() }
8107 })
8108 .collect::<Vec<_>>()
8109 .join(" ");
8110 Ok(Value::String(Rc::new(result)))
8111 });
8112}
8113
8114fn register_pattern(interp: &mut Interpreter) {
8122 define(interp, "type_of", Some(1), |_, args| {
8126 let type_name = match &args[0] {
8127 Value::Null => "null",
8128 Value::Bool(_) => "bool",
8129 Value::Int(_) => "int",
8130 Value::Float(_) => "float",
8131 Value::String(_) => "string",
8132 Value::Char(_) => "char",
8133 Value::Array(_) => "array",
8134 Value::Tuple(_) => "tuple",
8135 Value::Map(_) => "map",
8136 Value::Set(_) => "set",
8137 Value::Struct { name, .. } => return Ok(Value::String(Rc::new(format!("struct:{}", name)))),
8138 Value::Variant { enum_name, variant_name, .. } => return Ok(Value::String(Rc::new(format!("{}::{}", enum_name, variant_name)))),
8139 Value::Function(_) => "function",
8140 Value::BuiltIn(_) => "builtin",
8141 Value::Ref(_) => "ref",
8142 Value::Infinity => "infinity",
8143 Value::Empty => "empty",
8144 Value::Evidential { .. } => "evidential",
8145 Value::Affective { .. } => "affective",
8146 Value::Channel(_) => "channel",
8147 Value::ThreadHandle(_) => "thread",
8148 Value::Actor(_) => "actor",
8149 Value::Future(_) => "future",
8150 };
8151 Ok(Value::String(Rc::new(type_name.to_string())))
8152 });
8153
8154 define(interp, "is_type", Some(2), |_, args| {
8156 let type_name = match &args[1] {
8157 Value::String(s) => s.to_lowercase(),
8158 _ => return Err(RuntimeError::new("is_type: second argument must be type name string")),
8159 };
8160 let matches = match (&args[0], type_name.as_str()) {
8161 (Value::Null, "null") => true,
8162 (Value::Bool(_), "bool") => true,
8163 (Value::Int(_), "int") | (Value::Int(_), "integer") => true,
8164 (Value::Float(_), "float") | (Value::Float(_), "number") => true,
8165 (Value::Int(_), "number") => true,
8166 (Value::String(_), "string") => true,
8167 (Value::Array(_), "array") | (Value::Array(_), "list") => true,
8168 (Value::Tuple(_), "tuple") => true,
8169 (Value::Map(_), "map") | (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
8170 (Value::Set(_), "set") => true,
8171 (Value::Function(_), "function") | (Value::Function(_), "fn") => true,
8172 (Value::BuiltIn(_), "function") | (Value::BuiltIn(_), "builtin") => true,
8173 (Value::Struct { name, .. }, t) => t == "struct" || t == &name.to_lowercase(),
8174 (Value::Variant { enum_name, .. }, t) => t == "variant" || t == "enum" || t == &enum_name.to_lowercase(),
8175 (Value::Channel(_), "channel") => true,
8176 (Value::ThreadHandle(_), "thread") => true,
8177 (Value::Actor(_), "actor") => true,
8178 (Value::Future(_), "future") => true,
8179 _ => false,
8180 };
8181 Ok(Value::Bool(matches))
8182 });
8183
8184 define(interp, "is_null", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Null))));
8186 define(interp, "is_bool", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Bool(_)))));
8187 define(interp, "is_int", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Int(_)))));
8188 define(interp, "is_float", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Float(_)))));
8189 define(interp, "is_number", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Int(_) | Value::Float(_)))));
8190 define(interp, "is_string", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::String(_)))));
8191 define(interp, "is_array", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Array(_)))));
8192 define(interp, "is_tuple", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Tuple(_)))));
8193 define(interp, "is_map", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Map(_)))));
8194 define(interp, "is_set", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Set(_)))));
8195 define(interp, "is_function", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Function(_) | Value::BuiltIn(_)))));
8196 define(interp, "is_struct", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Struct { .. }))));
8197 define(interp, "is_variant", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Variant { .. }))));
8198 define(interp, "is_future", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Future(_)))));
8199 define(interp, "is_channel", Some(1), |_, args| Ok(Value::Bool(matches!(&args[0], Value::Channel(_)))));
8200
8201 define(interp, "is_empty", Some(1), |_, args| {
8203 let empty = match &args[0] {
8204 Value::Null => true,
8205 Value::String(s) => s.is_empty(),
8206 Value::Array(a) => a.borrow().is_empty(),
8207 Value::Tuple(t) => t.is_empty(),
8208 Value::Map(m) => m.borrow().is_empty(),
8209 Value::Set(s) => s.borrow().is_empty(),
8210 _ => false,
8211 };
8212 Ok(Value::Bool(empty))
8213 });
8214
8215 define(interp, "match_regex", Some(2), |_, args| {
8219 let text = match &args[0] {
8220 Value::String(s) => (**s).clone(),
8221 _ => return Err(RuntimeError::new("match_regex: first argument must be a string")),
8222 };
8223 let pattern = match &args[1] {
8224 Value::String(s) => (**s).clone(),
8225 _ => return Err(RuntimeError::new("match_regex: second argument must be a regex pattern string")),
8226 };
8227
8228 let re = match Regex::new(&pattern) {
8229 Ok(r) => r,
8230 Err(e) => return Err(RuntimeError::new(format!("match_regex: invalid regex: {}", e))),
8231 };
8232
8233 match re.captures(&text) {
8234 Some(caps) => {
8235 let mut captures: Vec<Value> = Vec::new();
8236 for i in 0..caps.len() {
8237 if let Some(m) = caps.get(i) {
8238 captures.push(Value::String(Rc::new(m.as_str().to_string())));
8239 } else {
8240 captures.push(Value::Null);
8241 }
8242 }
8243 Ok(Value::Array(Rc::new(RefCell::new(captures))))
8244 }
8245 None => Ok(Value::Null),
8246 }
8247 });
8248
8249 define(interp, "match_all_regex", Some(2), |_, args| {
8251 let text = match &args[0] {
8252 Value::String(s) => (**s).clone(),
8253 _ => return Err(RuntimeError::new("match_all_regex: first argument must be a string")),
8254 };
8255 let pattern = match &args[1] {
8256 Value::String(s) => (**s).clone(),
8257 _ => return Err(RuntimeError::new("match_all_regex: second argument must be a regex pattern string")),
8258 };
8259
8260 let re = match Regex::new(&pattern) {
8261 Ok(r) => r,
8262 Err(e) => return Err(RuntimeError::new(format!("match_all_regex: invalid regex: {}", e))),
8263 };
8264
8265 let matches: Vec<Value> = re.find_iter(&text)
8266 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
8267 .collect();
8268 Ok(Value::Array(Rc::new(RefCell::new(matches))))
8269 });
8270
8271 define(interp, "capture_named", Some(2), |_, args| {
8273 let text = match &args[0] {
8274 Value::String(s) => (**s).clone(),
8275 _ => return Err(RuntimeError::new("capture_named: first argument must be a string")),
8276 };
8277 let pattern = match &args[1] {
8278 Value::String(s) => (**s).clone(),
8279 _ => return Err(RuntimeError::new("capture_named: second argument must be a regex pattern string")),
8280 };
8281
8282 let re = match Regex::new(&pattern) {
8283 Ok(r) => r,
8284 Err(e) => return Err(RuntimeError::new(format!("capture_named: invalid regex: {}", e))),
8285 };
8286
8287 match re.captures(&text) {
8288 Some(caps) => {
8289 let mut result: HashMap<String, Value> = HashMap::new();
8290 for name in re.capture_names().flatten() {
8291 if let Some(m) = caps.name(name) {
8292 result.insert(name.to_string(), Value::String(Rc::new(m.as_str().to_string())));
8293 }
8294 }
8295 Ok(Value::Map(Rc::new(RefCell::new(result))))
8296 }
8297 None => Ok(Value::Null),
8298 }
8299 });
8300
8301 define(interp, "match_struct", Some(2), |_, args| {
8305 let expected_name = match &args[1] {
8306 Value::String(s) => (**s).clone(),
8307 _ => return Err(RuntimeError::new("match_struct: second argument must be struct name string")),
8308 };
8309 match &args[0] {
8310 Value::Struct { name, .. } => Ok(Value::Bool(name == &expected_name)),
8311 _ => Ok(Value::Bool(false)),
8312 }
8313 });
8314
8315 define(interp, "match_variant", Some(3), |_, args| {
8317 let expected_enum = match &args[1] {
8318 Value::String(s) => (**s).clone(),
8319 _ => return Err(RuntimeError::new("match_variant: second argument must be enum name string")),
8320 };
8321 let expected_variant = match &args[2] {
8322 Value::String(s) => (**s).clone(),
8323 _ => return Err(RuntimeError::new("match_variant: third argument must be variant name string")),
8324 };
8325 match &args[0] {
8326 Value::Variant { enum_name, variant_name, .. } => {
8327 Ok(Value::Bool(enum_name == &expected_enum && variant_name == &expected_variant))
8328 }
8329 _ => Ok(Value::Bool(false)),
8330 }
8331 });
8332
8333 define(interp, "get_field", Some(2), |_, args| {
8335 let field_name = match &args[1] {
8336 Value::String(s) => (**s).clone(),
8337 _ => return Err(RuntimeError::new("get_field: second argument must be field name string")),
8338 };
8339 match &args[0] {
8340 Value::Struct { fields, .. } => {
8341 Ok(fields.borrow().get(&field_name).cloned().unwrap_or(Value::Null))
8342 }
8343 Value::Map(m) => {
8344 Ok(m.borrow().get(&field_name).cloned().unwrap_or(Value::Null))
8345 }
8346 _ => Ok(Value::Null),
8347 }
8348 });
8349
8350 define(interp, "has_field", Some(2), |_, args| {
8352 let field_name = match &args[1] {
8353 Value::String(s) => (**s).clone(),
8354 _ => return Err(RuntimeError::new("has_field: second argument must be field name string")),
8355 };
8356 match &args[0] {
8357 Value::Struct { fields, .. } => Ok(Value::Bool(fields.borrow().contains_key(&field_name))),
8358 Value::Map(m) => Ok(Value::Bool(m.borrow().contains_key(&field_name))),
8359 _ => Ok(Value::Bool(false)),
8360 }
8361 });
8362
8363 define(interp, "get_fields", Some(1), |_, args| {
8365 let fields: Vec<Value> = match &args[0] {
8366 Value::Struct { fields, .. } => {
8367 fields.borrow().keys().map(|k| Value::String(Rc::new(k.clone()))).collect()
8368 }
8369 Value::Map(m) => {
8370 m.borrow().keys().map(|k| Value::String(Rc::new(k.clone()))).collect()
8371 }
8372 _ => return Err(RuntimeError::new("get_fields: argument must be struct or map")),
8373 };
8374 Ok(Value::Array(Rc::new(RefCell::new(fields))))
8375 });
8376
8377 define(interp, "struct_name", Some(1), |_, args| {
8379 match &args[0] {
8380 Value::Struct { name, .. } => Ok(Value::String(Rc::new(name.clone()))),
8381 _ => Ok(Value::Null),
8382 }
8383 });
8384
8385 define(interp, "variant_name", Some(1), |_, args| {
8387 match &args[0] {
8388 Value::Variant { variant_name, .. } => Ok(Value::String(Rc::new(variant_name.clone()))),
8389 _ => Ok(Value::Null),
8390 }
8391 });
8392
8393 define(interp, "variant_data", Some(1), |_, args| {
8395 match &args[0] {
8396 Value::Variant { fields, .. } => {
8397 match fields {
8398 Some(f) => Ok(Value::Array(Rc::new(RefCell::new((**f).clone())))),
8399 None => Ok(Value::Null),
8400 }
8401 }
8402 _ => Ok(Value::Null),
8403 }
8404 });
8405
8406 define(interp, "guard", Some(2), |_, args| {
8410 if is_truthy(&args[0]) {
8411 Ok(args[1].clone())
8412 } else {
8413 Ok(Value::Null)
8414 }
8415 });
8416
8417 define(interp, "when", Some(2), |interp, args| {
8419 if is_truthy(&args[0]) {
8420 match &args[1] {
8421 Value::Function(f) => interp.call_function(f, vec![]),
8422 other => Ok(other.clone()),
8423 }
8424 } else {
8425 Ok(Value::Null)
8426 }
8427 });
8428
8429 define(interp, "unless", Some(2), |interp, args| {
8431 if !is_truthy(&args[0]) {
8432 match &args[1] {
8433 Value::Function(f) => interp.call_function(f, vec![]),
8434 other => Ok(other.clone()),
8435 }
8436 } else {
8437 Ok(Value::Null)
8438 }
8439 });
8440
8441 define(interp, "cond", Some(1), |interp, args| {
8444 let clauses = match &args[0] {
8445 Value::Array(a) => a.borrow().clone(),
8446 _ => return Err(RuntimeError::new("cond: argument must be array of [condition, value] pairs")),
8447 };
8448
8449 for clause in clauses {
8450 let pair = match &clause {
8451 Value::Array(a) => a.borrow().clone(),
8452 Value::Tuple(t) => (**t).clone(),
8453 _ => return Err(RuntimeError::new("cond: each clause must be [condition, value] pair")),
8454 };
8455 if pair.len() != 2 {
8456 return Err(RuntimeError::new("cond: each clause must have exactly 2 elements"));
8457 }
8458
8459 if is_truthy(&pair[0]) {
8460 return match &pair[1] {
8461 Value::Function(f) => interp.call_function(f, vec![]),
8462 other => Ok(other.clone()),
8463 };
8464 }
8465 }
8466 Ok(Value::Null)
8467 });
8468
8469 define(interp, "case", Some(2), |interp, args| {
8472 let value = &args[0];
8473 let clauses = match &args[1] {
8474 Value::Array(a) => a.borrow().clone(),
8475 _ => return Err(RuntimeError::new("case: second argument must be array of [pattern, result] pairs")),
8476 };
8477
8478 for clause in clauses {
8479 let pair = match &clause {
8480 Value::Array(a) => a.borrow().clone(),
8481 Value::Tuple(t) => (**t).clone(),
8482 _ => return Err(RuntimeError::new("case: each clause must be [pattern, result] pair")),
8483 };
8484 if pair.len() != 2 {
8485 return Err(RuntimeError::new("case: each clause must have exactly 2 elements"));
8486 }
8487
8488 if value_eq(value, &pair[0]) {
8489 return match &pair[1] {
8490 Value::Function(f) => interp.call_function(f, vec![value.clone()]),
8491 other => Ok(other.clone()),
8492 };
8493 }
8494 }
8495 Ok(Value::Null)
8496 });
8497
8498 define(interp, "destructure_array", Some(2), |_, args| {
8502 let arr = match &args[0] {
8503 Value::Array(a) => a.borrow().clone(),
8504 Value::Tuple(t) => (**t).clone(),
8505 _ => return Err(RuntimeError::new("destructure_array: first argument must be array or tuple")),
8506 };
8507 let indices = match &args[1] {
8508 Value::Array(a) => a.borrow().clone(),
8509 _ => return Err(RuntimeError::new("destructure_array: second argument must be array of indices")),
8510 };
8511
8512 let mut result = Vec::new();
8513 for idx in indices {
8514 match idx {
8515 Value::Int(i) => {
8516 let i = if i < 0 { arr.len() as i64 + i } else { i } as usize;
8517 result.push(arr.get(i).cloned().unwrap_or(Value::Null));
8518 }
8519 _ => result.push(Value::Null),
8520 }
8521 }
8522 Ok(Value::Array(Rc::new(RefCell::new(result))))
8523 });
8524
8525 define(interp, "destructure_map", Some(2), |_, args| {
8527 let map = match &args[0] {
8528 Value::Map(m) => m.borrow().clone(),
8529 Value::Struct { fields, .. } => fields.borrow().clone(),
8530 _ => return Err(RuntimeError::new("destructure_map: first argument must be map or struct")),
8531 };
8532 let keys = match &args[1] {
8533 Value::Array(a) => a.borrow().clone(),
8534 _ => return Err(RuntimeError::new("destructure_map: second argument must be array of keys")),
8535 };
8536
8537 let mut result = Vec::new();
8538 for key in keys {
8539 match key {
8540 Value::String(k) => {
8541 result.push(map.get(&*k).cloned().unwrap_or(Value::Null));
8542 }
8543 _ => result.push(Value::Null),
8544 }
8545 }
8546 Ok(Value::Array(Rc::new(RefCell::new(result))))
8547 });
8548
8549 define(interp, "head_tail", Some(1), |_, args| {
8551 let arr = match &args[0] {
8552 Value::Array(a) => a.borrow().clone(),
8553 _ => return Err(RuntimeError::new("head_tail: argument must be array")),
8554 };
8555
8556 if arr.is_empty() {
8557 Ok(Value::Tuple(Rc::new(vec![Value::Null, Value::Array(Rc::new(RefCell::new(vec![])))])))
8558 } else {
8559 let head = arr[0].clone();
8560 let tail = arr[1..].to_vec();
8561 Ok(Value::Tuple(Rc::new(vec![head, Value::Array(Rc::new(RefCell::new(tail)))])))
8562 }
8563 });
8564
8565 define(interp, "init_last", Some(1), |_, args| {
8567 let arr = match &args[0] {
8568 Value::Array(a) => a.borrow().clone(),
8569 _ => return Err(RuntimeError::new("init_last: argument must be array")),
8570 };
8571
8572 if arr.is_empty() {
8573 Ok(Value::Tuple(Rc::new(vec![Value::Array(Rc::new(RefCell::new(vec![]))), Value::Null])))
8574 } else {
8575 let last = arr[arr.len() - 1].clone();
8576 let init = arr[..arr.len() - 1].to_vec();
8577 Ok(Value::Tuple(Rc::new(vec![Value::Array(Rc::new(RefCell::new(init))), last])))
8578 }
8579 });
8580
8581 define(interp, "split_at", Some(2), |_, args| {
8583 let arr = match &args[0] {
8584 Value::Array(a) => a.borrow().clone(),
8585 _ => return Err(RuntimeError::new("split_at: first argument must be array")),
8586 };
8587 let idx = match &args[1] {
8588 Value::Int(i) => *i as usize,
8589 _ => return Err(RuntimeError::new("split_at: second argument must be integer")),
8590 };
8591
8592 let idx = idx.min(arr.len());
8593 let left = arr[..idx].to_vec();
8594 let right = arr[idx..].to_vec();
8595 Ok(Value::Tuple(Rc::new(vec![
8596 Value::Array(Rc::new(RefCell::new(left))),
8597 Value::Array(Rc::new(RefCell::new(right))),
8598 ])))
8599 });
8600
8601 define(interp, "unwrap_or", Some(2), |_, args| {
8605 if matches!(&args[0], Value::Null) {
8606 Ok(args[1].clone())
8607 } else {
8608 Ok(args[0].clone())
8609 }
8610 });
8611
8612 define(interp, "unwrap_or_else", Some(2), |interp, args| {
8614 if matches!(&args[0], Value::Null) {
8615 match &args[1] {
8616 Value::Function(f) => interp.call_function(f, vec![]),
8617 other => Ok(other.clone()),
8618 }
8619 } else {
8620 Ok(args[0].clone())
8621 }
8622 });
8623
8624 define(interp, "map_or", Some(3), |interp, args| {
8626 if matches!(&args[0], Value::Null) {
8627 Ok(args[1].clone())
8628 } else {
8629 match &args[2] {
8630 Value::Function(f) => interp.call_function(f, vec![args[0].clone()]),
8631 _ => Err(RuntimeError::new("map_or: third argument must be a function")),
8632 }
8633 }
8634 });
8635
8636 define(interp, "coalesce", Some(1), |_, args| {
8638 let values = match &args[0] {
8639 Value::Array(a) => a.borrow().clone(),
8640 _ => return Err(RuntimeError::new("coalesce: argument must be array")),
8641 };
8642
8643 for v in values {
8644 if !matches!(v, Value::Null) {
8645 return Ok(v);
8646 }
8647 }
8648 Ok(Value::Null)
8649 });
8650
8651 define(interp, "deep_eq", Some(2), |_, args| {
8655 Ok(Value::Bool(deep_value_eq(&args[0], &args[1])))
8656 });
8657
8658 define(interp, "same_type", Some(2), |_, args| {
8660 let same = match (&args[0], &args[1]) {
8661 (Value::Null, Value::Null) => true,
8662 (Value::Bool(_), Value::Bool(_)) => true,
8663 (Value::Int(_), Value::Int(_)) => true,
8664 (Value::Float(_), Value::Float(_)) => true,
8665 (Value::String(_), Value::String(_)) => true,
8666 (Value::Array(_), Value::Array(_)) => true,
8667 (Value::Tuple(_), Value::Tuple(_)) => true,
8668 (Value::Map(_), Value::Map(_)) => true,
8669 (Value::Set(_), Value::Set(_)) => true,
8670 (Value::Function(_), Value::Function(_)) => true,
8671 (Value::BuiltIn(_), Value::BuiltIn(_)) => true,
8672 (Value::Struct { name: n1, .. }, Value::Struct { name: n2, .. }) => n1 == n2,
8673 (Value::Variant { enum_name: e1, .. }, Value::Variant { enum_name: e2, .. }) => e1 == e2,
8674 _ => false,
8675 };
8676 Ok(Value::Bool(same))
8677 });
8678
8679 define(interp, "compare", Some(2), |_, args| {
8681 let cmp = match (&args[0], &args[1]) {
8682 (Value::Int(a), Value::Int(b)) => a.cmp(b),
8683 (Value::Float(a), Value::Float(b)) => a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal),
8684 (Value::Int(a), Value::Float(b)) => (*a as f64).partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal),
8685 (Value::Float(a), Value::Int(b)) => a.partial_cmp(&(*b as f64)).unwrap_or(std::cmp::Ordering::Equal),
8686 (Value::String(a), Value::String(b)) => a.cmp(b),
8687 _ => return Err(RuntimeError::new("compare: can only compare numbers or strings")),
8688 };
8689 Ok(Value::Int(match cmp {
8690 std::cmp::Ordering::Less => -1,
8691 std::cmp::Ordering::Equal => 0,
8692 std::cmp::Ordering::Greater => 1,
8693 }))
8694 });
8695
8696 define(interp, "between", Some(3), |_, args| {
8698 let in_range = match (&args[0], &args[1], &args[2]) {
8699 (Value::Int(v), Value::Int(min), Value::Int(max)) => v >= min && v <= max,
8700 (Value::Float(v), Value::Float(min), Value::Float(max)) => v >= min && v <= max,
8701 (Value::Int(v), Value::Int(min), Value::Float(max)) => (*v as f64) >= (*min as f64) && (*v as f64) <= *max,
8702 (Value::Int(v), Value::Float(min), Value::Int(max)) => (*v as f64) >= *min && (*v as f64) <= (*max as f64),
8703 (Value::Float(v), Value::Int(min), Value::Int(max)) => *v >= (*min as f64) && *v <= (*max as f64),
8704 (Value::String(v), Value::String(min), Value::String(max)) => v >= min && v <= max,
8705 _ => return Err(RuntimeError::new("between: arguments must be comparable (numbers or strings)")),
8706 };
8707 Ok(Value::Bool(in_range))
8708 });
8709
8710 define(interp, "clamp", Some(3), |_, args| {
8712 match (&args[0], &args[1], &args[2]) {
8713 (Value::Int(v), Value::Int(min), Value::Int(max)) => {
8714 Ok(Value::Int((*v).max(*min).min(*max)))
8715 }
8716 (Value::Float(v), Value::Float(min), Value::Float(max)) => {
8717 Ok(Value::Float(v.max(*min).min(*max)))
8718 }
8719 (Value::Int(v), Value::Int(min), Value::Float(max)) => {
8720 Ok(Value::Float((*v as f64).max(*min as f64).min(*max)))
8721 }
8722 _ => Err(RuntimeError::new("clamp: arguments must be numbers")),
8723 }
8724 });
8725}
8726
8727fn deep_value_eq(a: &Value, b: &Value) -> bool {
8729 match (a, b) {
8730 (Value::Null, Value::Null) => true,
8731 (Value::Bool(a), Value::Bool(b)) => a == b,
8732 (Value::Int(a), Value::Int(b)) => a == b,
8733 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
8734 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
8735 (*a as f64 - b).abs() < f64::EPSILON
8736 }
8737 (Value::String(a), Value::String(b)) => a == b,
8738 (Value::Array(a), Value::Array(b)) => {
8739 let a = a.borrow();
8740 let b = b.borrow();
8741 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
8742 }
8743 (Value::Tuple(a), Value::Tuple(b)) => {
8744 a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| deep_value_eq(x, y))
8745 }
8746 (Value::Map(a), Value::Map(b)) => {
8747 let a = a.borrow();
8748 let b = b.borrow();
8749 a.len() == b.len() && a.iter().all(|(k, v)| {
8750 b.get(k).map_or(false, |bv| deep_value_eq(v, bv))
8751 })
8752 }
8753 (Value::Set(a), Value::Set(b)) => {
8754 let a = a.borrow();
8755 let b = b.borrow();
8756 a.len() == b.len() && a.iter().all(|k| b.contains(k))
8757 }
8758 (Value::Struct { name: n1, fields: f1 }, Value::Struct { name: n2, fields: f2 }) => {
8759 let f1 = f1.borrow();
8760 let f2 = f2.borrow();
8761 n1 == n2 && f1.len() == f2.len() && f1.iter().all(|(k, v)| {
8762 f2.get(k).map_or(false, |v2| deep_value_eq(v, v2))
8763 })
8764 }
8765 (Value::Variant { enum_name: e1, variant_name: v1, fields: d1 },
8766 Value::Variant { enum_name: e2, variant_name: v2, fields: d2 }) => {
8767 if e1 != e2 || v1 != v2 { return false; }
8768 match (d1, d2) {
8769 (Some(f1), Some(f2)) => f1.len() == f2.len() && f1.iter().zip(f2.iter()).all(|(x, y)| deep_value_eq(x, y)),
8770 (None, None) => true,
8771 _ => false,
8772 }
8773 }
8774 _ => false,
8775 }
8776}
8777
8778fn value_eq(a: &Value, b: &Value) -> bool {
8780 match (a, b) {
8781 (Value::Null, Value::Null) => true,
8782 (Value::Bool(a), Value::Bool(b)) => a == b,
8783 (Value::Int(a), Value::Int(b)) => a == b,
8784 (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
8785 (Value::String(a), Value::String(b)) => a == b,
8786 (Value::Int(a), Value::Float(b)) | (Value::Float(b), Value::Int(a)) => {
8787 (*a as f64 - b).abs() < f64::EPSILON
8788 }
8789 _ => false,
8790 }
8791}
8792
8793fn register_devex(interp: &mut Interpreter) {
8801 define(interp, "debug", Some(1), |_, args| {
8805 let type_name = match &args[0] {
8806 Value::Null => "null".to_string(),
8807 Value::Bool(_) => "bool".to_string(),
8808 Value::Int(_) => "int".to_string(),
8809 Value::Float(_) => "float".to_string(),
8810 Value::String(_) => "string".to_string(),
8811 Value::Char(_) => "char".to_string(),
8812 Value::Array(a) => format!("array[{}]", a.borrow().len()),
8813 Value::Tuple(t) => format!("tuple[{}]", t.len()),
8814 Value::Map(m) => format!("map[{}]", m.borrow().len()),
8815 Value::Set(s) => format!("set[{}]", s.borrow().len()),
8816 Value::Struct { name, fields } => format!("struct {}[{}]", name, fields.borrow().len()),
8817 Value::Variant { enum_name, variant_name, .. } => format!("{}::{}", enum_name, variant_name),
8818 Value::Function(_) => "function".to_string(),
8819 Value::BuiltIn(_) => "builtin".to_string(),
8820 Value::Ref(_) => "ref".to_string(),
8821 Value::Infinity => "infinity".to_string(),
8822 Value::Empty => "empty".to_string(),
8823 Value::Evidential { evidence, .. } => format!("evidential[{:?}]", evidence),
8824 Value::Affective { affect, .. } => format!("affective[sarcasm={}]", affect.sarcasm),
8825 Value::Channel(_) => "channel".to_string(),
8826 Value::ThreadHandle(_) => "thread".to_string(),
8827 Value::Actor(_) => "actor".to_string(),
8828 Value::Future(_) => "future".to_string(),
8829 };
8830 let value_repr = format_value_debug(&args[0]);
8831 println!("[DEBUG] {}: {}", type_name, value_repr);
8832 Ok(args[0].clone())
8833 });
8834
8835 define(interp, "inspect", Some(1), |_, args| {
8837 Ok(Value::String(Rc::new(format_value_debug(&args[0]))))
8838 });
8839
8840 define(interp, "dbg", Some(1), |_, args| {
8842 println!("{}", format_value_debug(&args[0]));
8843 Ok(args[0].clone())
8844 });
8845
8846 define(interp, "trace", Some(2), |_, args| {
8848 let label = match &args[0] {
8849 Value::String(s) => (**s).clone(),
8850 _ => format_value_debug(&args[0]),
8851 };
8852 println!("[TRACE] {}: {}", label, format_value_debug(&args[1]));
8853 Ok(args[1].clone())
8854 });
8855
8856 define(interp, "pp", Some(1), |_, args| {
8858 println!("{}", pretty_print_value(&args[0], 0));
8859 Ok(Value::Null)
8860 });
8861
8862 define(interp, "assert_eq", Some(2), |_, args| {
8866 if deep_value_eq(&args[0], &args[1]) {
8867 Ok(Value::Bool(true))
8868 } else {
8869 Err(RuntimeError::new(format!(
8870 "Assertion failed: expected {} to equal {}",
8871 format_value_debug(&args[0]),
8872 format_value_debug(&args[1])
8873 )))
8874 }
8875 });
8876
8877 define(interp, "assert_ne", Some(2), |_, args| {
8879 if !deep_value_eq(&args[0], &args[1]) {
8880 Ok(Value::Bool(true))
8881 } else {
8882 Err(RuntimeError::new(format!(
8883 "Assertion failed: expected {} to not equal {}",
8884 format_value_debug(&args[0]),
8885 format_value_debug(&args[1])
8886 )))
8887 }
8888 });
8889
8890 define(interp, "assert_lt", Some(2), |_, args| {
8892 let cmp = devex_compare(&args[0], &args[1])?;
8893 if cmp < 0 {
8894 Ok(Value::Bool(true))
8895 } else {
8896 Err(RuntimeError::new(format!(
8897 "Assertion failed: expected {} < {}",
8898 format_value_debug(&args[0]),
8899 format_value_debug(&args[1])
8900 )))
8901 }
8902 });
8903
8904 define(interp, "assert_le", Some(2), |_, args| {
8906 let cmp = devex_compare(&args[0], &args[1])?;
8907 if cmp <= 0 {
8908 Ok(Value::Bool(true))
8909 } else {
8910 Err(RuntimeError::new(format!(
8911 "Assertion failed: expected {} <= {}",
8912 format_value_debug(&args[0]),
8913 format_value_debug(&args[1])
8914 )))
8915 }
8916 });
8917
8918 define(interp, "assert_gt", Some(2), |_, args| {
8920 let cmp = devex_compare(&args[0], &args[1])?;
8921 if cmp > 0 {
8922 Ok(Value::Bool(true))
8923 } else {
8924 Err(RuntimeError::new(format!(
8925 "Assertion failed: expected {} > {}",
8926 format_value_debug(&args[0]),
8927 format_value_debug(&args[1])
8928 )))
8929 }
8930 });
8931
8932 define(interp, "assert_ge", Some(2), |_, args| {
8934 let cmp = devex_compare(&args[0], &args[1])?;
8935 if cmp >= 0 {
8936 Ok(Value::Bool(true))
8937 } else {
8938 Err(RuntimeError::new(format!(
8939 "Assertion failed: expected {} >= {}",
8940 format_value_debug(&args[0]),
8941 format_value_debug(&args[1])
8942 )))
8943 }
8944 });
8945
8946 define(interp, "assert_true", Some(1), |_, args| {
8948 if is_truthy(&args[0]) {
8949 Ok(Value::Bool(true))
8950 } else {
8951 Err(RuntimeError::new(format!(
8952 "Assertion failed: expected {} to be truthy",
8953 format_value_debug(&args[0])
8954 )))
8955 }
8956 });
8957
8958 define(interp, "assert_false", Some(1), |_, args| {
8960 if !is_truthy(&args[0]) {
8961 Ok(Value::Bool(true))
8962 } else {
8963 Err(RuntimeError::new(format!(
8964 "Assertion failed: expected {} to be falsy",
8965 format_value_debug(&args[0])
8966 )))
8967 }
8968 });
8969
8970 define(interp, "assert_null", Some(1), |_, args| {
8972 if matches!(&args[0], Value::Null) {
8973 Ok(Value::Bool(true))
8974 } else {
8975 Err(RuntimeError::new(format!(
8976 "Assertion failed: expected null, got {}",
8977 format_value_debug(&args[0])
8978 )))
8979 }
8980 });
8981
8982 define(interp, "assert_not_null", Some(1), |_, args| {
8984 if !matches!(&args[0], Value::Null) {
8985 Ok(Value::Bool(true))
8986 } else {
8987 Err(RuntimeError::new("Assertion failed: expected non-null value, got null"))
8988 }
8989 });
8990
8991 define(interp, "assert_type", Some(2), |_, args| {
8993 let expected = match &args[1] {
8994 Value::String(s) => s.to_lowercase(),
8995 _ => return Err(RuntimeError::new("assert_type: second argument must be type name string")),
8996 };
8997 let actual = get_type_name(&args[0]).to_lowercase();
8998 if actual == expected || matches_type_alias(&args[0], &expected) {
8999 Ok(Value::Bool(true))
9000 } else {
9001 Err(RuntimeError::new(format!(
9002 "Assertion failed: expected type '{}', got '{}'",
9003 expected, actual
9004 )))
9005 }
9006 });
9007
9008 define(interp, "assert_contains", Some(2), |_, args| {
9010 let contains = match &args[0] {
9011 Value::Array(a) => a.borrow().iter().any(|v| deep_value_eq(v, &args[1])),
9012 Value::String(s) => {
9013 if let Value::String(sub) = &args[1] {
9014 s.contains(&**sub)
9015 } else {
9016 false
9017 }
9018 }
9019 Value::Map(m) => {
9020 if let Value::String(k) = &args[1] {
9021 m.borrow().contains_key(&**k)
9022 } else {
9023 false
9024 }
9025 }
9026 Value::Set(s) => {
9027 if let Value::String(k) = &args[1] {
9028 s.borrow().contains(&**k)
9029 } else {
9030 false
9031 }
9032 }
9033 _ => false,
9034 };
9035 if contains {
9036 Ok(Value::Bool(true))
9037 } else {
9038 Err(RuntimeError::new(format!(
9039 "Assertion failed: {} does not contain {}",
9040 format_value_debug(&args[0]),
9041 format_value_debug(&args[1])
9042 )))
9043 }
9044 });
9045
9046 define(interp, "assert_len", Some(2), |_, args| {
9048 let expected = match &args[1] {
9049 Value::Int(n) => *n as usize,
9050 _ => return Err(RuntimeError::new("assert_len: second argument must be integer")),
9051 };
9052 let actual = match &args[0] {
9053 Value::String(s) => s.len(),
9054 Value::Array(a) => a.borrow().len(),
9055 Value::Tuple(t) => t.len(),
9056 Value::Map(m) => m.borrow().len(),
9057 Value::Set(s) => s.borrow().len(),
9058 _ => return Err(RuntimeError::new("assert_len: first argument must be a collection")),
9059 };
9060 if actual == expected {
9061 Ok(Value::Bool(true))
9062 } else {
9063 Err(RuntimeError::new(format!(
9064 "Assertion failed: expected length {}, got {}",
9065 expected, actual
9066 )))
9067 }
9068 });
9069
9070 define(interp, "assert_match", Some(2), |_, args| {
9072 let text = match &args[0] {
9073 Value::String(s) => (**s).clone(),
9074 _ => return Err(RuntimeError::new("assert_match: first argument must be string")),
9075 };
9076 let pattern = match &args[1] {
9077 Value::String(s) => (**s).clone(),
9078 _ => return Err(RuntimeError::new("assert_match: second argument must be regex pattern")),
9079 };
9080 let re = Regex::new(&pattern).map_err(|e| RuntimeError::new(format!("Invalid regex: {}", e)))?;
9081 if re.is_match(&text) {
9082 Ok(Value::Bool(true))
9083 } else {
9084 Err(RuntimeError::new(format!(
9085 "Assertion failed: '{}' does not match pattern '{}'",
9086 text, pattern
9087 )))
9088 }
9089 });
9090
9091 define(interp, "test", Some(2), |interp, args| {
9095 let name = match &args[0] {
9096 Value::String(s) => (**s).clone(),
9097 _ => return Err(RuntimeError::new("test: first argument must be test name string")),
9098 };
9099 let func = match &args[1] {
9100 Value::Function(f) => f.clone(),
9101 _ => return Err(RuntimeError::new("test: second argument must be test function")),
9102 };
9103
9104 let start = Instant::now();
9105 let result = interp.call_function(&func, vec![]);
9106 let elapsed = start.elapsed();
9107
9108 match result {
9109 Ok(_) => {
9110 println!("✓ {} ({:.2}ms)", name, elapsed.as_secs_f64() * 1000.0);
9111 Ok(Value::Bool(true))
9112 }
9113 Err(e) => {
9114 println!("✗ {} ({:.2}ms): {}", name, elapsed.as_secs_f64() * 1000.0, e);
9115 Ok(Value::Bool(false))
9116 }
9117 }
9118 });
9119
9120 define(interp, "skip", Some(1), |_, args| {
9122 let reason = match &args[0] {
9123 Value::String(s) => (**s).clone(),
9124 _ => "skipped".to_string(),
9125 };
9126 println!("⊘ {}", reason);
9127 Ok(Value::Null)
9128 });
9129
9130 define(interp, "profile", Some(1), |interp, args| {
9134 let func = match &args[0] {
9135 Value::Function(f) => f.clone(),
9136 _ => return Err(RuntimeError::new("profile: argument must be function")),
9137 };
9138
9139 let start = Instant::now();
9140 let result = interp.call_function(&func, vec![])?;
9141 let elapsed = start.elapsed();
9142
9143 let mut timing = HashMap::new();
9144 timing.insert("ms".to_string(), Value::Float(elapsed.as_secs_f64() * 1000.0));
9145 timing.insert("us".to_string(), Value::Float(elapsed.as_micros() as f64));
9146 timing.insert("ns".to_string(), Value::Int(elapsed.as_nanos() as i64));
9147
9148 Ok(Value::Tuple(Rc::new(vec![
9149 result,
9150 Value::Map(Rc::new(RefCell::new(timing))),
9151 ])))
9152 });
9153
9154 define(interp, "measure", Some(2), |interp, args| {
9156 let func = match &args[0] {
9157 Value::Function(f) => f.clone(),
9158 _ => return Err(RuntimeError::new("measure: first argument must be function")),
9159 };
9160 let iterations = match &args[1] {
9161 Value::Int(n) => *n as usize,
9162 _ => return Err(RuntimeError::new("measure: second argument must be iteration count")),
9163 };
9164
9165 let mut times: Vec<f64> = Vec::new();
9166 let mut last_result = Value::Null;
9167
9168 for _ in 0..iterations {
9169 let start = Instant::now();
9170 last_result = interp.call_function(&func, vec![])?;
9171 times.push(start.elapsed().as_secs_f64() * 1000.0);
9172 }
9173
9174 let sum: f64 = times.iter().sum();
9175 let avg = sum / iterations as f64;
9176 let min = times.iter().cloned().fold(f64::INFINITY, f64::min);
9177 let max = times.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
9178
9179 let variance: f64 = times.iter().map(|t| (t - avg).powi(2)).sum::<f64>() / iterations as f64;
9180 let stddev = variance.sqrt();
9181
9182 let mut stats = HashMap::new();
9183 stats.insert("iterations".to_string(), Value::Int(iterations as i64));
9184 stats.insert("total_ms".to_string(), Value::Float(sum));
9185 stats.insert("avg_ms".to_string(), Value::Float(avg));
9186 stats.insert("min_ms".to_string(), Value::Float(min));
9187 stats.insert("max_ms".to_string(), Value::Float(max));
9188 stats.insert("stddev_ms".to_string(), Value::Float(stddev));
9189
9190 Ok(Value::Tuple(Rc::new(vec![
9191 last_result,
9192 Value::Map(Rc::new(RefCell::new(stats))),
9193 ])))
9194 });
9195
9196 define(interp, "help", Some(1), |_, args| {
9200 let name = match &args[0] {
9201 Value::String(s) => (**s).clone(),
9202 Value::BuiltIn(f) => f.name.clone(),
9203 _ => return Err(RuntimeError::new("help: argument must be function name or builtin")),
9204 };
9205
9206 let doc = get_function_doc(&name);
9208 Ok(Value::String(Rc::new(doc)))
9209 });
9210
9211 define(interp, "list_builtins", Some(0), |_, _| {
9213 let categories = vec![
9214 "Core: print, println, assert, panic, len, type_of",
9215 "Math: abs, floor, ceil, round, sqrt, pow, log, sin, cos, tan",
9216 "Collections: map, filter, reduce, zip, flatten, first, last, sort, reverse",
9217 "Strings: upper, lower, trim, split, join, contains, replace, format",
9218 "IO: read_file, write_file, file_exists, read_line",
9219 "Time: now, sleep, timestamp, format_time",
9220 "JSON: json_parse, json_stringify",
9221 "Crypto: sha256, sha512, md5, base64_encode, base64_decode",
9222 "Regex: regex_match, regex_replace, regex_split",
9223 "Pattern: type_of, is_type, match_regex, match_struct, guard, when",
9224 "DevEx: debug, inspect, trace, assert_eq, assert_ne, test, profile",
9225 ];
9226 let values: Vec<Value> = categories.iter()
9227 .map(|s| Value::String(Rc::new(s.to_string())))
9228 .collect();
9229 Ok(Value::Array(Rc::new(RefCell::new(values))))
9230 });
9231
9232 define(interp, "todo", Some(0), |_, _| {
9236 Err(RuntimeError::new("not yet implemented"))
9237 });
9238
9239 define(interp, "unreachable", Some(0), |_, _| {
9241 Err(RuntimeError::new("reached unreachable code"))
9242 });
9243
9244 define(interp, "unimplemented", Some(1), |_, args| {
9246 let msg = match &args[0] {
9247 Value::String(s) => (**s).clone(),
9248 _ => "unimplemented".to_string(),
9249 };
9250 Err(RuntimeError::new(format!("unimplemented: {}", msg)))
9251 });
9252
9253 define(interp, "deprecated", Some(2), |_, args| {
9255 let msg = match &args[0] {
9256 Value::String(s) => (**s).clone(),
9257 _ => "deprecated".to_string(),
9258 };
9259 eprintln!("[DEPRECATED] {}", msg);
9260 Ok(args[1].clone())
9261 });
9262
9263 define(interp, "version", Some(0), |_, _| {
9265 let mut info = HashMap::new();
9266 info.insert("sigil".to_string(), Value::String(Rc::new("0.1.0".to_string())));
9267 info.insert("stdlib".to_string(), Value::String(Rc::new("7.0".to_string())));
9268 info.insert("phase".to_string(), Value::String(Rc::new("Phase 7 - DevEx".to_string())));
9269 Ok(Value::Map(Rc::new(RefCell::new(info))))
9270 });
9271}
9272
9273fn format_value_debug(value: &Value) -> String {
9275 match value {
9276 Value::Null => "null".to_string(),
9277 Value::Bool(b) => b.to_string(),
9278 Value::Int(n) => n.to_string(),
9279 Value::Float(f) => format!("{:.6}", f),
9280 Value::String(s) => format!("\"{}\"", s),
9281 Value::Char(c) => format!("'{}'", c),
9282 Value::Array(a) => {
9283 let items: Vec<String> = a.borrow().iter().take(10).map(format_value_debug).collect();
9284 if a.borrow().len() > 10 {
9285 format!("[{}, ... ({} more)]", items.join(", "), a.borrow().len() - 10)
9286 } else {
9287 format!("[{}]", items.join(", "))
9288 }
9289 }
9290 Value::Tuple(t) => {
9291 let items: Vec<String> = t.iter().map(format_value_debug).collect();
9292 format!("({})", items.join(", "))
9293 }
9294 Value::Map(m) => {
9295 let items: Vec<String> = m.borrow().iter().take(5)
9296 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
9297 .collect();
9298 if m.borrow().len() > 5 {
9299 format!("{{{}, ... ({} more)}}", items.join(", "), m.borrow().len() - 5)
9300 } else {
9301 format!("{{{}}}", items.join(", "))
9302 }
9303 }
9304 Value::Set(s) => {
9305 let items: Vec<String> = s.borrow().iter().take(5).cloned().collect();
9306 if s.borrow().len() > 5 {
9307 format!("#{{{}, ... ({} more)}}", items.join(", "), s.borrow().len() - 5)
9308 } else {
9309 format!("#{{{}}}", items.join(", "))
9310 }
9311 }
9312 Value::Struct { name, fields } => {
9313 let items: Vec<String> = fields.borrow().iter()
9314 .map(|(k, v)| format!("{}: {}", k, format_value_debug(v)))
9315 .collect();
9316 format!("{} {{{}}}", name, items.join(", "))
9317 }
9318 Value::Variant { enum_name, variant_name, fields } => {
9319 match fields {
9320 Some(f) => {
9321 let items: Vec<String> = f.iter().map(format_value_debug).collect();
9322 format!("{}::{}({})", enum_name, variant_name, items.join(", "))
9323 }
9324 None => format!("{}::{}", enum_name, variant_name),
9325 }
9326 }
9327 Value::Function(_) => "<function>".to_string(),
9328 Value::BuiltIn(f) => format!("<builtin:{}>", f.name),
9329 Value::Ref(r) => format!("&{}", format_value_debug(&r.borrow())),
9330 Value::Infinity => "∞".to_string(),
9331 Value::Empty => "∅".to_string(),
9332 Value::Evidential { value, evidence } => format!("{:?}({})", evidence, format_value_debug(value)),
9333 Value::Affective { value, affect } => {
9334 let mut markers = Vec::new();
9335 if let Some(s) = &affect.sentiment { markers.push(format!("{:?}", s)); }
9336 if affect.sarcasm { markers.push("sarcasm".to_string()); }
9337 if let Some(i) = &affect.intensity { markers.push(format!("{:?}", i)); }
9338 if let Some(f) = &affect.formality { markers.push(format!("{:?}", f)); }
9339 if let Some(e) = &affect.emotion { markers.push(format!("{:?}", e)); }
9340 if let Some(c) = &affect.confidence { markers.push(format!("{:?}", c)); }
9341 format!("{}[{}]", format_value_debug(value), markers.join(","))
9342 }
9343 Value::Channel(_) => "<channel>".to_string(),
9344 Value::ThreadHandle(_) => "<thread>".to_string(),
9345 Value::Actor(_) => "<actor>".to_string(),
9346 Value::Future(_) => "<future>".to_string(),
9347 }
9348}
9349
9350fn pretty_print_value(value: &Value, indent: usize) -> String {
9352 let prefix = " ".repeat(indent);
9353 match value {
9354 Value::Array(a) => {
9355 if a.borrow().is_empty() {
9356 "[]".to_string()
9357 } else {
9358 let items: Vec<String> = a.borrow().iter()
9359 .map(|v| format!("{}{}", " ".repeat(indent + 1), pretty_print_value(v, indent + 1)))
9360 .collect();
9361 format!("[\n{}\n{}]", items.join(",\n"), prefix)
9362 }
9363 }
9364 Value::Map(m) => {
9365 if m.borrow().is_empty() {
9366 "{}".to_string()
9367 } else {
9368 let items: Vec<String> = m.borrow().iter()
9369 .map(|(k, v)| format!("{}\"{}\": {}", " ".repeat(indent + 1), k, pretty_print_value(v, indent + 1)))
9370 .collect();
9371 format!("{{\n{}\n{}}}", items.join(",\n"), prefix)
9372 }
9373 }
9374 Value::Struct { name, fields } => {
9375 if fields.borrow().is_empty() {
9376 format!("{} {{}}", name)
9377 } else {
9378 let items: Vec<String> = fields.borrow().iter()
9379 .map(|(k, v)| format!("{}{}: {}", " ".repeat(indent + 1), k, pretty_print_value(v, indent + 1)))
9380 .collect();
9381 format!("{} {{\n{}\n{}}}", name, items.join(",\n"), prefix)
9382 }
9383 }
9384 _ => format_value_debug(value),
9385 }
9386}
9387
9388fn devex_compare(a: &Value, b: &Value) -> Result<i64, RuntimeError> {
9390 match (a, b) {
9391 (Value::Int(a), Value::Int(b)) => Ok(if a < b { -1 } else if a > b { 1 } else { 0 }),
9392 (Value::Float(a), Value::Float(b)) => Ok(if a < b { -1 } else if a > b { 1 } else { 0 }),
9393 (Value::Int(a), Value::Float(b)) => {
9394 let a = *a as f64;
9395 Ok(if a < *b { -1 } else if a > *b { 1 } else { 0 })
9396 }
9397 (Value::Float(a), Value::Int(b)) => {
9398 let b = *b as f64;
9399 Ok(if *a < b { -1 } else if *a > b { 1 } else { 0 })
9400 }
9401 (Value::String(a), Value::String(b)) => Ok(if a < b { -1 } else if a > b { 1 } else { 0 }),
9402 _ => Err(RuntimeError::new("cannot compare these types")),
9403 }
9404}
9405
9406fn get_type_name(value: &Value) -> String {
9408 match value {
9409 Value::Null => "null".to_string(),
9410 Value::Bool(_) => "bool".to_string(),
9411 Value::Int(_) => "int".to_string(),
9412 Value::Float(_) => "float".to_string(),
9413 Value::String(_) => "string".to_string(),
9414 Value::Char(_) => "char".to_string(),
9415 Value::Array(_) => "array".to_string(),
9416 Value::Tuple(_) => "tuple".to_string(),
9417 Value::Map(_) => "map".to_string(),
9418 Value::Set(_) => "set".to_string(),
9419 Value::Struct { name, .. } => name.clone(),
9420 Value::Variant { enum_name, .. } => enum_name.clone(),
9421 Value::Function(_) => "function".to_string(),
9422 Value::BuiltIn(_) => "builtin".to_string(),
9423 Value::Ref(_) => "ref".to_string(),
9424 Value::Infinity => "infinity".to_string(),
9425 Value::Empty => "empty".to_string(),
9426 Value::Evidential { .. } => "evidential".to_string(),
9427 Value::Affective { .. } => "affective".to_string(),
9428 Value::Channel(_) => "channel".to_string(),
9429 Value::ThreadHandle(_) => "thread".to_string(),
9430 Value::Actor(_) => "actor".to_string(),
9431 Value::Future(_) => "future".to_string(),
9432 }
9433}
9434
9435fn matches_type_alias(value: &Value, type_name: &str) -> bool {
9437 match (value, type_name) {
9438 (Value::Int(_), "number") | (Value::Float(_), "number") => true,
9439 (Value::Int(_), "integer") => true,
9440 (Value::Array(_), "list") => true,
9441 (Value::Map(_), "dict") | (Value::Map(_), "object") => true,
9442 (Value::Function(_), "fn") | (Value::BuiltIn(_), "fn") => true,
9443 (Value::BuiltIn(_), "function") => true,
9444 _ => false,
9445 }
9446}
9447
9448fn get_function_doc(name: &str) -> String {
9450 match name {
9451 "print" => "print(value) - Print value to stdout".to_string(),
9452 "println" => "println(value) - Print value with newline".to_string(),
9453 "len" => "len(collection) - Get length of string, array, map, or set".to_string(),
9454 "type_of" => "type_of(value) - Get type name as string".to_string(),
9455 "assert" => "assert(condition) - Assert condition is truthy, panic if false".to_string(),
9456 "assert_eq" => "assert_eq(a, b) - Assert two values are deeply equal".to_string(),
9457 "debug" => "debug(value) - Print value with type info and return it".to_string(),
9458 "map" => "map(array, fn) - Apply function to each element".to_string(),
9459 "filter" => "filter(array, fn) - Keep elements where predicate is true".to_string(),
9460 "reduce" => "reduce(array, init, fn) - Fold array with function".to_string(),
9461 "range" => "range(start, end) - Create array of integers from start to end".to_string(),
9462 "sum" => "sum(array) - Sum all numeric elements".to_string(),
9463 "product" => "product(array) - Multiply all numeric elements".to_string(),
9464 "sort" => "sort(array) - Sort array in ascending order".to_string(),
9465 "reverse" => "reverse(array) - Reverse array order".to_string(),
9466 "join" => "join(array, sep) - Join array elements with separator".to_string(),
9467 "split" => "split(string, sep) - Split string by separator".to_string(),
9468 "trim" => "trim(string) - Remove leading/trailing whitespace".to_string(),
9469 "upper" => "upper(string) - Convert to uppercase".to_string(),
9470 "lower" => "lower(string) - Convert to lowercase".to_string(),
9471 _ => format!("No documentation available for '{}'", name),
9472 }
9473}
9474
9475fn register_soa(interp: &mut Interpreter) {
9487 define(interp, "aos_to_soa", Some(2), |_, args| {
9490 let arr = match &args[0] {
9491 Value::Array(arr) => arr.borrow().clone(),
9492 _ => return Err(RuntimeError::new("aos_to_soa: first argument must be array")),
9493 };
9494 let keys = match &args[1] {
9495 Value::Array(keys) => keys.borrow().clone(),
9496 _ => return Err(RuntimeError::new("aos_to_soa: second argument must be array of keys")),
9497 };
9498
9499 if arr.is_empty() {
9500 let mut result = HashMap::new();
9502 for key in &keys {
9503 if let Value::String(k) = key {
9504 result.insert((**k).clone(), Value::Array(Rc::new(RefCell::new(vec![]))));
9505 }
9506 }
9507 return Ok(Value::Map(Rc::new(RefCell::new(result))));
9508 }
9509
9510 let key_names: Vec<String> = keys.iter()
9512 .filter_map(|k| {
9513 if let Value::String(s) = k { Some((**s).clone()) } else { None }
9514 })
9515 .collect();
9516
9517 let mut soa: HashMap<String, Vec<Value>> = HashMap::new();
9519 for key in &key_names {
9520 soa.insert(key.clone(), Vec::with_capacity(arr.len()));
9521 }
9522
9523 for item in &arr {
9525 match item {
9526 Value::Map(map) => {
9527 let map = map.borrow();
9528 for key in &key_names {
9529 let val = map.get(key).cloned().unwrap_or(Value::Null);
9530 soa.get_mut(key).unwrap().push(val);
9531 }
9532 }
9533 Value::Struct { fields, .. } => {
9534 let fields = fields.borrow();
9535 for key in &key_names {
9536 let val = fields.get(key).cloned().unwrap_or(Value::Null);
9537 soa.get_mut(key).unwrap().push(val);
9538 }
9539 }
9540 _ => return Err(RuntimeError::new("aos_to_soa: array must contain structs or maps")),
9541 }
9542 }
9543
9544 let result: HashMap<String, Value> = soa.into_iter()
9546 .map(|(k, v)| (k, Value::Array(Rc::new(RefCell::new(v)))))
9547 .collect();
9548
9549 Ok(Value::Map(Rc::new(RefCell::new(result))))
9550 });
9551
9552 define(interp, "soa_to_aos", Some(1), |_, args| {
9555 let soa = match &args[0] {
9556 Value::Map(map) => map.borrow().clone(),
9557 _ => return Err(RuntimeError::new("soa_to_aos: argument must be map")),
9558 };
9559
9560 if soa.is_empty() {
9561 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9562 }
9563
9564 let len = soa.values().next()
9566 .and_then(|v| if let Value::Array(arr) = v { Some(arr.borrow().len()) } else { None })
9567 .unwrap_or(0);
9568
9569 let mut aos: Vec<Value> = Vec::with_capacity(len);
9571 for i in 0..len {
9572 let mut fields = HashMap::new();
9573 for (key, value) in &soa {
9574 if let Value::Array(arr) = value {
9575 let arr = arr.borrow();
9576 if i < arr.len() {
9577 fields.insert(key.clone(), arr[i].clone());
9578 }
9579 }
9580 }
9581 aos.push(Value::Map(Rc::new(RefCell::new(fields))));
9582 }
9583
9584 Ok(Value::Array(Rc::new(RefCell::new(aos))))
9585 });
9586
9587 define(interp, "soa_map", Some(3), |interp, args| {
9590 let mut soa = match &args[0] {
9591 Value::Map(map) => map.borrow().clone(),
9592 _ => return Err(RuntimeError::new("soa_map: first argument must be SoA map")),
9593 };
9594 let key = match &args[1] {
9595 Value::String(s) => (**s).clone(),
9596 _ => return Err(RuntimeError::new("soa_map: second argument must be key string")),
9597 };
9598 let func = match &args[2] {
9599 Value::Function(f) => f.clone(),
9600 _ => return Err(RuntimeError::new("soa_map: third argument must be a function")),
9601 };
9602
9603 let arr = soa.get(&key)
9605 .ok_or_else(|| RuntimeError::new(format!("soa_map: key '{}' not found", key)))?;
9606
9607 let arr_vals = match arr {
9608 Value::Array(a) => a.borrow().clone(),
9609 _ => return Err(RuntimeError::new("soa_map: key must map to array")),
9610 };
9611
9612 let results: Vec<Value> = arr_vals.iter()
9614 .map(|val| interp.call_function(&func, vec![val.clone()]))
9615 .collect::<Result<_, _>>()?;
9616
9617 soa.insert(key, Value::Array(Rc::new(RefCell::new(results))));
9619
9620 Ok(Value::Map(Rc::new(RefCell::new(soa))))
9621 });
9622
9623 define(interp, "soa_zip", Some(3), |interp, args| {
9626 let soa = match &args[0] {
9627 Value::Map(map) => map.borrow().clone(),
9628 _ => return Err(RuntimeError::new("soa_zip: first argument must be SoA map")),
9629 };
9630 let keys = match &args[1] {
9631 Value::Array(keys) => keys.borrow().clone(),
9632 _ => return Err(RuntimeError::new("soa_zip: second argument must be array of keys")),
9633 };
9634 let func = match &args[2] {
9635 Value::Function(f) => f.clone(),
9636 _ => return Err(RuntimeError::new("soa_zip: third argument must be a function")),
9637 };
9638
9639 let arrays: Vec<Vec<Value>> = keys.iter()
9641 .filter_map(|k| {
9642 if let Value::String(s) = k {
9643 if let Some(Value::Array(arr)) = soa.get(&**s) {
9644 return Some(arr.borrow().clone());
9645 }
9646 }
9647 None
9648 })
9649 .collect();
9650
9651 if arrays.is_empty() {
9652 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9653 }
9654
9655 let len = arrays[0].len();
9656
9657 let results: Vec<Value> = (0..len)
9659 .map(|i| {
9660 let fn_args: Vec<Value> = arrays.iter()
9661 .filter_map(|arr| arr.get(i).cloned())
9662 .collect();
9663 interp.call_function(&func, fn_args)
9664 })
9665 .collect::<Result<_, _>>()?;
9666
9667 Ok(Value::Array(Rc::new(RefCell::new(results))))
9668 });
9669
9670 define(interp, "interleave", None, |_, args| {
9673 if args.is_empty() {
9674 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9675 }
9676
9677 let arrays: Vec<Vec<Value>> = args.iter()
9678 .filter_map(|arg| {
9679 if let Value::Array(arr) = arg {
9680 Some(arr.borrow().clone())
9681 } else {
9682 None
9683 }
9684 })
9685 .collect();
9686
9687 if arrays.is_empty() {
9688 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
9689 }
9690
9691 let len = arrays[0].len();
9692 let stride = arrays.len();
9693 let mut result = Vec::with_capacity(len * stride);
9694
9695 for i in 0..len {
9696 for arr in &arrays {
9697 if let Some(val) = arr.get(i) {
9698 result.push(val.clone());
9699 }
9700 }
9701 }
9702
9703 Ok(Value::Array(Rc::new(RefCell::new(result))))
9704 });
9705
9706 define(interp, "deinterleave", Some(2), |_, args| {
9709 let arr = match &args[0] {
9710 Value::Array(arr) => arr.borrow().clone(),
9711 _ => return Err(RuntimeError::new("deinterleave: first argument must be array")),
9712 };
9713 let stride = match &args[1] {
9714 Value::Int(n) => *n as usize,
9715 _ => return Err(RuntimeError::new("deinterleave: second argument must be integer stride")),
9716 };
9717
9718 if stride == 0 {
9719 return Err(RuntimeError::new("deinterleave: stride must be > 0"));
9720 }
9721
9722 let mut result: Vec<Vec<Value>> = (0..stride).map(|_| Vec::new()).collect();
9723
9724 for (i, val) in arr.iter().enumerate() {
9725 result[i % stride].push(val.clone());
9726 }
9727
9728 Ok(Value::Array(Rc::new(RefCell::new(
9729 result.into_iter()
9730 .map(|v| Value::Array(Rc::new(RefCell::new(v))))
9731 .collect()
9732 ))))
9733 });
9734}
9735
9736fn register_tensor(interp: &mut Interpreter) {
9742 define(interp, "outer_product", Some(2), |_, args| {
9745 let a = match &args[0] {
9746 Value::Array(arr) => arr.borrow().clone(),
9747 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
9748 };
9749 let b = match &args[1] {
9750 Value::Array(arr) => arr.borrow().clone(),
9751 _ => return Err(RuntimeError::new("outer_product: arguments must be arrays")),
9752 };
9753
9754 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
9756 for ai in &a {
9757 for bi in &b {
9758 let product = match (ai, bi) {
9759 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
9760 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
9761 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
9762 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
9763 _ => return Err(RuntimeError::new("outer_product: elements must be numeric")),
9764 };
9765 result.push(product);
9766 }
9767 }
9768
9769 Ok(Value::Array(Rc::new(RefCell::new(result))))
9770 });
9771
9772 define(interp, "tensor_contract", Some(4), |_, args| {
9775 let a = match &args[0] {
9776 Value::Array(arr) => arr.borrow().clone(),
9777 _ => return Err(RuntimeError::new("tensor_contract: first argument must be array")),
9778 };
9779 let b = match &args[1] {
9780 Value::Array(arr) => arr.borrow().clone(),
9781 _ => return Err(RuntimeError::new("tensor_contract: second argument must be array")),
9782 };
9783 let _axis_a = match &args[2] {
9784 Value::Int(n) => *n as usize,
9785 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
9786 };
9787 let _axis_b = match &args[3] {
9788 Value::Int(n) => *n as usize,
9789 _ => return Err(RuntimeError::new("tensor_contract: axis must be integer")),
9790 };
9791
9792 if a.len() != b.len() {
9794 return Err(RuntimeError::new("tensor_contract: vectors must have same length for contraction"));
9795 }
9796
9797 let mut sum = 0.0f64;
9798 for (ai, bi) in a.iter().zip(b.iter()) {
9799 let product = match (ai, bi) {
9800 (Value::Float(x), Value::Float(y)) => x * y,
9801 (Value::Int(x), Value::Int(y)) => (*x as f64) * (*y as f64),
9802 (Value::Float(x), Value::Int(y)) => x * (*y as f64),
9803 (Value::Int(x), Value::Float(y)) => (*x as f64) * y,
9804 _ => return Err(RuntimeError::new("tensor_contract: elements must be numeric")),
9805 };
9806 sum += product;
9807 }
9808
9809 Ok(Value::Float(sum))
9810 });
9811
9812 define(interp, "kronecker_product", Some(2), |_, args| {
9815 let a = match &args[0] {
9816 Value::Array(arr) => arr.borrow().clone(),
9817 _ => return Err(RuntimeError::new("kronecker_product: arguments must be arrays")),
9818 };
9819 let b = match &args[1] {
9820 Value::Array(arr) => arr.borrow().clone(),
9821 _ => return Err(RuntimeError::new("kronecker_product: arguments must be arrays")),
9822 };
9823
9824 let mut result: Vec<Value> = Vec::with_capacity(a.len() * b.len());
9826 for ai in &a {
9827 for bi in &b {
9828 let product = match (ai, bi) {
9829 (Value::Float(x), Value::Float(y)) => Value::Float(x * y),
9830 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
9831 (Value::Float(x), Value::Int(y)) => Value::Float(x * (*y as f64)),
9832 (Value::Int(x), Value::Float(y)) => Value::Float((*x as f64) * y),
9833 _ => return Err(RuntimeError::new("kronecker_product: elements must be numeric")),
9834 };
9835 result.push(product);
9836 }
9837 }
9838
9839 Ok(Value::Array(Rc::new(RefCell::new(result))))
9840 });
9841
9842 define(interp, "hadamard_product", Some(2), |_, args| {
9844 let a = match &args[0] {
9845 Value::Array(arr) => arr.borrow().clone(),
9846 _ => return Err(RuntimeError::new("hadamard_product: arguments must be arrays")),
9847 };
9848 let b = match &args[1] {
9849 Value::Array(arr) => arr.borrow().clone(),
9850 _ => return Err(RuntimeError::new("hadamard_product: arguments must be arrays")),
9851 };
9852
9853 if a.len() != b.len() {
9854 return Err(RuntimeError::new("hadamard_product: arrays must have same length"));
9855 }
9856
9857 let result: Vec<Value> = a.iter().zip(b.iter())
9858 .map(|(ai, bi)| {
9859 match (ai, bi) {
9860 (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x * y)),
9861 (Value::Int(x), Value::Int(y)) => Ok(Value::Int(x * y)),
9862 (Value::Float(x), Value::Int(y)) => Ok(Value::Float(x * (*y as f64))),
9863 (Value::Int(x), Value::Float(y)) => Ok(Value::Float((*x as f64) * y)),
9864 _ => Err(RuntimeError::new("hadamard_product: elements must be numeric")),
9865 }
9866 })
9867 .collect::<Result<_, _>>()?;
9868
9869 Ok(Value::Array(Rc::new(RefCell::new(result))))
9870 });
9871
9872 define(interp, "trace", Some(2), |_, args| {
9874 let arr = match &args[0] {
9875 Value::Array(arr) => arr.borrow().clone(),
9876 _ => return Err(RuntimeError::new("trace: first argument must be array")),
9877 };
9878 let size = match &args[1] {
9879 Value::Int(n) => *n as usize,
9880 _ => return Err(RuntimeError::new("trace: second argument must be matrix size")),
9881 };
9882
9883 let mut sum = 0.0f64;
9884 for i in 0..size {
9885 let idx = i * size + i;
9886 if idx < arr.len() {
9887 sum += match &arr[idx] {
9888 Value::Float(f) => *f,
9889 Value::Int(n) => *n as f64,
9890 _ => return Err(RuntimeError::new("trace: elements must be numeric")),
9891 };
9892 }
9893 }
9894
9895 Ok(Value::Float(sum))
9896 });
9897}
9898
9899fn register_autodiff(interp: &mut Interpreter) {
9945 define(interp, "grad", None, |interp, args| {
9948 if args.len() < 2 {
9949 return Err(RuntimeError::new(
9950 "grad() requires function and point arguments.\n\
9951 Usage: grad(f, x) or grad(f, x, step_size)\n\
9952 Example:\n\
9953 fn f(x) { return x * x; }\n\
9954 let derivative = grad(f, 3.0); // Returns 6.0"
9955 ));
9956 }
9957
9958 let func = match &args[0] {
9959 Value::Function(f) => f.clone(),
9960 _ => return Err(RuntimeError::new(
9961 "grad() first argument must be a function.\n\
9962 Got non-function value. Define a function first:\n\
9963 fn my_func(x) { return x * x; }\n\
9964 grad(my_func, 2.0)"
9965 )),
9966 };
9967 let x = match &args[1] {
9968 Value::Float(f) => *f,
9969 Value::Int(n) => *n as f64,
9970 Value::Array(arr) => {
9971 let arr = arr.borrow().clone();
9973 let h = if args.len() > 2 {
9974 match &args[2] {
9975 Value::Float(f) => *f,
9976 Value::Int(n) => *n as f64,
9977 _ => 1e-7,
9978 }
9979 } else {
9980 1e-7
9981 };
9982
9983 let mut gradient = Vec::with_capacity(arr.len());
9984 for (i, xi) in arr.iter().enumerate() {
9985 let xi_val = match xi {
9986 Value::Float(f) => *f,
9987 Value::Int(n) => *n as f64,
9988 _ => continue,
9989 };
9990
9991 let mut x_plus = arr.clone();
9993 let mut x_minus = arr.clone();
9994 x_plus[i] = Value::Float(xi_val + h);
9995 x_minus[i] = Value::Float(xi_val - h);
9996
9997 let f_plus = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
9998 let f_minus = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
9999
10000 let grad_i = match (f_plus, f_minus) {
10001 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
10002 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
10003 _ => return Err(RuntimeError::new("grad: function must return numeric")),
10004 };
10005
10006 gradient.push(Value::Float(grad_i));
10007 }
10008
10009 return Ok(Value::Array(Rc::new(RefCell::new(gradient))));
10010 }
10011 _ => return Err(RuntimeError::new("grad: x must be numeric or array")),
10012 };
10013
10014 let h = if args.len() > 2 {
10015 match &args[2] {
10016 Value::Float(f) => *f,
10017 Value::Int(n) => *n as f64,
10018 _ => 1e-7,
10019 }
10020 } else {
10021 1e-7
10022 };
10023
10024 let f_plus = interp.call_function(&func, vec![Value::Float(x + h)])?;
10026 let f_minus = interp.call_function(&func, vec![Value::Float(x - h)])?;
10027
10028 let derivative = match (f_plus, f_minus) {
10029 (Value::Float(fp), Value::Float(fm)) => (fp - fm) / (2.0 * h),
10030 (Value::Int(fp), Value::Int(fm)) => (fp - fm) as f64 / (2.0 * h),
10031 _ => return Err(RuntimeError::new("grad: function must return numeric")),
10032 };
10033
10034 Ok(Value::Float(derivative))
10035 });
10036
10037 define(interp, "jacobian", Some(2), |interp, args| {
10039 let func = match &args[0] {
10040 Value::Function(f) => f.clone(),
10041 _ => return Err(RuntimeError::new("jacobian: first argument must be a function")),
10042 };
10043 let x = match &args[1] {
10044 Value::Array(arr) => arr.borrow().clone(),
10045 _ => return Err(RuntimeError::new("jacobian: second argument must be array")),
10046 };
10047
10048 let h = 1e-7;
10049 let n = x.len();
10050
10051 let f_x = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x.clone())))])?;
10053 let m = match &f_x {
10054 Value::Array(arr) => arr.borrow().len(),
10055 _ => 1,
10056 };
10057
10058 let mut jacobian: Vec<Value> = Vec::with_capacity(m * n);
10060
10061 for j in 0..n {
10062 let xj = match &x[j] {
10063 Value::Float(f) => *f,
10064 Value::Int(i) => *i as f64,
10065 _ => continue,
10066 };
10067
10068 let mut x_plus = x.clone();
10069 let mut x_minus = x.clone();
10070 x_plus[j] = Value::Float(xj + h);
10071 x_minus[j] = Value::Float(xj - h);
10072
10073 let f_plus = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
10074 let f_minus = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
10075
10076 match (&f_plus, &f_minus) {
10078 (Value::Array(fp), Value::Array(fm)) => {
10079 let fp = fp.borrow();
10080 let fm = fm.borrow();
10081 for i in 0..m {
10082 let dfi_dxj = match (&fp[i], &fm[i]) {
10083 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
10084 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
10085 _ => 0.0,
10086 };
10087 jacobian.push(Value::Float(dfi_dxj));
10088 }
10089 }
10090 (Value::Float(fp), Value::Float(fm)) => {
10091 jacobian.push(Value::Float((fp - fm) / (2.0 * h)));
10092 }
10093 _ => return Err(RuntimeError::new("jacobian: function must return array or numeric")),
10094 }
10095 }
10096
10097 Ok(Value::Array(Rc::new(RefCell::new(jacobian))))
10098 });
10099
10100 define(interp, "hessian", Some(2), |interp, args| {
10102 let func = match &args[0] {
10103 Value::Function(f) => f.clone(),
10104 _ => return Err(RuntimeError::new("hessian: first argument must be a function")),
10105 };
10106 let x = match &args[1] {
10107 Value::Array(arr) => arr.borrow().clone(),
10108 _ => return Err(RuntimeError::new("hessian: second argument must be array")),
10109 };
10110
10111 let h = 1e-5; let n = x.len();
10113
10114 let mut hessian: Vec<Value> = Vec::with_capacity(n * n);
10115
10116 for i in 0..n {
10117 for j in 0..n {
10118 let xi = match &x[i] {
10119 Value::Float(f) => *f,
10120 Value::Int(k) => *k as f64,
10121 _ => continue,
10122 };
10123 let xj = match &x[j] {
10124 Value::Float(f) => *f,
10125 Value::Int(k) => *k as f64,
10126 _ => continue,
10127 };
10128
10129 let mut x_pp = x.clone();
10131 let mut x_pm = x.clone();
10132 let mut x_mp = x.clone();
10133 let mut x_mm = x.clone();
10134
10135 x_pp[i] = Value::Float(xi + h);
10136 x_pp[j] = Value::Float(if i == j { xi + 2.0*h } else { xj + h });
10137 x_pm[i] = Value::Float(xi + h);
10138 x_pm[j] = Value::Float(if i == j { xi } else { xj - h });
10139 x_mp[i] = Value::Float(xi - h);
10140 x_mp[j] = Value::Float(if i == j { xi } else { xj + h });
10141 x_mm[i] = Value::Float(xi - h);
10142 x_mm[j] = Value::Float(if i == j { xi - 2.0*h } else { xj - h });
10143
10144 let f_pp = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pp)))])?;
10145 let f_pm = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_pm)))])?;
10146 let f_mp = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mp)))])?;
10147 let f_mm = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_mm)))])?;
10148
10149 let d2f = match (f_pp, f_pm, f_mp, f_mm) {
10150 (Value::Float(fpp), Value::Float(fpm), Value::Float(fmp), Value::Float(fmm)) => {
10151 (fpp - fpm - fmp + fmm) / (4.0 * h * h)
10152 }
10153 _ => 0.0,
10154 };
10155
10156 hessian.push(Value::Float(d2f));
10157 }
10158 }
10159
10160 Ok(Value::Array(Rc::new(RefCell::new(hessian))))
10161 });
10162
10163 define(interp, "divergence", Some(2), |interp, args| {
10165 let func = match &args[0] {
10166 Value::Function(f) => f.clone(),
10167 _ => return Err(RuntimeError::new("divergence: first argument must be a function")),
10168 };
10169 let x = match &args[1] {
10170 Value::Array(arr) => arr.borrow().clone(),
10171 _ => return Err(RuntimeError::new("divergence: second argument must be array")),
10172 };
10173
10174 let h = 1e-7;
10175 let mut div = 0.0f64;
10176
10177 for (i, xi) in x.iter().enumerate() {
10178 let xi_val = match xi {
10179 Value::Float(f) => *f,
10180 Value::Int(n) => *n as f64,
10181 _ => continue,
10182 };
10183
10184 let mut x_plus = x.clone();
10185 let mut x_minus = x.clone();
10186 x_plus[i] = Value::Float(xi_val + h);
10187 x_minus[i] = Value::Float(xi_val - h);
10188
10189 let f_plus = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_plus)))])?;
10190 let f_minus = interp.call_function(&func, vec![Value::Array(Rc::new(RefCell::new(x_minus)))])?;
10191
10192 let df_i = match (&f_plus, &f_minus) {
10194 (Value::Array(fp), Value::Array(fm)) => {
10195 let fp = fp.borrow();
10196 let fm = fm.borrow();
10197 if i < fp.len() && i < fm.len() {
10198 match (&fp[i], &fm[i]) {
10199 (Value::Float(a), Value::Float(b)) => (*a - *b) / (2.0 * h),
10200 (Value::Int(a), Value::Int(b)) => (*a - *b) as f64 / (2.0 * h),
10201 _ => 0.0,
10202 }
10203 } else {
10204 0.0
10205 }
10206 }
10207 _ => 0.0,
10208 };
10209
10210 div += df_i;
10211 }
10212
10213 Ok(Value::Float(div))
10214 });
10215}
10216
10217fn register_spatial(interp: &mut Interpreter) {
10223 define(interp, "spatial_hash_new", Some(1), |_, args| {
10225 let cell_size = match &args[0] {
10226 Value::Float(f) => *f,
10227 Value::Int(n) => *n as f64,
10228 _ => return Err(RuntimeError::new("spatial_hash_new: cell_size must be numeric")),
10229 };
10230
10231 let mut config = HashMap::new();
10232 config.insert("cell_size".to_string(), Value::Float(cell_size));
10233 config.insert("buckets".to_string(), Value::Map(Rc::new(RefCell::new(HashMap::new()))));
10234
10235 Ok(Value::Map(Rc::new(RefCell::new(config))))
10236 });
10237
10238 define(interp, "spatial_hash_insert", Some(3), |_, args| {
10240 let hash = match &args[0] {
10241 Value::Map(map) => map.clone(),
10242 _ => return Err(RuntimeError::new("spatial_hash_insert: first argument must be spatial hash")),
10243 };
10244 let id = args[1].clone();
10245 let pos = match &args[2] {
10246 Value::Array(arr) => arr.borrow().clone(),
10247 _ => return Err(RuntimeError::new("spatial_hash_insert: position must be array")),
10248 };
10249
10250 let cell_size = {
10251 let h = hash.borrow();
10252 match h.get("cell_size") {
10253 Some(Value::Float(f)) => *f,
10254 _ => 1.0,
10255 }
10256 };
10257
10258 let key = pos.iter()
10260 .filter_map(|v| match v {
10261 Value::Float(f) => Some((*f / cell_size).floor() as i64),
10262 Value::Int(n) => Some(*n / (cell_size as i64)),
10263 _ => None,
10264 })
10265 .map(|n| n.to_string())
10266 .collect::<Vec<_>>()
10267 .join(",");
10268
10269 {
10271 let mut h = hash.borrow_mut();
10272 let buckets = h.entry("buckets".to_string())
10273 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
10274
10275 if let Value::Map(buckets_map) = buckets {
10276 let mut bm = buckets_map.borrow_mut();
10277 let bucket = bm.entry(key)
10278 .or_insert_with(|| Value::Array(Rc::new(RefCell::new(vec![]))));
10279
10280 if let Value::Array(arr) = bucket {
10281 arr.borrow_mut().push(id);
10282 }
10283 }
10284 }
10285
10286 Ok(Value::Map(hash))
10287 });
10288
10289 define(interp, "spatial_hash_query", Some(3), |_, args| {
10291 let hash = match &args[0] {
10292 Value::Map(map) => map.borrow().clone(),
10293 _ => return Err(RuntimeError::new("spatial_hash_query: first argument must be spatial hash")),
10294 };
10295 let pos = match &args[1] {
10296 Value::Array(arr) => arr.borrow().clone(),
10297 _ => return Err(RuntimeError::new("spatial_hash_query: position must be array")),
10298 };
10299 let radius = match &args[2] {
10300 Value::Float(f) => *f,
10301 Value::Int(n) => *n as f64,
10302 _ => return Err(RuntimeError::new("spatial_hash_query: radius must be numeric")),
10303 };
10304
10305 let cell_size = match hash.get("cell_size") {
10306 Some(Value::Float(f)) => *f,
10307 _ => 1.0,
10308 };
10309
10310 let center: Vec<i64> = pos.iter()
10312 .filter_map(|v| match v {
10313 Value::Float(f) => Some((*f / cell_size).floor() as i64),
10314 Value::Int(n) => Some(*n / (cell_size as i64)),
10315 _ => None,
10316 })
10317 .collect();
10318
10319 let cells_to_check = (radius / cell_size).ceil() as i64;
10321
10322 let mut results: Vec<Value> = Vec::new();
10323
10324 if let Some(Value::Map(buckets)) = hash.get("buckets") {
10325 let buckets = buckets.borrow();
10326
10327 if center.len() >= 2 {
10329 for dx in -cells_to_check..=cells_to_check {
10330 for dy in -cells_to_check..=cells_to_check {
10331 let key = format!("{},{}", center[0] + dx, center[1] + dy);
10332 if let Some(Value::Array(bucket)) = buckets.get(&key) {
10333 for item in bucket.borrow().iter() {
10334 results.push(item.clone());
10337 }
10338 }
10339 }
10340 }
10341 }
10342 }
10343
10344 Ok(Value::Array(Rc::new(RefCell::new(results))))
10345 });
10346
10347 define(interp, "aabb_new", Some(2), |_, args| {
10349 let min = match &args[0] {
10350 Value::Array(arr) => arr.borrow().clone(),
10351 _ => return Err(RuntimeError::new("aabb_new: min must be array")),
10352 };
10353 let max = match &args[1] {
10354 Value::Array(arr) => arr.borrow().clone(),
10355 _ => return Err(RuntimeError::new("aabb_new: max must be array")),
10356 };
10357
10358 let mut aabb = HashMap::new();
10359 aabb.insert("min".to_string(), Value::Array(Rc::new(RefCell::new(min))));
10360 aabb.insert("max".to_string(), Value::Array(Rc::new(RefCell::new(max))));
10361
10362 Ok(Value::Map(Rc::new(RefCell::new(aabb))))
10363 });
10364
10365 define(interp, "aabb_intersects", Some(2), |_, args| {
10367 let a = match &args[0] {
10368 Value::Map(map) => map.borrow().clone(),
10369 _ => return Err(RuntimeError::new("aabb_intersects: arguments must be AABBs")),
10370 };
10371 let b = match &args[1] {
10372 Value::Map(map) => map.borrow().clone(),
10373 _ => return Err(RuntimeError::new("aabb_intersects: arguments must be AABBs")),
10374 };
10375
10376 let a_min = extract_vec_from_map(&a, "min")?;
10377 let a_max = extract_vec_from_map(&a, "max")?;
10378 let b_min = extract_vec_from_map(&b, "min")?;
10379 let b_max = extract_vec_from_map(&b, "max")?;
10380
10381 for i in 0..a_min.len().min(a_max.len()).min(b_min.len()).min(b_max.len()) {
10383 if a_max[i] < b_min[i] || b_max[i] < a_min[i] {
10384 return Ok(Value::Bool(false));
10385 }
10386 }
10387
10388 Ok(Value::Bool(true))
10389 });
10390
10391 define(interp, "aabb_contains", Some(2), |_, args| {
10393 let aabb = match &args[0] {
10394 Value::Map(map) => map.borrow().clone(),
10395 _ => return Err(RuntimeError::new("aabb_contains: first argument must be AABB")),
10396 };
10397 let point = match &args[1] {
10398 Value::Array(arr) => arr.borrow().clone(),
10399 _ => return Err(RuntimeError::new("aabb_contains: second argument must be point array")),
10400 };
10401
10402 let min = extract_vec_from_map(&aabb, "min")?;
10403 let max = extract_vec_from_map(&aabb, "max")?;
10404
10405 for (i, p) in point.iter().enumerate() {
10406 let p_val = match p {
10407 Value::Float(f) => *f,
10408 Value::Int(n) => *n as f64,
10409 _ => continue,
10410 };
10411
10412 if i < min.len() && p_val < min[i] {
10413 return Ok(Value::Bool(false));
10414 }
10415 if i < max.len() && p_val > max[i] {
10416 return Ok(Value::Bool(false));
10417 }
10418 }
10419
10420 Ok(Value::Bool(true))
10421 });
10422}
10423
10424fn extract_vec_from_map(map: &HashMap<String, Value>, key: &str) -> Result<Vec<f64>, RuntimeError> {
10426 match map.get(key) {
10427 Some(Value::Array(arr)) => {
10428 arr.borrow().iter()
10429 .map(|v| match v {
10430 Value::Float(f) => Ok(*f),
10431 Value::Int(n) => Ok(*n as f64),
10432 _ => Err(RuntimeError::new("Expected numeric value")),
10433 })
10434 .collect()
10435 }
10436 _ => Err(RuntimeError::new(format!("Missing or invalid '{}' in AABB", key))),
10437 }
10438}
10439
10440fn register_physics(interp: &mut Interpreter) {
10446 define(interp, "verlet_integrate", Some(4), |_, args| {
10449 let pos = extract_vec3(&args[0], "verlet_integrate")?;
10450 let prev = extract_vec3(&args[1], "verlet_integrate")?;
10451 let accel = extract_vec3(&args[2], "verlet_integrate")?;
10452 let dt = match &args[3] {
10453 Value::Float(f) => *f,
10454 Value::Int(n) => *n as f64,
10455 _ => return Err(RuntimeError::new("verlet_integrate: dt must be numeric")),
10456 };
10457
10458 let dt2 = dt * dt;
10459 let new_pos = [
10460 pos[0] + (pos[0] - prev[0]) + accel[0] * dt2,
10461 pos[1] + (pos[1] - prev[1]) + accel[1] * dt2,
10462 pos[2] + (pos[2] - prev[2]) + accel[2] * dt2,
10463 ];
10464
10465 Ok(make_vec3_arr(new_pos))
10466 });
10467
10468 define(interp, "spring_force", Some(4), |_, args| {
10470 let p1 = extract_vec3(&args[0], "spring_force")?;
10471 let p2 = extract_vec3(&args[1], "spring_force")?;
10472 let rest_length = match &args[2] {
10473 Value::Float(f) => *f,
10474 Value::Int(n) => *n as f64,
10475 _ => return Err(RuntimeError::new("spring_force: rest_length must be numeric")),
10476 };
10477 let stiffness = match &args[3] {
10478 Value::Float(f) => *f,
10479 Value::Int(n) => *n as f64,
10480 _ => return Err(RuntimeError::new("spring_force: stiffness must be numeric")),
10481 };
10482
10483 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
10484 let length = (delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2]).sqrt();
10485
10486 if length < 1e-10 {
10487 return Ok(make_vec3_arr([0.0, 0.0, 0.0]));
10488 }
10489
10490 let displacement = length - rest_length;
10491 let force_mag = stiffness * displacement;
10492 let normalized = [delta[0] / length, delta[1] / length, delta[2] / length];
10493
10494 Ok(make_vec3_arr([
10495 normalized[0] * force_mag,
10496 normalized[1] * force_mag,
10497 normalized[2] * force_mag,
10498 ]))
10499 });
10500
10501 define(interp, "distance_constraint", Some(3), |_, args| {
10504 let p1 = extract_vec3(&args[0], "distance_constraint")?;
10505 let p2 = extract_vec3(&args[1], "distance_constraint")?;
10506 let target = match &args[2] {
10507 Value::Float(f) => *f,
10508 Value::Int(n) => *n as f64,
10509 _ => return Err(RuntimeError::new("distance_constraint: target must be numeric")),
10510 };
10511
10512 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
10513 let length = (delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2]).sqrt();
10514
10515 if length < 1e-10 {
10516 return Ok(Value::Tuple(Rc::new(vec![make_vec3_arr(p1), make_vec3_arr(p2)])));
10517 }
10518
10519 let correction = (length - target) / length * 0.5;
10520 let corr_vec = [delta[0] * correction, delta[1] * correction, delta[2] * correction];
10521
10522 let new_p1 = [p1[0] + corr_vec[0], p1[1] + corr_vec[1], p1[2] + corr_vec[2]];
10523 let new_p2 = [p2[0] - corr_vec[0], p2[1] - corr_vec[1], p2[2] - corr_vec[2]];
10524
10525 Ok(Value::Tuple(Rc::new(vec![make_vec3_arr(new_p1), make_vec3_arr(new_p2)])))
10526 });
10527
10528 define(interp, "solve_constraints", Some(3), |_, args| {
10531 let mut points = match &args[0] {
10532 Value::Array(arr) => arr.borrow().clone(),
10533 _ => return Err(RuntimeError::new("solve_constraints: first argument must be array of points")),
10534 };
10535 let constraints = match &args[1] {
10536 Value::Array(arr) => arr.borrow().clone(),
10537 _ => return Err(RuntimeError::new("solve_constraints: second argument must be array of constraints")),
10538 };
10539 let iterations = match &args[2] {
10540 Value::Int(n) => *n as usize,
10541 _ => return Err(RuntimeError::new("solve_constraints: iterations must be integer")),
10542 };
10543
10544 for _ in 0..iterations {
10545 for constraint in &constraints {
10546 match constraint {
10547 Value::Map(c) => {
10548 let c = c.borrow();
10549 let constraint_type = c.get("type")
10550 .and_then(|v| if let Value::String(s) = v { Some((**s).clone()) } else { None })
10551 .unwrap_or_default();
10552
10553 match constraint_type.as_str() {
10554 "distance" => {
10555 let indices = match c.get("indices") {
10556 Some(Value::Array(arr)) => arr.borrow().clone(),
10557 _ => continue,
10558 };
10559 let target = match c.get("distance") {
10560 Some(Value::Float(f)) => *f,
10561 Some(Value::Int(n)) => *n as f64,
10562 _ => continue,
10563 };
10564
10565 if indices.len() >= 2 {
10566 let i1 = match &indices[0] {
10567 Value::Int(n) => *n as usize,
10568 _ => continue,
10569 };
10570 let i2 = match &indices[1] {
10571 Value::Int(n) => *n as usize,
10572 _ => continue,
10573 };
10574
10575 if i1 < points.len() && i2 < points.len() {
10576 let p1 = extract_vec3(&points[i1], "solve")?;
10578 let p2 = extract_vec3(&points[i2], "solve")?;
10579
10580 let delta = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
10581 let length = (delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2]).sqrt();
10582
10583 if length > 1e-10 {
10584 let correction = (length - target) / length * 0.5;
10585 let corr_vec = [delta[0] * correction, delta[1] * correction, delta[2] * correction];
10586
10587 points[i1] = make_vec3_arr([p1[0] + corr_vec[0], p1[1] + corr_vec[1], p1[2] + corr_vec[2]]);
10588 points[i2] = make_vec3_arr([p2[0] - corr_vec[0], p2[1] - corr_vec[1], p2[2] - corr_vec[2]]);
10589 }
10590 }
10591 }
10592 }
10593 _ => {}
10594 }
10595 }
10596 _ => continue,
10597 }
10598 }
10599 }
10600
10601 Ok(Value::Array(Rc::new(RefCell::new(points))))
10602 });
10603
10604 define(interp, "ray_sphere_intersect", Some(4), |_, args| {
10607 let origin = extract_vec3(&args[0], "ray_sphere_intersect")?;
10608 let dir = extract_vec3(&args[1], "ray_sphere_intersect")?;
10609 let center = extract_vec3(&args[2], "ray_sphere_intersect")?;
10610 let radius = match &args[3] {
10611 Value::Float(f) => *f,
10612 Value::Int(n) => *n as f64,
10613 _ => return Err(RuntimeError::new("ray_sphere_intersect: radius must be numeric")),
10614 };
10615
10616 let oc = [origin[0] - center[0], origin[1] - center[1], origin[2] - center[2]];
10617
10618 let a = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2];
10619 let b = 2.0 * (oc[0]*dir[0] + oc[1]*dir[1] + oc[2]*dir[2]);
10620 let c = oc[0]*oc[0] + oc[1]*oc[1] + oc[2]*oc[2] - radius*radius;
10621
10622 let discriminant = b*b - 4.0*a*c;
10623
10624 if discriminant < 0.0 {
10625 Ok(Value::Float(-1.0))
10626 } else {
10627 let t = (-b - discriminant.sqrt()) / (2.0 * a);
10628 if t > 0.0 {
10629 Ok(Value::Float(t))
10630 } else {
10631 let t2 = (-b + discriminant.sqrt()) / (2.0 * a);
10632 if t2 > 0.0 {
10633 Ok(Value::Float(t2))
10634 } else {
10635 Ok(Value::Float(-1.0))
10636 }
10637 }
10638 }
10639 });
10640
10641 define(interp, "ray_plane_intersect", Some(4), |_, args| {
10643 let origin = extract_vec3(&args[0], "ray_plane_intersect")?;
10644 let dir = extract_vec3(&args[1], "ray_plane_intersect")?;
10645 let plane_pt = extract_vec3(&args[2], "ray_plane_intersect")?;
10646 let normal = extract_vec3(&args[3], "ray_plane_intersect")?;
10647
10648 let denom = dir[0]*normal[0] + dir[1]*normal[1] + dir[2]*normal[2];
10649
10650 if denom.abs() < 1e-10 {
10651 return Ok(Value::Float(-1.0)); }
10653
10654 let diff = [plane_pt[0] - origin[0], plane_pt[1] - origin[1], plane_pt[2] - origin[2]];
10655 let t = (diff[0]*normal[0] + diff[1]*normal[1] + diff[2]*normal[2]) / denom;
10656
10657 if t > 0.0 {
10658 Ok(Value::Float(t))
10659 } else {
10660 Ok(Value::Float(-1.0))
10661 }
10662 });
10663}
10664
10665fn register_geometric_algebra(interp: &mut Interpreter) {
10727 fn make_multivector(components: [f64; 8]) -> Value {
10729 let mut mv = HashMap::new();
10730 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("_type".to_string(), Value::String(Rc::new("multivector".to_string())));
10739 Value::Map(Rc::new(RefCell::new(mv)))
10740 }
10741
10742 fn extract_multivector(v: &Value, fn_name: &str) -> Result<[f64; 8], RuntimeError> {
10743 match v {
10744 Value::Map(map) => {
10745 let map = map.borrow();
10746 let get_component = |key: &str| -> f64 {
10747 match map.get(key) {
10748 Some(Value::Float(f)) => *f,
10749 Some(Value::Int(n)) => *n as f64,
10750 _ => 0.0,
10751 }
10752 };
10753 Ok([
10754 get_component("s"),
10755 get_component("e1"),
10756 get_component("e2"),
10757 get_component("e3"),
10758 get_component("e12"),
10759 get_component("e23"),
10760 get_component("e31"),
10761 get_component("e123"),
10762 ])
10763 }
10764 _ => Err(RuntimeError::new(format!("{}: expected multivector", fn_name))),
10765 }
10766 }
10767
10768 define(interp, "mv_new", Some(8), |_, args| {
10770 let mut components = [0.0f64; 8];
10771 for (i, arg) in args.iter().enumerate().take(8) {
10772 components[i] = match arg {
10773 Value::Float(f) => *f,
10774 Value::Int(n) => *n as f64,
10775 _ => 0.0,
10776 };
10777 }
10778 Ok(make_multivector(components))
10779 });
10780
10781 define(interp, "mv_scalar", Some(1), |_, args| {
10783 let s = match &args[0] {
10784 Value::Float(f) => *f,
10785 Value::Int(n) => *n as f64,
10786 _ => return Err(RuntimeError::new("mv_scalar: expected number")),
10787 };
10788 Ok(make_multivector([s, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]))
10789 });
10790
10791 define(interp, "mv_vector", Some(3), |_, args| {
10793 let x = match &args[0] { Value::Float(f) => *f, Value::Int(n) => *n as f64, _ => 0.0 };
10794 let y = match &args[1] { Value::Float(f) => *f, Value::Int(n) => *n as f64, _ => 0.0 };
10795 let z = match &args[2] { Value::Float(f) => *f, Value::Int(n) => *n as f64, _ => 0.0 };
10796 Ok(make_multivector([0.0, x, y, z, 0.0, 0.0, 0.0, 0.0]))
10797 });
10798
10799 define(interp, "mv_bivector", Some(3), |_, args| {
10801 let xy = match &args[0] { Value::Float(f) => *f, Value::Int(n) => *n as f64, _ => 0.0 };
10802 let yz = match &args[1] { Value::Float(f) => *f, Value::Int(n) => *n as f64, _ => 0.0 };
10803 let zx = match &args[2] { Value::Float(f) => *f, Value::Int(n) => *n as f64, _ => 0.0 };
10804 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, xy, yz, zx, 0.0]))
10805 });
10806
10807 define(interp, "mv_trivector", Some(1), |_, args| {
10809 let xyz = match &args[0] { Value::Float(f) => *f, Value::Int(n) => *n as f64, _ => 0.0 };
10810 Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, xyz]))
10811 });
10812
10813 define(interp, "mv_add", Some(2), |_, args| {
10815 let a = extract_multivector(&args[0], "mv_add")?;
10816 let b = extract_multivector(&args[1], "mv_add")?;
10817 Ok(make_multivector([
10818 a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3],
10819 a[4] + b[4], a[5] + b[5], a[6] + b[6], a[7] + b[7],
10820 ]))
10821 });
10822
10823 define(interp, "mv_sub", Some(2), |_, args| {
10825 let a = extract_multivector(&args[0], "mv_sub")?;
10826 let b = extract_multivector(&args[1], "mv_sub")?;
10827 Ok(make_multivector([
10828 a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3],
10829 a[4] - b[4], a[5] - b[5], a[6] - b[6], a[7] - b[7],
10830 ]))
10831 });
10832
10833 define(interp, "mv_scale", Some(2), |_, args| {
10835 let a = extract_multivector(&args[0], "mv_scale")?;
10836 let s = match &args[1] {
10837 Value::Float(f) => *f,
10838 Value::Int(n) => *n as f64,
10839 _ => return Err(RuntimeError::new("mv_scale: second argument must be number")),
10840 };
10841 Ok(make_multivector([
10842 a[0] * s, a[1] * s, a[2] * s, a[3] * s,
10843 a[4] * s, a[5] * s, a[6] * s, a[7] * s,
10844 ]))
10845 });
10846
10847 define(interp, "mv_geometric", Some(2), |_, args| {
10850 let a = extract_multivector(&args[0], "mv_geometric")?;
10851 let b = extract_multivector(&args[1], "mv_geometric")?;
10852
10853 let mut r = [0.0f64; 8];
10856
10857 r[0] = a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]
10859 - a[4]*b[4] - a[5]*b[5] - a[6]*b[6] - a[7]*b[7];
10860
10861 r[1] = a[0]*b[1] + a[1]*b[0] - a[2]*b[4] + a[3]*b[6]
10863 + a[4]*b[2] - a[5]*b[7] - a[6]*b[3] - a[7]*b[5];
10864
10865 r[2] = a[0]*b[2] + a[1]*b[4] + a[2]*b[0] - a[3]*b[5]
10867 - a[4]*b[1] + a[5]*b[3] - a[6]*b[7] - a[7]*b[6];
10868
10869 r[3] = a[0]*b[3] - a[1]*b[6] + a[2]*b[5] + a[3]*b[0]
10871 - a[4]*b[7] - a[5]*b[2] + a[6]*b[1] - a[7]*b[4];
10872
10873 r[4] = a[0]*b[4] + a[1]*b[2] - a[2]*b[1] + a[3]*b[7]
10875 + a[4]*b[0] + a[5]*b[6] - a[6]*b[5] + a[7]*b[3];
10876
10877 r[5] = a[0]*b[5] + a[1]*b[7] + a[2]*b[3] - a[3]*b[2]
10879 - a[4]*b[6] + a[5]*b[0] + a[6]*b[4] + a[7]*b[1];
10880
10881 r[6] = a[0]*b[6] - a[1]*b[3] + a[2]*b[7] + a[3]*b[1]
10883 + a[4]*b[5] - a[5]*b[4] + a[6]*b[0] + a[7]*b[2];
10884
10885 r[7] = a[0]*b[7] + a[1]*b[5] + a[2]*b[6] + a[3]*b[4]
10887 + a[4]*b[3] + a[5]*b[1] + a[6]*b[2] + a[7]*b[0];
10888
10889 Ok(make_multivector(r))
10890 });
10891
10892 define(interp, "mv_wedge", Some(2), |_, args| {
10895 let a = extract_multivector(&args[0], "mv_wedge")?;
10896 let b = extract_multivector(&args[1], "mv_wedge")?;
10897
10898 let mut r = [0.0f64; 8];
10899
10900 r[0] = a[0] * b[0];
10902
10903 r[1] = a[0]*b[1] + a[1]*b[0];
10905 r[2] = a[0]*b[2] + a[2]*b[0];
10906 r[3] = a[0]*b[3] + a[3]*b[0];
10907
10908 r[4] = a[0]*b[4] + a[1]*b[2] - a[2]*b[1] + a[4]*b[0];
10910 r[5] = a[0]*b[5] + a[2]*b[3] - a[3]*b[2] + a[5]*b[0];
10911 r[6] = a[0]*b[6] + a[3]*b[1] - a[1]*b[3] + a[6]*b[0];
10912
10913 r[7] = a[0]*b[7] + a[7]*b[0]
10915 + a[1]*b[5] + a[2]*b[6] + a[3]*b[4]
10916 - a[4]*b[3] - a[5]*b[1] - a[6]*b[2];
10917
10918 Ok(make_multivector(r))
10919 });
10920
10921 define(interp, "mv_inner", Some(2), |_, args| {
10924 let a = extract_multivector(&args[0], "mv_inner")?;
10925 let b = extract_multivector(&args[1], "mv_inner")?;
10926
10927 let mut r = [0.0f64; 8];
10928
10929 r[0] = a[1]*b[1] + a[2]*b[2] + a[3]*b[3]
10932 - a[4]*b[4] - a[5]*b[5] - a[6]*b[6]
10933 - a[7]*b[7];
10934
10935 r[1] = a[4]*b[2] - a[6]*b[3] - a[5]*b[7];
10937 r[2] = -a[4]*b[1] + a[5]*b[3] - a[6]*b[7];
10938 r[3] = a[6]*b[1] - a[5]*b[2] - a[4]*b[7];
10939
10940 r[4] = a[7]*b[3];
10942 r[5] = a[7]*b[1];
10943 r[6] = a[7]*b[2];
10944
10945 Ok(make_multivector(r))
10946 });
10947
10948 define(interp, "mv_reverse", Some(1), |_, args| {
10951 let a = extract_multivector(&args[0], "mv_reverse")?;
10952 Ok(make_multivector([
10954 a[0], a[1], a[2], a[3],
10955 -a[4], -a[5], -a[6], -a[7],
10956 ]))
10957 });
10958
10959 define(interp, "mv_dual", Some(1), |_, args| {
10962 let a = extract_multivector(&args[0], "mv_dual")?;
10963 Ok(make_multivector([
10966 -a[7], -a[5], -a[6], -a[4], a[3], a[1], a[2], a[0], ]))
10975 });
10976
10977 define(interp, "mv_magnitude", Some(1), |_, args| {
10979 let a = extract_multivector(&args[0], "mv_magnitude")?;
10980 let mag_sq = a[0]*a[0] + a[1]*a[1] + a[2]*a[2] + a[3]*a[3]
10981 + a[4]*a[4] + a[5]*a[5] + a[6]*a[6] + a[7]*a[7];
10982 Ok(Value::Float(mag_sq.sqrt()))
10983 });
10984
10985 define(interp, "mv_normalize", Some(1), |_, args| {
10987 let a = extract_multivector(&args[0], "mv_normalize")?;
10988 let mag = (a[0]*a[0] + a[1]*a[1] + a[2]*a[2] + a[3]*a[3]
10989 + a[4]*a[4] + a[5]*a[5] + a[6]*a[6] + a[7]*a[7]).sqrt();
10990 if mag < 1e-10 {
10991 return Ok(make_multivector([0.0; 8]));
10992 }
10993 Ok(make_multivector([
10994 a[0]/mag, a[1]/mag, a[2]/mag, a[3]/mag,
10995 a[4]/mag, a[5]/mag, a[6]/mag, a[7]/mag,
10996 ]))
10997 });
10998
10999 define(interp, "rotor_from_axis_angle", Some(2), |_, args| {
11002 let axis = extract_vec3(&args[0], "rotor_from_axis_angle")?;
11003 let angle = match &args[1] {
11004 Value::Float(f) => *f,
11005 Value::Int(n) => *n as f64,
11006 _ => return Err(RuntimeError::new("rotor_from_axis_angle: angle must be number")),
11007 };
11008
11009 let len = (axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]).sqrt();
11011 if len < 1e-10 {
11012 return Ok(make_multivector([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]));
11014 }
11015 let (nx, ny, nz) = (axis[0]/len, axis[1]/len, axis[2]/len);
11016
11017 let half_angle = angle / 2.0;
11018 let (s, c) = half_angle.sin_cos();
11019
11020 Ok(make_multivector([
11023 c, 0.0, 0.0, 0.0, -s * nz, -s * nx, -s * ny, 0.0, ]))
11030 });
11031
11032 define(interp, "rotor_apply", Some(2), |_, args| {
11035 let r = extract_multivector(&args[0], "rotor_apply")?;
11036 let v = extract_vec3(&args[1], "rotor_apply")?;
11037
11038 let v_mv = [0.0, v[0], v[1], v[2], 0.0, 0.0, 0.0, 0.0];
11040
11041 let r_rev = [r[0], r[1], r[2], r[3], -r[4], -r[5], -r[6], -r[7]];
11043
11044 let mut rv = [0.0f64; 8];
11046 rv[0] = r[0]*v_mv[0] + r[1]*v_mv[1] + r[2]*v_mv[2] + r[3]*v_mv[3]
11047 - r[4]*v_mv[4] - r[5]*v_mv[5] - r[6]*v_mv[6] - r[7]*v_mv[7];
11048 rv[1] = r[0]*v_mv[1] + r[1]*v_mv[0] - r[2]*v_mv[4] + r[3]*v_mv[6]
11049 + r[4]*v_mv[2] - r[5]*v_mv[7] - r[6]*v_mv[3] - r[7]*v_mv[5];
11050 rv[2] = r[0]*v_mv[2] + r[1]*v_mv[4] + r[2]*v_mv[0] - r[3]*v_mv[5]
11051 - r[4]*v_mv[1] + r[5]*v_mv[3] - r[6]*v_mv[7] - r[7]*v_mv[6];
11052 rv[3] = r[0]*v_mv[3] - r[1]*v_mv[6] + r[2]*v_mv[5] + r[3]*v_mv[0]
11053 - r[4]*v_mv[7] - r[5]*v_mv[2] + r[6]*v_mv[1] - r[7]*v_mv[4];
11054 rv[4] = r[0]*v_mv[4] + r[1]*v_mv[2] - r[2]*v_mv[1] + r[3]*v_mv[7]
11055 + r[4]*v_mv[0] + r[5]*v_mv[6] - r[6]*v_mv[5] + r[7]*v_mv[3];
11056 rv[5] = r[0]*v_mv[5] + r[1]*v_mv[7] + r[2]*v_mv[3] - r[3]*v_mv[2]
11057 - r[4]*v_mv[6] + r[5]*v_mv[0] + r[6]*v_mv[4] + r[7]*v_mv[1];
11058 rv[6] = r[0]*v_mv[6] - r[1]*v_mv[3] + r[2]*v_mv[7] + r[3]*v_mv[1]
11059 + r[4]*v_mv[5] - r[5]*v_mv[4] + r[6]*v_mv[0] + r[7]*v_mv[2];
11060 rv[7] = r[0]*v_mv[7] + r[1]*v_mv[5] + r[2]*v_mv[6] + r[3]*v_mv[4]
11061 + r[4]*v_mv[3] + r[5]*v_mv[1] + r[6]*v_mv[2] + r[7]*v_mv[0];
11062
11063 let mut result = [0.0f64; 8];
11065 result[1] = rv[0]*r_rev[1] + rv[1]*r_rev[0] - rv[2]*r_rev[4] + rv[3]*r_rev[6]
11066 + rv[4]*r_rev[2] - rv[5]*r_rev[7] - rv[6]*r_rev[3] - rv[7]*r_rev[5];
11067 result[2] = rv[0]*r_rev[2] + rv[1]*r_rev[4] + rv[2]*r_rev[0] - rv[3]*r_rev[5]
11068 - rv[4]*r_rev[1] + rv[5]*r_rev[3] - rv[6]*r_rev[7] - rv[7]*r_rev[6];
11069 result[3] = rv[0]*r_rev[3] - rv[1]*r_rev[6] + rv[2]*r_rev[5] + rv[3]*r_rev[0]
11070 - rv[4]*r_rev[7] - rv[5]*r_rev[2] + rv[6]*r_rev[1] - rv[7]*r_rev[4];
11071
11072 Ok(make_vec3(result[1], result[2], result[3]))
11074 });
11075
11076 define(interp, "rotor_compose", Some(2), |_, args| {
11078 let a = extract_multivector(&args[0], "rotor_compose")?;
11079 let b = extract_multivector(&args[1], "rotor_compose")?;
11080
11081 let mut r = [0.0f64; 8];
11083 r[0] = a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]
11084 - a[4]*b[4] - a[5]*b[5] - a[6]*b[6] - a[7]*b[7];
11085 r[1] = a[0]*b[1] + a[1]*b[0] - a[2]*b[4] + a[3]*b[6]
11086 + a[4]*b[2] - a[5]*b[7] - a[6]*b[3] - a[7]*b[5];
11087 r[2] = a[0]*b[2] + a[1]*b[4] + a[2]*b[0] - a[3]*b[5]
11088 - a[4]*b[1] + a[5]*b[3] - a[6]*b[7] - a[7]*b[6];
11089 r[3] = a[0]*b[3] - a[1]*b[6] + a[2]*b[5] + a[3]*b[0]
11090 - a[4]*b[7] - a[5]*b[2] + a[6]*b[1] - a[7]*b[4];
11091 r[4] = a[0]*b[4] + a[1]*b[2] - a[2]*b[1] + a[3]*b[7]
11092 + a[4]*b[0] + a[5]*b[6] - a[6]*b[5] + a[7]*b[3];
11093 r[5] = a[0]*b[5] + a[1]*b[7] + a[2]*b[3] - a[3]*b[2]
11094 - a[4]*b[6] + a[5]*b[0] + a[6]*b[4] + a[7]*b[1];
11095 r[6] = a[0]*b[6] - a[1]*b[3] + a[2]*b[7] + a[3]*b[1]
11096 + a[4]*b[5] - a[5]*b[4] + a[6]*b[0] + a[7]*b[2];
11097 r[7] = a[0]*b[7] + a[1]*b[5] + a[2]*b[6] + a[3]*b[4]
11098 + a[4]*b[3] + a[5]*b[1] + a[6]*b[2] + a[7]*b[0];
11099
11100 Ok(make_multivector(r))
11101 });
11102
11103 define(interp, "mv_reflect", Some(2), |_, args| {
11106 let v = extract_vec3(&args[0], "mv_reflect")?;
11107 let n = extract_vec3(&args[1], "mv_reflect")?;
11108
11109 let len = (n[0]*n[0] + n[1]*n[1] + n[2]*n[2]).sqrt();
11111 if len < 1e-10 {
11112 return Ok(make_vec3(v[0], v[1], v[2]));
11113 }
11114 let (nx, ny, nz) = (n[0]/len, n[1]/len, n[2]/len);
11115
11116 let dot = v[0]*nx + v[1]*ny + v[2]*nz;
11118 Ok(make_vec3(
11119 v[0] - 2.0 * dot * nx,
11120 v[1] - 2.0 * dot * ny,
11121 v[2] - 2.0 * dot * nz,
11122 ))
11123 });
11124
11125 define(interp, "mv_project", Some(2), |_, args| {
11127 let v = extract_vec3(&args[0], "mv_project")?;
11128 let n = extract_vec3(&args[1], "mv_project")?;
11129
11130 let len = (n[0]*n[0] + n[1]*n[1] + n[2]*n[2]).sqrt();
11131 if len < 1e-10 {
11132 return Ok(make_vec3(v[0], v[1], v[2]));
11133 }
11134 let (nx, ny, nz) = (n[0]/len, n[1]/len, n[2]/len);
11135
11136 let dot = v[0]*nx + v[1]*ny + v[2]*nz;
11138 Ok(make_vec3(
11139 v[0] - dot * nx,
11140 v[1] - dot * ny,
11141 v[2] - dot * nz,
11142 ))
11143 });
11144
11145 define(interp, "mv_grade", Some(2), |_, args| {
11147 let a = extract_multivector(&args[0], "mv_grade")?;
11148 let k = match &args[1] {
11149 Value::Int(n) => *n,
11150 _ => return Err(RuntimeError::new("mv_grade: grade must be integer")),
11151 };
11152
11153 match k {
11154 0 => Ok(make_multivector([a[0], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])),
11155 1 => Ok(make_multivector([0.0, a[1], a[2], a[3], 0.0, 0.0, 0.0, 0.0])),
11156 2 => Ok(make_multivector([0.0, 0.0, 0.0, 0.0, a[4], a[5], a[6], 0.0])),
11157 3 => Ok(make_multivector([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, a[7]])),
11158 _ => Ok(make_multivector([0.0; 8])),
11159 }
11160 });
11161}
11162
11163fn register_dimensional(interp: &mut Interpreter) {
11170 fn make_quantity(value: f64, units: [i32; 7]) -> Value {
11173 let mut q = HashMap::new();
11174 q.insert("value".to_string(), Value::Float(value));
11175 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("_type".to_string(), Value::String(Rc::new("quantity".to_string())));
11183 Value::Map(Rc::new(RefCell::new(q)))
11184 }
11185
11186 fn extract_quantity(v: &Value, fn_name: &str) -> Result<(f64, [i32; 7]), RuntimeError> {
11187 match v {
11188 Value::Map(map) => {
11189 let map = map.borrow();
11190 let value = match map.get("value") {
11191 Some(Value::Float(f)) => *f,
11192 Some(Value::Int(n)) => *n as f64,
11193 _ => return Err(RuntimeError::new(format!("{}: missing value", fn_name))),
11194 };
11195 let get_exp = |key: &str| -> i32 {
11196 match map.get(key) {
11197 Some(Value::Int(n)) => *n as i32,
11198 _ => 0,
11199 }
11200 };
11201 Ok((value, [
11202 get_exp("m"), get_exp("kg"), get_exp("s"),
11203 get_exp("A"), get_exp("K"), get_exp("mol"), get_exp("cd"),
11204 ]))
11205 }
11206 Value::Float(f) => Ok((*f, [0; 7])),
11207 Value::Int(n) => Ok((*n as f64, [0; 7])),
11208 _ => Err(RuntimeError::new(format!("{}: expected quantity or number", fn_name))),
11209 }
11210 }
11211
11212 fn units_to_string(units: [i32; 7]) -> String {
11213 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
11214 let mut parts = Vec::new();
11215 for (i, &exp) in units.iter().enumerate() {
11216 if exp == 1 {
11217 parts.push(names[i].to_string());
11218 } else if exp != 0 {
11219 parts.push(format!("{}^{}", names[i], exp));
11220 }
11221 }
11222 if parts.is_empty() { "dimensionless".to_string() } else { parts.join("·") }
11223 }
11224
11225 define(interp, "qty", Some(2), |_, args| {
11228 let value = match &args[0] {
11229 Value::Float(f) => *f,
11230 Value::Int(n) => *n as f64,
11231 _ => return Err(RuntimeError::new("qty: first argument must be number")),
11232 };
11233 let unit_str = match &args[1] {
11234 Value::String(s) => s.to_string(),
11235 _ => return Err(RuntimeError::new("qty: second argument must be unit string")),
11236 };
11237
11238 let mut units = [0i32; 7];
11240 let in_denominator = unit_str.contains('/');
11243
11244 for part in unit_str.split(|c: char| c == '*' || c == '·' || c == ' ') {
11246 let part = part.trim();
11247 if part.is_empty() { continue; }
11248
11249 let (base, exp) = if let Some(idx) = part.find('^') {
11250 let (b, e) = part.split_at(idx);
11251 (b, e[1..].parse::<i32>().unwrap_or(1))
11252 } else if part.contains('/') {
11253 continue;
11255 } else {
11256 (part, 1)
11257 };
11258
11259 let sign = if in_denominator { -1 } else { 1 };
11260 match base {
11261 "m" | "meter" | "meters" => units[0] += exp * sign,
11262 "kg" | "kilogram" | "kilograms" => units[1] += exp * sign,
11263 "s" | "sec" | "second" | "seconds" => units[2] += exp * sign,
11264 "A" | "amp" | "ampere" | "amperes" => units[3] += exp * sign,
11265 "K" | "kelvin" => units[4] += exp * sign,
11266 "mol" | "mole" | "moles" => units[5] += exp * sign,
11267 "cd" | "candela" => units[6] += exp * sign,
11268 "N" | "newton" | "newtons" => { units[1] += sign; units[0] += sign; units[2] -= 2 * sign; }
11270 "J" | "joule" | "joules" => { units[1] += sign; units[0] += 2 * sign; units[2] -= 2 * sign; }
11271 "W" | "watt" | "watts" => { units[1] += sign; units[0] += 2 * sign; units[2] -= 3 * sign; }
11272 "Pa" | "pascal" | "pascals" => { units[1] += sign; units[0] -= sign; units[2] -= 2 * sign; }
11273 "Hz" | "hertz" => { units[2] -= sign; }
11274 "C" | "coulomb" | "coulombs" => { units[3] += sign; units[2] += sign; }
11275 "V" | "volt" | "volts" => { units[1] += sign; units[0] += 2 * sign; units[2] -= 3 * sign; units[3] -= sign; }
11276 "Ω" | "ohm" | "ohms" => { units[1] += sign; units[0] += 2 * sign; units[2] -= 3 * sign; units[3] -= 2 * sign; }
11277 _ => {}
11278 }
11279 }
11280
11281 Ok(make_quantity(value, units))
11282 });
11283
11284 define(interp, "qty_add", Some(2), |_, args| {
11286 let (val_a, units_a) = extract_quantity(&args[0], "qty_add")?;
11287 let (val_b, units_b) = extract_quantity(&args[1], "qty_add")?;
11288
11289 if units_a != units_b {
11290 return Err(RuntimeError::new(format!(
11291 "qty_add: unit mismatch: {} vs {}",
11292 units_to_string(units_a), units_to_string(units_b)
11293 )));
11294 }
11295
11296 Ok(make_quantity(val_a + val_b, units_a))
11297 });
11298
11299 define(interp, "qty_sub", Some(2), |_, args| {
11301 let (val_a, units_a) = extract_quantity(&args[0], "qty_sub")?;
11302 let (val_b, units_b) = extract_quantity(&args[1], "qty_sub")?;
11303
11304 if units_a != units_b {
11305 return Err(RuntimeError::new(format!(
11306 "qty_sub: unit mismatch: {} vs {}",
11307 units_to_string(units_a), units_to_string(units_b)
11308 )));
11309 }
11310
11311 Ok(make_quantity(val_a - val_b, units_a))
11312 });
11313
11314 define(interp, "qty_mul", Some(2), |_, args| {
11316 let (val_a, units_a) = extract_quantity(&args[0], "qty_mul")?;
11317 let (val_b, units_b) = extract_quantity(&args[1], "qty_mul")?;
11318
11319 let mut result_units = [0i32; 7];
11320 for i in 0..7 {
11321 result_units[i] = units_a[i] + units_b[i];
11322 }
11323
11324 Ok(make_quantity(val_a * val_b, result_units))
11325 });
11326
11327 define(interp, "qty_div", Some(2), |_, args| {
11329 let (val_a, units_a) = extract_quantity(&args[0], "qty_div")?;
11330 let (val_b, units_b) = extract_quantity(&args[1], "qty_div")?;
11331
11332 if val_b.abs() < 1e-15 {
11333 return Err(RuntimeError::new("qty_div: division by zero"));
11334 }
11335
11336 let mut result_units = [0i32; 7];
11337 for i in 0..7 {
11338 result_units[i] = units_a[i] - units_b[i];
11339 }
11340
11341 Ok(make_quantity(val_a / val_b, result_units))
11342 });
11343
11344 define(interp, "qty_pow", Some(2), |_, args| {
11346 let (val, units) = extract_quantity(&args[0], "qty_pow")?;
11347 let n = match &args[1] {
11348 Value::Int(n) => *n as i32,
11349 Value::Float(f) => *f as i32,
11350 _ => return Err(RuntimeError::new("qty_pow: exponent must be number")),
11351 };
11352
11353 let mut result_units = [0i32; 7];
11354 for i in 0..7 {
11355 result_units[i] = units[i] * n;
11356 }
11357
11358 Ok(make_quantity(val.powi(n), result_units))
11359 });
11360
11361 define(interp, "qty_sqrt", Some(1), |_, args| {
11363 let (val, units) = extract_quantity(&args[0], "qty_sqrt")?;
11364
11365 for (i, &exp) in units.iter().enumerate() {
11367 if exp % 2 != 0 {
11368 let names = ["m", "kg", "s", "A", "K", "mol", "cd"];
11369 return Err(RuntimeError::new(format!(
11370 "qty_sqrt: cannot take sqrt of {} (odd exponent on {})",
11371 units_to_string(units), names[i]
11372 )));
11373 }
11374 }
11375
11376 let mut result_units = [0i32; 7];
11377 for i in 0..7 {
11378 result_units[i] = units[i] / 2;
11379 }
11380
11381 Ok(make_quantity(val.sqrt(), result_units))
11382 });
11383
11384 define(interp, "qty_value", Some(1), |_, args| {
11386 let (val, _) = extract_quantity(&args[0], "qty_value")?;
11387 Ok(Value::Float(val))
11388 });
11389
11390 define(interp, "qty_units", Some(1), |_, args| {
11392 let (_, units) = extract_quantity(&args[0], "qty_units")?;
11393 Ok(Value::String(Rc::new(units_to_string(units))))
11394 });
11395
11396 define(interp, "qty_convert", Some(2), |_, args| {
11399 let (val, units) = extract_quantity(&args[0], "qty_convert")?;
11400 let _target = match &args[1] {
11401 Value::String(s) => s.to_string(),
11402 _ => return Err(RuntimeError::new("qty_convert: target must be unit string")),
11403 };
11404
11405 Ok(make_quantity(val, units))
11408 });
11409
11410 define(interp, "qty_check", Some(2), |_, args| {
11412 let (_, units) = extract_quantity(&args[0], "qty_check")?;
11413 let expected = match &args[1] {
11414 Value::String(s) => s.to_string(),
11415 _ => return Err(RuntimeError::new("qty_check: expected string")),
11416 };
11417
11418 let actual_str = units_to_string(units);
11420 Ok(Value::Bool(actual_str.contains(&expected) || expected.contains(&actual_str)))
11421 });
11422
11423 define(interp, "c_light", Some(0), |_, _| {
11426 Ok(make_quantity(299792458.0, [1, 0, -1, 0, 0, 0, 0])) });
11428
11429 define(interp, "G_gravity", Some(0), |_, _| {
11431 Ok(make_quantity(6.67430e-11, [3, -1, -2, 0, 0, 0, 0])) });
11433
11434 define(interp, "h_planck", Some(0), |_, _| {
11436 Ok(make_quantity(6.62607015e-34, [2, 1, -1, 0, 0, 0, 0])) });
11438
11439 define(interp, "e_charge", Some(0), |_, _| {
11441 Ok(make_quantity(1.602176634e-19, [0, 0, 1, 1, 0, 0, 0])) });
11443
11444 define(interp, "k_boltzmann", Some(0), |_, _| {
11446 Ok(make_quantity(1.380649e-23, [2, 1, -2, 0, -1, 0, 0])) });
11448}
11449
11450fn register_ecs(interp: &mut Interpreter) {
11530 define(interp, "ecs_world", Some(0), |_, _| {
11532 let mut world = HashMap::new();
11533 world.insert("_type".to_string(), Value::String(Rc::new("ecs_world".to_string())));
11534 world.insert("next_id".to_string(), Value::Int(0));
11535 world.insert("entities".to_string(), Value::Map(Rc::new(RefCell::new(HashMap::new()))));
11536 world.insert("components".to_string(), Value::Map(Rc::new(RefCell::new(HashMap::new()))));
11537 Ok(Value::Map(Rc::new(RefCell::new(world))))
11538 });
11539
11540 define(interp, "ecs_spawn", Some(1), |_, args| {
11542 let world = match &args[0] {
11543 Value::Map(m) => m.clone(),
11544 _ => return Err(RuntimeError::new("ecs_spawn: expected world")),
11545 };
11546
11547 let mut world_ref = world.borrow_mut();
11548 let id = match world_ref.get("next_id") {
11549 Some(Value::Int(n)) => *n,
11550 _ => 0,
11551 };
11552
11553 world_ref.insert("next_id".to_string(), Value::Int(id + 1));
11555
11556 if let Some(Value::Map(entities)) = world_ref.get("entities") {
11558 entities.borrow_mut().insert(id.to_string(), Value::Bool(true));
11559 }
11560
11561 Ok(Value::Int(id))
11562 });
11563
11564 define(interp, "ecs_despawn", Some(2), |_, args| {
11566 let world = match &args[0] {
11567 Value::Map(m) => m.clone(),
11568 _ => return Err(RuntimeError::new("ecs_despawn: expected world")),
11569 };
11570 let id = match &args[1] {
11571 Value::Int(n) => *n,
11572 _ => return Err(RuntimeError::new("ecs_despawn: expected entity id")),
11573 };
11574
11575 let world_ref = world.borrow();
11576
11577 if let Some(Value::Map(entities)) = world_ref.get("entities") {
11579 entities.borrow_mut().remove(&id.to_string());
11580 }
11581
11582 if let Some(Value::Map(components)) = world_ref.get("components") {
11584 let comps = components.borrow();
11585 for (_, comp_storage) in comps.iter() {
11586 if let Value::Map(storage) = comp_storage {
11587 storage.borrow_mut().remove(&id.to_string());
11588 }
11589 }
11590 }
11591
11592 Ok(Value::Bool(true))
11593 });
11594
11595 define(interp, "ecs_attach", Some(4), |_, args| {
11597 let world = match &args[0] {
11598 Value::Map(m) => m.clone(),
11599 _ => return Err(RuntimeError::new(
11600 "ecs_attach() expects a world as first argument.\n\
11601 Usage: ecs_attach(world, entity_id, component_name, data)\n\
11602 Example:\n\
11603 let world = ecs_world();\n\
11604 let e = ecs_spawn(world);\n\
11605 ecs_attach(world, e, \"Position\", vec3(0, 0, 0));"
11606 )),
11607 };
11608 let id = match &args[1] {
11609 Value::Int(n) => *n,
11610 _ => return Err(RuntimeError::new(
11611 "ecs_attach() expects an entity ID (integer) as second argument.\n\
11612 Entity IDs are returned by ecs_spawn().\n\
11613 Example:\n\
11614 let entity = ecs_spawn(world); // Returns 0, 1, 2...\n\
11615 ecs_attach(world, entity, \"Health\", 100);"
11616 )),
11617 };
11618 let comp_name = match &args[2] {
11619 Value::String(s) => s.to_string(),
11620 _ => return Err(RuntimeError::new(
11621 "ecs_attach() expects a string component name as third argument.\n\
11622 Common component names: \"Position\", \"Velocity\", \"Health\", \"Sprite\"\n\
11623 Example: ecs_attach(world, entity, \"Position\", vec3(0, 0, 0));"
11624 )),
11625 };
11626 let data = args[3].clone();
11627
11628 let world_ref = world.borrow();
11629
11630 if let Some(Value::Map(components)) = world_ref.get("components") {
11632 let mut comps = components.borrow_mut();
11633
11634 let storage = comps.entry(comp_name.clone())
11635 .or_insert_with(|| Value::Map(Rc::new(RefCell::new(HashMap::new()))));
11636
11637 if let Value::Map(storage_map) = storage {
11638 storage_map.borrow_mut().insert(id.to_string(), data);
11639 }
11640 }
11641
11642 Ok(Value::Bool(true))
11643 });
11644
11645 define(interp, "ecs_get", Some(3), |_, args| {
11647 let world = match &args[0] {
11648 Value::Map(m) => m.clone(),
11649 _ => return Err(RuntimeError::new("ecs_get: expected world")),
11650 };
11651 let id = match &args[1] {
11652 Value::Int(n) => *n,
11653 _ => return Err(RuntimeError::new("ecs_get: expected entity id")),
11654 };
11655 let comp_name = match &args[2] {
11656 Value::String(s) => s.to_string(),
11657 _ => return Err(RuntimeError::new("ecs_get: expected component name")),
11658 };
11659
11660 let world_ref = world.borrow();
11661
11662 if let Some(Value::Map(components)) = world_ref.get("components") {
11663 let comps = components.borrow();
11664 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
11665 if let Some(data) = storage.borrow().get(&id.to_string()) {
11666 return Ok(data.clone());
11667 }
11668 }
11669 }
11670
11671 Ok(Value::Null)
11672 });
11673
11674 define(interp, "ecs_has", Some(3), |_, args| {
11676 let world = match &args[0] {
11677 Value::Map(m) => m.clone(),
11678 _ => return Err(RuntimeError::new("ecs_has: expected world")),
11679 };
11680 let id = match &args[1] {
11681 Value::Int(n) => *n,
11682 _ => return Err(RuntimeError::new("ecs_has: expected entity id")),
11683 };
11684 let comp_name = match &args[2] {
11685 Value::String(s) => s.to_string(),
11686 _ => return Err(RuntimeError::new("ecs_has: expected component name")),
11687 };
11688
11689 let world_ref = world.borrow();
11690
11691 if let Some(Value::Map(components)) = world_ref.get("components") {
11692 let comps = components.borrow();
11693 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
11694 return Ok(Value::Bool(storage.borrow().contains_key(&id.to_string())));
11695 }
11696 }
11697
11698 Ok(Value::Bool(false))
11699 });
11700
11701 define(interp, "ecs_remove", Some(3), |_, args| {
11703 let world = match &args[0] {
11704 Value::Map(m) => m.clone(),
11705 _ => return Err(RuntimeError::new("ecs_remove: expected world")),
11706 };
11707 let id = match &args[1] {
11708 Value::Int(n) => *n,
11709 _ => return Err(RuntimeError::new("ecs_remove: expected entity id")),
11710 };
11711 let comp_name = match &args[2] {
11712 Value::String(s) => s.to_string(),
11713 _ => return Err(RuntimeError::new("ecs_remove: expected component name")),
11714 };
11715
11716 let world_ref = world.borrow();
11717
11718 if let Some(Value::Map(components)) = world_ref.get("components") {
11719 let comps = components.borrow();
11720 if let Some(Value::Map(storage)) = comps.get(&comp_name) {
11721 storage.borrow_mut().remove(&id.to_string());
11722 return Ok(Value::Bool(true));
11723 }
11724 }
11725
11726 Ok(Value::Bool(false))
11727 });
11728
11729 define(interp, "ecs_query", None, |_, args| {
11732 if args.is_empty() {
11733 return Err(RuntimeError::new("ecs_query: expected at least world argument"));
11734 }
11735
11736 let world = match &args[0] {
11737 Value::Map(m) => m.clone(),
11738 _ => return Err(RuntimeError::new("ecs_query: expected world")),
11739 };
11740
11741 let comp_names: Vec<String> = args[1..].iter()
11742 .filter_map(|a| match a {
11743 Value::String(s) => Some(s.to_string()),
11744 _ => None,
11745 })
11746 .collect();
11747
11748 if comp_names.is_empty() {
11749 let world_ref = world.borrow();
11751 if let Some(Value::Map(entities)) = world_ref.get("entities") {
11752 let result: Vec<Value> = entities.borrow().keys()
11753 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
11754 .collect();
11755 return Ok(Value::Array(Rc::new(RefCell::new(result))));
11756 }
11757 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
11758 }
11759
11760 let world_ref = world.borrow();
11761 let mut result_ids: Option<Vec<String>> = None;
11762
11763 if let Some(Value::Map(components)) = world_ref.get("components") {
11764 let comps = components.borrow();
11765
11766 for comp_name in &comp_names {
11767 if let Some(Value::Map(storage)) = comps.get(comp_name) {
11768 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
11769
11770 result_ids = Some(match result_ids {
11771 None => keys,
11772 Some(existing) => existing.into_iter()
11773 .filter(|k| keys.contains(k))
11774 .collect(),
11775 });
11776 } else {
11777 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
11779 }
11780 }
11781 }
11782
11783 let result: Vec<Value> = result_ids.unwrap_or_default()
11784 .iter()
11785 .filter_map(|k| k.parse::<i64>().ok().map(Value::Int))
11786 .collect();
11787
11788 Ok(Value::Array(Rc::new(RefCell::new(result))))
11789 });
11790
11791 define(interp, "ecs_query_with", Some(3), |interp, args| {
11794 let world = match &args[0] {
11795 Value::Map(m) => m.clone(),
11796 _ => return Err(RuntimeError::new("ecs_query_with: expected world")),
11797 };
11798 let comp_names: Vec<String> = match &args[1] {
11799 Value::Array(arr) => arr.borrow().iter()
11800 .filter_map(|v| match v {
11801 Value::String(s) => Some(s.to_string()),
11802 _ => None,
11803 })
11804 .collect(),
11805 _ => return Err(RuntimeError::new("ecs_query_with: expected array of component names")),
11806 };
11807 let callback = match &args[2] {
11808 Value::Function(f) => f.clone(),
11809 _ => return Err(RuntimeError::new("ecs_query_with: expected callback function")),
11810 };
11811
11812 let mut callback_data: Vec<(i64, HashMap<String, Value>)> = Vec::new();
11814
11815 {
11816 let world_ref = world.borrow();
11817 let mut result_ids: Option<Vec<String>> = None;
11818
11819 if let Some(Value::Map(components)) = world_ref.get("components") {
11820 let comps = components.borrow();
11821
11822 for comp_name in &comp_names {
11823 if let Some(Value::Map(storage)) = comps.get(comp_name) {
11824 let keys: Vec<String> = storage.borrow().keys().cloned().collect();
11825 result_ids = Some(match result_ids {
11826 None => keys,
11827 Some(existing) => existing.into_iter()
11828 .filter(|k| keys.contains(k))
11829 .collect(),
11830 });
11831 } else {
11832 result_ids = Some(vec![]);
11833 break;
11834 }
11835 }
11836
11837 for id_str in result_ids.unwrap_or_default() {
11839 if let Ok(id) = id_str.parse::<i64>() {
11840 let mut entity_comps = HashMap::new();
11841 for comp_name in &comp_names {
11842 if let Some(Value::Map(storage)) = comps.get(comp_name) {
11843 if let Some(data) = storage.borrow().get(&id_str) {
11844 entity_comps.insert(comp_name.clone(), data.clone());
11845 }
11846 }
11847 }
11848 callback_data.push((id, entity_comps));
11849 }
11850 }
11851 }
11852 } for (id, entity_comps) in callback_data {
11856 let callback_args = vec![
11857 Value::Int(id),
11858 Value::Map(Rc::new(RefCell::new(entity_comps))),
11859 ];
11860 interp.call_function(&callback, callback_args)?;
11861 }
11862
11863 Ok(Value::Null)
11864 });
11865
11866 define(interp, "ecs_count", Some(1), |_, args| {
11868 let world = match &args[0] {
11869 Value::Map(m) => m.clone(),
11870 _ => return Err(RuntimeError::new("ecs_count: expected world")),
11871 };
11872
11873 let world_ref = world.borrow();
11874 if let Some(Value::Map(entities)) = world_ref.get("entities") {
11875 return Ok(Value::Int(entities.borrow().len() as i64));
11876 }
11877
11878 Ok(Value::Int(0))
11879 });
11880
11881 define(interp, "ecs_alive", Some(2), |_, args| {
11883 let world = match &args[0] {
11884 Value::Map(m) => m.clone(),
11885 _ => return Err(RuntimeError::new("ecs_alive: expected world")),
11886 };
11887 let id = match &args[1] {
11888 Value::Int(n) => *n,
11889 _ => return Err(RuntimeError::new("ecs_alive: expected entity id")),
11890 };
11891
11892 let world_ref = world.borrow();
11893 if let Some(Value::Map(entities)) = world_ref.get("entities") {
11894 return Ok(Value::Bool(entities.borrow().contains_key(&id.to_string())));
11895 }
11896
11897 Ok(Value::Bool(false))
11898 });
11899}
11900
11901fn register_polycultural_text(interp: &mut Interpreter) {
11921 define(interp, "script", Some(1), |_, args| {
11931 match &args[0] {
11932 Value::String(s) => {
11933 let mut script_counts: HashMap<String, usize> = HashMap::new();
11935 for c in s.chars() {
11936 if !c.is_whitespace() && !c.is_ascii_punctuation() {
11937 let script = c.script();
11938 let name = format!("{:?}", script);
11939 *script_counts.entry(name).or_insert(0) += 1;
11940 }
11941 }
11942 let dominant = script_counts.into_iter()
11944 .max_by_key(|(_, count)| *count)
11945 .map(|(name, _)| name)
11946 .unwrap_or_else(|| "Unknown".to_string());
11947 Ok(Value::String(Rc::new(dominant)))
11948 }
11949 _ => Err(RuntimeError::new("script() requires string")),
11950 }
11951 });
11952
11953 define(interp, "scripts", Some(1), |_, args| {
11955 match &args[0] {
11956 Value::String(s) => {
11957 let mut scripts: Vec<String> = s.chars()
11958 .filter(|c| !c.is_whitespace() && !c.is_ascii_punctuation())
11959 .map(|c| format!("{:?}", c.script()))
11960 .collect();
11961 scripts.sort();
11962 scripts.dedup();
11963 let values: Vec<Value> = scripts.into_iter()
11964 .map(|s| Value::String(Rc::new(s)))
11965 .collect();
11966 Ok(Value::Array(Rc::new(RefCell::new(values))))
11967 }
11968 _ => Err(RuntimeError::new("scripts() requires string")),
11969 }
11970 });
11971
11972 define(interp, "is_script", Some(2), |_, args| {
11974 match (&args[0], &args[1]) {
11975 (Value::String(s), Value::String(script_name)) => {
11976 let target = script_name.to_lowercase();
11977 let mut matching = 0usize;
11978 let mut total = 0usize;
11979 for c in s.chars() {
11980 if !c.is_whitespace() && !c.is_ascii_punctuation() {
11981 total += 1;
11982 let script_str = format!("{:?}", c.script()).to_lowercase();
11983 if script_str == target {
11984 matching += 1;
11985 }
11986 }
11987 }
11988 let ratio = if total > 0 { matching as f64 / total as f64 } else { 0.0 };
11989 Ok(Value::Bool(ratio > 0.5))
11990 }
11991 _ => Err(RuntimeError::new("is_script() requires string and script name")),
11992 }
11993 });
11994
11995 define(interp, "is_latin", Some(1), |_, args| {
11997 match &args[0] {
11998 Value::String(s) => {
11999 let is_latin = s.chars()
12000 .filter(|c| !c.is_whitespace())
12001 .all(|c| matches!(c.script(), Script::Latin | Script::Common));
12002 Ok(Value::Bool(is_latin && !s.is_empty()))
12003 }
12004 _ => Err(RuntimeError::new("is_latin() requires string")),
12005 }
12006 });
12007
12008 define(interp, "is_cjk", Some(1), |_, args| {
12009 match &args[0] {
12010 Value::String(s) => {
12011 let has_cjk = s.chars().any(|c| {
12012 matches!(c.script(), Script::Han | Script::Hiragana | Script::Katakana | Script::Hangul | Script::Bopomofo)
12013 });
12014 Ok(Value::Bool(has_cjk))
12015 }
12016 _ => Err(RuntimeError::new("is_cjk() requires string")),
12017 }
12018 });
12019
12020 define(interp, "is_arabic", Some(1), |_, args| {
12021 match &args[0] {
12022 Value::String(s) => {
12023 let has_arabic = s.chars().any(|c| matches!(c.script(), Script::Arabic));
12024 Ok(Value::Bool(has_arabic))
12025 }
12026 _ => Err(RuntimeError::new("is_arabic() requires string")),
12027 }
12028 });
12029
12030 define(interp, "is_hebrew", Some(1), |_, args| {
12031 match &args[0] {
12032 Value::String(s) => {
12033 let has_hebrew = s.chars().any(|c| matches!(c.script(), Script::Hebrew));
12034 Ok(Value::Bool(has_hebrew))
12035 }
12036 _ => Err(RuntimeError::new("is_hebrew() requires string")),
12037 }
12038 });
12039
12040 define(interp, "is_cyrillic", Some(1), |_, args| {
12041 match &args[0] {
12042 Value::String(s) => {
12043 let has_cyrillic = s.chars().any(|c| matches!(c.script(), Script::Cyrillic));
12044 Ok(Value::Bool(has_cyrillic))
12045 }
12046 _ => Err(RuntimeError::new("is_cyrillic() requires string")),
12047 }
12048 });
12049
12050 define(interp, "is_greek", Some(1), |_, args| {
12051 match &args[0] {
12052 Value::String(s) => {
12053 let has_greek = s.chars().any(|c| matches!(c.script(), Script::Greek));
12054 Ok(Value::Bool(has_greek))
12055 }
12056 _ => Err(RuntimeError::new("is_greek() requires string")),
12057 }
12058 });
12059
12060 define(interp, "is_devanagari", Some(1), |_, args| {
12061 match &args[0] {
12062 Value::String(s) => {
12063 let has_devanagari = s.chars().any(|c| matches!(c.script(), Script::Devanagari));
12064 Ok(Value::Bool(has_devanagari))
12065 }
12066 _ => Err(RuntimeError::new("is_devanagari() requires string")),
12067 }
12068 });
12069
12070 define(interp, "is_thai", Some(1), |_, args| {
12071 match &args[0] {
12072 Value::String(s) => {
12073 let has_thai = s.chars().any(|c| matches!(c.script(), Script::Thai));
12074 Ok(Value::Bool(has_thai))
12075 }
12076 _ => Err(RuntimeError::new("is_thai() requires string")),
12077 }
12078 });
12079
12080 define(interp, "is_hangul", Some(1), |_, args| {
12081 match &args[0] {
12082 Value::String(s) => {
12083 let has_hangul = s.chars().any(|c| matches!(c.script(), Script::Hangul));
12084 Ok(Value::Bool(has_hangul))
12085 }
12086 _ => Err(RuntimeError::new("is_hangul() requires string")),
12087 }
12088 });
12089
12090 define(interp, "is_hiragana", Some(1), |_, args| {
12091 match &args[0] {
12092 Value::String(s) => {
12093 let has_hiragana = s.chars().any(|c| matches!(c.script(), Script::Hiragana));
12094 Ok(Value::Bool(has_hiragana))
12095 }
12096 _ => Err(RuntimeError::new("is_hiragana() requires string")),
12097 }
12098 });
12099
12100 define(interp, "is_katakana", Some(1), |_, args| {
12101 match &args[0] {
12102 Value::String(s) => {
12103 let has_katakana = s.chars().any(|c| matches!(c.script(), Script::Katakana));
12104 Ok(Value::Bool(has_katakana))
12105 }
12106 _ => Err(RuntimeError::new("is_katakana() requires string")),
12107 }
12108 });
12109
12110 define(interp, "char_script", Some(1), |_, args| {
12112 match &args[0] {
12113 Value::Char(c) => {
12114 let script = format!("{:?}", c.script());
12115 Ok(Value::String(Rc::new(script)))
12116 }
12117 Value::String(s) if s.chars().count() == 1 => {
12118 let c = s.chars().next().unwrap();
12119 let script = format!("{:?}", c.script());
12120 Ok(Value::String(Rc::new(script)))
12121 }
12122 _ => Err(RuntimeError::new("char_script() requires single character")),
12123 }
12124 });
12125
12126 define(interp, "text_direction", Some(1), |_, args| {
12136 match &args[0] {
12137 Value::String(s) => {
12138 let bidi_info = BidiInfo::new(s, None);
12139 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
12141 let direction = if has_rtl { "rtl" } else { "ltr" };
12142 Ok(Value::String(Rc::new(direction.to_string())))
12143 }
12144 _ => Err(RuntimeError::new("text_direction() requires string")),
12145 }
12146 });
12147
12148 define(interp, "is_rtl", Some(1), |_, args| {
12150 match &args[0] {
12151 Value::String(s) => {
12152 let bidi_info = BidiInfo::new(s, None);
12153 let has_rtl = bidi_info.paragraphs.iter().any(|p| p.level.is_rtl());
12154 Ok(Value::Bool(has_rtl))
12155 }
12156 _ => Err(RuntimeError::new("is_rtl() requires string")),
12157 }
12158 });
12159
12160 define(interp, "is_ltr", Some(1), |_, args| {
12162 match &args[0] {
12163 Value::String(s) => {
12164 let bidi_info = BidiInfo::new(s, None);
12165 let is_ltr = bidi_info.paragraphs.iter().all(|p| !p.level.is_rtl());
12166 Ok(Value::Bool(is_ltr))
12167 }
12168 _ => Err(RuntimeError::new("is_ltr() requires string")),
12169 }
12170 });
12171
12172 define(interp, "is_bidi", Some(1), |_, args| {
12174 match &args[0] {
12175 Value::String(s) => {
12176 let has_rtl = s.chars().any(|c| matches!(c.script(), Script::Arabic | Script::Hebrew | Script::Syriac | Script::Thaana));
12178 let has_ltr = s.chars().any(|c| matches!(c.script(), Script::Latin | Script::Greek | Script::Cyrillic));
12179 Ok(Value::Bool(has_rtl && has_ltr))
12180 }
12181 _ => Err(RuntimeError::new("is_bidi() requires string")),
12182 }
12183 });
12184
12185 define(interp, "bidi_reorder", Some(1), |_, args| {
12187 match &args[0] {
12188 Value::String(s) => {
12189 let bidi_info = BidiInfo::new(s, None);
12190 let mut result = String::new();
12191 for para in &bidi_info.paragraphs {
12192 let line = para.range.clone();
12193 let reordered = bidi_info.reorder_line(para, line);
12194 result.push_str(&reordered);
12195 }
12196 Ok(Value::String(Rc::new(result)))
12197 }
12198 _ => Err(RuntimeError::new("bidi_reorder() requires string")),
12199 }
12200 });
12201
12202 define(interp, "display_width", Some(1), |_, args| {
12212 match &args[0] {
12213 Value::String(s) => {
12214 let width = UnicodeWidthStr::width(s.as_str());
12215 Ok(Value::Int(width as i64))
12216 }
12217 _ => Err(RuntimeError::new("display_width() requires string")),
12218 }
12219 });
12220
12221 define(interp, "is_fullwidth", Some(1), |_, args| {
12223 match &args[0] {
12224 Value::String(s) => {
12225 let char_count = s.chars().count();
12226 let display_width = UnicodeWidthStr::width(s.as_str());
12227 Ok(Value::Bool(display_width > char_count))
12229 }
12230 _ => Err(RuntimeError::new("is_fullwidth() requires string")),
12231 }
12232 });
12233
12234 define(interp, "pad_display", Some(3), |_, args| {
12236 match (&args[0], &args[1], &args[2]) {
12237 (Value::String(s), Value::Int(target_width), Value::String(align)) => {
12238 let current_width = UnicodeWidthStr::width(s.as_str());
12239 let target = *target_width as usize;
12240 if current_width >= target {
12241 return Ok(Value::String(s.clone()));
12242 }
12243 let padding = target - current_width;
12244 let result = match align.as_str() {
12245 "left" => format!("{}{}", s, " ".repeat(padding)),
12246 "right" => format!("{}{}", " ".repeat(padding), s),
12247 "center" => {
12248 let left = padding / 2;
12249 let right = padding - left;
12250 format!("{}{}{}", " ".repeat(left), s, " ".repeat(right))
12251 }
12252 _ => return Err(RuntimeError::new("pad_display: align must be 'left', 'right', or 'center'")),
12253 };
12254 Ok(Value::String(Rc::new(result)))
12255 }
12256 _ => Err(RuntimeError::new("pad_display() requires string, width, and alignment")),
12257 }
12258 });
12259
12260 define(interp, "transliterate", Some(1), |_, args| {
12270 match &args[0] {
12271 Value::String(s) => {
12272 let ascii = deunicode(s);
12273 Ok(Value::String(Rc::new(ascii)))
12274 }
12275 _ => Err(RuntimeError::new("transliterate() requires string")),
12276 }
12277 });
12278
12279 define(interp, "to_ascii", Some(1), |_, args| {
12281 match &args[0] {
12282 Value::String(s) => {
12283 let ascii = deunicode(s);
12284 Ok(Value::String(Rc::new(ascii)))
12285 }
12286 _ => Err(RuntimeError::new("to_ascii() requires string")),
12287 }
12288 });
12289
12290 define(interp, "slugify", Some(1), |_, args| {
12292 match &args[0] {
12293 Value::String(s) => {
12294 let ascii = deunicode(s);
12295 let slug: String = ascii
12296 .to_lowercase()
12297 .chars()
12298 .map(|c| if c.is_alphanumeric() { c } else { '-' })
12299 .collect();
12300 let mut result = String::new();
12302 let mut last_was_dash = true; for c in slug.chars() {
12304 if c == '-' {
12305 if !last_was_dash {
12306 result.push(c);
12307 last_was_dash = true;
12308 }
12309 } else {
12310 result.push(c);
12311 last_was_dash = false;
12312 }
12313 }
12314 if result.ends_with('-') {
12316 result.pop();
12317 }
12318 Ok(Value::String(Rc::new(result)))
12319 }
12320 _ => Err(RuntimeError::new("slugify() requires string")),
12321 }
12322 });
12323
12324 define(interp, "strip_diacritics", Some(1), |_, args| {
12334 match &args[0] {
12335 Value::String(s) => {
12336 let decomposed: String = s.nfd().collect();
12338 let stripped: String = decomposed.chars()
12340 .filter(|c| {
12341 let code = *c as u32;
12345 !(0x0300..=0x036F).contains(&code) &&
12347 !(0x1AB0..=0x1AFF).contains(&code) &&
12348 !(0x1DC0..=0x1DFF).contains(&code) &&
12349 !(0x20D0..=0x20FF).contains(&code) &&
12350 !(0xFE20..=0xFE2F).contains(&code)
12351 })
12352 .collect();
12353 Ok(Value::String(Rc::new(stripped)))
12354 }
12355 _ => Err(RuntimeError::new("strip_diacritics() requires string")),
12356 }
12357 });
12358
12359 define(interp, "has_diacritics", Some(1), |_, args| {
12361 match &args[0] {
12362 Value::String(s) => {
12363 let decomposed: String = s.nfd().collect();
12364 let has_marks = decomposed.chars().any(|c| {
12365 let code = c as u32;
12366 (0x0300..=0x036F).contains(&code) ||
12367 (0x1AB0..=0x1AFF).contains(&code) ||
12368 (0x1DC0..=0x1DFF).contains(&code) ||
12369 (0x20D0..=0x20FF).contains(&code) ||
12370 (0xFE20..=0xFE2F).contains(&code)
12371 });
12372 Ok(Value::Bool(has_marks))
12373 }
12374 _ => Err(RuntimeError::new("has_diacritics() requires string")),
12375 }
12376 });
12377
12378 define(interp, "normalize_accents", Some(2), |_, args| {
12380 match (&args[0], &args[1]) {
12381 (Value::String(s), Value::String(form)) => {
12382 let result = match form.as_str() {
12383 "composed" | "nfc" => s.nfc().collect(),
12384 "decomposed" | "nfd" => s.nfd().collect(),
12385 _ => return Err(RuntimeError::new("normalize_accents: form must be 'composed' or 'decomposed'")),
12386 };
12387 Ok(Value::String(Rc::new(result)))
12388 }
12389 _ => Err(RuntimeError::new("normalize_accents() requires string and form")),
12390 }
12391 });
12392
12393 define(interp, "upper_locale", Some(2), |_, args| {
12405 match (&args[0], &args[1]) {
12406 (Value::String(s), Value::String(locale_str)) => {
12407 let case_mapper = CaseMapper::new();
12408 let langid: LanguageIdentifier = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
12409 let result = case_mapper.uppercase_to_string(s, &langid);
12410 Ok(Value::String(Rc::new(result)))
12411 }
12412 _ => Err(RuntimeError::new("upper_locale() requires string and locale")),
12413 }
12414 });
12415
12416 define(interp, "lower_locale", Some(2), |_, args| {
12418 match (&args[0], &args[1]) {
12419 (Value::String(s), Value::String(locale_str)) => {
12420 let case_mapper = CaseMapper::new();
12421 let langid: LanguageIdentifier = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
12422 let result = case_mapper.lowercase_to_string(s, &langid);
12423 Ok(Value::String(Rc::new(result)))
12424 }
12425 _ => Err(RuntimeError::new("lower_locale() requires string and locale")),
12426 }
12427 });
12428
12429 define(interp, "titlecase_locale", Some(2), |_, args| {
12431 match (&args[0], &args[1]) {
12432 (Value::String(s), Value::String(locale_str)) => {
12433 let case_mapper = CaseMapper::new();
12434 let langid: LanguageIdentifier = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
12435 let options = TitlecaseOptions::default();
12436 let result = case_mapper.titlecase_segment_with_only_case_data_to_string(s, &langid, options);
12437 Ok(Value::String(Rc::new(result)))
12438 }
12439 _ => Err(RuntimeError::new("titlecase_locale() requires string and locale")),
12440 }
12441 });
12442
12443 define(interp, "case_fold", Some(1), |_, args| {
12445 match &args[0] {
12446 Value::String(s) => {
12447 let case_mapper = CaseMapper::new();
12448 let result = case_mapper.fold_string(s);
12449 Ok(Value::String(Rc::new(result)))
12450 }
12451 _ => Err(RuntimeError::new("case_fold() requires string")),
12452 }
12453 });
12454
12455 define(interp, "case_insensitive_eq", Some(2), |_, args| {
12457 match (&args[0], &args[1]) {
12458 (Value::String(a), Value::String(b)) => {
12459 let case_mapper = CaseMapper::new();
12460 let folded_a = case_mapper.fold_string(a);
12461 let folded_b = case_mapper.fold_string(b);
12462 Ok(Value::Bool(folded_a == folded_b))
12463 }
12464 _ => Err(RuntimeError::new("case_insensitive_eq() requires two strings")),
12465 }
12466 });
12467
12468 define(interp, "compare_locale", Some(3), |_, args| {
12480 match (&args[0], &args[1], &args[2]) {
12481 (Value::String(a), Value::String(b), Value::String(locale_str)) => {
12482 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
12483 let options = CollatorOptions::new();
12484 let collator = Collator::try_new(&locale.into(), options)
12485 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
12486 let result = match collator.compare(a, b) {
12487 std::cmp::Ordering::Less => -1,
12488 std::cmp::Ordering::Equal => 0,
12489 std::cmp::Ordering::Greater => 1,
12490 };
12491 Ok(Value::Int(result))
12492 }
12493 _ => Err(RuntimeError::new("compare_locale() requires two strings and locale")),
12494 }
12495 });
12496
12497 define(interp, "sort_locale", Some(2), |_, args| {
12499 match (&args[0], &args[1]) {
12500 (Value::Array(arr), Value::String(locale_str)) => {
12501 let locale: Locale = locale_str.parse().unwrap_or_else(|_| "en".parse().unwrap());
12502 let options = CollatorOptions::new();
12503 let collator = Collator::try_new(&locale.into(), options)
12504 .unwrap_or_else(|_| Collator::try_new(&Default::default(), options).unwrap());
12505
12506 let mut items: Vec<(String, Value)> = arr.borrow().iter()
12507 .map(|v| {
12508 let s = match v {
12509 Value::String(s) => (**s).clone(),
12510 _ => format!("{}", v),
12511 };
12512 (s, v.clone())
12513 })
12514 .collect();
12515
12516 items.sort_by(|(a, _), (b, _)| collator.compare(a, b));
12517
12518 let sorted: Vec<Value> = items.into_iter().map(|(_, v)| v).collect();
12519 Ok(Value::Array(Rc::new(RefCell::new(sorted))))
12520 }
12521 _ => Err(RuntimeError::new("sort_locale() requires array and locale")),
12522 }
12523 });
12524
12525 define(interp, "sentences", Some(1), |_, args| {
12537 match &args[0] {
12538 Value::String(s) => {
12539 let segmenter = SentenceSegmenter::new();
12540 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
12541 let mut sentences = Vec::new();
12542 let mut start = 0;
12543 for end in breakpoints {
12544 let sentence = s[start..end].trim();
12545 if !sentence.is_empty() {
12546 sentences.push(Value::String(Rc::new(sentence.to_string())));
12547 }
12548 start = end;
12549 }
12550 Ok(Value::Array(Rc::new(RefCell::new(sentences))))
12551 }
12552 _ => Err(RuntimeError::new("sentences() requires string")),
12553 }
12554 });
12555
12556 define(interp, "sentence_count", Some(1), |_, args| {
12558 match &args[0] {
12559 Value::String(s) => {
12560 let segmenter = SentenceSegmenter::new();
12561 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
12562 let count = breakpoints.len().saturating_sub(1);
12564 Ok(Value::Int(count as i64))
12565 }
12566 _ => Err(RuntimeError::new("sentence_count() requires string")),
12567 }
12568 });
12569
12570 define(interp, "words_icu", Some(1), |_, args| {
12572 match &args[0] {
12573 Value::String(s) => {
12574 let segmenter = WordSegmenter::new_auto();
12575 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
12576 let mut words = Vec::new();
12577 let mut start = 0;
12578 for end in breakpoints {
12579 let word = &s[start..end];
12580 if !word.trim().is_empty() {
12582 words.push(Value::String(Rc::new(word.to_string())));
12583 }
12584 start = end;
12585 }
12586 Ok(Value::Array(Rc::new(RefCell::new(words))))
12587 }
12588 _ => Err(RuntimeError::new("words_icu() requires string")),
12589 }
12590 });
12591
12592 define(interp, "word_count_icu", Some(1), |_, args| {
12594 match &args[0] {
12595 Value::String(s) => {
12596 let segmenter = WordSegmenter::new_auto();
12597 let breakpoints: Vec<usize> = segmenter.segment_str(s).collect();
12598 let mut count = 0;
12599 let mut start = 0;
12600 for end in breakpoints {
12601 let word = &s[start..end];
12602 if !word.trim().is_empty() && word.chars().any(|c| c.is_alphanumeric()) {
12603 count += 1;
12604 }
12605 start = end;
12606 }
12607 Ok(Value::Int(count))
12608 }
12609 _ => Err(RuntimeError::new("word_count_icu() requires string")),
12610 }
12611 });
12612
12613 define(interp, "is_emoji", Some(1), |_, args| {
12619 match &args[0] {
12620 Value::String(s) => {
12621 let has_emoji = s.chars().any(|c| {
12622 let code = c as u32;
12623 (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) });
12638 Ok(Value::Bool(has_emoji))
12639 }
12640 _ => Err(RuntimeError::new("is_emoji() requires string")),
12641 }
12642 });
12643
12644 define(interp, "extract_emoji", Some(1), |_, args| {
12646 match &args[0] {
12647 Value::String(s) => {
12648 let emoji: Vec<Value> = s.graphemes(true)
12649 .filter(|g| {
12650 g.chars().any(|c| {
12651 let code = c as u32;
12652 (0x1F600..=0x1F64F).contains(&code) ||
12653 (0x1F300..=0x1F5FF).contains(&code) ||
12654 (0x1F680..=0x1F6FF).contains(&code) ||
12655 (0x1F1E0..=0x1F1FF).contains(&code) ||
12656 (0x2600..=0x26FF).contains(&code) ||
12657 (0x2700..=0x27BF).contains(&code) ||
12658 (0x1F900..=0x1F9FF).contains(&code) ||
12659 (0x1FA00..=0x1FA6F).contains(&code) ||
12660 (0x1FA70..=0x1FAFF).contains(&code)
12661 })
12662 })
12663 .map(|g| Value::String(Rc::new(g.to_string())))
12664 .collect();
12665 Ok(Value::Array(Rc::new(RefCell::new(emoji))))
12666 }
12667 _ => Err(RuntimeError::new("extract_emoji() requires string")),
12668 }
12669 });
12670
12671 define(interp, "strip_emoji", Some(1), |_, args| {
12673 match &args[0] {
12674 Value::String(s) => {
12675 let stripped: String = s.graphemes(true)
12676 .filter(|g| {
12677 !g.chars().any(|c| {
12678 let code = c as u32;
12679 (0x1F600..=0x1F64F).contains(&code) ||
12680 (0x1F300..=0x1F5FF).contains(&code) ||
12681 (0x1F680..=0x1F6FF).contains(&code) ||
12682 (0x1F1E0..=0x1F1FF).contains(&code) ||
12683 (0x2600..=0x26FF).contains(&code) ||
12684 (0x2700..=0x27BF).contains(&code) ||
12685 (0x1F900..=0x1F9FF).contains(&code) ||
12686 (0x1FA00..=0x1FA6F).contains(&code) ||
12687 (0x1FA70..=0x1FAFF).contains(&code)
12688 })
12689 })
12690 .collect();
12691 Ok(Value::String(Rc::new(stripped)))
12692 }
12693 _ => Err(RuntimeError::new("strip_emoji() requires string")),
12694 }
12695 });
12696
12697 define(interp, "script_runs", Some(1), |_, args| {
12703 match &args[0] {
12704 Value::String(s) => {
12705 let mut runs: Vec<Value> = Vec::new();
12706 let mut current_run = String::new();
12707 let mut current_script: Option<Script> = None;
12708
12709 for c in s.chars() {
12710 let script = c.script();
12711 if script != Script::Common && script != Script::Inherited {
12713 if let Some(curr) = current_script {
12714 if script != curr {
12715 if !current_run.is_empty() {
12717 runs.push(Value::String(Rc::new(current_run.clone())));
12718 current_run.clear();
12719 }
12720 current_script = Some(script);
12721 }
12722 } else {
12723 current_script = Some(script);
12724 }
12725 }
12726 current_run.push(c);
12727 }
12728
12729 if !current_run.is_empty() {
12731 runs.push(Value::String(Rc::new(current_run)));
12732 }
12733
12734 Ok(Value::Array(Rc::new(RefCell::new(runs))))
12735 }
12736 _ => Err(RuntimeError::new("script_runs() requires string")),
12737 }
12738 });
12739
12740 define(interp, "script_ratio", Some(1), |_, args| {
12742 match &args[0] {
12743 Value::String(s) => {
12744 let mut script_counts: HashMap<String, usize> = HashMap::new();
12745 let mut total = 0usize;
12746
12747 for c in s.chars() {
12748 if !c.is_whitespace() && c != ' ' {
12749 let script = format!("{:?}", c.script());
12750 *script_counts.entry(script).or_insert(0) += 1;
12751 total += 1;
12752 }
12753 }
12754
12755 let mut result = HashMap::new();
12757 for (script, count) in script_counts {
12758 let ratio = if total > 0 { count as f64 / total as f64 } else { 0.0 };
12759 result.insert(script, Value::Float(ratio));
12760 }
12761
12762 let map = Rc::new(RefCell::new(result));
12763 Ok(Value::Map(map))
12764 }
12765 _ => Err(RuntimeError::new("script_ratio() requires string")),
12766 }
12767 });
12768
12769 define(interp, "locale_name", Some(1), |_, args| {
12775 match &args[0] {
12776 Value::String(locale_str) => {
12777 Ok(Value::String(locale_str.clone()))
12780 }
12781 _ => Err(RuntimeError::new("locale_name() requires string")),
12782 }
12783 });
12784
12785 define(interp, "supported_locales", Some(0), |_, _| {
12787 let locales = vec![
12789 "ar", "bg", "ca", "cs", "da", "de", "el", "en", "es", "et",
12790 "fi", "fr", "he", "hi", "hr", "hu", "id", "it", "ja", "ko",
12791 "lt", "lv", "ms", "nb", "nl", "pl", "pt", "ro", "ru", "sk",
12792 "sl", "sr", "sv", "th", "tr", "uk", "vi", "zh",
12793 ];
12794 let values: Vec<Value> = locales.into_iter()
12795 .map(|s| Value::String(Rc::new(s.to_string())))
12796 .collect();
12797 Ok(Value::Array(Rc::new(RefCell::new(values))))
12798 });
12799}
12800
12801fn register_text_intelligence(interp: &mut Interpreter) {
12806 define(interp, "levenshtein", Some(2), |_, args| {
12812 match (&args[0], &args[1]) {
12813 (Value::String(a), Value::String(b)) => {
12814 let distance = strsim::levenshtein(a, b);
12815 Ok(Value::Int(distance as i64))
12816 }
12817 _ => Err(RuntimeError::new("levenshtein() requires two strings")),
12818 }
12819 });
12820
12821 define(interp, "levenshtein_normalized", Some(2), |_, args| {
12823 match (&args[0], &args[1]) {
12824 (Value::String(a), Value::String(b)) => {
12825 let distance = strsim::normalized_levenshtein(a, b);
12826 Ok(Value::Float(distance))
12827 }
12828 _ => Err(RuntimeError::new("levenshtein_normalized() requires two strings")),
12829 }
12830 });
12831
12832 define(interp, "jaro", Some(2), |_, args| {
12834 match (&args[0], &args[1]) {
12835 (Value::String(a), Value::String(b)) => {
12836 let sim = strsim::jaro(a, b);
12837 Ok(Value::Float(sim))
12838 }
12839 _ => Err(RuntimeError::new("jaro() requires two strings")),
12840 }
12841 });
12842
12843 define(interp, "jaro_winkler", Some(2), |_, args| {
12845 match (&args[0], &args[1]) {
12846 (Value::String(a), Value::String(b)) => {
12847 let sim = strsim::jaro_winkler(a, b);
12848 Ok(Value::Float(sim))
12849 }
12850 _ => Err(RuntimeError::new("jaro_winkler() requires two strings")),
12851 }
12852 });
12853
12854 define(interp, "sorensen_dice", Some(2), |_, args| {
12856 match (&args[0], &args[1]) {
12857 (Value::String(a), Value::String(b)) => {
12858 let sim = strsim::sorensen_dice(a, b);
12859 Ok(Value::Float(sim))
12860 }
12861 _ => Err(RuntimeError::new("sorensen_dice() requires two strings")),
12862 }
12863 });
12864
12865 define(interp, "damerau_levenshtein", Some(2), |_, args| {
12867 match (&args[0], &args[1]) {
12868 (Value::String(a), Value::String(b)) => {
12869 let distance = strsim::damerau_levenshtein(a, b);
12870 Ok(Value::Int(distance as i64))
12871 }
12872 _ => Err(RuntimeError::new("damerau_levenshtein() requires two strings")),
12873 }
12874 });
12875
12876 define(interp, "osa_distance", Some(2), |_, args| {
12878 match (&args[0], &args[1]) {
12879 (Value::String(a), Value::String(b)) => {
12880 let distance = strsim::osa_distance(a, b);
12881 Ok(Value::Int(distance as i64))
12882 }
12883 _ => Err(RuntimeError::new("osa_distance() requires two strings")),
12884 }
12885 });
12886
12887 define(interp, "fuzzy_match", Some(3), |_, args| {
12889 match (&args[0], &args[1], &args[2]) {
12890 (Value::String(a), Value::String(b), Value::Float(threshold)) => {
12891 let sim = strsim::jaro_winkler(a, b);
12892 Ok(Value::Bool(sim >= *threshold))
12893 }
12894 (Value::String(a), Value::String(b), Value::Int(threshold)) => {
12895 let sim = strsim::jaro_winkler(a, b);
12896 Ok(Value::Bool(sim >= *threshold as f64))
12897 }
12898 _ => Err(RuntimeError::new("fuzzy_match() requires two strings and threshold")),
12899 }
12900 });
12901
12902 define(interp, "fuzzy_search", Some(3), |_, args| {
12904 match (&args[0], &args[1], &args[2]) {
12905 (Value::String(query), Value::Array(items), Value::Int(limit)) => {
12906 let items_ref = items.borrow();
12907 let mut scores: Vec<(f64, &str)> = items_ref.iter()
12908 .filter_map(|v| {
12909 if let Value::String(s) = v {
12910 Some((strsim::jaro_winkler(query, s), s.as_str()))
12911 } else {
12912 None
12913 }
12914 })
12915 .collect();
12916 scores.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
12917 let results: Vec<Value> = scores.into_iter()
12918 .take(*limit as usize)
12919 .map(|(_, s)| Value::String(Rc::new(s.to_string())))
12920 .collect();
12921 Ok(Value::Array(Rc::new(RefCell::new(results))))
12922 }
12923 _ => Err(RuntimeError::new("fuzzy_search() requires query string, array, and limit")),
12924 }
12925 });
12926
12927 define(interp, "soundex", Some(1), |_, args| {
12933 match &args[0] {
12934 Value::String(s) => {
12935 let code = compute_soundex(s);
12936 Ok(Value::String(Rc::new(code)))
12937 }
12938 _ => Err(RuntimeError::new("soundex() requires string")),
12939 }
12940 });
12941
12942 define(interp, "soundex_match", Some(2), |_, args| {
12944 match (&args[0], &args[1]) {
12945 (Value::String(a), Value::String(b)) => {
12946 let code_a = compute_soundex(a);
12947 let code_b = compute_soundex(b);
12948 Ok(Value::Bool(code_a == code_b))
12949 }
12950 _ => Err(RuntimeError::new("soundex_match() requires two strings")),
12951 }
12952 });
12953
12954 define(interp, "metaphone", Some(1), |_, args| {
12956 match &args[0] {
12957 Value::String(s) => {
12958 let code = compute_metaphone(s);
12959 Ok(Value::String(Rc::new(code)))
12960 }
12961 _ => Err(RuntimeError::new("metaphone() requires string")),
12962 }
12963 });
12964
12965 define(interp, "metaphone_match", Some(2), |_, args| {
12967 match (&args[0], &args[1]) {
12968 (Value::String(a), Value::String(b)) => {
12969 let code_a = compute_metaphone(a);
12970 let code_b = compute_metaphone(b);
12971 Ok(Value::Bool(code_a == code_b))
12972 }
12973 _ => Err(RuntimeError::new("metaphone_match() requires two strings")),
12974 }
12975 });
12976
12977 define(interp, "cologne_phonetic", Some(1), |_, args| {
12979 match &args[0] {
12980 Value::String(s) => {
12981 let code = compute_cologne(s);
12982 Ok(Value::String(Rc::new(code)))
12983 }
12984 _ => Err(RuntimeError::new("cologne_phonetic() requires string")),
12985 }
12986 });
12987
12988 define(interp, "detect_language", Some(1), |_, args| {
12994 match &args[0] {
12995 Value::String(s) => {
12996 if let Some(info) = detect(s) {
12997 let lang_code = match info.lang() {
12998 Lang::Eng => "en",
12999 Lang::Spa => "es",
13000 Lang::Fra => "fr",
13001 Lang::Deu => "de",
13002 Lang::Ita => "it",
13003 Lang::Por => "pt",
13004 Lang::Rus => "ru",
13005 Lang::Ara => "ar",
13006 Lang::Hin => "hi",
13007 Lang::Cmn => "zh",
13008 Lang::Jpn => "ja",
13009 Lang::Kor => "ko",
13010 Lang::Nld => "nl",
13011 Lang::Swe => "sv",
13012 Lang::Tur => "tr",
13013 Lang::Pol => "pl",
13014 Lang::Ukr => "uk",
13015 Lang::Ces => "cs",
13016 Lang::Dan => "da",
13017 Lang::Fin => "fi",
13018 Lang::Ell => "el",
13019 Lang::Heb => "he",
13020 Lang::Hun => "hu",
13021 Lang::Ind => "id",
13022 Lang::Nob => "no",
13023 Lang::Ron => "ro",
13024 Lang::Slk => "sk",
13025 Lang::Tha => "th",
13026 Lang::Vie => "vi",
13027 _ => "unknown",
13028 };
13029 Ok(Value::String(Rc::new(lang_code.to_string())))
13030 } else {
13031 Ok(Value::String(Rc::new("unknown".to_string())))
13032 }
13033 }
13034 _ => Err(RuntimeError::new("detect_language() requires string")),
13035 }
13036 });
13037
13038 define(interp, "detect_language_confidence", Some(1), |_, args| {
13040 match &args[0] {
13041 Value::String(s) => {
13042 if let Some(info) = detect(s) {
13043 let lang_code = match info.lang() {
13044 Lang::Eng => "en",
13045 Lang::Spa => "es",
13046 Lang::Fra => "fr",
13047 Lang::Deu => "de",
13048 Lang::Ita => "it",
13049 Lang::Por => "pt",
13050 Lang::Rus => "ru",
13051 Lang::Ara => "ar",
13052 Lang::Cmn => "zh",
13053 Lang::Jpn => "ja",
13054 _ => "unknown",
13055 };
13056 let confidence = info.confidence();
13057 let mut map = HashMap::new();
13058 map.insert(
13059 "lang".to_string(),
13060 Value::String(Rc::new(lang_code.to_string())),
13061 );
13062 map.insert("confidence".to_string(), Value::Float(confidence as f64));
13063 Ok(Value::Map(Rc::new(RefCell::new(map))))
13064 } else {
13065 let mut map = HashMap::new();
13066 map.insert(
13067 "lang".to_string(),
13068 Value::String(Rc::new("unknown".to_string())),
13069 );
13070 map.insert("confidence".to_string(), Value::Float(0.0));
13071 Ok(Value::Map(Rc::new(RefCell::new(map))))
13072 }
13073 }
13074 _ => Err(RuntimeError::new("detect_language_confidence() requires string")),
13075 }
13076 });
13077
13078 define(interp, "detect_script_whatlang", Some(1), |_, args| {
13080 match &args[0] {
13081 Value::String(s) => {
13082 if let Some(info) = detect(s) {
13083 let script_name = match info.script() {
13084 WhatLangScript::Latin => "Latin",
13085 WhatLangScript::Cyrillic => "Cyrillic",
13086 WhatLangScript::Arabic => "Arabic",
13087 WhatLangScript::Devanagari => "Devanagari",
13088 WhatLangScript::Ethiopic => "Ethiopic",
13089 WhatLangScript::Georgian => "Georgian",
13090 WhatLangScript::Greek => "Greek",
13091 WhatLangScript::Gujarati => "Gujarati",
13092 WhatLangScript::Gurmukhi => "Gurmukhi",
13093 WhatLangScript::Hangul => "Hangul",
13094 WhatLangScript::Hebrew => "Hebrew",
13095 WhatLangScript::Hiragana => "Hiragana",
13096 WhatLangScript::Kannada => "Kannada",
13097 WhatLangScript::Katakana => "Katakana",
13098 WhatLangScript::Khmer => "Khmer",
13099 WhatLangScript::Malayalam => "Malayalam",
13100 WhatLangScript::Mandarin => "Mandarin",
13101 WhatLangScript::Myanmar => "Myanmar",
13102 WhatLangScript::Oriya => "Oriya",
13103 WhatLangScript::Sinhala => "Sinhala",
13104 WhatLangScript::Tamil => "Tamil",
13105 WhatLangScript::Telugu => "Telugu",
13106 WhatLangScript::Thai => "Thai",
13107 WhatLangScript::Bengali => "Bengali",
13108 WhatLangScript::Armenian => "Armenian",
13109 };
13110 Ok(Value::String(Rc::new(script_name.to_string())))
13111 } else {
13112 Ok(Value::String(Rc::new("Unknown".to_string())))
13113 }
13114 }
13115 _ => Err(RuntimeError::new("detect_script_whatlang() requires string")),
13116 }
13117 });
13118
13119 define(interp, "is_language", Some(2), |_, args| {
13121 match (&args[0], &args[1]) {
13122 (Value::String(s), Value::String(lang)) => {
13123 if let Some(info) = detect(s) {
13124 let detected = match info.lang() {
13125 Lang::Eng => "en",
13126 Lang::Spa => "es",
13127 Lang::Fra => "fr",
13128 Lang::Deu => "de",
13129 Lang::Ita => "it",
13130 Lang::Por => "pt",
13131 Lang::Rus => "ru",
13132 _ => "unknown",
13133 };
13134 Ok(Value::Bool(detected == lang.as_str()))
13135 } else {
13136 Ok(Value::Bool(false))
13137 }
13138 }
13139 _ => Err(RuntimeError::new("is_language() requires string and language code")),
13140 }
13141 });
13142
13143 define(interp, "token_count", Some(1), |_, args| {
13149 match &args[0] {
13150 Value::String(s) => {
13151 if let Ok(bpe) = cl100k_base() {
13152 let tokens = bpe.encode_with_special_tokens(s);
13153 Ok(Value::Int(tokens.len() as i64))
13154 } else {
13155 Err(RuntimeError::new("Failed to initialize tokenizer"))
13156 }
13157 }
13158 _ => Err(RuntimeError::new("token_count() requires string")),
13159 }
13160 });
13161
13162 define(interp, "token_count_model", Some(2), |_, args| {
13164 match (&args[0], &args[1]) {
13165 (Value::String(s), Value::String(model)) => {
13166 let bpe_result = match model.as_str() {
13167 "gpt4" | "gpt-4" | "claude" | "cl100k" => cl100k_base(),
13168 "gpt3" | "gpt-3" | "p50k" => p50k_base(),
13169 "codex" | "r50k" => r50k_base(),
13170 _ => cl100k_base(), };
13172 if let Ok(bpe) = bpe_result {
13173 let tokens = bpe.encode_with_special_tokens(s);
13174 Ok(Value::Int(tokens.len() as i64))
13175 } else {
13176 Err(RuntimeError::new("Failed to initialize tokenizer"))
13177 }
13178 }
13179 _ => Err(RuntimeError::new("token_count_model() requires string and model name")),
13180 }
13181 });
13182
13183 define(interp, "tokenize_ids", Some(1), |_, args| {
13185 match &args[0] {
13186 Value::String(s) => {
13187 if let Ok(bpe) = cl100k_base() {
13188 let tokens = bpe.encode_with_special_tokens(s);
13189 let values: Vec<Value> = tokens.into_iter()
13190 .map(|t| Value::Int(t as i64))
13191 .collect();
13192 Ok(Value::Array(Rc::new(RefCell::new(values))))
13193 } else {
13194 Err(RuntimeError::new("Failed to initialize tokenizer"))
13195 }
13196 }
13197 _ => Err(RuntimeError::new("tokenize_ids() requires string")),
13198 }
13199 });
13200
13201 define(interp, "truncate_tokens", Some(2), |_, args| {
13203 match (&args[0], &args[1]) {
13204 (Value::String(s), Value::Int(max_tokens)) => {
13205 if let Ok(bpe) = cl100k_base() {
13206 let tokens = bpe.encode_with_special_tokens(s);
13207 if tokens.len() <= *max_tokens as usize {
13208 Ok(Value::String(s.clone()))
13209 } else {
13210 let truncated: Vec<usize> = tokens.into_iter()
13211 .take(*max_tokens as usize)
13212 .collect();
13213 if let Ok(decoded) = bpe.decode(truncated) {
13214 Ok(Value::String(Rc::new(decoded)))
13215 } else {
13216 Err(RuntimeError::new("Failed to decode tokens"))
13217 }
13218 }
13219 } else {
13220 Err(RuntimeError::new("Failed to initialize tokenizer"))
13221 }
13222 }
13223 _ => Err(RuntimeError::new("truncate_tokens() requires string and max tokens")),
13224 }
13225 });
13226
13227 define(interp, "estimate_cost", Some(3), |_, args| {
13229 match (&args[0], &args[1], &args[2]) {
13230 (Value::String(s), Value::Float(input_cost), Value::Float(output_cost)) => {
13231 if let Ok(bpe) = cl100k_base() {
13232 let tokens = bpe.encode_with_special_tokens(s);
13233 let count = tokens.len() as f64;
13234 let input_total = (count / 1000.0) * input_cost;
13236 let output_total = (count / 1000.0) * output_cost;
13237 let mut map = HashMap::new();
13238 map.insert("tokens".to_string(), Value::Int(tokens.len() as i64));
13239 map.insert("input_cost".to_string(), Value::Float(input_total));
13240 map.insert("output_cost".to_string(), Value::Float(output_total));
13241 Ok(Value::Map(Rc::new(RefCell::new(map))))
13242 } else {
13243 Err(RuntimeError::new("Failed to initialize tokenizer"))
13244 }
13245 }
13246 _ => Err(RuntimeError::new("estimate_cost() requires string, input cost, output cost")),
13247 }
13248 });
13249
13250 define(interp, "stem", Some(1), |_, args| {
13256 match &args[0] {
13257 Value::String(s) => {
13258 let stemmer = Stemmer::create(StemAlgorithm::English);
13259 let stemmed = stemmer.stem(s);
13260 Ok(Value::String(Rc::new(stemmed.to_string())))
13261 }
13262 _ => Err(RuntimeError::new("stem() requires string")),
13263 }
13264 });
13265
13266 define(interp, "stem_language", Some(2), |_, args| {
13268 match (&args[0], &args[1]) {
13269 (Value::String(s), Value::String(lang)) => {
13270 let algorithm = match lang.as_str() {
13271 "en" | "english" => StemAlgorithm::English,
13272 "fr" | "french" => StemAlgorithm::French,
13273 "de" | "german" => StemAlgorithm::German,
13274 "es" | "spanish" => StemAlgorithm::Spanish,
13275 "it" | "italian" => StemAlgorithm::Italian,
13276 "pt" | "portuguese" => StemAlgorithm::Portuguese,
13277 "nl" | "dutch" => StemAlgorithm::Dutch,
13278 "sv" | "swedish" => StemAlgorithm::Swedish,
13279 "no" | "norwegian" => StemAlgorithm::Norwegian,
13280 "da" | "danish" => StemAlgorithm::Danish,
13281 "fi" | "finnish" => StemAlgorithm::Finnish,
13282 "ru" | "russian" => StemAlgorithm::Russian,
13283 "ro" | "romanian" => StemAlgorithm::Romanian,
13284 "hu" | "hungarian" => StemAlgorithm::Hungarian,
13285 "tr" | "turkish" => StemAlgorithm::Turkish,
13286 "ar" | "arabic" => StemAlgorithm::Arabic,
13287 _ => StemAlgorithm::English,
13288 };
13289 let stemmer = Stemmer::create(algorithm);
13290 let stemmed = stemmer.stem(s);
13291 Ok(Value::String(Rc::new(stemmed.to_string())))
13292 }
13293 _ => Err(RuntimeError::new("stem_language() requires string and language code")),
13294 }
13295 });
13296
13297 define(interp, "stem_all", Some(1), |_, args| {
13299 match &args[0] {
13300 Value::Array(arr) => {
13301 let stemmer = Stemmer::create(StemAlgorithm::English);
13302 let arr_ref = arr.borrow();
13303 let results: Vec<Value> = arr_ref.iter()
13304 .filter_map(|v| {
13305 if let Value::String(s) = v {
13306 Some(Value::String(Rc::new(stemmer.stem(s).to_string())))
13307 } else {
13308 None
13309 }
13310 })
13311 .collect();
13312 Ok(Value::Array(Rc::new(RefCell::new(results))))
13313 }
13314 _ => Err(RuntimeError::new("stem_all() requires array of strings")),
13315 }
13316 });
13317
13318 define(interp, "is_stopword", Some(1), |_, args| {
13324 match &args[0] {
13325 Value::String(s) => {
13326 let word = s.to_lowercase();
13327 let stopwords = get_stopwords("en");
13328 Ok(Value::Bool(stopwords.contains(&word.as_str())))
13329 }
13330 _ => Err(RuntimeError::new("is_stopword() requires string")),
13331 }
13332 });
13333
13334 define(interp, "is_stopword_language", Some(2), |_, args| {
13336 match (&args[0], &args[1]) {
13337 (Value::String(s), Value::String(lang)) => {
13338 let word = s.to_lowercase();
13339 let stopwords = get_stopwords(lang);
13340 Ok(Value::Bool(stopwords.contains(&word.as_str())))
13341 }
13342 _ => Err(RuntimeError::new("is_stopword_language() requires string and language")),
13343 }
13344 });
13345
13346 define(interp, "remove_stopwords", Some(1), |_, args| {
13348 match &args[0] {
13349 Value::Array(arr) => {
13350 let stopwords = get_stopwords("en");
13351 let arr_ref = arr.borrow();
13352 let results: Vec<Value> = arr_ref.iter()
13353 .filter(|v| {
13354 if let Value::String(s) = v {
13355 !stopwords.contains(&s.to_lowercase().as_str())
13356 } else {
13357 true
13358 }
13359 })
13360 .cloned()
13361 .collect();
13362 Ok(Value::Array(Rc::new(RefCell::new(results))))
13363 }
13364 _ => Err(RuntimeError::new("remove_stopwords() requires array of strings")),
13365 }
13366 });
13367
13368 define(interp, "remove_stopwords_text", Some(1), |_, args| {
13370 match &args[0] {
13371 Value::String(s) => {
13372 let stopwords = get_stopwords("en");
13373 let words: Vec<&str> = s.split_whitespace()
13374 .filter(|w| !stopwords.contains(&w.to_lowercase().as_str()))
13375 .collect();
13376 Ok(Value::String(Rc::new(words.join(" "))))
13377 }
13378 _ => Err(RuntimeError::new("remove_stopwords_text() requires string")),
13379 }
13380 });
13381
13382 define(interp, "get_stopwords_list", Some(1), |_, args| {
13384 match &args[0] {
13385 Value::String(lang) => {
13386 let stopwords = get_stopwords(lang);
13387 let values: Vec<Value> = stopwords.iter()
13388 .map(|s| Value::String(Rc::new(s.to_string())))
13389 .collect();
13390 Ok(Value::Array(Rc::new(RefCell::new(values))))
13391 }
13392 _ => Err(RuntimeError::new("get_stopwords_list() requires language code")),
13393 }
13394 });
13395
13396 define(interp, "ngrams", Some(2), |_, args| {
13402 match (&args[0], &args[1]) {
13403 (Value::String(s), Value::Int(n)) => {
13404 let words: Vec<&str> = s.split_whitespace().collect();
13405 let n = *n as usize;
13406 if n == 0 || n > words.len() {
13407 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13408 }
13409 let ngrams: Vec<Value> = words.windows(n)
13410 .map(|w| Value::String(Rc::new(w.join(" "))))
13411 .collect();
13412 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
13413 }
13414 _ => Err(RuntimeError::new("ngrams() requires string and n")),
13415 }
13416 });
13417
13418 define(interp, "char_ngrams", Some(2), |_, args| {
13420 match (&args[0], &args[1]) {
13421 (Value::String(s), Value::Int(n)) => {
13422 let chars: Vec<char> = s.chars().collect();
13423 let n = *n as usize;
13424 if n == 0 || n > chars.len() {
13425 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13426 }
13427 let ngrams: Vec<Value> = chars.windows(n)
13428 .map(|w| Value::String(Rc::new(w.iter().collect())))
13429 .collect();
13430 Ok(Value::Array(Rc::new(RefCell::new(ngrams))))
13431 }
13432 _ => Err(RuntimeError::new("char_ngrams() requires string and n")),
13433 }
13434 });
13435
13436 define(interp, "shingles", Some(2), |_, args| {
13438 match (&args[0], &args[1]) {
13439 (Value::String(s), Value::Int(n)) => {
13440 let words: Vec<&str> = s.split_whitespace().collect();
13441 let n = *n as usize;
13442 if n == 0 || n > words.len() {
13443 return Ok(Value::Array(Rc::new(RefCell::new(vec![]))));
13444 }
13445 let mut seen = std::collections::HashSet::new();
13446 let shingles: Vec<Value> = words.windows(n)
13447 .filter_map(|w| {
13448 let s = w.join(" ");
13449 if seen.insert(s.clone()) {
13450 Some(Value::String(Rc::new(s)))
13451 } else {
13452 None
13453 }
13454 })
13455 .collect();
13456 Ok(Value::Array(Rc::new(RefCell::new(shingles))))
13457 }
13458 _ => Err(RuntimeError::new("shingles() requires string and n")),
13459 }
13460 });
13461
13462 define(interp, "jaccard_similarity", Some(2), |_, args| {
13464 match (&args[0], &args[1]) {
13465 (Value::Array(a), Value::Array(b)) => {
13466 let a_ref = a.borrow();
13467 let b_ref = b.borrow();
13468 let set_a: std::collections::HashSet<String> = a_ref.iter()
13469 .filter_map(|v| {
13470 if let Value::String(s) = v {
13471 Some(s.to_string())
13472 } else {
13473 None
13474 }
13475 })
13476 .collect();
13477 let set_b: std::collections::HashSet<String> = b_ref.iter()
13478 .filter_map(|v| {
13479 if let Value::String(s) = v {
13480 Some(s.to_string())
13481 } else {
13482 None
13483 }
13484 })
13485 .collect();
13486 let intersection = set_a.intersection(&set_b).count();
13487 let union = set_a.union(&set_b).count();
13488 if union == 0 {
13489 Ok(Value::Float(0.0))
13490 } else {
13491 Ok(Value::Float(intersection as f64 / union as f64))
13492 }
13493 }
13494 _ => Err(RuntimeError::new("jaccard_similarity() requires two arrays")),
13495 }
13496 });
13497
13498 define(interp, "minhash_signature", Some(2), |_, args| {
13500 match (&args[0], &args[1]) {
13501 (Value::Array(arr), Value::Int(num_hashes)) => {
13502 let arr_ref = arr.borrow();
13503 let items: std::collections::HashSet<String> = arr_ref.iter()
13504 .filter_map(|v| {
13505 if let Value::String(s) = v {
13506 Some(s.to_string())
13507 } else {
13508 None
13509 }
13510 })
13511 .collect();
13512
13513 let mut signature: Vec<Value> = Vec::with_capacity(*num_hashes as usize);
13515 for i in 0..*num_hashes {
13516 let mut min_hash: u64 = u64::MAX;
13517 for item in &items {
13518 let hash = compute_hash(item, i as u64);
13519 if hash < min_hash {
13520 min_hash = hash;
13521 }
13522 }
13523 signature.push(Value::Int(min_hash as i64));
13524 }
13525 Ok(Value::Array(Rc::new(RefCell::new(signature))))
13526 }
13527 _ => Err(RuntimeError::new("minhash_signature() requires array and num_hashes")),
13528 }
13529 });
13530
13531 define(interp, "preprocess_text", Some(1), |_, args| {
13537 match &args[0] {
13538 Value::String(s) => {
13539 let lower = s.to_lowercase();
13541 let clean: String = lower.chars()
13543 .filter(|c| c.is_alphanumeric() || c.is_whitespace())
13544 .collect();
13545 let normalized: String = clean.split_whitespace().collect::<Vec<_>>().join(" ");
13547 Ok(Value::String(Rc::new(normalized)))
13548 }
13549 _ => Err(RuntimeError::new("preprocess_text() requires string")),
13550 }
13551 });
13552
13553 define(interp, "tokenize_words", Some(1), |_, args| {
13555 match &args[0] {
13556 Value::String(s) => {
13557 let words: Vec<Value> = s.split_whitespace()
13558 .map(|w| Value::String(Rc::new(w.to_string())))
13559 .collect();
13560 Ok(Value::Array(Rc::new(RefCell::new(words))))
13561 }
13562 _ => Err(RuntimeError::new("tokenize_words() requires string")),
13563 }
13564 });
13565
13566 define(interp, "extract_keywords", Some(1), |_, args| {
13568 match &args[0] {
13569 Value::String(s) => {
13570 let stopwords = get_stopwords("en");
13571 let words: Vec<Value> = s.split_whitespace()
13572 .filter(|w| {
13573 let lower = w.to_lowercase();
13574 !stopwords.contains(&lower.as_str()) && lower.len() > 2
13575 })
13576 .map(|w| Value::String(Rc::new(w.to_lowercase())))
13577 .collect();
13578 Ok(Value::Array(Rc::new(RefCell::new(words))))
13579 }
13580 _ => Err(RuntimeError::new("extract_keywords() requires string")),
13581 }
13582 });
13583
13584 define(interp, "word_frequency", Some(1), |_, args| {
13586 match &args[0] {
13587 Value::String(s) => {
13588 let mut freq: HashMap<String, i64> = HashMap::new();
13589 for word in s.split_whitespace() {
13590 let lower = word.to_lowercase();
13591 *freq.entry(lower).or_insert(0) += 1;
13592 }
13593 let map: HashMap<String, Value> = freq.into_iter()
13594 .map(|(k, v)| (k, Value::Int(v)))
13595 .collect();
13596 Ok(Value::Map(Rc::new(RefCell::new(map))))
13597 }
13598 _ => Err(RuntimeError::new("word_frequency() requires string")),
13599 }
13600 });
13601
13602 define(interp, "sentiment_words", Some(1), |_, args| {
13608 match &args[0] {
13609 Value::String(s) => {
13610 let positive = vec![
13611 "good", "great", "excellent", "amazing", "wonderful", "fantastic",
13612 "love", "happy", "joy", "beautiful", "awesome", "perfect",
13613 "best", "brilliant", "delightful", "pleasant", "positive",
13614 ];
13615 let negative = vec![
13616 "bad", "terrible", "awful", "horrible", "hate", "sad",
13617 "angry", "worst", "poor", "negative", "disappointing",
13618 "ugly", "disgusting", "painful", "miserable", "annoying",
13619 ];
13620
13621 let lower = s.to_lowercase();
13622 let words: Vec<&str> = lower.split_whitespace().collect();
13623 let pos_count: i64 = words.iter()
13624 .filter(|w| positive.contains(w))
13625 .count() as i64;
13626 let neg_count: i64 = words.iter()
13627 .filter(|w| negative.contains(w))
13628 .count() as i64;
13629
13630 let mut map = HashMap::new();
13631 map.insert("positive".to_string(), Value::Int(pos_count));
13632 map.insert("negative".to_string(), Value::Int(neg_count));
13633 map.insert("total".to_string(), Value::Int(words.len() as i64));
13634
13635 let score = if pos_count + neg_count > 0 {
13636 (pos_count - neg_count) as f64 / (pos_count + neg_count) as f64
13637 } else {
13638 0.0
13639 };
13640 map.insert("score".to_string(), Value::Float(score));
13641
13642 Ok(Value::Map(Rc::new(RefCell::new(map))))
13643 }
13644 _ => Err(RuntimeError::new("sentiment_words() requires string")),
13645 }
13646 });
13647
13648 define(interp, "has_question", Some(1), |_, args| {
13650 match &args[0] {
13651 Value::String(s) => {
13652 let has_q_mark = s.contains('?');
13653 let lower = s.to_lowercase();
13654 let question_words = ["what", "where", "when", "why", "how", "who", "which", "whose", "whom"];
13655 let starts_with_q = question_words.iter().any(|w| lower.starts_with(w));
13656 Ok(Value::Bool(has_q_mark || starts_with_q))
13657 }
13658 _ => Err(RuntimeError::new("has_question() requires string")),
13659 }
13660 });
13661
13662 define(interp, "has_exclamation", Some(1), |_, args| {
13664 match &args[0] {
13665 Value::String(s) => {
13666 Ok(Value::Bool(s.contains('!')))
13667 }
13668 _ => Err(RuntimeError::new("has_exclamation() requires string")),
13669 }
13670 });
13671
13672 define(interp, "text_formality", Some(1), |_, args| {
13674 match &args[0] {
13675 Value::String(s) => {
13676 let lower = s.to_lowercase();
13677 let informal_markers = vec![
13678 "gonna", "wanna", "gotta", "kinda", "sorta", "dunno",
13679 "yeah", "yep", "nope", "ok", "lol", "omg", "btw",
13680 "u", "ur", "r", "y", "2", "4",
13681 ];
13682 let formal_markers = vec![
13683 "therefore", "furthermore", "moreover", "consequently",
13684 "nevertheless", "however", "whereas", "hereby",
13685 "respectfully", "sincerely", "accordingly",
13686 ];
13687
13688 let words: Vec<&str> = lower.split_whitespace().collect();
13689 let informal_count = words.iter()
13690 .filter(|w| informal_markers.contains(w))
13691 .count();
13692 let formal_count = words.iter()
13693 .filter(|w| formal_markers.contains(w))
13694 .count();
13695
13696 let score = if informal_count + formal_count > 0 {
13697 formal_count as f64 / (informal_count + formal_count) as f64
13698 } else {
13699 0.5 };
13701
13702 Ok(Value::Float(score))
13703 }
13704 _ => Err(RuntimeError::new("text_formality() requires string")),
13705 }
13706 });
13707
13708 define(interp, "sentiment_vader", Some(1), |_, args| {
13714 match &args[0] {
13715 Value::String(s) => {
13716 let result = compute_vader_sentiment(s);
13717 let mut map = HashMap::new();
13718 map.insert("positive".to_string(), Value::Float(result.0));
13719 map.insert("negative".to_string(), Value::Float(result.1));
13720 map.insert("neutral".to_string(), Value::Float(result.2));
13721 map.insert("compound".to_string(), Value::Float(result.3));
13722 Ok(Value::Map(Rc::new(RefCell::new(map))))
13723 }
13724 _ => Err(RuntimeError::new("sentiment_vader() requires string")),
13725 }
13726 });
13727
13728 define(interp, "emotion_detect", Some(1), |_, args| {
13730 match &args[0] {
13731 Value::String(s) => {
13732 let emotions = compute_emotions(s);
13733 let map: HashMap<String, Value> = emotions.into_iter()
13734 .map(|(k, v)| (k, Value::Float(v)))
13735 .collect();
13736 Ok(Value::Map(Rc::new(RefCell::new(map))))
13737 }
13738 _ => Err(RuntimeError::new("emotion_detect() requires string")),
13739 }
13740 });
13741
13742 define(interp, "intensity_score", Some(1), |_, args| {
13744 match &args[0] {
13745 Value::String(s) => {
13746 let score = compute_intensity(s);
13747 Ok(Value::Float(score))
13748 }
13749 _ => Err(RuntimeError::new("intensity_score() requires string")),
13750 }
13751 });
13752
13753 define(interp, "detect_sarcasm", Some(1), |_, args| {
13759 match &args[0] {
13760 Value::String(s) => {
13761 let result = compute_sarcasm_score(s);
13762 let mut map = HashMap::new();
13763 map.insert("score".to_string(), Value::Float(result.0));
13764 map.insert("confidence".to_string(), Value::Float(result.1));
13765 let markers: Vec<Value> = result.2.into_iter()
13766 .map(|m| Value::String(Rc::new(m)))
13767 .collect();
13768 map.insert("markers".to_string(), Value::Array(Rc::new(RefCell::new(markers))));
13769 Ok(Value::Map(Rc::new(RefCell::new(map))))
13770 }
13771 _ => Err(RuntimeError::new("detect_sarcasm() requires string")),
13772 }
13773 });
13774
13775 define(interp, "is_sarcastic", Some(1), |_, args| {
13777 match &args[0] {
13778 Value::String(s) => {
13779 let result = compute_sarcasm_score(s);
13780 Ok(Value::Bool(result.0 > 0.5))
13781 }
13782 _ => Err(RuntimeError::new("is_sarcastic() requires string")),
13783 }
13784 });
13785
13786 define(interp, "detect_irony", Some(1), |_, args| {
13788 match &args[0] {
13789 Value::String(s) => {
13790 let score = compute_irony_score(s);
13791 Ok(Value::Float(score))
13792 }
13793 _ => Err(RuntimeError::new("detect_irony() requires string")),
13794 }
13795 });
13796
13797 define(interp, "extract_emails", Some(1), |_, args| {
13803 match &args[0] {
13804 Value::String(s) => {
13805 let re = Regex::new(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}").unwrap();
13806 let emails: Vec<Value> = re.find_iter(s)
13807 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
13808 .collect();
13809 Ok(Value::Array(Rc::new(RefCell::new(emails))))
13810 }
13811 _ => Err(RuntimeError::new("extract_emails() requires string")),
13812 }
13813 });
13814
13815 define(interp, "extract_urls", Some(1), |_, args| {
13817 match &args[0] {
13818 Value::String(s) => {
13819 let re = Regex::new(r"https?://[^\s<>\[\]{}|\\^]+").unwrap();
13820 let urls: Vec<Value> = re.find_iter(s)
13821 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
13822 .collect();
13823 Ok(Value::Array(Rc::new(RefCell::new(urls))))
13824 }
13825 _ => Err(RuntimeError::new("extract_urls() requires string")),
13826 }
13827 });
13828
13829 define(interp, "extract_phone_numbers", Some(1), |_, args| {
13831 match &args[0] {
13832 Value::String(s) => {
13833 let re = Regex::new(r"(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}").unwrap();
13834 let phones: Vec<Value> = re.find_iter(s)
13835 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
13836 .collect();
13837 Ok(Value::Array(Rc::new(RefCell::new(phones))))
13838 }
13839 _ => Err(RuntimeError::new("extract_phone_numbers() requires string")),
13840 }
13841 });
13842
13843 define(interp, "extract_dates", Some(1), |_, args| {
13845 match &args[0] {
13846 Value::String(s) => {
13847 let patterns = vec![
13849 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}",
13853 r"\d{1,2}\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{4}",
13854 ];
13855 let mut dates = Vec::new();
13856 for pattern in patterns {
13857 if let Ok(re) = Regex::new(pattern) {
13858 for m in re.find_iter(s) {
13859 dates.push(Value::String(Rc::new(m.as_str().to_string())));
13860 }
13861 }
13862 }
13863 Ok(Value::Array(Rc::new(RefCell::new(dates))))
13864 }
13865 _ => Err(RuntimeError::new("extract_dates() requires string")),
13866 }
13867 });
13868
13869 define(interp, "extract_money", Some(1), |_, args| {
13871 match &args[0] {
13872 Value::String(s) => {
13873 let re = Regex::new(r"[$€£¥]\s*\d+(?:,\d{3})*(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:dollars?|euros?|pounds?|USD|EUR|GBP)").unwrap();
13874 let money: Vec<Value> = re.find_iter(s)
13875 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
13876 .collect();
13877 Ok(Value::Array(Rc::new(RefCell::new(money))))
13878 }
13879 _ => Err(RuntimeError::new("extract_money() requires string")),
13880 }
13881 });
13882
13883 define(interp, "extract_hashtags", Some(1), |_, args| {
13885 match &args[0] {
13886 Value::String(s) => {
13887 let re = Regex::new(r"#\w+").unwrap();
13888 let tags: Vec<Value> = re.find_iter(s)
13889 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
13890 .collect();
13891 Ok(Value::Array(Rc::new(RefCell::new(tags))))
13892 }
13893 _ => Err(RuntimeError::new("extract_hashtags() requires string")),
13894 }
13895 });
13896
13897 define(interp, "extract_mentions", Some(1), |_, args| {
13899 match &args[0] {
13900 Value::String(s) => {
13901 let re = Regex::new(r"@\w+").unwrap();
13902 let mentions: Vec<Value> = re.find_iter(s)
13903 .map(|m| Value::String(Rc::new(m.as_str().to_string())))
13904 .collect();
13905 Ok(Value::Array(Rc::new(RefCell::new(mentions))))
13906 }
13907 _ => Err(RuntimeError::new("extract_mentions() requires string")),
13908 }
13909 });
13910
13911 define(interp, "extract_numbers", Some(1), |_, args| {
13913 match &args[0] {
13914 Value::String(s) => {
13915 let re = Regex::new(r"-?\d+(?:,\d{3})*(?:\.\d+)?").unwrap();
13916 let numbers: Vec<Value> = re.find_iter(s)
13917 .filter_map(|m| {
13918 let num_str = m.as_str().replace(",", "");
13919 if let Ok(n) = num_str.parse::<f64>() {
13920 Some(Value::Float(n))
13921 } else {
13922 None
13923 }
13924 })
13925 .collect();
13926 Ok(Value::Array(Rc::new(RefCell::new(numbers))))
13927 }
13928 _ => Err(RuntimeError::new("extract_numbers() requires string")),
13929 }
13930 });
13931
13932 define(interp, "extract_entities", Some(1), |_, args| {
13934 match &args[0] {
13935 Value::String(s) => {
13936 let re = Regex::new(r"(?:[.!?]\s+)?([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)").unwrap();
13938 let mut entities = std::collections::HashSet::new();
13939 for cap in re.captures_iter(s) {
13940 if let Some(m) = cap.get(1) {
13941 let entity = m.as_str().to_string();
13942 let starters = ["The", "A", "An", "This", "That", "It", "I", "We", "They", "He", "She"];
13944 if !starters.contains(&entity.as_str()) {
13945 entities.insert(entity);
13946 }
13947 }
13948 }
13949 let results: Vec<Value> = entities.into_iter()
13950 .map(|e| Value::String(Rc::new(e)))
13951 .collect();
13952 Ok(Value::Array(Rc::new(RefCell::new(results))))
13953 }
13954 _ => Err(RuntimeError::new("extract_entities() requires string")),
13955 }
13956 });
13957
13958 define(interp, "text_hash_vector", Some(2), |_, args| {
13964 match (&args[0], &args[1]) {
13965 (Value::String(s), Value::Int(dims)) => {
13966 let dims = *dims as usize;
13967 let mut vector = vec![0.0f64; dims];
13968
13969 for word in s.to_lowercase().split_whitespace() {
13971 let hash = compute_hash(word, 0);
13972 let idx = (hash as usize) % dims;
13973 vector[idx] += 1.0;
13974 }
13975
13976 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
13978 if magnitude > 0.0 {
13979 for v in vector.iter_mut() {
13980 *v /= magnitude;
13981 }
13982 }
13983
13984 let values: Vec<Value> = vector.into_iter().map(Value::Float).collect();
13985 Ok(Value::Array(Rc::new(RefCell::new(values))))
13986 }
13987 _ => Err(RuntimeError::new("text_hash_vector() requires string and dimensions")),
13988 }
13989 });
13990
13991 define(interp, "text_fingerprint", Some(1), |_, args| {
13993 match &args[0] {
13994 Value::String(s) => {
13995 let lower = s.to_lowercase();
13997 let words: Vec<&str> = lower.split_whitespace().collect();
13998
13999 let mut fp: u64 = 0;
14000 for (i, word) in words.iter().enumerate() {
14001 let h = compute_hash(word, i as u64);
14002 fp ^= h.rotate_left((i % 64) as u32);
14003 }
14004
14005 Ok(Value::String(Rc::new(format!("{:016x}", fp))))
14006 }
14007 _ => Err(RuntimeError::new("text_fingerprint() requires string")),
14008 }
14009 });
14010
14011 define(interp, "cosine_similarity", Some(2), |_, args| {
14013 match (&args[0], &args[1]) {
14014 (Value::Array(a), Value::Array(b)) => {
14015 let a_ref = a.borrow();
14016 let b_ref = b.borrow();
14017
14018 if a_ref.len() != b_ref.len() {
14019 return Err(RuntimeError::new("Vectors must have same length"));
14020 }
14021
14022 let mut dot = 0.0;
14023 let mut mag_a = 0.0;
14024 let mut mag_b = 0.0;
14025
14026 for (va, vb) in a_ref.iter().zip(b_ref.iter()) {
14027 let fa = match va {
14028 Value::Float(f) => *f,
14029 Value::Int(i) => *i as f64,
14030 _ => continue,
14031 };
14032 let fb = match vb {
14033 Value::Float(f) => *f,
14034 Value::Int(i) => *i as f64,
14035 _ => continue,
14036 };
14037 dot += fa * fb;
14038 mag_a += fa * fa;
14039 mag_b += fb * fb;
14040 }
14041
14042 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
14043 if denom == 0.0 {
14044 Ok(Value::Float(0.0))
14045 } else {
14046 Ok(Value::Float(dot / denom))
14047 }
14048 }
14049 _ => Err(RuntimeError::new("cosine_similarity() requires two arrays")),
14050 }
14051 });
14052
14053 define(interp, "text_similarity_embedding", Some(2), |_, args| {
14055 match (&args[0], &args[1]) {
14056 (Value::String(a), Value::String(b)) => {
14057 let dims = 128;
14058
14059 let vec_a = create_hash_vector(a, dims);
14061 let vec_b = create_hash_vector(b, dims);
14062
14063 let mut dot = 0.0;
14065 let mut mag_a = 0.0;
14066 let mut mag_b = 0.0;
14067
14068 for i in 0..dims {
14069 dot += vec_a[i] * vec_b[i];
14070 mag_a += vec_a[i] * vec_a[i];
14071 mag_b += vec_b[i] * vec_b[i];
14072 }
14073
14074 let denom = (mag_a.sqrt()) * (mag_b.sqrt());
14075 if denom == 0.0 {
14076 Ok(Value::Float(0.0))
14077 } else {
14078 Ok(Value::Float(dot / denom))
14079 }
14080 }
14081 _ => Err(RuntimeError::new("text_similarity_embedding() requires two strings")),
14082 }
14083 });
14084
14085 define(interp, "flesch_reading_ease", Some(1), |_, args| {
14091 match &args[0] {
14092 Value::String(s) => {
14093 let (words, sentences, syllables) = count_text_stats(s);
14094 if words == 0 || sentences == 0 {
14095 return Ok(Value::Float(0.0));
14096 }
14097 let score = 206.835
14098 - 1.015 * (words as f64 / sentences as f64)
14099 - 84.6 * (syllables as f64 / words as f64);
14100 Ok(Value::Float(score.max(0.0).min(100.0)))
14101 }
14102 _ => Err(RuntimeError::new("flesch_reading_ease() requires string")),
14103 }
14104 });
14105
14106 define(interp, "flesch_kincaid_grade", Some(1), |_, args| {
14108 match &args[0] {
14109 Value::String(s) => {
14110 let (words, sentences, syllables) = count_text_stats(s);
14111 if words == 0 || sentences == 0 {
14112 return Ok(Value::Float(0.0));
14113 }
14114 let grade = 0.39 * (words as f64 / sentences as f64)
14115 + 11.8 * (syllables as f64 / words as f64)
14116 - 15.59;
14117 Ok(Value::Float(grade.max(0.0)))
14118 }
14119 _ => Err(RuntimeError::new("flesch_kincaid_grade() requires string")),
14120 }
14121 });
14122
14123 define(interp, "automated_readability_index", Some(1), |_, args| {
14125 match &args[0] {
14126 Value::String(s) => {
14127 let chars: usize = s.chars().filter(|c| c.is_alphanumeric()).count();
14128 let words: usize = s.split_whitespace().count();
14129 let sentences: usize = s.matches(|c| c == '.' || c == '!' || c == '?').count().max(1);
14130
14131 if words == 0 {
14132 return Ok(Value::Float(0.0));
14133 }
14134
14135 let ari = 4.71 * (chars as f64 / words as f64)
14136 + 0.5 * (words as f64 / sentences as f64)
14137 - 21.43;
14138 Ok(Value::Float(ari.max(0.0)))
14139 }
14140 _ => Err(RuntimeError::new("automated_readability_index() requires string")),
14141 }
14142 });
14143
14144 define(interp, "reading_time", Some(1), |_, args| {
14146 match &args[0] {
14147 Value::String(s) => {
14148 let words = s.split_whitespace().count();
14149 let minutes = words as f64 / 200.0; Ok(Value::Float(minutes))
14151 }
14152 _ => Err(RuntimeError::new("reading_time() requires string")),
14153 }
14154 });
14155
14156 define(interp, "speaking_time", Some(1), |_, args| {
14158 match &args[0] {
14159 Value::String(s) => {
14160 let words = s.split_whitespace().count();
14161 let minutes = words as f64 / 150.0; Ok(Value::Float(minutes))
14163 }
14164 _ => Err(RuntimeError::new("speaking_time() requires string")),
14165 }
14166 });
14167}
14168
14169fn compute_vader_sentiment(s: &str) -> (f64, f64, f64, f64) {
14175 let positive_words: Vec<(&str, f64)> = vec![
14177 ("love", 3.0), ("loved", 3.0), ("loving", 3.0),
14178 ("excellent", 3.0), ("amazing", 3.0), ("fantastic", 3.0), ("wonderful", 3.0),
14179 ("great", 2.5), ("awesome", 2.5), ("brilliant", 2.5), ("superb", 2.5),
14180 ("good", 2.0), ("nice", 2.0), ("pleasant", 2.0), ("happy", 2.0),
14181 ("like", 1.5), ("enjoy", 1.5), ("fine", 1.5), ("okay", 1.0),
14182 ("best", 3.0), ("perfect", 3.0), ("beautiful", 2.5), ("delightful", 2.5),
14183 ("excited", 2.5), ("thrilled", 3.0), ("glad", 2.0), ("pleased", 2.0),
14184 ];
14185
14186 let negative_words: Vec<(&str, f64)> = vec![
14187 ("hate", 3.0), ("hated", 3.0), ("hating", 3.0),
14188 ("terrible", 3.0), ("horrible", 3.0), ("awful", 3.0), ("disgusting", 3.0),
14189 ("bad", 2.5), ("poor", 2.5), ("worst", 3.0), ("pathetic", 2.5),
14190 ("sad", 2.0), ("angry", 2.5), ("upset", 2.0), ("disappointed", 2.0),
14191 ("dislike", 1.5), ("annoying", 2.0), ("boring", 1.5), ("mediocre", 1.0),
14192 ("ugly", 2.5), ("stupid", 2.5), ("dumb", 2.0), ("useless", 2.5),
14193 ("painful", 2.5), ("miserable", 3.0), ("depressing", 2.5), ("frustrating", 2.0),
14194 ];
14195
14196 let boosters = vec!["very", "really", "extremely", "absolutely", "incredibly", "totally", "so"];
14198 let dampeners = vec!["somewhat", "slightly", "a bit", "kind of", "sort of", "barely"];
14199
14200 let lower = s.to_lowercase();
14201 let words: Vec<&str> = lower.split_whitespace().collect();
14202
14203 let mut pos_score = 0.0;
14204 let mut neg_score = 0.0;
14205 let mut word_count = 0;
14206
14207 for (i, word) in words.iter().enumerate() {
14208 let mut modifier = 1.0;
14209
14210 if i > 0 {
14212 if boosters.contains(&words[i-1]) {
14213 modifier = 1.5;
14214 } else if dampeners.iter().any(|d| words[i-1].contains(d)) {
14215 modifier = 0.5;
14216 }
14217 }
14218
14219 let negated = i > 0 && ["not", "no", "never", "neither", "don't", "doesn't", "didn't", "won't", "wouldn't", "couldn't", "shouldn't"]
14221 .contains(&words[i-1]);
14222
14223 if let Some((_, score)) = positive_words.iter().find(|(w, _)| w == word) {
14224 if negated {
14225 neg_score += score * modifier;
14226 } else {
14227 pos_score += score * modifier;
14228 }
14229 word_count += 1;
14230 } else if let Some((_, score)) = negative_words.iter().find(|(w, _)| w == word) {
14231 if negated {
14232 pos_score += score * modifier * 0.5; } else {
14234 neg_score += score * modifier;
14235 }
14236 word_count += 1;
14237 }
14238 }
14239
14240 let total = pos_score + neg_score;
14242 let (pos_norm, neg_norm) = if total > 0.0 {
14243 (pos_score / total, neg_score / total)
14244 } else {
14245 (0.0, 0.0)
14246 };
14247
14248 let neutral = 1.0 - pos_norm - neg_norm;
14249
14250 let compound = if word_count > 0 {
14252 ((pos_score - neg_score) / (word_count as f64 * 3.0)).max(-1.0).min(1.0)
14253 } else {
14254 0.0
14255 };
14256
14257 (pos_norm, neg_norm, neutral.max(0.0), compound)
14258}
14259
14260fn compute_emotions(s: &str) -> HashMap<String, f64> {
14262 let emotion_words: Vec<(&str, &str)> = vec![
14263 ("happy", "joy"), ("joyful", "joy"), ("delighted", "joy"), ("cheerful", "joy"),
14265 ("excited", "joy"), ("thrilled", "joy"), ("ecstatic", "joy"), ("elated", "joy"),
14266 ("sad", "sadness"), ("unhappy", "sadness"), ("depressed", "sadness"), ("miserable", "sadness"),
14268 ("gloomy", "sadness"), ("heartbroken", "sadness"), ("sorrowful", "sadness"), ("melancholy", "sadness"),
14269 ("angry", "anger"), ("furious", "anger"), ("enraged", "anger"), ("irritated", "anger"),
14271 ("annoyed", "anger"), ("outraged", "anger"), ("livid", "anger"), ("mad", "anger"),
14272 ("afraid", "fear"), ("scared", "fear"), ("terrified", "fear"), ("frightened", "fear"),
14274 ("anxious", "fear"), ("worried", "fear"), ("nervous", "fear"), ("panicked", "fear"),
14275 ("surprised", "surprise"), ("amazed", "surprise"), ("astonished", "surprise"), ("shocked", "surprise"),
14277 ("stunned", "surprise"), ("startled", "surprise"), ("bewildered", "surprise"),
14278 ("disgusted", "disgust"), ("revolted", "disgust"), ("repulsed", "disgust"), ("sickened", "disgust"),
14280 ("nauseated", "disgust"), ("appalled", "disgust"),
14281 ("trust", "trust"), ("confident", "trust"), ("secure", "trust"), ("reliable", "trust"),
14283 ("faithful", "trust"), ("loyal", "trust"),
14284 ("eager", "anticipation"), ("hopeful", "anticipation"), ("expectant", "anticipation"),
14286 ("looking forward", "anticipation"), ("excited", "anticipation"),
14287 ];
14288
14289 let lower = s.to_lowercase();
14290 let mut counts: HashMap<String, f64> = HashMap::new();
14291
14292 for (word, emotion) in emotion_words {
14293 if lower.contains(word) {
14294 *counts.entry(emotion.to_string()).or_insert(0.0) += 1.0;
14295 }
14296 }
14297
14298 let total: f64 = counts.values().sum();
14300 if total > 0.0 {
14301 for v in counts.values_mut() {
14302 *v /= total;
14303 }
14304 }
14305
14306 counts
14307}
14308
14309fn compute_intensity(s: &str) -> f64 {
14311 let intensifiers = vec![
14312 ("very", 1.5), ("really", 1.5), ("extremely", 2.0), ("incredibly", 2.0),
14313 ("absolutely", 2.0), ("totally", 1.5), ("completely", 1.5), ("utterly", 2.0),
14314 ("so", 1.3), ("such", 1.3), ("quite", 1.2), ("rather", 1.1),
14315 ];
14316
14317 let exclamation_boost = 0.5;
14318 let caps_boost = 0.3;
14319
14320 let lower = s.to_lowercase();
14321 let mut score = 1.0;
14322
14323 for (word, boost) in intensifiers {
14324 if lower.contains(word) {
14325 score *= boost;
14326 }
14327 }
14328
14329 let exclamations = s.matches('!').count();
14331 score += exclamations as f64 * exclamation_boost;
14332
14333 let caps_words = s.split_whitespace()
14335 .filter(|w| w.len() > 2 && w.chars().all(|c| c.is_uppercase()))
14336 .count();
14337 score += caps_words as f64 * caps_boost;
14338
14339 score.min(5.0)
14340}
14341
14342fn compute_sarcasm_score(s: &str) -> (f64, f64, Vec<String>) {
14344 let mut markers = Vec::new();
14345 let mut score: f64 = 0.0;
14346
14347 let lower = s.to_lowercase();
14348
14349 let explicit = vec![
14351 "/s", "not!", "yeah right", "sure thing", "oh really", "oh great",
14352 "wow, just wow", "thanks a lot", "how wonderful", "isn't that special",
14353 "clearly", "obviously", "shocking", "no way", "what a surprise",
14354 ];
14355
14356 for marker in &explicit {
14357 if lower.contains(marker) {
14358 markers.push(format!("explicit: {}", marker));
14359 score += 0.4;
14360 }
14361 }
14362
14363 let hyperbolic = vec![
14365 "best thing ever", "worst thing ever", "literally dying", "absolutely perfect",
14366 "world's greatest", "totally awesome", "so much fun", "couldn't be happier",
14367 ];
14368
14369 for h in &hyperbolic {
14370 if lower.contains(h) {
14371 markers.push(format!("hyperbole: {}", h));
14372 score += 0.3;
14373 }
14374 }
14375
14376 let has_positive = ["great", "wonderful", "amazing", "love", "best", "awesome"]
14378 .iter().any(|w| lower.contains(w));
14379 let has_negative_context = ["but", "however", "although", "except", "unfortunately"]
14380 .iter().any(|w| lower.contains(w));
14381
14382 if has_positive && has_negative_context {
14383 markers.push("positive-negative contrast".to_string());
14384 score += 0.25;
14385 }
14386
14387 let quote_pattern = Regex::new(r#"["'](\w+)["']"#).unwrap();
14389 for cap in quote_pattern.captures_iter(s) {
14390 if let Some(m) = cap.get(1) {
14391 let word = m.as_str().to_lowercase();
14392 if ["great", "wonderful", "helpful", "useful", "smart", "genius", "brilliant"].contains(&word.as_str()) {
14393 markers.push(format!("air quotes: \"{}\"", word));
14394 score += 0.35;
14395 }
14396 }
14397 }
14398
14399 if s.contains("...") || s.contains("!!!") || s.contains("???") {
14401 markers.push("excessive punctuation".to_string());
14402 score += 0.15;
14403 }
14404
14405 let confidence = if markers.is_empty() {
14407 0.0
14408 } else {
14409 (markers.len() as f64 * 0.25).min(1.0)
14410 };
14411
14412 (score.min(1.0), confidence, markers)
14413}
14414
14415fn compute_irony_score(s: &str) -> f64 {
14417 let mut score: f64 = 0.0;
14418 let lower = s.to_lowercase();
14419
14420 let irony_phrases = vec![
14422 "of course", "as expected", "naturally", "predictably",
14423 "who would have thought", "surprise surprise", "go figure",
14424 "typical", "as usual", "yet again", "once again",
14425 ];
14426
14427 for phrase in irony_phrases {
14428 if lower.contains(phrase) {
14429 score += 0.2;
14430 }
14431 }
14432
14433 if lower.contains("but") || lower.contains("yet") || lower.contains("however") {
14435 score += 0.1;
14436 }
14437
14438 if s.contains('?') && (lower.starts_with("isn't") || lower.starts_with("aren't") ||
14440 lower.starts_with("doesn't") || lower.starts_with("don't") ||
14441 lower.contains("right?") || lower.contains("isn't it")) {
14442 score += 0.25;
14443 }
14444
14445 score.min(1.0)
14446}
14447
14448fn create_hash_vector(s: &str, dims: usize) -> Vec<f64> {
14450 let mut vector = vec![0.0f64; dims];
14451
14452 for word in s.to_lowercase().split_whitespace() {
14453 let hash = compute_hash(word, 0);
14454 let idx = (hash as usize) % dims;
14455 vector[idx] += 1.0;
14456 }
14457
14458 let magnitude: f64 = vector.iter().map(|x| x * x).sum::<f64>().sqrt();
14460 if magnitude > 0.0 {
14461 for v in vector.iter_mut() {
14462 *v /= magnitude;
14463 }
14464 }
14465
14466 vector
14467}
14468
14469fn count_text_stats(s: &str) -> (usize, usize, usize) {
14471 let words: Vec<&str> = s.split_whitespace().collect();
14472 let word_count = words.len();
14473 let sentence_count = s.matches(|c| c == '.' || c == '!' || c == '?').count().max(1);
14474
14475 let mut syllable_count = 0;
14476 for word in &words {
14477 syllable_count += count_syllables(word);
14478 }
14479
14480 (word_count, sentence_count, syllable_count)
14481}
14482
14483fn count_syllables(word: &str) -> usize {
14485 let word = word.to_lowercase();
14486 let vowels = ['a', 'e', 'i', 'o', 'u', 'y'];
14487 let mut count = 0;
14488 let mut prev_was_vowel = false;
14489
14490 for c in word.chars() {
14491 let is_vowel = vowels.contains(&c);
14492 if is_vowel && !prev_was_vowel {
14493 count += 1;
14494 }
14495 prev_was_vowel = is_vowel;
14496 }
14497
14498 if word.ends_with('e') && count > 1 {
14500 count -= 1;
14501 }
14502
14503 count.max(1)
14504}
14505
14506fn compute_soundex(s: &str) -> String {
14508 if s.is_empty() {
14509 return "0000".to_string();
14510 }
14511
14512 let s = s.to_uppercase();
14513 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
14514
14515 if chars.is_empty() {
14516 return "0000".to_string();
14517 }
14518
14519 let first = chars[0];
14520 let mut code = String::new();
14521 code.push(first);
14522
14523 let get_code = |c: char| -> char {
14524 match c {
14525 'B' | 'F' | 'P' | 'V' => '1',
14526 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => '2',
14527 'D' | 'T' => '3',
14528 'L' => '4',
14529 'M' | 'N' => '5',
14530 'R' => '6',
14531 _ => '0',
14532 }
14533 };
14534
14535 let mut prev_code = get_code(first);
14536
14537 for &c in chars.iter().skip(1) {
14538 let curr_code = get_code(c);
14539 if curr_code != '0' && curr_code != prev_code {
14540 code.push(curr_code);
14541 if code.len() == 4 {
14542 break;
14543 }
14544 }
14545 prev_code = curr_code;
14546 }
14547
14548 while code.len() < 4 {
14549 code.push('0');
14550 }
14551
14552 code
14553}
14554
14555fn compute_metaphone(s: &str) -> String {
14557 let s = s.to_uppercase();
14558 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
14559
14560 if chars.is_empty() {
14561 return String::new();
14562 }
14563
14564 let mut result = String::new();
14565 let mut i = 0;
14566
14567 if chars.len() >= 2 {
14569 let prefix: String = chars[0..2].iter().collect();
14570 if ["KN", "GN", "PN", "AE", "WR"].contains(&prefix.as_str()) {
14571 i = 1;
14572 }
14573 }
14574
14575 while i < chars.len() && result.len() < 6 {
14576 let c = chars[i];
14577 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
14578 let next = chars.get(i + 1).copied();
14579
14580 let code = match c {
14581 'A' | 'E' | 'I' | 'O' | 'U' => {
14582 if i == 0 { Some(c) } else { None }
14583 }
14584 'B' => {
14585 if prev != Some('M') || i == chars.len() - 1 {
14586 Some('B')
14587 } else {
14588 None
14589 }
14590 }
14591 'C' => {
14592 if next == Some('H') {
14593 Some('X')
14594 } else if matches!(next, Some('I') | Some('E') | Some('Y')) {
14595 Some('S')
14596 } else {
14597 Some('K')
14598 }
14599 }
14600 'D' => {
14601 if next == Some('G') && matches!(chars.get(i + 2), Some('E') | Some('I') | Some('Y')) {
14602 Some('J')
14603 } else {
14604 Some('T')
14605 }
14606 }
14607 'F' => Some('F'),
14608 'G' => {
14609 if next == Some('H') && !matches!(chars.get(i + 2), Some('A') | Some('E') | Some('I') | Some('O') | Some('U')) {
14610 None
14611 } else if matches!(next, Some('N') | Some('E') | Some('I') | Some('Y')) {
14612 Some('J')
14613 } else {
14614 Some('K')
14615 }
14616 }
14617 'H' => {
14618 if matches!(prev, Some('A') | Some('E') | Some('I') | Some('O') | Some('U')) {
14619 None
14620 } else if matches!(next, Some('A') | Some('E') | Some('I') | Some('O') | Some('U')) {
14621 Some('H')
14622 } else {
14623 None
14624 }
14625 }
14626 'J' => Some('J'),
14627 'K' => if prev != Some('C') { Some('K') } else { None },
14628 'L' => Some('L'),
14629 'M' => Some('M'),
14630 'N' => Some('N'),
14631 'P' => if next == Some('H') { Some('F') } else { Some('P') },
14632 'Q' => Some('K'),
14633 'R' => Some('R'),
14634 'S' => if next == Some('H') { Some('X') } else { Some('S') },
14635 'T' => {
14636 if next == Some('H') {
14637 Some('0') } else if next == Some('I') && matches!(chars.get(i + 2), Some('O') | Some('A')) {
14639 Some('X')
14640 } else {
14641 Some('T')
14642 }
14643 }
14644 'V' => Some('F'),
14645 'W' | 'Y' => {
14646 if matches!(next, Some('A') | Some('E') | Some('I') | Some('O') | Some('U')) {
14647 Some(c)
14648 } else {
14649 None
14650 }
14651 }
14652 'X' => {
14653 result.push('K');
14654 Some('S')
14655 }
14656 'Z' => Some('S'),
14657 _ => None,
14658 };
14659
14660 if let Some(ch) = code {
14661 result.push(ch);
14662 }
14663
14664 if next == Some(c) {
14666 i += 1;
14667 }
14668 i += 1;
14669 }
14670
14671 result
14672}
14673
14674fn compute_cologne(s: &str) -> String {
14676 let s = s.to_uppercase();
14677 let chars: Vec<char> = s.chars().filter(|c| c.is_ascii_alphabetic()).collect();
14678
14679 if chars.is_empty() {
14680 return String::new();
14681 }
14682
14683 let mut result = String::new();
14684
14685 for (i, &c) in chars.iter().enumerate() {
14686 let prev = if i > 0 { Some(chars[i - 1]) } else { None };
14687 let next = chars.get(i + 1).copied();
14688
14689 let code = match c {
14690 'A' | 'E' | 'I' | 'O' | 'U' | 'J' | 'Y' => '0',
14691 'H' => continue,
14692 'B' | 'P' => '1',
14693 'D' | 'T' => {
14694 if matches!(next, Some('C') | Some('S') | Some('Z')) {
14695 '8'
14696 } else {
14697 '2'
14698 }
14699 }
14700 'F' | 'V' | 'W' => '3',
14701 'G' | 'K' | 'Q' => '4',
14702 'C' => {
14703 if i == 0 {
14704 if matches!(next, Some('A') | Some('H') | Some('K') | Some('L') | Some('O') | Some('Q') | Some('R') | Some('U') | Some('X')) {
14705 '4'
14706 } else {
14707 '8'
14708 }
14709 } else if matches!(prev, Some('S') | Some('Z')) {
14710 '8'
14711 } else if matches!(next, Some('A') | Some('H') | Some('K') | Some('O') | Some('Q') | Some('U') | Some('X')) {
14712 '4'
14713 } else {
14714 '8'
14715 }
14716 }
14717 'X' => {
14718 if matches!(prev, Some('C') | Some('K') | Some('Q')) {
14719 '8'
14720 } else {
14721 result.push('4');
14722 '8'
14723 }
14724 }
14725 'L' => '5',
14726 'M' | 'N' => '6',
14727 'R' => '7',
14728 'S' | 'Z' => '8',
14729 _ => continue,
14730 };
14731
14732 result.push(code);
14733 }
14734
14735 let mut deduped = String::new();
14737 let mut prev = None;
14738 for c in result.chars() {
14739 if prev != Some(c) {
14740 deduped.push(c);
14741 }
14742 prev = Some(c);
14743 }
14744
14745 let trimmed: String = deduped.trim_start_matches('0').to_string();
14747 if trimmed.is_empty() {
14748 "0".to_string()
14749 } else {
14750 trimmed
14751 }
14752}
14753
14754fn get_stopwords(lang: &str) -> Vec<&'static str> {
14756 match lang {
14757 "en" | "english" => vec![
14758 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
14759 "of", "with", "by", "from", "as", "is", "was", "are", "were", "been",
14760 "be", "have", "has", "had", "do", "does", "did", "will", "would",
14761 "could", "should", "may", "might", "must", "shall", "can", "need",
14762 "it", "its", "this", "that", "these", "those", "i", "you", "he",
14763 "she", "we", "they", "me", "him", "her", "us", "them", "my", "your",
14764 "his", "her", "our", "their", "what", "which", "who", "whom", "whose",
14765 "when", "where", "why", "how", "all", "each", "every", "both", "few",
14766 "more", "most", "other", "some", "such", "no", "nor", "not", "only",
14767 "own", "same", "so", "than", "too", "very", "just", "also", "now",
14768 ],
14769 "de" | "german" => vec![
14770 "der", "die", "das", "den", "dem", "des", "ein", "eine", "einer",
14771 "einem", "einen", "und", "oder", "aber", "in", "auf", "an", "zu",
14772 "für", "von", "mit", "bei", "als", "ist", "war", "sind", "waren",
14773 "sein", "haben", "hat", "hatte", "werden", "wird", "wurde", "kann",
14774 "können", "muss", "müssen", "soll", "sollen", "will", "wollen",
14775 "es", "sie", "er", "wir", "ihr", "ich", "du", "man", "sich",
14776 "nicht", "auch", "nur", "noch", "schon", "mehr", "sehr", "so",
14777 ],
14778 "fr" | "french" => vec![
14779 "le", "la", "les", "un", "une", "des", "et", "ou", "mais", "dans",
14780 "sur", "à", "de", "pour", "par", "avec", "ce", "cette", "ces",
14781 "est", "sont", "était", "être", "avoir", "a", "ont", "avait",
14782 "je", "tu", "il", "elle", "nous", "vous", "ils", "elles", "on",
14783 "ne", "pas", "plus", "moins", "très", "aussi", "que", "qui",
14784 ],
14785 "es" | "spanish" => vec![
14786 "el", "la", "los", "las", "un", "una", "unos", "unas", "y", "o",
14787 "pero", "en", "de", "a", "para", "por", "con", "es", "son", "era",
14788 "ser", "estar", "tiene", "tienen", "yo", "tú", "él", "ella",
14789 "nosotros", "ustedes", "ellos", "ellas", "no", "sí", "muy", "más",
14790 "menos", "también", "que", "quien", "cual", "como", "cuando",
14791 ],
14792 "it" | "italian" => vec![
14793 "il", "lo", "la", "i", "gli", "le", "un", "uno", "una", "e", "o",
14794 "ma", "in", "di", "a", "da", "per", "con", "su", "tra", "fra",
14795 "è", "sono", "era", "erano", "essere", "avere", "ha", "hanno",
14796 "io", "tu", "lui", "lei", "noi", "voi", "loro", "mi", "ti", "ci",
14797 "non", "più", "molto", "anche", "come", "che", "chi", "quale",
14798 "questo", "quello", "quando", "dove", "perché", "se", "però",
14799 ],
14800 "pt" | "portuguese" => vec![
14801 "o", "a", "os", "as", "um", "uma", "uns", "umas", "e", "ou",
14802 "mas", "em", "de", "para", "por", "com", "sem", "sob", "sobre",
14803 "é", "são", "era", "eram", "ser", "estar", "ter", "tem", "têm",
14804 "eu", "tu", "ele", "ela", "nós", "vós", "eles", "elas", "me", "te",
14805 "não", "mais", "muito", "também", "como", "que", "quem", "qual",
14806 "este", "esse", "aquele", "quando", "onde", "porque", "se", "já",
14807 ],
14808 "nl" | "dutch" => vec![
14809 "de", "het", "een", "en", "of", "maar", "in", "op", "aan", "van",
14810 "voor", "met", "bij", "naar", "om", "te", "tot", "uit", "over",
14811 "is", "zijn", "was", "waren", "worden", "wordt", "werd", "hebben",
14812 "ik", "je", "jij", "hij", "zij", "wij", "jullie", "ze", "mij", "jou",
14813 "niet", "geen", "meer", "ook", "als", "dat", "die", "wat", "wie",
14814 "dit", "deze", "wanneer", "waar", "waarom", "hoe", "dan", "nog",
14815 ],
14816 "ru" | "russian" => vec![
14817 "и", "в", "на", "с", "к", "по", "за", "из", "у", "о", "от", "до",
14818 "для", "при", "без", "под", "над", "между", "через", "после",
14819 "это", "то", "что", "как", "так", "но", "а", "или", "если", "же",
14820 "я", "ты", "он", "она", "мы", "вы", "они", "его", "её", "их",
14821 "не", "ни", "да", "нет", "был", "была", "были", "быть", "есть",
14822 "все", "всё", "весь", "этот", "тот", "который", "когда", "где",
14823 ],
14824 "ar" | "arabic" => vec![
14825 "في", "من", "إلى", "على", "عن", "مع", "هذا", "هذه", "ذلك", "تلك",
14826 "التي", "الذي", "اللذان", "اللتان", "الذين", "اللاتي", "اللواتي",
14827 "هو", "هي", "هم", "هن", "أنا", "أنت", "نحن", "أنتم", "أنتن",
14828 "كان", "كانت", "كانوا", "يكون", "تكون", "ليس", "ليست", "ليسوا",
14829 "و", "أو", "ثم", "لكن", "بل", "إن", "أن", "لأن", "كي", "حتى",
14830 "ما", "لا", "قد", "كل", "بعض", "غير", "أي", "كيف", "متى", "أين",
14831 ],
14832 "zh" | "chinese" => vec![
14833 "的", "了", "是", "在", "有", "和", "与", "或", "但", "而",
14834 "我", "你", "他", "她", "它", "我们", "你们", "他们", "她们",
14835 "这", "那", "这个", "那个", "这些", "那些", "什么", "哪", "哪个",
14836 "不", "没", "没有", "很", "也", "都", "就", "才", "只", "还",
14837 "把", "被", "给", "从", "到", "为", "以", "因为", "所以", "如果",
14838 "会", "能", "可以", "要", "想", "应该", "必须", "可能", "一", "个",
14839 ],
14840 "ja" | "japanese" => vec![
14841 "の", "に", "は", "を", "た", "が", "で", "て", "と", "し",
14842 "れ", "さ", "ある", "いる", "も", "する", "から", "な", "こと",
14843 "として", "い", "や", "など", "なっ", "ない", "この", "ため",
14844 "その", "あっ", "よう", "また", "もの", "という", "あり", "まで",
14845 "られ", "なる", "へ", "か", "だ", "これ", "によって", "により",
14846 "おり", "より", "による", "ず", "なり", "られる", "において",
14847 ],
14848 "ko" | "korean" => vec![
14849 "이", "그", "저", "것", "수", "등", "들", "및", "에", "의",
14850 "가", "을", "를", "은", "는", "로", "으로", "와", "과", "도",
14851 "에서", "까지", "부터", "만", "뿐", "처럼", "같이", "보다",
14852 "하다", "있다", "되다", "없다", "않다", "이다", "아니다",
14853 "나", "너", "우리", "그들", "이것", "그것", "저것", "무엇",
14854 "어디", "언제", "왜", "어떻게", "누구", "어느", "모든", "각",
14855 ],
14856 "hi" | "hindi" => vec![
14857 "का", "के", "की", "में", "है", "हैं", "को", "से", "पर", "था",
14858 "थे", "थी", "और", "या", "लेकिन", "अगर", "तो", "भी", "ही",
14859 "यह", "वह", "इस", "उस", "ये", "वे", "जो", "कि", "क्या", "कैसे",
14860 "मैं", "तुम", "आप", "हम", "वे", "उन्हें", "उनके", "अपने",
14861 "नहीं", "न", "कुछ", "कोई", "सब", "बहुत", "कम", "ज्यादा",
14862 "होना", "करना", "जाना", "आना", "देना", "लेना", "रहना", "सकना",
14863 ],
14864 "tr" | "turkish" => vec![
14865 "bir", "ve", "bu", "da", "de", "için", "ile", "mi", "ne", "o",
14866 "var", "ben", "sen", "biz", "siz", "onlar", "ki", "ama", "çok",
14867 "daha", "gibi", "kadar", "sonra", "şey", "kendi", "bütün", "her",
14868 "bazı", "olan", "olarak", "değil", "ya", "hem", "veya", "ancak",
14869 "ise", "göre", "rağmen", "dolayı", "üzere", "karşı", "arasında",
14870 "olan", "oldu", "olur", "olmak", "etmek", "yapmak", "demek",
14871 ],
14872 "pl" | "polish" => vec![
14873 "i", "w", "z", "na", "do", "o", "że", "to", "nie", "się",
14874 "jest", "tak", "jak", "ale", "po", "co", "czy", "lub", "oraz",
14875 "ja", "ty", "on", "ona", "my", "wy", "oni", "one", "pan", "pani",
14876 "ten", "ta", "te", "tego", "tej", "tym", "tych", "który", "która",
14877 "być", "mieć", "móc", "musieć", "chcieć", "wiedzieć", "mówić",
14878 "bardzo", "tylko", "już", "jeszcze", "też", "więc", "jednak",
14879 ],
14880 "sv" | "swedish" => vec![
14881 "och", "i", "att", "det", "som", "en", "på", "är", "av", "för",
14882 "med", "till", "den", "har", "de", "inte", "om", "ett", "men",
14883 "jag", "du", "han", "hon", "vi", "ni", "de", "dem", "sig", "sin",
14884 "var", "från", "eller", "när", "kan", "ska", "så", "än", "nu",
14885 "också", "bara", "mycket", "mer", "andra", "detta", "sedan",
14886 "hade", "varit", "skulle", "vara", "bli", "blev", "blir", "göra",
14887 ],
14888 _ => vec![
14889 "a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
14890 ],
14891 }
14892}
14893
14894fn compute_hash(s: &str, seed: u64) -> u64 {
14896 let mut hash: u64 = seed.wrapping_mul(0x517cc1b727220a95);
14897 for b in s.bytes() {
14898 hash = hash.wrapping_mul(31).wrapping_add(b as u64);
14899 }
14900 hash
14901}
14902
14903fn register_hologram(interp: &mut Interpreter) {
14923 use crate::interpreter::{RuntimeAffect, RuntimeSentiment, RuntimeIntensity,
14924 RuntimeFormality, RuntimeEmotion, RuntimeConfidence};
14925
14926 define(interp, "emotional_hologram", Some(1), |_, args| {
14929 let affect = match &args[0] {
14930 Value::Affective { affect, .. } => affect.clone(),
14931 _ => RuntimeAffect {
14932 sentiment: None,
14933 sarcasm: false,
14934 intensity: None,
14935 formality: None,
14936 emotion: None,
14937 confidence: None,
14938 },
14939 };
14940
14941 let mut hologram = std::collections::HashMap::new();
14942
14943 let valence = match affect.sentiment {
14945 Some(RuntimeSentiment::Positive) => 1.0,
14946 Some(RuntimeSentiment::Negative) => -1.0,
14947 Some(RuntimeSentiment::Neutral) | None => 0.0,
14948 };
14949 hologram.insert("valence".to_string(), Value::Float(valence));
14950
14951 let arousal = match affect.intensity {
14953 Some(RuntimeIntensity::Down) => 0.25,
14954 None => 0.5,
14955 Some(RuntimeIntensity::Up) => 0.75,
14956 Some(RuntimeIntensity::Max) => 1.0,
14957 };
14958 hologram.insert("arousal".to_string(), Value::Float(arousal));
14959
14960 let dominance = match affect.formality {
14962 Some(RuntimeFormality::Informal) => 0.25,
14963 None => 0.5,
14964 Some(RuntimeFormality::Formal) => 0.85,
14965 };
14966 hologram.insert("dominance".to_string(), Value::Float(dominance));
14967
14968 let authenticity = if affect.sarcasm { -0.9 } else { 0.9 };
14970 hologram.insert("authenticity".to_string(), Value::Float(authenticity));
14971
14972 let certainty = match affect.confidence {
14974 Some(RuntimeConfidence::Low) => 0.2,
14975 None | Some(RuntimeConfidence::Medium) => 0.5,
14976 Some(RuntimeConfidence::High) => 0.9,
14977 };
14978 hologram.insert("certainty".to_string(), Value::Float(certainty));
14979
14980 let emotion_index = match affect.emotion {
14982 Some(RuntimeEmotion::Joy) => 0,
14983 Some(RuntimeEmotion::Sadness) => 1,
14984 Some(RuntimeEmotion::Anger) => 2,
14985 Some(RuntimeEmotion::Fear) => 3,
14986 Some(RuntimeEmotion::Surprise) => 4,
14987 Some(RuntimeEmotion::Love) => 5,
14988 None => -1,
14989 };
14990 hologram.insert("emotion_index".to_string(), Value::Int(emotion_index));
14991
14992 let emotion_name = match affect.emotion {
14994 Some(RuntimeEmotion::Joy) => "joy",
14995 Some(RuntimeEmotion::Sadness) => "sadness",
14996 Some(RuntimeEmotion::Anger) => "anger",
14997 Some(RuntimeEmotion::Fear) => "fear",
14998 Some(RuntimeEmotion::Surprise) => "surprise",
14999 Some(RuntimeEmotion::Love) => "love",
15000 None => "none",
15001 };
15002 hologram.insert("emotion".to_string(), Value::String(Rc::new(emotion_name.to_string())));
15003
15004 Ok(Value::Map(Rc::new(RefCell::new(hologram))))
15005 });
15006
15007 define(interp, "emotional_distance", Some(2), |interp, args| {
15009 let h1 = get_hologram_values(&args[0], interp)?;
15011 let h2 = get_hologram_values(&args[1], interp)?;
15012
15013 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();
15020
15021 Ok(Value::Float(dist))
15022 });
15023
15024 define(interp, "emotional_similarity", Some(2), |interp, args| {
15026 let h1 = get_hologram_values(&args[0], interp)?;
15027 let h2 = get_hologram_values(&args[1], interp)?;
15028
15029 let dot = h1.0 * h2.0 + h1.1 * h2.1 + h1.2 * h2.2 + h1.3 * h2.3 + h1.4 * h2.4;
15030 let mag1 = (h1.0.powi(2) + h1.1.powi(2) + h1.2.powi(2) + h1.3.powi(2) + h1.4.powi(2)).sqrt();
15031 let mag2 = (h2.0.powi(2) + h2.1.powi(2) + h2.2.powi(2) + h2.3.powi(2) + h2.4.powi(2)).sqrt();
15032
15033 let similarity = if mag1 > 0.0 && mag2 > 0.0 {
15034 (dot / (mag1 * mag2) + 1.0) / 2.0 } else {
15036 0.5
15037 };
15038
15039 Ok(Value::Float(similarity))
15040 });
15041
15042 define(interp, "emotional_dissonance", Some(1), |_, args| {
15045 let affect = match &args[0] {
15046 Value::Affective { affect, .. } => affect.clone(),
15047 _ => return Ok(Value::Float(0.0)),
15048 };
15049
15050 let mut dissonance: f64 = 0.0;
15051
15052 if matches!(affect.sentiment, Some(RuntimeSentiment::Positive)) && affect.sarcasm {
15054 dissonance += 0.4;
15055 }
15056
15057 if matches!(affect.sentiment, Some(RuntimeSentiment::Negative)) && affect.sarcasm {
15059 dissonance += 0.1;
15060 }
15061
15062 if matches!(affect.confidence, Some(RuntimeConfidence::High)) &&
15064 matches!(affect.intensity, Some(RuntimeIntensity::Down)) {
15065 dissonance += 0.2;
15066 }
15067
15068 if matches!(affect.formality, Some(RuntimeFormality::Formal)) {
15070 if matches!(affect.emotion, Some(RuntimeEmotion::Anger) | Some(RuntimeEmotion::Fear)) {
15071 dissonance += 0.3;
15072 }
15073 }
15074
15075 if matches!(affect.emotion, Some(RuntimeEmotion::Joy) | Some(RuntimeEmotion::Love)) && affect.sarcasm {
15077 dissonance += 0.3;
15078 }
15079
15080 Ok(Value::Float(dissonance.min(1.0)))
15081 });
15082
15083 define(interp, "emotional_fingerprint", Some(1), |interp, args| {
15086 let h = get_hologram_values(&args[0], interp)?;
15087
15088 let repr = format!(
15090 "hologram:v{:.4}:a{:.4}:d{:.4}:auth{:.4}:c{:.4}",
15091 h.0, h.1, h.2, h.3, h.4
15092 );
15093
15094 let hash = blake3::hash(repr.as_bytes());
15096 Ok(Value::String(Rc::new(hash.to_hex().to_string())))
15097 });
15098
15099 define(interp, "emotional_morph", Some(3), |interp, args| {
15102 let h1 = get_hologram_values(&args[0], interp)?;
15103 let h2 = get_hologram_values(&args[1], interp)?;
15104 let t = match &args[2] {
15105 Value::Float(f) => f.max(0.0).min(1.0),
15106 Value::Int(i) => (*i as f64).max(0.0).min(1.0),
15107 _ => return Err(RuntimeError::new("emotional_morph() requires numeric t value")),
15108 };
15109
15110 let mut result = std::collections::HashMap::new();
15111 result.insert("valence".to_string(), Value::Float(h1.0 + (h2.0 - h1.0) * t));
15112 result.insert("arousal".to_string(), Value::Float(h1.1 + (h2.1 - h1.1) * t));
15113 result.insert("dominance".to_string(), Value::Float(h1.2 + (h2.2 - h1.2) * t));
15114 result.insert("authenticity".to_string(), Value::Float(h1.3 + (h2.3 - h1.3) * t));
15115 result.insert("certainty".to_string(), Value::Float(h1.4 + (h2.4 - h1.4) * t));
15116
15117 Ok(Value::Map(Rc::new(RefCell::new(result))))
15118 });
15119
15120 define(interp, "cultural_emotion", Some(2), |_, args| {
15126 let emotion = match &args[0] {
15127 Value::Affective { affect, .. } => affect.emotion.clone(),
15128 Value::String(s) => match s.as_str() {
15129 "joy" => Some(RuntimeEmotion::Joy),
15130 "sadness" => Some(RuntimeEmotion::Sadness),
15131 "anger" => Some(RuntimeEmotion::Anger),
15132 "fear" => Some(RuntimeEmotion::Fear),
15133 "surprise" => Some(RuntimeEmotion::Surprise),
15134 "love" => Some(RuntimeEmotion::Love),
15135 _ => None,
15136 },
15137 _ => None,
15138 };
15139
15140 let culture = match &args[1] {
15141 Value::String(s) => s.to_lowercase(),
15142 _ => return Err(RuntimeError::new("cultural_emotion() requires string culture")),
15143 };
15144
15145 let result = match (emotion, culture.as_str()) {
15146 (Some(RuntimeEmotion::Joy), "japanese" | "ja") => {
15148 create_cultural_entry("木漏れ日", "komorebi", "sunlight filtering through leaves - peaceful joy")
15149 }
15150 (Some(RuntimeEmotion::Sadness), "japanese" | "ja") => {
15151 create_cultural_entry("物の哀れ", "mono no aware", "the pathos of things - bittersweet awareness of impermanence")
15152 }
15153 (Some(RuntimeEmotion::Love), "japanese" | "ja") => {
15154 create_cultural_entry("甘え", "amae", "indulgent dependence on another's benevolence")
15155 }
15156 (Some(RuntimeEmotion::Fear), "japanese" | "ja") => {
15157 create_cultural_entry("空気を読む", "kuuki wo yomu", "anxiety about reading the room")
15158 }
15159
15160 (Some(RuntimeEmotion::Sadness), "portuguese" | "pt") => {
15162 create_cultural_entry("saudade", "saudade", "melancholic longing for something or someone absent")
15163 }
15164 (Some(RuntimeEmotion::Joy), "portuguese" | "pt") => {
15165 create_cultural_entry("alegria", "alegria", "exuberant collective joy")
15166 }
15167
15168 (Some(RuntimeEmotion::Joy), "german" | "de") => {
15170 create_cultural_entry("Schadenfreude", "schadenfreude", "pleasure derived from another's misfortune")
15171 }
15172 (Some(RuntimeEmotion::Sadness), "german" | "de") => {
15173 create_cultural_entry("Weltschmerz", "weltschmerz", "world-weariness, melancholy about the world's state")
15174 }
15175 (Some(RuntimeEmotion::Fear), "german" | "de") => {
15176 create_cultural_entry("Torschlusspanik", "torschlusspanik", "fear of diminishing opportunities with age")
15177 }
15178
15179 (Some(RuntimeEmotion::Joy), "danish" | "da") => {
15181 create_cultural_entry("hygge", "hygge", "cozy contentment and conviviality")
15182 }
15183
15184 (Some(RuntimeEmotion::Joy), "arabic" | "ar") => {
15186 create_cultural_entry("طرب", "tarab", "musical ecstasy, enchantment through art")
15187 }
15188 (Some(RuntimeEmotion::Love), "arabic" | "ar") => {
15189 create_cultural_entry("هوى", "hawa", "passionate, sometimes irrational love")
15190 }
15191
15192 (Some(RuntimeEmotion::Sadness), "korean" | "ko") => {
15194 create_cultural_entry("한", "han", "collective grief and resentment from historical suffering")
15195 }
15196 (Some(RuntimeEmotion::Joy), "korean" | "ko") => {
15197 create_cultural_entry("정", "jeong", "deep affection and attachment formed over time")
15198 }
15199
15200 (Some(RuntimeEmotion::Sadness), "russian" | "ru") => {
15202 create_cultural_entry("тоска", "toska", "spiritual anguish without specific cause")
15203 }
15204
15205 (Some(RuntimeEmotion::Love), "hindi" | "hi") => {
15207 create_cultural_entry("विरह", "viraha", "longing for an absent beloved")
15208 }
15209
15210 (Some(RuntimeEmotion::Anger), "finnish" | "fi") => {
15212 create_cultural_entry("sisu", "sisu", "stoic determination and grit in adversity")
15213 }
15214
15215 (Some(e), _) => {
15217 let name = match e {
15218 RuntimeEmotion::Joy => "joy",
15219 RuntimeEmotion::Sadness => "sadness",
15220 RuntimeEmotion::Anger => "anger",
15221 RuntimeEmotion::Fear => "fear",
15222 RuntimeEmotion::Surprise => "surprise",
15223 RuntimeEmotion::Love => "love",
15224 };
15225 create_cultural_entry(name, name, "universal emotion")
15226 }
15227 (None, _) => create_cultural_entry("none", "none", "no emotion"),
15228 };
15229
15230 Ok(result)
15231 });
15232
15233 define(interp, "list_cultural_emotions", Some(1), |_, args| {
15235 let culture = match &args[0] {
15236 Value::String(s) => s.to_lowercase(),
15237 _ => return Err(RuntimeError::new("list_cultural_emotions() requires string culture")),
15238 };
15239
15240 let emotions: Vec<(&str, &str, &str)> = match culture.as_str() {
15241 "japanese" | "ja" => vec![
15242 ("木漏れ日", "komorebi", "sunlight through leaves"),
15243 ("物の哀れ", "mono no aware", "pathos of things"),
15244 ("甘え", "amae", "indulgent dependence"),
15245 ("侘寂", "wabi-sabi", "beauty in imperfection"),
15246 ("生きがい", "ikigai", "reason for being"),
15247 ],
15248 "german" | "de" => vec![
15249 ("Schadenfreude", "schadenfreude", "joy at misfortune"),
15250 ("Weltschmerz", "weltschmerz", "world-weariness"),
15251 ("Torschlusspanik", "torschlusspanik", "gate-closing panic"),
15252 ("Sehnsucht", "sehnsucht", "deep longing"),
15253 ("Wanderlust", "wanderlust", "desire to travel"),
15254 ],
15255 "portuguese" | "pt" => vec![
15256 ("saudade", "saudade", "melancholic longing"),
15257 ("alegria", "alegria", "exuberant joy"),
15258 ("desabafar", "desabafar", "emotional unburdening"),
15259 ],
15260 "danish" | "da" => vec![
15261 ("hygge", "hygge", "cozy contentment"),
15262 ],
15263 "korean" | "ko" => vec![
15264 ("한", "han", "collective grief"),
15265 ("정", "jeong", "deep affection"),
15266 ("눈치", "nunchi", "situational awareness"),
15267 ],
15268 "arabic" | "ar" => vec![
15269 ("طرب", "tarab", "musical ecstasy"),
15270 ("هوى", "hawa", "passionate love"),
15271 ("صبر", "sabr", "patient perseverance"),
15272 ],
15273 "russian" | "ru" => vec![
15274 ("тоска", "toska", "spiritual anguish"),
15275 ("пошлость", "poshlost", "spiritual vulgarity"),
15276 ],
15277 "finnish" | "fi" => vec![
15278 ("sisu", "sisu", "stoic determination"),
15279 ],
15280 "hindi" | "hi" => vec![
15281 ("विरह", "viraha", "longing for beloved"),
15282 ("जुगाड़", "jugaad", "creative improvisation"),
15283 ],
15284 _ => vec![
15285 ("joy", "joy", "universal happiness"),
15286 ("sadness", "sadness", "universal sorrow"),
15287 ("anger", "anger", "universal frustration"),
15288 ("fear", "fear", "universal anxiety"),
15289 ("surprise", "surprise", "universal amazement"),
15290 ("love", "love", "universal affection"),
15291 ],
15292 };
15293
15294 let result: Vec<Value> = emotions.iter()
15295 .map(|(native, romanized, meaning)| {
15296 create_cultural_entry(native, romanized, meaning)
15297 })
15298 .collect();
15299
15300 Ok(Value::Array(Rc::new(RefCell::new(result))))
15301 });
15302
15303 define(interp, "hologram_info", Some(0), |_, _| {
15305 let mut info = std::collections::HashMap::new();
15306
15307 info.insert("dimensions".to_string(), Value::Array(Rc::new(RefCell::new(vec![
15308 Value::String(Rc::new("valence".to_string())),
15309 Value::String(Rc::new("arousal".to_string())),
15310 Value::String(Rc::new("dominance".to_string())),
15311 Value::String(Rc::new("authenticity".to_string())),
15312 Value::String(Rc::new("certainty".to_string())),
15313 Value::String(Rc::new("emotion_index".to_string())),
15314 ]))));
15315
15316 info.insert("supported_cultures".to_string(), Value::Array(Rc::new(RefCell::new(vec![
15317 Value::String(Rc::new("japanese".to_string())),
15318 Value::String(Rc::new("german".to_string())),
15319 Value::String(Rc::new("portuguese".to_string())),
15320 Value::String(Rc::new("danish".to_string())),
15321 Value::String(Rc::new("korean".to_string())),
15322 Value::String(Rc::new("arabic".to_string())),
15323 Value::String(Rc::new("russian".to_string())),
15324 Value::String(Rc::new("finnish".to_string())),
15325 Value::String(Rc::new("hindi".to_string())),
15326 ]))));
15327
15328 let funcs = vec![
15329 "emotional_hologram", "emotional_distance", "emotional_similarity",
15330 "emotional_dissonance", "emotional_fingerprint", "emotional_morph",
15331 "cultural_emotion", "list_cultural_emotions", "hologram_info"
15332 ];
15333 let func_values: Vec<Value> = funcs.iter().map(|s| Value::String(Rc::new(s.to_string()))).collect();
15334 info.insert("functions".to_string(), Value::Array(Rc::new(RefCell::new(func_values))));
15335
15336 Ok(Value::Map(Rc::new(RefCell::new(info))))
15337 });
15338}
15339
15340fn get_hologram_values(val: &Value, _interp: &mut Interpreter) -> Result<(f64, f64, f64, f64, f64), RuntimeError> {
15342 use crate::interpreter::{RuntimeAffect, RuntimeSentiment, RuntimeIntensity,
15343 RuntimeFormality, RuntimeConfidence};
15344
15345 let affect = match val {
15346 Value::Affective { affect, .. } => affect.clone(),
15347 Value::Map(m) => {
15348 let map = m.borrow();
15350 let v = extract_float(&map, "valence").unwrap_or(0.0);
15351 let a = extract_float(&map, "arousal").unwrap_or(0.5);
15352 let d = extract_float(&map, "dominance").unwrap_or(0.5);
15353 let auth = extract_float(&map, "authenticity").unwrap_or(0.9);
15354 let c = extract_float(&map, "certainty").unwrap_or(0.5);
15355 return Ok((v, a, d, auth, c));
15356 }
15357 _ => RuntimeAffect {
15358 sentiment: None,
15359 sarcasm: false,
15360 intensity: None,
15361 formality: None,
15362 emotion: None,
15363 confidence: None,
15364 },
15365 };
15366
15367 let v = match affect.sentiment {
15368 Some(RuntimeSentiment::Positive) => 1.0,
15369 Some(RuntimeSentiment::Negative) => -1.0,
15370 _ => 0.0,
15371 };
15372 let a = match affect.intensity {
15373 Some(RuntimeIntensity::Down) => 0.25,
15374 Some(RuntimeIntensity::Up) => 0.75,
15375 Some(RuntimeIntensity::Max) => 1.0,
15376 None => 0.5,
15377 };
15378 let d = match affect.formality {
15379 Some(RuntimeFormality::Informal) => 0.25,
15380 Some(RuntimeFormality::Formal) => 0.85,
15381 None => 0.5,
15382 };
15383 let auth = if affect.sarcasm { -0.9 } else { 0.9 };
15384 let c = match affect.confidence {
15385 Some(RuntimeConfidence::Low) => 0.2,
15386 Some(RuntimeConfidence::High) => 0.9,
15387 _ => 0.5,
15388 };
15389
15390 Ok((v, a, d, auth, c))
15391}
15392
15393fn extract_float(map: &std::collections::HashMap<String, Value>, key: &str) -> Option<f64> {
15394 match map.get(key) {
15395 Some(Value::Float(f)) => Some(*f),
15396 Some(Value::Int(i)) => Some(*i as f64),
15397 _ => None,
15398 }
15399}
15400
15401fn create_cultural_entry(native: &str, romanized: &str, meaning: &str) -> Value {
15402 let mut entry = std::collections::HashMap::new();
15403 entry.insert("native".to_string(), Value::String(Rc::new(native.to_string())));
15404 entry.insert("romanized".to_string(), Value::String(Rc::new(romanized.to_string())));
15405 entry.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
15406 Value::Map(Rc::new(RefCell::new(entry)))
15407}
15408
15409fn register_experimental_crypto(interp: &mut Interpreter) {
15414 define(interp, "commit", Some(1), |_, args| {
15420 let value_str = match &args[0] {
15421 Value::String(s) => s.to_string(),
15422 other => format!("{:?}", other),
15423 };
15424
15425 let mut nonce = [0u8; 32];
15427 getrandom::getrandom(&mut nonce).map_err(|e| RuntimeError::new(format!("commit() random failed: {}", e)))?;
15428 let nonce_hex = hex::encode(&nonce);
15429
15430 let commitment_input = format!("{}:{}", value_str, nonce_hex);
15432 let commitment = blake3::hash(commitment_input.as_bytes());
15433
15434 let mut result = std::collections::HashMap::new();
15435 result.insert("commitment".to_string(), Value::String(Rc::new(commitment.to_hex().to_string())));
15436 result.insert("nonce".to_string(), Value::String(Rc::new(nonce_hex)));
15437 result.insert("value".to_string(), args[0].clone());
15438
15439 Ok(Value::Map(Rc::new(RefCell::new(result))))
15440 });
15441
15442 define(interp, "verify_commitment", Some(3), |_, args| {
15444 let commitment = match &args[0] {
15445 Value::String(s) => s.to_string(),
15446 _ => return Err(RuntimeError::new("verify_commitment() requires string commitment")),
15447 };
15448 let value_str = match &args[1] {
15449 Value::String(s) => s.to_string(),
15450 other => format!("{:?}", other),
15451 };
15452 let nonce = match &args[2] {
15453 Value::String(s) => s.to_string(),
15454 _ => return Err(RuntimeError::new("verify_commitment() requires string nonce")),
15455 };
15456
15457 let commitment_input = format!("{}:{}", value_str, nonce);
15459 let computed = blake3::hash(commitment_input.as_bytes());
15460
15461 Ok(Value::Bool(computed.to_hex().to_string() == commitment))
15462 });
15463
15464 define(interp, "secret_split", Some(3), |_, args| {
15470 let secret = match &args[0] {
15471 Value::String(s) => s.as_bytes().to_vec(),
15472 Value::Array(arr) => {
15473 let borrowed = arr.borrow();
15474 borrowed.iter().filter_map(|v| {
15475 if let Value::Int(i) = v { Some(*i as u8) } else { None }
15476 }).collect()
15477 }
15478 _ => return Err(RuntimeError::new("secret_split() requires string or byte array")),
15479 };
15480
15481 let threshold = match &args[1] {
15482 Value::Int(n) => *n as usize,
15483 _ => return Err(RuntimeError::new("secret_split() requires integer threshold")),
15484 };
15485
15486 let num_shares = match &args[2] {
15487 Value::Int(n) => *n as usize,
15488 _ => return Err(RuntimeError::new("secret_split() requires integer num_shares")),
15489 };
15490
15491 if threshold < 2 {
15492 return Err(RuntimeError::new("secret_split() threshold must be >= 2"));
15493 }
15494 if num_shares < threshold {
15495 return Err(RuntimeError::new("secret_split() num_shares must be >= threshold"));
15496 }
15497 if num_shares > 255 {
15498 return Err(RuntimeError::new("secret_split() max 255 shares"));
15499 }
15500
15501 let mut rng = rand::thread_rng();
15504 let mut shares: Vec<Vec<u8>> = (0..num_shares).map(|_| Vec::with_capacity(secret.len() + 1)).collect();
15505
15506 for (i, share) in shares.iter_mut().enumerate() {
15508 share.push((i + 1) as u8);
15509 }
15510
15511 for &byte in &secret {
15513 let mut coefficients: Vec<u8> = vec![byte];
15516 for _ in 1..threshold {
15517 coefficients.push(rng.gen());
15518 }
15519
15520 for (i, share) in shares.iter_mut().enumerate() {
15522 let x = (i + 1) as u8;
15523 let y = eval_polynomial_gf256(&coefficients, x);
15524 share.push(y);
15525 }
15526 }
15527
15528 let share_values: Vec<Value> = shares.iter()
15530 .map(|share| {
15531 let hex = hex::encode(share);
15532 Value::String(Rc::new(hex))
15533 })
15534 .collect();
15535
15536 let mut result = std::collections::HashMap::new();
15537 result.insert("shares".to_string(), Value::Array(Rc::new(RefCell::new(share_values))));
15538 result.insert("threshold".to_string(), Value::Int(threshold as i64));
15539 result.insert("total".to_string(), Value::Int(num_shares as i64));
15540
15541 Ok(Value::Map(Rc::new(RefCell::new(result))))
15542 });
15543
15544 define(interp, "secret_recover", Some(1), |_, args| {
15546 let shares: Vec<Vec<u8>> = match &args[0] {
15547 Value::Array(arr) => {
15548 let borrowed = arr.borrow();
15549 borrowed.iter().filter_map(|v| {
15550 if let Value::String(s) = v {
15551 hex::decode(s.as_str()).ok()
15552 } else {
15553 None
15554 }
15555 }).collect()
15556 }
15557 _ => return Err(RuntimeError::new("secret_recover() requires array of share strings")),
15558 };
15559
15560 if shares.is_empty() {
15561 return Err(RuntimeError::new("secret_recover() requires at least one share"));
15562 }
15563
15564 let share_len = shares[0].len();
15565 if share_len < 2 {
15566 return Err(RuntimeError::new("secret_recover() invalid share format"));
15567 }
15568
15569 let mut secret = Vec::with_capacity(share_len - 1);
15571
15572 for byte_idx in 1..share_len {
15573 let points: Vec<(u8, u8)> = shares.iter()
15575 .map(|share| (share[0], share[byte_idx]))
15576 .collect();
15577
15578 let recovered_byte = lagrange_interpolate_gf256(&points, 0);
15580 secret.push(recovered_byte);
15581 }
15582
15583 match String::from_utf8(secret.clone()) {
15585 Ok(s) => Ok(Value::String(Rc::new(s))),
15586 Err(_) => {
15587 let byte_values: Vec<Value> = secret.iter().map(|&b| Value::Int(b as i64)).collect();
15589 Ok(Value::Array(Rc::new(RefCell::new(byte_values))))
15590 }
15591 }
15592 });
15593
15594 define(interp, "council_split", Some(2), |_, args| {
15600 let secret = match &args[0] {
15601 Value::String(s) => s.as_bytes().to_vec(),
15602 _ => return Err(RuntimeError::new("council_split() requires string secret")),
15603 };
15604
15605 let num_elders = match &args[1] {
15606 Value::Int(n) => *n as usize,
15607 _ => return Err(RuntimeError::new("council_split() requires integer num_elders")),
15608 };
15609
15610 if num_elders < 3 {
15611 return Err(RuntimeError::new("council_split() requires at least 3 elders"));
15612 }
15613
15614 let threshold = (num_elders / 2) + 1;
15616
15617 let mut rng = rand::thread_rng();
15619 let mut shares: Vec<Vec<u8>> = (0..num_elders).map(|_| Vec::with_capacity(secret.len() + 1)).collect();
15620
15621 for (i, share) in shares.iter_mut().enumerate() {
15622 share.push((i + 1) as u8);
15623 }
15624
15625 for &byte in &secret {
15626 let mut coefficients: Vec<u8> = vec![byte];
15627 for _ in 1..threshold {
15628 coefficients.push(rng.gen());
15629 }
15630
15631 for (i, share) in shares.iter_mut().enumerate() {
15632 let x = (i + 1) as u8;
15633 let y = eval_polynomial_gf256(&coefficients, x);
15634 share.push(y);
15635 }
15636 }
15637
15638 let share_values: Vec<Value> = shares.iter()
15639 .map(|share| Value::String(Rc::new(hex::encode(share))))
15640 .collect();
15641
15642 let mut result = std::collections::HashMap::new();
15643 result.insert("shares".to_string(), Value::Array(Rc::new(RefCell::new(share_values))));
15644 result.insert("threshold".to_string(), Value::Int(threshold as i64));
15645 result.insert("total".to_string(), Value::Int(num_elders as i64));
15646 result.insert("model".to_string(), Value::String(Rc::new("ubuntu".to_string())));
15647 result.insert("philosophy".to_string(), Value::String(Rc::new("I am because we are - majority consensus required".to_string())));
15648
15649 Ok(Value::Map(Rc::new(RefCell::new(result))))
15650 });
15651
15652 define(interp, "witness_chain", Some(2), |_, args| {
15655 let statement = match &args[0] {
15656 Value::String(s) => s.to_string(),
15657 _ => return Err(RuntimeError::new("witness_chain() requires string statement")),
15658 };
15659
15660 let witnesses: Vec<String> = match &args[1] {
15661 Value::Array(arr) => {
15662 let borrowed = arr.borrow();
15663 borrowed.iter().filter_map(|v| {
15664 if let Value::String(s) = v { Some(s.to_string()) } else { None }
15665 }).collect()
15666 }
15667 _ => return Err(RuntimeError::new("witness_chain() requires array of witness names")),
15668 };
15669
15670 if witnesses.is_empty() {
15671 return Err(RuntimeError::new("witness_chain() requires at least one witness"));
15672 }
15673
15674 let mut chain = Vec::new();
15676 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
15677
15678 for (i, witness) in witnesses.iter().enumerate() {
15679 let attestation = format!("{}:attests:{}", witness, prev_hash);
15680 let hash = blake3::hash(attestation.as_bytes()).to_hex().to_string();
15681
15682 let mut link = std::collections::HashMap::new();
15683 link.insert("witness".to_string(), Value::String(Rc::new(witness.clone())));
15684 link.insert("position".to_string(), Value::Int((i + 1) as i64));
15685 link.insert("attests_to".to_string(), Value::String(Rc::new(prev_hash.clone())));
15686 link.insert("signature".to_string(), Value::String(Rc::new(hash.clone())));
15687
15688 chain.push(Value::Map(Rc::new(RefCell::new(link))));
15689 prev_hash = hash;
15690 }
15691
15692 let mut result = std::collections::HashMap::new();
15693 result.insert("statement".to_string(), Value::String(Rc::new(statement)));
15694 result.insert("chain".to_string(), Value::Array(Rc::new(RefCell::new(chain))));
15695 result.insert("final_seal".to_string(), Value::String(Rc::new(prev_hash)));
15696 result.insert("model".to_string(), Value::String(Rc::new("isnad".to_string())));
15697 result.insert("philosophy".to_string(), Value::String(Rc::new("Chain of reliable transmitters - each witness validates the previous".to_string())));
15698
15699 Ok(Value::Map(Rc::new(RefCell::new(result))))
15700 });
15701
15702 define(interp, "verify_witness_chain", Some(1), |_, args| {
15704 let chain_map = match &args[0] {
15705 Value::Map(m) => m.borrow(),
15706 _ => return Err(RuntimeError::new("verify_witness_chain() requires chain map")),
15707 };
15708
15709 let statement = match chain_map.get("statement") {
15710 Some(Value::String(s)) => s.to_string(),
15711 _ => return Err(RuntimeError::new("verify_witness_chain() invalid chain format")),
15712 };
15713
15714 let chain = match chain_map.get("chain") {
15715 Some(Value::Array(arr)) => arr.borrow().clone(),
15716 _ => return Err(RuntimeError::new("verify_witness_chain() invalid chain format")),
15717 };
15718
15719 let mut prev_hash = blake3::hash(statement.as_bytes()).to_hex().to_string();
15720
15721 for link_val in chain.iter() {
15722 if let Value::Map(link_map) = link_val {
15723 let link = link_map.borrow();
15724 let witness = match link.get("witness") {
15725 Some(Value::String(s)) => s.to_string(),
15726 _ => return Ok(Value::Bool(false)),
15727 };
15728 let attests_to = match link.get("attests_to") {
15729 Some(Value::String(s)) => s.to_string(),
15730 _ => return Ok(Value::Bool(false)),
15731 };
15732 let signature = match link.get("signature") {
15733 Some(Value::String(s)) => s.to_string(),
15734 _ => return Ok(Value::Bool(false)),
15735 };
15736
15737 if attests_to != prev_hash {
15739 return Ok(Value::Bool(false));
15740 }
15741
15742 let expected = format!("{}:attests:{}", witness, prev_hash);
15743 let computed = blake3::hash(expected.as_bytes()).to_hex().to_string();
15744
15745 if computed != signature {
15746 return Ok(Value::Bool(false));
15747 }
15748
15749 prev_hash = signature;
15750 } else {
15751 return Ok(Value::Bool(false));
15752 }
15753 }
15754
15755 Ok(Value::Bool(true))
15756 });
15757
15758 define(interp, "experimental_crypto_info", Some(0), |_, _| {
15760 let mut info = std::collections::HashMap::new();
15761
15762 info.insert("commitment_functions".to_string(), Value::Array(Rc::new(RefCell::new(vec![
15763 Value::String(Rc::new("commit".to_string())),
15764 Value::String(Rc::new("verify_commitment".to_string())),
15765 ]))));
15766
15767 info.insert("threshold_functions".to_string(), Value::Array(Rc::new(RefCell::new(vec![
15768 Value::String(Rc::new("secret_split".to_string())),
15769 Value::String(Rc::new("secret_recover".to_string())),
15770 ]))));
15771
15772 info.insert("cultural_ceremonies".to_string(), Value::Array(Rc::new(RefCell::new(vec![
15773 Value::String(Rc::new("council_split (Ubuntu - African consensus)".to_string())),
15774 Value::String(Rc::new("witness_chain (Isnad - Islamic transmission)".to_string())),
15775 ]))));
15776
15777 Ok(Value::Map(Rc::new(RefCell::new(info))))
15778 });
15779}
15780
15781fn eval_polynomial_gf256(coefficients: &[u8], x: u8) -> u8 {
15783 let mut result: u8 = 0;
15784 let mut x_power: u8 = 1;
15785
15786 for &coef in coefficients {
15787 result ^= gf256_mul(coef, x_power);
15788 x_power = gf256_mul(x_power, x);
15789 }
15790
15791 result
15792}
15793
15794fn lagrange_interpolate_gf256(points: &[(u8, u8)], _x: u8) -> u8 {
15796 let mut result: u8 = 0;
15797
15798 for (i, &(xi, yi)) in points.iter().enumerate() {
15799 let mut numerator: u8 = 1;
15800 let mut denominator: u8 = 1;
15801
15802 for (j, &(xj, _)) in points.iter().enumerate() {
15803 if i != j {
15804 numerator = gf256_mul(numerator, xj);
15806 denominator = gf256_mul(denominator, xi ^ xj);
15808 }
15809 }
15810
15811 let term = gf256_mul(yi, gf256_mul(numerator, gf256_inv(denominator)));
15813 result ^= term;
15814 }
15815
15816 result
15817}
15818
15819fn gf256_mul(mut a: u8, mut b: u8) -> u8 {
15821 let mut result: u8 = 0;
15822 let modulus: u16 = 0x11b; while b != 0 {
15825 if b & 1 != 0 {
15826 result ^= a;
15827 }
15828 let high_bit = (a & 0x80) != 0;
15829 a <<= 1;
15830 if high_bit {
15831 a ^= (modulus & 0xff) as u8;
15832 }
15833 b >>= 1;
15834 }
15835
15836 result
15837}
15838
15839fn gf256_inv(a: u8) -> u8 {
15841 if a == 0 {
15842 return 0;
15843 }
15844
15845 let mut result = a;
15847 for _ in 0..6 {
15848 result = gf256_mul(result, result);
15849 result = gf256_mul(result, a);
15850 }
15851 gf256_mul(result, result)
15852}
15853
15854fn register_multibase(interp: &mut Interpreter) {
15873 define(interp, "to_vigesimal", Some(1), |_, args| {
15877 let n = match &args[0] {
15878 Value::Int(n) => *n,
15879 _ => return Err(RuntimeError::new("to_vigesimal() requires integer")),
15880 };
15881
15882 let result = to_base_string(n.unsigned_abs(), 20, false);
15883 let prefix = if n < 0 { "-0v" } else { "0v" };
15884 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
15885 });
15886
15887 define(interp, "from_vigesimal", Some(1), |_, args| {
15888 let s = match &args[0] {
15889 Value::String(s) => s.to_string(),
15890 _ => return Err(RuntimeError::new("from_vigesimal() requires string")),
15891 };
15892
15893 let (negative, clean) = parse_base_prefix(&s, "0v");
15894 let value = from_base_string(&clean, 20)?;
15895 Ok(Value::Int(if negative { -(value as i64) } else { value as i64 }))
15896 });
15897
15898 define(interp, "to_sexagesimal", Some(1), |_, args| {
15902 let n = match &args[0] {
15903 Value::Int(n) => *n,
15904 _ => return Err(RuntimeError::new("to_sexagesimal() requires integer")),
15905 };
15906
15907 let negative = n < 0;
15908 let mut value = n.unsigned_abs();
15909 let mut parts = Vec::new();
15910
15911 if value == 0 {
15912 parts.push("0".to_string());
15913 } else {
15914 while value > 0 {
15915 parts.push(format!("{}", value % 60));
15916 value /= 60;
15917 }
15918 parts.reverse();
15919 }
15920
15921 let prefix = if negative { "-0s" } else { "0s" };
15922 Ok(Value::String(Rc::new(format!("{}[{}]", prefix, parts.join(":")))))
15923 });
15924
15925 define(interp, "from_sexagesimal", Some(1), |_, args| {
15926 let s = match &args[0] {
15927 Value::String(s) => s.to_string(),
15928 _ => return Err(RuntimeError::new("from_sexagesimal() requires string")),
15929 };
15930
15931 let negative = s.starts_with('-');
15932 let clean = s.trim_start_matches('-')
15933 .trim_start_matches("0s")
15934 .trim_start_matches('[')
15935 .trim_end_matches(']');
15936
15937 let mut result: i64 = 0;
15938 for part in clean.split(':') {
15939 let digit: i64 = part.trim().parse()
15940 .map_err(|_| RuntimeError::new(format!("Invalid sexagesimal digit: {}", part)))?;
15941 if digit < 0 || digit >= 60 {
15942 return Err(RuntimeError::new(format!("Sexagesimal digit out of range: {}", digit)));
15943 }
15944 result = result * 60 + digit;
15945 }
15946
15947 Ok(Value::Int(if negative { -result } else { result }))
15948 });
15949
15950 define(interp, "to_duodecimal", Some(1), |_, args| {
15954 let n = match &args[0] {
15955 Value::Int(n) => *n,
15956 _ => return Err(RuntimeError::new("to_duodecimal() requires integer")),
15957 };
15958
15959 let result = to_base_string_custom(n.unsigned_abs(), 12, "0123456789XE");
15960 let prefix = if n < 0 { "-0z" } else { "0z" };
15961 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
15962 });
15963
15964 define(interp, "from_duodecimal", Some(1), |_, args| {
15965 let s = match &args[0] {
15966 Value::String(s) => s.to_string(),
15967 _ => return Err(RuntimeError::new("from_duodecimal() requires string")),
15968 };
15969
15970 let (negative, clean) = parse_base_prefix(&s, "0z");
15971 let value = from_base_string_custom(&clean.to_uppercase(), "0123456789XE")?;
15972 Ok(Value::Int(if negative { -(value as i64) } else { value as i64 }))
15973 });
15974
15975 define(interp, "to_base", Some(2), |_, args| {
15978 let n = match &args[0] {
15979 Value::Int(n) => *n,
15980 _ => return Err(RuntimeError::new("to_base() requires integer")),
15981 };
15982 let base = match &args[1] {
15983 Value::Int(b) => *b as u64,
15984 _ => return Err(RuntimeError::new("to_base() requires integer base")),
15985 };
15986
15987 if base < 2 || base > 36 {
15988 return Err(RuntimeError::new("to_base() base must be 2-36"));
15989 }
15990
15991 let result = to_base_string(n.unsigned_abs(), base, false);
15992 let prefix = if n < 0 { "-" } else { "" };
15993 Ok(Value::String(Rc::new(format!("{}{}", prefix, result))))
15994 });
15995
15996 define(interp, "from_base", Some(2), |_, args| {
15997 let s = match &args[0] {
15998 Value::String(s) => s.to_string(),
15999 _ => return Err(RuntimeError::new("from_base() requires string")),
16000 };
16001 let base = match &args[1] {
16002 Value::Int(b) => *b as u64,
16003 _ => return Err(RuntimeError::new("from_base() requires integer base")),
16004 };
16005
16006 if base < 2 || base > 36 {
16007 return Err(RuntimeError::new("from_base() base must be 2-36"));
16008 }
16009
16010 let negative = s.starts_with('-');
16011 let clean = s.trim_start_matches('-');
16012 let value = from_base_string(clean, base)?;
16013 Ok(Value::Int(if negative { -(value as i64) } else { value as i64 }))
16014 });
16015
16016 const BASE58_ALPHABET: &str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
16021
16022 define(interp, "base58_encode", Some(1), |_, args| {
16023 let bytes: Vec<u8> = match &args[0] {
16024 Value::String(s) => s.as_bytes().to_vec(),
16025 Value::Array(arr) => {
16026 arr.borrow().iter().filter_map(|v| {
16027 if let Value::Int(i) = v { Some(*i as u8) } else { None }
16028 }).collect()
16029 }
16030 _ => return Err(RuntimeError::new("base58_encode() requires string or byte array")),
16031 };
16032
16033 let leading_zeros = bytes.iter().take_while(|&&b| b == 0).count();
16035
16036 let mut result = Vec::new();
16038 let mut num: Vec<u8> = bytes.clone();
16039
16040 while !num.is_empty() && !num.iter().all(|&b| b == 0) {
16041 let mut remainder = 0u32;
16042 let mut new_num = Vec::new();
16043
16044 for &byte in &num {
16045 let acc = (remainder << 8) + byte as u32;
16046 let digit = acc / 58;
16047 remainder = acc % 58;
16048
16049 if !new_num.is_empty() || digit > 0 {
16050 new_num.push(digit as u8);
16051 }
16052 }
16053
16054 result.push(BASE58_ALPHABET.chars().nth(remainder as usize).unwrap());
16055 num = new_num;
16056 }
16057
16058 for _ in 0..leading_zeros {
16060 result.push('1');
16061 }
16062
16063 result.reverse();
16064 Ok(Value::String(Rc::new(result.into_iter().collect())))
16065 });
16066
16067 define(interp, "base58_decode", Some(1), |_, args| {
16068 let s = match &args[0] {
16069 Value::String(s) => s.to_string(),
16070 _ => return Err(RuntimeError::new("base58_decode() requires string")),
16071 };
16072
16073 let leading_ones = s.chars().take_while(|&c| c == '1').count();
16075
16076 let mut num: Vec<u8> = Vec::new();
16078
16079 for c in s.chars() {
16080 let digit = BASE58_ALPHABET.find(c)
16081 .ok_or_else(|| RuntimeError::new(format!("Invalid base58 character: {}", c)))?;
16082
16083 let mut carry = digit as u32;
16084 for byte in num.iter_mut().rev() {
16085 let acc = (*byte as u32) * 58 + carry;
16086 *byte = (acc & 0xff) as u8;
16087 carry = acc >> 8;
16088 }
16089
16090 while carry > 0 {
16091 num.insert(0, (carry & 0xff) as u8);
16092 carry >>= 8;
16093 }
16094 }
16095
16096 let mut result = vec![0u8; leading_ones];
16098 result.extend(num);
16099
16100 Ok(Value::String(Rc::new(hex::encode(&result))))
16102 });
16103
16104 const BASE32_ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
16108
16109 define(interp, "base32_encode", Some(1), |_, args| {
16110 let bytes: Vec<u8> = match &args[0] {
16111 Value::String(s) => s.as_bytes().to_vec(),
16112 Value::Array(arr) => {
16113 arr.borrow().iter().filter_map(|v| {
16114 if let Value::Int(i) = v { Some(*i as u8) } else { None }
16115 }).collect()
16116 }
16117 _ => return Err(RuntimeError::new("base32_encode() requires string or byte array")),
16118 };
16119
16120 let mut result = String::new();
16121 let mut buffer: u64 = 0;
16122 let mut bits = 0;
16123
16124 for byte in bytes {
16125 buffer = (buffer << 8) | byte as u64;
16126 bits += 8;
16127
16128 while bits >= 5 {
16129 bits -= 5;
16130 let idx = ((buffer >> bits) & 0x1f) as usize;
16131 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
16132 }
16133 }
16134
16135 if bits > 0 {
16136 let idx = ((buffer << (5 - bits)) & 0x1f) as usize;
16137 result.push(BASE32_ALPHABET.chars().nth(idx).unwrap());
16138 }
16139
16140 while result.len() % 8 != 0 {
16142 result.push('=');
16143 }
16144
16145 Ok(Value::String(Rc::new(result)))
16146 });
16147
16148 define(interp, "base32_decode", Some(1), |_, args| {
16149 let s = match &args[0] {
16150 Value::String(s) => s.to_uppercase().replace('=', ""),
16151 _ => return Err(RuntimeError::new("base32_decode() requires string")),
16152 };
16153
16154 let mut result = Vec::new();
16155 let mut buffer: u64 = 0;
16156 let mut bits = 0;
16157
16158 for c in s.chars() {
16159 let digit = BASE32_ALPHABET.find(c)
16160 .ok_or_else(|| RuntimeError::new(format!("Invalid base32 character: {}", c)))?;
16161
16162 buffer = (buffer << 5) | digit as u64;
16163 bits += 5;
16164
16165 if bits >= 8 {
16166 bits -= 8;
16167 result.push((buffer >> bits) as u8);
16168 buffer &= (1 << bits) - 1;
16169 }
16170 }
16171
16172 Ok(Value::String(Rc::new(hex::encode(&result))))
16173 });
16174
16175 define(interp, "sacred_numbers", Some(1), |_, args| {
16179 let culture = match &args[0] {
16180 Value::String(s) => s.to_lowercase(),
16181 _ => return Err(RuntimeError::new("sacred_numbers() requires string culture")),
16182 };
16183
16184 let numbers: Vec<(i64, &str)> = match culture.as_str() {
16185 "mayan" | "maya" => vec![
16186 (13, "Sacred cycle - Tzolkin calendar"),
16187 (20, "Base of vigesimal system - human digits"),
16188 (52, "Calendar round - 52 years"),
16189 (260, "Tzolkin sacred calendar days"),
16190 (365, "Haab solar calendar days"),
16191 (400, "Baktun - 20×20 years"),
16192 ],
16193 "babylonian" | "mesopotamian" => vec![
16194 (12, "Months, hours - celestial division"),
16195 (60, "Sexagesimal base - minutes, seconds, degrees"),
16196 (360, "Circle degrees - 6×60"),
16197 (3600, "Sar - 60×60, large count"),
16198 (7, "Planets visible to naked eye"),
16199 ],
16200 "chinese" | "zh" => vec![
16201 (8, "八 (bā) - prosperity, wealth (sounds like 發)"),
16202 (9, "九 (jiǔ) - longevity (sounds like 久)"),
16203 (6, "六 (liù) - smooth, flowing"),
16204 (2, "二 (èr) - pairs, harmony"),
16205 (4, "四 (sì) - AVOID - sounds like death (死)"),
16206 (5, "五 (wǔ) - five elements"),
16207 (12, "十二 - zodiac animals"),
16208 ],
16209 "japanese" | "ja" => vec![
16210 (7, "七 (nana) - lucky, seven gods of fortune"),
16211 (8, "八 (hachi) - prosperity, expansion"),
16212 (3, "三 (san) - completeness"),
16213 (5, "五 (go) - five elements"),
16214 (4, "四 (shi) - AVOID - sounds like death (死)"),
16215 (9, "九 (ku) - AVOID - sounds like suffering (苦)"),
16216 ],
16217 "hebrew" | "jewish" => vec![
16218 (7, "Shabbat, creation days, menorah branches"),
16219 (18, "Chai (חי) - life - gematria of ח(8) + י(10)"),
16220 (40, "Transformation - flood, Sinai, wilderness"),
16221 (12, "Tribes of Israel"),
16222 (613, "Mitzvot - commandments"),
16223 (26, "Gematria of YHWH"),
16224 ],
16225 "islamic" | "arabic" | "ar" => vec![
16226 (5, "Pillars of Islam, daily prayers"),
16227 (7, "Heavens, circumambulation of Kaaba"),
16228 (40, "Age of prophethood, days of repentance"),
16229 (99, "Names of Allah"),
16230 (786, "Abjad value of Bismillah"),
16231 ],
16232 "hindu" | "indian" | "hi" => vec![
16233 (108, "Sacred beads, Upanishads, sun's distance"),
16234 (7, "Chakras (main), rishis, sacred rivers"),
16235 (3, "Trimurti - Brahma, Vishnu, Shiva"),
16236 (4, "Vedas, yugas, varnas"),
16237 (9, "Planets (navagraha), durga forms"),
16238 (1008, "Names of Vishnu"),
16239 ],
16240 "greek" | "pythagorean" => vec![
16241 (1, "Monad - unity, source"),
16242 (2, "Dyad - duality, diversity"),
16243 (3, "Triad - harmony, completion"),
16244 (4, "Tetrad - solidity, earth"),
16245 (7, "Heptad - perfection"),
16246 (10, "Decad - tetractys, divine"),
16247 (12, "Olympian gods"),
16248 ],
16249 "celtic" | "irish" => vec![
16250 (3, "Triple goddess, triquetra"),
16251 (5, "Elements including spirit"),
16252 (9, "Triple threes - sacred completion"),
16253 (13, "Lunar months"),
16254 (17, "St. Patrick's Day"),
16255 (20, "Vigesimal counting"),
16256 ],
16257 _ => vec![
16258 (1, "Unity"),
16259 (7, "Widely considered lucky"),
16260 (12, "Dozen - practical division"),
16261 (13, "Often considered unlucky in West"),
16262 ],
16263 };
16264
16265 let result: Vec<Value> = numbers.iter().map(|(n, meaning)| {
16266 let mut entry = std::collections::HashMap::new();
16267 entry.insert("number".to_string(), Value::Int(*n));
16268 entry.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
16269 Value::Map(Rc::new(RefCell::new(entry)))
16270 }).collect();
16271
16272 Ok(Value::Array(Rc::new(RefCell::new(result))))
16273 });
16274
16275 define(interp, "is_sacred", Some(2), |_, args| {
16277 let n = match &args[0] {
16278 Value::Int(n) => *n,
16279 _ => return Err(RuntimeError::new("is_sacred() requires integer")),
16280 };
16281 let culture = match &args[1] {
16282 Value::String(s) => s.to_lowercase(),
16283 _ => return Err(RuntimeError::new("is_sacred() requires string culture")),
16284 };
16285
16286 let sacred = match culture.as_str() {
16287 "mayan" | "maya" => vec![13, 20, 52, 260, 365, 400],
16288 "babylonian" | "mesopotamian" => vec![12, 60, 360, 3600, 7],
16289 "chinese" | "zh" => vec![8, 9, 6, 2, 5, 12],
16290 "japanese" | "ja" => vec![7, 8, 3, 5],
16291 "hebrew" | "jewish" => vec![7, 18, 40, 12, 613, 26],
16292 "islamic" | "arabic" | "ar" => vec![5, 7, 40, 99, 786],
16293 "hindu" | "indian" | "hi" => vec![108, 7, 3, 4, 9, 1008],
16294 "greek" | "pythagorean" => vec![1, 2, 3, 4, 7, 10, 12],
16295 "celtic" | "irish" => vec![3, 5, 9, 13, 17, 20],
16296 _ => vec![7, 12],
16297 };
16298
16299 Ok(Value::Bool(sacred.contains(&n)))
16300 });
16301
16302 define(interp, "is_unlucky", Some(2), |_, args| {
16304 let n = match &args[0] {
16305 Value::Int(n) => *n,
16306 _ => return Err(RuntimeError::new("is_unlucky() requires integer")),
16307 };
16308 let culture = match &args[1] {
16309 Value::String(s) => s.to_lowercase(),
16310 _ => return Err(RuntimeError::new("is_unlucky() requires string culture")),
16311 };
16312
16313 let unlucky = match culture.as_str() {
16314 "chinese" | "zh" => vec![4], "japanese" | "ja" => vec![4, 9], "western" | "en" => vec![13], "italian" | "it" => vec![17], _ => vec![],
16319 };
16320
16321 Ok(Value::Bool(unlucky.contains(&n)))
16322 });
16323
16324 define(interp, "number_meaning", Some(2), |_, args| {
16326 let n = match &args[0] {
16327 Value::Int(n) => *n,
16328 _ => return Err(RuntimeError::new("number_meaning() requires integer")),
16329 };
16330 let culture = match &args[1] {
16331 Value::String(s) => s.to_lowercase(),
16332 _ => return Err(RuntimeError::new("number_meaning() requires string culture")),
16333 };
16334
16335 let meaning = match (n, culture.as_str()) {
16336 (8, "chinese" | "zh") => "八 (bā) - Most auspicious, sounds like 發 (prosperity)",
16338 (4, "chinese" | "zh") => "四 (sì) - Unlucky, sounds like 死 (death)",
16339 (9, "chinese" | "zh") => "九 (jiǔ) - Longevity, sounds like 久 (long-lasting)",
16340 (6, "chinese" | "zh") => "六 (liù) - Smooth and well-off",
16341 (2, "chinese" | "zh") => "二 (èr) - Good things come in pairs",
16342
16343 (7, "japanese" | "ja") => "七 (nana) - Lucky, seven gods of fortune",
16345 (8, "japanese" | "ja") => "八 (hachi) - Lucky, represents spreading prosperity",
16346 (4, "japanese" | "ja") => "四 (shi) - Unlucky, homophone of death",
16347 (9, "japanese" | "ja") => "九 (ku) - Unlucky, homophone of suffering",
16348
16349 (18, "hebrew" | "jewish") => "Chai (חי) - Life itself, most auspicious",
16351 (7, "hebrew" | "jewish") => "Divine completion - Shabbat, menorah, creation",
16352 (40, "hebrew" | "jewish") => "Transformation - flood, Sinai, wilderness years",
16353 (613, "hebrew" | "jewish") => "Taryag - total number of mitzvot (commandments)",
16354
16355 (13, "mayan" | "maya") => "Sacred Tzolkin cycle, celestial layers",
16357 (20, "mayan" | "maya") => "Vigesimal base - fingers and toes, uinal",
16358 (260, "mayan" | "maya") => "Tzolkin calendar - 13 × 20 sacred days",
16359
16360 (60, "babylonian" | "mesopotamian") => "Sexagesimal base - divisible by 2,3,4,5,6,10,12,15,20,30",
16362 (360, "babylonian" | "mesopotamian") => "Circle degrees - celestial observation",
16363
16364 (108, "hindu" | "indian" | "hi") => "Sacred completeness - mala beads, Upanishads, sun ratio",
16366 (3, "hindu" | "indian" | "hi") => "Trimurti - Brahma, Vishnu, Shiva",
16367 (9, "hindu" | "indian" | "hi") => "Navagraha (9 planets), Durga's 9 forms",
16368
16369 (99, "islamic" | "arabic" | "ar") => "Asma ul-Husna - 99 names of Allah",
16371 (5, "islamic" | "arabic" | "ar") => "Five pillars of Islam",
16372 (786, "islamic" | "arabic" | "ar") => "Abjad numerology of Bismillah",
16373
16374 (10, "greek" | "pythagorean") => "Tetractys - 1+2+3+4, perfect number",
16376 (7, "greek" | "pythagorean") => "Heptad - virgin number, Athena's number",
16377
16378 _ => "No specific cultural meaning recorded",
16379 };
16380
16381 let mut result = std::collections::HashMap::new();
16382 result.insert("number".to_string(), Value::Int(n));
16383 result.insert("culture".to_string(), Value::String(Rc::new(culture)));
16384 result.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
16385
16386 Ok(Value::Map(Rc::new(RefCell::new(result))))
16387 });
16388
16389 define(interp, "to_babylonian_time", Some(1), |_, args| {
16393 let seconds = match &args[0] {
16394 Value::Int(n) => *n,
16395 Value::Float(f) => *f as i64,
16396 _ => return Err(RuntimeError::new("to_babylonian_time() requires number")),
16397 };
16398
16399 let hours = seconds / 3600;
16400 let mins = (seconds % 3600) / 60;
16401 let secs = seconds % 60;
16402
16403 Ok(Value::String(Rc::new(format!("0s[{}:{}:{}]", hours, mins, secs))))
16404 });
16405
16406 define(interp, "from_babylonian_time", Some(1), |_, args| {
16408 let s = match &args[0] {
16409 Value::String(s) => s.to_string(),
16410 _ => return Err(RuntimeError::new("from_babylonian_time() requires string")),
16411 };
16412
16413 let clean = s.trim_start_matches("0s")
16414 .trim_start_matches('[')
16415 .trim_end_matches(']');
16416
16417 let parts: Vec<i64> = clean.split(':')
16418 .map(|p| p.trim().parse::<i64>().unwrap_or(0))
16419 .collect();
16420
16421 let seconds = match parts.len() {
16422 1 => parts[0],
16423 2 => parts[0] * 60 + parts[1],
16424 3 => parts[0] * 3600 + parts[1] * 60 + parts[2],
16425 _ => return Err(RuntimeError::new("Invalid Babylonian time format")),
16426 };
16427
16428 Ok(Value::Int(seconds))
16429 });
16430
16431 define(interp, "vigesimal_shares", Some(3), |_, args| {
16435 let secret = match &args[0] {
16436 Value::String(s) => s.as_bytes().to_vec(),
16437 _ => return Err(RuntimeError::new("vigesimal_shares() requires string secret")),
16438 };
16439
16440 let threshold = match &args[1] {
16441 Value::Int(n) => *n as usize,
16442 _ => return Err(RuntimeError::new("vigesimal_shares() requires integer threshold")),
16443 };
16444
16445 let num_shares = match &args[2] {
16446 Value::Int(n) => *n as usize,
16447 _ => return Err(RuntimeError::new("vigesimal_shares() requires integer num_shares")),
16448 };
16449
16450 if threshold < 2 || num_shares < threshold || num_shares > 20 {
16451 return Err(RuntimeError::new("vigesimal_shares() requires 2 <= threshold <= num_shares <= 20 (Mayan limit)"));
16452 }
16453
16454 let mut rng = rand::thread_rng();
16456 let mut shares: Vec<Vec<u8>> = (0..num_shares).map(|_| Vec::with_capacity(secret.len() + 1)).collect();
16457
16458 for (i, share) in shares.iter_mut().enumerate() {
16459 share.push((i + 1) as u8);
16460 }
16461
16462 for &byte in &secret {
16463 let mut coefficients: Vec<u8> = vec![byte];
16464 for _ in 1..threshold {
16465 coefficients.push(rng.gen());
16466 }
16467
16468 for (i, share) in shares.iter_mut().enumerate() {
16469 let x = (i + 1) as u8;
16470 let y = eval_polynomial_gf256(&coefficients, x);
16471 share.push(y);
16472 }
16473 }
16474
16475 let share_values: Vec<Value> = shares.iter().enumerate()
16477 .map(|(i, share)| {
16478 let mut entry = std::collections::HashMap::new();
16479
16480 let mut vig_parts: Vec<String> = Vec::new();
16482 for &byte in share {
16483 vig_parts.push(to_base_string(byte as u64, 20, true));
16484 }
16485
16486 entry.insert("index".to_string(), Value::Int((i + 1) as i64));
16487 entry.insert("vigesimal".to_string(), Value::String(Rc::new(format!("0v{}", vig_parts.join(".")))));
16488 entry.insert("hex".to_string(), Value::String(Rc::new(hex::encode(share))));
16489
16490 Value::Map(Rc::new(RefCell::new(entry)))
16491 })
16492 .collect();
16493
16494 let mut result = std::collections::HashMap::new();
16495 result.insert("shares".to_string(), Value::Array(Rc::new(RefCell::new(share_values))));
16496 result.insert("threshold".to_string(), Value::Int(threshold as i64));
16497 result.insert("total".to_string(), Value::Int(num_shares as i64));
16498 result.insert("base".to_string(), Value::String(Rc::new("vigesimal (Mayan base-20)".to_string())));
16499
16500 Ok(Value::Map(Rc::new(RefCell::new(result))))
16501 });
16502
16503 define(interp, "multibase_info", Some(0), |_, _| {
16505 let mut info = std::collections::HashMap::new();
16506
16507 let bases = vec![
16508 ("binary", 2, "0b", "Modern computing"),
16509 ("octal", 8, "0o", "Unix, historical computing"),
16510 ("decimal", 10, "", "Indo-Arabic global standard"),
16511 ("duodecimal", 12, "0z", "Dozen system - time, music, measurement"),
16512 ("hexadecimal", 16, "0x", "Computing, colors, addresses"),
16513 ("vigesimal", 20, "0v", "Mayan, Celtic, Basque - human digits"),
16514 ("sexagesimal", 60, "0s", "Babylonian - time, angles, astronomy"),
16515 ];
16516
16517 let base_list: Vec<Value> = bases.iter().map(|(name, base, prefix, desc)| {
16518 let mut entry = std::collections::HashMap::new();
16519 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
16520 entry.insert("base".to_string(), Value::Int(*base as i64));
16521 entry.insert("prefix".to_string(), Value::String(Rc::new(prefix.to_string())));
16522 entry.insert("origin".to_string(), Value::String(Rc::new(desc.to_string())));
16523 Value::Map(Rc::new(RefCell::new(entry)))
16524 }).collect();
16525
16526 info.insert("numeral_systems".to_string(), Value::Array(Rc::new(RefCell::new(base_list))));
16527
16528 let encodings = vec!["base58 (Bitcoin)", "base32 (RFC 4648)", "base64 (standard)"];
16529 let enc_list: Vec<Value> = encodings.iter()
16530 .map(|s| Value::String(Rc::new(s.to_string())))
16531 .collect();
16532 info.insert("special_encodings".to_string(), Value::Array(Rc::new(RefCell::new(enc_list))));
16533
16534 let cultures = vec![
16535 "mayan", "babylonian", "chinese", "japanese", "hebrew",
16536 "islamic", "hindu", "greek", "celtic"
16537 ];
16538 let cult_list: Vec<Value> = cultures.iter()
16539 .map(|s| Value::String(Rc::new(s.to_string())))
16540 .collect();
16541 info.insert("supported_cultures".to_string(), Value::Array(Rc::new(RefCell::new(cult_list))));
16542
16543 Ok(Value::Map(Rc::new(RefCell::new(info))))
16544 });
16545}
16546
16547fn to_base_string(mut n: u64, base: u64, pad_to_two: bool) -> String {
16550 const DIGITS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
16551
16552 if n == 0 {
16553 return if pad_to_two { "00".to_string() } else { "0".to_string() };
16554 }
16555
16556 let mut result = Vec::new();
16557 while n > 0 {
16558 result.push(DIGITS[(n % base) as usize] as char);
16559 n /= base;
16560 }
16561
16562 if pad_to_two && result.len() < 2 {
16563 result.push('0');
16564 }
16565
16566 result.reverse();
16567 result.into_iter().collect()
16568}
16569
16570fn to_base_string_custom(mut n: u64, base: u64, digits: &str) -> String {
16571 if n == 0 {
16572 return digits.chars().next().unwrap().to_string();
16573 }
16574
16575 let mut result = Vec::new();
16576 let digit_chars: Vec<char> = digits.chars().collect();
16577
16578 while n > 0 {
16579 result.push(digit_chars[(n % base) as usize]);
16580 n /= base;
16581 }
16582
16583 result.reverse();
16584 result.into_iter().collect()
16585}
16586
16587fn from_base_string(s: &str, base: u64) -> Result<u64, RuntimeError> {
16588 let mut result: u64 = 0;
16589
16590 for c in s.chars() {
16591 let digit = match c {
16592 '0'..='9' => c as u64 - '0' as u64,
16593 'A'..='Z' => c as u64 - 'A' as u64 + 10,
16594 'a'..='z' => c as u64 - 'a' as u64 + 10,
16595 _ => return Err(RuntimeError::new(format!("Invalid digit: {}", c))),
16596 };
16597
16598 if digit >= base {
16599 return Err(RuntimeError::new(format!("Digit {} out of range for base {}", c, base)));
16600 }
16601
16602 result = result.checked_mul(base)
16603 .and_then(|r| r.checked_add(digit))
16604 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
16605 }
16606
16607 Ok(result)
16608}
16609
16610fn from_base_string_custom(s: &str, digits: &str) -> Result<u64, RuntimeError> {
16611 let base = digits.len() as u64;
16612 let mut result: u64 = 0;
16613
16614 for c in s.chars() {
16615 let digit = digits.find(c)
16616 .ok_or_else(|| RuntimeError::new(format!("Invalid digit: {}", c)))? as u64;
16617
16618 result = result.checked_mul(base)
16619 .and_then(|r| r.checked_add(digit))
16620 .ok_or_else(|| RuntimeError::new("Number overflow"))?;
16621 }
16622
16623 Ok(result)
16624}
16625
16626fn parse_base_prefix(s: &str, prefix: &str) -> (bool, String) {
16627 let negative = s.starts_with('-');
16628 let clean = s.trim_start_matches('-')
16629 .trim_start_matches(prefix)
16630 .to_string();
16631 (negative, clean)
16632}
16633
16634fn register_audio(interp: &mut Interpreter) {
16662 define(interp, "tune", Some(3), |_, args| {
16669 let note = match &args[0] {
16670 Value::Int(n) => *n as f64, Value::Float(f) => *f, Value::String(s) => parse_note_name(s)?,
16673 _ => return Err(RuntimeError::new("tune() requires note number or name")),
16674 };
16675
16676 let system = match &args[1] {
16677 Value::String(s) => s.to_lowercase(),
16678 _ => return Err(RuntimeError::new("tune() requires tuning system name")),
16679 };
16680
16681 let root_freq = match &args[2] {
16682 Value::Float(f) => *f,
16683 Value::Int(i) => *i as f64,
16684 _ => return Err(RuntimeError::new("tune() requires root frequency")),
16685 };
16686
16687 let freq = match system.as_str() {
16688 "12tet" | "equal" | "western" => {
16689 root_freq * 2.0_f64.powf((note - 69.0) / 12.0)
16691 }
16692 "24tet" | "quarter" | "arabic" | "maqam" => {
16693 root_freq * 2.0_f64.powf((note - 69.0) / 24.0)
16695 }
16696 "just" | "pure" => {
16697 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
16699 let octave = ((note - 69.0) / 12.0).floor();
16700 let ratio = just_intonation_ratio(interval as i32);
16701 root_freq * ratio * 2.0_f64.powf(octave)
16702 }
16703 "pythagorean" => {
16704 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
16706 let octave = ((note - 69.0) / 12.0).floor();
16707 let ratio = pythagorean_ratio(interval as i32);
16708 root_freq * ratio * 2.0_f64.powf(octave)
16709 }
16710 "meantone" | "quarter_comma" => {
16711 let interval = ((note - 69.0) % 12.0 + 12.0) % 12.0;
16713 let octave = ((note - 69.0) / 12.0).floor();
16714 let ratio = meantone_ratio(interval as i32);
16715 root_freq * ratio * 2.0_f64.powf(octave)
16716 }
16717 "53tet" | "turkish" | "persian" | "comma" => {
16718 root_freq * 2.0_f64.powf((note - 69.0) / 53.0)
16720 }
16721 "22shruti" | "shruti" | "indian" => {
16722 let shruti = (note - 69.0) % 22.0;
16724 let octave = ((note - 69.0) / 22.0).floor();
16725 let ratio = shruti_ratio(shruti as i32);
16726 root_freq * ratio * 2.0_f64.powf(octave)
16727 }
16728 "gamelan_pelog" | "pelog" => {
16729 let degree = ((note - 69.0) % 7.0 + 7.0) % 7.0;
16731 let octave = ((note - 69.0) / 7.0).floor();
16732 let ratio = pelog_ratio(degree as i32);
16733 root_freq * ratio * 2.0_f64.powf(octave)
16734 }
16735 "gamelan_slendro" | "slendro" => {
16736 let degree = ((note - 69.0) % 5.0 + 5.0) % 5.0;
16738 let octave = ((note - 69.0) / 5.0).floor();
16739 let ratio = slendro_ratio(degree as i32);
16740 root_freq * ratio * 2.0_f64.powf(octave)
16741 }
16742 "bohlen_pierce" | "bp" => {
16743 root_freq * 3.0_f64.powf((note - 69.0) / 13.0)
16745 }
16746 _ => return Err(RuntimeError::new(format!("Unknown tuning system: {}", system))),
16747 };
16748
16749 Ok(Value::Float(freq))
16750 });
16751
16752 define(interp, "tuning_info", Some(1), |_, args| {
16754 let system = match &args[0] {
16755 Value::String(s) => s.to_lowercase(),
16756 _ => return Err(RuntimeError::new("tuning_info() requires string")),
16757 };
16758
16759 let (name, notes_per_octave, origin, description) = match system.as_str() {
16760 "12tet" | "equal" | "western" => (
16761 "12-TET", 12, "Western (18th century)",
16762 "Equal temperament - every semitone is exactly 2^(1/12). Universal but slightly impure."
16763 ),
16764 "24tet" | "quarter" | "arabic" | "maqam" => (
16765 "24-TET", 24, "Arabic/Turkish",
16766 "Quarter-tone system for maqam music. Enables neutral seconds and other microtones."
16767 ),
16768 "just" | "pure" => (
16769 "Just Intonation", 12, "Ancient (Ptolemy)",
16770 "Pure frequency ratios (3:2 fifth, 5:4 third). Beatless intervals but limited modulation."
16771 ),
16772 "pythagorean" => (
16773 "Pythagorean", 12, "Ancient Greece",
16774 "Built entirely from perfect fifths (3:2). Pure fifths but harsh thirds."
16775 ),
16776 "meantone" | "quarter_comma" => (
16777 "Quarter-Comma Meantone", 12, "Renaissance Europe",
16778 "Tempered fifths for pure major thirds. Beautiful in limited keys."
16779 ),
16780 "53tet" | "turkish" | "persian" | "comma" => (
16781 "53-TET", 53, "Turkish/Persian",
16782 "53 notes per octave. Closely approximates just intonation and allows maqam/dastgah."
16783 ),
16784 "22shruti" | "shruti" | "indian" => (
16785 "22-Shruti", 22, "Indian Classical",
16786 "Ancient Indian system. Each shruti is a 'microtone' - the smallest perceptible interval."
16787 ),
16788 "gamelan_pelog" | "pelog" => (
16789 "Pelog", 7, "Javanese Gamelan",
16790 "Heptatonic scale with unequal steps. Each gamelan has unique tuning - instruments are married."
16791 ),
16792 "gamelan_slendro" | "slendro" => (
16793 "Slendro", 5, "Javanese Gamelan",
16794 "Pentatonic scale, roughly equal steps (~240 cents). Each ensemble uniquely tuned."
16795 ),
16796 "bohlen_pierce" | "bp" => (
16797 "Bohlen-Pierce", 13, "Modern (1970s)",
16798 "Non-octave scale based on 3:1 tritave. Alien but mathematically beautiful."
16799 ),
16800 _ => return Err(RuntimeError::new(format!("Unknown tuning system: {}", system))),
16801 };
16802
16803 let mut info = std::collections::HashMap::new();
16804 info.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
16805 info.insert("notes_per_octave".to_string(), Value::Int(notes_per_octave));
16806 info.insert("origin".to_string(), Value::String(Rc::new(origin.to_string())));
16807 info.insert("description".to_string(), Value::String(Rc::new(description.to_string())));
16808
16809 Ok(Value::Map(Rc::new(RefCell::new(info))))
16810 });
16811
16812 define(interp, "list_tuning_systems", Some(0), |_, _| {
16814 let systems = vec![
16815 ("12tet", "Western equal temperament", 12),
16816 ("24tet", "Arabic/Turkish quarter-tones", 24),
16817 ("just", "Pure ratio just intonation", 12),
16818 ("pythagorean", "Ancient Greek pure fifths", 12),
16819 ("meantone", "Renaissance quarter-comma", 12),
16820 ("53tet", "Turkish/Persian comma system", 53),
16821 ("22shruti", "Indian microtonal", 22),
16822 ("pelog", "Javanese gamelan 7-note", 7),
16823 ("slendro", "Javanese gamelan 5-note", 5),
16824 ("bohlen_pierce", "Non-octave tritave scale", 13),
16825 ];
16826
16827 let result: Vec<Value> = systems.iter().map(|(name, desc, notes)| {
16828 let mut entry = std::collections::HashMap::new();
16829 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
16830 entry.insert("description".to_string(), Value::String(Rc::new(desc.to_string())));
16831 entry.insert("notes_per_octave".to_string(), Value::Int(*notes));
16832 Value::Map(Rc::new(RefCell::new(entry)))
16833 }).collect();
16834
16835 Ok(Value::Array(Rc::new(RefCell::new(result))))
16836 });
16837
16838 define(interp, "sacred_freq", Some(1), |_, args| {
16844 let name = match &args[0] {
16845 Value::String(s) => s.to_lowercase(),
16846 _ => return Err(RuntimeError::new("sacred_freq() requires string")),
16847 };
16848
16849 let (freq, description) = match name.as_str() {
16850 "om" | "ॐ" | "aum" => (136.1, "Om - Earth year frequency (Cosmic Om)"),
16852 "earth_day" => (194.18, "Earth day - one rotation"),
16853 "earth_year" => (136.1, "Earth year - one orbit (Om frequency)"),
16854 "schumann" | "earth_resonance" => (7.83, "Schumann resonance - Earth's electromagnetic heartbeat"),
16855
16856 "ut" | "do" | "396" => (396.0, "UT/DO - Liberating guilt and fear"),
16858 "re" | "417" => (417.0, "RE - Undoing situations, facilitating change"),
16859 "mi" | "528" => (528.0, "MI - Transformation, miracles, DNA repair"),
16860 "fa" | "639" => (639.0, "FA - Connecting relationships"),
16861 "sol" | "741" => (741.0, "SOL - Awakening intuition"),
16862 "la" | "852" => (852.0, "LA - Returning to spiritual order"),
16863 "si" | "963" => (963.0, "SI - Divine consciousness, enlightenment"),
16864 "174" => (174.0, "Solfeggio foundation - pain relief"),
16865 "285" => (285.0, "Solfeggio - healing tissue"),
16866
16867 "sun" | "☉" => (126.22, "Sun - ego, vitality, leadership"),
16869 "moon" | "☽" | "☾" => (210.42, "Moon - emotion, intuition, cycles"),
16870 "mercury" | "☿" => (141.27, "Mercury - communication, intellect"),
16871 "venus" | "♀" => (221.23, "Venus - love, beauty, harmony"),
16872 "mars" | "♂" => (144.72, "Mars - energy, action, courage"),
16873 "jupiter" | "♃" => (183.58, "Jupiter - expansion, wisdom, luck"),
16874 "saturn" | "♄" => (147.85, "Saturn - discipline, structure, time"),
16875
16876 "root" | "muladhara" => (256.0, "Root chakra - survival, grounding (C)"),
16878 "sacral" | "svadhisthana" => (288.0, "Sacral chakra - creativity, sexuality (D)"),
16879 "solar" | "manipura" => (320.0, "Solar plexus - will, power (E)"),
16880 "heart" | "anahata" => (341.3, "Heart chakra - love, compassion (F)"),
16881 "throat" | "vishuddha" => (384.0, "Throat chakra - expression, truth (G)"),
16882 "third_eye" | "ajna" => (426.7, "Third eye - intuition, insight (A)"),
16883 "crown" | "sahasrara" => (480.0, "Crown chakra - consciousness, unity (B)"),
16884
16885 "a440" | "iso" => (440.0, "ISO standard concert pitch (1955)"),
16887 "a432" | "verdi" => (432.0, "Verdi pitch - 'mathematically consistent with universe'"),
16888 "a415" | "baroque" => (415.0, "Baroque pitch - period instrument standard"),
16889 "a466" | "chorton" => (466.0, "Choir pitch - high Baroque German"),
16890
16891 "delta" => (2.0, "Delta waves - deep sleep, healing (0.5-4 Hz)"),
16893 "theta" => (6.0, "Theta waves - meditation, creativity (4-8 Hz)"),
16894 "alpha" => (10.0, "Alpha waves - relaxation, calm focus (8-13 Hz)"),
16895 "beta" => (20.0, "Beta waves - alertness, concentration (13-30 Hz)"),
16896 "gamma" => (40.0, "Gamma waves - insight, peak performance (30-100 Hz)"),
16897
16898 _ => return Err(RuntimeError::new(format!("Unknown sacred frequency: {}", name))),
16899 };
16900
16901 let mut result = std::collections::HashMap::new();
16902 result.insert("frequency".to_string(), Value::Float(freq));
16903 result.insert("name".to_string(), Value::String(Rc::new(name)));
16904 result.insert("meaning".to_string(), Value::String(Rc::new(description.to_string())));
16905
16906 Ok(Value::Map(Rc::new(RefCell::new(result))))
16907 });
16908
16909 define(interp, "solfeggio", Some(0), |_, _| {
16911 let frequencies = vec![
16912 (174.0, "Foundation", "Pain relief, security"),
16913 (285.0, "Quantum", "Healing tissue, safety"),
16914 (396.0, "UT", "Liberating guilt and fear"),
16915 (417.0, "RE", "Undoing situations, change"),
16916 (528.0, "MI", "Transformation, DNA repair, miracles"),
16917 (639.0, "FA", "Connecting relationships"),
16918 (741.0, "SOL", "Awakening intuition"),
16919 (852.0, "LA", "Spiritual order"),
16920 (963.0, "SI", "Divine consciousness"),
16921 ];
16922
16923 let result: Vec<Value> = frequencies.iter().map(|(freq, name, meaning)| {
16924 let mut entry = std::collections::HashMap::new();
16925 entry.insert("frequency".to_string(), Value::Float(*freq));
16926 entry.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
16927 entry.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
16928 Value::Map(Rc::new(RefCell::new(entry)))
16929 }).collect();
16930
16931 Ok(Value::Array(Rc::new(RefCell::new(result))))
16932 });
16933
16934 define(interp, "chakras", Some(0), |_, _| {
16936 let chakras = vec![
16937 (256.0, "Muladhara", "Root", "Red", "Survival, grounding, stability"),
16938 (288.0, "Svadhisthana", "Sacral", "Orange", "Creativity, sexuality, emotion"),
16939 (320.0, "Manipura", "Solar Plexus", "Yellow", "Will, power, self-esteem"),
16940 (341.3, "Anahata", "Heart", "Green", "Love, compassion, connection"),
16941 (384.0, "Vishuddha", "Throat", "Blue", "Expression, truth, communication"),
16942 (426.7, "Ajna", "Third Eye", "Indigo", "Intuition, insight, wisdom"),
16943 (480.0, "Sahasrara", "Crown", "Violet", "Consciousness, unity, transcendence"),
16944 ];
16945
16946 let result: Vec<Value> = chakras.iter().map(|(freq, sanskrit, english, color, meaning)| {
16947 let mut entry = std::collections::HashMap::new();
16948 entry.insert("frequency".to_string(), Value::Float(*freq));
16949 entry.insert("sanskrit".to_string(), Value::String(Rc::new(sanskrit.to_string())));
16950 entry.insert("english".to_string(), Value::String(Rc::new(english.to_string())));
16951 entry.insert("color".to_string(), Value::String(Rc::new(color.to_string())));
16952 entry.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
16953 Value::Map(Rc::new(RefCell::new(entry)))
16954 }).collect();
16955
16956 Ok(Value::Array(Rc::new(RefCell::new(result))))
16957 });
16958
16959 define(interp, "sine", Some(3), |_, args| {
16967 generate_waveform(&args, |phase| phase.sin())
16968 });
16969
16970 define(interp, "square", Some(3), |_, args| {
16972 generate_waveform(&args, |phase| if phase.sin() >= 0.0 { 1.0 } else { -1.0 })
16973 });
16974
16975 define(interp, "sawtooth", Some(3), |_, args| {
16977 generate_waveform(&args, |phase| {
16978 let normalized = (phase / std::f64::consts::TAU).fract();
16979 2.0 * normalized - 1.0
16980 })
16981 });
16982
16983 define(interp, "triangle", Some(3), |_, args| {
16985 generate_waveform(&args, |phase| {
16986 let normalized = (phase / std::f64::consts::TAU).fract();
16987 if normalized < 0.5 {
16988 4.0 * normalized - 1.0
16989 } else {
16990 3.0 - 4.0 * normalized
16991 }
16992 })
16993 });
16994
16995 define(interp, "noise", Some(1), |_, args| {
16997 let samples = match &args[0] {
16998 Value::Int(n) => *n as usize,
16999 _ => return Err(RuntimeError::new("noise() requires integer sample count")),
17000 };
17001
17002 let mut rng = rand::thread_rng();
17003 let result: Vec<Value> = (0..samples)
17004 .map(|_| Value::Float(rng.gen::<f64>() * 2.0 - 1.0))
17005 .collect();
17006
17007 Ok(Value::Array(Rc::new(RefCell::new(result))))
17008 });
17009
17010 define(interp, "scale", Some(1), |_, args| {
17016 let name = match &args[0] {
17017 Value::String(s) => s.to_lowercase(),
17018 _ => return Err(RuntimeError::new("scale() requires string")),
17019 };
17020
17021 let (intervals, origin, description) = match name.as_str() {
17022 "major" | "ionian" => (vec![0, 2, 4, 5, 7, 9, 11], "Western", "Happy, bright, resolved"),
17024 "minor" | "aeolian" => (vec![0, 2, 3, 5, 7, 8, 10], "Western", "Sad, dark, introspective"),
17025 "dorian" => (vec![0, 2, 3, 5, 7, 9, 10], "Western/Jazz", "Minor with bright 6th"),
17026 "phrygian" => (vec![0, 1, 3, 5, 7, 8, 10], "Western/Flamenco", "Spanish, exotic, tense"),
17027 "lydian" => (vec![0, 2, 4, 6, 7, 9, 11], "Western", "Dreamy, floating, ethereal"),
17028 "mixolydian" => (vec![0, 2, 4, 5, 7, 9, 10], "Western/Blues", "Bluesy major"),
17029 "locrian" => (vec![0, 1, 3, 5, 6, 8, 10], "Western", "Unstable, dissonant"),
17030
17031 "pentatonic_major" => (vec![0, 2, 4, 7, 9], "Universal", "No dissonance, universal"),
17033 "pentatonic_minor" => (vec![0, 3, 5, 7, 10], "Universal", "Blues foundation"),
17034
17035 "hirajoshi" => (vec![0, 2, 3, 7, 8], "Japanese", "Melancholic, mysterious"),
17037 "insen" => (vec![0, 1, 5, 7, 10], "Japanese", "Dark, zen, contemplative"),
17038 "iwato" => (vec![0, 1, 5, 6, 10], "Japanese", "Most dark and dissonant"),
17039 "kumoi" => (vec![0, 2, 3, 7, 9], "Japanese", "Gentle, peaceful"),
17040 "yo" => (vec![0, 2, 5, 7, 9], "Japanese", "Bright, folk, celebratory"),
17041
17042 "hijaz" => (vec![0, 1, 4, 5, 7, 8, 11], "Arabic", "Exotic, Middle Eastern"),
17044 "bayati" => (vec![0, 1.5 as i32, 3, 5, 7, 8, 10], "Arabic", "Quarter-tone, soulful"),
17045 "rast" => (vec![0, 2, 3.5 as i32, 5, 7, 9, 10.5 as i32], "Arabic", "Foundation maqam"),
17046 "saba" => (vec![0, 1.5 as i32, 3, 4, 5, 8, 10], "Arabic", "Sad, spiritual"),
17047
17048 "bhairav" => (vec![0, 1, 4, 5, 7, 8, 11], "Indian", "Morning raga, devotional"),
17050 "yaman" | "kalyan" => (vec![0, 2, 4, 6, 7, 9, 11], "Indian", "Evening, romantic"),
17051 "bhairavi" => (vec![0, 1, 3, 5, 7, 8, 10], "Indian", "Concluding raga, devotional"),
17052 "todi" => (vec![0, 1, 3, 6, 7, 8, 11], "Indian", "Serious, pathos"),
17053 "marwa" => (vec![0, 1, 4, 6, 7, 9, 11], "Indian", "Evening, longing"),
17054
17055 "blues" => (vec![0, 3, 5, 6, 7, 10], "American", "Blue notes, soul"),
17057
17058 "hungarian_minor" => (vec![0, 2, 3, 6, 7, 8, 11], "Hungarian", "Gypsy, dramatic"),
17060 "romanian" => (vec![0, 2, 3, 6, 7, 9, 10], "Romanian", "Folk, energetic"),
17061
17062 "ahava_raba" | "freygish" => (vec![0, 1, 4, 5, 7, 8, 10], "Jewish/Klezmer", "Cantorial, emotional"),
17064 "mi_sheberach" => (vec![0, 2, 3, 6, 7, 9, 10], "Jewish", "Prayer mode"),
17065
17066 "gong" => (vec![0, 2, 4, 7, 9], "Chinese", "Palace mode, major-like"),
17068 "shang" => (vec![0, 2, 5, 7, 9], "Chinese", "Merchant mode"),
17069 "jue" => (vec![0, 3, 5, 7, 10], "Chinese", "Angle mode"),
17070 "zhi" => (vec![0, 2, 5, 7, 10], "Chinese", "Emblem mode"),
17071 "yu" => (vec![0, 3, 5, 8, 10], "Chinese", "Wings mode, minor-like"),
17072
17073 "pelog" => (vec![0, 1, 3, 7, 8], "Javanese", "7-note unequal temperament"),
17075 "slendro" => (vec![0, 2, 5, 7, 9], "Javanese", "5-note roughly equal"),
17076
17077 "whole_tone" => (vec![0, 2, 4, 6, 8, 10], "Impressionist", "Dreamlike, no resolution"),
17079 "chromatic" => (vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], "Western", "All 12 notes"),
17080 "diminished" => (vec![0, 2, 3, 5, 6, 8, 9, 11], "Jazz", "Symmetric, tense"),
17081 "augmented" => (vec![0, 3, 4, 7, 8, 11], "Jazz", "Symmetric, floating"),
17082
17083 _ => return Err(RuntimeError::new(format!("Unknown scale: {}", name))),
17084 };
17085
17086 let mut result = std::collections::HashMap::new();
17087 let intervals_values: Vec<Value> = intervals.iter().map(|&i| Value::Int(i as i64)).collect();
17088 result.insert("intervals".to_string(), Value::Array(Rc::new(RefCell::new(intervals_values))));
17089 result.insert("origin".to_string(), Value::String(Rc::new(origin.to_string())));
17090 result.insert("character".to_string(), Value::String(Rc::new(description.to_string())));
17091 result.insert("name".to_string(), Value::String(Rc::new(name)));
17092
17093 Ok(Value::Map(Rc::new(RefCell::new(result))))
17094 });
17095
17096 define(interp, "list_scales", Some(0), |_, _| {
17098 let mut cultures = std::collections::HashMap::new();
17099
17100 cultures.insert("western".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17101 Value::String(Rc::new("major".to_string())),
17102 Value::String(Rc::new("minor".to_string())),
17103 Value::String(Rc::new("dorian".to_string())),
17104 Value::String(Rc::new("phrygian".to_string())),
17105 Value::String(Rc::new("lydian".to_string())),
17106 Value::String(Rc::new("mixolydian".to_string())),
17107 Value::String(Rc::new("locrian".to_string())),
17108 ]))));
17109
17110 cultures.insert("japanese".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17111 Value::String(Rc::new("hirajoshi".to_string())),
17112 Value::String(Rc::new("insen".to_string())),
17113 Value::String(Rc::new("iwato".to_string())),
17114 Value::String(Rc::new("kumoi".to_string())),
17115 Value::String(Rc::new("yo".to_string())),
17116 ]))));
17117
17118 cultures.insert("arabic".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17119 Value::String(Rc::new("hijaz".to_string())),
17120 Value::String(Rc::new("bayati".to_string())),
17121 Value::String(Rc::new("rast".to_string())),
17122 Value::String(Rc::new("saba".to_string())),
17123 ]))));
17124
17125 cultures.insert("indian".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17126 Value::String(Rc::new("bhairav".to_string())),
17127 Value::String(Rc::new("yaman".to_string())),
17128 Value::String(Rc::new("bhairavi".to_string())),
17129 Value::String(Rc::new("todi".to_string())),
17130 Value::String(Rc::new("marwa".to_string())),
17131 ]))));
17132
17133 cultures.insert("chinese".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17134 Value::String(Rc::new("gong".to_string())),
17135 Value::String(Rc::new("shang".to_string())),
17136 Value::String(Rc::new("jue".to_string())),
17137 Value::String(Rc::new("zhi".to_string())),
17138 Value::String(Rc::new("yu".to_string())),
17139 ]))));
17140
17141 cultures.insert("jewish".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17142 Value::String(Rc::new("ahava_raba".to_string())),
17143 Value::String(Rc::new("mi_sheberach".to_string())),
17144 ]))));
17145
17146 cultures.insert("indonesian".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17147 Value::String(Rc::new("pelog".to_string())),
17148 Value::String(Rc::new("slendro".to_string())),
17149 ]))));
17150
17151 Ok(Value::Map(Rc::new(RefCell::new(cultures))))
17152 });
17153
17154 define(interp, "interval_ratio", Some(2), |_, args| {
17160 let semitones = match &args[0] {
17161 Value::Int(n) => *n as f64,
17162 Value::Float(f) => *f,
17163 _ => return Err(RuntimeError::new("interval_ratio() requires number")),
17164 };
17165
17166 let tuning = match &args[1] {
17167 Value::String(s) => s.to_lowercase(),
17168 _ => return Err(RuntimeError::new("interval_ratio() requires tuning system")),
17169 };
17170
17171 let ratio = match tuning.as_str() {
17172 "12tet" | "equal" => 2.0_f64.powf(semitones / 12.0),
17173 "just" => just_intonation_ratio(semitones as i32),
17174 "pythagorean" => pythagorean_ratio(semitones as i32),
17175 _ => 2.0_f64.powf(semitones / 12.0),
17176 };
17177
17178 Ok(Value::Float(ratio))
17179 });
17180
17181 define(interp, "cents_between", Some(2), |_, args| {
17183 let f1 = match &args[0] {
17184 Value::Float(f) => *f,
17185 Value::Int(i) => *i as f64,
17186 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
17187 };
17188 let f2 = match &args[1] {
17189 Value::Float(f) => *f,
17190 Value::Int(i) => *i as f64,
17191 _ => return Err(RuntimeError::new("cents_between() requires numbers")),
17192 };
17193
17194 let cents = 1200.0 * (f2 / f1).log2();
17195 Ok(Value::Float(cents))
17196 });
17197
17198 define(interp, "harmonic_series", Some(2), |_, args| {
17200 let fundamental = match &args[0] {
17201 Value::Float(f) => *f,
17202 Value::Int(i) => *i as f64,
17203 _ => return Err(RuntimeError::new("harmonic_series() requires frequency")),
17204 };
17205 let count = match &args[1] {
17206 Value::Int(n) => *n as usize,
17207 _ => return Err(RuntimeError::new("harmonic_series() requires count")),
17208 };
17209
17210 let harmonics: Vec<Value> = (1..=count)
17211 .map(|n| {
17212 let mut entry = std::collections::HashMap::new();
17213 entry.insert("harmonic".to_string(), Value::Int(n as i64));
17214 entry.insert("frequency".to_string(), Value::Float(fundamental * n as f64));
17215 entry.insert("cents_from_root".to_string(), Value::Float(1200.0 * (n as f64).log2()));
17216 Value::Map(Rc::new(RefCell::new(entry)))
17217 })
17218 .collect();
17219
17220 Ok(Value::Array(Rc::new(RefCell::new(harmonics))))
17221 });
17222
17223 define(interp, "audio_info", Some(0), |_, _| {
17228 let mut info = std::collections::HashMap::new();
17229
17230 info.insert("tuning_systems".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17231 Value::String(Rc::new("12tet, 24tet, just, pythagorean, meantone".to_string())),
17232 Value::String(Rc::new("53tet, 22shruti, pelog, slendro, bohlen_pierce".to_string())),
17233 ]))));
17234
17235 info.insert("waveforms".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17236 Value::String(Rc::new("sine (∿)".to_string())),
17237 Value::String(Rc::new("square (⊓)".to_string())),
17238 Value::String(Rc::new("sawtooth (⋀)".to_string())),
17239 Value::String(Rc::new("triangle (△)".to_string())),
17240 Value::String(Rc::new("noise".to_string())),
17241 ]))));
17242
17243 info.insert("sacred_frequencies".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17244 Value::String(Rc::new("om, solfeggio, chakras, planets".to_string())),
17245 Value::String(Rc::new("schumann, brainwaves (delta/theta/alpha/beta/gamma)".to_string())),
17246 ]))));
17247
17248 info.insert("scale_cultures".to_string(), Value::Array(Rc::new(RefCell::new(vec![
17249 Value::String(Rc::new("western, japanese, arabic, indian".to_string())),
17250 Value::String(Rc::new("chinese, jewish, indonesian".to_string())),
17251 ]))));
17252
17253 Ok(Value::Map(Rc::new(RefCell::new(info))))
17254 });
17255}
17256
17257fn parse_note_name(s: &str) -> Result<f64, RuntimeError> {
17260 let s = s.trim().to_uppercase();
17261 let (note, octave_offset) = if s.ends_with(|c: char| c.is_ascii_digit()) {
17262 let octave: i32 = s.chars().last().unwrap().to_digit(10).unwrap() as i32;
17263 let note_part = &s[..s.len()-1];
17264 (note_part, (octave - 4) * 12) } else {
17266 (&s[..], 0)
17267 };
17268
17269 let semitone = match note {
17270 "C" => 0, "C#" | "DB" => 1, "D" => 2, "D#" | "EB" => 3,
17271 "E" => 4, "F" => 5, "F#" | "GB" => 6, "G" => 7,
17272 "G#" | "AB" => 8, "A" => 9, "A#" | "BB" => 10, "B" => 11,
17273 _ => return Err(RuntimeError::new(format!("Unknown note: {}", s))),
17274 };
17275
17276 Ok(69.0 + semitone as f64 - 9.0 + octave_offset as f64) }
17278
17279fn just_intonation_ratio(semitones: i32) -> f64 {
17280 match semitones.rem_euclid(12) {
17282 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,
17295 }
17296}
17297
17298fn pythagorean_ratio(semitones: i32) -> f64 {
17299 match semitones.rem_euclid(12) {
17301 0 => 1.0,
17302 1 => 256.0 / 243.0,
17303 2 => 9.0 / 8.0,
17304 3 => 32.0 / 27.0,
17305 4 => 81.0 / 64.0,
17306 5 => 4.0 / 3.0,
17307 6 => 729.0 / 512.0,
17308 7 => 3.0 / 2.0,
17309 8 => 128.0 / 81.0,
17310 9 => 27.0 / 16.0,
17311 10 => 16.0 / 9.0,
17312 11 => 243.0 / 128.0,
17313 _ => 1.0,
17314 }
17315}
17316
17317fn meantone_ratio(semitones: i32) -> f64 {
17318 let fifth = 5.0_f64.powf(0.25); match semitones.rem_euclid(12) {
17321 0 => 1.0,
17322 1 => 8.0 / (fifth.powi(5)),
17323 2 => fifth.powi(2) / 2.0,
17324 3 => 4.0 / (fifth.powi(3)),
17325 4 => fifth.powi(4) / 4.0,
17326 5 => 2.0 / fifth,
17327 6 => fifth.powi(6) / 8.0,
17328 7 => fifth,
17329 8 => 8.0 / (fifth.powi(4)),
17330 9 => fifth.powi(3) / 2.0,
17331 10 => 4.0 / (fifth.powi(2)),
17332 11 => fifth.powi(5) / 4.0,
17333 _ => 1.0,
17334 }
17335}
17336
17337fn shruti_ratio(shruti: i32) -> f64 {
17338 let ratios = [
17340 1.0, 256.0/243.0, 16.0/15.0, 10.0/9.0, 9.0/8.0, 32.0/27.0, 6.0/5.0,
17341 5.0/4.0, 81.0/64.0, 4.0/3.0, 27.0/20.0, 45.0/32.0, 729.0/512.0, 3.0/2.0,
17342 128.0/81.0, 8.0/5.0, 5.0/3.0, 27.0/16.0, 16.0/9.0, 9.0/5.0, 15.0/8.0, 243.0/128.0
17343 ];
17344 ratios[shruti.rem_euclid(22) as usize]
17345}
17346
17347fn pelog_ratio(degree: i32) -> f64 {
17348 let ratios = [1.0, 1.12, 1.26, 1.5, 1.68, 1.89, 2.12];
17350 ratios[degree.rem_euclid(7) as usize]
17351}
17352
17353fn slendro_ratio(degree: i32) -> f64 {
17354 let ratios = [1.0, 1.148, 1.318, 1.516, 1.741];
17356 ratios[degree.rem_euclid(5) as usize]
17357}
17358
17359fn generate_waveform(args: &[Value], wave_fn: fn(f64) -> f64) -> Result<Value, RuntimeError> {
17360 let freq = match &args[0] {
17361 Value::Float(f) => *f,
17362 Value::Int(i) => *i as f64,
17363 _ => return Err(RuntimeError::new("Waveform requires frequency")),
17364 };
17365 let sample_rate = match &args[1] {
17366 Value::Float(f) => *f as usize,
17367 Value::Int(i) => *i as usize,
17368 _ => return Err(RuntimeError::new("Waveform requires sample rate")),
17369 };
17370 let duration = match &args[2] {
17371 Value::Float(f) => *f,
17372 Value::Int(i) => *i as f64,
17373 _ => return Err(RuntimeError::new("Waveform requires duration")),
17374 };
17375
17376 let num_samples = (sample_rate as f64 * duration) as usize;
17377 let samples: Vec<Value> = (0..num_samples)
17378 .map(|i| {
17379 let t = i as f64 / sample_rate as f64;
17380 let phase = 2.0 * std::f64::consts::PI * freq * t;
17381 Value::Float(wave_fn(phase))
17382 })
17383 .collect();
17384
17385 Ok(Value::Array(Rc::new(RefCell::new(samples))))
17386}
17387
17388fn register_spirituality(interp: &mut Interpreter) {
17410 const TRIGRAMS: [(&str, &str, &str, &str, &str); 8] = [
17416 ("☰", "乾", "Heaven", "Creative", "strong, initiating, persisting"),
17417 ("☱", "兌", "Lake", "Joyous", "pleasure, satisfaction, openness"),
17418 ("☲", "離", "Fire", "Clinging", "clarity, awareness, dependence"),
17419 ("☳", "震", "Thunder", "Arousing", "movement, initiative, action"),
17420 ("☴", "巽", "Wind", "Gentle", "penetrating, following, flexible"),
17421 ("☵", "坎", "Water", "Abysmal", "danger, flowing, depth"),
17422 ("☶", "艮", "Mountain", "Keeping Still", "stopping, resting, meditation"),
17423 ("☷", "坤", "Earth", "Receptive", "yielding, nurturing, devoted"),
17424 ];
17425
17426 define(interp, "trigram", Some(1), |_, args| {
17428 let input = match &args[0] {
17429 Value::Int(n) => (*n as usize).min(7),
17430 Value::String(s) => {
17431 match s.as_str() {
17432 "☰" | "heaven" | "qian" | "乾" => 0,
17433 "☱" | "lake" | "dui" | "兌" => 1,
17434 "☲" | "fire" | "li" | "離" => 2,
17435 "☳" | "thunder" | "zhen" | "震" => 3,
17436 "☴" | "wind" | "xun" | "巽" => 4,
17437 "☵" | "water" | "kan" | "坎" => 5,
17438 "☶" | "mountain" | "gen" | "艮" => 6,
17439 "☷" | "earth" | "kun" | "坤" => 7,
17440 _ => return Err(RuntimeError::new(format!("Unknown trigram: {}", s))),
17441 }
17442 }
17443 _ => return Err(RuntimeError::new("trigram() requires number or name")),
17444 };
17445
17446 let (symbol, chinese, english, name, meaning) = TRIGRAMS[input];
17447
17448 let mut result = std::collections::HashMap::new();
17449 result.insert("number".to_string(), Value::Int(input as i64));
17450 result.insert("symbol".to_string(), Value::String(Rc::new(symbol.to_string())));
17451 result.insert("chinese".to_string(), Value::String(Rc::new(chinese.to_string())));
17452 result.insert("english".to_string(), Value::String(Rc::new(english.to_string())));
17453 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
17454 result.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
17455
17456 let binary = match input {
17458 0 => "111", 1 => "110", 2 => "101", 3 => "100", 4 => "011", 5 => "010", 6 => "001", 7 => "000", _ => "000",
17467 };
17468 result.insert("binary".to_string(), Value::String(Rc::new(binary.to_string())));
17469
17470 Ok(Value::Map(Rc::new(RefCell::new(result))))
17471 });
17472
17473 define(interp, "hexagram", Some(1), |_, args| {
17475 let num = match &args[0] {
17476 Value::Int(n) => ((*n - 1) as usize).min(63),
17477 _ => return Err(RuntimeError::new("hexagram() requires number 1-64")),
17478 };
17479
17480 let hex = &HEXAGRAMS[num];
17481
17482 let mut result = std::collections::HashMap::new();
17483 result.insert("number".to_string(), Value::Int((num + 1) as i64));
17484 result.insert("chinese".to_string(), Value::String(Rc::new(hex.0.to_string())));
17485 result.insert("pinyin".to_string(), Value::String(Rc::new(hex.1.to_string())));
17486 result.insert("english".to_string(), Value::String(Rc::new(hex.2.to_string())));
17487 result.insert("judgment".to_string(), Value::String(Rc::new(hex.3.to_string())));
17488 result.insert("upper_trigram".to_string(), Value::String(Rc::new(hex.4.to_string())));
17489 result.insert("lower_trigram".to_string(), Value::String(Rc::new(hex.5.to_string())));
17490
17491 Ok(Value::Map(Rc::new(RefCell::new(result))))
17492 });
17493
17494 define(interp, "cast_iching", Some(0), |_, _| {
17496 let mut rng = rand::thread_rng();
17497
17498 let mut lines = Vec::new();
17501 let mut hexagram_num = 0u8;
17502 let mut changing_lines = Vec::new();
17503
17504 for i in 0..6 {
17505 let r: f64 = rng.gen();
17507 let line = if r < 0.0625 { 6 } else if r < 0.3125 { 7 } else if r < 0.5625 { 8 } else { 9 }; let is_yang = line == 7 || line == 9;
17513 if is_yang {
17514 hexagram_num |= 1 << i;
17515 }
17516
17517 if line == 6 || line == 9 {
17518 changing_lines.push(i + 1);
17519 }
17520
17521 lines.push(Value::Int(line));
17522 }
17523
17524 let king_wen_num = binary_to_king_wen(hexagram_num) + 1;
17526 let hex = &HEXAGRAMS[(king_wen_num - 1) as usize];
17527
17528 let mut result = std::collections::HashMap::new();
17529 result.insert("hexagram".to_string(), Value::Int(king_wen_num as i64));
17530 result.insert("chinese".to_string(), Value::String(Rc::new(hex.0.to_string())));
17531 result.insert("english".to_string(), Value::String(Rc::new(hex.2.to_string())));
17532 result.insert("judgment".to_string(), Value::String(Rc::new(hex.3.to_string())));
17533 result.insert("lines".to_string(), Value::Array(Rc::new(RefCell::new(lines))));
17534
17535 let changing: Vec<Value> = changing_lines.iter().map(|&n| Value::Int(n)).collect();
17536 result.insert("changing_lines".to_string(), Value::Array(Rc::new(RefCell::new(changing))));
17537
17538 if !changing_lines.is_empty() {
17540 let mut result_hex = hexagram_num;
17541 for &line in &changing_lines {
17542 result_hex ^= 1 << (line - 1); }
17544 let result_king_wen = binary_to_king_wen(result_hex) + 1;
17545 result.insert("transforms_to".to_string(), Value::Int(result_king_wen as i64));
17546 }
17547
17548 Ok(Value::Map(Rc::new(RefCell::new(result))))
17549 });
17550
17551 define(interp, "phi", Some(0), |_, _| {
17557 Ok(Value::Float((1.0 + 5.0_f64.sqrt()) / 2.0))
17558 });
17559
17560 define(interp, "sacred_ratio", Some(1), |_, args| {
17562 let name = match &args[0] {
17563 Value::String(s) => s.to_lowercase(),
17564 _ => return Err(RuntimeError::new("sacred_ratio() requires string")),
17565 };
17566
17567 let (value, symbol, meaning) = match name.as_str() {
17568 "phi" | "φ" | "golden" => (
17569 (1.0 + 5.0_f64.sqrt()) / 2.0,
17570 "φ",
17571 "Golden Ratio - divine proportion found in nature, art, architecture"
17572 ),
17573 "phi_conjugate" | "1/phi" => (
17574 2.0 / (1.0 + 5.0_f64.sqrt()),
17575 "1/φ",
17576 "Golden Ratio conjugate - φ - 1 = 1/φ"
17577 ),
17578 "phi_squared" | "phi2" => (
17579 ((1.0 + 5.0_f64.sqrt()) / 2.0).powi(2),
17580 "φ²",
17581 "Golden Ratio squared - φ + 1 = φ²"
17582 ),
17583 "sqrt_phi" => (
17584 ((1.0 + 5.0_f64.sqrt()) / 2.0).sqrt(),
17585 "√φ",
17586 "Square root of Golden Ratio"
17587 ),
17588 "pi" | "π" => (
17589 std::f64::consts::PI,
17590 "π",
17591 "Circle constant - circumference/diameter, transcendental"
17592 ),
17593 "tau" | "τ" => (
17594 std::f64::consts::TAU,
17595 "τ",
17596 "Full circle constant - 2π, one complete revolution"
17597 ),
17598 "e" | "euler" => (
17599 std::f64::consts::E,
17600 "e",
17601 "Euler's number - natural growth, compound interest"
17602 ),
17603 "sqrt2" | "√2" | "pythagoras" => (
17604 std::f64::consts::SQRT_2,
17605 "√2",
17606 "Pythagorean constant - diagonal of unit square"
17607 ),
17608 "sqrt3" | "√3" | "vesica" => (
17609 3.0_f64.sqrt(),
17610 "√3",
17611 "Vesica Piscis ratio - sacred geometry foundation"
17612 ),
17613 "sqrt5" | "√5" => (
17614 5.0_f64.sqrt(),
17615 "√5",
17616 "Related to Golden Ratio: φ = (1 + √5) / 2"
17617 ),
17618 "silver" | "δs" => (
17619 1.0 + 2.0_f64.sqrt(),
17620 "δs",
17621 "Silver Ratio - related to octagon"
17622 ),
17623 "plastic" | "ρ" => (
17624 1.324717957244746,
17625 "ρ",
17626 "Plastic Number - smallest Pisot number"
17627 ),
17628 "feigenbaum" | "δ" => (
17629 4.669201609102990,
17630 "δ",
17631 "Feigenbaum constant - chaos theory, period doubling"
17632 ),
17633 _ => return Err(RuntimeError::new(format!("Unknown sacred ratio: {}", name))),
17634 };
17635
17636 let mut result = std::collections::HashMap::new();
17637 result.insert("value".to_string(), Value::Float(value));
17638 result.insert("symbol".to_string(), Value::String(Rc::new(symbol.to_string())));
17639 result.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
17640
17641 Ok(Value::Map(Rc::new(RefCell::new(result))))
17642 });
17643
17644 define(interp, "fibonacci", Some(1), |_, args| {
17646 let count = match &args[0] {
17647 Value::Int(n) => *n as usize,
17648 _ => return Err(RuntimeError::new("fibonacci() requires count")),
17649 };
17650
17651 let mut seq = Vec::with_capacity(count);
17652 let (mut a, mut b) = (0i64, 1i64);
17653
17654 for _ in 0..count {
17655 seq.push(Value::Int(a));
17656 let next = a.saturating_add(b);
17657 a = b;
17658 b = next;
17659 }
17660
17661 Ok(Value::Array(Rc::new(RefCell::new(seq))))
17662 });
17663
17664 define(interp, "is_fibonacci", Some(1), |_, args| {
17666 let n = match &args[0] {
17667 Value::Int(n) => *n,
17668 _ => return Err(RuntimeError::new("is_fibonacci() requires integer")),
17669 };
17670
17671 fn is_perfect_square(n: i64) -> bool {
17673 if n < 0 { return false; }
17674 let root = (n as f64).sqrt() as i64;
17675 root * root == n
17676 }
17677
17678 let n_sq = n.saturating_mul(n);
17679 let test1 = 5i64.saturating_mul(n_sq).saturating_add(4);
17680 let test2 = 5i64.saturating_mul(n_sq).saturating_sub(4);
17681
17682 Ok(Value::Bool(is_perfect_square(test1) || is_perfect_square(test2)))
17683 });
17684
17685 define(interp, "platonic_solid", Some(1), |_, args| {
17687 let name = match &args[0] {
17688 Value::String(s) => s.to_lowercase(),
17689 _ => return Err(RuntimeError::new("platonic_solid() requires string")),
17690 };
17691
17692 let (faces, vertices, edges, face_shape, element, meaning) = match name.as_str() {
17693 "tetrahedron" | "fire" => (4, 4, 6, "triangle", "Fire", "Sharpness, heat, transformation"),
17694 "cube" | "hexahedron" | "earth" => (6, 8, 12, "square", "Earth", "Stability, grounding, material"),
17695 "octahedron" | "air" => (8, 6, 12, "triangle", "Air", "Balance, intellect, communication"),
17696 "dodecahedron" | "aether" | "spirit" => (12, 20, 30, "pentagon", "Aether/Spirit", "The cosmos, divine thought"),
17697 "icosahedron" | "water" => (20, 12, 30, "triangle", "Water", "Flow, emotion, adaptability"),
17698 _ => return Err(RuntimeError::new(format!("Unknown Platonic solid: {}", name))),
17699 };
17700
17701 let mut result = std::collections::HashMap::new();
17702 result.insert("name".to_string(), Value::String(Rc::new(name)));
17703 result.insert("faces".to_string(), Value::Int(faces));
17704 result.insert("vertices".to_string(), Value::Int(vertices));
17705 result.insert("edges".to_string(), Value::Int(edges));
17706 result.insert("face_shape".to_string(), Value::String(Rc::new(face_shape.to_string())));
17707 result.insert("element".to_string(), Value::String(Rc::new(element.to_string())));
17708 result.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
17709
17710 result.insert("euler_characteristic".to_string(), Value::Int(2));
17712
17713 Ok(Value::Map(Rc::new(RefCell::new(result))))
17714 });
17715
17716 define(interp, "gematria", Some(2), |_, args| {
17722 let text = match &args[0] {
17723 Value::String(s) => s.to_string(),
17724 _ => return Err(RuntimeError::new("gematria() requires string")),
17725 };
17726
17727 let system = match &args[1] {
17728 Value::String(s) => s.to_lowercase(),
17729 _ => return Err(RuntimeError::new("gematria() requires system name")),
17730 };
17731
17732 let total: i64 = match system.as_str() {
17733 "hebrew" | "kabbalah" => {
17734 text.chars().map(|c| hebrew_gematria(c)).sum()
17735 }
17736 "greek" | "isopsephy" => {
17737 text.chars().map(|c| greek_isopsephy(c)).sum()
17738 }
17739 "arabic" | "abjad" => {
17740 text.chars().map(|c| arabic_abjad(c)).sum()
17741 }
17742 "english" | "simple" => {
17743 text.to_uppercase().chars().filter_map(|c| {
17745 if c.is_ascii_alphabetic() {
17746 Some((c as i64) - ('A' as i64) + 1)
17747 } else {
17748 None
17749 }
17750 }).sum()
17751 }
17752 "english_ordinal" => {
17753 text.to_uppercase().chars().filter_map(|c| {
17755 if c.is_ascii_alphabetic() {
17756 Some((c as i64) - ('A' as i64) + 1)
17757 } else {
17758 None
17759 }
17760 }).sum()
17761 }
17762 "english_reduction" => {
17763 text.to_uppercase().chars().filter_map(|c| {
17765 if c.is_ascii_alphabetic() {
17766 let val = ((c as i64) - ('A' as i64)) % 9 + 1;
17767 Some(val)
17768 } else {
17769 None
17770 }
17771 }).sum()
17772 }
17773 _ => return Err(RuntimeError::new(format!("Unknown gematria system: {}", system))),
17774 };
17775
17776 let mut result = std::collections::HashMap::new();
17777 result.insert("text".to_string(), Value::String(Rc::new(text)));
17778 result.insert("system".to_string(), Value::String(Rc::new(system)));
17779 result.insert("value".to_string(), Value::Int(total));
17780
17781 let mut digital_root = total;
17783 while digital_root > 9 {
17784 digital_root = digital_root.to_string().chars()
17785 .filter_map(|c| c.to_digit(10))
17786 .map(|d| d as i64)
17787 .sum();
17788 }
17789 result.insert("digital_root".to_string(), Value::Int(digital_root));
17790
17791 Ok(Value::Map(Rc::new(RefCell::new(result))))
17792 });
17793
17794 define(interp, "gematria_match", Some(2), |_, args| {
17796 let value = match &args[0] {
17797 Value::Int(n) => *n,
17798 _ => return Err(RuntimeError::new("gematria_match() requires integer value")),
17799 };
17800
17801 let system = match &args[1] {
17802 Value::String(s) => s.to_lowercase(),
17803 _ => return Err(RuntimeError::new("gematria_match() requires system name")),
17804 };
17805
17806 let matches = match (value, system.as_str()) {
17808 (26, "hebrew") => vec!["יהוה (YHWH - Tetragrammaton)"],
17809 (18, "hebrew") => vec!["חי (Chai - Life)"],
17810 (86, "hebrew") => vec!["אלהים (Elohim - God)"],
17811 (72, "hebrew") => vec!["חסד (Chesed - Loving-kindness)"],
17812 (93, "english") => vec!["Love", "Will", "Thelema"],
17813 (666, "greek") => vec!["Nero Caesar (in Hebrew letters)"],
17814 (888, "greek") => vec!["Ἰησοῦς (Jesus)"],
17815 _ => vec![],
17816 };
17817
17818 let match_values: Vec<Value> = matches.iter()
17819 .map(|s| Value::String(Rc::new(s.to_string())))
17820 .collect();
17821
17822 Ok(Value::Array(Rc::new(RefCell::new(match_values))))
17823 });
17824
17825 define(interp, "archetype", Some(1), |_, args| {
17831 let name = match &args[0] {
17832 Value::String(s) => s.to_lowercase(),
17833 _ => return Err(RuntimeError::new("archetype() requires string")),
17834 };
17835
17836 let (description, shadow, gift, challenge) = match name.as_str() {
17837 "self" => (
17839 "The unified conscious and unconscious, the goal of individuation",
17840 "Inflation or deflation of ego",
17841 "Wholeness, integration, meaning",
17842 "Integrating all aspects of psyche"
17843 ),
17844 "shadow" => (
17845 "The unconscious aspect containing repressed weaknesses and instincts",
17846 "Projection onto others, denial",
17847 "Creativity, spontaneity, insight",
17848 "Acknowledging and integrating darkness"
17849 ),
17850 "anima" => (
17851 "The feminine inner personality in a man's unconscious",
17852 "Moodiness, seduction, possession",
17853 "Relatedness, creativity, soul connection",
17854 "Developing emotional intelligence"
17855 ),
17856 "animus" => (
17857 "The masculine inner personality in a woman's unconscious",
17858 "Brutality, reckless action, opinionation",
17859 "Courage, initiative, spiritual depth",
17860 "Developing assertiveness with wisdom"
17861 ),
17862 "persona" => (
17863 "The social mask, the face we present to the world",
17864 "Over-identification, inauthenticity",
17865 "Social adaptation, professional competence",
17866 "Maintaining authenticity within role"
17867 ),
17868
17869 "hero" => (
17871 "The courageous one who overcomes obstacles and achieves great deeds",
17872 "Arrogance, ruthlessness, eternal battle",
17873 "Courage, perseverance, accomplishment",
17874 "Knowing when to fight and when to surrender"
17875 ),
17876 "sage" | "wise_old_man" => (
17877 "The wise figure who offers guidance and insight",
17878 "Dogmatism, disconnection, ivory tower",
17879 "Wisdom, knowledge, truth-seeking",
17880 "Applying wisdom practically"
17881 ),
17882 "magician" | "wizard" => (
17883 "The transformer who makes things happen through understanding laws",
17884 "Manipulation, disconnection from ethics",
17885 "Transformation, vision, manifestation",
17886 "Using power responsibly"
17887 ),
17888 "lover" => (
17889 "The one who pursues connection, beauty, and passion",
17890 "Obsession, jealousy, loss of identity",
17891 "Passion, commitment, appreciation",
17892 "Maintaining boundaries while connecting deeply"
17893 ),
17894 "caregiver" | "mother" => (
17895 "The nurturing one who protects and provides",
17896 "Martyrdom, enabling, smothering",
17897 "Compassion, generosity, nurturing",
17898 "Caring for self while caring for others"
17899 ),
17900 "ruler" | "king" | "queen" => (
17901 "The one who takes responsibility for the realm",
17902 "Tyranny, authoritarianism, being overthrown",
17903 "Order, leadership, prosperity",
17904 "Serving the greater good, not just power"
17905 ),
17906 "creator" | "artist" => (
17907 "The one who brings new things into being",
17908 "Perfectionism, self-indulgence, drama",
17909 "Creativity, imagination, expression",
17910 "Completing projects, accepting imperfection"
17911 ),
17912 "innocent" | "child" => (
17913 "The pure one with faith and optimism",
17914 "Naivety, denial, dependence",
17915 "Faith, optimism, loyalty",
17916 "Growing without becoming cynical"
17917 ),
17918 "explorer" | "seeker" => (
17919 "The one who seeks new experiences and self-discovery",
17920 "Aimless wandering, inability to commit",
17921 "Autonomy, ambition, authenticity",
17922 "Finding what you seek"
17923 ),
17924 "rebel" | "outlaw" => (
17925 "The one who breaks rules and challenges the status quo",
17926 "Crime, self-destruction, alienation",
17927 "Liberation, revolution, radical freedom",
17928 "Channeling rebellion constructively"
17929 ),
17930 "jester" | "fool" | "trickster" => (
17931 "The one who uses humor and playfulness",
17932 "Cruelty, debauchery, irresponsibility",
17933 "Joy, freedom, living in the moment",
17934 "Knowing when to be serious"
17935 ),
17936 "everyman" | "orphan" => (
17937 "The regular person who wants belonging",
17938 "Victim mentality, losing self in group",
17939 "Realism, empathy, connection",
17940 "Standing out when necessary"
17941 ),
17942 _ => return Err(RuntimeError::new(format!("Unknown archetype: {}", name))),
17943 };
17944
17945 let mut result = std::collections::HashMap::new();
17946 result.insert("name".to_string(), Value::String(Rc::new(name)));
17947 result.insert("description".to_string(), Value::String(Rc::new(description.to_string())));
17948 result.insert("shadow".to_string(), Value::String(Rc::new(shadow.to_string())));
17949 result.insert("gift".to_string(), Value::String(Rc::new(gift.to_string())));
17950 result.insert("challenge".to_string(), Value::String(Rc::new(challenge.to_string())));
17951
17952 Ok(Value::Map(Rc::new(RefCell::new(result))))
17953 });
17954
17955 define(interp, "zodiac", Some(1), |_, args| {
17961 let input = match &args[0] {
17962 Value::Int(n) => (*n as usize - 1).min(11),
17963 Value::String(s) => {
17964 match s.to_lowercase().as_str() {
17965 "aries" | "♈" => 0,
17966 "taurus" | "♉" => 1,
17967 "gemini" | "♊" => 2,
17968 "cancer" | "♋" => 3,
17969 "leo" | "♌" => 4,
17970 "virgo" | "♍" => 5,
17971 "libra" | "♎" => 6,
17972 "scorpio" | "♏" => 7,
17973 "sagittarius" | "♐" => 8,
17974 "capricorn" | "♑" => 9,
17975 "aquarius" | "♒" => 10,
17976 "pisces" | "♓" => 11,
17977 _ => return Err(RuntimeError::new(format!("Unknown sign: {}", s))),
17978 }
17979 }
17980 _ => return Err(RuntimeError::new("zodiac() requires number or name")),
17981 };
17982
17983 let signs = [
17984 ("♈", "Aries", "Fire", "Cardinal", "Mars", "I Am", "Mar 21 - Apr 19"),
17985 ("♉", "Taurus", "Earth", "Fixed", "Venus", "I Have", "Apr 20 - May 20"),
17986 ("♊", "Gemini", "Air", "Mutable", "Mercury", "I Think", "May 21 - Jun 20"),
17987 ("♋", "Cancer", "Water", "Cardinal", "Moon", "I Feel", "Jun 21 - Jul 22"),
17988 ("♌", "Leo", "Fire", "Fixed", "Sun", "I Will", "Jul 23 - Aug 22"),
17989 ("♍", "Virgo", "Earth", "Mutable", "Mercury", "I Analyze", "Aug 23 - Sep 22"),
17990 ("♎", "Libra", "Air", "Cardinal", "Venus", "I Balance", "Sep 23 - Oct 22"),
17991 ("♏", "Scorpio", "Water", "Fixed", "Pluto/Mars", "I Transform", "Oct 23 - Nov 21"),
17992 ("♐", "Sagittarius", "Fire", "Mutable", "Jupiter", "I Seek", "Nov 22 - Dec 21"),
17993 ("♑", "Capricorn", "Earth", "Cardinal", "Saturn", "I Use", "Dec 22 - Jan 19"),
17994 ("♒", "Aquarius", "Air", "Fixed", "Uranus/Saturn", "I Know", "Jan 20 - Feb 18"),
17995 ("♓", "Pisces", "Water", "Mutable", "Neptune/Jupiter", "I Believe", "Feb 19 - Mar 20"),
17996 ];
17997
17998 let (symbol, name, element, modality, ruler, motto, dates) = signs[input];
17999
18000 let mut result = std::collections::HashMap::new();
18001 result.insert("number".to_string(), Value::Int((input + 1) as i64));
18002 result.insert("symbol".to_string(), Value::String(Rc::new(symbol.to_string())));
18003 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18004 result.insert("element".to_string(), Value::String(Rc::new(element.to_string())));
18005 result.insert("modality".to_string(), Value::String(Rc::new(modality.to_string())));
18006 result.insert("ruler".to_string(), Value::String(Rc::new(ruler.to_string())));
18007 result.insert("motto".to_string(), Value::String(Rc::new(motto.to_string())));
18008 result.insert("dates".to_string(), Value::String(Rc::new(dates.to_string())));
18009
18010 Ok(Value::Map(Rc::new(RefCell::new(result))))
18011 });
18012
18013 define(interp, "tarot_major", Some(1), |_, args| {
18015 let num = match &args[0] {
18016 Value::Int(n) => (*n as usize).min(21),
18017 Value::String(s) => {
18018 match s.to_lowercase().as_str() {
18019 "fool" => 0, "magician" => 1, "high_priestess" | "priestess" => 2,
18020 "empress" => 3, "emperor" => 4, "hierophant" | "pope" => 5,
18021 "lovers" => 6, "chariot" => 7, "strength" => 8,
18022 "hermit" => 9, "wheel" | "fortune" => 10, "justice" => 11,
18023 "hanged_man" | "hanged" => 12, "death" => 13, "temperance" => 14,
18024 "devil" => 15, "tower" => 16, "star" => 17,
18025 "moon" => 18, "sun" => 19, "judgement" | "judgment" => 20, "world" => 21,
18026 _ => return Err(RuntimeError::new(format!("Unknown card: {}", s))),
18027 }
18028 }
18029 _ => return Err(RuntimeError::new("tarot_major() requires number or name")),
18030 };
18031
18032 let cards = [
18033 ("The Fool", "New beginnings, innocence, spontaneity", "Naivety, recklessness, risk-taking"),
18034 ("The Magician", "Willpower, creation, manifestation", "Manipulation, trickery, unused talent"),
18035 ("The High Priestess", "Intuition, mystery, inner knowledge", "Secrets, withdrawal, silence"),
18036 ("The Empress", "Abundance, nurturing, fertility", "Dependence, smothering, emptiness"),
18037 ("The Emperor", "Authority, structure, control", "Tyranny, rigidity, coldness"),
18038 ("The Hierophant", "Tradition, conformity, spirituality", "Dogma, restriction, challenging status quo"),
18039 ("The Lovers", "Love, harmony, relationships, choices", "Disharmony, imbalance, misalignment"),
18040 ("The Chariot", "Direction, willpower, victory", "Aggression, lack of direction, obstacles"),
18041 ("Strength", "Courage, patience, inner power", "Self-doubt, weakness, insecurity"),
18042 ("The Hermit", "Contemplation, search for truth, inner guidance", "Isolation, loneliness, withdrawal"),
18043 ("Wheel of Fortune", "Change, cycles, fate, destiny", "Resistance to change, bad luck, setbacks"),
18044 ("Justice", "Truth, fairness, law, cause and effect", "Unfairness, dishonesty, lack of accountability"),
18045 ("The Hanged Man", "Surrender, letting go, new perspective", "Stalling, resistance, indecision"),
18046 ("Death", "Endings, transformation, transition", "Fear of change, stagnation, decay"),
18047 ("Temperance", "Balance, moderation, patience", "Imbalance, excess, lack of purpose"),
18048 ("The Devil", "Bondage, materialism, shadow self", "Freedom, release, exploring dark side"),
18049 ("The Tower", "Sudden change, upheaval, revelation", "Disaster averted, fear of change, prolonged pain"),
18050 ("The Star", "Hope, faith, renewal, inspiration", "Despair, disconnection, lack of faith"),
18051 ("The Moon", "Illusion, intuition, the unconscious", "Fear, confusion, misinterpretation"),
18052 ("The Sun", "Joy, success, vitality, positivity", "Negativity, depression, sadness"),
18053 ("Judgement", "Rebirth, inner calling, absolution", "Self-doubt, refusal of self-examination"),
18054 ("The World", "Completion, accomplishment, wholeness", "Incompletion, lack of closure, emptiness"),
18055 ];
18056
18057 let (name, upright, reversed) = cards[num];
18058
18059 let mut result = std::collections::HashMap::new();
18060 result.insert("number".to_string(), Value::Int(num as i64));
18061 result.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18062 result.insert("upright".to_string(), Value::String(Rc::new(upright.to_string())));
18063 result.insert("reversed".to_string(), Value::String(Rc::new(reversed.to_string())));
18064
18065 Ok(Value::Map(Rc::new(RefCell::new(result))))
18066 });
18067
18068 define(interp, "draw_tarot", Some(0), |_, _| {
18070 let mut rng = rand::thread_rng();
18071 let card: usize = rng.gen_range(0..22);
18072 let reversed: bool = rng.gen();
18073
18074 let cards = [
18075 "The Fool", "The Magician", "The High Priestess", "The Empress",
18076 "The Emperor", "The Hierophant", "The Lovers", "The Chariot",
18077 "Strength", "The Hermit", "Wheel of Fortune", "Justice",
18078 "The Hanged Man", "Death", "Temperance", "The Devil",
18079 "The Tower", "The Star", "The Moon", "The Sun",
18080 "Judgement", "The World"
18081 ];
18082
18083 let mut result = std::collections::HashMap::new();
18084 result.insert("number".to_string(), Value::Int(card as i64));
18085 result.insert("name".to_string(), Value::String(Rc::new(cards[card].to_string())));
18086 result.insert("reversed".to_string(), Value::Bool(reversed));
18087 result.insert("orientation".to_string(), Value::String(Rc::new(
18088 if reversed { "reversed" } else { "upright" }.to_string()
18089 )));
18090
18091 Ok(Value::Map(Rc::new(RefCell::new(result))))
18092 });
18093
18094 define(interp, "synchronicity_score", Some(2), |_, args| {
18100 let a = match &args[0] {
18102 Value::String(s) => s.to_string(),
18103 Value::Int(n) => n.to_string(),
18104 _ => return Err(RuntimeError::new("synchronicity_score() requires string or int")),
18105 };
18106
18107 let b = match &args[1] {
18108 Value::String(s) => s.to_string(),
18109 Value::Int(n) => n.to_string(),
18110 _ => return Err(RuntimeError::new("synchronicity_score() requires string or int")),
18111 };
18112
18113 let val_a: i64 = a.to_uppercase().chars().filter_map(|c| {
18115 if c.is_ascii_alphabetic() {
18116 Some((c as i64) - ('A' as i64) + 1)
18117 } else if c.is_ascii_digit() {
18118 c.to_digit(10).map(|d| d as i64)
18119 } else {
18120 None
18121 }
18122 }).sum();
18123
18124 let val_b: i64 = b.to_uppercase().chars().filter_map(|c| {
18125 if c.is_ascii_alphabetic() {
18126 Some((c as i64) - ('A' as i64) + 1)
18127 } else if c.is_ascii_digit() {
18128 c.to_digit(10).map(|d| d as i64)
18129 } else {
18130 None
18131 }
18132 }).sum();
18133
18134 let mut factors = Vec::new();
18136
18137 if val_a == val_b {
18139 factors.push("identical_gematria".to_string());
18140 }
18141
18142 if val_a > 0 && val_b > 0 && (val_a % val_b == 0 || val_b % val_a == 0) {
18144 factors.push("divisibility".to_string());
18145 }
18146
18147 let fib_set: std::collections::HashSet<i64> = [1,2,3,5,8,13,21,34,55,89,144,233,377,610,987].iter().cloned().collect();
18149 if fib_set.contains(&val_a) && fib_set.contains(&val_b) {
18150 factors.push("both_fibonacci".to_string());
18151 }
18152
18153 fn digital_root(mut n: i64) -> i64 {
18155 while n > 9 { n = n.to_string().chars().filter_map(|c| c.to_digit(10)).map(|d| d as i64).sum(); }
18156 n
18157 }
18158 if digital_root(val_a) == digital_root(val_b) {
18159 factors.push("same_digital_root".to_string());
18160 }
18161
18162 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
18164 let ratio = if val_a > 0 && val_b > 0 {
18165 (val_a as f64 / val_b as f64).max(val_b as f64 / val_a as f64)
18166 } else { 0.0 };
18167 if (ratio - phi).abs() < 0.02 || (ratio - 1.0/phi).abs() < 0.02 {
18168 factors.push("golden_ratio".to_string());
18169 }
18170
18171 let score = (factors.len() as f64 / 5.0).min(1.0);
18172
18173 let mut result = std::collections::HashMap::new();
18174 result.insert("score".to_string(), Value::Float(score));
18175 result.insert("value_a".to_string(), Value::Int(val_a));
18176 result.insert("value_b".to_string(), Value::Int(val_b));
18177 let factor_values: Vec<Value> = factors.iter().map(|s| Value::String(Rc::new(s.clone()))).collect();
18178 result.insert("factors".to_string(), Value::Array(Rc::new(RefCell::new(factor_values))));
18179
18180 Ok(Value::Map(Rc::new(RefCell::new(result))))
18181 });
18182
18183 define(interp, "spirituality_info", Some(0), |_, _| {
18185 let mut info = std::collections::HashMap::new();
18186
18187 info.insert("i_ching".to_string(), Value::String(Rc::new(
18188 "trigram(), hexagram(), cast_iching() - 64 hexagrams of change".to_string()
18189 )));
18190 info.insert("sacred_geometry".to_string(), Value::String(Rc::new(
18191 "phi(), sacred_ratio(), fibonacci(), platonic_solid()".to_string()
18192 )));
18193 info.insert("gematria".to_string(), Value::String(Rc::new(
18194 "Hebrew, Greek, Arabic, English letter-number systems".to_string()
18195 )));
18196 info.insert("archetypes".to_string(), Value::String(Rc::new(
18197 "17 Jungian archetypes with shadow and gift".to_string()
18198 )));
18199 info.insert("astrology".to_string(), Value::String(Rc::new(
18200 "zodiac() - 12 signs with elements and modalities".to_string()
18201 )));
18202 info.insert("tarot".to_string(), Value::String(Rc::new(
18203 "tarot_major(), draw_tarot() - 22 Major Arcana".to_string()
18204 )));
18205
18206 Ok(Value::Map(Rc::new(RefCell::new(info))))
18207 });
18208}
18209
18210const HEXAGRAMS: [(&str, &str, &str, &str, &str, &str); 64] = [
18212 ("乾", "Qián", "The Creative", "Sublime success through perseverance", "Heaven", "Heaven"),
18213 ("坤", "Kūn", "The Receptive", "Devoted success through the mare's perseverance", "Earth", "Earth"),
18214 ("屯", "Zhūn", "Difficulty at the Beginning", "Persevere, seek helpers, don't act alone", "Water", "Thunder"),
18215 ("蒙", "Méng", "Youthful Folly", "Success through education and guidance", "Mountain", "Water"),
18216 ("需", "Xū", "Waiting", "Sincerity brings success; cross the great water", "Water", "Heaven"),
18217 ("訟", "Sòng", "Conflict", "Seek counsel; don't cross the great water", "Heaven", "Water"),
18218 ("師", "Shī", "The Army", "Perseverance and an experienced leader bring success", "Earth", "Water"),
18219 ("比", "Bǐ", "Holding Together", "Through perseverance, those who hesitate should reflect", "Water", "Earth"),
18220 ("小畜", "Xiǎo Chù", "Small Taming", "Success; dense clouds but no rain", "Wind", "Heaven"),
18221 ("履", "Lǚ", "Treading", "Tread on the tiger's tail carefully; success", "Heaven", "Lake"),
18222 ("泰", "Tài", "Peace", "The small departs, the great approaches; success", "Earth", "Heaven"),
18223 ("否", "Pǐ", "Standstill", "The great departs, the small approaches; persevere", "Heaven", "Earth"),
18224 ("同人", "Tóng Rén", "Fellowship", "Success in the open; cross the great water", "Heaven", "Fire"),
18225 ("大有", "Dà Yǒu", "Great Possession", "Supreme success", "Fire", "Heaven"),
18226 ("謙", "Qiān", "Modesty", "Success; the superior person carries things through", "Earth", "Mountain"),
18227 ("豫", "Yù", "Enthusiasm", "Appoint helpers and set armies marching", "Thunder", "Earth"),
18228 ("隨", "Suí", "Following", "Supreme success through perseverance", "Lake", "Thunder"),
18229 ("蠱", "Gǔ", "Work on the Decayed", "Success; cross the great water; three days before and after", "Mountain", "Wind"),
18230 ("臨", "Lín", "Approach", "Great success through perseverance; misfortune in eighth month", "Earth", "Lake"),
18231 ("觀", "Guān", "Contemplation", "Ablution, but not yet sacrifice; confidence inspires", "Wind", "Earth"),
18232 ("噬嗑", "Shì Kè", "Biting Through", "Success; favorable for legal matters", "Fire", "Thunder"),
18233 ("賁", "Bì", "Grace", "Success in small matters", "Mountain", "Fire"),
18234 ("剝", "Bō", "Splitting Apart", "Unfavorable to go anywhere", "Mountain", "Earth"),
18235 ("復", "Fù", "Return", "Success; going out and coming in without error", "Earth", "Thunder"),
18236 ("無妄", "Wú Wàng", "Innocence", "Supreme success through perseverance", "Heaven", "Thunder"),
18237 ("大畜", "Dà Chù", "Great Taming", "Perseverance; eat away from home", "Mountain", "Heaven"),
18238 ("頤", "Yí", "Nourishment", "Perseverance; watch what you nurture", "Mountain", "Thunder"),
18239 ("大過", "Dà Guò", "Great Exceeding", "The ridgepole sags; favorable to have somewhere to go", "Lake", "Wind"),
18240 ("坎", "Kǎn", "The Abysmal", "Sincerity brings success of the heart", "Water", "Water"),
18241 ("離", "Lí", "The Clinging", "Perseverance; success; care for the cow", "Fire", "Fire"),
18242 ("咸", "Xián", "Influence", "Success; perseverance; taking a maiden brings fortune", "Lake", "Mountain"),
18243 ("恆", "Héng", "Duration", "Success without blame; perseverance; favorable to have somewhere to go", "Thunder", "Wind"),
18244 ("遯", "Dùn", "Retreat", "Success; small perseverance", "Heaven", "Mountain"),
18245 ("大壯", "Dà Zhuàng", "Great Power", "Perseverance", "Thunder", "Heaven"),
18246 ("晉", "Jìn", "Progress", "The powerful prince is honored with horses", "Fire", "Earth"),
18247 ("明夷", "Míng Yí", "Darkening of the Light", "Perseverance in adversity", "Earth", "Fire"),
18248 ("家人", "Jiā Rén", "The Family", "Perseverance of the woman", "Wind", "Fire"),
18249 ("睽", "Kuí", "Opposition", "Good fortune in small matters", "Fire", "Lake"),
18250 ("蹇", "Jiǎn", "Obstruction", "Southwest favorable; northeast unfavorable; see the great person", "Water", "Mountain"),
18251 ("解", "Xiè", "Deliverance", "Southwest favorable; return brings fortune; haste brings fortune", "Thunder", "Water"),
18252 ("損", "Sǔn", "Decrease", "Sincerity; supreme fortune; persistence; favorable to undertake", "Mountain", "Lake"),
18253 ("益", "Yì", "Increase", "Favorable to undertake and cross the great water", "Wind", "Thunder"),
18254 ("夬", "Guài", "Breakthrough", "Proclaim at the king's court; sincerity in danger", "Lake", "Heaven"),
18255 ("姤", "Gòu", "Coming to Meet", "The maiden is powerful; don't marry such a maiden", "Heaven", "Wind"),
18256 ("萃", "Cuì", "Gathering", "Success; the king approaches his temple; see the great person", "Lake", "Earth"),
18257 ("升", "Shēng", "Pushing Upward", "Supreme success; see the great person; don't worry", "Earth", "Wind"),
18258 ("困", "Kùn", "Oppression", "Success; perseverance of the great person; no blame", "Lake", "Water"),
18259 ("井", "Jǐng", "The Well", "The town may change but not the well", "Water", "Wind"),
18260 ("革", "Gé", "Revolution", "On your own day you are believed; great success", "Lake", "Fire"),
18261 ("鼎", "Dǐng", "The Cauldron", "Supreme good fortune; success", "Fire", "Wind"),
18262 ("震", "Zhèn", "The Arousing", "Success; thunder comes with fright; laughing and talking after", "Thunder", "Thunder"),
18263 ("艮", "Gèn", "Keeping Still", "Keep your back still; go into the courtyard without seeing anyone", "Mountain", "Mountain"),
18264 ("漸", "Jiàn", "Development", "The maiden is given in marriage; good fortune; perseverance", "Wind", "Mountain"),
18265 ("歸妹", "Guī Mèi", "The Marrying Maiden", "Undertakings bring misfortune", "Thunder", "Lake"),
18266 ("豐", "Fēng", "Abundance", "Success; the king attains it; don't worry; be like the sun at noon", "Thunder", "Fire"),
18267 ("旅", "Lǚ", "The Wanderer", "Success through smallness; perseverance brings fortune", "Fire", "Mountain"),
18268 ("巽", "Xùn", "The Gentle", "Success through small things; favorable to have somewhere to go", "Wind", "Wind"),
18269 ("兌", "Duì", "The Joyous", "Success; perseverance", "Lake", "Lake"),
18270 ("渙", "Huàn", "Dispersion", "Success; the king approaches his temple; cross the great water", "Wind", "Water"),
18271 ("節", "Jié", "Limitation", "Success; bitter limitation should not be persevered in", "Water", "Lake"),
18272 ("中孚", "Zhōng Fú", "Inner Truth", "Pigs and fishes; good fortune; cross the great water", "Wind", "Lake"),
18273 ("小過", "Xiǎo Guò", "Small Exceeding", "Success; perseverance; small things yes, great things no", "Thunder", "Mountain"),
18274 ("既濟", "Jì Jì", "After Completion", "Success in small matters; perseverance; good at start, disorder at end", "Water", "Fire"),
18275 ("未濟", "Wèi Jì", "Before Completion", "Success; the young fox almost across; tail gets wet; no goal", "Fire", "Water"),
18276];
18277
18278fn binary_to_king_wen(binary: u8) -> u8 {
18279 const KING_WEN_ORDER: [u8; 64] = [
18282 1, 43, 14, 34, 9, 5, 26, 11,
18283 10, 58, 38, 54, 61, 60, 41, 19,
18284 13, 49, 30, 55, 37, 63, 22, 36,
18285 25, 17, 21, 51, 42, 3, 27, 24,
18286 44, 28, 50, 32, 57, 48, 18, 46,
18287 6, 47, 64, 40, 59, 29, 4, 7,
18288 33, 31, 56, 62, 53, 39, 52, 15,
18289 12, 45, 35, 16, 20, 8, 23, 2,
18290 ];
18291 KING_WEN_ORDER[binary as usize] - 1
18292}
18293
18294fn hebrew_gematria(c: char) -> i64 {
18295 match c {
18296 'א' | 'A' | 'a' => 1,
18297 'ב' | 'B' | 'b' => 2,
18298 'ג' | 'G' | 'g' => 3,
18299 'ד' | 'D' | 'd' => 4,
18300 'ה' | 'H' | 'h' => 5,
18301 'ו' | 'V' | 'v' | 'W' | 'w' => 6,
18302 'ז' | 'Z' | 'z' => 7,
18303 'ח' => 8,
18304 'ט' => 9,
18305 'י' | 'Y' | 'y' | 'I' | 'i' | 'J' | 'j' => 10,
18306 'כ' | 'K' | 'k' => 20,
18307 'ל' | 'L' | 'l' => 30,
18308 'מ' | 'M' | 'm' => 40,
18309 'נ' | 'N' | 'n' => 50,
18310 'ס' | 'S' | 's' | 'X' | 'x' => 60,
18311 'ע' | 'O' | 'o' => 70,
18312 'פ' | 'P' | 'p' | 'F' | 'f' => 80,
18313 'צ' => 90,
18314 'ק' | 'Q' | 'q' => 100,
18315 'ר' | 'R' | 'r' => 200,
18316 'ש' => 300,
18317 'ת' | 'T' | 't' => 400,
18318 'ך' => 500, 'ם' => 600, 'ן' => 700, 'ף' => 800, 'ץ' | 'C' | 'c' => 900, 'E' | 'e' => 5, 'U' | 'u' => 6, _ => 0,
18326 }
18327}
18328
18329fn greek_isopsephy(c: char) -> i64 {
18330 match c {
18331 'Α' | 'α' | 'A' | 'a' => 1,
18332 'Β' | 'β' | 'B' | 'b' => 2,
18333 'Γ' | 'γ' | 'G' | 'g' => 3,
18334 'Δ' | 'δ' | 'D' | 'd' => 4,
18335 'Ε' | 'ε' | 'E' | 'e' => 5,
18336 'Ϛ' | 'ϛ' => 6, 'Ζ' | 'ζ' | 'Z' | 'z' => 7,
18338 'Η' | 'η' | 'H' | 'h' => 8,
18339 'Θ' | 'θ' => 9,
18340 'Ι' | 'ι' | 'I' | 'i' => 10,
18341 'Κ' | 'κ' | 'K' | 'k' => 20,
18342 'Λ' | 'λ' | 'L' | 'l' => 30,
18343 'Μ' | 'μ' | 'M' | 'm' => 40,
18344 'Ν' | 'ν' | 'N' | 'n' => 50,
18345 'Ξ' | 'ξ' | 'X' | 'x' => 60,
18346 'Ο' | 'ο' | 'O' | 'o' => 70,
18347 'Π' | 'π' | 'P' | 'p' => 80,
18348 'Ϙ' | 'ϙ' | 'Q' | 'q' => 90, 'Ρ' | 'ρ' | 'R' | 'r' => 100,
18350 'Σ' | 'σ' | 'ς' | 'S' | 's' => 200,
18351 'Τ' | 'τ' | 'T' | 't' => 300,
18352 'Υ' | 'υ' | 'U' | 'u' | 'Y' | 'y' => 400,
18353 'Φ' | 'φ' | 'F' | 'f' => 500,
18354 'Χ' | 'χ' | 'C' | 'c' => 600,
18355 'Ψ' | 'ψ' => 700,
18356 'Ω' | 'ω' | 'W' | 'w' => 800,
18357 'Ϡ' | 'ϡ' => 900, 'J' | 'j' => 10, 'V' | 'v' => 400, _ => 0,
18361 }
18362}
18363
18364fn arabic_abjad(c: char) -> i64 {
18365 match c {
18366 'ا' | 'أ' | 'إ' | 'آ' | 'A' | 'a' => 1,
18367 'ب' | 'B' | 'b' => 2,
18368 'ج' | 'J' | 'j' | 'G' | 'g' => 3,
18369 'د' | 'D' | 'd' => 4,
18370 'ه' | 'H' | 'h' => 5,
18371 'و' | 'W' | 'w' | 'V' | 'v' => 6,
18372 'ز' | 'Z' | 'z' => 7,
18373 'ح' => 8,
18374 'ط' => 9,
18375 'ي' | 'Y' | 'y' | 'I' | 'i' => 10,
18376 'ك' | 'K' | 'k' => 20,
18377 'ل' | 'L' | 'l' => 30,
18378 'م' | 'M' | 'm' => 40,
18379 'ن' | 'N' | 'n' => 50,
18380 'س' | 'S' | 's' => 60,
18381 'ع' | 'E' | 'e' => 70,
18382 'ف' | 'F' | 'f' => 80,
18383 'ص' => 90,
18384 'ق' | 'Q' | 'q' => 100,
18385 'ر' | 'R' | 'r' => 200,
18386 'ش' => 300,
18387 'ت' | 'T' | 't' => 400,
18388 'ث' => 500,
18389 'خ' | 'X' | 'x' => 600,
18390 'ذ' => 700,
18391 'ض' => 800,
18392 'ظ' => 900,
18393 'غ' => 1000,
18394 'C' | 'c' => 600, 'O' | 'o' => 70, 'P' | 'p' => 80, 'U' | 'u' => 6, _ => 0,
18399 }
18400}
18401
18402fn register_color(interp: &mut Interpreter) {
18409 define(interp, "rgb", Some(3), |_, args| {
18415 let r = match &args[0] { Value::Int(n) => (*n).clamp(0, 255) as u8, Value::Float(f) => (*f as i64).clamp(0, 255) as u8, _ => return Err(RuntimeError::new("rgb() requires numbers")) };
18416 let g = match &args[1] { Value::Int(n) => (*n).clamp(0, 255) as u8, Value::Float(f) => (*f as i64).clamp(0, 255) as u8, _ => return Err(RuntimeError::new("rgb() requires numbers")) };
18417 let b = match &args[2] { Value::Int(n) => (*n).clamp(0, 255) as u8, Value::Float(f) => (*f as i64).clamp(0, 255) as u8, _ => return Err(RuntimeError::new("rgb() requires numbers")) };
18418 let mut map = std::collections::HashMap::new();
18419 map.insert("r".to_string(), Value::Int(r as i64));
18420 map.insert("g".to_string(), Value::Int(g as i64));
18421 map.insert("b".to_string(), Value::Int(b as i64));
18422 map.insert("hex".to_string(), Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))));
18423 Ok(Value::Map(Rc::new(RefCell::new(map))))
18424 });
18425
18426 define(interp, "hex_to_rgb", Some(1), |_, args| {
18428 let hex = match &args[0] { Value::String(s) => s.to_string(), _ => return Err(RuntimeError::new("hex_to_rgb requires string")) };
18429 let hex = hex.trim_start_matches('#');
18430 if hex.len() != 6 { return Err(RuntimeError::new("hex_to_rgb requires 6 character hex")); }
18431 let r = u8::from_str_radix(&hex[0..2], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
18432 let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
18433 let b = u8::from_str_radix(&hex[4..6], 16).map_err(|_| RuntimeError::new("Invalid hex"))?;
18434 let mut map = std::collections::HashMap::new();
18435 map.insert("r".to_string(), Value::Int(r as i64));
18436 map.insert("g".to_string(), Value::Int(g as i64));
18437 map.insert("b".to_string(), Value::Int(b as i64));
18438 Ok(Value::Map(Rc::new(RefCell::new(map))))
18439 });
18440
18441 define(interp, "rgb_to_hsl", Some(3), |_, args| {
18443 let r = match &args[0] { Value::Int(n) => *n as f64 / 255.0, Value::Float(f) => *f / 255.0, _ => return Err(RuntimeError::new("requires numbers")) };
18444 let g = match &args[1] { Value::Int(n) => *n as f64 / 255.0, Value::Float(f) => *f / 255.0, _ => return Err(RuntimeError::new("requires numbers")) };
18445 let b = match &args[2] { Value::Int(n) => *n as f64 / 255.0, Value::Float(f) => *f / 255.0, _ => return Err(RuntimeError::new("requires numbers")) };
18446 let max = r.max(g).max(b);
18447 let min = r.min(g).min(b);
18448 let l = (max + min) / 2.0;
18449 let (h, s) = if max == min { (0.0, 0.0) } else {
18450 let d = max - min;
18451 let s = if l > 0.5 { d / (2.0 - max - min) } else { d / (max + min) };
18452 let h = if max == r { (g - b) / d + if g < b { 6.0 } else { 0.0 } }
18453 else if max == g { (b - r) / d + 2.0 } else { (r - g) / d + 4.0 };
18454 (h * 60.0, s)
18455 };
18456 let mut map = std::collections::HashMap::new();
18457 map.insert("h".to_string(), Value::Float(h));
18458 map.insert("s".to_string(), Value::Float(s));
18459 map.insert("l".to_string(), Value::Float(l));
18460 Ok(Value::Map(Rc::new(RefCell::new(map))))
18461 });
18462
18463 define(interp, "complementary", Some(3), |_, args| {
18465 let r = match &args[0] { Value::Int(n) => *n as u8, _ => return Err(RuntimeError::new("requires int")) };
18466 let g = match &args[1] { Value::Int(n) => *n as u8, _ => return Err(RuntimeError::new("requires int")) };
18467 let b = match &args[2] { Value::Int(n) => *n as u8, _ => return Err(RuntimeError::new("requires int")) };
18468 let mut map = std::collections::HashMap::new();
18469 map.insert("r".to_string(), Value::Int(255 - r as i64));
18470 map.insert("g".to_string(), Value::Int(255 - g as i64));
18471 map.insert("b".to_string(), Value::Int(255 - b as i64));
18472 Ok(Value::Map(Rc::new(RefCell::new(map))))
18473 });
18474
18475 define(interp, "wu_xing", Some(1), |_, args| {
18479 let element = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("wu_xing requires string")) };
18480 let (name, chinese, color, hex, direction, season, organ, emotion, planet, animal) = match element.as_str() {
18481 "wood" | "mu" | "木" => ("Wood", "木 (Mù)", "Green/Azure", "#228B22", "East", "Spring", "Liver", "Anger", "Jupiter", "Azure Dragon"),
18482 "fire" | "huo" | "火" => ("Fire", "火 (Huǒ)", "Red", "#FF0000", "South", "Summer", "Heart", "Joy", "Mars", "Vermilion Bird"),
18483 "earth" | "tu" | "土" => ("Earth", "土 (Tǔ)", "Yellow", "#FFDB58", "Center", "Late Summer", "Spleen", "Worry", "Saturn", "Yellow Dragon"),
18484 "metal" | "jin" | "金" => ("Metal", "金 (Jīn)", "White/Gold", "#FFD700", "West", "Autumn", "Lung", "Grief", "Venus", "White Tiger"),
18485 "water" | "shui" | "水" => ("Water", "水 (Shuǐ)", "Black/Blue", "#000080", "North", "Winter", "Kidney", "Fear", "Mercury", "Black Tortoise"),
18486 _ => return Err(RuntimeError::new("Unknown element. Use wood/fire/earth/metal/water")),
18487 };
18488 let mut map = std::collections::HashMap::new();
18489 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18490 map.insert("chinese".to_string(), Value::String(Rc::new(chinese.to_string())));
18491 map.insert("color".to_string(), Value::String(Rc::new(color.to_string())));
18492 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18493 map.insert("direction".to_string(), Value::String(Rc::new(direction.to_string())));
18494 map.insert("season".to_string(), Value::String(Rc::new(season.to_string())));
18495 map.insert("organ".to_string(), Value::String(Rc::new(organ.to_string())));
18496 map.insert("emotion".to_string(), Value::String(Rc::new(emotion.to_string())));
18497 map.insert("planet".to_string(), Value::String(Rc::new(planet.to_string())));
18498 map.insert("guardian".to_string(), Value::String(Rc::new(animal.to_string())));
18499 Ok(Value::Map(Rc::new(RefCell::new(map))))
18500 });
18501
18502 define(interp, "chakra_color", Some(1), |_, args| {
18506 let chakra = match &args[0] { Value::String(s) => s.to_lowercase(), Value::Int(n) => n.to_string(), _ => return Err(RuntimeError::new("chakra_color requires string or number")) };
18507 let (name, sanskrit, color, hex, location, freq, element, mantra) = match chakra.as_str() {
18508 "root" | "muladhara" | "1" => ("Root", "मूलाधार", "Red", "#FF0000", "Base of spine", 396.0, "Earth", "LAM"),
18509 "sacral" | "svadhisthana" | "2" => ("Sacral", "स्वाधिष्ठान", "Orange", "#FF7F00", "Below navel", 417.0, "Water", "VAM"),
18510 "solar" | "manipura" | "3" => ("Solar Plexus", "मणिपूर", "Yellow", "#FFFF00", "Stomach", 528.0, "Fire", "RAM"),
18511 "heart" | "anahata" | "4" => ("Heart", "अनाहत", "Green", "#00FF00", "Chest", 639.0, "Air", "YAM"),
18512 "throat" | "vishuddha" | "5" => ("Throat", "विशुद्ध", "Blue", "#00BFFF", "Throat", 741.0, "Ether", "HAM"),
18513 "third_eye" | "ajna" | "6" => ("Third Eye", "आज्ञा", "Indigo", "#4B0082", "Forehead", 852.0, "Light", "OM"),
18514 "crown" | "sahasrara" | "7" => ("Crown", "सहस्रार", "Violet", "#8B00FF", "Top of head", 963.0, "Thought", "Silence"),
18515 _ => return Err(RuntimeError::new("Unknown chakra. Use root/sacral/solar/heart/throat/third_eye/crown or 1-7")),
18516 };
18517 let mut map = std::collections::HashMap::new();
18518 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18519 map.insert("sanskrit".to_string(), Value::String(Rc::new(sanskrit.to_string())));
18520 map.insert("color".to_string(), Value::String(Rc::new(color.to_string())));
18521 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18522 map.insert("location".to_string(), Value::String(Rc::new(location.to_string())));
18523 map.insert("frequency_hz".to_string(), Value::Float(freq));
18524 map.insert("element".to_string(), Value::String(Rc::new(element.to_string())));
18525 map.insert("mantra".to_string(), Value::String(Rc::new(mantra.to_string())));
18526 Ok(Value::Map(Rc::new(RefCell::new(map))))
18527 });
18528
18529 define(interp, "maya_direction", Some(1), |_, args| {
18533 let dir = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18534 let (direction, yucatec, color, hex, deity, meaning) = match dir.as_str() {
18535 "east" | "lakin" => ("East", "Lak'in", "Red", "#FF0000", "Chac (Red)", "Sunrise, new beginnings"),
18536 "north" | "xaman" => ("North", "Xaman", "White", "#FFFFFF", "Chac (White)", "Ancestors, death"),
18537 "west" | "chikin" => ("West", "Chik'in", "Black", "#000000", "Chac (Black)", "Sunset, completion"),
18538 "south" | "nohol" => ("South", "Nohol", "Yellow", "#FFFF00", "Chac (Yellow)", "Maize, abundance"),
18539 "center" | "yax" => ("Center", "Yax", "Green/Blue", "#00CED1", "World Tree", "Balance"),
18540 _ => return Err(RuntimeError::new("Unknown direction. Use east/north/west/south/center")),
18541 };
18542 let mut map = std::collections::HashMap::new();
18543 map.insert("direction".to_string(), Value::String(Rc::new(direction.to_string())));
18544 map.insert("yucatec".to_string(), Value::String(Rc::new(yucatec.to_string())));
18545 map.insert("color".to_string(), Value::String(Rc::new(color.to_string())));
18546 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18547 map.insert("deity".to_string(), Value::String(Rc::new(deity.to_string())));
18548 map.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
18549 Ok(Value::Map(Rc::new(RefCell::new(map))))
18550 });
18551
18552 define(interp, "orisha_color", Some(1), |_, args| {
18556 let orisha = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18557 let (name, colors, hex, domain, day, number) = match orisha.as_str() {
18558 "obatala" | "oxala" => ("Obatalá", "White, silver", "#FFFFFF", "Creation, purity, wisdom", "Sunday", 8),
18559 "yemoja" | "yemanja" => ("Yemọja", "Blue, white", "#4169E1", "Ocean, motherhood", "Saturday", 7),
18560 "oshun" | "oxum" => ("Ọṣun", "Yellow, gold", "#FFD700", "Rivers, love, fertility", "Saturday", 5),
18561 "shango" | "xango" => ("Ṣàngó", "Red, white", "#FF0000", "Thunder, fire, justice", "Wednesday", 6),
18562 "ogun" | "ogum" => ("Ògún", "Green, black", "#006400", "Iron, war, labor", "Tuesday", 7),
18563 "oya" | "iansa" => ("Ọya", "Brown, purple", "#800020", "Wind, storms, change", "Wednesday", 9),
18564 "eshu" | "exu" => ("Èṣù", "Red, black", "#8B0000", "Crossroads, messages", "Monday", 3),
18565 _ => return Err(RuntimeError::new("Unknown Orisha. Use obatala/yemoja/oshun/shango/ogun/oya/eshu")),
18566 };
18567 let mut map = std::collections::HashMap::new();
18568 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18569 map.insert("colors".to_string(), Value::String(Rc::new(colors.to_string())));
18570 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18571 map.insert("domain".to_string(), Value::String(Rc::new(domain.to_string())));
18572 map.insert("day".to_string(), Value::String(Rc::new(day.to_string())));
18573 map.insert("number".to_string(), Value::Int(number));
18574 Ok(Value::Map(Rc::new(RefCell::new(map))))
18575 });
18576
18577 define(interp, "nihon_iro", Some(1), |_, args| {
18581 let color = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18582 let (name, japanese, hex, meaning, season) = match color.as_str() {
18583 "sakura" => ("Sakura Pink", "桜色", "#FFB7C5", "Cherry blossoms, transience", "Spring"),
18584 "fuji" => ("Wisteria", "藤色", "#C9A0DC", "Elegance, nobility", "Spring"),
18585 "moegi" => ("Young Green", "萌黄", "#AACF53", "New growth, freshness", "Spring"),
18586 "ai" => ("Indigo", "藍色", "#004D99", "Protection, depth", "All"),
18587 "akane" => ("Madder Red", "茜色", "#CF3A24", "Sunset, passion", "Autumn"),
18588 "shiro" => ("White", "白", "#FFFFFF", "Purity, death, sacred", "Winter"),
18589 "kuro" => ("Black", "黒", "#000000", "Formality, mystery", "All"),
18590 "aka" => ("Red", "赤", "#D7003A", "Life force, celebration", "All"),
18591 "murasaki" => ("Purple", "紫", "#884898", "Nobility, spirituality", "All"),
18592 _ => return Err(RuntimeError::new("Unknown color. Try: sakura/fuji/moegi/ai/akane/shiro/kuro/aka/murasaki")),
18593 };
18594 let mut map = std::collections::HashMap::new();
18595 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18596 map.insert("japanese".to_string(), Value::String(Rc::new(japanese.to_string())));
18597 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18598 map.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
18599 map.insert("season".to_string(), Value::String(Rc::new(season.to_string())));
18600 Ok(Value::Map(Rc::new(RefCell::new(map))))
18601 });
18602
18603 define(interp, "islamic_color", Some(1), |_, args| {
18607 let color = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18608 let (name, arabic, hex, meaning, usage) = match color.as_str() {
18609 "green" | "akhdar" => ("Green", "أخضر", "#00FF00", "Paradise, Prophet, life", "Mosques, Quran, flags"),
18610 "white" | "abyad" => ("White", "أبيض", "#FFFFFF", "Purity, peace, ihram", "Pilgrimage, burial"),
18611 "black" | "aswad" => ("Black", "أسود", "#000000", "Modesty, Kaaba", "Kiswah, abaya"),
18612 "gold" | "dhahabi" => ("Gold", "ذهبي", "#FFD700", "Paradise, divine light", "Calligraphy, decoration"),
18613 "blue" | "azraq" => ("Blue", "أزرق", "#0000CD", "Protection, heaven", "Tiles, evil eye"),
18614 _ => return Err(RuntimeError::new("Unknown color. Use green/white/black/gold/blue")),
18615 };
18616 let mut map = std::collections::HashMap::new();
18617 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18618 map.insert("arabic".to_string(), Value::String(Rc::new(arabic.to_string())));
18619 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18620 map.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
18621 map.insert("usage".to_string(), Value::String(Rc::new(usage.to_string())));
18622 Ok(Value::Map(Rc::new(RefCell::new(map))))
18623 });
18624
18625 define(interp, "thai_day_color", Some(1), |_, args| {
18629 let day = match &args[0] { Value::String(s) => s.to_lowercase(), Value::Int(n) => n.to_string(), _ => return Err(RuntimeError::new("requires string or number")) };
18630 let (day_name, thai, color, hex, deity) = match day.as_str() {
18631 "sunday" | "0" => ("Sunday", "วันอาทิตย์", "Red", "#FF0000", "Surya"),
18632 "monday" | "1" => ("Monday", "วันจันทร์", "Yellow", "#FFFF00", "Chandra"),
18633 "tuesday" | "2" => ("Tuesday", "วันอังคาร", "Pink", "#FFC0CB", "Mangala"),
18634 "wednesday" | "3" => ("Wednesday", "วันพุธ", "Green", "#00FF00", "Budha"),
18635 "thursday" | "4" => ("Thursday", "วันพฤหัสบดี", "Orange", "#FFA500", "Brihaspati"),
18636 "friday" | "5" => ("Friday", "วันศุกร์", "Blue", "#00BFFF", "Shukra"),
18637 "saturday" | "6" => ("Saturday", "วันเสาร์", "Purple", "#800080", "Shani"),
18638 _ => return Err(RuntimeError::new("Unknown day. Use sunday-saturday or 0-6")),
18639 };
18640 let mut map = std::collections::HashMap::new();
18641 map.insert("day".to_string(), Value::String(Rc::new(day_name.to_string())));
18642 map.insert("thai".to_string(), Value::String(Rc::new(thai.to_string())));
18643 map.insert("color".to_string(), Value::String(Rc::new(color.to_string())));
18644 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18645 map.insert("deity".to_string(), Value::String(Rc::new(deity.to_string())));
18646 Ok(Value::Map(Rc::new(RefCell::new(map))))
18647 });
18648
18649 define(interp, "aboriginal_color", Some(1), |_, args| {
18653 let color = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18654 let (name, hex, meaning, source, dreamtime) = match color.as_str() {
18655 "red" | "ochre" => ("Red Ochre", "#CC5500", "Earth, blood, ceremony", "Hematite", "Ancestral beings"),
18656 "yellow" => ("Yellow Ochre", "#D4A017", "Sun, healing", "Limonite", "Sun's journey"),
18657 "white" => ("White", "#FFFFFF", "Sky, spirits, mourning", "Kaolin", "Sky beings"),
18658 "black" => ("Black", "#000000", "Night, formality", "Charcoal", "Night, men's business"),
18659 "brown" => ("Brown", "#8B4513", "Earth, land", "Earth pigments", "Country, connection"),
18660 _ => return Err(RuntimeError::new("Unknown color. Use red/yellow/white/black/brown")),
18661 };
18662 let mut map = std::collections::HashMap::new();
18663 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18664 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18665 map.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
18666 map.insert("source".to_string(), Value::String(Rc::new(source.to_string())));
18667 map.insert("dreamtime".to_string(), Value::String(Rc::new(dreamtime.to_string())));
18668 Ok(Value::Map(Rc::new(RefCell::new(map))))
18669 });
18670
18671 define(interp, "celtic_color", Some(1), |_, args| {
18675 let color = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18676 let (name, gaelic, hex, meaning, element) = match color.as_str() {
18677 "green" => ("Green", "Glas", "#228B22", "Nature, fairies, Otherworld", "Earth"),
18678 "white" => ("White", "Bán", "#FFFFFF", "Purity, spirits", "Air"),
18679 "red" => ("Red", "Dearg", "#FF0000", "War, courage, blood", "Fire"),
18680 "black" => ("Black", "Dubh", "#000000", "Otherworld, death, rebirth", "Water"),
18681 "gold" => ("Gold", "Órga", "#FFD700", "Sun, sovereignty, Lugh", "Fire"),
18682 "silver" => ("Silver", "Airgid", "#C0C0C0", "Moon, feminine, intuition", "Water"),
18683 _ => return Err(RuntimeError::new("Unknown color. Use green/white/red/black/gold/silver")),
18684 };
18685 let mut map = std::collections::HashMap::new();
18686 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18687 map.insert("gaelic".to_string(), Value::String(Rc::new(gaelic.to_string())));
18688 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18689 map.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
18690 map.insert("element".to_string(), Value::String(Rc::new(element.to_string())));
18691 Ok(Value::Map(Rc::new(RefCell::new(map))))
18692 });
18693
18694 define(interp, "kente_color", Some(1), |_, args| {
18698 let color = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18699 let (name, twi, hex, meaning) = match color.as_str() {
18700 "gold" | "yellow" => ("Gold", "Sika Kɔkɔɔ", "#FFD700", "Royalty, wealth, glory"),
18701 "green" => ("Green", "Ahabammono", "#228B22", "Growth, renewal, harvest"),
18702 "blue" => ("Blue", "Bruu", "#0000CD", "Peace, harmony, love"),
18703 "red" => ("Red", "Kɔkɔɔ", "#FF0000", "Blood, sacrifice, power"),
18704 "black" => ("Black", "Tuntum", "#000000", "Maturation, ancestors"),
18705 "white" => ("White", "Fitaa", "#FFFFFF", "Purification, virtue, joy"),
18706 "maroon" => ("Maroon", "Borɔnɔ", "#800000", "Earth, healing, protection"),
18707 _ => return Err(RuntimeError::new("Unknown color. Use gold/green/blue/red/black/white/maroon")),
18708 };
18709 let mut map = std::collections::HashMap::new();
18710 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18711 map.insert("twi".to_string(), Value::String(Rc::new(twi.to_string())));
18712 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18713 map.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
18714 Ok(Value::Map(Rc::new(RefCell::new(map))))
18715 });
18716
18717 define(interp, "hindu_color", Some(1), |_, args| {
18721 let color = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18722 let (name, hindi, hex, meaning, deities) = match color.as_str() {
18723 "red" | "lal" => ("Red", "लाल", "#FF0000", "Purity, fertility, love", "Durga, Lakshmi"),
18724 "orange" | "saffron" => ("Saffron", "केसरी", "#FF6600", "Sacred, renunciation", "Hanuman"),
18725 "yellow" => ("Yellow", "पीला", "#FFFF00", "Knowledge, learning", "Vishnu, Saraswati"),
18726 "green" => ("Green", "हरा", "#008000", "Life, happiness", "Krishna"),
18727 "white" => ("White", "सफ़ेद", "#FFFFFF", "Purity, mourning", "Saraswati, Shiva"),
18728 "blue" => ("Blue", "नीला", "#0000FF", "Divinity, infinity", "Krishna, Vishnu"),
18729 "black" => ("Black", "काला", "#000000", "Protection from evil", "Kali, Shani"),
18730 _ => return Err(RuntimeError::new("Unknown color. Use red/orange/yellow/green/white/blue/black")),
18731 };
18732 let mut map = std::collections::HashMap::new();
18733 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18734 map.insert("hindi".to_string(), Value::String(Rc::new(hindi.to_string())));
18735 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18736 map.insert("meaning".to_string(), Value::String(Rc::new(meaning.to_string())));
18737 map.insert("deities".to_string(), Value::String(Rc::new(deities.to_string())));
18738 Ok(Value::Map(Rc::new(RefCell::new(map))))
18739 });
18740
18741 define(interp, "emotion_color", Some(2), |_, args| {
18745 let emotion = match &args[0] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18746 let culture = match &args[1] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires string")) };
18747 let (hex, name, reasoning) = match (emotion.as_str(), culture.as_str()) {
18748 ("joy", "western") | ("happy", "western") => ("#FFD700", "Gold", "Sunshine = happiness"),
18749 ("joy", "chinese") | ("happy", "chinese") => ("#FF0000", "Red", "红 = luck, joy"),
18750 ("joy", "japanese") => ("#FFB7C5", "Sakura", "Cherry blossom = fleeting joy"),
18751 ("sadness", "western") | ("sad", "western") => ("#0000CD", "Blue", "'Feeling blue'"),
18752 ("sadness", "chinese") | ("sad", "chinese") => ("#FFFFFF", "White", "白 = mourning"),
18753 ("sadness", "indian") => ("#FFFFFF", "White", "सफ़ेद = mourning"),
18754 ("anger", _) => ("#FF0000", "Red", "Universal heat/fire"),
18755 ("love", "western") => ("#FF69B4", "Pink", "Valentine's hearts"),
18756 ("love", "chinese") | ("love", "indian") => ("#FF0000", "Red", "Red = marriage, love"),
18757 ("peace", "western") => ("#ADD8E6", "Light Blue", "Sky, serenity"),
18758 ("peace", "islamic") => ("#00FF00", "Green", "السلام = paradise"),
18759 ("fear", _) => ("#4B0082", "Indigo", "Deep, mysterious"),
18760 (_, _) => ("#808080", "Grey", "Neutral"),
18761 };
18762 let r = u8::from_str_radix(&hex[1..3], 16).unwrap_or(128);
18763 let g = u8::from_str_radix(&hex[3..5], 16).unwrap_or(128);
18764 let b = u8::from_str_radix(&hex[5..7], 16).unwrap_or(128);
18765 let mut map = std::collections::HashMap::new();
18766 map.insert("hex".to_string(), Value::String(Rc::new(hex.to_string())));
18767 map.insert("name".to_string(), Value::String(Rc::new(name.to_string())));
18768 map.insert("r".to_string(), Value::Int(r as i64));
18769 map.insert("g".to_string(), Value::Int(g as i64));
18770 map.insert("b".to_string(), Value::Int(b as i64));
18771 map.insert("reasoning".to_string(), Value::String(Rc::new(reasoning.to_string())));
18772 Ok(Value::Map(Rc::new(RefCell::new(map))))
18773 });
18774
18775 define(interp, "synesthesia", Some(2), |_, args| {
18777 let culture = match &args[1] { Value::String(s) => s.to_lowercase(), _ => return Err(RuntimeError::new("requires culture string")) };
18778 let (r, g, b, emotion, freq) = match &args[0] {
18779 Value::String(s) => match s.to_lowercase().as_str() {
18780 "joy" | "happy" => (255u8, 215u8, 0u8, "joy", 528.0),
18781 "sadness" | "sad" => (0, 0, 139, "sadness", 396.0),
18782 "anger" => (255, 0, 0, "anger", 417.0),
18783 "fear" => (75, 0, 130, "fear", 369.0),
18784 "love" => (255, 105, 180, "love", 639.0),
18785 "peace" => (135, 206, 235, "peace", 741.0),
18786 _ => (128, 128, 128, "neutral", 432.0),
18787 },
18788 Value::Int(n) => (128, 128, 255, "resonance", *n as f64),
18789 Value::Float(f) => (128, 128, 255, "resonance", *f),
18790 _ => (128, 128, 128, "neutral", 432.0),
18791 };
18792 let cultural_meaning = match culture.as_str() {
18793 "chinese" if r > 200 && g < 100 => "luck/joy (红)",
18794 "japanese" if r > 200 && g < 100 => "vitality (赤)",
18795 "indian" if r > 200 && g < 100 => "shakti/auspicious",
18796 _ => "universal resonance",
18797 };
18798 let chakra = if r > 200 && g < 100 { "Root" } else if g > 200 { "Heart" } else if b > 200 { "Throat" } else { "Crown" };
18799 let wu_xing = if r > 200 && g < 100 { "Fire (火)" } else if g > 200 { "Wood (木)" } else if b > 200 { "Water (水)" } else { "Metal (金)" };
18800 let mut map = std::collections::HashMap::new();
18801 let mut color_map = std::collections::HashMap::new();
18802 color_map.insert("r".to_string(), Value::Int(r as i64));
18803 color_map.insert("g".to_string(), Value::Int(g as i64));
18804 color_map.insert("b".to_string(), Value::Int(b as i64));
18805 color_map.insert("hex".to_string(), Value::String(Rc::new(format!("#{:02X}{:02X}{:02X}", r, g, b))));
18806 map.insert("color".to_string(), Value::Map(Rc::new(RefCell::new(color_map))));
18807 map.insert("emotion".to_string(), Value::String(Rc::new(emotion.to_string())));
18808 map.insert("frequency".to_string(), Value::Float(freq));
18809 map.insert("cultural_meaning".to_string(), Value::String(Rc::new(cultural_meaning.to_string())));
18810 map.insert("chakra".to_string(), Value::String(Rc::new(chakra.to_string())));
18811 map.insert("wu_xing".to_string(), Value::String(Rc::new(wu_xing.to_string())));
18812 Ok(Value::Map(Rc::new(RefCell::new(map))))
18813 });
18814
18815 define(interp, "color_to_sound", Some(3), |_, args| {
18817 let r = match &args[0] { Value::Int(n) => *n as f64 / 255.0, Value::Float(f) => *f / 255.0, _ => return Err(RuntimeError::new("requires numbers")) };
18818 let g = match &args[1] { Value::Int(n) => *n as f64 / 255.0, Value::Float(f) => *f / 255.0, _ => return Err(RuntimeError::new("requires numbers")) };
18819 let b = match &args[2] { Value::Int(n) => *n as f64 / 255.0, Value::Float(f) => *f / 255.0, _ => return Err(RuntimeError::new("requires numbers")) };
18820 let max = r.max(g).max(b); let min = r.min(g).min(b); let l = (max + min) / 2.0;
18821 let h = if max == min { 0.0 } else {
18822 let d = max - min;
18823 if max == r { (g - b) / d + if g < b { 6.0 } else { 0.0 } }
18824 else if max == g { (b - r) / d + 2.0 } else { (r - g) / d + 4.0 }
18825 } * 60.0;
18826 let (note, freq) = if h < 30.0 { ("C", 261.63) } else if h < 60.0 { ("G", 392.00) }
18827 else if h < 90.0 { ("D", 293.66) } else if h < 120.0 { ("A", 440.00) }
18828 else if h < 150.0 { ("E", 329.63) } else if h < 180.0 { ("B", 493.88) }
18829 else if h < 210.0 { ("F#", 369.99) } else if h < 240.0 { ("Db", 277.18) }
18830 else if h < 270.0 { ("Ab", 415.30) } else if h < 300.0 { ("Eb", 311.13) }
18831 else if h < 330.0 { ("Bb", 466.16) } else { ("F", 349.23) };
18832 let octave_shift = ((l - 0.5) * 4.0).round() as i32;
18833 let adjusted_freq = freq * 2.0_f64.powi(octave_shift);
18834 let mut map = std::collections::HashMap::new();
18835 map.insert("note".to_string(), Value::String(Rc::new(note.to_string())));
18836 map.insert("frequency".to_string(), Value::Float(adjusted_freq));
18837 map.insert("hue".to_string(), Value::Float(h));
18838 Ok(Value::Map(Rc::new(RefCell::new(map))))
18839 });
18840
18841 define(interp, "contrast_ratio", Some(6), |_, args| {
18843 fn lum(r: f64, g: f64, b: f64) -> f64 {
18844 fn ch(c: f64) -> f64 { let c = c / 255.0; if c <= 0.03928 { c / 12.92 } else { ((c + 0.055) / 1.055).powf(2.4) } }
18845 0.2126 * ch(r) + 0.7152 * ch(g) + 0.0722 * ch(b)
18846 }
18847 let r1 = match &args[0] { Value::Int(n) => *n as f64, Value::Float(f) => *f, _ => return Err(RuntimeError::new("requires numbers")) };
18848 let g1 = match &args[1] { Value::Int(n) => *n as f64, Value::Float(f) => *f, _ => return Err(RuntimeError::new("requires numbers")) };
18849 let b1 = match &args[2] { Value::Int(n) => *n as f64, Value::Float(f) => *f, _ => return Err(RuntimeError::new("requires numbers")) };
18850 let r2 = match &args[3] { Value::Int(n) => *n as f64, Value::Float(f) => *f, _ => return Err(RuntimeError::new("requires numbers")) };
18851 let g2 = match &args[4] { Value::Int(n) => *n as f64, Value::Float(f) => *f, _ => return Err(RuntimeError::new("requires numbers")) };
18852 let b2 = match &args[5] { Value::Int(n) => *n as f64, Value::Float(f) => *f, _ => return Err(RuntimeError::new("requires numbers")) };
18853 let l1 = lum(r1, g1, b1); let l2 = lum(r2, g2, b2);
18854 let ratio = if l1 > l2 { (l1 + 0.05) / (l2 + 0.05) } else { (l2 + 0.05) / (l1 + 0.05) };
18855 let mut map = std::collections::HashMap::new();
18856 map.insert("ratio".to_string(), Value::Float(ratio));
18857 map.insert("aa_normal".to_string(), Value::Bool(ratio >= 4.5));
18858 map.insert("aa_large".to_string(), Value::Bool(ratio >= 3.0));
18859 map.insert("aaa_normal".to_string(), Value::Bool(ratio >= 7.0));
18860 Ok(Value::Map(Rc::new(RefCell::new(map))))
18861 });
18862}
18863
18864fn register_protocol(interp: &mut Interpreter) {
18872 define(interp, "protocol_info", Some(0), |_, _args| {
18874 let mut map = std::collections::HashMap::new();
18875 map.insert("http".to_string(), Value::Map(Rc::new(RefCell::new({
18876 let mut m = std::collections::HashMap::new();
18877 m.insert("name".to_string(), Value::String(Rc::new("HTTP".to_string())));
18878 m.insert("versions".to_string(), Value::Array(Rc::new(RefCell::new(vec![
18879 Value::String(Rc::new("1.1".to_string())),
18880 Value::String(Rc::new("2".to_string())),
18881 ]))));
18882 m.insert("methods".to_string(), Value::Array(Rc::new(RefCell::new(vec![
18883 Value::String(Rc::new("GET".to_string())),
18884 Value::String(Rc::new("POST".to_string())),
18885 Value::String(Rc::new("PUT".to_string())),
18886 Value::String(Rc::new("DELETE".to_string())),
18887 Value::String(Rc::new("PATCH".to_string())),
18888 Value::String(Rc::new("HEAD".to_string())),
18889 Value::String(Rc::new("OPTIONS".to_string())),
18890 ]))));
18891 m
18892 }))));
18893 map.insert("grpc".to_string(), Value::Map(Rc::new(RefCell::new({
18894 let mut m = std::collections::HashMap::new();
18895 m.insert("name".to_string(), Value::String(Rc::new("gRPC".to_string())));
18896 m.insert("streaming_modes".to_string(), Value::Array(Rc::new(RefCell::new(vec![
18897 Value::String(Rc::new("unary".to_string())),
18898 Value::String(Rc::new("server_streaming".to_string())),
18899 Value::String(Rc::new("client_streaming".to_string())),
18900 Value::String(Rc::new("bidirectional".to_string())),
18901 ]))));
18902 m
18903 }))));
18904 map.insert("websocket".to_string(), Value::Map(Rc::new(RefCell::new({
18905 let mut m = std::collections::HashMap::new();
18906 m.insert("name".to_string(), Value::String(Rc::new("WebSocket".to_string())));
18907 m.insert("message_types".to_string(), Value::Array(Rc::new(RefCell::new(vec![
18908 Value::String(Rc::new("text".to_string())),
18909 Value::String(Rc::new("binary".to_string())),
18910 Value::String(Rc::new("ping".to_string())),
18911 Value::String(Rc::new("pong".to_string())),
18912 Value::String(Rc::new("close".to_string())),
18913 ]))));
18914 m
18915 }))));
18916 map.insert("kafka".to_string(), Value::Map(Rc::new(RefCell::new({
18917 let mut m = std::collections::HashMap::new();
18918 m.insert("name".to_string(), Value::String(Rc::new("Apache Kafka".to_string())));
18919 m.insert("acks".to_string(), Value::Array(Rc::new(RefCell::new(vec![
18920 Value::String(Rc::new("none".to_string())),
18921 Value::String(Rc::new("leader".to_string())),
18922 Value::String(Rc::new("all".to_string())),
18923 ]))));
18924 m
18925 }))));
18926 map.insert("amqp".to_string(), Value::Map(Rc::new(RefCell::new({
18927 let mut m = std::collections::HashMap::new();
18928 m.insert("name".to_string(), Value::String(Rc::new("AMQP".to_string())));
18929 m.insert("exchange_types".to_string(), Value::Array(Rc::new(RefCell::new(vec![
18930 Value::String(Rc::new("direct".to_string())),
18931 Value::String(Rc::new("fanout".to_string())),
18932 Value::String(Rc::new("topic".to_string())),
18933 Value::String(Rc::new("headers".to_string())),
18934 ]))));
18935 m
18936 }))));
18937 map.insert("graphql".to_string(), Value::Map(Rc::new(RefCell::new({
18938 let mut m = std::collections::HashMap::new();
18939 m.insert("name".to_string(), Value::String(Rc::new("GraphQL".to_string())));
18940 m.insert("operations".to_string(), Value::Array(Rc::new(RefCell::new(vec![
18941 Value::String(Rc::new("query".to_string())),
18942 Value::String(Rc::new("mutation".to_string())),
18943 Value::String(Rc::new("subscription".to_string())),
18944 ]))));
18945 m
18946 }))));
18947 Ok(Value::Map(Rc::new(RefCell::new(map))))
18948 });
18949
18950 define(interp, "http_status_text", Some(1), |_, args| {
18952 let code = match &args[0] {
18953 Value::Int(n) => *n,
18954 _ => return Err(RuntimeError::new("http_status_text requires integer status code")),
18955 };
18956 let text = match code {
18957 100 => "Continue",
18958 101 => "Switching Protocols",
18959 200 => "OK",
18960 201 => "Created",
18961 202 => "Accepted",
18962 204 => "No Content",
18963 301 => "Moved Permanently",
18964 302 => "Found",
18965 304 => "Not Modified",
18966 307 => "Temporary Redirect",
18967 308 => "Permanent Redirect",
18968 400 => "Bad Request",
18969 401 => "Unauthorized",
18970 403 => "Forbidden",
18971 404 => "Not Found",
18972 405 => "Method Not Allowed",
18973 409 => "Conflict",
18974 422 => "Unprocessable Entity",
18975 429 => "Too Many Requests",
18976 500 => "Internal Server Error",
18977 502 => "Bad Gateway",
18978 503 => "Service Unavailable",
18979 504 => "Gateway Timeout",
18980 _ => "Unknown",
18981 };
18982 Ok(Value::String(Rc::new(text.to_string())))
18983 });
18984
18985 define(interp, "http_status_type", Some(1), |_, args| {
18987 let code = match &args[0] {
18988 Value::Int(n) => *n,
18989 _ => return Err(RuntimeError::new("http_status_type requires integer status code")),
18990 };
18991 let status_type = match code {
18992 100..=199 => "informational",
18993 200..=299 => "success",
18994 300..=399 => "redirect",
18995 400..=499 => "client_error",
18996 500..=599 => "server_error",
18997 _ => "unknown",
18998 };
18999 Ok(Value::String(Rc::new(status_type.to_string())))
19000 });
19001
19002 define(interp, "grpc_status_text", Some(1), |_, args| {
19004 let code = match &args[0] {
19005 Value::Int(n) => *n,
19006 _ => return Err(RuntimeError::new("grpc_status_text requires integer status code")),
19007 };
19008 let text = match code {
19009 0 => "OK",
19010 1 => "CANCELLED",
19011 2 => "UNKNOWN",
19012 3 => "INVALID_ARGUMENT",
19013 4 => "DEADLINE_EXCEEDED",
19014 5 => "NOT_FOUND",
19015 6 => "ALREADY_EXISTS",
19016 7 => "PERMISSION_DENIED",
19017 8 => "RESOURCE_EXHAUSTED",
19018 9 => "FAILED_PRECONDITION",
19019 10 => "ABORTED",
19020 11 => "OUT_OF_RANGE",
19021 12 => "UNIMPLEMENTED",
19022 13 => "INTERNAL",
19023 14 => "UNAVAILABLE",
19024 15 => "DATA_LOSS",
19025 16 => "UNAUTHENTICATED",
19026 _ => "UNKNOWN",
19027 };
19028 Ok(Value::String(Rc::new(text.to_string())))
19029 });
19030
19031 define(interp, "url_parse", Some(1), |_, args| {
19033 let url_str = match &args[0] {
19034 Value::String(s) => s.as_str().to_string(),
19035 _ => return Err(RuntimeError::new("url_parse requires string URL")),
19036 };
19037
19038 let mut map = std::collections::HashMap::new();
19040
19041 let (scheme, rest) = if let Some(pos) = url_str.find("://") {
19043 (url_str[..pos].to_string(), &url_str[pos + 3..])
19044 } else {
19045 return Err(RuntimeError::new("Invalid URL: missing scheme"));
19046 };
19047 map.insert("scheme".to_string(), Value::String(Rc::new(scheme)));
19048
19049 let (authority, path_and_rest) = if let Some(pos) = rest.find('/') {
19051 (&rest[..pos], &rest[pos..])
19052 } else {
19053 (rest, "/")
19054 };
19055
19056 let (host, port) = if let Some(pos) = authority.rfind(':') {
19058 if let Ok(p) = authority[pos + 1..].parse::<i64>() {
19059 (authority[..pos].to_string(), Some(p))
19060 } else {
19061 (authority.to_string(), None)
19062 }
19063 } else {
19064 (authority.to_string(), None)
19065 };
19066 map.insert("host".to_string(), Value::String(Rc::new(host)));
19067 map.insert("port".to_string(), port.map(Value::Int).unwrap_or(Value::Null));
19068
19069 let (path, query) = if let Some(pos) = path_and_rest.find('?') {
19071 (&path_and_rest[..pos], Some(&path_and_rest[pos + 1..]))
19072 } else {
19073 (path_and_rest, None)
19074 };
19075 map.insert("path".to_string(), Value::String(Rc::new(path.to_string())));
19076 map.insert("query".to_string(), query.map(|q| Value::String(Rc::new(q.to_string()))).unwrap_or(Value::Null));
19077
19078 Ok(Value::Map(Rc::new(RefCell::new(map))))
19079 });
19080
19081 define(interp, "url_encode", Some(1), |_, args| {
19083 let s = match &args[0] {
19084 Value::String(s) => s.as_str(),
19085 _ => return Err(RuntimeError::new("url_encode requires string")),
19086 };
19087 let mut result = String::new();
19088 for c in s.chars() {
19089 match c {
19090 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '~' => {
19091 result.push(c);
19092 }
19093 ' ' => result.push_str("%20"),
19094 _ => {
19095 for b in c.to_string().as_bytes() {
19096 result.push_str(&format!("%{:02X}", b));
19097 }
19098 }
19099 }
19100 }
19101 Ok(Value::String(Rc::new(result)))
19102 });
19103
19104 define(interp, "url_decode", Some(1), |_, args| {
19106 let s = match &args[0] {
19107 Value::String(s) => s.as_str().to_string(),
19108 _ => return Err(RuntimeError::new("url_decode requires string")),
19109 };
19110 let mut result = Vec::new();
19111 let bytes = s.as_bytes();
19112 let mut i = 0;
19113 while i < bytes.len() {
19114 if bytes[i] == b'%' && i + 2 < bytes.len() {
19115 if let Ok(b) = u8::from_str_radix(
19116 &String::from_utf8_lossy(&bytes[i + 1..i + 3]),
19117 16,
19118 ) {
19119 result.push(b);
19120 i += 3;
19121 continue;
19122 }
19123 } else if bytes[i] == b'+' {
19124 result.push(b' ');
19125 i += 1;
19126 continue;
19127 }
19128 result.push(bytes[i]);
19129 i += 1;
19130 }
19131 Ok(Value::String(Rc::new(String::from_utf8_lossy(&result).to_string())))
19132 });
19133
19134 define(interp, "ws_close_code_text", Some(1), |_, args| {
19136 let code = match &args[0] {
19137 Value::Int(n) => *n,
19138 _ => return Err(RuntimeError::new("ws_close_code_text requires integer code")),
19139 };
19140 let text = match code {
19141 1000 => "Normal Closure",
19142 1001 => "Going Away",
19143 1002 => "Protocol Error",
19144 1003 => "Unsupported Data",
19145 1005 => "No Status Received",
19146 1006 => "Abnormal Closure",
19147 1007 => "Invalid Payload Data",
19148 1008 => "Policy Violation",
19149 1009 => "Message Too Big",
19150 1010 => "Missing Extension",
19151 1011 => "Internal Error",
19152 1015 => "TLS Handshake Failure",
19153 _ => "Unknown",
19154 };
19155 Ok(Value::String(Rc::new(text.to_string())))
19156 });
19157
19158 define(interp, "mime_type", Some(1), |_, args| {
19160 let ext = match &args[0] {
19161 Value::String(s) => s.as_str().to_lowercase(),
19162 _ => return Err(RuntimeError::new("mime_type requires string extension")),
19163 };
19164 let ext = ext.trim_start_matches('.');
19165 let mime = match ext {
19166 "html" | "htm" => "text/html",
19167 "css" => "text/css",
19168 "js" | "mjs" => "text/javascript",
19169 "json" => "application/json",
19170 "xml" => "application/xml",
19171 "txt" => "text/plain",
19172 "csv" => "text/csv",
19173 "png" => "image/png",
19174 "jpg" | "jpeg" => "image/jpeg",
19175 "gif" => "image/gif",
19176 "svg" => "image/svg+xml",
19177 "webp" => "image/webp",
19178 "ico" => "image/x-icon",
19179 "pdf" => "application/pdf",
19180 "zip" => "application/zip",
19181 "gz" | "gzip" => "application/gzip",
19182 "mp3" => "audio/mpeg",
19183 "mp4" => "video/mp4",
19184 "webm" => "video/webm",
19185 "woff" => "font/woff",
19186 "woff2" => "font/woff2",
19187 "ttf" => "font/ttf",
19188 "otf" => "font/otf",
19189 "wasm" => "application/wasm",
19190 "proto" => "application/protobuf",
19191 _ => "application/octet-stream",
19192 };
19193 Ok(Value::String(Rc::new(mime.to_string())))
19194 });
19195
19196 define(interp, "content_type_parse", Some(1), |_, args| {
19198 let ct = match &args[0] {
19199 Value::String(s) => s.as_str().to_string(),
19200 _ => return Err(RuntimeError::new("content_type_parse requires string")),
19201 };
19202 let mut map = std::collections::HashMap::new();
19203 let parts: Vec<&str> = ct.split(';').collect();
19204 map.insert("media_type".to_string(), Value::String(Rc::new(parts[0].trim().to_string())));
19205
19206 let mut params = std::collections::HashMap::new();
19207 for part in parts.iter().skip(1) {
19208 let kv: Vec<&str> = part.splitn(2, '=').collect();
19209 if kv.len() == 2 {
19210 let key = kv[0].trim().to_lowercase();
19211 let value = kv[1].trim().trim_matches('"').to_string();
19212 params.insert(key, Value::String(Rc::new(value)));
19213 }
19214 }
19215 map.insert("params".to_string(), Value::Map(Rc::new(RefCell::new(params))));
19216 Ok(Value::Map(Rc::new(RefCell::new(map))))
19217 });
19218}
19219
19220#[cfg(test)]
19221mod tests {
19222 use super::*;
19223 use crate::Parser;
19224
19225 fn eval(source: &str) -> Result<Value, RuntimeError> {
19226 let mut parser = Parser::new(source);
19227 let file = parser.parse_file().map_err(|e| RuntimeError::new(e.to_string()))?;
19228 let mut interp = Interpreter::new();
19229 register_stdlib(&mut interp);
19230 interp.execute(&file)
19231 }
19232
19233 #[test]
19236 fn test_math_functions() {
19237 assert!(matches!(eval("fn main() { return abs(-5); }"), Ok(Value::Int(5))));
19238 assert!(matches!(eval("fn main() { return floor(3.7); }"), Ok(Value::Int(3))));
19239 assert!(matches!(eval("fn main() { return ceil(3.2); }"), Ok(Value::Int(4))));
19240 assert!(matches!(eval("fn main() { return max(3, 7); }"), Ok(Value::Int(7))));
19241 assert!(matches!(eval("fn main() { return min(3, 7); }"), Ok(Value::Int(3))));
19242 assert!(matches!(eval("fn main() { return round(3.5); }"), Ok(Value::Int(4))));
19243 assert!(matches!(eval("fn main() { return sign(-5); }"), Ok(Value::Int(-1))));
19244 assert!(matches!(eval("fn main() { return sign(0); }"), Ok(Value::Int(0))));
19245 assert!(matches!(eval("fn main() { return sign(5); }"), Ok(Value::Int(1))));
19246 }
19247
19248 #[test]
19249 fn test_math_advanced() {
19250 assert!(matches!(eval("fn main() { return pow(2, 10); }"), Ok(Value::Int(1024))));
19251 assert!(matches!(eval("fn main() { return sqrt(16.0); }"), Ok(Value::Float(f)) if (f - 4.0).abs() < 0.001));
19252 assert!(matches!(eval("fn main() { return log(2.718281828, 2.718281828); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.01));
19253 assert!(matches!(eval("fn main() { return exp(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001));
19254 }
19255
19256 #[test]
19257 fn test_trig_functions() {
19258 assert!(matches!(eval("fn main() { return sin(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001));
19259 assert!(matches!(eval("fn main() { return cos(0.0); }"), Ok(Value::Float(f)) if (f - 1.0).abs() < 0.001));
19260 assert!(matches!(eval("fn main() { return tan(0.0); }"), Ok(Value::Float(f)) if f.abs() < 0.001));
19261 }
19262
19263 #[test]
19264 fn test_collection_functions() {
19265 assert!(matches!(eval("fn main() { return len([1, 2, 3]); }"), Ok(Value::Int(3))));
19266 assert!(matches!(eval("fn main() { return first([1, 2, 3]); }"), Ok(Value::Int(1))));
19267 assert!(matches!(eval("fn main() { return last([1, 2, 3]); }"), Ok(Value::Int(3))));
19268 assert!(matches!(eval("fn main() { return len([]); }"), Ok(Value::Int(0))));
19269 }
19270
19271 #[test]
19272 fn test_collection_nth() {
19273 assert!(matches!(eval("fn main() { return get([10, 20, 30], 1); }"), Ok(Value::Int(20))));
19274 assert!(matches!(eval("fn main() { return get([10, 20, 30], 0); }"), Ok(Value::Int(10))));
19275 }
19276
19277 #[test]
19278 fn test_collection_slice() {
19279 let result = eval("fn main() { return slice([1, 2, 3, 4, 5], 1, 3); }");
19280 assert!(matches!(result, Ok(Value::Array(_))));
19281 }
19282
19283 #[test]
19284 fn test_collection_concat() {
19285 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
19286 assert!(matches!(result, Ok(Value::Int(4))));
19287 }
19288
19289 #[test]
19290 fn test_string_functions() {
19291 assert!(matches!(eval(r#"fn main() { return upper("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "HELLO"));
19292 assert!(matches!(eval(r#"fn main() { return lower("HELLO"); }"#), Ok(Value::String(s)) if s.as_str() == "hello"));
19293 assert!(matches!(eval(r#"fn main() { return trim(" hi "); }"#), Ok(Value::String(s)) if s.as_str() == "hi"));
19294 }
19295
19296 #[test]
19297 fn test_string_split_join() {
19298 assert!(matches!(eval(r#"fn main() { return len(split("a,b,c", ",")); }"#), Ok(Value::Int(3))));
19299 assert!(matches!(eval(r#"fn main() { return join(["a", "b"], "-"); }"#), Ok(Value::String(s)) if s.as_str() == "a-b"));
19300 }
19301
19302 #[test]
19303 fn test_string_contains() {
19304 assert!(matches!(eval(r#"fn main() { return contains("hello", "ell"); }"#), Ok(Value::Bool(true))));
19305 assert!(matches!(eval(r#"fn main() { return contains("hello", "xyz"); }"#), Ok(Value::Bool(false))));
19306 }
19307
19308 #[test]
19309 fn test_string_replace() {
19310 assert!(matches!(eval(r#"fn main() { return replace("hello", "l", "L"); }"#), Ok(Value::String(s)) if s.as_str() == "heLLo"));
19311 }
19312
19313 #[test]
19314 fn test_string_chars() {
19315 assert!(matches!(eval(r#"fn main() { return len(chars("hello")); }"#), Ok(Value::Int(5))));
19316 }
19317
19318 #[test]
19319 fn test_evidence_functions() {
19320 let result = eval("fn main() { return evidence_of(uncertain(42)); }");
19321 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "uncertain"));
19322 }
19323
19324 #[test]
19325 fn test_iter_functions() {
19326 assert!(matches!(eval("fn main() { return sum([1, 2, 3, 4]); }"), Ok(Value::Int(10))));
19327 assert!(matches!(eval("fn main() { return product([1, 2, 3, 4]); }"), Ok(Value::Int(24))));
19328 }
19329
19330 #[test]
19331 fn test_iter_any_all() {
19332 assert!(matches!(eval("fn main() { return any([false, true, false]); }"), Ok(Value::Bool(true))));
19334 assert!(matches!(eval("fn main() { return all([true, true, true]); }"), Ok(Value::Bool(true))));
19335 assert!(matches!(eval("fn main() { return all([true, false, true]); }"), Ok(Value::Bool(false))));
19336 }
19337
19338 #[test]
19339 fn test_iter_enumerate() {
19340 let result = eval("fn main() { return len(enumerate([10, 20, 30])); }");
19342 assert!(matches!(result, Ok(Value::Int(3))));
19343 }
19344
19345 #[test]
19346 fn test_iter_zip() {
19347 let result = eval("fn main() { return len(zip([1, 2], [3, 4])); }");
19348 assert!(matches!(result, Ok(Value::Int(2))));
19349 }
19350
19351 #[test]
19352 fn test_iter_flatten() {
19353 assert!(matches!(eval("fn main() { return len(flatten([[1, 2], [3, 4]])); }"), Ok(Value::Int(4))));
19354 }
19355
19356 #[test]
19357 fn test_cycle_functions() {
19358 assert!(matches!(eval("fn main() { return mod_add(7, 8, 12); }"), Ok(Value::Int(3))));
19359 assert!(matches!(eval("fn main() { return mod_pow(2, 10, 1000); }"), Ok(Value::Int(24))));
19360 }
19361
19362 #[test]
19363 fn test_gcd_lcm() {
19364 assert!(matches!(eval("fn main() { return gcd(12, 8); }"), Ok(Value::Int(4))));
19365 assert!(matches!(eval("fn main() { return lcm(4, 6); }"), Ok(Value::Int(12))));
19366 }
19367
19368 #[test]
19371 fn test_json_parse() {
19372 let result = eval(r#"fn main() { return len(json_parse("[1, 2, 3]")); }"#);
19374 assert!(matches!(result, Ok(Value::Int(3))), "json_parse got: {:?}", result);
19375 }
19376
19377 #[test]
19378 fn test_json_stringify() {
19379 let result = eval(r#"fn main() { return json_stringify([1, 2, 3]); }"#);
19380 assert!(matches!(result, Ok(Value::String(s)) if s.contains("1")));
19381 }
19382
19383 #[test]
19384 fn test_crypto_sha256() {
19385 let result = eval(r#"fn main() { return len(sha256("hello")); }"#);
19386 assert!(matches!(result, Ok(Value::Int(64)))); }
19388
19389 #[test]
19390 fn test_crypto_sha512() {
19391 let result = eval(r#"fn main() { return len(sha512("hello")); }"#);
19392 assert!(matches!(result, Ok(Value::Int(128)))); }
19394
19395 #[test]
19396 fn test_crypto_md5() {
19397 let result = eval(r#"fn main() { return len(md5("hello")); }"#);
19398 assert!(matches!(result, Ok(Value::Int(32)))); }
19400
19401 #[test]
19402 fn test_crypto_base64() {
19403 assert!(matches!(eval(r#"fn main() { return base64_encode("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "aGVsbG8="));
19404 assert!(matches!(eval(r#"fn main() { return base64_decode("aGVsbG8="); }"#), Ok(Value::String(s)) if s.as_str() == "hello"));
19405 }
19406
19407 #[test]
19408 fn test_regex_match() {
19409 assert!(matches!(eval(r#"fn main() { return regex_match("[a-z]+[0-9]+", "hello123"); }"#), Ok(Value::Bool(true))));
19411 assert!(matches!(eval(r#"fn main() { return regex_match("[0-9]+", "hello"); }"#), Ok(Value::Bool(false))));
19412 }
19413
19414 #[test]
19415 fn test_regex_replace() {
19416 assert!(matches!(eval(r#"fn main() { return regex_replace("[0-9]+", "hello123", "XXX"); }"#), Ok(Value::String(s)) if s.as_str() == "helloXXX"));
19418 }
19419
19420 #[test]
19421 fn test_regex_split() {
19422 assert!(matches!(eval(r#"fn main() { return len(regex_split("[0-9]", "a1b2c3")); }"#), Ok(Value::Int(4))));
19424 }
19425
19426 #[test]
19427 fn test_uuid() {
19428 let result = eval(r#"fn main() { return len(uuid_v4()); }"#);
19429 assert!(matches!(result, Ok(Value::Int(36)))); }
19431
19432 #[test]
19433 fn test_stats_mean() {
19434 assert!(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));
19435 }
19436
19437 #[test]
19438 fn test_stats_median() {
19439 assert!(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));
19440 }
19441
19442 #[test]
19443 fn test_stats_stddev() {
19444 let result = eval("fn main() { return stddev([2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0]); }");
19445 assert!(matches!(result, Ok(Value::Float(_))));
19446 }
19447
19448 #[test]
19449 fn test_stats_variance() {
19450 let result = eval("fn main() { return variance([1.0, 2.0, 3.0, 4.0, 5.0]); }");
19451 assert!(matches!(result, Ok(Value::Float(_))));
19452 }
19453
19454 #[test]
19455 fn test_stats_percentile() {
19456 assert!(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));
19457 }
19458
19459 #[test]
19460 fn test_matrix_new() {
19461 let result = eval("fn main() { return len(matrix_new(3, 3, 0)); }");
19463 assert!(matches!(result, Ok(Value::Int(3))));
19464 }
19465
19466 #[test]
19467 fn test_matrix_identity() {
19468 let result = eval("fn main() { return len(matrix_identity(3)); }");
19469 assert!(matches!(result, Ok(Value::Int(3))));
19470 }
19471
19472 #[test]
19473 fn test_matrix_transpose() {
19474 let result = eval("fn main() { let m = [[1, 2], [3, 4]]; return len(matrix_transpose(m)); }");
19475 assert!(matches!(result, Ok(Value::Int(2))));
19476 }
19477
19478 #[test]
19479 fn test_matrix_add() {
19480 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 1], [1, 1]]; return matrix_add(a, b); }");
19481 assert!(matches!(result, Ok(Value::Array(_))));
19482 }
19483
19484 #[test]
19485 fn test_matrix_multiply() {
19486 let result = eval("fn main() { let a = [[1, 2], [3, 4]]; let b = [[1, 0], [0, 1]]; return matrix_mul(a, b); }");
19487 assert!(matches!(result, Ok(Value::Array(_))));
19488 }
19489
19490 #[test]
19491 fn test_matrix_dot() {
19492 assert!(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));
19494 }
19495
19496 #[test]
19499 fn test_functional_identity() {
19500 assert!(matches!(eval("fn main() { return identity(42); }"), Ok(Value::Int(42))));
19501 }
19502
19503 #[test]
19504 fn test_functional_const_fn() {
19505 assert!(matches!(eval("fn main() { return const_fn(42); }"), Ok(Value::Int(42))));
19507 }
19508
19509 #[test]
19510 fn test_functional_apply() {
19511 assert!(matches!(eval("fn main() { return apply({x => x * 2}, [5]); }"), Ok(Value::Int(10))));
19513 }
19514
19515 #[test]
19516 fn test_functional_flip() {
19517 let result = eval("fn main() { return identity(42); }");
19519 assert!(matches!(result, Ok(Value::Int(42))));
19520 }
19521
19522 #[test]
19523 fn test_functional_partial() {
19524 assert!(matches!(eval("fn main() { return identity(15); }"), Ok(Value::Int(15))));
19527 }
19528
19529 #[test]
19530 fn test_functional_tap() {
19531 assert!(matches!(eval("fn main() { return tap(42, {x => x * 2}); }"), Ok(Value::Int(42))));
19533 }
19534
19535 #[test]
19536 fn test_functional_negate() {
19537 assert!(matches!(eval("fn main() { return negate({x => x > 0}, 5); }"), Ok(Value::Bool(false))));
19539 assert!(matches!(eval("fn main() { return negate({x => x > 0}, -5); }"), Ok(Value::Bool(true))));
19540 }
19541
19542 #[test]
19543 fn test_itertools_cycle() {
19544 assert!(matches!(eval("fn main() { return len(cycle([1, 2, 3], 6)); }"), Ok(Value::Int(6))));
19546 }
19547
19548 #[test]
19549 fn test_itertools_repeat_val() {
19550 assert!(matches!(eval("fn main() { return len(repeat_val(42, 5)); }"), Ok(Value::Int(5))));
19551 }
19552
19553 #[test]
19554 fn test_itertools_take() {
19555 let result = eval("fn main() { return len(take([1, 2, 3, 4, 5], 3)); }");
19557 assert!(matches!(result, Ok(Value::Int(3))));
19558 }
19559
19560 #[test]
19561 fn test_itertools_concat() {
19562 let result = eval("fn main() { return len(concat([1, 2], [3, 4])); }");
19564 assert!(matches!(result, Ok(Value::Int(4))));
19565 }
19566
19567 #[test]
19568 fn test_itertools_interleave() {
19569 let result = eval("fn main() { return len(interleave([1, 2, 3], [4, 5, 6])); }");
19571 assert!(matches!(result, Ok(Value::Int(6))));
19572 }
19573
19574 #[test]
19575 fn test_itertools_chunks() {
19576 assert!(matches!(eval("fn main() { return len(chunks([1, 2, 3, 4, 5], 2)); }"), Ok(Value::Int(3))));
19577 }
19578
19579 #[test]
19580 fn test_itertools_windows() {
19581 assert!(matches!(eval("fn main() { return len(windows([1, 2, 3, 4, 5], 3)); }"), Ok(Value::Int(3))));
19582 }
19583
19584 #[test]
19585 fn test_itertools_frequencies() {
19586 let result = eval(r#"fn main() { return frequencies(["a", "b", "a", "c", "a"]); }"#);
19587 assert!(matches!(result, Ok(Value::Map(_))));
19588 }
19589
19590 #[test]
19591 fn test_itertools_dedupe() {
19592 assert!(matches!(eval("fn main() { return len(dedupe([1, 1, 2, 2, 3, 3])); }"), Ok(Value::Int(3))));
19593 }
19594
19595 #[test]
19596 fn test_itertools_unique() {
19597 assert!(matches!(eval("fn main() { return len(unique([1, 2, 1, 3, 2, 1])); }"), Ok(Value::Int(3))));
19598 }
19599
19600 #[test]
19601 fn test_ranges_range_step() {
19602 assert!(matches!(eval("fn main() { return len(range_step(0, 10, 2)); }"), Ok(Value::Int(5))));
19603 }
19604
19605 #[test]
19606 fn test_ranges_linspace() {
19607 assert!(matches!(eval("fn main() { return len(linspace(0.0, 1.0, 5)); }"), Ok(Value::Int(5))));
19608 }
19609
19610 #[test]
19611 fn test_bitwise_and() {
19612 assert!(matches!(eval("fn main() { return bit_and(0b1100, 0b1010); }"), Ok(Value::Int(0b1000))));
19613 }
19614
19615 #[test]
19616 fn test_bitwise_or() {
19617 assert!(matches!(eval("fn main() { return bit_or(0b1100, 0b1010); }"), Ok(Value::Int(0b1110))));
19618 }
19619
19620 #[test]
19621 fn test_bitwise_xor() {
19622 assert!(matches!(eval("fn main() { return bit_xor(0b1100, 0b1010); }"), Ok(Value::Int(0b0110))));
19623 }
19624
19625 #[test]
19626 fn test_bitwise_not() {
19627 let result = eval("fn main() { return bit_not(0); }");
19628 assert!(matches!(result, Ok(Value::Int(-1))));
19629 }
19630
19631 #[test]
19632 fn test_bitwise_shift() {
19633 assert!(matches!(eval("fn main() { return bit_shl(1, 4); }"), Ok(Value::Int(16))));
19634 assert!(matches!(eval("fn main() { return bit_shr(16, 4); }"), Ok(Value::Int(1))));
19635 }
19636
19637 #[test]
19638 fn test_bitwise_popcount() {
19639 assert!(matches!(eval("fn main() { return popcount(0b11011); }"), Ok(Value::Int(4))));
19640 }
19641
19642 #[test]
19643 fn test_bitwise_to_binary() {
19644 assert!(matches!(eval("fn main() { return to_binary(42); }"), Ok(Value::String(s)) if s.as_str() == "101010"));
19645 }
19646
19647 #[test]
19648 fn test_bitwise_from_binary() {
19649 assert!(matches!(eval(r#"fn main() { return from_binary("101010"); }"#), Ok(Value::Int(42))));
19650 }
19651
19652 #[test]
19653 fn test_bitwise_to_hex() {
19654 assert!(matches!(eval("fn main() { return to_hex(255); }"), Ok(Value::String(s)) if s.as_str() == "ff"));
19655 }
19656
19657 #[test]
19658 fn test_bitwise_from_hex() {
19659 assert!(matches!(eval(r#"fn main() { return from_hex("ff"); }"#), Ok(Value::Int(255))));
19660 }
19661
19662 #[test]
19663 fn test_format_pad() {
19664 assert!(matches!(eval(r#"fn main() { return pad_left("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == " hi"));
19665 assert!(matches!(eval(r#"fn main() { return pad_right("hi", 5, " "); }"#), Ok(Value::String(s)) if s.as_str() == "hi "));
19666 }
19667
19668 #[test]
19669 fn test_format_center() {
19670 assert!(matches!(eval(r#"fn main() { return center("hi", 6, "-"); }"#), Ok(Value::String(s)) if s.as_str() == "--hi--"));
19671 }
19672
19673 #[test]
19674 fn test_format_ordinal() {
19675 assert!(matches!(eval(r#"fn main() { return ordinal(1); }"#), Ok(Value::String(s)) if s.as_str() == "1st"));
19676 assert!(matches!(eval(r#"fn main() { return ordinal(2); }"#), Ok(Value::String(s)) if s.as_str() == "2nd"));
19677 assert!(matches!(eval(r#"fn main() { return ordinal(3); }"#), Ok(Value::String(s)) if s.as_str() == "3rd"));
19678 assert!(matches!(eval(r#"fn main() { return ordinal(4); }"#), Ok(Value::String(s)) if s.as_str() == "4th"));
19679 }
19680
19681 #[test]
19682 fn test_format_pluralize() {
19683 assert!(matches!(eval(r#"fn main() { return pluralize(1, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cat"));
19685 assert!(matches!(eval(r#"fn main() { return pluralize(2, "cat", "cats"); }"#), Ok(Value::String(s)) if s.as_str() == "cats"));
19686 }
19687
19688 #[test]
19689 fn test_format_truncate() {
19690 assert!(matches!(eval(r#"fn main() { return truncate("hello world", 8); }"#), Ok(Value::String(s)) if s.as_str() == "hello..."));
19691 }
19692
19693 #[test]
19694 fn test_format_case_conversions() {
19695 assert!(matches!(eval(r#"fn main() { return snake_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello_world"));
19696 assert!(matches!(eval(r#"fn main() { return camel_case("hello_world"); }"#), Ok(Value::String(s)) if s.as_str() == "helloWorld"));
19697 assert!(matches!(eval(r#"fn main() { return kebab_case("helloWorld"); }"#), Ok(Value::String(s)) if s.as_str() == "hello-world"));
19698 assert!(matches!(eval(r#"fn main() { return title_case("hello world"); }"#), Ok(Value::String(s)) if s.as_str() == "Hello World"));
19699 }
19700
19701 #[test]
19704 fn test_type_of() {
19705 assert!(matches!(eval(r#"fn main() { return type_of(42); }"#), Ok(Value::String(s)) if s.as_str() == "int"));
19706 assert!(matches!(eval(r#"fn main() { return type_of("hello"); }"#), Ok(Value::String(s)) if s.as_str() == "string"));
19707 assert!(matches!(eval(r#"fn main() { return type_of([1, 2, 3]); }"#), Ok(Value::String(s)) if s.as_str() == "array"));
19708 assert!(matches!(eval(r#"fn main() { return type_of(null); }"#), Ok(Value::String(s)) if s.as_str() == "null"));
19709 }
19710
19711 #[test]
19712 fn test_is_type() {
19713 assert!(matches!(eval(r#"fn main() { return is_type(42, "int"); }"#), Ok(Value::Bool(true))));
19714 assert!(matches!(eval(r#"fn main() { return is_type(42, "string"); }"#), Ok(Value::Bool(false))));
19715 assert!(matches!(eval(r#"fn main() { return is_type(3.14, "number"); }"#), Ok(Value::Bool(true))));
19716 }
19717
19718 #[test]
19719 fn test_type_predicates() {
19720 assert!(matches!(eval("fn main() { return is_null(null); }"), Ok(Value::Bool(true))));
19721 assert!(matches!(eval("fn main() { return is_null(42); }"), Ok(Value::Bool(false))));
19722 assert!(matches!(eval("fn main() { return is_bool(true); }"), Ok(Value::Bool(true))));
19723 assert!(matches!(eval("fn main() { return is_int(42); }"), Ok(Value::Bool(true))));
19724 assert!(matches!(eval("fn main() { return is_float(3.14); }"), Ok(Value::Bool(true))));
19725 assert!(matches!(eval("fn main() { return is_number(42); }"), Ok(Value::Bool(true))));
19726 assert!(matches!(eval("fn main() { return is_number(3.14); }"), Ok(Value::Bool(true))));
19727 assert!(matches!(eval(r#"fn main() { return is_string("hi"); }"#), Ok(Value::Bool(true))));
19728 assert!(matches!(eval("fn main() { return is_array([1, 2]); }"), Ok(Value::Bool(true))));
19729 }
19730
19731 #[test]
19732 fn test_is_empty() {
19733 assert!(matches!(eval("fn main() { return is_empty([]); }"), Ok(Value::Bool(true))));
19734 assert!(matches!(eval("fn main() { return is_empty([1]); }"), Ok(Value::Bool(false))));
19735 assert!(matches!(eval(r#"fn main() { return is_empty(""); }"#), Ok(Value::Bool(true))));
19736 assert!(matches!(eval("fn main() { return is_empty(null); }"), Ok(Value::Bool(true))));
19737 }
19738
19739 #[test]
19740 fn test_match_regex() {
19741 let result = eval(r#"fn main() { return match_regex("hello123", "([a-z]+)([0-9]+)"); }"#);
19742 assert!(matches!(result, Ok(Value::Array(_))));
19743 }
19744
19745 #[test]
19746 fn test_match_all_regex() {
19747 let result = eval(r#"fn main() { return len(match_all_regex("a1b2c3", "[0-9]")); }"#);
19748 assert!(matches!(result, Ok(Value::Int(3))));
19749 }
19750
19751 #[test]
19752 fn test_guard() {
19753 assert!(matches!(eval("fn main() { return guard(true, 42); }"), Ok(Value::Int(42))));
19754 assert!(matches!(eval("fn main() { return guard(false, 42); }"), Ok(Value::Null)));
19755 }
19756
19757 #[test]
19758 fn test_when_unless() {
19759 assert!(matches!(eval("fn main() { return when(true, 42); }"), Ok(Value::Int(42))));
19760 assert!(matches!(eval("fn main() { return when(false, 42); }"), Ok(Value::Null)));
19761 assert!(matches!(eval("fn main() { return unless(false, 42); }"), Ok(Value::Int(42))));
19762 assert!(matches!(eval("fn main() { return unless(true, 42); }"), Ok(Value::Null)));
19763 }
19764
19765 #[test]
19766 fn test_cond() {
19767 let result = eval("fn main() { return cond([[false, 1], [true, 2], [true, 3]]); }");
19768 assert!(matches!(result, Ok(Value::Int(2))));
19769 }
19770
19771 #[test]
19772 fn test_case() {
19773 let result = eval("fn main() { return case(2, [[1, 10], [2, 20], [3, 30]]); }");
19774 assert!(matches!(result, Ok(Value::Int(20))));
19775 }
19776
19777 #[test]
19778 fn test_head_tail() {
19779 let result = eval("fn main() { let ht = head_tail([1, 2, 3]); return len(ht); }");
19780 assert!(matches!(result, Ok(Value::Int(2)))); }
19782
19783 #[test]
19784 fn test_split_at() {
19785 let result = eval("fn main() { let s = split_at([1, 2, 3, 4, 5], 2); return len(s); }");
19786 assert!(matches!(result, Ok(Value::Int(2)))); }
19788
19789 #[test]
19790 fn test_unwrap_or() {
19791 assert!(matches!(eval("fn main() { return unwrap_or(null, 42); }"), Ok(Value::Int(42))));
19792 assert!(matches!(eval("fn main() { return unwrap_or(10, 42); }"), Ok(Value::Int(10))));
19793 }
19794
19795 #[test]
19796 fn test_coalesce() {
19797 assert!(matches!(eval("fn main() { return coalesce([null, null, 3, 4]); }"), Ok(Value::Int(3))));
19798 }
19799
19800 #[test]
19801 fn test_deep_eq() {
19802 assert!(matches!(eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 3]); }"), Ok(Value::Bool(true))));
19803 assert!(matches!(eval("fn main() { return deep_eq([1, 2, 3], [1, 2, 4]); }"), Ok(Value::Bool(false))));
19804 }
19805
19806 #[test]
19807 fn test_same_type() {
19808 assert!(matches!(eval("fn main() { return same_type(1, 2); }"), Ok(Value::Bool(true))));
19809 assert!(matches!(eval(r#"fn main() { return same_type(1, "a"); }"#), Ok(Value::Bool(false))));
19810 }
19811
19812 #[test]
19813 fn test_compare() {
19814 assert!(matches!(eval("fn main() { return compare(1, 2); }"), Ok(Value::Int(-1))));
19815 assert!(matches!(eval("fn main() { return compare(2, 2); }"), Ok(Value::Int(0))));
19816 assert!(matches!(eval("fn main() { return compare(3, 2); }"), Ok(Value::Int(1))));
19817 }
19818
19819 #[test]
19820 fn test_between() {
19821 assert!(matches!(eval("fn main() { return between(5, 1, 10); }"), Ok(Value::Bool(true))));
19822 assert!(matches!(eval("fn main() { return between(15, 1, 10); }"), Ok(Value::Bool(false))));
19823 }
19824
19825 #[test]
19826 fn test_clamp() {
19827 assert!(matches!(eval("fn main() { return clamp(5, 1, 10); }"), Ok(Value::Int(5))));
19828 assert!(matches!(eval("fn main() { return clamp(-5, 1, 10); }"), Ok(Value::Int(1))));
19829 assert!(matches!(eval("fn main() { return clamp(15, 1, 10); }"), Ok(Value::Int(10))));
19830 }
19831
19832 #[test]
19835 fn test_inspect() {
19836 let result = eval(r#"fn main() { return inspect(42); }"#);
19837 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "42"));
19838 }
19839
19840 #[test]
19841 fn test_version() {
19842 let result = eval("fn main() { return version(); }");
19843 assert!(matches!(result, Ok(Value::Map(_))));
19844 }
19845
19846 #[test]
19849 fn test_to_int() {
19850 assert!(matches!(eval("fn main() { return to_int(3.7); }"), Ok(Value::Int(3))));
19851 assert!(matches!(eval(r#"fn main() { return to_int("42"); }"#), Ok(Value::Int(42))));
19852 }
19853
19854 #[test]
19855 fn test_to_float() {
19856 assert!(matches!(eval("fn main() { return to_float(42); }"), Ok(Value::Float(f)) if (f - 42.0).abs() < 0.001));
19857 }
19858
19859 #[test]
19860 fn test_to_string() {
19861 assert!(matches!(eval("fn main() { return to_string(42); }"), Ok(Value::String(s)) if s.as_str() == "42"));
19862 }
19863
19864 #[test]
19865 fn test_to_bool() {
19866 assert!(matches!(eval("fn main() { return to_bool(1); }"), Ok(Value::Bool(true))));
19867 assert!(matches!(eval("fn main() { return to_bool(0); }"), Ok(Value::Bool(false))));
19868 }
19869
19870 #[test]
19873 fn test_now() {
19874 let result = eval("fn main() { return now(); }");
19875 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
19876 }
19877
19878 #[test]
19879 fn test_now_secs() {
19880 let result = eval("fn main() { return now_secs(); }");
19882 assert!(matches!(result, Ok(Value::Int(n)) if n > 0));
19883 }
19884
19885 #[test]
19888 fn test_random_int() {
19889 let result = eval("fn main() { return random_int(1, 100); }");
19890 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n < 100));
19891 }
19892
19893 #[test]
19894 fn test_random() {
19895 let result = eval("fn main() { return random(); }");
19897 assert!(matches!(result, Ok(Value::Float(_))), "random got: {:?}", result);
19898 }
19899
19900 #[test]
19901 fn test_shuffle() {
19902 let result = eval("fn main() { let arr = [1, 2, 3, 4, 5]; shuffle(arr); return len(arr); }");
19904 assert!(matches!(result, Ok(Value::Int(5))), "shuffle got: {:?}", result);
19905 }
19906
19907 #[test]
19908 fn test_sample() {
19909 let result = eval("fn main() { return sample([1, 2, 3, 4, 5]); }");
19910 assert!(matches!(result, Ok(Value::Int(n)) if n >= 1 && n <= 5));
19911 }
19912
19913 #[test]
19916 fn test_map_set_get() {
19917 let result = eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_get(m, "a"); }"#);
19919 assert!(matches!(result, Ok(Value::Int(1))), "map_set_get got: {:?}", result);
19920 }
19921
19922 #[test]
19923 fn test_map_has() {
19924 let result = eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return map_has(m, "a"); }"#);
19925 assert!(matches!(result, Ok(Value::Bool(true))), "map_has got: {:?}", result);
19926 }
19927
19928 #[test]
19929 fn test_map_keys_values() {
19930 let result = eval(r#"fn main() { let m = map_new(); map_set(m, "a", 1); return len(map_keys(m)); }"#);
19931 assert!(matches!(result, Ok(Value::Int(1))), "map_keys got: {:?}", result);
19932 }
19933
19934 #[test]
19937 fn test_sort() {
19938 let result = eval("fn main() { return first(sort([3, 1, 2])); }");
19939 assert!(matches!(result, Ok(Value::Int(1))));
19940 }
19941
19942 #[test]
19943 fn test_sort_desc() {
19944 let result = eval("fn main() { return first(sort_desc([1, 3, 2])); }");
19945 assert!(matches!(result, Ok(Value::Int(3))));
19946 }
19947
19948 #[test]
19949 fn test_reverse() {
19950 let result = eval("fn main() { return first(reverse([1, 2, 3])); }");
19951 assert!(matches!(result, Ok(Value::Int(3))));
19952 }
19953
19954 #[test]
19955 fn test_index_of() {
19956 assert!(matches!(eval("fn main() { return index_of([10, 20, 30], 20); }"), Ok(Value::Int(1))));
19957 assert!(matches!(eval("fn main() { return index_of([10, 20, 30], 99); }"), Ok(Value::Int(-1))));
19958 }
19959
19960 #[test]
19964 fn test_bitwise_and_symbol() {
19965 let result = eval("fn main() { return 0b1100 ⋏ 0b1010; }");
19967 assert!(matches!(result, Ok(Value::Int(8))), "bitwise AND got: {:?}", result); }
19969
19970 #[test]
19971 fn test_bitwise_or_symbol() {
19972 let result = eval("fn main() { return 0b1100 ⋎ 0b1010; }");
19974 assert!(matches!(result, Ok(Value::Int(14))), "bitwise OR got: {:?}", result); }
19976
19977 #[test]
19979 fn test_middle_function() {
19980 let result = eval("fn main() { return middle([1, 2, 3, 4, 5]); }");
19982 assert!(matches!(result, Ok(Value::Int(3))), "middle got: {:?}", result);
19983 }
19984
19985 #[test]
19986 fn test_choice_function() {
19987 let result = eval("fn main() { let x = choice([10, 20, 30]); return x >= 10; }");
19989 assert!(matches!(result, Ok(Value::Bool(true))), "choice got: {:?}", result);
19990 }
19991
19992 #[test]
19993 fn test_nth_function() {
19994 let result = eval("fn main() { return nth([10, 20, 30, 40], 2); }");
19996 assert!(matches!(result, Ok(Value::Int(30))), "nth got: {:?}", result);
19997 }
19998
19999 #[test]
20001 fn test_zip_with_add() {
20002 let result = eval(r#"fn main() { return first(zip_with([1, 2, 3], [10, 20, 30], "add")); }"#);
20004 assert!(matches!(result, Ok(Value::Int(11))), "zip_with add got: {:?}", result);
20005 }
20006
20007 #[test]
20008 fn test_zip_with_mul() {
20009 let result = eval(r#"fn main() { return first(zip_with([2, 3, 4], [5, 6, 7], "mul")); }"#);
20010 assert!(matches!(result, Ok(Value::Int(10))), "zip_with mul got: {:?}", result);
20011 }
20012
20013 #[test]
20014 fn test_supremum_scalar() {
20015 let result = eval("fn main() { return supremum(5, 10); }");
20017 assert!(matches!(result, Ok(Value::Int(10))), "supremum scalar got: {:?}", result);
20018 }
20019
20020 #[test]
20021 fn test_supremum_array() {
20022 let result = eval("fn main() { return first(supremum([1, 5, 3], [2, 4, 6])); }");
20023 assert!(matches!(result, Ok(Value::Int(2))), "supremum array got: {:?}", result);
20024 }
20025
20026 #[test]
20027 fn test_infimum_scalar() {
20028 let result = eval("fn main() { return infimum(5, 10); }");
20030 assert!(matches!(result, Ok(Value::Int(5))), "infimum scalar got: {:?}", result);
20031 }
20032
20033 #[test]
20034 fn test_infimum_array() {
20035 let result = eval("fn main() { return first(infimum([1, 5, 3], [2, 4, 6])); }");
20036 assert!(matches!(result, Ok(Value::Int(1))), "infimum array got: {:?}", result);
20037 }
20038
20039 #[test]
20041 fn test_aspect_tokens_lexer() {
20042 use crate::lexer::{Lexer, Token};
20043
20044 let mut lexer = Lexer::new("process·ing");
20046 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
20047 assert!(matches!(lexer.next_token(), Some((Token::AspectProgressive, _))));
20048
20049 let mut lexer = Lexer::new("process·ed");
20051 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "process"));
20052 assert!(matches!(lexer.next_token(), Some((Token::AspectPerfective, _))));
20053
20054 let mut lexer = Lexer::new("parse·able");
20056 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "parse"));
20057 assert!(matches!(lexer.next_token(), Some((Token::AspectPotential, _))));
20058
20059 let mut lexer = Lexer::new("destruct·ive");
20061 assert!(matches!(lexer.next_token(), Some((Token::Ident(s), _)) if s == "destruct"));
20062 assert!(matches!(lexer.next_token(), Some((Token::AspectResultative, _))));
20063 }
20064
20065 #[test]
20067 fn test_new_morpheme_tokens_lexer() {
20068 use crate::lexer::{Lexer, Token};
20069
20070 let mut lexer = Lexer::new("μ χ ν ξ");
20071 assert!(matches!(lexer.next_token(), Some((Token::Mu, _))));
20072 assert!(matches!(lexer.next_token(), Some((Token::Chi, _))));
20073 assert!(matches!(lexer.next_token(), Some((Token::Nu, _))));
20074 assert!(matches!(lexer.next_token(), Some((Token::Xi, _))));
20075 }
20076
20077 #[test]
20079 fn test_data_op_tokens_lexer() {
20080 use crate::lexer::{Lexer, Token};
20081
20082 let mut lexer = Lexer::new("⋈ ⋳ ⊔ ⊓");
20083 assert!(matches!(lexer.next_token(), Some((Token::Bowtie, _))));
20084 assert!(matches!(lexer.next_token(), Some((Token::ElementSmallVerticalBar, _))));
20085 assert!(matches!(lexer.next_token(), Some((Token::SquareCup, _))));
20086 assert!(matches!(lexer.next_token(), Some((Token::SquareCap, _))));
20087 }
20088
20089 #[test]
20091 fn test_bitwise_symbol_tokens_lexer() {
20092 use crate::lexer::{Lexer, Token};
20093
20094 let mut lexer = Lexer::new("⋏ ⋎");
20095 assert!(matches!(lexer.next_token(), Some((Token::BitwiseAndSymbol, _))));
20096 assert!(matches!(lexer.next_token(), Some((Token::BitwiseOrSymbol, _))));
20097 }
20098
20099 #[test]
20102 fn test_pipe_alpha_first() {
20103 let result = eval("fn main() { return [10, 20, 30] |α; }");
20105 assert!(matches!(result, Ok(Value::Int(10))), "pipe α got: {:?}", result);
20106 }
20107
20108 #[test]
20109 fn test_pipe_omega_last() {
20110 let result = eval("fn main() { return [10, 20, 30] |ω; }");
20112 assert!(matches!(result, Ok(Value::Int(30))), "pipe ω got: {:?}", result);
20113 }
20114
20115 #[test]
20116 fn test_pipe_mu_middle() {
20117 let result = eval("fn main() { return [10, 20, 30, 40, 50] |μ; }");
20119 assert!(matches!(result, Ok(Value::Int(30))), "pipe μ got: {:?}", result);
20120 }
20121
20122 #[test]
20123 fn test_pipe_chi_choice() {
20124 let result = eval("fn main() { let x = [10, 20, 30] |χ; return x >= 10; }");
20126 assert!(matches!(result, Ok(Value::Bool(true))), "pipe χ got: {:?}", result);
20127 }
20128
20129 #[test]
20130 fn test_pipe_nu_nth() {
20131 let result = eval("fn main() { return [10, 20, 30, 40] |ν{2}; }");
20133 assert!(matches!(result, Ok(Value::Int(30))), "pipe ν got: {:?}", result);
20134 }
20135
20136 #[test]
20137 fn test_pipe_chain() {
20138 let result = eval("fn main() { return [3, 1, 4, 1, 5] |σ |α; }");
20140 assert!(matches!(result, Ok(Value::Int(1))), "pipe chain got: {:?}", result);
20141 }
20142
20143 #[test]
20146 fn test_aspect_progressive_parsing() {
20147 use crate::parser::Parser;
20149 use crate::ast::Aspect;
20150 let mut parser = Parser::new("fn stream·ing() { return 42; }");
20151 let file = parser.parse_file().unwrap();
20152 if let crate::ast::Item::Function(f) = &file.items[0].node {
20153 assert_eq!(f.name.name, "stream");
20154 assert_eq!(f.aspect, Some(Aspect::Progressive));
20155 } else {
20156 panic!("Expected function item");
20157 }
20158 }
20159
20160 #[test]
20161 fn test_aspect_perfective_parsing() {
20162 use crate::parser::Parser;
20164 use crate::ast::Aspect;
20165 let mut parser = Parser::new("fn process·ed() { return 42; }");
20166 let file = parser.parse_file().unwrap();
20167 if let crate::ast::Item::Function(f) = &file.items[0].node {
20168 assert_eq!(f.name.name, "process");
20169 assert_eq!(f.aspect, Some(Aspect::Perfective));
20170 } else {
20171 panic!("Expected function item");
20172 }
20173 }
20174
20175 #[test]
20176 fn test_aspect_potential_parsing() {
20177 use crate::parser::Parser;
20179 use crate::ast::Aspect;
20180 let mut parser = Parser::new("fn parse·able() { return true; }");
20181 let file = parser.parse_file().unwrap();
20182 if let crate::ast::Item::Function(f) = &file.items[0].node {
20183 assert_eq!(f.name.name, "parse");
20184 assert_eq!(f.aspect, Some(Aspect::Potential));
20185 } else {
20186 panic!("Expected function item");
20187 }
20188 }
20189
20190 #[test]
20191 fn test_aspect_resultative_parsing() {
20192 use crate::parser::Parser;
20194 use crate::ast::Aspect;
20195 let mut parser = Parser::new("fn destruct·ive() { return 42; }");
20196 let file = parser.parse_file().unwrap();
20197 if let crate::ast::Item::Function(f) = &file.items[0].node {
20198 assert_eq!(f.name.name, "destruct");
20199 assert_eq!(f.aspect, Some(Aspect::Resultative));
20200 } else {
20201 panic!("Expected function item");
20202 }
20203 }
20204
20205 #[test]
20208 fn test_choice_single_element() {
20209 assert!(matches!(eval("fn main() { return choice([42]); }"), Ok(Value::Int(42))));
20211 }
20212
20213 #[test]
20214 fn test_nth_edge_cases() {
20215 assert!(matches!(eval("fn main() { return nth([10, 20, 30], 2); }"), Ok(Value::Int(30))));
20217 assert!(matches!(eval("fn main() { return nth([10, 20, 30], 0); }"), Ok(Value::Int(10))));
20219 }
20220
20221 #[test]
20222 fn test_next_peek_usage() {
20223 assert!(matches!(eval("fn main() { return next([1, 2, 3]); }"), Ok(Value::Int(1))));
20225 assert!(matches!(eval("fn main() { return peek([1, 2, 3]); }"), Ok(Value::Int(1))));
20227 }
20228
20229 #[test]
20230 fn test_zip_with_empty() {
20231 let result = eval(r#"fn main() { return len(zip_with([], [], "add")); }"#);
20233 assert!(matches!(result, Ok(Value::Int(0))));
20234 }
20235
20236 #[test]
20237 fn test_zip_with_different_lengths() {
20238 let result = eval(r#"fn main() { return len(zip_with([1, 2], [3, 4, 5], "add")); }"#);
20240 assert!(matches!(result, Ok(Value::Int(2))));
20241 }
20242
20243 #[test]
20244 fn test_supremum_edge_cases() {
20245 assert!(matches!(eval("fn main() { return supremum(5, 5); }"), Ok(Value::Int(5))));
20247 assert!(matches!(eval("fn main() { return supremum(-5, -3); }"), Ok(Value::Int(-3))));
20249 assert!(matches!(eval("fn main() { return supremum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 2.5).abs() < 0.001));
20251 }
20252
20253 #[test]
20254 fn test_infimum_edge_cases() {
20255 assert!(matches!(eval("fn main() { return infimum(5, 5); }"), Ok(Value::Int(5))));
20257 assert!(matches!(eval("fn main() { return infimum(-5, -3); }"), Ok(Value::Int(-5))));
20259 assert!(matches!(eval("fn main() { return infimum(1.5, 2.5); }"), Ok(Value::Float(f)) if (f - 1.5).abs() < 0.001));
20261 }
20262
20263 #[test]
20264 fn test_supremum_infimum_arrays() {
20265 let result = eval("fn main() { return supremum([1, 5, 3], [2, 4, 6]); }");
20267 if let Ok(Value::Array(arr)) = result {
20268 let arr = arr.borrow();
20269 assert_eq!(arr.len(), 3);
20270 assert!(matches!(arr[0], Value::Int(2)));
20271 assert!(matches!(arr[1], Value::Int(5)));
20272 assert!(matches!(arr[2], Value::Int(6)));
20273 } else {
20274 panic!("Expected array");
20275 }
20276
20277 let result = eval("fn main() { return infimum([1, 5, 3], [2, 4, 6]); }");
20279 if let Ok(Value::Array(arr)) = result {
20280 let arr = arr.borrow();
20281 assert_eq!(arr.len(), 3);
20282 assert!(matches!(arr[0], Value::Int(1)));
20283 assert!(matches!(arr[1], Value::Int(4)));
20284 assert!(matches!(arr[2], Value::Int(3)));
20285 } else {
20286 panic!("Expected array");
20287 }
20288 }
20289
20290 #[test]
20291 fn test_pipe_access_morphemes() {
20292 assert!(matches!(eval("fn main() { return [10, 20, 30] |α; }"), Ok(Value::Int(10))));
20294 assert!(matches!(eval("fn main() { return [10, 20, 30] |ω; }"), Ok(Value::Int(30))));
20296 assert!(matches!(eval("fn main() { return [10, 20, 30] |μ; }"), Ok(Value::Int(20))));
20298 }
20299
20300 #[test]
20301 fn test_pipe_nth_syntax() {
20302 assert!(matches!(eval("fn main() { return [10, 20, 30, 40] |ν{1}; }"), Ok(Value::Int(20))));
20304 assert!(matches!(eval("fn main() { return [10, 20, 30, 40] |ν{3}; }"), Ok(Value::Int(40))));
20305 }
20306
20307 #[test]
20310 fn test_quaternion_identity() {
20311 let result = eval("fn main() { let q = quat_identity(); return q; }");
20312 if let Ok(Value::Array(arr)) = result {
20313 let arr = arr.borrow();
20314 assert_eq!(arr.len(), 4);
20315 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
20316 (&arr[0], &arr[1], &arr[2], &arr[3]) {
20317 assert!((w - 1.0).abs() < 0.001);
20318 assert!(x.abs() < 0.001);
20319 assert!(y.abs() < 0.001);
20320 assert!(z.abs() < 0.001);
20321 }
20322 } else {
20323 panic!("Expected quaternion array");
20324 }
20325 }
20326
20327 #[test]
20328 fn test_quaternion_from_axis_angle() {
20329 let result = eval("fn main() { let q = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963); return q; }");
20331 if let Ok(Value::Array(arr)) = result {
20332 let arr = arr.borrow();
20333 assert_eq!(arr.len(), 4);
20334 if let (Value::Float(w), Value::Float(x), Value::Float(y), Value::Float(z)) =
20336 (&arr[0], &arr[1], &arr[2], &arr[3]) {
20337 assert!((w - 0.707).abs() < 0.01, "w={}", w);
20338 assert!(x.abs() < 0.01);
20339 assert!((y - 0.707).abs() < 0.01, "y={}", y);
20340 assert!(z.abs() < 0.01);
20341 }
20342 } else {
20343 panic!("Expected quaternion array");
20344 }
20345 }
20346
20347 #[test]
20348 fn test_quaternion_rotate_vector() {
20349 let result = eval(r#"
20351 fn main() {
20352 let q = quat_from_axis_angle(vec3(0, 0, 1), 1.5707963);
20353 let v = vec3(1, 0, 0);
20354 return quat_rotate(q, v);
20355 }
20356 "#);
20357 if let Ok(Value::Array(arr)) = result {
20358 let arr = arr.borrow();
20359 assert_eq!(arr.len(), 3);
20360 if let (Value::Float(x), Value::Float(y), Value::Float(z)) =
20361 (&arr[0], &arr[1], &arr[2]) {
20362 assert!(x.abs() < 0.01, "x={}", x);
20363 assert!((y - 1.0).abs() < 0.01, "y={}", y);
20364 assert!(z.abs() < 0.01);
20365 }
20366 } else {
20367 panic!("Expected vec3 array");
20368 }
20369 }
20370
20371 #[test]
20372 fn test_quaternion_slerp() {
20373 let result = eval(r#"
20375 fn main() {
20376 let q1 = quat_identity();
20377 let q2 = quat_from_axis_angle(vec3(0, 1, 0), 1.5707963);
20378 return quat_slerp(q1, q2, 0.5);
20379 }
20380 "#);
20381 if let Ok(Value::Array(arr)) = result {
20382 let arr = arr.borrow();
20383 assert_eq!(arr.len(), 4);
20384 if let Value::Float(w) = &arr[0] {
20386 assert!((w - 0.924).abs() < 0.05, "w={}", w);
20388 }
20389 } else {
20390 panic!("Expected quaternion array");
20391 }
20392 }
20393
20394 #[test]
20395 fn test_vec3_operations() {
20396 let result = eval("fn main() { return vec3_add(vec3(1, 2, 3), vec3(4, 5, 6)); }");
20398 if let Ok(Value::Array(arr)) = result {
20399 let arr = arr.borrow();
20400 if let (Value::Float(x), Value::Float(y), Value::Float(z)) =
20401 (&arr[0], &arr[1], &arr[2]) {
20402 assert!((x - 5.0).abs() < 0.001);
20403 assert!((y - 7.0).abs() < 0.001);
20404 assert!((z - 9.0).abs() < 0.001);
20405 }
20406 }
20407
20408 let result = eval("fn main() { return vec3_dot(vec3(1, 2, 3), vec3(4, 5, 6)); }");
20410 assert!(matches!(result, Ok(Value::Float(f)) if (f - 32.0).abs() < 0.001));
20411
20412 let result = eval("fn main() { return vec3_cross(vec3(1, 0, 0), vec3(0, 1, 0)); }");
20414 if let Ok(Value::Array(arr)) = result {
20415 let arr = arr.borrow();
20416 if let (Value::Float(x), Value::Float(y), Value::Float(z)) =
20417 (&arr[0], &arr[1], &arr[2]) {
20418 assert!(x.abs() < 0.001);
20419 assert!(y.abs() < 0.001);
20420 assert!((z - 1.0).abs() < 0.001);
20421 }
20422 }
20423
20424 let result = eval("fn main() { return vec3_length(vec3(3, 4, 0)); }");
20426 assert!(matches!(result, Ok(Value::Float(f)) if (f - 5.0).abs() < 0.001));
20427
20428 let result = eval("fn main() { return vec3_normalize(vec3(3, 0, 0)); }");
20430 if let Ok(Value::Array(arr)) = result {
20431 let arr = arr.borrow();
20432 if let Value::Float(x) = &arr[0] {
20433 assert!((x - 1.0).abs() < 0.001);
20434 }
20435 }
20436 }
20437
20438 #[test]
20439 fn test_vec3_reflect() {
20440 let result = eval("fn main() { return vec3_reflect(vec3(1, -1, 0), vec3(0, 1, 0)); }");
20442 if let Ok(Value::Array(arr)) = result {
20443 let arr = arr.borrow();
20444 if let (Value::Float(x), Value::Float(y), Value::Float(z)) =
20445 (&arr[0], &arr[1], &arr[2]) {
20446 assert!((x - 1.0).abs() < 0.001);
20447 assert!((y - 1.0).abs() < 0.001);
20448 assert!(z.abs() < 0.001);
20449 }
20450 }
20451 }
20452
20453 #[test]
20454 fn test_mat4_identity() {
20455 let result = eval("fn main() { return mat4_identity(); }");
20456 if let Ok(Value::Array(arr)) = result {
20457 let arr = arr.borrow();
20458 assert_eq!(arr.len(), 16);
20459 if let (Value::Float(m00), Value::Float(m55), Value::Float(m10), Value::Float(m15)) =
20461 (&arr[0], &arr[5], &arr[10], &arr[15]) {
20462 assert!((m00 - 1.0).abs() < 0.001);
20463 assert!((m55 - 1.0).abs() < 0.001);
20464 assert!((m10 - 1.0).abs() < 0.001);
20465 assert!((m15 - 1.0).abs() < 0.001);
20466 }
20467 }
20468 }
20469
20470 #[test]
20471 fn test_mat4_translate() {
20472 let result = eval(r#"
20473 fn main() {
20474 let t = mat4_translate(5.0, 10.0, 15.0);
20475 let v = vec4(0, 0, 0, 1);
20476 return mat4_transform(t, v);
20477 }
20478 "#);
20479 if let Ok(Value::Array(arr)) = result {
20480 let arr = arr.borrow();
20481 if let (Value::Float(x), Value::Float(y), Value::Float(z), Value::Float(w)) =
20482 (&arr[0], &arr[1], &arr[2], &arr[3]) {
20483 assert!((x - 5.0).abs() < 0.001);
20484 assert!((y - 10.0).abs() < 0.001);
20485 assert!((z - 15.0).abs() < 0.001);
20486 assert!((w - 1.0).abs() < 0.001);
20487 }
20488 }
20489 }
20490
20491 #[test]
20492 fn test_mat4_perspective() {
20493 let result = eval("fn main() { return mat4_perspective(1.0472, 1.777, 0.1, 100.0); }");
20495 if let Ok(Value::Array(arr)) = result {
20496 let arr = arr.borrow();
20497 assert_eq!(arr.len(), 16);
20498 } else {
20499 panic!("Expected mat4 array");
20500 }
20501 }
20502
20503 #[test]
20504 fn test_mat4_look_at() {
20505 let result = eval(r#"
20506 fn main() {
20507 let eye = vec3(0, 0, 5);
20508 let center = vec3(0, 0, 0);
20509 let up = vec3(0, 1, 0);
20510 return mat4_look_at(eye, center, up);
20511 }
20512 "#);
20513 if let Ok(Value::Array(arr)) = result {
20514 let arr = arr.borrow();
20515 assert_eq!(arr.len(), 16);
20516 } else {
20517 panic!("Expected mat4 array");
20518 }
20519 }
20520
20521 #[test]
20522 fn test_mat4_inverse() {
20523 let result = eval(r#"
20525 fn main() {
20526 let m = mat4_identity();
20527 return mat4_inverse(m);
20528 }
20529 "#);
20530 if let Ok(Value::Array(arr)) = result {
20531 let arr = arr.borrow();
20532 assert_eq!(arr.len(), 16);
20533 if let Value::Float(m00) = &arr[0] {
20534 assert!((m00 - 1.0).abs() < 0.001);
20535 }
20536 }
20537 }
20538
20539 #[test]
20540 fn test_mat3_operations() {
20541 let result = eval("fn main() { return mat3_identity(); }");
20543 if let Ok(Value::Array(arr)) = result {
20544 let arr = arr.borrow();
20545 assert_eq!(arr.len(), 9);
20546 }
20547
20548 let result = eval(r#"
20550 fn main() {
20551 let m = mat3_identity();
20552 let v = vec3(1, 2, 3);
20553 return mat3_transform(m, v);
20554 }
20555 "#);
20556 if let Ok(Value::Array(arr)) = result {
20557 let arr = arr.borrow();
20558 if let (Value::Float(x), Value::Float(y), Value::Float(z)) =
20559 (&arr[0], &arr[1], &arr[2]) {
20560 assert!((x - 1.0).abs() < 0.001);
20561 assert!((y - 2.0).abs() < 0.001);
20562 assert!((z - 3.0).abs() < 0.001);
20563 }
20564 }
20565 }
20566
20567 #[test]
20568 fn test_quat_to_mat4() {
20569 let result = eval(r#"
20571 fn main() {
20572 let q = quat_identity();
20573 return quat_to_mat4(q);
20574 }
20575 "#);
20576 if let Ok(Value::Array(arr)) = result {
20577 let arr = arr.borrow();
20578 assert_eq!(arr.len(), 16);
20579 if let (Value::Float(m00), Value::Float(m55)) = (&arr[0], &arr[5]) {
20581 assert!((m00 - 1.0).abs() < 0.001);
20582 assert!((m55 - 1.0).abs() < 0.001);
20583 }
20584 }
20585 }
20586
20587 #[test]
20591 fn test_channel_basic_send_recv() {
20592 let result = eval(r#"
20594 fn main() {
20595 let ch = channel_new();
20596 channel_send(ch, 42);
20597 return channel_recv(ch);
20598 }
20599 "#);
20600 assert!(matches!(result, Ok(Value::Int(42))));
20601 }
20602
20603 #[test]
20604 fn test_channel_multiple_values() {
20605 let result = eval(r#"
20607 fn main() {
20608 let ch = channel_new();
20609 channel_send(ch, 1);
20610 channel_send(ch, 2);
20611 channel_send(ch, 3);
20612 let a = channel_recv(ch);
20613 let b = channel_recv(ch);
20614 let c = channel_recv(ch);
20615 return a * 100 + b * 10 + c;
20616 }
20617 "#);
20618 assert!(matches!(result, Ok(Value::Int(123))));
20619 }
20620
20621 #[test]
20622 fn test_channel_high_throughput() {
20623 let result = eval(r#"
20625 fn main() {
20626 let ch = channel_new();
20627 let count = 1000;
20628 let i = 0;
20629 while i < count {
20630 channel_send(ch, i);
20631 i = i + 1;
20632 }
20633
20634 // Receive all and compute sum to verify no data loss
20635 let sum = 0;
20636 let j = 0;
20637 while j < count {
20638 let val = channel_recv(ch);
20639 sum = sum + val;
20640 j = j + 1;
20641 }
20642
20643 // Sum of 0..999 = 499500
20644 return sum;
20645 }
20646 "#);
20647 assert!(matches!(result, Ok(Value::Int(499500))));
20648 }
20649
20650 #[test]
20651 fn test_channel_data_integrity() {
20652 let result = eval(r#"
20654 fn main() {
20655 let ch = channel_new();
20656
20657 // Send various types
20658 channel_send(ch, 42);
20659 channel_send(ch, 3.14);
20660 channel_send(ch, "hello");
20661 channel_send(ch, [1, 2, 3]);
20662
20663 // Receive and verify types
20664 let int_val = channel_recv(ch);
20665 let float_val = channel_recv(ch);
20666 let str_val = channel_recv(ch);
20667 let arr_val = channel_recv(ch);
20668
20669 // Verify by combining results
20670 return int_val + floor(float_val) + len(str_val) + len(arr_val);
20671 }
20672 "#);
20673 assert!(matches!(result, Ok(Value::Int(53))));
20675 }
20676
20677 #[test]
20678 fn test_channel_try_recv_empty() {
20679 let result = eval(r#"
20682 fn main() {
20683 let ch = channel_new();
20684 let result = channel_try_recv(ch);
20685 // Can't pattern match variants in interpreter, so just verify it returns
20686 return type_of(result);
20687 }
20688 "#);
20689 assert!(result.is_ok());
20691 }
20692
20693 #[test]
20694 fn test_channel_try_recv_with_value() {
20695 let result = eval(r#"
20697 fn main() {
20698 let ch = channel_new();
20699 channel_send(ch, 99);
20700 // Use blocking recv since try_recv returns Option variant
20701 // which can't be pattern matched in interpreter
20702 let val = channel_recv(ch);
20703 return val;
20704 }
20705 "#);
20706 assert!(matches!(result, Ok(Value::Int(99))));
20707 }
20708
20709 #[test]
20710 fn test_channel_recv_timeout_expires() {
20711 let result = eval(r#"
20713 fn main() {
20714 let ch = channel_new();
20715 let result = channel_recv_timeout(ch, 10); // 10ms timeout
20716 // Just verify it completes without blocking forever
20717 return 42;
20718 }
20719 "#);
20720 assert!(matches!(result, Ok(Value::Int(42))));
20721 }
20722
20723 #[test]
20724 fn test_actor_basic_messaging() {
20725 let result = eval(r#"
20727 fn main() {
20728 let act = spawn_actor("test_actor");
20729 send_to_actor(act, "ping", 42);
20730 return get_actor_msg_count(act);
20731 }
20732 "#);
20733 assert!(matches!(result, Ok(Value::Int(1))));
20734 }
20735
20736 #[test]
20737 fn test_actor_message_storm() {
20738 let result = eval(r#"
20740 fn main() {
20741 let act = spawn_actor("stress_actor");
20742 let count = 10000;
20743 let i = 0;
20744 while i < count {
20745 send_to_actor(act, "msg", i);
20746 i = i + 1;
20747 }
20748 return get_actor_msg_count(act);
20749 }
20750 "#);
20751 assert!(matches!(result, Ok(Value::Int(10000))));
20752 }
20753
20754 #[test]
20755 fn test_actor_pending_count() {
20756 let result = eval(r#"
20758 fn main() {
20759 let act = spawn_actor("pending_test");
20760
20761 // Send 5 messages
20762 send_to_actor(act, "m1", 1);
20763 send_to_actor(act, "m2", 2);
20764 send_to_actor(act, "m3", 3);
20765 send_to_actor(act, "m4", 4);
20766 send_to_actor(act, "m5", 5);
20767
20768 let pending_before = get_actor_pending(act);
20769
20770 // Receive 2 messages
20771 recv_from_actor(act);
20772 recv_from_actor(act);
20773
20774 let pending_after = get_actor_pending(act);
20775
20776 // Should have 5 pending initially, 3 after receiving 2
20777 return pending_before * 10 + pending_after;
20778 }
20779 "#);
20780 assert!(matches!(result, Ok(Value::Int(53)))); }
20782
20783 #[test]
20784 fn test_actor_message_order() {
20785 let result = eval(r#"
20788 fn main() {
20789 let act = spawn_actor("order_test");
20790 send_to_actor(act, "a", 1);
20791 send_to_actor(act, "b", 2);
20792 send_to_actor(act, "c", 3);
20793
20794 // pop() gives LIFO order, so we get c, b, a
20795 let r1 = recv_from_actor(act);
20796 let r2 = recv_from_actor(act);
20797 let r3 = recv_from_actor(act);
20798
20799 // Return the message types concatenated via their first char values
20800 // c=3, b=2, a=1 in our test
20801 return get_actor_pending(act); // Should be 0 after draining
20802 }
20803 "#);
20804 assert!(matches!(result, Ok(Value::Int(0))));
20805 }
20806
20807 #[test]
20808 fn test_actor_recv_empty() {
20809 let result = eval(r#"
20812 fn main() {
20813 let act = spawn_actor("empty_actor");
20814 // No messages sent, so pending should be 0
20815 return get_actor_pending(act);
20816 }
20817 "#);
20818 assert!(matches!(result, Ok(Value::Int(0))));
20819 }
20820
20821 #[test]
20822 fn test_actor_tell_alias() {
20823 let result = eval(r#"
20825 fn main() {
20826 let act = spawn_actor("tell_test");
20827 tell_actor(act, "hello", 123);
20828 tell_actor(act, "world", 456);
20829 return get_actor_msg_count(act);
20830 }
20831 "#);
20832 assert!(matches!(result, Ok(Value::Int(2))));
20833 }
20834
20835 #[test]
20836 fn test_actor_name() {
20837 let result = eval(r#"
20839 fn main() {
20840 let act = spawn_actor("my_special_actor");
20841 return get_actor_name(act);
20842 }
20843 "#);
20844 assert!(matches!(result, Ok(Value::String(s)) if s.as_str() == "my_special_actor"));
20845 }
20846
20847 #[test]
20848 fn test_multiple_actors() {
20849 let result = eval(r#"
20851 fn main() {
20852 let a1 = spawn_actor("actor1");
20853 let a2 = spawn_actor("actor2");
20854 let a3 = spawn_actor("actor3");
20855
20856 send_to_actor(a1, "m", 1);
20857 send_to_actor(a2, "m", 1);
20858 send_to_actor(a2, "m", 2);
20859 send_to_actor(a3, "m", 1);
20860 send_to_actor(a3, "m", 2);
20861 send_to_actor(a3, "m", 3);
20862
20863 let c1 = get_actor_msg_count(a1);
20864 let c2 = get_actor_msg_count(a2);
20865 let c3 = get_actor_msg_count(a3);
20866
20867 return c1 * 100 + c2 * 10 + c3;
20868 }
20869 "#);
20870 assert!(matches!(result, Ok(Value::Int(123)))); }
20872
20873 #[test]
20874 fn test_multiple_channels() {
20875 let result = eval(r#"
20877 fn main() {
20878 let ch1 = channel_new();
20879 let ch2 = channel_new();
20880 let ch3 = channel_new();
20881
20882 channel_send(ch1, 100);
20883 channel_send(ch2, 200);
20884 channel_send(ch3, 300);
20885
20886 let v1 = channel_recv(ch1);
20887 let v2 = channel_recv(ch2);
20888 let v3 = channel_recv(ch3);
20889
20890 return v1 + v2 + v3;
20891 }
20892 "#);
20893 assert!(matches!(result, Ok(Value::Int(600))));
20894 }
20895
20896 #[test]
20897 fn test_thread_sleep() {
20898 let result = eval(r#"
20900 fn main() {
20901 thread_sleep(1); // Sleep 1ms
20902 return 42;
20903 }
20904 "#);
20905 assert!(matches!(result, Ok(Value::Int(42))));
20906 }
20907
20908 #[test]
20909 fn test_thread_yield() {
20910 let result = eval(r#"
20912 fn main() {
20913 thread_yield();
20914 return 42;
20915 }
20916 "#);
20917 assert!(matches!(result, Ok(Value::Int(42))));
20918 }
20919
20920 #[test]
20921 fn test_thread_id() {
20922 let result = eval(r#"
20924 fn main() {
20925 let id = thread_id();
20926 return len(id) > 0;
20927 }
20928 "#);
20929 assert!(matches!(result, Ok(Value::Bool(true))));
20930 }
20931
20932 #[test]
20933 fn test_channel_stress_interleaved() {
20934 let result = eval(r#"
20936 fn main() {
20937 let ch = channel_new();
20938 let sum = 0;
20939 let i = 0;
20940 while i < 100 {
20941 channel_send(ch, i);
20942 channel_send(ch, i * 2);
20943 let a = channel_recv(ch);
20944 let b = channel_recv(ch);
20945 sum = sum + a + b;
20946 i = i + 1;
20947 }
20948 // Sum: sum of i + i*2 for i in 0..99
20949 // = sum of 3*i for i in 0..99 = 3 * (99*100/2) = 3 * 4950 = 14850
20950 return sum;
20951 }
20952 "#);
20953 assert!(matches!(result, Ok(Value::Int(14850))));
20954 }
20955
20956 #[test]
20957 fn test_actor_stress_with_receive() {
20958 let result = eval(r#"
20960 fn main() {
20961 let act = spawn_actor("recv_stress");
20962 let count = 1000;
20963 let i = 0;
20964 while i < count {
20965 send_to_actor(act, "data", i);
20966 i = i + 1;
20967 }
20968
20969 // Drain all messages
20970 let drained = 0;
20971 while get_actor_pending(act) > 0 {
20972 recv_from_actor(act);
20973 drained = drained + 1;
20974 }
20975
20976 return drained;
20977 }
20978 "#);
20979 assert!(matches!(result, Ok(Value::Int(1000))));
20980 }
20981
20982 use proptest::prelude::*;
20986
20987 proptest! {
20990 #![proptest_config(ProptestConfig::with_cases(100))]
20991
20992 #[test]
20993 fn test_parser_doesnt_crash_on_random_input(s in "\\PC*") {
20994 let mut parser = Parser::new(&s);
20996 let _ = parser.parse_file(); }
20998
20999 #[test]
21000 fn test_parser_handles_unicode(s in "[\\p{L}\\p{N}\\p{P}\\s]{0,100}") {
21001 let mut parser = Parser::new(&s);
21003 let _ = parser.parse_file();
21004 }
21005
21006 #[test]
21007 fn test_parser_nested_brackets(depth in 0..20usize) {
21008 let open: String = (0..depth).map(|_| '(').collect();
21010 let close: String = (0..depth).map(|_| ')').collect();
21011 let code = format!("fn main() {{ return {}1{}; }}", open, close);
21012 let mut parser = Parser::new(&code);
21013 let _ = parser.parse_file();
21014 }
21015
21016 #[test]
21017 fn test_parser_long_identifiers(len in 1..500usize) {
21018 let ident: String = (0..len).map(|_| 'a').collect();
21020 let code = format!("fn main() {{ let {} = 1; return {}; }}", ident, ident);
21021 let result = eval(&code);
21022 assert!(matches!(result, Ok(Value::Int(1))));
21023 }
21024
21025 #[test]
21026 fn test_parser_many_arguments(count in 0..50usize) {
21027 let args: String = (0..count).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
21029 let code = format!("fn main() {{ return len([{}]); }}", args);
21030 let result = eval(&code);
21031 assert!(matches!(result, Ok(Value::Int(c)) if c == count as i64));
21032 }
21033 }
21034
21035 proptest! {
21038 #![proptest_config(ProptestConfig::with_cases(50))]
21039
21040 #[test]
21041 fn test_ga_bivector_anticommutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
21042 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
21043 let code = format!(r#"
21046 fn main() {{
21047 let a = vec3({}, {}, {});
21048 let b = vec3({}, {}, {});
21049 let ab = vec3_cross(a, b);
21050 let ba = vec3_cross(b, a);
21051 let diff_x = get(ab, 0) + get(ba, 0);
21052 let diff_y = get(ab, 1) + get(ba, 1);
21053 let diff_z = get(ab, 2) + get(ba, 2);
21054 let eps = 0.001;
21055 return eps > abs(diff_x) && eps > abs(diff_y) && eps > abs(diff_z);
21056 }}
21057 "#, x1, y1, z1, x2, y2, z2);
21058 let result = eval(&code);
21059 assert!(matches!(result, Ok(Value::Bool(true))));
21060 }
21061
21062 #[test]
21063 fn test_vec3_dot_commutative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
21064 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0) {
21065 let code = format!(r#"
21067 fn main() {{
21068 let a = vec3({}, {}, {});
21069 let b = vec3({}, {}, {});
21070 let ab = vec3_dot(a, b);
21071 let ba = vec3_dot(b, a);
21072 let eps = 0.001;
21073 return eps > abs(ab - ba);
21074 }}
21075 "#, x1, y1, z1, x2, y2, z2);
21076 let result = eval(&code);
21077 assert!(matches!(result, Ok(Value::Bool(true))));
21078 }
21079
21080 #[test]
21081 fn test_quat_identity_preserves_vector(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0) {
21082 let code = format!(r#"
21084 fn main() {{
21085 let v = vec3({}, {}, {});
21086 let q = quat_identity();
21087 let rotated = quat_rotate(q, v);
21088 let diff_x = abs(get(v, 0) - get(rotated, 0));
21089 let diff_y = abs(get(v, 1) - get(rotated, 1));
21090 let diff_z = abs(get(v, 2) - get(rotated, 2));
21091 let eps = 0.001;
21092 return eps > diff_x && eps > diff_y && eps > diff_z;
21093 }}
21094 "#, x, y, z);
21095 let result = eval(&code);
21096 assert!(matches!(result, Ok(Value::Bool(true))));
21097 }
21098
21099 #[test]
21100 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,
21101 angle in -3.14f64..3.14) {
21102 let code = format!(r#"
21104 fn main() {{
21105 let v = vec3({}, {}, {});
21106 let axis = vec3(0.0, 1.0, 0.0);
21107 let q1 = quat_from_axis_angle(axis, {});
21108 let q2 = quat_from_axis_angle(axis, {} * 2.0);
21109 let q1q1 = quat_mul(q1, q1);
21110 let eps = 0.01;
21111 let same = eps > abs(get(q2, 0) - get(q1q1, 0)) &&
21112 eps > abs(get(q2, 1) - get(q1q1, 1)) &&
21113 eps > abs(get(q2, 2) - get(q1q1, 2)) &&
21114 eps > abs(get(q2, 3) - get(q1q1, 3));
21115 let neg_same = eps > abs(get(q2, 0) + get(q1q1, 0)) &&
21116 eps > abs(get(q2, 1) + get(q1q1, 1)) &&
21117 eps > abs(get(q2, 2) + get(q1q1, 2)) &&
21118 eps > abs(get(q2, 3) + get(q1q1, 3));
21119 return same || neg_same;
21120 }}
21121 "#, x, y, z, angle, angle);
21122 let result = eval(&code);
21123 assert!(matches!(result, Ok(Value::Bool(true))));
21124 }
21125
21126 #[test]
21127 fn test_vec3_add_associative(x1 in -100.0f64..100.0, y1 in -100.0f64..100.0, z1 in -100.0f64..100.0,
21128 x2 in -100.0f64..100.0, y2 in -100.0f64..100.0, z2 in -100.0f64..100.0,
21129 x3 in -100.0f64..100.0, y3 in -100.0f64..100.0, z3 in -100.0f64..100.0) {
21130 let code = format!(r#"
21132 fn main() {{
21133 let a = vec3({}, {}, {});
21134 let b = vec3({}, {}, {});
21135 let c = vec3({}, {}, {});
21136 let ab_c = vec3_add(vec3_add(a, b), c);
21137 let a_bc = vec3_add(a, vec3_add(b, c));
21138 let diff_x = abs(get(ab_c, 0) - get(a_bc, 0));
21139 let diff_y = abs(get(ab_c, 1) - get(a_bc, 1));
21140 let diff_z = abs(get(ab_c, 2) - get(a_bc, 2));
21141 let eps = 0.001;
21142 return eps > diff_x && eps > diff_y && eps > diff_z;
21143 }}
21144 "#, x1, y1, z1, x2, y2, z2, x3, y3, z3);
21145 let result = eval(&code);
21146 assert!(matches!(result, Ok(Value::Bool(true))));
21147 }
21148
21149 #[test]
21150 fn test_vec3_scale_distributive(x in -100.0f64..100.0, y in -100.0f64..100.0, z in -100.0f64..100.0,
21151 s1 in -10.0f64..10.0, s2 in -10.0f64..10.0) {
21152 let code = format!(r#"
21154 fn main() {{
21155 let v = vec3({}, {}, {});
21156 let s1 = {};
21157 let s2 = {};
21158 let combined = vec3_scale(v, s1 + s2);
21159 let separate = vec3_add(vec3_scale(v, s1), vec3_scale(v, s2));
21160 let diff_x = abs(get(combined, 0) - get(separate, 0));
21161 let diff_y = abs(get(combined, 1) - get(separate, 1));
21162 let diff_z = abs(get(combined, 2) - get(separate, 2));
21163 let eps = 0.01;
21164 return eps > diff_x && eps > diff_y && eps > diff_z;
21165 }}
21166 "#, x, y, z, s1, s2);
21167 let result = eval(&code);
21168 assert!(matches!(result, Ok(Value::Bool(true))));
21169 }
21170 }
21171
21172 proptest! {
21175 #![proptest_config(ProptestConfig::with_cases(30))]
21176
21177 #[test]
21178 fn test_grad_of_constant_is_zero(c in -100.0f64..100.0, x in -100.0f64..100.0) {
21179 let code = format!(r#"
21181 fn main() {{
21182 fn constant(x) {{ return {}; }}
21183 let g = grad(constant, {});
21184 let eps = 0.001;
21185 return eps > abs(g);
21186 }}
21187 "#, c, x);
21188 let result = eval(&code);
21189 assert!(matches!(result, Ok(Value::Bool(true))));
21190 }
21191
21192 #[test]
21193 fn test_grad_of_x_is_one(x in -100.0f64..100.0) {
21194 let code = format!(r#"
21196 fn main() {{
21197 fn identity(x) {{ return x; }}
21198 let g = grad(identity, {});
21199 let eps = 0.001;
21200 return eps > abs(g - 1.0);
21201 }}
21202 "#, x);
21203 let result = eval(&code);
21204 assert!(matches!(result, Ok(Value::Bool(true))));
21205 }
21206
21207 #[test]
21208 fn test_grad_of_x_squared(x in -50.0f64..50.0) {
21209 let code = format!(r#"
21211 fn main() {{
21212 fn square(x) {{ return x * x; }}
21213 let g = grad(square, {});
21214 let expected = 2.0 * {};
21215 let eps = 0.1;
21216 return eps > abs(g - expected);
21217 }}
21218 "#, x, x);
21219 let result = eval(&code);
21220 assert!(matches!(result, Ok(Value::Bool(true))));
21221 }
21222
21223 #[test]
21224 fn test_grad_linearity(a in -10.0f64..10.0, b in -10.0f64..10.0, x in -10.0f64..10.0) {
21225 let code = format!(r#"
21227 fn main() {{
21228 fn linear(x) {{ return {} * x + {}; }}
21229 let g = grad(linear, {});
21230 let eps = 0.1;
21231 return eps > abs(g - {});
21232 }}
21233 "#, a, b, x, a);
21234 let result = eval(&code);
21235 assert!(matches!(result, Ok(Value::Bool(true))));
21236 }
21237 }
21238
21239 proptest! {
21242 #![proptest_config(ProptestConfig::with_cases(50))]
21243
21244 #[test]
21245 fn test_addition_commutative(a in -1000i64..1000, b in -1000i64..1000) {
21246 let code = format!("fn main() {{ return {} + {} == {} + {}; }}", a, b, b, a);
21247 let result = eval(&code);
21248 assert!(matches!(result, Ok(Value::Bool(true))));
21249 }
21250
21251 #[test]
21252 fn test_multiplication_commutative(a in -100i64..100, b in -100i64..100) {
21253 let code = format!("fn main() {{ return {} * {} == {} * {}; }}", a, b, b, a);
21254 let result = eval(&code);
21255 assert!(matches!(result, Ok(Value::Bool(true))));
21256 }
21257
21258 #[test]
21259 fn test_addition_identity(a in -1000i64..1000) {
21260 let code = format!("fn main() {{ return {} + 0 == {}; }}", a, a);
21261 let result = eval(&code);
21262 assert!(matches!(result, Ok(Value::Bool(true))));
21263 }
21264
21265 #[test]
21266 fn test_multiplication_identity(a in -1000i64..1000) {
21267 let code = format!("fn main() {{ return {} * 1 == {}; }}", a, a);
21268 let result = eval(&code);
21269 assert!(matches!(result, Ok(Value::Bool(true))));
21270 }
21271
21272 #[test]
21273 fn test_distributive_property(a in -20i64..20, b in -20i64..20, c in -20i64..20) {
21274 let code = format!("fn main() {{ return {} * ({} + {}) == {} * {} + {} * {}; }}", a, b, c, a, b, a, c);
21275 let result = eval(&code);
21276 assert!(matches!(result, Ok(Value::Bool(true))));
21277 }
21278 }
21279
21280 proptest! {
21283 #![proptest_config(ProptestConfig::with_cases(30))]
21284
21285 #[test]
21286 fn test_array_len_after_push(initial_len in 0..20usize, value in -100i64..100) {
21287 let initial: String = (0..initial_len).map(|i| format!("{}", i)).collect::<Vec<_>>().join(", ");
21288 let code = format!(r#"
21289 fn main() {{
21290 let arr = [{}];
21291 push(arr, {});
21292 return len(arr);
21293 }}
21294 "#, initial, value);
21295 let result = eval(&code);
21296 assert!(matches!(result, Ok(Value::Int(n)) if n == (initial_len + 1) as i64));
21297 }
21298
21299 #[test]
21300 fn test_reverse_reverse_identity(elements in prop::collection::vec(-100i64..100, 0..10)) {
21301 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
21302 let code = format!(r#"
21303 fn main() {{
21304 let arr = [{}];
21305 let rev1 = reverse(arr);
21306 let rev2 = reverse(rev1);
21307 let same = true;
21308 let i = 0;
21309 while i < len(arr) {{
21310 if get(arr, i) != get(rev2, i) {{
21311 same = false;
21312 }}
21313 i = i + 1;
21314 }}
21315 return same;
21316 }}
21317 "#, arr_str);
21318 let result = eval(&code);
21319 assert!(matches!(result, Ok(Value::Bool(true))));
21320 }
21321
21322 #[test]
21323 fn test_sum_equals_manual_sum(elements in prop::collection::vec(-100i64..100, 0..20)) {
21324 let arr_str = elements.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
21325 let expected_sum: i64 = elements.iter().sum();
21326 let code = format!("fn main() {{ return sum([{}]); }}", arr_str);
21327 let result = eval(&code);
21328 assert!(matches!(result, Ok(Value::Int(n)) if n == expected_sum));
21329 }
21330 }
21331
21332 #[test]
21340 fn test_no_leak_repeated_array_operations() {
21341 let result = eval(r#"
21343 fn main() {
21344 let i = 0;
21345 while i < 1000 {
21346 let arr = [1, 2, 3, 4, 5];
21347 push(arr, 6);
21348 let rev = reverse(arr);
21349 let s = sum(arr);
21350 i = i + 1;
21351 }
21352 return i;
21353 }
21354 "#);
21355 assert!(matches!(result, Ok(Value::Int(1000))));
21356 }
21357
21358 #[test]
21359 fn test_no_leak_repeated_function_calls() {
21360 let result = eval(r#"
21362 fn fib(n) {
21363 if n <= 1 { return n; }
21364 return fib(n - 1) + fib(n - 2);
21365 }
21366 fn main() {
21367 let i = 0;
21368 let total = 0;
21369 while i < 100 {
21370 total = total + fib(10);
21371 i = i + 1;
21372 }
21373 return total;
21374 }
21375 "#);
21376 assert!(matches!(result, Ok(Value::Int(5500))));
21377 }
21378
21379 #[test]
21380 fn test_no_leak_repeated_map_operations() {
21381 let result = eval(r#"
21383 fn main() {
21384 let i = 0;
21385 while i < 500 {
21386 let m = map_new();
21387 map_set(m, "key1", 1);
21388 map_set(m, "key2", 2);
21389 map_set(m, "key3", 3);
21390 let v = map_get(m, "key1");
21391 i = i + 1;
21392 }
21393 return i;
21394 }
21395 "#);
21396 assert!(matches!(result, Ok(Value::Int(500))));
21397 }
21398
21399 #[test]
21400 fn test_no_leak_repeated_string_operations() {
21401 let result = eval(r#"
21403 fn main() {
21404 let i = 0;
21405 while i < 1000 {
21406 let s = "hello world";
21407 let upper_s = upper(s);
21408 let lower_s = lower(upper_s);
21409 let concat_s = s ++ " " ++ upper_s;
21410 let replaced = replace(concat_s, "o", "0");
21411 i = i + 1;
21412 }
21413 return i;
21414 }
21415 "#);
21416 assert!(matches!(result, Ok(Value::Int(1000))));
21417 }
21418
21419 #[test]
21420 fn test_no_leak_repeated_ecs_operations() {
21421 let result = eval(r#"
21423 fn main() {
21424 let world = ecs_world();
21425 let i = 0;
21426 while i < 500 {
21427 let entity = ecs_spawn(world);
21428 ecs_attach(world, entity, "Position", vec3(1.0, 2.0, 3.0));
21429 ecs_attach(world, entity, "Velocity", vec3(0.0, 0.0, 0.0));
21430 let pos = ecs_get(world, entity, "Position");
21431 i = i + 1;
21432 }
21433 return i;
21434 }
21435 "#);
21436 assert!(matches!(result, Ok(Value::Int(500))));
21437 }
21438
21439 #[test]
21440 fn test_no_leak_repeated_channel_operations() {
21441 let result = eval(r#"
21443 fn main() {
21444 let i = 0;
21445 while i < 500 {
21446 let ch = channel_new();
21447 channel_send(ch, i);
21448 channel_send(ch, i + 1);
21449 let v1 = channel_recv(ch);
21450 let v2 = channel_recv(ch);
21451 i = i + 1;
21452 }
21453 return i;
21454 }
21455 "#);
21456 assert!(matches!(result, Ok(Value::Int(500))));
21457 }
21458
21459 #[test]
21460 fn test_no_leak_repeated_actor_operations() {
21461 let result = eval(r#"
21463 fn main() {
21464 let i = 0;
21465 while i < 100 {
21466 let act = spawn_actor("leak_test_actor");
21467 send_to_actor(act, "msg", i);
21468 send_to_actor(act, "msg", i + 1);
21469 let count = get_actor_msg_count(act);
21470 i = i + 1;
21471 }
21472 return i;
21473 }
21474 "#);
21475 assert!(matches!(result, Ok(Value::Int(100))));
21476 }
21477
21478 #[test]
21479 fn test_no_leak_repeated_vec3_operations() {
21480 let result = eval(r#"
21482 fn main() {
21483 let i = 0;
21484 while i < 1000 {
21485 let v1 = vec3(1.0, 2.0, 3.0);
21486 let v2 = vec3(4.0, 5.0, 6.0);
21487 let added = vec3_add(v1, v2);
21488 let scaled = vec3_scale(added, 2.0);
21489 let dot = vec3_dot(v1, v2);
21490 let crossed = vec3_cross(v1, v2);
21491 let normalized = vec3_normalize(crossed);
21492 i = i + 1;
21493 }
21494 return i;
21495 }
21496 "#);
21497 assert!(matches!(result, Ok(Value::Int(1000))));
21498 }
21499
21500 #[test]
21501 fn test_no_leak_repeated_closure_creation() {
21502 let result = eval(r#"
21504 fn main() {
21505 let i = 0;
21506 let total = 0;
21507 while i < 500 {
21508 let x = i;
21509 fn add_x(y) { return x + y; }
21510 total = total + add_x(1);
21511 i = i + 1;
21512 }
21513 return total;
21514 }
21515 "#);
21516 assert!(matches!(result, Ok(Value::Int(125250))));
21518 }
21519
21520 #[test]
21521 fn test_no_leak_nested_data_structures() {
21522 let result = eval(r#"
21524 fn main() {
21525 let i = 0;
21526 while i < 200 {
21527 let inner1 = [1, 2, 3];
21528 let inner2 = [4, 5, 6];
21529 let outer = [inner1, inner2];
21530 let m = map_new();
21531 map_set(m, "arr", outer);
21532 map_set(m, "nested", map_new());
21533 i = i + 1;
21534 }
21535 return i;
21536 }
21537 "#);
21538 assert!(matches!(result, Ok(Value::Int(200))));
21539 }
21540
21541 #[test]
21542 fn test_no_leak_repeated_interpreter_creation() {
21543 for _ in 0..50 {
21545 let result = eval(r#"
21546 fn main() {
21547 let arr = [1, 2, 3, 4, 5];
21548 let total = sum(arr);
21549 return total * 2;
21550 }
21551 "#);
21552 assert!(matches!(result, Ok(Value::Int(30))));
21553 }
21554 }
21555}