diff options
Diffstat (limited to 'src/syntax')
| -rw-r--r-- | src/syntax/ast.rs | 9 | ||||
| -rw-r--r-- | src/syntax/parser.rs | 82 |
2 files changed, 70 insertions, 21 deletions
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 1fa6c89f..53e92949 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1594,23 +1594,19 @@ node! { #[derive(Debug, Clone, Hash)] pub enum Param { /// A positional parameter: `x`. - Pos(Ident), + Pos(Pattern), /// A named parameter with a default value: `draw: false`. Named(Named), /// An argument sink: `..args`. Sink(Spread), - /// A placeholder: `_`. - Placeholder(Underscore), } impl AstNode for Param { fn from_untyped(node: &SyntaxNode) -> Option<Self> { match node.kind() { - SyntaxKind::Ident => node.cast().map(Self::Pos), SyntaxKind::Named => node.cast().map(Self::Named), SyntaxKind::Spread => node.cast().map(Self::Sink), - SyntaxKind::Underscore => node.cast().map(Self::Placeholder), - _ => Option::None, + _ => node.cast().map(Self::Pos), } } @@ -1619,7 +1615,6 @@ impl AstNode for Param { Self::Pos(v) => v.as_untyped(), Self::Named(v) => v.as_untyped(), Self::Sink(v) => v.as_untyped(), - Self::Placeholder(v) => v.as_untyped(), } } } diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs index 0bbf77e2..ab73479e 100644 --- a/src/syntax/parser.rs +++ b/src/syntax/parser.rs @@ -523,7 +523,11 @@ fn code(p: &mut Parser, mut stop: impl FnMut(SyntaxKind) -> bool) { } fn code_expr(p: &mut Parser) { - code_expr_prec(p, false, 0) + code_expr_prec(p, false, 0, false) +} + +fn code_expr_or_pattern(p: &mut Parser) { + code_expr_prec(p, false, 0, true) } fn embedded_code_expr(p: &mut Parser) { @@ -542,7 +546,7 @@ fn embedded_code_expr(p: &mut Parser) { ); let prev = p.prev_end(); - code_expr_prec(p, true, 0); + code_expr_prec(p, true, 0, false); // Consume error for things like `#12p` or `#"abc\"`. if !p.progress(prev) { @@ -560,14 +564,19 @@ fn embedded_code_expr(p: &mut Parser) { p.unstop(); } -fn code_expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) { +fn code_expr_prec( + p: &mut Parser, + atomic: bool, + min_prec: usize, + allow_destructuring: bool, +) { let m = p.marker(); if let (false, Some(op)) = (atomic, ast::UnOp::from_kind(p.current())) { p.eat(); - code_expr_prec(p, atomic, op.precedence()); + code_expr_prec(p, atomic, op.precedence(), false); p.wrap(m, SyntaxKind::Unary); } else { - code_primary(p, atomic); + code_primary(p, atomic, allow_destructuring); } loop { @@ -615,7 +624,7 @@ fn code_expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) { } p.eat(); - code_expr_prec(p, false, prec); + code_expr_prec(p, false, prec, false); p.wrap(m, SyntaxKind::Binary); continue; } @@ -624,7 +633,7 @@ fn code_expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) { } } -fn code_primary(p: &mut Parser, atomic: bool) { +fn code_primary(p: &mut Parser, atomic: bool, allow_destructuring: bool) { let m = p.marker(); match p.current() { SyntaxKind::Ident => { @@ -650,7 +659,7 @@ fn code_primary(p: &mut Parser, atomic: bool) { SyntaxKind::LeftBrace => code_block(p), SyntaxKind::LeftBracket => content_block(p), - SyntaxKind::LeftParen => with_paren(p), + SyntaxKind::LeftParen => with_paren(p, allow_destructuring), SyntaxKind::Dollar => equation(p), SyntaxKind::Let => let_binding(p), SyntaxKind::Set => set_rule(p), @@ -716,7 +725,7 @@ fn content_block(p: &mut Parser) { p.wrap(m, SyntaxKind::ContentBlock); } -fn with_paren(p: &mut Parser) { +fn with_paren(p: &mut Parser, allow_destructuring: bool) { let m = p.marker(); let mut kind = collection(p, true); if p.at(SyntaxKind::Arrow) { @@ -739,11 +748,37 @@ fn with_paren(p: &mut Parser) { SyntaxKind::Array => validate_array(p, m), SyntaxKind::Dict => validate_dict(p, m), SyntaxKind::Parenthesized => validate_parenthesized(p, m), + SyntaxKind::Destructuring if !allow_destructuring => { + invalidate_destructuring(p, m) + } _ => {} } p.wrap(m, kind); } +fn invalidate_destructuring(p: &mut Parser, m: Marker) { + let mut collection_kind = Option::None; + for child in p.post_process(m) { + match child.kind() { + SyntaxKind::Named | SyntaxKind::Keyed => match collection_kind { + Some(SyntaxKind::Array) => child.convert_to_error(eco_format!( + "expected expression, found {}", + child.kind().name() + )), + _ => collection_kind = Some(SyntaxKind::Dict), + }, + SyntaxKind::LeftParen | SyntaxKind::RightParen | SyntaxKind::Comma => {} + kind => match collection_kind { + Some(SyntaxKind::Dict) => child.convert_to_error(eco_format!( + "expected named or keyed pair, found {}", + kind.name() + )), + _ => collection_kind = Some(SyntaxKind::Array), + }, + } + } +} + fn collection(p: &mut Parser, keyed: bool) -> SyntaxKind { p.stop_at_newline(false); p.assert(SyntaxKind::LeftParen); @@ -760,10 +795,17 @@ fn collection(p: &mut Parser, keyed: bool) -> SyntaxKind { let prev = p.prev_end(); match item(p, keyed) { SyntaxKind::Spread => parenthesized = false, - SyntaxKind::Named | SyntaxKind::Keyed if kind.is_none() => { - kind = Some(SyntaxKind::Dict); + SyntaxKind::Named | SyntaxKind::Keyed => { + match kind { + Some(SyntaxKind::Array) => kind = Some(SyntaxKind::Destructuring), + _ => kind = Some(SyntaxKind::Dict), + } parenthesized = false; } + SyntaxKind::Int => match kind { + Some(SyntaxKind::Array) | None => kind = Some(SyntaxKind::Array), + Some(_) => kind = Some(SyntaxKind::Destructuring), + }, _ if kind.is_none() => kind = Some(SyntaxKind::Array), _ => {} } @@ -809,7 +851,7 @@ fn item(p: &mut Parser, keyed: bool) -> SyntaxKind { } if !p.eat_if(SyntaxKind::Underscore) { - code_expr(p); + code_expr_or_pattern(p); } else { return SyntaxKind::Underscore; } @@ -1161,6 +1203,10 @@ fn validate_params(p: &mut Parser, m: Marker) { child.make_erroneous(); } } + SyntaxKind::Array | SyntaxKind::Dict | SyntaxKind::Destructuring => { + validate_pattern(child.children_mut().iter_mut(), &mut used, false); + child.convert_to_kind(SyntaxKind::Destructuring); + } SyntaxKind::LeftParen | SyntaxKind::RightParen | SyntaxKind::Comma @@ -1194,9 +1240,17 @@ fn validate_args(p: &mut Parser, m: Marker) { } fn validate_destruct_pattern(p: &mut Parser, m: Marker, forbid_expressions: bool) { - let mut used_spread = false; let mut used = HashSet::new(); - for child in p.post_process(m) { + validate_pattern(p.post_process(m), &mut used, forbid_expressions); +} + +fn validate_pattern<'a>( + children: impl Iterator<Item = &'a mut SyntaxNode>, + used: &mut HashSet<EcoString>, + forbid_expressions: bool, +) { + let mut used_spread = false; + for child in children { match child.kind() { SyntaxKind::Ident => { if !used.insert(child.text().clone()) { |
