difftreelog
feat return NumValue directly from parser
in: master
15 files changed
bindings/jsonnet/src/val_make.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/val_make.rs
+++ b/bindings/jsonnet/src/val_make.rs
@@ -5,10 +5,7 @@
os::raw::{c_char, c_double, c_int},
};
-use jrsonnet_evaluator::{
- ObjValue, Val,
- val::{ArrValue, NumValue},
-};
+use jrsonnet_evaluator::{NumValue, ObjValue, Val};
use crate::VM;
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -2,7 +2,9 @@
use jrsonnet_gcmodule::{Acyclic, Trace};
use jrsonnet_interner::IStr;
-use jrsonnet_ir::{BinaryOpType, Source, SourcePath, Span, Spanned, UnaryOpType};
+use jrsonnet_ir::{
+ BinaryOpType, ConvertNumValueError, Source, SourcePath, Span, Spanned, UnaryOpType,
+};
use jrsonnet_types::ValType;
use thiserror::Error;
@@ -11,7 +13,6 @@
function::{CallLocation, FunctionSignature, ParamName},
stdlib::format::FormatError,
typed::TypeLocError,
- val::ConvertNumValueError,
};
#[derive(Debug, Clone)]
@@ -228,6 +229,11 @@
Self::new(e)
}
}
+impl From<ConvertNumValueError> for Error {
+ fn from(e: ConvertNumValueError) -> Self {
+ Self::new(ErrorKind::ConvertNumValue(e))
+ }
+}
impl From<Infallible> for Error {
fn from(_value: Infallible) -> Self {
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -21,7 +21,7 @@
function::{CallLocation, FuncDesc, FuncVal, PreparedFuncVal},
in_frame,
typed::{FromUntyped, IntoUntyped as _, Typed},
- val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},
+ val::{CachedUnbound, IndexableVal, StrValue, Thunk},
with_state,
};
pub mod destructure;
@@ -58,9 +58,7 @@
}
Some(match expr {
Expr::Str(s) => Val::string(s.clone()),
- Expr::Num(n) => {
- Val::Num(NumValue::new(*n).expect("parser will not allow non-finite values"))
- }
+ Expr::Num(n) => Val::Num(*n),
Expr::Literal(LiteralType::False) => Val::Bool(false),
Expr::Literal(LiteralType::True) => Val::Bool(true),
Expr::Literal(LiteralType::Null) => Val::Null,
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -1,6 +1,7 @@
use std::borrow::Cow;
use jrsonnet_interner::{IBytes, IStr};
+use jrsonnet_ir::NumValue;
use serde::{
Deserialize, Serialize, Serializer,
de::{self, Visitor},
@@ -12,7 +13,6 @@
use crate::{
Error as JrError, ObjValue, ObjValueBuilder, Result, Val, in_description_frame, runtime_error,
- val::NumValue,
};
impl<'de> Deserialize<'de> for Val {
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -42,6 +42,7 @@
use jrsonnet_gcmodule::{Cc, Trace, cc_dyn};
pub use jrsonnet_interner::{IBytes, IStr};
pub use jrsonnet_ir as parser;
+pub use jrsonnet_ir::NumValue;
use jrsonnet_ir::{Expr, Source, SourcePath};
#[doc(hidden)]
pub use jrsonnet_macros;
crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/stdlib/format.rs
+++ b/crates/jrsonnet-evaluator/src/stdlib/format.rs
@@ -829,7 +829,7 @@
#[cfg(test)]
pub mod test_format {
use super::*;
- use crate::val::NumValue;
+ use crate::NumValue;
#[test]
fn parse() {
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -2,6 +2,8 @@
use jrsonnet_gcmodule::Trace;
use jrsonnet_interner::{IBytes, IStr};
+use jrsonnet_ir::NumValue;
+pub use jrsonnet_ir::{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER};
use jrsonnet_types::{ComplexValType, ValType};
use crate::{
@@ -10,7 +12,7 @@
bail,
function::FuncVal,
typed::CheckType,
- val::{IndexableVal, NumValue, StrValue, ThunkMapper},
+ val::{IndexableVal, StrValue, ThunkMapper},
};
#[doc(hidden)]
@@ -219,11 +221,6 @@
Ok(inner.map(<ThunkFromUntyped<T>>::default()))
}
}
-
-#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]
-pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;
-#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]
-pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;
macro_rules! impl_int {
($($ty:ty)*) => {$(
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -5,7 +5,6 @@
marker::PhantomData,
mem::replace,
num::NonZeroU32,
- ops::Deref,
rc::Rc,
};
@@ -14,16 +13,15 @@
pub use jrsonnet_macros::Thunk;
use jrsonnet_types::ValType;
use rustc_hash::FxHashMap;
-use thiserror::Error;
pub use crate::arr::{ArrValue, ArrayLike};
use crate::{
- ObjValue, Result, SupThis, Unbound, WeakSupThis, bail,
+ NumValue, ObjValue, Result, SupThis, Unbound, WeakSupThis, bail,
error::{Error, ErrorKind::*},
function::FuncVal,
gc::WithCapacityExt as _,
manifest::{ManifestFormat, ToStringFormat},
- typed::{BoundedUsize, MAX_SAFE_INTEGER, MIN_SAFE_INTEGER},
+ typed::BoundedUsize,
};
pub trait ThunkValue: Trace {
@@ -439,134 +437,6 @@
let a = self.clone().into_flat();
let b = other.clone().into_flat();
a.cmp(&b)
- }
-}
-
-/// Represents jsonnet number
-/// Jsonnet numbers are finite f64, with NaNs disallowed
-#[derive(Trace, Clone, Copy)]
-#[repr(transparent)]
-pub struct NumValue(f64);
-impl NumValue {
- /// Creates a [`NumValue`], if value is finite and not NaN
- pub fn new(v: f64) -> Option<Self> {
- if !v.is_finite() {
- return None;
- }
- Some(Self(v))
- }
- #[inline]
- pub const fn get(&self) -> f64 {
- self.0
- }
- pub(crate) fn truncate_for_bitwise(self) -> Result<i64> {
- if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {
- bail!("numberic value outside of safe integer range for bitwise operation");
- }
- #[expect(clippy::cast_possible_truncation, reason = "intended")]
- Ok(self.0 as i64)
- }
-}
-impl PartialEq for NumValue {
- fn eq(&self, other: &Self) -> bool {
- self.0 == other.0
- }
-}
-impl Eq for NumValue {}
-impl Ord for NumValue {
- #[inline]
- fn cmp(&self, other: &Self) -> Ordering {
- // Can't use `total_cmp`: its behavior for `-0` and `0`
- // is not following wanted.
- unsafe { self.0.partial_cmp(&other.0).unwrap_unchecked() }
- }
-}
-impl PartialOrd for NumValue {
- #[inline]
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-impl Debug for NumValue {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- Debug::fmt(&self.0, f)
- }
-}
-impl Display for NumValue {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- Display::fmt(&self.0, f)
- }
-}
-impl Deref for NumValue {
- type Target = f64;
-
- #[inline]
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-macro_rules! impl_num {
- ($($ty:ty),+) => {$(
- impl From<$ty> for NumValue {
- #[inline]
- fn from(value: $ty) -> Self {
- Self(value.into())
- }
- }
- )+};
-}
-impl_num!(i8, u8, i16, u16, i32, u32);
-
-#[derive(Clone, Copy, Debug, Error, Trace)]
-pub enum ConvertNumValueError {
- #[error("overflow")]
- Overflow,
- #[error("underflow")]
- Underflow,
- #[error("non-finite")]
- NonFinite,
-}
-impl From<ConvertNumValueError> for Error {
- fn from(e: ConvertNumValueError) -> Self {
- Self::new(e.into())
- }
-}
-
-macro_rules! impl_try_num {
- ($($ty:ty),+) => {$(
- impl TryFrom<$ty> for NumValue {
- type Error = ConvertNumValueError;
- #[inline]
- fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {
- #[expect(clippy::cast_precision_loss, reason = "precision loss is explicitly handled")]
- let value = value as f64;
- if value < MIN_SAFE_INTEGER {
- return Err(ConvertNumValueError::Underflow)
- } else if value > MAX_SAFE_INTEGER {
- return Err(ConvertNumValueError::Overflow)
- }
- // Number is finite.
- Ok(Self(value))
- }
- }
- )+};
-}
-impl_try_num!(usize, isize, i64, u64);
-
-impl TryFrom<f64> for NumValue {
- type Error = ConvertNumValueError;
-
- #[inline]
- fn try_from(value: f64) -> Result<Self, Self::Error> {
- Self::new(value).ok_or(ConvertNumValueError::NonFinite)
- }
-}
-impl TryFrom<f32> for NumValue {
- type Error = ConvertNumValueError;
-
- #[inline]
- fn try_from(value: f32) -> Result<Self, Self::Error> {
- Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)
}
}
crates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir-parser/src/lib.rs
+++ b/crates/jrsonnet-ir-parser/src/lib.rs
@@ -4,8 +4,8 @@
use jrsonnet_ir::{
ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BinaryOpType, BindSpec, CompSpec, Destruct, Expr,
ExprParam, ExprParams, FieldMember, FieldName, ForSpecData, IStr, IfElse, IfSpecData,
- ImportKind, IndexPart, LiteralType, Member, ObjBody, ObjComp, ObjMembers, Slice, SliceDesc,
- Source, Span, Spanned, UnaryOpType, Visibility, unescape,
+ ImportKind, IndexPart, LiteralType, Member, NumValue, ObjBody, ObjComp, ObjMembers, Slice,
+ SliceDesc, Source, Span, Spanned, UnaryOpType, Visibility, unescape,
};
use jrsonnet_lexer::{Lexeme, Lexer, Span as LexSpan, SyntaxKind, T, collect_lexed_str_block};
@@ -202,17 +202,21 @@
)
}
-fn parse_number(p: &mut Parser<'_>) -> Result<f64> {
+fn parse_number(p: &mut Parser<'_>) -> Result<NumValue> {
let text = p.text();
let n: f64 = text
.replace('_', "")
.parse()
.map_err(|_| p.error(format!("invalid number literal: {text}")))?;
- if !n.is_finite() {
- return Err(p.error("numbers are finite".into()));
- }
+
+ let v = match NumValue::try_from(n) {
+ Ok(v) => v,
+ Err(e) => return Err(p.error(format!("invalid number value: {e}"))),
+ };
+
p.eat_any();
- Ok(n)
+
+ Ok(v)
}
fn ident(p: &mut Parser<'_>) -> Result<IStr> {
crates/jrsonnet-ir/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-ir/Cargo.toml
+++ b/crates/jrsonnet-ir/Cargo.toml
@@ -19,6 +19,7 @@
static_assertions.workspace = true
peg.workspace = true
+thiserror.workspace = true
[dev-dependencies]
insta.workspace = true
crates/jrsonnet-ir/src/expr.rsdiffbeforeafterboth1use std::{2 fmt::{self, Debug, Display},3 ops::{Deref, RangeInclusive},4 rc::Rc,5};67use jrsonnet_gcmodule::Acyclic;8use jrsonnet_interner::IStr;910use crate::{11 function::{FunctionSignature, ParamDefault, ParamName, ParamParse},12 source::Source,13};1415#[derive(Debug, PartialEq, Acyclic)]16pub enum FieldName {17 /// {fixed: 2}18 Fixed(IStr),19 /// {["dyn"+"amic"]: 3}20 Dyn(Expr),21}2223#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]24#[repr(u8)]25pub enum Visibility {26 /// :27 Normal,28 /// ::29 Hidden,30 /// :::31 Unhide,32}3334impl Visibility {35 pub fn is_visible(&self) -> bool {36 matches!(self, Self::Normal | Self::Unhide)37 }38}3940#[derive(Debug, PartialEq, Acyclic)]41pub struct AssertStmt {42 pub assertion: Spanned<Expr>,43 pub message: Option<Expr>,44}4546#[derive(Debug, PartialEq, Acyclic)]47pub struct FieldMember {48 pub name: Spanned<FieldName>,49 pub plus: bool,50 pub params: Option<ExprParams>,51 pub visibility: Visibility,52 pub value: Rc<Expr>,53}5455#[derive(Debug, PartialEq, Acyclic)]56pub enum Member {57 Field(FieldMember),58 BindStmt(BindSpec),59 AssertStmt(AssertStmt),60}6162#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]63pub enum UnaryOpType {64 Plus,65 Minus,66 BitNot,67 Not,68}6970impl Display for UnaryOpType {71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {72 use UnaryOpType::*;73 write!(74 f,75 "{}",76 match self {77 Plus => "+",78 Minus => "-",79 BitNot => "~",80 Not => "!",81 }82 )83 }84}8586#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]87pub enum BinaryOpType {88 Mul,89 Div,9091 /// Implemented as intrinsic, put here for completeness92 Mod,9394 Add,95 Sub,9697 Lhs,98 Rhs,99100 Lt,101 Gt,102 Lte,103 Gte,104105 BitAnd,106 BitOr,107 BitXor,108109 Eq,110 Neq,111112 And,113 Or,114 #[cfg(feature = "exp-null-coaelse")]115 NullCoaelse,116117 // Equialent to std.objectHasEx(a, b, true)118 In,119}120121impl Display for BinaryOpType {122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {123 use BinaryOpType::*;124 write!(125 f,126 "{}",127 match self {128 Mul => "*",129 Div => "/",130 Mod => "%",131 Add => "+",132 Sub => "-",133 Lhs => "<<",134 Rhs => ">>",135 Lt => "<",136 Gt => ">",137 Lte => "<=",138 Gte => ">=",139 BitAnd => "&",140 BitOr => "|",141 BitXor => "^",142 Eq => "==",143 Neq => "!=",144 And => "&&",145 Or => "||",146 In => "in",147 #[cfg(feature = "exp-null-coaelse")]148 NullCoaelse => "??",149 }150 )151 }152}153154/// name, default value155#[derive(Debug, PartialEq, Acyclic)]156pub struct ExprParam {157 pub destruct: Destruct,158 pub default: Option<Rc<Expr>>,159}160161/// Defined function parameters162#[derive(Debug, Clone, PartialEq, Acyclic)]163pub struct ExprParams {164 pub exprs: Rc<Vec<ExprParam>>,165 pub signature: FunctionSignature,166 pub(crate) binds_len: usize,167}168impl ExprParams {169 pub fn len(&self) -> usize {170 self.exprs.len()171 }172 pub fn is_empty(&self) -> bool {173 self.exprs.is_empty()174 }175176 pub fn binds_len(&self) -> usize {177 self.binds_len178 }179 pub fn new(exprs: Vec<ExprParam>) -> Self {180 Self {181 signature: FunctionSignature::new(182 exprs183 .iter()184 .map(|p| {185 ParamParse::new(186 p.destruct.name(),187 ParamDefault::exists(p.default.is_some()),188 )189 })190 .collect(),191 ),192 binds_len: exprs.iter().map(|v| v.destruct.binds_len()).sum(),193 exprs: Rc::new(exprs),194 }195 }196}197198#[derive(Debug, PartialEq, Acyclic)]199pub struct ArgsDesc {200 pub unnamed: Vec<Rc<Expr>>,201 pub names: Vec<IStr>,202 pub values: Vec<Rc<Expr>>,203}204impl ArgsDesc {205 pub fn new(unnamed: Vec<Rc<Expr>>, names: Vec<IStr>, values: Vec<Rc<Expr>>) -> Self {206 Self {207 unnamed,208 names,209 values,210 }211 }212}213214#[derive(Debug, Clone, PartialEq, Eq, Acyclic)]215pub enum DestructRest {216 /// ...rest217 Keep(IStr),218 /// ...219 Drop,220}221222#[derive(Debug, Clone, PartialEq, Acyclic)]223pub enum Destruct {224 Full(IStr),225 #[cfg(feature = "exp-destruct")]226 Skip,227 #[cfg(feature = "exp-destruct")]228 Array {229 start: Vec<Destruct>,230 rest: Option<DestructRest>,231 end: Vec<Destruct>,232 },233 #[cfg(feature = "exp-destruct")]234 Object {235 #[allow(clippy::type_complexity)]236 fields: Vec<(IStr, Option<Destruct>, Option<Rc<Spanned<Expr>>>)>,237 rest: Option<DestructRest>,238 },239}240impl Destruct {241 /// Name of destructure, used for function parameter names242 pub fn name(&self) -> ParamName {243 match self {244 Self::Full(name) => ParamName::Named(name.clone()),245 #[cfg(feature = "exp-destruct")]246 _ => ParamName::Unnamed,247 }248 }249 pub fn binds_len(&self) -> usize {250 #[cfg(feature = "exp-destruct")]251 fn cap_rest(rest: &Option<DestructRest>) -> usize {252 match rest {253 Some(DestructRest::Keep(_)) => 1,254 Some(DestructRest::Drop) => 0,255 None => 0,256 }257 }258 match self {259 Self::Full(_) => 1,260 #[cfg(feature = "exp-destruct")]261 Self::Skip => 0,262 #[cfg(feature = "exp-destruct")]263 Self::Array { start, rest, end } => {264 start.iter().map(Destruct::binds_len).sum::<usize>()265 + end.iter().map(Destruct::binds_len).sum::<usize>()266 + cap_rest(rest)267 }268 #[cfg(feature = "exp-destruct")]269 Self::Object { fields, rest } => {270 let mut out = 0;271 for (_, into, _) in fields {272 match into {273 Some(v) => out += v.binds_len(),274 // Field is destructured to default name275 None => out += 1,276 }277 }278 out + cap_rest(rest)279 }280 }281 }282}283284#[derive(Debug, PartialEq, Acyclic)]285pub enum BindSpec {286 Field {287 into: Destruct,288 value: Rc<Expr>,289 },290 Function {291 name: IStr,292 params: ExprParams,293 value: Rc<Expr>,294 },295}296impl BindSpec {297 pub fn binds_len(&self) -> usize {298 match self {299 BindSpec::Field { into, .. } => into.binds_len(),300 BindSpec::Function { .. } => 1,301 }302 }303}304305#[derive(Debug, PartialEq, Acyclic)]306pub struct IfSpecData {307 pub span: Span,308 pub cond: Expr,309}310311#[derive(Debug, PartialEq, Acyclic)]312pub struct ForSpecData {313 pub destruct: Destruct,314 pub over: Expr,315}316317#[derive(Debug, PartialEq, Acyclic)]318pub enum CompSpec {319 IfSpec(IfSpecData),320 ForSpec(ForSpecData),321}322323#[derive(Debug, PartialEq, Acyclic)]324pub struct ObjComp {325 pub locals: Rc<Vec<BindSpec>>,326 pub field: Rc<FieldMember>,327 pub compspecs: Vec<CompSpec>,328}329330#[derive(Debug, PartialEq, Acyclic)]331pub struct ObjMembers {332 pub locals: Rc<Vec<BindSpec>>,333 pub asserts: Rc<Vec<AssertStmt>>,334 pub fields: Vec<FieldMember>,335}336337#[derive(Debug, PartialEq, Acyclic)]338pub enum ObjBody {339 MemberList(ObjMembers),340 ObjComp(ObjComp),341}342343#[derive(Debug, PartialEq, Eq, Clone, Copy, Acyclic)]344pub enum LiteralType {345 This,346 Super,347 Dollar,348 Null,349 True,350 False,351}352353#[derive(Debug, PartialEq, Acyclic)]354pub struct SliceDesc {355 pub start: Option<Spanned<Expr>>,356 pub end: Option<Spanned<Expr>>,357 pub step: Option<Spanned<Expr>>,358}359360#[derive(Debug, PartialEq, Acyclic)]361pub struct AssertExpr {362 pub assert: AssertStmt,363 pub rest: Expr,364}365366#[derive(Debug, PartialEq, Acyclic)]367pub struct BinaryOp {368 pub lhs: Expr,369 pub op: BinaryOpType,370 pub rhs: Expr,371}372373#[derive(Debug, PartialEq, Acyclic)]374pub enum ImportKind {375 Normal,376 Str,377 Bin,378}379380#[derive(Debug, PartialEq, Acyclic)]381pub struct IfElse {382 pub cond: IfSpecData,383 pub cond_then: Expr,384 pub cond_else: Option<Expr>,385}386387#[derive(Debug, PartialEq, Acyclic)]388pub struct Slice {389 pub value: Expr,390 pub slice: SliceDesc,391}392393/// Syntax base394#[derive(Debug, PartialEq, Acyclic)]395pub enum Expr {396 Literal(LiteralType),397398 /// String value: "hello"399 Str(IStr),400 /// Number: 1, 2.0, 2e+20401 Num(f64),402 /// Variable name: test403 Var(Spanned<IStr>),404405 /// Array of expressions: [1, 2, "Hello"]406 Arr(Rc<Vec<Expr>>),407 /// Array comprehension:408 /// ```jsonnet409 /// ingredients: [410 /// { kind: kind, qty: 4 / 3 }411 /// for kind in [412 /// 'Honey Syrup',413 /// 'Lemon Juice',414 /// 'Farmers Gin',415 /// ]416 /// ],417 /// ```418 ArrComp(Rc<Expr>, Vec<CompSpec>),419420 /// Object: {a: 2}421 Obj(ObjBody),422 /// Object extension: var1 {b: 2}423 ObjExtend(Rc<Expr>, ObjBody),424425 /// -2426 UnaryOp(UnaryOpType, Box<Expr>),427 /// 2 - 2428 BinaryOp(Box<BinaryOp>),429 /// assert 2 == 2 : "Math is broken"430 AssertExpr(Rc<AssertExpr>),431 /// local a = 2; { b: a }432 LocalExpr(Vec<BindSpec>, Box<Expr>),433434 /// import* "hello"435 Import(Spanned<ImportKind>, Box<Expr>),436 /// error "I'm broken"437 ErrorStmt(Span, Box<Expr>),438 /// a(b, c)439 Apply(Box<Expr>, Spanned<ArgsDesc>, bool),440 /// a[b], a.b, a?.b441 Index {442 indexable: Box<Expr>,443 parts: Vec<IndexPart>,444 },445 /// function(x) x446 Function(ExprParams, Rc<Expr>),447 /// if true == false then 1 else 2448 IfElse(Box<IfElse>),449 Slice(Box<Slice>),450}451452#[derive(Debug, PartialEq, Acyclic)]453pub struct IndexPart {454 pub span: Span,455 pub value: Expr,456 #[cfg(feature = "exp-null-coaelse")]457 pub null_coaelse: bool,458}459460/// file, begin offset, end offset461#[derive(Clone, PartialEq, Eq, Acyclic)]462#[repr(C)]463pub struct Span(pub Source, pub u32, pub u32);464impl Span {465 pub fn belongs_to(&self, other: &Span) -> bool {466 other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2467 }468 pub fn range(&self) -> RangeInclusive<usize> {469 let start = self.1;470 let mut end = self.2;471 if end > start {472 // Because it is originally exclusive473 end -= 1;474 }475 start as usize..=end as usize476 }477}478479#[cfg(target_pointer_width = "64")]480static_assertions::assert_eq_size!(Span, (usize, usize));481482impl Debug for Span {483 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {484 write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)485 }486}487488#[derive(Clone, PartialEq, Acyclic)]489pub struct Spanned<T: Acyclic> {490 pub value: T,491 pub span: Span,492}493impl<T: Acyclic> Deref for Spanned<T> {494 type Target = T;495 fn deref(&self) -> &Self::Target {496 &self.value497 }498}499impl<T: Acyclic> Spanned<T> {500 #[inline]501 pub fn new(value: T, span: Span) -> Self {502 Self { value, span }503 }504 pub fn map<U: Acyclic>(self, v: impl FnOnce(T) -> U) -> Spanned<U> {505 Spanned {506 span: self.span,507 value: v(self.value),508 }509 }510 pub fn as_ref<'a>(&'a self) -> Spanned<&'a T>511 where512 &'a T: Acyclic,513 {514 Spanned {515 span: self.span.clone(),516 value: &self.value,517 }518 }519}520521impl<T: Debug + Acyclic> Debug for Spanned<T> {522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {523 let expr = &**self;524 if f.alternate() {525 write!(f, "{:#?}", expr)?;526 } else {527 write!(f, "{:?}", expr)?;528 }529 write!(f, " from {:?}", self.span)?;530 Ok(())531 }532}1use std::{2 fmt::{self, Debug, Display},3 ops::{Deref, RangeInclusive},4 rc::Rc,5};67use jrsonnet_gcmodule::Acyclic;8use jrsonnet_interner::IStr;910use crate::{11 NumValue,12 function::{FunctionSignature, ParamDefault, ParamName, ParamParse},13 source::Source,14};1516#[derive(Debug, PartialEq, Acyclic)]17pub enum FieldName {18 /// {fixed: 2}19 Fixed(IStr),20 /// {["dyn"+"amic"]: 3}21 Dyn(Expr),22}2324#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]25#[repr(u8)]26pub enum Visibility {27 /// :28 Normal,29 /// ::30 Hidden,31 /// :::32 Unhide,33}3435impl Visibility {36 pub fn is_visible(&self) -> bool {37 matches!(self, Self::Normal | Self::Unhide)38 }39}4041#[derive(Debug, PartialEq, Acyclic)]42pub struct AssertStmt {43 pub assertion: Spanned<Expr>,44 pub message: Option<Expr>,45}4647#[derive(Debug, PartialEq, Acyclic)]48pub struct FieldMember {49 pub name: Spanned<FieldName>,50 pub plus: bool,51 pub params: Option<ExprParams>,52 pub visibility: Visibility,53 pub value: Rc<Expr>,54}5556#[derive(Debug, PartialEq, Acyclic)]57pub enum Member {58 Field(FieldMember),59 BindStmt(BindSpec),60 AssertStmt(AssertStmt),61}6263#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]64pub enum UnaryOpType {65 Plus,66 Minus,67 BitNot,68 Not,69}7071impl Display for UnaryOpType {72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {73 use UnaryOpType::*;74 write!(75 f,76 "{}",77 match self {78 Plus => "+",79 Minus => "-",80 BitNot => "~",81 Not => "!",82 }83 )84 }85}8687#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]88pub enum BinaryOpType {89 Mul,90 Div,9192 /// Implemented as intrinsic, put here for completeness93 Mod,9495 Add,96 Sub,9798 Lhs,99 Rhs,100101 Lt,102 Gt,103 Lte,104 Gte,105106 BitAnd,107 BitOr,108 BitXor,109110 Eq,111 Neq,112113 And,114 Or,115 #[cfg(feature = "exp-null-coaelse")]116 NullCoaelse,117118 // Equialent to std.objectHasEx(a, b, true)119 In,120}121122impl Display for BinaryOpType {123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {124 use BinaryOpType::*;125 write!(126 f,127 "{}",128 match self {129 Mul => "*",130 Div => "/",131 Mod => "%",132 Add => "+",133 Sub => "-",134 Lhs => "<<",135 Rhs => ">>",136 Lt => "<",137 Gt => ">",138 Lte => "<=",139 Gte => ">=",140 BitAnd => "&",141 BitOr => "|",142 BitXor => "^",143 Eq => "==",144 Neq => "!=",145 And => "&&",146 Or => "||",147 In => "in",148 #[cfg(feature = "exp-null-coaelse")]149 NullCoaelse => "??",150 }151 )152 }153}154155/// name, default value156#[derive(Debug, PartialEq, Acyclic)]157pub struct ExprParam {158 pub destruct: Destruct,159 pub default: Option<Rc<Expr>>,160}161162/// Defined function parameters163#[derive(Debug, Clone, PartialEq, Acyclic)]164pub struct ExprParams {165 pub exprs: Rc<Vec<ExprParam>>,166 pub signature: FunctionSignature,167 pub(crate) binds_len: usize,168}169impl ExprParams {170 pub fn len(&self) -> usize {171 self.exprs.len()172 }173 pub fn is_empty(&self) -> bool {174 self.exprs.is_empty()175 }176177 pub fn binds_len(&self) -> usize {178 self.binds_len179 }180 pub fn new(exprs: Vec<ExprParam>) -> Self {181 Self {182 signature: FunctionSignature::new(183 exprs184 .iter()185 .map(|p| {186 ParamParse::new(187 p.destruct.name(),188 ParamDefault::exists(p.default.is_some()),189 )190 })191 .collect(),192 ),193 binds_len: exprs.iter().map(|v| v.destruct.binds_len()).sum(),194 exprs: Rc::new(exprs),195 }196 }197}198199#[derive(Debug, PartialEq, Acyclic)]200pub struct ArgsDesc {201 pub unnamed: Vec<Rc<Expr>>,202 pub names: Vec<IStr>,203 pub values: Vec<Rc<Expr>>,204}205impl ArgsDesc {206 pub fn new(unnamed: Vec<Rc<Expr>>, names: Vec<IStr>, values: Vec<Rc<Expr>>) -> Self {207 Self {208 unnamed,209 names,210 values,211 }212 }213}214215#[derive(Debug, Clone, PartialEq, Eq, Acyclic)]216pub enum DestructRest {217 /// ...rest218 Keep(IStr),219 /// ...220 Drop,221}222223#[derive(Debug, Clone, PartialEq, Acyclic)]224pub enum Destruct {225 Full(IStr),226 #[cfg(feature = "exp-destruct")]227 Skip,228 #[cfg(feature = "exp-destruct")]229 Array {230 start: Vec<Destruct>,231 rest: Option<DestructRest>,232 end: Vec<Destruct>,233 },234 #[cfg(feature = "exp-destruct")]235 Object {236 #[allow(clippy::type_complexity)]237 fields: Vec<(IStr, Option<Destruct>, Option<Rc<Spanned<Expr>>>)>,238 rest: Option<DestructRest>,239 },240}241impl Destruct {242 /// Name of destructure, used for function parameter names243 pub fn name(&self) -> ParamName {244 match self {245 Self::Full(name) => ParamName::Named(name.clone()),246 #[cfg(feature = "exp-destruct")]247 _ => ParamName::Unnamed,248 }249 }250 pub fn binds_len(&self) -> usize {251 #[cfg(feature = "exp-destruct")]252 fn cap_rest(rest: &Option<DestructRest>) -> usize {253 match rest {254 Some(DestructRest::Keep(_)) => 1,255 Some(DestructRest::Drop) => 0,256 None => 0,257 }258 }259 match self {260 Self::Full(_) => 1,261 #[cfg(feature = "exp-destruct")]262 Self::Skip => 0,263 #[cfg(feature = "exp-destruct")]264 Self::Array { start, rest, end } => {265 start.iter().map(Destruct::binds_len).sum::<usize>()266 + end.iter().map(Destruct::binds_len).sum::<usize>()267 + cap_rest(rest)268 }269 #[cfg(feature = "exp-destruct")]270 Self::Object { fields, rest } => {271 let mut out = 0;272 for (_, into, _) in fields {273 match into {274 Some(v) => out += v.binds_len(),275 // Field is destructured to default name276 None => out += 1,277 }278 }279 out + cap_rest(rest)280 }281 }282 }283}284285#[derive(Debug, PartialEq, Acyclic)]286pub enum BindSpec {287 Field {288 into: Destruct,289 value: Rc<Expr>,290 },291 Function {292 name: IStr,293 params: ExprParams,294 value: Rc<Expr>,295 },296}297impl BindSpec {298 pub fn binds_len(&self) -> usize {299 match self {300 BindSpec::Field { into, .. } => into.binds_len(),301 BindSpec::Function { .. } => 1,302 }303 }304}305306#[derive(Debug, PartialEq, Acyclic)]307pub struct IfSpecData {308 pub span: Span,309 pub cond: Expr,310}311312#[derive(Debug, PartialEq, Acyclic)]313pub struct ForSpecData {314 pub destruct: Destruct,315 pub over: Expr,316}317318#[derive(Debug, PartialEq, Acyclic)]319pub enum CompSpec {320 IfSpec(IfSpecData),321 ForSpec(ForSpecData),322}323324#[derive(Debug, PartialEq, Acyclic)]325pub struct ObjComp {326 pub locals: Rc<Vec<BindSpec>>,327 pub field: Rc<FieldMember>,328 pub compspecs: Vec<CompSpec>,329}330331#[derive(Debug, PartialEq, Acyclic)]332pub struct ObjMembers {333 pub locals: Rc<Vec<BindSpec>>,334 pub asserts: Rc<Vec<AssertStmt>>,335 pub fields: Vec<FieldMember>,336}337338#[derive(Debug, PartialEq, Acyclic)]339pub enum ObjBody {340 MemberList(ObjMembers),341 ObjComp(ObjComp),342}343344#[derive(Debug, PartialEq, Eq, Clone, Copy, Acyclic)]345pub enum LiteralType {346 This,347 Super,348 Dollar,349 Null,350 True,351 False,352}353354#[derive(Debug, PartialEq, Acyclic)]355pub struct SliceDesc {356 pub start: Option<Spanned<Expr>>,357 pub end: Option<Spanned<Expr>>,358 pub step: Option<Spanned<Expr>>,359}360361#[derive(Debug, PartialEq, Acyclic)]362pub struct AssertExpr {363 pub assert: AssertStmt,364 pub rest: Expr,365}366367#[derive(Debug, PartialEq, Acyclic)]368pub struct BinaryOp {369 pub lhs: Expr,370 pub op: BinaryOpType,371 pub rhs: Expr,372}373374#[derive(Debug, PartialEq, Acyclic)]375pub enum ImportKind {376 Normal,377 Str,378 Bin,379}380381#[derive(Debug, PartialEq, Acyclic)]382pub struct IfElse {383 pub cond: IfSpecData,384 pub cond_then: Expr,385 pub cond_else: Option<Expr>,386}387388#[derive(Debug, PartialEq, Acyclic)]389pub struct Slice {390 pub value: Expr,391 pub slice: SliceDesc,392}393394/// Syntax base395#[derive(Debug, PartialEq, Acyclic)]396pub enum Expr {397 Literal(LiteralType),398399 /// String value: "hello"400 Str(IStr),401 /// Number: 1, 2.0, 2e+20402 Num(NumValue),403 /// Variable name: test404 Var(Spanned<IStr>),405406 /// Array of expressions: [1, 2, "Hello"]407 Arr(Rc<Vec<Expr>>),408 /// Array comprehension:409 /// ```jsonnet410 /// ingredients: [411 /// { kind: kind, qty: 4 / 3 }412 /// for kind in [413 /// 'Honey Syrup',414 /// 'Lemon Juice',415 /// 'Farmers Gin',416 /// ]417 /// ],418 /// ```419 ArrComp(Rc<Expr>, Vec<CompSpec>),420421 /// Object: {a: 2}422 Obj(ObjBody),423 /// Object extension: var1 {b: 2}424 ObjExtend(Rc<Expr>, ObjBody),425426 /// -2427 UnaryOp(UnaryOpType, Box<Expr>),428 /// 2 - 2429 BinaryOp(Box<BinaryOp>),430 /// assert 2 == 2 : "Math is broken"431 AssertExpr(Rc<AssertExpr>),432 /// local a = 2; { b: a }433 LocalExpr(Vec<BindSpec>, Box<Expr>),434435 /// import* "hello"436 Import(Spanned<ImportKind>, Box<Expr>),437 /// error "I'm broken"438 ErrorStmt(Span, Box<Expr>),439 /// a(b, c)440 Apply(Box<Expr>, Spanned<ArgsDesc>, bool),441 /// a[b], a.b, a?.b442 Index {443 indexable: Box<Expr>,444 parts: Vec<IndexPart>,445 },446 /// function(x) x447 Function(ExprParams, Rc<Expr>),448 /// if true == false then 1 else 2449 IfElse(Box<IfElse>),450 Slice(Box<Slice>),451}452453#[derive(Debug, PartialEq, Acyclic)]454pub struct IndexPart {455 pub span: Span,456 pub value: Expr,457 #[cfg(feature = "exp-null-coaelse")]458 pub null_coaelse: bool,459}460461/// file, begin offset, end offset462#[derive(Clone, PartialEq, Eq, Acyclic)]463#[repr(C)]464pub struct Span(pub Source, pub u32, pub u32);465impl Span {466 pub fn belongs_to(&self, other: &Span) -> bool {467 other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2468 }469 pub fn range(&self) -> RangeInclusive<usize> {470 let start = self.1;471 let mut end = self.2;472 if end > start {473 // Because it is originally exclusive474 end -= 1;475 }476 start as usize..=end as usize477 }478}479480#[cfg(target_pointer_width = "64")]481static_assertions::assert_eq_size!(Span, (usize, usize));482483impl Debug for Span {484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {485 write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)486 }487}488489#[derive(Clone, PartialEq, Acyclic)]490pub struct Spanned<T: Acyclic> {491 pub value: T,492 pub span: Span,493}494impl<T: Acyclic> Deref for Spanned<T> {495 type Target = T;496 fn deref(&self) -> &Self::Target {497 &self.value498 }499}500impl<T: Acyclic> Spanned<T> {501 #[inline]502 pub fn new(value: T, span: Span) -> Self {503 Self { value, span }504 }505 pub fn map<U: Acyclic>(self, v: impl FnOnce(T) -> U) -> Spanned<U> {506 Spanned {507 span: self.span,508 value: v(self.value),509 }510 }511 pub fn as_ref<'a>(&'a self) -> Spanned<&'a T>512 where513 &'a T: Acyclic,514 {515 Spanned {516 span: self.span.clone(),517 value: &self.value,518 }519 }520}521522impl<T: Debug + Acyclic> Debug for Spanned<T> {523 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {524 let expr = &**self;525 if f.alternate() {526 write!(f, "{:#?}", expr)?;527 } else {528 write!(f, "{:?}", expr)?;529 }530 write!(f, " from {:?}", self.span)?;531 Ok(())532 }533}crates/jrsonnet-ir/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir/src/lib.rs
+++ b/crates/jrsonnet-ir/src/lib.rs
@@ -1,7 +1,10 @@
#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]
mod expr;
+use std::{cmp::Ordering, fmt, ops::Deref};
+
pub use expr::*;
+use jrsonnet_gcmodule::Acyclic;
pub use jrsonnet_interner::IStr;
pub mod function;
mod location;
@@ -14,3 +17,134 @@
Source, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,
SourcePathT, SourceVirtual,
};
+
+// It seels to be a wrong place for this kind of stuff, but as it would also be used for static analysis and
+// is already wanted for NumValue, I don't know a better place.
+#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]
+pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;
+#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]
+pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;
+
+/// Represents jsonnet number
+/// Jsonnet numbers are finite f64, with NaNs disallowed
+#[derive(Acyclic, Clone, Copy)]
+pub struct NumValue(f64);
+impl NumValue {
+ /// Creates a [`NumValue`], if value is finite and not NaN
+ pub fn new(v: f64) -> Option<Self> {
+ if !v.is_finite() {
+ return None;
+ }
+ Some(Self(v))
+ }
+ #[inline]
+ pub const fn get(&self) -> f64 {
+ self.0
+ }
+ pub fn truncate_for_bitwise(self) -> Result<i64, ConvertNumValueError> {
+ if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {
+ return Err(ConvertNumValueError::BitwiseSafeRange);
+ }
+ #[expect(clippy::cast_possible_truncation, reason = "intended")]
+ Ok(self.0 as i64)
+ }
+}
+impl PartialEq for NumValue {
+ fn eq(&self, other: &Self) -> bool {
+ self.0 == other.0
+ }
+}
+impl Eq for NumValue {}
+impl Ord for NumValue {
+ #[inline]
+ fn cmp(&self, other: &Self) -> Ordering {
+ // Can't use `total_cmp`: its behavior for `-0` and `0`
+ // is not following wanted.
+ unsafe { self.0.partial_cmp(&other.0).unwrap_unchecked() }
+ }
+}
+impl PartialOrd for NumValue {
+ #[inline]
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+impl fmt::Debug for NumValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&self.0, f)
+ }
+}
+impl fmt::Display for NumValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+impl Deref for NumValue {
+ type Target = f64;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+macro_rules! impl_num {
+ ($($ty:ty),+) => {$(
+ impl From<$ty> for NumValue {
+ #[inline]
+ fn from(value: $ty) -> Self {
+ Self(value.into())
+ }
+ }
+ )+};
+}
+impl_num!(i8, u8, i16, u16, i32, u32);
+
+#[derive(Clone, Copy, Debug, thiserror::Error, Acyclic)]
+pub enum ConvertNumValueError {
+ #[error("overflow")]
+ Overflow,
+ #[error("underflow")]
+ Underflow,
+ #[error("non-finite")]
+ NonFinite,
+ #[error("float out of safe int range")]
+ BitwiseSafeRange,
+}
+
+macro_rules! impl_try_num {
+ ($($ty:ty),+) => {$(
+ impl TryFrom<$ty> for NumValue {
+ type Error = ConvertNumValueError;
+ #[inline]
+ fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {
+ #[expect(clippy::cast_precision_loss, reason = "precision loss is explicitly handled")]
+ let value = value as f64;
+ if value < MIN_SAFE_INTEGER {
+ return Err(ConvertNumValueError::Underflow)
+ } else if value > MAX_SAFE_INTEGER {
+ return Err(ConvertNumValueError::Overflow)
+ }
+ // Number is finite.
+ Ok(Self(value))
+ }
+ }
+ )+};
+}
+impl_try_num!(usize, isize, i64, u64);
+
+impl TryFrom<f64> for NumValue {
+ type Error = ConvertNumValueError;
+
+ #[inline]
+ fn try_from(value: f64) -> Result<Self, Self::Error> {
+ Self::new(value).ok_or(ConvertNumValueError::NonFinite)
+ }
+}
+impl TryFrom<f32> for NumValue {
+ type Error = ConvertNumValueError;
+
+ #[inline]
+ fn try_from(value: f32) -> Result<Self, Self::Error> {
+ Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)
+ }
+}
crates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-peg-parser/src/lib.rs
+++ b/crates/jrsonnet-peg-parser/src/lib.rs
@@ -4,8 +4,8 @@
use jrsonnet_ir::{
ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BindSpec, CompSpec, Destruct, DestructRest, Expr,
ExprParam, ExprParams, FieldMember, FieldName, ForSpecData, IStr, IfElse, IfSpecData,
- ImportKind, IndexPart, LiteralType, Member, ObjBody, ObjComp, ObjMembers, Slice, SliceDesc,
- Source, Span, Spanned, Visibility, unescape,
+ ImportKind, IndexPart, LiteralType, Member, NumValue, ObjBody, ObjComp, ObjMembers, Slice,
+ SliceDesc, Source, Span, Spanned, Visibility, unescape,
};
use peg::parser;
@@ -52,7 +52,7 @@
/// Sequence of digits
rule uint_str() -> &'input str = a:$(digit()+ ("_" digit()+)*) { a }
/// Number in scientific notation format
- rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.replace("_","").parse().map_err(|_| "<number>") }} / expected!("<number>")
+ rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.replace('_',"").parse().map_err(|_| "<number>") }} / expected!("<number>")
/// Reserved word followed by any non-alphanumberic
rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()
@@ -267,7 +267,7 @@
Expr::ArrComp(Rc::new(expr), specs)
}
pub rule number_expr(s: &ParserSettings) -> Expr
- = n:number() {? if n.is_finite() {
+ = n:number() {? if let Some(n) = NumValue::new(n) {
Ok(Expr::Num(n))
} else {
Err("!!!numbers are finite")
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -12,13 +12,12 @@
pub use encoding::*;
pub use hash::*;
use jrsonnet_evaluator::{
- ContextBuilder, IStr, ObjValue, ObjValueBuilder, Thunk, Val,
+ ContextBuilder, IStr, NumValue, ObjValue, ObjValueBuilder, Thunk, Val,
error::Result,
function::{CallLocation, FuncVal, builtin_id},
tla::TlaArg,
trace::PathResolver,
typed::SerializeTypedObj as _,
- val::NumValue,
};
use jrsonnet_gcmodule::{Acyclic, Cc, Trace};
use jrsonnet_ir::Source;
crates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/operator.rs
+++ b/crates/jrsonnet-stdlib/src/operator.rs
@@ -2,12 +2,12 @@
//! However, in our case we instead implement them in native, and implement native functions on top of core for backwards compatibility
use jrsonnet_evaluator::{
- IStr, Result, Val,
+ IStr, NumValue, Result, Val,
function::builtin,
operator::evaluate_mod_op,
stdlib::std_format,
typed::{Either, Either2},
- val::{NumValue, equals, primitive_equals},
+ val::{equals, primitive_equals},
};
#[builtin]