From 1a5489c61db82af64104e01e359217154efd0d24 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 14 Dec 2020 19:16:45 +0100 Subject: [PATCH] feat: Allow macros to refer to symbols in scope at the expansion site Fixes #895 --- README.md | 10 +++++----- src/import.rs | 5 +++-- src/lift_io.rs | 3 ++- vm/src/macros.rs | 23 +++++++++++++++-------- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 16e636a106..678624616b 100644 --- a/README.md +++ b/README.md @@ -60,17 +60,17 @@ factorial 10 // * Only the following operators/functions are allowed: multiplication, division, addition, subtraction // * Division should use floating point or rational arithmetic, etc, to preserve remainders. // * Brackets are allowed, if using an infix expression evaluator. -// * Forming multiple digit numbers from the supplied digits is disallowed. (So an answer of 12+12 - when given 1, 2, 2, and 1 - is wrong). -// * The order of the digits, when given, does not have to be preserved. +// * Forming multiple digit numbers from the supplied digits is disallowed. (So an answer of 12+12 when given 1, 2, 2, and 1 is wrong). +// * The order of the digits when given does not have to be preserved. // // // ## Notes // // The type of expression evaluator used is not mandated. An RPN evaluator is equally acceptable for example. -// The task is not for the program to generate the expression or test whether an expression is even possible. +// The task is not for the program to generate the expression, or test whether an expression is even possible. -// The `import!` macro is used to load and refer to other modules. +// The `import!` macro are used to load and refer to other modules. // It gets replaced by the value returned by evaluating that module (cached of course, so that // multiple `import!`s to the same module only evaluates the module once) let io @ { ? } = import! std.io @@ -83,7 +83,7 @@ let list @ { List, ? } = import! std.list let random = import! std.random let string = import! std.string -// Since imports in gluon return regular values we can load specific parts of a module using pattern matches. +// Since imports in gluon returns regular values we can load specific parts of a module using pattern matches. let char @ { ? } = import! std.char let { (<>) } = import! std.semigroup diff --git a/src/import.rs b/src/import.rs index f2cfa94f17..c0e249a57e 100644 --- a/src/import.rs +++ b/src/import.rs @@ -25,7 +25,7 @@ use crate::base::{ ast::{self, expr_to_path, Expr, Literal, SpannedExpr}, filename_to_module, pos, source::FileId, - symbol::Symbol, + symbol::{Symbol, Symbols}, types::ArcType, }; @@ -477,9 +477,10 @@ where } } - fn expand<'r, 'a: 'r, 'b: 'r, 'ast: 'r>( + fn expand<'r, 'a: 'r, 'b: 'r, 'c: 'r, 'ast: 'r>( &self, macros: &'b mut MacroExpander<'a>, + _symbols: &'c mut Symbols, _arena: &'b mut ast::OwnedArena<'ast, Symbol>, args: &'b mut [SpannedExpr<'ast, Symbol>], ) -> MacroFuture<'r, 'ast> { diff --git a/src/lift_io.rs b/src/lift_io.rs index 5ebc799cd7..8bd6cc937f 100644 --- a/src/lift_io.rs +++ b/src/lift_io.rs @@ -22,9 +22,10 @@ use { pub(crate) struct LiftIo; impl Macro for LiftIo { - fn expand<'r, 'a: 'r, 'b: 'r, 'ast: 'r>( + fn expand<'r, 'a: 'r, 'b: 'r, 'c: 'r, 'ast: 'r>( &self, env: &'b mut MacroExpander<'a>, + _symbols: &'c mut Symbols, arena: &'b mut ast::OwnedArena<'ast, Symbol>, args: &'b mut [SpannedExpr<'ast, Symbol>], ) -> MacroFuture<'r, 'ast> { diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 24f02bdf32..25d3a16f0c 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -245,9 +245,13 @@ pub trait Macro: Trace + DowncastArc + Send + Sync { None } - fn expand<'r, 'a: 'r, 'b: 'r, 'ast: 'r>( + /// Creating a symbol in `symbols` will put it in the same scope as the code surrounding the + /// expansion. If you want to create a unique symbol then call `Symbol::from` or create a new + /// `Symbols` table + fn expand<'r, 'a: 'r, 'b: 'r, 'c: 'r, 'ast: 'r>( &self, env: &'b mut MacroExpander<'a>, + symbols: &'c mut Symbols, arena: &'b mut ast::OwnedArena<'ast, Symbol>, args: &'b mut [SpannedExpr<'ast, Symbol>], ) -> MacroFuture<'r, 'ast>; @@ -283,13 +287,14 @@ where (**self).get_capability_impl(thread, arc_self, id) } - fn expand<'r, 'a: 'r, 'b: 'r, 'ast: 'r>( + fn expand<'r, 'a: 'r, 'b: 'r, 'c: 'r, 'ast: 'r>( &self, env: &'b mut MacroExpander<'a>, + symbols: &'c mut Symbols, arena: &'b mut ast::OwnedArena<'ast, Symbol>, args: &'b mut [SpannedExpr<'ast, Symbol>], ) -> MacroFuture<'r, 'ast> { - (**self).expand(env, arena, args) + (**self).expand(env, symbols, arena, args) } } @@ -307,13 +312,14 @@ where (**self).get_capability_impl(thread, arc_self, id) } - fn expand<'r, 'a: 'r, 'b: 'r, 'ast: 'r>( + fn expand<'r, 'a: 'r, 'b: 'r, 'c: 'r, 'ast: 'r>( &self, env: &'b mut MacroExpander<'a>, + symbols: &'c mut Symbols, arena: &'b mut ast::OwnedArena<'ast, Symbol>, args: &'b mut [SpannedExpr<'ast, Symbol>], ) -> MacroFuture<'r, 'ast> { - (**self).expand(env, arena, args) + (**self).expand(env, symbols, arena, args) } } @@ -457,19 +463,20 @@ impl<'a> MacroExpander<'a> { exprs: Vec::new(), }; visitor.visit_expr(expr); - let MacroVisitor { exprs, .. } = visitor; - self.expand(arena, exprs).await + let MacroVisitor { exprs, symbols, .. } = visitor; + self.expand(symbols, arena, exprs).await } async fn expand<'ast>( &mut self, + symbols: &mut Symbols, arena: &mut ast::OwnedArena<'ast, Symbol>, mut exprs: Vec<(&'_ mut SpannedExpr<'ast, Symbol>, Arc)>, ) { let mut futures = Vec::with_capacity(exprs.len()); for (expr, mac) in exprs.drain(..) { let result = match &mut expr.value { - Expr::App { args, .. } => mac.expand(self, arena, args).await, + Expr::App { args, .. } => mac.expand(self, symbols, arena, args).await, _ => unreachable!("{:?}", expr), }; match result {