Add embed-repl macro. v1 PROPOSED

Phil Hagelberg: 1
 Add embed-repl macro.

 3 files changed, 56 insertions(+), 2 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~technomancy/fennel/patches/44820/mbox | git am -3
Learn more about email & git

[PATCH] Add embed-repl macro. Export this patch

This is primarily meant for debugging; it allows you to use a repl to
see what's going on in the middle of a function and experiment with
whatever values are in scope interactively.

See the description in the documentation for details.
 reference.md          | 25 +++++++++++++++++++++++++
 src/fennel/macros.fnl | 19 ++++++++++++++++++-
 test/macro.fnl        | 14 +++++++++++++-
 3 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/reference.md b/reference.md
index 61faef8..0a5985a 100644
--- a/reference.md
+++ b/reference.md
@@ -1476,6 +1476,31 @@ module paths similarly to `import-macros`.  See the [relative
require](tutorial#relative-require) section in the tutorial for more

### `embed-repl`

Sometimes it's helpful for debugging purposes to drop a repl right
into the middle of your code to see what's really going on. You can
use the `embed-repl` macro to do this:

(let [input (get-input)
      value []]
  (fn helper [x]
    (table.insert value (calculate x)))

This will drop you into a repl when you hit that point in the
code. The repl will have access to all the locals that are in
scope. (This would be `input`, `value`, and `helper` in the example
above.) It takes an optional options table which accepts all the same
values as the `fennel.repl` function in the API.

Note that this is meant for use in development and will not work with
ahead-of-time compilation unless your build also includes Fennel as a

## Macros

All forms which introduce macros do so inside the current scope. This
diff --git a/src/fennel/macros.fnl b/src/fennel/macros.fnl
index 9b645d1..a4c2e07 100644
--- a/src/fennel/macros.fnl
+++ b/src/fennel/macros.fnl
@@ -1,3 +1,5 @@
;; fennel-ls: macro-file

;; These macros are awkward because their definition cannot rely on the any
;; built-in macros, only special forms. (no when, no icollect, etc)

@@ -393,6 +395,20 @@ Example:
            (tset scope.macros import-key (. macros* macro-name))))))

(fn embed-repl* [?opts]
  "Embed a repl with access to locals at the point of the call.
Takes an optional table of arguments which will be passed to fennel.repl."
  (let [locals []]
    (fn add-locals [{: symmeta : parent}]
      (each [name (pairs symmeta)]
        (tset locals name (sym name)))
      (if parent (add-locals parent)))
    (add-locals (get-scope))
    `(let [opts# (or ,?opts {})
           fennel# (require (or opts#.module-name :fennel))]
       (set opts#.env (collect [k# v# (pairs _G) &into ,locals] k# v#))
       (fennel#.repl opts#))))

{:-> ->*
 :->> ->>*
 :-?> -?>*
@@ -413,4 +429,5 @@ Example:
 :pick-values pick-values*
 :macro macro*
 :macrodebug macrodebug*
 :import-macros import-macros*}
 :import-macros import-macros*
 :embed-repl embed-repl*}
diff --git a/test/macro.fnl b/test/macro.fnl
index af44ab5..6991d9b 100644
--- a/test/macro.fnl
+++ b/test/macro.fnl
@@ -4,7 +4,7 @@
(macro == [form expected ?msg ?opts]
  `(let [(ok# val#) (pcall fennel.eval ,(view form) ,?opts)]
     (t.is ok# val#)
     (t.= val# ,expected ,?msg)))
     (t.= ,expected val# ,?msg)))

(fn test-arrows []
  (== (-> (+ 85 21) (+ 1) (- 99)) 8)
@@ -732,6 +732,17 @@
          (splice {:greetings "comrade"}))
      {:hello "world" :greetings "comrade"}))

(fn test-embed-repl []
  (== (let [inputs ["hello\n" "[:literal]\n" "(inc 991)\n" ",exit\n"]
            outputs []
            opts {:readChunk #(table.remove inputs 1)
                  :onValues #(table.insert outputs (. $ 1))}
            hello :world]
        (fn inc [x] (+ x 1))
        (embed-repl opts)
      ["\"world\"" "[\"literal\"]" "992"]))

{: test-arrows
 : test-doto
 : test-?.
@@ -748,6 +759,7 @@
 : test-case
 : test-lua-module
 : test-disabled-sandbox-searcher
 : test-embed-repl
 : test-expand
 : test-match-try
 : test-case-try