go_types/
constant.rs

1// Copyright 2022 The Goscript Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4//
5//
6// This code is adapted from the offical Go code written in Go
7// with license as follows:
8// Copyright 2013 The Go Authors. All rights reserved.
9// Use of this source code is governed by a BSD-style
10// license that can be found in the LICENSE file.
11
12use super::typ::{BasicDetail, BasicInfo, BasicType};
13use go_parser::Token;
14use num_bigint::{BigInt, Sign};
15use num_rational::BigRational;
16use num_traits::cast::FromPrimitive;
17use num_traits::cast::ToPrimitive;
18use num_traits::sign::Signed;
19use num_traits::Num;
20use ordered_float;
21use std::borrow::Borrow;
22use std::borrow::Cow;
23use std::fmt;
24
25type F32 = ordered_float::OrderedFloat<f32>;
26type F64 = ordered_float::OrderedFloat<f64>;
27
28/// constant implements Values representing untyped
29/// Go constants and their corresponding operations.
30///
31/// A special Unknown value may be used when a value
32/// is unknown due to an error. Operations on unknown
33/// values produce unknown values unless specified
34/// otherwise.
35///
36/// Because BigFloat library is not available at the moment(2020/5)
37/// float numbers arbitrary precision is not supported for now
38/// float numbers is simply represented as f64
39/// todo: This is against the Go specs.
40
41/// All the values involved in the evaluation
42#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub enum Value {
44    Unknown,
45    Bool(bool),
46    Str(String),
47    Int(BigInt),
48    Rat(BigRational),
49    Float(F64),
50    Complex(Box<Value>, Box<Value>),
51}
52
53impl fmt::Display for Value {
54    /// For numeric values, the result may be an approximation;
55    /// for String values the result may be a shortened string.
56    /// Use ExactString for a string representing a value exactly.
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        match self {
59            Value::Unknown => write!(f, "unknown"),
60            Value::Bool(b) => {
61                //f.write_str("bool: ")?;
62                b.fmt(f)
63            }
64            Value::Str(s) => {
65                //f.write_str("string: ")?;
66                write!(f, "{}", short_quote_str(s, 72))
67            }
68            Value::Int(s) => {
69                //f.write_str("int: ")?;
70                s.fmt(f)
71            }
72            Value::Rat(r) => {
73                //f.write_str("rat: ")?;
74                r.fmt(f)
75            }
76            Value::Float(s) => {
77                //f.write_str("float: ")?;
78                s.fmt(f)
79            }
80            Value::Complex(r, i) => {
81                //f.write_str("complex: ")?;
82                write!(f, "({} + {}i)", r, i)
83            }
84        }
85    }
86}
87
88impl Value {
89    pub fn with_bool(b: bool) -> Value {
90        Value::Bool(b)
91    }
92
93    pub fn with_str(s: String) -> Value {
94        Value::Str(s)
95    }
96
97    pub fn with_i64(i: i64) -> Value {
98        Value::Int(BigInt::from_i64(i).unwrap())
99    }
100
101    pub fn with_u64(u: u64) -> Value {
102        Value::Int(BigInt::from_u64(u).unwrap())
103    }
104
105    pub fn with_f64(f: f64) -> Value {
106        Value::Float(f.into())
107    }
108
109    pub fn with_literal(tok: &Token) -> Value {
110        match tok {
111            Token::INT(ilit) => int_from_literal(ilit.as_str()),
112            Token::FLOAT(flit) => float_from_literal(flit.as_str()),
113            Token::IMAG(imlit) => {
114                let s = imlit.as_str();
115                let v = float_from_literal(&s[..(s.len() - 1)]);
116                if let Value::Float(_) = &v {
117                    Value::Complex(Box::new(Value::with_f64(0.0)), Box::new(v))
118                } else {
119                    Value::Unknown
120                }
121            }
122            Token::CHAR(clit) => {
123                let (_, ch) = clit.as_str_char();
124                Value::with_i64(*ch as i64)
125            }
126            Token::STRING(slit) => {
127                let (_, s) = slit.as_str_str();
128                Value::with_str(s.clone())
129            }
130            _ => Value::Unknown,
131        }
132    }
133
134    pub fn is_int(&self) -> bool {
135        match self {
136            Value::Int(_) => true,
137            Value::Rat(r) => r.is_integer(),
138            _ => false,
139        }
140    }
141
142    pub fn representable(&self, base: &BasicDetail, rounded: Option<&mut Value>) -> bool {
143        if let Value::Unknown = self {
144            return true; // avoid follow-up errors
145        }
146
147        let float_representable =
148            |val: &Value, btype: BasicType, rounded: Option<&mut Value>| -> bool {
149                match val.to_float() {
150                    Value::Float(f) => match btype {
151                        BasicType::Float64 => true,
152                        BasicType::Float32 => {
153                            let f32_ = *f as f32;
154                            let ok = !f32_.is_infinite();
155                            if let Some(r) = rounded {
156                                *r = Value::Float(((*f as f32) as f64).into());
157                            }
158                            ok
159                        }
160                        BasicType::UntypedFloat => true,
161                        _ => unreachable!(),
162                    },
163                    _ => false,
164                }
165            };
166
167        match base.info() {
168            BasicInfo::IsInteger => match self.to_int().borrow() {
169                Value::Int(ival) => {
170                    if let Some(r) = rounded {
171                        *r = Value::Int(ival.clone())
172                    }
173                    match base.typ() {
174                        BasicType::Int => ival.to_isize().is_some(),
175                        BasicType::Int8 => ival.to_i8().is_some(),
176                        BasicType::Int16 => ival.to_i16().is_some(),
177                        BasicType::Int32 | BasicType::Rune => ival.to_i32().is_some(),
178                        BasicType::Int64 => ival.to_i64().is_some(),
179                        BasicType::Uint | BasicType::Uintptr => ival.to_usize().is_some(),
180                        BasicType::Uint8 | BasicType::Byte => ival.to_u8().is_some(),
181                        BasicType::Uint16 => ival.to_u16().is_some(),
182                        BasicType::Uint32 => ival.to_u32().is_some(),
183                        BasicType::Uint64 => ival.to_u64().is_some(),
184                        BasicType::UntypedInt => true,
185                        _ => unreachable!(),
186                    }
187                }
188                _ => false,
189            },
190            BasicInfo::IsFloat => float_representable(self, base.typ(), rounded),
191            BasicInfo::IsComplex => {
192                let ty = match base.typ() {
193                    BasicType::Complex64 => BasicType::Float32,
194                    BasicType::Complex128 => BasicType::Float64,
195                    BasicType::UntypedComplex => BasicType::UntypedFloat,
196                    _ => unreachable!(),
197                };
198                match self.to_complex() {
199                    Value::Complex(r, i) => {
200                        let (rrounded, irounded): (Option<&mut Value>, Option<&mut Value>) =
201                            match rounded {
202                                Some(val) => {
203                                    *val = Value::Complex(
204                                        Box::new(Value::with_f64(0.0)),
205                                        Box::new(Value::with_f64(0.0)),
206                                    );
207                                    if let Value::Complex(r, i) = &mut *val {
208                                        (Some(r.as_mut()), Some(i.as_mut()))
209                                    } else {
210                                        unreachable!()
211                                    }
212                                }
213                                None => (None, None),
214                            };
215                        let rok = float_representable(&r, ty, rrounded);
216                        let iok = float_representable(&i, ty, irounded);
217                        rok && iok
218                    }
219                    _ => false,
220                }
221            }
222            BasicInfo::IsBoolean => match self {
223                Value::Bool(_) => true,
224                _ => false,
225            },
226            BasicInfo::IsString => match self {
227                Value::Str(_) => true,
228                _ => false,
229            },
230            _ => false,
231        }
232    }
233
234    pub fn to_int(&self) -> Cow<Value> {
235        let f64_to_int = |x| -> Cow<Value> {
236            match BigRational::from_f64(x) {
237                Some(v) => {
238                    if v.is_integer() {
239                        Cow::Owned(Value::Int(v.to_integer()))
240                    } else {
241                        Cow::Owned(Value::Unknown)
242                    }
243                }
244                None => Cow::Owned(Value::Unknown),
245            }
246        };
247        match self {
248            Value::Int(_) => Cow::Borrowed(self),
249            Value::Rat(r) => {
250                if r.is_integer() {
251                    Cow::Owned(Value::Int(r.to_integer()))
252                } else {
253                    Cow::Owned(Value::Unknown)
254                }
255            }
256            Value::Float(f) => f64_to_int(**f),
257            Value::Complex(r, i) => {
258                let (ival, ok) = i.to_int().int_as_i64();
259                if ok && ival == 0 {
260                    r.to_int()
261                } else {
262                    Cow::Owned(Value::Unknown)
263                }
264            }
265            _ => Cow::Owned(Value::Unknown),
266        }
267    }
268
269    pub fn to_float(&self) -> Value {
270        let v = match self {
271            Value::Int(i) => i.to_f64(),
272            Value::Rat(r) => rat_to_f64(r),
273            Value::Float(f) => Some(**f),
274            Value::Complex(r, i) => {
275                let (ival, ok) = i.to_float().num_as_f64();
276                if ok && ival == 0.0 {
277                    let (rval, ok) = r.to_float().num_as_f64();
278                    if ok {
279                        Some(*rval)
280                    } else {
281                        None
282                    }
283                } else {
284                    None
285                }
286            }
287            _ => None,
288        };
289        v.map_or(Value::Unknown, |x| Value::Float(x.into()))
290    }
291
292    pub fn to_complex(&self) -> Value {
293        match self {
294            Value::Int(_) | Value::Rat(_) | Value::Float(_) => {
295                Value::Complex(Box::new(self.clone()), Box::new(Value::with_f64(0.0)))
296            }
297            Value::Complex(_, _) => self.clone(),
298            _ => Value::Unknown,
299        }
300    }
301
302    // make_imag returns the Complex value x*i;
303    // x must be Int, Float, or Unknown.
304    // If x is Unknown, the result is Unknown.
305    pub fn make_imag(&self) -> Value {
306        match self {
307            Value::Int(_) | Value::Float(_) | Value::Rat(_) => {
308                Value::Complex(Box::new(Value::with_f64(0.0)), Box::new(self.clone()))
309            }
310            Value::Unknown => Value::Unknown,
311            _ => panic!("{} not Int or Float", self),
312        }
313    }
314
315    /// real returns the real part of x, which must be a numeric or unknown value.
316    /// If x is Unknown, the result is Unknown.
317    pub fn real(&self) -> Value {
318        match self {
319            Value::Int(_) | Value::Float(_) | Value::Rat(_) | Value::Unknown => self.clone(),
320            Value::Complex(r, _) => *r.clone(),
321            _ => panic!("{} not numeric", self),
322        }
323    }
324
325    /// imag returns the imaginary part of x, which must be a numeric or unknown value.
326    /// If x is Unknown, the result is Unknown.
327    pub fn imag(&self) -> Value {
328        match self {
329            Value::Int(_) | Value::Float(_) | Value::Rat(_) => Value::with_f64(0.0),
330            Value::Complex(_, i) => *i.clone(),
331            Value::Unknown => Value::Unknown,
332            _ => panic!("{} not numeric", self),
333        }
334    }
335
336    /// sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0;
337    /// x must be numeric or Unknown. For complex values x, the sign is 0 if x == 0,
338    /// otherwise it is != 0. If x is Unknown, the result is 1.
339    pub fn sign(&self) -> isize {
340        match self {
341            Value::Int(i) => match i.sign() {
342                Sign::Plus => 1,
343                Sign::Minus => -1,
344                Sign::NoSign => 0,
345            },
346            Value::Rat(r) => {
347                if r.is_positive() {
348                    1
349                } else if r.is_negative() {
350                    -1
351                } else {
352                    0
353                }
354            }
355            Value::Float(v) => {
356                let f: f64 = **v;
357                if f > 0.0 {
358                    1
359                } else if f < 0.0 {
360                    -1
361                } else {
362                    0
363                }
364            }
365            Value::Complex(r, i) => r.sign() | i.sign(),
366            Value::Unknown => 1, // avoid spurious division by zero errors
367            _ => panic!("{} not numeric", self),
368        }
369    }
370
371    /// binary_op returns the result of the binary expression x op y.
372    /// The operation must be defined for the operands. If one of the
373    /// operands is Unknown, the result is Unknown.
374    /// binary_op doesn't handle comparisons or shifts; use compare
375    /// or shift instead.
376    ///
377    /// To force integer division of Int operands, use op == Token::QUO_ASSIGN
378    /// instead of Token::QUO; the result is guaranteed to be Int in this case.
379    /// Division by zero leads to a run-time panic.
380    pub fn binary_op(x: &Value, op: &Token, y: &Value) -> Value {
381        let add = |x, y| Value::binary_op(x, &Token::ADD, y);
382        let sub = |x, y| Value::binary_op(x, &Token::SUB, y);
383        let mul = |x, y| Value::binary_op(x, &Token::MUL, y);
384        let div = |x, y| Value::binary_op(x, &Token::QUO, y);
385        let bx = |x| Box::new(x);
386
387        let (x, y) = Value::match_type(Cow::Borrowed(x), Cow::Borrowed(y));
388        match (&*x, &*y) {
389            (Value::Unknown, Value::Unknown) => Value::Unknown,
390            (Value::Bool(a), Value::Bool(b)) => match op {
391                Token::LAND => Value::Bool(*a && *b),
392                Token::LOR => Value::Bool(*a || *b),
393                _ => unreachable!(),
394            },
395            (Value::Int(a), Value::Int(b)) => {
396                match op {
397                    Token::ADD => Value::Int(a + b),
398                    Token::SUB => Value::Int(a - b),
399                    Token::MUL => Value::Int(a * b),
400                    Token::QUO => Value::Rat(BigRational::new(a.clone(), b.clone())),
401                    Token::QUO_ASSIGN => Value::Int(a / b), // force integer division
402                    Token::REM => Value::Int(a % b),
403                    Token::AND => Value::Int(a & b),
404                    Token::OR => Value::Int(a | b),
405                    Token::XOR => Value::Int(a ^ b),
406                    Token::AND_NOT => Value::Int(a & !b),
407                    _ => unreachable!(),
408                }
409            }
410            (Value::Rat(a), Value::Rat(b)) => match op {
411                Token::ADD => Value::Rat(a + b),
412                Token::SUB => Value::Rat(a - b),
413                Token::MUL => Value::Rat(a * b),
414                Token::QUO => Value::Rat(a / b),
415                _ => unreachable!(),
416            },
417            (Value::Float(a), Value::Float(b)) => match op {
418                Token::ADD => Value::Float(*a + *b),
419                Token::SUB => Value::Float(*a - *b),
420                Token::MUL => Value::Float(*a * *b),
421                Token::QUO => Value::Float(*a / *b),
422                _ => unreachable!(),
423            },
424            (Value::Complex(ar, ai), Value::Complex(br, bi)) => match op {
425                Token::ADD => Value::Complex(bx(add(ar, br)), bx(add(ai, bi))),
426                Token::SUB => Value::Complex(bx(sub(ar, br)), bx(sub(ai, bi))),
427                Token::MUL => {
428                    let (a, b, c, d) = (ar, ai, br, bi);
429                    let ac = mul(&a, &c);
430                    let bd = mul(&b, &d);
431                    let bc = mul(&b, &c);
432                    let ad = mul(&a, &d);
433                    Value::Complex(bx(sub(&ac, &bd)), bx(add(&bc, &ad)))
434                }
435                Token::QUO => {
436                    // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
437                    let (a, b, c, d) = (ar, ai, br, bi);
438                    let cc = mul(&c, &c);
439                    let dd = mul(&d, &d);
440                    let s = add(&cc, &dd);
441                    let ac = mul(&a, &c);
442                    let bd = mul(&b, &d);
443                    let acbd = add(&ac, &bd);
444                    let bc = mul(&b, &c);
445                    let ad = mul(&a, &d);
446                    let bcad = sub(&bc, &ad);
447                    Value::Complex(bx(div(&acbd, &s)), bx(div(&bcad, &s)))
448                }
449                _ => unreachable!(),
450            },
451            (Value::Str(a), Value::Str(b)) => match op {
452                Token::ADD => Value::Str(format!("{}{}", a, b)),
453                _ => unreachable!(),
454            },
455            _ => unreachable!(),
456        }
457    }
458
459    /// unary_op returns the result of the unary expression op y.
460    /// The operation must be defined for the operand.
461    /// If prec > 0 it specifies the ^ (xor) result size in bits.
462    /// If y is Unknown, the result is Unknown.
463    pub fn unary_op(op: &Token, y: &Value, prec: usize) -> Value {
464        match op {
465            Token::ADD => match y {
466                Value::Str(_) => unreachable!(),
467                _ => y.clone(),
468            },
469            Token::SUB => match y {
470                Value::Unknown => Value::Unknown,
471                Value::Int(i) => Value::Int(-i),
472                Value::Rat(r) => Value::Rat(-r),
473                Value::Float(f) => Value::Float(-(*f)),
474                Value::Complex(r, i) => Value::Complex(
475                    Box::new(Value::unary_op(op, r, 0)),
476                    Box::new(Value::unary_op(op, i, 0)),
477                ),
478                _ => unreachable!(),
479            },
480            Token::XOR => match y {
481                Value::Unknown => Value::Unknown,
482                Value::Int(i) => {
483                    let mut v = !i;
484                    if prec > 0 {
485                        v = v & (!(BigInt::from_i64(-1).unwrap() << prec * 8));
486                    }
487                    Value::Int(v)
488                }
489                _ => unreachable!(),
490            },
491            Token::NOT => match y {
492                Value::Unknown => Value::Unknown,
493                Value::Bool(b) => Value::Bool(!b),
494                _ => unreachable!(),
495            },
496            _ => unreachable!(),
497        }
498    }
499
500    /// compare returns the result of the comparison x op y.
501    /// The comparison must be defined for the operands.
502    /// If one of the operands is Unknown, the result is
503    /// false.
504    pub fn compare(x: &Value, op: &Token, y: &Value) -> bool {
505        let (x, y) = Value::match_type(Cow::Borrowed(x), Cow::Borrowed(y));
506        match (&*x, &*y) {
507            (Value::Unknown, _) | (_, Value::Unknown) => false,
508            (Value::Bool(a), Value::Bool(b)) => match op {
509                Token::EQL => a == b,
510                Token::NEQ => a != b,
511                _ => unreachable!(),
512            },
513            (Value::Int(a), Value::Int(b)) => match op {
514                Token::EQL => a == b,
515                Token::NEQ => a != b,
516                Token::LSS => a < b,
517                Token::LEQ => a <= b,
518                Token::GTR => a > b,
519                Token::GEQ => a >= b,
520                _ => unreachable!(),
521            },
522            (Value::Rat(a), Value::Rat(b)) => match op {
523                Token::EQL => a == b,
524                Token::NEQ => a != b,
525                Token::LSS => a < b,
526                Token::LEQ => a <= b,
527                Token::GTR => a > b,
528                Token::GEQ => a >= b,
529                _ => unreachable!(),
530            },
531            (Value::Float(a), Value::Float(b)) => match op {
532                Token::EQL => a == b,
533                Token::NEQ => a != b,
534                Token::LSS => a < b,
535                Token::LEQ => a <= b,
536                Token::GTR => a > b,
537                Token::GEQ => a >= b,
538                _ => unreachable!(),
539            },
540            (Value::Complex(ar, ai), Value::Complex(br, bi)) => {
541                let r = Value::compare(ar, op, br);
542                let i = Value::compare(ai, op, bi);
543                match op {
544                    Token::EQL => r && i,
545                    Token::NEQ => !r || !i,
546                    _ => unreachable!(),
547                }
548            }
549            (Value::Str(a), Value::Str(b)) => match op {
550                Token::EQL => a == b,
551                Token::NEQ => a != b,
552                Token::LSS => a < b,
553                Token::LEQ => a <= b,
554                Token::GTR => a > b,
555                Token::GEQ => a >= b,
556                _ => unreachable!(),
557            },
558            _ => unreachable!(),
559        }
560    }
561
562    // shift returns the result of the shift expression x op s
563    // with op == Token::SHL or Token::SHR (<< or >>). x must be
564    // an Int or an Unknown. If x is Unknown, the result is Unknown.
565    pub fn shift(x: &Value, op: &Token, s: usize) -> Value {
566        match x {
567            Value::Unknown => Value::Unknown,
568            Value::Int(i) => match op {
569                Token::SHL => Value::Int(i << s),
570                Token::SHR => Value::Int(i >> s),
571                _ => unreachable!(),
572            },
573            _ => unreachable!(),
574        }
575    }
576
577    pub fn bool_as_bool(&self) -> bool {
578        match self {
579            Value::Bool(b) => *b,
580            Value::Unknown => false,
581            _ => panic!("not a bool"),
582        }
583    }
584
585    pub fn str_as_string(&self) -> String {
586        match self {
587            Value::Str(s) => s.to_string(), //quote_str(s)
588            Value::Unknown => "".to_owned(),
589            _ => panic!("not a string"),
590        }
591    }
592
593    /// int_as_u64 returns the Go uint64 value and whether the result is exact;
594    pub fn int_as_u64(&self) -> (u64, bool) {
595        match self {
596            Value::Int(i) => match i.to_u64() {
597                Some(v) => (v, true),
598                _ => (std::u64::MAX, false),
599            },
600            Value::Unknown => (0, false),
601            _ => panic!("not an integer"),
602        }
603    }
604
605    /// int_as_i64 returns the Go int64 value and whether the result is exact;
606    pub fn int_as_i64(&self) -> (i64, bool) {
607        match self {
608            Value::Int(i) => match i.to_i64() {
609                Some(v) => (v, true),
610                _ => (
611                    if self.sign() > 0 {
612                        std::i64::MAX
613                    } else {
614                        std::i64::MIN
615                    },
616                    false,
617                ),
618            },
619            Value::Unknown => (0, false),
620            _ => panic!("not an integer"),
621        }
622    }
623
624    /// num_as_f64 returns the nearest Go float64 value of x and whether the result is exact;
625    /// x must be numeric or an Unknown, but not Complex. For values too small (too close to 0)
626    /// to represent as float64, num_as_f64 silently underflows to 0. The result sign always
627    /// matches the sign of x, even for 0.
628    /// If x is Unknown, the result is (0, false).
629    pub fn num_as_f64(&self) -> (F64, bool) {
630        match self {
631            Value::Int(_) | Value::Rat(_) => {
632                let vf = self.to_float();
633                if vf == Value::Unknown {
634                    (
635                        if self.sign() > 0 {
636                            std::f64::MAX.into()
637                        } else {
638                            std::f64::MIN.into()
639                        },
640                        false,
641                    )
642                } else {
643                    vf.num_as_f64()
644                }
645            }
646            Value::Float(f) => (*f, true),
647            Value::Unknown => (0.0.into(), false),
648            _ => panic!("not a number"),
649        }
650    }
651
652    /// num_as_f32 is like num_as_f64 but for float32 instead of float64.
653    pub fn num_as_f32(&self) -> (F32, bool) {
654        match self {
655            Value::Int(_) | Value::Rat(_) => {
656                let vf = self.to_float();
657                if vf == Value::Unknown {
658                    (
659                        if self.sign() > 0 {
660                            std::f32::MAX.into()
661                        } else {
662                            std::f32::MIN.into()
663                        },
664                        false,
665                    )
666                } else {
667                    vf.num_as_f32()
668                }
669            }
670            Value::Float(v) => {
671                let min: f64 = std::f32::MIN as f64;
672                let max: f64 = std::f32::MAX as f64;
673                let f: f64 = v.into_inner();
674                if f > min && f < max {
675                    ((f as f32).into(), true)
676                } else if f < min {
677                    (std::f32::MIN.into(), false)
678                } else {
679                    (std::f32::MAX.into(), false)
680                }
681            }
682            Value::Unknown => (0.0.into(), false),
683            _ => panic!("not a number"),
684        }
685    }
686
687    pub fn complex_as_complex64(&self) -> (F32, F32, bool) {
688        match self {
689            Value::Complex(r, i) => {
690                let (num_r, exact_r) = r.num_as_f32();
691                let (num_i, exact_i) = i.num_as_f32();
692                (num_r, num_i, exact_r && exact_i)
693            }
694            _ => panic!("not a complex"),
695        }
696    }
697
698    pub fn complex_as_complex128(&self) -> (F64, F64, bool) {
699        match self {
700            Value::Complex(r, i) => {
701                let (num_r, exact_r) = r.num_as_f64();
702                let (num_i, exact_i) = i.num_as_f64();
703                (num_r, num_i, exact_r && exact_i)
704            }
705            _ => panic!("not a complex"),
706        }
707    }
708
709    fn ord(&self) -> usize {
710        match self {
711            Value::Unknown => 0,
712            Value::Bool(_) | Value::Str(_) => 1,
713            Value::Int(_) => 2,
714            Value::Rat(_) => 3,
715            Value::Float(_) => 4,
716            Value::Complex(_, _) => 5,
717        }
718    }
719
720    /// match_type returns the matching representation (same type) with the
721    /// smallest complexity for two values x and y. If one of them is
722    /// numeric, both of them must be numeric. If one of them is Unknown
723    /// both results are Unknown
724    fn match_type<'a>(x: Cow<'a, Value>, y: Cow<'a, Value>) -> (Cow<'a, Value>, Cow<'a, Value>) {
725        if x.ord() > y.ord() {
726            let (y, x) = Value::match_type(y, x);
727            return (x, y);
728        }
729        match &*x {
730            Value::Bool(_) | Value::Str(_) | Value::Complex(_, _) => (x, y),
731            Value::Int(iv) => match &*y {
732                Value::Int(_) => (x, y),
733                Value::Rat(_) => (
734                    Cow::Owned(Value::Rat(BigRational::new(iv.clone(), 1.into()))),
735                    y,
736                ),
737                Value::Float(_) => match iv.to_f64() {
738                    Some(f) => (Cow::Owned(Value::Float(f.into())), y),
739                    None => (Cow::Owned(Value::Unknown), Cow::Owned(Value::Unknown)),
740                },
741                Value::Complex(_, _) => (
742                    Cow::Owned(Value::Complex(
743                        Box::new(x.into_owned()),
744                        Box::new(Value::with_f64(0.0)),
745                    )),
746                    y,
747                ),
748                Value::Unknown => (x.clone(), x),
749                _ => unreachable!(),
750            },
751            Value::Rat(rv) => match &*y {
752                Value::Rat(_) => (x, y),
753                Value::Float(_) => match rat_to_f64(rv) {
754                    Some(f) => (Cow::Owned(Value::Float(f.into())), y),
755                    None => (Cow::Owned(Value::Unknown), Cow::Owned(Value::Unknown)),
756                },
757                Value::Complex(_, _) => (
758                    Cow::Owned(Value::Complex(
759                        Box::new(x.into_owned()),
760                        Box::new(Value::with_f64(0.0)),
761                    )),
762                    y,
763                ),
764                Value::Unknown => (x.clone(), x),
765                _ => unreachable!(),
766            },
767            Value::Float(_) => match &*y {
768                Value::Float(_) => (x, y),
769                Value::Complex(_, _) => (
770                    Cow::Owned(Value::Complex(
771                        Box::new(x.into_owned()),
772                        Box::new(Value::with_f64(0.0)),
773                    )),
774                    y,
775                ),
776                Value::Unknown => (x.clone(), x),
777                _ => unreachable!(),
778            },
779            Value::Unknown => (x.clone(), x),
780        }
781    }
782}
783
784// ----------------------------------------------------------------------------
785// utilities
786
787fn short_quote_str(s: &str, max: usize) -> String {
788    let result = s.escape_default().collect();
789    shorten_with_ellipsis(result, max)
790}
791
792fn int_from_literal(lit: &str) -> Value {
793    let result = if lit.starts_with("0x") {
794        BigInt::from_str_radix(&lit[2..], 16)
795    } else if lit.starts_with("0o") {
796        BigInt::from_str_radix(&lit[2..], 8)
797    } else if lit.starts_with("0b") {
798        BigInt::from_str_radix(&lit[2..], 2)
799    } else {
800        BigInt::from_str_radix(lit, 10)
801    };
802    match result {
803        Ok(i) => Value::Int(i),
804        Err(_) => Value::Unknown,
805    }
806}
807
808fn float_from_literal(lit: &str) -> Value {
809    match lit.parse::<f64>() {
810        Ok(f) => Value::with_f64(f),
811        Err(_) => Value::Unknown,
812    }
813}
814
815fn shorten_with_ellipsis(s: String, max: usize) -> String {
816    if s.len() <= max {
817        s
818    } else {
819        let mut buf: Vec<char> = s.chars().collect();
820        buf = buf[0..(buf.len() - 3)].to_vec();
821        buf.append(&mut "...".to_owned().chars().collect());
822        buf.into_iter().collect()
823    }
824}
825
826fn rat_to_f64(r: &BigRational) -> Option<f64> {
827    match (r.numer().to_f64(), r.denom().to_f64()) {
828        (Some(n), Some(d)) => Some(n / d),
829        _ => None,
830    }
831}
832
833#[cfg(test)]
834mod test {
835    #[test]
836    fn test_str_unquote() {
837        let s = "\\111";
838        dbg!(s);
839    }
840}