1use std::{clone::Clone, fmt::Debug};23use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;67use crate::{Pending, Result, SupThis, Thunk, Val, analyze::LocalId, error, error::ErrorKind::*};89#[derive(Debug, Trace, Clone, Educe)]10#[educe(PartialEq)]11pub struct Context(#[educe(PartialEq(method = Cc::ptr_eq))] Cc<ContextInternal>);1213#[derive(Debug, Trace, Clone)]14struct ContextInternal {15 sup_this: Option<SupThis>,16 17 bindings: Vec<Option<Thunk<Val>>>,18 offset: u32,19 parent: Option<Context>,20}2122impl Context {23 pub fn new_future() -> Pending<Self> {24 Pending::new()25 }2627 pub fn sup_this(&self) -> Option<&SupThis> {28 self.0.sup_this.as_ref()29 }3031 pub fn try_sup_this(&self) -> Result<SupThis> {32 self.033 .sup_this34 .clone()35 .ok_or_else(|| error!(CantUseSelfSupOutsideOfObject))36 }3738 39 40 pub(crate) fn cow_fill_binding(&mut self, id: LocalId, value: Thunk<Val>) {41 let mut value = Some(Some(value));4243 self.0.update_with(|inner| {44 let local_idx = (id.0 - inner.offset) as usize;45 while inner.bindings.len() <= local_idx {46 inner.bindings.push(None);47 }48 inner.bindings[local_idx] = value.take().expect("called once");49 });50 }5152 pub fn binding(&self, id: LocalId) -> Option<Thunk<Val>> {53 let id_num = id.0;54 if id_num >= self.0.offset {55 let local_idx = (id_num - self.0.offset) as usize;56 if let Some(Some(thunk)) = self.0.bindings.get(local_idx) {57 return Some(thunk.clone());58 }59 }60 if let Some(parent) = &self.0.parent {61 return parent.binding(id);62 }63 None64 }6566 #[must_use]67 pub fn into_future(self, ctx: Pending<Self>) -> Self {68 {69 ctx.clone().fill(self);70 }71 ctx.unwrap()72 }73}7475#[derive(Clone)]76pub struct ContextBuilder {77 sup_this: Option<SupThis>,78 bindings: Vec<Option<Thunk<Val>>>,79 offset: u32,80 parent: Option<Context>,81}8283impl ContextBuilder {84 pub fn new() -> Self {85 Self {86 sup_this: None,87 bindings: Vec::new(),88 offset: 0,89 parent: None,90 }91 }9293 pub(crate) fn extend(parent: Context, capacity: usize) -> Self {94 let offset = parent.0.offset + parent.0.bindings.len() as u32;95 Self {96 sup_this: parent.0.sup_this.clone(),97 bindings: Vec::with_capacity(capacity),98 offset,99 parent: Some(parent),100 }101 }102103 pub(crate) fn bind(&mut self, id: LocalId, value: Thunk<Val>) {104 debug_assert!(105 id.0 >= self.offset,106 "cannot bind {id:?} below offset {}",107 self.offset,108 );109 let local_idx = (id.0 - self.offset) as usize;110 self.bindings.reserve(local_idx);111 while self.bindings.len() <= local_idx {112 self.bindings.push(None);113 }114 self.bindings[local_idx] = Some(value);115 }116117 pub(crate) fn build(self) -> Context {118 Context(Cc::new(ContextInternal {119 sup_this: self.sup_this,120 bindings: self.bindings,121 offset: self.offset,122 parent: self.parent,123 }))124 }125126 pub(crate) fn build_sup_this(mut self, st: SupThis) -> Context {127 self.sup_this = Some(st);128 self.build()129 }130}131132impl Default for ContextBuilder {133 fn default() -> Self {134 Self::new()135 }136}137138pub struct InitialContextBuilder {139 builder: ContextBuilder,140 externals: Vec<(IStr, LocalId)>,141 next_id: u32,142}143144impl InitialContextBuilder {145 pub(crate) fn new() -> Self {146 Self {147 builder: ContextBuilder::new(),148 externals: Vec::new(),149 next_id: 0,150 }151 }152153 pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) {154 let name = name.into();155 let id = LocalId(self.next_id);156 self.next_id += 1;157 self.externals.push((name, id));158 self.builder.bind(id, value);159 }160161 pub(crate) fn build(self) -> (ContextBuilder, Vec<(IStr, LocalId)>) {162 (self.builder, self.externals)163 }164}165166impl Default for InitialContextBuilder {167 fn default() -> Self {168 Self::new()169 }170}