Skip to content

Commit

Permalink
Added support for big integer literals
Browse files Browse the repository at this point in the history
Adapted more `Integer` primitives to work with big integers
  • Loading branch information
Hirevo committed Jun 29, 2020
1 parent 838b7dc commit ec15ca3
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 78 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions som-core/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Literal>),
}
1 change: 1 addition & 0 deletions som-interpreter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions som-interpreter/src/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))),
Expand Down
159 changes: 107 additions & 52 deletions som-interpreter/src/primitives/integer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::rc::Rc;

use num_bigint::{BigInt, Sign};
use num_traits::ToPrimitive;
use rand::distributions::Uniform;
use rand::Rng;

Expand All @@ -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<Value>) -> Return {
Expand Down Expand Up @@ -138,17 +134,18 @@ fn plus(_: &mut Universe, args: Vec<Value>) -> 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<Value>) -> Return {
Expand All @@ -162,17 +159,18 @@ fn minus(_: &mut Universe, args: Vec<Value>) -> 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<Value>) -> Return {
Expand All @@ -186,17 +184,18 @@ fn times(_: &mut Universe, args: Vec<Value>) -> 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<Value>) -> Return {
Expand All @@ -210,19 +209,20 @@ fn divide(_: &mut Universe, args: Vec<Value>) -> 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<Value>) -> Return {
Expand Down Expand Up @@ -286,39 +286,59 @@ fn sqrt(_: &mut Universe, args: Vec<Value>) -> 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)),
}
}

fn bitand(_: &mut Universe, args: Vec<Value>) -> 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<Value>) -> 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<Value>) -> Return {
Expand All @@ -329,10 +349,19 @@ fn lt(_: &mut Universe, args: Vec<Value>) -> 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<Value>) -> Return {
Expand All @@ -343,7 +372,15 @@ fn eq(_: &mut Universe, args: Vec<Value>) -> 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<Value>) -> Return {
Expand All @@ -355,6 +392,15 @@ fn shift_left(_: &mut Universe, args: Vec<Value>) -> 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<Value>) -> Return {
Expand All @@ -366,6 +412,15 @@ fn shift_right(_: &mut Universe, args: Vec<Value>) -> 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.
Expand Down
45 changes: 19 additions & 26 deletions som-lexer/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<i64>() {
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
Expand Down
2 changes: 2 additions & 0 deletions som-lexer/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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'`).
Expand Down
Loading

0 comments on commit ec15ca3

Please sign in to comment.