aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/library/lux/meta/macro.lux
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib/source/library/lux/meta/macro.lux')
-rw-r--r--stdlib/source/library/lux/meta/macro.lux213
1 files changed, 213 insertions, 0 deletions
diff --git a/stdlib/source/library/lux/meta/macro.lux b/stdlib/source/library/lux/meta/macro.lux
new file mode 100644
index 000000000..86e9fe57d
--- /dev/null
+++ b/stdlib/source/library/lux/meta/macro.lux
@@ -0,0 +1,213 @@
+(.require
+ [library
+ [lux (.except local symbol function macro)
+ [abstract
+ ["[0]" monad (.only do)]]
+ [data
+ ["[0]" text (.use "[1]#[0]" monoid)]
+ [collection
+ ["[0]" list (.use "[1]#[0]" monoid monad)]]]
+ [math
+ [number
+ ["[0]" nat]
+ ["[0]" int]]]]]
+ ["[0]" // (.only)
+ ["[0]" code]
+ ["[0]" location]
+ ["[0]" symbol (.use "[1]#[0]" codec)]])
+
+(def .public (single_expansion syntax)
+ (-> Code (Meta (List Code)))
+ (case syntax
+ [_ {.#Form {.#Item [[_ {.#Symbol name}] args]}}]
+ (do //.monad
+ [?macro (//.macro name)]
+ (case ?macro
+ {.#Some macro}
+ ((as Macro' macro) args)
+
+ {.#None}
+ (at //.monad in (list syntax))))
+
+ _
+ (at //.monad in (list syntax))))
+
+(def .public (expansion syntax)
+ (-> Code (Meta (List Code)))
+ (case syntax
+ [_ {.#Form {.#Item [[_ {.#Symbol name}] args]}}]
+ (do //.monad
+ [?macro (//.macro name)]
+ (case ?macro
+ {.#Some macro}
+ (do [! //.monad]
+ [top_level_expansion ((as Macro' macro) args)]
+ (|> top_level_expansion
+ (monad.each //.monad expansion)
+ (at ! each list#conjoint)))
+
+ {.#None}
+ (at //.monad in (list syntax))))
+
+ _
+ (at //.monad in (list syntax))))
+
+(def .public (full_expansion syntax)
+ (-> Code (Meta (List Code)))
+ (case syntax
+ [_ {.#Form {.#Item [[_ {.#Symbol name}] args]}}]
+ (do //.monad
+ [?macro (//.macro name)]
+ (case ?macro
+ {.#Some macro}
+ (do //.monad
+ [expansion ((as Macro' macro) args)
+ expansion' (monad.each //.monad full_expansion expansion)]
+ (in (list#conjoint expansion')))
+
+ {.#None}
+ (do //.monad
+ [parts' (monad.each //.monad full_expansion (list.partial (code.symbol name) args))]
+ (in (list (code.form (list#conjoint parts')))))))
+
+ [_ {.#Form {.#Item [harg targs]}}]
+ (do //.monad
+ [harg+ (full_expansion harg)
+ targs+ (monad.each //.monad full_expansion targs)]
+ (in (list (code.form (list#composite harg+ (list#conjoint (is (List (List Code)) targs+)))))))
+
+ [_ {.#Variant members}]
+ (do //.monad
+ [members' (monad.each //.monad full_expansion members)]
+ (in (list (code.variant (list#conjoint members')))))
+
+ [_ {.#Tuple members}]
+ (do //.monad
+ [members' (monad.each //.monad full_expansion members)]
+ (in (list (code.tuple (list#conjoint members')))))
+
+ _
+ (at //.monad in (list syntax))))
+
+(def .public (symbol prefix)
+ (-> Text (Meta Code))
+ (do //.monad
+ [id //.seed]
+ (in (|> id
+ (at nat.decimal encoded)
+ (all text#composite "__gensym__" prefix)
+ [""] code.symbol))))
+
+(def (local ast)
+ (-> Code (Meta Text))
+ (case ast
+ [_ {.#Symbol [_ name]}]
+ (at //.monad in name)
+
+ _
+ (//.failure (text#composite "Code is not a local symbol: " (code.format ast)))))
+
+(def .public wrong_syntax_error
+ (-> Symbol Text)
+ (|>> symbol#encoded
+ (text.prefix (text#composite "Wrong syntax for " text.\''))
+ (text.suffix (text#composite text.\'' "."))))
+
+(def .public with_symbols
+ (.macro (_ tokens)
+ (case tokens
+ (pattern (list [_ {.#Tuple symbols}] body))
+ (do [! //.monad]
+ [symbol_names (monad.each ! ..local symbols)
+ .let [symbol_defs (list#conjoint (list#each (is (-> Text (List Code))
+ (.function (_ name) (list (code.symbol ["" name]) (` (..symbol (, (code.text name)))))))
+ symbol_names))]]
+ (in (list (` ((,! do) (,! //.monad)
+ [(,* symbol_defs)]
+ (, body))))))
+
+ _
+ (//.failure (..wrong_syntax_error (.symbol ..with_symbols))))))
+
+(def .public (one_expansion token)
+ (-> Code (Meta Code))
+ (do //.monad
+ [token+ (..expansion token)]
+ (case token+
+ (pattern (list token'))
+ (in token')
+
+ _
+ (//.failure "Macro expanded to more than 1 element."))))
+
+(with_template [<macro> <func>]
+ [(def .public <macro>
+ (.macro (_ tokens)
+ (let [[module _] (.symbol .._)
+ [_ short] (.symbol <macro>)
+ macro_name [module short]]
+ (case (is (Maybe [Bit Code])
+ (case tokens
+ (pattern (list [_ {.#Text "omit"}]
+ token))
+ {.#Some [#1 token]}
+
+ (pattern (list token))
+ {.#Some [#0 token]}
+
+ _
+ {.#None}))
+ {.#Some [omit? token]}
+ (do //.monad
+ [location //.location
+ output (<func> token)
+ .let [_ ("lux io log" (all text#composite (symbol#encoded macro_name) " " (location.format location)))
+ _ (list#each (|>> code.format "lux io log")
+ output)
+ _ ("lux io log" "")]]
+ (in (if omit?
+ (list)
+ output)))
+
+ {.#None}
+ (//.failure (..wrong_syntax_error macro_name))))))]
+
+ [log_single_expansion! ..single_expansion]
+ [log_expansion! ..expansion]
+ [log_full_expansion! ..full_expansion]
+ )
+
+(def .public times
+ (.macro (_ tokens)
+ (case tokens
+ (pattern (list.partial [_ {.#Nat times}] terms))
+ (loop (again [times times
+ before terms])
+ (case times
+ 0
+ (at //.monad in before)
+
+ _
+ (do [! //.monad]
+ [after (|> before
+ (monad.each ! ..single_expansion)
+ (at ! each list#conjoint))]
+ (again (-- times) after))))
+
+ _
+ (//.failure (..wrong_syntax_error (.symbol ..times))))))
+
+(def .public final
+ (.macro (_ it)
+ (let [! //.monad]
+ (|> it
+ (monad.each ! ..expansion)
+ (at ! each list#conjoint)))))
+
+(def .public function
+ (-> Macro Macro')
+ (|>> (as Macro')))
+
+(def .public macro
+ (-> Macro' Macro)
+ (|>> (as Macro)))