yog-script-analyze 0.2.2

Yog Script (.ys, .yns) analyzed: its lexer, parser and syntax tree, names resolved, the class index model — what the build and the editors read a source with.
Documentation
//! Primary expressions: literals, names, brackets and tuples, collections.

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(_) if self.macro_ahead() => return self.macro_call(),
            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 })
    }

    /// A string's parts, each `{expr}` parsed.
    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,
        })
    }

    /// `(e)`, `(a, b)`.
    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()),
        })
    }

    /// `[a, b]`, `[k => v]`, `[..xs]`.
    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()),
        })
    }
}

impl Parser<'_, '_, '_> {
    /// `name!(` with nothing between: a macro's call (`x!` alone asserts
    /// `x` isn't null).
    fn macro_ahead(&self) -> bool {
        let [name, bang, open] = [0, 1, 2].map(|n| self.span_at(n));
        self.nth_is(1, Punct::Bang)
            && self.nth_is(2, Punct::LParen)
            && name.end == bang.start
            && bang.end == open.start
    }

    /// `name!(a, b)`.
    fn macro_call(&mut self) -> PResult<Expr> {
        let name = self.name("a macro")?;
        self.need(Punct::Bang)?;
        self.need(Punct::LParen)?;
        let args = self.braces(|p| p.list(Punct::RParen, Self::expr))?;
        let span = name.span.to(self.prev());
        Ok(Expr {
            kind: ExprKind::Macro { name, args },
            span,
        })
    }
}