diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-03-03 17:53:40 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-03-03 17:53:40 +0100 |
| commit | c94a18833f23d2b57de1b87971458fd54b56d088 (patch) | |
| tree | 9e1ed55cfca15aef6d39ced50a3a5b14d2800aae /src/syntax | |
| parent | 4d90a066f197264341eff6bf67e8c06cae434eb4 (diff) | |
Closures and function definitions 🚀
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 `..`
Diffstat (limited to 'src/syntax')
| -rw-r--r-- | src/syntax/expr.rs | 20 | ||||
| -rw-r--r-- | src/syntax/visit.rs | 53 |
2 files changed, 63 insertions, 10 deletions
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<Vec<Ident>>, + /// The body of the closure. + pub body: Rc<Expr>, +} + /// 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<Box<Expr>>, } 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); } |
