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