diff --git a/app/Commands/Html.hs b/app/Commands/Html.hs
index b94a49d1bd..fe8e739c2e 100644
--- a/app/Commands/Html.hs
+++ b/app/Commands/Html.hs
@@ -1,7 +1,7 @@
module Commands.Html where
import Juvix.Prelude hiding (Doc)
-import Juvix.Syntax.Concrete.Scoped.Pretty.Html
+import Juvix.Syntax.Concrete.Scoped.Pretty.Html hiding (HtmlOptions)
import Options.Applicative
data HtmlOptions = HtmlOptions
diff --git a/app/Commands/Internal.hs b/app/Commands/Internal.hs
index c568e2e8e5..2c177a22f6 100644
--- a/app/Commands/Internal.hs
+++ b/app/Commands/Internal.hs
@@ -3,10 +3,12 @@ module Commands.Internal
module Commands.Internal.MicroJuvix,
module Commands.Internal.Parse,
module Commands.Internal.Scope,
+ module Commands.Internal.Doc,
module Commands.Internal.Termination,
)
where
+import Commands.Internal.Doc
import Commands.Internal.MicroJuvix
import Commands.Internal.Parse
import Commands.Internal.Scope
@@ -24,6 +26,7 @@ data InternalCommand
| Parse ParseOptions
| Scope ScopeOptions
| Termination TerminationCommand
+ | Doc DocOptions
parseInternalCommand :: Parser InternalCommand
parseInternalCommand =
@@ -35,12 +38,20 @@ parseInternalCommand =
commandMiniHaskell,
commandMonoJuvix,
commandParse,
+ commandDoc,
commandScope,
commandShowRoot,
commandTermination
]
)
+commandDoc :: Mod CommandFields InternalCommand
+commandDoc =
+ command "doc" $
+ info
+ (Doc <$> parseDoc)
+ (progDesc "Generate documentation")
+
commandHighlight :: Mod CommandFields InternalCommand
commandHighlight =
command "highlight" $
diff --git a/app/Commands/Internal/Doc.hs b/app/Commands/Internal/Doc.hs
new file mode 100644
index 0000000000..a2eb19f10e
--- /dev/null
+++ b/app/Commands/Internal/Doc.hs
@@ -0,0 +1,30 @@
+module Commands.Internal.Doc where
+
+import Juvix.Prelude hiding (Doc)
+import Options.Applicative
+
+data DocOptions = DocOptions
+ { _docOutputDir :: FilePath,
+ _docOpen :: Bool
+ }
+
+makeLenses ''DocOptions
+
+parseDoc :: Parser DocOptions
+parseDoc = do
+ _docOutputDir <-
+ option
+ str
+ ( long "output-dir"
+ <> metavar "DIR"
+ <> value "doc"
+ <> showDefault
+ <> help "html output directory"
+ <> action "directory"
+ )
+ _docOpen <-
+ switch
+ ( long "open"
+ <> help "open the documentation after generating it"
+ )
+ pure DocOptions {..}
diff --git a/app/Main.hs b/app/Main.hs
index e161999fb4..2f5052f840 100644
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -9,6 +9,7 @@ import Data.HashMap.Strict qualified as HashMap
import Juvix.Analysis.Scoping.Scoper qualified as Scoper
import Juvix.Analysis.Termination qualified as Termination
import Juvix.Analysis.TypeChecking qualified as MicroTyped
+import Juvix.Documentation.Compiler qualified as Doc
import Juvix.Parsing.Parser qualified as Parser
import Juvix.Pipeline
import Juvix.Prelude hiding (Doc)
@@ -19,7 +20,7 @@ import Juvix.Syntax.Abstract.Pretty qualified as Abstract
import Juvix.Syntax.Concrete.Scoped.Highlight qualified as Highlight
import Juvix.Syntax.Concrete.Scoped.InfoTable qualified as Scoper
import Juvix.Syntax.Concrete.Scoped.Pretty qualified as Scoper
-import Juvix.Syntax.Concrete.Scoped.Pretty.Html
+import Juvix.Syntax.Concrete.Scoped.Pretty.Html qualified as Html
import Juvix.Syntax.MicroJuvix.MicroJuvixArityResult qualified as MicroArity
import Juvix.Syntax.MicroJuvix.Pretty qualified as Micro
import Juvix.Syntax.MiniHaskell.Pretty qualified as MiniHaskell
@@ -32,6 +33,7 @@ import Juvix.Translation.ScopedToAbstract qualified as Abstract
import Juvix.Utils.Version (runDisplayVersion)
import Options.Applicative
import System.Environment (getProgName)
+import System.Process qualified as Process
import Text.Show.Pretty hiding (Html)
juvixYamlFile :: FilePath
@@ -106,7 +108,7 @@ runCommand cmdWithOpts = do
Html HtmlOptions {..} -> do
res <- runPipeline (upToScoping entryPoint)
let m = head (res ^. Scoper.resultModules)
- embed (genHtml Scoper.defaultOptions _htmlRecursive _htmlTheme _htmlOutputDir _htmlPrintMetadata m)
+ embed (Html.genHtml Scoper.defaultOptions _htmlRecursive _htmlTheme _htmlOutputDir _htmlPrintMetadata m)
(Internal cmd') -> case cmd' of
Highlight -> do
res <- runPipelineEither (upToScoping entryPoint)
@@ -137,6 +139,14 @@ runCommand cmdWithOpts = do
(upToScoping entryPoint)
forM_ l $ \s -> do
renderStdOut (Scoper.ppOut (mkScopePrettyOptions globalOpts localOpts) s)
+ Doc localOpts -> do
+ l <-
+ (^. Scoper.mainModule)
+ <$> runPipeline
+ (upToScoping entryPoint)
+ let docDir = localOpts ^. docOutputDir
+ Doc.compileModuleHtmlText docDir "proj" l
+ embed (when (localOpts ^. docOpen) (Process.callProcess "xdg-open" [docDir > Doc.indexFileName]))
MicroJuvix Pretty -> do
micro <-
head . (^. Micro.resultModules)
diff --git a/assets/linuwial.css b/assets/linuwial.css
new file mode 100644
index 0000000000..127d3dd6f2
--- /dev/null
+++ b/assets/linuwial.css
@@ -0,0 +1,883 @@
+@import "extra.css";
+
+/* @group Fundamentals */
+
+* { margin: 0; padding: 0 }
+
+/* Is this portable? */
+html {
+ background-color: white;
+ width: 100%;
+ height: 100%;
+}
+
+body {
+ background: #fefefe;
+ color: #111;
+ text-align: left;
+ min-height: 100vh;
+ position: relative;
+ -webkit-text-size-adjust: 100%;
+ -webkit-font-feature-settings: "kern" 1, "liga" 0;
+ -moz-font-feature-settings: "kern" 1, "liga" 0;
+ -o-font-feature-settings: "kern" 1, "liga" 0;
+ font-feature-settings: "kern" 1, "liga" 0;
+ letter-spacing: 0.0015rem;
+}
+
+#content a {
+ overflow-wrap: break-word;
+}
+
+p {
+ margin: 0.8em 0;
+}
+
+ul, ol {
+ margin: 0.8em 0 0.8em 2em;
+}
+
+dl {
+ margin: 0.8em 0;
+}
+
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-left: 2em;
+}
+
+a { text-decoration: none; }
+a[href]:link { color: #D54E54; }
+a[href]:visited {color: #802e31; }
+a[href]:hover { text-decoration:underline; }
+
+a[href].def:link, a[href].def:visited { color: rgba(69, 59, 97, 0.8); }
+a[href].def:hover { color: rgb(78, 98, 114); }
+
+/* @end */
+
+/* @group Show and hide with JS */
+
+body.js-enabled .hide-when-js-enabled {
+ display: none;
+}
+
+/* @end */
+
+
+/* @group responsive */
+
+#package-header .caption {
+ margin: 0px 1em 0 2em;
+}
+
+@media only screen and (min-width: 1280px) {
+ #content {
+ width: 63vw;
+ max-width: 1450px;
+ }
+
+ #table-of-contents {
+ position: fixed;
+ max-width: 10vw;
+ top: 10.2em;
+ left: 2em;
+ bottom: 1em;
+ overflow-y: auto;
+ }
+
+ #synopsis {
+ display: block;
+ position: fixed;
+ float: left;
+ top: 5em;
+ bottom: 1em;
+ right: 0;
+ max-width: 65vw;
+ overflow-y: auto;
+ /* Ensure that synopsis covers everything (including MathJAX markup) */
+ z-index: 1;
+ }
+
+ #synopsis .show {
+ border: 1px solid #5E5184;
+ padding: 0.7em;
+ max-height: 65vh;
+ }
+
+}
+
+@media only screen and (max-width: 1279px) {
+ #content {
+ width: 80vw;
+ }
+
+ #synopsis {
+ display: block;
+ padding: 0;
+ position: relative;
+ margin: 0;
+ width: 100%;
+ }
+}
+
+@media only screen and (max-width: 999px) {
+ #content {
+ width: 93vw;
+ }
+}
+
+
+/* menu for wider screens
+
+ Display the package name at the left and the menu links at the right,
+ inline with each other:
+ The package name Source . Contents . Index
+*/
+@media only screen and (min-width: 1000px) {
+ #package-header {
+ text-align: left;
+ white-space: nowrap;
+ height: 40px;
+ padding: 4px 1.5em 0px 1.5em;
+ overflow: visible;
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ #package-header .caption {
+ display: inline-block;
+ margin: 0;
+ }
+
+ #package-header ul.links {
+ margin: 0;
+ display: inline-table;
+ }
+
+ #package-header .caption + ul.links {
+ margin-left: 1em;
+ }
+}
+
+/* menu for smaller screens
+
+Display the package name on top of the menu links and center both elements:
+ The package name
+ Source . Contents . Index
+*/
+@media only screen and (max-width: 999px) {
+ #package-header {
+ text-align: center;
+ padding: 6px 0 4px 0;
+ overflow: hidden;
+ }
+
+ #package-header ul.links {
+ display: block;
+ text-align: center;
+ margin: 0;
+
+ /* Hide scrollbar but allow scrolling menu links horizontally */
+ white-space: nowrap;
+ overflow-x: auto;
+ overflow-y: hidden;
+ margin-bottom: -17px;
+ height: 50px;
+ }
+
+ #package-header .caption {
+ display: block;
+ margin: 4px 0;
+ text-align: center;
+ }
+
+ #package-header ul.links::-webkit-scrollbar {
+ display: none;
+ }
+
+ #package-header ul.links li:first-of-type {
+ padding-left: 1em;
+ }
+
+ #package-header ul.links li:last-of-type {
+ /*
+ The last link of the menu should offer the same distance to the right
+ as the #package-header enforces at the left.
+ */
+ padding-right: 1em;
+ }
+
+ #package-header .caption + ul.links {
+ padding-top: 9px;
+ }
+
+ #module-header table.info {
+ float: none;
+ top: 0;
+ margin: 0 auto;
+ overflow: hidden;
+ max-width: 80vw;
+ }
+}
+
+/* @end */
+
+
+/* @group Fonts & Sizes */
+
+/* Basic technique & IE workarounds from YUI 3
+ For reasons, see:
+ http://yui.yahooapis.com/3.1.1/build/cssfonts/fonts.css
+ */
+
+ body, button {
+ font: 400 14px/1.4 'PT Sans',
+ /* Fallback Font Stack */
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ Oxygen-Sans,
+ Cantarell,
+ 'Helvetica Neue',
+ sans-serif;
+ *font-size: medium; /* for IE */
+ *font:x-small; /* for IE in quirks mode */
+ }
+
+h1 { font-size: 146.5%; /* 19pt */ }
+h2 { font-size: 131%; /* 17pt */ }
+h3 { font-size: 116%; /* 15pt */ }
+h4 { font-size: 100%; /* 13pt */ }
+h5 { font-size: 100%; /* 13pt */ }
+
+table {
+ font-size:inherit;
+ font:100%;
+}
+
+pre, code, kbd, samp, tt, .src {
+ font-family:monospace;
+}
+
+.links, .link {
+ font-size: 85%; /* 11pt */
+}
+
+#module-header .caption {
+ font-size: 182%; /* 24pt */
+}
+
+#module-header .caption sup {
+ font-size: 80%;
+ font-weight: normal;
+}
+
+#package-header #page-menu a:link, #package-header #page-menu a:visited { color: white; }
+
+
+.info {
+ font-size: 90%;
+}
+
+
+/* @end */
+
+/* @group Common */
+
+.caption, h1, h2, h3, h4, h5, h6, summary {
+ font-weight: bold;
+ color: #D84E53;
+ margin: 1.5em 0 1em 0;
+}
+
+
+* + h1, * + h2, * + h3, * + h4, * + h5, * + h6 {
+ margin-top: 2em;
+}
+
+h1 + h2, h2 + h3, h3 + h4, h4 + h5, h5 + h6 {
+ margin-top: inherit;
+}
+
+ul li + li {
+ margin-top: 0.2rem;
+}
+
+ul + p {
+ margin-top: 0.93em;
+}
+
+p + ul {
+ margin-top: 0.5em;
+}
+
+p {
+ margin-top: 0.7rem;
+}
+
+ul, ol {
+ margin: 0.8em 0 0.8em 2em;
+}
+
+ul.links {
+ list-style: none;
+ text-align: left;
+ font-size: 0.95em;
+}
+
+#package-header ul.links, #package-header ul.links button {
+ font-size: 1rem;
+}
+
+ul.links li {
+ display: inline;
+ white-space: nowrap;
+ padding: 0;
+}
+
+ul.links > li + li:before {
+ content: '\00B7';
+}
+
+ul.links li a {
+ padding: 0.2em 0.5em;
+}
+
+.hide { display: none; }
+.show { display: inherit; }
+.clear { clear: both; }
+
+.collapser:before, .expander:before, .noexpander:before {
+ font-size: 1.2em;
+ color: #9C5791;
+ display: inline-block;
+ padding-right: 7px;
+}
+
+.collapser:before {
+ content: '▿';
+}
+.expander:before {
+ content: '▹';
+}
+.noexpander:before {
+ content: '▿';
+ visibility: hidden;
+}
+
+.collapser, .expander {
+ cursor: pointer;
+}
+
+.instance.collapser, .instance.expander {
+ margin-left: 0px;
+ background-position: left center;
+ min-width: 9px;
+ min-height: 9px;
+}
+
+summary {
+ cursor: pointer;
+ outline: none;
+}
+
+pre {
+ padding: 0.5rem 1rem;
+ margin: 1em 0 0 0;
+ background-color: #f7f7f7;
+ overflow: auto;
+ border: 1px solid #ddd;
+ border-radius: 0.3em;
+}
+
+pre + p {
+ margin-top: 1em;
+}
+
+pre + pre {
+ margin-top: 0.5em;
+}
+
+blockquote {
+ border-left: 3px solid #c7a5d3;
+ background-color: #eee4f1;
+ margin: 0.5em;
+ padding: 0.0005em 0.3em 0.5em 0.5em;
+}
+
+.src {
+ background: #f2f2f2;
+ padding: 0.2em 0.5em;
+}
+
+.keyword { font-weight: normal; }
+.def { font-weight: bold; }
+
+@media print {
+ #footer { display: none; }
+}
+
+/* @end */
+
+/* @group Page Structure */
+
+#content {
+ margin: 3em auto 6em auto;
+ padding: 0;
+}
+
+#package-header {
+ background: #803353;
+ border-bottom: 5px solid rgba(69, 59, 97, 0.5);
+ color: #ddd;
+ position: relative;
+ font-size: 1.2em;
+ text-align: left;
+ margin: 0 auto;
+}
+
+#package-header .caption {
+ color: white;
+ font-style: normal;
+ font-size: 1rem;
+ font-weight: bold;
+}
+
+#module-header .caption {
+ font-weight: bold;
+ border-bottom: 1px solid #ddd;
+}
+
+table.info {
+ float: right;
+ padding: 0.5em 1em;
+ border: 1px solid #ddd;
+ color: rgb(78,98,114);
+ background-color: #fff;
+ max-width: 60%;
+ border-spacing: 0;
+ position: relative;
+ top: -0.78em;
+ margin: 0 0 0 2em;
+}
+
+.info th {
+ padding: 0 1em 0 0;
+ text-align: right;
+}
+
+#style-menu li {
+ display: block;
+ border-style: none;
+ list-style-type: none;
+}
+
+#footer {
+ background: #ededed;
+ border-top: 1px solid #aaa;
+ padding: 0.5em 0;
+ color: #222;
+ text-align: center;
+ width: 100%;
+ height: 7em;
+ margin-top: 3em;
+ position: relative;
+ clear: both;
+}
+
+#footer #tara {
+ width: 4em;
+}
+
+/* @end */
+
+/* @group Front Matter */
+
+#synopsis .caption,
+#contents-list .caption {
+ font-size: 1rem;
+}
+
+#synopsis, #table-of-contents {
+ font-size: 16px;
+}
+
+#contents-list {
+ background: #f4f4f4;
+ padding: 1em;
+ margin: 0;
+}
+
+#contents-list .caption {
+ text-align: left;
+ margin: 0;
+}
+
+#contents-list ul {
+ list-style: none;
+ margin: 0;
+ margin-top: 10px;
+ font-size: 14px;
+}
+
+#contents-list ul ul {
+ margin-left: 1.5em;
+}
+
+#description .caption {
+ display: none;
+}
+
+#synopsis summary {
+ display: block;
+ float: right;
+ width: 29px;
+ color: rgba(255,255,255,0);
+ height: 110px;
+ margin: 0;
+ font-size: 1px;
+ padding: 0;
+ background: url(synopsis.png) no-repeat 0px -8px;
+}
+
+#synopsis details[open] > summary {
+ background: url(synopsis.png) no-repeat -75px -8px;
+}
+
+#synopsis ul {
+ height: 100%;
+ overflow: auto;
+ padding: 0.5em;
+ margin: 0;
+}
+
+#synopsis ul ul {
+ overflow: hidden;
+}
+
+#synopsis ul,
+#synopsis ul li.src {
+ background-color: rgb(250,247,224);
+ white-space: nowrap;
+ list-style: none;
+ margin-left: 0;
+}
+
+#interface td.src {
+ white-space: nowrap;
+}
+
+/* @end */
+
+/* @group Main Content */
+
+#interface div.top + div.top {
+ margin-top: 1.5em;
+}
+
+#interface p + div.top,
+#interface h1 + div.top,
+#interface h2 + div.top,
+#interface h3 + div.top,
+#interface h4 + div.top,
+#interface h5 + div.top {
+ margin-top: 1em;
+}
+#interface .src .selflink,
+#interface .src .link {
+ float: right;
+ color: #888;
+ padding: 0 7px;
+ -moz-user-select: none;
+ font-weight: bold;
+ line-height: 30px;
+}
+#interface .src .selflink {
+ margin: 0 -0.5em 0 0.5em;
+}
+
+#interface span.fixity {
+ color: #919191;
+ border-left: 1px solid #919191;
+ padding: 0.2em 0.5em 0.2em 0.5em;
+ margin: 0 -1em 0 1em;
+}
+
+#interface span.rightedge {
+ border-left: 1px solid #919191;
+ padding: 0.2em 0 0.2em 0;
+ margin: 0 0 0 1em;
+}
+
+#interface table { border-spacing: 2px; }
+#interface td {
+ vertical-align: top;
+ padding-left: 0.5em;
+}
+
+#interface td.doc p {
+ margin: 0;
+}
+#interface td.doc p + p {
+ margin-top: 0.8em;
+}
+
+.doc table {
+ border-collapse: collapse;
+ border-spacing: 0px;
+}
+
+.doc th,
+.doc td {
+ padding: 5px;
+ border: 1px solid #ddd;
+}
+
+.doc th {
+ background-color: #f0f0f0;
+}
+
+.clearfix:after {
+ clear: both;
+ content: " ";
+ display: block;
+ height: 0;
+ visibility: hidden;
+}
+
+.subs, .top > .doc, .subs > .doc {
+ padding-left: 1em;
+ border-left: 1px solid gainsboro;
+ margin-bottom: 1em;
+}
+
+.top .subs {
+ margin-bottom: 0.6em;
+}
+
+.subs.fields ul {
+ list-style: none;
+ display: table;
+ margin: 0;
+}
+
+.subs.fields ul li {
+ display: table-row;
+}
+
+.subs ul li dfn {
+ display: table-cell;
+ font-style: normal;
+ font-weight: bold;
+ margin: 1px 0;
+ white-space: nowrap;
+}
+
+.subs ul li > .doc {
+ display: table-cell;
+ padding-left: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+.subs ul li > .doc p {
+ margin: 0;
+}
+
+.subs .subs p.src {
+ border: none;
+ background-color: #f8f8f8;
+}
+
+.subs .subs .caption {
+ margin-top: 1em ;
+ margin-bottom: 0px;
+}
+
+.subs p.caption {
+ margin-top: 0;
+}
+
+.subs .subs .caption + .src {
+ margin: 0px;
+ margin-top: 8px;
+}
+
+.subs .subs .src + .src {
+ margin: 7px 0 0 0;
+}
+
+/* Render short-style data instances */
+.inst ul {
+ height: 100%;
+ padding: 0.5em;
+ margin: 0;
+}
+
+.inst, .inst li {
+ list-style: none;
+ margin-left: 1em;
+}
+
+/* Workaround for bug in Firefox (issue #384) */
+.inst-left {
+ float: left;
+}
+
+.top p.src {
+ border-bottom: 3px solid #e5e5e5;
+ line-height: 2rem;
+ margin-bottom: 1em;
+}
+
+.warning {
+ color: red;
+}
+
+.arguments {
+ margin-top: -0.4em;
+}
+.arguments .caption {
+ display: none;
+}
+
+.fields { padding-left: 1em; }
+
+.fields .caption { display: none; }
+
+.fields p { margin: 0 0; }
+
+/* this seems bulky to me
+.methods, .constructors {
+ background: #f8f8f8;
+ border: 1px solid #eee;
+}
+*/
+
+/* @end */
+
+/* @group Auxillary Pages */
+
+
+.extension-list {
+ list-style-type: none;
+ margin-left: 0;
+}
+
+#mini {
+ margin: 0 auto;
+ padding: 0 1em 1em;
+}
+
+#mini > * {
+ font-size: 93%; /* 12pt */
+}
+
+#mini #module-list .caption,
+#mini #module-header .caption {
+ font-size: 125%; /* 15pt */
+}
+
+#mini #interface h1,
+#mini #interface h2,
+#mini #interface h3,
+#mini #interface h4 {
+ font-size: 109%; /* 13pt */
+ margin: 1em 0 0;
+}
+
+#mini #interface .top,
+#mini #interface .src {
+ margin: 0;
+}
+
+#mini #module-list ul {
+ list-style: none;
+ margin: 0;
+}
+
+#alphabet ul {
+ list-style: none;
+ padding: 0;
+ margin: 0.5em 0 0;
+ text-align: center;
+}
+
+#alphabet li {
+ display: inline;
+ margin: 0 0.25em;
+}
+
+#alphabet a {
+ font-weight: bold;
+}
+
+#index .caption,
+#module-list .caption { font-size: 131%; /* 17pt */ }
+
+#index table {
+ margin-left: 2em;
+}
+
+#index .src {
+ font-weight: bold;
+}
+#index .alt {
+ font-size: 77%; /* 10pt */
+ font-style: italic;
+ padding-left: 2em;
+}
+
+#index td + td {
+ padding-left: 1em;
+}
+
+#module-list ul {
+ list-style: none;
+ margin: 0 0 0 2em;
+}
+
+#module-list li {
+ clear: right;
+}
+
+#module-list span.collapser,
+#module-list span.expander {
+ background-position: 0 0.3em;
+}
+
+#module-list .package {
+ float: right;
+}
+
+:target {
+ background: -webkit-linear-gradient(top, transparent 0%, transparent 65%, #fbf36d 60%, #fbf36d 100%);
+ background: -moz-linear-gradient(top, transparent 0%, transparent 65%, #fbf36d 60%, #fbf36d 100%);
+ background: -o-linear-gradient(top, transparent 0%, transparent 65%, #fbf36d 60%, #fbf36d 100%);
+ background: -ms-linear-gradient(top, transparent 0%, transparent 65%, #fbf36d 60%, #fbf36d 100%);
+ background: linear-gradient(to bottom, transparent 0%, transparent 65%, #fbf36d 60%, #fbf36d 100%);
+}
+
+:target:hover {
+ background: -webkit-linear-gradient(top, transparent 0%, transparent 0%, #fbf36d 0%, #fbf36d 100%);
+ background: -moz-linear-gradient(top, transparent 0%, transparent 0%, #fbf36d 0%, #fbf36d 100%);
+ background: -o-linear-gradient(top, transparent 0%, transparent 0%, #fbf36d 0%, #fbf36d 100%);
+ background: -ms-linear-gradient(top, transparent 0%, transparent 0%, #fbf36d 0%, #fbf36d 100%);
+ background: linear-gradient(to bottom, transparent 0%, transparent 0%, #fbf36d 0%, #fbf36d 100%);
+}
+
+/* @end */
+
+/* @group Dropdown menus */
+
+#preferences-menu, #style-menu {
+ width: 25em;
+ overflow-y: auto;
+}
+
+/* @end */
diff --git a/assets/source-ayu-light.css b/assets/source-ayu-light.css
index fca4920f0e..dddaa497d2 100644
--- a/assets/source-ayu-light.css
+++ b/assets/source-ayu-light.css
@@ -39,6 +39,10 @@ body {
color: #000000;
}
+.ju-define {
+ font-weight: bold;
+}
+
a:hover, a.hover-highlight {
background-color: #dadbdc ;
}
diff --git a/assets/source-nord.css b/assets/source-nord.css
index d21ad7ba35..0d7cb6949b 100644
--- a/assets/source-nord.css
+++ b/assets/source-nord.css
@@ -39,6 +39,10 @@ body {
color: #d8dee9
}
+.ju-defined {
+ font-weight: bold;
+}
+
a:link, a:visited {
text-decoration: none;
}
diff --git a/examples/milestone/MiniTicTacToe/MiniTicTacToe.juvix b/examples/milestone/MiniTicTacToe/MiniTicTacToe.juvix
index f8a3b69ec2..61bb1cae94 100644
--- a/examples/milestone/MiniTicTacToe/MiniTicTacToe.juvix
+++ b/examples/milestone/MiniTicTacToe/MiniTicTacToe.juvix
@@ -1,18 +1,27 @@
+--- Tic-tac-toe is a paper-and-pencil game for two players who take turns marking the spaces
+--- in a three-by-three grid with X or O.
+---
+--- The player who succeeds in placing three of their marks in a horizontal, vertical, or
+--- diagonal row is the winner. It is a solved game, with a forced draw assuming best play from both players.
+---
+--- The function ;run; contains the game logic.
module MiniTicTacToe;
open import Stdlib.Data.Nat.Ord;
open import Stdlib.Prelude;
---------------------------------------------------------------------------------
-- Render Utils
---------------------------------------------------------------------------------
infixr 5 ++str;
+--- Concatenation of ;String;s
axiom ++str : String → String → String;
compile ++str {
c ↦ "concat";
};
+--- Concatenates a list of strings
+---
+--- ;concat (("a" ∷ nil) ∷ ("b" ∷ nil)); evaluates to ;"a" ∷ "b" ∷ nil;
concat : List String → String;
concat ≔ foldl (++str) "";
@@ -22,62 +31,77 @@ surround x xs ≔ (x ∷ intersperse x xs) ++ (x ∷ nil);
intercalate : String → List String → String;
intercalate sep xs ≔ concat (intersperse sep xs);
+--- Joins a list of strings with the newline character
unlines : List String → String;
unlines ≔ intercalate "\n";
---------------------------------------------------------------------------------
-- Symbol
---------------------------------------------------------------------------------
+--- A symbol represents a token that can be placed in a square
inductive Symbol {
+--- The circle token
O : Symbol;
+--- The cross token
X : Symbol;
};
+--- Equality for ;Symbol;s
==Symbol : Symbol → Symbol → Bool;
==Symbol O O ≔ true;
==Symbol X X ≔ true;
==Symbol _ _ ≔ false;
+--- Turns ;O; into ;X; and ;X; into ;O;
switch : Symbol → Symbol;
switch O ≔ X;
switch X ≔ O;
+--- Textual representation of a ;Symbol;
showSymbol : Symbol → String;
showSymbol O ≔ "O";
showSymbol X ≔ "X";
---------------------------------------------------------------------------------
-- Square
---------------------------------------------------------------------------------
-
+--- A square is each of the holes in a board
inductive Square {
+ --- An empty square has a ;ℕ; that uniquely identifies it
empty : ℕ → Square;
+ --- An occupied square has a ;Symbol; in it
occupied : Symbol → Square;
};
+--- Equality for ;Square;s
==Square : Square → Square → Bool;
==Square (empty m) (empty n) ≔ m == n;
==Square (occupied s) (occupied t) ≔ ==Symbol s t;
==Square _ _ ≔ false;
+--- Textual representation of a ;Square;
showSquare : Square → String;
showSquare (empty n) ≔ " " ++str natToStr n ++str " ";
showSquare (occupied s) ≔ " " ++str showSymbol s ++str " ";
---------------------------------------------------------------------------------
--- Board
---------------------------------------------------------------------------------
+-- A row in a board implemented as ;List Square;
+-- Row : Type;
+-- Row ≔ List Square;
+
+-- A column in a board implemented as ;List Square;
+-- Column : Type;
+-- Column ≔ Row;
+-- TODO use a type alias?
+--- A grid of squares
inductive Board {
board : List (List Square) → Board;
};
+--- Returns the list of numbers corresponding to the empty ;Square;s
possibleMoves : List Square → List ℕ;
possibleMoves nil ≔ nil;
possibleMoves ((empty n) ∷ xs) ≔ n ∷ possibleMoves xs;
possibleMoves (_ ∷ xs) ≔ possibleMoves xs;
+--- ;true; if all the ;Square;s in the list are equal
full : List Square → Bool;
full (a ∷ b ∷ c ∷ nil) ≔ (==Square a b) && (==Square b c);
@@ -90,51 +114,54 @@ columns ≔ transpose;
rows : List (List Square) → List (List Square);
rows ≔ id;
-showRow : List Square → String;
+--- Textual representation of a ;(List Square);
+showRow : (List Square) → String;
showRow xs ≔ concat (surround "|" (map showSquare xs));
showBoard : Board → String;
showBoard (board squares) ≔ unlines (surround "+---+---+---+" (map showRow squares));
---------------------------------------------------------------------------------
-- Error
---------------------------------------------------------------------------------
inductive Error {
+--- no error occurred
noError : Error;
+--- a non-fatal error occurred
continue : String → Error;
+--- a fatal occurred
terminate : String → Error;
};
---------------------------------------------------------------------------------
-- GameState
---------------------------------------------------------------------------------
inductive GameState {
state : Board → Symbol → Error → GameState;
};
+--- Textual representation of a ;GameState;
showGameState : GameState → String;
showGameState (state b _ _) ≔ showBoard b;
+--- Projects the player
player : GameState → Symbol;
player (state _ p _) ≔ p;
+--- initial ;GameState;
beginState : GameState;
beginState ≔ state
(board (map (map empty) ((one ∷ two ∷ three ∷ nil) ∷ (four ∷ five ∷ six ∷ nil) ∷ (seven ∷ eight ∷ nine ∷ nil) ∷ nil)))
X
noError;
+--- ;true; if some player has won the game
won : GameState → Bool;
won (state (board squares) _ _) ≔ any full (diagonals squares ++ rows squares ++ columns squares);
+--- ;true; if there is a draw
draw : GameState → Bool;
draw (state (board squares) _ _) ≔ null (possibleMoves (flatten squares));
---------------------------------------------------------------------------------
-- Move
---------------------------------------------------------------------------------
replace : Symbol → ℕ → Square → Square;
replace player k (empty n) ≔ if (n Stdlib.Data.Nat.Ord.== k) (occupied player) (empty n);
@@ -158,9 +185,7 @@ playMove (just k) (state (board s) player e) ≔
(switch player)
noError));
---------------------------------------------------------------------------------
-- IO Utils
---------------------------------------------------------------------------------
axiom readline : String;
compile readline {
@@ -173,9 +198,7 @@ compile parsePositiveInt {
c ↦ "parsePositiveInt";
};
---------------------------------------------------------------------------------
-- IO
---------------------------------------------------------------------------------
validMove : ℕ → Maybe ℕ;
validMove n ≔ if ((n <= nine) && (n >= one)) (just n) nothing;
@@ -195,9 +218,11 @@ run _ (state b p (terminate msg)) ≔ putStrLn ("\n" ++str (showGameState (state
run f (state b p (continue msg)) ≔ run f (f (putStr (msg ++str prompt (state b p noError)) , state b p noError));
run f x ≔ run f (f (putStr (prompt x) , x));
+--- The welcome message
welcome : String;
welcome ≔ "MiniTicTacToe\n-------------\n\nType a number then ENTER to make a move";
+--- The entry point of the program
main : IO;
main ≔ putStrLn welcome >> run do beginState;
end;
diff --git a/src/Juvix/Analysis/Scoping/Scoper.hs b/src/Juvix/Analysis/Scoping/Scoper.hs
index 620ae51ed8..c82d3ffea0 100644
--- a/src/Juvix/Analysis/Scoping/Scoper.hs
+++ b/src/Juvix/Analysis/Scoping/Scoper.hs
@@ -409,7 +409,8 @@ checkTypeSignature ::
checkTypeSignature TypeSignature {..} = do
sigType' <- checkParseExpressionAtoms _sigType
sigName' <- bindFunctionSymbol _sigName
- registerFunction' TypeSignature {_sigName = sigName', _sigType = sigType', ..}
+ sigDoc' <- mapM checkJudoc _sigDoc
+ registerFunction' TypeSignature {_sigName = sigName', _sigType = sigType', _sigDoc = sigDoc', ..}
checkConstructorDef ::
Members '[Error ScoperError, Reader LocalVars, State Scope, State ScoperState, InfoTableBuilder, NameIdGen] r =>
@@ -418,10 +419,12 @@ checkConstructorDef ::
checkConstructorDef InductiveConstructorDef {..} = do
constructorType' <- checkParseExpressionAtoms _constructorType
constructorName' <- bindConstructorSymbol _constructorName
+ doc' <- mapM checkJudoc _constructorDoc
registerConstructor'
InductiveConstructorDef
{ _constructorName = constructorName',
- _constructorType = constructorType'
+ _constructorType = constructorType',
+ _constructorDoc = doc'
}
withParams ::
@@ -464,11 +467,13 @@ checkInductiveDef ::
checkInductiveDef ty@InductiveDef {..} = do
withParams _inductiveParameters $ \inductiveParameters' -> do
inductiveType' <- mapM checkParseExpressionAtoms _inductiveType
+ inductiveDoc' <- mapM checkJudoc _inductiveDoc
inductiveName' <- bindInductiveSymbol _inductiveName
inductiveConstructors' <- mapM checkConstructorDef _inductiveConstructors
registerInductive'
InductiveDef
{ _inductiveName = inductiveName',
+ _inductiveDoc = inductiveDoc',
_inductiveBuiltin = _inductiveBuiltin,
_inductiveParameters = inductiveParameters',
_inductiveType = inductiveType',
@@ -508,7 +513,7 @@ checkTopModule ::
Members '[Error ScoperError, Reader ScopeParameters, Files, State ScoperState, InfoTableBuilder, Parser.InfoTableBuilder, NameIdGen] r =>
Module 'Parsed 'ModuleTop ->
Sem r (ModuleRef'' 'S.NotConcrete 'ModuleTop)
-checkTopModule m@(Module path params body) = do
+checkTopModule m@(Module path params doc body) = do
checkPath
r <- checkedModule
modify (over (scoperModulesCache . cachedModules) (HashMap.insert path r))
@@ -554,11 +559,13 @@ checkTopModule m@(Module path params body) = do
localScope $
withParams params $ \params' -> do
(_moduleExportInfo, body') <- checkModuleBody body
+ doc' <- mapM checkJudoc doc
let _moduleRefModule =
Module
{ _modulePath = path',
_moduleParameters = params',
- _moduleBody = body'
+ _moduleBody = body',
+ _moduleDoc = doc'
}
_moduleRefName = set S.nameConcrete () path'
return ModuleRef'' {..}
@@ -588,12 +595,13 @@ checkLocalModule ::
Module 'Parsed 'ModuleLocal ->
Sem r (Module 'Scoped 'ModuleLocal)
checkLocalModule Module {..} = do
- (_moduleExportInfo, moduleBody', moduleParameters') <-
+ (_moduleExportInfo, moduleBody', moduleParameters', moduleDoc') <-
withScope $
withParams _moduleParameters $ \p' -> do
inheritScope
(e, b) <- checkModuleBody _moduleBody
- return (e, b, p')
+ doc' <- mapM checkJudoc _moduleDoc
+ return (e, b, p', doc')
_modulePath' <- reserveSymbolOf S.KNameLocalModule _modulePath
let moduleId = _modulePath' ^. S.nameId
_moduleRefName = set S.nameConcrete () _modulePath'
@@ -601,7 +609,8 @@ checkLocalModule Module {..} = do
Module
{ _modulePath = _modulePath',
_moduleParameters = moduleParameters',
- _moduleBody = moduleBody'
+ _moduleBody = moduleBody',
+ _moduleDoc = moduleDoc'
}
entry :: ModuleRef' 'S.NotConcrete
entry = mkModuleRef' @'ModuleLocal ModuleRef'' {..}
@@ -844,7 +853,8 @@ checkAxiomDef ::
checkAxiomDef AxiomDef {..} = do
axiomType' <- localScope (checkParseExpressionAtoms _axiomType)
axiomName' <- bindAxiomSymbol _axiomName
- registerAxiom' AxiomDef {_axiomName = axiomName', _axiomType = axiomType', ..}
+ axiomDoc' <- localScope (mapM checkJudoc _axiomDoc)
+ registerAxiom' AxiomDef {_axiomName = axiomName', _axiomType = axiomType', _axiomDoc = axiomDoc', ..}
checkCompile ::
Members '[InfoTableBuilder, Error ScoperError, State Scope, Reader LocalVars, State ScoperState] r =>
@@ -1211,6 +1221,21 @@ checkExpressionAtoms ::
checkExpressionAtoms (ExpressionAtoms l i) = do
(`ExpressionAtoms` i) <$> mapM checkExpressionAtom l
+checkJudoc ::
+ Members '[Error ScoperError, State Scope, State ScoperState, Reader LocalVars, InfoTableBuilder, NameIdGen] r =>
+ Judoc 'Parsed ->
+ Sem r (Judoc 'Scoped)
+checkJudoc (Judoc atoms) = Judoc <$> mapM checkJudocAtom atoms
+
+checkJudocAtom ::
+ Members '[Error ScoperError, State Scope, State ScoperState, Reader LocalVars, InfoTableBuilder, NameIdGen] r =>
+ JudocAtom 'Parsed ->
+ Sem r (JudocAtom 'Scoped)
+checkJudocAtom = \case
+ JudocText t -> return (JudocText t)
+ JudocNewline -> return JudocNewline
+ JudocExpression e -> JudocExpression <$> checkParseExpressionAtoms e
+
checkParseExpressionAtoms ::
Members '[Error ScoperError, State Scope, State ScoperState, Reader LocalVars, InfoTableBuilder, NameIdGen] r =>
ExpressionAtoms 'Parsed ->
diff --git a/src/Juvix/Analysis/Scoping/ScoperResult.hs b/src/Juvix/Analysis/Scoping/ScoperResult.hs
index 5eb5f19c39..8ff1ea2963 100644
--- a/src/Juvix/Analysis/Scoping/ScoperResult.hs
+++ b/src/Juvix/Analysis/Scoping/ScoperResult.hs
@@ -15,3 +15,6 @@ data ScoperResult = ScoperResult
}
makeLenses ''ScoperResult
+
+mainModule :: Lens' ScoperResult (Module 'Scoped 'ModuleTop)
+mainModule = resultModules . _head
diff --git a/src/Juvix/Documentation/Compiler.hs b/src/Juvix/Documentation/Compiler.hs
new file mode 100644
index 0000000000..da548ad576
--- /dev/null
+++ b/src/Juvix/Documentation/Compiler.hs
@@ -0,0 +1,461 @@
+module Juvix.Documentation.Compiler where
+
+import Data.ByteString qualified as BS
+import Data.ByteString.Builder qualified as Builder
+import Data.HashMap.Strict qualified as HashMap
+import Data.Time.Clock
+import Juvix.Documentation.Extra
+import Juvix.Internal.Strings qualified as Str
+import Juvix.Prelude
+import Juvix.Prelude qualified as Prelude
+import Juvix.Prelude.Html
+import Juvix.Prelude.Pretty
+import Juvix.Syntax.Concrete.Language
+import Juvix.Syntax.Concrete.Scoped.Name qualified as S
+import Juvix.Syntax.Concrete.Scoped.Pretty
+import Juvix.Syntax.Concrete.Scoped.Pretty.Html hiding (go)
+import Juvix.Syntax.Concrete.Scoped.Utils
+import Juvix.Syntax.NameId
+import Juvix.Utils.Paths
+import Juvix.Utils.Version
+import Text.Blaze.Html.Renderer.Utf8 qualified as Html
+-- import Data.HashMap.Strict qualified as HashMap
+import Text.Blaze.Html5 as Html hiding (map)
+import Text.Blaze.Html5.Attributes qualified as Attr
+
+data DocParams = DocParams
+ { _docParamBase :: Text,
+ _docOutputDir :: FilePath
+ }
+
+makeLenses ''DocParams
+
+data Tree k a = Tree
+ { _treeLabel :: a,
+ _treeChildren :: HashMap k (Tree k a)
+ }
+
+makeLenses ''Tree
+
+type ModuleTree = Tree Symbol (Maybe TopModulePath)
+
+indexTree :: [TopModulePath] -> Tree Symbol (Maybe TopModulePath)
+indexTree = foldr insertModule emptyTree
+ where
+ insertModule :: TopModulePath -> ModuleTree -> ModuleTree
+ insertModule m@(TopModulePath ps s) t = go (Just t) (snoc ps s)
+ where
+ go :: Maybe ModuleTree -> [Symbol] -> ModuleTree
+ go tree = \case
+ [] -> set treeLabel (Just m) t'
+ (k : ks) -> over (treeChildren . at k) (Just . flip go ks) t'
+ where
+ t' :: ModuleTree
+ t' = fromMaybe emptyTree tree
+ emptyTree :: Tree Symbol (Maybe TopModulePath)
+ emptyTree = Tree Nothing mempty
+
+indexFileName :: FilePath
+indexFileName = "index.html"
+
+createIndexFile ::
+ forall r.
+ Members '[Reader DocParams, Embed IO, Reader HtmlOptions] r =>
+ [TopModulePath] ->
+ Sem r ()
+createIndexFile ps = do
+ outDir <- asks (^. docOutputDir)
+ indexHtml >>= writeHtml (outDir > indexFileName) . template mempty
+ where
+ indexHtml :: Sem r Html
+ indexHtml = do
+ tree' <- root tree
+ return $
+ Html.div ! Attr.id "content" $
+ Html.div ! Attr.id "module-list" $
+ (p ! Attr.class_ "caption" $ "Modules")
+ <> tree'
+ tree :: ModuleTree
+ tree = indexTree ps
+ root :: ModuleTree -> Sem r Html
+ root (Tree _ t) = do
+ c' <- mconcatMapM (uncurry goChild) (HashMap.toList t)
+ return $ ul c'
+
+ goChild :: Symbol -> ModuleTree -> Sem r Html
+ goChild s (Tree lbl children) = node
+ where
+ nodeRow :: Sem r Html
+ nodeRow = case lbl of
+ Nothing ->
+ return $
+ Html.span ! Attr.class_ attrBare $
+ toHtml (prettyText s)
+ Just lbl' -> do
+ lnk <- nameIdAttrRef lbl' Nothing
+ return $
+ Html.span ! Attr.class_ attrBare $
+ (a ! Attr.href lnk $ toHtml (prettyText lbl'))
+ attrBase :: Html.AttributeValue
+ attrBase = "details-toggle-control details-toggle collapser"
+ -- dataDetailsId :: AttributeValue -> Attribute
+ -- dataDetailsId = Html.dataAttribute "details-id"
+ attrBare :: Html.AttributeValue
+ attrBare = attrBase <> "module"
+ -- attr :: Html.AttributeValue
+ -- attr = case lbl of
+ -- Nothing -> attrBare
+ -- Just {} -> attrBase
+ node :: Sem r Html
+ node = do
+ row' <- nodeRow
+ childs' <- childs
+ return (row' <>? childs')
+ where
+ childs :: Sem r (Maybe Html)
+ childs
+ | null children = return Nothing
+ | otherwise = do
+ c' <- mapM (uncurry goChild) (HashMap.toList children)
+ return $
+ Just $
+ details ! Attr.open "open" $
+ -- (summary ! Attr.class_ "hide-when-js-enabled" $ "Submodules")
+ summary "Subtree"
+ <> ul (mconcatMap li c')
+
+compileModuleHtmlText :: Members '[Embed IO] r => FilePath -> Text -> Module 'Scoped 'ModuleTop -> Sem r ()
+compileModuleHtmlText dir baseName m = runReader params $ do
+ copyAssets
+ mapM_ goTopModule topModules
+ runReader docHtmlOpts (createIndexFile (map topModulePath (toList topModules)))
+ where
+ copyAssets :: forall s. Members '[Embed IO, Reader DocParams] s => Sem s ()
+ copyAssets = do
+ toAssetsDir <- (> "assets") <$> asks (^. docOutputDir)
+ let writeAsset :: (FilePath, BS.ByteString) -> Sem s ()
+ writeAsset (filePath, fileContents) =
+ Prelude.embed $ BS.writeFile (toAssetsDir > takeFileName filePath) fileContents
+ Prelude.embed (createDirectoryIfMissing True toAssetsDir)
+ mapM_ writeAsset assetFiles
+ where
+ assetFiles :: [(FilePath, BS.ByteString)]
+ assetFiles = $(assetsDir)
+
+ params :: DocParams
+ params =
+ DocParams
+ { _docParamBase = baseName,
+ _docOutputDir = dir
+ }
+ topModules :: HashMap NameId (Module 'Scoped 'ModuleTop)
+ topModules = getAllModules m
+
+writeHtml :: Members '[Embed IO] r => FilePath -> Html -> Sem r ()
+writeHtml f h = Prelude.embed $ do
+ createDirectoryIfMissing True dir
+ Builder.writeFile f (Html.renderHtmlBuilder h)
+ where
+ dir :: FilePath
+ dir = takeDirectory f
+
+moduleDocPath :: Members '[Reader HtmlOptions, Reader DocParams] r => Module 'Scoped 'ModuleTop -> Sem r FilePath
+moduleDocPath m = do
+ relPath <- moduleDocRelativePath (m ^. modulePath . S.nameConcrete)
+ outDir <- asks (^. docOutputDir)
+ return (outDir > relPath)
+
+topModulePath ::
+ Module 'Scoped 'ModuleTop -> TopModulePath
+topModulePath = (^. modulePath . S.nameConcrete)
+
+srcHtmlOpts :: HtmlOptions
+srcHtmlOpts = HtmlOptions HtmlSrc
+
+docHtmlOpts :: HtmlOptions
+docHtmlOpts = HtmlOptions HtmlDoc
+
+template :: Html -> Html -> Html
+template rightMenu' content' = do
+ docTypeHtml (mhead <> body')
+ where
+ mhead :: Html
+ mhead =
+ Html.head $
+ title titleStr
+ <> Html.meta
+ ! Attr.httpEquiv "Content-Type"
+ ! Attr.content "text/html; charset=UTF-8"
+ <> Html.meta
+ ! Attr.name "viewport"
+ ! Attr.content "width=device-width, initial-scale=1"
+ <> mathJaxCdn
+ -- <> highlightJs
+ <> livejs
+ <> ayuCss
+ <> linuwialCss
+
+ titleStr :: Html
+ titleStr = "Juvix Documentation"
+
+ packageHeader :: Html
+ packageHeader =
+ Html.div ! Attr.id "package-header" $
+ ( Html.span ! Attr.class_ "caption" $
+ "package name - version"
+ )
+ <> rightMenu'
+
+ body' :: Html
+ body' =
+ body ! Attr.class_ "js-enabled" $
+ packageHeader
+ <> content'
+ <> mfooter
+
+ mfooter :: Html
+ mfooter =
+ Html.div ! Attr.id "footer" $
+ ( p
+ ( "Build by "
+ <> (Html.a ! Attr.href Str.juvixDotOrg $ "Juvix")
+ <> " version "
+ <> toHtml versionDoc
+ )
+ )
+ <> ( Html.a ! Attr.href Str.juvixDotOrg $
+ Html.img
+ ! Attr.id "tara"
+ ! Attr.src "assets/Seating_Tara_smiling.svg"
+ ! Attr.alt "Tara"
+ )
+
+-- | This function compiles a datalang module into Html documentation.
+goTopModule ::
+ forall r.
+ Members '[Reader DocParams, Embed IO] r =>
+ Module 'Scoped 'ModuleTop ->
+ Sem r ()
+goTopModule m = do
+ runReader docHtmlOpts $ do
+ fpath <- moduleDocPath m
+ Prelude.embed (putStrLn ("processing " <> pack fpath))
+ docHtml >>= writeHtml fpath
+
+ runReader srcHtmlOpts $ do
+ fpath <- moduleDocPath m
+ srcHtml >>= writeHtml fpath
+ where
+ tmp :: TopModulePath
+ tmp = m ^. modulePath . S.nameConcrete
+
+ srcHtml :: forall s. Members '[Reader HtmlOptions, Embed IO] s => Sem s Html
+ srcHtml = do
+ utc <- Prelude.embed getCurrentTime
+ return (genModuleHtml defaultOptions True utc Ayu m)
+
+ docHtml :: forall s. Members '[Reader HtmlOptions] s => Sem s Html
+ docHtml = do
+ content' <- content
+ rightMenu' <- rightMenu
+ return (template rightMenu' content')
+ where
+ rightMenu :: Sem s Html
+ rightMenu = do
+ sourceRef' <- local (set htmlOptionsKind HtmlSrc) (nameIdAttrRef tmp Nothing)
+ return $
+ ul ! Attr.id "page-menu" ! Attr.class_ "links" $
+ li (a ! Attr.href sourceRef' $ "Source")
+ <> li (a ! Attr.href (fromString indexFileName) $ "Index")
+
+ content :: Sem s Html
+ content = do
+ preface' <- docPreface
+ interface' <- interface
+ return $
+ Html.div ! Attr.id "content" $
+ moduleHeader
+ <> toc
+ <> preface'
+ -- <> synopsis
+ <> interface'
+
+ docPreface :: Sem s Html
+ docPreface = do
+ pref <- goJudocMay (m ^. moduleDoc)
+ return $
+ Html.div ! Attr.id "description" $
+ Html.div ! Attr.class_ "doc" $
+ ( a ! Attr.id "sec:description" ! Attr.href "sec:description" $
+ h1 "Description"
+ )
+ <> pref
+
+ toc :: Html
+ toc =
+ Html.div ! Attr.id "table-of-contents" $
+ Html.div ! Attr.id "contents-list" $
+ ( p
+ ! Attr.class_ "caption"
+ ! Attr.onclick "window.scrollTo(0,0)"
+ $ "Contents"
+ )
+ <> tocEntries
+ where
+ tocEntries :: Html
+ tocEntries =
+ ul $
+ li (a ! Attr.href "#sec:description" $ "Description")
+ <> li (a ! Attr.href "#sec:interface" $ "Definitions")
+
+ moduleHeader :: Html
+ moduleHeader =
+ Html.div ! Attr.id "module-header" $
+ (p ! Attr.class_ "caption" $ toHtml (prettyText tmp))
+
+ interface :: Sem s Html
+ interface = do
+ sigs' <- mconcatMapM goStatement (m ^. moduleBody)
+ return $
+ Html.div ! Attr.id "interface" $
+ ( a ! Attr.id "sec:interface" ! Attr.href "sec:interface" $
+ h1 "Definitions"
+ )
+ <> sigs'
+
+goJudocMay :: Members '[Reader HtmlOptions] r => Maybe (Judoc 'Scoped) -> Sem r Html
+goJudocMay = maybe (return mempty) goJudoc
+
+goJudoc :: forall r. Members '[Reader HtmlOptions] r => Judoc 'Scoped -> Sem r Html
+goJudoc (Judoc atoms) = mconcatMapM goParagraph paragraphs
+ where
+ goParagraph :: [JudocAtom 'Scoped] -> Sem r Html
+ goParagraph = fmap p . mconcatMapM goAtom
+ goAtom :: JudocAtom 'Scoped -> Sem r Html
+ goAtom = \case
+ JudocNewline -> return " "
+ JudocExpression e -> ppCodeHtml e
+ JudocText txt -> return (toHtml txt)
+ paragraphs :: [[JudocAtom 'Scoped]]
+ paragraphs = splitOn [JudocNewline, JudocNewline] atoms
+
+goStatement :: Members '[Reader HtmlOptions] r => Statement 'Scoped -> Sem r Html
+goStatement = \case
+ StatementTypeSignature t -> goTypeSignature t
+ StatementAxiom t -> goAxiom t
+ StatementInductive t -> goInductive t
+ StatementOpenModule t -> goOpen t
+ _ -> mempty
+
+goOpen :: forall r. Members '[Reader HtmlOptions] r => OpenModule 'Scoped -> Sem r Html
+goOpen op
+ | Public <- op ^. openPublic = noDefHeader <$> ppCodeHtml op
+ | otherwise = mempty
+
+goAxiom :: forall r. Members '[Reader HtmlOptions] r => AxiomDef 'Scoped -> Sem r Html
+goAxiom axiom = do
+ header' <- axiomHeader
+ defHeader tmp uid header' Nothing
+ where
+ uid :: NameId
+ uid = axiom ^. axiomName . S.nameId
+ tmp :: TopModulePath
+ tmp = axiom ^. axiomName . S.nameDefinedIn . S.absTopModulePath
+ axiomHeader :: Sem r Html
+ axiomHeader = ppCodeHtml (set axiomDoc Nothing axiom)
+
+goInductive :: forall r. Members '[Reader HtmlOptions] r => InductiveDef 'Scoped -> Sem r Html
+goInductive def = do
+ sig' <- inductiveHeader
+ header' <- defHeader tmp uid sig' (def ^. inductiveDoc)
+ body' <- goConstructors (def ^. inductiveConstructors)
+ return (header' <> body')
+ where
+ uid :: NameId
+ uid = def ^. inductiveName . S.nameId
+ tmp :: TopModulePath
+ tmp = def ^. inductiveName . S.nameDefinedIn . S.absTopModulePath
+ inductiveHeader :: Sem r Html
+ inductiveHeader =
+ runReader defaultOptions (ppInductiveSignature def) >>= ppCodeHtml
+
+goConstructors :: forall r. Members '[Reader HtmlOptions] r => [InductiveConstructorDef 'Scoped] -> Sem r Html
+goConstructors cc = do
+ tbl' <- table . tbody <$> mconcatMapM goConstructor cc
+ return $
+ Html.div ! Attr.class_ "subs constructors" $
+ (p ! Attr.class_ "caption" $ "Constructors")
+ <> tbl'
+ where
+ goConstructor :: InductiveConstructorDef 'Scoped -> Sem r Html
+ goConstructor c = do
+ src' <- srcPart
+ doc' <- docPart
+ return (tr (src' <> doc'))
+ where
+ docPart :: Sem r Html
+ docPart = do
+ td ! Attr.class_ "doc"
+ <$> goJudocMay (c ^. constructorDoc)
+
+ srcPart :: Sem r Html
+ srcPart = do
+ sig' <- ppCodeHtml (set constructorDoc Nothing c)
+ return $
+ td ! Attr.class_ "src" $
+ sig'
+
+noDefHeader :: Html -> Html
+noDefHeader = p ! Attr.class_ "src"
+
+defHeader :: forall r. Members '[Reader HtmlOptions] r => TopModulePath -> NameId -> Html -> Maybe (Judoc 'Scoped) -> Sem r Html
+defHeader tmp uid sig mjudoc = do
+ funHeader' <- functionHeader
+ judoc' <- judoc
+ return $
+ Html.div ! Attr.class_ "top" $
+ funHeader'
+ <> judoc'
+ where
+ judoc :: Sem r Html
+ judoc = do
+ judoc' <- goJudocMay mjudoc
+ return (Html.div ! Attr.class_ "doc" $ judoc')
+
+ functionHeader :: Sem r Html
+ functionHeader = do
+ sourceLink' <- sourceAndSelfLink tmp uid
+ return $ noDefHeader (sig <> sourceLink')
+
+goTypeSignature :: forall r. Members '[Reader HtmlOptions] r => TypeSignature 'Scoped -> Sem r Html
+goTypeSignature sig = do
+ sig' <- typeSig
+ defHeader tmp uid sig' (sig ^. sigDoc)
+ where
+ tmp :: TopModulePath
+ tmp = sig ^. sigName . S.nameDefinedIn . S.absTopModulePath
+ uid :: NameId
+ uid = sig ^. sigName . S.nameId
+ typeSig :: Sem r Html
+ typeSig = ppCodeHtml (set sigDoc Nothing sig)
+
+sourceAndSelfLink :: Members '[Reader HtmlOptions] r => TopModulePath -> NameId -> Sem r Html
+sourceAndSelfLink tmp name = do
+ ref' <- local (set htmlOptionsKind HtmlSrc) (nameIdAttrRef tmp (Just name))
+ return $
+ ( a
+ ! Attr.href ref'
+ ! Attr.class_ "link"
+ $ "Source"
+ )
+ <> ( a
+ ! Attr.href (selfLinkName name)
+ ! Attr.class_ "selflink"
+ $ "#"
+ )
+
+tagIden :: IsString a => NameId -> a
+tagIden x = fromText $ prettyText x
+
+selfLinkName :: IsString a => NameId -> a
+selfLinkName x = fromText $ "#" <> tagIden x
diff --git a/src/Juvix/Documentation/Extra.hs b/src/Juvix/Documentation/Extra.hs
new file mode 100644
index 0000000000..d064cce9cb
--- /dev/null
+++ b/src/Juvix/Documentation/Extra.hs
@@ -0,0 +1,48 @@
+module Juvix.Documentation.Extra where
+
+import Juvix.Prelude
+import Text.Blaze.Html5 as Html hiding (map)
+import Text.Blaze.Html5.Attributes qualified as Attr
+
+mathJaxCdn :: Html
+mathJaxCdn = script1 <> script2
+ where
+ script1 =
+ script
+ ! Attr.src src1
+ $ mempty
+ script2 =
+ script
+ ! Attr.type_ "text/javascript"
+ ! Attr.id "MathJax-script"
+ ! Attr.src src2
+ $ mempty
+ src1 :: AttributeValue
+ src1 = "https://polyfill.io/v3/polyfill.min.js?features=es6"
+ src2 :: AttributeValue
+ src2 = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"
+
+-- | This is useful fore debugging only. Note that it only works on a server
+-- protocol, opening the file from the local system won't work. For that, one
+-- can use @python3 -m http.server@.
+livejs :: Html
+livejs =
+ script
+ ! Attr.type_ "text/javascript"
+ ! Attr.src "https://livejs.com/live.js"
+ $ mempty
+
+linuwialCss :: Html
+linuwialCss =
+ link
+ ! Attr.href "assets/linuwial.css"
+ ! Attr.rel "stylesheet"
+ ! Attr.type_ "text/css"
+ ! Attr.title "Linuwial"
+
+sourceCss :: Html
+sourceCss =
+ link
+ ! Attr.href "assets/source.css"
+ ! Attr.rel "stylesheet"
+ ! Attr.type_ "text/css"
diff --git a/src/Juvix/Internal/Strings.hs b/src/Juvix/Internal/Strings.hs
index b3c8d40117..1d6f5e019a 100644
--- a/src/Juvix/Internal/Strings.hs
+++ b/src/Juvix/Internal/Strings.hs
@@ -8,6 +8,9 @@ module_ = "module"
axiom :: IsString s => s
axiom = "axiom"
+judocStart :: IsString s => s
+judocStart = "---"
+
end :: IsString s => s
end = "end"
@@ -235,3 +238,6 @@ putStrLn_ = "putStrLn"
juvixFunctionT :: IsString s => s
juvixFunctionT = "juvix_function_t"
+
+juvixDotOrg :: IsString s => s
+juvixDotOrg = "https://juvix.org"
diff --git a/src/Juvix/Parsing/Lexer.hs b/src/Juvix/Parsing/Lexer.hs
index be2ab47bdc..ab87099130 100644
--- a/src/Juvix/Parsing/Lexer.hs
+++ b/src/Juvix/Parsing/Lexer.hs
@@ -23,13 +23,26 @@ makeLenses ''ParserParams
space :: forall r. Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r ()
space = L.space space1 lineComment block
where
+ skipLineComment :: ParsecS r ()
+ skipLineComment = do
+ notFollowedBy (P.chunk Str.judocStart)
+ void (P.chunk "--")
+ void (P.takeWhileP Nothing (/= '\n'))
+
lineComment :: ParsecS r ()
- lineComment =
- interval (L.skipLineComment "--") >>= lift . registerComment . snd
+ lineComment = comment_ skipLineComment
block :: ParsecS r ()
- block =
- interval (L.skipBlockComment "{-" "-}") >>= lift . registerComment . snd
+ block = comment_ (L.skipBlockComment "{-" "-}")
+
+comment :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r a -> ParsecS r a
+comment c = do
+ (a, i) <- interval c
+ P.lift (registerComment i)
+ return a
+
+comment_ :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r a -> ParsecS r ()
+comment_ = void . comment
lexeme :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r a -> ParsecS r a
lexeme = L.lexeme space
diff --git a/src/Juvix/Parsing/Parser.hs b/src/Juvix/Parsing/Parser.hs
index f4a61d0d11..d805ebfdd4 100644
--- a/src/Juvix/Parsing/Parser.hs
+++ b/src/Juvix/Parsing/Parser.hs
@@ -8,6 +8,7 @@ where
import Data.List.NonEmpty.Extra qualified as NonEmpty
import Data.Singletons
+import Juvix.Internal.Strings qualified as Str
import Juvix.Parsing.Error
import Juvix.Parsing.InfoTable
import Juvix.Parsing.InfoTableBuilder
@@ -16,12 +17,11 @@ import Juvix.Parsing.ParserResult
import Juvix.Pipeline.EntryPoint
import Juvix.Prelude
import Juvix.Prelude.Pretty (Pretty, prettyText)
+import Juvix.Syntax.Concrete.Base (MonadParsec (takeWhile1P))
import Juvix.Syntax.Concrete.Base qualified as P
import Juvix.Syntax.Concrete.Language
---------------------------------------------------------------------------------
--- Running the parser
---------------------------------------------------------------------------------
+type JudocStash = State (Maybe (Judoc 'Parsed))
entryParser ::
Members '[Files, Error ParserError] r =>
@@ -46,7 +46,11 @@ entryParser e = do
-- an empty string.
runModuleParser :: FilePath -> FilePath -> Text -> Either ParserError (InfoTable, Module 'Parsed 'ModuleTop)
runModuleParser root fileName input =
- case run $ runInfoTableBuilder $ runReader params $ P.runParserT topModuleDef fileName input of
+ case run $
+ runInfoTableBuilder $
+ runReader params $
+ evalState (Nothing @(Judoc 'Parsed)) $
+ P.runParserT topModuleDef fileName input of
(_, Left err) -> Left (ParserError err)
(tbl, Right r) -> return (tbl, r)
where
@@ -56,32 +60,29 @@ runModuleParser root fileName input =
}
top ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
+ Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r =>
ParsecS r a ->
ParsecS r a
top p = space >> p <* (optional kwSemicolon >> P.eof)
topModuleDef ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
+ Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r =>
ParsecS r (Module 'Parsed 'ModuleTop)
-topModuleDef = top moduleDef
-
-topStatement ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
- ParsecS r (Statement 'Parsed)
-topStatement = top statement
+topModuleDef = do
+ void (optional judocBlock)
+ top moduleDef
--------------------------------------------------------------------------------
-- Symbols and names
--------------------------------------------------------------------------------
-symbol :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r Symbol
+symbol :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r Symbol
symbol = uncurry (flip WithLoc) <$> identifierL
-dottedSymbol :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (NonEmpty Symbol)
+dottedSymbol :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (NonEmpty Symbol)
dottedSymbol = fmap (uncurry (flip WithLoc)) <$> dottedIdentifier
-name :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r Name
+name :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r Name
name = do
parts <- dottedSymbol
return $ case nonEmptyUnsnoc parts of
@@ -91,18 +92,19 @@ name = do
mkTopModulePath :: NonEmpty Symbol -> TopModulePath
mkTopModulePath l = TopModulePath (NonEmpty.init l) (NonEmpty.last l)
-symbolList :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (NonEmpty Symbol)
+symbolList :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (NonEmpty Symbol)
symbolList = braces (P.sepBy1 symbol kwSemicolon)
-topModulePath :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r TopModulePath
+topModulePath :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r TopModulePath
topModulePath = mkTopModulePath <$> dottedSymbol
--------------------------------------------------------------------------------
-- Top level statement
--------------------------------------------------------------------------------
-statement :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (Statement 'Parsed)
-statement =
+statement :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (Statement 'Parsed)
+statement = do
+ void (optional judocBlock)
(StatementOperator <$> operatorSyntaxDef)
<|> (StatementOpenModule <$> openModule)
<|> (StatementImport <$> import_)
@@ -116,17 +118,46 @@ statement =
<$> auxTypeSigFunClause
)
-builtinInductive :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r BuiltinInductive
+judocBlock :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r ()
+judocBlock = do
+ b :: Judoc 'Parsed <- mconcat . toList <$> P.some judocLine
+ P.lift (modify (<> Just b))
+
+judocLine :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (Judoc 'Parsed)
+judocLine = lexeme $ do
+ comment_ (P.chunk Str.judocStart)
+ ln <- Judoc . (`snoc` JudocNewline) <$> many judocAtom
+ P.newline
+ return ln
+
+judocAtom :: forall r. Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (JudocAtom 'Parsed)
+judocAtom =
+ JudocText <$> judocText
+ <|> JudocExpression <$> judocExpression
+ where
+ judocText :: ParsecS r Text
+ judocText = comment (takeWhile1P Nothing isValidText)
+ where
+ isValidText :: Char -> Bool
+ isValidText = (`notElem` ['\n', ';'])
+ judocExpression :: ParsecS r (ExpressionAtoms 'Parsed)
+ judocExpression = do
+ comment_ (P.char ';')
+ e <- parseExpressionAtoms
+ comment_ (P.char ';')
+ return e
+
+builtinInductive :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r BuiltinInductive
builtinInductive = builtinHelper
-builtinFunction :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r BuiltinFunction
+builtinFunction :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r BuiltinFunction
builtinFunction = builtinHelper
-builtinAxiom :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r BuiltinAxiom
+builtinAxiom :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r BuiltinAxiom
builtinAxiom = builtinHelper
builtinHelper ::
- (Members '[Reader ParserParams, InfoTableBuilder] r, Bounded a, Enum a, Pretty a) =>
+ (Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r, Bounded a, Enum a, Pretty a) =>
ParsecS r a
builtinHelper =
P.choice
@@ -134,24 +165,24 @@ builtinHelper =
| a <- allElements
]
-builtinInductiveDef :: Members '[Reader ParserParams, InfoTableBuilder] r => BuiltinInductive -> ParsecS r (InductiveDef 'Parsed)
+builtinInductiveDef :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => BuiltinInductive -> ParsecS r (InductiveDef 'Parsed)
builtinInductiveDef = inductiveDef . Just
builtinAxiomDef ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
+ Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r =>
BuiltinAxiom ->
ParsecS r (AxiomDef 'Parsed)
builtinAxiomDef = axiomDef . Just
builtinTypeSig ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
+ Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r =>
BuiltinFunction ->
ParsecS r (TypeSignature 'Parsed)
builtinTypeSig b = do
fun <- symbol
typeSignature False fun (Just b)
-builtinStatement :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (Statement 'Parsed)
+builtinStatement :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (Statement 'Parsed)
builtinStatement = do
kwBuiltin
(builtinInductive >>= fmap StatementInductive . builtinInductiveDef)
@@ -162,7 +193,7 @@ builtinStatement = do
-- Compile
--------------------------------------------------------------------------------
-compileBlock :: forall r. Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (Compile 'Parsed)
+compileBlock :: forall r. Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (Compile 'Parsed)
compileBlock = do
kwCompile
_compileName <- symbol
@@ -181,10 +212,10 @@ compileBlock = do
-- Foreign
--------------------------------------------------------------------------------
-backend :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r Backend
+backend :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r Backend
backend = ghc $> BackendGhc <|> cBackend $> BackendC
-foreignBlock :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r ForeignBlock
+foreignBlock :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r ForeignBlock
foreignBlock = do
kwForeign
_foreignBackend <- backend
@@ -195,10 +226,10 @@ foreignBlock = do
-- Operator syntax declaration
--------------------------------------------------------------------------------
-precedence :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r Precedence
+precedence :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r Precedence
precedence = PrecNat <$> (fst <$> decimal)
-operatorSyntaxDef :: forall r. Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r OperatorSyntaxDef
+operatorSyntaxDef :: forall r. Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r OperatorSyntaxDef
operatorSyntaxDef = do
_fixityArity <- arity
_fixityPrecedence <- precedence
@@ -218,7 +249,7 @@ operatorSyntaxDef = do
-- Import statement
--------------------------------------------------------------------------------
-import_ :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (Import 'Parsed)
+import_ :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (Import 'Parsed)
import_ = do
kwImport
_importModule <- topModulePath
@@ -228,7 +259,7 @@ import_ = do
-- Expression
--------------------------------------------------------------------------------
-expressionAtom :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (ExpressionAtom 'Parsed)
+expressionAtom :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (ExpressionAtom 'Parsed)
expressionAtom =
AtomLiteral <$> P.try literal
<|> (AtomIdentifier <$> name)
@@ -242,7 +273,7 @@ expressionAtom =
<|> braces (AtomBraces <$> withLoc parseExpressionAtoms)
parseExpressionAtoms ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
+ Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r =>
ParsecS r (ExpressionAtoms 'Parsed)
parseExpressionAtoms = do
(_expressionAtoms, _expressionAtomsLoc) <- interval (P.some expressionAtom)
@@ -252,38 +283,34 @@ parseExpressionAtoms = do
-- Holes
--------------------------------------------------------------------------------
-hole :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (HoleType 'Parsed)
+hole :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (HoleType 'Parsed)
hole = snd <$> interval kwHole
--------------------------------------------------------------------------------
-- Literals
--------------------------------------------------------------------------------
-literalInteger :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r LiteralLoc
+literalInteger :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r LiteralLoc
literalInteger = do
(x, loc) <- integer
return (WithLoc loc (LitInteger x))
-literalString :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r LiteralLoc
+literalString :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r LiteralLoc
literalString = do
(x, loc) <- string
return (WithLoc loc (LitString x))
-literal :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r LiteralLoc
+literal :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r LiteralLoc
literal = do
l <-
literalInteger
<|> literalString
P.lift (registerLiteral l)
---------------------------------------------------------------------------------
--- Let expression
---------------------------------------------------------------------------------
-
-letClause :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (LetClause 'Parsed)
+letClause :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (LetClause 'Parsed)
letClause = either LetTypeSig LetFunClause <$> auxTypeSigFunClause
-letBlock :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (LetBlock 'Parsed)
+letBlock :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (LetBlock 'Parsed)
letBlock = do
kwLet
_letClauses <- braces (P.sepEndBy letClause kwSemicolon)
@@ -295,7 +322,7 @@ letBlock = do
-- Universe expression
--------------------------------------------------------------------------------
-universe :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r Universe
+universe :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r Universe
universe = do
i <- snd <$> interval kwType
uni <- optional decimal
@@ -305,12 +332,14 @@ universe = do
Just (lvl, i') -> Universe (Just lvl) (i <> i')
)
--------------------------------------------------------------------------------
--- Type signature declaration
--------------------------------------------------------------------------------
+getJudoc :: Members '[JudocStash] r => ParsecS r (Maybe (Judoc 'Parsed))
+getJudoc = P.lift $ do
+ j <- get
+ put (Nothing @(Judoc 'Parsed))
+ return j
typeSignature ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
+ Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r =>
Bool ->
Symbol ->
Maybe BuiltinFunction ->
@@ -318,11 +347,12 @@ typeSignature ::
typeSignature _sigTerminating _sigName _sigBuiltin = do
kwColon
_sigType <- parseExpressionAtoms
+ _sigDoc <- getJudoc
return TypeSignature {..}
-- | Used to minimize the amount of required @P.try@s.
auxTypeSigFunClause ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
+ Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r =>
ParsecS r (Either (TypeSignature 'Parsed) (FunctionClause 'Parsed))
auxTypeSigFunClause = do
terminating <- isJust <$> optional kwTerminating
@@ -330,16 +360,13 @@ auxTypeSigFunClause = do
(Left <$> typeSignature terminating sym Nothing)
<|> (Right <$> functionClause sym)
--------------------------------------------------------------------------------
--- Axioms
--------------------------------------------------------------------------------
-
axiomDef ::
- Members '[Reader ParserParams, InfoTableBuilder] r =>
+ Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r =>
Maybe BuiltinAxiom ->
ParsecS r (AxiomDef 'Parsed)
axiomDef _axiomBuiltin = do
kwAxiom
+ _axiomDoc <- getJudoc
_axiomName <- symbol
kwColon
_axiomType <- parseExpressionAtoms
@@ -349,17 +376,17 @@ axiomDef _axiomBuiltin = do
-- Function expression
--------------------------------------------------------------------------------
-implicitOpen :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r IsImplicit
+implicitOpen :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r IsImplicit
implicitOpen =
lbrace $> Implicit
<|> lparen $> Explicit
-implicitClose :: Members '[Reader ParserParams, InfoTableBuilder] r => IsImplicit -> ParsecS r ()
+implicitClose :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => IsImplicit -> ParsecS r ()
implicitClose = \case
Implicit -> rbrace
Explicit -> rparen
-functionParam :: forall r. Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (FunctionParameter 'Parsed)
+functionParam :: forall r. Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (FunctionParameter 'Parsed)
functionParam = do
(_paramName, _paramUsage, _paramImplicit) <- P.try $ do
impl <- implicitOpen
@@ -381,7 +408,7 @@ functionParam = do
<|> (Just UsageOmega <$ kwColonOmega)
<|> (Nothing <$ kwColon)
-function :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (Function 'Parsed)
+function :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (Function 'Parsed)
function = do
_funParameter <- functionParam
kwRightArrow
@@ -392,12 +419,12 @@ function = do
-- Where block clauses
--------------------------------------------------------------------------------
-whereBlock :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (WhereBlock 'Parsed)
+whereBlock :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (WhereBlock 'Parsed)
whereBlock = do
kwWhere
WhereBlock <$> braces (P.sepEndBy1 whereClause kwSemicolon)
-whereClause :: forall r. Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (WhereClause 'Parsed)
+whereClause :: forall r. Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (WhereClause 'Parsed)
whereClause =
(WhereOpenModule <$> openModule)
<|> sigOrFun
@@ -409,14 +436,14 @@ whereClause =
-- Lambda expression
--------------------------------------------------------------------------------
-lambdaClause :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (LambdaClause 'Parsed)
+lambdaClause :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (LambdaClause 'Parsed)
lambdaClause = do
lambdaParameters <- P.some patternAtom
kwMapsTo
lambdaBody <- parseExpressionAtoms
return LambdaClause {..}
-lambda :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (Lambda 'Parsed)
+lambda :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (Lambda 'Parsed)
lambda = do
kwLambda
_lambdaClauses <- braces (P.sepEndBy lambdaClause kwSemicolon)
@@ -426,45 +453,47 @@ lambda = do
-- Data type construction declaration
-------------------------------------------------------------------------------
-inductiveDef :: Members '[Reader ParserParams, InfoTableBuilder] r => Maybe BuiltinInductive -> ParsecS r (InductiveDef 'Parsed)
+inductiveDef :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => Maybe BuiltinInductive -> ParsecS r (InductiveDef 'Parsed)
inductiveDef _inductiveBuiltin = do
_inductivePositive <- isJust <$> optional kwPositive
kwInductive
+ _inductiveDoc <- getJudoc
_inductiveName <- symbol
_inductiveParameters <- P.many inductiveParam
_inductiveType <- optional (kwColon >> parseExpressionAtoms)
_inductiveConstructors <- braces $ P.sepEndBy constructorDef kwSemicolon
return InductiveDef {..}
-inductiveParam :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (InductiveParameter 'Parsed)
+inductiveParam :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (InductiveParameter 'Parsed)
inductiveParam = parens $ do
_inductiveParameterName <- symbol
kwColon
_inductiveParameterType <- parseExpressionAtoms
return InductiveParameter {..}
-constructorDef :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (InductiveConstructorDef 'Parsed)
+constructorDef :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (InductiveConstructorDef 'Parsed)
constructorDef = do
+ _constructorDoc <- optional judocBlock >> getJudoc
_constructorName <- symbol
kwColon
_constructorType <- parseExpressionAtoms
return InductiveConstructorDef {..}
-wildcard :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r Wildcard
+wildcard :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r Wildcard
wildcard = Wildcard . snd <$> interval kwWildcard
--------------------------------------------------------------------------------
-- Pattern section
--------------------------------------------------------------------------------
-patternAtom :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (PatternAtom 'Parsed)
+patternAtom :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (PatternAtom 'Parsed)
patternAtom =
PatternAtomIden <$> name
<|> PatternAtomWildcard <$> wildcard
<|> (PatternAtomParens <$> parens parsePatternAtoms)
<|> (PatternAtomBraces <$> braces parsePatternAtoms)
-parsePatternAtoms :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (PatternAtoms 'Parsed)
+parsePatternAtoms :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (PatternAtoms 'Parsed)
parsePatternAtoms = do
(_patternAtoms, _patternAtomsLoc) <- interval (P.some patternAtom)
return PatternAtoms {..}
@@ -473,7 +502,7 @@ parsePatternAtoms = do
-- Function binding declaration
--------------------------------------------------------------------------------
-functionClause :: Members '[Reader ParserParams, InfoTableBuilder] r => Symbol -> ParsecS r (FunctionClause 'Parsed)
+functionClause :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => Symbol -> ParsecS r (FunctionClause 'Parsed)
functionClause _clauseOwnerFunction = do
_clausePatterns <- P.many patternAtom
kwAssignment
@@ -485,14 +514,15 @@ functionClause _clauseOwnerFunction = do
-- Module declaration
--------------------------------------------------------------------------------
-pmodulePath :: forall t r. (SingI t, Members '[Reader ParserParams, InfoTableBuilder] r) => ParsecS r (ModulePathType 'Parsed t)
+pmodulePath :: forall t r. (SingI t, Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r) => ParsecS r (ModulePathType 'Parsed t)
pmodulePath = case sing :: SModuleIsTop t of
SModuleTop -> topModulePath
SModuleLocal -> symbol
-moduleDef :: (SingI t, Members '[Reader ParserParams, InfoTableBuilder] r) => ParsecS r (Module 'Parsed t)
+moduleDef :: (SingI t, Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r) => ParsecS r (Module 'Parsed t)
moduleDef = do
kwModule
+ _moduleDoc <- getJudoc
_modulePath <- pmodulePath
_moduleParameters <- many inductiveParam
kwSemicolon
@@ -501,7 +531,7 @@ moduleDef = do
return Module {..}
-- | An ExpressionAtom which is a valid expression on its own.
-atomicExpression :: Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (ExpressionAtoms 'Parsed)
+atomicExpression :: Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (ExpressionAtoms 'Parsed)
atomicExpression = do
(atom, loc) <- interval expressionAtom
case atom of
@@ -509,7 +539,7 @@ atomicExpression = do
_ -> return ()
return $ ExpressionAtoms (NonEmpty.singleton atom) loc
-openModule :: forall r. Members '[Reader ParserParams, InfoTableBuilder] r => ParsecS r (OpenModule 'Parsed)
+openModule :: forall r. Members '[Reader ParserParams, InfoTableBuilder, JudocStash] r => ParsecS r (OpenModule 'Parsed)
openModule = do
kwOpen
_openModuleImport <- isJust <$> optional kwImport
diff --git a/src/Juvix/Prelude/Base.hs b/src/Juvix/Prelude/Base.hs
index cfbb4f46c7..5283030418 100644
--- a/src/Juvix/Prelude/Base.hs
+++ b/src/Juvix/Prelude/Base.hs
@@ -278,19 +278,22 @@ infixl 7 <+?>
(<+?>) :: Doc ann -> Maybe (Doc ann) -> Doc ann
(<+?>) a = maybe a (a <+>)
-infixl 7 +>
+infixr 7 +>
(+>) :: Maybe (Doc ann) -> Doc ann -> Doc ann
(+>) = \case
Nothing -> id
Just a -> (a <+>)
-infixl 7 >
+infixr 7 ?<>
-(>) :: Semigroup m => m -> Maybe m -> m
-(>) a = maybe a (a <>)
+(?<>) :: Semigroup m => Maybe m -> m -> m
+(?<>) = maybe id (<>)
---------------------------------------------------------------------------------
+infixl 7 <>?
+
+(<>?) :: Semigroup m => m -> Maybe m -> m
+(<>?) a = maybe a (a <>)
data Indexed a = Indexed
{ _indexedIx :: Int,
diff --git a/src/Juvix/Prelude/Html.hs b/src/Juvix/Prelude/Html.hs
new file mode 100644
index 0000000000..232aac5cf8
--- /dev/null
+++ b/src/Juvix/Prelude/Html.hs
@@ -0,0 +1,28 @@
+module Juvix.Prelude.Html where
+
+import Juvix.Prelude
+import Text.Blaze.Html5 as Html hiding (map)
+import Text.Blaze.Html5.Attributes qualified as Attr
+
+cssLink :: AttributeValue -> Html
+cssLink css =
+ link
+ ! Attr.href css
+ ! Attr.rel "stylesheet"
+ ! Attr.type_ "text/css"
+
+ayuCss :: Html
+ayuCss = cssLink "assets/source-ayu-light.css"
+
+nordCss :: Html
+nordCss = cssLink "assets/source-nord.css"
+
+highlightJs :: Html
+highlightJs =
+ script
+ ! Attr.src "assets/highlight.js"
+ ! Attr.type_ "text/javascript"
+ $ mempty
+
+metaUtf8 :: Html
+metaUtf8 = meta ! Attr.charset "UTF-8"
diff --git a/src/Juvix/Prelude/Pretty.hs b/src/Juvix/Prelude/Pretty.hs
index 0d066b4e9f..3b8140551a 100644
--- a/src/Juvix/Prelude/Pretty.hs
+++ b/src/Juvix/Prelude/Pretty.hs
@@ -78,6 +78,9 @@ vsep2 = concatWith (\a b -> a <> line <> line <> b)
hsep :: Foldable f => f (Doc a) -> Doc a
hsep = PP.hsep . toList
+enclose1 :: Doc a -> Doc a -> Doc a
+enclose1 delim = enclose delim delim
+
vsepMaybe :: Foldable f => f (Doc a) -> Maybe (Doc a)
vsepMaybe l
| null l = Nothing
diff --git a/src/Juvix/Syntax/Abstract/Name.hs b/src/Juvix/Syntax/Abstract/Name.hs
index e012a77f9f..03f0a56542 100644
--- a/src/Juvix/Syntax/Abstract/Name.hs
+++ b/src/Juvix/Syntax/Abstract/Name.hs
@@ -51,7 +51,7 @@ prettyName :: HasNameKindAnn a => Bool -> Name -> Doc a
prettyName showNameId n =
annotate
(annNameKind (n ^. nameKind))
- (pretty (n ^. namePretty) > uid)
+ (pretty (n ^. namePretty) <>? uid)
where
uid
| showNameId = Just ("@" <> pretty (n ^. nameId))
diff --git a/src/Juvix/Syntax/Abstract/Pretty/Ann.hs b/src/Juvix/Syntax/Abstract/Pretty/Ann.hs
index c894c221b7..a21a5607a7 100644
--- a/src/Juvix/Syntax/Abstract/Pretty/Ann.hs
+++ b/src/Juvix/Syntax/Abstract/Pretty/Ann.hs
@@ -16,6 +16,7 @@ fromScopedAnn s = case s of
S.AnnKind nk -> Just (AnnKind nk)
S.AnnKeyword -> Nothing
S.AnnDelimiter -> Nothing
+ S.AnnComment -> Nothing
S.AnnUnkindedSym -> Nothing
S.AnnDef {} -> Nothing
S.AnnRef {} -> Nothing
diff --git a/src/Juvix/Syntax/Concrete/Language.hs b/src/Juvix/Syntax/Concrete/Language.hs
index 60a2a2ed70..68a601e6a8 100644
--- a/src/Juvix/Syntax/Concrete/Language.hs
+++ b/src/Juvix/Syntax/Concrete/Language.hs
@@ -187,6 +187,7 @@ instance HasLoc OperatorSyntaxDef where
data TypeSignature (s :: Stage) = TypeSignature
{ _sigName :: FunctionName s,
_sigType :: ExpressionType s,
+ _sigDoc :: Maybe (Judoc s),
_sigBuiltin :: Maybe BuiltinFunction,
_sigTerminating :: Bool
}
@@ -202,7 +203,8 @@ deriving stock instance (Ord (ExpressionType s), Ord (SymbolType s)) => Ord (Typ
-------------------------------------------------------------------------------
data AxiomDef (s :: Stage) = AxiomDef
- { _axiomName :: SymbolType s,
+ { _axiomDoc :: Maybe (Judoc s),
+ _axiomName :: SymbolType s,
_axiomBuiltin :: Maybe BuiltinAxiom,
_axiomType :: ExpressionType s
}
@@ -223,6 +225,7 @@ type InductiveName s = SymbolType s
data InductiveConstructorDef (s :: Stage) = InductiveConstructorDef
{ _constructorName :: InductiveConstructorName s,
+ _constructorDoc :: Maybe (Judoc s),
_constructorType :: ExpressionType s
}
@@ -245,6 +248,7 @@ deriving stock instance (Ord (ExpressionType s), Ord (SymbolType s)) => Ord (Ind
data InductiveDef (s :: Stage) = InductiveDef
{ _inductiveBuiltin :: Maybe BuiltinInductive,
+ _inductiveDoc :: Maybe (Judoc s),
_inductiveName :: InductiveName s,
_inductiveParameters :: [InductiveParameter s],
_inductiveType :: Maybe (ExpressionType s),
@@ -383,6 +387,7 @@ type LocalModuleName s = SymbolType s
data Module (s :: Stage) (t :: ModuleIsTop) = Module
{ _modulePath :: ModulePathType s t,
_moduleParameters :: [InductiveParameter s],
+ _moduleDoc :: Maybe (Judoc s),
_moduleBody :: [Statement s]
}
@@ -897,7 +902,30 @@ data ExpressionAtoms (s :: Stage) = ExpressionAtoms
_expressionAtomsLoc :: Interval
}
+newtype Judoc (s :: Stage) = Judoc
+ { _block :: [JudocAtom s]
+ }
+ deriving newtype (Semigroup, Monoid)
+
+deriving stock instance (Show (ExpressionType s), Show (SymbolType s)) => Show (Judoc s)
+
+deriving stock instance (Eq (ExpressionType s), Eq (SymbolType s)) => Eq (Judoc s)
+
+deriving stock instance (Ord (ExpressionType s), Ord (SymbolType s)) => Ord (Judoc s)
+
+data JudocAtom (s :: Stage)
+ = JudocExpression (ExpressionType s)
+ | JudocText Text
+ | JudocNewline
+
+deriving stock instance (Show (ExpressionType s), Show (SymbolType s)) => Show (JudocAtom s)
+
+deriving stock instance (Eq (ExpressionType s), Eq (SymbolType s)) => Eq (JudocAtom s)
+
+deriving stock instance (Ord (ExpressionType s), Ord (SymbolType s)) => Ord (JudocAtom s)
+
makeLenses ''PatternArg
+makeLenses ''Judoc
makeLenses ''Function
makeLenses ''InductiveDef
makeLenses ''PostfixApplication
diff --git a/src/Juvix/Syntax/Concrete/Name.hs b/src/Juvix/Syntax/Concrete/Name.hs
index 2d2c861e2c..bcfd4a41a1 100644
--- a/src/Juvix/Syntax/Concrete/Name.hs
+++ b/src/Juvix/Syntax/Concrete/Name.hs
@@ -2,7 +2,7 @@ module Juvix.Syntax.Concrete.Name where
import Data.List.NonEmpty.Extra qualified as NonEmpty
import Juvix.Prelude
-import Juvix.Prelude.Pretty
+import Juvix.Prelude.Pretty as Pretty
import Juvix.Syntax.Loc
type Symbol = WithLoc Text
@@ -70,6 +70,10 @@ data TopModulePath = TopModulePath
makeLenses ''TopModulePath
+instance Pretty TopModulePath where
+ pretty (TopModulePath path name) =
+ mconcat (punctuate Pretty.dot (map pretty (snoc path name)))
+
instance HasLoc TopModulePath where
getLoc TopModulePath {..} =
case _modulePathDir of
@@ -79,17 +83,28 @@ instance HasLoc TopModulePath where
topModulePathToFilePath :: FilePath -> TopModulePath -> FilePath
topModulePathToFilePath = topModulePathToFilePath' (Just ".juvix")
-topModulePathToFilePath' ::
- Maybe String -> FilePath -> TopModulePath -> FilePath
-topModulePathToFilePath' ext root mp = normalise absPath
+topModulePathToRelativeFilePath :: Maybe String -> String -> (FilePath -> FilePath -> FilePath) -> TopModulePath -> FilePath
+topModulePathToRelativeFilePath ext suffix joinpath mp = relFilePath
where
- relDirPath = foldr ((>) . toPath) mempty (mp ^. modulePathDir)
- relFilePath = relDirPath > toPath (mp ^. modulePathName)
- absPath = case ext of
- Nothing -> root > relFilePath
- Just e -> root > relFilePath <.> e
+ relDirPath :: FilePath
+ relDirPath = foldr (joinpath . toPath) mempty (mp ^. modulePathDir)
+ relFilePath :: FilePath
+ relFilePath = addExt (relDirPath `joinpath'` toPath (mp ^. modulePathName) <> suffix)
+ joinpath' :: FilePath -> FilePath -> FilePath
+ joinpath' l r
+ | null l = r
+ | otherwise = joinpath l r
+ addExt = case ext of
+ Nothing -> id
+ Just e -> (<.> e)
toPath :: Symbol -> FilePath
- toPath s = unpack (s ^. withLocParam)
+ toPath s = unpack (s ^. symbolText)
+
+topModulePathToFilePath' ::
+ Maybe String -> FilePath -> TopModulePath -> FilePath
+topModulePathToFilePath' ext root mp =
+ normalise
+ (root > topModulePathToRelativeFilePath ext "" (>) mp)
topModulePathToDottedPath :: IsString s => TopModulePath -> s
topModulePathToDottedPath (TopModulePath l r) =
diff --git a/src/Juvix/Syntax/Concrete/Scoped/Error/Types.hs b/src/Juvix/Syntax/Concrete/Scoped/Error/Types.hs
index d64b92652b..15417451b0 100644
--- a/src/Juvix/Syntax/Concrete/Scoped/Error/Types.hs
+++ b/src/Juvix/Syntax/Concrete/Scoped/Error/Types.hs
@@ -259,8 +259,9 @@ instance ToGenericError NotInScope where
}
where
msg =
- "Symbol not in scope:" <+> ppCode _notInScopeSymbol
- > ((line <>) <$> suggestion)
+ "Symbol not in scope:"
+ <+> ppCode _notInScopeSymbol
+ <>? ((line <>) <$> suggestion)
suggestion :: Maybe (Doc a)
suggestion
| null suggestions = Nothing
diff --git a/src/Juvix/Syntax/Concrete/Scoped/Pretty/Ann.hs b/src/Juvix/Syntax/Concrete/Scoped/Pretty/Ann.hs
index 1f58d9dce0..b52b767d0c 100644
--- a/src/Juvix/Syntax/Concrete/Scoped/Pretty/Ann.hs
+++ b/src/Juvix/Syntax/Concrete/Scoped/Pretty/Ann.hs
@@ -6,6 +6,7 @@ import Juvix.Syntax.Concrete.Scoped.Name qualified as S
data Ann
= AnnKind S.NameKind
| AnnKeyword
+ | AnnComment
| AnnDelimiter
| AnnLiteralString
| AnnLiteralInteger
diff --git a/src/Juvix/Syntax/Concrete/Scoped/Pretty/Ansi.hs b/src/Juvix/Syntax/Concrete/Scoped/Pretty/Ansi.hs
index 89322b919a..db099b74f7 100644
--- a/src/Juvix/Syntax/Concrete/Scoped/Pretty/Ansi.hs
+++ b/src/Juvix/Syntax/Concrete/Scoped/Pretty/Ansi.hs
@@ -9,6 +9,7 @@ stylize :: Ann -> AnsiStyle
stylize a = case a of
AnnKind k -> nameKindAnsi k
AnnDelimiter -> colorDull White
+ AnnComment -> colorDull Cyan
AnnKeyword -> colorDull Blue
AnnDef {} -> mempty
AnnRef {} -> mempty
diff --git a/src/Juvix/Syntax/Concrete/Scoped/Pretty/Base.hs b/src/Juvix/Syntax/Concrete/Scoped/Pretty/Base.hs
index dca4110df2..0780904be0 100644
--- a/src/Juvix/Syntax/Concrete/Scoped/Pretty/Base.hs
+++ b/src/Juvix/Syntax/Concrete/Scoped/Pretty/Base.hs
@@ -319,7 +319,12 @@ instance PrettyCode BackendItem where
backend <+> kwMapsto <+> ppStringLit _backendItemCode
ppStringLit :: Text -> Doc Ann
-ppStringLit = annotate AnnLiteralString . doubleQuotes . pretty
+ppStringLit = annotate AnnLiteralString . doubleQuotes . escaped
+ where
+ showChar :: Char -> String
+ showChar c = showLitChar c ("" :: String)
+ escaped :: Text -> Doc a
+ escaped = mconcatMap (pretty . showChar) . unpack
ppTopModulePath ::
forall s r.
@@ -363,16 +368,18 @@ instance (SingI s, SingI t) => PrettyCode (Module s t) where
moduleBody' <- ppCode _moduleBody >>= indented
modulePath' <- ppModulePathType _modulePath
moduleParameters' <- ppInductiveParameters _moduleParameters
- return
- $ kwModule
+ moduleDoc' <- mapM ppCode _moduleDoc
+ return $
+ moduleDoc'
+ ?<> kwModule
<+> modulePath'
<+?> moduleParameters'
- <> kwSemicolon
- <> line
- <> moduleBody'
- <> line
- <> kwEnd
- > lastSemicolon
+ <> kwSemicolon
+ <> line
+ <> moduleBody'
+ <> line
+ <> kwEnd
+ <>? lastSemicolon
where
lastSemicolon = case sing :: SModuleIsTop t of
SModuleLocal -> Nothing
@@ -408,7 +415,8 @@ instance SingI s => PrettyCode (InductiveConstructorDef s) where
ppCode InductiveConstructorDef {..} = do
constructorName' <- annDef _constructorName <$> ppSymbol _constructorName
constructorType' <- ppExpression _constructorType
- return $ constructorName' <+> kwColon <+> constructorType'
+ doc' <- mapM ppCode _constructorDoc
+ return $ doc' ?<> constructorName' <+> kwColon <+> constructorType'
instance PrettyCode BuiltinInductive where
ppCode i = return (kwBuiltin <+> keyword' i)
@@ -419,26 +427,34 @@ instance PrettyCode BuiltinFunction where
instance PrettyCode BuiltinAxiom where
ppCode i = return (kwBuiltin <+> keyword' i)
+ppInductiveSignature :: forall r s. (SingI s, Members '[Reader Options] r) => InductiveDef s -> Sem r (Doc Ann)
+ppInductiveSignature InductiveDef {..} = do
+ inductivebuiltin' <- traverse ppCode _inductiveBuiltin
+ inductiveName' <- annDef _inductiveName <$> ppSymbol _inductiveName
+ inductiveParameters' <- ppInductiveParameters _inductiveParameters
+ inductiveType' <- ppTypeType
+ return $
+ inductivebuiltin'
+ +> kwInductive
+ <+> inductiveName'
+ <+?> inductiveParameters'
+ <+?> inductiveType'
+ where
+ ppTypeType :: Sem r (Maybe (Doc Ann))
+ ppTypeType = case _inductiveType of
+ Nothing -> return Nothing
+ Just e -> Just . (kwColon <+>) <$> ppExpression e
+
+instance PrettyCode (Doc Ann) where
+ ppCode d = return d
+
instance SingI s => PrettyCode (InductiveDef s) where
ppCode :: forall r. Members '[Reader Options] r => InductiveDef s -> Sem r (Doc Ann)
- ppCode InductiveDef {..} = do
- inductiveName' <- annDef _inductiveName <$> ppSymbol _inductiveName
- inductiveParameters' <- ppInductiveParameters _inductiveParameters
- inductiveType' <- ppTypeType
+ ppCode d@InductiveDef {..} = do
+ doc' <- mapM ppCode _inductiveDoc
+ sig' <- ppInductiveSignature d
inductiveConstructors' <- ppBlock _inductiveConstructors
- inductivebuiltin' <- traverse ppCode _inductiveBuiltin
- return $
- inductivebuiltin'
- +> kwInductive
- <+> inductiveName'
- <+?> inductiveParameters'
- <+?> inductiveType'
- <+> inductiveConstructors'
- where
- ppTypeType :: Sem r (Maybe (Doc Ann))
- ppTypeType = case _inductiveType of
- Nothing -> return Nothing
- Just e -> Just . (kwColon <+>) <$> ppExpression e
+ return $ doc' ?<> sig' <+> inductiveConstructors'
dotted :: Foldable f => f (Doc Ann) -> Doc Ann
dotted = concatWith (surround kwDot)
@@ -487,7 +503,7 @@ instance PrettyCode n => PrettyCode (S.Name' n) where
ppCode S.Name' {..} = do
nameConcrete' <- annotateKind _nameKind <$> ppCode _nameConcrete
uid <- nameIdSuffix _nameId
- return $ annSRef (nameConcrete' > uid)
+ return $ annSRef (nameConcrete' <>? uid)
where
annSRef :: Doc Ann -> Doc Ann
annSRef = annotate (AnnRef (_nameDefinedIn ^. S.absTopModulePath) _nameId)
@@ -505,7 +521,7 @@ instance SingI s => PrettyCode (OpenModule s) where
openParameters' <- ppOpenParams
let openPublic' = ppPublic
import_
- | _openModuleImport = Just Str.import_
+ | _openModuleImport = Just kwImport
| otherwise = Nothing
return $ kwOpen <+?> import_ <+> openModuleName' <+?> openParameters' <+?> openUsingHiding' <+?> openPublic'
where
@@ -531,13 +547,43 @@ instance SingI s => PrettyCode (OpenModule s) where
Public -> Just kwPublic
NoPublic -> Nothing
+instance SingI s => PrettyCode (Judoc s) where
+ ppCode :: forall r. Members '[Reader Options] r => Judoc s -> Sem r (Doc Ann)
+ ppCode (Judoc blck) =
+ mconcatMapM ppLine (linesBy isNewLine blck)
+ where
+ isNewLine :: JudocAtom s -> Bool
+ isNewLine = \case
+ JudocNewline -> True
+ _ -> False
+ ppLine :: [JudocAtom s] -> Sem r (Doc Ann)
+ ppLine atoms = do
+ atoms' <- mconcatMapM ppCode atoms
+ let prefix = pretty (Str.judocStart :: Text) :: Doc Ann
+ return $ prefix <> atoms' <> line
+
+instance SingI s => PrettyCode (JudocAtom s) where
+ ppCode :: forall r. (Members '[Reader Options] r) => JudocAtom s -> Sem r (Doc Ann)
+ ppCode = \case
+ JudocNewline -> return "\n"
+ JudocExpression e -> goExpression e
+ JudocText t -> return (annotate AnnComment (pretty t))
+ where
+ goExpression :: ExpressionType s -> Sem r (Doc Ann)
+ goExpression e = do
+ e' <- ppExpression e
+ return $ semiDelim e'
+ semiDelim :: Doc Ann -> Doc Ann
+ semiDelim = enclose1 (annotate AnnComment ";")
+
instance SingI s => PrettyCode (TypeSignature s) where
ppCode TypeSignature {..} = do
let sigTerminating' = if _sigTerminating then kwTerminating <> line else mempty
sigName' <- annDef _sigName <$> ppSymbol _sigName
sigType' <- ppExpression _sigType
builtin' <- traverse ppCode _sigBuiltin
- return $ builtin' +> sigTerminating' <> sigName' <+> kwColon <+> sigType'
+ doc' <- mapM ppCode _sigDoc
+ return $ doc' ?<> builtin' +> sigTerminating' <> sigName' <+> kwColon <+> sigType'
instance SingI s => PrettyCode (Function s) where
ppCode :: forall r. Members '[Reader Options] r => Function s -> Sem r (Doc Ann)
@@ -624,10 +670,11 @@ instance SingI s => PrettyCode (WhereClause s) where
instance SingI s => PrettyCode (AxiomDef s) where
ppCode AxiomDef {..} = do
- axiomName' <- ppSymbol _axiomName
+ axiomName' <- annDef _axiomName <$> ppSymbol _axiomName
+ axiomDoc' <- mapM ppCode _axiomDoc
axiomType' <- ppExpression _axiomType
builtin' <- traverse ppCode _axiomBuiltin
- return $ builtin' +> kwAxiom <+> axiomName' <+> kwColon <+> axiomType'
+ return $ axiomDoc' ?<> builtin' +> kwAxiom <+> axiomName' <+> kwColon <+> axiomType'
instance SingI s => PrettyCode (Import s) where
ppCode :: forall r. Members '[Reader Options] r => Import s -> Sem r (Doc Ann)
@@ -833,7 +880,7 @@ ppHole w = case sing :: SStage s of
instance PrettyCode Hole where
ppCode h = do
suff <- nameIdSuffix (h ^. holeId)
- return (kwWildcard > suff)
+ return (kwWildcard <>? suff)
instance SingI s => PrettyCode (ExpressionAtom s) where
ppCode = \case
diff --git a/src/Juvix/Syntax/Concrete/Scoped/Pretty/Html.hs b/src/Juvix/Syntax/Concrete/Scoped/Pretty/Html.hs
index 46187a2961..6374e337a3 100644
--- a/src/Juvix/Syntax/Concrete/Scoped/Pretty/Html.hs
+++ b/src/Juvix/Syntax/Concrete/Scoped/Pretty/Html.hs
@@ -1,4 +1,4 @@
-module Juvix.Syntax.Concrete.Scoped.Pretty.Html (genHtml, Theme (..)) where
+module Juvix.Syntax.Concrete.Scoped.Pretty.Html where
import Data.ByteString qualified as BS
import Data.Text qualified as Text
@@ -7,6 +7,7 @@ import Data.Text.Lazy (toStrict)
import Data.Time.Clock
import Data.Time.Format
import Juvix.Prelude
+import Juvix.Prelude.Html
import Juvix.Syntax.Concrete.Language
import Juvix.Syntax.Concrete.Scoped.Name qualified as S
import Juvix.Syntax.Concrete.Scoped.Pretty.Base
@@ -25,6 +26,21 @@ data Theme
| Ayu
deriving stock (Show)
+data HtmlKind
+ = HtmlDoc
+ | HtmlSrc
+
+newtype HtmlOptions = HtmlOptions
+ { _htmlOptionsKind :: HtmlKind
+ }
+
+makeLenses ''HtmlOptions
+
+kindSuffix :: HtmlKind -> String
+kindSuffix = \case
+ HtmlDoc -> ""
+ HtmlSrc -> "-src"
+
genHtml :: Options -> Bool -> Theme -> FilePath -> Bool -> Module 'Scoped 'ModuleTop -> IO ()
genHtml opts recursive theme outputDir printMetadata entry = do
createDirectoryIfMissing True outputDir
@@ -58,26 +74,31 @@ genHtml opts recursive theme outputDir printMetadata entry = do
where
htmlFile = topModulePathToDottedPath (m ^. modulePath . S.nameConcrete) <.> ".html"
-genModule :: Options -> Bool -> UTCTime -> Theme -> Module 'Scoped 'ModuleTop -> Text
-genModule opts printMetadata utc theme m =
- toStrict $
- Html.renderHtml $
- docTypeHtml ! Attr.xmlns "http://www.w3.org/1999/xhtml" $
- mhead
- <> mbody
- <> if printMetadata then infoFooter else mempty
+genModuleHtml :: Options -> Bool -> UTCTime -> Theme -> Module 'Scoped 'ModuleTop -> Html
+genModuleHtml opts printMetadata utc theme m =
+ docTypeHtml ! Attr.xmlns "http://www.w3.org/1999/xhtml" $
+ mhead
+ <> mbody
+ <> if printMetadata then infoFooter else mempty
where
themeCss :: Html
themeCss = case theme of
Ayu -> ayuCss
Nord -> nordCss
+ htmlOpts :: HtmlOptions
+ htmlOpts =
+ HtmlOptions
+ { _htmlOptionsKind = HtmlSrc
+ }
+
+ pp :: PrettyCode a => a -> Html
+ pp = ppCodeHtml' htmlOpts opts
+
prettySrc :: Html
prettySrc =
(pre ! Attr.id "src-content") $
- renderTree $
- treeForm $
- docStream' opts m
+ pp m
mheader :: Html
mheader =
@@ -109,44 +130,69 @@ genModule opts printMetadata utc theme m =
formattedTime = formatTime defaultTimeLocale "%Y-%m-%d %-H:%M %Z" utc
-docStream' :: Options -> Module 'Scoped 'ModuleTop -> SimpleDocStream Ann
+genModule :: Options -> Bool -> UTCTime -> Theme -> Module 'Scoped 'ModuleTop -> Text
+genModule opts printMetadata utc theme =
+ toStrict
+ . Html.renderHtml
+ . genModuleHtml opts printMetadata utc theme
+
+docStream' :: PrettyCode a => Options -> a -> SimpleDocStream Ann
docStream' opts m = layoutPretty defaultLayoutOptions (runPrettyCode opts m)
-renderTree :: SimpleDocTree Ann -> Html
+renderTree :: Members '[Reader HtmlOptions] r => SimpleDocTree Ann -> Sem r Html
renderTree = go
-go :: SimpleDocTree Ann -> Html
+ppCodeHtml' :: PrettyCode a => HtmlOptions -> Options -> a -> Html
+ppCodeHtml' htmlOpts opts = run . runReader htmlOpts . renderTree . treeForm . docStream' opts
+
+ppCodeHtml :: (Members '[Reader HtmlOptions] r, PrettyCode a) => a -> Sem r Html
+ppCodeHtml x = do
+ o <- ask
+ return (ppCodeHtml' o defaultOptions x)
+
+go :: Members '[Reader HtmlOptions] r => SimpleDocTree Ann -> Sem r Html
go sdt = case sdt of
- STEmpty -> mempty
- STChar c -> toHtml c
- STText _ t -> toHtml t
- STLine s -> "\n" <> toHtml (textSpaces s)
- STAnn ann content -> putTag ann (go content)
+ STEmpty -> return mempty
+ STChar c -> return (toHtml c)
+ STText _ t -> return (toHtml t)
+ STLine s -> return ("\n" <> toHtml (textSpaces s))
+ STAnn ann content -> go content >>= putTag ann
STConcat l -> mconcatMap go l
where
textSpaces :: Int -> Text
textSpaces n = Text.replicate n (Text.singleton ' ')
-putTag :: Ann -> Html -> Html
+putTag :: forall r. Members '[Reader HtmlOptions] r => Ann -> Html -> Sem r Html
putTag ann x = case ann of
- AnnKind k -> tagKind k x
- AnnLiteralInteger -> Html.span ! Attr.class_ "ju-number" $ x
- AnnLiteralString -> Html.span ! Attr.class_ "ju-string" $ x
- AnnKeyword -> Html.span ! Attr.class_ "ju-keyword" $ x
- AnnUnkindedSym -> Html.span ! Attr.class_ "ju-var" $ x
- AnnDelimiter -> Html.span ! Attr.class_ "ju-delimiter" $ x
- AnnDef tmp ni -> tagDef tmp ni
+ AnnKind k -> return (tagKind k x)
+ AnnLiteralInteger -> return (Html.span ! Attr.class_ "ju-number" $ x)
+ AnnLiteralString -> return (Html.span ! Attr.class_ "ju-string" $ x)
+ AnnKeyword -> return (Html.span ! Attr.class_ "ju-keyword" $ x)
+ AnnUnkindedSym -> return (Html.span ! Attr.class_ "ju-var" $ x)
+ AnnComment -> return (Html.span ! Attr.class_ "ju-var" $ x) -- TODO add comment class
+ AnnDelimiter -> return (Html.span ! Attr.class_ "ju-delimiter" $ x)
+ AnnDef tmp ni -> boldDefine <*> tagDef tmp ni
AnnRef tmp ni -> tagRef tmp ni
where
- tagDef :: TopModulePath -> S.NameId -> Html
- tagDef tmp nid =
- Html.span ! Attr.id (nameIdAttr nid) $
- tagRef tmp nid
-
- tagRef tmp ni =
- Html.span ! Attr.class_ "annot" $
- a ! Attr.href (nameIdAttrRef tmp ni) $
- x
+ boldDefine :: Sem r (Html -> Html)
+ boldDefine =
+ asks (^. htmlOptionsKind) <&> \case
+ HtmlDoc -> Html.span ! Attr.class_ "ju-define"
+ HtmlSrc -> id
+
+ tagDef :: TopModulePath -> S.NameId -> Sem r Html
+ tagDef tmp nid = do
+ ref' <- tagRef tmp nid
+ return $ (Html.span ! Attr.id (nameIdAttr nid)) ref'
+
+ tagRef :: TopModulePath -> S.NameId -> Sem r Html
+ tagRef tmp ni = do
+ pth <- nameIdAttrRef tmp (Just ni)
+ return $
+ Html.span ! Attr.class_ "annot" $
+ a ! Attr.href pth $
+ x
+
tagKind k =
Html.span
! Attr.class_
@@ -163,29 +209,15 @@ putTag ann x = case ann of
nameIdAttr :: S.NameId -> AttributeValue
nameIdAttr (S.NameId k) = fromString . show $ k
-nameIdAttrRef :: TopModulePath -> S.NameId -> AttributeValue
-nameIdAttrRef tp s =
- topModulePathToDottedPath tp <> ".html" <> preEscapedToValue '#' <> nameIdAttr s
-
-cssLink :: AttributeValue -> Html
-cssLink css =
- link
- ! Attr.href css
- ! Attr.rel "stylesheet"
- ! Attr.type_ "text/css"
-
-ayuCss :: Html
-ayuCss = cssLink "assets/source-ayu-light.css"
-
-nordCss :: Html
-nordCss = cssLink "assets/source-nord.css"
-
-highlightJs :: Html
-highlightJs =
- script
- ! Attr.src "assets/highlight.js"
- ! Attr.type_ "text/javascript"
- $ mempty
+moduleDocRelativePath :: Members '[Reader HtmlOptions] r => TopModulePath -> Sem r FilePath
+moduleDocRelativePath m = do
+ suff <- kindSuffix <$> asks (^. htmlOptionsKind)
+ return (topModulePathToRelativeFilePath (Just "html") suff joinDot m)
+ where
+ joinDot :: FilePath -> FilePath -> FilePath
+ joinDot l r = l <.> r
-metaUtf8 :: Html
-metaUtf8 = meta ! Attr.charset "UTF-8"
+nameIdAttrRef :: Members '[Reader HtmlOptions] r => TopModulePath -> Maybe S.NameId -> Sem r AttributeValue
+nameIdAttrRef tp s = do
+ pth <- moduleDocRelativePath tp
+ return (fromString pth <> preEscapedToValue '#' <>? (nameIdAttr <$> s))
diff --git a/src/Juvix/Syntax/Concrete/Scoped/Utils.hs b/src/Juvix/Syntax/Concrete/Scoped/Utils.hs
index 560c8a6a02..e1a2481726 100644
--- a/src/Juvix/Syntax/Concrete/Scoped/Utils.hs
+++ b/src/Juvix/Syntax/Concrete/Scoped/Utils.hs
@@ -18,8 +18,7 @@ getAllModules' ::
Member (Output (S.NameId, Module 'Scoped 'ModuleTop)) r =>
Module 'Scoped 'ModuleTop ->
Sem r ()
-getAllModules' m = do
- recordModule m
+getAllModules' m = recordModule m
where
recordModule :: Module 'Scoped 'ModuleTop -> Sem r ()
recordModule n = do
diff --git a/src/Juvix/Syntax/MonoJuvix/Pretty/Base.hs b/src/Juvix/Syntax/MonoJuvix/Pretty/Base.hs
index 5d30dcd55b..9f4e22c97d 100644
--- a/src/Juvix/Syntax/MonoJuvix/Pretty/Base.hs
+++ b/src/Juvix/Syntax/MonoJuvix/Pretty/Base.hs
@@ -39,10 +39,10 @@ instance PrettyCode Name where
if
| showNameId -> Just . ("@" <>) <$> ppCode (n ^. nameId)
| otherwise -> return Nothing
- return
- $ annotate (AnnKind (n ^. nameKind))
- $ pretty (n ^. nameText)
- > uid
+ return $
+ annotate (AnnKind (n ^. nameKind)) $
+ pretty (n ^. nameText)
+ <>? uid
instance PrettyCode Iden where
ppCode :: Member (Reader Options) r => Iden -> Sem r (Doc Ann)
diff --git a/src/Juvix/Translation/ScopedToAbstract.hs b/src/Juvix/Translation/ScopedToAbstract.hs
index 2d3b086f6d..2a6ed7a77b 100644
--- a/src/Juvix/Translation/ScopedToAbstract.hs
+++ b/src/Juvix/Translation/ScopedToAbstract.hs
@@ -65,7 +65,7 @@ goModule m = case sing :: SModuleIsTop t of
SModuleLocal -> goModule' m
where
goModule' :: Module 'Scoped t -> Sem r Abstract.Module
- goModule' (Module n par b) = case par of
+ goModule' (Module n par _ b) = case par of
[] -> Abstract.Module modName <$> goModuleBody b
_ -> unsupported "Module parameters"
where
@@ -265,7 +265,7 @@ goConstructorDef ::
Member (Error ScoperError) r =>
InductiveConstructorDef 'Scoped ->
Sem r Abstract.InductiveConstructorDef
-goConstructorDef (InductiveConstructorDef c ty) =
+goConstructorDef (InductiveConstructorDef c _ ty) =
Abstract.InductiveConstructorDef (goSymbol c) <$> goExpression ty
goExpression ::
diff --git a/tests/positive/Judoc.juvix b/tests/positive/Judoc.juvix
new file mode 100644
index 0000000000..1b7924f87f
--- /dev/null
+++ b/tests/positive/Judoc.juvix
@@ -0,0 +1,21 @@
+module Judoc;
+
+axiom A : Type;
+axiom b : Type;
+
+inductive T {
+
+};
+
+--- he ;id A; and ;A A id T A id; this is another ;id id id; example
+--- hahahah
+--- and another one ;T;
+id : {A : Type} → A → A;
+id a ≔ a;
+
+--- hellowww
+id2 : {A : Type} → A → A;
+id2 a ≔ a;
+
+
+end;