Browse Source

Initial, alpha commit

I've decided to move all of these disparate repositories together. It
was getting difficult to maintain all of them, especially when finding a
bug in the parser while writing the manual.
Alex Feldman-Crough 2 years ago
  1. 46
  2. 2
  3. 211
  4. 1
  5. 7
  6. 373
  7. 4
  8. 2
  9. 64
  10. 3
  11. 4
  12. 8
  13. 0
  14. 13
  15. 139
  16. 23
  17. 24
  18. 1
  19. 21
  20. 1
  21. 22
  22. 20
  23. 42
  24. 183
  25. 117
  26. 44
  27. 230
  28. 25
  29. 225
  30. 1
  31. 7
  32. 114
  33. 88
  34. 379
  35. 412
  36. 61
  37. 164
  38. 18
  39. 1
  40. 1
  41. 16
  42. 75
  43. 40
  44. 69
  45. 36
  46. 1
  47. 280
  48. 216
  49. 115
  50. 110
  51. 66
  52. 30
  53. 6
  54. 1
  55. 13
  56. 153


@ -0,0 +1,46 @@
dconf_roundtrip_exactprint_only: false
dconf_dump_bridoc_simpl_par: false
dconf_dump_ast_unknown: false
dconf_dump_bridoc_simpl_floating: false
dconf_dump_config: false
dconf_dump_bridoc_raw: false
dconf_dump_bridoc_final: false
dconf_dump_bridoc_simpl_alt: false
dconf_dump_bridoc_simpl_indent: false
dconf_dump_annotations: false
dconf_dump_bridoc_simpl_columns: false
dconf_dump_ast_full: false
options_ghc: []
econf_ExactPrintFallback: ExactPrintFallbackModeInline
econf_Werror: true
econf_omit_output_valid_check: false
econf_produceOutputOnErrors: false
ppconf_CPPMode: CPPModeAbort
ppconf_hackAroundIncludes: false
conf_obfuscate: false
conf_roundtrip_exactprint_only: false
conf_version: 1
lconfig_reformatModulePreamble: true
tag: AltChooserBoundedSearch
contents: 3
lconfig_allowSingleLineExportList: false
lconfig_importColumn: 50
lconfig_hangingTypeSignature: false
lconfig_importAsColumn: 50
lconfig_alignmentLimit: 30
lconfig_allowHangingQuasiQuotes: true
lconfig_indentListSpecial: true
lconfig_indentAmount: 4
lconfig_alignmentBreakOnMultiline: true
lconfig_cols: 80
lconfig_indentPolicy: IndentPolicyFree
lconfig_indentWhereSpecial: true
tag: ColumnAlignModeMajority
contents: 0.7


@ -0,0 +1,2 @@
PATH_add bin
PATH_add bin/cabal


@ -0,0 +1,211 @@
-- This is a GHC environment file written by cabal. This means you can
-- run ghc or ghci and get the environment of the project as a whole.
-- But you still need to use cabal repl $target to get the environment
-- of specific components (libs, exes, tests etc) because each one can
-- have its own source dirs, cpp flags etc.
package-db /home/alex/.cabal/store/ghc-8.6.5/package.db
package-db dist-newstyle/packagedb/ghc-8.6.5
package-id chronicle-
package-id ansi-terminal-0.10.2-2dc0f2e43ea327068915d6a979c22f0864572a00911d7c8231eebe006e40d638
package-id base-
package-id ghc-prim-0.5.3
package-id rts
package-id integer-gmp-
package-id colour-2.3.5-53776e4fee39fa728d7c4e1d1c1a4d54210c3b31d5231c8b47a4d2dac66d4989
package-id ansi-wl-pprint-0.6.9-dbea1004a1359534184431eb85935e37c2b2ad58262801a29af65960f370399d
package-id fast-logger-3.0.0-8c714680508f5da0888ec93bade69f26c20093e5c47ca5660b58a5d359237294
package-id array-
package-id auto-update-0.1.6-cc73461ce30c11f2e57a0d9118e737f59a19e35ce50d089a5151e0ae71712293
package-id bytestring-
package-id deepseq-
package-id directory-
package-id filepath-
package-id time-
package-id unix-
package-id easy-file-0.2.2-c35470a0131847077c1b64c9e0b8677fe02738f648838690932c9528af59b3df
package-id text-
package-id binary-
package-id containers-
package-id template-haskell-
package-id ghc-boot-th-8.6.5
package-id pretty-
package-id unix-compat-0.5.2-bb5c52439947b0c45487d82e35cd18bbf9a84cab4665295e263e169eff1a7697
package-id unix-time-0.4.7-d680486b3e3df3c8d9190e172a4ec6e23b8ffa621a8bfad29a71fdedc748fb1c
package-id old-time-
package-id old-locale-
package-id hakyll-contrib-prosidy-
package-id hakyll-
package-id blaze-html-
package-id blaze-builder-
package-id blaze-markup-
package-id cryptohash-0.11.9-e5f0cc5d7262104cf59e05c69f244faed8915436b1a6214e716ff09366858e1c
package-id byteable-0.1.1-3d5bfb82d8764d5b710caf10b5829d9fce40b35535d5ea64d6366af56be299b0
package-id cryptonite-0.26-459ba3060d2b7ae4dadcf78efc751ebd96ecb43c01c871bc11b35a65be48ddcf
package-id basement-0.0.11-c93aa5ce46a8be61160cd4cffde61c36d7141f3bee8b863b4c7fd534fdc33aac
package-id memory-0.15.0-265032d929ebed3af5c8b2c81e3fde1e9ffb17ffc9b89805198a4442d8398140
package-id data-default-
package-id data-default-class-
package-id data-default-instances-containers-0.0.1-30039a5c1e8aed0ae03845e8046c68462dbcc559a851bc008b76b98ec09f9a32
package-id data-default-instances-dlist-0.0.1-cacac25c1f9191eddd45b7411bd2ab2d88976143a87f80832032683b2980e26c
package-id dlist-
package-id data-default-instances-old-locale-0.0.1-aa5bb51f79f36cc79f923eba3de7e876efaf69512cda663de8c818ee0f198cd5
package-id file-embed-
package-id fsnotify-
package-id async-2.2.2-f117595b0248a9635549f29a47d9866b0e710108024e778e5ccaffeda75ea504
package-id hashable-
package-id stm-
package-id hinotify-0.4-77e286626c0be4a4eff0fb1abcfd1750a1a3f85c067083f4313229d1449398c5
package-id shelly-1.9.0-badb2d84cc60dd870b7179bd92700fb3f95ce1ff24c8b6fce383595a6b55340c
package-id enclosed-exceptions-1.0.3-aca12fad3612eeb782e1e1752869f2a625aa1420cb027740f729626dffbddbe6
package-id lifted-base-
package-id monad-control-
package-id transformers-
package-id transformers-base-
package-id base-orphans-0.8.1-b7d9c0aa2d11bf7d30fb3363c9d5830bb0efb7ab2778ae7fea2f276fea1b585b
package-id transformers-compat-0.6.5-ad082516100fea2a07b4d10b032dcdea07dec2a1580b8069eeabcfcb5dcd6059
package-id exceptions-0.10.4-a25f0ac6af85bd262493de16c1743cf01e323bd0fd2861756e66de853786b62e
package-id mtl-2.2.2-e66e24691de619909dbe1204b9d470038c60f1595d3bbd1895488778c7930ceb
package-id lifted-async-
package-id constraints-0.11.2-ee8ed544816922f1386029e2ec791f8626503c28779a27c512297b02d704cfe5
package-id semigroups-0.19.1-587d7c85481bbb19606f3f4e028e747346d745c88567cf723a7937d96356cb46
package-id type-equality-1-1167d5a826f5562883cc857b7c1c12db70de62726f6e1080a218e0615603e4c0
package-id process-
package-id http-conduit-
package-id aeson-
package-id attoparsec-
package-id scientific-
package-id integer-logarithms-1.0.3-8760a784241e76cefb032afbbca4e5532e21247e657f7e520b4fbfccd7c46bd3
package-id primitive-
package-id base-compat-0.11.0-0b7ffcf467ea32ca9441d66d233af48c6bf1a1c721d354b367b7c738676031aa
package-id tagged-0.8.6-8e056b71367655353ca5b31af065862b0cba5cecc3365360968be8d76a4ad7e4
package-id th-abstraction-
package-id time-compat-
package-id unordered-containers-
package-id uuid-types-1.0.3-4baf991d63beb201de7dd2bd47026a16165d80d275bb2dfc6f648e272eeaa58a
package-id random-1.1-7b66fa297bfcbd595493a6c644955fd593a367c517e9baf40a8112df6e857f69
package-id vector-
package-id conduit-
package-id mono-traversable-
package-id split-
package-id vector-algorithms-
package-id resourcet-1.2.2-90729c605357d6ef0fa13874aeef5fa67464488a2c2a4d7701a980bab528b4e9
package-id unliftio-core-
package-id conduit-extra-1.3.4-c53f3791f6f502bccdd55eb203179ecd85bf383ad138bd00399e1bf32620de57
package-id network-
package-id streaming-commons-
package-id zlib-
package-id typed-process-
package-id http-client-0.6.4-d645633c971fa90769e5722c72da62100aae032fe6116ce30f4b37f74fe92292
package-id case-insensitive-
package-id cookie-0.4.5-1884d1b8dc2b3857cac86ee558c68c5806ac4b53d25771ffe884efb51cb7b3c2
package-id http-types-0.12.3-6b9bb4b26d26af03849d3a6e7bb6f5255ba09746fccdd6678d410d22ebc0f865
package-id mime-types-
package-id network-uri-
package-id parsec-
package-id http-client-tls-
package-id connection-0.3.1-9bb94bbb5c44e66933c7553342e9af924c9aaeefa4d58def06b0ac9be8c12c75
package-id socks-0.6.1-084244795e91e028a9637adb0cfc5396a0c85fdb3c02d09b4f5bec4e26c3a4f2
package-id cereal-
package-id tls-1.5.2-563ef19c6d40fd6e5b1d41bff0507df8ff0b74d9a33dc0b6fdd13113618d3f4e
package-id asn1-encoding-0.9.6-80366a64ab0cf61eee543d3085ea62e5c0a78758bbc5ec577c795288d6433a6d
package-id asn1-types-0.3.3-37238c65adaa3f68ce9150ea9f51001cc4071e223a00bf5866bd42c7f9f712a9
package-id hourglass-0.2.12-4b7addf2d42e07db47ea81be6d3c094b23dd3afc12c06890c047ffc2775c4dc2
package-id x509-1.7.5-8563fe0deb989bf0dfd50108e39039dcf750964223a4a72b313512a23a0a0b06
package-id asn1-parse-0.9.5-52d8adba50e3ab1b9112c3bb51ed8db02d18d2149bb2314a5472829fb4662b7b
package-id pem-0.2.4-16116a8757fdca6332d94cb164ded55943752fc67353f07b64f30b0ccc3dd1d1
package-id x509-store-1.6.7-62e43449a9f3830b70257a1e3909f1ae78bcd798a3545dc2b8a099290d5b07c0
package-id x509-validation-1.6.11-8029463ccfa3a79af06b155b584b9cdab55547f4db3f563aabd732c9bddc17ac
package-id x509-system-1.6.6-260fc710aff1a47628cc462d4e26f1e0a604315ab511c3b6fe89232d630b71a4
package-id lrucache-
package-id contravariant-1.5.2-a82bf270197d1ba9e479e28198e1bb256d87c8bfa6e6927a18af9e27c31827b1
package-id StateVar-1.2-099a8543251b4537d06c4021fe3bff1b4611e4c27cdadcf9ac4bd60cb8c065ec
package-id optparse-applicative-
package-id pandoc-2.7.3-5f3fb74ab1bb1f8fa479f1a02ddc37d864394a41e0b53b09e78ce18d4055eb0a
package-id Glob-0.10.0-a9488112fecab3b6567b4907656e4b0e31c310c58c03a2d31a277197ab419fd5
package-id HTTP-4000.3.14-5278a967ab54f38f2a5dab560be1f2e0e96adc4906bb460bcf86b7efc3c96a88
package-id HsYAML-
package-id JuicyPixels-3.3.4-1b795504daf969b0774c6df7a6de162ace4caecd42928e9150d9f5f445c24805
package-id SHA-
package-id aeson-pretty-0.8.8-9645a0aa2f8cdb15354521f9d9c4ff01c95f0ac08049b8625366c4f960297b38
package-id base64-bytestring-
package-id cmark-gfm-0.2.1-cdd84f5fba2b98aa39474b26c449922b5697cb7f95ca58d5c3e00dc5ea1183c1
package-id doctemplates-
package-id haddock-library-1.7.0-71c2ae719a6bce1c6a7e7245f79bae1902e01bda3a32c266c3c2fbb0452d8fb4
package-id hslua-
package-id fail-
package-id hslua-module-system-0.2.1-e6d1c66a2dab2b31afcbe290f4528c73fd47f82e8bf12d71d5b4c1456e2faa98
package-id temporary-1.3-db1f911d34d81330dd15e966aa3dbb8c751f2f6042bdcf3f7d282a9b45bf0dd1
package-id hslua-module-text-0.2.1-153acbf37e418010839732ed210b9f6ec0855452dcf52132bc412a8f7755e69c
package-id ipynb-0.1-2dd459a4134a799424179a1f09af089ec0f304697a2bef4d5d3d51ca63a79b82
package-id pandoc-types-
package-id QuickCheck-2.13.2-6892514883ea925cbada79f45c7367660140f96d3f5148e22fb8d87046b0a2f4
package-id splitmix-0.0.3-c93b56a8d33021fad4c5d26869aebcbc6436a39260d13958ac135dcb8d534c29
package-id syb-0.7.1-bac5f137bab7f119c112c8356bf2a9057a84680b8be81df52b7071594d5a6267
package-id safe-0.3.18-2aaead6220f580f273343474cf44bcfd65451ab082a240f57e425e935fea292c
package-id skylighting-0.8.3-1ded9694c1985a36287813d3e6e86358ff091417fe726db07646fb9090701872
package-id skylighting-core-0.8.3-3d87935daaf2a188cbd8f988a4a37cd837304cce78fa0d2764223138d97de37e
package-id hxt-
package-id hxt-charproperties-
package-id hxt-regex-xmlschema-
package-id hxt-unicode-
package-id regex-pcre-builtin-
package-id regex-base-0.93.2-e4e20670eba07de737dc5601f21a59af12813b36e0d19ec2ccec6649f354361b
package-id utf8-string-
package-id tagsoup-0.14.8-aeec797b415ccc265f89689f67118f0d3e357f97bec4c95613ead8663d4b7f15
package-id texmath-0.11.3-5076cce34dfc625cc171efffcbe7d6aa8215774d709d03a516daf419407e72ee
package-id xml-1.3.14-35539c805765698b7cb99f7a066659d00fbc6818cee2e670af697680bc7c6e10
package-id unicode-transforms-0.3.6-a984ca749b8b6efe6907cf8f9defcbad7a8ad80cd2ba235f8b9838723e319497
package-id bitarray-
package-id zip-archive-0.4.1-f2b68e4c37b5f1d1dc935affcf042854c8595f7357f5a8b1715c17efd5edabfe
package-id digest-
package-id pandoc-citeproc-0.16.2-b2ded21c06268617d98c00cfbfc4f7cad7fce844d9d50d1ced223873c206fa63
package-id hs-bibutils-
package-id rfc5051-
package-id setenv-
package-id xml-conduit-
package-id xml-types-0.3.6-619b2597bbc5110bd20535debf5f468c3e11ac4ed52813599a8fac989301ace5
package-id yaml-
package-id libyaml-
package-id regex-tdfa-
package-id time-locale-compat-
package-id wai-
package-id vault-
package-id wai-app-static-
package-id http-date-0.0.8-e5161b82f93cb42874d96f23b03e3e4d7ade83c77476908364ef89a36dcf9ccf
package-id wai-extra-3.0.28-a04bb514d2109f9f755b410fa0738fab5d7ca2dff82421da7f6f04a960d828c4
package-id http2-1.6.5-798f768d1d3a482c9396380d2189d833ee153ef58d556197ab652f5a3c87c012
package-id network-byte-order-
package-id psqueues-
package-id iproute-1.7.8-c639a354ae083e4be14084e6fbcca83464d2b8967a4384a4acce7d799a59ec93
package-id appar-0.1.8-d2837d97bae942ef0684de8a53847e19ed79f6db57ceb68e26aa2857a95ed34c
package-id byteorder-1.0.4-ad8485c043e6cfc29c886f9970bba17c985fb7c4ebc7a05b629af67a35a3c8c5
package-id void-0.7.3-d04b704a30ddba5da20a732cd6e4dc5e1babf8be8d271a9ba9eb97b613a3ffdb
package-id wai-logger-2.3.6-4f6e1753c2c06a7415d05c43ba4a392b7e3259ff5be893ee7caddf0934284791
package-id word8-0.1.3-cc46026d2b3c47330f9a75731f7c963904ac73c35a3abd261e2aafe76979b9f5
package-id warp-3.2.28-dcb0155990e605f29a70016cbfe1f042a8e14cc4a7e7508f486fd6793a9e742c
package-id bsb-http-chunked-
package-id simple-sendfile-0.2.30-91c1c6a2b4d717ad7351d3f2891d678293a53e8ec0611b98b73a4b98d3c2d470
package-id time-manager-0.0.0-c6b539fee52306fb5280d72997a910d30a970455171420569791b6de78b8fe46
package-id prosidy-
package-id megaparsec-8.0.0-d503b2a9001a4196e16aadd3e7ae59530e16bcc74ee37bc1f34b3ef5d66b6e72
package-id parser-combinators-1.2.1-4e18800a8002d07febb9fc8cabb1b82848eddc05509d85511635df821da98c91
package-id prettyprinter-1.5.1-ad4aae585f34d8dce9237b8f3e6b5b44f75305c0f80f3f575a3c216a0eb0bdea
package-id profunctors-5.3-0b30be1b9ad99323c7acb44a29f3e14bb3aba19ed105b01f72178bb638e09e5f
package-id bifunctors-5.5.6-7d692f765d57c8afea5ab00e94ac76a47815a60e923bf89cdca2c8f31d006065
package-id comonad-5.0.6-ff05e2462fd8c09e47d2a1d5fa1e922d52bf9a91e52098ad97ebcdf9f4e93108
package-id distributive-0.6.1-15cb8a79958164ea3f1eabb29be8bff4bed15d9ed9402726b77b12c8adba32ad
package-id lens-4.17.1-878d5531961d3c2f4042cc242d517e1c7d7ae6c8560e09eaaf3e10c90f9bdf81
package-id call-stack-0.2.0-1af5ab25b7e32de4a30b41e2e1b9a92f8bc3e52b11af007e5cca028121292373
package-id free-5.1.3-1aecedbd8ae9190ae402d357c7703d797344df53b8e9be7c3f3875bd324e054e
package-id semigroupoids-5.3.4-baec15c25abbbdd203b8d19f872f686073198a159533bb65f4c3ab20812ed308
package-id kan-extensions-5.2-896c8c4728979e8d7af6158de7772943daef0078c0c3e046dd6f9df1544b7745
package-id adjunctions-4.4-b96906896afb2181bf3265a54aaee354366c537f5147f9da1eb7ddade95316e4
package-id invariant-0.5.3-63a5e9886a8aa72abe306d0a84564853ed56aa7d7381659c1571941af85c936e
package-id parallel-
package-id reflection-2.1.5-5758fcc02f8caa64644197676f755b2a441848e286c3eb4608534af5d237eb0e
package-id generic-lens-
package-id prosidyc-
package-id mmorph-1.1.3-38b8670425a13bbb2cd0b7cdb129945f8ac00cc403d8bda6de825c9c728a8890


@ -0,0 +1 @@
*.golden -text


@ -0,0 +1,7 @@


@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
(a) for any code that a Contributor has removed from Covered Software;
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
8. Litigation
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.


@ -0,0 +1,4 @@
title: Prosidy README
Prosidy is a small language for writing documents.


@ -0,0 +1,2 @@
exec cabal v2-build "${@:-all}"


@ -0,0 +1,64 @@
set -euo pipefail
cd "$(dirname ${BASH_SOURCE})/.."
# File(s) that brittany can't currently handle b/c view patterns
msg () { printf -- '\e[1m----> %s\e[m\n' "$*" >&2; }
warn () { printf -- '\e[33;1m----! %s\e[m\n' "$*" >&2; }
err () { printf -- '\e[31;1m----! %s\e[m\n' "$*" >&2; exit 1; }
# Make sure that everything is staged to Git, so that bad formats can be
# undone.
function ensure_staged
local -a unstaged
unstaged=( $(git ls-files --modified '*.hs') )
if [ -n "${unstaged:-}" ]
warn 'Formatting will overwrite the contents of files.'
warn 'The following files have been modified but not staged.'
for path in "${unstaged[@]}"
echo "$path" >&2
err 'Please stage (git add) these files and try again.'
# Ensure that brittany is installed; if it isn't, install it via Cabal
function install_brittany
[ -x bin/cabal/brittany ] || {
warn 'The command `brittany` was not found.'
msg 'Installing `brittany`: this may take a few minutes...'
cabal v2-install --verbose=0 --symlink-bindir="$PWD/bin/cabal" brittany
# Format Haskell files
function do_format
local -a hsfiles
git ls-files '*.hs' | grep -v "$IGNORE_PATTERN"
[ -n "${hsfiles:-}" ] || {
err 'Didn'\''t find any Haskell sources.'
bin/cabal/brittany \
--no-user-config --config-file="$PWD/.brittany.yaml" \
--write-mode inplace \


@ -0,0 +1,3 @@
set -e
exec cabal v2-exec -- prosidy-manual -- "$@"


@ -0,0 +1,4 @@
exec cabal v2-build --disable-optimization --flags '-fno-code' "${@:-all}"


@ -0,0 +1,8 @@
packages: src/*/*.cabal
jobs: $ncpus
package *
split-sections: true
optimization: true
executable-stripping: true
executable-static: true



@ -0,0 +1,13 @@
title: An overview of Prosidy
Prosidy is a small language for writing,
designed to be extensible and fun.
It's #i{unopinionated}
different types of writing have highly different requirements
so Prosidy is designed to allow users to define their own #i{dialects}.
#-section[ title = 'Prosidy compared to other markup languages'
, slug = 'language-comparison' ]:


@ -0,0 +1,139 @@
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StrictData #-}
module System.Log.Chronicle
( Level(..)
, withLogger
, log
, trace
, debug
, info
, warn
, error
, fatal
import Prelude hiding ( log
, error
import Control.Concurrent ( MVar
, newEmptyMVar
, tryReadMVar
, tryPutMVar
, takeMVar
import Control.Monad ( when )
import Data.Char ( toUpper )
import Data.Foldable ( for_ )
import System.IO.Unsafe ( unsafePerformIO )
import Control.Monad.IO.Class ( MonadIO(liftIO) )
import qualified Control.Exception as Exception
import qualified System.Exit as Exit
import qualified System.Log.FastLogger as Log
import qualified System.Log.FastLogger.Date as Log.Date
import qualified Text.PrettyPrint.ANSI.Leijen as PP
import qualified System.Console.ANSI as ANSI
import qualified System.IO as IO
data Level =
| Debug
| Info
| Warn
| Error
| Fatal
deriving (Show, Eq, Ord, Enum)
data Logger = Logger
{ color :: Bool
, minLevel :: Maybe Level
, loggerSet :: Log.LoggerSet
, getTime :: IO Log.FormattedTime
globalLogger :: MVar Logger
globalLogger = unsafePerformIO newEmptyMVar
withLogger :: Maybe Bool -> Maybe Level -> IO a -> IO a
withLogger shouldColor minLevel =
Exception.bracket install (Exception.mask_ . remove) . const . handler
handler :: IO x -> IO x
handler = Exception.handle $ \e -> do
case Exception.fromException @Exit.ExitCode e of
Just exit -> do
info @Log.LogStr "ExitCode exception was thrown; exiting"
Exception.throwIO exit
Nothing -> do
error @String "An unhandled exception caused Prosidy to fail"
fatal $ Exception.displayException e
install :: IO Bool
install = do
color <- maybe (ANSI.hSupportsANSI IO.stderr) pure shouldColor
loggerSet <- Log.newStderrLoggerSet Log.defaultBufSize
getTime <- Log.Date.newTimeCache "%Y-%m-%d %H:%M:%S"
tryPutMVar globalLogger Logger { color, minLevel, loggerSet, getTime }
remove :: Bool -> IO ()
remove installed = when installed $ do
Logger { loggerSet } <- takeMVar globalLogger
Log.rmLoggerSet loggerSet
log :: Log.ToLogStr msg => Level -> msg -> IO ()
log level msg = do
maybeLogger <- tryReadMVar globalLogger
for_ maybeLogger $ \Logger { color, getTime, minLevel, loggerSet } ->
when (maybe False (level >=) minLevel) $ do
-- First, write out the formatted timestamp
now <- getTime
Log.pushLogStr loggerSet . Log.toLogStr $ now
-- Then write out the log level, formatted to all capital letters
let levelStr = fmap toUpper . show $ level
levelPad = replicate (6 - length levelStr) ' '
Log.pushLogStr loggerSet . Log.toLogStr $ levelPad
Log.pushLogStr loggerSet . logColor color level . PP.text $ levelStr
Log.pushLogStr loggerSet " | "
-- Then write the message, followed by a new line
Log.pushLogStrLn loggerSet . Log.toLogStr $ msg
logColor :: Bool -> Level -> PP.Doc -> Log.LogStr
logColor True Fatal = docToLogStr . PP.bold . PP.white . PP.onred
logColor True Error = docToLogStr . PP.bold .
logColor True Warn = docToLogStr . PP.bold . PP.yellow
logColor True Info = docToLogStr . PP.bold .
logColor True Debug = docToLogStr . PP.bold .
logColor True Trace = docToLogStr . PP.magenta
logColor False _ = docToLogStr
docToLogStr :: PP.Doc -> Log.LogStr
docToLogStr = Log.toLogStr . flip PP.displayS "" . PP.renderPretty 1.0 maxBound
trace, debug, info, warn, error, fatal
:: forall msg m . (MonadIO m, Log.ToLogStr msg) => msg -> m ()
trace = liftIO . log Trace
{-# INLINE trace #-}
{-# SPECIALIZE INLINE trace :: Log.ToLogStr msg => msg -> IO () #-}
debug = liftIO . log Debug
{-# INLINE debug #-}
{-# SPECIALIZE INLINE debug :: Log.ToLogStr msg => msg -> IO () #-}
info = liftIO . log Info
{-# INLINE info #-}
{-# SPECIALIZE INLINE info :: Log.ToLogStr msg => msg -> IO () #-}
warn = liftIO . log Warn
{-# INLINE warn #-}
{-# SPECIALIZE INLINE warn :: Log.ToLogStr msg => msg -> IO () #-}
error = liftIO . log Error
{-# INLINE error #-}
{-# SPECIALIZE INLINE error :: Log.ToLogStr msg => msg -> IO () #-}
fatal = liftIO . log Fatal
{-# INLINE fatal #-}
{-# SPECIALIZE INLINE fatal :: Log.ToLogStr msg => msg -> IO () #-}


@ -0,0 +1,23 @@
name: chronicle
synopsis: A wrapper around fast-logger
author: James Alexander Feldman-Crough
copyright: Copyright 2020 to James Alexander Feldman-Crough
category: System
cabal-version: >=1.10
license: MPL-2.0
license-file: LICENSE
build-type: Simple
default-language: Haskell2010
hs-source-dirs: .
ghc-options: -Wall
build-depends: base >=4.12 && <4.14
, ansi-wl-pprint >=0.6 && <0.7
, ansi-terminal >=0.9 && <0.11
, fast-logger >=2.4 && <3.1
exposed-modules: System.Log.Chronicle


@ -0,0 +1,24 @@
module Hakyll.Contrib.Prosidy
( module Prosidy
, prosidyCompiler
import Prosidy
import Hakyll
import Data.Text ( Text )
import qualified Data.Text.Lazy as Text.Lazy
import Data.Text.Lazy.Encoding ( decodeUtf8 )
import Control.Monad.Error.Class ( liftEither )
import Data.Bifunctor ( first )
prosidyCompiler :: Compiler (Item Document)
prosidyCompiler = do
filePath <- getResourceFilePath
Item iid src <- getResourceText
doc <- liftEither . first (pure . show) $ parseDocument filePath src
pure $ Item iid doc
getResourceText :: Compiler (Item Text)
getResourceText = fmap (Text.Lazy.toStrict . decodeUtf8) <$> getResourceLBS


@ -0,0 +1 @@


@ -0,0 +1,21 @@
cabal-version: >=1.10
name: hakyll-contrib-prosidy
synopsis: Hakyll compiler handling Prosidy files
license: MPL-2.0
license-file: LICENSE
author: James Alexander Feldman-Crough
copyright: Copyright 2020 to James Alexander Feldman-Crough
build-type: Simple
default-language: Haskell2010
hs-source-dirs: .
exposed-modules: Hakyll.Contrib.Prosidy
build-depends: base >=4.7 && <5
, prosidy
, hakyll >=4.13 && <4.14
, bytestring >=0.10 && <0.11
, mtl >=2.2 && <2.3
, text >=1.2 && <1.3


@ -0,0 +1 @@


@ -0,0 +1,22 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
module Main where
import Prosidy
import Control.Lens
import System.Environment ( getArgs )
import qualified Data.Text.IO as Text.IO
main :: IO ()
main = do
args <- getArgs
let [_, input, output] = filter ((/= "-") . take 1) args
doc <- readDocument input
^. cosmosOnOf (content . folded) (_BlockTag . content . folded)
. _BlockLiteral
. filtered (has $ tag . only [keyQ|haskell|])
. content
. coerced
. to (<> "\n")
& Text.IO.writeFile output


@ -0,0 +1,20 @@
name: propp
license: MPL-2.0
license-file: LICENSE
author: James Alexander Feldman-Crough
copyright: Copyright 2020 to James Alexander Feldman-Crough
category: Language
build-type: Simple
cabal-version: >=1.10
synopsis: Prosidy pre-processor for literate source code.
executable propp
hs-source-dirs: .
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5
, prosidy
, lens >=4.17 && <4.18
, text >=1.2 && <1.3


@ -0,0 +1,42 @@
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Prosidy ( readDocument )
import Prosidy.Manual ( compileDocument )
import Hakyll
import Hakyll.Contrib.Prosidy
import Data.Foldable ( foldlM
, for_
import System.Exit ( exitFailure )
import System.FilePath ( (</>)
, (-<.>)
import Text.Blaze.Html.Renderer.Utf8 ( renderHtml )
import Data.ByteString.Lazy as LBS
import qualified Data.Text.IO as Text.IO
import qualified System.IO as IO
import qualified System.FilePath as Path
import qualified System.Directory as Dir
import Control.Monad.Except (throwError)
main :: IO ()
main = hakyll' $ do
match "*.pro" $ do
route (setExtension "html")
compile $
prosidyCompiler >>=
withItemBody compileDocumentIO
hakyll' :: Rules a -> IO ()
hakyll' = hakyllWith $ defaultConfiguration
{ providerDirectory = "./doc"
compileDocumentIO :: Document -> Compiler LBS.ByteString
compileDocumentIO = either (throwError . (:[]) . show) (pure . renderHtml) . compileDocument


@ -0,0 +1,183 @@
## vi: ft=prosidy wrap
title: Manual.lhs
created: 2020-01-01T17:22-8000
updated: 2020-01-01T17:22-8000
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE TypeApplications #-}
module Prosidy.Manual
( compileDocument
, ManualError(..)
) where
import Prosidy
import Prosidy.Compile
import Prosidy.Manual.Monad
import Data.Bifunctor (Bifunctor(..))
import Data.Text (Text)
import Text.Blaze.Html5 ((!))
import Text.Read (readEither)
import Type.Reflection (Typeable)
import qualified Control.Lens as L
import qualified Data.Text as Text
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
type Html input = RuleT input Manual H.Html
Our rules for parsing are all contained inside of the #lit{document} rule. Prosidy requires that a rule must be registered before it can be used to prevent some issues with non-termination.
Because rules are mutually recursive, we have to use #lit{MonadFix} for this to work! #lit{RecursiveDo} is a wonderful extension that provides from syntactic sugar for this.
document :: Html Document
document = mdo
Let's start with the non-recursive rules.
For text nodes, we should just convert their content directly into HTML. The #lit{self} descriptor returns the focus of a rule directly.
plainText <- rule @Text @Manual @H.Html "plain text" $
fmap H.text self
Prosidy inserts empty markers called #def{breaks} between lines in a paragraph and before or after an inline tag (if it has a space on that side). Because #i{CJKV} languages don't really care about spacing, we don't assume the user wants a space in these situations.
The Prosidy manual, however, is in English, which #i{does} want spaces in these locations. When we encounter a break, we should render a single space.
break <- rule "break" $
pure $ H.text " "
block <- choose "block context"
[ _BlockParagraph @? paragraph
, _BlockTag @? blockTag
inline <- choose "inline context"
[ _Break @? break
, _InlineTag @? inlineTag
, _InlineText @? plainText
paragraph <- rule "paragraph" $ do
body <- descend inline $ _Paragraph . L.folded
pure $ H.p body
blockTag <- choose "block tag"
[ tagged [keyQ|section|] @? section
inlineTag <- choose "inline tag"
[ tagged [keyQ|b|] @? bold
, tagged [keyQ|i|] @? italics
With that out of the way, lets start defining custom elements for the manual.
#-section[title='Custom Markup']:
bold <- rule @InlineTag @Manual @H.Html "boldface" $ do
body <- descend inline $ content . L.folded
pure $ H.strong body
italics <- rule @InlineTag @Manual @H.Html "italics" $ do
body <- descend inline $ content . L.folded
pure $ H.em body
section <- rule @BlockTag @Manual @H.Html "section" $ do
sTitle <- reqText [keyQ|title|]
"The title of the section."
sSlug <- optText [keyQ|slug|]
"The anchor tag associated with the section.\
\ If not provided, one will be generated based on the title.\
\ Generally, providing a slug is better as it allows the section's\
\ name to be changed without breaking permalinks."
hTag <- embed headerTag
body <- descend (contextualize nestSection block) $
content . L.folded
pure $ do
let title = H.text sTitle
H.section $ do
H.h1 title
Finally, we wrap up all of the rules we previously defined into a final rule which processes the whole #lit{Document}:
rule "manual page" $ do
title <- reqText [keyQ|title|] "The manual page's title."
body <- descend (block :: Item Block Manual H.Html) $
content . L.folded
pure $ do
let htmlTitle = H.text title
H.html $ do
H.head $ do
H.meta ! A.charset "UTF-8"
H.title htmlTitle
H.body $ do
H.header $ do
H.h1 htmlTitle
H.main body
compileDocument :: Document -> Either ManualError H.Html
compileDocument doc =
runManual (compileM document doc) >>= first CompileError
tagged :: Key -> L.Prism' (Tagged a) (Tagged a)
tagged name = L.prism' id (\t -> if L.has (tag . L.only name) t
then Just t
else Nothing)
:: (Typeable output, Read output, HasMetadata input, Monad context)
=> Key -> Text -> Desc input context output
reqAuto = req $ \inputText ->
input = Text.unpack inputText
annotate msg = msg <> "(input: " <> show inputText <> ")"
first annotate $ readEither input
reqText :: (HasMetadata input, Monad context) => Key -> Text -> Desc input context Text
reqText = req Right
optText :: (HasMetadata input, Monad context) => Key -> Text -> Desc input context (Maybe Text)
optText = opt Right


@ -0,0 +1,117 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
module Prosidy.Manual.Monad
( Manual
, ManualError(..)
, runManual
, nestSection
, recordTerm
, headerTag
import Control.Applicative ( liftA2 )
import Control.Lens hiding ( setting )
import Control.Monad.Reader ( ReaderT(..)
, runReaderT
, asks
import Control.Monad.State.Strict ( StateT(..)
, runStateT
import Data.HashSet ( HashSet )
import Data.Text ( Text )
import GHC.Generics ( Generic )
import Data.Generics.Product ( field )
import Numeric.Natural ( Natural )
import Control.Monad.Fix ( MonadFix )
import Data.Functor.Identity ( Identity(..) )
import Prosidy.Compile ( Error )
import qualified Text.Blaze.Html5 as H
import qualified Data.Char as Char
import qualified Data.Text as Text
import qualified Data.HashSet as HashSet
newtype Manual a = Manual (ManualRead -> ManualState -> Either ManualError (a, ManualState))
deriving (Functor, Applicative, Monad, MonadFix)
via (ReaderT ManualRead (StateT ManualState (Either ManualError)))
instance Semigroup a => Semigroup (Manual a) where
(<>) = liftA2 (<>)
instance Monoid a => Monoid (Manual a) where
mempty = pure mempty
data ManualRead = ManualRead
{ sectionDepth :: Natural
deriving (Eq, Generic, Show)
data ManualState = ManualState
{ definedTerms :: HashSet Text
, referencedTerms :: HashSet Text
deriving (Eq, Generic, Show)
data ManualError =
CompileError Error
| TooDeep
| UndefinedTerms (HashSet Text)
deriving (Eq, Show)
:: ReaderT ManualRead (StateT ManualState (Either ManualError)) a
-> Manual a
manualM m = Manual $ runStateT . runReaderT m
headerTag :: Manual (H.Html -> H.Html)
headerTag = manualM $ do
depth <- asks sectionDepth
case depth of
0 -> pure H.h2
1 -> pure H.h3
2 -> pure H.h4
3 -> pure H.h5
4 -> pure H.h6
_ -> ReaderT . const $ StateT . const $ Left TooDeep
nestSection :: Manual a -> Manual a
nestSection (Manual m) = Manual $ \r -> m $ over (field @"sectionDepth") succ r
runManual :: Manual a -> Either ManualError a
runManual (Manual m) = do
(output, state) <- m (ManualRead 0) (ManualState mempty mempty)
let undefinedTerms =
referencedTerms state `HashSet.difference` definedTerms state
if null undefinedTerms
then Right output
else Left $ UndefinedTerms undefinedTerms
-- | Record the use of a glossary term. Returns a sanitized identifier.
:: Bool -- ^ Is this term the definition site that should be referenced by other occurances of the term?
-> Text -- ^ This is the term text itself.
-> Manual Text
recordTerm isDef term = manualM $ do
selector . contains identifier .= True
pure identifier
identifier = toIdentifier term
selector | isDef = field @"definedTerms"
| otherwise = field @"referencedTerms"
-- | Convert text into an html-friendly identifier by replacing all
-- non-alphanumeric characters with hyphens and lower-casing the string. This
-- is less-permissive than HTML5's spec, but makes things a bit more readable.
toIdentifier :: Text -> Text
toIdentifier =
Text.intercalate "-"
. filter (not . Text.null)
. Text.split (not . Char.isAlphaNum)
. Text.toLower


@ -0,0 +1,44 @@
name: prosidy-manual
synopsis: A Prosidy dialect for Prosidy's own manual
author: James Alexander Feldman-Crough
copyright: Copyright 2020 to James Alexander Feldman-Crough
category: Language
cabal-version: >=1.10
license: MPL-2.0
license-file: LICENSE
build-type: Simple
executable prosidy-manual
hs-source-dirs: .
default-language: Haskell2010
ghc-options: -pgmL propp
main-is: Main.hs
other-modules: Prosidy.Manual
, Prosidy.Manual.Monad
build-tool-depends: propp:propp
build-depends: base >= 4.7 && < 5
, prosidy
, prosidyc
, hakyll-contrib-prosidy
, hakyll
, mtl
, text
, lens
, blaze-markup
, blaze-html
, unordered-containers
, containers
, generic-lens
, directory
, filepath
, bytestring


@ -0,0 +1,230 @@
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StrictData #-}
module Main (main) where
import Prosidy
import Prosidy.Compile
import Options.Applicative
import Data.Text (Text)
import Control.Exception (throwIO, bracket)
import Text.Blaze.Html.Renderer.Text (renderHtml)
import Data.Text.Lazy (toStrict)
import Text.Blaze.Html5 ((!))
import qualified Text.Blaze.Internal as Blaze
import qualified System.IO as IO
import qualified Data.Text.IO as Text.IO
import qualified Control.Lens as L
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
main :: IO ()
main = do
opts@Opts{..} <- getOpts
input <- withFile' IO.stdin inputFile IO.ReadMode Text.IO.hGetContents
document <- either throwIO pure $ parseDocument (maybe "<stdin>" id inputFile) input
html <- either (fail . show) (pure . toStrict . renderHtml) $
compile (compiler standalone breakUsing) document
withFile' IO.stdout outputFile IO.WriteMode (`Text.IO.hPutStrLn` html)
withFile' :: IO.Handle -> Maybe FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a
withFile' hdl path mode = bracket
(maybe (pure hdl) (`IO.openFile` mode) path)
compiler :: Bool -> Text -> Rule Document H.Html
compiler standalone space = mdo
break <- rule "break" $
pure $
H.text space
text <- rule "plain text" $ do
body <- self
pure $ H.text body
literalText <- rule "literal text" $ do
body <- descend text _Literal
pure $ H.code body
block <- choose "block context"
[ _BlockTag @? blockTag
, _BlockLiteral @? codeBlock
, _BlockParagraph @? paragraph
inline <- choose "inline context"
[ _Break @? break
, _InlineTag @? inlineTag
, _InlineText @? text
blockTag <- choose "block tag"
[ _Tagged [keyQ|h|] @? heading
, _Tagged [keyQ|h+|] @? subheading
, _Tagged [keyQ|h++|] @? subsubheading
, _Tagged [keyQ|list|] @? list
, _Tagged [keyQ|section|] @? section
, _Tagged [keyQ|quote|] @? quote
, _Tagged [keyQ|image|] @? blockImage
finalBlock <- choose "final block tag"
[ _BlockParagraph @? paragraph
inlineTag <- choose "inline tag"
[ _Tagged [keyQ|b|] @? bold
, _Tagged [keyQ|i|] @? italic
, _Tagged [keyQ|image|] @? image
, _Tagged [keyQ|link|] @? link
, _Tagged [keyQ|lit|] @? lit
codeBlock <- rule "codeBlock" $ do
body <- descend literalText content
pure $ H.pre body
paragraph <- rule "paragraph" $ do
body <- descend inline (_Paragraph . L.folded)
pure $ H.p body
heading <- rule "heading" $ do
body <- descend finalBlock (content . L.folded)
pure $ H.h2 body
subheading <- rule "subheading" $ do
body <- descend finalBlock (content . L.folded)
pure $ H.h3 body
subsubheading <- rule "subsubheading" $ do
body <- descend finalBlock (content . L.folded)
pure $ H.h4 body
image <- rule "image" $ do
url <- req Right [keyQ|url|] "The URL of the image to embed."
title <- opt Right [keyQ|title|] "The title/alt-text of the image."
pure $ do
H.img ! A.src (H.toValue url)
! foldMap (A.title . H.toValue) title
blockImage <- rule "blockImage" $ do
url <- req Right [keyQ|url|] "The URL of the image to embed."
title <- opt Right [keyQ|title|] "The title/alt-text of the image."
body <- descend finalBlock $ content . L.folded
pure $ do
H.figure $ do
H.img ! A.src (H.toValue url)
! foldMap (A.title . H.toValue) title
H.figcaption body
bold <- rule "bold" $ do
body <- descend inline (content . L.folded)
pure $ H.strong body
italic <- rule "italic" $ do
body <- descend inline (content . L.folded)
pure $ H.em body
link <- rule "link" $ do
url <- req Right [keyQ|url|] "The URL to link to."
body <- descend inline $ content . L.folded
pure $ H.a body ! A.href (H.toValue url)
lit <- rule "literal" $ do
body <- descend inline $ content . L.folded
pure $ H.code body
listItem <- choose "list item"
[ _BlockTag . _Tagged [keyQ|item|] @? listBody ]
listBody <- rule "list body" $ do
body <- descend block (content . L.folded)
pure $ body
list <- rule "list" $ do
isOrdered <- prop [keyQ|ord|] "If provided, treat the list as a ordered-list."
body <- descend listItem (content . L.folded)
pure $ do
(if isOrdered then H.ol else H.ul) body
quote <- rule "quote" $ do
body <- descend block (content . L.folded)
pure $ H.blockquote body
section <- rule "section" $ do
class_ <- opt Right [keyQ|class|] "Optional classes to be attached."
body <- descend block (content . L.folded)
pure $ H.section body ! foldMap (A.class_ . H.toValue) class_
rule "document" $ do
title <- req Right [keyQ|title|] "The document's title, used as the header."
style <- if standalone
then opt Right [keyQ|style|] "A stylesheet to attach to the document"
else pure Nothing
body <- descend block (content . L.folded)
pure $ do
let titleHtml = H.text title
if standalone
H.html $ do
H.head $ do
H.title titleHtml
foldMap (\s -> ! A.rel "stylesheet"
! A.type_ "text/css"
! A.href (H.toValue s)) style
H.body $ do
H.header $ H.h1 titleHtml
H.main body
else do
H.h1 titleHtml
data Opts = Opts
{ breakUsing :: Text
, inputFile :: Maybe FilePath
, outputFile :: Maybe FilePath
, standalone :: Bool
deriving Show
getOpts = execParser $ info optParse optInfo
optParse = do
breakUsing <- strOption $ mconcat
[ help
"Replace newlines and white-space on either side of tags\
\ with this sequence."
, long "break", short 'b'
, metavar "STR"
, value " "
, showDefault
inputFile <- optional . strOption $ mconcat
[ help
"Read a Prosidy Markup document from this filepath.\
\ If not provided, standard-input will be used."
, long "in", short 'i'
, metavar "FILEPATH"
outputFile <- optional . strOption $ mconcat
[ help
"Write the output HTML to this filepath.\
\ If not provided, standard-output will be used."
, long "out", short 'o'
, metavar "FILEPATH"
standalone <- switch $ mconcat
[ help "When provided, generate the full HTML document."
, long "standalone", short 's'
pure Opts{..}
optInfo = mconcat


@ -0,0 +1,25 @@
name: prosidy-markup
synopsis: A minimal Prosidy dialect meant for Markdown-style uses.
author: James Alexander Feldman-Crough
copyright: Copyright 2020 to James Alexander Feldman-Crough
category: Language
cabal-version: >=1.10
license: MPL-2.0
license-file: LICENSE
build-type: Simple
executable prosidy-markup
hs-source-dirs: .
default-language: Haskell2010
ghc-options: -pgmL propp
main-is: Main.hs
build-depends: base >= 4.7 && < 5
, prosidy
, prosidyc
, blaze-html
, blaze-markup
, text
, lens
, optparse-applicative


@ -0,0 +1,225 @@
title: Syntax Guide
style: style.css
#-h{This will be rendered as an h2 tag.}
#-h+{This will be rendered as an h3 tag.}
#-h++{This will be rendered as an h4 tag.}
#-h{This will be rendered as an h2 tag.}
#-h+{This will be rendered as an h3 tag.}
#-h++{This will be rendered as an h4 tag.}
#i{This text will be italic,} #b{and this text will be bold.}
You #i{#b{can}} combine them if you like.