(.module: [library [lux "*" [abstract ["." monad {"+" [do]}]] [data ["." text ("#\." monoid)] ["." name ("#\." codec)] [collection ["." list ("#\." monoid monad)]]] [macro ["." code]] [math [number ["." nat] ["." int]]]]] ["." // "_" ["#" meta ["." location]]]) (def: .public (single_expansion syntax) (-> Code (Meta (List Code))) (case syntax [_ (#.Form (#.Item [[_ (#.Identifier 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 [[_ (#.Identifier 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 [[_ (#.Identifier 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& (code.identifier 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 (: (List (List Code)) targs+))))))) [_ (#.Tuple members)] (do //.monad [members' (monad.each //.monad full_expansion members)] (in (list (code.tuple (list\conjoint members'))))) [_ (#.Record members)] (|> members (monad.each //.monad (function (_ [left right]) (do //.monad [left (full_expansion left) right (full_expansion right)] (case [left right] [(#.Item left #.End) (#.Item right #.End)] (in [left right]) _ (//.failure "Record members must expand into singletons."))))) (\ //.monad each (|>> code.record list))) _ (\ //.monad in (list syntax)))) (def: .public (identifier prefix) (-> Text (Meta Code)) (do //.monad [id //.seed] (in (|> id (\ nat.decimal encoded) ($_ text\composite "__gensym__" prefix) [""] code.identifier)))) (def: (local_identifier ast) (-> Code (Meta Text)) (case ast [_ (#.Identifier [_ name])] (\ //.monad in name) _ (//.failure (text\composite "Code is not a local identifier: " (code.format ast))))) (def: .public wrong_syntax_error (-> Name Text) (|>> name\encoded (text.prefix (text\composite "Wrong syntax for " text.\'')) (text.suffix (text\composite text.\'' ".")))) (macro: .public (with_identifiers tokens) (case tokens (^ (list [_ (#.Tuple identifiers)] body)) (do {! //.monad} [identifier_names (monad.each ! ..local_identifier identifiers) .let [identifier_defs (list\conjoint (list\each (: (-> Text (List Code)) (function (_ name) (list (code.identifier ["" name]) (` (..identifier (~ (code.text name))))))) identifier_names))]] (in (list (` ((~! do) (~! //.monad) [(~+ identifier_defs)] (~ body)))))) _ (//.failure (..wrong_syntax_error (name_of ..with_identifiers))))) (def: .public (one_expansion token) (-> Code (Meta Code)) (do //.monad [token+ (..expansion token)] (case token+ (^ (list token')) (in token') _ (//.failure "Macro expanded to more than 1 element.")))) (template [ ] [(macro: .public ( tokens) (let [[module _] (name_of .._) [_ short] (name_of ) macro_name [module short]] (case (: (Maybe [Bit Code]) (case tokens (^ (list [_ (#.Text "omit")] token)) (#.Some [#1 token]) (^ (list token)) (#.Some [#0 token]) _ #.None)) (#.Some [omit? token]) (do //.monad [location //.location output ( token) .let [_ ("lux io log" ($_ text\composite (name\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] )