summaryrefslogtreecommitdiff
path: root/src/pipeline.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/pipeline.rs')
-rw-r--r--src/pipeline.rs203
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()))
+ }
+}