From a86c4f9e4af3f4a6010771e8d7217b33ce9b86dd Mon Sep 17 00:00:00 2001 From: Chris Mounce Date: Thu, 3 Jul 2025 20:46:21 -0700 Subject: [PATCH] Add Context struct for locating compiler messages --- src/error.rs | 62 ++++++++++++++++++++++++++++++++++++++----- src/labels/parse.rs | 23 ++++------------ src/labels/process.rs | 32 ++++++++-------------- src/main.rs | 12 ++++----- 4 files changed, 77 insertions(+), 52 deletions(-) diff --git a/src/error.rs b/src/error.rs index 0d02b2f..fa79270 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,58 @@ -use std::{error::Error, fmt::Display, ops::Range}; +use std::{cell::RefCell, error::Error, fmt::Display, ops::Range, rc::Rc}; use crate::world::World; -pub type CompileResult = Result, Vec>; +#[derive(Clone, Default)] +pub struct Context { + messages: Rc>>, + location: Location, +} + +impl Context { + pub fn with_file_path(&self, s: &str) -> Self { + let mut result = self.clone(); + result.location.file_path = Some(s.into()); + result + } + + pub fn with_board(&self, board: usize) -> Self { + let mut result = self.clone(); + result.location.board = Some(board); + result + } + + pub fn with_stat(&self, stat: usize) -> Self { + let mut result = self.clone(); + result.location.stat = Some(stat); + result + } + + pub fn with_span(&self, span: Range) -> Self { + let mut result = self.clone(); + result.location.span = Some(span); + result + } + + pub fn error(&self, message: &str) { + self.messages.borrow_mut().push(CompileMessage { + level: Level::Error, + message: message.into(), + location: self.location.clone(), + }); + } + + pub fn warning(&self, message: &str) { + self.messages.borrow_mut().push(CompileMessage { + level: Level::Warning, + message: message.into(), + location: self.location.clone(), + }); + } + + pub fn into_messages(self) -> Vec { + self.messages.take() + } +} #[derive(Debug)] pub struct CompileMessage { @@ -17,7 +67,7 @@ pub enum Level { Warning, } -#[derive(Debug)] +#[derive(Clone, Debug, Default)] pub struct Location { pub file_path: Option, pub board: Option, @@ -28,8 +78,8 @@ pub struct Location { impl Display for CompileMessage { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let level = match self.level { - Level::Error => "Error", - Level::Warning => "Warning", + Level::Error => "error", + Level::Warning => "warning", }; write!(f, "{}: {}", level, self.message) } @@ -40,7 +90,7 @@ impl Error for CompileMessage {} impl CompileMessage { pub fn rich_format(&self, world: &World) -> String { format!( - "{}\n{}", + "{}\n {}", self.to_string(), self.location.rich_format(&world) ) diff --git a/src/labels/parse.rs b/src/labels/parse.rs index 0eacba3..20fa37a 100644 --- a/src/labels/parse.rs +++ b/src/labels/parse.rs @@ -3,11 +3,7 @@ use std::ops::Range; use compact_str::CompactString; use grammar::Tag; -use crate::{ - error::{CompileMessage, Level, Location}, - peg::ParseState, - world::Stat, -}; +use crate::{error::Context, peg::ParseState, world::Stat}; pub type ParsedStat = Vec; @@ -28,7 +24,7 @@ pub struct LabelName { pub local: Option, } -pub fn parse_stat_labels(stat: &Stat) -> (ParsedStat, Vec) { +pub fn parse_stat_labels(stat: &Stat, ctx: &Context) -> ParsedStat { let code = &stat.code; let mut parser = ParseState::new(code); assert!( @@ -37,20 +33,11 @@ pub fn parse_stat_labels(stat: &Stat) -> (ParsedStat, Vec) { code ); - let mut messages = vec![]; for cap in parser.walk_captures() { match cap.kind() { Tag::WarnTrailing => { - messages.push(CompileMessage { - level: Level::Warning, - message: "Trailing characters at end of line".into(), - location: Location { - file_path: None, - board: None, - stat: None, - span: Some(cap.span()), - }, - }); + ctx.with_span(cap.span()) + .warning("trailing characters at end of line"); } _ => {} } @@ -106,7 +93,7 @@ pub fn parse_stat_labels(stat: &Stat) -> (ParsedStat, Vec) { if last_index < code.len() { result.push(Chunk::Verbatim(code[last_index..code.len()].into())); } - (result, messages) + result } mod grammar { diff --git a/src/labels/process.rs b/src/labels/process.rs index 7957050..9e8d115 100644 --- a/src/labels/process.rs +++ b/src/labels/process.rs @@ -1,31 +1,20 @@ use compact_str::CompactString; use rustc_hash::FxHashMap; -use crate::{ - error::{CompileMessage, CompileResult}, - world::Board, -}; +use crate::{error::Context, world::Board}; use super::{ parse::{Chunk, ParsedStat, parse_stat_labels}, sanitize::Registry, }; -pub fn process_labels(board: &mut Board) -> CompileResult { +pub fn process_labels(board: &mut Board, ctx: &Context) { // Parse stats into chunks - let mut messages: Vec = vec![]; let mut stats: Vec = board .stats .iter() .enumerate() - .map(|(index, stat)| { - let (parsed_stat, mut new_messages) = parse_stat_labels(&stat); - new_messages - .iter_mut() - .for_each(|m| m.location.stat = Some(index)); - messages.extend(new_messages); - parsed_stat - }) + .map(|(index, stat)| parse_stat_labels(&stat, &ctx.with_stat(index))) .collect(); let mut registry = Registry::new(); @@ -51,8 +40,6 @@ pub fn process_labels(board: &mut Board) -> CompileResult { .collect(); old_stat.code = new_code; } - - Ok(messages) } /// Resolve ".local" labels to "name.local" form. @@ -218,7 +205,10 @@ mod test { use insta::assert_snapshot; - use crate::world::{Board, Stat}; + use crate::{ + error::Context, + world::{Board, Stat}, + }; use super::process_labels; @@ -258,28 +248,28 @@ mod test { #[test] fn test_label_sanitization() { let mut board = board_from_text("tests/labels/sanitize.txt"); - let _ = process_labels(&mut board); + let _ = process_labels(&mut board, &Context::default()); assert_snapshot!(board_to_text(board)); } #[test] fn test_anonymous_labels() { let mut board = board_from_text("tests/labels/anonymous.txt"); - let _ = process_labels(&mut board); + let _ = process_labels(&mut board, &Context::default()); assert_snapshot!(board_to_text(board)); } #[test] fn test_local_labels() { let mut board = board_from_text("tests/labels/local.txt"); - let _ = process_labels(&mut board); + let _ = process_labels(&mut board, &Context::default()); assert_snapshot!(board_to_text(board)); } #[test] fn test_namespaces() { let mut board = board_from_text("tests/labels/namespaces.txt"); - let _ = process_labels(&mut board); + let _ = process_labels(&mut board, &Context::default()); assert_snapshot!(board_to_text(board)); } } diff --git a/src/main.rs b/src/main.rs index 8fbf229..5ffd2e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod preprocess; mod world; use anyhow::anyhow; +use error::Context as ErrContext; use labels::process::process_labels; use preprocess::eval::Context; use std::{env, error::Error, fs, path::PathBuf, process::exit}; @@ -37,17 +38,14 @@ fn main() -> Result<(), Box> { } // Resolve labels to proper ZZT-OOP - let mut messages = vec![]; + let ctx = ErrContext::default().with_file_path(&world_filename); for (i, mut board) in world.boards.iter_mut().enumerate() { - let mut result = process_labels(&mut board).unwrap(); - for message in result.iter_mut() { - message.location.board = Some(i); - message.location.file_path = Some(world_filename.clone()); - } - messages.extend(result); + let ctx = ctx.with_board(i); + process_labels(&mut board, &ctx); } // Print diagnostics + let messages = ctx.into_messages(); for (i, message) in messages.iter().enumerate() { if i > 0 { println!();