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
+24
-23
@@ -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()))
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
+13
-7
@@ -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()))
|
||||
}
|
||||
|
||||
+25
-58
@@ -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;
|
||||
|
||||
+43
-37
@@ -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> {
|
||||
|
||||
+79
-53
@@ -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))
|
||||
}
|
||||
|
||||
+109
-81
@@ -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!(
|
||||
|
||||
+14
-14
@@ -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
@@ -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())
|
||||
}
|
||||
}
|
||||
+190
-93
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user