mirror of
https://github.com/oxalica/nil.git
synced 2024-11-22 02:55:39 +03:00
Impl experimental pipe operators
`|>` and `<|` operators are introduced as experimental feature "pipe-operator" since Nix 2.24. Ref: <https://github.com/NixOS/nix/blob/2.24.0/doc/manual/src/language/operators.md#pipe-operators> Co-authored-by: Kira Malinova <llathasa@outlook.com>
This commit is contained in:
parent
c8e8ce7244
commit
52304da8e9
@ -51,6 +51,7 @@ pub enum HlOperator {
|
||||
Comparison,
|
||||
Arithmetic,
|
||||
Aggregation,
|
||||
Pipe,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
@ -161,6 +162,7 @@ pub(crate) fn highlight(
|
||||
}
|
||||
T![+] | T![-] | T![*] | T![/] => HlTag::Operator(HlOperator::Arithmetic),
|
||||
T![++] | T!["//"] => HlTag::Operator(HlOperator::Aggregation),
|
||||
T![|>] | T![<|] => HlTag::Operator(HlOperator::Pipe),
|
||||
T!['{'] | T!['}'] | T!["${"] => HlTag::Punct(HlPunct::Brace),
|
||||
T!['['] | T![']'] => HlTag::Punct(HlPunct::Bracket),
|
||||
T!['('] | T![')'] => HlTag::Punct(HlPunct::Paren),
|
||||
|
@ -213,6 +213,17 @@ impl<'db> InferCtx<'db> {
|
||||
self.unify_var(then_ty, else_ty);
|
||||
then_ty
|
||||
}
|
||||
&Expr::Apply(lam, arg)
|
||||
| &Expr::Binary(Some(BinaryOpKind::PipeRight), arg, lam)
|
||||
| &Expr::Binary(Some(BinaryOpKind::PipeLeft), lam, arg) => {
|
||||
let param_ty = self.new_ty_var();
|
||||
let ret_ty = self.new_ty_var();
|
||||
let lam_ty = self.infer_expr(lam);
|
||||
self.unify_var_ty(lam_ty, Ty::Lambda(param_ty, ret_ty));
|
||||
let arg_ty = self.infer_expr(arg);
|
||||
self.unify_var(arg_ty, param_ty);
|
||||
ret_ty
|
||||
}
|
||||
&Expr::Binary(op, lhs, rhs) => {
|
||||
let lhs_ty = self.infer_expr(lhs);
|
||||
let rhs_ty = self.infer_expr(rhs);
|
||||
@ -256,6 +267,8 @@ impl<'db> InferCtx<'db> {
|
||||
self.unify_var(rhs_ty, ret_ty);
|
||||
ret_ty
|
||||
}
|
||||
// Already handled by the outer match.
|
||||
BinaryOpKind::PipeLeft | BinaryOpKind::PipeRight => unreachable!(),
|
||||
}
|
||||
}
|
||||
&Expr::Unary(op, arg) => {
|
||||
@ -270,15 +283,6 @@ impl<'db> InferCtx<'db> {
|
||||
Some(UnaryOpKind::Negate) => arg_ty,
|
||||
}
|
||||
}
|
||||
&Expr::Apply(lam, arg) => {
|
||||
let param_ty = self.new_ty_var();
|
||||
let ret_ty = self.new_ty_var();
|
||||
let lam_ty = self.infer_expr(lam);
|
||||
self.unify_var_ty(lam_ty, Ty::Lambda(param_ty, ret_ty));
|
||||
let arg_ty = self.infer_expr(arg);
|
||||
self.unify_var(arg_ty, param_ty);
|
||||
ret_ty
|
||||
}
|
||||
Expr::HasAttr(set_expr, path) => {
|
||||
// TODO: Store the information of referenced paths somehow.
|
||||
self.infer_expr(*set_expr);
|
||||
|
@ -179,6 +179,12 @@ fn select() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pipe() {
|
||||
check("f: 1 |> f", expect!["(int → ?) → ?"]);
|
||||
check("f: f <| 1", expect!["(int → ?) → ?"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external() {
|
||||
check_all_expect(
|
||||
|
@ -25,6 +25,9 @@ pub enum BinaryOpKind {
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
|
||||
PipeLeft,
|
||||
PipeRight,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
@ -263,16 +266,20 @@ impl Expr {
|
||||
}
|
||||
|
||||
pub fn contains_without_paren(&self, inner: &Self) -> bool {
|
||||
fn bp(e: &Expr) -> Option<u8> {
|
||||
const MIN_BP: i8 = i8::MIN;
|
||||
const MAX_BP: i8 = i8::MAX;
|
||||
|
||||
fn bp(e: &Expr) -> Option<i8> {
|
||||
Some(match e {
|
||||
Expr::With(_)
|
||||
| Expr::Lambda(_)
|
||||
| Expr::LetIn(_)
|
||||
| Expr::IfThenElse(_)
|
||||
| Expr::Assert(_) => TOPLEVEL,
|
||||
| Expr::Assert(_) => MIN_BP,
|
||||
|
||||
// Binary and unary ops. They follow `infix_bp` in parser.
|
||||
Expr::BinaryOp(e) => match e.op_kind()? {
|
||||
BinaryOpKind::PipeLeft | BinaryOpKind::PipeRight => 0,
|
||||
BinaryOpKind::Imply => 1,
|
||||
BinaryOpKind::Or => 3,
|
||||
BinaryOpKind::And => 5,
|
||||
@ -307,18 +314,15 @@ impl Expr {
|
||||
| Expr::Ref(_) => 29,
|
||||
|
||||
// Special. See below.
|
||||
Expr::Paren(_) => PAREN,
|
||||
Expr::Paren(_) => MAX_BP,
|
||||
})
|
||||
}
|
||||
|
||||
const TOPLEVEL: u8 = 0;
|
||||
const PAREN: u8 = 31;
|
||||
|
||||
match (bp(self), bp(inner)) {
|
||||
// Special case 1: `Paren`s can safely contain or be contained by anything.
|
||||
(Some(PAREN), _) | (_, Some(PAREN)) => true,
|
||||
(Some(MAX_BP), _) | (_, Some(MAX_BP)) => true,
|
||||
// Special case 2: top-levels can contain each other without ambiguity.
|
||||
(Some(TOPLEVEL), Some(TOPLEVEL)) => true,
|
||||
(Some(MIN_BP), Some(MIN_BP)) => true,
|
||||
// Otherwise, expressions with lower binding power contain higher ones.
|
||||
(Some(outer), Some(inner)) => outer < inner,
|
||||
// `false` by default.
|
||||
@ -382,6 +386,8 @@ asts! {
|
||||
let op = match tok.kind() {
|
||||
T![->] => BinaryOpKind::Imply,
|
||||
T![&&] => BinaryOpKind::And,
|
||||
T![|>] => BinaryOpKind::PipeRight,
|
||||
T![<|] => BinaryOpKind::PipeLeft,
|
||||
T![||] => BinaryOpKind::Or,
|
||||
T![==] => BinaryOpKind::Equal,
|
||||
T![!=] => BinaryOpKind::NotEqual,
|
||||
|
@ -111,9 +111,11 @@ def! {
|
||||
EQ2 = [==],
|
||||
GT_EQ = [>=],
|
||||
LT_EQ = [<=],
|
||||
LT_OR = [<|],
|
||||
MINUS_GT = [->],
|
||||
NOT_EQ = [!=],
|
||||
OR2 = [||],
|
||||
OR_GT = [|>],
|
||||
PLUS2 = [++],
|
||||
QUOTE2 = ["''"],
|
||||
SLASH2 = ["//"],
|
||||
|
@ -109,6 +109,8 @@ regex_dfa! {
|
||||
QUOTE2 = r"''",
|
||||
|
||||
DOT3 = r"\.\.\.",
|
||||
OR_GT = r"\|>",
|
||||
LT_OR = r"<\|",
|
||||
MINUS_GT = r"->",
|
||||
OR2 = r"\|\|",
|
||||
AND2 = r"&&",
|
||||
@ -654,4 +656,18 @@ mod tests {
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pipe() {
|
||||
check_lex(
|
||||
"1<|2|>3",
|
||||
expect![[r#"
|
||||
INT "1"
|
||||
LT_OR "<|"
|
||||
INT "2"
|
||||
OR_GT "|>"
|
||||
INT "3"
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -367,12 +367,12 @@ impl<'i> Parser<'i> {
|
||||
/// Operator level expression (low priority).
|
||||
/// Maybe consume nothing.
|
||||
fn expr_operator_opt(&mut self) {
|
||||
self.expr_bp(0);
|
||||
self.expr_bp(i8::MIN);
|
||||
}
|
||||
|
||||
// Pratt parser.
|
||||
// Ref: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
|
||||
fn expr_bp(&mut self, min_bp: u8) {
|
||||
fn expr_bp(&mut self, min_bp: i8) {
|
||||
// Always consume whitespace first, even though not `allow_prefix`.
|
||||
let Some(tok) = self.peek_non_ws() else {
|
||||
self.error(ErrorKind::ExpectExpr);
|
||||
@ -847,7 +847,7 @@ impl SyntaxKind {
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn prefix_bp(self) -> Option<u8> {
|
||||
fn prefix_bp(self) -> Option<i8> {
|
||||
// See `infix_bp`.
|
||||
Some(match self {
|
||||
T![!] => 13,
|
||||
@ -857,7 +857,7 @@ impl SyntaxKind {
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn postfix_bp(self) -> Option<u8> {
|
||||
fn postfix_bp(self) -> Option<i8> {
|
||||
// See `infix_bp`.
|
||||
Some(match self {
|
||||
T![?] => 21,
|
||||
@ -866,8 +866,10 @@ impl SyntaxKind {
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn infix_bp(self) -> Option<(u8, u8)> {
|
||||
fn infix_bp(self) -> Option<(i8, i8)> {
|
||||
Some(match self {
|
||||
T![|>] => (-1, 0),
|
||||
T![<|] => (0, -1),
|
||||
T![->] => (2, 1),
|
||||
T![||] => (3, 4),
|
||||
T![&&] => (5, 6),
|
||||
@ -892,4 +894,4 @@ impl SyntaxKind {
|
||||
}
|
||||
}
|
||||
|
||||
const APPLY_RBP: u8 = 26;
|
||||
const APPLY_RBP: i8 = 26;
|
||||
|
39
crates/syntax/test_data/parser/ok/0010-pipe.ast
Normal file
39
crates/syntax/test_data/parser/ok/0010-pipe.ast
Normal file
@ -0,0 +1,39 @@
|
||||
SOURCE_FILE@0..27
|
||||
LIST@0..26
|
||||
L_BRACK@0..1 "["
|
||||
PAREN@1..13
|
||||
L_PAREN@1..2 "("
|
||||
BINARY_OP@2..12
|
||||
BINARY_OP@2..9
|
||||
BINARY_OP@2..6
|
||||
LITERAL@2..3
|
||||
INT@2..3 "1"
|
||||
MINUS_GT@3..5 "->"
|
||||
LITERAL@5..6
|
||||
INT@5..6 "2"
|
||||
OR_GT@6..8 "|>"
|
||||
LITERAL@8..9
|
||||
INT@8..9 "3"
|
||||
OR_GT@9..11 "|>"
|
||||
LITERAL@11..12
|
||||
INT@11..12 "4"
|
||||
R_PAREN@12..13 ")"
|
||||
PAREN@13..25
|
||||
L_PAREN@13..14 "("
|
||||
BINARY_OP@14..24
|
||||
LITERAL@14..15
|
||||
INT@14..15 "5"
|
||||
LT_OR@15..17 "<|"
|
||||
BINARY_OP@17..24
|
||||
LITERAL@17..18
|
||||
INT@17..18 "6"
|
||||
LT_OR@18..20 "<|"
|
||||
BINARY_OP@20..24
|
||||
LITERAL@20..21
|
||||
INT@20..21 "7"
|
||||
MINUS_GT@21..23 "->"
|
||||
LITERAL@23..24
|
||||
INT@23..24 "8"
|
||||
R_PAREN@24..25 ")"
|
||||
R_BRACK@25..26 "]"
|
||||
SPACE@26..27 "\n"
|
1
crates/syntax/test_data/parser/ok/0010-pipe.nix
Normal file
1
crates/syntax/test_data/parser/ok/0010-pipe.nix
Normal file
@ -0,0 +1 @@
|
||||
[(1->2|>3|>4)(5<|6<|7->8)]
|
Loading…
Reference in New Issue
Block a user