Add Context struct for locating compiler messages
This commit is contained in:
+56
-6
@@ -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;
|
use crate::world::World;
|
||||||
|
|
||||||
pub type CompileResult = Result<Vec<CompileMessage>, Vec<CompileMessage>>;
|
#[derive(Clone, Default)]
|
||||||
|
pub struct Context {
|
||||||
|
messages: Rc<RefCell<Vec<CompileMessage>>>,
|
||||||
|
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<usize>) -> 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<CompileMessage> {
|
||||||
|
self.messages.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CompileMessage {
|
pub struct CompileMessage {
|
||||||
@@ -17,7 +67,7 @@ pub enum Level {
|
|||||||
Warning,
|
Warning,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
pub file_path: Option<String>,
|
pub file_path: Option<String>,
|
||||||
pub board: Option<usize>,
|
pub board: Option<usize>,
|
||||||
@@ -28,8 +78,8 @@ pub struct Location {
|
|||||||
impl Display for CompileMessage {
|
impl Display for CompileMessage {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
let level = match self.level {
|
let level = match self.level {
|
||||||
Level::Error => "Error",
|
Level::Error => "error",
|
||||||
Level::Warning => "Warning",
|
Level::Warning => "warning",
|
||||||
};
|
};
|
||||||
write!(f, "{}: {}", level, self.message)
|
write!(f, "{}: {}", level, self.message)
|
||||||
}
|
}
|
||||||
@@ -40,7 +90,7 @@ impl Error for CompileMessage {}
|
|||||||
impl CompileMessage {
|
impl CompileMessage {
|
||||||
pub fn rich_format(&self, world: &World) -> String {
|
pub fn rich_format(&self, world: &World) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{}\n{}",
|
"{}\n {}",
|
||||||
self.to_string(),
|
self.to_string(),
|
||||||
self.location.rich_format(&world)
|
self.location.rich_format(&world)
|
||||||
)
|
)
|
||||||
|
|||||||
+5
-18
@@ -3,11 +3,7 @@ use std::ops::Range;
|
|||||||
use compact_str::CompactString;
|
use compact_str::CompactString;
|
||||||
use grammar::Tag;
|
use grammar::Tag;
|
||||||
|
|
||||||
use crate::{
|
use crate::{error::Context, peg::ParseState, world::Stat};
|
||||||
error::{CompileMessage, Level, Location},
|
|
||||||
peg::ParseState,
|
|
||||||
world::Stat,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type ParsedStat = Vec<Chunk>;
|
pub type ParsedStat = Vec<Chunk>;
|
||||||
|
|
||||||
@@ -28,7 +24,7 @@ pub struct LabelName {
|
|||||||
pub local: Option<CompactString>,
|
pub local: Option<CompactString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_stat_labels(stat: &Stat) -> (ParsedStat, Vec<CompileMessage>) {
|
pub fn parse_stat_labels(stat: &Stat, ctx: &Context) -> ParsedStat {
|
||||||
let code = &stat.code;
|
let code = &stat.code;
|
||||||
let mut parser = ParseState::new(code);
|
let mut parser = ParseState::new(code);
|
||||||
assert!(
|
assert!(
|
||||||
@@ -37,20 +33,11 @@ pub fn parse_stat_labels(stat: &Stat) -> (ParsedStat, Vec<CompileMessage>) {
|
|||||||
code
|
code
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut messages = vec![];
|
|
||||||
for cap in parser.walk_captures() {
|
for cap in parser.walk_captures() {
|
||||||
match cap.kind() {
|
match cap.kind() {
|
||||||
Tag::WarnTrailing => {
|
Tag::WarnTrailing => {
|
||||||
messages.push(CompileMessage {
|
ctx.with_span(cap.span())
|
||||||
level: Level::Warning,
|
.warning("trailing characters at end of line");
|
||||||
message: "Trailing characters at end of line".into(),
|
|
||||||
location: Location {
|
|
||||||
file_path: None,
|
|
||||||
board: None,
|
|
||||||
stat: None,
|
|
||||||
span: Some(cap.span()),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -106,7 +93,7 @@ pub fn parse_stat_labels(stat: &Stat) -> (ParsedStat, Vec<CompileMessage>) {
|
|||||||
if last_index < code.len() {
|
if last_index < code.len() {
|
||||||
result.push(Chunk::Verbatim(code[last_index..code.len()].into()));
|
result.push(Chunk::Verbatim(code[last_index..code.len()].into()));
|
||||||
}
|
}
|
||||||
(result, messages)
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
mod grammar {
|
mod grammar {
|
||||||
|
|||||||
+11
-21
@@ -1,31 +1,20 @@
|
|||||||
use compact_str::CompactString;
|
use compact_str::CompactString;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{error::Context, world::Board};
|
||||||
error::{CompileMessage, CompileResult},
|
|
||||||
world::Board,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
parse::{Chunk, ParsedStat, parse_stat_labels},
|
parse::{Chunk, ParsedStat, parse_stat_labels},
|
||||||
sanitize::Registry,
|
sanitize::Registry,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn process_labels(board: &mut Board) -> CompileResult {
|
pub fn process_labels(board: &mut Board, ctx: &Context) {
|
||||||
// Parse stats into chunks
|
// Parse stats into chunks
|
||||||
let mut messages: Vec<CompileMessage> = vec![];
|
|
||||||
let mut stats: Vec<ParsedStat> = board
|
let mut stats: Vec<ParsedStat> = board
|
||||||
.stats
|
.stats
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, stat)| {
|
.map(|(index, stat)| parse_stat_labels(&stat, &ctx.with_stat(index)))
|
||||||
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
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
let mut registry = Registry::new();
|
let mut registry = Registry::new();
|
||||||
|
|
||||||
@@ -51,8 +40,6 @@ pub fn process_labels(board: &mut Board) -> CompileResult {
|
|||||||
.collect();
|
.collect();
|
||||||
old_stat.code = new_code;
|
old_stat.code = new_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(messages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve ".local" labels to "name.local" form.
|
/// Resolve ".local" labels to "name.local" form.
|
||||||
@@ -218,7 +205,10 @@ mod test {
|
|||||||
|
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
|
|
||||||
use crate::world::{Board, Stat};
|
use crate::{
|
||||||
|
error::Context,
|
||||||
|
world::{Board, Stat},
|
||||||
|
};
|
||||||
|
|
||||||
use super::process_labels;
|
use super::process_labels;
|
||||||
|
|
||||||
@@ -258,28 +248,28 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_label_sanitization() {
|
fn test_label_sanitization() {
|
||||||
let mut board = board_from_text("tests/labels/sanitize.txt");
|
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));
|
assert_snapshot!(board_to_text(board));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_anonymous_labels() {
|
fn test_anonymous_labels() {
|
||||||
let mut board = board_from_text("tests/labels/anonymous.txt");
|
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));
|
assert_snapshot!(board_to_text(board));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_local_labels() {
|
fn test_local_labels() {
|
||||||
let mut board = board_from_text("tests/labels/local.txt");
|
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));
|
assert_snapshot!(board_to_text(board));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_namespaces() {
|
fn test_namespaces() {
|
||||||
let mut board = board_from_text("tests/labels/namespaces.txt");
|
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));
|
assert_snapshot!(board_to_text(board));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-7
@@ -6,6 +6,7 @@ mod preprocess;
|
|||||||
mod world;
|
mod world;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
use error::Context as ErrContext;
|
||||||
use labels::process::process_labels;
|
use labels::process::process_labels;
|
||||||
use preprocess::eval::Context;
|
use preprocess::eval::Context;
|
||||||
use std::{env, error::Error, fs, path::PathBuf, process::exit};
|
use std::{env, error::Error, fs, path::PathBuf, process::exit};
|
||||||
@@ -37,17 +38,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resolve labels to proper ZZT-OOP
|
// 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() {
|
for (i, mut board) in world.boards.iter_mut().enumerate() {
|
||||||
let mut result = process_labels(&mut board).unwrap();
|
let ctx = ctx.with_board(i);
|
||||||
for message in result.iter_mut() {
|
process_labels(&mut board, &ctx);
|
||||||
message.location.board = Some(i);
|
|
||||||
message.location.file_path = Some(world_filename.clone());
|
|
||||||
}
|
|
||||||
messages.extend(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print diagnostics
|
// Print diagnostics
|
||||||
|
let messages = ctx.into_messages();
|
||||||
for (i, message) in messages.iter().enumerate() {
|
for (i, message) in messages.iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
println!();
|
println!();
|
||||||
|
|||||||
Reference in New Issue
Block a user