From c94a18833f23d2b57de1b87971458fd54b56d088 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 3 Mar 2021 17:53:40 +0100 Subject: =?UTF-8?q?Closures=20and=20function=20definitions=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports: - Closure syntax: `(x, y) => z` - Shorthand for a single argument: `x => y` - Function syntax: `let f(x) = y` - Capturing of variables from the environment - Error messages for too few / many passed arguments Does not support: - Named arguments - Variadic arguments with `..` --- src/syntax/expr.rs | 20 +++++++++++++++++--- src/syntax/visit.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 10 deletions(-) (limited to 'src/syntax') diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 638d9dd3..d76ada69 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -25,8 +25,10 @@ pub enum Expr { Unary(ExprUnary), /// A binary operation: `a + b`. Binary(ExprBinary), - /// An invocation of a function: `foo(...)`. + /// An invocation of a function: `f(x, y)`. Call(ExprCall), + /// A closure expression: `(x, y) => { z }`. + Closure(ExprClosure), /// A let expression: `let x = 1`. Let(ExprLet), /// An if expression: `if x { y } else { z }`. @@ -51,6 +53,7 @@ impl Expr { Self::Unary(v) => v.span, Self::Binary(v) => v.span, Self::Call(v) => v.span, + Self::Closure(v) => v.span, Self::Let(v) => v.span, Self::If(v) => v.span, Self::While(v) => v.span, @@ -58,7 +61,7 @@ impl Expr { } } - /// Whether the expression can be shorten in markup with a hashtag. + /// Whether the expression can be shortened in markup with a hashtag. pub fn has_short_form(&self) -> bool { matches!(self, Expr::Ident(_) @@ -411,6 +414,17 @@ impl ExprArg { } } +/// A closure expression: `(x, y) => { z }`. +#[derive(Debug, Clone, PartialEq)] +pub struct ExprClosure { + /// The source code location. + pub span: Span, + /// The parameter bindings. + pub params: Rc>, + /// The body of the closure. + pub body: Rc, +} + /// A let expression: `let x = 1`. #[derive(Debug, Clone, PartialEq)] pub struct ExprLet { @@ -418,7 +432,7 @@ pub struct ExprLet { pub span: Span, /// The binding to assign to. pub binding: Ident, - /// The expression the pattern is initialized with. + /// The expression the binding is initialized with. pub init: Option>, } diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs index 1bf260c7..15613233 100644 --- a/src/syntax/visit.rs +++ b/src/syntax/visit.rs @@ -3,27 +3,42 @@ use super::*; macro_rules! visit { - ($(fn $name:ident($v:ident, $node:ident: &$ty:ty) $body:block)*) => { + ($(fn $name:ident($v:ident $(, $node:ident: &$ty:ty)?) $body:block)*) => { /// Traverses the syntax tree. pub trait Visit<'ast> { - $(fn $name(&mut self, $node: &'ast $ty) { - $name(self, $node); + $(fn $name(&mut self $(, $node: &'ast $ty)?) { + $name(self, $($node)?); })* + + /// Visit a definition of a binding. + /// + /// Bindings are, for example, left-hand side of let expressions, + /// and key/value patterns in for loops. + fn visit_binding(&mut self, _: &'ast Ident) {} + + /// Visit the entry into a scope. + fn visit_enter(&mut self) {} + + /// Visit the exit from a scope. + fn visit_exit(&mut self) {} } $(visit! { - @concat!("Walk a node of type [`", stringify!($ty), "`]."), - pub fn $name<'ast, V>($v: &mut V, $node: &'ast $ty) + @$(concat!("Walk a node of type [`", stringify!($ty), "`]."), )? + pub fn $name<'ast, V>( + #[allow(unused)] $v: &mut V + $(, #[allow(unused)] $node: &'ast $ty)? + ) where V: Visit<'ast> + ?Sized $body })* }; + (@$doc:expr, $($tts:tt)*) => { #[doc = $doc] $($tts)* - } - + }; } visit! { @@ -59,6 +74,7 @@ visit! { Expr::Unary(e) => v.visit_unary(e), Expr::Binary(e) => v.visit_binary(e), Expr::Call(e) => v.visit_call(e), + Expr::Closure(e) => v.visit_closure(e), Expr::Let(e) => v.visit_let(e), Expr::If(e) => v.visit_if(e), Expr::While(e) => v.visit_while(e), @@ -79,7 +95,9 @@ visit! { } fn visit_template(v, node: &ExprTemplate) { + v.visit_enter(); v.visit_tree(&node.tree); + v.visit_exit(); } fn visit_group(v, node: &ExprGroup) { @@ -87,9 +105,15 @@ visit! { } fn visit_block(v, node: &ExprBlock) { + if node.scoping { + v.visit_enter(); + } for expr in &node.exprs { v.visit_expr(&expr); } + if node.scoping { + v.visit_exit(); + } } fn visit_binary(v, node: &ExprBinary) { @@ -106,6 +130,13 @@ visit! { v.visit_args(&node.args); } + fn visit_closure(v, node: &ExprClosure) { + for param in node.params.iter() { + v.visit_binding(param); + } + v.visit_expr(&node.body); + } + fn visit_args(v, node: &ExprArgs) { for arg in &node.items { v.visit_arg(arg); @@ -120,6 +151,7 @@ visit! { } fn visit_let(v, node: &ExprLet) { + v.visit_binding(&node.binding); if let Some(init) = &node.init { v.visit_expr(&init); } @@ -139,6 +171,13 @@ visit! { } fn visit_for(v, node: &ExprFor) { + match &node.pattern { + ForPattern::Value(value) => v.visit_binding(value), + ForPattern::KeyValue(key, value) => { + v.visit_binding(key); + v.visit_binding(value); + } + } v.visit_expr(&node.iter); v.visit_expr(&node.body); } -- cgit v1.2.3