Skip to content

Commit

Permalink
Added support for the experimental pipe operators
Browse files Browse the repository at this point in the history
  • Loading branch information
aftix committed Dec 13, 2024
1 parent 01e6582 commit 020fe8d
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 4 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = "MIT"
name = "rnix"
readme = "README.md"
repository = "https://github.com/nix-community/rnix-parser"
version = "0.11.0"
version = "0.11.1"

[[bench]]
harness = false
Expand Down
5 changes: 5 additions & 0 deletions src/ast/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum BinOpKind {
MoreOrEq,
NotEqual,
Or,
PipeRight,
PipeLeft,
}

impl BinOpKind {
Expand All @@ -43,6 +45,9 @@ impl BinOpKind {
TOKEN_NOT_EQUAL => Some(BinOpKind::NotEqual),
TOKEN_OR_OR => Some(BinOpKind::Or),

TOKEN_PIPE_RIGHT => Some(BinOpKind::PipeRight),
TOKEN_PIPE_LEFT => Some(BinOpKind::PipeLeft),

_ => None,
}
}
Expand Down
82 changes: 82 additions & 0 deletions src/ast/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,88 @@ mod tests {
use super::*;
use rowan::ast::AstNode;

#[test]
fn pipe_right() {
let s = "1 |> toString";

let parsed = Root::parse(s).ok();
assert!(parsed.is_ok(), "Failed to parse syntax `{s}`: {:?}", parsed);
let parsed = parsed.unwrap();

let nodes: Vec<_> = parsed.syntax().children().collect();
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].kind(), NODE_BIN_OP);

let op_children: Vec<_> = nodes[0].children().collect();
assert_eq!(op_children.len(), 2);
assert_eq!(op_children[0].kind(), NODE_LITERAL);
assert_eq!(op_children[1].kind(), NODE_IDENT);
}

#[test]
fn pipe_right_precedence() {
let s = "toString <| 1 + 1";

let parsed = Root::parse(s).ok();
assert!(parsed.is_ok(), "Failed to parse syntax `{s}`: {:?}", parsed);
let parsed = parsed.unwrap();

let nodes: Vec<_> = parsed.syntax().children().collect();
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].kind(), NODE_BIN_OP);

let op_children: Vec<_> = nodes[0].children().collect();
assert_eq!(op_children.len(), 2);
assert_eq!(op_children[0].kind(), NODE_BIN_OP);
assert_eq!(op_children[1].kind(), NODE_LITERAL);

let op_children: Vec<_> = op_children[0].children().collect();
assert_eq!(op_children.len(), 2);
assert_eq!(op_children[0].kind(), NODE_IDENT);
assert_eq!(op_children[1].kind(), NODE_LITERAL);
}

#[test]
fn pipe_left() {
let s = "toString <| 1";

let parsed = Root::parse(s).ok();
assert!(parsed.is_ok(), "Failed to parse syntax `{s}`: {:?}", parsed);
let parsed = parsed.unwrap();

let nodes: Vec<_> = parsed.syntax().children().collect();
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].kind(), NODE_BIN_OP);

let op_children: Vec<_> = nodes[0].children().collect();
assert_eq!(op_children.len(), 2);
assert_eq!(op_children[0].kind(), NODE_IDENT);
assert_eq!(op_children[1].kind(), NODE_LITERAL);
}

#[test]
fn pipe_left_precedence() {
let s = "1 + 1 |> toString";

let parsed = Root::parse(s).ok();
assert!(parsed.is_ok(), "Failed to parse syntax `{s}`: {:?}", parsed);
let parsed = parsed.unwrap();

let nodes: Vec<_> = parsed.syntax().children().collect();
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].kind(), NODE_BIN_OP);

let op_children: Vec<_> = nodes[0].children().collect();
assert_eq!(op_children.len(), 2);
assert_eq!(op_children[0].kind(), NODE_LITERAL);
assert_eq!(op_children[1].kind(), NODE_BIN_OP);

let op_children: Vec<_> = op_children[1].children().collect();
assert_eq!(op_children.len(), 2);
assert_eq!(op_children[0].kind(), NODE_LITERAL);
assert_eq!(op_children[1].kind(), NODE_IDENT);
}

#[test]
fn comment() {
let s = "# comment bruh
Expand Down
2 changes: 2 additions & 0 deletions src/kinds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ pub enum SyntaxKind {
TOKEN_MORE_OR_EQ,
TOKEN_NOT_EQUAL,
TOKEN_OR_OR,
TOKEN_PIPE_RIGHT,
TOKEN_PIPE_LEFT,

// Identifiers and values
TOKEN_FLOAT,
Expand Down
2 changes: 2 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,7 @@ macro_rules! T {
(>=) => ($crate::SyntaxKind::TOKEN_MORE_OR_EQ);
(!=) => ($crate::SyntaxKind::TOKEN_NOT_EQUAL);
(||) => ($crate::SyntaxKind::TOKEN_OR_OR);
("|>") => ($crate::SyntaxKind::TOKEN_PIPE_RIGHT);
("<|") => ($crate::SyntaxKind::TOKEN_PIPE_LEFT);
($kind:ident) => ($crate::SyntaxKind::$kind);
}
5 changes: 4 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,8 +583,11 @@ where

checkpoint
}
fn parse_pipe(&mut self) -> Checkpoint {
self.parse_left_assoc(Self::parse_simple, T!["|>"] | T!["<|"])
}
fn parse_fn(&mut self) -> Checkpoint {
let checkpoint = self.parse_simple();
let checkpoint = self.parse_pipe();

while self.peek().map(|t| t.is_fn_arg()).unwrap_or(false) {
self.start_node_at(checkpoint, NODE_APPLY);
Expand Down
8 changes: 8 additions & 0 deletions src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ impl Tokenizer<'_> {
'-' => TOKEN_SUB,
'*' => TOKEN_MUL,
'/' => TOKEN_DIV,
'<' if self.peek() == Some('|') => {
self.next().unwrap();
TOKEN_PIPE_LEFT
}
'<' if kind == Some(IdentType::Store) => {
self.consume(is_valid_path_char);
if self.next() != Some('>') {
Expand All @@ -376,6 +380,10 @@ impl Tokenizer<'_> {
self.next().unwrap();
TOKEN_AND_AND
}
'|' if self.peek() == Some('>') => {
self.next().unwrap();
TOKEN_PIPE_RIGHT
}
'|' if self.peek() == Some('|') => {
self.next().unwrap();
TOKEN_OR_OR
Expand Down

0 comments on commit 020fe8d

Please sign in to comment.