diff options
-rw-r--r-- | src/main.rs | 9 | ||||
-rw-r--r-- | src/queries.rs | 62 |
2 files changed, 53 insertions, 18 deletions
diff --git a/src/main.rs b/src/main.rs index 54f17a5..2a4898d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,7 @@ fn parse_nexp(path: &PathBuf) -> anyhow::Result<(String, rnix::Root)> { Ok((content, parse.tree())) } + fn main() { let args = Args::parse(); @@ -51,13 +52,15 @@ fn main() { let query = match parse.to_query() { Err(es) => { - eprintln!("{}", es.join("\n")); + queries::print_query_errors(&args.query, es); exit(1); }, Ok(query) => query }; - // println!("{nexp:#?}"); + if args.debug { + println!("{query:?}"); + } if args.batchmode { batchmode::batchmode(args.path, query, args.debug); @@ -196,7 +199,7 @@ fn surrounding_noise(node: &rnix::SyntaxNode) -> (NixNodeOrToken, NixNodeOrToken fn remove_node(node: &rnix::SyntaxNode) -> (usize, usize) { let (before, after) = surrounding_noise(node); - let (mut prev, mut next) = + let (mut prev, /*mut*/ next) = eat_whitespace(&before, &after); // println!("{prev:?}, {next:?}"); diff --git a/src/queries.rs b/src/queries.rs index 3f7be8e..ae00060 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -2,7 +2,10 @@ // https://github.com/rust-analyzer/rowan/blob/master/examples/s_expressions.rs use itertools::Itertools; -use rowan::{GreenNode, GreenNodeBuilder}; +use rowan::{GreenNode, GreenNodeBuilder, TextRange}; + +type ParseError = (TextRange, String); +type ParseResult<T> = Result<T, Vec<ParseError>>; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[allow(non_camel_case_types)] @@ -86,13 +89,8 @@ pub struct Parse { pub fn parse(text: &str) -> Parse { struct Parser { - /// input tokens, including whitespace, - /// in *reverse* order. tokens: Vec<(SyntaxKind, String)>, - /// the in-progress tree. builder: GreenNodeBuilder<'static>, - /// the list of syntax errors we've accumulated - /// so far. errors: Vec<String>, } @@ -115,9 +113,9 @@ pub fn parse(text: &str) -> Parse { QexpRes::Ok => (), unmatched_bracket => { self.builder.start_node(ERROR.into()); - self.errors.push(format!("lone `{:?}`", unmatched_bracket)); self.bump(); // be sure to chug along in case of error self.builder.finish_node(); + self.errors.push(format!("lone `{:?}`", unmatched_bracket)); } } } @@ -135,7 +133,9 @@ pub fn parse(text: &str) -> Parse { loop { match self.word() { QexpRes::Eof => { + self.builder.start_node(ERROR.into()); self.errors.push("expected `]`".to_string()); + self.builder.finish_node(); break; } QexpRes::RBracket => { @@ -292,9 +292,6 @@ pub enum Operator { Keep } -type ParseError = String; -type ParseResult<T> = Result<T, Vec<ParseError>>; - impl NixSyntaxRole { fn from_str(from: &str) -> Option<NixSyntaxRole> { use NixSyntaxRole::*; @@ -315,7 +312,7 @@ impl Parse { match node.to_string().as_str() { "remove" => Ok(Operator::Remove), "keep" => Ok(Operator::Keep), - unknown => Err(vec![format!("unknown operator {unknown}")]) + unknown => Err(vec![(node.text_range(), format!("unknown operator {unknown}"))]) } } fn parse_args(node: SyntaxNode) -> ParseResult<Option<Operator>> { @@ -337,7 +334,7 @@ impl Parse { match args { Err(e) => Err(e), Ok(ops) if ops.len() == 1 => Ok(Some(ops.into_iter().exactly_one().unwrap())), - _ => Err(vec!["cannot have multiple operators at the same node (for now)".to_string()]) + _ => Err(vec![(node.text_range(), "cannot have multiple operators at the same node (for now)".to_string())]) } } else { Ok(None) @@ -378,10 +375,45 @@ impl Parse { let root = self.syntax(); assert_eq!(root.kind(), ROOT); - root.children() + let (errors, filters): (Vec<_>, Vec<_>) = root.children() .map(parse_filter) - .collect::<Result<Vec<Filter>, Vec<ParseError>>>() - .map(|filters| Query {filters}) + .partition_map(From::from); + + if errors.len() == 0 { + Ok(Query { filters }) + } else { + Err(errors.concat()) + } + } +} + +pub fn print_query_errors(content: &str, mut errors: Vec<ParseError>) { + errors.sort_by_key(|(range, _)| range.start()); + + let mut errors_iter = errors.into_iter().peekable(); + + eprintln!("Errors in Query:"); + + let mut line_start = 0usize; + for line in content.split('\n') { + + eprintln!("| {line}"); + let line_end: usize = line_start + line.len(); + + while Some(true) == errors_iter.peek().map(|e| Into::<usize>::into(e.0.end()) < line_end) { + match errors_iter.next() { + None => return, + Some((range, msg)) => { + eprintln!( + "| {pad}{line} {msg}", + pad = " ".repeat(Into::<usize>::into(range.start()).checked_sub(line_start).unwrap_or(0)), + line = "^".repeat(range.len().into()) + ) + } + } + } + + line_start += line_end + 1; } } |