diff --git a/lexer/src/lib.rs b/lexer/src/lib.rs index 5c82dae..1640f64 100644 --- a/lexer/src/lib.rs +++ b/lexer/src/lib.rs @@ -107,7 +107,7 @@ pub enum Token<'a> { #[token("function", accept_expression)] #[token("func", |lex| parse_non_posix_keyword(lex, Token::Function))] Function, - #[token("length", accept_expression)] + #[token("length", accept_operator)] Length, #[token("substr", accept_expression)] Substr, diff --git a/parser/src/pratt.rs b/parser/src/pratt.rs index f002f00..0aa3439 100644 --- a/parser/src/pratt.rs +++ b/parser/src/pratt.rs @@ -9,9 +9,9 @@ use lexer::{Span, Token}; use crate::{ ArrayPlace, IdentifierExt, Lexer, Parser, Result, ast::{ - ArrayOperator, Atom, BinaryOperator, BinaryPlaceOperator, BindingPower, Expr, ExprNode, - Getline, Place, Redirection, Ternary, UnaryOperator, UnaryPlaceOperator, Variable, - WriteKind, + ArrayOperator, Atom, BinaryOperator, BinaryPlaceOperator, BindingPower, BuiltinFunction, + Expr, ExprNode, Getline, Place, Redirection, Ternary, UnaryOperator, UnaryPlaceOperator, + Variable, WriteKind, }, diagnostics::ParsingError, lex::{SpanExt, TokenExt}, @@ -358,6 +358,13 @@ impl<'a, 'b> Pratt<'a, 'b> { lex.span().since(anchor), )) } + } else if next == Token::Length && !lex.peek_is(&Token::OpenParent) { + // POSIX: `length` without an argument list is `length($0)`. + Ok(Expr::node( + ExprNode::BuiltinCall(BuiltinFunction::Length, Vec::new_in(self.parser.arena)), + self.parser, + lex.span().since(anchor), + )) } else if let Some(builtin) = next.maps_to_builtin() { self.parser .parse_function_call::(lex, |args| ExprNode::BuiltinCall(builtin, args)) diff --git a/tests/by-util/test_awk.rs b/tests/by-util/test_awk.rs index 3003aab..1c16d6b 100644 --- a/tests/by-util/test_awk.rs +++ b/tests/by-util/test_awk.rs @@ -982,3 +982,17 @@ fn nr_counts_across_files() { .succeeds() .stdout_is("1 1\n2 2\n3 1\n"); } + +#[test] +fn length_without_parentheses_is_length_of_record() { + ucmd() + .arg("length > 2 { print length, length / 2 } BEGIN { print length }") + .pipe_in("ab\nabcd\nxyz\n") + .succeeds() + .stdout_only("0\n4 2\n3 1.5\n"); + ucmd() + .arg("{ print length(/b/), length $1 }") + .pipe_in("abc def\nxyz\n") + .succeeds() + .stdout_only("1 7abc\n1 3xyz\n"); +}