diff --git a/Cargo.lock b/Cargo.lock index 034b40c9..f5ae9b41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,7 @@ dependencies = [ "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "som-core 0.1.0", "som-lexer 0.1.0", diff --git a/som-core/src/ast.rs b/som-core/src/ast.rs index 0492cccc..384e96cd 100644 --- a/som-core/src/ast.rs +++ b/som-core/src/ast.rs @@ -237,6 +237,8 @@ pub enum Literal { Double(f64), /// Represents a integer number literal (eg. `42`). Integer(i64), + /// Represents a big integer (bigger than a 64-bit signed integer can represent). + BigInteger(String), /// Represents an array literal (eg. `$(1 2 3)`) Array(Vec), } diff --git a/som-interpreter/Cargo.toml b/som-interpreter/Cargo.toml index a21d87a9..582760e3 100644 --- a/som-interpreter/Cargo.toml +++ b/som-interpreter/Cargo.toml @@ -32,6 +32,7 @@ indexmap = "1.4.0" # big integers num-bigint = "0.2.6" +num-traits = "0.2.11" # random numbers rand = "0.7.3" diff --git a/som-interpreter/src/evaluate.rs b/som-interpreter/src/evaluate.rs index a7e5eb95..a7df3aeb 100644 --- a/som-interpreter/src/evaluate.rs +++ b/som-interpreter/src/evaluate.rs @@ -127,6 +127,10 @@ impl Evaluate for ast::Literal { Return::Local(Value::Array(Rc::new(RefCell::new(output)))) } Self::Integer(int) => Return::Local(Value::Integer(*int)), + Self::BigInteger(int) => match int.parse() { + Ok(value) => Return::Local(Value::BigInteger(value)), + Err(err) => Return::Exception(err.to_string()), + }, Self::Double(double) => Return::Local(Value::Double(*double)), Self::Symbol(sym) => Return::Local(Value::Symbol(universe.intern_symbol(sym))), Self::String(string) => Return::Local(Value::String(Rc::new(string.clone()))), diff --git a/som-interpreter/src/primitives/integer.rs b/som-interpreter/src/primitives/integer.rs index a0aa152a..fb8497bf 100644 --- a/som-interpreter/src/primitives/integer.rs +++ b/som-interpreter/src/primitives/integer.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use num_bigint::{BigInt, Sign}; +use num_traits::ToPrimitive; use rand::distributions::Uniform; use rand::Rng; @@ -10,19 +11,14 @@ use crate::primitives::PrimitiveFn; use crate::universe::Universe; use crate::value::Value; -macro_rules! promote { - ($signature:expr, $value:expr) => { - match $value { - Value::Integer(value) => value as f64, - Value::Double(value) => value, - _ => { - return Return::Exception(format!( - "'{}': wrong type (expected `integer` or `double`)", - $signature - )) - } +macro_rules! demote { + ($expr:expr) => {{ + let value = $expr; + match value.to_i64() { + Some(value) => Return::Local(Value::Integer(value)), + None => Return::Local(Value::BigInteger(value)), } - }; + }}; } fn from_string(universe: &mut Universe, args: Vec) -> Return { @@ -138,17 +134,18 @@ fn plus(_: &mut Universe, args: Vec) -> Return { match (a, b) { (Value::Integer(a), Value::Integer(b)) => match a.checked_add(b) { Some(value) => Return::Local(Value::Integer(value)), - None => Return::Local(Value::BigInteger(BigInt::from(a) + BigInt::from(b))), + None => demote!(BigInt::from(a) + BigInt::from(b)), }, + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(a + b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(a + BigInt::from(b)) + } + (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a + b)), (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { Return::Local(Value::Double((a as f64) + b)) } - (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a + b)), - (Value::BigInteger(a), Value::BigInteger(b)) => Return::Local(Value::BigInteger(a + b)), _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), } - - // promoted_expr!(SIGNATURE, a, b, |a, b| a + b) } fn minus(_: &mut Universe, args: Vec) -> Return { @@ -162,17 +159,18 @@ fn minus(_: &mut Universe, args: Vec) -> Return { match (a, b) { (Value::Integer(a), Value::Integer(b)) => match a.checked_sub(b) { Some(value) => Return::Local(Value::Integer(value)), - None => Return::Local(Value::BigInteger(BigInt::from(a) - BigInt::from(b))), + None => demote!(BigInt::from(a) - BigInt::from(b)), }, + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(a - b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(a - BigInt::from(b)) + } + (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a - b)), (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { Return::Local(Value::Double((a as f64) - b)) } - (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a - b)), - (Value::BigInteger(a), Value::BigInteger(b)) => Return::Local(Value::BigInteger(a - b)), _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), } - - // promoted_expr!(SIGNATURE, a, b, |a, b| a - b) } fn times(_: &mut Universe, args: Vec) -> Return { @@ -186,17 +184,18 @@ fn times(_: &mut Universe, args: Vec) -> Return { match (a, b) { (Value::Integer(a), Value::Integer(b)) => match a.checked_mul(b) { Some(value) => Return::Local(Value::Integer(value)), - None => Return::Local(Value::BigInteger(BigInt::from(a) * BigInt::from(b))), + None => demote!(BigInt::from(a) * BigInt::from(b)), }, + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(a * b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(a * BigInt::from(b)) + } + (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a * b)), (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { Return::Local(Value::Double((a as f64) * b)) } - (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a * b)), - (Value::BigInteger(a), Value::BigInteger(b)) => Return::Local(Value::BigInteger(a * b)), _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), } - - // promoted_expr!(SIGNATURE, a, b, |a, b| a * b) } fn divide(_: &mut Universe, args: Vec) -> Return { @@ -210,19 +209,20 @@ fn divide(_: &mut Universe, args: Vec) -> Return { ]); match (a, b) { - (Value::Integer(a), Value::Integer(b)) => match a.checked_div_euclid(b) { + (Value::Integer(a), Value::Integer(b)) => match a.checked_div(b) { Some(value) => Return::Local(Value::Integer(value)), - None => Return::Local(Value::BigInteger(BigInt::from(a) / BigInt::from(b))), + None => demote!(BigInt::from(a) / BigInt::from(b)), }, + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(a / b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(a / BigInt::from(b)) + } + (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a / b)), (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { Return::Local(Value::Double((a as f64) / b)) } - (Value::Double(a), Value::Double(b)) => Return::Local(Value::Double(a / b)), - (Value::BigInteger(a), Value::BigInteger(b)) => Return::Local(Value::BigInteger(a / b)), _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), } - - // Return::Local(Value::Integer(a.div_euclid(b))) } fn divide_float(_: &mut Universe, args: Vec) -> Return { @@ -286,16 +286,22 @@ fn sqrt(_: &mut Universe, args: Vec) -> Return { const SIGNATURE: &str = "Integer>>#sqrt"; expect_args!(SIGNATURE, args, [ - Value::Integer(a) => a, + a => a, ]); - let sqrt = (a as f64).sqrt(); - let trucated = sqrt.trunc(); - - if sqrt == trucated { - Return::Local(Value::Integer(trucated as i64)) - } else { - Return::Local(Value::Double(sqrt)) + match a { + Value::Integer(a) => { + let sqrt = (a as f64).sqrt(); + let trucated = sqrt.trunc(); + if sqrt == trucated { + Return::Local(Value::Integer(trucated as i64)) + } else { + Return::Local(Value::Double(sqrt)) + } + } + Value::BigInteger(a) => demote!(a.sqrt()), + Value::Double(a) => Return::Local(Value::Double(a.sqrt())), + _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), } } @@ -303,22 +309,36 @@ fn bitand(_: &mut Universe, args: Vec) -> Return { const SIGNATURE: &str = "Integer>>#&"; expect_args!(SIGNATURE, args, [ - Value::Integer(a) => a, - Value::Integer(b) => b, + a => a, + b => b, ]); - Return::Local(Value::Integer(a & b)) + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Integer(a & b)), + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(a & b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(a & BigInt::from(b)) + } + _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), + } } fn bitxor(_: &mut Universe, args: Vec) -> Return { const SIGNATURE: &str = "Integer>>#bitXor:"; expect_args!(SIGNATURE, args, [ - Value::Integer(a) => a, - Value::Integer(b) => b, + a => a, + b => b, ]); - Return::Local(Value::Integer(a ^ b)) + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Integer(a ^ b)), + (Value::BigInteger(a), Value::BigInteger(b)) => demote!(a ^ b), + (Value::BigInteger(a), Value::Integer(b)) | (Value::Integer(b), Value::BigInteger(a)) => { + demote!(a ^ BigInt::from(b)) + } + _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), + } } fn lt(_: &mut Universe, args: Vec) -> Return { @@ -329,10 +349,19 @@ fn lt(_: &mut Universe, args: Vec) -> Return { b => b, ]); - let a = promote!(SIGNATURE, a); - let b = promote!(SIGNATURE, b); - - Return::Local(Value::Boolean(a < b)) + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Boolean(a < b)), + (Value::BigInteger(a), Value::BigInteger(b)) => Return::Local(Value::Boolean(a < b)), + (Value::Double(a), Value::Double(b)) => Return::Local(Value::Boolean(a < b)), + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + Return::Local(Value::Boolean((a as f64) < b)) + } + (Value::BigInteger(_), Value::Integer(_)) => Return::Local(Value::Boolean(false)), + (Value::Integer(_), Value::BigInteger(_)) => Return::Local(Value::Boolean(true)), + (a, b) => { + return Return::Exception(format!("'{}': wrong types ({:?} | {:?})", SIGNATURE, a, b)) + } + } } fn eq(_: &mut Universe, args: Vec) -> Return { @@ -343,7 +372,15 @@ fn eq(_: &mut Universe, args: Vec) -> Return { b => b, ]); - Return::Local(Value::Boolean(a == b)) + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => Return::Local(Value::Boolean(a == b)), + (Value::BigInteger(a), Value::BigInteger(b)) => Return::Local(Value::Boolean(a == b)), + (Value::Double(a), Value::Double(b)) => Return::Local(Value::Boolean(a == b)), + (Value::Integer(a), Value::Double(b)) | (Value::Double(b), Value::Integer(a)) => { + Return::Local(Value::Boolean((a as f64) == b)) + } + _ => Return::Local(Value::Boolean(false)), + } } fn shift_left(_: &mut Universe, args: Vec) -> Return { @@ -355,6 +392,15 @@ fn shift_left(_: &mut Universe, args: Vec) -> Return { ]); Return::Local(Value::Integer(a << b)) + + // match a { + // Value::Integer(a) => match a.checked_shl(b as u32) { + // Some(value) => Return::Local(Value::Integer(value)), + // None => demote!(BigInt::from(a) << (b as usize)), + // }, + // Value::BigInteger(a) => demote!(a << (b as usize)), + // _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), + // } } fn shift_right(_: &mut Universe, args: Vec) -> Return { @@ -366,6 +412,15 @@ fn shift_right(_: &mut Universe, args: Vec) -> Return { ]); Return::Local(Value::Integer(a >> b)) + + // match a { + // Value::Integer(a) => match a.checked_shr(b as u32) { + // Some(value) => Return::Local(Value::Integer(value)), + // None => demote!(BigInt::from(a) >> (b as usize)), + // }, + // Value::BigInteger(a) => demote!(a >> (b as usize)), + // _ => return Return::Exception(format!("'{}': wrong types", SIGNATURE)), + // } } /// Search for a primitive matching the given signature. diff --git a/som-lexer/src/lexer.rs b/som-lexer/src/lexer.rs index fef5aec3..4b053335 100644 --- a/som-lexer/src/lexer.rs +++ b/som-lexer/src/lexer.rs @@ -263,36 +263,29 @@ impl Iterator for Lexer { let iter = self.chars.iter().rev().copied(); let int_part_len = iter.clone().take_while(|c| c.is_digit(10)).count(); let mut dec_iter = iter.clone().skip(int_part_len).peekable(); - if let Some('.') = dec_iter.peek().copied() { - dec_iter.next()?; - match dec_iter.peek() { - Some(v) if v.is_digit(10) => { - let dec_part_len = - dec_iter.clone().take_while(|c| c.is_digit(10)).count(); - let total_len = int_part_len + dec_part_len + 1; - let repr: String = iter.take(total_len).collect(); - let number: f64 = repr.parse().ok()?; - for _ in 0..total_len { - self.chars.pop()?; - } - Some(Token::LitDouble(number)) + match (dec_iter.next(), dec_iter.peek()) { + (Some('.'), Some(ch)) if ch.is_digit(10) => { + let dec_part_len = + dec_iter.clone().take_while(|c| c.is_digit(10)).count(); + let total_len = int_part_len + dec_part_len + 1; + let repr: String = iter.take(total_len).collect(); + let number: f64 = repr.parse().ok()?; + for _ in 0..total_len { + self.chars.pop()?; } - _ => { - let repr: String = iter.take(int_part_len).collect(); - let number: i64 = repr.parse().ok()?; - for _ in 0..int_part_len { - self.chars.pop()?; - } + Some(Token::LitDouble(number)) + } + _ => { + let repr: String = iter.take(int_part_len).collect(); + for _ in 0..int_part_len { + self.chars.pop()?; + } + if let Ok(number) = repr.parse::() { Some(Token::LitInteger(number)) + } else { + Some(Token::LitBigInteger(repr)) } } - } else { - let repr: String = iter.take(int_part_len).collect(); - let number: i64 = repr.parse().ok()?; - for _ in 0..int_part_len { - self.chars.pop()?; - } - Some(Token::LitInteger(number)) } } else { None diff --git a/som-lexer/src/token.rs b/som-lexer/src/token.rs index b8b77fda..a6759a39 100644 --- a/som-lexer/src/token.rs +++ b/som-lexer/src/token.rs @@ -55,6 +55,8 @@ pub enum Token { Separator, /// An integer literal (`10`). LitInteger(i64), + /// A big integer literal (`1542252643255252434`). + LitBigInteger(String), /// A floating-point literal (`10.6`). LitDouble(f64), /// A string literal (`'hello, world'`). diff --git a/som-parser-symbols/src/lang.rs b/som-parser-symbols/src/lang.rs index fdd78f0e..3cbf0212 100644 --- a/som-parser-symbols/src/lang.rs +++ b/som-parser-symbols/src/lang.rs @@ -42,6 +42,19 @@ pub fn exact_ident<'a, 'b: 'a>(string: &'b str) -> impl Parser<'a, ()> { } } +pub fn big_integer<'a>() -> impl Parser<'a, String> { + move |input: &'a [Token]| { + let (sign, input) = optional(exact(Token::Minus)).parse(input)?; + let sign = if sign.is_some() { "-" } else { "" }; + + let (head, tail) = input.split_first()?; + match head { + Token::LitBigInteger(value) => Some((format!("{}{}", sign, value), tail)), + _ => None, + } + } +} + pub fn integer<'a>() -> impl Parser<'a, i64> { move |input: &'a [Token]| { let (sign, input) = optional(exact(Token::Minus)).parse(input)?; @@ -149,6 +162,7 @@ pub fn array<'a>() -> impl Parser<'a, Vec> { pub fn literal<'a>() -> impl Parser<'a, Literal> { (double().map(Literal::Double)) .or(integer().map(Literal::Integer)) + .or(big_integer().map(Literal::BigInteger)) .or(string().map(Literal::String)) .or(symbol().map(Literal::Symbol)) .or(array().map(Literal::Array))