git.delta.rocks / jrsonnet / refs/commits / 1925b3a76ba9

difftreelog

source

crates/jrsonnet-evaluator/src/ctx.rs4.5 KiBsourcehistory
1use std::fmt::Debug;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;56use crate::{7	error::Error::*, gc::GcHashMap, map::LayeredHashMap, ObjValue, Pending, Result, State, Thunk,8	Val,9};1011#[derive(Trace)]12struct ContextInternals {13	state: Option<State>,14	dollar: Option<ObjValue>,15	sup: Option<ObjValue>,16	this: Option<ObjValue>,17	bindings: LayeredHashMap,18}19impl Debug for ContextInternals {20	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {21		f.debug_struct("Context").finish()22	}23}2425/// Context keeps information about current lexical code location26///27/// This information includes local variables, top-level object (`$`), current object (`this`), and super object (`super`)28#[derive(Debug, Clone, Trace)]29pub struct Context(Cc<ContextInternals>);30impl Context {31	pub fn new_future() -> Pending<Self> {32		Pending::new()33	}3435	pub fn state(&self) -> &State {36		self.037			.state38			.as_ref()39			.expect("used state from dummy context")40	}4142	pub fn dollar(&self) -> &Option<ObjValue> {43		&self.0.dollar44	}4546	pub fn this(&self) -> &Option<ObjValue> {47		&self.0.this48	}4950	pub fn super_obj(&self) -> &Option<ObjValue> {51		&self.0.sup52	}5354	#[cfg(not(feature = "friendly-errors"))]55	pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {56		Ok(self57			.058			.bindings59			.get(&name)60			.cloned()61			.ok_or(VariableIsNotDefined(name, vec![]))?)62	}6364	#[cfg(feature = "friendly-errors")]65	pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {66		use std::cmp::Ordering;6768		use crate::throw;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		throw!(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.0.borrow_mut().replace(self);96		}97		ctx.unwrap()98	}99100	#[must_use]101	pub fn with_var(self, name: IStr, value: Val) -> Self {102		let mut new_bindings = GcHashMap::with_capacity(1);103		new_bindings.insert(name, Thunk::evaluated(value));104		self.extend(new_bindings, None, None, None)105	}106107	#[must_use]108	pub fn extend(109		self,110		new_bindings: GcHashMap<IStr, Thunk<Val>>,111		new_dollar: Option<ObjValue>,112		new_sup: Option<ObjValue>,113		new_this: Option<ObjValue>,114	) -> Self {115		let ctx = &self.0;116		let dollar = new_dollar.or_else(|| ctx.dollar.clone());117		let this = new_this.or_else(|| ctx.this.clone());118		let sup = new_sup.or_else(|| ctx.sup.clone());119		let bindings = if new_bindings.is_empty() {120			ctx.bindings.clone()121		} else {122			ctx.bindings.clone().extend(new_bindings)123		};124		Self(Cc::new(ContextInternals {125			state: ctx.state.clone(),126			dollar,127			sup,128			this,129			bindings,130		}))131	}132}133134// impl Default for Context {135// 	fn default() -> Self {136// 		Self::new()137// 	}138// }139140impl PartialEq for Context {141	fn eq(&self, other: &Self) -> bool {142		Cc::ptr_eq(&self.0, &other.0)143	}144}145146pub struct ContextBuilder {147	state: Option<State>,148	bindings: GcHashMap<IStr, Thunk<Val>>,149	extend: Option<Context>,150}151152impl ContextBuilder {153	/// # Panics154	/// Panics aren't directly caused by this function, but if state from resulting context is used155	pub fn dangerous_empty_state() -> Self {156		Self {157			state: None,158			bindings: GcHashMap::new(),159			extend: None,160		}161	}162	pub fn new(state: State) -> Self {163		Self::with_capacity(state, 0)164	}165	pub fn with_capacity(state: State, capacity: usize) -> Self {166		Self {167			state: Some(state),168			bindings: GcHashMap::with_capacity(capacity),169			extend: None,170		}171	}172	pub fn extend(parent: Context) -> Self {173		Self {174			state: parent.0.state.clone(),175			bindings: GcHashMap::new(),176			extend: Some(parent),177		}178	}179	/// # Panics180	/// If `name` is already bound181	pub fn bind(&mut self, name: IStr, value: Thunk<Val>) -> &mut Self {182		let old = self.bindings.insert(name, value);183		assert!(old.is_none(), "variable bound twice in single context call");184		self185	}186	pub fn build(self) -> Context {187		if let Some(parent) = self.extend {188			// TODO: replace self.extend with Result<Context, State>, and remove `state` field189			parent.extend(self.bindings, None, None, None)190		} else {191			Context(Cc::new(ContextInternals {192				state: self.state,193				bindings: LayeredHashMap::new(self.bindings),194				dollar: None,195				sup: None,196				this: None,197			}))198		}199	}200}