Reproduce roc check bug

To repro: run `roc check` in www/wip_new_website/
This commit is contained in:
Richard Feldman 2023-10-25 21:18:15 -04:00
parent 563713b4b8
commit 34ef8436b5
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
4 changed files with 332 additions and 13 deletions

View file

@ -3,5 +3,105 @@ app "helloWorld"
imports [pf.Stdout] imports [pf.Stdout]
provides [main] to pf provides [main] to pf
Section : [Desc (List Token) Str, Indent, Outdent, Newline]
Token : [Kw Str, Ident Str, Str Str, Num Str]
main = main =
Stdout.line "Hello, World!" output =
# subject = "Awesome Programmer"
#
# helloWorld =
# Str.withCapacity 45
# |> Str.concat greeting
# |> Str.concat ", "
# |> Str.concat subject
# |> Str.concat "!"
sectionsToStr [
Desc [Ident "subject", Kw "=", Str "Awesome Programmer"] "<p>This assigns the name <code class=\"ident\">subject</code> to the string \"Awesome programmer\".</p><p>In Roc, assignments are always constant, which means writing <code class=\"ident\">subject =</code> again in the same scope would give an error.</p><p><a href=\"https://www.roc-lang.org/tutorial#naming-things\">Learn more about naming things</a></p>",
Newline,
Newline,
Desc [Ident "helloWorld", Kw "="] "<p>This assigns the name <code class=\"ident\">helloWorld</code> to the value returned by this chain of function calls.</p><p>In Roc, assignments are always constant, which means writing <code class=\"ident\">helloWorld =</code> again in the same scope would give an error.</p><p><a href=\"https://www.roc-lang.org/tutorial#naming-things\">Learn more about naming things</a></p>",
Indent,
Desc [Ident "Str.withCapacity", Num "45"] "<p>This calls the <a href=\"https://www.roc-lang.org/builtins/Str#withCapacity\"><code class=\"ident\">Str.withCapacity</code> function</a> passing <code class=\"ident\">45</code> as its argument.</p><p>This creates a new string with capacity for 45 bytes without needing to allocate more space.</p>",
Newline,
Desc [Kw "|>", Ident "Str.concat", Ident "greeting"] "<p>This calls the <a href=\"https://www.roc-lang.org/builtins/Str#concat\"><code class=\"ident\">Str.concat</code> function</a> passing <code class=\"ident\">greeting</code> as its second argument, and the output of the pipeline up to this point as its first argument.</p>",
]
Stdout.line output
tokensToStr : List Token -> Str
tokensToStr = \tokens ->
List.walk tokens "" \buf, token ->
bufWithSpace =
if Str.isEmpty buf then
buf
else
Str.concat buf " "
when token is
Kw str ->
Str.concat bufWithSpace "<span class=\"kw\">\(str)</span>"
Num str ->
Str.concat bufWithSpace "<span class=\"literal\">\(str)</span>"
Str str ->
Str.concat bufWithSpace "<span class=\"literal\">\"\(str)\"</span>"
Ident str ->
html =
List.walk (Str.split str ".") "" \accum, ident ->
identHtml = "<span class=\"ident\">\(ident)</span>"
if Str.isEmpty accum then
identHtml
else
"\(accum)<span class=\"kw\">.</span>\(identHtml)"
Str.concat bufWithSpace html
sectionsToStr : List Section -> Str
sectionsToStr = \sections ->
answer = List.walk sections { buf: "", count: 0, indent: 0 } \{ buf, count, indent }, section ->
bufWithSpace =
if Str.isEmpty buf then
buf
else if buf |> Str.endsWith "\n" then
Str.concat buf (Str.repeat " " indent)
else
Str.concat buf " "
(afterSpaces, nextCount) =
when section is
Newline | Indent | Outdent ->
# Indent and outdent changes happen on the next iteration,
# so we only need a newline for them here.
(Str.concat buf "\n", count)
Desc tokens str ->
html = radio count (tokensToStr tokens) str
(Str.concat bufWithSpace html, count + 1)
nextIndent =
when section is
Indent -> indent + 4
Outdent -> indent - 4
Newline | Desc _ _ -> indent
{
buf: afterSpaces,
count: nextCount,
indent: nextIndent
}
answer.buf
radio : U16, Str, Str -> Str
radio = \index, labelHtml, descHtml ->
# The first radio button should always be checked, and none of the others should be.
checkedHtml = if index == 0 then " checked" else ""
"""
<input class="interactive-radio" type="radio" name="r" id="r\(Num.toStr index)" autocomplete=\"off\"\(checkedHtml)><label for="r\(Num.toStr index)" title="Tap to learn about this syntax">\(labelHtml)</label><div class="interactive-desc">\(descHtml)</div>
"""

View file

@ -0,0 +1,151 @@
interface InteractiveExample
exposes [view]
imports [pf.Html.{ pre, samp }, pf.Html.Attributes.{ class }]
Section : [Desc (List Token) Str, Indent, Outdent, Newline]
Token : [Kw Str, Ident Str, Str Str, Num Str, Comment Str, ParensAround (List Token), Lambda (List Token)]
view : Html.Node
view =
output =
# # Select anything here to see an explanation.
# main =
# cacheUserInfo (Path.fromStr "url.txt")
# |> Task.onErr handleErr
#
# cacheUserInfo = \filename ->
# url <- File.readUtf8 filename |> Task.await
# { username, email } <- Http.get url Json.codec |> Task.await
#
# File.writeUtf8 (Path.fromStr "\(username).txt") email
#
# handleUrl = \err ->
# when err is
# HttpErr url _ -> Stderr.line "Error fetching URL \(url)"
# FileReadErr path _ -> Stderr.line "Error reading \(Path.display path)"
# FileWriteErr path _ -> Stderr.line "Error writing \(Path.display path)"
sectionsToStr [
Desc [Comment "<span class='desktop'>Click anything here to see an explanation.</span><span class='mobile'>Tap anything here to\n# see an explanation.</span>"] "<p>Comments in Roc begin with a <code>#</code> and go to the end of the line.</p>",
Newline,
Desc [Ident "main", Kw "="] "<p>This begins the definition of <code class=\"ident\">main</code>, which is the code our program will run when it starts up.</p><p>In Roc, assignments are always constant, which means writing <code class=\"ident\">main =</code> again in the same scope would give an error.</p><p><a href=\"https://www.roc-lang.org/tutorial#naming-things\">Learn more about naming things</a></p>",
Indent,
Desc [Ident "cacheUserInfo", Str "\"url.txt\""] "<p>This calls the <code class=\"ident\">cacheUserInfo</code> function, passing the string <code class=\"str\">\"url.txt\"</code> as an argument.</p><p>In Roc, function arguments are separated with spaces and/or newlines. Parentheses are only used in nested function calls.</p>",
Newline,
Desc [Kw "|>", Ident "Task.onErr", Ident "handleErr"] "<p>TODO</p>",
Outdent,
Newline,
# Desc [Ident "cacheUserInfo", Kw "=", Lambda [Ident "filename"]] "<p>TODO</p>",
# Indent,
# Desc [Ident "url", Kw "<-", Ident "File.readUtf8", Ident "filename"] "<p>TODO backpassing</p>",
# Desc [Kw "|>", Ident "Task.await"] "<p>TODO Task.await</p>",
# Newline,
# Desc [Literal "{", Ident "username", Literal ",", Ident "email", Literal "}", Kw "<-"] "<p>TODO record destructuring and backpassing</p>",
]
pre [] [
samp [class "interactive-example"] [
Html.text output,
],
]
tokensToStr : List Token -> Str
tokensToStr = \tokens ->
List.walk tokens "" tokenToStr
tokenToStr : Str, Token -> Str
tokenToStr = \buf, token ->
bufWithSpace =
if Str.isEmpty buf then
buf
else
Str.concat buf " "
when token is
ParensAround wrapped ->
# Don't put spaces after opening parens or before closing parens
bufWithSpace
|> Str.concat "<span class=\"kw\">(</span>"
|> Str.concat (tokensToStr wrapped)
|> Str.concat "<span class=\"kw\">)</span>"
Lambda args ->
# Don't put spaces after opening parens or before closing parens
argsWithCommas =
args
|> List.map \t -> tokenToStr "" t
|> Str.joinWith "<span class=\"literal\">,</span> "
bufWithSpace
|> Str.concat "<span class=\"kw\">\\</span>"
|> Str.concat argsWithCommas
|> Str.concat "<span class=\"kw\"> -></span>"
Kw str ->
Str.concat bufWithSpace "<span class=\"kw\">\(str)</span>"
Num str ->
Str.concat bufWithSpace "<span class=\"literal\">\(str)</span>"
Str str ->
Str.concat bufWithSpace "<span class=\"literal\">\(str)</span>"
Comment str ->
Str.concat bufWithSpace "<span class=\"comment\"># \(str)</span>"
Ident str ->
html =
List.walk (Str.split str ".") "" \accum, ident ->
identHtml = "<span class=\"ident\">\(ident)</span>"
if Str.isEmpty accum then
identHtml
else
"\(accum)<span class=\"kw\">.</span>\(identHtml)"
Str.concat bufWithSpace html
sectionsToStr : List Section -> Str
sectionsToStr = \sections ->
answer = List.walk sections { buf: "", count: 0, indent: 0 } \{ buf, count, indent }, section ->
bufWithSpace =
if Str.isEmpty buf then
buf
else if buf |> Str.endsWith "\n" then
Str.concat buf (Str.repeat " " indent)
else
Str.concat buf " "
(afterSpaces, nextCount) =
when section is
Newline | Indent | Outdent ->
# Indent and outdent changes happen on the next iteration,
# so we only need a newline for them here.
(Str.concat buf "\n", count)
Desc tokens str ->
html = radio count (tokensToStr tokens) str
(Str.concat bufWithSpace html, count + 1)
nextIndent =
when section is
Indent -> indent + 4
Outdent -> indent - 4
Newline | Desc _ _ -> indent
{
buf: afterSpaces,
count: nextCount,
indent: nextIndent,
}
answer.buf
radio : U16, Str, Str -> Str
radio = \index, labelHtml, descHtml ->
# The first radio button should always be checked, and none of the others should be.
checkedHtml = if index == 0 then " checked" else ""
"""
<input class="interactive-radio" type="radio" name="r" id="r\(Num.toStr index)" autocomplete=\"off\"\(checkedHtml)><label for="r\(Num.toStr index)" title="Tap to learn about this syntax">\(labelHtml)</label><div class="interactive-desc">\(descHtml)</div>
"""

View file

@ -3,6 +3,7 @@ app "roc-website"
imports [ imports [
pf.Html.{ html, head, body, footer, div, main, text, nav, a, link, meta }, pf.Html.{ html, head, body, footer, div, main, text, nav, a, link, meta },
pf.Html.Attributes.{ content, name, id, href, rel, lang, class, title, charset, color }, pf.Html.Attributes.{ content, name, id, href, rel, lang, class, title, charset, color },
InteractiveExample,
] ]
provides [transformFileContent] to pf provides [transformFileContent] to pf
@ -19,7 +20,7 @@ pageData =
getPage : Str -> { title : Str, description : Str } getPage : Str -> { title : Str, description : Str }
getPage = \current -> getPage = \current ->
Dict.get pageData current Dict.get pageData current
|> Result.withDefault { title: "", description: ""} |> Result.withDefault { title: "", description: "" }
getTitle : Str -> Str getTitle : Str -> Str
getTitle = \current -> getTitle = \current ->
@ -35,6 +36,12 @@ transformFileContent = \page, htmlContent ->
view : Str, Str -> Html.Node view : Str, Str -> Html.Node
view = \page, htmlContent -> view = \page, htmlContent ->
mainBody =
if page == "index.html" then
[text htmlContent, InteractiveExample.view]
else
[text htmlContent]
html [lang "en"] [ html [lang "en"] [
head [] [ head [] [
meta [charset "utf-8"] [], meta [charset "utf-8"] [],
@ -50,15 +57,13 @@ view = \page, htmlContent ->
], ],
body [] [ body [] [
viewNavbar page, viewNavbar page,
main [] [ main [] mainBody,
text htmlContent,
],
footer [] [ footer [] [
div [id "footer"] [ div [id "footer"] [
text " powered by ", text " powered by ",
a [href "https://www.netlify.com"] [ text "Netlify"], a [href "https://www.netlify.com"] [text "Netlify"],
] ],
] ],
], ],
# TODO - add site.js if needed # TODO - add site.js if needed
# script [src "/site.js"] [], # script [src "/site.js"] [],
@ -81,17 +86,22 @@ viewNavbar = \page ->
], ],
] ]
rocLogo : Html.Node
rocLogo = rocLogo =
(Html.element "svg") [ (Html.element "svg")
[
(Html.attribute "viewBox") "0 -6 51 58", (Html.attribute "viewBox") "0 -6 51 58",
(Html.attribute "xmlns") "http://www.w3.org/2000/svg", (Html.attribute "xmlns") "http://www.w3.org/2000/svg",
(Html.attribute "aria-labelledby") "logo-link", (Html.attribute "aria-labelledby") "logo-link",
(Html.attribute "role") "img", (Html.attribute "role") "img",
class "roc-logo" class "roc-logo",
] [ ]
[
(Html.element "title") [id "logo-link"] [text "Return to Roc Home"], (Html.element "title") [id "logo-link"] [text "Return to Roc Home"],
(Html.element "polygon") [ (Html.element "polygon")
[
(Html.attribute "role") "presentation", (Html.attribute "role") "presentation",
(Html.attribute "points") "0,0 23.8834,3.21052 37.2438,19.0101 45.9665,16.6324 50.5,22 45,22 44.0315,26.3689 26.4673,39.3424 27.4527,45.2132 17.655,53 23.6751,22.7086", (Html.attribute "points") "0,0 23.8834,3.21052 37.2438,19.0101 45.9665,16.6324 50.5,22 45,22 44.0315,26.3689 26.4673,39.3424 27.4527,45.2132 17.655,53 23.6751,22.7086",
] [], ]
[],
] ]

View file

@ -63,6 +63,17 @@ footer {
margin: 0 auto; margin: 0 auto;
} }
/* Used for e.g. displaying the instruction "Click" on desktop and "Touch" on mobile.
* When we think we're on mobile (based on max-width), we can switch the instruction.
*/
.desktop {
display: inline;
}
.mobile {
display: none;
}
section p:last-child { section p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@ -303,6 +314,14 @@ li {
/* Mobile-friendly screen width */ /* Mobile-friendly screen width */
@media only screen and (max-width: 1024px) { @media only screen and (max-width: 1024px) {
/* Used for e.g. displaying the instruction "Click" on desktop and "Touch" on mobile. */
.desktop {
display: none;
}
.mobile {
display: inline;
}
p, p,
aside, aside,
@ -759,3 +778,42 @@ code .dim {
line-height: 1.5; line-height: 1.5;
margin-bottom: 2em; margin-bottom: 2em;
} }
/* Interactive example on homepage */
.interactive-example {
position: relative;
display: block;
width: 100%;
height: 800px;
padding-right: 300px;
cursor: default;
}
.interactive-example label:hover,
.interactive-radio:checked+label {
background-color: turquoise;
cursor: pointer;
}
.interactive-desc {
display: none;
background-color: #ddd;
padding: 8px 16px;
margin-top: 9px;
}
.interactive-radio {
display: none;
}
.interactive-radio:checked+label+.interactive-desc {
display: block;
position: absolute;
top: 0;
right: 300px;
width: 300px;
white-space: normal;
font-family: -apple-system, BlinkMacSystemFont, Roboto, Helvetica, Arial;
}