1use crate::{
6 dict::{dict_new, dict_path_insert, dict_path_remove, list_to_dict},
7 interp::Interp,
8 types::*,
9 util, *,
10};
11use std::fs;
12cfg_if::cfg_if! {
13 if #[cfg(feature = "wasm")] {
14 use wasm_timer::Instant;
15 }else{
16 use std::time::Instant;
17 }
18}
19
20pub const _APPEND: &str = "append";
21pub const _ARRAY: &str = "array";
22pub const _ASSERT_EQ: &str = "assert_eq";
23pub const _BREAK: &str = "break";
24pub const _CATCH: &str = "catch";
25pub const _CONTINUE: &str = "continue";
26pub const _DICT: &str = "dict";
27pub const _ERROR: &str = "error";
28pub const _EXPR: &str = "expr";
29pub const _FOR: &str = "for";
30pub const _FOREACH: &str = "foreach";
31pub const _GLOBAL: &str = "global";
32pub const _IF: &str = "if";
33pub const _INCR: &str = "incr";
34pub const _INFO: &str = "info";
35pub const _JOIN: &str = "join";
36pub const _LAPPEND: &str = "lappend";
37pub const _LINDEX: &str = "lindex";
38pub const _LIST: &str = "list";
39pub const _LLENGTH: &str = "llength";
40pub const _PROC: &str = "proc";
41pub const _PUTS: &str = "puts";
42pub const _RENAME: &str = "rename";
43pub const _RETURN: &str = "return";
44pub const _SET: &str = "set";
45pub const _STRING: &str = "string";
46pub const _THROW: &str = "throw";
47pub const _TIME: &str = "time";
48pub const _UNSET: &str = "unset";
49pub const _WHILE: &str = "while";
50pub const _SOURCE: &str = "source";
51pub const _EXIT: &str = "exit";
52pub const _PARSE: &str = "parse";
53pub const _PDUMP: &str = "pdump";
54pub const _PCLEAR: &str = "pclear";
55
56pub fn cmd_append<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
61 check_args(1, argv, 2, 0, "varName ?value value ...?")?;
62
63 let mut new_string: String = interp
66 .var(&argv[1])
67 .and_then(|val| Ok(val.to_string()))
68 .unwrap_or_else(|_| String::new());
69
70 for item in &argv[2..] {
72 new_string.push_str(item.as_str());
73 }
74
75 interp.set_var_return(&argv[1], new_string.into())
77}
78
79pub fn cmd_array<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
83 let f = _gen_subcommand_generic!(
104 1,
105 [
106 ("exists", cmd_array_exists),
107 ("get", cmd_array_get),
108 ("names", cmd_array_names),
109 ("set", cmd_array_set),
110 ("size", cmd_array_size),
111 ("unset", cmd_array_unset),
112 ],
113 );
114 f(interp, argv)
115}
116
117pub fn cmd_array_exists<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
119 check_args(2, argv, 3, 3, "arrayName")?;
120 molt_ok!(Value::from(interp.array_exists(argv[2].as_str())))
121}
122
123pub fn cmd_array_names<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
126 check_args(2, argv, 3, 3, "arrayName")?;
127 molt_ok!(Value::from(interp.array_names(argv[2].as_str())))
128}
129
130pub fn cmd_array_get<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
133 check_args(2, argv, 3, 3, "arrayName")?;
134 molt_ok!(Value::from(interp.array_get(argv[2].as_str())))
135}
136
137pub fn cmd_parse<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
142 check_args(1, argv, 2, 2, "script")?;
143
144 let script = &argv[1];
145
146 molt_ok!(format!("{:?}", parser::parse(script.as_str())?))
147}
148
149pub fn cmd_array_set<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
151 check_args(2, argv, 4, 4, "arrayName list")?;
152
153 let var_name = argv[2].as_var_name();
157
158 if var_name.index().is_none() {
159 interp.array_set(var_name.name(), &*argv[3].as_list()?)
160 } else {
161 interp.array_set(var_name.name(), &*Value::empty().as_list()?)?;
165
166 molt_err!("can't set \"{}\": variable isn't array", &argv[2])
169 }
170}
171
172pub fn cmd_array_size<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
174 check_args(2, argv, 3, 3, "arrayName")?;
175 molt_ok!(Value::from(interp.array_size(argv[2].as_str()) as MoltInt))
176}
177
178pub fn cmd_array_unset<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
180 check_args(2, argv, 3, 4, "arrayName ?index?")?;
181
182 if argv.len() == 3 {
183 interp.array_unset(argv[2].as_str());
184 } else {
185 interp.unset_element(argv[2].as_str(), argv[3].as_str());
186 }
187 molt_ok!()
188}
189
190pub fn cmd_assert_eq<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
195 check_args(1, argv, 3, 3, "received expected")?;
196
197 if argv[1] == argv[2] {
198 molt_ok!()
199 } else {
200 molt_err!("assertion failed: received \"{}\", expected \"{}\".", argv[1], argv[2])
201 }
202}
203
204pub fn cmd_break<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
209 check_args(1, argv, 1, 1, "")?;
210
211 Err(Exception::molt_break())
212}
213
214pub fn cmd_catch<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
220 check_args(1, argv, 2, 4, "script ?resultVarName? ?optionsVarName?")?;
221
222 let result = interp.eval_value(&argv[1]);
224
225 let (code, value) = match &result {
226 Ok(val) => (0, val.clone()),
227 Err(exception) => match exception.code() {
228 ResultCode::Okay => unreachable!(), ResultCode::Error => (1, exception.value()),
230 ResultCode::Return => (2, exception.value()),
231 ResultCode::Break => (3, exception.value()),
232 ResultCode::Continue => (4, exception.value()),
233 ResultCode::Other(_) => unimplemented!(), },
235 };
236
237 if argv.len() >= 3 {
238 interp.set_var(&argv[2], value)?;
239 }
240
241 if argv.len() == 4 {
242 interp.set_var(&argv[3], interp.return_options(&result))?;
243 }
244
245 Ok(Value::from(code))
246}
247
248pub fn cmd_continue<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
252 check_args(1, argv, 1, 1, "")?;
253
254 Err(Exception::molt_continue())
255}
256
257pub fn cmd_dict<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
261 let f = _gen_subcommand_generic!(
294 1,
295 [
296 ("create", cmd_dict_new),
297 ("exists", cmd_dict_exists),
298 ("get", cmd_dict_get),
299 ("keys", cmd_dict_keys),
300 ("remove", cmd_dict_remove),
301 ("set", cmd_dict_set),
302 ("size", cmd_dict_size),
303 ("unset", cmd_dict_unset),
304 ("values", cmd_dict_values),
305 ],
306 );
307
308 f(interp, argv)
311}
312
313fn cmd_dict_new<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
315 if argv.len() % 2 != 0 {
317 return molt_err!(
318 "wrong # args: should be \"{} {}\"",
319 Value::from(&argv[0..2]).to_string(),
320 "?key value?"
321 );
322 }
323
324 if argv.len() > 2 {
326 molt_ok!(Value::from(list_to_dict(&argv[2..])))
327 } else {
328 molt_ok!(Value::from(dict_new()))
329 }
330}
331
332fn cmd_dict_exists<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
334 check_args(2, argv, 4, 0, "dictionary key ?key ...?")?;
335
336 let mut value: Value = argv[2].clone();
337 let indices = &argv[3..];
338
339 for index in indices {
340 if let Ok(dict) = value.as_dict() {
341 if let Some(val) = dict.get(index) {
342 value = val.clone();
343 } else {
344 return molt_ok!(false);
345 }
346 } else {
347 return molt_ok!(false);
348 }
349 }
350
351 molt_ok!(true)
352}
353
354fn cmd_dict_get<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
356 check_args(2, argv, 3, 0, "dictionary ?key ...?")?;
357
358 let mut value: Value = argv[2].clone();
359 let indices = &argv[3..];
360
361 for index in indices {
362 let dict = value.as_dict()?;
363
364 if let Some(val) = dict.get(index) {
365 value = val.clone();
366 } else {
367 return molt_err!("key \"{}\" not known in dictionary", index);
368 }
369 }
370
371 molt_ok!(value)
372}
373
374fn cmd_dict_keys<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
377 check_args(2, argv, 3, 3, "dictionary")?;
378
379 let dict = argv[2].as_dict()?;
380 let keys: MoltList = dict.keys().cloned().collect();
381 molt_ok!(keys)
382}
383
384fn cmd_dict_remove<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
386 check_args(2, argv, 3, 0, "dictionary ?key ...?")?;
387
388 let mut dict = (&*argv[2].as_dict()?).clone();
390
391 for key in &argv[3..] {
393 dict.shift_remove(key);
395 }
396
397 molt_ok!(dict)
399}
400
401fn cmd_dict_set<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
403 check_args(2, argv, 5, 0, "dictVarName key ?key ...? value")?;
404
405 let value = &argv[argv.len() - 1];
406 let keys = &argv[3..(argv.len() - 1)];
407
408 if let Ok(old_dict_val) = interp.var(&argv[2]) {
409 interp.set_var_return(&argv[2], dict_path_insert(&old_dict_val, keys, value)?)
410 } else {
411 let new_val = Value::from(dict_new());
412 interp.set_var_return(&argv[2], dict_path_insert(&new_val, keys, value)?)
413 }
414}
415
416fn cmd_dict_size<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
418 check_args(2, argv, 3, 3, "dictionary")?;
419
420 let dict = argv[2].as_dict()?;
421 molt_ok!(dict.len() as MoltInt)
422}
423
424fn cmd_dict_unset<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
426 check_args(2, argv, 4, 0, "dictVarName key ?key ...?")?;
427
428 let keys = &argv[3..];
429
430 if let Ok(old_dict_val) = interp.var(&argv[2]) {
431 interp.set_var_return(&argv[2], dict_path_remove(&old_dict_val, keys)?)
432 } else {
433 let new_val = Value::from(dict_new());
434 interp.set_var_return(&argv[2], dict_path_remove(&new_val, keys)?)
435 }
436}
437
438fn cmd_dict_values<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
441 check_args(2, argv, 3, 3, "dictionary")?;
442
443 let dict = argv[2].as_dict()?;
444 let values: MoltList = dict.values().cloned().collect();
445 molt_ok!(values)
446}
447
448pub fn cmd_error<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
456 check_args(1, argv, 2, 2, "message")?;
457
458 molt_err!(argv[1].clone())
459}
460
461pub fn cmd_exit<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
467 check_args(1, argv, 1, 2, "?returnCode?")?;
468
469 let return_code: MoltInt = if argv.len() == 1 { 0 } else { argv[1].as_int()? };
470
471 std::process::exit(return_code as i32)
472}
473
474pub fn cmd_expr<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
483 check_args(1, argv, 2, 0, "expr")?;
484
485 if argv.len() == 2 {
486 interp.expr(&argv[1])
487 } else {
488 let values = argv[1..].iter().map(|v| v.as_str()).collect::<Vec<_>>();
489 interp.expr(&Value::from(values.join(" ")))
490 }
491}
492
493pub fn cmd_for<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
498 check_args(1, argv, 5, 5, "start test next command")?;
499
500 let start = &argv[1];
501 let test = &argv[2];
502 let next = &argv[3];
503 let command = &argv[4];
504
505 interp.eval_value(start)?;
507
508 while interp.expr_bool(test)? {
509 let result = interp.eval_value(command);
510
511 if let Err(exception) = result {
512 match exception.code() {
513 ResultCode::Break => break,
514 ResultCode::Continue => (),
515 _ => return Err(exception),
516 }
517 }
518
519 let result = interp.eval_value(next);
521
522 if let Err(exception) = result {
523 match exception.code() {
524 ResultCode::Break => break,
525 ResultCode::Continue => {
526 return molt_err!("invoked \"continue\" outside of a loop");
527 }
528 _ => return Err(exception),
529 }
530 }
531 }
532
533 molt_ok!()
534}
535
536pub fn cmd_foreach<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
547 check_args(1, argv, 4, 4, "varList list body")?;
548
549 let var_list = &*argv[1].as_list()?;
550 let list = &*argv[2].as_list()?;
551 let body = &argv[3];
552
553 let mut i = 0;
554
555 while i < list.len() {
556 for var in var_list {
557 if i < list.len() {
558 interp.set_var(&var, list[i].clone())?;
559 i += 1;
560 } else {
561 interp.set_var(&var, Value::empty())?;
562 }
563 }
564
565 let result = interp.eval_value(body);
566
567 if let Err(exception) = result {
568 match exception.code() {
569 ResultCode::Break => break,
570 ResultCode::Continue => (),
571 _ => return Err(exception),
572 }
573 }
574 }
575
576 molt_ok!()
577}
578
579pub fn cmd_global<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
584 if interp.scope_level() > 0 {
588 for name in &argv[1..] {
589 interp.upvar(0, name.as_str());
591 }
592 }
593 molt_ok!()
594}
595
596#[derive(Eq, PartialEq, Debug)]
597enum IfWants {
598 Expr,
599 ThenBody,
600 SkipThenClause,
601 ElseClause,
602 ElseBody,
603}
604
605pub fn cmd_if<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
615 let mut argi = 1;
616 let mut wants = IfWants::Expr;
617
618 while argi < argv.len() {
619 match wants {
620 IfWants::Expr => {
621 wants = if interp.expr_bool(&argv[argi])? {
622 IfWants::ThenBody
623 } else {
624 IfWants::SkipThenClause
625 };
626 }
627 IfWants::ThenBody => {
628 if argv[argi].as_str() == "then" {
629 argi += 1;
630 }
631
632 if argi < argv.len() {
633 return interp.eval_value(&argv[argi]);
634 } else {
635 break;
636 }
637 }
638 IfWants::SkipThenClause => {
639 if argv[argi].as_str() == "then" {
640 argi += 1;
641 }
642
643 if argi < argv.len() {
644 argi += 1;
645 wants = IfWants::ElseClause;
646 }
647 continue;
648 }
649 IfWants::ElseClause => {
650 if argv[argi].as_str() == "elseif" {
651 wants = IfWants::Expr;
652 } else {
653 wants = IfWants::ElseBody;
654 continue;
655 }
656 }
657 IfWants::ElseBody => {
658 if argv[argi].as_str() == "else" {
659 argi += 1;
660
661 if argi == argv.len() {
663 return molt_err!(
664 "wrong # args: no script following after \"{}\" argument",
665 argv[argi - 1]
666 );
667 }
668 }
669
670 if argi < argv.len() {
671 return interp.eval_value(&argv[argi]);
672 } else {
673 break;
674 }
675 }
676 }
677
678 argi += 1;
679 }
680
681 if argi < argv.len() {
682 return molt_err!(
683 "wrong # args: extra words after \"else\" clause in \"if\" command"
684 );
685 } else if wants == IfWants::Expr {
686 return molt_err!(
687 "wrong # args: no expression after \"{}\" argument",
688 argv[argi - 1]
689 );
690 } else if wants == IfWants::ThenBody || wants == IfWants::SkipThenClause {
691 return molt_err!(
692 "wrong # args: no script following after \"{}\" argument",
693 argv[argi - 1]
694 );
695 } else {
696 molt_ok!() }
699}
700
701pub fn cmd_incr<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
705 check_args(1, argv, 2, 3, "varName ?increment?")?;
706
707 let increment: MoltInt = if argv.len() == 3 { argv[2].as_int()? } else { 1 };
708
709 let new_value = increment
710 + interp
711 .var(&argv[1])
712 .and_then(|val| Ok(val.as_int()?))
713 .unwrap_or_else(|_| 0);
714
715 interp.set_var_return(&argv[1], new_value.into())
716}
717
718pub fn cmd_info<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
720 let f = _gen_subcommand_generic!(
721 1,
722 [
723 ("args", cmd_info_args),
724 ("body", cmd_info_body),
725 ("cmdtype", cmd_info_cmdtype),
726 ("commands", cmd_info_commands),
727 ("complete", cmd_info_complete),
728 ("default", cmd_info_default),
729 ("exists", cmd_info_exists),
730 ("globals", cmd_info_globals),
731 ("locals", cmd_info_locals),
732 ("procs", cmd_info_procs),
733 ("vars", cmd_info_vars),
734 ],
735 );
736 f(interp, argv)
737}
738
739pub fn cmd_info_args<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
741 check_args(2, argv, 3, 3, "procname")?;
742 interp.proc_args(&argv[2].as_str())
743}
744
745pub fn cmd_info_body<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
747 check_args(2, argv, 3, 3, "procname")?;
748 interp.proc_body(&argv[2].as_str())
749}
750
751pub fn cmd_info_cmdtype<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
753 check_args(2, argv, 3, 3, "command")?;
754 interp.command_type(&argv[2].as_str())
755}
756
757pub fn cmd_info_commands<Ctx>(interp: &mut Interp<Ctx>, _argv: &[Value]) -> MoltResult {
759 molt_ok!(Value::from(interp.command_names()))
760}
761
762pub fn cmd_info_default<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
764 check_args(2, argv, 5, 5, "procname arg varname")?;
765
766 if let Some(val) = interp.proc_default(&argv[2].as_str(), &argv[3].as_str())? {
767 interp.set_var(&argv[4], val)?;
768 molt_ok!(1)
769 } else {
770 interp.set_var(&argv[4], Value::empty())?;
771 molt_ok!(0)
772 }
773}
774
775pub fn cmd_info_exists<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
777 check_args(2, argv, 3, 3, "varname")?;
778 Ok(interp.var_exists(&argv[2]).into())
779}
780
781pub fn cmd_info_complete<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
783 check_args(2, argv, 3, 3, "command")?;
784
785 if interp.complete(argv[2].as_str()) {
786 molt_ok!(true)
787 } else {
788 molt_ok!(false)
789 }
790}
791
792pub fn cmd_info_globals<Ctx>(interp: &mut Interp<Ctx>, _argv: &[Value]) -> MoltResult {
795 molt_ok!(Value::from(interp.vars_in_global_scope()))
796}
797
798pub fn cmd_info_locals<Ctx>(interp: &mut Interp<Ctx>, _argv: &[Value]) -> MoltResult {
801 molt_ok!(Value::from(interp.vars_in_local_scope()))
802}
803
804pub fn cmd_info_procs<Ctx>(interp: &mut Interp<Ctx>, _argv: &[Value]) -> MoltResult {
806 molt_ok!(Value::from(interp.proc_names()))
807}
808
809pub fn cmd_info_vars<Ctx>(interp: &mut Interp<Ctx>, _argv: &[Value]) -> MoltResult {
812 molt_ok!(Value::from(interp.vars_in_scope()))
813}
814
815pub fn cmd_join<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
819 check_args(1, argv, 2, 3, "list ?joinString?")?;
820
821 let list = &argv[1].as_list()?;
822
823 let join_string = if argv.len() == 3 { argv[2].to_string() } else { " ".to_string() };
824
825 let list: Vec<String> = list.iter().map(|v| v.to_string()).collect();
827
828 molt_ok!(list.join(&join_string))
829}
830
831pub fn cmd_lappend<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
836 check_args(1, argv, 2, 0, "varName ?value ...?")?;
837
838 let var_result = interp.var(&argv[1]);
839
840 let mut list: MoltList = if var_result.is_ok() {
841 var_result.expect("got value").to_list()?
842 } else {
843 Vec::new()
844 };
845
846 let mut values = argv[2..].to_owned();
847 list.append(&mut values);
848 interp.set_var_return(&argv[1], Value::from(list))
849}
850
851pub fn cmd_lindex<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
855 check_args(1, argv, 2, 0, "list ?index ...?")?;
856
857 if argv.len() != 3 {
858 lindex_into(&argv[1], &argv[2..])
859 } else {
860 lindex_into(&argv[1], &*argv[2].as_list()?)
861 }
862}
863
864pub fn lindex_into(list: &Value, indices: &[Value]) -> MoltResult {
865 let mut value: Value = list.clone();
866
867 for index_val in indices {
868 let list = value.as_list()?;
869 let index = index_val.as_int()?;
870
871 value = if index < 0 || index as usize >= list.len() {
872 Value::empty()
873 } else {
874 list[index as usize].clone()
875 };
876 }
877
878 molt_ok!(value)
879}
880
881pub fn cmd_list<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
885 molt_ok!(&argv[1..])
887}
888
889pub fn cmd_llength<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
893 check_args(1, argv, 2, 2, "list")?;
894
895 molt_ok!(argv[1].as_list()?.len() as MoltInt)
896}
897
898pub fn cmd_pdump<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
902 check_args(1, argv, 1, 1, "")?;
903
904 interp.profile_dump();
905
906 molt_ok!()
907}
908
909pub fn cmd_pclear<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
913 check_args(1, argv, 1, 1, "")?;
914
915 interp.profile_clear();
916
917 molt_ok!()
918}
919
920pub fn cmd_proc<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
924 check_args(1, argv, 4, 4, "name args body")?;
925
926 let name = argv[1].as_str();
928 let args = &*argv[2].as_list()?;
929
930 for arg in args {
932 let vec = arg.as_list()?;
933
934 if vec.is_empty() {
935 return molt_err!("argument with no name");
936 } else if vec.len() > 2 {
937 return molt_err!("too many fields in argument specifier \"{}\"", arg);
938 }
939 }
940
941 interp.add_proc(name, args, &argv[3]);
943
944 molt_ok!()
945}
946
947pub fn cmd_puts<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
956 check_args(1, argv, 2, 2, "string")?;
957 cfg_if::cfg_if! {
958 if #[cfg(feature = "std_buff")] {
959 interp.std_buff.push(Ok(argv[1].clone()));
960 } else {
961 println!("{}", argv[1]);
962 }
963 }
964 molt_ok!()
965}
966
967pub fn cmd_rename<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
972 check_args(1, argv, 3, 3, "oldName newName")?;
973
974 let old_name = argv[1].as_str();
976 let new_name = argv[2].as_str();
977
978 if !interp.has_proc(old_name) {
979 return molt_err!("can't rename \"{}\": command doesn't exist", old_name);
980 }
981
982 if new_name.is_empty() {
984 interp.remove_proc(old_name);
985 } else {
986 interp.rename_proc(old_name, new_name);
987 }
988
989 molt_ok!()
990}
991
992pub fn cmd_return<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1001 check_args(1, argv, 1, 0, "?options...? ?value?")?;
1002
1003 let mut code = ResultCode::Okay;
1005 let mut level: MoltInt = 1;
1006 let mut error_code: Option<Value> = None;
1007 let mut error_info: Option<Value> = None;
1008
1009 if argv.len() == 1 {
1011 return Err(Exception::molt_return_ext(Value::empty(), level as usize, code));
1012 }
1013
1014 let return_value: Value;
1017
1018 let opt_args: &[Value] = if argv.len() % 2 == 0 {
1019 return_value = argv[argv.len() - 1].clone();
1021 &argv[1..argv.len() - 1]
1022 } else {
1023 return_value = Value::empty();
1025 &argv[1..argv.len()]
1026 };
1027
1028 let mut queue = opt_args.iter();
1030
1031 while let Some(opt) = queue.next() {
1032 let val = queue
1035 .next()
1036 .expect("missing option value: coding error in cmd_return");
1037
1038 match opt.as_str() {
1039 "-code" => {
1040 code = ResultCode::from_value(val)?;
1041 }
1042 "-errorcode" => {
1043 error_code = Some(val.clone());
1044 }
1045 "-errorinfo" => {
1046 error_info = Some(val.clone());
1047 }
1048 "-level" => {
1049 level = val.as_int()?;
1052 }
1053 _ => return molt_err!("invalid return option: \"{}\"", opt),
1055 }
1056 }
1057
1058 if code == ResultCode::Error {
1060 Err(Exception::molt_return_err(
1061 return_value,
1062 level as usize,
1063 error_code,
1064 error_info,
1065 ))
1066 } else if level == 0 && code == ResultCode::Okay {
1067 Ok(return_value)
1069 } else {
1070 Err(Exception::molt_return_ext(return_value, level as usize, code))
1071 }
1072}
1073
1074pub fn cmd_set<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1080 check_args(1, argv, 2, 3, "varName ?newValue?")?;
1081
1082 if argv.len() == 3 {
1083 interp.set_var_return(&argv[1], argv[2].clone())
1084 } else {
1085 molt_ok!(interp.var(&argv[1])?)
1086 }
1087}
1088
1089pub fn cmd_source<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1093 check_args(1, argv, 2, 2, "filename")?;
1094
1095 let filename = argv[1].as_str();
1096
1097 match fs::read_to_string(filename) {
1098 Ok(script) => interp.eval(&script),
1099 Err(e) => molt_err!("couldn't read file \"{}\": {}", filename, e),
1100 }
1101}
1102
1103pub fn cmd_string<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1107 let f = _gen_subcommand_generic!(
1141 1,
1142 [
1143 ("cat", cmd_string_cat),
1144 ("compare", cmd_string_compare),
1145 ("equal", cmd_string_equal),
1146 ("first", cmd_string_first),
1147 ("last", cmd_string_last),
1149 ("length", cmd_string_length),
1150 ("map", cmd_string_map),
1151 ("range", cmd_string_range),
1152 ("tolower", cmd_string_tolower),
1156 ("toupper", cmd_string_toupper),
1157 ("trim", cmd_string_trim),
1158 ("trimleft", cmd_string_trim),
1159 ("trimright", cmd_string_trim),
1160 ],
1161 );
1162 f(interp, argv)
1165}
1166
1167pub fn cmd_todo<Ctx>(_interp: &mut Interp<Ctx>, _argv: &[Value]) -> MoltResult {
1169 molt_err!("TODO")
1170}
1171
1172pub fn cmd_string_cat<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1174 let mut buff = String::new();
1175
1176 for arg in &argv[2..] {
1177 buff.push_str(arg.as_str());
1178 }
1179
1180 molt_ok!(buff)
1181}
1182
1183pub fn cmd_string_compare<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1185 check_args(2, argv, 4, 7, "?-nocase? ?-length length? string1 string2")?;
1186
1187 let arglen = argv.len();
1189 let mut nocase = false;
1190 let mut length: Option<MoltInt> = None;
1191
1192 let opt_args = &argv[2..arglen - 2];
1194 let mut queue = opt_args.iter();
1195
1196 while let Some(opt) = queue.next() {
1197 match opt.as_str() {
1198 "-nocase" => nocase = true,
1199 "-length" => {
1200 if let Some(val) = queue.next() {
1201 length = Some(val.as_int()?);
1202 } else {
1203 return molt_err!("wrong # args: should be \"string compare ?-nocase? ?-length length? string1 string2\"");
1204 }
1205 }
1206 _ => return molt_err!("bad option \"{}\": must be -nocase or -length", opt),
1207 }
1208 }
1209
1210 if nocase {
1211 let val1 = &argv[arglen - 2];
1212 let val2 = &argv[arglen - 1];
1213
1214 let val1 = Value::from(val1.as_str().to_lowercase());
1216 let val2 = Value::from(val2.as_str().to_lowercase());
1217
1218 molt_ok!(util::compare_len(val1.as_str(), val2.as_str(), length)?)
1219 } else {
1220 molt_ok!(util::compare_len(
1221 argv[arglen - 2].as_str(),
1222 argv[arglen - 1].as_str(),
1223 length
1224 )?)
1225 }
1226}
1227
1228pub fn cmd_string_equal<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1230 check_args(2, argv, 4, 7, "?-nocase? ?-length length? string1 string2")?;
1231
1232 let arglen = argv.len();
1234 let mut nocase = false;
1235 let mut length: Option<MoltInt> = None;
1236
1237 let opt_args = &argv[2..arglen - 2];
1239 let mut queue = opt_args.iter();
1240
1241 while let Some(opt) = queue.next() {
1242 match opt.as_str() {
1243 "-nocase" => nocase = true,
1244 "-length" => {
1245 if let Some(val) = queue.next() {
1246 length = Some(val.as_int()?);
1247 } else {
1248 return molt_err!("wrong # args: should be \"string equal ?-nocase? ?-length length? string1 string2\"");
1249 }
1250 }
1251 _ => return molt_err!("bad option \"{}\": must be -nocase or -length", opt),
1252 }
1253 }
1254
1255 if nocase {
1256 let val1 = &argv[arglen - 2];
1257 let val2 = &argv[arglen - 1];
1258
1259 let val1 = Value::from(val1.as_str().to_lowercase());
1261 let val2 = Value::from(val2.as_str().to_lowercase());
1262
1263 let flag = util::compare_len(val1.as_str(), val2.as_str(), length)? == 0;
1264 molt_ok!(flag)
1265 } else {
1266 let flag = util::compare_len(
1267 argv[arglen - 2].as_str(),
1268 argv[arglen - 1].as_str(),
1269 length,
1270 )? == 0;
1271 molt_ok!(flag)
1272 }
1273}
1274
1275pub fn cmd_string_first<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1277 check_args(2, argv, 4, 5, "needleString haystackString ?startIndex?")?;
1278
1279 let needle = argv[2].as_str();
1280 let haystack = argv[3].as_str();
1281
1282 let start_char: usize = if argv.len() == 5 {
1283 let arg = argv[4].as_int()?;
1284
1285 if arg < 0 {
1286 0
1287 } else {
1288 arg as usize
1289 }
1290 } else {
1291 0
1292 };
1293
1294 let pos_byte: Option<usize> = haystack
1295 .char_indices()
1296 .nth(start_char)
1297 .and_then(|(start_byte, _)| haystack[start_byte..].find(needle));
1298
1299 let pos_char: MoltInt = match pos_byte {
1300 None => -1,
1301 Some(b) => {
1302 haystack[b..].char_indices().take_while(|(i, _)| *i < b).count() as MoltInt
1303 + start_char as MoltInt
1304 }
1305 };
1306
1307 molt_ok!(pos_char)
1308}
1309
1310pub fn cmd_string_last<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1312 check_args(2, argv, 4, 5, "needleString haystackString ?lastIndex?")?;
1313
1314 let needle = argv[2].as_str();
1315 let haystack = argv[3].as_str();
1316
1317 let count = haystack.chars().count();
1318
1319 let last: Option<usize> = if argv.len() == 5 {
1320 let arg = argv[4].as_int()?;
1321
1322 if arg < 0 {
1323 return molt_ok!(-1);
1324 }
1325
1326 if arg as usize >= count {
1327 None
1328 } else {
1329 Some(arg as usize)
1330 }
1331 } else {
1332 None
1333 };
1334
1335 let slice = match last {
1336 None => haystack,
1337 Some(n) => match haystack.char_indices().nth(n + 1) {
1338 None => haystack,
1339 Some((byte, _)) => &haystack[..byte],
1340 },
1341 };
1342
1343 let pos_byte = slice.rfind(needle);
1344
1345 let pos_char: MoltInt = match pos_byte {
1346 None => -1,
1347 Some(b) => haystack.char_indices().take_while(|(i, _)| *i < b).count() as MoltInt,
1348 };
1349
1350 molt_ok!(pos_char)
1351}
1352
1353pub fn cmd_string_length<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1355 check_args(2, argv, 3, 3, "string")?;
1356
1357 let len: MoltInt = argv[2].as_str().chars().count() as MoltInt;
1358 molt_ok!(len)
1359}
1360
1361pub fn cmd_string_map<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1363 check_args(2, argv, 4, 5, "?-nocase? charMap string")?;
1364
1365 let mut nocase = false;
1366
1367 if argv.len() == 5 {
1368 let opt = argv[2].as_str();
1369
1370 if opt == "-nocase" {
1371 nocase = true;
1372 } else {
1373 return molt_err!("bad option \"{}\": must be -nocase", opt);
1374 }
1375 }
1376
1377 let char_map = argv[argv.len() - 2].as_dict()?;
1378 let s = argv[argv.len() - 1].as_str();
1379
1380 let filtered_keys = char_map
1381 .iter()
1382 .map(|(k, v)| {
1383 let new_k =
1384 if nocase { Value::from(k.as_str().to_lowercase()) } else { k.clone() };
1385
1386 let count = new_k.as_str().chars().count();
1387
1388 (new_k, count, v.clone())
1389 })
1390 .filter(|(_, count, _)| *count > 0)
1391 .collect::<Vec<_>>();
1392
1393 let string_lower: Option<String> = if nocase { Some(s.to_lowercase()) } else { None };
1394
1395 let mut result = String::new();
1396 let mut skip = 0;
1397
1398 for (i, c) in s.char_indices() {
1399 if skip > 0 {
1400 skip -= 1;
1401 continue;
1402 }
1403
1404 let mut matched = false;
1405
1406 for (from, from_char_count, to) in &filtered_keys {
1407 let haystack: &str = match &string_lower {
1408 Some(x) => &x[i..],
1409 None => &s[i..],
1410 };
1411
1412 if haystack.starts_with(&from.as_str()) {
1413 matched = true;
1414
1415 result.push_str(to.as_str());
1416 skip = from_char_count - 1;
1417
1418 break;
1419 }
1420 }
1421
1422 if !matched {
1423 result.push(c);
1424 }
1425 }
1426
1427 molt_ok!(result)
1428}
1429
1430pub fn cmd_string_range<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1432 check_args(2, argv, 5, 5, "string first last")?;
1433
1434 let s = argv[2].as_str();
1435 let first = argv[3].as_int()?;
1436 let last = argv[4].as_int()?;
1437
1438 if last < 0 {
1439 return molt_ok!("");
1440 }
1441
1442 let clamp = { |i: MoltInt| if i < 0 { 0 } else { i } };
1443
1444 let substr = s
1445 .chars()
1446 .skip(clamp(first) as usize)
1447 .take((clamp(last) - clamp(first) + 1) as usize)
1448 .collect::<String>();
1449
1450 molt_ok!(substr)
1451}
1452
1453pub fn cmd_string_tolower<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1455 check_args(2, argv, 3, 3, "string")?;
1456
1457 let lower = argv[2].as_str().to_lowercase();
1458 molt_ok!(lower)
1459}
1460
1461pub fn cmd_string_toupper<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1463 check_args(2, argv, 3, 3, "string")?;
1464
1465 let upper = argv[2].as_str().to_uppercase();
1466 molt_ok!(upper)
1467}
1468
1469pub fn cmd_string_trim<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1471 check_args(2, argv, 3, 3, "string")?;
1472
1473 let s = argv[2].as_str();
1474 let trimmed = match argv[1].as_str() {
1475 "trimleft" => s.trim_start(),
1476 "trimright" => s.trim_end(),
1477 _ => s.trim(),
1478 };
1479
1480 molt_ok!(trimmed)
1481}
1482
1483pub fn cmd_throw<Ctx>(_interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1487 check_args(1, argv, 3, 3, "type message")?;
1488
1489 Err(Exception::molt_err2(argv[1].clone(), argv[2].clone()))
1490}
1491
1492pub fn cmd_time<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1497 check_args(1, argv, 2, 3, "command ?count?")?;
1498
1499 let command = &argv[1];
1500
1501 let count = if argv.len() == 3 { argv[2].as_int()? } else { 1 };
1502
1503 let start = Instant::now();
1504
1505 for _i in 0..count {
1506 let result = interp.eval_value(command);
1507 if result.is_err() {
1508 return result;
1509 }
1510 }
1511
1512 let span = start.elapsed();
1513
1514 let avg = if count > 0 { span.as_nanos() / (count as u128) } else { 0 } as MoltInt;
1515
1516 molt_ok!("{} nanoseconds per iteration", avg)
1517}
1518
1519pub fn cmd_unset<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1525 check_args(1, argv, 1, 0, "?-nocomplain? ?--? ?name name name...?")?;
1526
1527 let mut options_ok = true;
1528
1529 for arg in argv {
1530 let var = arg.as_str();
1531
1532 if options_ok {
1533 if var == "--" {
1534 options_ok = false;
1535 continue;
1536 } else if var == "-nocomplain" {
1537 continue;
1538 }
1539 }
1540
1541 interp.unset_var(arg);
1542 }
1543
1544 molt_ok!()
1545}
1546
1547pub fn cmd_while<Ctx>(interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult {
1552 check_args(1, argv, 3, 3, "test command")?;
1553
1554 while interp.expr_bool(&argv[1])? {
1555 let result = interp.eval_value(&argv[2]);
1556
1557 if let Err(exception) = result {
1558 match exception.code() {
1559 ResultCode::Break => break,
1560 ResultCode::Continue => (),
1561 _ => return Err(exception),
1562 }
1563 }
1564 }
1565
1566 molt_ok!()
1567}