From 1643be20cb10baf3cabcab502f0013b7faebe322 Mon Sep 17 00:00:00 2001
From: Eduardo Julian
Date: Sun, 12 Jun 2022 20:05:43 -0400
Subject: Generalized macro-let.

---
 .gitignore                                         |  5 +++
 lux-jvm/source/program.lux                         |  4 +-
 lux-lua/source/program.lux                         | 30 +++++++--------
 lux-python/source/program.lux                      |  6 +--
 lux-ruby/source/program.lux                        | 10 ++---
 stdlib/source/library/lux/macro/local.lux          | 45 ++++++++++++++++++++--
 stdlib/source/library/lux/macro/template.lux       |  1 +
 .../source/library/lux/target/jvm/type/parser.lux  |  2 +-
 .../language/lux/phase/extension/analysis/js.lux   |  6 +--
 .../language/lux/phase/extension/analysis/lua.lux  |  6 +--
 .../lux/phase/extension/analysis/python.lux        |  6 +--
 .../language/lux/phase/extension/analysis/ruby.lux |  6 +--
 .../language/lux/phase/generation/jvm/case.lux     |  2 +-
 .../language/lux/phase/generation/jvm/function.lux |  2 +-
 .../lux/phase/generation/jvm/reference.lux         |  2 +-
 .../compiler/language/lux/phase/generation/lua.lux |  2 +-
 .../language/lux/phase/generation/lua/runtime.lux  |  2 +-
 .../language/lux/phase/generation/python.lux       |  2 +-
 .../lux/phase/generation/python/runtime.lux        |  2 +-
 stdlib/source/test/lux/macro/local.lux             | 37 ++++++++++++++++++
 20 files changed, 127 insertions(+), 51 deletions(-)

diff --git a/.gitignore b/.gitignore
index 40d60ba10..606ff0d0a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@ aedifex.jar
 /lux-jvm/source/unsafe
 /lux-jvm/source/program
 /lux-jvm/source/spec
+/lux-jvm/source/parser
 
 /lux-js/RELEASE
 /lux-js/target
@@ -32,6 +33,7 @@ aedifex.jar
 /lux-js/source/unsafe
 /lux-js/source/program
 /lux-js/source/spec
+/lux-js/source/parser
 /lux-js/node_based_compiler.js
 /lux-js/lux.js
 
@@ -41,6 +43,7 @@ aedifex.jar
 /lux-python/source/unsafe
 /lux-python/source/program
 /lux-python/source/spec
+/lux-python/source/parser
 
 /lux-lua/RELEASE
 /lux-lua/target
@@ -48,6 +51,7 @@ aedifex.jar
 /lux-lua/source/unsafe
 /lux-lua/source/program
 /lux-lua/source/spec
+/lux-lua/source/parser
 
 /lux-ruby/RELEASE
 /lux-ruby/target
@@ -55,6 +59,7 @@ aedifex.jar
 /lux-ruby/source/unsafe
 /lux-ruby/source/program
 /lux-ruby/source/spec
+/lux-ruby/source/parser
 
 /stdlib/RELEASE
 /stdlib/target
diff --git a/lux-jvm/source/program.lux b/lux-jvm/source/program.lux
index 82e5f400b..a22211aad 100644
--- a/lux-jvm/source/program.lux
+++ b/lux-jvm/source/program.lux
@@ -42,7 +42,7 @@
        [analysis
         [macro (.only Expander)]]
        [phase
-        ["[0]" extension
+        ["[0]" extension (.only)
          ["[0]" analysis
           ["[1]" jvm]]
          ["[0]" generation
@@ -50,7 +50,7 @@
          ["[0]" directive
           ["[1]" jvm]]]
         [generation
-         ["/" jvm
+         ["/" jvm (.only)
           ["[1][0]" runtime (.only Anchor Definition)]
           ["[1][0]" host]
           ["[1][0]" program]]]]]]]]]]
diff --git a/lux-lua/source/program.lux b/lux-lua/source/program.lux
index ab567ae31..b3af368bb 100644
--- a/lux-lua/source/program.lux
+++ b/lux-lua/source/program.lux
@@ -24,7 +24,7 @@
      ["[0]" list (.open: "[1]#[0]" monad)]]]
    [macro
     ["^" pattern]
-    ["[0]" template]
+    ["[0]" local]
     ["[0]" code]]
    [math
     [number (.only hex)
@@ -33,7 +33,7 @@
    ["[0]" world
     ["[0]" file]
     ["[1]/[0]" program]]
-   ["@" target
+   ["@" target (.only)
     ["_" lua]]
    [tool
     ["[0]" compiler
@@ -55,7 +55,7 @@
           ["[1]" lua]]]
         [generation
          ["[0]" reference]
-         ["[0]" lua
+         ["[0]" lua (.only)
           ["[0]" runtime]]]]]]
      [default
       ["[0]" platform (.only Platform)]]
@@ -502,19 +502,17 @@
 (with_expansions [<jvm> (these (with_expansions [$var_args (_.var "...")
                                                  $str_rel_to_abs (_.var "_utf8_str_rel_to_abs")
                                                  $decode (_.var "_utf8_decode")]
-                                 (template.let [(!int <hex>)
-                                                [(_.int (.int (hex <hex>)))]
-
-                                                (!&| <or> <and> <raw>)
-                                                [(|> <raw>
-                                                     (_.bit_and (!int <and>))
-                                                     (_.bit_or (!int <or>)))]
-                                                
-                                                (!&|< <or> <and> <shift> <raw>)
-                                                [(|> <raw>
-                                                     (_.bit_shr (_.int <shift>))
-                                                     (_.bit_and (!int <and>))
-                                                     (_.bit_or (!int <or>)))]]
+                                 (local.let [!int (template (_ <hex>)
+                                                    [(_.int (.int (hex <hex>)))])
+                                             !&| (template (_ <or> <and> <raw>)
+                                                   [(|> <raw>
+                                                        (_.bit_and (!int <and>))
+                                                        (_.bit_or (!int <or>)))])
+                                             !&|< (template (_ <or> <and> <shift> <raw>)
+                                                    [(|> <raw>
+                                                         (_.bit_shr (_.int <shift>))
+                                                         (_.bit_and (!int <and>))
+                                                         (_.bit_or (!int <or>)))])]
                                    (these (def: rembulan//char
                                             (let [$buffer (_.var "buffer")
                                                   $k (_.var "k")
diff --git a/lux-python/source/program.lux b/lux-python/source/program.lux
index 64139ca38..2368323f7 100644
--- a/lux-python/source/program.lux
+++ b/lux-python/source/program.lux
@@ -24,7 +24,7 @@
     [collection
      ["[0]" array (.only Array)]
      ["[0]" list (.open: "[1]#[0]" functor)]]]
-   ["[0]" macro
+   [macro
     ["^" pattern]
     ["[0]" template]
     ["[0]" code]]
@@ -35,7 +35,7 @@
    ["[0]" world
     ["[0]" file]
     ["[1]/[0]" program]]
-   ["@" target
+   ["@" target (.only)
     ["_" python]]
    [tool
     ["[0]" compiler
@@ -58,7 +58,7 @@
           ["[1]" python]]]
         [generation
          ["[0]" reference]
-         ["[0]" python
+         ["[0]" python (.only)
           ["[0]" runtime]]]]]]
      [default
       ["[0]" platform (.only Platform)]]
diff --git a/lux-ruby/source/program.lux b/lux-ruby/source/program.lux
index 5e06719fa..128e5d720 100644
--- a/lux-ruby/source/program.lux
+++ b/lux-ruby/source/program.lux
@@ -14,9 +14,7 @@
     ["[0]" io (.only IO io)]
     ["[0]" function]
     [concurrency
-     ["[0]" async (.only Async)]]
-    ["<>" parser
-     ["<[0]>" code]]]
+     ["[0]" async (.only Async)]]]
    [data
     ["[0]" text (.open: "[1]#[0]" hash)
      ["%" format (.only format)]
@@ -25,7 +23,7 @@
     [collection
      ["[0]" array (.only Array)]
      ["[0]" list (.open: "[1]#[0]" functor)]]]
-   ["[0]" macro
+   [macro
     ["^" pattern]
     ["[0]" template]]
    [math
@@ -36,7 +34,7 @@
    ["[0]" world
     ["[0]" file]
     ["[1]/[0]" program]]
-   ["@" target
+   ["@" target (.only)
     ["_" ruby]]
    [tool
     ["[0]" compiler
@@ -59,7 +57,7 @@
           ["[1]" ruby]]]
         [generation
          ["[0]" reference]
-         ["[0]" ruby
+         ["[0]" ruby (.only)
           ["[0]" runtime]]]]]]
      [default
       ["[0]" platform (.only Platform)]]
diff --git a/stdlib/source/library/lux/macro/local.lux b/stdlib/source/library/lux/macro/local.lux
index a921afee1..5a07b4a48 100644
--- a/stdlib/source/library/lux/macro/local.lux
+++ b/stdlib/source/library/lux/macro/local.lux
@@ -1,12 +1,14 @@
 (.using
  [library
-  [lux (.except)
+  [lux (.except with let)
    ["[0]" meta]
    [abstract
     ["[0]" monad (.only do)]]
    [control
     ["[0]" try (.only Try)]
-    ["[0]" exception (.only exception:)]]
+    ["[0]" exception (.only exception:)]
+    ["<>" parser (.only)
+     ["<[0]>" code]]]
    [data
     ["[0]" product]
     ["[0]" text]
@@ -15,6 +17,7 @@
      [dictionary
       ["[0]" plist (.only PList)]]]]]]
  ["[0]" // (.only)
+  [syntax (.only syntax)]
   ["[1][0]" code]])
 
 (exception: .public (unknown_module [module Text])
@@ -102,7 +105,41 @@
     [_ (monad.each meta.monad ..push_one macros)
      seed meta.seed
      g!pop (//.symbol "pop")
-     _ (let [g!pop (is Symbol
-                       ["" (//code.format g!pop)])]
+     _ (.let [g!pop (is Symbol
+                        ["" (//code.format g!pop)])]
          (..push_one [g!pop (..pop_all (list#each product.left macros) g!pop)]))]
     (in (` ((~ g!pop))))))
+
+(def: .public (with macros body)
+  (-> (List [Symbol Macro]) Code (Meta (List Code)))
+  (do [! meta.monad]
+    [expression? (is (Meta Bit)
+                     (function (_ lux)
+                       {try.#Success [lux (case (the .#expected lux)
+                                            {.#None}
+                                            false
+
+                                            {.#Some _}
+                                            true)]}))
+     g!pop (..push macros)]
+    (.if expression?
+      (//.with_symbols [g!body]
+        (in (list (` (.let [(~ g!body) (~ body)]
+                       (exec
+                         (~ g!pop)
+                         (~ g!body)))))))
+      (in (list body
+                g!pop)))))
+
+(def: .public let
+  (syntax (_ [locals (<code>.tuple (<>.some (<>.and <code>.local <code>.any)))
+              body <code>.any])
+    (do [! meta.monad]
+      [here_name meta.current_module_name
+       locals (monad.each ! (function (_ [name value])
+                              (|> value
+                                  (meta.eval .Macro)
+                                  (at ! each (|>> (as .Macro)
+                                                  [[here_name name]]))))
+                          locals)]
+      (..with locals body))))
diff --git a/stdlib/source/library/lux/macro/template.lux b/stdlib/source/library/lux/macro/template.lux
index 439c99a3a..2251051d5 100644
--- a/stdlib/source/library/lux/macro/template.lux
+++ b/stdlib/source/library/lux/macro/template.lux
@@ -157,6 +157,7 @@
          #parameters parameters
          #template template])))
 
+... TODO: Get rid of this (and any local definitions it depends on) once the bootstrapping compiler is gone.
 (def: .public let
   (syntax (_ [locals (<code>.tuple (<>.some ..local))
               body <code>.any])
diff --git a/stdlib/source/library/lux/target/jvm/type/parser.lux b/stdlib/source/library/lux/target/jvm/type/parser.lux
index 53bd29aa0..aa64e4334 100644
--- a/stdlib/source/library/lux/target/jvm/type/parser.lux
+++ b/stdlib/source/library/lux/target/jvm/type/parser.lux
@@ -18,7 +18,7 @@
   [category (.only Void Value Return Method Primitive Object Class Array Var Parameter Declaration)]
   ["[1][0]" signature]
   ["[1][0]" descriptor]
-  ["[0]" // (.only)
+  [//
    [encoding
     ["[1][0]" name (.only External)]]]])
 
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/js.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/js.lux
index d477f443e..8342ce912 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/js.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/js.lux
@@ -5,16 +5,16 @@
    [abstract
     ["[0]" monad (.only do)]]
    [control
-    ["<>" parser
+    ["<>" parser (.only)
      ["<[0]>" code (.only Parser)]]]
    [data
     [collection
      ["[0]" array]
      ["[0]" dictionary]
      ["[0]" list]]]
-   ["[0]" type
+   ["[0]" type (.only)
     ["[0]" check]]
-   ["@" target
+   ["@" target (.only)
     ["_" js]]]]
  [//
   ["/" lux (.only custom)]
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/lua.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/lua.lux
index 428ea1068..fa08d5be0 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/lua.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/lua.lux
@@ -5,16 +5,16 @@
    [abstract
     ["[0]" monad (.only do)]]
    [control
-    ["<>" parser
+    ["<>" parser (.only)
      ["<[0]>" code (.only Parser)]]]
    [data
     [collection
      ["[0]" array]
      ["[0]" dictionary]
      ["[0]" list]]]
-   ["[0]" type
+   ["[0]" type (.only)
     ["[0]" check]]
-   ["@" target
+   ["@" target (.only)
     ["_" lua]]]]
  [//
   ["/" lux (.only custom)]
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/python.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/python.lux
index 3defef9c9..aa2944967 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/python.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/python.lux
@@ -5,16 +5,16 @@
    [abstract
     ["[0]" monad (.only do)]]
    [control
-    ["<>" parser
+    ["<>" parser (.only)
      ["<[0]>" code (.only Parser)]]]
    [data
     [collection
      ["[0]" array]
      ["[0]" dictionary]
      ["[0]" list]]]
-   ["[0]" type
+   ["[0]" type (.only)
     ["[0]" check]]
-   ["@" target
+   ["@" target (.only)
     ["_" python]]]]
  [//
   ["/" lux (.only custom)]
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/ruby.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/ruby.lux
index 4678fe9e1..01895a3e7 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/ruby.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/extension/analysis/ruby.lux
@@ -5,16 +5,16 @@
    [abstract
     ["[0]" monad (.only do)]]
    [control
-    ["<>" parser
+    ["<>" parser (.only)
      ["<[0]>" code (.only Parser)]]]
    [data
     [collection
      ["[0]" array]
      ["[0]" dictionary]
      ["[0]" list]]]
-   ["[0]" type
+   ["[0]" type (.only)
     ["[0]" check]]
-   ["@" target
+   ["@" target (.only)
     ["_" ruby]]]]
  [//
   ["/" lux (.only custom)]
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/case.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/case.lux
index e96a88889..3ab3c67f7 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/case.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/case.lux
@@ -35,7 +35,7 @@
     [access
      ["[0]" member (.only Member)]]]
    [///
-    ["[0]" phase ("operation#[0]" monad)]
+    ["[0]" phase (.open: "operation#[0]" monad)]
     [reference
      [variable (.only Register)]]]]])
 
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/function.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/function.lux
index e9f9b5800..3de519160 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/function.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/function.lux
@@ -1,6 +1,6 @@
 (.using
  [library
-  [lux (.except Type Label)
+  [lux (.except Type Label with)
    [abstract
     ["[0]" monad (.only do)]]
    [data
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/reference.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/reference.lux
index 7f6ade8c6..21b5a57b4 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/reference.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/jvm/reference.lux
@@ -20,7 +20,7 @@
    [//
     ["[0]" generation]
     [///
-     ["[1]" phase ("operation#[0]" monad)]
+     ["[1]" phase (.open: "operation#[0]" monad)]
      [reference
       ["[0]" variable (.only Register Variable)]]
      [meta
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/lua.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/lua.lux
index e14772296..669e1667b 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/lua.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/lua.lux
@@ -20,7 +20,7 @@
   ["/[1]" //
    ["[1][0]" reference]
    ["/[1]" //
-    ["[1][0]" extension
+    ["[1][0]" extension (.only)
      [generation
       [lua
        ["[1]/[0]" common]]]]
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/lua/runtime.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/lua/runtime.lux
index 503638aeb..8898da66d 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/lua/runtime.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/lua/runtime.lux
@@ -1,6 +1,6 @@
 (.using
  [library
-  [lux (.except Label Location)
+  [lux (.except Label Location left right)
    ["[0]" meta]
    [abstract
     ["[0]" monad (.only do)]]
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/python.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/python.lux
index 054e84344..a0c15f71e 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/python.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/python.lux
@@ -20,7 +20,7 @@
   ["/[1]" //
    ["[1][0]" reference]
    ["/[1]" //
-    ["[1][0]" extension
+    ["[1][0]" extension (.only)
      [generation
       [python
        ["[1]/[0]" common]]]]
diff --git a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/python/runtime.lux b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/python/runtime.lux
index 12d1f65c7..c0101452a 100644
--- a/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/python/runtime.lux
+++ b/stdlib/source/library/lux/tool/compiler/language/lux/phase/generation/python/runtime.lux
@@ -1,6 +1,6 @@
 (.using
  [library
-  [lux (.except ++)
+  [lux (.except ++ left right)
    [abstract
     ["[0]" monad (.only do)]]
    [control
diff --git a/stdlib/source/test/lux/macro/local.lux b/stdlib/source/test/lux/macro/local.lux
index de1073f55..bb03574a1 100644
--- a/stdlib/source/test/lux/macro/local.lux
+++ b/stdlib/source/test/lux/macro/local.lux
@@ -27,6 +27,12 @@
  [\\library
   ["[0]" /]])
 
+(/.let [!pow/2 (template (_ <scalar>)
+                 [(n.* <scalar> <scalar>)])]
+  (def: pow/2
+    (-> Nat Nat)
+    (|>> !pow/2)))
+
 (def: macro_error
   (syntax (_ [macro <code>.any])
     (function (_ compiler)
@@ -90,4 +96,35 @@
                                  (<| ..macro_error
                                      (..with ["" "actual"] expected #1)
                                      (n.= expected (..actual)))))
+             (do !
+               [scalar random.nat]
+               (_.coverage [/.let]
+                 (let [can_use_with_statements!
+                       (n.= (all n.* scalar scalar)
+                            (..pow/2 scalar))]
+                   (and can_use_with_statements!
+                        (/.let [pow/3 (template (_ <scalar>)
+                                        [(all n.* <scalar> <scalar> <scalar>)])
+                                pow/9 (template (_ <scalar>)
+                                        [(pow/3 (pow/3 <scalar>))])]
+                          (let [can_use_with_expressions!
+                                (n.= (all n.* scalar scalar scalar)
+                                     (pow/3 scalar))
+
+                                can_refer!
+                                (n.= (all n.*
+                                          scalar scalar scalar
+                                          scalar scalar scalar
+                                          scalar scalar scalar)
+                                     (pow/9 scalar))
+
+                                can_shadow!
+                                (let [pow/3 (function (_ scalar)
+                                              (all n.+ scalar scalar scalar))]
+                                  (n.= (all n.+ scalar scalar scalar)
+                                       (pow/3 scalar)))]
+                            (and can_use_with_expressions!
+                                 can_refer!
+                                 can_shadow!)))
+                        ))))
              ))))
-- 
cgit v1.2.3