1use crate::eval_ptr::EvalPtr;
7use crate::interp::Interp;
8use crate::list;
9use crate::parser::Word;
10use crate::tokenizer::Tokenizer;
11use crate::*;
12
13type DatumResult = Result<Datum, Exception>;
17
18#[derive(Debug, PartialEq, Eq, Copy, Clone)]
20pub(crate) enum Type {
21 Int,
22 Float,
23 String,
24}
25
26#[derive(Debug, PartialEq)]
36pub(crate) struct Datum {
37 vtype: Type,
38 int: MoltInt,
39 flt: MoltFloat,
40 str: String,
41}
42
43impl Datum {
44 fn none() -> Self {
45 Self {
46 vtype: Type::String,
47 int: 0,
48 flt: 0.0,
49 str: String::new(),
50 }
51 }
52
53 pub(crate) fn int(int: MoltInt) -> Self {
54 Self {
55 vtype: Type::Int,
56 int,
57 flt: 0.0,
58 str: String::new(),
59 }
60 }
61
62 pub(crate) fn float(flt: MoltFloat) -> Self {
63 Self {
64 vtype: Type::Float,
65 int: 0,
66 flt,
67 str: String::new(),
68 }
69 }
70
71 fn string(string: &str) -> Self {
72 Self {
73 vtype: Type::String,
74 int: 0,
75 flt: 0.0,
76 str: string.to_string(),
77 }
78 }
79
80 fn is_true(&self) -> bool {
82 match self.vtype {
83 Type::Int => self.int != 0,
84 _ => {
85 panic!("Datum::is_true called for non-integer");
86 }
87 }
88 }
89}
90
91const MAX_MATH_ARGS: usize = 2;
95
96#[derive(Debug, PartialEq, Eq, Copy, Clone)]
98enum ArgType {
99 None,
100 Float, Int, Number, }
104
105type MathFunc = fn(args: &[Datum; MAX_MATH_ARGS]) -> DatumResult;
106
107struct BuiltinFunc {
108 name: &'static str,
109 num_args: usize,
110 arg_types: [ArgType; MAX_MATH_ARGS],
111 func: MathFunc,
112}
113
114const FUNC_TABLE: [BuiltinFunc; 4] = [
115 BuiltinFunc {
116 name: "abs",
117 num_args: 1,
118 arg_types: [ArgType::Number, ArgType::None],
119 func: expr_abs_func,
120 },
121 BuiltinFunc {
122 name: "double",
123 num_args: 1,
124 arg_types: [ArgType::Number, ArgType::None],
125 func: expr_double_func,
126 },
127 BuiltinFunc {
128 name: "int",
129 num_args: 1,
130 arg_types: [ArgType::Number, ArgType::None],
131 func: expr_int_func,
132 },
133 BuiltinFunc {
134 name: "round",
135 num_args: 1,
136 arg_types: [ArgType::Number, ArgType::None],
137 func: expr_round_func,
138 },
139];
140
141struct ExprInfo<'a> {
146 original_expr: String,
148
149 expr: Tokenizer<'a>,
151
152 token: i32,
154
155 no_eval: i32,
157}
158
159impl<'a> ExprInfo<'a> {
160 fn new(expr: &'a str) -> Self {
161 Self {
162 original_expr: expr.to_string(),
163 expr: Tokenizer::new(expr),
164 token: -1,
165 no_eval: 0,
166 }
167 }
168}
169
170const VALUE: i32 = 0;
180const OPEN_PAREN: i32 = 1;
181const CLOSE_PAREN: i32 = 2;
182const COMMA: i32 = 3;
183const END: i32 = 4;
184const UNKNOWN: i32 = 5;
185
186const MULT: i32 = 8;
190const DIVIDE: i32 = 9;
191const MOD: i32 = 10;
192const PLUS: i32 = 11;
193const MINUS: i32 = 12;
194const LEFT_SHIFT: i32 = 13;
195const RIGHT_SHIFT: i32 = 14;
196const LESS: i32 = 15;
197const GREATER: i32 = 16;
198const LEQ: i32 = 17;
199const GEQ: i32 = 18;
200const EQUAL: i32 = 19;
201const NEQ: i32 = 20;
202const STRING_EQ: i32 = 21;
203const STRING_NE: i32 = 22;
204const IN: i32 = 23;
205const NI: i32 = 24;
206const BIT_AND: i32 = 25;
207const BIT_XOR: i32 = 26;
208const BIT_OR: i32 = 27;
209const AND: i32 = 28;
210const OR: i32 = 29;
211const QUESTY: i32 = 30;
212const COLON: i32 = 31;
213
214const UNARY_MINUS: i32 = 32;
216const UNARY_PLUS: i32 = 33;
217const NOT: i32 = 34;
218const BIT_NOT: i32 = 35;
219
220const PREC_TABLE: [i32; 36] = [
223 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 13, 13, 12, 12, 11, 11, 11, 11, 10, 10, 9, 9, 8, 8, 7, 6, 5, 4, 3, 2, 1, 13, 13, 13, 13, ];
239
240const OP_STRINGS: [&str; 36] = [
241 "VALUE", "(", ")", ",", "END", "UNKNOWN", "6", "7", "*", "/", "%", "+", "-", "<<", ">>", "<",
242 ">", "<=", ">=", "==", "!=", "eq", "ne", "in", "ni", "&", "^", "|", "&&", "||", "?", ":", "-",
243 "+", "!", "~",
244];
245
246pub fn expr(interp: &mut Interp, expr: &Value) -> MoltResult {
251 let value = expr_top_level(interp, expr.as_str())?;
252
253 match value.vtype {
254 Type::Int => molt_ok!(Value::from(value.int)),
255 Type::Float => molt_ok!(Value::from(value.flt)),
256 Type::String => molt_ok!(Value::from(value.str)),
257 }
258}
259
260fn expr_top_level<'a>(interp: &mut Interp, string: &'a str) -> DatumResult {
265 let info = &mut ExprInfo::new(string);
266
267 let result = expr_get_value(interp, info, -1);
268
269 match result {
270 Ok(value) => {
271 if info.token != END {
272 return molt_err!("syntax error in expression \"{}\"", string);
273 }
274
275 if value.vtype == Type::Float {
276 }
278
279 Ok(value)
280 }
281 Err(exception) => match exception.code() {
282 ResultCode::Break => molt_err!("invoked \"break\" outside of a loop"),
283 ResultCode::Continue => molt_err!("invoked \"continue\" outside of a loop"),
284 _ => Err(exception),
285 },
286 }
287}
288
289#[allow(clippy::collapsible_if)]
294#[allow(clippy::cognitive_complexity)]
295#[allow(clippy::float_cmp)]
296fn expr_get_value<'a>(interp: &mut Interp, info: &'a mut ExprInfo, prec: i32) -> DatumResult {
297 let mut got_op = false;
300 let mut value = expr_lex(interp, info)?;
301 let mut value2: Datum;
302 let mut operator: i32;
303
304 if info.token == OPEN_PAREN {
305 value = expr_get_value(interp, info, -1)?;
307
308 if info.token != CLOSE_PAREN {
309 return molt_err!(
310 "unmatched parentheses in expression \"{}\"",
311 info.original_expr
312 );
313 }
314 } else {
315 if info.token == MINUS {
316 info.token = UNARY_MINUS;
317 }
318
319 if info.token == PLUS {
320 info.token = UNARY_PLUS;
321 }
322
323 if info.token >= UNARY_MINUS {
324 operator = info.token;
326 value = expr_get_value(interp, info, PREC_TABLE[info.token as usize])?;
327
328 if info.no_eval == 0 {
329 match operator {
330 UNARY_MINUS => match value.vtype {
331 Type::Int => {
332 value.int = -value.int;
333 }
334 Type::Float => {
335 value.flt = -value.flt;
336 }
337 _ => {
338 return illegal_type(value.vtype, operator);
339 }
340 },
341 UNARY_PLUS => {
342 if !value.is_numeric() {
343 return illegal_type(value.vtype, operator);
344 }
345 }
346 NOT => {
347 match value.vtype {
348 Type::Int => {
349 if value.int == 0 {
352 value.int = 1;
353 } else {
354 value.int = 0;
355 }
356 }
357 Type::Float => {
358 if value.flt == 0.0 {
359 value = Datum::int(1);
360 } else {
361 value = Datum::int(0);
362 }
363 }
364 _ => {
365 return illegal_type(value.vtype, operator);
366 }
367 }
368 }
369 BIT_NOT => {
370 if let Type::Int = value.vtype {
371 value.int = !value.int;
373 } else {
374 return illegal_type(value.vtype, operator);
375 }
376 }
377 _ => {
378 return molt_err!("unknown unary op: \"{}\"", operator);
379 }
380 }
381 }
382 got_op = true;
383 } else if info.token != VALUE {
384 return syntax_error(info);
385 }
386 }
387
388 if !got_op {
391 let _ = expr_lex(interp, info)?;
395 }
396
397 loop {
398 operator = info.token;
399 if operator < MULT || operator >= UNARY_MINUS {
402 if operator == END || operator == CLOSE_PAREN || operator == COMMA {
403 return Ok(value);
404 } else {
405 return syntax_error(info);
406 }
407 }
408
409 if PREC_TABLE[operator as usize] <= prec {
410 return Ok(value);
411 }
412
413 if operator == AND || operator == OR || operator == QUESTY {
418 match value.vtype {
421 Type::Float => {
422 if value.flt == 0.0 {
423 value = Datum::int(0);
424 } else {
425 value = Datum::int(1);
426 }
427 }
428 Type::String => {
429 if info.no_eval == 0 {
430 return illegal_type(value.vtype, operator);
431 }
432 value = Datum::int(0);
433 }
434 _ => {}
435 }
436
437 if (operator == AND && !value.is_true()) || (operator == OR && value.is_true()) {
438 info.no_eval += 1;
441 let _ = expr_get_value(interp, info, PREC_TABLE[operator as usize])?;
442 info.no_eval -= 1;
443
444 if operator == OR {
445 value = Datum::int(1);
446 }
447
448 continue;
450 } else if operator == QUESTY {
451 if value.int != 0 {
455 value = expr_get_value(interp, info, PREC_TABLE[QUESTY as usize] - 1)?;
456
457 if info.token != COLON {
458 return syntax_error(info);
459 }
460
461 info.no_eval += 1;
462 value2 = expr_get_value(interp, info, PREC_TABLE[QUESTY as usize] - 1)?;
463 info.no_eval -= 1;
464 } else {
465 info.no_eval += 1;
466 value2 = expr_get_value(interp, info, PREC_TABLE[QUESTY as usize] - 1)?;
467 info.no_eval -= 1;
468
469 if info.token != COLON {
470 return syntax_error(info);
471 }
472
473 value = expr_get_value(interp, info, PREC_TABLE[QUESTY as usize] - 1)?;
474 }
475 } else {
476 value2 = expr_get_value(interp, info, PREC_TABLE[operator as usize])?;
477 }
478 } else {
479 value2 = expr_get_value(interp, info, PREC_TABLE[operator as usize])?;
480 }
481
482 if info.token < MULT
483 && info.token != VALUE
484 && info.token != END
485 && info.token != COMMA
486 && info.token != CLOSE_PAREN
487 {
488 return syntax_error(info);
489 }
490
491 if info.no_eval > 0 {
492 continue;
493 }
494
495 match operator {
500 MULT | DIVIDE | PLUS | MINUS => {
503 if value.vtype == Type::String || value2.vtype == Type::String {
504 return illegal_type(Type::String, operator);
505 }
506
507 if value.vtype == Type::Float {
508 if value2.vtype == Type::Int {
509 value2.flt = value2.int as MoltFloat;
510 value2.vtype = Type::Float;
511 }
512 } else if value2.vtype == Type::Float {
513 if value.vtype == Type::Int {
514 value.flt = value.int as MoltFloat;
515 value.vtype = Type::Float;
516 }
517 }
518 }
519
520 MOD | LEFT_SHIFT | RIGHT_SHIFT | BIT_AND | BIT_XOR | BIT_OR => {
522 if value.vtype != Type::Int {
523 return illegal_type(value.vtype, operator);
524 } else if value2.vtype != Type::Int {
525 return illegal_type(value2.vtype, operator);
526 }
527 }
528
529 LESS | GREATER | LEQ | GEQ | EQUAL | NEQ => {
532 if value.vtype == Type::String {
533 if value2.vtype != Type::String {
534 value2 = expr_as_str(value2);
535 }
536 } else if value2.vtype == Type::String {
537 if value.vtype != Type::String {
538 value = expr_as_str(value);
539 }
540 } else if value.vtype == Type::Float {
541 if value2.vtype == Type::Int {
542 value2 = Datum::float(value2.int as MoltFloat);
543 }
544 } else if value2.vtype == Type::Float {
545 if value.vtype == Type::Int {
546 value = Datum::float(value.int as MoltFloat);
547 }
548 }
549 }
550
551 STRING_EQ | STRING_NE | IN | NI => {
555 if value.vtype != Type::String {
556 value = expr_as_str(value);
557 }
558 if value2.vtype != Type::String {
559 value2 = expr_as_str(value2);
560 }
561 }
562
563 AND | OR => {
566 if value.vtype == Type::String {
567 return illegal_type(value.vtype, operator);
568 }
569 if value2.vtype == Type::String {
570 return illegal_type(value2.vtype, operator);
571 }
572 }
573
574 QUESTY | COLON => {
577 }
579
580 _ => return molt_err!("unknown operator in expression"),
581 }
582
583 match operator {
585 MULT => {
586 if value.vtype == Type::Int {
587 if let Some(int) = value.int.checked_mul(value2.int) {
589 value.int = int;
590 } else {
591 return molt_err!("integer overflow");
592 }
593 } else {
594 value.flt *= value2.flt;
595 }
596 }
597 DIVIDE => {
598 if value.vtype == Type::Int {
599 if value2.int == 0 {
600 return molt_err!("divide by zero");
601 }
602
603 if let Some(int) = value.int.checked_div(value2.int) {
604 value.int = int;
605 } else {
606 return molt_err!("integer overflow");
607 }
608 } else {
609 if value2.flt == 0.0 {
610 return molt_err!("divide by zero");
612 }
613 value.flt /= value2.flt;
614 }
615 }
616 MOD => {
617 assert!(value.vtype == Type::Int);
618
619 if value2.int == 0 {
620 return molt_err!("divide by zero");
621 }
622
623 if let Some(int) = value.int.checked_rem(value2.int) {
624 value.int = int;
625 } else {
626 return molt_err!("integer overflow");
627 }
628 }
629 PLUS => {
630 if value.vtype == Type::Int {
631 if let Some(int) = value.int.checked_add(value2.int) {
633 value.int = int;
634 } else {
635 return molt_err!("integer overflow");
636 }
637 } else {
638 value.flt += value2.flt;
639 }
640 }
641 MINUS => {
642 if value.vtype == Type::Int {
643 if let Some(int) = value.int.checked_sub(value2.int) {
645 value.int = int;
646 } else {
647 return molt_err!("integer overflow");
648 }
649 } else {
650 value.flt -= value2.flt;
651 }
652 }
653 LEFT_SHIFT => {
654 value.int <<= value2.int;
656 }
657 RIGHT_SHIFT => {
658 if value.int < 0 {
665 value.int = !((!value.int) >> value2.int)
666 } else {
667 value.int >>= value2.int;
668 }
669 }
670 LESS => {
671 let flag = match value.vtype {
672 Type::Int => value.int < value2.int,
673 Type::Float => value.flt < value2.flt,
674 Type::String => value.str < value2.str,
675 };
676
677 value = if flag { Datum::int(1) } else { Datum::int(0) };
678 }
679 GREATER => {
680 let flag = match value.vtype {
681 Type::Int => value.int > value2.int,
682 Type::Float => value.flt > value2.flt,
683 Type::String => value.str > value2.str,
684 };
685
686 value = if flag { Datum::int(1) } else { Datum::int(0) };
687 }
688 LEQ => {
689 let flag = match value.vtype {
690 Type::Int => value.int <= value2.int,
691 Type::Float => value.flt <= value2.flt,
692 Type::String => value.str <= value2.str,
693 };
694
695 value = if flag { Datum::int(1) } else { Datum::int(0) };
696 }
697 GEQ => {
698 let flag = match value.vtype {
699 Type::Int => value.int >= value2.int,
700 Type::Float => value.flt >= value2.flt,
701 Type::String => value.str >= value2.str,
702 };
703
704 value = if flag { Datum::int(1) } else { Datum::int(0) };
705 }
706 EQUAL => {
707 let flag = match value.vtype {
710 Type::Int => value.int == value2.int,
711 Type::Float => value.flt == value2.flt,
712 Type::String => value.str == value2.str,
713 };
714
715 value = if flag { Datum::int(1) } else { Datum::int(0) };
716 }
717 NEQ => {
718 let flag = match value.vtype {
721 Type::Int => value.int != value2.int,
722 Type::Float => value.flt != value2.flt,
723 Type::String => value.str != value2.str,
724 };
725
726 value = if flag { Datum::int(1) } else { Datum::int(0) };
727 }
728 STRING_EQ => {
729 value = if value.str == value2.str {
730 Datum::int(1)
731 } else {
732 Datum::int(0)
733 };
734 }
735 STRING_NE => {
736 value = if value.str != value2.str {
737 Datum::int(1)
738 } else {
739 Datum::int(0)
740 };
741 }
742 IN => {
743 let list = list::get_list(&value2.str)?;
744 value = if list.contains(&Value::from(&value.str)) {
746 Datum::int(1)
747 } else {
748 Datum::int(0)
749 };
750 }
751 NI => {
752 let list = list::get_list(&value2.str)?;
753 value = if list.contains(&Value::from(&value.str)) {
755 Datum::int(0)
756 } else {
757 Datum::int(1)
758 };
759 }
760 BIT_AND => {
761 value.int &= value2.int;
762 }
763 BIT_XOR => {
764 value.int ^= value2.int;
765 }
766 BIT_OR => {
767 value.int |= value2.int;
768 }
769
770 AND => {
774 if value2.vtype == Type::Float {
775 value2.vtype = Type::Int;
776 value2.int = if value2.flt != 0.0 { 1 } else { 0 };
777 }
778 value.int = if value.int != 0 && value2.int != 0 {
779 1
780 } else {
781 0
782 };
783 }
784 OR => {
785 if value2.vtype == Type::Float {
786 value2.vtype = Type::Int;
787 value2.int = if value2.flt != 0.0 { 1 } else { 0 };
788 }
789 value.int = if value.int != 0 || value2.int != 0 {
790 1
791 } else {
792 0
793 };
794 }
795
796 COLON => {
797 return molt_err!("can't have : operator without ? first");
798 }
799
800 _ => {
801 }
803 }
804 }
805}
806
807fn expr_lex(interp: &mut Interp, info: &mut ExprInfo) -> DatumResult {
818 let mut p = info.expr.clone();
820
821 p.skip_while(|c| c.is_whitespace());
822
823 if p.at_end() {
824 info.token = END;
825 info.expr = p;
826 return Ok(Datum::none());
827 }
828
829 if !p.is('+') && !p.is('-') {
835 if expr_looks_like_int(&p) {
836 let token = util::read_int(&mut p).unwrap();
838 let int = Value::get_int(&token)?;
839 info.token = VALUE;
840 info.expr = p;
841 return Ok(Datum::int(int));
842 } else if let Some(token) = util::read_float(&mut p) {
843 info.token = VALUE;
844 info.expr = p;
845 return Ok(Datum::float(Value::get_float(&token)?));
846 }
847 }
848
849 info.expr = p.clone();
851 info.expr.skip();
852
853 match p.peek() {
854 Some('$') => {
855 let mut ctx = EvalPtr::from_tokenizer(&p);
856 ctx.set_no_eval(info.no_eval > 0);
857 let var_val = parse_and_eval_variable(interp, &mut ctx)?;
858 info.token = VALUE;
859 info.expr = ctx.to_tokenizer();
860 if info.no_eval > 0 {
861 Ok(Datum::none())
862 } else {
863 expr_parse_value(&var_val)
864 }
865 }
866 Some('[') => {
867 let mut ctx = EvalPtr::from_tokenizer(&p);
868 ctx.set_no_eval(info.no_eval > 0);
869 let script_val = parse_and_eval_script(interp, &mut ctx)?;
870 info.token = VALUE;
871 info.expr = ctx.to_tokenizer();
872 if info.no_eval > 0 {
873 Ok(Datum::none())
874 } else {
875 expr_parse_value(&script_val)
876 }
877 }
878 Some('"') => {
879 let mut ctx = EvalPtr::from_tokenizer(&p);
880 ctx.set_no_eval(info.no_eval > 0);
881 let val = parse_and_eval_quoted_word(interp, &mut ctx)?;
882 info.token = VALUE;
883 info.expr = ctx.to_tokenizer();
884 if info.no_eval > 0 {
885 Ok(Datum::none())
886 } else {
887 expr_parse_string(val.as_str())
890 }
891 }
892 Some('{') => {
893 let mut ctx = EvalPtr::from_tokenizer(&p);
894 ctx.set_no_eval(info.no_eval > 0);
895 let val = parse_and_eval_braced_word(&mut ctx)?;
896 info.token = VALUE;
897 info.expr = ctx.to_tokenizer();
898 if info.no_eval > 0 {
899 Ok(Datum::none())
900 } else {
901 expr_parse_string(val.as_str())
904 }
905 }
906 Some('(') => {
907 info.token = OPEN_PAREN;
908 Ok(Datum::none())
909 }
910 Some(')') => {
911 info.token = CLOSE_PAREN;
912 Ok(Datum::none())
913 }
914 Some(',') => {
915 info.token = COMMA;
916 Ok(Datum::none())
917 }
918 Some('*') => {
919 info.token = MULT;
920 Ok(Datum::none())
921 }
922 Some('/') => {
923 info.token = DIVIDE;
924 Ok(Datum::none())
925 }
926 Some('%') => {
927 info.token = MOD;
928 Ok(Datum::none())
929 }
930 Some('+') => {
931 info.token = PLUS;
932 Ok(Datum::none())
933 }
934 Some('-') => {
935 info.token = MINUS;
936 Ok(Datum::none())
937 }
938 Some('?') => {
939 info.token = QUESTY;
940 Ok(Datum::none())
941 }
942 Some(':') => {
943 info.token = COLON;
944 Ok(Datum::none())
945 }
946 Some('<') => {
947 p.skip();
948 match p.peek() {
949 Some('<') => {
950 info.token = LEFT_SHIFT;
951 p.skip();
952 info.expr = p;
953 Ok(Datum::none())
954 }
955 Some('=') => {
956 info.token = LEQ;
957 p.skip();
958 info.expr = p;
959 Ok(Datum::none())
960 }
961 _ => {
962 info.token = LESS;
963 Ok(Datum::none())
964 }
965 }
966 }
967 Some('>') => {
968 p.skip();
969 match p.peek() {
970 Some('>') => {
971 info.token = RIGHT_SHIFT;
972 p.skip();
973 info.expr = p;
974 Ok(Datum::none())
975 }
976 Some('=') => {
977 info.token = GEQ;
978 p.skip();
979 info.expr = p;
980 Ok(Datum::none())
981 }
982 _ => {
983 info.token = GREATER;
984 Ok(Datum::none())
985 }
986 }
987 }
988 Some('=') => {
989 p.skip();
990 if let Some('=') = p.peek() {
991 info.token = EQUAL;
992 p.skip();
993 info.expr = p;
994 } else {
995 info.token = UNKNOWN;
996 }
997 Ok(Datum::none())
998 }
999 Some('!') => {
1000 p.skip();
1001 if let Some('=') = p.peek() {
1002 info.token = NEQ;
1003 p.skip();
1004 info.expr = p;
1005 } else {
1006 info.token = NOT;
1007 }
1008 Ok(Datum::none())
1009 }
1010 Some('&') => {
1011 p.skip();
1012 if let Some('&') = p.peek() {
1013 info.token = AND;
1014 p.skip();
1015 info.expr = p;
1016 } else {
1017 info.token = BIT_AND;
1018 }
1019 Ok(Datum::none())
1020 }
1021 Some('^') => {
1022 info.token = BIT_XOR;
1023 Ok(Datum::none())
1024 }
1025 Some('|') => {
1026 p.skip();
1027 if let Some('|') = p.peek() {
1028 info.token = OR;
1029 p.skip();
1030 info.expr = p;
1031 } else {
1032 info.token = BIT_OR;
1033 }
1034 Ok(Datum::none())
1035 }
1036 Some('~') => {
1037 info.token = BIT_NOT;
1038 Ok(Datum::none())
1039 }
1040 Some(_) => {
1041 if p.has(|c| c.is_alphabetic()) {
1042 let mut str = String::new();
1043 while p.has(|c| c.is_alphabetic() || c.is_digit(10)) {
1044 str.push(p.next().unwrap());
1045 }
1046
1047 match str.as_ref() {
1050 "true" | "yes" | "on" => {
1051 info.expr = p;
1052 info.token = VALUE;
1053 Ok(Datum::int(1))
1054 }
1055 "false" | "no" | "off" => {
1056 info.expr = p;
1057 info.token = VALUE;
1058 Ok(Datum::int(0))
1059 }
1060 "eq" => {
1061 info.expr = p;
1062 info.token = STRING_EQ;
1063 Ok(Datum::none())
1064 }
1065 "ne" => {
1066 info.expr = p;
1067 info.token = STRING_NE;
1068 Ok(Datum::none())
1069 }
1070 "in" => {
1071 info.expr = p;
1072 info.token = IN;
1073 Ok(Datum::none())
1074 }
1075 "ni" => {
1076 info.expr = p;
1077 info.token = NI;
1078 Ok(Datum::none())
1079 }
1080 _ => {
1081 info.expr = p;
1082 expr_math_func(interp, info, &str)
1083 }
1084 }
1085 } else {
1086 p.skip();
1087 info.expr = p;
1088 info.token = UNKNOWN;
1089 Ok(Datum::none())
1090 }
1091 }
1092 None => {
1093 p.skip();
1094 info.expr = p;
1095 info.token = UNKNOWN;
1096 Ok(Datum::none())
1097 }
1098 }
1099}
1100
1101fn parse_and_eval_variable(interp: &mut Interp, ctx: &mut EvalPtr) -> MoltResult {
1103 ctx.skip_char('$');
1105
1106 if !ctx.next_is_varname_char() && !ctx.next_is('{') {
1108 return molt_err!("invalid character \"$\"");
1109 }
1110
1111 let word = parser::parse_varname(ctx)?;
1113
1114 if ctx.is_no_eval() {
1115 Ok(Value::empty())
1116 } else {
1117 interp.eval_word(&word)
1118 }
1119}
1120
1121fn parse_and_eval_script(interp: &mut Interp, ctx: &mut EvalPtr) -> MoltResult {
1125 ctx.skip_char('[');
1127
1128 let old_flag = ctx.is_bracket_term();
1130 ctx.set_bracket_term(true);
1131
1132 let script = parser::parse_script(ctx)?;
1133 let result = if ctx.is_no_eval() {
1134 Ok(Value::empty())
1135 } else {
1136 interp.eval_script(&script)
1137 };
1138
1139 ctx.set_bracket_term(old_flag);
1140
1141 if result.is_ok() {
1143 if ctx.next_is(']') {
1144 ctx.next();
1145 } else {
1146 return molt_err!("missing close-bracket");
1147 }
1148 }
1149
1150 result
1151}
1152
1153fn parse_and_eval_quoted_word(interp: &mut Interp, ctx: &mut EvalPtr) -> MoltResult {
1157 let word = parser::parse_quoted_word(ctx)?;
1158
1159 if ctx.is_no_eval() {
1160 Ok(Value::empty())
1161 } else {
1162 interp.eval_word(&word)
1163 }
1164}
1165
1166fn parse_and_eval_braced_word(ctx: &mut EvalPtr) -> MoltResult {
1168 if let Word::Value(val) = parser::parse_braced_word(ctx)? {
1169 Ok(val)
1170 } else {
1171 unreachable!()
1172 }
1173}
1174
1175#[allow(clippy::needless_range_loop)]
1177fn expr_math_func(interp: &mut Interp, info: &mut ExprInfo, func_name: &str) -> DatumResult {
1178 let bfunc = expr_find_func(func_name)?;
1183
1184 let _ = expr_lex(interp, info)?;
1186
1187 if info.token != OPEN_PAREN {
1188 return syntax_error(info);
1189 }
1190
1191 let mut args: [Datum; MAX_MATH_ARGS] = [Datum::none(), Datum::none()];
1193
1194 if bfunc.num_args == 0 {
1195 let _ = expr_lex(interp, info)?;
1196 if info.token != OPEN_PAREN {
1197 return syntax_error(info);
1198 }
1199 } else {
1200 for i in 0..bfunc.num_args {
1201 let arg = expr_get_value(interp, info, -1)?;
1202
1203 if arg.vtype == Type::String {
1205 return molt_err!("argument to math function didn't have numeric value");
1206 }
1207
1208 if arg.vtype == Type::Int {
1210 if bfunc.arg_types[i] == ArgType::Float {
1211 args[i] = Datum::float(arg.int as MoltFloat);
1212 } else {
1213 args[i] = arg;
1214 }
1215 } else {
1216 if bfunc.arg_types[i] == ArgType::Int {
1218 args[i] = Datum::int(arg.flt as MoltInt);
1220 } else {
1221 args[i] = arg;
1222 }
1223 }
1224
1225 if i == bfunc.num_args - 1 {
1228 if info.token == CLOSE_PAREN {
1229 break;
1230 }
1231 if info.token == COMMA {
1232 return molt_err!("too many arguments for math function");
1233 } else {
1234 return syntax_error(info);
1235 }
1236 }
1237
1238 if info.token != COMMA {
1239 if info.token == CLOSE_PAREN {
1240 return molt_err!("too few arguments for math function");
1241 } else {
1242 return syntax_error(info);
1243 }
1244 }
1245 }
1246 }
1247
1248 if info.no_eval > 0 {
1250 return Ok(Datum::none());
1251 }
1252
1253 info.token = VALUE;
1255 (bfunc.func)(&args)
1256}
1257
1258fn expr_find_func(func_name: &str) -> Result<&'static BuiltinFunc, Exception> {
1261 for bfunc in &FUNC_TABLE {
1262 if bfunc.name == func_name {
1263 return Ok(bfunc);
1264 }
1265 }
1266
1267 molt_err!("unknown math function \"{}\"", func_name)
1268}
1269
1270fn expr_parse_value(value: &Value) -> DatumResult {
1276 match value.already_number() {
1277 Some(datum) => Ok(datum),
1278 _ => expr_parse_string(value.as_str()),
1279 }
1280}
1281
1282fn expr_parse_string(string: &str) -> DatumResult {
1287 if !string.is_empty() {
1288 let mut p = Tokenizer::new(string);
1289
1290 if expr_looks_like_int(&p) {
1291 p.skip_while(|c| c.is_whitespace());
1293
1294 let token = util::read_int(&mut p).unwrap();
1297
1298 p.skip_while(|c| c.is_whitespace());
1301
1302 if p.at_end() {
1303 let int = Value::get_int(&token)?;
1306 return Ok(Datum::int(int));
1307 }
1308 } else {
1309 p.skip_while(|c| c.is_whitespace());
1311
1312 if let Some(token) = util::read_float(&mut p) {
1314 p.skip_while(|c| c.is_whitespace());
1317
1318 if p.at_end() {
1319 let flt = Value::get_float(&token)?;
1322 return Ok(Datum::float(flt));
1323 }
1324 }
1325 }
1326 }
1327
1328 Ok(Datum::string(string))
1329}
1330
1331fn expr_as_str(value: Datum) -> Datum {
1333 match value.vtype {
1334 Type::Int => Datum::string(&format!("{}", value.int)),
1335 Type::Float => Datum::string(&format!("{}", value.flt)),
1336 _ => value,
1337 }
1338}
1339
1340fn expr_looks_like_int<'a>(ptr: &Tokenizer<'a>) -> bool {
1342 let mut p = ptr.clone();
1344 p.skip_while(|c| c.is_whitespace());
1345
1346 if p.is('+') || p.is('-') {
1347 p.skip();
1348 }
1349
1350 if !p.has(|ch| ch.is_digit(10)) {
1351 return false;
1352 }
1353 p.skip();
1354
1355 while p.has(|ch| ch.is_digit(10)) {
1356 p.skip();
1357 }
1358
1359 !p.is('.') && !p.is('e') && !p.is('E')
1360}
1361
1362impl Datum {
1363 fn is_numeric(&self) -> bool {
1364 match self.vtype {
1365 Type::Int => true,
1366 Type::Float => true,
1367 Type::String => false,
1368 }
1369 }
1370}
1371
1372#[allow(clippy::collapsible_if)]
1373fn expr_abs_func(args: &[Datum; MAX_MATH_ARGS]) -> DatumResult {
1374 let arg = &args[0];
1375 if arg.vtype == Type::Float {
1376 if arg.flt < 0.0 {
1377 Ok(Datum::float(-arg.flt))
1378 } else {
1379 Ok(Datum::float(arg.flt))
1380 }
1381 } else {
1382 if arg.int < 0 {
1384 Ok(Datum::int(-arg.int))
1385 } else {
1386 Ok(Datum::int(arg.int))
1387 }
1388 }
1389}
1390
1391fn expr_double_func(args: &[Datum; MAX_MATH_ARGS]) -> DatumResult {
1392 let arg = &args[0];
1393 if arg.vtype == Type::Float {
1394 Ok(Datum::float(arg.flt))
1395 } else {
1396 Ok(Datum::float(arg.int as MoltFloat))
1397 }
1398}
1399
1400fn expr_int_func(args: &[Datum; MAX_MATH_ARGS]) -> DatumResult {
1401 let arg = &args[0];
1402 if arg.vtype == Type::Int {
1403 Ok(Datum::int(arg.int))
1404 } else {
1405 Ok(Datum::int(arg.flt as MoltInt))
1407 }
1408}
1409
1410fn expr_round_func(args: &[Datum; MAX_MATH_ARGS]) -> DatumResult {
1411 let arg = &args[0];
1413 if arg.vtype == Type::Int {
1414 Ok(Datum::int(arg.int))
1415 } else if arg.flt < 0.0 {
1416 Ok(Datum::int((arg.flt - 0.5) as MoltInt))
1417 } else {
1418 Ok(Datum::int((arg.flt + 0.5) as MoltInt))
1419 }
1420}
1421
1422fn syntax_error(info: &mut ExprInfo) -> DatumResult {
1424 molt_err!("syntax error in expression \"{}\"", info.original_expr)
1425}
1426
1427fn illegal_type(bad_type: Type, op: i32) -> DatumResult {
1429 let type_str = if bad_type == Type::Float {
1430 "floating-point value"
1431 } else {
1432 "non-numeric string"
1433 };
1434
1435 molt_err!(
1436 "can't use {} as operand of \"{}\"",
1437 type_str,
1438 OP_STRINGS[op as usize]
1439 )
1440}
1441
1442#[cfg(test)]
1443mod tests {
1444 use super::*;
1445
1446 fn call_expr_looks_like_int(str: &str) -> bool {
1447 let p = Tokenizer::new(str);
1448
1449 expr_looks_like_int(&p)
1450 }
1451
1452 #[allow(clippy::float_cmp)]
1456 fn veq(val1: &Datum, val2: &Datum) -> bool {
1457 if val1.vtype != val2.vtype {
1458 return false;
1459 }
1460
1461 match &val1.vtype {
1462 Type::Int => val1.int == val2.int,
1463 Type::Float => val1.flt == val2.flt,
1464 Type::String => val1.str == val2.str,
1465 }
1466 }
1467
1468 #[test]
1469 fn test_expr_looks_like_int() {
1470 assert!(call_expr_looks_like_int("1"));
1471 assert!(call_expr_looks_like_int("+1"));
1472 assert!(call_expr_looks_like_int("-1"));
1473 assert!(call_expr_looks_like_int("123"));
1474 assert!(call_expr_looks_like_int("123a"));
1475 assert!(!call_expr_looks_like_int(""));
1476 assert!(!call_expr_looks_like_int("a"));
1477 assert!(!call_expr_looks_like_int("123."));
1478 assert!(!call_expr_looks_like_int("123e"));
1479 assert!(!call_expr_looks_like_int("123E"));
1480 assert!(!call_expr_looks_like_int("."));
1481 assert!(!call_expr_looks_like_int("e"));
1482 assert!(!call_expr_looks_like_int("E"));
1483 }
1484
1485 #[test]
1486 fn test_expr_parse_string() {
1487 let result = expr_parse_string("");
1488 assert!(result.is_ok());
1489 assert!(veq(&result.unwrap(), &Datum::string("")));
1490
1491 let result = expr_parse_string("abc");
1492 assert!(result.is_ok());
1493 assert!(veq(&result.unwrap(), &Datum::string("abc")));
1494
1495 let result = expr_parse_string(" 123abc");
1496 assert!(result.is_ok());
1497 assert!(veq(&result.unwrap(), &Datum::string(" 123abc")));
1498
1499 let result = expr_parse_string(" 123.0abc");
1500 assert!(result.is_ok());
1501 assert!(veq(&result.unwrap(), &Datum::string(" 123.0abc")));
1502
1503 let result = expr_parse_string(" 123 ");
1504 assert!(result.is_ok());
1505 assert!(veq(&result.unwrap(), &Datum::int(123)));
1506
1507 let result = expr_parse_string(" 1.0 ");
1508 assert!(result.is_ok());
1509 assert!(veq(&result.unwrap(), &Datum::float(1.0)));
1510
1511 let result = expr_parse_string("1234567890123456789012345678901234567890");
1512 assert!(result.is_err());
1513
1514 }
1517
1518 #[test]
1519 fn call_expr() {
1520 let mut interp = Interp::new();
1521
1522 let result = expr(&mut interp, &Value::from("1 + 1"));
1523 assert!(result.is_ok());
1524 assert_eq!(result.unwrap().as_int().unwrap(), 2);
1525
1526 let result = expr(&mut interp, &Value::from("1.1 + 1.1"));
1527 assert!(result.is_ok());
1528 let flt: MoltFloat = result.unwrap().as_float().unwrap();
1529 assert!(near(flt, 2.2));
1530
1531 let result = expr(&mut interp, &Value::from("[set x foo]"));
1532 assert!(result.is_ok());
1533 assert_eq!(result.unwrap().as_str(), "foo");
1534 }
1535
1536 fn near(x: MoltFloat, target: MoltFloat) -> bool {
1537 x >= target - std::f64::EPSILON && x <= target + std::f64::EPSILON
1538 }
1539}