mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Formalize concept of a Number (#55)
Number is implemented using rust_decimal::Decimal which uses a 96 bit mantissa. TODO: a) Support u64, i64 variants b) Determine desired semantics for floating-point c) Determine desired big integer length d) Explore other big int/big float crates Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
bb0ca29753
commit
ed3492fd7b
@@ -8,9 +8,8 @@ edition = "2021"
|
||||
[dependencies]
|
||||
anyhow = "1.0.66"
|
||||
semver = "1.0.20"
|
||||
ordered-float = "3.4.0"
|
||||
serde = {version = "1.0.150", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.89"
|
||||
serde_json = {version = "1.0.89", features = ["arbitrary_precision"] }
|
||||
serde_yaml = "0.9.16"
|
||||
log = "0.4.17"
|
||||
env_logger="0.10.0"
|
||||
@@ -18,6 +17,8 @@ lazy_static = "1.4.0"
|
||||
rand = "0.8.5"
|
||||
data-encoding = "2.4.0"
|
||||
regex = "1.10.2"
|
||||
num = "0.4.1"
|
||||
rust_decimal = { version = "1.33.1", features = ["serde-with-arbitrary-precision", "serde_json", "maths"] }
|
||||
|
||||
[dev-dependencies]
|
||||
clap = { version = "4.4.7", features = ["derive"] }
|
||||
|
||||
@@ -5,7 +5,8 @@ use crate::ast::{Expr, Ref};
|
||||
use crate::builtins;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_numeric};
|
||||
use crate::lexer::Span;
|
||||
use crate::value::{Float, Value};
|
||||
use crate::number::Number;
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -23,18 +24,18 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
fn count(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, "count", params, args, 1)?;
|
||||
|
||||
Ok(Value::from_float(match &args[0] {
|
||||
Value::Array(a) => a.len() as Float,
|
||||
Value::Set(a) => a.len() as Float,
|
||||
Value::Object(a) => a.len() as Float,
|
||||
Value::String(a) => a.encode_utf16().count() as Float,
|
||||
Ok(Value::from(Number::from(match &args[0] {
|
||||
Value::Array(a) => a.len(),
|
||||
Value::Set(a) => a.len(),
|
||||
Value::Object(a) => a.len(),
|
||||
Value::String(a) => a.encode_utf16().count(),
|
||||
a => {
|
||||
let span = params[0].span();
|
||||
bail!(span.error(
|
||||
format!("`count` requires array/object/set/string argument. Got `{a}`.").as_str()
|
||||
))
|
||||
}
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
fn max(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -70,26 +71,26 @@ fn min(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn product(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, "product", params, args, 1)?;
|
||||
|
||||
let mut v = 1 as Float;
|
||||
Ok(match &args[0] {
|
||||
let mut v = Number::from(1_u64);
|
||||
Ok(Value::from(match &args[0] {
|
||||
Value::Array(a) => {
|
||||
for e in a.iter() {
|
||||
v *= ensure_numeric("product", ¶ms[0], e)?;
|
||||
v.mul_assign(&ensure_numeric("product", ¶ms[0], e)?)?;
|
||||
}
|
||||
Value::from_float(v)
|
||||
v
|
||||
}
|
||||
|
||||
Value::Set(a) => {
|
||||
for e in a.iter() {
|
||||
v *= ensure_numeric("product", ¶ms[0], e)?;
|
||||
v.mul_assign(&ensure_numeric("product", ¶ms[0], e)?)?;
|
||||
}
|
||||
Value::from_float(v)
|
||||
v
|
||||
}
|
||||
a => {
|
||||
let span = params[0].span();
|
||||
bail!(span.error(format!("`product` requires array/set argument. Got `{a}`.").as_str()))
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -98,10 +99,10 @@ fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Value::Array(a) => {
|
||||
let mut ac = (**a).clone();
|
||||
ac.sort();
|
||||
Value::from_array(ac)
|
||||
Value::from(ac)
|
||||
}
|
||||
// Sorting a set produces array.
|
||||
Value::Set(a) => Value::from_array(a.iter().cloned().collect()),
|
||||
Value::Set(a) => Value::from(a.iter().cloned().collect::<Vec<Value>>()),
|
||||
a => {
|
||||
let span = params[0].span();
|
||||
bail!(span.error(format!("`sort` requires array/set argument. Got `{a}`.").as_str()))
|
||||
@@ -112,24 +113,24 @@ fn sort(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
fn sum(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, "sum", params, args, 1)?;
|
||||
|
||||
let mut v = 0 as Float;
|
||||
Ok(match &args[0] {
|
||||
let mut v = Number::from(0_u64);
|
||||
Ok(Value::from(match &args[0] {
|
||||
Value::Array(a) => {
|
||||
for e in a.iter() {
|
||||
v += ensure_numeric("sum", ¶ms[0], e)?;
|
||||
v.add_assign(&ensure_numeric("sum", ¶ms[0], e)?)?;
|
||||
}
|
||||
Value::from_float(v)
|
||||
v
|
||||
}
|
||||
|
||||
Value::Set(a) => {
|
||||
for e in a.iter() {
|
||||
v += ensure_numeric("sum", ¶ms[0], e)?;
|
||||
v.add_assign(&ensure_numeric("sum", ¶ms[0], e)?)?;
|
||||
}
|
||||
Value::from_float(v)
|
||||
v
|
||||
}
|
||||
a => {
|
||||
let span = params[0].span();
|
||||
bail!(span.error(format!("`sum` requires array/set argument. Got `{a}`.").as_str()))
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -45,15 +45,21 @@ fn slice(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let start = ensure_numeric(name, ¶ms[1], &args[1].clone())?;
|
||||
let stop = ensure_numeric(name, ¶ms[2], &args[2].clone())?;
|
||||
|
||||
if start != start.floor() || stop != stop.floor() {
|
||||
if !start.is_integer() || !stop.is_integer() {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
// TODO: usize conversion checks.
|
||||
let start = start as usize;
|
||||
let stop = match stop as usize {
|
||||
s if s > array.len() => array.len(),
|
||||
s => s,
|
||||
let start = match start.as_i64() {
|
||||
Some(n) if n < 0 => 0,
|
||||
Some(n) => n as usize,
|
||||
_ => return Ok(Value::Undefined),
|
||||
};
|
||||
|
||||
let stop = match stop.as_i64() {
|
||||
Some(n) if n < 0 => 0,
|
||||
Some(n) if n as usize > array.len() => array.len(),
|
||||
Some(n) => n as usize,
|
||||
_ => return Ok(Value::Undefined),
|
||||
};
|
||||
|
||||
if start >= stop {
|
||||
@@ -61,5 +67,5 @@ fn slice(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
}
|
||||
|
||||
let slice = &array[start..stop];
|
||||
Ok(Value::from_array(slice.to_vec()))
|
||||
Ok(Value::from(slice.to_vec()))
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::builtins;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_numeric};
|
||||
|
||||
use crate::lexer::Span;
|
||||
use crate::value::{Float, Value};
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -28,14 +28,10 @@ fn and(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if v1 != v1.floor() || v2 != v2.floor() {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
// TODO: precision
|
||||
let v1 = v1 as i64;
|
||||
let v2 = v2 as i64;
|
||||
Ok(Value::from_float((v1 & v2) as Float))
|
||||
Ok(match v1.and(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -45,19 +41,10 @@ fn lsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if v1 != v1.floor() || v2 != v2.floor() {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
// TODO: precision
|
||||
let v1 = v1 as i64;
|
||||
let v2 = v2 as i64;
|
||||
|
||||
if v2 <= 0 {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
Ok(Value::from_float((v1 << v2) as Float))
|
||||
Ok(match v1.lsh(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -66,13 +53,10 @@ fn negate(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
|
||||
let v = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
|
||||
if v != v.floor() {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
// TODO: precision
|
||||
let v = v as i64;
|
||||
Ok(Value::from_float((!v) as Float))
|
||||
Ok(match v.neg() {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn or(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -82,14 +66,10 @@ fn or(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if v1 != v1.floor() || v2 != v2.floor() {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
// TODO: precision
|
||||
let v1 = v1 as i64;
|
||||
let v2 = v2 as i64;
|
||||
Ok(Value::from_float((v1 | v2) as Float))
|
||||
Ok(match v1.or(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -99,19 +79,10 @@ fn rsh(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if v1 != v1.floor() || v2 != v2.floor() {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
// TODO: precision
|
||||
let v1 = v1 as i64;
|
||||
let v2 = v2 as i64;
|
||||
|
||||
if v2 < 0 {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
Ok(Value::from_float((v1 >> v2) as Float))
|
||||
Ok(match v1.rsh(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn xor(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -121,12 +92,8 @@ fn xor(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let v1 = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let v2 = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
|
||||
if v1 != v1.floor() || v2 != v2.floor() {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
// TODO: precision
|
||||
let v1 = v1 as i64;
|
||||
let v2 = v2 as i64;
|
||||
Ok(Value::from_float((v1 ^ v2) as Float))
|
||||
Ok(match v1.xor(&v2) {
|
||||
Some(v) => Value::from(v),
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ fn to_number(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
|
||||
let span = params[0].span();
|
||||
Ok(match &args[0] {
|
||||
Value::Bool(true) => Value::from_float(1.0),
|
||||
Value::Bool(false) => Value::from_float(0.0),
|
||||
Value::Bool(true) => Value::from(1u64),
|
||||
Value::Bool(false) => Value::from(0u64),
|
||||
Value::Number(_) => args[0].clone(),
|
||||
// Eventhough the doc says that strings are converted using strconv.Atoi golang method,
|
||||
// in practice strings seems to be read as json numbers. This means that floating point
|
||||
|
||||
@@ -8,6 +8,7 @@ pub mod comparison;
|
||||
mod conversions;
|
||||
mod debugging;
|
||||
pub mod deprecated;
|
||||
|
||||
mod encoding;
|
||||
pub mod numbers;
|
||||
mod objects;
|
||||
|
||||
@@ -5,11 +5,12 @@ use crate::ast::{ArithOp, Expr, Ref};
|
||||
use crate::builtins;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_numeric, ensure_string};
|
||||
use crate::lexer::Span;
|
||||
use crate::value::{Float, Value};
|
||||
use crate::number::Number;
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Result};
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
@@ -22,6 +23,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
}
|
||||
|
||||
pub fn arithmetic_operation(
|
||||
span: &Span,
|
||||
op: &ArithOp,
|
||||
expr1: &Expr,
|
||||
expr2: &Expr,
|
||||
@@ -32,36 +34,37 @@ pub fn arithmetic_operation(
|
||||
let v1 = ensure_numeric(op_name.as_str(), expr1, &v1)?;
|
||||
let v2 = ensure_numeric(op_name.as_str(), expr2, &v2)?;
|
||||
|
||||
Ok(Value::from_float(match op {
|
||||
ArithOp::Add => v1 + v2,
|
||||
ArithOp::Sub => v1 - v2,
|
||||
ArithOp::Mul => v1 * v2,
|
||||
ArithOp::Div if v2 == 0.0 => return Ok(Value::Undefined),
|
||||
ArithOp::Div => v1 / v2,
|
||||
ArithOp::Mod if v2 == 0.0 => return Ok(Value::Undefined),
|
||||
ArithOp::Mod if v1.floor() != v1 => return Ok(Value::Undefined),
|
||||
ArithOp::Mod if v2.floor() != v2 => return Ok(Value::Undefined),
|
||||
ArithOp::Mod => v1 % v2,
|
||||
Ok(Value::from(match op {
|
||||
ArithOp::Add => v1.add(&v2)?,
|
||||
ArithOp::Sub => v1.sub(&v2)?,
|
||||
ArithOp::Mul => v1.mul(&v2)?,
|
||||
ArithOp::Div if v2 == Number::from(0u64) => bail!(span.error("divide by zero")),
|
||||
ArithOp::Div => v1.divide(&v2)?,
|
||||
ArithOp::Mod if v2 == Number::from(0u64) => bail!(span.error("modulo by zero")),
|
||||
ArithOp::Mod if !v1.is_integer() || !v2.is_integer() => {
|
||||
bail!(span.error("modulo on floating-point number"))
|
||||
}
|
||||
ArithOp::Mod => v1.modulo(&v2)?,
|
||||
}))
|
||||
}
|
||||
|
||||
fn abs(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, "abs", params, args, 1)?;
|
||||
Ok(Value::from_float(
|
||||
Ok(Value::from(
|
||||
ensure_numeric("abs", ¶ms[0], &args[0])?.abs(),
|
||||
))
|
||||
}
|
||||
|
||||
fn ceil(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, "ceil", params, args, 1)?;
|
||||
Ok(Value::from_float(
|
||||
Ok(Value::from(
|
||||
ensure_numeric("ceil", ¶ms[0], &args[0])?.ceil(),
|
||||
))
|
||||
}
|
||||
|
||||
fn floor(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, "floor", params, args, 1)?;
|
||||
Ok(Value::from_float(
|
||||
Ok(Value::from(
|
||||
ensure_numeric("floor", ¶ms[0], &args[0])?.floor(),
|
||||
))
|
||||
}
|
||||
@@ -71,27 +74,31 @@ fn range(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let v1 = ensure_numeric("numbers.range", ¶ms[0], &args[0].clone())?;
|
||||
let v2 = ensure_numeric("numbers.range", ¶ms[1], &args[1].clone())?;
|
||||
|
||||
if v1 != v1.floor() || v2 != v2.floor() {
|
||||
// TODO: OPA returns undefined here.
|
||||
// Can we emit a warning?
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
let incr = if v2 >= v1 { 1 } else { -1 } as Float;
|
||||
let (incr, num_elements) = match (v1.as_i64(), v2.as_i64()) {
|
||||
(Some(v1), Some(v2)) if v2 > v1 => (1, v2 + 1 - v1),
|
||||
(Some(v1), Some(v2)) => (-1, v1 + 1 - v2),
|
||||
_ => {
|
||||
// TODO: OPA returns undefined here.
|
||||
// Can we emit a warning?
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
};
|
||||
|
||||
let mut values = Vec::with_capacity((v2 - v1).abs() as usize + 1);
|
||||
let mut values = Vec::with_capacity(num_elements as usize);
|
||||
|
||||
let mut v = v1;
|
||||
let incr = Number::from(incr as i64);
|
||||
while v != v2 {
|
||||
values.push(Value::from_float(v));
|
||||
v += incr;
|
||||
values.push(Value::from(v.clone()));
|
||||
v.add_assign(&incr)?;
|
||||
}
|
||||
values.push(Value::from_float(v));
|
||||
values.push(Value::from(v));
|
||||
Ok(Value::from_array(values))
|
||||
}
|
||||
|
||||
fn round(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, "round", params, args, 1)?;
|
||||
Ok(Value::from_float(
|
||||
Ok(Value::from(
|
||||
ensure_numeric("round", ¶ms[0], &args[0])?.round(),
|
||||
))
|
||||
}
|
||||
@@ -101,16 +108,15 @@ fn intn(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, fcn, params, args, 2)?;
|
||||
let _ = ensure_string(fcn, ¶ms[0], &args[0])?;
|
||||
let n = ensure_numeric(fcn, ¶ms[0], &args[1])?;
|
||||
if n != n.floor() || n < 0 as Float {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
if n == 0.0 {
|
||||
return Ok(Value::from_float(0 as Float));
|
||||
}
|
||||
|
||||
// TODO: bounds checking; arbitrary precision
|
||||
let mut rng = thread_rng();
|
||||
let v = rng.gen_range(0..n as u64);
|
||||
Ok(Value::from_float(v as f64))
|
||||
Ok(match n.as_u64() {
|
||||
Some(0) => Value::from(0u64),
|
||||
Some(n) => {
|
||||
// TODO: bounds checking; arbitrary precision
|
||||
let mut rng = thread_rng();
|
||||
let v = rng.gen_range(0..n);
|
||||
Value::from(v)
|
||||
}
|
||||
_ => Value::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ fn compare(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Ordering::Equal => 0,
|
||||
Ordering::Greater => 1,
|
||||
};
|
||||
Ok(Value::from_float(result as f64))
|
||||
Ok(Value::from(result as i64))
|
||||
}
|
||||
|
||||
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
|
||||
@@ -8,7 +8,8 @@ use crate::builtins::utils::{
|
||||
ensure_string_collection,
|
||||
};
|
||||
use crate::lexer::Span;
|
||||
use crate::value::{Float, Number, Value};
|
||||
use crate::number::Number;
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -65,24 +66,31 @@ fn endswith(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
Ok(Value::Bool(s1.ends_with(s2.as_ref())))
|
||||
}
|
||||
|
||||
fn format_number(n: &Number, base: u64) -> String {
|
||||
match base {
|
||||
2 => n.format_bin(),
|
||||
8 => n.format_octal(),
|
||||
10 => n.format_decimal(),
|
||||
16 => n.format_hex(),
|
||||
_ => "".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_int(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "endswith";
|
||||
let name = "format_int";
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let mut n = ensure_numeric(name, ¶ms[0], &args[0])?;
|
||||
let mut sign = "";
|
||||
if n < 0.0 {
|
||||
if n < Number::from(0u64) {
|
||||
n = n.abs();
|
||||
sign = "-";
|
||||
}
|
||||
let n = n.floor() as u64;
|
||||
let n = n.floor();
|
||||
|
||||
Ok(Value::String(
|
||||
(sign.to_owned()
|
||||
+ &match ensure_numeric(name, ¶ms[1], &args[1])? as u64 {
|
||||
2 => format!("{:b}", n),
|
||||
8 => format!("{:o}", n),
|
||||
10 => format!("{}", n),
|
||||
16 => format!("{:x}", n),
|
||||
+ &match ensure_numeric(name, ¶ms[1], &args[1])?.as_u64() {
|
||||
Some(b) => format_number(&n, b),
|
||||
_ => return Ok(Value::Undefined),
|
||||
})
|
||||
.into(),
|
||||
@@ -94,10 +102,10 @@ fn indexof(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
ensure_args_count(span, name, params, args, 2)?;
|
||||
let s1 = ensure_string(name, ¶ms[0], &args[0])?;
|
||||
let s2 = ensure_string(name, ¶ms[1], &args[1])?;
|
||||
Ok(Value::from_float(match s1.find(s2.as_ref()) {
|
||||
Ok(Value::from(Number::from(match s1.find(s2.as_ref()) {
|
||||
Some(pos) => pos as i64,
|
||||
_ => -1,
|
||||
} as Float))
|
||||
})))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -111,7 +119,7 @@ fn indexof_n(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
let mut idx = 0;
|
||||
while idx < s1.len() {
|
||||
if let Some(pos) = s1.find(s2.as_ref()) {
|
||||
positions.push(Value::from_float(pos as Float));
|
||||
positions.push(Value::from(pos as u64));
|
||||
idx = pos + 1;
|
||||
} else {
|
||||
break;
|
||||
@@ -201,64 +209,81 @@ fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let get_sign_value = |f: &Number| match (emit_sign, f.0 .0) {
|
||||
(_, v) if v < 0.0 => ("-", v),
|
||||
(true, v) => ("+", v),
|
||||
(false, v) if leave_space_for_elided_sign => (" ", v),
|
||||
(false, v) => ("", v),
|
||||
let get_sign_value = |f: &Number| match (emit_sign, f) {
|
||||
(_, v) if v < &Number::from(0.0) => ("-", v.clone()),
|
||||
(true, v) => ("+", v.clone()),
|
||||
(false, v) if leave_space_for_elided_sign => (" ", v.clone()),
|
||||
(false, v) => ("", v.clone()),
|
||||
};
|
||||
|
||||
// Handle Golang printing verbs.
|
||||
// https://pkg.go.dev/fmt
|
||||
match (verb, arg) {
|
||||
('v', _) => s += format!("{arg}").as_str(),
|
||||
('b', Value::Number(f)) if f.0 == f.0.floor() => {
|
||||
('b', Value::Number(f)) if f.is_integer() => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
s += sign;
|
||||
s += format!("{:b}", v as u64).as_str()
|
||||
s += v.format_bin().as_str()
|
||||
}
|
||||
('c', Value::Number(f)) if f.0 == f.0.floor() => {
|
||||
('c', Value::Number(f)) if f.is_integer() => {
|
||||
// TODO: range error
|
||||
let ival = f.0 .0 as u32;
|
||||
match char::from_u32(ival) {
|
||||
Some(c) => s.push(c),
|
||||
None => {
|
||||
let ch_opt = f.as_u64().map(|ival| char::from_u32(ival as u32));
|
||||
match ch_opt {
|
||||
Some(Some(c)) => s.push(c),
|
||||
_ => {
|
||||
bail!(args_span.error(
|
||||
format!("invalid integer value {ival} for format verb c.").as_str()
|
||||
format!("invalid value {} for format verb c.", f.format_decimal())
|
||||
.as_str()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
('d', Value::Number(f)) if f.0 == f.0.floor() => {
|
||||
('d', Value::Number(f)) if f.is_integer() => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
s += sign;
|
||||
s += format!("{v}").as_str()
|
||||
s += v.format_decimal().as_str()
|
||||
}
|
||||
('o', Value::Number(f)) if f.0 == f.0.floor() => {
|
||||
('o', Value::Number(f)) if f.is_integer() => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
s += sign;
|
||||
s += format!("{:o}", v as u64).as_str()
|
||||
s += ("0O".to_owned() + &v.format_octal()).as_str()
|
||||
}
|
||||
('O', Value::Number(f)) if f.0 == f.0.floor() => {
|
||||
('O', Value::Number(f)) if f.is_integer() => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
s += sign;
|
||||
s += format!("0o{:o}", v as u64).as_str()
|
||||
s += ("0o".to_owned() + &v.format_octal()).as_str()
|
||||
}
|
||||
('x', Value::Number(f)) if f.0 == f.0.floor() => {
|
||||
('x', Value::Number(f)) if f.is_integer() => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
s += sign;
|
||||
s += format!("{:x}", v as u64).as_str()
|
||||
s += v.format_hex().as_str()
|
||||
}
|
||||
('X', Value::Number(f)) if f.0 == f.0.floor() => {
|
||||
('X', Value::Number(f)) if f.is_integer() => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
s += sign;
|
||||
s += format!("{:X}", v as u64).as_str()
|
||||
s += v.format_big_hex().as_str()
|
||||
}
|
||||
('e', Value::Number(f)) => s += format!("{:e}", f.0).as_str(),
|
||||
('E', Value::Number(f)) => s += format!("{:E}", f.0).as_str(),
|
||||
('f' | 'F', Value::Number(f)) => s += format!("{}", f.0).as_str(),
|
||||
('e', Value::Number(f)) => {
|
||||
s += match f.as_f64() {
|
||||
Some(f) => format!("{:e}", f),
|
||||
_ => bail!(span.error("cannot print large float using e format specifier")),
|
||||
}
|
||||
.as_str()
|
||||
}
|
||||
('E', Value::Number(f)) => {
|
||||
s += match f.as_f64() {
|
||||
Some(f) => format!("{:E}", f),
|
||||
_ => bail!(span.error("cannot print large float using E format specifier")),
|
||||
}
|
||||
.as_str()
|
||||
}
|
||||
('f' | 'F', Value::Number(f)) => s += f.format_decimal().as_str(),
|
||||
('g', Value::Number(f)) => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
let v = match v.as_f64() {
|
||||
Some(v) => v,
|
||||
_ => bail!(span.error("cannot print large float using g format specified")),
|
||||
};
|
||||
s += sign;
|
||||
let bits = v.to_bits();
|
||||
let exponent = (bits >> 52) & 0x7ff;
|
||||
@@ -271,6 +296,10 @@ fn sprintf(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
}
|
||||
('G', Value::Number(f)) => {
|
||||
let (sign, v) = get_sign_value(f);
|
||||
let v = match v.as_f64() {
|
||||
Some(v) => v,
|
||||
_ => bail!("cannot print large float using g format specified"),
|
||||
};
|
||||
s += sign;
|
||||
let bits = v.to_bits();
|
||||
let exponent = (bits >> 52) & 0x7ff;
|
||||
@@ -423,25 +452,22 @@ fn substring(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value>
|
||||
let offset = ensure_numeric(name, ¶ms[1], &args[1])?;
|
||||
let length = ensure_numeric(name, ¶ms[2], &args[2])?;
|
||||
|
||||
if offset.floor() != offset || length.floor() != length {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
if offset < 0.0 || length < 0.0 {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
let offset = offset as usize;
|
||||
let length = length as usize;
|
||||
|
||||
// TODO: distinguish between 20.0 and 20
|
||||
// Also: behavior of
|
||||
// x = substring("hello", 20 + 0.0, 25)
|
||||
if offset > s.len() || length <= offset {
|
||||
return Ok(Value::String("".into()));
|
||||
}
|
||||
match (offset.as_u64(), length.as_u64()) {
|
||||
(Some(offset), Some(length)) => {
|
||||
let offset = offset as usize;
|
||||
let length = length as usize;
|
||||
|
||||
Ok(Value::String(s[offset..offset + length].into()))
|
||||
if offset > s.len() || length <= offset {
|
||||
return Ok(Value::String("".into()));
|
||||
}
|
||||
|
||||
Ok(Value::String(s[offset..offset + length].into()))
|
||||
}
|
||||
_ => Ok(Value::Undefined),
|
||||
}
|
||||
}
|
||||
|
||||
fn trim(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
|
||||
@@ -26,5 +26,5 @@ fn now_ns(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
Err(e) => bail!(span.error(format!("could not fetch elapsed time. {e}").as_str())),
|
||||
};
|
||||
let nanos = elapsed.as_nanos();
|
||||
Ok(Value::from_u128(nanos))
|
||||
Ok(Value::from(nanos))
|
||||
}
|
||||
|
||||
@@ -5,18 +5,67 @@ use crate::ast::{Expr, Ref};
|
||||
use crate::builtins;
|
||||
use crate::builtins::utils::{ensure_args_count, ensure_string};
|
||||
use crate::lexer::Span;
|
||||
use crate::value::{Float, Number, Value};
|
||||
use crate::number::Number;
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
|
||||
m.insert("units.parse", (parse, 1));
|
||||
m.insert("units.parse_bytes", (parse_bytes, 1));
|
||||
}
|
||||
|
||||
fn ten_exp(suffix: &str) -> Option<i32> {
|
||||
Some(match suffix {
|
||||
"E" | "e" => 18,
|
||||
"P" | "p" => 15,
|
||||
"T" | "t" => 12,
|
||||
"G" | "g" => 9,
|
||||
"M" => 6,
|
||||
"K" | "k" => 3,
|
||||
"m" => -3,
|
||||
|
||||
// The following are not supported by OPA
|
||||
"Q" => 30,
|
||||
"R" => 27,
|
||||
"Y" => 24,
|
||||
"Z" => 21,
|
||||
"h" => 2,
|
||||
"da" => 1,
|
||||
"d" => -1,
|
||||
"c" => -2,
|
||||
|
||||
"μ" => -6,
|
||||
"n" => -9,
|
||||
"f" => -15,
|
||||
"a" => -18,
|
||||
"z" => -21,
|
||||
"y" => -24,
|
||||
"r" => -27,
|
||||
"q" => -30,
|
||||
|
||||
// No suffix specified.
|
||||
"" => 0,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn two_exp(suffix: &str) -> Option<i32> {
|
||||
Some(match suffix.to_ascii_lowercase().as_str() {
|
||||
"ki" => 10,
|
||||
"mi" => 20,
|
||||
"gi" => 30,
|
||||
"ti" => 40,
|
||||
"pi" => 50,
|
||||
"ei" => 60,
|
||||
"zi" => 70,
|
||||
"yi" => 80,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
let name = "units.parse";
|
||||
ensure_args_count(span, name, params, args, 1)?;
|
||||
@@ -40,61 +89,58 @@ fn parse(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
_ => (string, ""),
|
||||
};
|
||||
|
||||
let n: Float = if number_part.starts_with('.') {
|
||||
let v: Value = if number_part.starts_with('.') {
|
||||
serde_json::from_str(format!("0{number_part}").as_str())
|
||||
} else {
|
||||
serde_json::from_str(number_part)
|
||||
}
|
||||
.with_context(|| span.error("could not parse number"))?;
|
||||
|
||||
Ok(Value::Number(Number(OrderedFloat(
|
||||
n * 10f64.powf(match suffix {
|
||||
"E" | "e" => 18,
|
||||
"P" | "p" => 15,
|
||||
"T" | "t" => 12,
|
||||
"G" | "g" => 9,
|
||||
"M" => 6,
|
||||
"K" | "k" => 3,
|
||||
"m" => -3,
|
||||
let mut n = match v {
|
||||
Value::Number(n) => n.clone(),
|
||||
_ => bail!(span.error("could not parse number")),
|
||||
};
|
||||
|
||||
// The following are not supported by OPA
|
||||
"Q" => 30,
|
||||
"R" => 27,
|
||||
"Y" => 24,
|
||||
"Z" => 21,
|
||||
"h" => 2,
|
||||
"da" => 1,
|
||||
"d" => -1,
|
||||
"c" => -2,
|
||||
if let Some(e) = ten_exp(suffix) {
|
||||
n.mul_assign(&Number::ten_pow(e))?;
|
||||
Ok(Value::from(n))
|
||||
} else if let Some(e) = two_exp(suffix) {
|
||||
n.mul_assign(&Number::two_pow(e))?;
|
||||
Ok(Value::from(n))
|
||||
} else {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
}
|
||||
|
||||
"μ" => -6,
|
||||
"n" => -9,
|
||||
"f" => -15,
|
||||
"a" => -18,
|
||||
"z" => -21,
|
||||
"y" => -24,
|
||||
"r" => -27,
|
||||
"q" => -30,
|
||||
fn twob_exp(suffix: &str) -> Option<i32> {
|
||||
Some(match suffix.to_ascii_lowercase().as_str() {
|
||||
"yi" | "yib" => 80,
|
||||
"zi" | "zib" => 70,
|
||||
"ei" | "eib" => 60,
|
||||
"pi" | "pib" => 50,
|
||||
"ti" | "tib" => 40,
|
||||
"gi" | "gib" => 30,
|
||||
"mi" | "mib" => 20,
|
||||
"ki" | "kib" => 10,
|
||||
"" => 0,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
// No suffix specified.
|
||||
"" => 0,
|
||||
_ => {
|
||||
return Ok(Value::Number(Number(OrderedFloat(
|
||||
n * 2f64.powf(match suffix.to_ascii_lowercase().as_str() {
|
||||
"ki" => 10,
|
||||
"mi" => 20,
|
||||
"gi" => 30,
|
||||
"ti" => 40,
|
||||
"pi" => 50,
|
||||
"ei" => 60,
|
||||
"zi" => 70,
|
||||
"yi" => 80,
|
||||
_ => return Ok(Value::Undefined),
|
||||
} as f64),
|
||||
))));
|
||||
}
|
||||
} as f64),
|
||||
))))
|
||||
fn tenb_exp(suffix: &str) -> Option<i32> {
|
||||
Some(match suffix.to_ascii_lowercase().as_str() {
|
||||
"q" | "qb" => 30,
|
||||
"r" | "rb" => 27,
|
||||
"y" | "yb" => 24,
|
||||
"z" | "zb" => 21,
|
||||
"e" | "eb" => 18,
|
||||
"p" | "pb" => 15,
|
||||
"t" | "tb" => 12,
|
||||
"g" | "gb" => 9,
|
||||
"m" | "mb" => 6,
|
||||
"k" | "kb" => 3,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
|
||||
@@ -120,43 +166,25 @@ fn parse_bytes(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Valu
|
||||
_ => (string, ""),
|
||||
};
|
||||
|
||||
let n: Float = if number_part.starts_with('.') {
|
||||
let v: Value = if number_part.starts_with('.') {
|
||||
serde_json::from_str(format!("0{number_part}").as_str())
|
||||
} else {
|
||||
serde_json::from_str(number_part)
|
||||
}
|
||||
.with_context(|| span.error("could not parse number"))?;
|
||||
|
||||
Ok(Value::Number(Number(OrderedFloat(f64::round(
|
||||
n * 2f64.powf(match suffix.to_ascii_lowercase().as_str() {
|
||||
"yi" | "yib" => 80,
|
||||
"zi" | "zib" => 70,
|
||||
"ei" | "eib" => 60,
|
||||
"pi" | "pib" => 50,
|
||||
"ti" | "tib" => 40,
|
||||
"gi" | "gib" => 30,
|
||||
"mi" | "mib" => 20,
|
||||
"ki" | "kib" => 10,
|
||||
"" => 0,
|
||||
_ => {
|
||||
return Ok(Value::Number(Number(OrderedFloat(
|
||||
n * 10f64.powf(match suffix.to_ascii_lowercase().as_str() {
|
||||
"q" | "qb" => 30,
|
||||
"r" | "rb" => 27,
|
||||
"y" | "yb" => 24,
|
||||
"z" | "zb" => 21,
|
||||
"e" | "eb" => 18,
|
||||
"p" | "pb" => 15,
|
||||
"t" | "tb" => 12,
|
||||
"g" | "gb" => 9,
|
||||
"m" | "mb" => 6,
|
||||
"k" | "kb" => 3,
|
||||
_ => {
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
} as f64),
|
||||
))))
|
||||
}
|
||||
} as f64),
|
||||
)))))
|
||||
let mut n = match v {
|
||||
Value::Number(n) => n.clone(),
|
||||
_ => bail!(span.error("could not parse number")),
|
||||
};
|
||||
|
||||
if let Some(e) = twob_exp(suffix) {
|
||||
n.mul_assign(&Number::two_pow(e))?;
|
||||
Ok(Value::from(n.round()))
|
||||
} else if let Some(e) = tenb_exp(suffix) {
|
||||
n.mul_assign(&Number::ten_pow(e))?;
|
||||
Ok(Value::from(n.round()))
|
||||
} else {
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
use crate::ast::{Expr, Ref};
|
||||
use crate::lexer::Span;
|
||||
use crate::value::{Float, Value};
|
||||
use crate::number::Number;
|
||||
use crate::value::Value;
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::rc::Rc;
|
||||
@@ -31,9 +32,9 @@ pub fn ensure_args_count(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_numeric(fcn: &str, arg: &Expr, v: &Value) -> Result<Float> {
|
||||
pub fn ensure_numeric(fcn: &str, arg: &Expr, v: &Value) -> Result<Number> {
|
||||
Ok(match &v {
|
||||
Value::Number(n) => n.0 .0,
|
||||
Value::Number(n) => n.clone(),
|
||||
_ => {
|
||||
let span = arg.span();
|
||||
bail!(
|
||||
|
||||
@@ -446,7 +446,13 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_arith_expr(&mut self, op: &ArithOp, lhs: &ExprRef, rhs: &ExprRef) -> Result<Value> {
|
||||
fn eval_arith_expr(
|
||||
&mut self,
|
||||
span: &Span,
|
||||
op: &ArithOp,
|
||||
lhs: &ExprRef,
|
||||
rhs: &ExprRef,
|
||||
) -> Result<Value> {
|
||||
let lhs_value = self.eval_expr(lhs)?;
|
||||
let rhs_value = self.eval_expr(rhs)?;
|
||||
|
||||
@@ -458,7 +464,7 @@ impl Interpreter {
|
||||
(ArithOp::Sub, Value::Set(_), _) | (ArithOp::Sub, _, Value::Set(_)) => {
|
||||
builtins::sets::difference(lhs, rhs, lhs_value, rhs_value)
|
||||
}
|
||||
_ => builtins::numbers::arithmetic_operation(op, lhs, rhs, lhs_value, rhs_value),
|
||||
_ => builtins::numbers::arithmetic_operation(span, op, lhs, rhs, lhs_value, rhs_value),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,7 +613,7 @@ impl Interpreter {
|
||||
for (idx, v) in a.iter().enumerate() {
|
||||
self.add_variable(&value.source_str(), v.clone())?;
|
||||
if let Some(key) = key {
|
||||
self.add_variable(&key.source_str(), Value::from_float(idx as Float))?;
|
||||
self.add_variable(&key.source_str(), Value::from(idx))?;
|
||||
}
|
||||
if !self.eval_query(query)? {
|
||||
r = false;
|
||||
@@ -815,7 +821,7 @@ impl Interpreter {
|
||||
&mut type_match,
|
||||
&mut cache,
|
||||
(key_expr, value_expr),
|
||||
(&Value::from_float(idx as Float), value),
|
||||
(&Value::from(idx), value),
|
||||
)? {
|
||||
continue;
|
||||
}
|
||||
@@ -877,14 +883,8 @@ impl Interpreter {
|
||||
|
||||
fn make_expression_result(span: &Span, v: &Value) -> Value {
|
||||
let mut loc = BTreeMap::new();
|
||||
loc.insert(
|
||||
Value::String("row".into()),
|
||||
Value::from_float(span.line as f64),
|
||||
);
|
||||
loc.insert(
|
||||
Value::String("col".into()),
|
||||
Value::from_float(span.col as f64),
|
||||
);
|
||||
loc.insert(Value::String("row".into()), Value::from(span.line as i64));
|
||||
loc.insert(Value::String("col".into()), Value::from(span.col as i64));
|
||||
|
||||
let mut expr = BTreeMap::new();
|
||||
expr.insert(Value::String("value".into()), v.clone());
|
||||
@@ -1142,7 +1142,7 @@ impl Interpreter {
|
||||
for (idx, v) in items.iter().enumerate() {
|
||||
self.loop_var_values
|
||||
.insert(loop_expr.expr.clone(), v.clone());
|
||||
self.add_variable(&loop_expr.index, Value::from_float(idx as Float))?;
|
||||
self.add_variable(&loop_expr.index, Value::from(idx))?;
|
||||
|
||||
result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result;
|
||||
self.loop_var_values.remove(&loop_expr.expr);
|
||||
@@ -1960,7 +1960,7 @@ impl Interpreter {
|
||||
Expr::RefBrack { .. } => self.eval_chained_ref_dot_or_brack(expr),
|
||||
|
||||
// Expressions with operators
|
||||
Expr::ArithExpr { op, lhs, rhs, .. } => self.eval_arith_expr(op, lhs, rhs),
|
||||
Expr::ArithExpr { op, lhs, rhs, .. } => self.eval_arith_expr(expr.span(), op, lhs, rhs),
|
||||
Expr::AssignExpr { op, lhs, rhs, .. } => self.eval_assign_expr(op, lhs, rhs),
|
||||
Expr::BinExpr { op, lhs, rhs, .. } => self.eval_bin_expr(op, lhs, rhs),
|
||||
Expr::BoolExpr { op, lhs, rhs, .. } => self.eval_bool_expr(op, lhs, rhs),
|
||||
|
||||
@@ -6,6 +6,7 @@ mod builtins;
|
||||
pub mod engine;
|
||||
pub mod interpreter;
|
||||
pub mod lexer;
|
||||
pub mod number;
|
||||
pub mod parser;
|
||||
pub mod scheduler;
|
||||
mod utils;
|
||||
@@ -15,6 +16,7 @@ pub use ast::*;
|
||||
pub use engine::*;
|
||||
pub use interpreter::*;
|
||||
pub use lexer::*;
|
||||
pub use number::*;
|
||||
pub use parser::*;
|
||||
pub use scheduler::*;
|
||||
pub use value::*;
|
||||
|
||||
366
src/number.rs
Normal file
366
src/number.rs
Normal file
@@ -0,0 +1,366 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use std::cmp::{Ord, Ordering};
|
||||
use std::ops::{AddAssign, Div, MulAssign, Rem, SubAssign};
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use num::{FromPrimitive, ToPrimitive};
|
||||
use rust_decimal;
|
||||
|
||||
use serde::ser::Serializer;
|
||||
use serde::Serialize;
|
||||
|
||||
pub type BigInt = i128;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct BigDecimal {
|
||||
d: rust_decimal::Decimal,
|
||||
}
|
||||
|
||||
impl AsRef<rust_decimal::Decimal> for BigDecimal {
|
||||
fn as_ref(&self) -> &rust_decimal::Decimal {
|
||||
&self.d
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<rust_decimal::Decimal> for BigDecimal {
|
||||
fn as_mut(&mut self) -> &mut rust_decimal::Decimal {
|
||||
&mut self.d
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rust_decimal::Decimal> for BigDecimal {
|
||||
fn from(value: rust_decimal::Decimal) -> Self {
|
||||
BigDecimal { d: value }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i128> for BigDecimal {
|
||||
fn from(value: i128) -> Self {
|
||||
BigDecimal { d: value.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for BigDecimal {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = self.d.to_string();
|
||||
let v = serde_json::Number::from_str(&s)
|
||||
.map_err(|_| serde::ser::Error::custom("could not serialize big number"))?;
|
||||
v.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Number {
|
||||
// TODO: maybe specialize for u64, i64, f64
|
||||
Big(Rc<BigDecimal>),
|
||||
}
|
||||
|
||||
use Number::*;
|
||||
|
||||
impl From<u64> for Number {
|
||||
fn from(n: u64) -> Self {
|
||||
Self::Big(BigDecimal { d: n.into() }.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Number {
|
||||
fn from(n: usize) -> Self {
|
||||
Self::Big(BigDecimal { d: n.into() }.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Number {
|
||||
fn from(n: u128) -> Self {
|
||||
Self::Big(BigDecimal { d: n.into() }.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i128> for Number {
|
||||
fn from(n: i128) -> Self {
|
||||
Self::Big(BigDecimal { d: n.into() }.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Number {
|
||||
fn from(n: i64) -> Self {
|
||||
Self::Big(BigDecimal { d: n.into() }.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Number {
|
||||
fn from(n: f64) -> Self {
|
||||
match rust_decimal::Decimal::from_f64(n) {
|
||||
Some(v) => v.into(),
|
||||
_ => rust_decimal::Decimal::ZERO.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rust_decimal::Decimal> for Number {
|
||||
fn from(d: rust_decimal::Decimal) -> Self {
|
||||
Self::Big(BigDecimal { d }.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Number {
|
||||
pub fn as_u64(&self) -> Option<u64> {
|
||||
match self {
|
||||
Big(b) if b.d.is_integer() => b.d.to_u64(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
match self {
|
||||
Big(b) if b.d.is_integer() => b.d.to_i64(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
match self {
|
||||
Big(b) => b.d.to_f64(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_big(&self) -> Option<Rc<BigDecimal>> {
|
||||
Some(match self {
|
||||
Big(b) => b.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_big(&self) -> Result<Rc<BigDecimal>> {
|
||||
match self.as_big() {
|
||||
Some(b) => Ok(b),
|
||||
_ => bail!("Number::to_big failed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ParseNumberError;
|
||||
|
||||
impl FromStr for Number {
|
||||
type Err = ParseNumberError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match rust_decimal::Decimal::from_str(s) {
|
||||
Ok(v) => v.into(),
|
||||
_ => f64::from_str(s).map_err(|_| ParseNumberError)?.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Number {}
|
||||
|
||||
impl PartialEq for Number {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Big(a), Big(b)) => a.d == b.d,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Number {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self, other) {
|
||||
(Big(a), Big(b)) => a.d.cmp(&b.d),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Number {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Number {
|
||||
pub fn add_assign(&mut self, rhs: &Self) -> Result<()> {
|
||||
match (self, rhs) {
|
||||
(Big(ref mut a), Big(b)) => {
|
||||
Rc::make_mut(a).d.add_assign(b.d);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add(&self, rhs: &Self) -> Result<Number> {
|
||||
let mut c = self.clone();
|
||||
c.add_assign(rhs)?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub fn sub_assign(&mut self, rhs: &Self) -> Result<()> {
|
||||
match (self, rhs) {
|
||||
(Big(ref mut a), Big(b)) => {
|
||||
Rc::make_mut(a).d.sub_assign(b.d);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sub(&self, rhs: &Self) -> Result<Number> {
|
||||
let mut c = self.clone();
|
||||
c.sub_assign(rhs)?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub fn mul_assign(&mut self, rhs: &Self) -> Result<()> {
|
||||
match (self, rhs) {
|
||||
(Big(ref mut a), Big(b)) => {
|
||||
Rc::make_mut(a).d.mul_assign(b.d);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mul(&self, rhs: &Self) -> Result<Number> {
|
||||
let mut c = self.clone();
|
||||
c.mul_assign(rhs)?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub fn divide(self, rhs: &Self) -> Result<Number> {
|
||||
Ok(match (self, rhs) {
|
||||
(Big(a), Big(b)) => a.d.div(b.d).into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn modulo(self, rhs: &Self) -> Result<Number> {
|
||||
Ok(match (self, rhs) {
|
||||
(Big(a), Big(b)) => a.d.rem(b.d).into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_integer(&self) -> bool {
|
||||
match self {
|
||||
Big(b) => b.d.is_integer(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_integers(a: &Number, b: &Number) -> Option<(BigInt, BigInt)> {
|
||||
match (a, b) {
|
||||
(Big(a), Big(b)) if a.d.is_integer() && b.d.is_integer() => {
|
||||
Some((a.d.mantissa(), b.d.mantissa()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_integer(&self) -> Option<BigInt> {
|
||||
match self {
|
||||
Big(a) if a.d.is_integer() => Some(a.d.mantissa()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn and(&self, rhs: &Self) -> Option<Number> {
|
||||
match Self::ensure_integers(self, rhs) {
|
||||
Some((a, b)) => Some((a & b).into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or(&self, rhs: &Self) -> Option<Number> {
|
||||
match Self::ensure_integers(self, rhs) {
|
||||
Some((a, b)) => Some((a | b).into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xor(&self, rhs: &Self) -> Option<Number> {
|
||||
match Self::ensure_integers(self, rhs) {
|
||||
Some((a, b)) => Some((a ^ b).into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lsh(&self, rhs: &Self) -> Option<Number> {
|
||||
match Self::ensure_integers(self, rhs) {
|
||||
Some((a, b)) => a.checked_shl(b as u32).map(|v| v.into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rsh(&self, rhs: &Self) -> Option<Number> {
|
||||
match Self::ensure_integers(self, rhs) {
|
||||
Some((a, b)) => a.checked_shr(b as u32).map(|v| v.into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn neg(&self) -> Option<Number> {
|
||||
self.ensure_integer().map(|a| (!a).into())
|
||||
}
|
||||
|
||||
pub fn abs(&self) -> Number {
|
||||
match self {
|
||||
Big(b) => b.d.abs().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floor(&self) -> Number {
|
||||
match self {
|
||||
Big(b) => b.d.floor().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ceil(&self) -> Number {
|
||||
match self {
|
||||
Big(b) => b.d.ceil().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn round(&self) -> Number {
|
||||
match self {
|
||||
Big(b) => b.d.round().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn two_pow(e: i32) -> Number {
|
||||
2.0_f64.powi(e).into()
|
||||
}
|
||||
|
||||
pub fn ten_pow(e: i32) -> Number {
|
||||
10.0_f64.powi(e).into()
|
||||
}
|
||||
|
||||
pub fn format_bin(&self) -> String {
|
||||
self.ensure_integer()
|
||||
.map(|a| format!("{:b}", a))
|
||||
.unwrap_or("".to_string())
|
||||
}
|
||||
|
||||
pub fn format_octal(&self) -> String {
|
||||
self.ensure_integer()
|
||||
.map(|a| format!("{:o}", a))
|
||||
.unwrap_or("".to_string())
|
||||
}
|
||||
|
||||
pub fn format_decimal(&self) -> String {
|
||||
self.ensure_integer()
|
||||
.map(|a| format!("{}", a))
|
||||
.unwrap_or("".to_string())
|
||||
}
|
||||
|
||||
pub fn format_hex(&self) -> String {
|
||||
self.ensure_integer()
|
||||
.map(|a| format!("{:x}", a))
|
||||
.unwrap_or("".to_string())
|
||||
}
|
||||
|
||||
pub fn format_big_hex(&self) -> String {
|
||||
self.ensure_integer()
|
||||
.map(|a| format!("{:X}", a))
|
||||
.unwrap_or("".to_string())
|
||||
}
|
||||
}
|
||||
283
src/value.rs
283
src/value.rs
@@ -1,90 +1,23 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::number::Number;
|
||||
|
||||
use core::fmt;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::ops;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use ordered_float::OrderedFloat;
|
||||
use serde::de::{self, Deserializer};
|
||||
use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor};
|
||||
use serde::ser::{SerializeMap, Serializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub type Float = f64;
|
||||
|
||||
// TODO: rego uses BigNum which has arbitrary precision. But there seems
|
||||
// to be some bugs with it e.g ((a + b) -a) == b doesn't return true for large
|
||||
// values of a and b.
|
||||
// Json doesn't specify a limit on precision, but in practice double (f64) seems
|
||||
// to be enough to support most use cases and portability too.
|
||||
// See discussions in jq's repository.
|
||||
// For now we use OrderedFloat<f64>. We can't use f64 directly since it doesn't
|
||||
// implement Ord trait.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Number(pub OrderedFloat<Float>);
|
||||
|
||||
impl Serialize for Number {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let n_float = self.0 .0;
|
||||
let n_i64 = n_float as i64;
|
||||
let n_u64 = n_float as u64;
|
||||
|
||||
if n_u64 as f64 == n_float {
|
||||
serializer.serialize_u64(n_u64)
|
||||
} else if n_i64 as f64 == n_float {
|
||||
serializer.serialize_i64(n_i64)
|
||||
} else {
|
||||
serializer.serialize_f64(n_float)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NumberVisitor;
|
||||
impl<'de> de::Visitor<'de> for NumberVisitor {
|
||||
type Value = Number;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "a json number")
|
||||
}
|
||||
|
||||
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E> {
|
||||
Ok(Number(OrderedFloat(v)))
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
|
||||
Ok(Number(OrderedFloat(v as f64)))
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
|
||||
Ok(Number(OrderedFloat(v as f64)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Number {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Number, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_f64(NumberVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot use serde_json::Value because Rego has set type and object's key can be
|
||||
// other rego values.
|
||||
// BTree is more efficient that a hast table. Another alternative is a sorted vector.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
// BTree is more efficient than a hash table. Another alternative is a sorted vector.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Value {
|
||||
// Json data types. serde will automatically map json to these variants.
|
||||
Null,
|
||||
@@ -136,6 +69,123 @@ impl Serialize for Value {
|
||||
}
|
||||
}
|
||||
|
||||
struct ValueVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ValueVisitor {
|
||||
type Value = Value;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a value")
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::Bool(v))
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::from(v))
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::from(v))
|
||||
}
|
||||
|
||||
fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::from(v))
|
||||
}
|
||||
|
||||
fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::from(v))
|
||||
}
|
||||
|
||||
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::from(Number::from(v)))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::String(s.to_string().into()))
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, s: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Value::String(s.into()))
|
||||
}
|
||||
|
||||
fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
let mut arr = vec![];
|
||||
while let Some(v) = visitor.next_element()? {
|
||||
arr.push(v);
|
||||
}
|
||||
Ok(Value::from(arr))
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: MapAccess<'de>,
|
||||
{
|
||||
if let Some((key, value)) = visitor.next_entry()? {
|
||||
if let (Value::String(k), Value::String(v)) = (&key, &value) {
|
||||
if k.as_ref() == "$serde_json::private::Number" {
|
||||
match Number::from_str(v) {
|
||||
Ok(n) => return Ok(Value::from(n)),
|
||||
_ => return Err(de::Error::custom("failed to read big number")),
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(key, value);
|
||||
while let Some((key, value)) = visitor.next_entry()? {
|
||||
map.insert(key, value);
|
||||
}
|
||||
Ok(Value::from(map))
|
||||
} else {
|
||||
Ok(Value::new_object())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Value {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(ValueVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match serde_json::to_string(self) {
|
||||
@@ -147,15 +197,15 @@ impl fmt::Display for Value {
|
||||
|
||||
impl Value {
|
||||
pub fn new_object() -> Value {
|
||||
Value::from_map(BTreeMap::new())
|
||||
Value::from(BTreeMap::new())
|
||||
}
|
||||
|
||||
pub fn new_set() -> Value {
|
||||
Value::from_set(BTreeSet::new())
|
||||
Value::from(BTreeSet::new())
|
||||
}
|
||||
|
||||
pub fn new_array() -> Value {
|
||||
Value::from_array(vec![])
|
||||
Value::from(vec![])
|
||||
}
|
||||
|
||||
pub fn from_json_str(json: &str) -> Result<Value> {
|
||||
@@ -185,26 +235,77 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn from_float(v: Float) -> Value {
|
||||
Value::Number(Number(OrderedFloat(v)))
|
||||
impl From<u128> for Value {
|
||||
fn from(n: u128) -> Self {
|
||||
Value::Number(Number::from(n))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_u128(v: u128) -> Value {
|
||||
// TODO: fix precision loss
|
||||
Value::Number(Number(OrderedFloat(v as f64)))
|
||||
impl From<i128> for Value {
|
||||
fn from(n: i128) -> Self {
|
||||
Value::Number(Number::from(n))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_array(a: Vec<Value>) -> Value {
|
||||
impl From<u64> for Value {
|
||||
fn from(n: u64) -> Self {
|
||||
Value::Number(Number::from(n))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(n: i64) -> Self {
|
||||
Value::Number(Number::from(n))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(n: f64) -> Self {
|
||||
Value::Number(Number::from(n))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Value {
|
||||
fn from(n: usize) -> Self {
|
||||
Value::Number(Number::from(n))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Number> for Value {
|
||||
fn from(n: Number) -> Self {
|
||||
Value::Number(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Value>> for Value {
|
||||
fn from(a: Vec<Value>) -> Self {
|
||||
Value::Array(Rc::new(a))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BTreeSet<Value>> for Value {
|
||||
fn from(s: BTreeSet<Value>) -> Self {
|
||||
Value::Set(Rc::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BTreeMap<Value, Value>> for Value {
|
||||
fn from(s: BTreeMap<Value, Value>) -> Self {
|
||||
Value::Object(Rc::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn from_array(a: Vec<Value>) -> Value {
|
||||
Value::from(a)
|
||||
}
|
||||
|
||||
pub fn from_set(s: BTreeSet<Value>) -> Value {
|
||||
Value::Set(Rc::new(s))
|
||||
Value::from(s)
|
||||
}
|
||||
|
||||
pub fn from_map(m: BTreeMap<Value, Value>) -> Value {
|
||||
Value::Object(Rc::new(m))
|
||||
Value::from(m)
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
@@ -404,14 +505,10 @@ impl ops::Index<&Value> for Value {
|
||||
Some(v) => v,
|
||||
_ => &Value::Undefined,
|
||||
},
|
||||
(Value::Array(a), Value::Number(n)) => {
|
||||
let index = n.0 .0 as usize;
|
||||
if index < a.len() {
|
||||
&a[index]
|
||||
} else {
|
||||
&Value::Undefined
|
||||
}
|
||||
}
|
||||
(Value::Array(a), Value::Number(n)) => match n.as_u64() {
|
||||
Some(index) if (index as usize) < a.len() => &a[index as usize],
|
||||
_ => &Value::Undefined,
|
||||
},
|
||||
_ => &Value::Undefined,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,18 +9,25 @@ cases:
|
||||
package test
|
||||
import future.keywords.if
|
||||
|
||||
x = 1/3
|
||||
|
||||
# Undefined
|
||||
y if false
|
||||
a = 1 / 0
|
||||
b = y / 1
|
||||
c = 1 / y
|
||||
d = 13.3 % 3
|
||||
e = 13 % 3.1
|
||||
d = 15.3 / 3
|
||||
e = 13 / 4
|
||||
|
||||
|
||||
|
||||
z {
|
||||
x = 1/3
|
||||
y = 0.3333333333333333333333333333
|
||||
x == y
|
||||
}
|
||||
query: data.test
|
||||
want_result:
|
||||
x: 0.3333333333333333
|
||||
d: 5.1
|
||||
e: 3.25
|
||||
z: true
|
||||
|
||||
- note: non-numeric
|
||||
data: {}
|
||||
@@ -30,3 +37,13 @@ cases:
|
||||
x = "1" / 9
|
||||
query: data.test.x
|
||||
error: "`div` expects numeric argument."
|
||||
|
||||
- note: div by zero
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
|
||||
a = 1/ 0
|
||||
query: data.test
|
||||
error: divide by zero
|
||||
|
||||
@@ -11,7 +11,6 @@ cases:
|
||||
|
||||
# Undefined
|
||||
y { false }
|
||||
a = 1 % 0
|
||||
b = y % 1
|
||||
c = 1 % y
|
||||
query: data.test
|
||||
@@ -44,4 +43,22 @@ cases:
|
||||
package test
|
||||
x = 1%0
|
||||
query: data.test
|
||||
want_result: {}
|
||||
error: modulo by zero
|
||||
|
||||
- note: float numerator
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
x = 1.1 % 1
|
||||
query: data.test
|
||||
error: modulo on floating-point number
|
||||
|
||||
- note: float denom
|
||||
data: {}
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
x = 1 % 1.1
|
||||
query: data.test
|
||||
error: modulo on floating-point number
|
||||
|
||||
@@ -58,8 +58,8 @@ cases:
|
||||
query: data.test.results
|
||||
want_result:
|
||||
p1:
|
||||
- 1.2089258196146292e24
|
||||
- 1.1805916207174113e21
|
||||
- 1208925819614629174706176
|
||||
- 1180591620717411303424
|
||||
- 1152921504606846976
|
||||
- 1125899906842624
|
||||
- 1099511627776
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
aggregates
|
||||
all
|
||||
any
|
||||
arithmetic
|
||||
array
|
||||
assignments
|
||||
bitsand
|
||||
bitsnegate
|
||||
bitsor
|
||||
bitsshiftleft
|
||||
bitsshiftright
|
||||
bitsxor
|
||||
comparisonexpr
|
||||
@@ -16,6 +18,7 @@ embeddedvirtualdoc
|
||||
evaltermexpr
|
||||
example
|
||||
fix1863
|
||||
helloworld
|
||||
indexing
|
||||
intersection
|
||||
invalidkeyerror
|
||||
|
||||
@@ -13,12 +13,12 @@ fn non_string_key() -> Result<()> {
|
||||
obj.as_object_mut()?.insert(Value::Null, Value::Null);
|
||||
obj.as_object_mut()?.insert(Value::Bool(false), Value::Null);
|
||||
obj.as_object_mut()?
|
||||
.insert(Value::from_float(std::f64::consts::PI), Value::Null);
|
||||
.insert(Value::from(std::f64::consts::PI), Value::Null);
|
||||
obj.as_object_mut()?.insert(
|
||||
Value::from_array(vec![
|
||||
Value::Bool(true),
|
||||
Value::Null,
|
||||
Value::from_float(std::f64::consts::PI),
|
||||
Value::from(std::f64::consts::PI),
|
||||
]),
|
||||
Value::Null,
|
||||
);
|
||||
@@ -27,8 +27,7 @@ fn non_string_key() -> Result<()> {
|
||||
set.as_set_mut()?.insert(Value::Bool(true));
|
||||
set.as_set_mut()?.insert(Value::Bool(false));
|
||||
set.as_set_mut()?.insert(Value::Bool(true));
|
||||
set.as_set_mut()?
|
||||
.insert(Value::from_float(std::f64::consts::PI));
|
||||
set.as_set_mut()?.insert(Value::from(std::f64::consts::PI));
|
||||
obj.as_object_mut()?.insert(set, Value::Null);
|
||||
|
||||
obj.as_object_mut()?.insert(Value::Undefined, Value::Null);
|
||||
@@ -57,30 +56,20 @@ fn non_string_key() -> Result<()> {
|
||||
#[test]
|
||||
fn serialize_number() -> Result<()> {
|
||||
// Check that integer values are serialized without fractional part
|
||||
assert_eq!(serde_json::to_string_pretty(&Value::from_float(1.0))?, "1");
|
||||
assert_eq!(
|
||||
serde_json::to_string_pretty(&Value::from_float(-1.0))?,
|
||||
"-1"
|
||||
);
|
||||
assert_eq!(serde_json::to_string_pretty(&Value::from(1.0))?, "1");
|
||||
assert_eq!(serde_json::to_string_pretty(&Value::from(-1.0))?, "-1");
|
||||
|
||||
// Ensure that fractional parts are also serialized.
|
||||
assert_eq!(
|
||||
serde_json::to_string_pretty(&Value::from_float(1.1))?,
|
||||
"1.1"
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string_pretty(&Value::from_float(-1.1))?,
|
||||
"-1.1"
|
||||
);
|
||||
assert_eq!(serde_json::to_string_pretty(&Value::from(1.1))?, "1.1");
|
||||
assert_eq!(serde_json::to_string_pretty(&Value::from(-1.1))?, "-1.1");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_number() {
|
||||
use ordered_float::OrderedFloat;
|
||||
let n = Number(OrderedFloat(123456f64));
|
||||
assert_eq!(format!("{}", &n), "123456");
|
||||
let n = Number::from(123456f64);
|
||||
assert_eq!(format!("{}", n.format_decimal()), "123456");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -101,18 +90,18 @@ fn constructors() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn value_as_index() -> Result<()> {
|
||||
let idx = Value::from_float(2.0);
|
||||
let idx = Value::from(2.0);
|
||||
|
||||
let mut item = Value::new_array();
|
||||
item.as_array_mut()?.push(Value::from_float(3.0));
|
||||
item.as_array_mut()?.push(Value::from_float(4.0));
|
||||
item.as_array_mut()?.push(Value::from_float(5.0));
|
||||
item.as_array_mut()?.push(Value::from(3.0));
|
||||
item.as_array_mut()?.push(Value::from(4.0));
|
||||
item.as_array_mut()?.push(Value::from(5.0));
|
||||
|
||||
// Check case of item present.
|
||||
assert_eq!(&Value::from_json_str("[1, 2, [3, 4, 5]]")?[&idx], &item);
|
||||
|
||||
// Check case of item not present.
|
||||
let idx = Value::from_float(5.0);
|
||||
let idx = Value::from(5.0);
|
||||
assert_eq!(
|
||||
&Value::from_json_str("[1, 2, [3, 4, 5]]")?[&idx],
|
||||
&Value::Undefined
|
||||
@@ -131,17 +120,14 @@ fn value_as_index() -> Result<()> {
|
||||
#[test]
|
||||
fn string_as_index() -> Result<()> {
|
||||
let obj = Value::from_json_str(r#"{ "a" : 5, "b" : 6 }"#)?;
|
||||
assert_eq!(&obj["a"], &Value::from_float(5.0));
|
||||
assert_eq!(&obj[&"b".to_owned()], &Value::from_float(6.0));
|
||||
assert_eq!(&obj["a"], &Value::from(5.0));
|
||||
assert_eq!(&obj[&"b".to_owned()], &Value::from(6.0));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usize_as_index() -> Result<()> {
|
||||
assert_eq!(
|
||||
&Value::from_json_str("[1, 2, 3]")?[0],
|
||||
&Value::from_float(1.0)
|
||||
);
|
||||
assert_eq!(&Value::from_json_str("[1, 2, 3]")?[0], &Value::from(1.0));
|
||||
assert_eq!(&Value::from_json_str("[1, 2, 3]")?[5], &Value::Undefined);
|
||||
Ok(())
|
||||
}
|
||||
@@ -151,8 +137,8 @@ fn api() -> Result<()> {
|
||||
assert!(&Value::from_json_str("{}")?.as_object()?.is_empty());
|
||||
let mut v = Value::new_object();
|
||||
v.as_object_mut()?
|
||||
.insert(Value::String("a".into()), Value::from_float(3.145));
|
||||
assert_eq!(v["a"], Value::from_float(3.145));
|
||||
.insert(Value::String("a".into()), Value::from(3.145));
|
||||
assert_eq!(v["a"], Value::from(3.145));
|
||||
assert_eq!(v.as_object()?.len(), 1);
|
||||
|
||||
// Null
|
||||
@@ -174,7 +160,7 @@ fn api() -> Result<()> {
|
||||
assert!(Value::new_object().as_number().is_err());
|
||||
assert!(Value::new_object().as_number_mut().is_err());
|
||||
|
||||
assert!(Value::from_float(5.6).as_bool().is_err());
|
||||
assert!(Value::from_float(5.6).as_bool_mut().is_err());
|
||||
assert!(Value::from(5.6).as_bool().is_err());
|
||||
assert!(Value::from(5.6).as_bool_mut().is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user