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