use core::mem::take;
use core::ops;
use crate::ast::prelude::*;
#[test]
#[cfg(not(miri))]
fn ast_parse() {
rt::<ast::Expr>("()");
rt::<ast::Expr>("foo[\"foo\"]");
rt::<ast::Expr>("foo[\"bar\"]");
rt::<ast::Expr>("foo.bar()");
rt::<ast::Expr>("var()");
rt::<ast::Expr>("var");
rt::<ast::Expr>("42");
rt::<ast::Expr>("1 + 2 / 3 - 4 * 1");
rt::<ast::Expr>("let var = 42");
rt::<ast::Expr>("let var = \"foo bar\"");
rt::<ast::Expr>("var[\"foo\"] = \"bar\"");
rt::<ast::Expr>("let var = objects[\"foo\"] + 1");
rt::<ast::Expr>("var = 42");
let expr = rt::<ast::Expr>(
r#"
if 1 { } else { if 2 { } else { } }
"#,
);
assert!(matches!(expr, ast::Expr::If(..)));
rt::<ast::Expr>("foo.bar.baz()");
rt::<ast::Expr>("foo[0][1][2]");
rt::<ast::Expr>("foo.bar()[0].baz()[1]");
rt::<ast::Expr>("42 is i64::i64");
rt::<ast::Expr>("{ let x = 1; x }");
let expr = rt::<ast::Expr>("#[cfg(debug_assertions)] { assert_eq(x, 32); }");
assert!(
matches!(expr, ast::Expr::Block(b) if b.attributes.len() == 1 && b.block.statements.len() == 1)
);
rt::<ast::Expr>("#{\"foo\": b\"bar\"}");
rt::<ast::Expr>("Disco {\"never_died\": true }");
rt::<ast::Expr>("(false, 1, 'n')");
rt::<ast::Expr>("[false, 1, 'b']");
let expr = rt::<ast::Expr>(r#"if true {} else {}"#);
assert!(matches!(expr, ast::Expr::If(..)));
let expr = rt::<ast::Expr>("if 1 { } else { if 2 { } else { } }");
assert!(matches!(expr, ast::Expr::If(..)));
let expr = rt::<ast::Expr>(r#"while true {}"#);
assert!(matches!(expr, ast::Expr::While(..)));
rt::<ast::Expr>("format!(\"{}\", a).bar()");
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct EagerBrace(bool);
pub(crate) const EAGER_BRACE: EagerBrace = EagerBrace(true);
pub(crate) const NOT_EAGER_BRACE: EagerBrace = EagerBrace(false);
impl ops::Deref for EagerBrace {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct EagerBinary(bool);
pub(crate) const EAGER_BINARY: EagerBinary = EagerBinary(true);
pub(crate) const NOT_EAGER_BINARY: EagerBinary = EagerBinary(false);
impl ops::Deref for EagerBinary {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Callable(bool);
pub(crate) const CALLABLE: Callable = Callable(true);
pub(crate) const NOT_CALLABLE: Callable = Callable(false);
impl ops::Deref for Callable {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
pub enum Expr {
Path(ast::Path),
Assign(ast::ExprAssign),
While(ast::ExprWhile),
Loop(ast::ExprLoop),
For(ast::ExprFor),
Let(ast::ExprLet),
If(ast::ExprIf),
Match(ast::ExprMatch),
Call(ast::ExprCall),
FieldAccess(ast::ExprFieldAccess),
Binary(ast::ExprBinary),
Unary(ast::ExprUnary),
Index(ast::ExprIndex),
Break(ast::ExprBreak),
Continue(ast::ExprContinue),
Yield(ast::ExprYield),
Block(ast::ExprBlock),
Return(ast::ExprReturn),
Await(ast::ExprAwait),
Try(ast::ExprTry),
Select(ast::ExprSelect),
Closure(ast::ExprClosure),
Lit(ast::ExprLit),
Object(ast::ExprObject),
Tuple(ast::ExprTuple),
Vec(ast::ExprVec),
Range(ast::ExprRange),
Empty(ast::ExprEmpty),
Group(ast::ExprGroup),
MacroCall(ast::MacroCall),
}
impl Expr {
pub(crate) fn attributes(&self) -> &[ast::Attribute] {
match self {
Self::Path(_) => &[],
Self::Break(expr) => &expr.attributes,
Self::Continue(expr) => &expr.attributes,
Self::Yield(expr) => &expr.attributes,
Self::Block(expr) => &expr.attributes,
Self::Return(expr) => &expr.attributes,
Self::Closure(expr) => &expr.attributes,
Self::Match(expr) => &expr.attributes,
Self::While(expr) => &expr.attributes,
Self::Loop(expr) => &expr.attributes,
Self::For(expr) => &expr.attributes,
Self::Let(expr) => &expr.attributes,
Self::If(expr) => &expr.attributes,
Self::Select(expr) => &expr.attributes,
Self::Lit(expr) => &expr.attributes,
Self::Assign(expr) => &expr.attributes,
Self::Binary(expr) => &expr.attributes,
Self::Call(expr) => &expr.attributes,
Self::FieldAccess(expr) => &expr.attributes,
Self::Group(expr) => &expr.attributes,
Self::Empty(expr) => &expr.attributes,
Self::Unary(expr) => &expr.attributes,
Self::Index(expr) => &expr.attributes,
Self::Await(expr) => &expr.attributes,
Self::Try(expr) => &expr.attributes,
Self::MacroCall(expr) => &expr.attributes,
Self::Object(expr) => &expr.attributes,
Self::Range(expr) => &expr.attributes,
Self::Tuple(expr) => &expr.attributes,
Self::Vec(expr) => &expr.attributes,
}
}
pub(crate) fn needs_semi(&self) -> bool {
match self {
Self::While(_) => false,
Self::Loop(_) => false,
Self::For(_) => false,
Self::If(_) => false,
Self::Match(_) => false,
Self::Block(_) => false,
Self::Select(_) => false,
Self::MacroCall(macro_call) => macro_call.needs_semi(),
_ => true,
}
}
pub(crate) fn is_callable(&self, callable: bool) -> bool {
match self {
Self::While(_) => false,
Self::Loop(_) => callable,
Self::For(_) => false,
Self::If(_) => callable,
Self::Match(_) => callable,
Self::Select(_) => callable,
_ => true,
}
}
pub(crate) fn take_attributes(&mut self) -> Vec<ast::Attribute> {
match self {
Self::Path(_) => Vec::new(),
Self::Break(expr) => take(&mut expr.attributes),
Self::Continue(expr) => take(&mut expr.attributes),
Self::Yield(expr) => take(&mut expr.attributes),
Self::Block(expr) => take(&mut expr.attributes),
Self::Return(expr) => take(&mut expr.attributes),
Self::Closure(expr) => take(&mut expr.attributes),
Self::Match(expr) => take(&mut expr.attributes),
Self::While(expr) => take(&mut expr.attributes),
Self::Loop(expr) => take(&mut expr.attributes),
Self::For(expr) => take(&mut expr.attributes),
Self::Let(expr) => take(&mut expr.attributes),
Self::If(expr) => take(&mut expr.attributes),
Self::Select(expr) => take(&mut expr.attributes),
Self::Lit(expr) => take(&mut expr.attributes),
Self::Assign(expr) => take(&mut expr.attributes),
Self::Binary(expr) => take(&mut expr.attributes),
Self::Call(expr) => take(&mut expr.attributes),
Self::FieldAccess(expr) => take(&mut expr.attributes),
Self::Group(expr) => take(&mut expr.attributes),
Self::Empty(expr) => take(&mut expr.attributes),
Self::Unary(expr) => take(&mut expr.attributes),
Self::Index(expr) => take(&mut expr.attributes),
Self::Await(expr) => take(&mut expr.attributes),
Self::Try(expr) => take(&mut expr.attributes),
Self::Object(expr) => take(&mut expr.attributes),
Self::Range(expr) => take(&mut expr.attributes),
Self::Vec(expr) => take(&mut expr.attributes),
Self::Tuple(expr) => take(&mut expr.attributes),
Self::MacroCall(expr) => take(&mut expr.attributes),
}
}
pub(crate) fn is_lit(&self) -> bool {
match self {
Self::Lit(..) => return true,
Self::Unary(ast::ExprUnary {
op: ast::UnOp::Neg(..),
expr,
..
}) => {
return matches!(
&**expr,
Self::Lit(ast::ExprLit {
lit: ast::Lit::Number(..),
..
})
);
}
_ => (),
}
false
}
pub(crate) fn from_lit(lit: ast::Lit) -> Self {
Self::Lit(ast::ExprLit {
attributes: Vec::new(),
lit,
})
}
pub(crate) fn parse_without_eager_brace(p: &mut Parser<'_>) -> Result<Self> {
Self::parse_with(p, NOT_EAGER_BRACE, EAGER_BINARY, CALLABLE)
}
pub(crate) fn parse_with_meta(
p: &mut Parser<'_>,
attributes: &mut Vec<ast::Attribute>,
callable: Callable,
) -> Result<Self> {
let lhs = primary(p, attributes, EAGER_BRACE, callable)?;
let lookahead = ast::BinOp::from_peeker(p.peeker());
binary(p, lhs, lookahead, 0, EAGER_BRACE)
}
pub(crate) fn parse_with(
p: &mut Parser<'_>,
eager_brace: EagerBrace,
eager_binary: EagerBinary,
callable: Callable,
) -> Result<Self> {
let mut attributes = p.parse()?;
let expr = primary(p, &mut attributes, eager_brace, callable)?;
let expr = if *eager_binary {
let lookeahead = ast::BinOp::from_peeker(p.peeker());
binary(p, expr, lookeahead, 0, eager_brace)?
} else {
expr
};
if let Some(span) = attributes.option_span() {
return Err(compile::Error::unsupported(span, "attributes"));
}
Ok(expr)
}
pub(crate) fn parse_with_meta_path(
p: &mut Parser<'_>,
attributes: &mut Vec<ast::Attribute>,
path: ast::Path,
eager_brace: EagerBrace,
) -> Result<Self> {
if *eager_brace && p.peek::<T!['{']>()? {
let ident = ast::ObjectIdent::Named(path);
return Ok(Self::Object(ast::ExprObject::parse_with_meta(
p,
take(attributes),
ident,
)?));
}
if p.peek::<T![!]>()? {
return Ok(Self::MacroCall(ast::MacroCall::parse_with_meta_path(
p,
take(attributes),
path,
)?));
}
Ok(Self::Path(path))
}
pub(crate) fn peek_with_brace(p: &mut Peeker<'_>, eager_brace: EagerBrace) -> bool {
match p.nth(0) {
K![async] => true,
K![self] => true,
K![select] => true,
K![#] => true,
K![-] => true,
K![!] => true,
K![&] => true,
K![*] => true,
K![while] => true,
K![loop] => true,
K![for] => true,
K![let] => true,
K![if] => true,
K![break] => true,
K![continue] => true,
K![return] => true,
K![true] => true,
K![false] => true,
K![ident] => true,
K![::] => true,
K![number] => true,
K![char] => true,
K![byte] => true,
K![str] => true,
K![bytestr] => true,
K!['label] => matches!(p.nth(1), K![:]),
K![..] => true,
K!['('] => true,
K!['['] => true,
K!['{'] if *eager_brace => true,
_ => false,
}
}
}
impl Parse for Expr {
fn parse(p: &mut Parser<'_>) -> Result<Self> {
Self::parse_with(p, EAGER_BRACE, EAGER_BINARY, CALLABLE)
}
}
impl Peek for Expr {
fn peek(p: &mut Peeker<'_>) -> bool {
Self::peek_with_brace(p, EAGER_BRACE)
}
}
fn primary(
p: &mut Parser<'_>,
attributes: &mut Vec<ast::Attribute>,
eager_brace: EagerBrace,
callable: Callable,
) -> Result<Expr> {
let expr = base(p, attributes, eager_brace)?;
chain(p, expr, callable)
}
fn base(
p: &mut Parser<'_>,
attributes: &mut Vec<ast::Attribute>,
eager_brace: EagerBrace,
) -> Result<Expr> {
if let Some(path) = p.parse::<Option<ast::Path>>()? {
return Expr::parse_with_meta_path(p, attributes, path, eager_brace);
}
if ast::Lit::peek_in_expr(p.peeker()) {
return Ok(Expr::Lit(ast::ExprLit::parse_with_meta(
p,
take(attributes),
)?));
}
let mut label = p.parse::<Option<(ast::Label, T![:])>>()?;
let mut async_token = p.parse::<Option<T![async]>>()?;
let mut const_token = p.parse::<Option<T![const]>>()?;
let mut move_token = p.parse::<Option<T![move]>>()?;
let expr = match p.nth(0)? {
K![..] => {
let limits = ast::ExprRangeLimits::HalfOpen(p.parse()?);
range(p, take(attributes), None, limits, eager_brace)?
}
K![..=] => {
let limits = ast::ExprRangeLimits::Closed(p.parse()?);
range(p, take(attributes), None, limits, eager_brace)?
}
K![#] => {
let ident = ast::ObjectIdent::Anonymous(p.parse()?);
Expr::Object(ast::ExprObject::parse_with_meta(
p,
take(attributes),
ident,
)?)
}
K![||] | K![|] => Expr::Closure(ast::ExprClosure::parse_with_meta(
p,
take(attributes),
take(&mut async_token),
take(&mut move_token),
)?),
K![select] => Expr::Select(ast::ExprSelect::parse_with_attributes(p, take(attributes))?),
K![!] | K![-] | K![&] | K![*] => Expr::Unary(ast::ExprUnary::parse_with_meta(
p,
take(attributes),
eager_brace,
)?),
K![while] => Expr::While(ast::ExprWhile::parse_with_meta(
p,
take(attributes),
take(&mut label),
)?),
K![loop] => Expr::Loop(ast::ExprLoop::parse_with_meta(
p,
take(attributes),
take(&mut label),
)?),
K![for] => Expr::For(ast::ExprFor::parse_with_meta(
p,
take(attributes),
take(&mut label),
)?),
K![let] => Expr::Let(ast::ExprLet::parse_with_meta(p, take(attributes))?),
K![if] => Expr::If(ast::ExprIf::parse_with_meta(p, take(attributes))?),
K![match] => Expr::Match(ast::ExprMatch::parse_with_attributes(p, take(attributes))?),
K!['['] => Expr::Vec(ast::ExprVec::parse_with_meta(p, take(attributes))?),
ast::Kind::Open(ast::Delimiter::Empty) => empty_group(p, take(attributes))?,
K!['('] => paren_group(p, take(attributes))?,
K!['{'] => Expr::Block(ast::ExprBlock {
attributes: take(attributes),
async_token: take(&mut async_token),
const_token: take(&mut const_token),
move_token: take(&mut move_token),
label: take(&mut label),
block: p.parse()?,
}),
K![break] => Expr::Break(ast::ExprBreak::parse_with_meta(p, take(attributes))?),
K![continue] => Expr::Continue(ast::ExprContinue::parse_with_meta(p, take(attributes))?),
K![yield] => Expr::Yield(ast::ExprYield::parse_with_meta(p, take(attributes))?),
K![return] => Expr::Return(ast::ExprReturn::parse_with_meta(p, take(attributes))?),
_ => {
return Err(compile::Error::expected(
p.tok_at(0)?,
Expectation::Expression,
));
}
};
if let Some(span) = label.option_span() {
return Err(compile::Error::unsupported(span, "label"));
}
if let Some(span) = async_token.option_span() {
return Err(compile::Error::unsupported(span, "async modifier"));
}
if let Some(span) = const_token.option_span() {
return Err(compile::Error::unsupported(span, "const modifier"));
}
if let Some(span) = move_token.option_span() {
return Err(compile::Error::unsupported(span, "move modifier"));
}
Ok(expr)
}
fn chain(p: &mut Parser<'_>, mut expr: Expr, callable: Callable) -> Result<Expr> {
while !p.is_eof()? {
let is_callable = expr.is_callable(*callable);
match p.nth(0)? {
K!['['] if is_callable => {
expr = Expr::Index(ast::ExprIndex {
attributes: expr.take_attributes(),
target: Box::try_new(expr)?,
open: p.parse()?,
index: p.parse()?,
close: p.parse()?,
});
}
K!['('] if is_callable => {
expr = Expr::Call(ast::ExprCall::parse_with_meta(
p,
expr.take_attributes(),
Box::try_new(expr)?,
)?);
}
K![?] => {
expr = Expr::Try(ast::ExprTry {
attributes: expr.take_attributes(),
expr: Box::try_new(expr)?,
try_token: p.parse()?,
});
}
K![=] => {
let eq = p.parse()?;
let rhs = Expr::parse_with(p, EAGER_BRACE, EAGER_BINARY, CALLABLE)?;
expr = Expr::Assign(ast::ExprAssign {
attributes: expr.take_attributes(),
lhs: Box::try_new(expr)?,
eq,
rhs: Box::try_new(rhs)?,
});
}
K![.] => {
match p.nth(1)? {
K![await] => {
expr = Expr::Await(ast::ExprAwait {
attributes: expr.take_attributes(),
expr: Box::try_new(expr)?,
dot: p.parse()?,
await_token: p.parse()?,
});
}
K![ident] => {
expr = Expr::FieldAccess(ast::ExprFieldAccess {
attributes: expr.take_attributes(),
expr: Box::try_new(expr)?,
dot: p.parse()?,
expr_field: ast::ExprField::Path(p.parse()?),
});
}
K![number] => {
expr = Expr::FieldAccess(ast::ExprFieldAccess {
attributes: expr.take_attributes(),
expr: Box::try_new(expr)?,
dot: p.parse()?,
expr_field: ast::ExprField::LitNumber(p.parse()?),
});
}
_ => {
return Err(compile::Error::new(p.span(0..1), ErrorKind::BadFieldAccess));
}
}
}
_ => break,
}
}
Ok(expr)
}
fn binary(
p: &mut Parser<'_>,
mut lhs: Expr,
mut lookahead: Option<ast::BinOp>,
min_precedence: usize,
eager_brace: EagerBrace,
) -> Result<Expr> {
while let Some(op) = lookahead {
let precedence = op.precedence();
if precedence < min_precedence {
break;
}
op.advance(p)?;
match op {
ast::BinOp::DotDot(token) => {
lhs = range(
p,
lhs.take_attributes(),
Some(Box::try_new(lhs)?),
ast::ExprRangeLimits::HalfOpen(token),
eager_brace,
)?;
lookahead = ast::BinOp::from_peeker(p.peeker());
continue;
}
ast::BinOp::DotDotEq(token) => {
lhs = range(
p,
lhs.take_attributes(),
Some(Box::try_new(lhs)?),
ast::ExprRangeLimits::Closed(token),
eager_brace,
)?;
lookahead = ast::BinOp::from_peeker(p.peeker());
continue;
}
_ => (),
}
let mut rhs = primary(p, &mut Vec::new(), eager_brace, CALLABLE)?;
lookahead = ast::BinOp::from_peeker(p.peeker());
while let Some(next) = lookahead {
match (precedence, next.precedence()) {
(lh, rh) if lh < rh => {
rhs = binary(p, rhs, Some(next), lh + 1, eager_brace)?;
lookahead = ast::BinOp::from_peeker(p.peeker());
continue;
}
(lh, rh) if lh == rh => {
if !next.is_assoc() {
return Err(compile::Error::new(
lhs.span().join(rhs.span()),
ErrorKind::PrecedenceGroupRequired,
));
}
}
_ => {}
};
break;
}
lhs = Expr::Binary(ast::ExprBinary {
attributes: lhs.take_attributes(),
lhs: Box::try_new(lhs)?,
op,
rhs: Box::try_new(rhs)?,
});
}
Ok(lhs)
}
fn range(
p: &mut Parser<'_>,
attributes: Vec<ast::Attribute>,
from: Option<Box<Expr>>,
limits: ast::ExprRangeLimits,
eager_brace: EagerBrace,
) -> Result<Expr> {
let to = if Expr::peek_with_brace(p.peeker(), eager_brace) {
Some(Box::try_new(Expr::parse_with(
p,
eager_brace,
EAGER_BINARY,
CALLABLE,
)?)?)
} else {
None
};
Ok(Expr::Range(ast::ExprRange {
attributes,
start: from,
limits,
end: to,
}))
}
fn empty_group(p: &mut Parser<'_>, attributes: Vec<ast::Attribute>) -> Result<Expr> {
let open = p.parse::<ast::OpenEmpty>()?;
let expr = p.parse::<Expr>()?;
let close = p.parse::<ast::CloseEmpty>()?;
Ok(Expr::Empty(ast::ExprEmpty {
attributes,
open,
expr: Box::try_new(expr)?,
close,
}))
}
fn paren_group(p: &mut Parser<'_>, attributes: Vec<ast::Attribute>) -> Result<Expr> {
if let (K!['('], K![')']) = (p.nth(0)?, p.nth(1)?) {
return Ok(Expr::Tuple(ast::ExprTuple::parse_with_meta(p, attributes)?));
}
let open = p.parse::<T!['(']>()?;
let expr = p.parse::<Expr>()?;
if p.peek::<T![')']>()? {
return Ok(Expr::Group(ast::ExprGroup {
attributes,
open,
expr: Box::try_new(expr)?,
close: p.parse()?,
}));
}
Ok(Expr::Tuple(ast::ExprTuple::parse_from_first_expr(
p, attributes, open, expr,
)?))
}