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]
provides [main] to pf
Section : [Desc (List Token) Str, Indent, Outdent, Newline]
Token : [Kw Str, Ident Str, Str Str, Num Str]
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 [
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 },
InteractiveExample,
]
provides [transformFileContent] to pf
@ -19,7 +20,7 @@ pageData =
getPage : Str -> { title : Str, description : Str }
getPage = \current ->
Dict.get pageData current
|> Result.withDefault { title: "", description: ""}
|> Result.withDefault { title: "", description: "" }
getTitle : Str -> Str
getTitle = \current ->
@ -35,6 +36,12 @@ transformFileContent = \page, htmlContent ->
view : Str, Str -> Html.Node
view = \page, htmlContent ->
mainBody =
if page == "index.html" then
[text htmlContent, InteractiveExample.view]
else
[text htmlContent]
html [lang "en"] [
head [] [
meta [charset "utf-8"] [],
@ -50,15 +57,13 @@ view = \page, htmlContent ->
],
body [] [
viewNavbar page,
main [] [
text htmlContent,
],
main [] mainBody,
footer [] [
div [id "footer"] [
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
# script [src "/site.js"] [],
@ -81,17 +86,22 @@ viewNavbar = \page ->
],
]
rocLogo : Html.Node
rocLogo =
(Html.element "svg") [
(Html.element "svg")
[
(Html.attribute "viewBox") "0 -6 51 58",
(Html.attribute "xmlns") "http://www.w3.org/2000/svg",
(Html.attribute "aria-labelledby") "logo-link",
(Html.attribute "role") "img",
class "roc-logo"
] [
class "roc-logo",
]
[
(Html.element "title") [id "logo-link"] [text "Return to Roc Home"],
(Html.element "polygon") [
(Html.element "polygon")
[
(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",
] [],
]
[],
]

View file

@ -63,6 +63,17 @@ footer {
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 {
margin-bottom: 0;
}
@ -303,6 +314,14 @@ li {
/* Mobile-friendly screen width */
@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,
aside,
@ -759,3 +778,42 @@ code .dim {
line-height: 1.5;
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;
}