mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Pattern as parameter (#854)
This commit is contained in:
parent
6134e3f4ee
commit
59957746e9
@ -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())
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -145,6 +145,29 @@
|
||||
test(greet("Typst", whatever: 10))
|
||||
}
|
||||
|
||||
---
|
||||
// Parameter unpacking.
|
||||
#let f((a, b), ..c) = (a, b, c)
|
||||
#test(f((1, 2), 3, 4), (1, 2, (3, 4)))
|
||||
|
||||
#let f((k: a, b), c: 3, (d,)) = (a, b, c, d)
|
||||
#test(f((k: 1, b: 2), (4,)), (1, 2, 3, 4))
|
||||
|
||||
// Error: 22-23 duplicate parameter: a
|
||||
#let f((a: b), (c,), a) = none
|
||||
|
||||
// Error: 8-14 expected identifier, found array
|
||||
#let f((a, b): 0) = none
|
||||
|
||||
// Error: 10-19 expected identifier, found destructuring pattern
|
||||
#let f(..(a, b: c)) = none
|
||||
|
||||
// Error: 10-16 expected identifier, found array
|
||||
#let f(..(a, b)) = none
|
||||
|
||||
// Error: 10-19 expected identifier, found destructuring pattern
|
||||
#let f(..(a, b: c)) = none
|
||||
|
||||
---
|
||||
// Error: 11-12 duplicate parameter: x
|
||||
#let f(x, x) = none
|
||||
|
Loading…
x
Reference in New Issue
Block a user