(.using [library [lux (.except local symbol) [abstract ["[0]" monad (.only do)]] [data ["[0]" text ("[1]#[0]" monoid)] [collection ["[0]" list ("[1]#[0]" monoid monad)]]] [math [number ["[0]" nat] ["[0]" int]]]]] [/ ["[0]" code] ["[0]" // "_" ["[1]" meta ["[0]" location] ["[0]" symbol ("[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} (# //.monad in (list syntax)))) _ (# //.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) (# ! each list#conjoint))) {.#None} (# //.monad in (list syntax)))) _ (# //.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 (partial_list (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'))))) _ (# //.monad in (list syntax)))) (def: .public (symbol prefix) (-> Text (Meta Code)) (do //.monad [id //.seed] (in (|> id (# nat.decimal encoded) (all text#composite "__gensym__" prefix) [""] code.symbol)))) (def: (local ast) (-> Code (Meta Text)) (case ast [_ {.#Symbol [_ name]}] (# //.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.\'' ".")))) (macro: .public (with_symbols 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.")))) (template [ ] [(macro: .public ( tokens) (let [[module _] (.symbol .._) [_ short] (.symbol ) 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 ( 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] ) (macro: .public (times tokens) (case tokens (pattern (partial_list [_ {.#Nat times}] terms)) (loop (again [times times before terms]) (case times 0 (# //.monad in before) _ (do [! //.monad] [after (|> before (monad.each ! ..single_expansion) (# ! each list#conjoint))] (again (-- times) after)))) _ (//.failure (..wrong_syntax_error (.symbol ..times))))) (macro: .public (final it) (let [! //.monad] (# ! each list#conjoint (monad.each ! ..expansion it))))