summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstuebinm2024-04-16 20:18:00 +0200
committerstuebinm2024-04-16 20:18:00 +0200
commitcf418ce76679f019834e4b6ff2cbafc6d40c180e (patch)
treed772f07ea4754073675ed07359729f453ef3bf5c
parent238dabec513eac8af699756281d5aec12720686c (diff)
produces & print errors for unknown query constructs
Not sure yet if i'll keep the printing code, it's kinda fragile & there's almost definitely a crate which i could use instead.
Diffstat (limited to '')
-rw-r--r--src/main.rs9
-rw-r--r--src/queries.rs62
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;
}
}