texlang/
variable.rs

1//! Texlang variables API
2//!
3//! The documentation website contains
4//!   [a tutorial specifically about Texlang's variables API](https://texcraft.dev/texlang/07-variables.html).
5//! See that documentation for an overview of the API.
6//! As usual, the documentation here is meant as reference.
7
8use crate::command;
9use crate::error;
10use crate::parse::OptionalEquals;
11use crate::prelude as txl;
12use crate::token;
13use crate::traits::*;
14use crate::types;
15use crate::vm;
16use common::font;
17use std::borrow::Cow;
18use std::collections::HashMap;
19use std::fmt::Debug;
20use std::hash::{Hash, Hasher};
21use texcraft_stdext::collections::groupingmap;
22
23/// Function signature for a variable's immutable getter.
24///
25/// In Texcraft all [Variable]s are built from an immutable getter and a mutable getter.
26/// This type alias just defines the signature of immutable getters.
27/// The first argument is the state, and the second argument is the index of the variable.
28pub type RefFn<S, T> = fn(state: &S, index: Index) -> &T;
29
30/// Function signature for a variable's mutable getters.
31///
32/// In Texcraft all [Variable]s are built from an immutable getter and a mutable getter.
33/// This type alias just defines the signature of mutable getters.
34/// The first argument is the state, and the second argument is the index of the variable.
35pub type MutRefFn<S, T> = fn(state: &mut S, index: Index) -> &mut T;
36
37/// Index of a variable within an array.
38#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
39pub struct Index(pub usize);
40
41impl From<usize> for Index {
42    fn from(value: usize) -> Self {
43        Index(value)
44    }
45}
46
47/// Specification for how the index of an array variable is determined.
48///
49/// Obtaining a variable from a command involves determining the variable's [Index].
50/// This index is ultimately passed into the variable's getters to get a reference or mutable reference
51/// to the underlying Rust value.
52pub enum IndexResolver<S> {
53    /// A static index, provided in the enum variant.
54    ///
55    /// This resolver is used for commands that point to a specific entry in a array.
56    /// For example, after executing `\countdef\A 30`, the `\A` control sequence points
57    /// at the count register with index 30.
58    /// The command backing `\A` uses a static resolver with index 30.
59    Static(Index),
60    /// A dynamic index that is determined by reading the input token stream.
61    ///
62    /// For example, in `\count 4` the index of `4` is determined by parsing a number
63    /// from the input token stream.
64    Dynamic(fn(token::Token, &mut vm::ExpandedStream<S>) -> txl::Result<Index>),
65}
66
67impl<S> IndexResolver<S> {
68    /// Determine the index of a variable using the input token stream.
69    fn resolve(
70        &self,
71        token: token::Token,
72        input: &mut vm::ExpandedStream<S>,
73    ) -> txl::Result<Index> {
74        match self {
75            IndexResolver::Static(addr) => Ok(*addr),
76            // TODO: we should add support for propagation information here.
77            // We need to add to the execution stack
78            IndexResolver::Dynamic(f) => f(token, input),
79        }
80    }
81}
82
83/// A TeX variable command.
84///
85/// Variable commands are _resolved_ to obtain a [Variable].
86///
87/// A command consists of three parts.
88///
89/// The first two parts are an immutable getter (of type [RefFn])
90/// and a mutable getter (of type [MutRefFn]).
91/// Both of these getters accept a reference to the state (where the variable's value lives)
92/// and an [Index].
93/// The index can be used by the getters to return different values in memory.
94/// For example, if the getters read from an array, the index may be just the index in the array.
95/// This mechanism allows users of Texlang to write one pair of getters that can be used in many variables.
96///
97/// The third part of a command is an [IndexResolver] that is used to determine the aforementioned index
98/// of a variable at runtime.
99/// The process of resolving a command involves determining the [Index] and returning a [Variable] type,
100/// which under the hood is just the two getters and this index.
101pub struct Command<S> {
102    getters: Getters<S>,
103    index_resolver: Option<IndexResolver<S>>,
104}
105
106impl<S> Command<S> {
107    /// Create a new variable command.
108    pub fn new_singleton<T: SupportedType>(
109        ref_fn: RefFn<S, T>,
110        ref_mut_fn: MutRefFn<S, T>,
111    ) -> Command<S> {
112        SupportedType::new_command(ref_fn, ref_mut_fn, None)
113    }
114
115    /// Create a new variable command.
116    pub fn new_array<T: SupportedType>(
117        ref_fn: RefFn<S, T>,
118        ref_mut_fn: MutRefFn<S, T>,
119        index_resolver: IndexResolver<S>,
120    ) -> Command<S> {
121        SupportedType::new_command(ref_fn, ref_mut_fn, Some(index_resolver))
122    }
123
124    /// Create a new getter provider.
125    ///
126    /// A getter provider is a variable command that is not intended to be invoked directly -
127    ///     in fact, the variable command will panic the program if it is invoked.
128    /// Instead the provider is included in a VM's initial commands so that
129    ///     the VM has a reference to the getters inside the command.
130    /// If a variable with the same getters is subsequently inserted into the commands map
131    ///     (for example, by a `\countdef` type command), the VM can still serialize that
132    ///     variable using the getters provided here.
133    pub fn new_getter_provider<T: SupportedType>(
134        ref_fn: RefFn<S, T>,
135        ref_mut_fn: MutRefFn<S, T>,
136    ) -> Command<S> {
137        SupportedType::new_command(
138            ref_fn,
139            ref_mut_fn,
140            Some(IndexResolver::Dynamic(|_, _| panic!())),
141        )
142    }
143
144    /// Create a command that points to a specific element in the array referenced by this command.
145    pub(crate) fn new_array_element(&self, index: Index) -> Self {
146        Self {
147            getters: self.getters.clone(),
148            index_resolver: Some(IndexResolver::Static(index)),
149        }
150    }
151
152    /// Returns true if the variable command corresponds to arithmetic variable(s) (e.g. integer or glue).
153    ///
154    /// TODO: we should just expose the type.
155    pub fn is_arithmetic(&self) -> bool {
156        match self.getters {
157            Getters::Int(_, _)
158            | Getters::SmallInt(_, _)
159            | Getters::Dimen(_, _)
160            | Getters::Glue(_, _) => true,
161            Getters::CatCode(_, _)
162            | Getters::MathCode(_, _)
163            | Getters::TokenList(_, _)
164            | Getters::Font(_, _) => false,
165        }
166    }
167}
168
169impl<S: TexlangState> Command<S> {
170    /// Resolve the command to obtain a [Variable].
171    pub fn resolve(
172        &self,
173        token: token::Token,
174        input: &mut vm::ExpandedStream<S>,
175    ) -> txl::Result<Variable<S>> {
176        let index = match &self.index_resolver {
177            None => Index(0),
178            Some(index_resolver) => {
179                input
180                    .vm_mut()
181                    .stack_push(token, error::OperationKind::VariableIndex);
182                let err_or = index_resolver.resolve(token, input);
183                input.vm_mut().stack_pop();
184                err_or?
185            }
186        };
187        Ok(new_variable(&self.getters, index))
188    }
189
190    /// Resolve the command to obtain a [Variable] of a specific type.
191    pub fn resolve_type<T: SupportedType>(
192        &self,
193        token: token::Token,
194        input: &mut vm::ExpandedStream<S>,
195    ) -> txl::Result<Option<TypedVariable<S, T>>> {
196        let Some((ref_fn, ref_mut_fn)) = T::try_cast(self) else {
197            return Ok(None);
198        };
199        let index = match &self.index_resolver {
200            None => Index(0),
201            Some(index_resolver) => index_resolver.resolve(token, input)?,
202        };
203        Ok(Some(TypedVariable(ref_fn, ref_mut_fn, index)))
204    }
205}
206
207impl<S> Command<S> {
208    pub(crate) fn key(&self) -> CommandKey {
209        let getters_key = self.getters.key();
210        match &self.index_resolver {
211            None => CommandKey::Singleton(getters_key),
212            Some(index_resolver) => match index_resolver {
213                IndexResolver::Static(a) => CommandKey::ArrayStatic(getters_key, *a),
214                IndexResolver::Dynamic(f) => CommandKey::ArrayDynamic(getters_key, *f as usize),
215            },
216        }
217    }
218}
219
220impl<S: TexlangState> Command<S> {
221    /// Resolve the command to a variable and return the value of the variable.
222    pub fn value<'a>(
223        &self,
224        token: token::Token,
225        input: &'a mut vm::ExpandedStream<S>,
226    ) -> txl::Result<ValueRef<'a>> {
227        Ok(self.resolve(token, input)?.value(input.state()))
228    }
229
230    /// Resolve the command to a variable and set the value of the variable using the following tokens in the input stream.
231    ///
232    /// This function is used in TeX code like `\variable = 3`.
233    /// In this case `\variable` is a command which resolves to a variable without consuming any more input.
234    /// The variable is populated using the input `= 3` that follows.
235    pub(crate) fn set_value_using_input(
236        &self,
237        token: token::Token,
238        input: &mut vm::ExecutionInput<S>,
239        scope: groupingmap::Scope,
240    ) -> txl::Result<()> {
241        let variable = self.resolve(token, input.as_mut())?;
242        input
243            .vm_mut()
244            .stack_push(token, error::OperationKind::VariableAssignment);
245        let err_or = variable.set_value_using_input(input, scope);
246        input.vm_mut().stack_pop();
247        err_or
248    }
249}
250
251/// A key that uniquely identifies commands. If two commands have the same command key, they are the same command.
252#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
253pub(crate) enum CommandKey {
254    Singleton(GettersKey),
255    ArrayStatic(GettersKey, Index),
256    ArrayDynamic(GettersKey, usize),
257}
258
259impl CommandKey {
260    pub(crate) fn getter_key(&self) -> GettersKey {
261        match self {
262            CommandKey::Singleton(k) => *k,
263            CommandKey::ArrayStatic(k, _) => *k,
264            CommandKey::ArrayDynamic(k, _) => *k,
265        }
266    }
267}
268
269/// A key that uniquely identifies the getters ([RefFn] and [MutRefFn]) in a command or variable.
270#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
271pub(crate) struct GettersKey(usize, usize);
272
273/// A TeX variable of a specific Rust type `T`.
274pub struct TypedVariable<S, T>(RefFn<S, T>, MutRefFn<S, T>, Index);
275
276impl<S, T> Copy for TypedVariable<S, T> {}
277
278impl<S, T> Clone for TypedVariable<S, T> {
279    fn clone(&self) -> Self {
280        *self
281    }
282}
283
284impl<S, T> TypedVariable<S, T> {
285    /// Returns an immutable reference to the variable's value.
286    pub fn get<'a>(&self, state: &'a S) -> &'a T {
287        (self.0)(state, self.2)
288    }
289
290    fn key(&self) -> (usize, usize, Index) {
291        (self.0 as usize, self.1 as usize, self.2)
292    }
293}
294
295impl<S, T> TypedVariable<S, T>
296where
297    S: TexlangState,
298    T: SupportedType,
299{
300    /// Sets the value of the variable.
301    ///
302    /// The input and scope must be passed because of TeX's grouping semantics.
303    /// When the current group ends, any variable assignments made in the group
304    ///     are rolled back.
305    /// Thus this function generally saves the current value of the variable in the VM so that it
306    ///     can be restored later.
307    /// This is why the full input must be provided, and not just the state.
308    pub fn set(&self, input: &mut vm::ExecutionInput<S>, scope: groupingmap::Scope, value: T) {
309        let r: &mut T = (self.1)(input.state_mut(), self.2);
310        let overwritten_value = std::mem::replace(r, value);
311        // We guard the function call behind this conditional to make the current function small
312        // to make it beneficial to inline it.
313        if !input.groups().is_empty() {
314            SupportedType::update_save_stack(input, self, scope, overwritten_value);
315        } else {
316            SupportedType::recycle(input, overwritten_value);
317        }
318    }
319}
320impl<S, T> TypedVariable<S, T>
321where
322    S: TexlangState,
323    T: SupportedType,
324{
325    fn set_using_input(
326        &self,
327        input: &mut vm::ExecutionInput<S>,
328        scope: groupingmap::Scope,
329    ) -> txl::Result<()> {
330        let (_, value) = <(OptionalEquals, T)>::parse(input)?;
331        self.set(input, scope, value);
332        Ok(())
333    }
334}
335
336impl<S, T> PartialEq for TypedVariable<S, T> {
337    fn eq(&self, rhs: &TypedVariable<S, T>) -> bool {
338        self.key() == rhs.key()
339    }
340}
341
342impl<S, T> Eq for TypedVariable<S, T> {}
343
344impl<S, T> Hash for TypedVariable<S, T> {
345    fn hash<H>(&self, state: &mut H)
346    where
347        H: Hasher,
348    {
349        self.key().hash(state);
350    }
351}
352
353/// Trait satisfied by all Rust types that can be used as TeX variables.
354///
355/// It exists to make the variables API more ergonomic.
356/// For example, it is used to provide a uniform constructor [Command::new_array] for commands.
357/// The trait cannot be implemented for new types.
358pub trait SupportedType: Sized + Debug + Parsable {
359    /// Create a new command of this type with the provided reference functions and index resolver.
360    fn new_command<S>(
361        ref_fn: RefFn<S, Self>,
362        ref_mut_fn: MutRefFn<S, Self>,
363        index_resolver: Option<IndexResolver<S>>,
364    ) -> Command<S>;
365
366    /// Update the VM's save stack after a variable assignment.
367    fn update_save_stack<S>(
368        input: &mut vm::ExecutionInput<S>,
369        variable: &TypedVariable<S, Self>,
370        scope: groupingmap::Scope,
371        overwritten_value: Self,
372    ) {
373        (_, _, _, _) = (input, variable, scope, overwritten_value);
374    }
375
376    /// Recycle a value that's about to be dropped.
377    ///
378    /// This method is intended for token lists, in which case the vector is returned
379    /// to the buffers pool.
380    fn recycle<S>(input: &mut vm::ExecutionInput<S>, overwritten_value: Self) {
381        (_, _) = (input, overwritten_value)
382    }
383
384    /// Create a new typed variable of this type from the getters in the command and the provided index.
385    ///
386    /// Return `None` if the command has a different type to `Self`.
387    fn new_typed_variable<S>(command: &Command<S>, index: Index) -> Option<TypedVariable<S, Self>>;
388
389    /// Try to cast a variable command to this type.
390    fn try_cast<S>(command: &Command<S>) -> Option<(RefFn<S, Self>, MutRefFn<S, Self>)>;
391}
392
393/// This function is used to implement the [SupportedType::update_save_stack] method.
394///
395/// It's a bit janky to implement the logic as a free function, rather than, say, as
396///     a default trait implementation.
397/// The main problem is that the implementation calls into the save stack, which is an
398///     internal VM data structure.
399/// Any way that does this on the trait directly requires making some of the save stack
400///     type public, because [SupportedType] is public.
401fn update_save_stack<S, T: Clone + SupportedType, F>(
402    input: &mut vm::ExecutionInput<S>,
403    variable: &TypedVariable<S, T>,
404    scope: groupingmap::Scope,
405    overwritten_value: T,
406    map_getter: F,
407) where
408    F: Fn(&mut SaveStackElement<S>) -> &mut SaveStackMap<S, T>,
409{
410    match scope {
411        groupingmap::Scope::Global => {
412            let n = input.groups().len();
413            for _ in 0..n {
414                let group = &mut input.groups()[0];
415                if let Some(stale_value) = map_getter(group).remove(variable) {
416                    SupportedType::recycle(input, stale_value);
417                }
418            }
419        }
420        groupingmap::Scope::Local => {
421            if let Some((group, _)) = input.current_group_mut() {
422                if let Some(stale_value) = map_getter(group).save(*variable, overwritten_value) {
423                    SupportedType::recycle(input, stale_value);
424                }
425            }
426        }
427    }
428}
429
430macro_rules! supported_type_impl {
431    ( $(
432        {
433            rust_type: $rust_type: path,
434            enum_variant: $enum_variant: ident,
435            $( save_stack_field: $save_stack_field: ident, )?
436            $( recycle_fn: $recycle_fn: ident, )?
437        },
438    )+ ) => {
439
440        /// Immutable reference to the value of a variable.
441        pub enum ValueRef<'a> {
442            $(
443                $enum_variant(&'a $rust_type),
444            )+
445        }
446
447        /// TeX variable of any type.
448        ///
449        /// A variable uniquely identifies a Rust value in the state, like an `i32`.
450        /// Operations on this value (like reading or setting the value) can be done in two ways:
451        ///
452        /// 1. (Easy, less flexible) Use the methods directly on this type like [Variable::value]
453        ///    to read the value.
454        ///    These methods are really ergonomic.
455        ///    The problem with the value method specifically is that the result
456        ///    is a reference which keeps the borrow of the state alive.
457        ///    Thus, while holding onto the result of the value, you can't do anything this the
458        ///    input stream like reading an argument.
459        ///    This is especially a problem when you need to perform a different action depending on the concrete type of the variable.
460        ///
461        /// 2. (Trickier, more flexible) Match on the type's enum variants to determine the
462        ///    concrete type of the variable.
463        ///    The [TypedVariable] value obtained in this way can be used to perform operations on the value.
464        ///    The main benefit of this approach is that after matching on the type, you can still use the input
465        ///    stream to do things because there is not borrow alive.
466        ///
467        pub enum Variable<S> {
468            $(
469                $enum_variant(TypedVariable<S, $rust_type>),
470            )+
471        }
472
473        fn new_variable<S>(getters: &Getters<S>, index: Index) -> Variable<S> {
474            match getters {
475                $(
476                    Getters::$enum_variant(a, b) => Variable::$enum_variant(TypedVariable(*a, *b, index)),
477                )+
478            }
479        }
480
481        impl<S: TexlangState> Variable<S> {
482            /// Return a reference to the value of the variable.
483            pub fn value<'a>(&self, state: &'a S) -> ValueRef<'a> {
484                match self {
485                    $(
486                        Variable::$enum_variant(variable) => ValueRef::$enum_variant(variable.get(state)),
487                    )+
488                }
489            }
490
491            /// Set the value of a variable using the following tokens in the input stream.
492            fn set_value_using_input(
493                &self,
494                input: &mut vm::ExecutionInput<S>,
495                scope: groupingmap::Scope,
496            ) -> txl::Result<()> {
497                match self {
498                    $(
499                        Variable::$enum_variant(variable) => variable.set_using_input(input, scope),
500                    )+
501                }
502            }
503        }
504
505        enum Getters<S> {
506            $(
507                $enum_variant(RefFn<S, $rust_type>, MutRefFn<S, $rust_type>),
508            )+
509        }
510
511        impl<S> Clone for Getters<S> {
512            fn clone(&self) -> Self {
513                match self {
514                    $(
515                        Self::$enum_variant(a, b) => Self::$enum_variant(*a, *b),
516                    )+
517                }
518            }
519        }
520
521        impl<S> Getters<S> {
522            fn key(&self) -> GettersKey {
523                match self {
524                    $(
525                        Getters::$enum_variant(a, b) => GettersKey(*a as usize, *b as usize),
526                    )+
527                }
528            }
529        }
530
531        $(
532        impl SupportedType for $rust_type {
533            fn new_command<S>(
534                ref_fn: RefFn<S, Self>,
535                ref_mut_fn: MutRefFn<S, Self>,
536                index_resolver: Option<IndexResolver<S>>,
537            ) -> Command<S> {
538                Command {
539                    getters: Getters::$enum_variant(ref_fn, ref_mut_fn),
540                    index_resolver,
541                }
542            }
543            $(
544            fn update_save_stack<S>(
545                input: &mut vm::ExecutionInput<S>,
546                variable: &TypedVariable<S, Self>,
547                scope: groupingmap::Scope,
548                overwritten_value: Self,
549            ) {
550                update_save_stack(input, variable, scope, overwritten_value, |element| {
551                    &mut element.$save_stack_field
552                })
553            }
554            )?
555            $(
556            fn recycle<S>(input: &mut vm::ExecutionInput<S>, overwritten_value: Self) {
557                $recycle_fn(input, overwritten_value)
558            }
559            )?
560            fn new_typed_variable<S>(
561                command: &Command<S>,
562                index: Index,
563            ) -> Option<TypedVariable<S, Self>> {
564                match command.getters {
565                    Getters::$enum_variant(a, b) => Some(TypedVariable(a, b, index)),
566                    _ => None,
567                }
568            }
569            fn try_cast<S>(command: &Command<S>) -> Option<(RefFn<S, Self>, MutRefFn<S, Self>)> {
570                match command.getters {
571                    Getters::$enum_variant(a, b) => Some((a,b)),
572                    _ => None,
573                }
574            }
575        }
576        )+
577
578        /// Internal VM data structure used to implement TeX's grouping semantics.
579        pub(crate) struct SaveStackElement<S> {
580            $( $(
581                $save_stack_field: SaveStackMap<S, $rust_type>,
582            )? )+
583        }
584
585        impl<S> Default for SaveStackElement<S> {
586            fn default() -> Self {
587                Self {
588                    $( $(
589                        $save_stack_field: Default::default(),
590                    )? )+
591                }
592            }
593        }
594
595        impl<S> SaveStackElement<S> {
596            pub(crate) fn restore(self, input: &mut vm::ExecutionInput<S>) {
597                $( $(
598                    self.$save_stack_field.restore(input);
599                )? )+
600            }
601
602            pub(crate) fn serializable<'a>(
603                &'a self,
604                built_ins: &HashMap<GettersKey, token::CsName>,
605            ) -> SerializableSaveStackElement<'a> {
606                SerializableSaveStackElement {
607                    $( $(
608                        $save_stack_field: self.$save_stack_field.serializable(built_ins),
609                    )? )+
610                }
611            }
612        }
613
614        #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
615        pub(crate) struct SerializableSaveStackElement<'a> {
616            $( $(
617                $save_stack_field: Vec<(token::CsName, usize, Cow<'a, $rust_type>)>,
618            )? )+
619        }
620
621        impl<'a> SerializableSaveStackElement<'a> {
622            pub(crate) fn finish_deserialization<S>(
623                self,
624                built_ins: &HashMap<token::CsName, command::BuiltIn<S>>,
625            ) -> SaveStackElement<S> {
626                SaveStackElement {
627                    $( $(
628                        $save_stack_field: SaveStackMap::from_deserialized(self.$save_stack_field, built_ins),
629                    )? )+
630                }
631            }
632        }
633    };
634}
635
636supported_type_impl!(
637    {
638        rust_type: i32,
639        enum_variant: Int,
640        save_stack_field: i32,
641    },
642    {
643        rust_type: u8,
644        enum_variant: SmallInt,
645        save_stack_field: u8,
646    },
647    {
648        rust_type: common::Scaled,
649        enum_variant: Dimen,
650        save_stack_field: dimen,
651    },
652    {
653        rust_type: common::Glue,
654        enum_variant: Glue,
655        save_stack_field: glue,
656    },
657    {
658        rust_type: types::CatCode,
659        enum_variant: CatCode,
660        save_stack_field: catcode,
661    },
662    {
663        rust_type: types::MathCode,
664        enum_variant: MathCode,
665        save_stack_field: math_code,
666    },
667    {
668        rust_type: Vec<token::Token>,
669        enum_variant: TokenList,
670        save_stack_field: token_list,
671        recycle_fn: recycle_token_list,
672    },
673    {
674        rust_type: font::Id,
675        enum_variant: Font,
676        save_stack_field: font,
677    },
678    // {
679    //    rust_type: FontParam,
680    //    enum_variant: FontParam,
681    //    // no save_stack_field because font params are global
682    // }
683);
684
685fn recycle_token_list<S>(input: &mut vm::ExecutionInput<S>, overwritten_value: Vec<token::Token>) {
686    input.return_token_buffer(overwritten_value);
687}
688
689/// Internal VM data structure used to implement TeX's grouping semantics.
690struct SaveStackMap<S, T>(HashMap<TypedVariable<S, T>, T>);
691
692impl<S, T> Default for SaveStackMap<S, T> {
693    fn default() -> Self {
694        Self(HashMap::new())
695    }
696}
697
698impl<S, T: Clone + SupportedType> SaveStackMap<S, T> {
699    fn save(&mut self, variable: TypedVariable<S, T>, value: T) -> Option<T> {
700        match self.0.entry(variable) {
701            std::collections::hash_map::Entry::Occupied(_) => Some(value),
702            std::collections::hash_map::Entry::Vacant(v) => {
703                v.insert(value);
704                None
705            }
706        }
707    }
708
709    fn remove(&mut self, variable: &TypedVariable<S, T>) -> Option<T> {
710        self.0.remove(variable)
711    }
712
713    fn restore(self, input: &mut vm::ExecutionInput<S>) {
714        for (v, restored_value) in self.0 {
715            let dest = (v.1)(input.state_mut(), v.2);
716            let overwritten_value = std::mem::replace(dest, restored_value);
717            SupportedType::recycle(input, overwritten_value);
718        }
719    }
720
721    fn serializable<'a>(
722        &'a self,
723        built_ins: &HashMap<GettersKey, token::CsName>,
724    ) -> Vec<(token::CsName, usize, Cow<'a, T>)> {
725        self.0
726            .iter()
727            .map(|(typed_variable, value): (&TypedVariable<S, T>, &T)| {
728                let key = GettersKey(typed_variable.0 as usize, typed_variable.1 as usize);
729                let cs_name = built_ins.get(&key).unwrap();
730                (*cs_name, typed_variable.2 .0, Cow::Borrowed(value))
731            })
732            .collect()
733    }
734}
735
736impl<S, T: SupportedType + Clone> SaveStackMap<S, T> {
737    fn from_deserialized<'a>(
738        deserialized: Vec<(token::CsName, usize, Cow<'a, T>)>,
739        built_ins: &HashMap<token::CsName, command::BuiltIn<S>>,
740    ) -> Self {
741        let m = deserialized
742            .into_iter()
743            .map(
744                |(cs_name, index, value): (token::CsName, usize, Cow<'a, T>)| {
745                    // TODO: surface error here
746                    let built_in = built_ins.get(&cs_name).unwrap();
747                    let typed_variable = match built_in.cmd() {
748                        command::Command::Variable(variable_command) => {
749                            // TODO: error instead of unwrap
750                            SupportedType::new_typed_variable(variable_command, Index(index))
751                                .unwrap()
752                        }
753                        _ => panic!("wrong type of built in TODO return an error here"),
754                    };
755                    (typed_variable, value.into_owned())
756                },
757            )
758            .collect();
759        Self(m)
760    }
761}