diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-01-02 19:37:10 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-01-02 19:37:10 +0100 |
| commit | 1c40dc42e7bc7b799b77f06d25414aca59a044ba (patch) | |
| tree | ea8bdedaebf59f5bc601346b0108236c7264a29d /src/parse/collection.rs | |
| parent | 8cad78481cd52680317032c3bb84cacda5666489 (diff) | |
Dynamic values, Types, Arrays, and Dictionaries 🚀
- Identifiers are now evaluated as variables instead of being plain values
- Constants like `left` or `bold` are stored as dynamic values containing the respective rust types
- We now distinguish between arrays and dictionaries to make things more intuitive (at the cost of a bit more complex parsing)
- Spans were removed from collections (arrays, dictionaries), function arguments still have spans for the top-level values to enable good diagnostics
Diffstat (limited to 'src/parse/collection.rs')
| -rw-r--r-- | src/parse/collection.rs | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/parse/collection.rs b/src/parse/collection.rs new file mode 100644 index 00000000..db267dbe --- /dev/null +++ b/src/parse/collection.rs @@ -0,0 +1,142 @@ +use super::*; +use crate::diag::Deco; + +/// Parse the arguments to a function call. +pub fn arguments(p: &mut Parser) -> Arguments { + collection(p, vec![]) +} + +/// 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); + let state = if p.eat_if(Token::Colon) { + collection(p, State::Dict(vec![])) + } else { + collection(p, State::Unknown) + }; + p.end_group(); + state.into_expr() +} + +/// 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) = p.span_if(argument) { + collection.push_arg(p, arg); + + if let Some(pos) = missing_coma.take() { + p.diag_expected_at("comma", pos); + } + + if p.eof() { + break; + } + + let behind = p.last_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<Argument> { + let first = p.span_if(expr)?; + if p.eat_if(Token::Colon) { + if let Expr::Lit(Lit::Ident(ident)) = first.v { + let expr = p.span_if(expr)?; + let name = ident.with_span(first.span); + p.deco(Deco::Name.with_span(name.span)); + Some(Argument::Named(Named { name, expr })) + } else { + p.diag(error!(first.span, "name must be identifier")); + expr(p); + None + } + } else { + Some(Argument::Pos(first)) + } +} + +/// Abstraction for comma-separated list of expression / named pairs. +trait Collection { + fn push_arg(&mut self, p: &mut Parser, arg: Spanned<Argument>); + fn push_comma(&mut self) {} +} + +impl Collection for Arguments { + fn push_arg(&mut self, _: &mut Parser, arg: Spanned<Argument>) { + self.push(arg.v); + } +} + +/// State of collection parsing. +#[derive(Debug)] +enum State { + Unknown, + Expr(Spanned<Expr>), + Array(Array), + Dict(Dict), +} + +impl State { + fn into_expr(self) -> Expr { + match self { + Self::Unknown => Expr::Lit(Lit::Array(vec![])), + Self::Expr(expr) => expr.v, + Self::Array(array) => Expr::Lit(Lit::Array(array)), + Self::Dict(dict) => Expr::Lit(Lit::Dict(dict)), + } + } +} + +impl Collection for State { + fn push_arg(&mut self, p: &mut Parser, arg: Spanned<Argument>) { + match self { + Self::Unknown => match arg.v { + Argument::Pos(expr) => *self = Self::Expr(expr), + Argument::Named(named) => *self = Self::Dict(vec![named]), + }, + Self::Expr(prev) => match arg.v { + Argument::Pos(expr) => *self = Self::Array(vec![take(prev), expr]), + Argument::Named(_) => diag(p, arg), + }, + Self::Array(array) => match arg.v { + Argument::Pos(expr) => array.push(expr), + Argument::Named(_) => diag(p, arg), + }, + Self::Dict(dict) => match arg.v { + Argument::Pos(_) => diag(p, arg), + Argument::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 Spanned<Expr>) -> Spanned<Expr> { + // Replace with anything, it's overwritten anyway. + std::mem::replace(expr, Spanned::zero(Expr::Lit(Lit::Bool(false)))) +} + +fn diag(p: &mut Parser, arg: Spanned<Argument>) { + p.diag(error!(arg.span, "{}", match arg.v { + Argument::Pos(_) => "expected named pair, found expression", + Argument::Named(_) => "expected expression, found named pair", + })); +} |
