summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/parse/collection.rs143
-rw-r--r--src/parse/mod.rs122
2 files changed, 115 insertions, 150 deletions
diff --git a/src/parse/collection.rs b/src/parse/collection.rs
deleted file mode 100644
index ab358f76..00000000
--- a/src/parse/collection.rs
+++ /dev/null
@@ -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",
- }));
-}
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index d4cf90c7..327a99f3 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -1,6 +1,5 @@
//! Parsing and tokenization.
-mod collection;
mod lines;
mod parser;
mod resolve;
@@ -17,7 +16,6 @@ use std::rc::Rc;
use crate::diag::Pass;
use crate::syntax::*;
-use collection::{args, parenthesized};
/// Parse a string of source code.
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.
Some(Token::Let) => expr_let(p),
Some(Token::If) => expr_if(p),
Some(Token::While) => expr_while(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.
_ => {
p.expected("expression");
@@ -230,6 +228,109 @@ fn literal(p: &mut Parser) -> Option<Expr> {
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: `[...]`.
fn template(p: &mut Parser) -> Expr {
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.
fn expr_let(p: &mut Parser) -> Option<Expr> {
let start = p.start();