mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Destructuring improvements (#3463)
This commit is contained in:
parent
b2e509d472
commit
be49935753
@ -1285,7 +1285,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
let mut sibling = Some(node.clone());
|
let mut sibling = Some(node.clone());
|
||||||
while let Some(node) = &sibling {
|
while let Some(node) = &sibling {
|
||||||
if let Some(v) = node.cast::<ast::LetBinding>() {
|
if let Some(v) = node.cast::<ast::LetBinding>() {
|
||||||
for ident in v.kind().idents() {
|
for ident in v.kind().bindings() {
|
||||||
defined.insert(ident.get().clone());
|
defined.insert(ident.get().clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1323,7 +1323,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
if let Some(v) = parent.cast::<ast::ForLoop>() {
|
if let Some(v) = parent.cast::<ast::ForLoop>() {
|
||||||
if node.prev_sibling_kind() != Some(SyntaxKind::In) {
|
if node.prev_sibling_kind() != Some(SyntaxKind::In) {
|
||||||
let pattern = v.pattern();
|
let pattern = v.pattern();
|
||||||
for ident in pattern.idents() {
|
for ident in pattern.bindings() {
|
||||||
defined.insert(ident.get().clone());
|
defined.insert(ident.get().clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1157,9 +1157,18 @@ node! {
|
|||||||
|
|
||||||
impl<'a> Parenthesized<'a> {
|
impl<'a> Parenthesized<'a> {
|
||||||
/// The wrapped expression.
|
/// The wrapped expression.
|
||||||
|
///
|
||||||
|
/// Should only be accessed if this is contained in an `Expr`.
|
||||||
pub fn expr(self) -> Expr<'a> {
|
pub fn expr(self) -> Expr<'a> {
|
||||||
self.0.cast_first_match().unwrap_or_default()
|
self.0.cast_first_match().unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The wrapped pattern.
|
||||||
|
///
|
||||||
|
/// Should only be accessed if this is contained in a `Pattern`.
|
||||||
|
pub fn pattern(self) -> Pattern<'a> {
|
||||||
|
self.0.cast_first_match().unwrap_or_default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
@ -1180,13 +1189,13 @@ pub enum ArrayItem<'a> {
|
|||||||
/// A bare expression: `12`.
|
/// A bare expression: `12`.
|
||||||
Pos(Expr<'a>),
|
Pos(Expr<'a>),
|
||||||
/// A spread expression: `..things`.
|
/// A spread expression: `..things`.
|
||||||
Spread(Expr<'a>),
|
Spread(Spread<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AstNode<'a> for ArrayItem<'a> {
|
impl<'a> AstNode<'a> for ArrayItem<'a> {
|
||||||
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
SyntaxKind::Spread => node.cast_first_match().map(Self::Spread),
|
SyntaxKind::Spread => node.cast().map(Self::Spread),
|
||||||
_ => node.cast().map(Self::Pos),
|
_ => node.cast().map(Self::Pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1219,7 +1228,7 @@ pub enum DictItem<'a> {
|
|||||||
/// A keyed pair: `"spacy key": true`.
|
/// A keyed pair: `"spacy key": true`.
|
||||||
Keyed(Keyed<'a>),
|
Keyed(Keyed<'a>),
|
||||||
/// A spread expression: `..things`.
|
/// A spread expression: `..things`.
|
||||||
Spread(Expr<'a>),
|
Spread(Spread<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AstNode<'a> for DictItem<'a> {
|
impl<'a> AstNode<'a> for DictItem<'a> {
|
||||||
@ -1227,7 +1236,7 @@ impl<'a> AstNode<'a> for DictItem<'a> {
|
|||||||
match node.kind() {
|
match node.kind() {
|
||||||
SyntaxKind::Named => node.cast().map(Self::Named),
|
SyntaxKind::Named => node.cast().map(Self::Named),
|
||||||
SyntaxKind::Keyed => node.cast().map(Self::Keyed),
|
SyntaxKind::Keyed => node.cast().map(Self::Keyed),
|
||||||
SyntaxKind::Spread => node.cast_first_match().map(Self::Spread),
|
SyntaxKind::Spread => node.cast().map(Self::Spread),
|
||||||
_ => Option::None,
|
_ => Option::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1253,13 +1262,19 @@ impl<'a> Named<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The right-hand side of the pair: `3pt`.
|
/// The right-hand side of the pair: `3pt`.
|
||||||
|
///
|
||||||
|
/// This should only be accessed if this `Named` is contained in a
|
||||||
|
/// `DictItem`, `Arg`, or `Param`.
|
||||||
pub fn expr(self) -> Expr<'a> {
|
pub fn expr(self) -> Expr<'a> {
|
||||||
self.0.cast_last_match().unwrap_or_default()
|
self.0.cast_last_match().unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The right-hand side of the pair as an identifier.
|
/// The right-hand side of the pair as a pattern.
|
||||||
pub fn expr_ident(self) -> Option<Ident<'a>> {
|
///
|
||||||
self.0.cast_last_match()
|
/// This should only be accessed if this `Named` is contained in a
|
||||||
|
/// `Destructuring`.
|
||||||
|
pub fn pattern(self) -> Pattern<'a> {
|
||||||
|
self.0.cast_last_match().unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1275,11 +1290,45 @@ impl<'a> Keyed<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The right-hand side of the pair: `true`.
|
/// The right-hand side of the pair: `true`.
|
||||||
|
///
|
||||||
|
/// This should only be accessed if this `Keyed` is contained in a
|
||||||
|
/// `DictItem`.
|
||||||
pub fn expr(self) -> Expr<'a> {
|
pub fn expr(self) -> Expr<'a> {
|
||||||
self.0.cast_last_match().unwrap_or_default()
|
self.0.cast_last_match().unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node! {
|
||||||
|
/// A spread: `..x` or `..x.at(0)`.
|
||||||
|
Spread
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Spread<'a> {
|
||||||
|
/// The spreaded expression.
|
||||||
|
///
|
||||||
|
/// This should only be accessed if this `Spread` is contained in an
|
||||||
|
/// `ArrayItem`, `DictItem`, or `Arg`.
|
||||||
|
pub fn expr(self) -> Expr<'a> {
|
||||||
|
self.0.cast_first_match().unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The sink identifier, if present.
|
||||||
|
///
|
||||||
|
/// This should only be accessed if this `Spread` is contained in a
|
||||||
|
/// `Param` or binding `DestructuringItem`.
|
||||||
|
pub fn sink_ident(self) -> Option<Ident<'a>> {
|
||||||
|
self.0.cast_first_match()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The sink expressions, if present.
|
||||||
|
///
|
||||||
|
/// This should only be accessed if this `Spread` is contained in a
|
||||||
|
/// `DestructuringItem`.
|
||||||
|
pub fn sink_expr(self) -> Option<Expr<'a>> {
|
||||||
|
self.0.cast_first_match()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A unary operation: `-x`.
|
/// A unary operation: `-x`.
|
||||||
Unary
|
Unary
|
||||||
@ -1591,14 +1640,14 @@ pub enum Arg<'a> {
|
|||||||
/// A named argument: `draw: false`.
|
/// A named argument: `draw: false`.
|
||||||
Named(Named<'a>),
|
Named(Named<'a>),
|
||||||
/// A spread argument: `..things`.
|
/// A spread argument: `..things`.
|
||||||
Spread(Expr<'a>),
|
Spread(Spread<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AstNode<'a> for Arg<'a> {
|
impl<'a> AstNode<'a> for Arg<'a> {
|
||||||
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
SyntaxKind::Named => node.cast().map(Self::Named),
|
SyntaxKind::Named => node.cast().map(Self::Named),
|
||||||
SyntaxKind::Spread => node.cast_first_match().map(Self::Spread),
|
SyntaxKind::Spread => node.cast().map(Self::Spread),
|
||||||
_ => node.cast().map(Self::Pos),
|
_ => node.cast().map(Self::Pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1648,28 +1697,6 @@ impl<'a> Params<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
|
||||||
/// A spread: `..x` or `..x.at(0)`.
|
|
||||||
Spread
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Spread<'a> {
|
|
||||||
/// Try to get an identifier.
|
|
||||||
pub fn name(self) -> Option<Ident<'a>> {
|
|
||||||
self.0.cast_first_match()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to get an expression.
|
|
||||||
pub fn expr(self) -> Option<Expr<'a>> {
|
|
||||||
self.0.cast_first_match()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node! {
|
|
||||||
/// An underscore: `_`
|
|
||||||
Underscore
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A parameter to a closure.
|
/// A parameter to a closure.
|
||||||
#[derive(Debug, Copy, Clone, Hash)]
|
#[derive(Debug, Copy, Clone, Hash)]
|
||||||
pub enum Param<'a> {
|
pub enum Param<'a> {
|
||||||
@ -1677,15 +1704,15 @@ pub enum Param<'a> {
|
|||||||
Pos(Pattern<'a>),
|
Pos(Pattern<'a>),
|
||||||
/// A named parameter with a default value: `draw: false`.
|
/// A named parameter with a default value: `draw: false`.
|
||||||
Named(Named<'a>),
|
Named(Named<'a>),
|
||||||
/// An argument sink: `..args`.
|
/// An argument sink: `..args` or `..`.
|
||||||
Sink(Spread<'a>),
|
Spread(Spread<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AstNode<'a> for Param<'a> {
|
impl<'a> AstNode<'a> for Param<'a> {
|
||||||
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
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::Spread),
|
||||||
_ => node.cast().map(Self::Pos),
|
_ => node.cast().map(Self::Pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1694,62 +1721,7 @@ impl<'a> AstNode<'a> for Param<'a> {
|
|||||||
match self {
|
match self {
|
||||||
Self::Pos(v) => v.to_untyped(),
|
Self::Pos(v) => v.to_untyped(),
|
||||||
Self::Named(v) => v.to_untyped(),
|
Self::Named(v) => v.to_untyped(),
|
||||||
Self::Sink(v) => v.to_untyped(),
|
Self::Spread(v) => v.to_untyped(),
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node! {
|
|
||||||
/// A destructuring pattern: `x` or `(x, _, ..y)`.
|
|
||||||
Destructuring
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Destructuring<'a> {
|
|
||||||
/// The bindings of the destructuring.
|
|
||||||
pub fn bindings(self) -> impl DoubleEndedIterator<Item = DestructuringKind<'a>> {
|
|
||||||
self.0.children().filter_map(SyntaxNode::cast)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a list of all identifiers in the pattern.
|
|
||||||
pub fn idents(self) -> impl DoubleEndedIterator<Item = Ident<'a>> {
|
|
||||||
self.bindings().filter_map(|binding| match binding {
|
|
||||||
DestructuringKind::Normal(Expr::Ident(ident)) => Some(ident),
|
|
||||||
DestructuringKind::Sink(spread) => spread.name(),
|
|
||||||
DestructuringKind::Named(named) => named.expr_ident(),
|
|
||||||
_ => Option::None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The kind of an element in a destructuring pattern.
|
|
||||||
#[derive(Debug, Copy, Clone, Hash)]
|
|
||||||
pub enum DestructuringKind<'a> {
|
|
||||||
/// An expression: `x`.
|
|
||||||
Normal(Expr<'a>),
|
|
||||||
/// An argument sink: `..y`.
|
|
||||||
Sink(Spread<'a>),
|
|
||||||
/// Named arguments: `x: 1`.
|
|
||||||
Named(Named<'a>),
|
|
||||||
/// A placeholder: `_`.
|
|
||||||
Placeholder(Underscore<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AstNode<'a> for DestructuringKind<'a> {
|
|
||||||
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
|
||||||
match node.kind() {
|
|
||||||
SyntaxKind::Named => node.cast().map(Self::Named),
|
|
||||||
SyntaxKind::Spread => node.cast().map(Self::Sink),
|
|
||||||
SyntaxKind::Underscore => node.cast().map(Self::Placeholder),
|
|
||||||
_ => node.cast().map(Self::Normal),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_untyped(self) -> &'a SyntaxNode {
|
|
||||||
match self {
|
|
||||||
Self::Normal(v) => v.to_untyped(),
|
|
||||||
Self::Named(v) => v.to_untyped(),
|
|
||||||
Self::Sink(v) => v.to_untyped(),
|
|
||||||
Self::Placeholder(v) => v.to_untyped(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1761,6 +1733,8 @@ pub enum Pattern<'a> {
|
|||||||
Normal(Expr<'a>),
|
Normal(Expr<'a>),
|
||||||
/// A placeholder: `_`.
|
/// A placeholder: `_`.
|
||||||
Placeholder(Underscore<'a>),
|
Placeholder(Underscore<'a>),
|
||||||
|
/// A parenthesized pattern.
|
||||||
|
Parenthesized(Parenthesized<'a>),
|
||||||
/// A destructuring pattern: `(x, _, ..y)`.
|
/// A destructuring pattern: `(x, _, ..y)`.
|
||||||
Destructuring(Destructuring<'a>),
|
Destructuring(Destructuring<'a>),
|
||||||
}
|
}
|
||||||
@ -1768,8 +1742,9 @@ pub enum Pattern<'a> {
|
|||||||
impl<'a> AstNode<'a> for Pattern<'a> {
|
impl<'a> AstNode<'a> for Pattern<'a> {
|
||||||
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
SyntaxKind::Destructuring => node.cast().map(Self::Destructuring),
|
|
||||||
SyntaxKind::Underscore => node.cast().map(Self::Placeholder),
|
SyntaxKind::Underscore => node.cast().map(Self::Placeholder),
|
||||||
|
SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
|
||||||
|
SyntaxKind::Destructuring => node.cast().map(Self::Destructuring),
|
||||||
_ => node.cast().map(Self::Normal),
|
_ => node.cast().map(Self::Normal),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1777,18 +1752,20 @@ impl<'a> AstNode<'a> for Pattern<'a> {
|
|||||||
fn to_untyped(self) -> &'a SyntaxNode {
|
fn to_untyped(self) -> &'a SyntaxNode {
|
||||||
match self {
|
match self {
|
||||||
Self::Normal(v) => v.to_untyped(),
|
Self::Normal(v) => v.to_untyped(),
|
||||||
Self::Destructuring(v) => v.to_untyped(),
|
|
||||||
Self::Placeholder(v) => v.to_untyped(),
|
Self::Placeholder(v) => v.to_untyped(),
|
||||||
|
Self::Parenthesized(v) => v.to_untyped(),
|
||||||
|
Self::Destructuring(v) => v.to_untyped(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Pattern<'a> {
|
impl<'a> Pattern<'a> {
|
||||||
/// Returns a list of all identifiers in the pattern.
|
/// Returns a list of all new bindings introduced by the pattern.
|
||||||
pub fn idents(self) -> Vec<Ident<'a>> {
|
pub fn bindings(self) -> Vec<Ident<'a>> {
|
||||||
match self {
|
match self {
|
||||||
Pattern::Normal(Expr::Ident(ident)) => vec![ident],
|
Self::Normal(Expr::Ident(ident)) => vec![ident],
|
||||||
Pattern::Destructuring(destruct) => destruct.idents().collect(),
|
Self::Parenthesized(v) => v.pattern().bindings(),
|
||||||
|
Self::Destructuring(v) => v.bindings(),
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1800,6 +1777,65 @@ impl Default for Pattern<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node! {
|
||||||
|
/// An underscore: `_`
|
||||||
|
Underscore
|
||||||
|
}
|
||||||
|
|
||||||
|
node! {
|
||||||
|
/// A destructuring pattern: `x` or `(x, _, ..y)`.
|
||||||
|
Destructuring
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Destructuring<'a> {
|
||||||
|
/// The items of the destructuring.
|
||||||
|
pub fn items(self) -> impl DoubleEndedIterator<Item = DestructuringItem<'a>> {
|
||||||
|
self.0.children().filter_map(SyntaxNode::cast)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of all new bindings introduced by the destructuring.
|
||||||
|
pub fn bindings(self) -> Vec<Ident<'a>> {
|
||||||
|
self.items()
|
||||||
|
.flat_map(|binding| match binding {
|
||||||
|
DestructuringItem::Pattern(pattern) => pattern.bindings(),
|
||||||
|
DestructuringItem::Named(named) => named.pattern().bindings(),
|
||||||
|
DestructuringItem::Spread(spread) => {
|
||||||
|
spread.sink_ident().into_iter().collect()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The kind of an element in a destructuring pattern.
|
||||||
|
#[derive(Debug, Copy, Clone, Hash)]
|
||||||
|
pub enum DestructuringItem<'a> {
|
||||||
|
/// A sub-pattern: `x`.
|
||||||
|
Pattern(Pattern<'a>),
|
||||||
|
/// A renamed destructuring: `x: y`.
|
||||||
|
Named(Named<'a>),
|
||||||
|
/// A destructuring sink: `..y` or `..`.
|
||||||
|
Spread(Spread<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AstNode<'a> for DestructuringItem<'a> {
|
||||||
|
fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
|
||||||
|
match node.kind() {
|
||||||
|
SyntaxKind::Named => node.cast().map(Self::Named),
|
||||||
|
SyntaxKind::Spread => node.cast().map(Self::Spread),
|
||||||
|
_ => node.cast().map(Self::Pattern),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_untyped(self) -> &'a SyntaxNode {
|
||||||
|
match self {
|
||||||
|
Self::Pattern(v) => v.to_untyped(),
|
||||||
|
Self::Named(v) => v.to_untyped(),
|
||||||
|
Self::Spread(v) => v.to_untyped(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A let binding: `let x = 1`.
|
/// A let binding: `let x = 1`.
|
||||||
LetBinding
|
LetBinding
|
||||||
@ -1815,13 +1851,11 @@ pub enum LetBindingKind<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LetBindingKind<'a> {
|
impl<'a> LetBindingKind<'a> {
|
||||||
/// Returns a list of all identifiers in the pattern.
|
/// Returns a list of all new bindings introduced by the let binding.
|
||||||
pub fn idents(self) -> Vec<Ident<'a>> {
|
pub fn bindings(self) -> Vec<Ident<'a>> {
|
||||||
match self {
|
match self {
|
||||||
LetBindingKind::Normal(pattern) => pattern.idents(),
|
LetBindingKind::Normal(pattern) => pattern.bindings(),
|
||||||
LetBindingKind::Closure(ident) => {
|
LetBindingKind::Closure(ident) => vec![ident],
|
||||||
vec![ident]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1840,7 +1874,7 @@ impl<'a> LetBinding<'a> {
|
|||||||
/// The expression the binding is initialized with.
|
/// The expression the binding is initialized with.
|
||||||
pub fn init(self) -> Option<Expr<'a>> {
|
pub fn init(self) -> Option<Expr<'a>> {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
LetBindingKind::Normal(Pattern::Normal(_)) => {
|
LetBindingKind::Normal(Pattern::Normal(_) | Pattern::Parenthesized(_)) => {
|
||||||
self.0.children().filter_map(SyntaxNode::cast).nth(1)
|
self.0.children().filter_map(SyntaxNode::cast).nth(1)
|
||||||
}
|
}
|
||||||
LetBindingKind::Normal(_) => self.0.cast_first_match(),
|
LetBindingKind::Normal(_) => self.0.cast_first_match(),
|
||||||
|
@ -136,7 +136,7 @@ pub enum SyntaxKind {
|
|||||||
StarEq,
|
StarEq,
|
||||||
/// The divide-assign operator: `/=`.
|
/// The divide-assign operator: `/=`.
|
||||||
SlashEq,
|
SlashEq,
|
||||||
/// The spread operator: `..`.
|
/// Indicates a spread or sink: `..`.
|
||||||
Dots,
|
Dots,
|
||||||
/// An arrow between a closure's parameters and body: `=>`.
|
/// An arrow between a closure's parameters and body: `=>`.
|
||||||
Arrow,
|
Arrow,
|
||||||
|
@ -3,7 +3,7 @@ use std::ops::{Deref, Range};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ecow::{eco_vec, EcoString, EcoVec};
|
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||||
|
|
||||||
use crate::ast::AstNode;
|
use crate::ast::AstNode;
|
||||||
use crate::{FileId, Span, SyntaxKind};
|
use crate::{FileId, Span, SyntaxKind};
|
||||||
@ -177,14 +177,9 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxNode {
|
impl SyntaxNode {
|
||||||
/// Mark this node as erroneous.
|
|
||||||
pub(super) fn make_erroneous(&mut self) {
|
|
||||||
if let Repr::Inner(inner) = &mut self.0 {
|
|
||||||
Arc::make_mut(inner).erroneous = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the child to another kind.
|
/// Convert the child to another kind.
|
||||||
|
///
|
||||||
|
/// Don't use this for converting to an error!
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(super) fn convert_to_kind(&mut self, kind: SyntaxKind) {
|
pub(super) fn convert_to_kind(&mut self, kind: SyntaxKind) {
|
||||||
debug_assert!(!kind.is_error());
|
debug_assert!(!kind.is_error());
|
||||||
@ -195,11 +190,31 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the child to an error.
|
/// Convert the child to an error, if it isn't already one.
|
||||||
pub(super) fn convert_to_error(&mut self, message: impl Into<EcoString>) {
|
pub(super) fn convert_to_error(&mut self, message: impl Into<EcoString>) {
|
||||||
|
if !self.kind().is_error() {
|
||||||
let text = std::mem::take(self).into_text();
|
let text = std::mem::take(self).into_text();
|
||||||
*self = SyntaxNode::error(message, text);
|
*self = SyntaxNode::error(message, text);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the child to an error stating that the given thing was
|
||||||
|
/// expected, but the current kind was found.
|
||||||
|
pub(super) fn expected(&mut self, expected: &str) {
|
||||||
|
let kind = self.kind();
|
||||||
|
self.convert_to_error(eco_format!("expected {expected}, found {}", kind.name()));
|
||||||
|
if kind.is_keyword() && matches!(expected, "identifier" | "pattern") {
|
||||||
|
self.hint(eco_format!(
|
||||||
|
"keyword `{text}` is not allowed as an identifier; try `{text}_` instead",
|
||||||
|
text = self.text(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the child to an error stating it was unexpected.
|
||||||
|
pub(super) fn unexpected(&mut self) {
|
||||||
|
self.convert_to_error(eco_format!("unexpected {}", self.kind().name()));
|
||||||
|
}
|
||||||
|
|
||||||
/// Assign spans to each node.
|
/// Assign spans to each node.
|
||||||
pub(super) fn numberize(
|
pub(super) fn numberize(
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -10,17 +10,16 @@ pub struct SyntaxSet(u128);
|
|||||||
|
|
||||||
impl SyntaxSet {
|
impl SyntaxSet {
|
||||||
/// Create a new set from a slice of kinds.
|
/// Create a new set from a slice of kinds.
|
||||||
pub const fn new(slice: &[SyntaxKind]) -> Self {
|
pub const fn new() -> Self {
|
||||||
let mut bits = 0;
|
Self(0)
|
||||||
let mut i = 0;
|
|
||||||
while i < slice.len() {
|
|
||||||
bits |= bit(slice[i]);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
Self(bits)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a syntax kind into the set.
|
/// Insert a syntax kind into the set.
|
||||||
|
pub const fn add(self, kind: SyntaxKind) -> Self {
|
||||||
|
Self(self.0 | bit(kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Combine two syntax sets.
|
||||||
pub const fn union(self, other: Self) -> Self {
|
pub const fn union(self, other: Self) -> Self {
|
||||||
Self(self.0 | other.0)
|
Self(self.0 | other.0)
|
||||||
}
|
}
|
||||||
@ -36,56 +35,53 @@ const fn bit(kind: SyntaxKind) -> u128 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Syntax kinds that can start a statement.
|
/// Syntax kinds that can start a statement.
|
||||||
pub const STMT: SyntaxSet = SyntaxSet::new(&[
|
pub const STMT: SyntaxSet = SyntaxSet::new()
|
||||||
SyntaxKind::Let,
|
.add(SyntaxKind::Let)
|
||||||
SyntaxKind::Set,
|
.add(SyntaxKind::Set)
|
||||||
SyntaxKind::Show,
|
.add(SyntaxKind::Show)
|
||||||
SyntaxKind::Import,
|
.add(SyntaxKind::Import)
|
||||||
SyntaxKind::Include,
|
.add(SyntaxKind::Include)
|
||||||
SyntaxKind::Return,
|
.add(SyntaxKind::Return);
|
||||||
]);
|
|
||||||
|
|
||||||
/// Syntax kinds that can start a markup expression.
|
/// Syntax kinds that can start a markup expression.
|
||||||
pub const MARKUP_EXPR: SyntaxSet = SyntaxSet::new(&[
|
pub const MARKUP_EXPR: SyntaxSet = SyntaxSet::new()
|
||||||
SyntaxKind::Space,
|
.add(SyntaxKind::Space)
|
||||||
SyntaxKind::Parbreak,
|
.add(SyntaxKind::Parbreak)
|
||||||
SyntaxKind::LineComment,
|
.add(SyntaxKind::LineComment)
|
||||||
SyntaxKind::BlockComment,
|
.add(SyntaxKind::BlockComment)
|
||||||
SyntaxKind::Text,
|
.add(SyntaxKind::Text)
|
||||||
SyntaxKind::Linebreak,
|
.add(SyntaxKind::Linebreak)
|
||||||
SyntaxKind::Escape,
|
.add(SyntaxKind::Escape)
|
||||||
SyntaxKind::Shorthand,
|
.add(SyntaxKind::Shorthand)
|
||||||
SyntaxKind::SmartQuote,
|
.add(SyntaxKind::SmartQuote)
|
||||||
SyntaxKind::Raw,
|
.add(SyntaxKind::Raw)
|
||||||
SyntaxKind::Link,
|
.add(SyntaxKind::Link)
|
||||||
SyntaxKind::Label,
|
.add(SyntaxKind::Label)
|
||||||
SyntaxKind::Hash,
|
.add(SyntaxKind::Hash)
|
||||||
SyntaxKind::Star,
|
.add(SyntaxKind::Star)
|
||||||
SyntaxKind::Underscore,
|
.add(SyntaxKind::Underscore)
|
||||||
SyntaxKind::HeadingMarker,
|
.add(SyntaxKind::HeadingMarker)
|
||||||
SyntaxKind::ListMarker,
|
.add(SyntaxKind::ListMarker)
|
||||||
SyntaxKind::EnumMarker,
|
.add(SyntaxKind::EnumMarker)
|
||||||
SyntaxKind::TermMarker,
|
.add(SyntaxKind::TermMarker)
|
||||||
SyntaxKind::RefMarker,
|
.add(SyntaxKind::RefMarker)
|
||||||
SyntaxKind::Dollar,
|
.add(SyntaxKind::Dollar)
|
||||||
SyntaxKind::LeftBracket,
|
.add(SyntaxKind::LeftBracket)
|
||||||
SyntaxKind::RightBracket,
|
.add(SyntaxKind::RightBracket)
|
||||||
SyntaxKind::Colon,
|
.add(SyntaxKind::Colon);
|
||||||
]);
|
|
||||||
|
|
||||||
/// Syntax kinds that can start a math expression.
|
/// Syntax kinds that can start a math expression.
|
||||||
pub const MATH_EXPR: SyntaxSet = SyntaxSet::new(&[
|
pub const MATH_EXPR: SyntaxSet = SyntaxSet::new()
|
||||||
SyntaxKind::Hash,
|
.add(SyntaxKind::Hash)
|
||||||
SyntaxKind::MathIdent,
|
.add(SyntaxKind::MathIdent)
|
||||||
SyntaxKind::Text,
|
.add(SyntaxKind::Text)
|
||||||
SyntaxKind::Shorthand,
|
.add(SyntaxKind::Shorthand)
|
||||||
SyntaxKind::Linebreak,
|
.add(SyntaxKind::Linebreak)
|
||||||
SyntaxKind::MathAlignPoint,
|
.add(SyntaxKind::MathAlignPoint)
|
||||||
SyntaxKind::Escape,
|
.add(SyntaxKind::Escape)
|
||||||
SyntaxKind::Str,
|
.add(SyntaxKind::Str)
|
||||||
SyntaxKind::Root,
|
.add(SyntaxKind::Root)
|
||||||
SyntaxKind::Prime,
|
.add(SyntaxKind::Prime);
|
||||||
]);
|
|
||||||
|
|
||||||
/// Syntax kinds that can start a code expression.
|
/// Syntax kinds that can start a code expression.
|
||||||
pub const CODE_EXPR: SyntaxSet = CODE_PRIMARY.union(UNARY_OP);
|
pub const CODE_EXPR: SyntaxSet = CODE_PRIMARY.union(UNARY_OP);
|
||||||
@ -94,63 +90,81 @@ pub const CODE_EXPR: SyntaxSet = CODE_PRIMARY.union(UNARY_OP);
|
|||||||
pub const ATOMIC_CODE_EXPR: SyntaxSet = ATOMIC_CODE_PRIMARY;
|
pub const ATOMIC_CODE_EXPR: SyntaxSet = ATOMIC_CODE_PRIMARY;
|
||||||
|
|
||||||
/// Syntax kinds that can start a code primary.
|
/// Syntax kinds that can start a code primary.
|
||||||
pub const CODE_PRIMARY: SyntaxSet =
|
pub const CODE_PRIMARY: SyntaxSet = ATOMIC_CODE_PRIMARY.add(SyntaxKind::Underscore);
|
||||||
ATOMIC_CODE_PRIMARY.union(SyntaxSet::new(&[SyntaxKind::Underscore]));
|
|
||||||
|
|
||||||
/// Syntax kinds that can start an atomic code primary.
|
/// Syntax kinds that can start an atomic code primary.
|
||||||
pub const ATOMIC_CODE_PRIMARY: SyntaxSet = SyntaxSet::new(&[
|
pub const ATOMIC_CODE_PRIMARY: SyntaxSet = SyntaxSet::new()
|
||||||
SyntaxKind::Ident,
|
.add(SyntaxKind::Ident)
|
||||||
SyntaxKind::LeftBrace,
|
.add(SyntaxKind::LeftBrace)
|
||||||
SyntaxKind::LeftBracket,
|
.add(SyntaxKind::LeftBracket)
|
||||||
SyntaxKind::LeftParen,
|
.add(SyntaxKind::LeftParen)
|
||||||
SyntaxKind::Dollar,
|
.add(SyntaxKind::Dollar)
|
||||||
SyntaxKind::Let,
|
.add(SyntaxKind::Let)
|
||||||
SyntaxKind::Set,
|
.add(SyntaxKind::Set)
|
||||||
SyntaxKind::Show,
|
.add(SyntaxKind::Show)
|
||||||
SyntaxKind::If,
|
.add(SyntaxKind::If)
|
||||||
SyntaxKind::While,
|
.add(SyntaxKind::While)
|
||||||
SyntaxKind::For,
|
.add(SyntaxKind::For)
|
||||||
SyntaxKind::Import,
|
.add(SyntaxKind::Import)
|
||||||
SyntaxKind::Include,
|
.add(SyntaxKind::Include)
|
||||||
SyntaxKind::Break,
|
.add(SyntaxKind::Break)
|
||||||
SyntaxKind::Continue,
|
.add(SyntaxKind::Continue)
|
||||||
SyntaxKind::Return,
|
.add(SyntaxKind::Return)
|
||||||
SyntaxKind::None,
|
.add(SyntaxKind::None)
|
||||||
SyntaxKind::Auto,
|
.add(SyntaxKind::Auto)
|
||||||
SyntaxKind::Int,
|
.add(SyntaxKind::Int)
|
||||||
SyntaxKind::Float,
|
.add(SyntaxKind::Float)
|
||||||
SyntaxKind::Bool,
|
.add(SyntaxKind::Bool)
|
||||||
SyntaxKind::Numeric,
|
.add(SyntaxKind::Numeric)
|
||||||
SyntaxKind::Str,
|
.add(SyntaxKind::Str)
|
||||||
SyntaxKind::Label,
|
.add(SyntaxKind::Label)
|
||||||
SyntaxKind::Raw,
|
.add(SyntaxKind::Raw);
|
||||||
]);
|
|
||||||
|
|
||||||
/// Syntax kinds that are unary operators.
|
/// Syntax kinds that are unary operators.
|
||||||
pub const UNARY_OP: SyntaxSet =
|
pub const UNARY_OP: SyntaxSet = SyntaxSet::new()
|
||||||
SyntaxSet::new(&[SyntaxKind::Plus, SyntaxKind::Minus, SyntaxKind::Not]);
|
.add(SyntaxKind::Plus)
|
||||||
|
.add(SyntaxKind::Minus)
|
||||||
|
.add(SyntaxKind::Not);
|
||||||
|
|
||||||
/// Syntax kinds that are binary operators.
|
/// Syntax kinds that are binary operators.
|
||||||
pub const BINARY_OP: SyntaxSet = SyntaxSet::new(&[
|
pub const BINARY_OP: SyntaxSet = SyntaxSet::new()
|
||||||
SyntaxKind::Plus,
|
.add(SyntaxKind::Plus)
|
||||||
SyntaxKind::Minus,
|
.add(SyntaxKind::Minus)
|
||||||
SyntaxKind::Star,
|
.add(SyntaxKind::Star)
|
||||||
SyntaxKind::Slash,
|
.add(SyntaxKind::Slash)
|
||||||
SyntaxKind::And,
|
.add(SyntaxKind::And)
|
||||||
SyntaxKind::Or,
|
.add(SyntaxKind::Or)
|
||||||
SyntaxKind::EqEq,
|
.add(SyntaxKind::EqEq)
|
||||||
SyntaxKind::ExclEq,
|
.add(SyntaxKind::ExclEq)
|
||||||
SyntaxKind::Lt,
|
.add(SyntaxKind::Lt)
|
||||||
SyntaxKind::LtEq,
|
.add(SyntaxKind::LtEq)
|
||||||
SyntaxKind::Gt,
|
.add(SyntaxKind::Gt)
|
||||||
SyntaxKind::GtEq,
|
.add(SyntaxKind::GtEq)
|
||||||
SyntaxKind::Eq,
|
.add(SyntaxKind::Eq)
|
||||||
SyntaxKind::In,
|
.add(SyntaxKind::In)
|
||||||
SyntaxKind::PlusEq,
|
.add(SyntaxKind::PlusEq)
|
||||||
SyntaxKind::HyphEq,
|
.add(SyntaxKind::HyphEq)
|
||||||
SyntaxKind::StarEq,
|
.add(SyntaxKind::StarEq)
|
||||||
SyntaxKind::SlashEq,
|
.add(SyntaxKind::SlashEq);
|
||||||
]);
|
|
||||||
|
/// Syntax kinds that can start an argument in a function call.
|
||||||
|
pub const ARRAY_OR_DICT_ITEM: SyntaxSet = CODE_EXPR.add(SyntaxKind::Dots);
|
||||||
|
|
||||||
|
/// Syntax kinds that can start an argument in a function call.
|
||||||
|
pub const ARG: SyntaxSet = CODE_EXPR.add(SyntaxKind::Dots);
|
||||||
|
|
||||||
|
/// Syntax kinds that can start a parameter in a parameter list.
|
||||||
|
pub const PARAM: SyntaxSet = PATTERN.add(SyntaxKind::Dots);
|
||||||
|
|
||||||
|
/// Syntax kinds that can start a destructuring item.
|
||||||
|
pub const DESTRUCTURING_ITEM: SyntaxSet = PATTERN.add(SyntaxKind::Dots);
|
||||||
|
|
||||||
|
/// Syntax kinds that can start a pattern.
|
||||||
|
pub const PATTERN: SyntaxSet =
|
||||||
|
PATTERN_LEAF.add(SyntaxKind::LeftParen).add(SyntaxKind::Underscore);
|
||||||
|
|
||||||
|
/// Syntax kinds that can start a pattern leaf.
|
||||||
|
pub const PATTERN_LEAF: SyntaxSet = ATOMIC_CODE_EXPR;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@ -163,7 +177,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set() {
|
fn test_set() {
|
||||||
let set = SyntaxSet::new(&[SyntaxKind::And, SyntaxKind::Or]);
|
let set = SyntaxSet::new().add(SyntaxKind::And).add(SyntaxKind::Or);
|
||||||
assert!(set.contains(SyntaxKind::And));
|
assert!(set.contains(SyntaxKind::And));
|
||||||
assert!(set.contains(SyntaxKind::Or));
|
assert!(set.contains(SyntaxKind::Or));
|
||||||
assert!(!set.contains(SyntaxKind::Not));
|
assert!(!set.contains(SyntaxKind::Not));
|
||||||
|
@ -31,7 +31,7 @@ impl Eval for ast::DestructAssignment<'_> {
|
|||||||
|
|
||||||
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let value = self.value().eval(vm)?;
|
let value = self.value().eval(vm)?;
|
||||||
destructure_impl(vm, self.pattern(), value, |vm, expr, value| {
|
destructure_impl(vm, self.pattern(), value, &mut |vm, expr, value| {
|
||||||
let location = expr.access(vm)?;
|
let location = expr.access(vm)?;
|
||||||
*location = value;
|
*location = value;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -46,33 +46,34 @@ pub(crate) fn destructure(
|
|||||||
pattern: ast::Pattern,
|
pattern: ast::Pattern,
|
||||||
value: Value,
|
value: Value,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
destructure_impl(vm, pattern, value, |vm, expr, value| match expr {
|
destructure_impl(vm, pattern, value, &mut |vm, expr, value| match expr {
|
||||||
ast::Expr::Ident(ident) => {
|
ast::Expr::Ident(ident) => {
|
||||||
vm.define(ident, value);
|
vm.define(ident, value);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => bail!(expr.span(), "nested patterns are currently not supported"),
|
_ => bail!(expr.span(), "cannot assign to this expression"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destruct the given value into the pattern and apply the function to each binding.
|
/// Destruct the given value into the pattern and apply the function to each binding.
|
||||||
fn destructure_impl<T>(
|
fn destructure_impl<F>(
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
pattern: ast::Pattern,
|
pattern: ast::Pattern,
|
||||||
value: Value,
|
value: Value,
|
||||||
f: T,
|
f: &mut F,
|
||||||
) -> SourceResult<()>
|
) -> SourceResult<()>
|
||||||
where
|
where
|
||||||
T: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<()>,
|
F: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<()>,
|
||||||
{
|
{
|
||||||
match pattern {
|
match pattern {
|
||||||
ast::Pattern::Normal(expr) => {
|
ast::Pattern::Normal(expr) => f(vm, expr, value)?,
|
||||||
f(vm, expr, value)?;
|
|
||||||
}
|
|
||||||
ast::Pattern::Placeholder(_) => {}
|
ast::Pattern::Placeholder(_) => {}
|
||||||
|
ast::Pattern::Parenthesized(parenthesized) => {
|
||||||
|
destructure_impl(vm, parenthesized.pattern(), value, f)?
|
||||||
|
}
|
||||||
ast::Pattern::Destructuring(destruct) => match value {
|
ast::Pattern::Destructuring(destruct) => match value {
|
||||||
Value::Array(value) => destructure_array(vm, pattern, value, f, destruct)?,
|
Value::Array(value) => destructure_array(vm, destruct, value, f)?,
|
||||||
Value::Dict(value) => destructure_dict(vm, value, f, destruct)?,
|
Value::Dict(value) => destructure_dict(vm, destruct, value, f)?,
|
||||||
_ => bail!(pattern.span(), "cannot destructure {}", value.ty()),
|
_ => bail!(pattern.span(), "cannot destructure {}", value.ty()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -81,51 +82,44 @@ where
|
|||||||
|
|
||||||
fn destructure_array<F>(
|
fn destructure_array<F>(
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
pattern: ast::Pattern,
|
|
||||||
value: Array,
|
|
||||||
f: F,
|
|
||||||
destruct: ast::Destructuring,
|
destruct: ast::Destructuring,
|
||||||
|
value: Array,
|
||||||
|
f: &mut F,
|
||||||
) -> SourceResult<()>
|
) -> SourceResult<()>
|
||||||
where
|
where
|
||||||
F: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<()>,
|
F: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<()>,
|
||||||
{
|
{
|
||||||
let mut i = 0;
|
|
||||||
let len = value.as_slice().len();
|
let len = value.as_slice().len();
|
||||||
for p in destruct.bindings() {
|
let mut i = 0;
|
||||||
|
|
||||||
|
for p in destruct.items() {
|
||||||
match p {
|
match p {
|
||||||
ast::DestructuringKind::Normal(expr) => {
|
ast::DestructuringItem::Pattern(pattern) => {
|
||||||
let Ok(v) = value.at(i as i64, None) else {
|
let Ok(v) = value.at(i as i64, None) else {
|
||||||
bail!(expr.span(), "not enough elements to destructure");
|
bail!(pattern.span(), "not enough elements to destructure");
|
||||||
};
|
};
|
||||||
f(vm, expr, v)?;
|
destructure_impl(vm, pattern, v, f)?;
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
ast::DestructuringKind::Sink(spread) => {
|
ast::DestructuringItem::Spread(spread) => {
|
||||||
let sink_size = (1 + len).checked_sub(destruct.bindings().count());
|
let sink_size = (1 + len).checked_sub(destruct.items().count());
|
||||||
let sink = sink_size.and_then(|s| value.as_slice().get(i..i + s));
|
let sink = sink_size.and_then(|s| value.as_slice().get(i..i + s));
|
||||||
if let (Some(sink_size), Some(sink)) = (sink_size, sink) {
|
let (Some(sink_size), Some(sink)) = (sink_size, sink) else {
|
||||||
if let Some(expr) = spread.expr() {
|
bail!(spread.span(), "not enough elements to destructure");
|
||||||
|
};
|
||||||
|
if let Some(expr) = spread.sink_expr() {
|
||||||
f(vm, expr, Value::Array(sink.into()))?;
|
f(vm, expr, Value::Array(sink.into()))?;
|
||||||
}
|
}
|
||||||
i += sink_size;
|
i += sink_size;
|
||||||
} else {
|
}
|
||||||
bail!(pattern.span(), "not enough elements to destructure")
|
ast::DestructuringItem::Named(named) => {
|
||||||
|
bail!(named.span(), "cannot destructure named pattern from an array")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::DestructuringKind::Named(named) => {
|
|
||||||
bail!(named.span(), "cannot destructure named elements from an array")
|
|
||||||
}
|
}
|
||||||
ast::DestructuringKind::Placeholder(underscore) => {
|
|
||||||
if i < len {
|
if i < len {
|
||||||
i += 1
|
bail!(destruct.span(), "too many elements to destructure");
|
||||||
} else {
|
|
||||||
bail!(underscore.span(), "not enough elements to destructure")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i < len {
|
|
||||||
bail!(pattern.span(), "too many elements to destructure");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -133,32 +127,35 @@ where
|
|||||||
|
|
||||||
fn destructure_dict<F>(
|
fn destructure_dict<F>(
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
dict: Dict,
|
|
||||||
f: F,
|
|
||||||
destruct: ast::Destructuring,
|
destruct: ast::Destructuring,
|
||||||
|
dict: Dict,
|
||||||
|
f: &mut F,
|
||||||
) -> SourceResult<()>
|
) -> SourceResult<()>
|
||||||
where
|
where
|
||||||
F: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<()>,
|
F: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<()>,
|
||||||
{
|
{
|
||||||
let mut sink = None;
|
let mut sink = None;
|
||||||
let mut used = HashSet::new();
|
let mut used = HashSet::new();
|
||||||
for p in destruct.bindings() {
|
|
||||||
|
for p in destruct.items() {
|
||||||
match p {
|
match p {
|
||||||
ast::DestructuringKind::Normal(ast::Expr::Ident(ident)) => {
|
// Shorthand for a direct identifier.
|
||||||
|
ast::DestructuringItem::Pattern(ast::Pattern::Normal(ast::Expr::Ident(
|
||||||
|
ident,
|
||||||
|
))) => {
|
||||||
let v = dict.get(&ident).at(ident.span())?;
|
let v = dict.get(&ident).at(ident.span())?;
|
||||||
f(vm, ast::Expr::Ident(ident), v.clone())?;
|
f(vm, ast::Expr::Ident(ident), v.clone())?;
|
||||||
used.insert(ident.as_str());
|
used.insert(ident.get().clone());
|
||||||
}
|
}
|
||||||
ast::DestructuringKind::Sink(spread) => sink = spread.expr(),
|
ast::DestructuringItem::Named(named) => {
|
||||||
ast::DestructuringKind::Named(named) => {
|
|
||||||
let name = named.name();
|
let name = named.name();
|
||||||
let v = dict.get(&name).at(name.span())?;
|
let v = dict.get(&name).at(name.span())?;
|
||||||
f(vm, named.expr(), v.clone())?;
|
destructure_impl(vm, named.pattern(), v.clone(), f)?;
|
||||||
used.insert(name.as_str());
|
used.insert(name.get().clone());
|
||||||
}
|
}
|
||||||
ast::DestructuringKind::Placeholder(_) => {}
|
ast::DestructuringItem::Spread(spread) => sink = spread.sink_expr(),
|
||||||
ast::DestructuringKind::Normal(expr) => {
|
ast::DestructuringItem::Pattern(expr) => {
|
||||||
bail!(expr.span(), "expected key, found expression");
|
bail!(expr.span(), "cannot destructure unnamed pattern from dictionary");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,13 +192,14 @@ impl Eval for ast::Args<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ast::Arg::Named(named) => {
|
ast::Arg::Named(named) => {
|
||||||
|
let expr = named.expr();
|
||||||
items.push(Arg {
|
items.push(Arg {
|
||||||
span,
|
span,
|
||||||
name: Some(named.name().get().clone().into()),
|
name: Some(named.name().get().clone().into()),
|
||||||
value: Spanned::new(named.expr().eval(vm)?, named.expr().span()),
|
value: Spanned::new(expr.eval(vm)?, expr.span()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ast::Arg::Spread(expr) => match expr.eval(vm)? {
|
ast::Arg::Spread(spread) => match spread.expr().eval(vm)? {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Array(array) => {
|
Value::Array(array) => {
|
||||||
items.extend(array.into_iter().map(|value| Arg {
|
items.extend(array.into_iter().map(|value| Arg {
|
||||||
@ -215,7 +216,7 @@ impl Eval for ast::Args<'_> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Value::Args(args) => items.extend(args.items),
|
Value::Args(args) => items.extend(args.items),
|
||||||
v => bail!(expr.span(), "cannot spread {}", v.ty()),
|
v => bail!(spread.span(), "cannot spread {}", v.ty()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,7 +312,6 @@ pub(crate) fn call_closure(
|
|||||||
ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
|
ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
|
||||||
vm.define(ident, args.expect::<Value>(&ident)?)
|
vm.define(ident, args.expect::<Value>(&ident)?)
|
||||||
}
|
}
|
||||||
ast::Pattern::Normal(_) => unreachable!(),
|
|
||||||
pattern => {
|
pattern => {
|
||||||
crate::eval::destructure(
|
crate::eval::destructure(
|
||||||
&mut vm,
|
&mut vm,
|
||||||
@ -320,8 +320,8 @@ pub(crate) fn call_closure(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ast::Param::Sink(ident) => {
|
ast::Param::Spread(spread) => {
|
||||||
sink = Some(ident.name());
|
sink = Some(spread.sink_ident());
|
||||||
if let Some(sink_size) = sink_size {
|
if let Some(sink_size) = sink_size {
|
||||||
sink_pos_values = Some(args.consume(sink_size)?);
|
sink_pos_values = Some(args.consume(sink_size)?);
|
||||||
}
|
}
|
||||||
@ -336,10 +336,10 @@ pub(crate) fn call_closure(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(sink_name) = sink {
|
if let Some(sink) = sink {
|
||||||
// Remaining args are captured regardless of whether the sink is named.
|
// Remaining args are captured regardless of whether the sink is named.
|
||||||
let mut remaining_args = args.take();
|
let mut remaining_args = args.take();
|
||||||
if let Some(sink_name) = sink_name {
|
if let Some(sink_name) = sink {
|
||||||
if let Some(sink_pos_values) = sink_pos_values {
|
if let Some(sink_pos_values) = sink_pos_values {
|
||||||
remaining_args.items.extend(sink_pos_values);
|
remaining_args.items.extend(sink_pos_values);
|
||||||
}
|
}
|
||||||
@ -436,13 +436,15 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
for param in expr.params().children() {
|
for param in expr.params().children() {
|
||||||
match param {
|
match param {
|
||||||
ast::Param::Pos(pattern) => {
|
ast::Param::Pos(pattern) => {
|
||||||
for ident in pattern.idents() {
|
for ident in pattern.bindings() {
|
||||||
self.bind(ident);
|
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::Spread(spread) => {
|
||||||
self.bind(spread.name().unwrap_or_default())
|
if let Some(ident) = spread.sink_ident() {
|
||||||
|
self.bind(ident);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -458,7 +460,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
self.visit(init.to_untyped());
|
self.visit(init.to_untyped());
|
||||||
}
|
}
|
||||||
|
|
||||||
for ident in expr.kind().idents() {
|
for ident in expr.kind().bindings() {
|
||||||
self.bind(ident);
|
self.bind(ident);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -471,7 +473,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
self.internal.enter();
|
self.internal.enter();
|
||||||
|
|
||||||
let pattern = expr.pattern();
|
let pattern = expr.pattern();
|
||||||
for ident in pattern.idents() {
|
for ident in pattern.bindings() {
|
||||||
self.bind(ident);
|
self.bind(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,10 +210,10 @@ impl Eval for ast::Array<'_> {
|
|||||||
for item in items {
|
for item in items {
|
||||||
match item {
|
match item {
|
||||||
ast::ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
|
ast::ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
|
||||||
ast::ArrayItem::Spread(expr) => match expr.eval(vm)? {
|
ast::ArrayItem::Spread(spread) => match spread.expr().eval(vm)? {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Array(array) => vec.extend(array.into_iter()),
|
Value::Array(array) => vec.extend(array.into_iter()),
|
||||||
v => bail!(expr.span(), "cannot spread {} into array", v.ty()),
|
v => bail!(spread.span(), "cannot spread {} into array", v.ty()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,7 +227,6 @@ impl Eval for ast::Dict<'_> {
|
|||||||
|
|
||||||
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let mut map = indexmap::IndexMap::new();
|
let mut map = indexmap::IndexMap::new();
|
||||||
|
|
||||||
let mut invalid_keys = eco_vec![];
|
let mut invalid_keys = eco_vec![];
|
||||||
|
|
||||||
for item in self.items() {
|
for item in self.items() {
|
||||||
@ -245,10 +244,10 @@ impl Eval for ast::Dict<'_> {
|
|||||||
});
|
});
|
||||||
map.insert(key, keyed.expr().eval(vm)?);
|
map.insert(key, keyed.expr().eval(vm)?);
|
||||||
}
|
}
|
||||||
ast::DictItem::Spread(expr) => match expr.eval(vm)? {
|
ast::DictItem::Spread(spread) => match spread.expr().eval(vm)? {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Dict(dict) => map.extend(dict.into_iter()),
|
Value::Dict(dict) => map.extend(dict.into_iter()),
|
||||||
v => bail!(expr.span(), "cannot spread {} into dictionary", v.ty()),
|
v => bail!(spread.span(), "cannot spread {} into dictionary", v.ty()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -884,7 +884,7 @@ fn print_annotation(
|
|||||||
let start_col = 1 + source.byte_to_column(range.start).unwrap();
|
let start_col = 1 + source.byte_to_column(range.start).unwrap();
|
||||||
let end_line = 1 + line + source.byte_to_line(range.end).unwrap();
|
let end_line = 1 + line + source.byte_to_line(range.end).unwrap();
|
||||||
let end_col = 1 + source.byte_to_column(range.end).unwrap();
|
let end_col = 1 + source.byte_to_column(range.end).unwrap();
|
||||||
write!(output, "{start_line}:{start_col}-{end_line}:{end_col}: ").unwrap();
|
write!(output, "{start_line}:{start_col}-{end_line}:{end_col} ").unwrap();
|
||||||
}
|
}
|
||||||
writeln!(output, "{text}").unwrap();
|
writeln!(output, "{text}").unwrap();
|
||||||
}
|
}
|
||||||
|
86
tests/typ/bugs/parenthesized.typ
Normal file
86
tests/typ/bugs/parenthesized.typ
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Ref: false
|
||||||
|
// Test bugs related to destructuring and parenthesized parsing.
|
||||||
|
|
||||||
|
---
|
||||||
|
// https://github.com/typst/typst/issues/1338
|
||||||
|
#let foo = "foo"
|
||||||
|
#let bar = "bar"
|
||||||
|
// Error: 8-9 expected expression, found underscore
|
||||||
|
// Error: 16-17 expected expression, found underscore
|
||||||
|
#(foo: _, bar: _)
|
||||||
|
|
||||||
|
---
|
||||||
|
// https://github.com/typst/typst/issues/1342
|
||||||
|
// Error: 5-8 expected named or keyed pair, found identifier
|
||||||
|
// Error: 10-13 expected named or keyed pair, found identifier
|
||||||
|
#(: foo, bar)
|
||||||
|
|
||||||
|
---
|
||||||
|
// https://github.com/typst/typst/issues/1351
|
||||||
|
// Error: 17-22 expected pattern, found string
|
||||||
|
#let foo((test: "bar")) = {}
|
||||||
|
|
||||||
|
---
|
||||||
|
// https://github.com/typst/typst/issues/3014
|
||||||
|
// Error: 8-17 expected expression, found named pair
|
||||||
|
#(box, fill: red)
|
||||||
|
|
||||||
|
---
|
||||||
|
// https://github.com/typst/typst/issues/3144
|
||||||
|
#let f(a: 10) = a(1) + 1
|
||||||
|
#test(f(a: _ => 5), 6)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 18-20 missing argument: pattern parameter
|
||||||
|
#let f(a: 10) = a() + 1
|
||||||
|
#f(a: _ => 5)
|
||||||
|
|
||||||
|
---
|
||||||
|
// This wasn't allowed.
|
||||||
|
#let ((x)) = 1
|
||||||
|
#test(x, 1)
|
||||||
|
|
||||||
|
---
|
||||||
|
// This also wasn't allowed.
|
||||||
|
#let ((a, b)) = (1, 2)
|
||||||
|
#test(a, 1)
|
||||||
|
#test(b, 2)
|
||||||
|
|
||||||
|
---
|
||||||
|
// This was unintentionally allowed ...
|
||||||
|
// Error: 9 expected equals sign
|
||||||
|
#let (a)
|
||||||
|
|
||||||
|
---
|
||||||
|
// ... where this wasn't.
|
||||||
|
// Error: 12 expected equals sign
|
||||||
|
#let (a, b)
|
||||||
|
|
||||||
|
---
|
||||||
|
// This wasn't allowed before the bug fix ...
|
||||||
|
#let f(..) = {}
|
||||||
|
#f(arg: 1)
|
||||||
|
|
||||||
|
---
|
||||||
|
// ... but this was.
|
||||||
|
#let f(..x) = {}
|
||||||
|
#f(arg: 1)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Here, `best` was accessed as a variable, where it shouldn't have.
|
||||||
|
#{
|
||||||
|
(best: _) = (best: "brr")
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Same here.
|
||||||
|
#{
|
||||||
|
let array = (1, 2, 3, 4)
|
||||||
|
(test: array.at(1), best: _) = (test: "baz", best: "brr")
|
||||||
|
test(array, (1, "baz", 3, 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Here, `a` is not duplicate, where it was previously identified as one.
|
||||||
|
#let f((a: b), (c,), a) = (a, b, c)
|
||||||
|
#test(f((a: 1), (2,), 3), (3, 1, 2))
|
33
tests/typ/compiler/backtracking.typ
Normal file
33
tests/typ/compiler/backtracking.typ
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Ensure that parser backtracking doesn't lead to exponential time consumption.
|
||||||
|
// If this regresses, the test suite will not terminate, which is a bit
|
||||||
|
// unfortunate compared to a good error, but at least we know something is up.
|
||||||
|
//
|
||||||
|
// Ref: false
|
||||||
|
|
||||||
|
---
|
||||||
|
#{
|
||||||
|
let s = "(x: 1) => x"
|
||||||
|
let pat = "(x: {}) => 1 + x()"
|
||||||
|
for _ in range(50) {
|
||||||
|
s = pat.replace("{}", s)
|
||||||
|
}
|
||||||
|
test(eval(s)(), 51)
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
#{
|
||||||
|
let s = "(x) = 1"
|
||||||
|
let pat = "(x: {_}) = 1"
|
||||||
|
for _ in range(100) {
|
||||||
|
s = pat.replace("_", s)
|
||||||
|
}
|
||||||
|
// Error: 8-9 cannot destructure integer
|
||||||
|
eval(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test whitespace after memoized part.
|
||||||
|
#( (x: () => 1 ) => 1 )
|
||||||
|
// -------
|
||||||
|
// This is memoized and we want to ensure that whitespace after this
|
||||||
|
// is handled correctly.
|
@ -126,8 +126,7 @@
|
|||||||
|
|
||||||
// Should output `3`.
|
// Should output `3`.
|
||||||
#{
|
#{
|
||||||
// Error: 6 expected identifier
|
// Error: 7-10 expected pattern, found string
|
||||||
// Error: 10 expected block
|
|
||||||
for "v"
|
for "v"
|
||||||
|
|
||||||
// Error: 8 expected keyword `in`
|
// Error: 8 expected keyword `in`
|
||||||
|
@ -75,8 +75,7 @@
|
|||||||
#f[1](2)
|
#f[1](2)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7 expected expression
|
// Error: 7-8 unexpected colon
|
||||||
// Error: 8 expected expression
|
|
||||||
#func(:)
|
#func(:)
|
||||||
|
|
||||||
// Error: 10-12 unexpected end of block comment
|
// Error: 10-12 unexpected end of block comment
|
||||||
|
@ -171,25 +171,26 @@
|
|||||||
#let f((k: a, b), c: 3, (d,)) = (a, b, c, d)
|
#let f((k: a, b), c: 3, (d,)) = (a, b, c, d)
|
||||||
#test(f((k: 1, b: 2), (4,)), (1, 2, 3, 4))
|
#test(f((k: 1, b: 2), (4,)), (1, 2, 3, 4))
|
||||||
|
|
||||||
// Error: 22-23 duplicate parameter: a
|
// Error: 8-14 expected identifier, found destructuring pattern
|
||||||
#let f((a: b), (c,), a) = none
|
|
||||||
|
|
||||||
// Error: 8-14 expected identifier, found array
|
|
||||||
#let f((a, b): 0) = none
|
#let f((a, b): 0) = none
|
||||||
|
|
||||||
// Error: 10-19 expected identifier, found destructuring pattern
|
// Error: 10-19 expected pattern, found array
|
||||||
#let f(..(a, b: c)) = none
|
#let f(..(a, b: c)) = none
|
||||||
|
|
||||||
// Error: 10-16 expected identifier, found array
|
// Error: 10-16 expected pattern, found array
|
||||||
#let f(..(a, b)) = none
|
#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
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 21 expected comma
|
||||||
|
// Error: 22-23 expected pattern, found integer
|
||||||
|
// Error: 24-25 unexpected plus
|
||||||
|
// Error: 26-27 expected pattern, found integer
|
||||||
|
#let f = (x: () => 1 2 + 3) => 4
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 14-15 duplicate parameter: a
|
// Error: 14-15 duplicate parameter: a
|
||||||
// Error: 23-24 duplicate parameter: b
|
// Error: 23-24 duplicate parameter: b
|
||||||
@ -201,17 +202,18 @@
|
|||||||
#let f(a, ..a) = none
|
#let f(a, ..a) = none
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7-17 expected identifier, named pair or argument sink, found keyed pair
|
// Error: 7-14 expected pattern, found string
|
||||||
#((a, "named": b) => none)
|
#((a, "named": b) => none)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 10-15 expected identifier, found string
|
// Error: 10-15 expected pattern, found string
|
||||||
#let foo("key": b) = key
|
#let foo("key": b) = key
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 10-14 expected identifier, found `none`
|
// Error: 10-14 expected pattern, found `none`
|
||||||
|
// Hint: 10-14 keyword `none` is not allowed as an identifier; try `none_` instead
|
||||||
#let foo(none: b) = key
|
#let foo(none: b) = key
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 11 expected comma
|
// Error: 10-11 expected identifier, found underscore
|
||||||
#let foo(_: 3) = none
|
#let foo(_: 3) = none
|
||||||
|
@ -110,7 +110,6 @@
|
|||||||
// Identified as dictionary due to initial colon.
|
// Identified as dictionary due to initial colon.
|
||||||
// The boolean key is allowed for now since it will only cause an error at the evaluation stage.
|
// The boolean key is allowed for now since it will only cause an error at the evaluation stage.
|
||||||
// Error: 4-5 expected named or keyed pair, found integer
|
// Error: 4-5 expected named or keyed pair, found integer
|
||||||
// Error: 5 expected comma
|
|
||||||
// Error: 17 expected expression
|
// Error: 17 expected expression
|
||||||
#(:1 b:"", true:)
|
#(:1 b:"", true:)
|
||||||
|
|
||||||
@ -152,7 +151,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7-10 expected identifier, found group
|
// Error: 7-10 expected identifier, found group
|
||||||
// Error: 12-14 expected identifier, found integer
|
// Error: 12-14 expected pattern, found integer
|
||||||
#let ((a): 10) = "world"
|
#let ((a): 10) = "world"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
// Ref: false
|
// Ref: false
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 6-8 expected identifier, found keyword `as`
|
// Error: 6-8 expected pattern, found keyword `as`
|
||||||
// Hint: 6-8 keyword `as` is not allowed as an identifier; try `as_` instead
|
// Hint: 6-8 keyword `as` is not allowed as an identifier; try `as_` instead
|
||||||
#let as = 1 + 2
|
#let as = 1 + 2
|
||||||
|
|
||||||
---
|
---
|
||||||
#{
|
#{
|
||||||
// Error: 7-9 expected identifier, found keyword `as`
|
// Error: 7-9 expected pattern, found keyword `as`
|
||||||
// Hint: 7-9 keyword `as` is not allowed as an identifier; try `as_` instead
|
// Hint: 7-9 keyword `as` is not allowed as an identifier; try `as_` instead
|
||||||
let as = 10
|
let as = 10
|
||||||
}
|
}
|
||||||
|
@ -92,19 +92,24 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Destructuring without parentheses.
|
// Destructuring without parentheses.
|
||||||
// Error: 7 expected keyword `in`
|
// Error: 7-8 unexpected comma
|
||||||
// Hint: 7 did you mean to use a destructuring pattern?
|
// Hint: 7-8 destructuring patterns must be wrapped in parentheses
|
||||||
#for k, v in (a: 4, b: 5) {
|
#for k, v in (a: 4, b: 5) {
|
||||||
dont-care
|
dont-care
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error: 5 expected identifier
|
// Error: 7-8 unexpected comma
|
||||||
|
// Hint: 7-8 destructuring patterns must be wrapped in parentheses
|
||||||
|
#for k, in () {}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 5 expected pattern
|
||||||
#for
|
#for
|
||||||
|
|
||||||
// Error: 5 expected identifier
|
// Error: 5 expected pattern
|
||||||
#for//
|
#for//
|
||||||
|
|
||||||
// Error: 6 expected identifier
|
// Error: 6 expected pattern
|
||||||
#{for}
|
#{for}
|
||||||
|
|
||||||
// Error: 7 expected keyword `in`
|
// Error: 7 expected keyword `in`
|
||||||
@ -116,15 +121,15 @@
|
|||||||
// Error: 15 expected block
|
// Error: 15 expected block
|
||||||
#for v in iter
|
#for v in iter
|
||||||
|
|
||||||
// Error: 5 expected identifier
|
// Error: 5 expected pattern
|
||||||
#for
|
#for
|
||||||
v in iter {}
|
v in iter {}
|
||||||
|
|
||||||
// Error: 6 expected identifier
|
// Error: 7-10 expected pattern, found string
|
||||||
// Error: 10 expected block
|
// Error: 16 expected block
|
||||||
A#for "v" thing
|
A#for "v" thing
|
||||||
|
|
||||||
// Error: 5 expected identifier
|
// Error: 6-9 expected pattern, found string
|
||||||
#for "v" in iter {}
|
#for "v" in iter {}
|
||||||
|
|
||||||
// Error: 7 expected keyword `in`
|
// Error: 7 expected keyword `in`
|
||||||
|
@ -125,22 +125,22 @@ Three
|
|||||||
#test(a, 1)
|
#test(a, 1)
|
||||||
#test(b, 4)
|
#test(b, 4)
|
||||||
|
|
||||||
// Error: 10-11 at most one binding per identifier is allowed
|
// Error: 10-11 duplicate binding: a
|
||||||
#let (a, a) = (1, 2)
|
#let (a, a) = (1, 2)
|
||||||
|
|
||||||
// Error: 12-15 at most one destructuring sink is allowed
|
// Error: 12-15 only one destructuring sink is allowed
|
||||||
#let (..a, ..a) = (1, 2)
|
#let (..a, ..a) = (1, 2)
|
||||||
|
|
||||||
// Error: 12-13 at most one binding per identifier is allowed
|
// Error: 12-13 duplicate binding: a
|
||||||
#let (a, ..a) = (1, 2)
|
#let (a, ..a) = (1, 2)
|
||||||
|
|
||||||
// Error: 13-14 at most one binding per identifier is allowed
|
// Error: 13-14 duplicate binding: a
|
||||||
#let (a: a, a) = (a: 1, b: 2)
|
#let (a: a, a) = (a: 1, b: 2)
|
||||||
|
|
||||||
// Error: 13-20 expected identifier, found function call
|
// Error: 13-20 expected pattern, found function call
|
||||||
#let (a, b: b.at(0)) = (a: 1, b: 2)
|
#let (a, b: b.at(0)) = (a: 1, b: 2)
|
||||||
|
|
||||||
// Error: 7-14 expected identifier or destructuring sink, found function call
|
// Error: 7-14 expected pattern, found function call
|
||||||
#let (a.at(0),) = (1,)
|
#let (a.at(0),) = (1,)
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -148,7 +148,7 @@ Three
|
|||||||
#let (a, b, c) = (1, 2)
|
#let (a, b, c) = (1, 2)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 6-20 not enough elements to destructure
|
// Error: 7-10 not enough elements to destructure
|
||||||
#let (..a, b, c, d) = (1, 2)
|
#let (..a, b, c, d) = (1, 2)
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -193,6 +193,24 @@ Three
|
|||||||
#let (a, ..) = (a: 1, b: 2)
|
#let (a, ..) = (a: 1, b: 2)
|
||||||
#test(a, 1)
|
#test(a, 1)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Ref: false
|
||||||
|
// Nested destructuring.
|
||||||
|
#let ((a, b), (key: c)) = ((1, 2), (key: 3))
|
||||||
|
#test((a, b, c), (1, 2, 3))
|
||||||
|
|
||||||
|
---
|
||||||
|
// Keyed destructuring is not currently supported.
|
||||||
|
// Error: 7-18 expected pattern, found string
|
||||||
|
#let ("spacy key": val) = ("spacy key": 123)
|
||||||
|
#val
|
||||||
|
|
||||||
|
---
|
||||||
|
// Keyed destructuring is not currently supported.
|
||||||
|
#let x = "spacy key"
|
||||||
|
// Error: 7-10 expected identifier, found group
|
||||||
|
#let ((x): v) = ("spacy key": 123)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Trailing placeholders.
|
// Trailing placeholders.
|
||||||
// Error: 10-11 not enough elements to destructure
|
// Error: 10-11 not enough elements to destructure
|
||||||
@ -200,8 +218,8 @@ Three
|
|||||||
#test(a, 1)
|
#test(a, 1)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 10-13 expected identifier, found string
|
// Error: 10-13 expected pattern, found string
|
||||||
// Error: 18-19 expected identifier, found integer
|
// Error: 18-19 expected pattern, found integer
|
||||||
#let (a: "a", b: 2) = (a: 1, b: 2)
|
#let (a: "a", b: 2) = (a: 1, b: 2)
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -213,18 +231,17 @@ Three
|
|||||||
#let (a, b: b) = (a: 1)
|
#let (a, b: b) = (a: 1)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7-11 cannot destructure named elements from an array
|
// Error: 7-11 cannot destructure named pattern from an array
|
||||||
#let (a: a, b) = (1, 2, 3)
|
#let (a: a, b) = (1, 2, 3)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 5 expected identifier
|
// Error: 5 expected pattern
|
||||||
#let
|
#let
|
||||||
|
|
||||||
// Error: 6 expected identifier
|
// Error: 6 expected pattern
|
||||||
#{let}
|
#{let}
|
||||||
|
|
||||||
// Error: 5 expected identifier
|
// Error: 6-9 expected pattern, found string
|
||||||
// Error: 5 expected semicolon or line break
|
|
||||||
#let "v"
|
#let "v"
|
||||||
|
|
||||||
// Error: 7 expected semicolon or line break
|
// Error: 7 expected semicolon or line break
|
||||||
@ -233,8 +250,7 @@ Three
|
|||||||
// Error: 9 expected expression
|
// Error: 9 expected expression
|
||||||
#let v =
|
#let v =
|
||||||
|
|
||||||
// Error: 5 expected identifier
|
// Error: 6-9 expected pattern, found string
|
||||||
// Error: 5 expected semicolon or line break
|
|
||||||
#let "v" = 1
|
#let "v" = 1
|
||||||
|
|
||||||
// Terminated because expression ends.
|
// Terminated because expression ends.
|
||||||
@ -246,7 +262,7 @@ Three
|
|||||||
// Error: 11-12 unclosed delimiter
|
// Error: 11-12 unclosed delimiter
|
||||||
#let v5 = (1, 2 + ; Five
|
#let v5 = (1, 2 + ; Five
|
||||||
|
|
||||||
// Error: 9-13 expected identifier, found boolean
|
// Error: 9-13 expected pattern, found boolean
|
||||||
#let (..true) = false
|
#let (..true) = false
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -257,7 +273,7 @@ Three
|
|||||||
// Error: 2-3 unexpected underscore
|
// Error: 2-3 unexpected underscore
|
||||||
#_
|
#_
|
||||||
|
|
||||||
// Error: 8-9 unexpected underscore
|
// Error: 8-9 expected expression, found underscore
|
||||||
#lorem(_)
|
#lorem(_)
|
||||||
|
|
||||||
// Error: 3-4 expected expression, found underscore
|
// Error: 3-4 expected expression, found underscore
|
||||||
@ -275,9 +291,11 @@ Three
|
|||||||
|
|
||||||
// Error: 15 expected expression
|
// Error: 15 expected expression
|
||||||
#let func(x) =
|
#let func(x) =
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 12 expected equals sign
|
// Error: 12 expected equals sign
|
||||||
#let (func)(x)
|
#let (func)(x)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 12 expected equals sign
|
// Error: 12 expected equals sign
|
||||||
// Error: 15-15 expected semicolon or line break
|
// Error: 15-15 expected semicolon or line break
|
||||||
|
@ -273,6 +273,38 @@
|
|||||||
#test(a, ((2, 3, 4), 2))
|
#test(a, ((2, 3, 4), 2))
|
||||||
#test(b, 1)
|
#test(b, 1)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test comma placement in destructuring assignment.
|
||||||
|
#let array = (1, 2, 3)
|
||||||
|
#((key: array.at(1)) = (key: "hi"))
|
||||||
|
#test(array, (1, "hi", 3))
|
||||||
|
|
||||||
|
#let array = (1, 2, 3)
|
||||||
|
#((array.at(1)) = ("hi"))
|
||||||
|
#test(array, (1, "hi", 3))
|
||||||
|
|
||||||
|
#let array = (1, 2, 3)
|
||||||
|
#((array.at(1),) = ("hi",))
|
||||||
|
#test(array, (1, "hi", 3))
|
||||||
|
|
||||||
|
#let array = (1, 2, 3)
|
||||||
|
#((array.at(1)) = ("hi",))
|
||||||
|
#test(array, (1, ("hi",), 3))
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test nested destructuring assignment.
|
||||||
|
#let a
|
||||||
|
#let b
|
||||||
|
#let c
|
||||||
|
#(((a, b), (key: c)) = ((1, 2), (key: 3)))
|
||||||
|
#test((a, b, c), (1, 2, 3))
|
||||||
|
|
||||||
|
---
|
||||||
|
#let array = (1, 2, 3)
|
||||||
|
// Error: 3-17 cannot destructure string
|
||||||
|
#((array.at(1),) = ("hi"))
|
||||||
|
#test(array, (1, ("hi",), 3))
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 3-6 cannot mutate a constant: box
|
// Error: 3-6 cannot mutate a constant: box
|
||||||
#(box = 1)
|
#(box = 1)
|
||||||
|
@ -61,11 +61,11 @@
|
|||||||
#test(f(1, 2, 3), 3)
|
#test(f(1, 2, 3), 3)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 13-19 cannot spread string
|
// Error: 11-19 cannot spread string
|
||||||
#calc.min(.."nope")
|
#calc.min(.."nope")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 10-14 expected identifier, found boolean
|
// Error: 10-14 expected pattern, found boolean
|
||||||
#let f(..true) = none
|
#let f(..true) = none
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -90,11 +90,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 11-17 cannot spread dictionary into array
|
// Error: 9-17 cannot spread dictionary into array
|
||||||
#(1, 2, ..(a: 1))
|
#(1, 2, ..(a: 1))
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 5-11 cannot spread array into dictionary
|
// Error: 3-11 cannot spread array into dictionary
|
||||||
#(..(1, 2), a: 1)
|
#(..(1, 2), a: 1)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -86,7 +86,7 @@
|
|||||||
#eval("RR_1^NN", mode: "math", scope: (RR: math.NN, NN: math.RR))
|
#eval("RR_1^NN", mode: "math", scope: (RR: math.NN, NN: math.RR))
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 7-12 expected identifier
|
// Error: 7-12 expected pattern
|
||||||
#eval("let")
|
#eval("let")
|
||||||
|
|
||||||
---
|
---
|
||||||
|
Loading…
x
Reference in New Issue
Block a user