1use std::fmt::Debug;23use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6use rustc_hash::{FxHashMap, FxHashSet};78use crate::{9 ObjValue, Pending, Result, SupThis, Thunk, Val, bail, error::ErrorKind::*,10 gc::WithCapacityExt as _,11};12131415#[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: FxHashMap<IStr, Thunk<Val>>,2425 branch_point: Option<Context>,26}27impl Context {28 pub fn new_future() -> Pending<Self> {29 Pending::new()30 }3132 pub fn dollar(&self) -> Option<&ObjValue> {33 self.0.dollar.as_ref()34 }3536 pub fn try_dollar(&self) -> Result<ObjValue> {37 self.038 .dollar39 .clone()40 .ok_or_else(|| CantUseSelfSupOutsideOfObject.into())41 }4243 pub fn this(&self) -> Option<&ObjValue> {44 self.0.sup_this.as_ref().map(SupThis::this)45 }4647 pub fn try_this(&self) -> Result<ObjValue> {48 self.049 .sup_this50 .as_ref()51 .ok_or_else(|| CantUseSelfSupOutsideOfObject.into())52 .map(SupThis::this)53 .cloned()54 }5556 pub fn sup_this(&self) -> Option<&SupThis> {57 self.0.sup_this.as_ref()58 }5960 pub fn try_sup_this(&self) -> Result<SupThis> {61 self.062 .sup_this63 .clone()64 .ok_or_else(|| CantUseSelfSupOutsideOfObject.into())65 }6667 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {68 use std::cmp::Ordering;6970 use crate::bail;7172 if let Some(val) = self.0.bindings.get(&name).cloned() {73 return Ok(val);74 }7576 if let Some(branch_point) = &self.0.branch_point {77 return branch_point.binding(name);78 }7980 let mut heap = Vec::new();81 for k in self.0.bindings.keys() {82 let conf = strsim::jaro_winkler(k as &str, &name as &str);83 if conf < 0.8 {84 continue;85 }86 heap.push((conf, k.clone()));87 }88 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));8990 bail!(VariableIsNotDefined(91 name,92 heap.into_iter().map(|(_, k)| k).collect()93 ))94 }95 pub fn contains_binding(&self, name: IStr) -> bool {96 self.0.bindings.contains_key(&name)97 }98 #[must_use]99 pub fn into_future(self, ctx: Pending<Self>) -> Self {100 {101 ctx.clone().fill(self);102 }103 ctx.unwrap()104 }105106 #[must_use]107 pub fn branch_point(self) -> Self {108 if self.0.bindings.is_empty() {109 self110 } else {111 ContextBuilder::extend(self).build()112 }113 }114}115116#[derive(Clone)]117pub struct ContextBuilder {118 dollar: Option<ObjValue>,119 sup_this: Option<SupThis>,120 bindings: FxHashMap<IStr, Thunk<Val>>,121 filled: FxHashSet<IStr>,122 branch_point: Option<Context>,123}124125impl ContextBuilder {126 pub fn new() -> Self {127 Self {128 dollar: None,129 sup_this: None,130 bindings: FxHashMap::new(),131 filled: FxHashSet::new(),132 branch_point: None,133 }134 }135136 pub fn extend_fast(parent: Context) -> Self {137 Self {138 dollar: parent.0.dollar.clone(),139 sup_this: parent.0.sup_this.clone(),140 bindings: parent.0.bindings.clone(),141 filled: FxHashSet::new(),142 branch_point: parent.0.branch_point.clone(),143 }144 }145146 pub fn extend(parent: Context) -> Self {147 Self {148 dollar: parent.0.dollar.clone(),149 sup_this: parent.0.sup_this.clone(),150 bindings: FxHashMap::new(),151 filled: FxHashSet::new(),152 branch_point: Some(parent.clone()),153 }154 }155156 pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) {157 let _ = self.bindings.insert(name.into(), value);158 }159 160 #[must_use]161 pub fn commit(mut self) -> Self {162 self.filled.clear();163 self164 }165 pub fn try_bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) -> Result<()> {166 let name = name.into();167 if !self.filled.insert(name.clone()) {168 bail!(DuplicateLocalVar(name))169 }170 self.bind(name, value);171 Ok(())172 }173 pub fn build(self) -> Context {174 Context(Cc::new(ContextInternal {175 dollar: self.dollar,176 sup_this: self.sup_this,177 bindings: self.bindings,178 branch_point: self.branch_point,179 }))180 }181 pub fn build_sup_this(mut self, st: SupThis) -> Context {182 if self.dollar.is_none() {183 self.dollar = Some(st.this().clone());184 }185 self.sup_this = Some(st);186 self.build()187 }188}189190impl Default for ContextBuilder {191 fn default() -> Self {192 Self::new()193 }194}