diff options
Diffstat (limited to 'stdlib/source/lux/macro')
-rw-r--r-- | stdlib/source/lux/macro/code.lux | 143 | ||||
-rw-r--r-- | stdlib/source/lux/macro/poly.lux | 448 | ||||
-rw-r--r-- | stdlib/source/lux/macro/poly/eq.lux | 147 | ||||
-rw-r--r-- | stdlib/source/lux/macro/poly/functor.lux | 95 | ||||
-rw-r--r-- | stdlib/source/lux/macro/poly/json.lux | 312 | ||||
-rw-r--r-- | stdlib/source/lux/macro/syntax.lux | 297 | ||||
-rw-r--r-- | stdlib/source/lux/macro/syntax/common.lux | 27 | ||||
-rw-r--r-- | stdlib/source/lux/macro/syntax/common/reader.lux | 150 | ||||
-rw-r--r-- | stdlib/source/lux/macro/syntax/common/writer.lux | 24 |
9 files changed, 1643 insertions, 0 deletions
diff --git a/stdlib/source/lux/macro/code.lux b/stdlib/source/lux/macro/code.lux new file mode 100644 index 000000000..d41dbe240 --- /dev/null +++ b/stdlib/source/lux/macro/code.lux @@ -0,0 +1,143 @@ +(;module: + lux + (lux (control [eq #+ Eq]) + (data bool + number + [text #+ Eq<Text> "Text/" Monoid<Text>] + ident + (coll [list #* "" Functor<List> Fold<List>]) + ))) + +## [Types] +## (type: (Code' w) +## (#;Bool Bool) +## (#;Nat Nat) +## (#;Int Int) +## (#;Frac Frac) +## (#;Text Text) +## (#;Symbol Text Text) +## (#;Tag Text Text) +## (#;Form (List (w (Code' w)))) +## (#;Tuple (List (w (Code' w)))) +## (#;Record (List [(w (Code' w)) (w (Code' w))]))) + +## (type: Code +## (Ann Cursor (Code' (Ann Cursor)))) + +## [Utils] +(def: _cursor Cursor ["" +0 +0]) + +## [Functions] +(do-template [<name> <type> <tag>] + [(def: #export (<name> x) + (-> <type> Code) + [_cursor (<tag> x)])] + + [bool Bool #;Bool] + [nat Nat #;Nat] + [int Int #;Int] + [deg Deg #;Deg] + [frac Frac #;Frac] + [text Text #;Text] + [symbol Ident #;Symbol] + [tag Ident #;Tag] + [form (List Code) #;Form] + [tuple (List Code) #;Tuple] + [record (List [Code Code]) #;Record] + ) + +(do-template [<name> <tag> <doc>] + [(def: #export (<name> name) + {#;doc <doc>} + (-> Text Code) + [_cursor (<tag> ["" name])])] + + [local-symbol #;Symbol "Produces a local symbol (a symbol with no module prefix)."] + [local-tag #;Tag "Produces a local tag (a tag with no module prefix)."]) + +## [Structures] +(struct: #export _ (Eq Code) + (def: (= x y) + (case [x y] + (^template [<tag> <eq>] + [[_ (<tag> x')] [_ (<tag> y')]] + (:: <eq> = x' y')) + ([#;Bool Eq<Bool>] + [#;Nat Eq<Nat>] + [#;Int Eq<Int>] + [#;Deg Eq<Deg>] + [#;Frac Eq<Frac>] + [#;Text Eq<Text>] + [#;Symbol Eq<Ident>] + [#;Tag Eq<Ident>]) + + (^template [<tag>] + [[_ (<tag> xs')] [_ (<tag> ys')]] + (and (:: Eq<Nat> = (size xs') (size ys')) + (fold (function [[x' y'] old] + (and old (= x' y'))) + true + (zip2 xs' ys')))) + ([#;Form] + [#;Tuple]) + + [[_ (#;Record xs')] [_ (#;Record ys')]] + (and (:: Eq<Nat> = (size xs') (size ys')) + (fold (function [[[xl' xr'] [yl' yr']] old] + (and old (= xl' yl') (= xr' yr'))) + true + (zip2 xs' ys'))) + + _ + false))) + +## [Values] +(def: #export (to-text ast) + (-> Code Text) + (case ast + (^template [<tag> <struct>] + [_ (<tag> value)] + (:: <struct> encode value)) + ([#;Bool Codec<Text,Bool>] + [#;Nat Codec<Text,Nat>] + [#;Int Codec<Text,Int>] + [#;Deg Codec<Text,Deg>] + [#;Frac Codec<Text,Frac>] + [#;Symbol Codec<Text,Ident>]) + + [_ (#;Text value)] + (text;encode value) + + [_ (#;Tag ident)] + (Text/compose "#" (:: Codec<Text,Ident> encode ident)) + + (^template [<tag> <open> <close>] + [_ (<tag> members)] + ($_ Text/compose <open> (|> members (map to-text) (interpose " ") (text;join-with "")) <close>)) + ([#;Form "(" ")"] + [#;Tuple "[" "]"]) + + [_ (#;Record pairs)] + ($_ Text/compose "{" (|> pairs (map (function [[left right]] ($_ Text/compose (to-text left) " " (to-text right)))) (interpose " ") (text;join-with "")) "}") + )) + +(def: #export (replace original substitute ast) + {#;doc "Replaces all code that looks like the 'original' with the 'substitute'."} + (-> Code Code Code Code) + (if (:: Eq<Code> = original ast) + substitute + (case ast + (^template [<tag>] + [cursor (<tag> parts)] + [cursor (<tag> (map (replace original substitute) parts))]) + ([#;Form] + [#;Tuple]) + + [cursor (#;Record parts)] + [cursor (#;Record (map (function [[left right]] + [(replace original substitute left) + (replace original substitute right)]) + parts))] + + _ + ast))) diff --git a/stdlib/source/lux/macro/poly.lux b/stdlib/source/lux/macro/poly.lux new file mode 100644 index 000000000..7ed7fb2ee --- /dev/null +++ b/stdlib/source/lux/macro/poly.lux @@ -0,0 +1,448 @@ +(;module: + [lux #- function] + (lux (control [monad #+ do Monad] + [eq] + ["p" parser]) + [function] + (data [text "text/" Monoid<Text>] + (coll [list "list/" Fold<List> Monad<List> Monoid<List>] + [dict #+ Dict]) + [number "nat/" Codec<Text,Nat>] + [product] + [bool] + [maybe] + [ident "ident/" Eq<Ident> Codec<Text,Ident>] + ["e" error]) + [macro #+ with-gensyms] + (macro [code] + ["s" syntax #+ syntax: Syntax] + (syntax ["cs" common] + (common ["csr" reader] + ["csw" writer]))) + (lang [type] + (type [check])) + )) + +(type: #export Env (Dict Nat [Type Code])) + +(type: #export (Poly a) + (p;Parser [Env (List Type)] a)) + +(def: #export fresh Env (dict;new number;Hash<Nat>)) + +(def: (run' env types poly) + (All [a] (-> Env (List Type) (Poly a) (e;Error a))) + (case (p;run [env types] poly) + (#e;Error error) + (#e;Error error) + + (#e;Success [[env' remaining] output]) + (case remaining + #;Nil + (#e;Success output) + + _ + (#e;Error (|> remaining + (list/map type;to-text) + (text;join-with ", ") + (text/compose "Unconsumed types: ")))))) + +(def: #export (run type poly) + (All [a] (-> Type (Poly a) (e;Error a))) + (run' fresh (list type) poly)) + +(def: #export env + (Poly Env) + (;function [[env inputs]] + (#e;Success [[env inputs] env]))) + +(def: (with-env temp poly) + (All [a] (-> Env (Poly a) (Poly a))) + (;function [[env inputs]] + (case (p;run [temp inputs] poly) + (#e;Error error) + (#e;Error error) + + (#e;Success [[_ remaining] output]) + (#e;Success [[env remaining] output])))) + +(def: #export peek + (Poly Type) + (;function [[env inputs]] + (case inputs + #;Nil + (#e;Error "Empty stream of types.") + + (#;Cons headT tail) + (#e;Success [[env inputs] headT])))) + +(def: #export any + (Poly Type) + (;function [[env inputs]] + (case inputs + #;Nil + (#e;Error "Empty stream of types.") + + (#;Cons headT tail) + (#e;Success [[env tail] headT])))) + +(def: #export (local types poly) + (All [a] (-> (List Type) (Poly a) (Poly a))) + (;function [[env pass-through]] + (case (run' env types poly) + (#e;Error error) + (#e;Error error) + + (#e;Success output) + (#e;Success [[env pass-through] output])))) + +(def: (label idx) + (-> Nat Code) + (code;local-symbol (text/compose "label\u0000" (nat/encode idx)))) + +(def: #export (with-extension type poly) + (All [a] (-> Type (Poly a) (Poly [Code a]))) + (;function [[env inputs]] + (let [current-id (dict;size env) + g!var (label current-id)] + (case (p;run [(dict;put current-id [type g!var] env) + inputs] + poly) + (#e;Error error) + (#e;Error error) + + (#e;Success [[_ inputs'] output]) + (#e;Success [[env inputs'] [g!var output]]))))) + +(do-template [<combinator> <name> <type>] + [(def: #export <combinator> + (Poly Unit) + (do p;Monad<Parser> + [headT any] + (case (type;un-name headT) + <type> + (wrap []) + + _ + (p;fail ($_ text/compose "Not " <name> " type: " (type;to-text headT))))))] + + [void "Void" #;Void] + [unit "Unit" #;Unit] + [bool "Bool" (#;Primitive "#Bool" #;Nil)] + [nat "Nat" (#;Primitive "#Nat" #;Nil)] + [int "Int" (#;Primitive "#Int" #;Nil)] + [deg "Deg" (#;Primitive "#Deg" #;Nil)] + [frac "Frac" (#;Primitive "#Frac" #;Nil)] + [text "Text" (#;Primitive "#Text" #;Nil)] + ) + +(def: #export basic + (Poly Type) + (do p;Monad<Parser> + [headT any] + (case (run headT ($_ p;either + void + unit + bool + nat + int + deg + frac + text)) + (#e;Error error) + (p;fail error) + + (#e;Success _) + (wrap headT)))) + +(do-template [<name> <flattener> <tag>] + [(def: #export (<name> poly) + (All [a] (-> (Poly a) (Poly a))) + (do p;Monad<Parser> + [headT any] + (let [members (<flattener> (type;un-name headT))] + (if (n.> +1 (list;size members)) + (local members poly) + (p;fail ($_ text/compose "Not a " (ident/encode (ident-for <tag>)) " type: " (type;to-text headT)))))))] + + [variant type;flatten-variant #;Sum] + [tuple type;flatten-tuple #;Product] + ) + +(def: polymorphic' + (Poly [Nat Type]) + (do p;Monad<Parser> + [headT any + #let [[num-arg bodyT] (type;flatten-univ-q (type;un-name headT))]] + (if (n.= +0 num-arg) + (p;fail ($_ text/compose "Non-polymorphic type: " (type;to-text headT))) + (wrap [num-arg bodyT])))) + +(def: #export (polymorphic poly) + (All [a] (-> (Poly a) (Poly [Code (List Code) a]))) + (do p;Monad<Parser> + [headT any + funcI (:: @ map dict;size ;;env) + [num-args non-poly] (local (list headT) polymorphic') + env ;;env + #let [funcL (label funcI) + [all-varsL env'] (loop [current-arg +0 + env' env + all-varsL (: (List Code) (list))] + (if (n.< num-args current-arg) + (if (n.= +0 current-arg) + (let [varL (label (n.inc funcI))] + (recur (n.inc current-arg) + (|> env' + (dict;put funcI [headT funcL]) + (dict;put (n.inc funcI) [(#;Bound (n.inc funcI)) varL])) + (#;Cons varL all-varsL))) + (let [partialI (|> current-arg (n.* +2) (n.+ funcI)) + partial-varI (n.inc partialI) + partial-varL (label partial-varI) + partialC (` ((~ funcL) (~@ (|> (list;n.range +0 (n.dec num-args)) + (list/map (|>. (n.* +2) n.inc (n.+ funcI) label)) + list;reverse))))] + (recur (n.inc current-arg) + (|> env' + (dict;put partialI [;Void partialC]) + (dict;put partial-varI [(#;Bound partial-varI) partial-varL])) + (#;Cons partial-varL all-varsL)))) + [all-varsL env']))]] + (|> (do @ + [output poly] + (wrap [funcL all-varsL output])) + (local (list non-poly)) + (with-env env')))) + +(def: #export (function in-poly out-poly) + (All [i o] (-> (Poly i) (Poly o) (Poly [i o]))) + (do p;Monad<Parser> + [headT any + #let [[inputsT outputT] (type;flatten-function (type;un-name headT))]] + (if (n.> +0 (list;size inputsT)) + (p;seq (local inputsT in-poly) + (local (list outputT) out-poly)) + (p;fail ($_ text/compose "Non-function type: " (type;to-text headT)))))) + +(def: #export (apply poly) + (All [a] (-> (Poly a) (Poly a))) + (do p;Monad<Parser> + [headT any + #let [[funcT paramsT] (type;flatten-application (type;un-name headT))]] + (if (n.= +0 (list;size paramsT)) + (p;fail ($_ text/compose "Non-application type: " (type;to-text headT))) + (local (#;Cons funcT paramsT) poly)))) + +(def: #export (this expected) + (-> Type (Poly Unit)) + (do p;Monad<Parser> + [actual any] + (if (check;checks? expected actual) + (wrap []) + (p;fail ($_ text/compose + "Types do not match." "\n" + "Expected: " (type;to-text expected) "\n" + " Actual: " (type;to-text actual)))))) + +(def: (adjusted-idx env idx) + (-> Env Nat Nat) + (let [env-level (n./ +2 (dict;size env)) + bound-level (n./ +2 idx) + bound-idx (n.% +2 idx)] + (|> env-level n.dec (n.- bound-level) (n.* +2) (n.+ bound-idx)))) + +(def: #export bound + (Poly Code) + (do p;Monad<Parser> + [env ;;env + headT any] + (case headT + (#;Bound idx) + (case (dict;get (adjusted-idx env idx) env) + (#;Some [poly-type poly-ast]) + (wrap poly-ast) + + #;None + (p;fail ($_ text/compose "Unknown bound type: " (type;to-text headT)))) + + _ + (p;fail ($_ text/compose "Not a bound type: " (type;to-text headT)))))) + +(def: #export (var id) + (-> Nat (Poly Unit)) + (do p;Monad<Parser> + [env ;;env + headT any] + (case headT + (#;Bound idx) + (if (n.= id (adjusted-idx env idx)) + (wrap []) + (p;fail ($_ text/compose "Wrong bound type.\n" + "Expected: " (nat/encode id) "\n" + " Actual: " (nat/encode idx)))) + + _ + (p;fail ($_ text/compose "Not a bound type: " (type;to-text headT)))))) + +(def: #export (recursive poly) + (All [a] (-> (Poly a) (Poly [Code a]))) + (do p;Monad<Parser> + [headT any] + (case (type;un-name headT) + (#;Apply #;Void (#;UnivQ _ headT')) + (do @ + [[recT _ output] (|> poly + (with-extension #;Void) + (with-extension headT) + (local (list headT')))] + (wrap [recT output])) + + _ + (p;fail ($_ text/compose "Not a recursive type: " (type;to-text headT)))))) + +(def: #export recursive-self + (Poly Code) + (do p;Monad<Parser> + [env ;;env + headT any] + (case (type;un-name headT) + (^multi (#;Apply #;Void (#;Bound funcT-idx)) + (n.= +0 (adjusted-idx env funcT-idx)) + [(dict;get +0 env) (#;Some [self-type self-call])]) + (wrap self-call) + + _ + (p;fail ($_ text/compose "Not a recursive type: " (type;to-text headT)))))) + +(def: #export recursive-call + (Poly Code) + (do p;Monad<Parser> + [env ;;env + [funcT argsT] (apply (p;seq any (p;many any))) + _ (local (list funcT) (var +0)) + allC (let [allT (list& funcT argsT)] + (|> allT + (monad;map @ (function;const bound)) + (local allT)))] + (wrap (` ((~@ allC)))))) + +(def: #export log + (All [a] (Poly a)) + (do p;Monad<Parser> + [current any + #let [_ (log! ($_ text/compose + "{" (ident/encode (ident-for ;;log)) "} " + (type;to-text current)))]] + (p;fail "LOGGING"))) + +## [Syntax] +(syntax: #export (poly: [export csr;export] + [name s;local-symbol] + body) + (with-gensyms [g!type g!output] + (let [g!name (code;symbol ["" name])] + (wrap (;list (` (syntax: (~@ (csw;export export)) ((~ g!name) [(~ g!type) s;symbol]) + (do macro;Monad<Meta> + [(~ g!type) (macro;find-type-def (~ g!type))] + (case (|> (~ body) + (;function [(~ g!name)]) + p;rec + (do p;Monad<Parser> []) + (;;run (~ g!type)) + (: (;Either ;Text ;Code))) + (#;Left (~ g!output)) + (macro;fail (~ g!output)) + + (#;Right (~ g!output)) + ((~' wrap) (;list (~ g!output)))))))))))) + +(def: (common-poly-name? poly-func) + (-> Text Bool) + (text;contains? "?" poly-func)) + +(def: (derivation-name poly args) + (-> Text (List Text) (Maybe Text)) + (if (common-poly-name? poly) + (#;Some (list/fold (text;replace-once "?") poly args)) + #;None)) + +(syntax: #export (derived: [export csr;export] + [?name (p;maybe s;local-symbol)] + [[poly-func poly-args] (s;form (p;seq s;symbol (p;many s;symbol)))] + [?custom-impl (p;maybe s;any)]) + (do @ + [poly-args (monad;map @ macro;normalize poly-args) + name (case ?name + (#;Some name) + (wrap name) + + (^multi #;None + [(derivation-name (product;right poly-func) (list/map product;right poly-args)) + (#;Some derived-name)]) + (wrap derived-name) + + _ + (p;fail "derived: was given no explicit name, and cannot generate one from given information.")) + #let [impl (case ?custom-impl + (#;Some custom-impl) + custom-impl + + #;None + (` ((~ (code;symbol poly-func)) (~@ (list/map code;symbol poly-args)))))]] + (wrap (;list (` (def: (~@ (csw;export export)) + (~ (code;symbol ["" name])) + {#;struct? true} + (~ impl))))))) + +## [Derivers] +(def: #export (to-ast env type) + (-> Env Type Code) + (case type + (#;Primitive name params) + (` (#;Primitive (~ (code;text name)) + (list (~@ (list/map (to-ast env) params))))) + + (^template [<tag>] + <tag> + (` <tag>)) + ([#;Void] [#;Unit]) + + (^template [<tag>] + (<tag> idx) + (` (<tag> (~ (code;nat idx))))) + ([#;Var] [#;Ex]) + + (#;Bound idx) + (let [idx (adjusted-idx env idx)] + (if (n.= +0 idx) + (|> (dict;get idx env) maybe;assume product;left (to-ast env)) + (` (;$ (~ (code;nat (n.dec idx))))))) + + (#;Apply #;Void (#;Bound idx)) + (let [idx (adjusted-idx env idx)] + (if (n.= +0 idx) + (|> (dict;get idx env) maybe;assume product;left (to-ast env)) + (undefined))) + + (^template [<tag>] + (<tag> left right) + (` (<tag> (~ (to-ast env left)) + (~ (to-ast env right))))) + ([#;Function] [#;Apply]) + + (^template [<tag> <macro> <flattener>] + (<tag> left right) + (` (<macro> (~@ (list/map (to-ast env) (<flattener> type)))))) + ([#;Sum | type;flatten-variant] + [#;Product & type;flatten-tuple]) + + (#;Named name sub-type) + (code;symbol name) + + (^template [<tag>] + (<tag> scope body) + (` (<tag> (list (~@ (list/map (to-ast env) scope))) + (~ (to-ast env body))))) + ([#;UnivQ] [#;ExQ]) + )) diff --git a/stdlib/source/lux/macro/poly/eq.lux b/stdlib/source/lux/macro/poly/eq.lux new file mode 100644 index 000000000..099febb24 --- /dev/null +++ b/stdlib/source/lux/macro/poly/eq.lux @@ -0,0 +1,147 @@ +(;module: + lux + (lux (control [monad #+ do Monad] + [eq] + ["p" parser]) + (data [text "text/" Monoid<Text>] + text/format + (coll [list "list/" Monad<List>] + [sequence] + [array] + [queue] + [set] + [dict #+ Dict] + (tree [rose])) + [number "nat/" Codec<Text,Nat>] + [product] + [bool] + [maybe]) + (time ["du" duration] + ["da" date] + ["i" instant]) + [macro] + (macro [code] + [syntax #+ syntax: Syntax] + (syntax [common]) + [poly #+ poly:]) + (type [unit]) + (lang [type]) + )) + +## [Derivers] +(poly: #export Eq<?> + (`` (do @ + [#let [g!_ (code;local-symbol "\u0000_")] + *env* poly;env + inputT poly;peek + #let [@Eq (: (-> Type Code) + (function [type] + (` (eq;Eq (~ (poly;to-ast *env* type))))))]] + ($_ p;either + ## Basic types + (~~ (do-template [<matcher> <eq>] + [(do @ + [_ <matcher>] + (wrap (` (: (~ (@Eq inputT)) + <eq>))))] + + [poly;unit (function [(~ g!_) (~ g!_)] true)] + [poly;bool bool;Eq<Bool>] + [poly;nat number;Eq<Nat>] + [poly;int number;Eq<Int>] + [poly;deg number;Eq<Deg>] + [poly;frac number;Eq<Frac>] + [poly;text text;Eq<Text>])) + ## Composite types + (~~ (do-template [<name> <eq>] + [(do @ + [[_ argC] (poly;apply (p;seq (poly;this <name>) + Eq<?>))] + (wrap (` (: (~ (@Eq inputT)) + (<eq> (~ argC))))))] + + [;Maybe maybe;Eq<Maybe>] + [;List list;Eq<List>] + [sequence;Sequence sequence;Eq<Sequence>] + [;Array array;Eq<Array>] + [queue;Queue queue;Eq<Queue>] + [set;Set set;Eq<Set>] + [rose;Tree rose;Eq<Tree>] + )) + (do @ + [[_ _ valC] (poly;apply ($_ p;seq + (poly;this dict;Dict) + poly;any + Eq<?>))] + (wrap (` (: (~ (@Eq inputT)) + (dict;Eq<Dict> (~ valC)))))) + ## Models + (~~ (do-template [<type> <eq>] + [(do @ + [_ (poly;this <type>)] + (wrap (` (: (~ (@Eq inputT)) + <eq>))))] + + [du;Duration du;Eq<Duration>] + [i;Instant i;Eq<Instant>] + [da;Date da;Eq<Date>] + [da;Day da;Eq<Day>] + [da;Month da;Eq<Month>])) + (do @ + [_ (poly;apply (p;seq (poly;this unit;Qty) + poly;any))] + (wrap (` (: (~ (@Eq inputT)) + unit;Eq<Qty>)))) + ## Variants + (do @ + [members (poly;variant (p;many Eq<?>)) + #let [g!left (code;local-symbol "\u0000left") + g!right (code;local-symbol "\u0000right")]] + (wrap (` (: (~ (@Eq inputT)) + (function [(~ g!left) (~ g!right)] + (case [(~ g!left) (~ g!right)] + (~@ (list/join (list/map (function [[tag g!eq]] + (list (` [((~ (code;nat tag)) (~ g!left)) + ((~ (code;nat tag)) (~ g!right))]) + (` ((~ g!eq) (~ g!left) (~ g!right))))) + (list;enumerate members)))) + (~ g!_) + false)))))) + ## Tuples + (do @ + [g!eqs (poly;tuple (p;many Eq<?>)) + #let [indices (|> (list;size g!eqs) n.dec (list;n.range +0)) + g!lefts (list/map (|>. nat/encode (text/compose "left") code;local-symbol) indices) + g!rights (list/map (|>. nat/encode (text/compose "right") code;local-symbol) indices)]] + (wrap (` (: (~ (@Eq inputT)) + (function [[(~@ g!lefts)] [(~@ g!rights)]] + (and (~@ (|> (list;zip3 g!eqs g!lefts g!rights) + (list/map (function [[g!eq g!left g!right]] + (` ((~ g!eq) (~ g!left) (~ g!right))))))))))))) + ## Type recursion + (do @ + [[g!self bodyC] (poly;recursive Eq<?>)] + (wrap (` (: (~ (@Eq inputT)) + (eq;rec (;function [(~ g!self)] + (~ bodyC))))))) + poly;recursive-self + ## Type applications + (do @ + [[funcC argsC] (poly;apply (p;seq Eq<?> (p;many Eq<?>)))] + (wrap (` ((~ funcC) (~@ argsC))))) + ## Bound type-vars + poly;bound + ## Polymorphism + (do @ + [[funcC varsC bodyC] (poly;polymorphic Eq<?>)] + (wrap (` (: (All [(~@ varsC)] + (-> (~@ (list/map (|>. (~) eq;Eq (`)) varsC)) + (eq;Eq ((~ (poly;to-ast *env* inputT)) (~@ varsC))))) + (function (~ funcC) [(~@ varsC)] + (~ bodyC)))))) + poly;recursive-call + ## If all else fails... + (|> poly;any + (:: @ map (|>. %type (format "Cannot create Eq for: ") p;fail)) + (:: @ join)) + )))) diff --git a/stdlib/source/lux/macro/poly/functor.lux b/stdlib/source/lux/macro/poly/functor.lux new file mode 100644 index 000000000..ba847d35b --- /dev/null +++ b/stdlib/source/lux/macro/poly/functor.lux @@ -0,0 +1,95 @@ +(;module: + lux + (lux (control [monad #+ do Monad] + [functor] + ["p" parser]) + (data [text] + text/format + (coll [list "L/" Monad<List> Monoid<List>]) + [product]) + [macro] + (macro [code] + [syntax #+ syntax: Syntax] + (syntax [common]) + [poly #+ poly:]) + (lang [type]) + )) + +(poly: #export Functor<?> + (do @ + [#let [type-funcC (code;local-symbol "\u0000type-funcC") + funcC (code;local-symbol "\u0000funcC") + inputC (code;local-symbol "\u0000inputC")] + *env* poly;env + inputT poly;peek + [polyC varsC non-functorT] (poly;local (list inputT) + (poly;polymorphic poly;any)) + #let [num-vars (list;size varsC)] + #let [@Functor (: (-> Type Code) + (function [unwrappedT] + (if (n.= +1 num-vars) + (` (functor;Functor (~ (poly;to-ast *env* unwrappedT)))) + (let [paramsC (|> num-vars n.dec list;indices (L/map (|>. %n code;local-symbol)))] + (` (All [(~@ paramsC)] + (functor;Functor ((~ (poly;to-ast *env* unwrappedT)) (~@ paramsC))))))))) + Arg<?> (: (-> Code (poly;Poly Code)) + (function Arg<?> [valueC] + ($_ p;either + ## Type-var + (do p;Monad<Parser> + [#let [varI (|> num-vars (n.* +2) n.dec)] + _ (poly;var varI)] + (wrap (` ((~ funcC) (~ valueC))))) + ## Variants + (do @ + [_ (wrap []) + membersC (poly;variant (p;many (Arg<?> valueC)))] + (wrap (` (case (~ valueC) + (~@ (L/join (L/map (function [[tag memberC]] + (list (` ((~ (code;nat tag)) (~ valueC))) + (` ((~ (code;nat tag)) (~ memberC))))) + (list;enumerate membersC)))))))) + ## Tuples + (do p;Monad<Parser> + [pairsCC (: (poly;Poly (List [Code Code])) + (poly;tuple (loop [idx +0 + pairsCC (: (List [Code Code]) + (list))] + (p;either (let [slotC (|> idx %n (format "\u0000slot") code;local-symbol)] + (do @ + [_ (wrap []) + memberC (Arg<?> slotC)] + (recur (n.inc idx) + (L/compose pairsCC (list [slotC memberC]))))) + (wrap pairsCC)))))] + (wrap (` (case (~ valueC) + [(~@ (L/map product;left pairsCC))] + [(~@ (L/map product;right pairsCC))])))) + ## Functions + (do @ + [_ (wrap []) + #let [outL (code;local-symbol "\u0000outL")] + [inT+ outC] (poly;function (p;many poly;any) + (Arg<?> outL)) + #let [inC+ (|> (list;size inT+) n.dec + (list;n.range +0) + (L/map (|>. %n (format "\u0000inC") code;local-symbol)))]] + (wrap (` (function [(~@ inC+)] + (let [(~ outL) ((~ valueC) (~@ inC+))] + (~ outC)))))) + ## Recursion + (do p;Monad<Parser> + [_ poly;recursive-call] + (wrap (` ((~' map) (~ funcC) (~ valueC))))) + ## Bound type-variables + (do p;Monad<Parser> + [_ poly;any] + (wrap valueC)) + )))] + [_ _ outputC] (: (poly;Poly [Code (List Code) Code]) + (p;either (poly;polymorphic + (Arg<?> inputC)) + (p;fail (format "Cannot create Functor for: " (%type inputT)))))] + (wrap (` (: (~ (@Functor inputT)) + (struct (def: ((~' map) (~ funcC) (~ inputC)) + (~ outputC)))))))) diff --git a/stdlib/source/lux/macro/poly/json.lux b/stdlib/source/lux/macro/poly/json.lux new file mode 100644 index 000000000..5c3a645ee --- /dev/null +++ b/stdlib/source/lux/macro/poly/json.lux @@ -0,0 +1,312 @@ +(;module: {#;doc "Codecs for values in the JSON format."} + lux + (lux (control [monad #+ do Monad] + [eq #+ Eq] + codec + ["p" parser "p/" Monad<Parser>]) + (data [bool] + [bit] + [text "text/" Eq<Text> Monoid<Text>] + (text ["l" lexer]) + [number "frac/" Codec<Text,Frac> "nat/" Codec<Text,Nat>] + maybe + ["e" error] + [sum] + [product] + (coll [list "list/" Fold<List> Monad<List>] + [sequence #+ Sequence sequence "sequence/" Monad<Sequence>] + ["d" dict]) + (format [".." json #+ JSON])) + (time ["i" instant] + ["du" duration] + ["da" date]) + [macro #+ with-gensyms] + (macro ["s" syntax #+ syntax:] + [code] + [poly #+ poly:]) + (type [unit]) + (lang [type]) + )) + +(def: #hidden _map_ + (All [a b] (-> (-> a b) (List a) (List b))) + list/map) + +(def: tag + (-> Nat Frac) + (|>. nat-to-int int-to-frac)) + +(def: #hidden (rec-encode non-rec) + (All [a] (-> (-> (-> a JSON) + (-> a JSON)) + (-> a JSON))) + (function [input] + (non-rec (rec-encode non-rec) input))) + +(def: low-mask Nat (|> +1 (bit;shift-left +32) n.dec)) +(def: high-mask Nat (|> low-mask (bit;shift-left +32))) + +(struct: #hidden _ (Codec JSON Nat) + (def: (encode input) + (let [high (|> input (bit;and high-mask) (bit;shift-right +32)) + low (bit;and low-mask input)] + (#..;Array (sequence (|> high nat-to-int int-to-frac #..;Number) + (|> low nat-to-int int-to-frac #..;Number))))) + (def: (decode input) + (<| (..;run input) + (do p;Monad<Parser> + [high ..;number + low ..;number]) + (wrap (n.+ (|> high frac-to-int int-to-nat (bit;shift-left +32)) + (|> low frac-to-int int-to-nat)))))) + +(struct: #hidden _ (Codec JSON Int) + (def: encode (|>. int-to-nat (:: Codec<JSON,Nat> encode))) + (def: decode + (|>. (:: Codec<JSON,Nat> decode) (:: e;Functor<Error> map nat-to-int)))) + +(def: #hidden (nullable writer) + {#;doc "Builds a JSON generator for potentially inexistent values."} + (All [a] (-> (-> a JSON) (-> (Maybe a) JSON))) + (function [elem] + (case elem + #;None #..;Null + (#;Some value) (writer value)))) + +(struct: #hidden (Codec<JSON,Qty> carrier) + (All [unit] (-> unit (Codec JSON (unit;Qty unit)))) + (def: encode + (|>. unit;out (:: Codec<JSON,Int> encode))) + (def: decode + (|>. (:: Codec<JSON,Int> decode) (:: e;Functor<Error> map (unit;in carrier))))) + +(poly: #hidden Codec<JSON,?>//encode + (with-expansions + [<basic> (do-template [<type> <matcher> <encoder>] + [(do @ + [_ <matcher>] + (wrap (` (: (~ (@JSON//encode inputT)) + <encoder>))))] + + [Unit poly;unit (function [(~ (code;symbol ["" "0"]))] #..;Null)] + [Bool poly;bool (|>. #..;Boolean)] + [Nat poly;nat (:: ;;Codec<JSON,Nat> (~' encode))] + [Int poly;int (:: ;;Codec<JSON,Int> (~' encode))] + [Frac poly;frac (|>. #..;Number)] + [Text poly;text (|>. #..;String)]) + <time> (do-template [<type> <codec>] + [(do @ + [_ (poly;this <type>)] + (wrap (` (: (~ (@JSON//encode inputT)) + (|>. (:: <codec> (~' encode)) #..;String)))))] + + [du;Duration du;Codec<Text,Duration>] + [i;Instant i;Codec<Text,Instant>] + [da;Date da;Codec<Text,Date>] + [da;Day da;Codec<Text,Day>] + [da;Month da;Codec<Text,Month>])] + (do @ + [*env* poly;env + #let [@JSON//encode (: (-> Type Code) + (function [type] + (` (-> (~ (poly;to-ast *env* type)) ..;JSON))))] + inputT poly;peek] + ($_ p;either + <basic> + <time> + (do @ + [unitT (poly;apply (p;after (poly;this unit;Qty) + poly;any))] + (wrap (` (: (~ (@JSON//encode inputT)) + (:: (Codec<JSON,Qty> (:! (~ (poly;to-ast *env* unitT)) [])) (~' encode)))))) + (do @ + [#let [g!key (code;local-symbol "\u0000key") + g!val (code;local-symbol "\u0000val")] + [_ _ .val.] (poly;apply ($_ p;seq + (poly;this d;Dict) + poly;text + Codec<JSON,?>//encode))] + (wrap (` (: (~ (@JSON//encode inputT)) + (|>. d;entries + (;;_map_ (function [[(~ g!key) (~ g!val)]] + [(~ g!key) ((~ .val.) (~ g!val))])) + (d;from-list text;Hash<Text>) + #..;Object))))) + (do @ + [[_ .sub.] (poly;apply ($_ p;seq + (poly;this ;Maybe) + Codec<JSON,?>//encode))] + (wrap (` (: (~ (@JSON//encode inputT)) + (;;nullable (~ .sub.)))))) + (do @ + [[_ .sub.] (poly;apply ($_ p;seq + (poly;this ;List) + Codec<JSON,?>//encode))] + (wrap (` (: (~ (@JSON//encode inputT)) + (|>. (;;_map_ (~ .sub.)) sequence;from-list #..;Array))))) + (do @ + [#let [g!input (code;local-symbol "\u0000input")] + members (poly;variant (p;many Codec<JSON,?>//encode))] + (wrap (` (: (~ (@JSON//encode inputT)) + (function [(~ g!input)] + (case (~ g!input) + (~@ (list/join (list/map (function [[tag g!encode]] + (list (` ((~ (code;nat tag)) (~ g!input))) + (` (..;json [(~ (code;frac (;;tag tag))) + ((~ g!encode) (~ g!input))])))) + (list;enumerate members)))))))))) + (do @ + [g!encoders (poly;tuple (p;many Codec<JSON,?>//encode)) + #let [g!members (|> (list;size g!encoders) n.dec + (list;n.range +0) + (list/map (|>. nat/encode code;local-symbol)))]] + (wrap (` (: (~ (@JSON//encode inputT)) + (function [[(~@ g!members)]] + (..;json [(~@ (list/map (function [[g!member g!encode]] + (` ((~ g!encode) (~ g!member)))) + (list;zip2 g!members g!encoders)))])))))) + ## Type recursion + (do @ + [[selfC non-recC] (poly;recursive Codec<JSON,?>//encode)] + (wrap (` (: (~ (@JSON//encode inputT)) + (;;rec-encode (;function [(~ selfC)] + (~ non-recC))))))) + poly;recursive-self + ## Type applications + (do @ + [partsC (poly;apply (p;many Codec<JSON,?>//encode))] + (wrap (` ((~@ partsC))))) + ## Polymorphism + (do @ + [[funcC varsC bodyC] (poly;polymorphic Codec<JSON,?>//encode)] + (wrap (` (: (All [(~@ varsC)] + (-> (~@ (list/map (function [varC] (` (-> (~ varC) ..;JSON))) + varsC)) + (-> ((~ (poly;to-ast *env* inputT)) (~@ varsC)) + ..;JSON))) + (function (~ funcC) [(~@ varsC)] + (~ bodyC)))))) + poly;bound + poly;recursive-call + ## If all else fails... + (p;fail (text/compose "Cannot create JSON encoder for: " (type;to-text inputT))) + )))) + +(poly: #hidden Codec<JSON,?>//decode + (with-expansions + [<basic> (do-template [<type> <matcher> <decoder>] + [(do @ + [_ <matcher>] + (wrap (` (: (~ (@JSON//decode inputT)) + <decoder>))))] + + [Unit poly;unit ..;null] + [Bool poly;bool ..;boolean] + [Nat poly;nat (p;codec ;;Codec<JSON,Nat> ..;any)] + [Int poly;int (p;codec ;;Codec<JSON,Int> ..;any)] + [Frac poly;frac ..;number] + [Text poly;text ..;string]) + <time> (do-template [<type> <codec>] + [(do @ + [_ (poly;this <type>)] + (wrap (` (: (~ (@JSON//decode inputT)) + (p;codec <codec> ..;string)))))] + + [du;Duration du;Codec<Text,Duration>] + [i;Instant i;Codec<Text,Instant>] + [da;Date da;Codec<Text,Date>] + [da;Day da;Codec<Text,Day>] + [da;Month da;Codec<Text,Month>])] + (do @ + [*env* poly;env + #let [@JSON//decode (: (-> Type Code) + (function [type] + (` (..;Reader (~ (poly;to-ast *env* type))))))] + inputT poly;peek] + ($_ p;either + <basic> + <time> + (do @ + [unitT (poly;apply (p;after (poly;this unit;Qty) + poly;any))] + (wrap (` (: (~ (@JSON//decode inputT)) + (p;codec (Codec<JSON,Qty> (:! (~ (poly;to-ast *env* unitT)) [])) ..;any))))) + (do @ + [[_ _ valC] (poly;apply ($_ p;seq + (poly;this d;Dict) + poly;text + Codec<JSON,?>//decode))] + (wrap (` (: (~ (@JSON//decode inputT)) + (..;object (~ valC)))))) + (do @ + [[_ subC] (poly;apply (p;seq (poly;this ;Maybe) + Codec<JSON,?>//decode))] + (wrap (` (: (~ (@JSON//decode inputT)) + (..;nullable (~ subC)))))) + (do @ + [[_ subC] (poly;apply (p;seq (poly;this ;List) + Codec<JSON,?>//decode))] + (wrap (` (: (~ (@JSON//decode inputT)) + (..;array (p;some (~ subC))))))) + (do @ + [members (poly;variant (p;many Codec<JSON,?>//decode))] + (wrap (` (: (~ (@JSON//decode inputT)) + ($_ p;alt + (~@ (list/map (function [[tag memberC]] + (` (|> (~ memberC) + (p;after (..;number! (~ (code;frac (;;tag tag))))) + ..;array))) + (list;enumerate members)))))))) + (do @ + [g!decoders (poly;tuple (p;many Codec<JSON,?>//decode))] + (wrap (` (: (~ (@JSON//decode inputT)) + (..;array ($_ p;seq (~@ g!decoders))))))) + ## Type recursion + (do @ + [[selfC bodyC] (poly;recursive Codec<JSON,?>//decode)] + (wrap (` (: (~ (@JSON//decode inputT)) + (p;rec (;function [(~ selfC)] + (~ bodyC))))))) + poly;recursive-self + ## Type applications + (do @ + [[funcC argsC] (poly;apply (p;seq Codec<JSON,?>//decode (p;many Codec<JSON,?>//decode)))] + (wrap (` ((~ funcC) (~@ argsC))))) + ## Polymorphism + (do @ + [[funcC varsC bodyC] (poly;polymorphic Codec<JSON,?>//decode)] + (wrap (` (: (All [(~@ varsC)] + (-> (~@ (list/map (|>. (~) ..;Reader (`)) varsC)) + (..;Reader ((~ (poly;to-ast *env* inputT)) (~@ varsC))))) + (function (~ funcC) [(~@ varsC)] + (~ bodyC)))))) + poly;bound + poly;recursive-call + ## If all else fails... + (p;fail (text/compose "Cannot create JSON decoder for: " (type;to-text inputT))) + )))) + +(syntax: #export (Codec<JSON,?> inputT) + {#;doc (doc "A macro for automatically producing JSON codecs." + (type: Variant + (#Case0 Bool) + (#Case1 Text) + (#Case2 Frac)) + + (type: Record + {#unit Unit + #bool Bool + #frac Frac + #text Text + #maybe (Maybe Frac) + #list (List Frac) + #variant Variant + #tuple [Bool Frac Text] + #dict (Dict Text Frac)}) + + (derived: (Codec<JSON,?> Record)))} + (with-gensyms [g!inputs] + (wrap (list (` (: (Codec ..;JSON (~ inputT)) + (struct (def: (~' encode) (Codec<JSON,?>//encode (~ inputT))) + (def: ((~' decode) (~ g!inputs)) (..;run (~ g!inputs) (Codec<JSON,?>//decode (~ inputT)))) + ))))))) diff --git a/stdlib/source/lux/macro/syntax.lux b/stdlib/source/lux/macro/syntax.lux new file mode 100644 index 000000000..917b7e094 --- /dev/null +++ b/stdlib/source/lux/macro/syntax.lux @@ -0,0 +1,297 @@ +(;module: + lux + (lux [macro #+ with-gensyms] + (control [monad #+ do Monad] + [eq #+ Eq] + ["p" parser]) + (data [bool] + [number] + [text "text/" Monoid<Text>] + [ident] + (coll [list "list/" Functor<List>]) + [product] + [maybe] + ["E" error])) + (.. [code "code/" Eq<Code>])) + +## [Utils] +(def: (join-pairs pairs) + (All [a] (-> (List [a a]) (List a))) + (case pairs + #;Nil #;Nil + (#;Cons [[x y] pairs']) (list& x y (join-pairs pairs')))) + +## [Types] +(type: #export Syntax + {#;doc "A Lux syntax parser."} + (p;Parser (List Code))) + +## [Utils] +(def: (remaining-inputs asts) + (-> (List Code) Text) + ($_ text/compose "\nRemaining input: " + (|> asts (list/map code;to-text) (list;interpose " ") (text;join-with "")))) + +## [Syntaxs] +(def: #export any + {#;doc "Just returns the next input without applying any logic."} + (Syntax Code) + (function [tokens] + (case tokens + #;Nil (#E;Error "There are no tokens to parse!") + (#;Cons [t tokens']) (#E;Success [tokens' t])))) + +(do-template [<get-name> <type> <tag> <eq> <desc>] + [(def: #export <get-name> + {#;doc (code;text ($_ text/compose "Parses the next " <desc> " input Code."))} + (Syntax <type>) + (function [tokens] + (case tokens + (#;Cons [[_ (<tag> x)] tokens']) + (#E;Success [tokens' x]) + + _ + (#E;Error ($_ text/compose "Cannot parse " <desc> (remaining-inputs tokens))))))] + + [ bool Bool #;Bool bool;Eq<Bool> "bool"] + [ nat Nat #;Nat number;Eq<Nat> "nat"] + [ int Int #;Int number;Eq<Int> "int"] + [ deg Deg #;Deg number;Eq<Deg> "deg"] + [ frac Frac #;Frac number;Eq<Frac> "frac"] + [ text Text #;Text text;Eq<Text> "text"] + [symbol Ident #;Symbol ident;Eq<Ident> "symbol"] + [ tag Ident #;Tag ident;Eq<Ident> "tag"] + ) + +(def: #export (this? ast) + {#;doc "Asks if the given Code is the next input."} + (-> Code (Syntax Bool)) + (function [tokens] + (case tokens + (#;Cons [token tokens']) + (let [is-it? (code/= ast token) + remaining (if is-it? + tokens' + tokens)] + (#E;Success [remaining is-it?])) + + _ + (#E;Success [tokens false])))) + +(def: #export (this ast) + {#;doc "Ensures the given Code is the next input."} + (-> Code (Syntax Unit)) + (function [tokens] + (case tokens + (#;Cons [token tokens']) + (if (code/= ast token) + (#E;Success [tokens' []]) + (#E;Error ($_ text/compose "Expected a " (code;to-text ast) " but instead got " (code;to-text token) + (remaining-inputs tokens)))) + + _ + (#E;Error "There are no tokens to parse!")))) + +(do-template [<name> <comp> <error>] + [(def: #export <name> + (Syntax Int) + (do p;Monad<Parser> + [n int + _ (p;assert <error> (<comp> 0 n))] + (wrap n)))] + + [pos-int i.> "Expected a positive integer: N > 0"] + [neg-int i.< "Expected a negative integer: N < 0"] + ) + +(do-template [<name> <tag> <desc>] + [(def: #export <name> + {#;doc (code;text ($_ text/compose "Parse a local " <desc> " (a " <desc> " that has no module prefix)."))} + (Syntax Text) + (function [tokens] + (case tokens + (#;Cons [[_ (<tag> ["" x])] tokens']) + (#E;Success [tokens' x]) + + _ + (#E;Error ($_ text/compose "Cannot parse local " <desc> (remaining-inputs tokens))))))] + + [local-symbol #;Symbol "symbol"] + [ local-tag #;Tag "tag"] + ) + +(do-template [<name> <tag> <desc>] + [(def: #export (<name> p) + {#;doc (code;text ($_ text/compose "Parse inside the contents of a " <desc> " as if they were the input Codes."))} + (All [a] + (-> (Syntax a) (Syntax a))) + (function [tokens] + (case tokens + (#;Cons [[_ (<tag> members)] tokens']) + (case (p members) + (#E;Success [#;Nil x]) (#E;Success [tokens' x]) + _ (#E;Error ($_ text/compose "Syntax was expected to fully consume " <desc> (remaining-inputs tokens)))) + + _ + (#E;Error ($_ text/compose "Cannot parse " <desc> (remaining-inputs tokens))))))] + + [ form #;Form "form"] + [tuple #;Tuple "tuple"] + ) + +(def: #export (record p) + {#;doc (code;text ($_ text/compose "Parse inside the contents of a record as if they were the input Codes."))} + (All [a] + (-> (Syntax a) (Syntax a))) + (function [tokens] + (case tokens + (#;Cons [[_ (#;Record pairs)] tokens']) + (case (p (join-pairs pairs)) + (#E;Success [#;Nil x]) (#E;Success [tokens' x]) + _ (#E;Error ($_ text/compose "Syntax was expected to fully consume record" (remaining-inputs tokens)))) + + _ + (#E;Error ($_ text/compose "Cannot parse record" (remaining-inputs tokens)))))) + +(def: #export end! + {#;doc "Ensures there are no more inputs."} + (Syntax Unit) + (function [tokens] + (case tokens + #;Nil (#E;Success [tokens []]) + _ (#E;Error ($_ text/compose "Expected list of tokens to be empty!" (remaining-inputs tokens)))))) + +(def: #export end? + {#;doc "Checks whether there are no more inputs."} + (Syntax Bool) + (function [tokens] + (case tokens + #;Nil (#E;Success [tokens true]) + _ (#E;Success [tokens false])))) + +(def: #export (on compiler action) + {#;doc "Run a Lux operation as if it was a Syntax parser."} + (All [a] (-> Compiler (Meta a) (Syntax a))) + (function [input] + (case (macro;run compiler action) + (#E;Error error) + (#E;Error error) + + (#E;Success value) + (#E;Success [input value]) + ))) + +(def: #export (run inputs syntax) + (All [a] (-> (List Code) (Syntax a) (E;Error a))) + (case (syntax inputs) + (#E;Error error) + (#E;Error error) + + (#E;Success [unconsumed value]) + (case unconsumed + #;Nil + (#E;Success value) + + _ + (#E;Error (text/compose "Unconsumed inputs: " + (|> (list/map code;to-text unconsumed) + (text;join-with ", "))))))) + +(def: #export (local inputs syntax) + {#;doc "Run a syntax parser with the given list of inputs, instead of the real ones."} + (All [a] (-> (List Code) (Syntax a) (Syntax a))) + (function [real] + (do E;Monad<Error> + [value (run inputs syntax)] + (wrap [real value])))) + +## [Syntax] +(def: #hidden text.join-with text;join-with) + +(def: #hidden _run_ p;run) +(def: #hidden _Monad<Parser>_ p;Monad<Parser>) + +(macro: #export (syntax: tokens) + {#;doc (doc "A more advanced way to define macros than macro:." + "The inputs to the macro can be parsed in complex ways through the use of syntax parsers." + "The macro body is also (implicitly) run in the Monad<Meta>, to save some typing." + "Also, the compiler state can be accessed through the *compiler* binding." + (syntax: #export (object [#let [imports (class-imports *compiler*)]] + [#let [class-vars (list)]] + [super (opt (super-class-decl^ imports class-vars))] + [interfaces (tuple (some (super-class-decl^ imports class-vars)))] + [constructor-args (constructor-args^ imports class-vars)] + [methods (some (overriden-method-def^ imports))]) + (let [def-code ($_ text/compose "anon-class:" + (spaced (list (super-class-decl$ (maybe;default object-super-class super)) + (with-brackets (spaced (list/map super-class-decl$ interfaces))) + (with-brackets (spaced (list/map constructor-arg$ constructor-args))) + (with-brackets (spaced (list/map (method-def$ id) methods))))))] + (wrap (list (` ((~ (code;text def-code)))))))))} + (let [[exported? tokens] (case tokens + (^ (list& [_ (#;Tag ["" "hidden"])] tokens')) + [(#;Some #;Left) tokens'] + + (^ (list& [_ (#;Tag ["" "export"])] tokens')) + [(#;Some #;Right) tokens'] + + _ + [#;None tokens]) + ?parts (: (Maybe [Text (List Code) Code Code]) + (case tokens + (^ (list [_ (#;Form (list& [_ (#;Symbol ["" name])] args))] + body)) + (#;Some name args (` {}) body) + + (^ (list [_ (#;Form (list& [_ (#;Symbol ["" name])] args))] + meta-data + body)) + (#;Some name args meta-data body) + + _ + #;None))] + (case ?parts + (#;Some [name args meta body]) + (with-gensyms [g!tokens g!body g!msg] + (do macro;Monad<Meta> + [vars+parsers (monad;map @ + (: (-> Code (Meta [Code Code])) + (function [arg] + (case arg + (^ [_ (#;Tuple (list var parser))]) + (wrap [var parser]) + + [_ (#;Symbol var-name)] + (wrap [(code;symbol var-name) (` any)]) + + _ + (macro;fail "Syntax pattern expects tuples or symbols.")))) + args) + #let [g!state (code;symbol ["" "*compiler*"]) + error-msg (code;text (text/compose "Wrong syntax for " name)) + export-ast (: (List Code) (case exported? + (#;Some #E;Error) + (list (' #hidden)) + + (#;Some #E;Success) + (list (' #export)) + + _ + (list)))]] + (wrap (list (` (macro: (~@ export-ast) ((~ (code;symbol ["" name])) (~ g!tokens) (~ g!state)) + (~ meta) + ("lux case" (;;run (~ g!tokens) + (: (Syntax (Meta (List Code))) + (do ;;_Monad<Parser>_ + [(~@ (join-pairs vars+parsers))] + ((~' wrap) (do macro;Monad<Meta> + [] + (~ body)))))) + {(#E;Success (~ g!body)) + ((~ g!body) (~ g!state)) + + (#E;Error (~ g!msg)) + (#E;Error (text.join-with ": " (list (~ error-msg) (~ g!msg))))}))))))) + + _ + (macro;fail "Wrong syntax for syntax:")))) diff --git a/stdlib/source/lux/macro/syntax/common.lux b/stdlib/source/lux/macro/syntax/common.lux new file mode 100644 index 000000000..72e52a4ab --- /dev/null +++ b/stdlib/source/lux/macro/syntax/common.lux @@ -0,0 +1,27 @@ +(;module: {#;doc "Commons syntax readers and writers. + + The goal is to be able to reuse common syntax in macro definitions across libraries."} + lux) + +(type: #export Export + #Exported + #Hidden) + +(type: #export Declaration + {#declaration-name Text + #declaration-args (List Text)}) + +(type: #export Annotations + (List [Ident Code])) + +(def: #export empty-annotations + Annotations + (list)) + +(type: #export Definition + {#definition-name Text + #definition-type (Maybe Code) + #definition-value Code + #definition-anns Annotations + #definition-args (List Text) + }) diff --git a/stdlib/source/lux/macro/syntax/common/reader.lux b/stdlib/source/lux/macro/syntax/common/reader.lux new file mode 100644 index 000000000..9ab6d6381 --- /dev/null +++ b/stdlib/source/lux/macro/syntax/common/reader.lux @@ -0,0 +1,150 @@ +(;module: {#;doc "Commons syntax readers."} + lux + (lux (control monad + ["p" parser]) + (data (coll [list]) + [ident "ident/" Eq<Ident>] + [product] + [maybe]) + [macro] + (macro ["s" syntax #+ syntax: Syntax])) + [.. #*]) + +## Exports +(def: #export export + {#;doc (doc "A reader for export levels." + "Such as:" + #export + #hidden)} + (Syntax (Maybe Export)) + (p;maybe (p;alt (s;this (' #export)) + (s;this (' #hidden))))) + +## Declarations +(def: #export declaration + {#;doc (doc "A reader for declaration syntax." + "Such as:" + quux + (foo bar baz))} + (Syntax Declaration) + (p;either (p;seq s;local-symbol + (:: p;Monad<Parser> wrap (list))) + (s;form (p;seq s;local-symbol + (p;many s;local-symbol))))) + +## Annotations +(def: #export annotations + {#;doc "Reader for the common annotations syntax used by def: statements."} + (Syntax Annotations) + (s;record (p;some (p;seq s;tag s;any)))) + +## Definitions +(def: check^ + (Syntax [(Maybe Code) Code]) + (p;either (s;form (do p;Monad<Parser> + [_ (s;this (' "lux check")) + type s;any + value s;any] + (wrap [(#;Some type) value]))) + (p;seq (:: p;Monad<Parser> wrap #;None) + s;any))) + +(def: _definition-anns-tag^ + (Syntax Ident) + (s;tuple (p;seq s;text s;text))) + +(def: (_definition-anns^ _) + (-> Top (Syntax Annotations)) + (p;alt (s;this (' #lux;Nil)) + (s;form (do p;Monad<Parser> + [_ (s;this (' #lux;Cons)) + [head tail] (p;seq (s;tuple (p;seq _definition-anns-tag^ s;any)) + (_definition-anns^ []))] + (wrap [head tail]))) + )) + +(def: (flat-list^ _) + (-> Top (Syntax (List Code))) + (p;either (do p;Monad<Parser> + [_ (s;this (' #lux;Nil))] + (wrap (list))) + (s;form (do p;Monad<Parser> + [_ (s;this (' #lux;Cons)) + [head tail] (s;tuple (p;seq s;any s;any)) + tail (s;local (list tail) (flat-list^ []))] + (wrap (#;Cons head tail)))))) + +(do-template [<name> <type> <tag> <then>] + [(def: <name> + (Syntax <type>) + (<| s;tuple + (p;after s;any) + s;form + (do p;Monad<Parser> + [_ (s;this (' <tag>))] + <then>)))] + + [tuple-meta^ (List Code) #lux;Tuple (flat-list^ [])] + [text-meta^ Text #lux;Text s;text] + ) + +(def: (find-definition-args meta-data) + (-> (List [Ident Code]) (List Text)) + (<| (maybe;default (list)) + (case (list;find (|>. product;left (ident/= ["lux" "func-args"])) meta-data) + (^multi (#;Some [_ value]) + [(p;run (list value) tuple-meta^) + (#;Right [_ args])] + [(p;run args (p;some text-meta^)) + (#;Right [_ args])]) + (#;Some args) + + _ + #;None) + )) + +(def: #export (definition compiler) + {#;doc "A reader that first macro-expands and then analyses the input Code, to ensure it's a definition."} + (-> Compiler (Syntax Definition)) + (do p;Monad<Parser> + [definition-raw s;any + me-definition-raw (s;on compiler + (macro;expand-all definition-raw))] + (s;local me-definition-raw + (s;form (do @ + [_ (s;this (' "lux def")) + definition-name s;local-symbol + [?definition-type definition-value] check^ + definition-anns s;any + definition-anns (s;local (list definition-anns) + (_definition-anns^ [])) + #let [definition-args (find-definition-args definition-anns)]] + (wrap {#..;definition-name definition-name + #..;definition-type ?definition-type + #..;definition-anns definition-anns + #..;definition-value definition-value + #..;definition-args definition-args})))))) + +(def: #export (typed-definition compiler) + {#;doc "A reader for definitions that ensures the input syntax is typed."} + (-> Compiler (Syntax Definition)) + (do p;Monad<Parser> + [_definition (definition compiler) + _ (case (get@ #..;definition-type _definition) + (#;Some _) + (wrap []) + + #;None + (p;fail "Typed definition must have a type!") + )] + (wrap _definition))) + +(def: #export typed-input + {#;doc "Reader for the common typed-argument syntax used by many macros."} + (Syntax [Text Code]) + (s;tuple (p;seq s;local-symbol s;any))) + +(def: #export type-variables + {#;doc "Reader for the common type var/param used by many macros."} + (Syntax (List Text)) + (s;tuple (p;some s;local-symbol))) diff --git a/stdlib/source/lux/macro/syntax/common/writer.lux b/stdlib/source/lux/macro/syntax/common/writer.lux new file mode 100644 index 000000000..72e4a11eb --- /dev/null +++ b/stdlib/source/lux/macro/syntax/common/writer.lux @@ -0,0 +1,24 @@ +(;module: {#;doc "Commons syntax writers."} + lux + (lux (data (coll [list "L/" Functor<List>]) + [product]) + (macro [code])) + [.. #*]) + +## Exports +(def: #export (export ?el) + (-> (Maybe Export) (List Code)) + (case ?el + #;None + (list) + + (#;Some #..;Exported) + (list (' #export)) + + (#;Some #..;Hidden) + (list (' #hidden)))) + +## Annotations +(def: #export (annotations anns) + (-> Annotations Code) + (|> anns (L/map (product;both code;tag id)) code;record)) |