summaryrefslogtreecommitdiff
path: root/src/syntax
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-03-03 17:53:40 +0100
committerLaurenz <laurmaedje@gmail.com>2021-03-03 17:53:40 +0100
commitc94a18833f23d2b57de1b87971458fd54b56d088 (patch)
tree9e1ed55cfca15aef6d39ced50a3a5b14d2800aae /src/syntax
parent4d90a066f197264341eff6bf67e8c06cae434eb4 (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.rs20
-rw-r--r--src/syntax/visit.rs53
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);
}