diff options
author | Eduardo Julian | 2019-02-05 02:14:27 -0400 |
---|---|---|
committer | Eduardo Julian | 2019-02-05 02:14:27 -0400 |
commit | be5710d104e6ee085dcb9d871be0b80305e48f8b (patch) | |
tree | 4efce24bf16672dd0a3042b36567ec0f136a4ae8 /stdlib/source/program | |
parent | 55d1b60e0fec6f24f1ab21d299fe04640c3e2ce3 (diff) |
Migrated documentation tool's code into stdlib.
Diffstat (limited to 'stdlib/source/program')
-rw-r--r-- | stdlib/source/program/scriptum.lux | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/stdlib/source/program/scriptum.lux b/stdlib/source/program/scriptum.lux new file mode 100644 index 000000000..a9a72ca36 --- /dev/null +++ b/stdlib/source/program/scriptum.lux @@ -0,0 +1,518 @@ +(.module: + [lux #* + [control + [pipe (#+ when>)] + ["." monad (#+ do)] + ["ex" exception (#+ exception:)] + [security + ["!" capability]]] + [cli (#+ program:)] + [data + ["." maybe] + ["." product] + ["." error (#+ Error)] + [format + ["md" markdown (#+ Markdown Span Block)]] + ["." text ("text/." equivalence) + format + ["." encoding]] + [collection + ["." sequence (#+ Sequence) ("sequence/." functor)] + ["." list ("list/." functor fold)]]] + ["." function] + ["." type] + ["." macro] + ["." io (#+ IO io)] + [world + ["." file (#+ File)]]]) + +(def: name-options "abcdefghijklmnopqrstuvwxyz") +(def: name-options-count (text.size name-options)) + +(def: (parameter-type-name id) + (-> Nat Text) + (case (text.nth id ..name-options) + (#.Some char) + (text.from-code char) + + #.None + (format (parameter-type-name (n// name-options-count id)) + (parameter-type-name (n/% name-options-count id))))) + +(def: type-var-names + (Sequence Text) + (|> 0 (sequence.iterate inc) (sequence/map parameter-type-name))) + +(do-template [<name> <partition>] + [(def: (<name> id) + (-> Nat Bit) + (<partition> id))] + + [type-func? n/even?] + [type-arg? n/odd?] + ) + +(def: (arg-id level id) + (-> Nat Nat Nat) + (n/- (n// 2 id) level)) + +(def: (parameter->name [type-func-name type-function-arguments] level id) + (-> [Text (List Text)] Nat Nat Text) + (if (type-arg? id) + (let [arg-id (..arg-id level id)] + (case (list.nth arg-id type-function-arguments) + (#.Some found) + found + + _ + (|> type-var-names + (sequence.filter (function (_ var-name) + (not (list.member? text.equivalence type-function-arguments var-name)))) + (sequence.nth arg-id)))) + type-func-name)) + +(def: (level->args offset level) + (-> Nat Nat (List Text)) + (if (n/= 0 level) + (list) + (|> level + dec + (list.n/range 0) + (list/map (|>> (n/+ (inc offset)) parameter-type-name))))) + +(def: (prefix-lines prefix lines) + (-> Text Text Text) + (|> lines + (text.split-all-with text.new-line) + (list/map (|>> (format prefix))) + (text.join-with text.new-line))) + +(def: (pprint-type-definition level type-func-info tags module signature? recursive-type? type) + (-> Nat [Text (List Text)] (List Name) Text Bit Bit Type Text) + (case tags + (^ (list [_ single-tag])) + (if signature? + (format "(: " (pprint-type-definition level type-func-info #.None module signature? recursive-type? type) text.new-line " " single-tag ")") + (format "{#" single-tag " " (pprint-type-definition level type-func-info #.None module signature? recursive-type? type) "}")) + + _ + (case [recursive-type? type] + [_ (#.Primitive name params)] + (case params + #.Nil + (format "(primitive " (%t name) ")") + + _ + (format "(primitive " (%t name) " " (|> params (list/map (pprint-type-definition level type-func-info #.None module signature? recursive-type?)) (text.join-with " ")) ")")) + + [_ (#.Sum _)] + (let [members (type.flatten-variant type)] + (case tags + #.Nil + (format "(| " + (|> members + (list/map (pprint-type-definition level type-func-info #.None module signature? recursive-type?)) + (text.join-with " ")) + ")") + + _ + (|> members + (list.zip2 tags) + (list/map (function (_ [[_ t-name] type]) + (case type + (#.Product _) + (let [types (type.flatten-tuple type)] + (format "(#" t-name " " + (|> types + (list/map (pprint-type-definition level type-func-info #.None module signature? recursive-type?)) + (text.join-with " ")) + ")")) + + _ + (format "(#" t-name " " (pprint-type-definition level type-func-info #.None module signature? recursive-type? type) ")")))) + (text.join-with text.new-line)))) + + [_ (#.Product _)] + (let [members (type.flatten-tuple type)] + (case tags + #.Nil + (format "[" (|> members (list/map (pprint-type-definition level type-func-info #.None module signature? recursive-type?)) (text.join-with " ")) "]") + + _ + (let [member-docs (|> members + (list.zip2 tags) + (list/map (function (_ [[_ t-name] type]) + (if signature? + (format "(: " (pprint-type-definition level type-func-info #.None module signature? recursive-type? type) text.new-line " " t-name ")") + (format "#" t-name " " (pprint-type-definition level type-func-info #.None module signature? recursive-type? type))))) + (text.join-with (format text.new-line " ")))] + (if signature? + member-docs + (format "{" member-docs "}"))))) + + [_ (#.Function input output)] + (let [[ins out] (type.flatten-function type)] + (format "(-> " (|> ins (list/map (pprint-type-definition level type-func-info #.None module signature? recursive-type?)) (text.join-with " ")) + " " + (pprint-type-definition level type-func-info #.None module signature? recursive-type? out) + ")")) + + [_ (#.Parameter idx)] + (parameter->name type-func-info level idx) + + (^template [<tag> <pre> <post>] + [_ (<tag> id)] + (format <pre> (%n id) <post>)) + ([#.Var "⌈v:" "⌋"] + [#.Ex "⟨e:" "⟩"]) + + (^template [<tag> <name> <flatten>] + [_ (<tag> _)] + (let [[level' body] (<flatten> type) + args (level->args level level') + body-doc (pprint-type-definition (n/+ level level') type-func-info tags module signature? recursive-type? body)] + (format "(" <name> " " "[" (text.join-with " " args) "]" + (case tags + #.Nil + (format " " body-doc) + + _ + (format text.new-line (prefix-lines " " body-doc))) + ")"))) + ([#.UnivQ "All" type.flatten-univ-q] + [#.ExQ "Ex" type.flatten-ex-q]) + + [true (#.Apply (#.Parameter 1) (#.Parameter 0))] + (product.left type-func-info) + + [_ (#.Apply param fun)] + (let [[type-func type-arguments] (type.flatten-application type)] + (format "(" (pprint-type-definition level type-func-info tags module signature? recursive-type? type-func) " " (|> type-arguments (list/map (pprint-type-definition level type-func-info #.None module signature? recursive-type?)) (text.join-with " ")) ")")) + + [_ (#.Named [_module _name] type)] + (if (text/= module _module) + _name + (%name [_module _name])) + ))) + +(def: (pprint-type level type-func-name module type) + (-> Nat Text Text Type Text) + (case type + (#.Primitive name params) + (case params + #.Nil + (format "(primitive " (%t name) ")") + + _ + (format "(primitive " (%t name) " " (|> params (list/map (pprint-type level type-func-name module)) (list.interpose " ") (text.join-with "")) ")")) + + (#.Sum _) + (let [members (type.flatten-variant type)] + (format "(| " (|> members (list/map (pprint-type level type-func-name module)) (list.interpose " ") (text.join-with "")) ")")) + + (#.Product _) + (let [members (type.flatten-tuple type)] + (format "[" (|> members (list/map (pprint-type level type-func-name module)) (list.interpose " ") (text.join-with "")) "]")) + + (#.Function input output) + (let [[ins out] (type.flatten-function type)] + (format "(-> " + (|> ins (list/map (pprint-type level type-func-name module)) (list.interpose " ") (text.join-with "")) + " " + (pprint-type level type-func-name module out) + ")")) + + (#.Parameter idx) + (parameter->name [type-func-name (list)] level idx) + + (^template [<tag> <pre> <post>] + (<tag> id) + (format <pre> (%n id) <post>)) + ([#.Var "⌈" "⌋"] + [#.Ex "⟨" "⟩"]) + + (^template [<tag> <name> <flatten>] + (<tag> _) + (let [[level' body] (<flatten> type) + args (level->args level level') + body-doc (pprint-type (n/+ level level') type-func-name module body)] + (format "(" <name> " " "[" (|> args (list.interpose " ") (text.join-with "")) "]" + (format " " body-doc) + ")"))) + ([#.UnivQ "All" type.flatten-univ-q] + [#.ExQ "Ex" type.flatten-ex-q]) + + (#.Apply param fun) + (let [[type-func type-arguments] (type.flatten-application type)] + (format "(" (pprint-type level type-func-name module type-func) " " (|> type-arguments (list/map (pprint-type level type-func-name module)) (list.interpose " ") (text.join-with "")) ")")) + + (#.Named [_module _name] type) + (if (text/= module _module) + _name + (%name [_module _name])) + )) + +(type: (Mutation a) + (-> a a)) + +(type: Value [Text Code Type]) + +(type: Organization + {#types (List Value) + #macros (List [Text Code]) + #structures (List Value) + #values (List Value)}) + +(def: (lux-module? module-name) + (-> Text Bit) + (or (text/= "lux" module-name) + (text.starts-with? "lux/" module-name))) + +(def: (add-definition [name [def-type def-annotations def-value]] organization) + (-> [Text Definition] Organization Organization) + (cond (macro.type? def-annotations) + (update@ #types + (: (Mutation (List Value)) + (|>> (#.Cons [name def-annotations (:coerce Type def-value)]))) + organization) + + (macro.macro? def-annotations) + (update@ #macros + (: (Mutation (List [Text Code])) + (|>> (#.Cons [name def-annotations]))) + organization) + + (macro.structure? def-annotations) + (update@ #structures + (: (Mutation (List Value)) + (|>> (#.Cons [name def-annotations def-type]))) + organization) + + ## else + (update@ #values + (: (Mutation (List Value)) + (|>> (#.Cons [name def-annotations def-type]))) + organization))) + +(def: name-sort + (All [r] (-> [Text r] [Text r] Bit)) + (let [text/< (:: text.order <)] + (function (_ [n1 _] [n2 _]) + (text/< n1 n2)))) + +(def: (organize-definitions defs) + (-> (List [Text Definition]) Organization) + (let [init {#types (list) + #macros (list) + #structures (list) + #values (list)}] + (|> (list/fold add-definition init defs) + (update@ #types (list.sort name-sort)) + (update@ #macros (list.sort name-sort)) + (update@ #structures (list.sort name-sort)) + (update@ #values (list.sort name-sort))))) + +(def: (unravel-type-func level type) + (-> Nat Type Type) + (if (n/> 0 level) + (case type + (#.UnivQ _env _type) + (unravel-type-func (dec level) _type) + + _ + type) + type)) + +(def: (unrecurse-type type) + (-> Type Type) + (case type + (#.Apply _ (#.UnivQ _env _type)) + _type + + _ + type)) + +(exception: #export (anonymous-type-definition {type Type}) + (ex.report ["Type" (%type type)])) + +(def: (document-type module type def-annotations) + (-> Text Type Code (Meta (Markdown Block))) + (case type + (#.Named type-name type) + (do macro.monad + [tags (macro.tags-of type-name) + #let [[_ _name] type-name + recursive-type? (macro.recursive-type? def-annotations) + ## type (: Type + ## (case [recursive-type? type] + ## [#1 (#.Apply dummyT (#.UnivQ _closure recursiveT))] + ## recursiveT + + ## _ + ## type)) + type-arguments (macro.type-arguments def-annotations) + signature? (macro.signature? def-annotations) + usage (case type-arguments + #.Nil + _name + + _ + (format "(" (text.join-with " " (list& _name type-arguments)) ")")) + nesting (list.size type-arguments)]] + (wrap (md.code (format (if signature? "(signature: " "(type: ") + (if recursive-type? "#rec " "") + usage text.new-line + (|> type + (unravel-type-func nesting) + (when> recursive-type? [unrecurse-type]) + (pprint-type-definition (dec nesting) [_name type-arguments] (maybe.default (list) tags) module signature? recursive-type?) + (text.split-all-with text.new-line) + (list/map (|>> (format " "))) + (text.join-with text.new-line)) + ")")))) + + _ + (macro.fail (ex.construct anonymous-type-definition type)))) + +(def: (document-types module types) + (-> Text (List Value) (Meta (Markdown Block))) + (do macro.monad + [type-docs (monad.map @ + (: (-> Value (Meta (Markdown Block))) + (function (_ [name def-annotations type]) + (do macro.monad + [#let [?doc (macro.get-documentation def-annotations)] + type-code (document-type module type def-annotations)] + (wrap ($_ md.then + (md.heading/3 name) + (case ?doc + (#.Some doc) + (md.paragraph (md.text doc)) + + _ + md.empty) + type-code))))) + types)] + (wrap (list/fold (function.flip md.then) + (md.heading/2 "Types") + type-docs)))) + +(def: (document-macros module-name names) + (-> Text (List [Text Code]) (Markdown Block)) + (|> names + (list/map (: (-> [Text Code] (Markdown Block)) + (function (_ [name def-annotations]) + ($_ md.then + (md.heading/3 name) + (<| (: (Markdown Block)) + (maybe.default md.empty) + (do maybe.monad + [documentation (macro.get-documentation def-annotations)] + (wrap (md.code documentation)))))))) + (list/fold (function.flip md.then) + (md.heading/2 "Macros")))) + +(do-template [<singular> <plural> <header>] + [(def: (<singular> module type) + (-> Text Type (Markdown Block)) + (md.code (pprint-type (dec 0) "?" module type))) + + (def: (<plural> module values) + (-> Text (List Value) (Markdown Block)) + (|> values + (list/map (function (_ [name def-annotations value-type]) + (let [?doc (macro.get-documentation def-annotations) + usage (case (macro.function-arguments def-annotations) + #.Nil + name + + args + (format "(" (text.join-with " " (list& name args)) ")"))] + ($_ md.then + (md.heading/3 usage) + (case ?doc + (#.Some doc) + (md.code doc) + + _ + md.empty) + (<singular> module value-type))))) + (list/fold (function.flip md.then) + (md.heading/2 <header>))))] + + [document-structure document-structures "Structures"] + [document-value document-values "Values"] + ) + +(def: (enclose-lines pre+post block) + (-> [Text Text] Text Text) + (|> block + (text.split-all-with text.new-line) + (list/map (text.enclose pre+post)) + (text.join-with text.new-line))) + +(def: (document-module [[module-name module] organization]) + (-> [[Text Module] Organization] (Meta [Text (Markdown Block)])) + (do macro.monad + [#let [(^slots [#types #macros #structures #values]) organization + annotations (|> module + (get@ #.module-annotations) + (maybe.default (' {})) + macro.get-documentation) + description (case annotations + (#.Some doc-text) + (md.quote (md.paragraph (md.text doc-text))) + + #.None + md.empty) + empty-block (: (Markdown Block) md.empty)] + types-documentation (if (list.empty? types) + (wrap empty-block) + (document-types module-name types)) + #let [documentation ($_ md.then + types-documentation + (if (list.empty? macros) empty-block (document-macros module-name macros)) + (if (list.empty? structures) empty-block (document-structures module-name structures)) + (if (list.empty? values) empty-block (document-values module-name values)))]] + (wrap [module-name + ($_ md.then + (md.heading/1 module-name) + description + documentation)]))) + +(exception: #export (io-error {error Text}) + error) + +(def: (save-documentation! [module-name documentation]) + (-> [Text (Markdown Block)] (IO Any)) + (let [path (format (text.replace-all "/" "_" module-name) ".md")] + (do io.monad + [outcome (do (error.with-error io.monad) + [target (: (IO (Error (File IO))) + (file.get-file io.monad file.system path))] + (!.use (:: target over-write) (encoding.to-utf8 (md.markdown documentation))))] + (case outcome + (#error.Failure error) + (wrap (log! (ex.construct io-error error))) + + (#error.Success _) + (wrap []))))) + +(macro: (gen-documentation! _) + (do macro.monad + [all-modules macro.modules + #let [lux-modules (|> all-modules + (list.filter (function.compose lux-module? product.left)) + (list.sort name-sort))] + lux-exports (monad.map @ (function.compose macro.exports product.left) + lux-modules) + module-documentation (|> (list/map organize-definitions lux-exports) + (list.zip2 lux-modules) + (monad.map @ document-module)) + #let [_ (io.run (monad.map io.monad save-documentation! module-documentation))]] + (wrap (list)))) + +(gen-documentation!) + +(program: args + (io (log! "Done!"))) |