diff options
Diffstat (limited to 'src/pipeline.rs')
-rw-r--r-- | src/pipeline.rs | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/src/pipeline.rs b/src/pipeline.rs new file mode 100644 index 0000000..cba046f --- /dev/null +++ b/src/pipeline.rs @@ -0,0 +1,203 @@ + +use itertools::Itertools; +use rnix::{match_ast, ast}; +use rowan::ast::AstNode; +use crate::queries::*; +use crate::queries::SyntaxKind::*; + +#[derive(Debug)] +pub struct Change { + pub node: rnix::SyntaxNode, + pub kind: ChangeKind +} + +#[derive(Copy, Clone, Debug)] +pub enum ChangeKind { + Remove, + Keep +} + +type NixExprs = Box<dyn Iterator<Item = rnix::SyntaxNode>>; +type Pipe = (Vec<Change>, NixExprs); + +macro_rules! ast_node { + ($ast:ident, $kind:ident) => { + #[derive(PartialEq, Eq, Hash)] + #[repr(transparent)] + struct $ast(SyntaxNode); + impl $ast { + #[allow(unused)] + fn cast(node: SyntaxNode) -> Option<Self> { + if node.kind() == $kind { + Some(Self(node)) + } else { + None + } + } + } + }; +} + +ast_node!(Root, ROOT); +ast_node!(Atom, ATOM); +ast_node!(List, LIST); + +#[derive(PartialEq, Eq, Hash, Debug)] +#[repr(transparent)] +struct Qexp(SyntaxNode); + +enum QexpKind { + Atom(Atom), + List(List), +} + +impl Qexp { + fn cast(node: SyntaxNode) -> Option<Self> { + if Atom::cast(node.clone()).is_some() || List::cast(node.clone()).is_some() { + Some(Qexp(node)) + } else { + None + } + } + + fn kind(&self) -> QexpKind { + Atom::cast(self.0.clone()) + .map(QexpKind::Atom) + .or_else(|| List::cast(self.0.clone()).map(QexpKind::List)) + .unwrap() + } + + fn apply(&self, _acc: Pipe) -> Pipe { + todo!() + } +} + +impl Root { + fn qexps(&self) -> impl Iterator<Item = Qexp> + '_ { + self.0.children().filter_map(Qexp::cast) + } +} + +enum Op { + Down, + DownRecursive, + Up, + UpRecursive, + NixSyntaxNode(rnix::SyntaxKind), + Named(String) +} + +impl Atom { + fn eval(&self) -> Option<i64> { + self.text().parse().ok() + } + fn as_op(&self) -> Option<Op> { + let op = match self.text().as_str() { + ">" => Op::Down, + ">>" => Op::DownRecursive, + "<" => Op::Up, + "<<" => Op::UpRecursive, + "Inherit" => Op::NixSyntaxNode(rnix::SyntaxKind::NODE_INHERIT), + "String" => Op::NixSyntaxNode(rnix::SyntaxKind::NODE_STRING), + // TODO other syntax nodes + name => Op::Named(name.to_owned()), + }; + Some(op) + } + fn as_change(&self) -> Option<ChangeKind> { + let change = match self.text().as_str() { + "remove" => ChangeKind::Remove, + "keep" => ChangeKind::Keep, + _ => return None + }; + Some(change) + } + fn iter_args(&self) -> impl Iterator<Item = Atom> { + self.0.children().find_map(List::cast).into_iter().map(|arglist| arglist.iter()).flatten() + } + fn text(&self) -> String { + match self.0.green().children().next() { + Some(rowan::NodeOrToken::Token(token)) => token.text().to_string(), + _ => unreachable!(), + } + } + fn apply(&self, (mut changes, acc): Pipe) -> Pipe { + let mut acc: NixExprs = match self.as_op() { + Some(Op::Down) => Box::new(acc.map(|s| s.children()).flatten()), + Some(Op::DownRecursive) => Box::new(acc.map(|s| s.descendants()).flatten()), + Some(Op::Up) => Box::new(acc.filter_map(|s| s.parent())), + Some(Op::UpRecursive) => Box::new(acc.map(|s| s.ancestors()).flatten()), + // TODO: how to select roles relative to previous node? + Some(Op::NixSyntaxNode(kind)) => Box::new(acc.filter(move |s| s.kind() == kind)), + Some(Op::Named(name)) => + Box::new(acc + .filter(move |node| match_ast! { match node { + ast::AttrpathValue(value) => { + name == value.attrpath().unwrap().to_string() + }, + ast::Apply(value) => { + // TODO: special case lambda = NODE_SELECT here? + name == value.lambda().unwrap().to_string() + }, + // TODO: this is difficult — I want to use free-form names + // to select things below, too, but that might not always be + // possible. perhaps it is possible to skip over descendants? + ast::Ident(value) => { + name == value.to_string() + }, + _ => false + }})), + _ => todo!() + }; + + if let Ok(arg) = self.iter_args().exactly_one() { + if let Some(change) = arg.as_change() { + let (mut nchanges, nacc): (Vec<_>, Vec<_>) = acc + .map(|node| (Change { node: node.clone(), kind: change }, node)) + .unzip(); + acc = Box::new(nacc.into_iter()); + changes.append(&mut nchanges); + } + } + + (changes, acc) + } +} + +impl List { + fn sexps(&self) -> impl Iterator<Item = Qexp> + '_ { + self.0.children().filter_map(Qexp::cast) + } + + fn iter(&self) -> impl Iterator<Item = Atom> { + self.0.children().filter_map(Atom::cast) + } +} + + + +impl Parse { + fn root(&self) -> Root { + Root::cast(self.syntax()).unwrap() + } + + pub fn apply(&self, _content: &str, nexp: rnix::SyntaxNode) -> anyhow::Result<(Vec<Change>, Vec<rnix::SyntaxNode>)> { + + let mut pipe: Pipe = (Vec::new(), Box::new(std::iter::once(nexp))); + + for qexp in self.root().qexps() { + match qexp.kind() { + QexpKind::Atom(filter) => { + pipe = filter.apply(pipe); + } + _ => panic!("???") + } + } + + // let results = + // acc.map(|node| content[node.text_range().start().into()..node.text_range().end().into()].to_owned()) + // .collect(); + + Ok((pipe.0, pipe.1.collect())) + } +} |