summaryrefslogtreecommitdiff
path: root/src/parse/collection.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-01-02 19:37:10 +0100
committerLaurenz <laurmaedje@gmail.com>2021-01-02 19:37:10 +0100
commit1c40dc42e7bc7b799b77f06d25414aca59a044ba (patch)
treeea8bdedaebf59f5bc601346b0108236c7264a29d /src/parse/collection.rs
parent8cad78481cd52680317032c3bb84cacda5666489 (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.rs142
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",
+ }));
+}