mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +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)]
|
#[derive(Hash)]
|
||||||
pub enum Param {
|
pub enum Param {
|
||||||
/// A positional parameter: `x`.
|
/// A positional parameter: `x`.
|
||||||
Pos(Ident),
|
Pos(ast::Pattern),
|
||||||
/// A named parameter with a default value: `draw: false`.
|
/// A named parameter with a default value: `draw: false`.
|
||||||
Named(Ident, Value),
|
Named(Ident, Value),
|
||||||
/// An argument sink: `..args`.
|
/// An argument sink: `..args`.
|
||||||
Sink(Option<Ident>),
|
Sink(Option<Ident>),
|
||||||
/// A placeholder: `_`.
|
|
||||||
Placeholder,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Closure {
|
impl Closure {
|
||||||
@ -330,9 +328,18 @@ impl Closure {
|
|||||||
let mut sink_pos_values = None;
|
let mut sink_pos_values = None;
|
||||||
for p in &closure.params {
|
for p in &closure.params {
|
||||||
match p {
|
match p {
|
||||||
Param::Pos(ident) => {
|
Param::Pos(pattern) => match pattern {
|
||||||
vm.define(ident.clone(), args.expect::<Value>(ident)?);
|
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) => {
|
Param::Sink(ident) => {
|
||||||
sink = ident.clone();
|
sink = ident.clone();
|
||||||
if let Some(sink_size) = sink_size {
|
if let Some(sink_size) = sink_size {
|
||||||
@ -344,9 +351,6 @@ impl Closure {
|
|||||||
args.named::<Value>(ident)?.unwrap_or_else(|| default.clone());
|
args.named::<Value>(ident)?.unwrap_or_else(|| default.clone());
|
||||||
vm.define(ident.clone(), value);
|
vm.define(ident.clone(), value);
|
||||||
}
|
}
|
||||||
Param::Placeholder => {
|
|
||||||
args.eat::<Value>()?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,12 +447,15 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
|
|
||||||
for param in expr.params().children() {
|
for param in expr.params().children() {
|
||||||
match param {
|
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::Named(named) => self.bind(named.name()),
|
||||||
ast::Param::Sink(spread) => {
|
ast::Param::Sink(spread) => {
|
||||||
self.bind(spread.name().unwrap_or_default())
|
self.bind(spread.name().unwrap_or_default())
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1207,14 +1207,11 @@ impl Eval for ast::Closure {
|
|||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
for param in self.params().children() {
|
for param in self.params().children() {
|
||||||
match param {
|
match param {
|
||||||
ast::Param::Pos(name) => {
|
ast::Param::Pos(pattern) => params.push(Param::Pos(pattern)),
|
||||||
params.push(Param::Pos(name));
|
|
||||||
}
|
|
||||||
ast::Param::Named(named) => {
|
ast::Param::Named(named) => {
|
||||||
params.push(Param::Named(named.name(), named.expr().eval(vm)?));
|
params.push(Param::Named(named.name(), named.expr().eval(vm)?));
|
||||||
}
|
}
|
||||||
ast::Param::Sink(spread) => params.push(Param::Sink(spread.name())),
|
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)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub enum Param {
|
pub enum Param {
|
||||||
/// A positional parameter: `x`.
|
/// A positional parameter: `x`.
|
||||||
Pos(Ident),
|
Pos(Pattern),
|
||||||
/// A named parameter with a default value: `draw: false`.
|
/// A named parameter with a default value: `draw: false`.
|
||||||
Named(Named),
|
Named(Named),
|
||||||
/// An argument sink: `..args`.
|
/// An argument sink: `..args`.
|
||||||
Sink(Spread),
|
Sink(Spread),
|
||||||
/// A placeholder: `_`.
|
|
||||||
Placeholder(Underscore),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstNode for Param {
|
impl AstNode for Param {
|
||||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
SyntaxKind::Ident => node.cast().map(Self::Pos),
|
|
||||||
SyntaxKind::Named => node.cast().map(Self::Named),
|
SyntaxKind::Named => node.cast().map(Self::Named),
|
||||||
SyntaxKind::Spread => node.cast().map(Self::Sink),
|
SyntaxKind::Spread => node.cast().map(Self::Sink),
|
||||||
SyntaxKind::Underscore => node.cast().map(Self::Placeholder),
|
_ => node.cast().map(Self::Pos),
|
||||||
_ => Option::None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1619,7 +1615,6 @@ impl AstNode for Param {
|
|||||||
Self::Pos(v) => v.as_untyped(),
|
Self::Pos(v) => v.as_untyped(),
|
||||||
Self::Named(v) => v.as_untyped(),
|
Self::Named(v) => v.as_untyped(),
|
||||||
Self::Sink(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) {
|
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) {
|
fn embedded_code_expr(p: &mut Parser) {
|
||||||
@ -542,7 +546,7 @@ fn embedded_code_expr(p: &mut Parser) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let prev = p.prev_end();
|
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\"`.
|
// Consume error for things like `#12p` or `#"abc\"`.
|
||||||
if !p.progress(prev) {
|
if !p.progress(prev) {
|
||||||
@ -560,14 +564,19 @@ fn embedded_code_expr(p: &mut Parser) {
|
|||||||
p.unstop();
|
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();
|
let m = p.marker();
|
||||||
if let (false, Some(op)) = (atomic, ast::UnOp::from_kind(p.current())) {
|
if let (false, Some(op)) = (atomic, ast::UnOp::from_kind(p.current())) {
|
||||||
p.eat();
|
p.eat();
|
||||||
code_expr_prec(p, atomic, op.precedence());
|
code_expr_prec(p, atomic, op.precedence(), false);
|
||||||
p.wrap(m, SyntaxKind::Unary);
|
p.wrap(m, SyntaxKind::Unary);
|
||||||
} else {
|
} else {
|
||||||
code_primary(p, atomic);
|
code_primary(p, atomic, allow_destructuring);
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -615,7 +624,7 @@ fn code_expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.eat();
|
p.eat();
|
||||||
code_expr_prec(p, false, prec);
|
code_expr_prec(p, false, prec, false);
|
||||||
p.wrap(m, SyntaxKind::Binary);
|
p.wrap(m, SyntaxKind::Binary);
|
||||||
continue;
|
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();
|
let m = p.marker();
|
||||||
match p.current() {
|
match p.current() {
|
||||||
SyntaxKind::Ident => {
|
SyntaxKind::Ident => {
|
||||||
@ -650,7 +659,7 @@ fn code_primary(p: &mut Parser, atomic: bool) {
|
|||||||
|
|
||||||
SyntaxKind::LeftBrace => code_block(p),
|
SyntaxKind::LeftBrace => code_block(p),
|
||||||
SyntaxKind::LeftBracket => content_block(p),
|
SyntaxKind::LeftBracket => content_block(p),
|
||||||
SyntaxKind::LeftParen => with_paren(p),
|
SyntaxKind::LeftParen => with_paren(p, allow_destructuring),
|
||||||
SyntaxKind::Dollar => equation(p),
|
SyntaxKind::Dollar => equation(p),
|
||||||
SyntaxKind::Let => let_binding(p),
|
SyntaxKind::Let => let_binding(p),
|
||||||
SyntaxKind::Set => set_rule(p),
|
SyntaxKind::Set => set_rule(p),
|
||||||
@ -716,7 +725,7 @@ fn content_block(p: &mut Parser) {
|
|||||||
p.wrap(m, SyntaxKind::ContentBlock);
|
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 m = p.marker();
|
||||||
let mut kind = collection(p, true);
|
let mut kind = collection(p, true);
|
||||||
if p.at(SyntaxKind::Arrow) {
|
if p.at(SyntaxKind::Arrow) {
|
||||||
@ -739,11 +748,37 @@ fn with_paren(p: &mut Parser) {
|
|||||||
SyntaxKind::Array => validate_array(p, m),
|
SyntaxKind::Array => validate_array(p, m),
|
||||||
SyntaxKind::Dict => validate_dict(p, m),
|
SyntaxKind::Dict => validate_dict(p, m),
|
||||||
SyntaxKind::Parenthesized => validate_parenthesized(p, m),
|
SyntaxKind::Parenthesized => validate_parenthesized(p, m),
|
||||||
|
SyntaxKind::Destructuring if !allow_destructuring => {
|
||||||
|
invalidate_destructuring(p, m)
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
p.wrap(m, kind);
|
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 {
|
fn collection(p: &mut Parser, keyed: bool) -> SyntaxKind {
|
||||||
p.stop_at_newline(false);
|
p.stop_at_newline(false);
|
||||||
p.assert(SyntaxKind::LeftParen);
|
p.assert(SyntaxKind::LeftParen);
|
||||||
@ -760,10 +795,17 @@ fn collection(p: &mut Parser, keyed: bool) -> SyntaxKind {
|
|||||||
let prev = p.prev_end();
|
let prev = p.prev_end();
|
||||||
match item(p, keyed) {
|
match item(p, keyed) {
|
||||||
SyntaxKind::Spread => parenthesized = false,
|
SyntaxKind::Spread => parenthesized = false,
|
||||||
SyntaxKind::Named | SyntaxKind::Keyed if kind.is_none() => {
|
SyntaxKind::Named | SyntaxKind::Keyed => {
|
||||||
kind = Some(SyntaxKind::Dict);
|
match kind {
|
||||||
|
Some(SyntaxKind::Array) => kind = Some(SyntaxKind::Destructuring),
|
||||||
|
_ => kind = Some(SyntaxKind::Dict),
|
||||||
|
}
|
||||||
parenthesized = false;
|
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),
|
_ 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) {
|
if !p.eat_if(SyntaxKind::Underscore) {
|
||||||
code_expr(p);
|
code_expr_or_pattern(p);
|
||||||
} else {
|
} else {
|
||||||
return SyntaxKind::Underscore;
|
return SyntaxKind::Underscore;
|
||||||
}
|
}
|
||||||
@ -1161,6 +1203,10 @@ fn validate_params(p: &mut Parser, m: Marker) {
|
|||||||
child.make_erroneous();
|
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::LeftParen
|
||||||
| SyntaxKind::RightParen
|
| SyntaxKind::RightParen
|
||||||
| SyntaxKind::Comma
|
| 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) {
|
fn validate_destruct_pattern(p: &mut Parser, m: Marker, forbid_expressions: bool) {
|
||||||
let mut used_spread = false;
|
|
||||||
let mut used = HashSet::new();
|
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() {
|
match child.kind() {
|
||||||
SyntaxKind::Ident => {
|
SyntaxKind::Ident => {
|
||||||
if !used.insert(child.text().clone()) {
|
if !used.insert(child.text().clone()) {
|
||||||
|
@ -145,6 +145,29 @@
|
|||||||
test(greet("Typst", whatever: 10))
|
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
|
// Error: 11-12 duplicate parameter: x
|
||||||
#let f(x, x) = none
|
#let f(x, x) = none
|
||||||
|
Loading…
x
Reference in New Issue
Block a user