use super::{PResult, Parser};
use crate::ast::{Element, Expr, ExprKind, StrSeg};
use crate::source::token::{Punct, Span, StrPart, Tok, Token};
impl Parser<'_, '_, '_> {
pub(crate) fn primary(&mut self) -> PResult<Expr> {
let span = self.span();
let kind = match self.tok().clone() {
Tok::Int(v) => ExprKind::Int(v),
Tok::Float(v) => ExprKind::Float(v),
Tok::Unit(value, unit) => ExprKind::Unit { value, unit },
Tok::RawStr(s) => ExprKind::RawStr(s),
Tok::Char(c) => ExprKind::Char(c),
Tok::Text(key) => ExprKind::Text(key),
Tok::Content(kind, id) => ExprKind::Content { kind, id },
Tok::Tag(id) => ExprKind::Tag(id),
Tok::Ident(name) => ExprKind::Ident(name),
Tok::Str(parts) => {
self.bump();
return Ok(Expr {
kind: ExprKind::Str(self.segments(&parts, span)),
span,
});
}
Tok::Punct(Punct::LParen) => return self.braces(Self::paren),
Tok::Punct(Punct::LBracket) => return self.braces(Self::collection),
_ => return Err(self.expected("an expression")),
};
self.bump();
Ok(Expr { kind, span })
}
fn segments(&mut self, parts: &[StrPart], span: Span) -> Vec<StrSeg> {
parts
.iter()
.map(|part| match part {
StrPart::Text(t) => StrSeg::Text(t.clone()),
StrPart::Expr(toks) => StrSeg::Expr(self.interpolated(toks, span)),
})
.collect()
}
fn interpolated(&mut self, toks: &[Token], span: Span) -> Expr {
let mut toks = toks.to_vec();
let end = toks.last().map_or(span, |t| t.span);
toks.push(Token {
tok: Tok::Eof,
span: Span {
start: end.end,
end: end.end,
},
});
let mut sub = Parser::new(&toks, &mut *self.report, self.nanites);
let expr = sub.expr().and_then(|e| {
if *sub.tok() == Tok::Eof {
Ok(e)
} else {
Err(sub.expected("the end of `{…}`"))
}
});
expr.unwrap_or(Expr {
kind: ExprKind::Error,
span,
})
}
fn paren(&mut self) -> PResult<Expr> {
let start = self.need(Punct::LParen)?;
let mut first = self.expr()?;
if self.eat(Punct::RParen) {
first.span = start.to(self.prev());
return Ok(first);
}
self.need(Punct::Comma)?;
let mut items = vec![first];
items.extend(self.list(Punct::RParen, Self::expr)?);
Ok(Expr {
kind: ExprKind::Tuple(items),
span: start.to(self.prev()),
})
}
fn collection(&mut self) -> PResult<Expr> {
let start = self.need(Punct::LBracket)?;
let items = self.list(Punct::RBracket, |p| {
if p.eat(Punct::Range) {
return Ok(Element::Spread(p.conditional()?));
}
let item = p.conditional()?;
if p.eat(Punct::FatArrow) {
return Ok(Element::Entry(item, p.expr()?));
}
Ok(Element::Item(item))
})?;
Ok(Expr {
kind: ExprKind::Collection(items),
span: start.to(self.prev()),
})
}
}