difftreelog
feat return NumValue directly from parser
in: master
15 files changed
bindings/jsonnet/src/val_make.rsdiffbeforeafterboth5 os::raw::{c_char, c_double, c_int},5 os::raw::{c_char, c_double, c_int},6};6};778use jrsonnet_evaluator::{8use jrsonnet_evaluator::{NumValue, ObjValue, Val};9 ObjValue, Val,10 val::{ArrValue, NumValue},11};12913use crate::VM;10use crate::VM;crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth3use jrsonnet_gcmodule::{Acyclic, Trace};3use jrsonnet_gcmodule::{Acyclic, Trace};4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;5use jrsonnet_ir::{BinaryOpType, Source, SourcePath, Span, Spanned, UnaryOpType};5use jrsonnet_ir::{6 BinaryOpType, ConvertNumValueError, Source, SourcePath, Span, Spanned, UnaryOpType,7};6use jrsonnet_types::ValType;8use jrsonnet_types::ValType;7use thiserror::Error;9use thiserror::Error;11 function::{CallLocation, FunctionSignature, ParamName},13 function::{CallLocation, FunctionSignature, ParamName},12 stdlib::format::FormatError,14 stdlib::format::FormatError,13 typed::TypeLocError,15 typed::TypeLocError,14 val::ConvertNumValueError,15};16};161717#[derive(Debug, Clone)]18#[derive(Debug, Clone)]228 Self::new(e)229 Self::new(e)229 }230 }230}231}232impl From<ConvertNumValueError> for Error {233 fn from(e: ConvertNumValueError) -> Self {234 Self::new(ErrorKind::ConvertNumValue(e))235 }236}231237232impl From<Infallible> for Error {238impl From<Infallible> for Error {233 fn from(_value: Infallible) -> Self {239 fn from(_value: Infallible) -> Self {crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth21 function::{CallLocation, FuncDesc, FuncVal, PreparedFuncVal},21 function::{CallLocation, FuncDesc, FuncVal, PreparedFuncVal},22 in_frame,22 in_frame,23 typed::{FromUntyped, IntoUntyped as _, Typed},23 typed::{FromUntyped, IntoUntyped as _, Typed},24 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},24 val::{CachedUnbound, IndexableVal, StrValue, Thunk},25 with_state,25 with_state,26};26};27pub mod destructure;27pub mod destructure;58 }58 }59 Some(match expr {59 Some(match expr {60 Expr::Str(s) => Val::string(s.clone()),60 Expr::Str(s) => Val::string(s.clone()),61 Expr::Num(n) => {61 Expr::Num(n) => Val::Num(*n),62 Val::Num(NumValue::new(*n).expect("parser will not allow non-finite values"))63 }64 Expr::Literal(LiteralType::False) => Val::Bool(false),62 Expr::Literal(LiteralType::False) => Val::Bool(false),65 Expr::Literal(LiteralType::True) => Val::Bool(true),63 Expr::Literal(LiteralType::True) => Val::Bool(true),66 Expr::Literal(LiteralType::Null) => Val::Null,64 Expr::Literal(LiteralType::Null) => Val::Null,crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth1use std::borrow::Cow;1use std::borrow::Cow;223use jrsonnet_interner::{IBytes, IStr};3use jrsonnet_interner::{IBytes, IStr};4use jrsonnet_ir::NumValue;4use serde::{5use serde::{5 Deserialize, Serialize, Serializer,6 Deserialize, Serialize, Serializer,6 de::{self, Visitor},7 de::{self, Visitor},121313use crate::{14use crate::{14 Error as JrError, ObjValue, ObjValueBuilder, Result, Val, in_description_frame, runtime_error,15 Error as JrError, ObjValue, ObjValueBuilder, Result, Val, in_description_frame, runtime_error,15 val::NumValue,16};16};171718impl<'de> Deserialize<'de> for Val {18impl<'de> Deserialize<'de> for Val {crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth42use jrsonnet_gcmodule::{Cc, Trace, cc_dyn};42use jrsonnet_gcmodule::{Cc, Trace, cc_dyn};43pub use jrsonnet_interner::{IBytes, IStr};43pub use jrsonnet_interner::{IBytes, IStr};44pub use jrsonnet_ir as parser;44pub use jrsonnet_ir as parser;45pub use jrsonnet_ir::NumValue;45use jrsonnet_ir::{Expr, Source, SourcePath};46use jrsonnet_ir::{Expr, Source, SourcePath};46#[doc(hidden)]47#[doc(hidden)]47pub use jrsonnet_macros;48pub use jrsonnet_macros;crates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth829#[cfg(test)]829#[cfg(test)]830pub mod test_format {830pub mod test_format {831 use super::*;831 use super::*;832 use crate::val::NumValue;832 use crate::NumValue;833833834 #[test]834 #[test]835 fn parse() {835 fn parse() {crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth223use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::{IBytes, IStr};4use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_ir::NumValue;6pub use jrsonnet_ir::{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER};5use jrsonnet_types::{ComplexValType, ValType};7use jrsonnet_types::{ComplexValType, ValType};687use crate::{9use crate::{10 bail,12 bail,11 function::FuncVal,13 function::FuncVal,12 typed::CheckType,14 typed::CheckType,13 val::{IndexableVal, NumValue, StrValue, ThunkMapper},15 val::{IndexableVal, StrValue, ThunkMapper},14};16};151716#[doc(hidden)]18#[doc(hidden)]220 }222 }221}223}222223#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]224pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;225#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]226pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;227224228macro_rules! impl_int {225macro_rules! impl_int {229 ($($ty:ty)*) => {$(226 ($($ty:ty)*) => {$(crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth5 marker::PhantomData,5 marker::PhantomData,6 mem::replace,6 mem::replace,7 num::NonZeroU32,7 num::NonZeroU32,8 ops::Deref,9 rc::Rc,8 rc::Rc,10};9};111014pub use jrsonnet_macros::Thunk;13pub use jrsonnet_macros::Thunk;15use jrsonnet_types::ValType;14use jrsonnet_types::ValType;16use rustc_hash::FxHashMap;15use rustc_hash::FxHashMap;17use thiserror::Error;181619pub use crate::arr::{ArrValue, ArrayLike};17pub use crate::arr::{ArrValue, ArrayLike};20use crate::{18use crate::{21 ObjValue, Result, SupThis, Unbound, WeakSupThis, bail,19 NumValue, ObjValue, Result, SupThis, Unbound, WeakSupThis, bail,22 error::{Error, ErrorKind::*},20 error::{Error, ErrorKind::*},23 function::FuncVal,21 function::FuncVal,24 gc::WithCapacityExt as _,22 gc::WithCapacityExt as _,25 manifest::{ManifestFormat, ToStringFormat},23 manifest::{ManifestFormat, ToStringFormat},26 typed::{BoundedUsize, MAX_SAFE_INTEGER, MIN_SAFE_INTEGER},24 typed::BoundedUsize,27};25};282629pub trait ThunkValue: Trace {27pub trait ThunkValue: Trace {442 }440 }443}441}444445/// Represents jsonnet number446/// Jsonnet numbers are finite f64, with NaNs disallowed447#[derive(Trace, Clone, Copy)]448#[repr(transparent)]449pub struct NumValue(f64);450impl NumValue {451 /// Creates a [`NumValue`], if value is finite and not NaN452 pub fn new(v: f64) -> Option<Self> {453 if !v.is_finite() {454 return None;455 }456 Some(Self(v))457 }458 #[inline]459 pub const fn get(&self) -> f64 {460 self.0461 }462 pub(crate) fn truncate_for_bitwise(self) -> Result<i64> {463 if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {464 bail!("numberic value outside of safe integer range for bitwise operation");465 }466 #[expect(clippy::cast_possible_truncation, reason = "intended")]467 Ok(self.0 as i64)468 }469}470impl PartialEq for NumValue {471 fn eq(&self, other: &Self) -> bool {472 self.0 == other.0473 }474}475impl Eq for NumValue {}476impl Ord for NumValue {477 #[inline]478 fn cmp(&self, other: &Self) -> Ordering {479 // Can't use `total_cmp`: its behavior for `-0` and `0`480 // is not following wanted.481 unsafe { self.0.partial_cmp(&other.0).unwrap_unchecked() }482 }483}484impl PartialOrd for NumValue {485 #[inline]486 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {487 Some(self.cmp(other))488 }489}490impl Debug for NumValue {491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {492 Debug::fmt(&self.0, f)493 }494}495impl Display for NumValue {496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {497 Display::fmt(&self.0, f)498 }499}500impl Deref for NumValue {501 type Target = f64;502503 #[inline]504 fn deref(&self) -> &Self::Target {505 &self.0506 }507}508macro_rules! impl_num {509 ($($ty:ty),+) => {$(510 impl From<$ty> for NumValue {511 #[inline]512 fn from(value: $ty) -> Self {513 Self(value.into())514 }515 }516 )+};517}518impl_num!(i8, u8, i16, u16, i32, u32);519520#[derive(Clone, Copy, Debug, Error, Trace)]521pub enum ConvertNumValueError {522 #[error("overflow")]523 Overflow,524 #[error("underflow")]525 Underflow,526 #[error("non-finite")]527 NonFinite,528}529impl From<ConvertNumValueError> for Error {530 fn from(e: ConvertNumValueError) -> Self {531 Self::new(e.into())532 }533}534535macro_rules! impl_try_num {536 ($($ty:ty),+) => {$(537 impl TryFrom<$ty> for NumValue {538 type Error = ConvertNumValueError;539 #[inline]540 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {541 #[expect(clippy::cast_precision_loss, reason = "precision loss is explicitly handled")]542 let value = value as f64;543 if value < MIN_SAFE_INTEGER {544 return Err(ConvertNumValueError::Underflow)545 } else if value > MAX_SAFE_INTEGER {546 return Err(ConvertNumValueError::Overflow)547 }548 // Number is finite.549 Ok(Self(value))550 }551 }552 )+};553}554impl_try_num!(usize, isize, i64, u64);555556impl TryFrom<f64> for NumValue {557 type Error = ConvertNumValueError;558559 #[inline]560 fn try_from(value: f64) -> Result<Self, Self::Error> {561 Self::new(value).ok_or(ConvertNumValueError::NonFinite)562 }563}564impl TryFrom<f32> for NumValue {565 type Error = ConvertNumValueError;566567 #[inline]568 fn try_from(value: f32) -> Result<Self, Self::Error> {569 Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)570 }571}572442573/// Represents any valid Jsonnet value.443/// Represents any valid Jsonnet value.574#[derive(Debug, Clone, Trace, Default)]444#[derive(Debug, Clone, Trace, Default)]crates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth4use jrsonnet_ir::{4use jrsonnet_ir::{5 ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BinaryOpType, BindSpec, CompSpec, Destruct, Expr,5 ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BinaryOpType, BindSpec, CompSpec, Destruct, Expr,6 ExprParam, ExprParams, FieldMember, FieldName, ForSpecData, IStr, IfElse, IfSpecData,6 ExprParam, ExprParams, FieldMember, FieldName, ForSpecData, IStr, IfElse, IfSpecData,7 ImportKind, IndexPart, LiteralType, Member, ObjBody, ObjComp, ObjMembers, Slice, SliceDesc,7 ImportKind, IndexPart, LiteralType, Member, NumValue, ObjBody, ObjComp, ObjMembers, Slice,8 Source, Span, Spanned, UnaryOpType, Visibility, unescape,8 SliceDesc, Source, Span, Spanned, UnaryOpType, Visibility, unescape,9};9};10use jrsonnet_lexer::{Lexeme, Lexer, Span as LexSpan, SyntaxKind, T, collect_lexed_str_block};10use jrsonnet_lexer::{Lexeme, Lexer, Span as LexSpan, SyntaxKind, T, collect_lexed_str_block};1111202 )202 )203}203}204204205fn parse_number(p: &mut Parser<'_>) -> Result<f64> {205fn parse_number(p: &mut Parser<'_>) -> Result<NumValue> {206 let text = p.text();206 let text = p.text();207 let n: f64 = text207 let n: f64 = text208 .replace('_', "")208 .replace('_', "")209 .parse()209 .parse()210 .map_err(|_| p.error(format!("invalid number literal: {text}")))?;210 .map_err(|_| p.error(format!("invalid number literal: {text}")))?;211211 if !n.is_finite() {212 let v = match NumValue::try_from(n) {212 return Err(p.error("numbers are finite".into()));213 Ok(v) => v,214 Err(e) => return Err(p.error(format!("invalid number value: {e}"))),213 }215 };216214 p.eat_any();217 p.eat_any();218215 Ok(n)219 Ok(v)216}220}217221218fn ident(p: &mut Parser<'_>) -> Result<IStr> {222fn ident(p: &mut Parser<'_>) -> Result<IStr> {crates/jrsonnet-ir/Cargo.tomldiffbeforeafterboth19static_assertions.workspace = true19static_assertions.workspace = true202021peg.workspace = true21peg.workspace = true22thiserror.workspace = true222323[dev-dependencies]24[dev-dependencies]24insta.workspace = true25insta.workspace = truecrates/jrsonnet-ir/src/expr.rsdiffbeforeafterboth8use jrsonnet_interner::IStr;8use jrsonnet_interner::IStr;9910use crate::{10use crate::{11 NumValue,11 function::{FunctionSignature, ParamDefault, ParamName, ParamParse},12 function::{FunctionSignature, ParamDefault, ParamName, ParamParse},12 source::Source,13 source::Source,13};14};398 /// String value: "hello"399 /// String value: "hello"399 Str(IStr),400 Str(IStr),400 /// Number: 1, 2.0, 2e+20401 /// Number: 1, 2.0, 2e+20401 Num(f64),402 Num(NumValue),402 /// Variable name: test403 /// Variable name: test403 Var(Spanned<IStr>),404 Var(Spanned<IStr>),404405crates/jrsonnet-ir/src/lib.rsdiffbeforeafterboth1#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]1#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]223mod expr;3mod expr;4use std::{cmp::Ordering, fmt, ops::Deref};54pub use expr::*;6pub use expr::*;7use jrsonnet_gcmodule::Acyclic;5pub use jrsonnet_interner::IStr;8pub use jrsonnet_interner::IStr;6pub mod function;9pub mod function;7mod location;10mod location;15 SourcePathT, SourceVirtual,18 SourcePathT, SourceVirtual,16};19};2021// It seels to be a wrong place for this kind of stuff, but as it would also be used for static analysis and22// is already wanted for NumValue, I don't know a better place.23#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]24pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;25#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]26pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;2728/// Represents jsonnet number29/// Jsonnet numbers are finite f64, with NaNs disallowed30#[derive(Acyclic, Clone, Copy)]31pub struct NumValue(f64);32impl NumValue {33 /// Creates a [`NumValue`], if value is finite and not NaN34 pub fn new(v: f64) -> Option<Self> {35 if !v.is_finite() {36 return None;37 }38 Some(Self(v))39 }40 #[inline]41 pub const fn get(&self) -> f64 {42 self.043 }44 pub fn truncate_for_bitwise(self) -> Result<i64, ConvertNumValueError> {45 if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {46 return Err(ConvertNumValueError::BitwiseSafeRange);47 }48 #[expect(clippy::cast_possible_truncation, reason = "intended")]49 Ok(self.0 as i64)50 }51}52impl PartialEq for NumValue {53 fn eq(&self, other: &Self) -> bool {54 self.0 == other.055 }56}57impl Eq for NumValue {}58impl Ord for NumValue {59 #[inline]60 fn cmp(&self, other: &Self) -> Ordering {61 // Can't use `total_cmp`: its behavior for `-0` and `0`62 // is not following wanted.63 unsafe { self.0.partial_cmp(&other.0).unwrap_unchecked() }64 }65}66impl PartialOrd for NumValue {67 #[inline]68 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {69 Some(self.cmp(other))70 }71}72impl fmt::Debug for NumValue {73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {74 fmt::Debug::fmt(&self.0, f)75 }76}77impl fmt::Display for NumValue {78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {79 fmt::Display::fmt(&self.0, f)80 }81}82impl Deref for NumValue {83 type Target = f64;8485 #[inline]86 fn deref(&self) -> &Self::Target {87 &self.088 }89}90macro_rules! impl_num {91 ($($ty:ty),+) => {$(92 impl From<$ty> for NumValue {93 #[inline]94 fn from(value: $ty) -> Self {95 Self(value.into())96 }97 }98 )+};99}100impl_num!(i8, u8, i16, u16, i32, u32);101102#[derive(Clone, Copy, Debug, thiserror::Error, Acyclic)]103pub enum ConvertNumValueError {104 #[error("overflow")]105 Overflow,106 #[error("underflow")]107 Underflow,108 #[error("non-finite")]109 NonFinite,110 #[error("float out of safe int range")]111 BitwiseSafeRange,112}113114macro_rules! impl_try_num {115 ($($ty:ty),+) => {$(116 impl TryFrom<$ty> for NumValue {117 type Error = ConvertNumValueError;118 #[inline]119 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {120 #[expect(clippy::cast_precision_loss, reason = "precision loss is explicitly handled")]121 let value = value as f64;122 if value < MIN_SAFE_INTEGER {123 return Err(ConvertNumValueError::Underflow)124 } else if value > MAX_SAFE_INTEGER {125 return Err(ConvertNumValueError::Overflow)126 }127 // Number is finite.128 Ok(Self(value))129 }130 }131 )+};132}133impl_try_num!(usize, isize, i64, u64);134135impl TryFrom<f64> for NumValue {136 type Error = ConvertNumValueError;137138 #[inline]139 fn try_from(value: f64) -> Result<Self, Self::Error> {140 Self::new(value).ok_or(ConvertNumValueError::NonFinite)141 }142}143impl TryFrom<f32> for NumValue {144 type Error = ConvertNumValueError;145146 #[inline]147 fn try_from(value: f32) -> Result<Self, Self::Error> {148 Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)149 }150}17151crates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth4use jrsonnet_ir::{4use jrsonnet_ir::{5 ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BindSpec, CompSpec, Destruct, DestructRest, Expr,5 ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BindSpec, CompSpec, Destruct, DestructRest, Expr,6 ExprParam, ExprParams, FieldMember, FieldName, ForSpecData, IStr, IfElse, IfSpecData,6 ExprParam, ExprParams, FieldMember, FieldName, ForSpecData, IStr, IfElse, IfSpecData,7 ImportKind, IndexPart, LiteralType, Member, ObjBody, ObjComp, ObjMembers, Slice, SliceDesc,7 ImportKind, IndexPart, LiteralType, Member, NumValue, ObjBody, ObjComp, ObjMembers, Slice,8 Source, Span, Spanned, Visibility, unescape,8 SliceDesc, Source, Span, Spanned, Visibility, unescape,9};9};10use peg::parser;10use peg::parser;52 /// Sequence of digits52 /// Sequence of digits53 rule uint_str() -> &'input str = a:$(digit()+ ("_" digit()+)*) { a }53 rule uint_str() -> &'input str = a:$(digit()+ ("_" digit()+)*) { a }54 /// Number in scientific notation format54 /// Number in scientific notation format55 rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.replace("_","").parse().map_err(|_| "<number>") }} / expected!("<number>")55 rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.replace('_',"").parse().map_err(|_| "<number>") }} / expected!("<number>")565657 /// Reserved word followed by any non-alphanumberic57 /// Reserved word followed by any non-alphanumberic58 rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()58 rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()267 Expr::ArrComp(Rc::new(expr), specs)267 Expr::ArrComp(Rc::new(expr), specs)268 }268 }269 pub rule number_expr(s: &ParserSettings) -> Expr269 pub rule number_expr(s: &ParserSettings) -> Expr270 = n:number() {? if n.is_finite() {270 = n:number() {? if let Some(n) = NumValue::new(n) {271 Ok(Expr::Num(n))271 Ok(Expr::Num(n))272 } else {272 } else {273 Err("!!!numbers are finite")273 Err("!!!numbers are finite")crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth12pub use encoding::*;12pub use encoding::*;13pub use hash::*;13pub use hash::*;14use jrsonnet_evaluator::{14use jrsonnet_evaluator::{15 ContextBuilder, IStr, ObjValue, ObjValueBuilder, Thunk, Val,15 ContextBuilder, IStr, NumValue, ObjValue, ObjValueBuilder, Thunk, Val,16 error::Result,16 error::Result,17 function::{CallLocation, FuncVal, builtin_id},17 function::{CallLocation, FuncVal, builtin_id},18 tla::TlaArg,18 tla::TlaArg,19 trace::PathResolver,19 trace::PathResolver,20 typed::SerializeTypedObj as _,20 typed::SerializeTypedObj as _,21 val::NumValue,22};21};23use jrsonnet_gcmodule::{Acyclic, Cc, Trace};22use jrsonnet_gcmodule::{Acyclic, Cc, Trace};24use jrsonnet_ir::Source;23use jrsonnet_ir::Source;crates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth2//! However, in our case we instead implement them in native, and implement native functions on top of core for backwards compatibility2//! However, in our case we instead implement them in native, and implement native functions on top of core for backwards compatibility334use jrsonnet_evaluator::{4use jrsonnet_evaluator::{5 IStr, Result, Val,5 IStr, NumValue, Result, Val,6 function::builtin,6 function::builtin,7 operator::evaluate_mod_op,7 operator::evaluate_mod_op,8 stdlib::std_format,8 stdlib::std_format,9 typed::{Either, Either2},9 typed::{Either, Either2},10 val::{NumValue, equals, primitive_equals},10 val::{equals, primitive_equals},11};11};121213#[builtin]13#[builtin]