1use std::cell::RefCell;
8
9pub const ZCONTEXT_HIST: u32 = 1;
11pub const ZCONTEXT_LEX: u32 = 2;
12pub const ZCONTEXT_PARSE: u32 = 4;
13
14#[derive(Clone, Default)]
16pub struct HistStack {
17 pub curhist: usize,
18 pub histsiz: usize,
19 pub savehistsiz: usize,
20}
21
22#[derive(Clone, Default)]
24pub struct LexStack {
25 pub tok: i32,
26 pub tokstr: Option<String>,
27 pub zsession: Option<String>,
28}
29
30#[derive(Clone, Default)]
32pub struct ParseStack {
33 pub ecused: usize,
34 pub ecnpats: usize,
35}
36
37#[derive(Clone, Default)]
39pub struct ContextStack {
40 pub hist_stack: HistStack,
41 pub lex_stack: LexStack,
42 pub parse_stack: ParseStack,
43}
44
45pub struct ContextManager {
47 stack: Vec<ContextStack>,
48}
49
50impl Default for ContextManager {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl ContextManager {
57 pub fn new() -> Self {
58 ContextManager { stack: Vec::new() }
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.stack.is_empty()
64 }
65
66 pub fn save_partial(
68 &mut self,
69 parts: u32,
70 hist: &HistStack,
71 lex: &LexStack,
72 parse: &ParseStack,
73 ) {
74 let mut ctx = ContextStack::default();
75
76 if (parts & ZCONTEXT_HIST) != 0 {
77 ctx.hist_stack = hist.clone();
78 }
79 if (parts & ZCONTEXT_LEX) != 0 {
80 ctx.lex_stack = lex.clone();
81 }
82 if (parts & ZCONTEXT_PARSE) != 0 {
83 ctx.parse_stack = parse.clone();
84 }
85
86 self.stack.push(ctx);
87 }
88
89 pub fn save(&mut self, hist: &HistStack, lex: &LexStack, parse: &ParseStack) {
91 self.save_partial(
92 ZCONTEXT_HIST | ZCONTEXT_LEX | ZCONTEXT_PARSE,
93 hist,
94 lex,
95 parse,
96 );
97 }
98
99 pub fn restore_partial(&mut self, parts: u32) -> Option<ContextStack> {
101 let ctx = self.stack.pop()?;
102
103 let mut result = ContextStack::default();
104 if (parts & ZCONTEXT_HIST) != 0 {
105 result.hist_stack = ctx.hist_stack;
106 }
107 if (parts & ZCONTEXT_LEX) != 0 {
108 result.lex_stack = ctx.lex_stack;
109 }
110 if (parts & ZCONTEXT_PARSE) != 0 {
111 result.parse_stack = ctx.parse_stack;
112 }
113
114 Some(result)
115 }
116
117 pub fn restore(&mut self) -> Option<ContextStack> {
119 self.restore_partial(ZCONTEXT_HIST | ZCONTEXT_LEX | ZCONTEXT_PARSE)
120 }
121
122 pub fn depth(&self) -> usize {
124 self.stack.len()
125 }
126}
127
128thread_local! {
129 static CONTEXT_STACK: RefCell<ContextManager> = RefCell::new(ContextManager::new());
130}
131
132pub fn zcontext_save(hist: &HistStack, lex: &LexStack, parse: &ParseStack) {
134 CONTEXT_STACK.with(|cs| {
135 cs.borrow_mut().save(hist, lex, parse);
136 });
137}
138
139pub fn zcontext_save_partial(parts: u32, hist: &HistStack, lex: &LexStack, parse: &ParseStack) {
141 CONTEXT_STACK.with(|cs| {
142 cs.borrow_mut().save_partial(parts, hist, lex, parse);
143 });
144}
145
146pub fn zcontext_restore() -> Option<ContextStack> {
148 CONTEXT_STACK.with(|cs| cs.borrow_mut().restore())
149}
150
151pub fn zcontext_restore_partial(parts: u32) -> Option<ContextStack> {
153 CONTEXT_STACK.with(|cs| cs.borrow_mut().restore_partial(parts))
154}
155
156pub fn zcontext_is_toplevel() -> bool {
158 CONTEXT_STACK.with(|cs| cs.borrow().is_empty())
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_context_save_restore() {
167 let mut mgr = ContextManager::new();
168
169 let hist = HistStack {
170 curhist: 100,
171 histsiz: 1000,
172 savehistsiz: 500,
173 };
174 let lex = LexStack {
175 tok: 42,
176 tokstr: Some("test".to_string()),
177 zsession: None,
178 };
179 let parse = ParseStack {
180 ecused: 10,
181 ecnpats: 5,
182 };
183
184 mgr.save(&hist, &lex, &parse);
185 assert_eq!(mgr.depth(), 1);
186
187 let restored = mgr.restore().unwrap();
188 assert_eq!(restored.hist_stack.curhist, 100);
189 assert_eq!(restored.lex_stack.tok, 42);
190 assert_eq!(restored.parse_stack.ecused, 10);
191 assert_eq!(mgr.depth(), 0);
192 }
193
194 #[test]
195 fn test_context_partial_save() {
196 let mut mgr = ContextManager::new();
197
198 let hist = HistStack {
199 curhist: 50,
200 histsiz: 500,
201 savehistsiz: 250,
202 };
203 let lex = LexStack::default();
204 let parse = ParseStack::default();
205
206 mgr.save_partial(ZCONTEXT_HIST, &hist, &lex, &parse);
207
208 let restored = mgr.restore_partial(ZCONTEXT_HIST).unwrap();
209 assert_eq!(restored.hist_stack.curhist, 50);
210 }
211
212 #[test]
213 fn test_nested_contexts() {
214 let mut mgr = ContextManager::new();
215
216 let hist1 = HistStack {
217 curhist: 1,
218 histsiz: 100,
219 savehistsiz: 50,
220 };
221 let hist2 = HistStack {
222 curhist: 2,
223 histsiz: 200,
224 savehistsiz: 100,
225 };
226 let lex = LexStack::default();
227 let parse = ParseStack::default();
228
229 mgr.save(&hist1, &lex, &parse);
230 mgr.save(&hist2, &lex, &parse);
231
232 assert_eq!(mgr.depth(), 2);
233
234 let restored2 = mgr.restore().unwrap();
235 assert_eq!(restored2.hist_stack.curhist, 2);
236
237 let restored1 = mgr.restore().unwrap();
238 assert_eq!(restored1.hist_stack.curhist, 1);
239
240 assert!(mgr.is_empty());
241 }
242}