summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval/func.rs29
-rw-r--r--src/eval/mod.rs5
-rw-r--r--src/syntax/ast.rs9
-rw-r--r--src/syntax/parser.rs82
4 files changed, 89 insertions, 36 deletions
diff --git a/src/eval/func.rs b/src/eval/func.rs
index e3b6a99c..a6e0de84 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -276,13 +276,11 @@ pub(super) struct Closure {
#[derive(Hash)]
pub enum Param {
/// A positional parameter: `x`.
- Pos(Ident),
+ Pos(ast::Pattern),
/// A named parameter with a default value: `draw: false`.
Named(Ident, Value),
/// An argument sink: `..args`.
Sink(Option<Ident>),
- /// A placeholder: `_`.
- Placeholder,
}
impl Closure {
@@ -330,9 +328,18 @@ impl Closure {
let mut sink_pos_values = None;
for p in &closure.params {
match p {
- Param::Pos(ident) => {
- vm.define(ident.clone(), args.expect::<Value>(ident)?);
- }
+ Param::Pos(pattern) => match pattern {
+ ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
+ vm.define(ident.clone(), args.expect::<Value>(ident)?)
+ }
+ ast::Pattern::Normal(_) => unreachable!(),
+ _ => {
+ pattern.define(
+ &mut vm,
+ args.expect::<Value>("pattern parameter")?,
+ )?;
+ }
+ },
Param::Sink(ident) => {
sink = ident.clone();
if let Some(sink_size) = sink_size {
@@ -344,9 +351,6 @@ impl Closure {
args.named::<Value>(ident)?.unwrap_or_else(|| default.clone());
vm.define(ident.clone(), value);
}
- Param::Placeholder => {
- args.eat::<Value>()?;
- }
}
}
@@ -443,12 +447,15 @@ impl<'a> CapturesVisitor<'a> {
for param in expr.params().children() {
match param {
- ast::Param::Pos(ident) => self.bind(ident),
+ ast::Param::Pos(pattern) => {
+ for ident in pattern.idents() {
+ self.bind(ident);
+ }
+ }
ast::Param::Named(named) => self.bind(named.name()),
ast::Param::Sink(spread) => {
self.bind(spread.name().unwrap_or_default())
}
- _ => {}
}
}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index d2ca0e74..e8e1af51 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -1207,14 +1207,11 @@ impl Eval for ast::Closure {
let mut params = Vec::new();
for param in self.params().children() {
match param {
- ast::Param::Pos(name) => {
- params.push(Param::Pos(name));
- }
+ ast::Param::Pos(pattern) => params.push(Param::Pos(pattern)),
ast::Param::Named(named) => {
params.push(Param::Named(named.name(), named.expr().eval(vm)?));
}
ast::Param::Sink(spread) => params.push(Param::Sink(spread.name())),
- ast::Param::Placeholder(_) => params.push(Param::Placeholder),
}
}
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()) {