1use std::{2 cell::RefCell,3 fmt::{self, Debug, Display},4 mem::replace,5 rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::ArrValue;13use crate::{14 error::{Error, ErrorKind::*},15 function::FuncVal,16 gc::{GcHashMap, TraceBox},17 manifest::{ManifestFormat, ToStringFormat},18 throw,19 typed::BoundedUsize,20 ObjValue, Result, Unbound, WeakObjValue,21};2223pub trait ThunkValue: Trace {24 type Output;25 fn get(self: Box<Self>) -> Result<Self::Output>;26}2728#[derive(Trace)]29enum ThunkInner<T: Trace> {30 Computed(T),31 Errored(Error),32 Waiting(TraceBox<dyn ThunkValue<Output = T>>),33 Pending,34}3536#[allow(clippy::module_name_repetitions)]37#[derive(Clone, Trace)]38pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);3940impl<T: Trace> Thunk<T> {41 pub fn evaluated(val: T) -> Self {42 Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))43 }44 pub fn new(f: TraceBox<dyn ThunkValue<Output = T>>) -> Self {45 Self(Cc::new(RefCell::new(ThunkInner::Waiting(f))))46 }47 pub fn errored(e: Error) -> Self {48 Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))49 }50}5152impl<T> Thunk<T>53where54 T: Clone + Trace,55{56 pub fn force(&self) -> Result<()> {57 self.evaluate()?;58 Ok(())59 }60 pub fn evaluate(&self) -> Result<T> {61 match &*self.0.borrow() {62 ThunkInner::Computed(v) => return Ok(v.clone()),63 ThunkInner::Errored(e) => return Err(e.clone()),64 ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),65 ThunkInner::Waiting(..) => (),66 };67 let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending) else {68 unreachable!();69 };70 let new_value = match value.0.get() {71 Ok(v) => v,72 Err(e) => {73 *self.0.borrow_mut() = ThunkInner::Errored(e.clone());74 return Err(e);75 }76 };77 *self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());78 Ok(new_value)79 }80}8182type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);8384#[derive(Trace, Clone)]85pub struct CachedUnbound<I, T>86where87 I: Unbound<Bound = T>,88 T: Trace,89{90 cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,91 value: I,92}93impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {94 pub fn new(value: I) -> Self {95 Self {96 cache: Cc::new(RefCell::new(GcHashMap::new())),97 value,98 }99 }100}101impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {102 type Bound = T;103 fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {104 let cache_key = (105 sup.as_ref().map(|s| s.clone().downgrade()),106 this.as_ref().map(|t| t.clone().downgrade()),107 );108 {109 if let Some(t) = self.cache.borrow().get(&cache_key) {110 return Ok(t.clone());111 }112 }113 let bound = self.value.bind(sup, this)?;114115 {116 let mut cache = self.cache.borrow_mut();117 cache.insert(cache_key, bound.clone());118 }119120 Ok(bound)121 }122}123124impl<T: Debug + Trace> Debug for Thunk<T> {125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {126 write!(f, "Lazy")127 }128}129impl<T: Trace> PartialEq for Thunk<T> {130 fn eq(&self, other: &Self) -> bool {131 Cc::ptr_eq(&self.0, &other.0)132 }133}134135136#[allow(clippy::module_name_repetitions)]137pub enum IndexableVal {138 139 Str(IStr),140 141 Arr(ArrValue),142}143impl IndexableVal {144 145 146 147 148 149 150 151 pub fn slice(152 self,153 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,154 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,155 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,156 ) -> Result<Self> {157 match &self {158 IndexableVal::Str(s) => {159 let index = index.as_deref().copied().unwrap_or(0);160 let end = end.as_deref().copied().unwrap_or(usize::MAX);161 let step = step.as_deref().copied().unwrap_or(1);162163 if index >= end {164 return Ok(Self::Str("".into()));165 }166167 Ok(Self::Str(168 (s.chars()169 .skip(index)170 .take(end - index)171 .step_by(step)172 .collect::<String>())173 .into(),174 ))175 }176 IndexableVal::Arr(arr) => {177 let index = index.as_deref().copied().unwrap_or(0);178 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());179 let step = step.as_deref().copied().unwrap_or(1);180181 if index >= end {182 return Ok(Self::Arr(ArrValue::empty()));183 }184185 Ok(Self::Arr(186 arr.clone()187 .slice(Some(index), Some(end), Some(step))188 .expect("arguments checked"),189 ))190 }191 }192 }193}194195#[derive(Debug, Clone, Trace)]196pub enum StrValue {197 Flat(IStr),198 Tree(Rc<(StrValue, StrValue, usize)>),199}200impl StrValue {201 pub fn concat(a: StrValue, b: StrValue) -> Self {202 if a.is_empty() {203 b204 } else if b.is_empty() {205 a206 } else {207 let len = a.len() + b.len();208 Self::Tree(Rc::new((a, b, len)))209 }210 }211 pub fn into_flat(self) -> IStr {212 match self {213 StrValue::Flat(f) => f,214 StrValue::Tree(_) => {215 let mut buf = String::new();216 self.into_flat_buf(&mut buf);217 buf.into()218 }219 }220 }221 fn into_flat_buf(&self, out: &mut String) {222 match self {223 StrValue::Flat(f) => out.push_str(f),224 StrValue::Tree(t) => {225 t.0.into_flat_buf(out);226 t.1.into_flat_buf(out);227 }228 }229 }230 pub fn len(&self) -> usize {231 match self {232 StrValue::Flat(v) => v.len(),233 StrValue::Tree(t) => t.2,234 }235 }236 pub fn is_empty(&self) -> bool {237 match self {238 Self::Flat(v) => v.is_empty(),239 _ => false,240 }241 }242}243impl Display for StrValue {244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {245 match self {246 StrValue::Flat(v) => write!(f, "{v}"),247 StrValue::Tree(t) => {248 write!(f, "{}", t.0)?;249 write!(f, "{}", t.1)250 }251 }252 }253}254impl PartialEq for StrValue {255 fn eq(&self, other: &Self) -> bool {256 let a = self.clone().into_flat();257 let b = other.clone().into_flat();258 a == b259 }260}261impl Eq for StrValue {}262impl PartialOrd for StrValue {263 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {264 let a = self.clone().into_flat();265 let b = other.clone().into_flat();266 Some(a.cmp(&b))267 }268}269impl Ord for StrValue {270 fn cmp(&self, other: &Self) -> std::cmp::Ordering {271 self.partial_cmp(other)272 .expect("partial_cmp always returns Some")273 }274}275276277#[derive(Debug, Clone, Trace)]278pub enum Val {279 280 Bool(bool),281 282 Null,283 284 Str(StrValue),285 286 287 288 Num(f64),289 290 Arr(ArrValue),291 292 Obj(ObjValue),293 294 Func(FuncVal),295}296297static_assertions::assert_eq_size!(Val, [u8; 24]);298299impl From<IndexableVal> for Val {300 fn from(v: IndexableVal) -> Self {301 match v {302 IndexableVal::Str(s) => Self::Str(StrValue::Flat(s)),303 IndexableVal::Arr(a) => Self::Arr(a),304 }305 }306}307308impl Val {309 pub const fn as_bool(&self) -> Option<bool> {310 match self {311 Self::Bool(v) => Some(*v),312 _ => None,313 }314 }315 pub const fn as_null(&self) -> Option<()> {316 match self {317 Self::Null => Some(()),318 _ => None,319 }320 }321 pub fn as_str(&self) -> Option<IStr> {322 match self {323 Self::Str(s) => Some(s.clone().into_flat()),324 _ => None,325 }326 }327 pub const fn as_num(&self) -> Option<f64> {328 match self {329 Self::Num(n) => Some(*n),330 _ => None,331 }332 }333 pub fn as_arr(&self) -> Option<ArrValue> {334 match self {335 Self::Arr(a) => Some(a.clone()),336 _ => None,337 }338 }339 pub fn as_obj(&self) -> Option<ObjValue> {340 match self {341 Self::Obj(o) => Some(o.clone()),342 _ => None,343 }344 }345 pub fn as_func(&self) -> Option<FuncVal> {346 match self {347 Self::Func(f) => Some(f.clone()),348 _ => None,349 }350 }351352 353 354 pub fn new_checked_num(num: f64) -> Result<Self> {355 if num.is_finite() {356 Ok(Self::Num(num))357 } else {358 throw!("overflow")359 }360 }361362 pub const fn value_type(&self) -> ValType {363 match self {364 Self::Str(..) => ValType::Str,365 Self::Num(..) => ValType::Num,366 Self::Arr(..) => ValType::Arr,367 Self::Obj(..) => ValType::Obj,368 Self::Bool(_) => ValType::Bool,369 Self::Null => ValType::Null,370 Self::Func(..) => ValType::Func,371 }372 }373374 pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {375 fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {376 manifest.manifest(val.clone())377 }378 manifest_dyn(self, &format)379 }380381 pub fn to_string(&self) -> Result<IStr> {382 Ok(match self {383 Self::Bool(true) => "true".into(),384 Self::Bool(false) => "false".into(),385 Self::Null => "null".into(),386 Self::Str(s) => s.clone().into_flat(),387 _ => self.manifest(ToStringFormat).map(IStr::from)?,388 })389 }390391 pub fn into_indexable(self) -> Result<IndexableVal> {392 Ok(match self {393 Val::Str(s) => IndexableVal::Str(s.into_flat()),394 Val::Arr(arr) => IndexableVal::Arr(arr),395 _ => throw!(ValueIsNotIndexable(self.value_type())),396 })397 }398}399400const fn is_function_like(val: &Val) -> bool {401 matches!(val, Val::Func(_))402}403404405pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {406 Ok(match (val_a, val_b) {407 (Val::Bool(a), Val::Bool(b)) => a == b,408 (Val::Null, Val::Null) => true,409 (Val::Str(a), Val::Str(b)) => a == b,410 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,411 (Val::Arr(_), Val::Arr(_)) => {412 throw!("primitiveEquals operates on primitive types, got array")413 }414 (Val::Obj(_), Val::Obj(_)) => {415 throw!("primitiveEquals operates on primitive types, got object")416 }417 (a, b) if is_function_like(a) && is_function_like(b) => {418 throw!("cannot test equality of functions")419 }420 (_, _) => false,421 })422}423424425pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {426 if val_a.value_type() != val_b.value_type() {427 return Ok(false);428 }429 match (val_a, val_b) {430 (Val::Arr(a), Val::Arr(b)) => {431 if ArrValue::ptr_eq(a, b) {432 return Ok(true);433 }434 if a.len() != b.len() {435 return Ok(false);436 }437 for (a, b) in a.iter().zip(b.iter()) {438 if !equals(&a?, &b?)? {439 return Ok(false);440 }441 }442 Ok(true)443 }444 (Val::Obj(a), Val::Obj(b)) => {445 if ObjValue::ptr_eq(a, b) {446 return Ok(true);447 }448 let fields = a.fields(449 #[cfg(feature = "exp-preserve-order")]450 false,451 );452 if fields453 != b.fields(454 #[cfg(feature = "exp-preserve-order")]455 false,456 ) {457 return Ok(false);458 }459 for field in fields {460 if !equals(461 &a.get(field.clone())?.expect("field exists"),462 &b.get(field)?.expect("field exists"),463 )? {464 return Ok(false);465 }466 }467 Ok(true)468 }469 (a, b) => Ok(primitive_equals(a, b)?),470 }471}