git.delta.rocks / jrsonnet / refs/commits / b3f009bb7b2e

difftreelog

source

crates/jrsonnet-evaluator/src/ctx.rs4.5 KiBsourcehistory
1use std::fmt::Debug;23use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6use rustc_hash::FxHashMap;78use crate::{9	error::ErrorKind::*, gc::WithCapacityExt as _, map::LayeredHashMap, ObjValue, Pending, Result,10	SupThis, Thunk, Val,11};12/// Context keeps information about current lexical code location13///14/// This information includes local variables, top-level object (`$`), current object (`this`), and super object (`super`)15#[derive(Debug, Trace, Clone, Educe)]16#[educe(PartialEq)]17pub struct Context(#[educe(PartialEq(method = Cc::ptr_eq))] Cc<ContextInternal>);1819#[derive(Debug, Trace)]20struct ContextInternal {21	dollar: Option<ObjValue>,22	sup_this: Option<SupThis>,23	bindings: LayeredHashMap,24}25impl Context {26	pub fn new_future() -> Pending<Self> {27		Pending::new()28	}2930	pub fn dollar(&self) -> Option<&ObjValue> {31		self.0.dollar.as_ref()32	}3334	pub fn try_dollar(&self) -> Result<ObjValue> {35		self.036			.dollar37			.clone()38			.ok_or_else(|| CantUseSelfSupOutsideOfObject.into())39	}4041	pub fn this(&self) -> Option<&ObjValue> {42		self.0.sup_this.as_ref().map(SupThis::this)43	}4445	pub fn try_this(&self) -> Result<ObjValue> {46		self.047			.sup_this48			.as_ref()49			.ok_or_else(|| CantUseSelfSupOutsideOfObject.into())50			.map(SupThis::this)51			.cloned()52	}5354	pub fn sup_this(&self) -> Option<&SupThis> {55		self.0.sup_this.as_ref()56	}5758	pub fn try_sup_this(&self) -> Result<SupThis> {59		self.060			.sup_this61			.clone()62			.ok_or_else(|| CantUseSelfSupOutsideOfObject.into())63	}6465	pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {66		use std::cmp::Ordering;6768		use crate::bail;6970		if let Some(val) = self.0.bindings.get(&name).cloned() {71			return Ok(val);72		}7374		let mut heap = Vec::new();75		self.0.bindings.clone().iter_keys(|k| {76			let conf = strsim::jaro_winkler(&k as &str, &name as &str);77			if conf < 0.8 {78				return;79			}80			heap.push((conf, k));81		});82		heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));8384		bail!(VariableIsNotDefined(85			name,86			heap.into_iter().map(|(_, k)| k).collect()87		))88	}89	pub fn contains_binding(&self, name: IStr) -> bool {90		self.0.bindings.contains_key(&name)91	}92	#[must_use]93	pub fn into_future(self, ctx: Pending<Self>) -> Self {94		{95			ctx.clone().fill(self);96		}97		ctx.unwrap()98	}99100	#[must_use]101	pub fn with_var(self, name: impl Into<IStr>, value: Val) -> Self {102		let mut new_bindings = FxHashMap::with_capacity(1);103		new_bindings.insert(name.into(), Thunk::evaluated(value));104		self.extend_bindings(new_bindings)105	}106107	#[must_use]108	pub fn extend_bindings_sup_this(109		self,110		new_bindings: FxHashMap<IStr, Thunk<Val>>,111		sup_this: SupThis,112	) -> Self {113		let ctx = &self;114		let dollar = ctx115			.0116			.dollar117			.clone()118			.or_else(|| Some(sup_this.this().clone()));119		let bindings = if new_bindings.is_empty() {120			ctx.0.bindings.clone()121		} else {122			ctx.0.bindings.clone().extend(new_bindings)123		};124		Self(Cc::new(ContextInternal {125			dollar,126			sup_this: Some(sup_this),127			bindings,128		}))129	}130	#[must_use]131	pub fn extend_bindings(self, new_bindings: FxHashMap<IStr, Thunk<Val>>) -> Self {132		if new_bindings.is_empty() {133			return self;134		}135		let ctx = &self;136		let bindings = if new_bindings.is_empty() {137			ctx.0.bindings.clone()138		} else {139			ctx.0.bindings.clone().extend(new_bindings)140		};141		Self(Cc::new(ContextInternal {142			dollar: ctx.0.dollar.clone(),143			sup_this: ctx.0.sup_this.clone(),144			bindings,145		}))146	}147}148149#[derive(Default)]150pub struct ContextBuilder {151	bindings: FxHashMap<IStr, Thunk<Val>>,152	extend: Option<Context>,153}154155impl ContextBuilder {156	pub fn new() -> Self {157		Self::with_capacity(0)158	}159160	pub fn with_capacity(capacity: usize) -> Self {161		Self {162			bindings: FxHashMap::with_capacity(capacity),163			extend: None,164		}165	}166167	pub fn extend(parent: Context) -> Self {168		Self {169			bindings: FxHashMap::new(),170			extend: Some(parent),171		}172	}173174	/// # Panics175	///176	/// If `name` is already bound. Makes no sense to bind same local multiple times,177	/// unless it is separate context layers.178	pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) -> &mut Self {179		let old = self.bindings.insert(name.into(), value);180		assert!(old.is_none(), "variable bound twice in single context call");181		self182	}183	pub fn binds(&mut self, bindings: FxHashMap<IStr, Thunk<Val>>) -> &mut Self {184		for (k, v) in bindings {185			self.bind(k, v);186		}187		self188	}189	pub fn build(self) -> Context {190		if let Some(parent) = self.extend {191			parent.extend_bindings(self.bindings)192		} else {193			Context(Cc::new(ContextInternal {194				bindings: LayeredHashMap::new(self.bindings),195				dollar: None,196				sup_this: None,197			}))198		}199	}200}