parser: refactor lvalues & ast ownership; add getline pipes

This commit is contained in:
Guillem L. Jara
2026-05-06 04:29:29 +02:00
parent e63ff815f2
commit 48c2c6abc4
7 changed files with 279 additions and 168 deletions
+120 -66
View File
@@ -5,14 +5,14 @@
use std::fmt::Debug;
use bumpalo::{Bump, collections::Vec};
use bumpalo::{Bump, boxed::Box, collections::Vec};
use either::Either;
use hashbrown::{DefaultHashBuilder, HashMap};
use lexer::{Slice, Span, Token};
use crate::{ParsingError, Result, lex::TokenExt};
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Ast<'a> {
pub loads: Vec<'a, Slice<'a>>,
pub begin: Vec<'a, Body<'a>>,
@@ -24,7 +24,7 @@ pub struct Ast<'a> {
pub functions: HashMap<Identifier<'a>, Function<'a>, DefaultHashBuilder, &'a Bump>,
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Rule<'a> {
pub pattern: Option<RulePattern<'a>>,
pub actions: Option<Body<'a>>,
@@ -40,7 +40,7 @@ pub enum Atom<'a> {
Regex(Slice<'a>),
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub enum RulePattern<'a> {
Expression(Expr<'a>),
Range(Expr<'a>, Expr<'a>),
@@ -81,22 +81,21 @@ pub enum Variable<'a> {
Environ,
}
#[derive(Clone)]
pub enum Expr<'a> {
Leaf(Atom<'a>),
Node(&'a ExprNode<'a>),
Node(Box<'a, ExprNode<'a>>),
}
#[derive(Clone)]
pub struct Body<'a>(pub Vec<'a, Statement<'a>>);
pub type Pattern<'a> = Either<RulePattern<'a>, SpecialPattern>;
#[derive(Debug, Clone)]
#[derive(Debug)]
pub enum ExprNode<'a> {
FunctionCall(Identifier<'a>, Vec<'a, Expr<'a>>),
UnaryOperation(UnaryOperator, Expr<'a>),
BinaryOperation(BinaryOperator, Expr<'a>, Expr<'a>),
PlaceOperation(PlaceOperator, Variable<'a>, Expr<'a>),
UnaryPlaceOperation(UnaryPlaceOperator, Place<'a>),
BinaryPlaceOperation(BinaryPlaceOperator, Place<'a>, Expr<'a>),
Ternary(Expr<'a>, Expr<'a>, Expr<'a>),
Getline(Getline<'a>),
}
@@ -131,12 +130,32 @@ pub enum BinaryOperator {
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PlaceOperator {
pub enum UnaryPlaceOperator {
IncrementL,
DecrementL,
IncrementR,
DecrementR,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum BinaryPlaceOperator {
Assignment,
AddAssign,
SubAssign,
MulAssign,
DivAssign,
PowAssign,
ModAssign,
ArrayAccess,
InArray,
}
pub enum Place<'a> {
Record(Expr<'a>),
Variable(Variable<'a>),
ArrayElement(Variable<'a>, Expr<'a>),
}
/// GNU docs: https://www.gnu.org/software/gawk/manual/html_node/Redirection.html
#[derive(Debug, Clone)]
pub enum Redirection<'a> {
@@ -152,19 +171,18 @@ pub enum WriteKind {
Coprocess,
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub enum Getline<'a> {
// getline (var)?
FromInput(Option<Variable<'a>>),
FromInput(Option<Place<'a>>),
// getline (var)? < (file)
FromFile(Option<Variable<'a>>, Expr<'a>),
FromFile(Option<Place<'a>>, Expr<'a>),
// (expr) | getline (var)?
PipeOut(Option<Variable<'a>>, Expr<'a>),
PipeOut(Option<Place<'a>>, Expr<'a>),
// (expr) |& getline (var)?
CoprocessOut(Option<Variable<'a>>, Expr<'a>),
CoprocessOut(Option<Place<'a>>, Expr<'a>),
}
#[derive(Clone)]
pub enum Statement<'a> {
Expression(Expr<'a>),
Command {
@@ -194,7 +212,7 @@ pub enum Statement<'a> {
body: Body<'a>,
},
ForEach {
place: Variable<'a>,
variable: Variable<'a>,
array: Variable<'a>,
body: Body<'a>,
},
@@ -211,13 +229,13 @@ pub enum Statement<'a> {
Exit(Option<Expr<'a>>),
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Function<'a> {
pub args: Vec<'a, Identifier<'a>>,
pub body: Body<'a>,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum Command {
Print,
Printf,
@@ -229,7 +247,7 @@ impl<'a> Expr<'a> {
}
pub fn node(op: impl Into<ExprNode<'a>>, arena: &'a Bump) -> Self {
Self::Node(arena.alloc(op.into()))
Self::Node(Box::new_in(op.into(), arena))
}
}
@@ -245,9 +263,15 @@ impl BinaryOperator {
}
}
impl PlaceOperator {
pub fn expr<'a>(self, a: Variable<'a>, b: Expr<'a>) -> ExprNode<'a> {
ExprNode::PlaceOperation(self, a, b)
impl UnaryPlaceOperator {
pub fn expr(self, a: Place<'_>) -> ExprNode<'_> {
ExprNode::UnaryPlaceOperation(self, a)
}
}
impl BinaryPlaceOperator {
pub fn expr<'a>(self, a: Place<'a>, b: Expr<'a>) -> ExprNode<'a> {
ExprNode::BinaryPlaceOperation(self, a, b)
}
}
@@ -275,6 +299,12 @@ impl<'a> From<Variable<'a>> for Atom<'a> {
}
}
impl<'a> From<Variable<'a>> for Place<'a> {
fn from(value: Variable<'a>) -> Self {
Self::Variable(value)
}
}
impl From<f64> for Atom<'_> {
fn from(value: f64) -> Self {
Self::Number(value)
@@ -323,16 +353,34 @@ impl<'a> BinaryOperator {
}
}
impl<'a> PlaceOperator {
impl UnaryPlaceOperator {
pub fn parse_prefix(value: &Token<'_>, span: &Span) -> Result<Self> {
match value {
Token::Increment => Ok(Self::IncrementL),
Token::Decrement => Ok(Self::DecrementL),
_ => Err(ParsingError::OperatorExpectsVariable(span.clone())),
}
}
pub fn parse_suffix(value: &Token<'_>, span: &Span) -> Result<Self> {
match value {
Token::Increment => Ok(Self::IncrementR),
Token::Decrement => Ok(Self::DecrementR),
_ => Err(ParsingError::OperatorExpectsVariable(span.clone())),
}
}
}
impl<'a> BinaryPlaceOperator {
pub fn parse(value: &Token<'a>, span: &Span) -> Result<Self> {
match value {
Token::Assignment
| Token::PlusAssign
| Token::MinusAssign
| Token::StarAssign
| Token::SlashAssign
| Token::CaretAssign
| Token::PercentAssign => Ok(Self::Assignment),
Token::Assignment => Ok(Self::Assignment),
Token::PlusAssign => Ok(Self::AddAssign),
Token::MinusAssign => Ok(Self::SubAssign),
Token::StarAssign => Ok(Self::MulAssign),
Token::SlashAssign => Ok(Self::DivAssign),
Token::CaretAssign => Ok(Self::PowAssign),
Token::PercentAssign => Ok(Self::ModAssign),
Token::OpenBracket => Ok(Self::ArrayAccess),
Token::In => Ok(Self::InArray),
_ => Err(ParsingError::UnexpectedToken(
@@ -343,6 +391,34 @@ impl<'a> PlaceOperator {
}
}
impl<'a> Place<'a> {
pub fn promote_from(expr: Expr<'a>, span: Span) -> Result<Self, (Expr<'a>, ParsingError)> {
match expr {
Expr::Leaf(Atom::Variable(var)) => Ok(Self::Variable(var)),
Expr::Node(node)
if matches!(
&*node,
&ExprNode::UnaryOperation(UnaryOperator::Record, _)
| &ExprNode::BinaryPlaceOperation(
BinaryPlaceOperator::ArrayAccess,
Place::Variable(_),
_
)
) =>
{
match Box::into_inner(node) {
ExprNode::UnaryOperation(_, index) => Ok(Self::Record(index)),
ExprNode::BinaryPlaceOperation(_, Place::Variable(var), index) => {
Ok(Self::ArrayElement(var, index))
}
_ => unreachable!("Box is magic; handled awkwardly in the match guard."),
}
}
_ => Err((expr, ParsingError::OperatorExpectsVariable(span))),
}
}
}
impl WriteKind {
pub fn parse(value: &Token) -> Option<Self> {
match value {
@@ -352,7 +428,7 @@ impl WriteKind {
}
}
pub fn expr_getline<'a>(self, var: Option<Variable<'a>>, expr: Expr<'a>) -> Getline<'a> {
pub fn expr_getline<'a>(self, var: Option<Place<'a>>, expr: Expr<'a>) -> Getline<'a> {
match self {
Self::Pipe => Getline::PipeOut(var, expr),
Self::Coprocess => Getline::CoprocessOut(var, expr),
@@ -360,35 +436,6 @@ impl WriteKind {
}
}
impl BinaryOperator {
pub fn unfold(token: &Token) -> Option<Self> {
match token {
Token::PlusAssign => Some(Self::Add),
Token::MinusAssign => Some(Self::Subtract),
Token::StarAssign => Some(Self::Multiply),
Token::SlashAssign => Some(Self::Divide),
Token::PercentAssign => Some(Self::Modulo),
Token::CaretAssign => Some(Self::Raise),
_ => None,
}
}
pub fn unfold_prefix(token: &Token<'_>) -> Option<(Self, u8)> {
match token {
Token::Increment => Some((Self::Add, binding_powers::BP_INC_DEC)),
Token::Decrement => Some((Self::Subtract, binding_powers::BP_INC_DEC)),
_ => None,
}
}
pub fn unfold_suffix(token: &Token<'_>) -> Option<(Self, Self, u8)> {
match token {
Token::Increment => Some((Self::Add, Self::Subtract, binding_powers::BP_INC_DEC)),
Token::Decrement => Some((Self::Subtract, Self::Add, binding_powers::BP_INC_DEC)),
_ => None,
}
}
}
pub struct Ternary;
mod binding_powers {
@@ -435,13 +482,20 @@ impl BindingPower for BinaryOperator {
}
}
impl BindingPower for PlaceOperator {
impl BindingPower for UnaryPlaceOperator {
type Bp = u8;
fn binding_power(&self) -> Self::Bp {
binding_powers::BP_INC_DEC
}
}
impl BindingPower for BinaryPlaceOperator {
type Bp = (u8, u8);
fn binding_power(&self) -> Self::Bp {
match self {
Self::Assignment => binding_powers::BP_ASSIGN,
Self::ArrayAccess => (binding_powers::BP_GROUPING, 0),
Self::InArray => binding_powers::BP_IN,
_ => binding_powers::BP_ASSIGN,
}
}
}
@@ -460,7 +514,6 @@ impl BindingPower for UnaryOperator {
impl BindingPower for Ternary {
type Bp = (u8, u8);
fn binding_power(&self) -> Self::Bp {
binding_powers::BP_TERNARY
}
@@ -476,7 +529,7 @@ impl Debug for Expr<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Leaf(atom) => write!(f, "{atom:?}"),
Self::Node(expr) => match expr {
Self::Node(expr) => match expr.as_ref() {
ExprNode::FunctionCall(ident, args) => {
write!(f, "({ident:?}")?;
for arg in args {
@@ -486,7 +539,8 @@ impl Debug for Expr<'_> {
}
ExprNode::UnaryOperation(op, a) => write!(f, "({op:?} {a:?})"),
ExprNode::BinaryOperation(op, a, b) => write!(f, "({op:?} {a:?} {b:?})"),
ExprNode::PlaceOperation(op, a, b) => write!(f, "({op:?} {a:?} {b:?})"),
ExprNode::BinaryPlaceOperation(op, a, b) => write!(f, "({op:?} {a:?} {b:?})"),
ExprNode::UnaryPlaceOperation(op, a) => write!(f, "({op:?} {a:?})"),
ExprNode::Ternary(a, b, c) => write!(f, "(?: {a:?} {b:?} {c:?})"),
ExprNode::Getline(getline) => match getline {
Getline::FromInput(Some(a)) => write!(f, "(getline {a:?})"),
+6
View File
@@ -150,3 +150,9 @@ pub fn report_error<'a>(
}
(Box::new(report.finish()), Source::from(source))
}
impl<T> From<(T, Self)> for ParsingError {
fn from(value: (T, Self)) -> Self {
value.1
}
}
+51 -14
View File
@@ -3,8 +3,9 @@ use std::fmt::{Debug, Display, Formatter, Result, Write};
use crate::{
Ast, Function,
ast::{
Atom, BinaryOperator, BindingPower, Body, Command, Expr, ExprNode, Getline, PlaceOperator,
Rule, RulePattern, Statement, Ternary, UnaryOperator,
Atom, BinaryOperator, BinaryPlaceOperator, BindingPower, Body, Command, Expr, ExprNode,
Getline, Place, Rule, RulePattern, Statement, Ternary, UnaryOperator, UnaryPlaceOperator,
Variable,
},
};
@@ -138,8 +139,12 @@ impl Display for Statement<'_> {
write!(f, ") ")?;
write_body(f, body, indent)
}
Self::ForEach { place, array, body } => {
write!(f, "for ({place:?} in {array:?}) ")?;
Self::ForEach {
variable,
array,
body,
} => {
write!(f, "for ({variable} in {array}) ")?;
write_body(f, body, indent)
}
Self::Switch {
@@ -220,6 +225,23 @@ impl Display for Atom<'_> {
}
}
impl Display for Variable<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
<Self as Debug>::fmt(self, f)
}
}
impl Display for Place<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Variable(var) => <_ as Display>::fmt(var, f),
Self::Record(Expr::Leaf(leaf)) => write!(f, "${leaf}"),
Self::Record(Expr::Node(node)) => write!(f, "$({node})"),
Self::ArrayElement(var, expr) => write!(f, "{var}[{expr}]"),
}
}
}
impl Display for ExprNode<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let (indent, parent_bp) = decode(f);
@@ -249,21 +271,27 @@ impl Display for ExprNode<'_> {
write!(f, "{a:left_w$}{op}{b:right_w$}")
}
}
Self::PlaceOperation(op, place, idx) => {
Self::UnaryPlaceOperation(op, place) => match op {
UnaryPlaceOperator::IncrementL => write!(f, "++{place}"),
UnaryPlaceOperator::DecrementL => write!(f, "--{place}"),
UnaryPlaceOperator::DecrementR => write!(f, "{place}--"),
UnaryPlaceOperator::IncrementR => write!(f, "{place}++"),
},
Self::BinaryPlaceOperation(op, place, idx) => {
let (left_bp, right_bp) = op.binding_power();
if op == &PlaceOperator::ArrayAccess {
if op == &BinaryPlaceOperator::ArrayAccess {
let idx_w = encode(indent, 0);
if left_bp < parent_bp {
write!(f, "({place:?}[{idx:idx_w$}])")
write!(f, "({place}[{idx:idx_w$}])")
} else {
write!(f, "{place:?}[{idx:idx_w$}]")
write!(f, "{place}[{idx:idx_w$}]")
}
} else {
let right_w = encode(indent, right_bp);
if left_bp < parent_bp {
write!(f, "({place:?}{op}{idx:right_w$})")
write!(f, "({place}{op}{idx:right_w$})")
} else {
write!(f, "{place:?}{op}{idx:right_w$}")
write!(f, "{place}{op}{idx:right_w$}")
}
}
}
@@ -283,11 +311,14 @@ impl Display for ExprNode<'_> {
}
}
Self::Getline(getline) => match getline {
Getline::FromInput(Some(var)) => write!(f, "getline {var:?}"),
Getline::FromInput(Some(var)) => write!(f, "getline {var}"),
Getline::FromInput(None) => write!(f, "getline"),
Getline::FromFile(Some(var), file) => write!(f, "getline {var:?} < {file}"),
Getline::FromFile(Some(var), file) => write!(f, "getline {var} < {file}"),
Getline::FromFile(None, file) => write!(f, "getline < {file}"),
_ => todo!(),
Getline::PipeOut(Some(place), e) => write!(f, "{e} | getline {place}"),
Getline::PipeOut(None, e) => write!(f, "{e} | getline"),
Getline::CoprocessOut(Some(place), e) => write!(f, "{e} |& getline {place}"),
Getline::CoprocessOut(None, e) => write!(f, "{e} |& getline"),
},
}
}
@@ -328,10 +359,16 @@ impl Display for BinaryOperator {
}
}
impl Display for PlaceOperator {
impl Display for BinaryPlaceOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Assignment => write!(f, " = "),
Self::AddAssign => write!(f, " += "),
Self::SubAssign => write!(f, " -= "),
Self::MulAssign => write!(f, " *= "),
Self::DivAssign => write!(f, " /= "),
Self::PowAssign => write!(f, " ^= "),
Self::ModAssign => write!(f, " %= "),
Self::InArray => write!(f, " in "),
Self::ArrayAccess => unreachable!("Array access needs special handling."),
}
+3 -1
View File
@@ -189,7 +189,8 @@ impl TokenExt for Token<'_> {
)
}
fn is_atom(&self) -> bool {
matches!(self, Token::Number(_) | Token::String(_) | Token::Regex(_)) || self.is_place()
matches!(self, Token::Number(_) | Token::String(_) | Token::Regex(_))
|| self.is_place() && self != &Token::Record
}
fn is_expr_start(&self) -> bool {
self.is_atom()
@@ -216,6 +217,7 @@ impl TokenExt for Token<'_> {
| Token::RlengthVariable
| Token::EnvironVariable
| Token::Identifier(_)
| Token::Record
)
}
fn is_pattern_start(&self) -> bool {
+14 -11
View File
@@ -15,7 +15,7 @@ mod tests;
use std::{fmt::Debug, mem::replace};
use bumpalo::{Bump, collections::Vec, vec};
use bumpalo::{Bump, boxed::Box, collections::Vec, vec};
use either::Either::{Left, Right};
use hashbrown::HashMap;
use lexer::{LexingError, Span, Token};
@@ -24,8 +24,8 @@ pub use crate::ast::Ast;
pub use crate::lex::Lexer;
use crate::{
ast::{
Atom, Body, Command, Expr, ExprNode, Function, Identifier, Pattern, PlaceOperator, Rule,
RulePattern, SpecialPattern, Statement, Variable,
Atom, BinaryPlaceOperator, Body, Command, Expr, ExprNode, Function, Identifier, Pattern,
Rule, RulePattern, SpecialPattern, Statement, Variable,
},
diagnostics::{ParsingError, report_error},
lex::TokenExt,
@@ -50,7 +50,7 @@ impl From<LexingError> for ParsingError {
}
type AriadneErr<'a> = (
Box<ariadne::Report<'a, (&'a str, Span)>>,
std::boxed::Box<ariadne::Report<'a, (&'a str, Span)>>,
ariadne::Source<&'a str>,
);
@@ -242,7 +242,7 @@ impl<'a> Parser<'a> {
let init = (!lex.consume(&Token::Semicolon))
.then(|| self.parse_expression(lex))
.transpose()?;
if lex.consume(&Token::Semicolon) || init.is_none() {
if init.is_none() || lex.consume(&Token::Semicolon) {
let condition = self.parse_for_fragment::<false>(lex)?;
let update = self.parse_for_fragment::<true>(lex)?;
let body = self.parse_statement_body(lex)?;
@@ -253,11 +253,14 @@ impl<'a> Parser<'a> {
body,
}
} else {
let Some(Expr::Node(ExprNode::PlaceOperation(
PlaceOperator::InArray,
place,
let Some(Expr::Node(node)) = init else {
return Err(ParsingError::InvalidForLoop(lex.span()));
};
let ExprNode::BinaryPlaceOperation(
BinaryPlaceOperator::InArray,
ast::Place::Variable(variable),
Expr::Leaf(Atom::Variable(array)),
))) = init
) = Box::into_inner(node)
else {
return Err(ParsingError::InvalidForLoop(lex.span()));
};
@@ -267,8 +270,8 @@ impl<'a> Parser<'a> {
)?;
let body = self.parse_statement_body(lex)?;
Statement::ForEach {
place: *place,
array: *array,
variable,
array,
body,
}
}
+67 -72
View File
@@ -3,8 +3,8 @@ use lexer::Token;
use crate::{
IdentifierExt, Lexer, Parser, Result,
ast::{
Atom, BinaryOperator, BindingPower, Expr, ExprNode, Getline, PlaceOperator, Ternary,
UnaryOperator, Variable, WriteKind,
BinaryOperator, BinaryPlaceOperator, BindingPower, Expr, ExprNode, Getline, Place, Ternary,
UnaryOperator, UnaryPlaceOperator, Variable, WriteKind,
},
diagnostics::ParsingError,
lex::TokenExt,
@@ -39,35 +39,35 @@ impl<'a, 'b> Pratt<'a, 'b> {
fn fold_rhs(&mut self, lex: &mut Lexer<'a>, mut lhs: Expr<'a>, min_bp: u8) -> Result<Expr<'a>> {
while let Some((next, span)) = lex.peek_with_span() {
let next = next?;
lhs = if let Ok(op) = BinaryOperator::parse(next, &span)
lhs = if let Ok(op) = UnaryPlaceOperator::parse_suffix(next, &span) {
if op.binding_power() < min_bp {
break;
}
lex.next();
let place = Place::promote_from(lhs.take(), lex.span())?;
Expr::node(op.expr(place), self.parser.arena)
} else if let Ok(op) = BinaryPlaceOperator::parse(next, &span) {
if op.binding_power().0 < min_bp {
break;
}
let place = Place::promote_from(lhs.take(), lex.span())?;
self.parse_place_op(lex, op, place)?
} else if let Ok(op) = BinaryOperator::parse(next, &span)
&& !matches!(next, Token::Increment | Token::Decrement)
{
if op.binding_power().0 < min_bp {
break;
}
self.parse_infix_op(lex, op, lhs)?
} else if let Ok(op) = PlaceOperator::parse(next, &span) {
let Expr::Leaf(Atom::Variable(var)) = lhs.take() else {
return Err(ParsingError::OperatorExpectsVariable(lex.span()));
};
if op.binding_power().0 < min_bp {
break;
}
self.parse_place_op(lex, op, var)?
} else if next == &Token::QuestionMark {
if Ternary.binding_power().0 < min_bp {
break;
}
self.parse_ternary(lex, lhs)?
} else if let Some((op, reciprocal, bp)) = BinaryOperator::unfold_suffix(next) {
let Expr::Leaf(Atom::Variable(rhs)) = lhs else {
return Err(ParsingError::OperatorExpectsVariable(lex.span()));
};
if bp < min_bp {
} else if let Some(op) = WriteKind::parse(next) {
if BinaryOperator::Concat.binding_power().0 < min_bp {
break;
}
self.unfolded_suffix_op(lex, op, reciprocal, rhs)
} else if let Some(op) = WriteKind::parse(next) {
self.parse_getline_pipe(lex, op, lhs)?
} else {
break;
@@ -87,15 +87,10 @@ impl<'a, 'b> Pratt<'a, 'b> {
fn parse_prefix(&mut self, lex: &mut Lexer<'a>) -> Result<Expr<'a>> {
let next = lex.expect_next()?;
if let Some((op, bp)) = BinaryOperator::unfold_prefix(&next) {
let Expr::Leaf(Atom::Variable(rhs)) = self.parse_expression(lex, bp)? else {
return Err(ParsingError::OperatorExpectsVariable(lex.span()));
};
if let Ok(op) = UnaryPlaceOperator::parse_prefix(&next, &lex.span()) {
let rhs = self.parse_expression(lex, op.binding_power())?;
Ok(Expr::node(
PlaceOperator::Assignment.expr(
rhs,
Expr::node(op.expr(Expr::leaf(rhs), Expr::leaf(1.)), self.parser.arena),
),
op.expr(Place::promote_from(rhs, lex.span())?),
self.parser.arena,
))
} else if let Ok(op) = UnaryOperator::parse(&next, &lex.peeked_span()?) {
@@ -107,22 +102,32 @@ impl<'a, 'b> Pratt<'a, 'b> {
}
fn parse_prefix_getline(&mut self, lex: &mut Lexer<'a>) -> Result<Expr<'a>> {
// Consumes with maximum precedence the following ident and/or
// Consumes with maximum precedence the following place and/or
// redirection reading from file.
// TODO: move to specialized rhs loop to disambiguate call/variable.
let var = if lex.peek_with(Token::is_place) {
let next = lex.expect_next()?;
self.parser.get_place(lex, next)
} else {
None
};
if lex.consume(&Token::LesserThan) {
Ok(Expr::node(
Getline::FromFile(var, self.parse_expression(lex, 0)?),
self.parser.arena,
let place = if lex.peek_with(Token::is_place) {
Some(Place::promote_from(
self.parse_expression(lex, BinaryOperator::Concat.binding_power().1)?,
lex.span(),
))
} else {
Ok(Expr::node(Getline::FromInput(var), self.parser.arena))
None
}
.transpose();
let getline = |gl| Expr::node(ExprNode::Getline(gl), self.parser.arena);
match place {
Err((expr, _)) => Ok(Expr::node(
BinaryOperator::Concat.expr(getline(Getline::FromInput(None)), expr),
self.parser.arena,
)),
Ok(place) => {
if lex.consume(&Token::LesserThan) {
let file = self.parse_expression(lex, BinaryOperator::Lt.binding_power().1)?;
Ok(getline(Getline::FromFile(place, file)))
} else {
Ok(getline(Getline::FromInput(place)))
}
}
}
}
@@ -154,15 +159,15 @@ impl<'a, 'b> Pratt<'a, 'b> {
fn parse_place_op(
&mut self,
lex: &mut Lexer<'a>,
op: PlaceOperator,
var: Variable<'a>,
op: BinaryPlaceOperator,
place: Place<'a>,
) -> Result<Expr<'a>> {
let token_op = lex.expect_next()?;
lex.next();
let mut rhs = self.parse_expression(lex, op.binding_power().1)?;
if let Some(op) = BinaryOperator::unfold(&token_op) {
rhs = Expr::node(op.expr(Expr::leaf(var), rhs), self.parser.arena);
} else if op == PlaceOperator::ArrayAccess {
if op == BinaryPlaceOperator::ArrayAccess {
if !matches!(place, Place::Variable(_)) {
return Err(ParsingError::OperatorExpectsVariable(lex.span()));
}
while lex.consume(&Token::Comma) {
rhs = Expr::node(
BinaryOperator::Concat.expr(
@@ -178,7 +183,7 @@ impl<'a, 'b> Pratt<'a, 'b> {
}
lex.expect(&Token::ClosedBracket, ParsingError::UnclosedArrayAccess)?;
}
Ok(Expr::node(op.expr(var, rhs), self.parser.arena))
Ok(Expr::node(op.expr(place, rhs), self.parser.arena))
}
fn parse_ternary(&mut self, lex: &mut Lexer<'a>, lhs: Expr<'a>) -> Result<Expr<'a>> {
@@ -193,42 +198,32 @@ impl<'a, 'b> Pratt<'a, 'b> {
))
}
fn unfolded_suffix_op(
&mut self,
lex: &mut Lexer<'a>,
op: BinaryOperator,
reciprocal: BinaryOperator,
rhs: Variable<'a>,
) -> Expr<'a> {
lex.next();
Expr::node(
reciprocal.expr(
Expr::node(
PlaceOperator::Assignment.expr(
rhs,
Expr::node(op.expr(Expr::leaf(rhs), Expr::leaf(1.)), self.parser.arena),
),
self.parser.arena,
),
Expr::leaf(1.),
),
self.parser.arena,
)
}
fn parse_getline_pipe(
&mut self,
lex: &mut Lexer<'a>,
op: WriteKind,
lhs: Expr<'a>,
) -> Result<Expr<'a>> {
lex.next();
lex.expect(&Token::Getline, |span| {
ParsingError::UnexpectedToken(
span,
"operand must precede `getline` in an expression.".into(),
)
})?;
// TODO: move to specialized rhs loop to disambiguate call/variable.
Ok(Expr::node(op.expr_getline(None, lhs), self.parser.arena))
let pipe = |place| Expr::node(op.expr_getline(place, lhs), self.parser.arena);
if lex.peek_with(Token::is_place) {
let expr = self.parse_expression(lex, BinaryOperator::Concat.binding_power().1)?;
match Place::promote_from(expr, lex.span()) {
Ok(place) => Ok(pipe(Some(place))),
Err((expr, _)) => Ok(Expr::node(
BinaryOperator::Concat.expr(pipe(None), expr),
self.parser.arena,
)),
}
} else {
Ok(pipe(None))
}
}
}
+18 -4
View File
@@ -5,7 +5,7 @@
use std::fmt::{Debug, Display, Formatter, Result};
use crate::ast::{Atom, Body, Expr, Identifier, Statement, Variable};
use crate::ast::{Atom, Body, Expr, Identifier, Place, Statement, Variable};
const PRETTY_PRINT_INDENT: usize = 2;
@@ -111,11 +111,15 @@ impl Debug for Statement<'_> {
write!(f, "(for {init:?} {condition:?} {update:?} {body:?})")
}
}
Self::ForEach { place, array, body } => {
Self::ForEach {
variable,
array,
body,
} => {
if alt {
write!(f, "(for-each {place:?} {array:?}\n{pad}{body:#ni$?})")
write!(f, "(for-each {variable:?} {array:?}\n{pad}{body:#ni$?})")
} else {
write!(f, "(for-each {place:?} {array:?} {body:?})")
write!(f, "(for-each {variable:?} {array:?} {body:?})")
}
}
Self::Switch {
@@ -237,6 +241,16 @@ impl Debug for Atom<'_> {
}
}
impl Debug for Place<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::Record(expr) => write!(f, "($ {expr:?})"),
Self::Variable(var) => <_ as Debug>::fmt(var, f),
Self::ArrayElement(var, index) => write!(f, "(index {var:?} {index:?})"),
}
}
}
impl Debug for Variable<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {