molt_ng/
expr.rs

1//! The Expr command and parser
2//!
3//! * Ultimately, the command should probably move to commands.rs.
4//!   But this is convenient for now.
5
6use crate::eval_ptr::EvalPtr;
7use crate::interp::Interp;
8use crate::list;
9use crate::parser::Word;
10use crate::tokenizer::Tokenizer;
11use crate::*;
12
13//------------------------------------------------------------------------------------------------
14// Datum Representation
15
16type DatumResult = Result<Datum, Exception>;
17
18/// The value type.
19#[derive(Debug, PartialEq, Eq, Copy, Clone)]
20pub(crate) enum Type {
21    Int,
22    Float,
23    String,
24}
25
26/// A parsed value.
27///
28/// **Note**: Originally did this as a struct containing an enum with associated values
29/// for the data, but that complicated the logic.  We need to easily compare the types
30/// of two values (which `if let` doesn't allow), and we need to be able to refer to a
31/// type without reference to the typed value.
32///
33/// I could have used a union to save space, but we don't keep large numbers of these
34/// around.
35#[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    // Only for checking integers.
81    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
91//------------------------------------------------------------------------------------------------
92// Functions
93
94const MAX_MATH_ARGS: usize = 2;
95
96/// The argument type.
97#[derive(Debug, PartialEq, Eq, Copy, Clone)]
98enum ArgType {
99    None,
100    Float,  // Must convert to Type::Float
101    Int,    // Must convert to Type::Int
102    Number, // Either Type::Int or Type::Float is OK
103}
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
141//------------------------------------------------------------------------------------------------
142// Parsing Context
143
144/// Context for expr parsing
145struct ExprInfo<'a> {
146    // The full expr.
147    original_expr: String,
148
149    // The input iterator, e.g., the pointer to the next character.
150    expr: Tokenizer<'a>,
151
152    // Last token's type; see constants
153    token: i32,
154
155    // No Evaluation if > 0
156    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
170//------------------------------------------------------------------------------------------------
171// Constants and Lookup Tables
172
173// Token constants
174//
175// The token types are defined below.  In addition, there is a table
176// associating a precedence with each operator.  The order of types
177// is important.  Consult the code before changing it.
178
179const 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
186// Tokens 6 and 7 are unused.
187
188// Binary operators:
189const 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
214// Unary operators:
215const UNARY_MINUS: i32 = 32;
216const UNARY_PLUS: i32 = 33;
217const NOT: i32 = 34;
218const BIT_NOT: i32 = 35;
219
220// Precedence table.  The values for non-operator token types are ignored.
221
222const PREC_TABLE: [i32; 36] = [
223    0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, // MULT, DIVIDE, MOD
224    13, 13, // PLUS, MINUS
225    12, 12, // LEFT_SHIFT, RIGHT_SHIFT
226    11, 11, 11, 11, // LESS, GREATER, LEQ, GEQ
227    10, 10, // EQUAL, NEQ
228    9, 9, // STRING_EQ, STRING_NE
229    8, 8, // IN, NI
230    7, // BIT_AND
231    6, // BIT_XOR
232    5, // BIT_OR
233    4, // AND
234    3, // OR
235    2, // QUESTY
236    1, // COLON
237    13, 13, 13, 13, // UNARY_MINUS, UNARY_PLUS, NOT, BIT_NOT
238];
239
240const OP_STRINGS: [&str; 36] = [
241    "VALUE", "(", ")", ",", "END", "UNKNOWN", "6", "7", "*", "/", "%", "+", "-", "<<", ">>", "<",
242    ">", "<=", ">=", "==", "!=", "eq", "ne", "in", "ni", "&", "^", "|", "&&", "||", "?", ":", "-",
243    "+", "!", "~",
244];
245
246//------------------------------------------------------------------------------------------------
247// Public API
248
249/// Evaluates an expression and returns its value.
250pub 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
260//------------------------------------------------------------------------------------------------
261// Expression Internals
262
263/// Provides top-level functionality shared by molt_expr_string, molt_expr_int, etc.
264fn 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                // TODO: check for NaN, INF, and throw IEEE floating point error.
277            }
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/// Parse a "value" from the remainder of the expression in info.
290/// The `prec` is a precedence value; treat any unparenthesized operator
291/// with precedence less than or equal to `prec` as the end of the
292/// expression.
293#[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    // There are two phases to this procedure.  First, pick off an initial value.
298    // Then, parse (binary operator, value) pairs until done.
299    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        // Parenthesized sub-expression.
306        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            // Process unary operators
325            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                                // NOTE: Tcl uses !int here, but in Rust !int_value is a bitwise
350                                // operator, not a logical one.
351                                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                            // Note: in Rust, unlike C, !int_value is a bitwise operator.
372                            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    // Got the first operand.  Now fetch (operator, operand) pairs
389
390    if !got_op {
391        // This reads the next token, which we expect to be an operator.
392        // All we really care about is the token enum; if it's a value, it doesn't matter
393        // what the value is.
394        let _ = expr_lex(interp, info)?;
395    }
396
397    loop {
398        operator = info.token;
399        // ??? value2.pv.next = value2.pv.buffer;
400
401        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 we're doing an AND or OR and the first operand already determines
414        // the result, don't execute anything in the second operand: just parse.
415        // Same style for ?: pairs.
416
417        if operator == AND || operator == OR || operator == QUESTY {
418            // For these operators, we need an integer value.  Convert or return
419            // an error.
420            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                // Short-circuit; we don't care about the next operand, but it must be
439                // syntactically correct.
440                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                // Go on to the next operator.
449                continue;
450            } else if operator == QUESTY {
451                // Special note: ?: operators must associate right to left.  To make
452                // this happen, use a precedence one lower than QUESTY when calling
453                // expr_get_value recursively.
454                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        // At this point we've got two values and an operator.  Check to make sure that the
496        // particular data types are appropriate for the particular operator, and perform
497        // type conversion if necessary.
498
499        match operator {
500            // For the operators below, no strings are allowed and ints get converted to
501            // floats if necessary.
502            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            // For the operators below, only integers are allowed.
521            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            // For the operators below, any type is allowed, but the operators must have
530            // the same type.
531            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            // For the operators below, everything's treated as a string.
552            // For IN and NI, the second value is a list, but we'll parse it as a list
553            // as part of evaluation.
554            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            // For the operators below, no strings are allowed, but no int->float conversions
564            // are performed.
565            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            // For the operators below, type and conversions are irrelevant: they're
575            // handled elsewhere.
576            QUESTY | COLON => {
577                // Nothing to do
578            }
579
580            _ => return molt_err!("unknown operator in expression"),
581        }
582
583        // Carry out the function of the specified operator.
584        match operator {
585            MULT => {
586                if value.vtype == Type::Int {
587                    // value.int *= value2.int
588                    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                        // TODO: return Inf or -Inf?  Waiting for response from KBK
611                        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                    // value.int += value2.int;
632                    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                    // value.int -= value2.int;
644                    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                // TODO: Use checked_shl
655                value.int <<= value2.int;
656            }
657            RIGHT_SHIFT => {
658                // The following code is a bit tricky:  it ensures that
659                // right shifts propagate the sign bit even on machines
660                // where ">>" won't do it by default.
661                // WHD: Not sure if this is an issue in Rust.
662
663                // TODO: Use checked_shr
664                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                // NOTE: comparing floats using == is dangerous; but Tcl leaves that to the
708                // TCL programmer.
709                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                // NOTE: comparing floats using == is dangerous; but Tcl leaves that to the
719                // TCL programmer.
720                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                // TODO: Need a better MoltList contains() method.
745                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                // TODO: Need a better MoltList contains() method.
754                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            // For AND and OR, we know that the first value has already been converted to
771            // an integer.  Thus we need only consider the possibility of int vs. double
772            // for the second value.
773            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                // Nothing to do.
802            }
803        }
804    }
805}
806
807/// Lexical analyzer for the expression parser.  Parses a single value, operator, or other
808/// syntactic element from an expression string.
809///
810/// ## Results
811///
812/// Returns an error result if an error occurs while doing lexical analysis or
813/// executing an embedded command.  On success, info.token is set to the last token type,
814/// and info is updated to point to the next token.  If the token is VALUE, the returned
815/// Datum contains it.
816
817fn expr_lex(interp: &mut Interp, info: &mut ExprInfo) -> DatumResult {
818    // FIRST, skip white space.
819    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    // First try to parse the token as an integer or floating-point number.
830    // Don't want to check for a number if the first character is "+"
831    // or "-".  If we do, we might treat a binary operator as unary by
832    // mistake, which will eventually cause a syntax error.
833
834    if !p.is('+') && !p.is('-') {
835        if expr_looks_like_int(&p) {
836            // There's definitely an integer to parse; parse it.
837            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    // It isn't a number, so the next character will determine what it is.
850    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                // Note: we got a Value, but since it was parsed from a quoted string,
888                // it won't already be numeric.
889                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                // Note: we got a Value, but since it was parsed from a braced string,
902                // it won't already be numeric.
903                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                // NOTE: Could use get_boolean to test for the boolean constants, but it's
1048                // probably overkill.
1049                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
1101// Parses a variable reference.  A bare "$" is an error.
1102fn parse_and_eval_variable(interp: &mut Interp, ctx: &mut EvalPtr) -> MoltResult {
1103    // FIRST, skip the '$'
1104    ctx.skip_char('$');
1105
1106    // NEXT, make sure this is really a variable reference.
1107    if !ctx.next_is_varname_char() && !ctx.next_is('{') {
1108        return molt_err!("invalid character \"$\"");
1109    }
1110
1111    // NEXT, get the variable reference.
1112    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
1121/// Parses and evaluates an interpolated script in Molt input, i.e., a string beginning with
1122/// a "[", returning a MoltResult.  If the no_eval flag is set, returns an empty value.
1123/// This is used to handled interpolated scripts in expressions.
1124fn parse_and_eval_script(interp: &mut Interp, ctx: &mut EvalPtr) -> MoltResult {
1125    // FIRST, skip the '['
1126    ctx.skip_char('[');
1127
1128    // NEXT, parse the script up to the matching ']'
1129    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    // NEXT, make sure there's a closing bracket
1142    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
1153/// Parses and evaluates a quoted word in Molt input, i.e., a string beginning with
1154/// a double quote, returning a MoltResult.  If the no_eval flag is set, returns an empty
1155/// value.  This is used to handle double-quoted strings in expressions.
1156fn 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
1166/// Parses a braced word, returning a Value.
1167fn 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/// Parses math functions, returning the evaluated value.
1176#[allow(clippy::needless_range_loop)]
1177fn expr_math_func(interp: &mut Interp, info: &mut ExprInfo, func_name: &str) -> DatumResult {
1178    // FIRST, is this actually a function?
1179    // TODO: this does a linear search of the FUNC_TABLE.  Ultimately, it should probably
1180    // be a hash lookup.  And if we want to allow users to add functions, it should be
1181    // kept in the Interp.
1182    let bfunc = expr_find_func(func_name)?;
1183
1184    // NEXT, get the open paren.
1185    let _ = expr_lex(interp, info)?;
1186
1187    if info.token != OPEN_PAREN {
1188        return syntax_error(info);
1189    }
1190
1191    // NEXT, scan off the arguments for the function, if there are any.
1192    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            // At present we have no string functions.
1204            if arg.vtype == Type::String {
1205                return molt_err!("argument to math function didn't have numeric value");
1206            }
1207
1208            // Copy the value to the argument record, converting it if necessary.
1209            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                // Type::Float
1217                if bfunc.arg_types[i] == ArgType::Int {
1218                    // TODO: Need to handle overflow?
1219                    args[i] = Datum::int(arg.flt as MoltInt);
1220                } else {
1221                    args[i] = arg;
1222                }
1223            }
1224
1225            // Check for a comma separator between arguments or a close-paren to end
1226            // the argument list.
1227            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    // NEXT, if we aren't evaluating, return an empty value.
1249    if info.no_eval > 0 {
1250        return Ok(Datum::none());
1251    }
1252
1253    // NEXT, invoke the math function.
1254    info.token = VALUE;
1255    (bfunc.func)(&args)
1256}
1257
1258// Find the function in the table.
1259// TODO: Really, functions should be registered with the interpreter.
1260fn 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
1270/// If the value already has a numeric data rep, just gets it as a Datum; otherwise,
1271/// tries to parse it out as a string.
1272///
1273/// NOTE: We don't just use `Value::as_float` or `Value::as_int`, as those expect
1274/// to parse strings with no extra whitespace.  (That may be a bug.)
1275fn 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
1282/// Given a string (such as one coming from command or variable substitution) make a
1283/// Datum based on the string.  The value will be floating-point or integer if possible,
1284/// or else it will just be a copy of the string.  Returns an error on failed numeric
1285/// conversions.
1286fn 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            // FIRST, skip leading whitespace.
1292            p.skip_while(|c| c.is_whitespace());
1293
1294            // NEXT, get the integer token from it.  We know there has to be something,
1295            // since it "looks like int".
1296            let token = util::read_int(&mut p).unwrap();
1297
1298            // NEXT, did we read the whole string?  If not, it isn't really an integer.
1299            // Otherwise, drop through and return it as a string.
1300            p.skip_while(|c| c.is_whitespace());
1301
1302            if p.at_end() {
1303                // Can return an error if the number is too long to represent as a
1304                // MoltInt.  This is consistent with Tcl 7.6.  (Tcl 8 uses BigNums.)
1305                let int = Value::get_int(&token)?;
1306                return Ok(Datum::int(int));
1307            }
1308        } else {
1309            // FIRST, see if it's a double. Skip leading whitespace.
1310            p.skip_while(|c| c.is_whitespace());
1311
1312            // NEXT, see if we can get a float token from it.
1313            if let Some(token) = util::read_float(&mut p) {
1314                // Did we read the whole string?  If not, it isn't really a float.
1315                // Otherwise, drop through and return it as a string.
1316                p.skip_while(|c| c.is_whitespace());
1317
1318                if p.at_end() {
1319                    // Can theoretically return an error.  This is consistent with
1320                    // Tcl 7.6.  Molt and Tcl 8 return 0, Inf, or -Inf instead.
1321                    let flt = Value::get_float(&token)?;
1322                    return Ok(Datum::float(flt));
1323                }
1324            }
1325        }
1326    }
1327
1328    Ok(Datum::string(string))
1329}
1330
1331// Converts values to strings for string comparisons.
1332fn 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
1340// Distinguished between decimal integers and floating-point values
1341fn expr_looks_like_int<'a>(ptr: &Tokenizer<'a>) -> bool {
1342    // FIRST, skip whitespace
1343    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        // TODO: need to handle integer overflow here.
1383        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        // TODO: need to handle integer overflow here.
1406        Ok(Datum::int(arg.flt as MoltInt))
1407    }
1408}
1409
1410fn expr_round_func(args: &[Datum; MAX_MATH_ARGS]) -> DatumResult {
1411    // TODO: need to handle integer overflow here.
1412    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
1422// Return standard syntax error
1423fn syntax_error(info: &mut ExprInfo) -> DatumResult {
1424    molt_err!("syntax error in expression \"{}\"", info.original_expr)
1425}
1426
1427// Return standard illegal type error
1428fn 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    // Note: comparing floating point values for equality is usually a mistake.  In this
1453    // case, we are simply converting simple floating-point values to and from strings, and
1454    // verifying that we got the number we expected, so this is probably OK.
1455    #[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        // Should have an example of a float overflow/underflow, but I've not found a literal
1515        // string that gives one.
1516    }
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}