mirror of
https://github.com/uutils/awk.git
synced 2026-06-10 16:15:04 -07:00
parser: start enforcing non-associativity
This commit is contained in:
@@ -73,6 +73,8 @@ pub enum ParsingError {
|
||||
SpecialVariableCall(Span, String),
|
||||
#[error("Can't use special variable `{}` for indirect function call.", .1)]
|
||||
SpecialVariableIndirectCall(Span, String),
|
||||
#[error("Can't chain non-associative operators.")]
|
||||
NonAssociativeOperator(Span),
|
||||
}
|
||||
|
||||
impl ParsingError {
|
||||
@@ -118,6 +120,7 @@ impl ParsingError {
|
||||
Self::UnexpectedTypedRegex(span) => Some(span.clone()),
|
||||
Self::SpecialVariableCall(span, _) => Some(span.clone()),
|
||||
Self::SpecialVariableIndirectCall(span, _) => Some(span.clone()),
|
||||
Self::NonAssociativeOperator(span) => Some(span.clone()),
|
||||
}
|
||||
}
|
||||
fn hint(&self) -> Option<&'static str> {
|
||||
@@ -151,6 +154,9 @@ impl ParsingError {
|
||||
Self::UnexpectedTypedRegex(_) => Some(
|
||||
"This is only valid in some contexts, like a right-hand assignment or a function argument.",
|
||||
),
|
||||
Self::NonAssociativeOperator(_) => Some(
|
||||
"Some operators can't be chained to avoid logical errors, such as comparison ones.\nExample: write `a == b && b == c` instead of `a == b == c`.",
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +231,12 @@ impl<'a, 'b> Pratt<'a, 'b> {
|
||||
self.typecheck(lex, &lhs)?;
|
||||
// This is just a parsing construct; we only skip if it's a real token.
|
||||
lex.consume_with(|_| op != BinaryOperator::Concat);
|
||||
// Checks invalids like `a == b == c`. The docs are ambiguous about the
|
||||
// associativity of redirection operators, but I couldn't get awk to
|
||||
// error out when chaining them.
|
||||
if op.is_non_associative() && lhs.is_non_associative() {
|
||||
return Err(ParsingError::NonAssociativeOperator(lex.span()));
|
||||
}
|
||||
self.typed_regex = matches!(op, BinaryOperator::Matches | BinaryOperator::MatchesNot);
|
||||
|
||||
let rhs = self.parse_expression(lex, op.binding_power().1)?;
|
||||
@@ -352,6 +358,32 @@ impl<'a, 'b> Pratt<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
trait NonAssociativity {
|
||||
fn is_non_associative(&self) -> bool;
|
||||
}
|
||||
|
||||
impl NonAssociativity for Expr<'_> {
|
||||
fn is_non_associative(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Expr::Node(x) if matches!(x.as_ref(), ExprNode::BinaryOperation(
|
||||
op,
|
||||
_,
|
||||
_
|
||||
) if op.is_non_associative())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl NonAssociativity for BinaryOperator {
|
||||
fn is_non_associative(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Eq | Self::NEq | Self::Gt | Self::Lt | Self::LtE | Self::GtE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_special_var(name: &str) -> bool {
|
||||
matches!(
|
||||
name,
|
||||
|
||||
Reference in New Issue
Block a user