Add static variable resolution with scope tracking
- Add distance-based get_at on EnvironmentStack - Add distance-based assign_at on EnvironmentStack - Introduce ErrorSink to accumulate diagnostics across passes - Implement Resolver with a ScopeStack and per-node distance map - Extend interpreter to store locals distances for runtime lookup - Add tests for resolution behavior and error accumulation Add static variable resolution with scope tracking
This commit is contained in:
@@ -479,3 +479,90 @@ pub fn io_error<T>(message: impl Into<String>) -> LoxResult<T> {
|
||||
message: message.into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Accumulates diagnostics during a pass instead of bailing on the first one.
|
||||
///
|
||||
/// Static analyses (e.g. the resolver) `report` problems and keep traversing so
|
||||
/// the user sees every error at once, then surface them at the end.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ErrorSink {
|
||||
errors: Vec<LoxError>,
|
||||
}
|
||||
|
||||
impl ErrorSink {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Record a diagnostic and keep going.
|
||||
pub fn report(&mut self, error: LoxError) {
|
||||
self.errors.push(error);
|
||||
}
|
||||
|
||||
pub fn has_errors(&self) -> bool {
|
||||
!self.errors.is_empty()
|
||||
}
|
||||
|
||||
pub fn errors(&self) -> &[LoxError] {
|
||||
&self.errors
|
||||
}
|
||||
|
||||
/// Merge another sink's errors into this one (e.g. from a separate pass).
|
||||
pub fn extend(&mut self, other: ErrorSink) {
|
||||
self.errors.extend(other.errors);
|
||||
}
|
||||
|
||||
pub fn into_errors(self) -> Vec<LoxError> {
|
||||
self.errors
|
||||
}
|
||||
|
||||
/// Bridge to the single-error [`LoxResult`] API: `Ok` if empty, otherwise
|
||||
/// the first reported error.
|
||||
pub fn into_result(self) -> LoxResult<()> {
|
||||
match self.errors.into_iter().next() {
|
||||
Some(error) => Err(error),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::frontend::source_registry::SourceSlice;
|
||||
|
||||
fn parse_err(message: &str) -> LoxError {
|
||||
LoxError::ParseError {
|
||||
source_slice: SourceSlice::synthetic(),
|
||||
message: message.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_sink_is_ok() {
|
||||
let sink = ErrorSink::new();
|
||||
assert!(!sink.has_errors());
|
||||
assert!(sink.into_result().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accumulates_multiple_errors() {
|
||||
let mut sink = ErrorSink::new();
|
||||
sink.report(parse_err("first"));
|
||||
sink.report(parse_err("second"));
|
||||
assert!(sink.has_errors());
|
||||
assert_eq!(sink.errors().len(), 2);
|
||||
assert!(sink.into_result().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_merges_sinks() {
|
||||
let mut a = ErrorSink::new();
|
||||
a.report(parse_err("a"));
|
||||
let mut b = ErrorSink::new();
|
||||
b.report(parse_err("b1"));
|
||||
b.report(parse_err("b2"));
|
||||
a.extend(b);
|
||||
assert_eq!(a.into_errors().len(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user