mirror of
https://github.com/typst/typst
synced 2025-05-15 01:25:28 +08:00
Simplify collection parsing ♻
In preparation for closure expressions.
This commit is contained in:
parent
d5d187a8c2
commit
4d90a066f1
@ -1,143 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
/// Parse the arguments to a function call.
|
|
||||||
pub fn args(p: &mut Parser) -> ExprArgs {
|
|
||||||
let start = p.start();
|
|
||||||
let items = collection(p, vec![]);
|
|
||||||
ExprArgs { span: p.span(start), items }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a parenthesized group, which can be either of:
|
|
||||||
/// - Array literal
|
|
||||||
/// - Dictionary literal
|
|
||||||
/// - Parenthesized expression
|
|
||||||
pub fn parenthesized(p: &mut Parser) -> Expr {
|
|
||||||
p.start_group(Group::Paren, TokenMode::Code);
|
|
||||||
let state = if p.eat_if(Token::Colon) {
|
|
||||||
collection(p, State::Dict(vec![]))
|
|
||||||
} else {
|
|
||||||
collection(p, State::Unknown)
|
|
||||||
};
|
|
||||||
let span = p.end_group();
|
|
||||||
state.into_expr(span)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a collection.
|
|
||||||
fn collection<T: Collection>(p: &mut Parser, mut collection: T) -> T {
|
|
||||||
let mut missing_coma = None;
|
|
||||||
|
|
||||||
while !p.eof() {
|
|
||||||
if let Some(arg) = argument(p) {
|
|
||||||
collection.push_arg(p, arg);
|
|
||||||
|
|
||||||
if let Some(pos) = missing_coma.take() {
|
|
||||||
p.expected_at("comma", pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.eof() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let behind = p.end();
|
|
||||||
if p.eat_if(Token::Comma) {
|
|
||||||
collection.push_comma();
|
|
||||||
} else {
|
|
||||||
missing_coma = Some(behind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collection
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse an expression or a named pair.
|
|
||||||
fn argument(p: &mut Parser) -> Option<ExprArg> {
|
|
||||||
let first = expr(p)?;
|
|
||||||
if p.eat_if(Token::Colon) {
|
|
||||||
if let Expr::Ident(name) = first {
|
|
||||||
Some(ExprArg::Named(Named { name, expr: expr(p)? }))
|
|
||||||
} else {
|
|
||||||
p.diag(error!(first.span(), "expected identifier"));
|
|
||||||
expr(p);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(ExprArg::Pos(first))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Abstraction for comma-separated list of expression / named pairs.
|
|
||||||
trait Collection {
|
|
||||||
fn push_arg(&mut self, p: &mut Parser, arg: ExprArg);
|
|
||||||
fn push_comma(&mut self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Collection for Vec<ExprArg> {
|
|
||||||
fn push_arg(&mut self, _: &mut Parser, arg: ExprArg) {
|
|
||||||
self.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// State of collection parsing.
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum State {
|
|
||||||
Unknown,
|
|
||||||
Expr(Expr),
|
|
||||||
Array(Vec<Expr>),
|
|
||||||
Dict(Vec<Named>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
fn into_expr(self, span: Span) -> Expr {
|
|
||||||
match self {
|
|
||||||
Self::Unknown => Expr::Array(ExprArray { span, items: vec![] }),
|
|
||||||
Self::Expr(expr) => Expr::Group(ExprGroup { span, expr: Box::new(expr) }),
|
|
||||||
Self::Array(items) => Expr::Array(ExprArray { span, items }),
|
|
||||||
Self::Dict(items) => Expr::Dict(ExprDict { span, items }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Collection for State {
|
|
||||||
fn push_arg(&mut self, p: &mut Parser, arg: ExprArg) {
|
|
||||||
match self {
|
|
||||||
Self::Unknown => match arg {
|
|
||||||
ExprArg::Pos(expr) => *self = Self::Expr(expr),
|
|
||||||
ExprArg::Named(named) => *self = Self::Dict(vec![named]),
|
|
||||||
},
|
|
||||||
Self::Expr(prev) => match arg {
|
|
||||||
ExprArg::Pos(expr) => *self = Self::Array(vec![take(prev), expr]),
|
|
||||||
ExprArg::Named(_) => diag(p, arg),
|
|
||||||
},
|
|
||||||
Self::Array(array) => match arg {
|
|
||||||
ExprArg::Pos(expr) => array.push(expr),
|
|
||||||
ExprArg::Named(_) => diag(p, arg),
|
|
||||||
},
|
|
||||||
Self::Dict(dict) => match arg {
|
|
||||||
ExprArg::Pos(_) => diag(p, arg),
|
|
||||||
ExprArg::Named(named) => dict.push(named),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_comma(&mut self) {
|
|
||||||
if let Self::Expr(expr) = self {
|
|
||||||
*self = Self::Array(vec![take(expr)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take(expr: &mut Expr) -> Expr {
|
|
||||||
// Replace with anything, it's overwritten anyway.
|
|
||||||
std::mem::replace(
|
|
||||||
expr,
|
|
||||||
Expr::Lit(Lit { span: Span::ZERO, kind: LitKind::None }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diag(p: &mut Parser, arg: ExprArg) {
|
|
||||||
p.diag(error!(arg.span(), "{}", match arg {
|
|
||||||
ExprArg::Pos(_) => "expected named pair, found expression",
|
|
||||||
ExprArg::Named(_) => "expected expression, found named pair",
|
|
||||||
}));
|
|
||||||
}
|
|
122
src/parse/mod.rs
122
src/parse/mod.rs
@ -1,6 +1,5 @@
|
|||||||
//! Parsing and tokenization.
|
//! Parsing and tokenization.
|
||||||
|
|
||||||
mod collection;
|
|
||||||
mod lines;
|
mod lines;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
@ -17,7 +16,6 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use crate::diag::Pass;
|
use crate::diag::Pass;
|
||||||
use crate::syntax::*;
|
use crate::syntax::*;
|
||||||
use collection::{args, parenthesized};
|
|
||||||
|
|
||||||
/// Parse a string of source code.
|
/// Parse a string of source code.
|
||||||
pub fn parse(src: &str) -> Pass<Tree> {
|
pub fn parse(src: &str) -> Pass<Tree> {
|
||||||
@ -188,17 +186,17 @@ fn primary(p: &mut Parser) -> Option<Expr> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Structures.
|
||||||
|
Some(Token::LeftParen) => Some(parenthesized(p)),
|
||||||
|
Some(Token::LeftBracket) => Some(template(p)),
|
||||||
|
Some(Token::LeftBrace) => Some(block(p, true)),
|
||||||
|
|
||||||
// Keywords.
|
// Keywords.
|
||||||
Some(Token::Let) => expr_let(p),
|
Some(Token::Let) => expr_let(p),
|
||||||
Some(Token::If) => expr_if(p),
|
Some(Token::If) => expr_if(p),
|
||||||
Some(Token::While) => expr_while(p),
|
Some(Token::While) => expr_while(p),
|
||||||
Some(Token::For) => expr_for(p),
|
Some(Token::For) => expr_for(p),
|
||||||
|
|
||||||
// Structures.
|
|
||||||
Some(Token::LeftBrace) => Some(block(p, true)),
|
|
||||||
Some(Token::LeftBracket) => Some(template(p)),
|
|
||||||
Some(Token::LeftParen) => Some(parenthesized(p)),
|
|
||||||
|
|
||||||
// Nothing.
|
// Nothing.
|
||||||
_ => {
|
_ => {
|
||||||
p.expected("expression");
|
p.expected("expression");
|
||||||
@ -230,6 +228,109 @@ fn literal(p: &mut Parser) -> Option<Expr> {
|
|||||||
Some(Expr::Lit(Lit { span: p.eat_span(), kind }))
|
Some(Expr::Lit(Lit { span: p.eat_span(), kind }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a parenthesized expression, which can be either of:
|
||||||
|
/// - Array literal
|
||||||
|
/// - Dictionary literal
|
||||||
|
/// - Parenthesized expression
|
||||||
|
pub fn parenthesized(p: &mut Parser) -> Expr {
|
||||||
|
p.start_group(Group::Paren, TokenMode::Code);
|
||||||
|
let colon = p.eat_if(Token::Colon);
|
||||||
|
let (items, has_comma) = collection(p);
|
||||||
|
let span = p.end_group();
|
||||||
|
|
||||||
|
if colon {
|
||||||
|
// Leading colon makes this a dictionary.
|
||||||
|
return dict(p, items, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find out which kind of collection this is.
|
||||||
|
match items.as_slice() {
|
||||||
|
[] => array(p, items, span),
|
||||||
|
[ExprArg::Pos(_)] if !has_comma => match items.into_iter().next() {
|
||||||
|
Some(ExprArg::Pos(expr)) => {
|
||||||
|
Expr::Group(ExprGroup { span, expr: Box::new(expr) })
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
[ExprArg::Pos(_), ..] => array(p, items, span),
|
||||||
|
[ExprArg::Named(_), ..] => dict(p, items, span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a collection.
|
||||||
|
///
|
||||||
|
/// Returns whether the literal contained any commas.
|
||||||
|
fn collection(p: &mut Parser) -> (Vec<ExprArg>, bool) {
|
||||||
|
let mut items = vec![];
|
||||||
|
let mut has_comma = false;
|
||||||
|
let mut missing_coma = None;
|
||||||
|
|
||||||
|
while !p.eof() {
|
||||||
|
if let Some(arg) = item(p) {
|
||||||
|
items.push(arg);
|
||||||
|
|
||||||
|
if let Some(pos) = missing_coma.take() {
|
||||||
|
p.expected_at("comma", pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.eof() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let behind = p.end();
|
||||||
|
if p.eat_if(Token::Comma) {
|
||||||
|
has_comma = true;
|
||||||
|
} else {
|
||||||
|
missing_coma = Some(behind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(items, has_comma)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse an expression or a named pair.
|
||||||
|
fn item(p: &mut Parser) -> Option<ExprArg> {
|
||||||
|
let first = expr(p)?;
|
||||||
|
if p.eat_if(Token::Colon) {
|
||||||
|
if let Expr::Ident(name) = first {
|
||||||
|
Some(ExprArg::Named(Named { name, expr: expr(p)? }))
|
||||||
|
} else {
|
||||||
|
p.diag(error!(first.span(), "expected identifier"));
|
||||||
|
expr(p);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Some(ExprArg::Pos(first))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a collection into an array, producing errors for named items.
|
||||||
|
fn array(p: &mut Parser, items: Vec<ExprArg>, span: Span) -> Expr {
|
||||||
|
let items = items.into_iter().filter_map(|item| match item {
|
||||||
|
ExprArg::Pos(expr) => Some(expr),
|
||||||
|
ExprArg::Named(_) => {
|
||||||
|
p.diag(error!(item.span(), "expected expression, found named pair"));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Expr::Array(ExprArray { span, items: items.collect() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a collection into a dictionary, producing errors for expressions.
|
||||||
|
fn dict(p: &mut Parser, items: Vec<ExprArg>, span: Span) -> Expr {
|
||||||
|
let items = items.into_iter().filter_map(|item| match item {
|
||||||
|
ExprArg::Named(named) => Some(named),
|
||||||
|
ExprArg::Pos(_) => {
|
||||||
|
p.diag(error!(item.span(), "expected named pair, found expression"));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Expr::Dict(ExprDict { span, items: items.collect() })
|
||||||
|
}
|
||||||
|
|
||||||
// Parse a template value: `[...]`.
|
// Parse a template value: `[...]`.
|
||||||
fn template(p: &mut Parser) -> Expr {
|
fn template(p: &mut Parser) -> Expr {
|
||||||
p.start_group(Group::Bracket, TokenMode::Markup);
|
p.start_group(Group::Bracket, TokenMode::Markup);
|
||||||
@ -330,6 +431,13 @@ fn call(p: &mut Parser, name: Ident) -> Expr {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse the arguments to a function call.
|
||||||
|
fn args(p: &mut Parser) -> ExprArgs {
|
||||||
|
let start = p.start();
|
||||||
|
let items = collection(p).0;
|
||||||
|
ExprArgs { span: p.span(start), items }
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a let expression.
|
/// Parse a let expression.
|
||||||
fn expr_let(p: &mut Parser) -> Option<Expr> {
|
fn expr_let(p: &mut Parser) -> Option<Expr> {
|
||||||
let start = p.start();
|
let start = p.start();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user