diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-05-04 23:04:19 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-05-04 23:12:31 +0200 |
| commit | 2a45650dcc87c3bf047adaf030fd392bbe9fbb5e (patch) | |
| tree | c78ce67e341d2ad1e7f6ffb6058906075b76798a /src | |
| parent | e674fd7e909c273c36952f01829544a2efc11c92 (diff) | |
Keyed pairs
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/dict.rs | 7 | ||||
| -rw-r--r-- | src/eval/mod.rs | 3 | ||||
| -rw-r--r-- | src/parse/mod.rs | 61 | ||||
| -rw-r--r-- | src/syntax/ast.rs | 37 | ||||
| -rw-r--r-- | src/syntax/highlight.rs | 1 | ||||
| -rw-r--r-- | src/syntax/mod.rs | 6 |
6 files changed, 83 insertions, 32 deletions
diff --git a/src/eval/dict.rs b/src/eval/dict.rs index 6b4dbbd7..22b73e76 100644 --- a/src/eval/dict.rs +++ b/src/eval/dict.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use super::{Args, Array, Func, Value}; use crate::diag::{StrResult, TypResult}; +use crate::parse::is_ident; use crate::syntax::Spanned; use crate::util::{ArcExt, EcoString}; use crate::Context; @@ -127,7 +128,11 @@ impl Debug for Dict { f.write_char(':')?; } for (i, (key, value)) in self.iter().enumerate() { - f.write_str(key)?; + if is_ident(key) { + f.write_str(key)?; + } else { + write!(f, "{key:?}")?; + } f.write_str(": ")?; value.fmt(f)?; if i + 1 < self.0.len() { diff --git a/src/eval/mod.rs b/src/eval/mod.rs index e9d62a69..677fa4aa 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -335,6 +335,9 @@ impl Eval for DictExpr { DictItem::Named(named) => { map.insert(named.name().take(), named.expr().eval(ctx, scp)?); } + DictItem::Keyed(keyed) => { + map.insert(keyed.key(), keyed.expr().eval(ctx, scp)?); + } DictItem::Spread(expr) => match expr.eval(ctx, scp)? { Value::None => {} Value::Dict(dict) => map.extend(dict.into_iter()), diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 7811482b..8bf7b1c5 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -526,7 +526,7 @@ fn parenthesized(p: &mut Parser, atomic: bool) -> ParseResult { p.start_group(Group::Paren); let colon = p.eat_if(NodeKind::Colon); - let kind = collection(p).0; + let kind = collection(p, true).0; p.end_group(); // Leading colon makes this a dictionary. @@ -568,14 +568,14 @@ enum CollectionKind { /// /// Returns the length of the collection and whether the literal contained any /// commas. -fn collection(p: &mut Parser) -> (CollectionKind, usize) { +fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) { let mut kind = None; let mut items = 0; let mut can_group = true; let mut missing_coma: Option<Marker> = None; while !p.eof() { - if let Ok(item_kind) = item(p) { + if let Ok(item_kind) = item(p, keyed) { match item_kind { NodeKind::Spread => can_group = false, NodeKind::Named if kind.is_none() => { @@ -619,7 +619,7 @@ fn collection(p: &mut Parser) -> (CollectionKind, usize) { /// Parse an expression or a named pair, returning whether it's a spread or a /// named pair. -fn item(p: &mut Parser) -> ParseResult<NodeKind> { +fn item(p: &mut Parser, keyed: bool) -> ParseResult<NodeKind> { let marker = p.marker(); if p.eat_if(NodeKind::Dots) { marker.perform(p, NodeKind::Spread, expr)?; @@ -629,18 +629,27 @@ fn item(p: &mut Parser) -> ParseResult<NodeKind> { expr(p)?; if p.at(NodeKind::Colon) { - marker.perform(p, NodeKind::Named, |p| { - if let Some(NodeKind::Ident(_)) = marker.after(p).map(|c| c.kind()) { + match marker.after(p).map(|c| c.kind()) { + Some(NodeKind::Ident(_)) => { p.eat(); - expr(p) - } else { - let error = NodeKind::Error(ErrorPos::Full, "expected identifier".into()); + marker.perform(p, NodeKind::Named, expr)?; + } + Some(NodeKind::Str(_)) if keyed => { + p.eat(); + marker.perform(p, NodeKind::Keyed, expr)?; + } + _ => { + let mut msg = EcoString::from("expected identifier"); + if keyed { + msg.push_str(" or string"); + } + let error = NodeKind::Error(ErrorPos::Full, msg); marker.end(p, error); p.eat(); - expr(p).ok(); - Err(ParseError) + marker.perform(p, NodeKind::Named, expr).ok(); + return Err(ParseError); } - })?; + } Ok(NodeKind::Named) } else { @@ -653,6 +662,7 @@ fn item(p: &mut Parser) -> ParseResult<NodeKind> { fn array(p: &mut Parser, marker: Marker) { marker.filter_children(p, |x| match x.kind() { NodeKind::Named => Err("expected expression, found named pair"), + NodeKind::Keyed => Err("expected expression, found keyed pair"), _ => Ok(()), }); marker.end(p, NodeKind::ArrayExpr); @@ -664,18 +674,18 @@ fn dict(p: &mut Parser, marker: Marker) { let mut used = HashSet::new(); marker.filter_children(p, |x| match x.kind() { kind if kind.is_paren() => Ok(()), - NodeKind::Named => { - if let Some(NodeKind::Ident(ident)) = + NodeKind::Named | NodeKind::Keyed => { + if let Some(NodeKind::Ident(key) | NodeKind::Str(key)) = x.children().first().map(|child| child.kind()) { - if !used.insert(ident.clone()) { + if !used.insert(key.clone()) { return Err("pair has duplicate key"); } } Ok(()) } - NodeKind::Comma | NodeKind::Colon | NodeKind::Spread => Ok(()), - _ => Err("expected named pair, found expression"), + NodeKind::Spread | NodeKind::Comma | NodeKind::Colon => Ok(()), + _ => Err("expected named or keyed pair, found expression"), }); marker.end(p, NodeKind::DictExpr); } @@ -685,7 +695,7 @@ fn dict(p: &mut Parser, marker: Marker) { fn params(p: &mut Parser, marker: Marker) { marker.filter_children(p, |x| match x.kind() { kind if kind.is_paren() => Ok(()), - NodeKind::Named | NodeKind::Comma | NodeKind::Ident(_) => Ok(()), + NodeKind::Named | NodeKind::Ident(_) | NodeKind::Comma => Ok(()), NodeKind::Spread if matches!( x.children().last().map(|child| child.kind()), @@ -694,7 +704,7 @@ fn params(p: &mut Parser, marker: Marker) { { Ok(()) } - _ => Err("expected identifier"), + _ => Err("expected identifier, named pair or argument sink"), }); marker.end(p, NodeKind::ClosureParams); } @@ -746,12 +756,12 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult { if p.at(NodeKind::LeftParen) { let marker = p.marker(); p.start_group(Group::Paren); - collection(p); + collection(p, false); p.end_group(); let mut used = HashSet::new(); - marker.filter_children(p, |x| { - if x.kind() == &NodeKind::Named { + marker.filter_children(p, |x| match x.kind() { + NodeKind::Named => { if let Some(NodeKind::Ident(ident)) = x.children().first().map(|child| child.kind()) { @@ -759,8 +769,9 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult { return Err("duplicate argument"); } } + Ok(()) } - Ok(()) + _ => Ok(()), }); } @@ -785,7 +796,7 @@ fn let_expr(p: &mut Parser) -> ParseResult { if has_params { let marker = p.marker(); p.start_group(Group::Paren); - collection(p); + collection(p, false); p.end_group(); params(p, marker); } @@ -904,7 +915,7 @@ fn import_expr(p: &mut Parser) -> ParseResult { p.perform(NodeKind::ImportItems, |p| { p.start_group(Group::Imports); let marker = p.marker(); - let items = collection(p).1; + let items = collection(p, false).1; if items == 0 { p.expected("import items"); } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index fa00fe4b..1add2fd6 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -472,7 +472,7 @@ impl ArrayExpr { /// An item in an array expresssion. #[derive(Debug, Clone, PartialEq, Hash)] pub enum ArrayItem { - /// A simple value: `12`. + /// A bare value: `12`. Pos(Expr), /// A spreaded value: `..things`. Spread(Expr), @@ -509,8 +509,10 @@ impl DictExpr { /// An item in an dictionary expresssion. #[derive(Debug, Clone, PartialEq, Hash)] pub enum DictItem { - /// A simple named pair: `12`. + /// A named pair: `thickness: 3pt`. Named(Named), + /// A keyed pair: `"spaced key": true`. + Keyed(Keyed), /// A spreaded value: `..things`. Spread(Expr), } @@ -519,6 +521,7 @@ impl TypedNode for DictItem { fn from_red(node: RedRef) -> Option<Self> { match node.kind() { NodeKind::Named => node.cast().map(Self::Named), + NodeKind::Keyed => node.cast().map(Self::Keyed), NodeKind::Spread => node.cast_first_child().map(Self::Spread), _ => None, } @@ -527,29 +530,53 @@ impl TypedNode for DictItem { fn as_red(&self) -> RedRef<'_> { match self { Self::Named(v) => v.as_red(), + Self::Keyed(v) => v.as_red(), Self::Spread(v) => v.as_red(), } } } node! { - /// A pair of a name and an expression: `pattern: dashed`. + /// A pair of a name and an expression: `thickness: 3pt`. Named } impl Named { - /// The name: `pattern`. + /// The name: `thickness`. pub fn name(&self) -> Ident { self.0.cast_first_child().expect("named pair is missing name") } - /// The right-hand side of the pair: `dashed`. + /// The right-hand side of the pair: `3pt`. pub fn expr(&self) -> Expr { self.0.cast_last_child().expect("named pair is missing expression") } } node! { + /// A pair of a string key and an expression: `"spaced key": true`. + Keyed +} + +impl Keyed { + /// The key: `"spaced key"`. + pub fn key(&self) -> EcoString { + self.0 + .children() + .find_map(|node| match node.kind() { + NodeKind::Str(key) => Some(key.clone()), + _ => None, + }) + .expect("keyed pair is missing key") + } + + /// The right-hand side of the pair: `true`. + pub fn expr(&self) -> Expr { + self.0.cast_last_child().expect("keyed pair is missing expression") + } +} + +node! { /// A unary operation: `-x`. UnaryExpr: UnaryExpr } diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs index dae379ac..06e88691 100644 --- a/src/syntax/highlight.rs +++ b/src/syntax/highlight.rs @@ -215,6 +215,7 @@ impl Category { NodeKind::ArrayExpr => None, NodeKind::DictExpr => None, NodeKind::Named => None, + NodeKind::Keyed => None, NodeKind::UnaryExpr => None, NodeKind::BinaryExpr => None, NodeKind::FieldAccess => None, diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index d21597ff..09d7265d 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -648,6 +648,8 @@ pub enum NodeKind { DictExpr, /// A named pair: `thickness: 3pt`. Named, + /// A keyed pair: `"spaced key": true`. + Keyed, /// A unary operation: `-x`. UnaryExpr, /// A binary operation: `a + b`. @@ -896,7 +898,8 @@ impl NodeKind { Self::GroupExpr => "group", Self::ArrayExpr => "array", Self::DictExpr => "dictionary", - Self::Named => "named argument", + Self::Named => "named pair", + Self::Keyed => "keyed pair", Self::UnaryExpr => "unary expression", Self::BinaryExpr => "binary expression", Self::FieldAccess => "field access", @@ -1021,6 +1024,7 @@ impl Hash for NodeKind { Self::ArrayExpr => {} Self::DictExpr => {} Self::Named => {} + Self::Keyed => {} Self::UnaryExpr => {} Self::BinaryExpr => {} Self::FieldAccess => {} |
