assembler/
state.rs

1//! Represents the state of the source code parser.
2//!
3//! State information is:
4//!
5//! - Current numeric base (octal or decimal)
6//! - Currently-known macro definitions
7use std::collections::BTreeMap;
8use std::fmt::Debug;
9
10use chumsky::{inspector::Inspector, prelude::Input};
11
12use super::manuscript::MacroDefinition;
13use super::symbol::SymbolName;
14
15#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
16pub enum NumeralMode {
17    #[default]
18    Octal,
19    Decimal,
20}
21
22impl NumeralMode {
23    pub(crate) fn radix(self, alternate: bool) -> u32 {
24        match (&self, alternate) {
25            (&NumeralMode::Octal, false) | (&NumeralMode::Decimal, true) => 8,
26            (&NumeralMode::Decimal, false) | (&NumeralMode::Octal, true) => 10,
27        }
28    }
29
30    pub(crate) fn set_numeral_mode(&mut self, mode: NumeralMode) {
31        *self = mode;
32    }
33}
34
35#[test]
36fn test_numeral_mode_default() {
37    assert_eq!(NumeralMode::default(), NumeralMode::Octal);
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub(super) struct TruncatableMap<K: Eq, V: Eq> {
42    in_insertion_order: Vec<K>,
43    items: BTreeMap<K, V>,
44}
45
46impl<K: Eq, V: Eq> Default for TruncatableMap<K, V> {
47    fn default() -> Self {
48        TruncatableMap {
49            in_insertion_order: Default::default(),
50            items: BTreeMap::new(),
51        }
52    }
53}
54
55impl<K, V> TruncatableMap<K, V>
56where
57    K: Clone + Eq + Ord + Debug,
58    V: Eq,
59{
60    pub(crate) fn insert(&mut self, k: K, v: V) {
61        if self.items.insert(k.clone(), v).is_some() {
62            panic!("cannot insert duplicate entry for {k:?}");
63        } else {
64            self.in_insertion_order.push(k);
65        }
66    }
67
68    pub(crate) fn get(&self, k: &K) -> Option<&V> {
69        self.items.get(k)
70    }
71
72    pub(crate) fn len(&self) -> usize {
73        self.in_insertion_order.len()
74    }
75
76    pub(crate) fn truncate(&mut self, newlen: usize) {
77        for k in self.in_insertion_order.drain(newlen..) {
78            self.items.remove(&k);
79        }
80    }
81
82    pub(crate) fn map_ref(&self) -> &BTreeMap<K, V> {
83        &self.items
84    }
85}
86
87#[derive(Debug, PartialEq, Eq, Clone)]
88pub(crate) struct State<'src> {
89    pub(super) numeral_mode: NumeralMode,
90    pub(super) body: &'src str,
91    pub(super) macros: TruncatableMap<SymbolName, MacroDefinition>,
92}
93
94impl<'src> State<'src> {
95    pub(crate) fn new(body: &'src str, numeral_mode: NumeralMode) -> State<'src> {
96        State {
97            numeral_mode,
98            body,
99            macros: Default::default(),
100        }
101    }
102
103    pub(crate) fn define_macro(&mut self, definition: MacroDefinition) {
104        // TODO: provide a diagnostic when a macro is redefined.
105        self.macros.insert(definition.name.clone(), definition);
106    }
107
108    pub(crate) fn get_macro_definition(&self, name: &SymbolName) -> Option<&MacroDefinition> {
109        self.macros.get(name)
110    }
111
112    pub(crate) fn macros(&self) -> &BTreeMap<SymbolName, MacroDefinition> {
113        self.macros.map_ref()
114    }
115}
116
117impl<'src, I: Input<'src>> Inspector<'src, I> for State<'src> {
118    type Checkpoint = usize;
119
120    #[inline(always)]
121    fn on_token(&mut self, _: &<I as Input<'src>>::Token) {}
122
123    fn on_save<'parse>(
124        &self,
125        _cursor: &chumsky::input::Cursor<'src, 'parse, I>,
126    ) -> Self::Checkpoint {
127        self.macros.len()
128    }
129
130    fn on_rewind<'parse>(
131        &mut self,
132        marker: &chumsky::input::Checkpoint<'src, 'parse, I, Self::Checkpoint>,
133    ) {
134        self.macros.truncate(*marker.inspector());
135    }
136}