rant/runtime/
stack.rs

1use std::{rc::Rc};
2use fnv::{FnvBuildHasher};
3use quickscope::ScopeMap;
4use crate::{lang::{Sequence, Expression}, RantValue, Rant};
5use crate::runtime::*;
6use super::{output::OutputWriter};
7
8type CallStackVector<I> = SmallVec<[StackFrame<I>; super::CALL_STACK_INLINE_COUNT]>;
9
10/// Represents a call stack and its associated locals.
11pub struct CallStack<I> {
12  frames: CallStackVector<I>,
13  locals: ScopeMap<InternalString, RantVar, FnvBuildHasher>,
14}
15
16impl<I> Default for CallStack<I> {
17  #[inline]
18  fn default() -> Self {
19    Self::new()
20  }
21}
22
23impl<I> CallStack<I> {
24  #[inline]
25  pub(crate) fn new() -> Self {
26    Self {
27      frames: Default::default(),
28      locals: Default::default(),
29    }
30  }
31
32  /// Returns `true` if the stack is empty.
33  #[inline]
34  pub fn is_empty(&self) -> bool {
35    self.frames.is_empty()
36  }
37
38  /// Gets the number of frames in the stack.
39  #[inline]
40  pub fn len(&self) -> usize {
41    self.frames.len()
42  }
43
44  /// Removes the topmost frame from the stack and returns it.
45  #[inline]
46  pub fn pop_frame(&mut self) -> Option<StackFrame<I>> {
47    if let Some(frame) = self.frames.pop() {
48      if frame.has_scope {
49        self.locals.pop_layer();
50      }
51      return Some(frame)
52    }
53    None
54  }
55
56  /// Adds a frame to the top of the stack.
57  #[inline]
58  pub fn push_frame(&mut self, frame: StackFrame<I>) {
59    if frame.has_scope {
60      self.locals.push_layer();
61    }
62    self.frames.push(frame);
63  }
64
65  /// Returns a mutable reference to the topmost frame in the stack.
66  #[inline]
67  pub fn top_mut(&mut self) -> Option<&mut StackFrame<I>> {
68    self.frames.last_mut()
69  }
70
71
72  /// Returns a mutable reference to the frame `depth` frames below the top of the stack.
73  #[inline]
74  pub fn parent_mut(&mut self, depth: usize) -> Option<&mut StackFrame<I>> {
75    self.frames.iter_mut().rev().nth(depth)
76  }
77
78  /// Returns a reference to the frame `depth` frames below the top of the stack.
79  #[inline]
80  pub fn parent(&self, depth: usize) -> Option<&StackFrame<I>> {
81    self.frames.iter().rev().nth(depth)
82  }
83
84  /// Returna reference to the topmost frame in the stack.
85  #[inline]
86  pub fn top(&self) -> Option<&StackFrame<I>> {
87    self.frames.last()
88  }
89
90  /// Generates a stack trace string from the current state of the stack.
91  pub fn gen_stack_trace(&self) -> String {
92    let mut trace = String::new();
93    let mut last_frame_info: Option<(String, usize)> = None;
94    for frame in self.frames.iter().rev() {
95      let current_frame_string = frame.to_string();
96
97      if let Some((last_frame_string, count)) = last_frame_info.take() {
98        if current_frame_string == last_frame_string {
99          last_frame_info = Some((last_frame_string, count + 1));
100        } else {
101          // spit out last repeated frame
102          match count {
103            1 => trace.push_str(&format!("-> {}\n", last_frame_string)),
104            _ => trace.push_str(&format!("-> {} ({} frames)\n", last_frame_string, count)),
105          }
106          last_frame_info = Some((current_frame_string, 1));
107        }
108      } else {
109        last_frame_info = Some((current_frame_string, 1));
110      }
111    }
112
113    // emit bottom frame
114    if let Some((last_frame_string, count)) = last_frame_info.take() {
115      match count {
116        1 => trace.push_str(&format!("-> {}", last_frame_string)),
117        _ => trace.push_str(&format!("-> {} ({} frames)", last_frame_string, count)),
118      }
119    }
120
121    trace
122  }
123
124  /// Sets a variable's value using the specified access type.
125  #[inline]
126  pub fn set_var_value(&mut self, context: &mut Rant, id: &str, access: VarAccessMode, val: RantValue) -> RuntimeResult<()> {
127    match access {
128      VarAccessMode::Local => {
129        if let Some(var) = self.locals.get_mut(id) {
130          if !var.write(val) {
131            runtime_error!(RuntimeErrorType::InvalidAccess, "cannot reassign local constant '{}'", id);
132          }
133          return Ok(())
134        }
135      },
136      VarAccessMode::Descope(n) => {
137        if let Some(var) = self.locals.get_parent_mut(id, n) {
138          if !var.write(val) {
139            runtime_error!(RuntimeErrorType::InvalidAccess, "cannot reassign local constant '{}'", id);
140          }
141          return Ok(())
142        }
143      },
144      // Skip locals completely if it's a global accessor
145      VarAccessMode::ExplicitGlobal => {}
146    }
147
148    // Check globals
149    if context.has_global(id) {
150      if !context.set_global(id, val) {
151        runtime_error!(RuntimeErrorType::InvalidAccess, "cannot reassign global constant '{}'", id);
152      }
153      return Ok(())
154    }
155
156    runtime_error!(RuntimeErrorType::InvalidAccess, "variable '{}' not found", id);
157  }
158
159  /// Gets a variable's value using the specified access type.
160  #[inline]
161  pub fn get_var_value(&self, context: &Rant, id: &str, access: VarAccessMode, prefer_function: bool) -> RuntimeResult<RantValue> {
162
163    macro_rules! percolating_func_lookup {
164      ($value_iter:expr) => {
165        if let Some(mut vars) = $value_iter {
166          if let Some(mut var) = vars.next() {
167            // If the topmost value isn't callable, check the whole pile and then globals for something that is
168            if !var.value_ref().is_callable() {
169              if let Some(func_var) = vars
170              .find(|v| v.value_ref().is_callable())
171              .or_else(|| context.get_global_var(id).filter(|v| v.value_ref().is_callable())) 
172              {
173                var = func_var;
174              }
175            }
176            return Ok(var.value_cloned())
177          }
178        }
179      }
180    }
181
182    match access {
183      VarAccessMode::Local => {
184        // If the caller requested a function, perform function percolation
185        if prefer_function {
186          percolating_func_lookup!(self.locals.get_all(id));
187        } else if let Some(var) = self.locals.get(id) {
188          return Ok(var.value_cloned())
189        }
190      },
191      VarAccessMode::Descope(n) => {
192        if prefer_function {
193          percolating_func_lookup!(self.locals.get_parents(id, n));
194        } else if let Some(var) = self.locals.get_parent(id, n) {
195          return Ok(var.value_cloned())
196        }
197      },
198      VarAccessMode::ExplicitGlobal => {},
199    }    
200
201    // Check globals
202    if let Some(val) = context.get_global(id) {
203      return Ok(val)
204    }
205
206    Err(RuntimeError {
207      error_type: RuntimeErrorType::InvalidAccess,
208      description: Some(format!("{} '{}' not found", if prefer_function { "function" } else { "variable" }, id)),
209      stack_trace: None,
210    })
211  }
212
213  /// Gets a mutable reference to a variable.
214  pub fn get_var_mut<'a>(&'a mut self, context: &'a mut Rant, id: &str, access: VarAccessMode) -> RuntimeResult<&'a mut RantVar> {
215    match access {
216      VarAccessMode::Local => {
217        if let Some(var) = self.locals.get_mut(id) {
218          return Ok(var)
219        }
220      },
221      VarAccessMode::Descope(n) => {
222        if let Some(var) = self.locals.get_parent_mut(id, n) {
223          return Ok(var)
224        }
225      },
226      VarAccessMode::ExplicitGlobal => {},
227    }    
228
229    // Check globals
230    if let Some(var) = context.get_global_var_mut(id) {
231      return Ok(var)
232    }
233
234    Err(RuntimeError {
235      error_type: RuntimeErrorType::InvalidAccess,
236      description: Some(format!("variable '{}' not found", id)),
237      stack_trace: None,
238    })
239  }
240
241  /// Defines a local variable of the specified name.
242  ///
243  /// ## Notes
244  ///
245  /// This function does not perform any identifier validation.
246  pub fn def_local_var(&mut self, id: &str, var: RantVar) -> RuntimeResult<()> {
247    self.locals.define(InternalString::from(id), var);
248    Ok(())
249  }
250
251  /// Defines a variable of the specified name by-value.
252  ///
253  /// ## Notes
254  ///
255  /// This function does not perform any identifier validation.
256  #[inline]
257  pub fn def_var_value(&mut self, context: &mut Rant, id: &str, access: VarAccessMode, val: RantValue, is_const: bool) -> RuntimeResult<()> {
258    // In CLI mode, skip the program's root variable scope when defining variables.
259    #[cfg(feature = "cli")]
260    let access = match access {
261      VarAccessMode::Local if self.locals.depth() <= 2 => VarAccessMode::ExplicitGlobal,
262      VarAccessMode::Descope(n) if self.locals.depth() - n <= 2 => VarAccessMode::ExplicitGlobal,
263      other => other,
264    };
265
266    match access {
267      VarAccessMode::Local => {
268        // Don't allow redefs of local constants
269        if let Some(v) = self.locals.get(id) {
270          if v.is_const() && self.locals.depth_of(id) == Some(0) {
271            runtime_error!(RuntimeErrorType::InvalidAccess, "attempted to redefine local constant '{}'", id);
272          }
273        }
274
275        let variable = if is_const { RantVar::ByValConst(val) } else { RantVar::ByVal(val) };
276        self.locals.define(InternalString::from(id), variable);
277        return Ok(())
278      },
279      VarAccessMode::Descope(descope_count) => {
280        // Don't allow redefs of parent constants
281        if let Some((v, vd)) = self.locals.get_parent_depth(id, descope_count) {
282          if v.is_const() && vd == descope_count {
283            runtime_error!(RuntimeErrorType::InvalidAccess, "attempted to redefine parent constant '{}'", id);
284          }
285        }
286
287        let variable = if is_const { RantVar::ByValConst(val) } else { RantVar::ByVal(val) };
288        self.locals.define_parent(InternalString::from(id), variable, descope_count);
289        return Ok(())
290      },
291      VarAccessMode::ExplicitGlobal => {}
292    }
293    
294    // Don't allow redefs of global constants
295    if !(if is_const { context.set_global_const(id, val) } else { context.set_global(id, val) }) {
296      runtime_error!(RuntimeErrorType::InvalidAccess, "attempted to redefine global constant '{}'", id);
297    }
298
299    Ok(())
300  }
301
302  /// Scans ("tastes") the stack from the top looking for the first occurrence of the specified frame flavor.
303  /// Returns the top-relative index of the first occurrence, or `None` if no match was found or a stronger flavor was found first.
304  #[inline]
305  pub fn taste_for_first(&self, target_flavor: StackFrameFlavor) -> Option<usize> {
306    for (frame_index, frame) in self.frames.iter().rev().enumerate() {
307      if frame.flavor > target_flavor {
308        return None
309      } else if frame.flavor == target_flavor {
310        return Some(frame_index)
311      }
312    }
313    None
314  }
315
316  /// Scans ("tastes") the stack from the top looking for the first occurrence of the specified frame flavor.
317  /// Returns the top-relative index of the first occurrence, or `None` if no match was found or another flavor was found first.
318  #[inline]
319  pub fn taste_for(&self, target_flavor: StackFrameFlavor) -> Option<usize> {
320    for (frame_index, frame) in self.frames.iter().rev().enumerate() {
321      if frame.flavor == target_flavor {
322        return Some(frame_index)
323      }
324    }
325    None
326  }
327}
328
329/// Represents a call stack frame.
330pub struct StackFrame<I> {
331  /// Node sequence being executed by the frame
332  sequence: Option<Rc<Sequence>>,
333  /// Does this stack frame have a variable scope?
334  has_scope: bool,
335  /// Program Counter (as index in sequence) for the current frame
336  pc: usize,
337  /// Output for the frame
338  output: OutputWriter,
339  /// Intent queue for the frame
340  intents: Vec<I>,
341  /// Debug cursor (line/col)
342  debug_cursor: (usize, usize),
343  /// Origin of sequence
344  origin: Rc<RantProgramInfo>,
345  /// A usage hint provided by the program element that created the frame.
346  flavor: StackFrameFlavor,
347}
348
349impl<I> StackFrame<I> {
350  #[inline]
351  pub(crate) fn new(sequence: Rc<Sequence>, prev_output: Option<&OutputWriter>) -> Self {
352    Self {
353      origin: Rc::clone(&sequence.origin),
354      sequence: Some(sequence),
355      output: OutputWriter::new(prev_output),
356      has_scope: true,
357      pc: 0,
358      intents: Default::default(),
359      debug_cursor: (0, 0),
360      flavor: Default::default(),
361    }
362  }
363
364  #[inline]
365  pub(crate) fn with_extended_config(
366    sequence: Option<Rc<Sequence>>,
367    prev_output: Option<&OutputWriter>, 
368    origin: Rc<RantProgramInfo>, 
369    has_scope: bool,
370    debug_pos: (usize, usize),
371    flavor: StackFrameFlavor
372  ) -> Self 
373  {
374    Self {
375      origin,
376      sequence,
377      output: OutputWriter::new(prev_output),
378      has_scope,
379      pc: 0,
380      intents: Default::default(),
381      debug_cursor: debug_pos,
382      flavor,
383    }
384  }
385
386  #[inline]
387  pub(crate) fn without_scope(self) -> Self {
388    let mut frame = self;
389    frame.has_scope = false;
390    frame
391  }
392
393  #[inline]
394  pub(crate) fn has_scope(self, has_scope: bool) -> Self {
395    let mut frame = self;
396    frame.has_scope = has_scope;
397    frame
398  }
399
400  #[inline(always)]
401  pub(crate) fn with_flavor(self, flavor: StackFrameFlavor) -> Self {
402    let mut frame = self;
403    frame.flavor = flavor;
404    frame
405  }
406}
407
408impl<I> StackFrame<I> {
409  #[inline]
410  pub(crate) fn seq_next(&mut self) -> Option<Rc<Expression>> {
411    if self.is_done() {
412      return None
413    }
414    
415    let next = self.sequence.as_ref().and_then(|seq| seq.get(self.pc).map(Rc::clone));
416    
417    // Increment PC
418    self.pc += 1;
419    
420    next
421  }
422  
423  /// Gets the Program Counter (PC) for the frame.
424  #[inline(always)]
425  pub fn pc(&self) -> usize {
426    self.pc
427  }
428
429  /// Gets the flavor of the frame.
430  #[inline(always)]
431  pub fn flavor(&self) -> StackFrameFlavor {
432    self.flavor
433  }
434
435  #[inline(always)]
436  pub fn output(&self) -> &OutputWriter {
437    &self.output
438  }
439
440  #[inline(always)]
441  pub fn output_mut(&mut self) -> &mut OutputWriter {
442    &mut self.output
443  }
444
445  #[inline]
446  pub fn render_and_reset_output(&mut self) -> RantValue {
447    let mut other = OutputWriter::new(Some(&self.output));
448    std::mem::swap(&mut self.output, &mut other);
449    other.render_value()
450  }
451
452  #[inline(always)]
453  pub fn origin(&self) -> &Rc<RantProgramInfo> {
454    &self.origin
455  }
456
457  #[inline(always)]
458  pub fn debug_cursor(&self) -> (usize, usize) {
459    self.debug_cursor
460  }
461
462  #[inline]
463  pub fn origin_name(&self) -> &str {
464    self.origin.path
465      .as_deref()
466      .unwrap_or_else(|| 
467        self.origin.name
468          .as_deref()
469          .unwrap_or(DEFAULT_PROGRAM_NAME)
470      )
471  }
472
473  /// Pushes an intent to the top of the intent stack.
474  #[inline(always)]
475  pub fn push_intent(&mut self, intent: I) {
476    self.intents.push(intent);
477  }
478
479  /// Removes the topmost intent from the intent stack and returns it.
480  #[inline(always)]
481  pub(crate) fn pop_intent(&mut self) -> Option<I> {
482    self.intents.pop()
483  }
484
485  /// Writes debug information to the current frame to be used in stack trace generation.
486  #[inline]
487  pub fn set_debug_info(&mut self, info: &DebugInfo) {
488    match info {
489      DebugInfo::Location { line, col } => self.debug_cursor = (*line, *col),
490    }
491  }
492}
493
494impl<I> StackFrame<I> {
495  #[inline(always)]
496  fn is_done(&self) -> bool {
497    self.sequence.is_none() || self.pc >= self.sequence.as_ref().unwrap().len()
498  }
499  
500  /// Writes a fragment to the frame's output.
501  #[inline]
502  pub fn write_frag(&mut self, frag: &str) {
503    self.output.write_frag(frag);
504  }
505  
506  /// Writes a whitespace string to the frame's output.
507  #[inline]
508  pub fn write_ws(&mut self, ws: &str) {
509    self.output.write_ws(ws);
510  }
511
512  /// Writes a value to the frame's output.
513  #[inline]
514  pub fn write<T: IntoRant>(&mut self, val: T) {
515    self.output.write_value(val.into_rant());
516  }
517
518  /// Consumes the frame's output and returns the final value generated by it.
519  #[inline]
520  pub fn into_output(self) -> RantValue {
521    self.output.render_value()
522  }
523}
524
525impl<I> Display for StackFrame<I> {
526  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
527    write!(f, "[{}:{}:{}] in {}", 
528      self.origin_name(), 
529      self.debug_cursor.0, 
530      self.debug_cursor.1,
531      self.sequence.as_ref()
532        .and_then(|seq| seq.name().map(|name| name.as_str()))
533        .unwrap_or_else(|| self.flavor.name()), 
534    )
535  }
536}
537
538/// Hints at what kind of program element a specific stack frame represents.
539///
540/// The runtime can use this information to find where to unwind the call stack to on specific operations like breaking, returning, etc.
541#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
542pub enum StackFrameFlavor {
543  /// Nothing special.
544  Original,
545  /// Native function call.
546  NativeCall,
547  /// Frame is used for a block element.
548  BlockElement,
549  /// Frame is used for a repeater element.
550  RepeaterElement,
551  /// Frame is used for the body of a function.
552  FunctionBody,
553  /// Frame is used to evaluate a dynamic key.
554  DynamicKeyExpression,
555  /// Frame is used to evaluate a function argument.
556  ArgumentExpression,
557}
558
559impl Default for StackFrameFlavor {
560  fn default() -> Self {
561    Self::Original
562  }
563}
564
565impl StackFrameFlavor {
566  fn name(&self) -> &'static str {
567    match self {
568      Self::Original => "sequence",
569      Self::NativeCall => "native call",
570      Self::BlockElement => "block element",
571      Self::RepeaterElement => "repeater element",
572      Self::FunctionBody => "function body",
573      Self::DynamicKeyExpression => "dynamic key",
574      Self::ArgumentExpression => "argument",
575    }
576  }
577}