mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 20:28:02 +00:00
Add Task as a built-in module/type
This commit is contained in:
parent
d47a073634
commit
700c7ae9aa
86 changed files with 925 additions and 2670 deletions
|
@ -1,6 +1,6 @@
|
|||
interface AStar
|
||||
exposes [findPath, Model, initialModel, cheapestOpen, reconstructPath]
|
||||
imports [Quicksort]
|
||||
module [findPath, Model, initialModel, cheapestOpen, reconstructPath]
|
||||
|
||||
import Quicksort
|
||||
|
||||
findPath = \costFn, moveFn, start, end ->
|
||||
astar costFn moveFn end (initialModel start)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
interface Base64 exposes [fromBytes, fromStr, toBytes, toStr] imports []
|
||||
module [fromBytes, fromStr, toBytes, toStr]
|
||||
|
||||
import Base64.Decode
|
||||
import Base64.Encode
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
interface Base64.Decode exposes [fromBytes] imports []
|
||||
module [fromBytes]
|
||||
|
||||
import Bytes.Decode exposing [ByteDecoder, DecodeProblem]
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
interface Base64.Encode
|
||||
exposes [toBytes]
|
||||
imports []
|
||||
|
||||
module [toBytes]
|
||||
|
||||
import Bytes.Encode exposing [ByteEncoder]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
interface Bytes.Decode exposes [ByteDecoder, decode, map, map2, u8, loop, Step, succeed, DecodeProblem, after, map3] imports []
|
||||
module [ByteDecoder, decode, map, map2, u8, loop, Step, succeed, DecodeProblem, after, map3]
|
||||
|
||||
State : { bytes : List U8, cursor : U64 }
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
interface Bytes.Encode exposes [ByteEncoder, sequence, u8, u16, bytes, empty, encode] imports []
|
||||
module [ByteEncoder, sequence, u8, u16, bytes, empty, encode]
|
||||
|
||||
Endianness : [BE, LE]
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
interface Issue2279Help
|
||||
exposes [text, asText]
|
||||
imports []
|
||||
module [text, asText]
|
||||
|
||||
text = "Hello, world!"
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
interface Quicksort exposes [sortBy, sortWith, show] imports []
|
||||
module [sortBy, sortWith, show]
|
||||
|
||||
show : List I64 -> Str
|
||||
show = \list ->
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
app "cfold"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.PlatformTask
|
||||
|
||||
# adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs
|
||||
main : Task.Task {} []
|
||||
main : Task {} []
|
||||
main =
|
||||
inputResult <- Task.attempt Task.getInt
|
||||
inputResult <- Task.attempt PlatformTask.getInt
|
||||
|
||||
when inputResult is
|
||||
Ok n ->
|
||||
|
@ -18,10 +17,10 @@ main =
|
|||
|> Num.toStr
|
||||
|> Str.concat " & "
|
||||
|> Str.concat (Num.toStr optimized)
|
||||
|> Task.putLine
|
||||
|> PlatformTask.putLine
|
||||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
PlatformTask.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
Expr : [
|
||||
Add Expr Expr,
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
app "closure"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
# see https://github.com/roc-lang/roc/issues/985
|
||||
main : Task.Task {} []
|
||||
main : Task {} []
|
||||
main = closure1 {}
|
||||
# |> Task.after (\_ -> closure2 {})
|
||||
# |> Task.after (\_ -> closure3 {})
|
||||
# |> Task.after (\_ -> closure4 {})
|
||||
# ---
|
||||
closure1 : {} -> Task.Task {} []
|
||||
closure1 : {} -> Task {} []
|
||||
closure1 = \_ ->
|
||||
Task.succeed (foo toUnitBorrowed "a long string such that it's malloced")
|
||||
Task.ok (foo toUnitBorrowed "a long string such that it's malloced")
|
||||
|> Task.map \_ -> {}
|
||||
|
||||
toUnitBorrowed = \x -> Str.countUtf8Bytes x
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
app "deriv"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.PlatformTask
|
||||
|
||||
# based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs
|
||||
IO a : Task.Task a []
|
||||
IO a : Task a []
|
||||
|
||||
main : Task.Task {} []
|
||||
main : Task {} []
|
||||
main =
|
||||
inputResult <- Task.attempt Task.getInt
|
||||
inputResult <- Task.attempt PlatformTask.getInt
|
||||
|
||||
when inputResult is
|
||||
Ok n ->
|
||||
|
@ -22,14 +21,15 @@ main =
|
|||
|> Task.map \_ -> {}
|
||||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
PlatformTask.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
nestHelp : I64, (I64, Expr -> IO Expr), I64, Expr -> IO Expr
|
||||
nestHelp = \s, f, m, x -> when m is
|
||||
0 -> Task.succeed x
|
||||
_ ->
|
||||
w <- Task.after (f (s - m) x)
|
||||
nestHelp s f (m - 1) w
|
||||
nestHelp = \s, f, m, x ->
|
||||
when m is
|
||||
0 -> Task.ok x
|
||||
_ ->
|
||||
w = f! (s - m) x
|
||||
nestHelp s f (m - 1) w
|
||||
|
||||
nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr
|
||||
nest = \f, n, e -> nestHelp n f n e
|
||||
|
@ -162,6 +162,5 @@ deriv = \i, f ->
|
|||
Num.toStr (i + 1)
|
||||
|> Str.concat " count: "
|
||||
|> Str.concat (Num.toStr (count fprime))
|
||||
|
||||
Task.putLine line
|
||||
|> Task.after \_ -> Task.succeed fprime
|
||||
PlatformTask.putLine! line
|
||||
Task.ok fprime
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
app "issue2279"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [Issue2279Help, pf.Task]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import Issue2279Help
|
||||
import pf.PlatformTask
|
||||
|
||||
main =
|
||||
text =
|
||||
|
@ -10,4 +10,4 @@ main =
|
|||
else
|
||||
Issue2279Help.asText 42
|
||||
|
||||
Task.putLine text
|
||||
PlatformTask.putLine text
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
app "nqueens"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
main : Task.Task {} []
|
||||
import pf.PlatformTask
|
||||
|
||||
main : Task {} []
|
||||
main =
|
||||
inputResult <- Task.attempt Task.getInt
|
||||
inputResult = Task.getInt!
|
||||
|
||||
when inputResult is
|
||||
Ok n ->
|
||||
queens n # original koka 13
|
||||
|> Num.toStr
|
||||
|> Task.putLine
|
||||
|> PlatformTask.putLine
|
||||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
PlatformTask.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
ConsList a : [Nil, Cons a (ConsList a)]
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
hosted Effect
|
||||
exposes [Effect, after, map, always, forever, loop, putLine, putInt, getInt]
|
||||
imports []
|
||||
generates Effect with [after, map, always, forever, loop]
|
||||
|
||||
putLine : Str -> Effect {}
|
||||
|
||||
putInt : I64 -> Effect {}
|
||||
|
||||
getInt : Effect { value : I64, isError : Bool }
|
9
crates/cli/tests/benchmarks/platform/PlatformTask.roc
Normal file
9
crates/cli/tests/benchmarks/platform/PlatformTask.roc
Normal file
|
@ -0,0 +1,9 @@
|
|||
hosted
|
||||
exposes [putLine, putInt, getInt]
|
||||
imports []
|
||||
|
||||
putLine : Str -> Task {} *
|
||||
|
||||
putInt : I64 -> Task {} *
|
||||
|
||||
getInt : Task { value : I64, isError : Bool } *
|
|
@ -1,88 +0,0 @@
|
|||
interface Task
|
||||
exposes [Task, succeed, fail, after, map, putLine, putInt, getInt, forever, loop, attempt]
|
||||
imports [pf.Effect]
|
||||
|
||||
Task ok err : Effect.Effect (Result ok err)
|
||||
|
||||
forever : Task val err -> Task * err
|
||||
forever = \task ->
|
||||
looper = \{} ->
|
||||
task
|
||||
|> Effect.map
|
||||
\res ->
|
||||
when res is
|
||||
Ok _ -> Step {}
|
||||
Err e -> Done (Err e)
|
||||
|
||||
Effect.loop {} looper
|
||||
|
||||
loop : state, (state -> Task [Step state, Done done] err) -> Task done err
|
||||
loop = \state, step ->
|
||||
looper = \current ->
|
||||
step current
|
||||
|> Effect.map
|
||||
\res ->
|
||||
when res is
|
||||
Ok (Step newState) -> Step newState
|
||||
Ok (Done result) -> Done (Ok result)
|
||||
Err e -> Done (Err e)
|
||||
|
||||
Effect.loop state looper
|
||||
|
||||
succeed : val -> Task val *
|
||||
succeed = \val ->
|
||||
Effect.always (Ok val)
|
||||
|
||||
fail : err -> Task * err
|
||||
fail = \val ->
|
||||
Effect.always (Err val)
|
||||
|
||||
after : Task a err, (a -> Task b err) -> Task b err
|
||||
after = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok a -> transform a
|
||||
Err err -> Task.fail err
|
||||
|
||||
attempt : Task a b, (Result a b -> Task c d) -> Task c d
|
||||
attempt = \task, transform ->
|
||||
Effect.after
|
||||
task
|
||||
\result ->
|
||||
when result is
|
||||
Ok ok -> transform (Ok ok)
|
||||
Err err -> transform (Err err)
|
||||
|
||||
map : Task a err, (a -> b) -> Task b err
|
||||
map = \effect, transform ->
|
||||
Effect.map
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok a -> Ok (transform a)
|
||||
Err err -> Err err
|
||||
|
||||
putLine : Str -> Task {} *
|
||||
putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
|
||||
|
||||
putInt : I64 -> Task {} *
|
||||
putInt = \line -> Effect.map (Effect.putInt line) (\_ -> Ok {})
|
||||
|
||||
getInt : Task I64 [GetIntError]
|
||||
getInt =
|
||||
Effect.after
|
||||
Effect.getInt
|
||||
\{ isError, value } ->
|
||||
if
|
||||
isError
|
||||
then
|
||||
# TODO
|
||||
# when errorCode is
|
||||
# # A -> Task.fail InvalidCharacter
|
||||
# # B -> Task.fail IOError
|
||||
# _ ->
|
||||
Task.fail GetIntError
|
||||
else
|
||||
Task.succeed value
|
|
@ -2,7 +2,7 @@ platform "benchmarks"
|
|||
requires {} { main : Task {} [] }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Task.{ Task }]
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Task {} []
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
app "quicksortapp"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task, Quicksort]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.PlatformTask
|
||||
import Quicksort
|
||||
|
||||
main : Task.Task {} []
|
||||
main =
|
||||
inputResult <- Task.attempt Task.getInt
|
||||
inputResult = PlatformTask.getInt!
|
||||
|
||||
when inputResult is
|
||||
Ok n ->
|
||||
|
@ -19,10 +19,10 @@ main =
|
|||
|
||||
sort unsortedList
|
||||
|> Quicksort.show
|
||||
|> Task.putLine
|
||||
|> PlatformTask.putLine
|
||||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
PlatformTask.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
sort : List I64 -> List I64
|
||||
sort = \list ->
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
app "rbtree-ck"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.PlatformTask
|
||||
|
||||
Color : [Red, Black]
|
||||
|
||||
|
@ -38,9 +37,9 @@ fold = \f, tree, b ->
|
|||
Leaf -> b
|
||||
Node _ l k v r -> fold f r (f k v (fold f l b))
|
||||
|
||||
main : Task.Task {} []
|
||||
main : Task {} []
|
||||
main =
|
||||
inputResult <- Task.attempt Task.getInt
|
||||
inputResult = PlatformTask.getInt!
|
||||
|
||||
when inputResult is
|
||||
Ok n ->
|
||||
|
@ -54,13 +53,13 @@ main =
|
|||
|
||||
val
|
||||
|> Num.toStr
|
||||
|> Task.putLine
|
||||
|> PlatformTask.putLine
|
||||
|
||||
Nil ->
|
||||
Task.putLine "fail"
|
||||
PlatformTask.putLine "fail"
|
||||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
PlatformTask.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
insert : Tree (Num k) v, Num k, v -> Tree (Num k) v
|
||||
insert = \t, k, v -> if isRed t then setBlack (ins t k v) else ins t k v
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
app "rbtree-del"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.PlatformTask
|
||||
|
||||
Color : [Red, Black]
|
||||
|
||||
|
@ -11,9 +10,9 @@ Map : Tree I64 Bool
|
|||
|
||||
ConsList a : [Nil, Cons a (ConsList a)]
|
||||
|
||||
main : Task.Task {} []
|
||||
main : Task {} []
|
||||
main =
|
||||
inputResult <- Task.attempt Task.getInt
|
||||
inputResult = PlatformTask.getInt!
|
||||
|
||||
when inputResult is
|
||||
Ok n ->
|
||||
|
@ -22,10 +21,10 @@ main =
|
|||
|
||||
val
|
||||
|> Num.toStr
|
||||
|> Task.putLine
|
||||
|> PlatformTask.putLine
|
||||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
PlatformTask.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
boom : Str -> a
|
||||
boom = \_ -> boom ""
|
||||
|
@ -248,4 +247,4 @@ del = \t, k ->
|
|||
rebalanceLeft cx lx ky vy ry
|
||||
|
||||
Delmin (Del ry Bool.false) ky vy ->
|
||||
Del (Node cx lx ky vy ry) Bool.false
|
||||
Del (Node cx lx ky vy ry) Bool.false
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
app "rbtree-insert"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
main : Task.Task {} []
|
||||
import pf.PlatformTask
|
||||
|
||||
main : Task {} []
|
||||
main =
|
||||
tree : RedBlackTree I64 {}
|
||||
tree = insert 0 {} Empty
|
||||
|
||||
tree
|
||||
|> show
|
||||
|> Task.putLine
|
||||
|> PlatformTask.putLine
|
||||
|
||||
show : RedBlackTree I64 {} -> Str
|
||||
show = \tree -> showRBTree tree Num.toStr (\{} -> "{}")
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
app "test-astar"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task, AStar]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
main : Task.Task {} []
|
||||
import pf.PlatformTask
|
||||
import AStar
|
||||
|
||||
main : Task {} []
|
||||
main =
|
||||
Task.putLine (showBool test1)
|
||||
PlatformTask.putLine! (showBool test1)
|
||||
|
||||
n = PlatformTask.getInt!
|
||||
when n is
|
||||
1 ->
|
||||
PlatformTask.putLine (showBool test1)
|
||||
|
||||
_ ->
|
||||
ns = Num.toStr n
|
||||
PlatformTask.putLine "No test $(ns)"
|
||||
|
||||
# Task.after Task.getInt \n ->
|
||||
# when n is
|
||||
# 1 ->
|
||||
# Task.putLine (showBool test1)
|
||||
#
|
||||
# _ ->
|
||||
# ns = Num.toStr n
|
||||
# Task.putLine "No test $(ns)"
|
||||
showBool : Bool -> Str
|
||||
showBool = \b ->
|
||||
if
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
app "test-base64"
|
||||
packages { pf: "platform/main.roc" }
|
||||
imports [pf.Task, Base64]
|
||||
provides [main] to pf
|
||||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
IO a : Task.Task a []
|
||||
import Base64
|
||||
import pf.PlatformTask
|
||||
|
||||
IO a : Task a []
|
||||
|
||||
main : IO {}
|
||||
main =
|
||||
when Base64.fromBytes (Str.toUtf8 "Hello World") is
|
||||
Err _ -> Task.putLine "sadness"
|
||||
Err _ -> PlatformTask.putLine "sadness"
|
||||
Ok encoded ->
|
||||
Task.after
|
||||
(Task.putLine (Str.concat "encoded: " encoded))
|
||||
\_ ->
|
||||
when Base64.toStr encoded is
|
||||
Ok decoded -> Task.putLine (Str.concat "decoded: " decoded)
|
||||
Err _ -> Task.putLine "sadness"
|
||||
Task.putLine! (Str.concat "encoded: " encoded)
|
||||
|
||||
when Base64.toStr encoded is
|
||||
Ok decoded -> Task.putLine (Str.concat "decoded: " decoded)
|
||||
Err _ -> Task.putLine "sadness"
|
||||
|
|
|
@ -2,7 +2,6 @@ app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/downlo
|
|||
|
||||
import pf.Stdout
|
||||
import pf.Arg
|
||||
import pf.Task exposing [Task]
|
||||
|
||||
main =
|
||||
args = Arg.list!
|
||||
|
|
|
@ -2,18 +2,17 @@ app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/downlo
|
|||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
import pf.Task exposing [await, loop]
|
||||
|
||||
main =
|
||||
_ <- await (Stdout.line "\nLet's count down from 3 together - all you have to do is press <ENTER>.")
|
||||
_ <- await Stdin.line
|
||||
loop 3 tick
|
||||
_ <- Task.await (Stdout.line "\nLet's count down from 3 together - all you have to do is press <ENTER>.")
|
||||
_ <- Task.await Stdin.line
|
||||
Task.loop 3 tick
|
||||
|
||||
tick = \n ->
|
||||
if n == 0 then
|
||||
_ <- await (Stdout.line "🎉 SURPRISE! Happy Birthday! 🎂")
|
||||
_ <- Task.await (Stdout.line "🎉 SURPRISE! Happy Birthday! 🎂")
|
||||
Task.ok (Done {})
|
||||
else
|
||||
_ <- await (n |> Num.toStr |> \s -> "$(s)..." |> Stdout.line)
|
||||
_ <- await Stdin.line
|
||||
_ <- Task.await (n |> Num.toStr |> \s -> "$(s)..." |> Stdout.line)
|
||||
_ <- Task.await Stdin.line
|
||||
Task.ok (Step (n - 1))
|
||||
|
|
|
@ -2,7 +2,6 @@ app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/downlo
|
|||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
import pf.Task exposing [Task]
|
||||
|
||||
main =
|
||||
_ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂")
|
||||
|
|
|
@ -3,7 +3,6 @@ app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/downlo
|
|||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
import pf.Env
|
||||
import pf.Task exposing [Task]
|
||||
|
||||
main =
|
||||
task =
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Task exposing [Task]
|
||||
import pf.File
|
||||
import pf.Path
|
||||
import pf.Env
|
||||
|
|
|
@ -2,7 +2,6 @@ app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/downlo
|
|||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
import pf.Task exposing [await, Task]
|
||||
|
||||
main =
|
||||
Stdout.line! "What's your first name?"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
|
||||
import pf.Http
|
||||
import pf.Task exposing [Task]
|
||||
import pf.Stdout
|
||||
|
||||
main =
|
||||
|
|
|
@ -5,7 +5,6 @@ app [main] {
|
|||
|
||||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
import pf.Task exposing [Task]
|
||||
import parser.Core exposing [Parser, map, keep]
|
||||
import parser.String exposing [strFromUtf8]
|
||||
import parser.CSV exposing [CSV]
|
||||
|
|
|
@ -1458,7 +1458,7 @@ mod cli_run {
|
|||
|
||||
But the type annotation on main says it should be:
|
||||
|
||||
Effect.Effect (Result {} [])
|
||||
Task {} []
|
||||
|
||||
Tip: Type comparisons between an opaque type are only ever equal if
|
||||
both types are the same opaque type. Did you mean to create an opaque
|
||||
|
@ -1539,30 +1539,6 @@ mod cli_run {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_generates_with() {
|
||||
check_compile_error(
|
||||
&known_bad_file("UnknownGeneratesWith.roc"),
|
||||
&[],
|
||||
indoc!(
|
||||
r#"
|
||||
── UNKNOWN GENERATES FUNCTION in tests/known_bad/UnknownGeneratesWith.roc ──────
|
||||
|
||||
I don't know how to generate the foobar function.
|
||||
|
||||
4│ generates Effect with [after, map, always, foobar]
|
||||
^^^^^^
|
||||
|
||||
Only specific functions like `after` and `map` can be generated.Learn
|
||||
more about hosted modules at TODO.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
1 error and 0 warnings found in <ignored for test> ms."#
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_check_good() {
|
||||
check_format_check_as_expected(&fixture_file("format", "Formatted.roc"), true);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
app "type-error"
|
||||
packages { pf: "../../../../examples/cli/false-interpreter/platform/main.roc" }
|
||||
imports [pf.Task.{ Task }]
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main : Str -> Task {} []
|
||||
main = \_ ->
|
||||
"this is a string, not a Task {} [] function like the platform expects."
|
||||
"this is a string, not a Task {} [] function like the platform expects."
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
hosted UnknownGeneratesWith
|
||||
exposes [Effect, after, map, always]
|
||||
imports []
|
||||
generates Effect with [after, map, always, foobar]
|
247
crates/compiler/builtins/roc/Task.roc
Normal file
247
crates/compiler/builtins/roc/Task.roc
Normal file
|
@ -0,0 +1,247 @@
|
|||
module [
|
||||
Task,
|
||||
ok,
|
||||
err,
|
||||
await,
|
||||
map,
|
||||
mapErr,
|
||||
onErr,
|
||||
attempt,
|
||||
forever,
|
||||
loop,
|
||||
fromResult,
|
||||
batch,
|
||||
seq,
|
||||
forEach,
|
||||
result,
|
||||
]
|
||||
|
||||
import List
|
||||
import Result exposing [Result]
|
||||
|
||||
## A Task represents an effect; an interaction with state outside your Roc
|
||||
## program, such as the terminal's standard output, or a file.
|
||||
Task ok err := {} -> Result ok err
|
||||
|
||||
## Run a task repeatedly, until it fails with `err`. Note that this task does not return a success value.
|
||||
forever : Task a err -> Task * err
|
||||
forever = \@Task task ->
|
||||
looper = \{} ->
|
||||
when task {} is
|
||||
Err e -> Err e
|
||||
Ok _ -> looper {}
|
||||
|
||||
@Task \{} -> looper {}
|
||||
|
||||
# TODO: consider moving referenced documentation here.
|
||||
#
|
||||
## Run a task repeatedly, until it fails with `err` or completes with `done`.
|
||||
## Check out [this example](https://www.roc-lang.org/examples/TaskLoop/README.html).
|
||||
loop : state, (state -> Task [Step state, Done done] err) -> Task done err
|
||||
loop = \state, step ->
|
||||
looper = \current ->
|
||||
(@Task next) = step current
|
||||
when next {} is
|
||||
Err e -> Err e
|
||||
Ok (Done newResult) -> Ok newResult
|
||||
Ok (Step newState) -> looper (newState)
|
||||
|
||||
@Task \{} -> looper state
|
||||
|
||||
## Create a task that always succeeds with the value provided.
|
||||
##
|
||||
## ```
|
||||
## # Always succeeds with "Louis"
|
||||
## getName : Task.Task Str *
|
||||
## getName = Task.ok "Louis"
|
||||
## ```
|
||||
##
|
||||
ok : a -> Task a *
|
||||
ok = \a -> @Task \{} -> Ok a
|
||||
|
||||
## Create a task that always fails with the error provided.
|
||||
##
|
||||
## ```
|
||||
## # Always fails with the tag `CustomError Str`
|
||||
## customError : Str -> Task.Task {} [CustomError Str]
|
||||
## customError = \err -> Task.err (CustomError err)
|
||||
## ```
|
||||
##
|
||||
err : a -> Task * a
|
||||
err = \a -> @Task \{} -> Err a
|
||||
|
||||
## Transform a given Task with a function that handles the success or error case
|
||||
## and returns another task based on that. This is useful for chaining tasks
|
||||
## together or performing error handling and recovery.
|
||||
##
|
||||
## Consider a the following task;
|
||||
##
|
||||
## `canFail : Task {} [Failure, AnotherFail, YetAnotherFail]`
|
||||
##
|
||||
## We can use [attempt] to handle the failure cases using the following;
|
||||
##
|
||||
## ```
|
||||
## result <- canFail |> Task.attempt
|
||||
## when result is
|
||||
## Ok Success -> Stdout.line "Success!"
|
||||
## Err Failure -> Stdout.line "Oops, failed!"
|
||||
## Err AnotherFail -> Stdout.line "Ooooops, another failure!"
|
||||
## Err YetAnotherFail -> Stdout.line "Really big oooooops, yet again!"
|
||||
## ```
|
||||
##
|
||||
## Here we know that the `canFail` task may fail, and so we use
|
||||
## `Task.attempt` to convert the task to a `Result` and then use pattern
|
||||
## matching to handle the success and possible failure cases.
|
||||
##
|
||||
attempt : Task a b, (Result a b -> Task c d) -> Task c d
|
||||
attempt = \@Task task, transform ->
|
||||
@Task \{} ->
|
||||
(@Task transformed) = transform (task {})
|
||||
|
||||
transformed {}
|
||||
|
||||
## Take the success value from a given [Task] and use that to generate a new [Task].
|
||||
##
|
||||
## For example we can use this to run tasks in sequence like follows;
|
||||
##
|
||||
## ```
|
||||
## # Prints "Hello World!\n" to standard output.
|
||||
## {} <- Stdout.write "Hello "|> Task.await
|
||||
## {} <- Stdout.write "World!\n"|> Task.await
|
||||
##
|
||||
## Task.ok {}
|
||||
## ```
|
||||
await : Task a b, (a -> Task c b) -> Task c b
|
||||
await = \@Task task, transform ->
|
||||
@Task \{} ->
|
||||
when task {} is
|
||||
Ok a ->
|
||||
(@Task transformed) = transform a
|
||||
transformed {}
|
||||
|
||||
Err b ->
|
||||
Err b
|
||||
|
||||
## Take the error value from a given [Task] and use that to generate a new [Task].
|
||||
##
|
||||
## ```
|
||||
## # Prints "Something went wrong!" to standard error if `canFail` fails.
|
||||
## canFail
|
||||
## |> Task.onErr \_ -> Stderr.line "Something went wrong!"
|
||||
## ```
|
||||
onErr : Task a b, (b -> Task a c) -> Task a c
|
||||
onErr = \@Task task, transform ->
|
||||
@Task \{} ->
|
||||
when task {} is
|
||||
Ok a ->
|
||||
Ok a
|
||||
|
||||
Err b ->
|
||||
(@Task transformed) = transform b
|
||||
transformed {}
|
||||
|
||||
## Transform the success value of a given [Task] with a given function.
|
||||
##
|
||||
## ```
|
||||
## # Succeeds with a value of "Bonjour Louis!"
|
||||
## Task.ok "Louis"
|
||||
## |> Task.map (\name -> "Bonjour $(name)!")
|
||||
## ```
|
||||
map : Task a c, (a -> b) -> Task b c
|
||||
map = \@Task task, transform ->
|
||||
@Task \{} ->
|
||||
when task {} is
|
||||
Ok a -> Ok (transform a)
|
||||
Err b -> Err b
|
||||
|
||||
## Transform the error value of a given [Task] with a given function.
|
||||
##
|
||||
## ```
|
||||
## # Ignore the fail value, and map it to the tag `CustomError`
|
||||
## canFail
|
||||
## |> Task.mapErr \_ -> CustomError
|
||||
## ```
|
||||
mapErr : Task c a, (a -> b) -> Task c b
|
||||
mapErr = \@Task task, transform ->
|
||||
@Task \{} ->
|
||||
when task {} is
|
||||
Ok a -> Ok a
|
||||
Err b -> Err (transform b)
|
||||
|
||||
## Use a Result among other Tasks by converting it into a [Task].
|
||||
fromResult : Result a b -> Task a b
|
||||
fromResult = \res ->
|
||||
@Task \{} -> res
|
||||
|
||||
## Apply a task to another task applicatively. This can be used with
|
||||
## [ok] to build a [Task] that returns a record.
|
||||
##
|
||||
## The following example returns a Record with two fields, `apples` and
|
||||
## `oranges`, each of which is a `List Str`. If it fails it returns the tag
|
||||
## `NoFruitAvailable`.
|
||||
##
|
||||
## ```
|
||||
## getFruitBasket : Task { apples : List Str, oranges : List Str } [NoFruitAvailable]
|
||||
## getFruitBasket = Task.ok {
|
||||
## apples: <- getFruit Apples |> Task.batch,
|
||||
## oranges: <- getFruit Oranges |> Task.batch,
|
||||
## }
|
||||
## ```
|
||||
batch : Task a c -> (Task (a -> b) c -> Task b c)
|
||||
batch = \current -> \next ->
|
||||
f <- next |> await
|
||||
|
||||
map current f
|
||||
|
||||
## Apply each task in a list sequentially, and return a list of the resulting values.
|
||||
## Each task will be awaited before beginning the next task.
|
||||
##
|
||||
## ```
|
||||
## fetchAuthorTasks : List (Task Author [DbError])
|
||||
##
|
||||
## getAuthors : Task (List Author) [DbError]
|
||||
## getAuthors = Task.seq fetchAuthorTasks
|
||||
## ```
|
||||
##
|
||||
seq : List (Task ok err) -> Task (List ok) err
|
||||
seq = \tasks ->
|
||||
List.walk tasks (ok []) \state, task ->
|
||||
value <- task |> await
|
||||
|
||||
state |> map \values -> List.append values value
|
||||
|
||||
## Apply a task repeatedly for each item in a list
|
||||
##
|
||||
## ```
|
||||
## authors : List Author
|
||||
## saveAuthor : Author -> Task {} [DbError]
|
||||
##
|
||||
## saveAuthors : Task (List Author) [DbError]
|
||||
## saveAuthors = Task.forEach authors saveAuthor
|
||||
## ```
|
||||
##
|
||||
forEach : List a, (a -> Task {} b) -> Task {} b
|
||||
forEach = \items, fn ->
|
||||
List.walk items (ok {}) \state, item ->
|
||||
state |> await \_ -> fn item
|
||||
|
||||
## Transform a task that can either succeed with `ok`, or fail with `err`, into
|
||||
## a task that succeeds with `Result ok err`.
|
||||
##
|
||||
## This is useful when chaining tasks using the `!` suffix. For example:
|
||||
##
|
||||
## ```
|
||||
## # Path.roc
|
||||
## checkFile : Str -> Task [Good, Bad] [IOError]
|
||||
##
|
||||
## # main.roc
|
||||
## when checkFile "/usr/local/bin/roc" |> Task.result! is
|
||||
## Ok Good -> "..."
|
||||
## Ok Bad -> "..."
|
||||
## Err IOError -> "..."
|
||||
## ```
|
||||
##
|
||||
result : Task ok err -> Task (Result ok err) *
|
||||
result = \@Task task ->
|
||||
@Task \{} ->
|
||||
Ok (task {})
|
|
@ -17,6 +17,7 @@ pub fn module_source(module_id: ModuleId) -> &'static str {
|
|||
ModuleId::HASH => HASH,
|
||||
ModuleId::INSPECT => INSPECT,
|
||||
ModuleId::JSON => JSON,
|
||||
ModuleId::TASK => TASK,
|
||||
_ => internal_error!(
|
||||
"ModuleId {:?} is not part of the standard library",
|
||||
module_id
|
||||
|
@ -37,3 +38,4 @@ const DECODE: &str = include_str!("../roc/Decode.roc");
|
|||
const HASH: &str = include_str!("../roc/Hash.roc");
|
||||
const INSPECT: &str = include_str!("../roc/Inspect.roc");
|
||||
const JSON: &str = include_str!("../roc/TotallyNotJson.roc");
|
||||
const TASK: &str = include_str!("../roc/Task.roc");
|
||||
|
|
|
@ -2637,12 +2637,11 @@ pub fn report_unused_imports(
|
|||
for (symbol, region) in &import.exposed_symbols {
|
||||
if !references.has_unqualified_type_or_value_lookup(*symbol)
|
||||
&& !scope.abilities_store.is_specialization_name(*symbol)
|
||||
&& !import.is_task(env)
|
||||
{
|
||||
env.problem(Problem::UnusedImport(*symbol, *region));
|
||||
}
|
||||
}
|
||||
} else if !import.is_task(env) {
|
||||
} else {
|
||||
env.problem(Problem::UnusedModuleImport(import.module_id, import.region));
|
||||
}
|
||||
}
|
||||
|
@ -2912,16 +2911,6 @@ pub struct IntroducedImport {
|
|||
exposed_symbols: Vec<(Symbol, Region)>,
|
||||
}
|
||||
|
||||
impl IntroducedImport {
|
||||
pub fn is_task(&self, env: &Env<'_>) -> bool {
|
||||
// Temporarily needed for `!` convenience. Can be removed when Task becomes a builtin.
|
||||
match env.qualified_module_ids.get_name(self.module_id) {
|
||||
Some(name) => name.as_inner().as_str() == "Task",
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn to_pending_value_def<'a>(
|
||||
env: &mut Env<'a>,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,6 @@ pub mod copy;
|
|||
pub mod def;
|
||||
mod derive;
|
||||
pub mod desugar;
|
||||
pub mod effect_module;
|
||||
pub mod env;
|
||||
pub mod exhaustive;
|
||||
pub mod expected;
|
||||
|
@ -26,6 +25,7 @@ pub mod procedure;
|
|||
pub mod scope;
|
||||
pub mod string;
|
||||
pub mod suffixed;
|
||||
pub mod task_module;
|
||||
pub mod traverse;
|
||||
|
||||
pub use derive::DERIVED_REGION;
|
||||
|
|
|
@ -3,7 +3,6 @@ use std::path::Path;
|
|||
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
|
||||
use crate::annotation::{canonicalize_annotation, AnnotationFor};
|
||||
use crate::def::{canonicalize_defs, report_unused_imports, Def};
|
||||
use crate::effect_module::HostedGeneratedFunctions;
|
||||
use crate::env::Env;
|
||||
use crate::expr::{
|
||||
ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
|
||||
|
@ -23,7 +22,7 @@ use roc_parse::pattern::PatternType;
|
|||
use roc_problem::can::{Problem, RuntimeError};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable};
|
||||
use roc_types::types::{AbilitySet, Alias, AliasKind, AliasVar, Type};
|
||||
use roc_types::types::{AbilitySet, Alias, Type};
|
||||
|
||||
/// The types of all exposed values/functions of a collection of modules
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -161,98 +160,6 @@ pub struct ModuleOutput {
|
|||
pub loc_dbgs: VecMap<Symbol, DbgLookup>,
|
||||
}
|
||||
|
||||
fn validate_generate_with<'a>(
|
||||
generate_with: &'a [Loc<roc_parse::header::ExposedName<'a>>],
|
||||
) -> (HostedGeneratedFunctions, Vec<Loc<Ident>>) {
|
||||
let mut functions = HostedGeneratedFunctions::default();
|
||||
let mut unknown = Vec::new();
|
||||
|
||||
for generated in generate_with {
|
||||
match generated.value.as_str() {
|
||||
"after" => functions.after = true,
|
||||
"map" => functions.map = true,
|
||||
"always" => functions.always = true,
|
||||
"loop" => functions.loop_ = true,
|
||||
"forever" => functions.forever = true,
|
||||
other => {
|
||||
// we don't know how to generate this function
|
||||
let ident = Ident::from(other);
|
||||
unknown.push(Loc::at(generated.region, ident));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(functions, unknown)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum GeneratedInfo {
|
||||
Hosted {
|
||||
effect_symbol: Symbol,
|
||||
generated_functions: HostedGeneratedFunctions,
|
||||
},
|
||||
Builtin,
|
||||
NotSpecial,
|
||||
}
|
||||
|
||||
impl GeneratedInfo {
|
||||
fn from_header_type(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
var_store: &mut VarStore,
|
||||
header_type: &HeaderType,
|
||||
) -> Self {
|
||||
match header_type {
|
||||
HeaderType::Hosted {
|
||||
generates,
|
||||
generates_with,
|
||||
name: _,
|
||||
exposes: _,
|
||||
} => {
|
||||
let name: &str = generates.into();
|
||||
let (generated_functions, unknown_generated) =
|
||||
validate_generate_with(generates_with);
|
||||
|
||||
for unknown in unknown_generated {
|
||||
env.problem(Problem::UnknownGeneratesWith(unknown));
|
||||
}
|
||||
|
||||
let effect_symbol = scope.introduce(name.into(), Region::zero()).unwrap();
|
||||
|
||||
{
|
||||
let a_var = var_store.fresh();
|
||||
|
||||
let actual =
|
||||
crate::effect_module::build_effect_actual(Type::Variable(a_var), var_store);
|
||||
|
||||
scope.add_alias(
|
||||
effect_symbol,
|
||||
Region::zero(),
|
||||
vec![Loc::at_zero(AliasVar::unbound("a".into(), a_var))],
|
||||
vec![],
|
||||
actual,
|
||||
AliasKind::Opaque,
|
||||
);
|
||||
}
|
||||
|
||||
GeneratedInfo::Hosted {
|
||||
effect_symbol,
|
||||
generated_functions,
|
||||
}
|
||||
}
|
||||
HeaderType::Builtin {
|
||||
generates_with,
|
||||
name: _,
|
||||
exposes: _,
|
||||
} => {
|
||||
debug_assert!(generates_with.is_empty());
|
||||
GeneratedInfo::Builtin
|
||||
}
|
||||
_ => GeneratedInfo::NotSpecial,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_no_implementation(expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. }) => true,
|
||||
|
@ -320,9 +227,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
);
|
||||
}
|
||||
|
||||
let generated_info =
|
||||
GeneratedInfo::from_header_type(&mut env, &mut scope, var_store, header_type);
|
||||
|
||||
// Desugar operators (convert them to Apply calls, taking into account
|
||||
// operator precedence and associativity rules), before doing other canonicalization.
|
||||
//
|
||||
|
@ -335,7 +239,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
let mut rigid_variables = RigidVariables::default();
|
||||
|
||||
// Iniital scope values are treated like defs that appear before any others.
|
||||
// Initial scope values are treated like defs that appear before any others.
|
||||
// They include builtin types that are automatically imported, and for a platform
|
||||
// package, the required values from the app.
|
||||
//
|
||||
|
@ -495,24 +399,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
report_unused_imports(imports_introduced, &output.references, &mut env, &mut scope);
|
||||
|
||||
if let GeneratedInfo::Hosted {
|
||||
effect_symbol,
|
||||
generated_functions,
|
||||
} = generated_info
|
||||
{
|
||||
let mut exposed_symbols = VecSet::default();
|
||||
|
||||
// NOTE this currently builds all functions, not just the ones that the user requested
|
||||
crate::effect_module::build_effect_builtins(
|
||||
&mut scope,
|
||||
effect_symbol,
|
||||
var_store,
|
||||
&mut exposed_symbols,
|
||||
&mut declarations,
|
||||
generated_functions,
|
||||
);
|
||||
}
|
||||
|
||||
for index in 0..declarations.len() {
|
||||
use crate::expr::DeclarationTag::*;
|
||||
|
||||
|
@ -533,8 +419,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
// and which are meant to be normal definitions without a body. So for now
|
||||
// we just assume they are hosted functions (meant to be provided by the platform)
|
||||
if has_no_implementation(&declarations.expressions[index].value) {
|
||||
match generated_info {
|
||||
GeneratedInfo::Builtin => {
|
||||
match header_type {
|
||||
HeaderType::Builtin { .. } => {
|
||||
match crate::builtins::builtin_defs_map(*symbol, var_store) {
|
||||
None => {
|
||||
internal_error!("A builtin module contains a signature without implementation for {:?}", symbol)
|
||||
|
@ -544,7 +430,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
GeneratedInfo::Hosted { effect_symbol, .. } => {
|
||||
HeaderType::Hosted { .. } => {
|
||||
let ident_id = symbol.ident_id();
|
||||
let ident = scope
|
||||
.locals
|
||||
|
@ -562,13 +448,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
let hosted_def = crate::effect_module::build_host_exposed_def(
|
||||
&mut scope,
|
||||
*symbol,
|
||||
&ident,
|
||||
effect_symbol,
|
||||
var_store,
|
||||
annotation,
|
||||
let hosted_def = crate::task_module::build_host_exposed_def(
|
||||
&mut scope, *symbol, &ident, var_store, annotation,
|
||||
);
|
||||
|
||||
declarations.update_builtin_def(index, hosted_def);
|
||||
|
@ -591,8 +472,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
// and which are meant to be normal definitions without a body. So for now
|
||||
// we just assume they are hosted functions (meant to be provided by the platform)
|
||||
if has_no_implementation(&declarations.expressions[index].value) {
|
||||
match generated_info {
|
||||
GeneratedInfo::Builtin => {
|
||||
match header_type {
|
||||
HeaderType::Builtin { .. } => {
|
||||
match crate::builtins::builtin_defs_map(*symbol, var_store) {
|
||||
None => {
|
||||
internal_error!("A builtin module contains a signature without implementation for {:?}", symbol)
|
||||
|
@ -602,7 +483,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
GeneratedInfo::Hosted { effect_symbol, .. } => {
|
||||
HeaderType::Hosted { .. } => {
|
||||
let ident_id = symbol.ident_id();
|
||||
let ident = scope
|
||||
.locals
|
||||
|
@ -620,13 +501,8 @@ pub fn canonicalize_module_defs<'a>(
|
|||
aliases: Default::default(),
|
||||
};
|
||||
|
||||
let hosted_def = crate::effect_module::build_host_exposed_def(
|
||||
&mut scope,
|
||||
*symbol,
|
||||
&ident,
|
||||
effect_symbol,
|
||||
var_store,
|
||||
annotation,
|
||||
let hosted_def = crate::task_module::build_host_exposed_def(
|
||||
&mut scope, *symbol, &ident, var_store, annotation,
|
||||
);
|
||||
|
||||
declarations.update_builtin_def(index, hosted_def);
|
||||
|
@ -652,18 +528,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
|
||||
let mut aliases = MutMap::default();
|
||||
|
||||
if let GeneratedInfo::Hosted { effect_symbol, .. } = generated_info {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
// we can see if there were any
|
||||
// exposed symbols which did not have
|
||||
// corresponding defs.
|
||||
exposed_but_not_defined.remove(&effect_symbol);
|
||||
|
||||
let hosted_alias = scope.lookup_alias(effect_symbol).unwrap().clone();
|
||||
aliases.insert(effect_symbol, hosted_alias);
|
||||
}
|
||||
|
||||
for (symbol, alias) in output.aliases {
|
||||
// Remove this from exposed_symbols,
|
||||
// so that at the end of the process,
|
||||
|
|
210
crates/compiler/can/src/task_module.rs
Normal file
210
crates/compiler/can/src/task_module.rs
Normal file
|
@ -0,0 +1,210 @@
|
|||
use crate::def::Def;
|
||||
use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive};
|
||||
use crate::pattern::Pattern;
|
||||
use crate::scope::Scope;
|
||||
use roc_collections::SendMap;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{LambdaSet, OptAbleVar, Type};
|
||||
|
||||
pub fn build_host_exposed_def(
|
||||
scope: &mut Scope,
|
||||
symbol: Symbol,
|
||||
ident: &str,
|
||||
var_store: &mut VarStore,
|
||||
annotation: crate::annotation::Annotation,
|
||||
) -> Def {
|
||||
if symbol.contains("PlatformTask") {
|
||||
dbg!(symbol, ident, &annotation);
|
||||
}
|
||||
|
||||
let expr_var = var_store.fresh();
|
||||
let pattern = Pattern::Identifier(symbol);
|
||||
let mut pattern_vars = SendMap::default();
|
||||
pattern_vars.insert(symbol, expr_var);
|
||||
|
||||
let mut arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)> = Vec::new();
|
||||
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
|
||||
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
|
||||
|
||||
let crate::annotation::Annotation {
|
||||
introduced_variables,
|
||||
typ,
|
||||
aliases,
|
||||
..
|
||||
} = annotation;
|
||||
|
||||
let def_body = {
|
||||
match typ.shallow_structural_dealias() {
|
||||
Type::Function(args, _, _) => {
|
||||
for i in 0..args.len() {
|
||||
let name = format!("closure_arg_{ident}_{i}");
|
||||
|
||||
let arg_symbol = {
|
||||
let ident = name.clone().into();
|
||||
scope.introduce(ident, Region::zero()).unwrap()
|
||||
};
|
||||
|
||||
let arg_var = var_store.fresh();
|
||||
|
||||
arguments.push((
|
||||
arg_var,
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(Pattern::Identifier(arg_symbol)),
|
||||
));
|
||||
|
||||
captured_symbols.push((arg_symbol, arg_var));
|
||||
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var)));
|
||||
}
|
||||
|
||||
let foreign_symbol_name = format!("roc_fx_{ident}");
|
||||
let low_level_call = Expr::ForeignCall {
|
||||
foreign_symbol: foreign_symbol_name.into(),
|
||||
args: linked_symbol_arguments,
|
||||
ret_var: var_store.fresh(),
|
||||
};
|
||||
|
||||
let task_closure_symbol = {
|
||||
let name = format!("task_closure_{ident}");
|
||||
|
||||
let ident = name.into();
|
||||
scope.introduce(ident, Region::zero()).unwrap()
|
||||
};
|
||||
|
||||
let task_closure = Expr::Closure(ClosureData {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: task_closure_symbol,
|
||||
captured_symbols,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern(var_store)),
|
||||
)],
|
||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||
});
|
||||
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
let body = Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
name: Symbol::TASK_TASK,
|
||||
argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))),
|
||||
specialized_def_type,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
};
|
||||
|
||||
Expr::Closure(ClosureData {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: symbol,
|
||||
captured_symbols: std::vec::Vec::new(),
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(Loc::at_zero(body)),
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
// not a function
|
||||
|
||||
let foreign_symbol_name = format!("roc_fx_{ident}");
|
||||
let low_level_call = Expr::ForeignCall {
|
||||
foreign_symbol: foreign_symbol_name.into(),
|
||||
args: linked_symbol_arguments,
|
||||
ret_var: var_store.fresh(),
|
||||
};
|
||||
|
||||
let task_closure_symbol = {
|
||||
let name = format!("task_closure_{ident}");
|
||||
|
||||
let ident = name.into();
|
||||
scope.introduce(ident, Region::zero()).unwrap()
|
||||
};
|
||||
|
||||
let task_closure = Expr::Closure(ClosureData {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: task_closure_symbol,
|
||||
captured_symbols,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
AnnotatedMark::new(var_store),
|
||||
Loc::at_zero(empty_record_pattern(var_store)),
|
||||
)],
|
||||
loc_body: Box::new(Loc::at_zero(low_level_call)),
|
||||
});
|
||||
|
||||
let (specialized_def_type, type_arguments, lambda_set_variables) =
|
||||
build_fresh_opaque_variables(var_store);
|
||||
Expr::OpaqueRef {
|
||||
opaque_var: var_store.fresh(),
|
||||
name: Symbol::TASK_TASK,
|
||||
argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))),
|
||||
specialized_def_type,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let def_annotation = crate::def::Annotation {
|
||||
signature: typ,
|
||||
introduced_variables,
|
||||
aliases,
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
let def = Def {
|
||||
loc_pattern: Loc::at_zero(pattern),
|
||||
loc_expr: Loc::at_zero(def_body),
|
||||
expr_var,
|
||||
pattern_vars,
|
||||
annotation: Some(def_annotation),
|
||||
};
|
||||
|
||||
if symbol.contains("PlatformTask") {
|
||||
dbg!(&def);
|
||||
}
|
||||
|
||||
def
|
||||
}
|
||||
|
||||
fn build_fresh_opaque_variables(
|
||||
var_store: &mut VarStore,
|
||||
) -> (Box<Type>, Vec<OptAbleVar>, Vec<LambdaSet>) {
|
||||
let closure_var = var_store.fresh();
|
||||
|
||||
// NB: if there are bugs, check whether not introducing variables is a problem!
|
||||
// introduced_variables.insert_wildcard(Loc::at_zero(closure_var));
|
||||
|
||||
let a_var = var_store.fresh();
|
||||
let actual = Type::Function(
|
||||
vec![Type::EmptyRec],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(Type::Variable(a_var)),
|
||||
);
|
||||
let type_arguments = vec![OptAbleVar {
|
||||
var: a_var,
|
||||
opt_abilities: None,
|
||||
}];
|
||||
let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];
|
||||
|
||||
(Box::new(actual), type_arguments, lambda_set_variables)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn empty_record_pattern(var_store: &mut VarStore) -> Pattern {
|
||||
Pattern::RecordDestructure {
|
||||
whole_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
destructs: vec![],
|
||||
}
|
||||
}
|
|
@ -9,11 +9,10 @@ use crate::Buf;
|
|||
use bumpalo::Bump;
|
||||
use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces};
|
||||
use roc_parse::header::{
|
||||
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
||||
ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader,
|
||||
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword,
|
||||
PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
|
||||
WithKeyword,
|
||||
AppHeader, ExposedName, ExposesKeyword, HostedHeader, ImportsEntry, ImportsKeyword, Keyword,
|
||||
KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackageKeyword,
|
||||
PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword, PlatformRequires,
|
||||
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
|
||||
};
|
||||
use roc_parse::ident::UppercaseIdent;
|
||||
use roc_region::all::Loc;
|
||||
|
@ -71,8 +70,6 @@ macro_rules! keywords {
|
|||
keywords! {
|
||||
ExposesKeyword,
|
||||
ImportsKeyword,
|
||||
WithKeyword,
|
||||
GeneratesKeyword,
|
||||
PackageKeyword,
|
||||
PackagesKeyword,
|
||||
RequiresKeyword,
|
||||
|
@ -208,9 +205,6 @@ pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) {
|
|||
fmt_exposes(buf, header.exposes.item, indent);
|
||||
header.imports.keyword.format(buf, indent);
|
||||
fmt_imports(buf, header.imports.item, indent);
|
||||
header.generates.format(buf, indent);
|
||||
header.generates_with.keyword.format(buf, indent);
|
||||
fmt_exposes(buf, header.generates_with.item, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_app_header<'a>(buf: &mut Buf, header: &'a AppHeader<'a>) {
|
||||
|
|
|
@ -328,8 +328,6 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
|
|||
name: header.name.remove_spaces(arena),
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
generates: header.generates.remove_spaces(arena),
|
||||
generates_with: header.generates_with.remove_spaces(arena),
|
||||
}),
|
||||
};
|
||||
Module {
|
||||
|
|
|
@ -26,6 +26,7 @@ const MODULES: &[(ModuleId, &str)] = &[
|
|||
(ModuleId::HASH, "Hash.roc"),
|
||||
(ModuleId::INSPECT, "Inspect.roc"),
|
||||
(ModuleId::JSON, "TotallyNotJson.roc"),
|
||||
(ModuleId::TASK, "Task.roc"),
|
||||
];
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -256,6 +256,7 @@ fn read_cached_types() -> MutMap<ModuleId, TypeState> {
|
|||
let mod_decode = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Decode.dat"));
|
||||
let mod_hash = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Hash.dat"));
|
||||
let mod_inspect = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Inspect.dat"));
|
||||
let mod_task = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Task.dat"));
|
||||
|
||||
let mut output = MutMap::default();
|
||||
|
||||
|
@ -279,6 +280,8 @@ fn read_cached_types() -> MutMap<ModuleId, TypeState> {
|
|||
|
||||
output.insert(ModuleId::HASH, deserialize_help(mod_hash));
|
||||
output.insert(ModuleId::INSPECT, deserialize_help(mod_inspect));
|
||||
|
||||
output.insert(ModuleId::TASK, deserialize_help(mod_task));
|
||||
}
|
||||
|
||||
output
|
||||
|
|
|
@ -6301,13 +6301,14 @@ In roc, functions are always written as a lambda, like{}
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: this test seems out of date (what is the `effects` clause?) and as such should be removed
|
||||
#[test]
|
||||
fn platform_requires_rigids() {
|
||||
report_header_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
platform "folkertdev/foo"
|
||||
requires { main : Effect {} }
|
||||
requires { main : Task {} [] }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Task]
|
||||
|
@ -6327,7 +6328,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
I am partway through parsing a header, but I got stuck here:
|
||||
|
||||
1│ platform "folkertdev/foo"
|
||||
2│ requires { main : Effect {} }
|
||||
2│ requires { main : Task {} [] }
|
||||
^
|
||||
|
||||
I am expecting a list of type names like `{}` or `{ Model }` next. A full
|
||||
|
|
|
@ -2333,6 +2333,7 @@ fn update<'a>(
|
|||
extend_module_with_builtin_import(parsed, ModuleId::DECODE);
|
||||
extend_module_with_builtin_import(parsed, ModuleId::HASH);
|
||||
extend_module_with_builtin_import(parsed, ModuleId::INSPECT);
|
||||
extend_module_with_builtin_import(parsed, ModuleId::TASK);
|
||||
}
|
||||
|
||||
state
|
||||
|
@ -3520,7 +3521,6 @@ fn load_builtin_module_help<'a>(
|
|||
header_type: HeaderType::Builtin {
|
||||
name: header::ModuleName::new(name_stem),
|
||||
exposes: unspace(arena, header.exposes.items),
|
||||
generates_with: &[],
|
||||
},
|
||||
module_comments: comments,
|
||||
header_imports: header.interface_imports,
|
||||
|
@ -3603,6 +3603,7 @@ fn load_module<'a>(
|
|||
"Hash", ModuleId::HASH
|
||||
"Inspect", ModuleId::INSPECT
|
||||
"TotallyNotJson", ModuleId::JSON
|
||||
"Task", ModuleId::TASK
|
||||
}
|
||||
|
||||
let (filename, opt_shorthand) = module_name_to_path(src_dir, &module_name, arc_shorthands);
|
||||
|
@ -3835,8 +3836,6 @@ fn parse_header<'a>(
|
|||
header_type: HeaderType::Hosted {
|
||||
name: header.name.value,
|
||||
exposes: unspace(arena, header.exposes.item.items),
|
||||
generates: header.generates.item,
|
||||
generates_with: unspace(arena, header.generates_with.item.items),
|
||||
},
|
||||
module_comments: comments,
|
||||
header_imports: Some(header.imports),
|
||||
|
@ -5063,6 +5062,7 @@ fn canonicalize_and_constrain<'a>(
|
|||
| ModuleId::SET
|
||||
| ModuleId::HASH
|
||||
| ModuleId::INSPECT
|
||||
| ModuleId::TASK
|
||||
);
|
||||
|
||||
if !name.is_builtin() || should_include_builtin {
|
||||
|
|
|
@ -26,4 +26,5 @@ pub const BUILTIN_MODULES: &[(ModuleId, &str)] = &[
|
|||
(ModuleId::HASH, "Hash"),
|
||||
(ModuleId::INSPECT, "Inspect"),
|
||||
(ModuleId::JSON, "TotallyNotJson"),
|
||||
(ModuleId::TASK, "Task"),
|
||||
];
|
||||
|
|
|
@ -92,6 +92,7 @@ impl Default for ModuleCache<'_> {
|
|||
HASH,
|
||||
INSPECT,
|
||||
JSON,
|
||||
TASK,
|
||||
}
|
||||
|
||||
Self {
|
||||
|
|
|
@ -1747,6 +1747,23 @@ define_builtins! {
|
|||
5 JSON_ARRAY_CLOSING_STATE: "ArrayClosingState"
|
||||
6 JSON_OBJECT_STATE: "ObjectState"
|
||||
}
|
||||
16 TASK: "Task" => {
|
||||
0 TASK_TASK: "Task" exposed_type=true // the Task.Task opaque type
|
||||
1 TASK_FOREVER: "forever"
|
||||
2 TASK_LOOP: "loop"
|
||||
3 TASK_OK: "ok"
|
||||
4 TASK_ERR: "err"
|
||||
5 TASK_ATTEMPT: "attempt"
|
||||
6 TASK_AWAIT: "await"
|
||||
7 TASK_ON_ERR: "onErr"
|
||||
8 TASK_MAP: "map"
|
||||
9 TASK_MAP_ERR: "mapErr"
|
||||
10 TASK_FROM_RESULT: "fromResult"
|
||||
11 TASK_BATCH: "batch"
|
||||
12 TASK_SEQ: "seq"
|
||||
13 TASK_FOR_EACH: "forEach"
|
||||
14 TASK_RESULT: "result"
|
||||
}
|
||||
|
||||
num_modules: 16 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
|
||||
num_modules: 17 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::ident::{lowercase_ident, UppercaseIdent};
|
|||
use crate::parser::{byte, specialize_err, EPackageEntry, EPackageName, Parser};
|
||||
use crate::parser::{optional, then};
|
||||
use crate::string_literal;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_module::symbol::ModuleId;
|
||||
use roc_region::all::Loc;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
@ -44,14 +44,11 @@ pub enum HeaderType<'a> {
|
|||
Hosted {
|
||||
name: ModuleName<'a>,
|
||||
exposes: &'a [Loc<ExposedName<'a>>],
|
||||
generates: UppercaseIdent<'a>,
|
||||
generates_with: &'a [Loc<ExposedName<'a>>],
|
||||
},
|
||||
/// Only created during canonicalization, never actually parsed from source
|
||||
Builtin {
|
||||
name: ModuleName<'a>,
|
||||
exposes: &'a [Loc<ExposedName<'a>>],
|
||||
generates_with: &'a [Symbol],
|
||||
},
|
||||
Package {
|
||||
/// usually something other than `pf`
|
||||
|
@ -101,11 +98,9 @@ impl<'a> HeaderType<'a> {
|
|||
|
||||
pub fn to_maybe_builtin(self, module_id: ModuleId) -> Self {
|
||||
match self {
|
||||
HeaderType::Module { name, exposes } if module_id.is_builtin() => HeaderType::Builtin {
|
||||
name,
|
||||
exposes,
|
||||
generates_with: &[],
|
||||
},
|
||||
HeaderType::Module { name, exposes } if module_id.is_builtin() => {
|
||||
HeaderType::Builtin { name, exposes }
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
|
@ -222,8 +217,6 @@ macro_rules! keywords {
|
|||
|
||||
keywords! {
|
||||
ExposesKeyword => "exposes",
|
||||
WithKeyword => "with",
|
||||
GeneratesKeyword => "generates",
|
||||
PackageKeyword => "package",
|
||||
PackagesKeyword => "packages",
|
||||
RequiresKeyword => "requires",
|
||||
|
@ -267,10 +260,6 @@ pub struct HostedHeader<'a> {
|
|||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
|
||||
pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
|
||||
|
||||
pub generates: KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>,
|
||||
pub generates_with:
|
||||
KeywordItem<'a, WithKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -349,7 +338,7 @@ pub enum ImportsEntry<'a> {
|
|||
|
||||
/// e.g.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
/// printLine : Str -> Result {} *
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct TypedIdent<'a> {
|
||||
pub ident: Loc<&'a str>,
|
||||
|
|
|
@ -365,7 +365,7 @@ fn fast_forward_to(
|
|||
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Error));
|
||||
}
|
||||
|
||||
pub const HEADER_KEYWORDS: [&str; 14] = [
|
||||
pub const HEADER_KEYWORDS: [&str; 12] = [
|
||||
"interface",
|
||||
"app",
|
||||
"package",
|
||||
|
@ -373,8 +373,6 @@ pub const HEADER_KEYWORDS: [&str; 14] = [
|
|||
"hosted",
|
||||
"exposes",
|
||||
"imports",
|
||||
"with",
|
||||
"generates",
|
||||
"package",
|
||||
"packages",
|
||||
"requires",
|
||||
|
|
|
@ -2,18 +2,18 @@ use crate::ast::{Collection, CommentOrNewline, Defs, Header, Module, Spaced, Spa
|
|||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::expr::merge_spaces;
|
||||
use crate::header::{
|
||||
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
||||
HostedHeader, ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword,
|
||||
KeywordItem, ModuleHeader, ModuleName, ModuleParams, PackageEntry, PackageHeader,
|
||||
PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
|
||||
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, HostedHeader,
|
||||
ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword, KeywordItem,
|
||||
ModuleHeader, ModuleName, ModuleParams, PackageEntry, PackageHeader, PackagesKeyword,
|
||||
PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword,
|
||||
TypedIdent,
|
||||
};
|
||||
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
|
||||
use crate::ident::{self, lowercase_ident, unqualified_ident, UppercaseIdent};
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
backtrackable, byte, increment_min_indent, optional, reset_min_indent, specialize_err,
|
||||
two_bytes, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages, EParams,
|
||||
EProvides, ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
|
||||
two_bytes, EExposes, EHeader, EImports, EPackages, EParams, EProvides, ERequires, ETypedIdent,
|
||||
Parser, SourceError, SpaceProblem, SyntaxError,
|
||||
};
|
||||
use crate::pattern::record_pattern_fields;
|
||||
use crate::state::State;
|
||||
|
@ -186,8 +186,6 @@ fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
|
|||
name: loc!(module_name_help(EHeader::ModuleName)),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_values_kw()),
|
||||
imports: specialize_err(EHeader::Imports, imports()),
|
||||
generates: specialize_err(EHeader::Generates, generates()),
|
||||
generates_with: specialize_err(EHeader::GeneratesWith, generates_with()),
|
||||
})
|
||||
.trace("hosted_header")
|
||||
}
|
||||
|
@ -758,43 +756,6 @@ fn packages_collection<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn generates<'a>(
|
||||
) -> impl Parser<'a, KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>, EGenerates> {
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
GeneratesKeyword,
|
||||
EGenerates::Generates,
|
||||
EGenerates::IndentGenerates,
|
||||
EGenerates::IndentTypeStart
|
||||
),
|
||||
item: specialize_err(|(), pos| EGenerates::Identifier(pos), uppercase())
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn generates_with<'a>() -> impl Parser<
|
||||
'a,
|
||||
KeywordItem<'a, WithKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
EGeneratesWith,
|
||||
> {
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
WithKeyword,
|
||||
EGeneratesWith::With,
|
||||
EGeneratesWith::IndentWith,
|
||||
EGeneratesWith::IndentListStart
|
||||
),
|
||||
item: collection_trailing_sep_e!(
|
||||
byte(b'[', EGeneratesWith::ListStart),
|
||||
exposes_entry(EGeneratesWith::Identifier),
|
||||
byte(b',', EGeneratesWith::ListEnd),
|
||||
byte(b']', EGeneratesWith::ListEnd),
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn imports<'a>() -> impl Parser<
|
||||
'a,
|
||||
|
@ -823,7 +784,7 @@ fn imports<'a>() -> impl Parser<
|
|||
pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> {
|
||||
// e.g.
|
||||
//
|
||||
// printLine : Str -> Effect {}
|
||||
// printLine : Str -> Task {} *
|
||||
map!(
|
||||
and!(
|
||||
and!(
|
||||
|
|
|
@ -83,8 +83,6 @@ impl_space_problem! {
|
|||
EExpect<'a>,
|
||||
EExposes,
|
||||
EExpr<'a>,
|
||||
EGenerates,
|
||||
EGeneratesWith,
|
||||
EHeader<'a>,
|
||||
EIf<'a>,
|
||||
EImport<'a>,
|
||||
|
@ -122,8 +120,6 @@ pub enum EHeader<'a> {
|
|||
Imports(EImports, Position),
|
||||
Requires(ERequires<'a>, Position),
|
||||
Packages(EPackages<'a>, Position),
|
||||
Generates(EGenerates, Position),
|
||||
GeneratesWith(EGeneratesWith, Position),
|
||||
|
||||
Space(BadInputError, Position),
|
||||
Start(Position),
|
||||
|
@ -251,30 +247,6 @@ pub enum EImports {
|
|||
StrLiteral(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EGenerates {
|
||||
Open(Position),
|
||||
Generates(Position),
|
||||
IndentGenerates(Position),
|
||||
Identifier(Position),
|
||||
Space(BadInputError, Position),
|
||||
IndentTypeStart(Position),
|
||||
IndentTypeEnd(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EGeneratesWith {
|
||||
Open(Position),
|
||||
With(Position),
|
||||
IndentWith(Position),
|
||||
IndentListStart(Position),
|
||||
IndentListEnd(Position),
|
||||
ListStart(Position),
|
||||
ListEnd(Position),
|
||||
Identifier(Position),
|
||||
Space(BadInputError, Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BadInputError {
|
||||
HasTab,
|
||||
|
|
|
@ -39,7 +39,6 @@ pub enum Problem {
|
|||
UnusedImport(Symbol, Region),
|
||||
UnusedModuleImport(ModuleId, Region),
|
||||
ExposedButNotDefined(Symbol),
|
||||
UnknownGeneratesWith(Loc<Ident>),
|
||||
ImportNameConflict {
|
||||
name: ModuleName,
|
||||
is_alias: bool,
|
||||
|
@ -251,7 +250,6 @@ impl Problem {
|
|||
Problem::ExplicitBuiltinTypeImport(_, _) => Warning,
|
||||
Problem::ImportShadowsSymbol { .. } => RuntimeError,
|
||||
Problem::ExposedButNotDefined(_) => RuntimeError,
|
||||
Problem::UnknownGeneratesWith(_) => RuntimeError,
|
||||
Problem::UnusedArgument(_, _, _, _) => Warning,
|
||||
Problem::UnusedBranchDef(_, _) => Warning,
|
||||
Problem::PrecedenceProblem(_) => RuntimeError,
|
||||
|
@ -333,7 +331,6 @@ impl Problem {
|
|||
| Problem::ExplicitBuiltinImport(_, region)
|
||||
| Problem::ExplicitBuiltinTypeImport(_, region)
|
||||
| Problem::ImportShadowsSymbol { region, .. }
|
||||
| Problem::UnknownGeneratesWith(Loc { region, .. })
|
||||
| Problem::UnusedArgument(_, _, _, region)
|
||||
| Problem::UnusedBranchDef(_, region)
|
||||
| Problem::PrecedenceProblem(PrecedenceProblem::BothNonAssociative(region, _, _))
|
||||
|
|
|
@ -1885,22 +1885,22 @@ fn task_always_twice() {
|
|||
effectAfter : Effect a, (a -> Effect b) -> Effect b
|
||||
effectAfter = \(@Effect thunk), transform -> transform (thunk {})
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
MyTask a err : Effect (Result a err)
|
||||
|
||||
always : a -> Task a *
|
||||
always : a -> MyTask a *
|
||||
always = \x -> effectAlways (Ok x)
|
||||
|
||||
fail : err -> Task * err
|
||||
fail : err -> MyTask * err
|
||||
fail = \x -> effectAlways (Err x)
|
||||
|
||||
after : Task a err, (a -> Task b err) -> Task b err
|
||||
after : MyTask a err, (a -> MyTask b err) -> MyTask b err
|
||||
after = \task, transform ->
|
||||
effectAfter task \res ->
|
||||
when res is
|
||||
Ok x -> transform x
|
||||
Err e -> fail e
|
||||
|
||||
main : Task {} F64
|
||||
main : MyTask {} F64
|
||||
main = after (always "foo") (\_ -> always {})
|
||||
|
||||
"#
|
||||
|
@ -1921,17 +1921,17 @@ fn wildcard_rigid() {
|
|||
|
||||
Effect a := {} -> a
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
MyTask a err : Effect (Result a err)
|
||||
|
||||
# this failed because of the `*`, but worked with `err`
|
||||
always : a -> Task a *
|
||||
always : a -> MyTask a *
|
||||
always = \x ->
|
||||
inner = \{} -> (Ok x)
|
||||
|
||||
@Effect inner
|
||||
|
||||
|
||||
main : Task {} (Frac *)
|
||||
main : MyTask {} (Frac *)
|
||||
main = always {}
|
||||
"#
|
||||
),
|
||||
|
@ -1950,16 +1950,16 @@ fn alias_of_alias_with_type_arguments() {
|
|||
|
||||
Effect a := a
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
MyTask a err : Effect (Result a err)
|
||||
|
||||
always : a -> Task a *
|
||||
always : a -> MyTask a *
|
||||
always = \x ->
|
||||
inner = (Ok x)
|
||||
|
||||
@Effect inner
|
||||
|
||||
|
||||
main : Task {} F64
|
||||
main : MyTask {} F64
|
||||
main = always {}
|
||||
"#
|
||||
),
|
||||
|
@ -1989,24 +1989,24 @@ fn todo_bad_error_message() {
|
|||
effectAfter : Effect a, (a -> Effect b) -> Effect b
|
||||
effectAfter = \(@Effect thunk), transform -> transform (thunk {})
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
MyTask a err : Effect (Result a err)
|
||||
|
||||
always : a -> Task a (Frac *)
|
||||
always : a -> MyTask a (Frac *)
|
||||
always = \x -> effectAlways (Ok x)
|
||||
|
||||
# the problem is that this restricts to `Task {} *`
|
||||
fail : err -> Task {} err
|
||||
# the problem is that this restricts to `MyTask {} *`
|
||||
fail : err -> MyTask {} err
|
||||
fail = \x -> effectAlways (Err x)
|
||||
|
||||
after : Task a err, (a -> Task b err) -> Task b err
|
||||
after : MyTask a err, (a -> MyTask b err) -> MyTask b err
|
||||
after = \task, transform ->
|
||||
effectAfter task \res ->
|
||||
when res is
|
||||
Ok x -> transform x
|
||||
# but here it must be `forall b. Task b {}`
|
||||
# but here it must be `forall b. MyTask b {}`
|
||||
Err e -> fail e
|
||||
|
||||
main : Task {} (Frac *)
|
||||
main : MyTask {} (Frac *)
|
||||
main =
|
||||
after (always "foo") (\_ -> always {})
|
||||
"#
|
||||
|
|
|
@ -1 +1 @@
|
|||
hosted Foo exposes [] imports [] generates Bar with []
|
||||
hosted Foo exposes [] imports []
|
||||
|
|
|
@ -22,24 +22,6 @@ Module {
|
|||
},
|
||||
item: [],
|
||||
},
|
||||
generates: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [],
|
||||
item: GeneratesKeyword,
|
||||
after: [],
|
||||
},
|
||||
item: UppercaseIdent(
|
||||
"Bar",
|
||||
),
|
||||
},
|
||||
generates_with: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [],
|
||||
item: WithKeyword,
|
||||
after: [],
|
||||
},
|
||||
item: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
hosted Foo exposes [] imports [] generates Bar with []
|
||||
hosted Foo exposes [] imports []
|
||||
|
|
|
@ -10,9 +10,3 @@ hosted Foo
|
|||
Blah,
|
||||
Baz.{ stuff, things },
|
||||
]
|
||||
generates Bar with
|
||||
[
|
||||
map,
|
||||
after,
|
||||
loop,
|
||||
]
|
||||
|
|
|
@ -95,58 +95,6 @@ Module {
|
|||
],
|
||||
},
|
||||
},
|
||||
generates: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [
|
||||
Newline,
|
||||
],
|
||||
item: GeneratesKeyword,
|
||||
after: [],
|
||||
},
|
||||
item: UppercaseIdent(
|
||||
"Bar",
|
||||
),
|
||||
},
|
||||
generates_with: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [],
|
||||
item: WithKeyword,
|
||||
after: [
|
||||
Newline,
|
||||
],
|
||||
},
|
||||
item: Collection {
|
||||
items: [
|
||||
@239-242 SpaceBefore(
|
||||
ExposedName(
|
||||
"map",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@256-261 SpaceBefore(
|
||||
ExposedName(
|
||||
"after",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@275-279 SpaceBefore(
|
||||
ExposedName(
|
||||
"loop",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
final_comments: [
|
||||
Newline,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
|
@ -10,9 +10,3 @@ hosted Foo
|
|||
Blah,
|
||||
Baz.{ stuff, things },
|
||||
]
|
||||
generates Bar with
|
||||
[
|
||||
map,
|
||||
after,
|
||||
loop,
|
||||
]
|
||||
|
|
|
@ -4930,10 +4930,10 @@ mod test_fmt {
|
|||
fn single_line_platform() {
|
||||
module_formats_same(
|
||||
"platform \"folkertdev/foo\" \
|
||||
requires { Model, Msg } { main : Effect {} } \
|
||||
requires { Model, Msg } { main : Task {} [] } \
|
||||
exposes [] \
|
||||
packages {} \
|
||||
imports [Task.{ Task }] \
|
||||
imports [] \
|
||||
provides [mainForHost]",
|
||||
);
|
||||
}
|
||||
|
@ -5002,7 +5002,7 @@ mod test_fmt {
|
|||
fn single_line_hosted() {
|
||||
module_formats_same(indoc!(
|
||||
r"
|
||||
hosted Foo exposes [] imports [] generates Bar with []"
|
||||
hosted Foo exposes [] imports []"
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -5019,11 +5019,6 @@ mod test_fmt {
|
|||
imports [
|
||||
Blah,
|
||||
Baz.{ stuff, things },
|
||||
]
|
||||
generates Bar with [
|
||||
map,
|
||||
after,
|
||||
loop,
|
||||
]"
|
||||
));
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ Effect : [
|
|||
DoIt {} ({} -> Effect),
|
||||
]
|
||||
|
||||
Task := ({} -> Effect) -> Effect
|
||||
MockTask := ({} -> Effect) -> Effect
|
||||
|
||||
doIt : {} -> Task
|
||||
doIt : {} -> MockTask
|
||||
doIt = \{} ->
|
||||
#^^^^{-1} {} -[[doIt(0)]]-> Task
|
||||
@Task \toNext ->
|
||||
#^^^^{-1} {} -[[doIt(0)]]-> MockTask
|
||||
@MockTask \toNext ->
|
||||
DoIt {} \{} -> (toNext {})
|
||||
|
|
|
@ -2,8 +2,8 @@ app "test" provides [always] to "./platform"
|
|||
|
||||
Effect a := {} -> a
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
MockTask a err : Effect (Result a err)
|
||||
|
||||
always : a -> Task a *
|
||||
always : a -> MockTask a *
|
||||
always = \x -> @Effect (\{} -> Ok x)
|
||||
#^^^^^^{-1} a -[[always(0)]]-> Task a *
|
||||
#^^^^^^{-1} a -[[always(0)]]-> MockTask a *
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
app "test" provides [tforever] to "./platform"
|
||||
|
||||
Effect a := {} -> a
|
||||
Task ok err := {} -> ok err
|
||||
|
||||
eforever : Effect a -> Effect b
|
||||
|
||||
Task a err : Effect (Result a err)
|
||||
|
||||
tforever : Task val err -> Task * *
|
||||
tforever = \task -> eforever task
|
||||
tforever : Task val * -> Task * *
|
||||
tforever = \task -> Task.forever task
|
||||
#^^^^^^^^{-1} Task val err -[[tforever(0)]]-> Task * *
|
||||
|
|
|
@ -315,14 +315,11 @@ impl IterTokens for HostedHeader<'_> {
|
|||
name,
|
||||
exposes,
|
||||
imports,
|
||||
generates: _,
|
||||
generates_with,
|
||||
} = self;
|
||||
|
||||
(name.iter_tokens(arena).into_iter())
|
||||
.chain(exposes.item.iter_tokens(arena))
|
||||
.chain(imports.item.iter_tokens(arena))
|
||||
.chain(generates_with.item.iter_tokens(arena))
|
||||
.collect_in(arena)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ const WILDCARD_NOT_ALLOWED: &str = "WILDCARD NOT ALLOWED HERE";
|
|||
const UNDERSCORE_NOT_ALLOWED: &str = "UNDERSCORE NOT ALLOWED HERE";
|
||||
const UNUSED_ARG: &str = "UNUSED ARGUMENT";
|
||||
const MISSING_DEFINITION: &str = "MISSING DEFINITION";
|
||||
const UNKNOWN_GENERATES_WITH: &str = "UNKNOWN GENERATES FUNCTION";
|
||||
const DUPLICATE_FIELD_NAME: &str = "DUPLICATE FIELD NAME";
|
||||
const DUPLICATE_TAG_NAME: &str = "DUPLICATE TAG NAME";
|
||||
const INVALID_UNICODE: &str = "INVALID UNICODE";
|
||||
|
@ -287,20 +286,6 @@ pub fn can_problem<'b>(
|
|||
|
||||
title = MISSING_DEFINITION.to_string();
|
||||
}
|
||||
Problem::UnknownGeneratesWith(loc_ident) => {
|
||||
doc = alloc.stack([
|
||||
alloc
|
||||
.reflow("I don't know how to generate the ")
|
||||
.append(alloc.ident(loc_ident.value))
|
||||
.append(alloc.reflow(" function.")),
|
||||
alloc.region(lines.convert_region(loc_ident.region), severity),
|
||||
alloc
|
||||
.reflow("Only specific functions like `after` and `map` can be generated.")
|
||||
.append(alloc.reflow("Learn more about hosted modules at TODO.")),
|
||||
]);
|
||||
|
||||
title = UNKNOWN_GENERATES_WITH.to_string();
|
||||
}
|
||||
Problem::UnusedArgument(closure_symbol, is_anonymous, argument_symbol, region) => {
|
||||
let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used.";
|
||||
|
||||
|
|
|
@ -3722,98 +3722,6 @@ fn to_header_report<'a>(
|
|||
}
|
||||
|
||||
EHeader::Space(error, pos) => to_space_report(alloc, lines, filename, error, *pos),
|
||||
EHeader::Generates(_, pos) => {
|
||||
let surroundings = Region::new(start, *pos);
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(*pos));
|
||||
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I am partway through parsing a header, but got stuck here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
|
||||
alloc.concat([
|
||||
alloc.reflow("I am expecting a type name next, like "),
|
||||
alloc.parser_suggestion("Effect"),
|
||||
alloc.reflow(". Type names must start with an uppercase letter."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "WEIRD GENERATED TYPE NAME".to_string(),
|
||||
severity,
|
||||
}
|
||||
}
|
||||
EHeader::GeneratesWith(generates_with, pos) => {
|
||||
to_generates_with_report(alloc, lines, filename, generates_with, *pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_generates_with_report<'a>(
|
||||
alloc: &'a RocDocAllocator<'a>,
|
||||
lines: &LineInfo,
|
||||
filename: PathBuf,
|
||||
parse_problem: &roc_parse::parser::EGeneratesWith,
|
||||
start: Position,
|
||||
) -> Report<'a> {
|
||||
use roc_parse::parser::EGeneratesWith;
|
||||
|
||||
let severity = Severity::RuntimeError;
|
||||
|
||||
match *parse_problem {
|
||||
EGeneratesWith::ListEnd(pos) | // TODO: give this its own error message
|
||||
EGeneratesWith::Identifier(pos) => {
|
||||
let surroundings = Region::new(start, pos);
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
let doc = alloc.stack([
|
||||
alloc
|
||||
.reflow(r"I am partway through parsing a provides list, but I got stuck here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
|
||||
alloc.concat([alloc.reflow(
|
||||
"I was expecting a type name, value name or function name next, like",
|
||||
)]),
|
||||
alloc
|
||||
.parser_suggestion("provides [Animal, default, tame]")
|
||||
.indent(4),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "WEIRD GENERATES".to_string(),
|
||||
severity,
|
||||
}
|
||||
}
|
||||
|
||||
EGeneratesWith::With(pos) => {
|
||||
let surroundings = Region::new(start, pos);
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
|
||||
alloc.concat([
|
||||
alloc.reflow("I am expecting the "),
|
||||
alloc.keyword("with"),
|
||||
alloc.reflow(" keyword next, like"),
|
||||
]),
|
||||
alloc
|
||||
.parser_suggestion("with [after, map]")
|
||||
.indent(4),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "WEIRD GENERATES".to_string(),
|
||||
severity,
|
||||
}
|
||||
}
|
||||
|
||||
EGeneratesWith::Space(error, pos) => to_space_report(alloc, lines, filename, &error, pos),
|
||||
|
||||
_ => todo!("unhandled parse error {:?}", parse_problem),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4232,7 +4140,7 @@ fn to_requires_report<'a>(
|
|||
alloc.reflow(" definition looks like"),
|
||||
]),
|
||||
alloc
|
||||
.parser_suggestion("requires {model=>Model, msg=>Msg} {main : Effect {}}")
|
||||
.parser_suggestion("requires {model=>Model, msg=>Msg} {main : Task {} []}")
|
||||
.indent(4),
|
||||
]);
|
||||
|
||||
|
@ -4261,7 +4169,7 @@ fn to_requires_report<'a>(
|
|||
alloc.reflow(" definition looks like"),
|
||||
]),
|
||||
alloc
|
||||
.parser_suggestion("requires { Model, Msg } {main : Effect {}}")
|
||||
.parser_suggestion("requires { Model, Msg } {main : Task {} []}")
|
||||
.indent(4),
|
||||
]);
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
hosted Effect
|
||||
exposes [Effect, after, map, always, forever, putLine, getLine]
|
||||
imports []
|
||||
generates Effect with [after, map, always, forever]
|
||||
|
||||
putLine : Str -> Effect {}
|
||||
|
||||
getLine : Effect Str
|
7
examples/cli/effects-platform/PlatformTask.roc
Normal file
7
examples/cli/effects-platform/PlatformTask.roc
Normal file
|
@ -0,0 +1,7 @@
|
|||
hosted PlatformTask
|
||||
exposes [putLine, getLine]
|
||||
imports []
|
||||
|
||||
putLine : Str -> Task {} *
|
||||
|
||||
getLine : Task Str *
|
|
@ -1,9 +1,9 @@
|
|||
platform "effects"
|
||||
requires {} { main : Effect.Effect {} }
|
||||
requires {} { main : Task {} [] }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Effect]
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Effect.Effect {}
|
||||
mainForHost : Task {} []
|
||||
mainForHost = main
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
app [main] { pf: platform "effects-platform/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
import pf.PlatformTask
|
||||
|
||||
main : Effect.Effect {}
|
||||
main : Task {} []
|
||||
main =
|
||||
Effect.after
|
||||
(Effect.getLine)
|
||||
\line ->
|
||||
Effect.after
|
||||
(Effect.putLine "You entered: $(line)")
|
||||
\{} ->
|
||||
Effect.after
|
||||
(Effect.putLine "It is known")
|
||||
\{} ->
|
||||
Effect.always {}
|
||||
line = PlatformTask.getLine!
|
||||
PlatformTask.putLine! "You entered: $(line)"
|
||||
PlatformTask.putLine! "It is known"
|
||||
|
||||
Task.ok {}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
module [Context, Data, with, getChar, Option, pushStack, popStack, toStr, inWhileScope]
|
||||
|
||||
import pf.File
|
||||
import pf.Task exposing [Task]
|
||||
import Variable exposing [Variable]
|
||||
|
||||
Option a : [Some a, None]
|
||||
|
@ -72,32 +71,32 @@ getChar : Context -> Task [T U8 Context] [EndOfData, NoScope]
|
|||
getChar = \ctx ->
|
||||
when List.last ctx.scopes is
|
||||
Ok scope ->
|
||||
(T val newScope) <- Task.await (getCharScope scope)
|
||||
Task.succeed (T val { ctx & scopes: List.set ctx.scopes (List.len ctx.scopes - 1) newScope })
|
||||
(T val newScope) = getCharScope! scope
|
||||
Task.ok (T val { ctx & scopes: List.set ctx.scopes (List.len ctx.scopes - 1) newScope })
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
getCharScope : Scope -> Task [T U8 Scope] [EndOfData, NoScope]
|
||||
getCharScope = \scope ->
|
||||
when List.get scope.buf scope.index is
|
||||
Ok val ->
|
||||
Task.succeed (T val { scope & index: scope.index + 1 })
|
||||
Task.ok (T val { scope & index: scope.index + 1 })
|
||||
|
||||
Err OutOfBounds ->
|
||||
when scope.data is
|
||||
Some h ->
|
||||
bytes <- Task.await (File.chunk h)
|
||||
bytes = File.chunk! h
|
||||
when List.first bytes is
|
||||
Ok val ->
|
||||
# This starts at 1 because the first character is already being returned.
|
||||
Task.succeed (T val { scope & buf: bytes, index: 1 })
|
||||
Task.ok (T val { scope & buf: bytes, index: 1 })
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Task.fail EndOfData
|
||||
Task.err EndOfData
|
||||
|
||||
None ->
|
||||
Task.fail EndOfData
|
||||
Task.err EndOfData
|
||||
|
||||
inWhileScope : Context -> Bool
|
||||
inWhileScope = \ctx ->
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
app [main] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.Task exposing [Task]
|
||||
import pf.Stdout
|
||||
import pf.Stdin
|
||||
import Context exposing [Context]
|
||||
|
@ -24,51 +23,51 @@ InterpreterErrors : [BadUtf8, DivByZero, EmptyStack, InvalidBooleanValue, Invali
|
|||
main : Str -> Task {} []
|
||||
main = \filename ->
|
||||
interpretFile filename
|
||||
|> Task.onFail \StringErr e -> Stdout.line "Ran into problem:\n$(e)\n"
|
||||
|> Task.onErr \StringErr e -> Stdout.line "Ran into problem:\n$(e)\n"
|
||||
|
||||
interpretFile : Str -> Task {} [StringErr Str]
|
||||
interpretFile = \filename ->
|
||||
ctx <- Context.with filename
|
||||
result <- Task.attempt (interpretCtx ctx)
|
||||
when result is
|
||||
Ok _ ->
|
||||
Task.succeed {}
|
||||
Task.attempt (interpretCtx ctx) \result ->
|
||||
when result is
|
||||
Ok _ ->
|
||||
Task.ok {}
|
||||
|
||||
Err BadUtf8 ->
|
||||
Task.fail (StringErr "Failed to convert string from Utf8 bytes")
|
||||
Err BadUtf8 ->
|
||||
Task.err (StringErr "Failed to convert string from Utf8 bytes")
|
||||
|
||||
Err DivByZero ->
|
||||
Task.fail (StringErr "Division by zero")
|
||||
Err DivByZero ->
|
||||
Task.err (StringErr "Division by zero")
|
||||
|
||||
Err EmptyStack ->
|
||||
Task.fail (StringErr "Tried to pop a value off of the stack when it was empty")
|
||||
Err EmptyStack ->
|
||||
Task.err (StringErr "Tried to pop a value off of the stack when it was empty")
|
||||
|
||||
Err InvalidBooleanValue ->
|
||||
Task.fail (StringErr "Ran into an invalid boolean that was neither false (0) or true (-1)")
|
||||
Err InvalidBooleanValue ->
|
||||
Task.err (StringErr "Ran into an invalid boolean that was neither false (0) or true (-1)")
|
||||
|
||||
Err (InvalidChar char) ->
|
||||
Task.fail (StringErr "Ran into an invalid character with ascii code: $(char)")
|
||||
Err (InvalidChar char) ->
|
||||
Task.err (StringErr "Ran into an invalid character with ascii code: $(char)")
|
||||
|
||||
Err MaxInputNumber ->
|
||||
Task.fail (StringErr "Like the original false compiler, the max input number is 320,000")
|
||||
Err MaxInputNumber ->
|
||||
Task.err (StringErr "Like the original false compiler, the max input number is 320,000")
|
||||
|
||||
Err NoLambdaOnStack ->
|
||||
Task.fail (StringErr "Tried to run a lambda when no lambda was on the stack")
|
||||
Err NoLambdaOnStack ->
|
||||
Task.err (StringErr "Tried to run a lambda when no lambda was on the stack")
|
||||
|
||||
Err NoNumberOnStack ->
|
||||
Task.fail (StringErr "Tried to run a number when no number was on the stack")
|
||||
Err NoNumberOnStack ->
|
||||
Task.err (StringErr "Tried to run a number when no number was on the stack")
|
||||
|
||||
Err NoVariableOnStack ->
|
||||
Task.fail (StringErr "Tried to load a variable when no variable was on the stack")
|
||||
Err NoVariableOnStack ->
|
||||
Task.err (StringErr "Tried to load a variable when no variable was on the stack")
|
||||
|
||||
Err NoScope ->
|
||||
Task.fail (StringErr "Tried to run code when not in any scope")
|
||||
Err NoScope ->
|
||||
Task.err (StringErr "Tried to run code when not in any scope")
|
||||
|
||||
Err OutOfBounds ->
|
||||
Task.fail (StringErr "Tried to load from an offset that was outside of the stack")
|
||||
Err OutOfBounds ->
|
||||
Task.err (StringErr "Tried to load from an offset that was outside of the stack")
|
||||
|
||||
Err UnexpectedEndOfData ->
|
||||
Task.fail (StringErr "Hit end of data while still parsing something")
|
||||
Err UnexpectedEndOfData ->
|
||||
Task.err (StringErr "Hit end of data while still parsing something")
|
||||
|
||||
isDigit : U8 -> Bool
|
||||
isDigit = \char ->
|
||||
|
@ -107,37 +106,37 @@ interpretCtxLoop = \ctx ->
|
|||
if n == 0 then
|
||||
newScope = { scope & whileInfo: None }
|
||||
|
||||
Task.succeed (Step { popCtx & scopes: List.set ctx.scopes last newScope })
|
||||
Task.ok (Step { popCtx & scopes: List.set ctx.scopes last newScope })
|
||||
else
|
||||
newScope = { scope & whileInfo: Some { state: InBody, body, cond } }
|
||||
|
||||
Task.succeed (Step { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } })
|
||||
Task.ok (Step { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } })
|
||||
|
||||
Err e ->
|
||||
Task.fail e
|
||||
Task.err e
|
||||
|
||||
Some { state: InBody, body, cond } ->
|
||||
# Just rand the body. Run the condition again.
|
||||
newScope = { scope & whileInfo: Some { state: InCond, body, cond } }
|
||||
|
||||
Task.succeed (Step { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } })
|
||||
Task.ok (Step { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } })
|
||||
|
||||
None ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Err OutOfBounds ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Executing ->
|
||||
# {} <- Task.await (Stdout.line (Context.toStr ctx))
|
||||
result <- Task.attempt (Context.getChar ctx)
|
||||
# Stdout.line! (Context.toStr ctx)
|
||||
result = Task.result! (Context.getChar ctx)
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
execCtx <- Task.await (stepExecCtx newCtx val)
|
||||
Task.succeed (Step execCtx)
|
||||
execCtx = stepExecCtx! newCtx val
|
||||
Task.ok (Step execCtx)
|
||||
|
||||
Err NoScope ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
# Computation complete for this scope.
|
||||
|
@ -146,28 +145,28 @@ interpretCtxLoop = \ctx ->
|
|||
|
||||
# If no scopes left, all execution complete.
|
||||
if List.isEmpty dropCtx.scopes then
|
||||
Task.succeed (Done dropCtx)
|
||||
Task.ok (Done dropCtx)
|
||||
else
|
||||
Task.succeed (Step dropCtx)
|
||||
Task.ok (Step dropCtx)
|
||||
|
||||
InComment ->
|
||||
result <- Task.attempt (Context.getChar ctx)
|
||||
result = Task.result! (Context.getChar ctx)
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
if val == 0x7D then
|
||||
# `}` end of comment
|
||||
Task.succeed (Step { newCtx & state: Executing })
|
||||
Task.ok (Step { newCtx & state: Executing })
|
||||
else
|
||||
Task.succeed (Step { newCtx & state: InComment })
|
||||
Task.ok (Step { newCtx & state: InComment })
|
||||
|
||||
Err NoScope ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.fail UnexpectedEndOfData
|
||||
Task.err UnexpectedEndOfData
|
||||
|
||||
InNumber accum ->
|
||||
result <- Task.attempt (Context.getChar ctx)
|
||||
result = Task.result! (Context.getChar ctx)
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
if isDigit val then
|
||||
|
@ -177,68 +176,68 @@ interpretCtxLoop = \ctx ->
|
|||
# so this is make i64 mul by 10 then convert back to i32.
|
||||
nextAccum = (10 * Num.intCast accum) + Num.intCast (val - 0x30)
|
||||
|
||||
Task.succeed (Step { newCtx & state: InNumber (Num.intCast nextAccum) })
|
||||
Task.ok (Step { newCtx & state: InNumber (Num.intCast nextAccum) })
|
||||
else
|
||||
# outside of number now, this needs to be executed.
|
||||
pushCtx = Context.pushStack newCtx (Number accum)
|
||||
|
||||
execCtx <- Task.await (stepExecCtx { pushCtx & state: Executing } val)
|
||||
Task.succeed (Step execCtx)
|
||||
execCtx = stepExecCtx! { pushCtx & state: Executing } val
|
||||
Task.ok (Step execCtx)
|
||||
|
||||
Err NoScope ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.fail UnexpectedEndOfData
|
||||
Task.err UnexpectedEndOfData
|
||||
|
||||
InString bytes ->
|
||||
result <- Task.attempt (Context.getChar ctx)
|
||||
result = Task.result! (Context.getChar ctx)
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
if val == 0x22 then
|
||||
# `"` end of string
|
||||
when Str.fromUtf8 bytes is
|
||||
Ok str ->
|
||||
{} <- Task.await (Stdout.raw str)
|
||||
Task.succeed (Step { newCtx & state: Executing })
|
||||
Stdout.raw! str
|
||||
Task.ok (Step { newCtx & state: Executing })
|
||||
|
||||
Err _ ->
|
||||
Task.fail BadUtf8
|
||||
Task.err BadUtf8
|
||||
else
|
||||
Task.succeed (Step { newCtx & state: InString (List.append bytes val) })
|
||||
Task.ok (Step { newCtx & state: InString (List.append bytes val) })
|
||||
|
||||
Err NoScope ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.fail UnexpectedEndOfData
|
||||
Task.err UnexpectedEndOfData
|
||||
|
||||
InLambda depth bytes ->
|
||||
result <- Task.attempt (Context.getChar ctx)
|
||||
result = Task.result! (Context.getChar ctx)
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
if val == 0x5B then
|
||||
# start of a nested lambda `[`
|
||||
Task.succeed (Step { newCtx & state: InLambda (depth + 1) (List.append bytes val) })
|
||||
Task.ok (Step { newCtx & state: InLambda (depth + 1) (List.append bytes val) })
|
||||
else if val == 0x5D then
|
||||
# `]` end of current lambda
|
||||
if depth == 0 then
|
||||
# end of all lambdas
|
||||
Task.succeed (Step (Context.pushStack { newCtx & state: Executing } (Lambda bytes)))
|
||||
Task.ok (Step (Context.pushStack { newCtx & state: Executing } (Lambda bytes)))
|
||||
else
|
||||
# end of nested lambda
|
||||
Task.succeed (Step { newCtx & state: InLambda (depth - 1) (List.append bytes val) })
|
||||
Task.ok (Step { newCtx & state: InLambda (depth - 1) (List.append bytes val) })
|
||||
else
|
||||
Task.succeed (Step { newCtx & state: InLambda depth (List.append bytes val) })
|
||||
Task.ok (Step { newCtx & state: InLambda depth (List.append bytes val) })
|
||||
|
||||
Err NoScope ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.fail UnexpectedEndOfData
|
||||
Task.err UnexpectedEndOfData
|
||||
|
||||
InSpecialChar ->
|
||||
result <- Task.attempt (Context.getChar { ctx & state: Executing })
|
||||
result = Task.result! (Context.getChar { ctx & state: Executing })
|
||||
when result is
|
||||
Ok (T 0xB8 newCtx) ->
|
||||
result2 =
|
||||
|
@ -254,35 +253,35 @@ interpretCtxLoop = \ctx ->
|
|||
Err OutOfBounds
|
||||
|
||||
when result2 is
|
||||
Ok a -> Task.succeed (Step a)
|
||||
Err e -> Task.fail e
|
||||
Ok a -> Task.ok (Step a)
|
||||
Err e -> Task.err e
|
||||
|
||||
Ok (T 0x9F newCtx) ->
|
||||
# This is supposed to flush io buffers. We don't buffer, so it does nothing
|
||||
Task.succeed (Step newCtx)
|
||||
Task.ok (Step newCtx)
|
||||
|
||||
Ok (T x _) ->
|
||||
data = Num.toStr (Num.intCast x)
|
||||
|
||||
Task.fail (InvalidChar data)
|
||||
Task.err (InvalidChar data)
|
||||
|
||||
Err NoScope ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.fail UnexpectedEndOfData
|
||||
Task.err UnexpectedEndOfData
|
||||
|
||||
LoadChar ->
|
||||
result <- Task.attempt (Context.getChar { ctx & state: Executing })
|
||||
result = Task.result! (Context.getChar { ctx & state: Executing })
|
||||
when result is
|
||||
Ok (T x newCtx) ->
|
||||
Task.succeed (Step (Context.pushStack newCtx (Number (Num.intCast x))))
|
||||
Task.ok (Step (Context.pushStack newCtx (Number (Num.intCast x))))
|
||||
|
||||
Err NoScope ->
|
||||
Task.fail NoScope
|
||||
Task.err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.fail UnexpectedEndOfData
|
||||
Task.err UnexpectedEndOfData
|
||||
|
||||
# If it weren't for reading stdin or writing to stdout, this could return a result.
|
||||
stepExecCtx : Context, U8 -> Task Context InterpreterErrors
|
||||
|
@ -333,15 +332,15 @@ stepExecCtx = \ctx, char ->
|
|||
# Switching this to List.last and changing the error to ListWasEmpty leads to a compiler bug.
|
||||
# Complains about the types eq not matching.
|
||||
when List.get ctx.stack (List.len ctx.stack - 1) is
|
||||
Ok dupItem -> Task.succeed (Context.pushStack ctx dupItem)
|
||||
Err OutOfBounds -> Task.fail EmptyStack
|
||||
Ok dupItem -> Task.ok (Context.pushStack ctx dupItem)
|
||||
Err OutOfBounds -> Task.err EmptyStack
|
||||
|
||||
0x25 ->
|
||||
# `%` drop
|
||||
when Context.popStack ctx is
|
||||
# Dropping with an empty stack, all results here are fine
|
||||
Ok (T popCtx _) -> Task.succeed popCtx
|
||||
Err _ -> Task.succeed ctx
|
||||
Ok (T popCtx _) -> Task.ok popCtx
|
||||
Err _ -> Task.ok ctx
|
||||
|
||||
0x5C ->
|
||||
# `\` swap
|
||||
|
@ -352,11 +351,11 @@ stepExecCtx = \ctx, char ->
|
|||
|
||||
when result2 is
|
||||
Ok a ->
|
||||
Task.succeed a
|
||||
Task.ok a
|
||||
|
||||
# Being explicit with error type is required to stop the need to propogate the error parameters to Context.popStack
|
||||
Err EmptyStack ->
|
||||
Task.fail EmptyStack
|
||||
Task.err EmptyStack
|
||||
|
||||
0x40 ->
|
||||
# `@` rot
|
||||
|
@ -368,17 +367,17 @@ stepExecCtx = \ctx, char ->
|
|||
|
||||
when result2 is
|
||||
Ok a ->
|
||||
Task.succeed a
|
||||
Task.ok a
|
||||
|
||||
# Being explicit with error type is required to stop the need to propogate the error parameters to Context.popStack
|
||||
Err EmptyStack ->
|
||||
Task.fail EmptyStack
|
||||
Task.err EmptyStack
|
||||
|
||||
0xC3 ->
|
||||
# `ø` pick or `ß` flush
|
||||
# these are actually 2 bytes, 0xC3 0xB8 or 0xC3 0x9F
|
||||
# requires special parsing
|
||||
Task.succeed { ctx & state: InSpecialChar }
|
||||
Task.ok { ctx & state: InSpecialChar }
|
||||
|
||||
0x4F ->
|
||||
# `O` also treat this as pick for easier script writing
|
||||
|
@ -399,11 +398,11 @@ stepExecCtx = \ctx, char ->
|
|||
0x42 ->
|
||||
# `B` also treat this as flush for easier script writing
|
||||
# This is supposed to flush io buffers. We don't buffer, so it does nothing
|
||||
Task.succeed ctx
|
||||
Task.ok ctx
|
||||
|
||||
0x27 ->
|
||||
# `'` load next char
|
||||
Task.succeed { ctx & state: LoadChar }
|
||||
Task.ok { ctx & state: LoadChar }
|
||||
|
||||
0x2B ->
|
||||
# `+` add
|
||||
|
@ -472,33 +471,33 @@ stepExecCtx = \ctx, char ->
|
|||
Ok (T popCtx num) ->
|
||||
when Str.fromUtf8 [Num.intCast num] is
|
||||
Ok str ->
|
||||
{} <- Task.await (Stdout.raw str)
|
||||
Task.succeed popCtx
|
||||
Stdout.raw! str
|
||||
Task.ok popCtx
|
||||
|
||||
Err _ ->
|
||||
Task.fail BadUtf8
|
||||
Task.err BadUtf8
|
||||
|
||||
Err e ->
|
||||
Task.fail e
|
||||
Task.err e
|
||||
|
||||
0x2E ->
|
||||
# `.` write int
|
||||
when popNumber ctx is
|
||||
Ok (T popCtx num) ->
|
||||
{} <- Task.await (Stdout.raw (Num.toStr (Num.intCast num)))
|
||||
Task.succeed popCtx
|
||||
Stdout.raw! (Num.toStr (Num.intCast num))
|
||||
Task.ok popCtx
|
||||
|
||||
Err e ->
|
||||
Task.fail e
|
||||
Task.err e
|
||||
|
||||
0x5E ->
|
||||
# `^` read char as int
|
||||
in <- Task.await Stdin.char
|
||||
in = Stdin.char!
|
||||
if in == 255 then
|
||||
# max char sent on EOF. Change to -1
|
||||
Task.succeed (Context.pushStack ctx (Number -1))
|
||||
Task.ok (Context.pushStack ctx (Number -1))
|
||||
else
|
||||
Task.succeed (Context.pushStack ctx (Number (Num.intCast in)))
|
||||
Task.ok (Context.pushStack ctx (Number (Num.intCast in)))
|
||||
|
||||
0x3A ->
|
||||
# `:` store to variable
|
||||
|
@ -521,33 +520,33 @@ stepExecCtx = \ctx, char ->
|
|||
|
||||
0x22 ->
|
||||
# `"` string start
|
||||
Task.succeed { ctx & state: InString [] }
|
||||
Task.ok { ctx & state: InString [] }
|
||||
|
||||
0x5B ->
|
||||
# `"` string start
|
||||
Task.succeed { ctx & state: InLambda 0 [] }
|
||||
Task.ok { ctx & state: InLambda 0 [] }
|
||||
|
||||
0x7B ->
|
||||
# `{` comment start
|
||||
Task.succeed { ctx & state: InComment }
|
||||
Task.ok { ctx & state: InComment }
|
||||
|
||||
x if isDigit x ->
|
||||
# number start
|
||||
Task.succeed { ctx & state: InNumber (Num.intCast (x - 0x30)) }
|
||||
Task.ok { ctx & state: InNumber (Num.intCast (x - 0x30)) }
|
||||
|
||||
x if isWhitespace x ->
|
||||
Task.succeed ctx
|
||||
Task.ok ctx
|
||||
|
||||
x ->
|
||||
when Variable.fromUtf8 x is
|
||||
# letters are variable names
|
||||
Ok var ->
|
||||
Task.succeed (Context.pushStack ctx (Var var))
|
||||
Task.ok (Context.pushStack ctx (Var var))
|
||||
|
||||
Err _ ->
|
||||
data = Num.toStr (Num.intCast x)
|
||||
|
||||
Task.fail (InvalidChar data)
|
||||
Task.err (InvalidChar data)
|
||||
|
||||
unaryOp : Context, (I32 -> I32) -> Result Context InterpreterErrors
|
||||
unaryOp = \ctx, op ->
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
hosted Effect
|
||||
exposes [Effect, after, map, always, forever, loop, openFile, closeFile, withFileOpen, getFileLine, getFileBytes, putLine, putRaw, getLine, getChar]
|
||||
imports []
|
||||
generates Effect with [after, map, always, forever, loop]
|
||||
|
||||
openFile : Str -> Effect U64
|
||||
|
||||
closeFile : U64 -> Effect {}
|
||||
|
||||
withFileOpen : Str, (U64 -> Effect (Result ok err)) -> Effect {}
|
||||
|
||||
getFileLine : U64 -> Effect Str
|
||||
|
||||
getFileBytes : U64 -> Effect (List U8)
|
||||
|
||||
putLine : Str -> Effect {}
|
||||
|
||||
putRaw : Str -> Effect {}
|
||||
|
||||
getLine : Effect Str
|
||||
|
||||
getChar : Effect U8
|
|
@ -1,28 +1,28 @@
|
|||
module [line, Handle, withOpen, chunk]
|
||||
module [line, withOpen, chunk, Handle]
|
||||
|
||||
import pf.Effect
|
||||
import Task exposing [Task]
|
||||
import pf.PlatformTask
|
||||
|
||||
Handle := U64
|
||||
|
||||
line : Handle -> Task.Task Str *
|
||||
line = \@Handle handle -> Effect.after (Effect.getFileLine handle) Task.succeed
|
||||
line : Handle -> Task Str *
|
||||
line = \@Handle handle -> PlatformTask.getFileLine handle
|
||||
|
||||
chunk : Handle -> Task.Task (List U8) *
|
||||
chunk = \@Handle handle -> Effect.after (Effect.getFileBytes handle) Task.succeed
|
||||
chunk : Handle -> Task (List U8) *
|
||||
chunk = \@Handle handle -> PlatformTask.getFileBytes handle
|
||||
|
||||
open : Str -> Task.Task Handle *
|
||||
open : Str -> Task Handle *
|
||||
open = \path ->
|
||||
Effect.openFile path
|
||||
|> Effect.map (\id -> @Handle id)
|
||||
|> Effect.after Task.succeed
|
||||
PlatformTask.openFile path
|
||||
|> Task.map @Handle
|
||||
|
||||
close : Handle -> Task.Task {} *
|
||||
close = \@Handle handle -> Effect.after (Effect.closeFile handle) Task.succeed
|
||||
close = \@Handle handle -> PlatformTask.closeFile handle
|
||||
|
||||
withOpen : Str, (Handle -> Task {} a) -> Task {} a
|
||||
withOpen = \path, callback ->
|
||||
handle <- Task.await (open path)
|
||||
result <- Task.attempt (callback handle)
|
||||
{} <- Task.await (close handle)
|
||||
Task.fromResult result
|
||||
handle = open! path
|
||||
|
||||
callback handle
|
||||
|> Task.attempt \result ->
|
||||
close! handle
|
||||
Task.fromResult result
|
||||
|
|
21
examples/cli/false-interpreter/platform/PlatformTask.roc
Normal file
21
examples/cli/false-interpreter/platform/PlatformTask.roc
Normal file
|
@ -0,0 +1,21 @@
|
|||
hosted PlatformTask
|
||||
exposes [openFile, closeFile, withFileOpen, getFileLine, getFileBytes, putLine, putRaw, getLine, getChar]
|
||||
imports []
|
||||
|
||||
openFile : Str -> Task U64 *
|
||||
|
||||
closeFile : U64 -> Task {} *
|
||||
|
||||
withFileOpen : Str, (U64 -> Task ok err) -> Task {} *
|
||||
|
||||
getFileLine : U64 -> Task Str *
|
||||
|
||||
getFileBytes : U64 -> Task (List U8) *
|
||||
|
||||
putLine : Str -> Task {} *
|
||||
|
||||
putRaw : Str -> Task {} *
|
||||
|
||||
getLine : Task Str *
|
||||
|
||||
getChar : Task U8 *
|
|
@ -1,9 +1,9 @@
|
|||
module [char]
|
||||
|
||||
import pf.Effect
|
||||
import Task
|
||||
import pf.PlatformTask
|
||||
|
||||
# line : Task.Task Str *
|
||||
# line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice
|
||||
char : Task.Task U8 *
|
||||
char = Effect.after Effect.getChar Task.succeed # TODO FIXME Effect.getLine should suffice
|
||||
line : Task Str *
|
||||
line = PlatformTask.getLine
|
||||
|
||||
char : Task U8 *
|
||||
char = PlatformTask.getChar
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
module [line, raw]
|
||||
|
||||
import pf.Effect
|
||||
import Task exposing [Task]
|
||||
import pf.PlatformTask
|
||||
|
||||
line : Str -> Task {} *
|
||||
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})
|
||||
line = PlatformTask.putLine
|
||||
|
||||
raw : Str -> Task {} *
|
||||
raw = \str -> Effect.map (Effect.putRaw str) (\_ -> Ok {})
|
||||
raw = PlatformTask.putRaw
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
module [Task, succeed, fail, await, map, onFail, attempt, fromResult, loop]
|
||||
|
||||
import pf.Effect
|
||||
|
||||
Task ok err : Effect.Effect (Result ok err)
|
||||
|
||||
loop : state, (state -> Task [Step state, Done done] err) -> Task done err
|
||||
loop = \state, step ->
|
||||
looper = \current ->
|
||||
step current
|
||||
|> Effect.map
|
||||
\res ->
|
||||
when res is
|
||||
Ok (Step newState) -> Step newState
|
||||
Ok (Done result) -> Done (Ok result)
|
||||
Err e -> Done (Err e)
|
||||
|
||||
Effect.loop state looper
|
||||
|
||||
succeed : val -> Task val *
|
||||
succeed = \val ->
|
||||
Effect.always (Ok val)
|
||||
|
||||
fail : err -> Task * err
|
||||
fail = \val ->
|
||||
Effect.always (Err val)
|
||||
|
||||
fromResult : Result a e -> Task a e
|
||||
fromResult = \result ->
|
||||
when result is
|
||||
Ok a -> succeed a
|
||||
Err e -> fail e
|
||||
|
||||
attempt : Task a b, (Result a b -> Task c d) -> Task c d
|
||||
attempt = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok ok -> transform (Ok ok)
|
||||
Err err -> transform (Err err)
|
||||
|
||||
await : Task a err, (a -> Task b err) -> Task b err
|
||||
await = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok a -> transform a
|
||||
Err err -> Task.fail err
|
||||
|
||||
onFail : Task ok a, (a -> Task ok b) -> Task ok b
|
||||
onFail = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok a -> Task.succeed a
|
||||
Err err -> transform err
|
||||
|
||||
map : Task a err, (a -> b) -> Task b err
|
||||
map = \effect, transform ->
|
||||
Effect.after
|
||||
effect
|
||||
\result ->
|
||||
when result is
|
||||
Ok a -> Task.succeed (transform a)
|
||||
Err err -> Task.fail err
|
|
@ -2,7 +2,7 @@ platform "false-interpreter"
|
|||
requires {} { main : Str -> Task {} [] }
|
||||
exposes []
|
||||
packages {}
|
||||
imports [Task.{ Task }]
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Str -> Task {} []
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use core::ffi::c_void;
|
||||
use core::mem::MaybeUninit;
|
||||
use libc;
|
||||
use roc_std::{RocList, RocStr};
|
||||
use roc_std::{RocList, RocResult, RocStr};
|
||||
use std::env;
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
|
@ -146,55 +146,59 @@ unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 {
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getLine() -> RocStr {
|
||||
pub extern "C" fn roc_fx_getLine() -> RocResult<RocStr, ()> {
|
||||
let stdin = std::io::stdin();
|
||||
let line1 = stdin.lock().lines().next().unwrap().unwrap();
|
||||
|
||||
RocStr::from(line1.as_str())
|
||||
RocResult::ok(RocStr::from(line1.as_str()))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getChar() -> u8 {
|
||||
pub extern "C" fn roc_fx_getChar() -> RocResult<u8, ()> {
|
||||
let mut buffer = [0];
|
||||
|
||||
if let Err(ioerr) = std::io::stdin().lock().read_exact(&mut buffer[..]) {
|
||||
if ioerr.kind() == std::io::ErrorKind::UnexpectedEof {
|
||||
u8::MAX
|
||||
RocResult::ok(u8::MAX)
|
||||
} else {
|
||||
panic!("Got an unexpected error while reading char from stdin");
|
||||
}
|
||||
} else {
|
||||
buffer[0]
|
||||
RocResult::ok(buffer[0])
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_putLine(line: &RocStr) {
|
||||
pub extern "C" fn roc_fx_putLine(line: &RocStr) -> RocResult<(), ()> {
|
||||
let string = line.as_str();
|
||||
println!("{}", string);
|
||||
let _ = std::io::stdout().lock().flush();
|
||||
|
||||
RocResult::ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_putRaw(line: &RocStr) {
|
||||
pub extern "C" fn roc_fx_putRaw(line: &RocStr) -> RocResult<(), ()> {
|
||||
let string = line.as_str();
|
||||
print!("{}", string);
|
||||
let _ = std::io::stdout().lock().flush();
|
||||
|
||||
RocResult::ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getFileLine(br_ptr: *mut BufReader<File>) -> RocStr {
|
||||
pub extern "C" fn roc_fx_getFileLine(br_ptr: *mut BufReader<File>) -> RocResult<RocStr, ()> {
|
||||
let br = unsafe { &mut *br_ptr };
|
||||
let mut line1 = String::default();
|
||||
|
||||
br.read_line(&mut line1)
|
||||
.expect("Failed to read line from file");
|
||||
|
||||
RocStr::from(line1.as_str())
|
||||
RocResult::ok(RocStr::from(line1.as_str()))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u8> {
|
||||
pub extern "C" fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocResult<RocList<u8>, ()> {
|
||||
let br = unsafe { &mut *br_ptr };
|
||||
let mut buffer = [0; 0x10 /* This is intentionally small to ensure correct implementation */];
|
||||
|
||||
|
@ -202,25 +206,27 @@ pub extern "C" fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u
|
|||
.read(&mut buffer[..])
|
||||
.expect("Failed to read bytes from file");
|
||||
|
||||
RocList::from_slice(&buffer[..count])
|
||||
RocResult::ok(RocList::from_slice(&buffer[..count]))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_closeFile(br_ptr: *mut BufReader<File>) {
|
||||
pub extern "C" fn roc_fx_closeFile(br_ptr: *mut BufReader<File>) -> RocResult<(), ()> {
|
||||
unsafe {
|
||||
let boxed = Box::from_raw(br_ptr);
|
||||
drop(boxed)
|
||||
}
|
||||
|
||||
RocResult::ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_openFile(name: &RocStr) -> *mut BufReader<File> {
|
||||
pub extern "C" fn roc_fx_openFile(name: &RocStr) -> RocResult<*mut BufReader<File>, ()> {
|
||||
let string = name.as_str();
|
||||
match File::open(string) {
|
||||
Ok(f) => {
|
||||
let br = BufReader::new(f);
|
||||
|
||||
Box::into_raw(Box::new(br))
|
||||
RocResult::ok(Box::into_raw(Box::new(br)))
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("unable to open file {:?}", name)
|
||||
|
@ -229,7 +235,7 @@ pub extern "C" fn roc_fx_openFile(name: &RocStr) -> *mut BufReader<File> {
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_withFileOpen(_name: &RocStr, _buffer: *const u8) {
|
||||
pub extern "C" fn roc_fx_withFileOpen(_name: &RocStr, _buffer: *const u8) -> RocResult<(), ()> {
|
||||
// TODO: figure out accepting a closure in an fx and passing data to it.
|
||||
// let f = File::open(name.as_str()).expect("Unable to open file");
|
||||
// let mut br = BufReader::new(f);
|
||||
|
@ -238,4 +244,6 @@ pub extern "C" fn roc_fx_withFileOpen(_name: &RocStr, _buffer: *const u8) {
|
|||
// let closure_data_ptr = buffer.offset(8);
|
||||
// call_the_closure(closure_data_ptr);
|
||||
// }
|
||||
|
||||
RocResult::ok(())
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@ module [
|
|||
dispatchEvent,
|
||||
]
|
||||
|
||||
import Effect exposing [
|
||||
Effect,
|
||||
import PlatformTask exposing [
|
||||
NodeId,
|
||||
HandlerId,
|
||||
TagName,
|
||||
|
@ -81,16 +80,16 @@ DiffState state : { rendered : RenderedTree state, patches : List Patch }
|
|||
# -------------------------------
|
||||
# INITIALISATION
|
||||
# -------------------------------
|
||||
initClientApp : List U8, App state initData -> Effect (PlatformState state initData) where initData implements Decoding
|
||||
initClientApp : List U8, App state initData -> Task (PlatformState state initData) * where initData implements Decoding
|
||||
initClientApp = \json, app ->
|
||||
# Initialise the Roc representation of the rendered DOM, and calculate patches (for event listeners)
|
||||
{ state, rendered, patches } =
|
||||
initClientAppHelp json app
|
||||
|
||||
# Call out to JS to patch the DOM, attaching the event listeners
|
||||
_ <- applyPatches patches |> Effect.after
|
||||
applyPatches! patches
|
||||
|
||||
Effect.always {
|
||||
Task.ok {
|
||||
app,
|
||||
state,
|
||||
rendered,
|
||||
|
@ -162,30 +161,28 @@ indexNodes = \{ nodes, siblingIds }, unrendered ->
|
|||
# -------------------------------
|
||||
# Patches
|
||||
# -------------------------------
|
||||
applyPatch : Patch -> Effect {}
|
||||
applyPatch : Patch -> Task {} *
|
||||
applyPatch = \patch ->
|
||||
when patch is
|
||||
CreateElement nodeId tagName -> Effect.createElement nodeId tagName
|
||||
CreateTextNode nodeId content -> Effect.createTextNode nodeId content
|
||||
UpdateTextNode nodeId content -> Effect.updateTextNode nodeId content
|
||||
AppendChild parentId childId -> Effect.appendChild parentId childId
|
||||
RemoveNode id -> Effect.removeNode id
|
||||
ReplaceNode oldId newId -> Effect.replaceNode oldId newId
|
||||
SetAttribute nodeId attrName value -> Effect.setAttribute nodeId attrName value
|
||||
RemoveAttribute nodeId attrName -> Effect.removeAttribute nodeId attrName
|
||||
SetProperty nodeId propName json -> Effect.setProperty nodeId propName json
|
||||
RemoveProperty nodeId propName -> Effect.removeProperty nodeId propName
|
||||
SetStyle nodeId key value -> Effect.setStyle nodeId key value
|
||||
SetListener nodeId eventType accessorsJson handlerId -> Effect.setListener nodeId eventType accessorsJson handlerId
|
||||
RemoveListener nodeId handlerId -> Effect.removeListener nodeId handlerId
|
||||
CreateElement nodeId tagName -> PlatformTask.createElement nodeId tagName
|
||||
CreateTextNode nodeId content -> PlatformTask.createTextNode nodeId content
|
||||
UpdateTextNode nodeId content -> PlatformTask.updateTextNode nodeId content
|
||||
AppendChild parentId childId -> PlatformTask.appendChild parentId childId
|
||||
RemoveNode id -> PlatformTask.removeNode id
|
||||
ReplaceNode oldId newId -> PlatformTask.replaceNode oldId newId
|
||||
SetAttribute nodeId attrName value -> PlatformTask.setAttribute nodeId attrName value
|
||||
RemoveAttribute nodeId attrName -> PlatformTask.removeAttribute nodeId attrName
|
||||
SetProperty nodeId propName json -> PlatformTask.setProperty nodeId propName json
|
||||
RemoveProperty nodeId propName -> PlatformTask.removeProperty nodeId propName
|
||||
SetStyle nodeId key value -> PlatformTask.setStyle nodeId key value
|
||||
SetListener nodeId eventType accessorsJson handlerId -> PlatformTask.setListener nodeId eventType accessorsJson handlerId
|
||||
RemoveListener nodeId handlerId -> PlatformTask.removeListener nodeId handlerId
|
||||
|
||||
walkPatches : Effect {}, Patch -> Effect {}
|
||||
walkPatches = \previousEffects, patch ->
|
||||
Effect.after previousEffects \{} -> applyPatch patch
|
||||
|
||||
applyPatches : List Patch -> Effect {}
|
||||
applyPatches : List Patch -> Task {} *
|
||||
applyPatches = \patches ->
|
||||
List.walk patches (Effect.always {}) walkPatches
|
||||
List.walk patches (Task.ok {}) \previousEffects, patch ->
|
||||
previousEffects!
|
||||
applyPatch patch
|
||||
|
||||
# -------------------------------
|
||||
# EVENT HANDLING
|
||||
|
@ -197,7 +194,7 @@ JsEventResult state initData : {
|
|||
}
|
||||
|
||||
## Dispatch a JavaScript event to a Roc handler, given the handler ID and some JSON event data.
|
||||
dispatchEvent : PlatformState state initData, List (List U8), HandlerId -> Effect (JsEventResult state initData) where initData implements Decoding
|
||||
dispatchEvent : PlatformState state initData, List (List U8), HandlerId -> Task (JsEventResult state initData) * where initData implements Decoding
|
||||
dispatchEvent = \platformState, eventData, handlerId ->
|
||||
{ app, state, rendered } =
|
||||
platformState
|
||||
|
@ -222,8 +219,8 @@ dispatchEvent = \platformState, eventData, handlerId ->
|
|||
{ rendered: newRendered, patches } =
|
||||
diff { rendered, patches: [] } newViewUnrendered
|
||||
|
||||
_ <- applyPatches patches |> Effect.after
|
||||
Effect.always {
|
||||
applyPatches! patches
|
||||
Task.ok {
|
||||
platformState: {
|
||||
app,
|
||||
state: newState,
|
||||
|
@ -234,7 +231,7 @@ dispatchEvent = \platformState, eventData, handlerId ->
|
|||
}
|
||||
|
||||
None ->
|
||||
Effect.always { platformState, stopPropagation, preventDefault }
|
||||
Task.ok { platformState, stopPropagation, preventDefault }
|
||||
|
||||
# -------------------------------
|
||||
# DIFF
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
hosted Effect
|
||||
hosted PlatformTask
|
||||
exposes [
|
||||
Effect,
|
||||
NodeId,
|
||||
HandlerId,
|
||||
TagName,
|
||||
AttrType,
|
||||
EventType,
|
||||
after,
|
||||
always,
|
||||
map,
|
||||
createElement,
|
||||
createTextNode,
|
||||
updateTextNode,
|
||||
|
@ -26,7 +22,6 @@ hosted Effect
|
|||
disableVdomAllocator,
|
||||
]
|
||||
imports []
|
||||
generates Effect with [after, always, map]
|
||||
|
||||
# TODO: private types
|
||||
NodeId : U64
|
||||
|
@ -39,43 +34,43 @@ AttrType : Str
|
|||
EventType : Str
|
||||
|
||||
## createElement tagName
|
||||
createElement : NodeId, TagName -> Effect {}
|
||||
createElement : NodeId, TagName -> Task {} *
|
||||
|
||||
## createTextNode content
|
||||
createTextNode : NodeId, Str -> Effect {}
|
||||
createTextNode : NodeId, Str -> Task {} *
|
||||
|
||||
## updateTextNode content
|
||||
updateTextNode : NodeId, Str -> Effect {}
|
||||
updateTextNode : NodeId, Str -> Task {} *
|
||||
|
||||
## appendChild parentId childId
|
||||
appendChild : NodeId, NodeId -> Effect {}
|
||||
appendChild : NodeId, NodeId -> Task {} *
|
||||
|
||||
## removeNode id
|
||||
removeNode : NodeId -> Effect {}
|
||||
removeNode : NodeId -> Task {} *
|
||||
|
||||
## replaceNode oldId newId
|
||||
replaceNode : NodeId, NodeId -> Effect {}
|
||||
replaceNode : NodeId, NodeId -> Task {} *
|
||||
|
||||
## setAttribute nodeId attrName value
|
||||
setAttribute : NodeId, AttrType, Str -> Effect {}
|
||||
setAttribute : NodeId, AttrType, Str -> Task {} *
|
||||
|
||||
## removeAttribute nodeId attrName
|
||||
removeAttribute : NodeId, AttrType -> Effect {}
|
||||
removeAttribute : NodeId, AttrType -> Task {} *
|
||||
|
||||
## setProperty nodeId propName json
|
||||
setProperty : NodeId, Str, List U8 -> Effect {}
|
||||
setProperty : NodeId, Str, List U8 -> Task {} *
|
||||
|
||||
## removeProperty nodeId propName
|
||||
removeProperty : NodeId, Str -> Effect {}
|
||||
removeProperty : NodeId, Str -> Task {} *
|
||||
|
||||
## setStyle nodeId key value
|
||||
setStyle : NodeId, Str, Str -> Effect {}
|
||||
setStyle : NodeId, Str, Str -> Task {} *
|
||||
|
||||
## setListener nodeId eventType accessorsJson handlerId
|
||||
setListener : NodeId, EventType, List U8, HandlerId -> Effect {}
|
||||
setListener : NodeId, EventType, List U8, HandlerId -> Task {} *
|
||||
|
||||
## removeListener nodeId handlerId
|
||||
removeListener : NodeId, HandlerId -> Effect {}
|
||||
removeListener : NodeId, HandlerId -> Task {} *
|
||||
|
||||
# Enable a special memory allocator for virtual DOM
|
||||
# This consists of two arenas, "even" and "odd", which alternately hold the "old" and "new" VDOM.
|
||||
|
@ -83,9 +78,9 @@ removeListener : NodeId, HandlerId -> Effect {}
|
|||
# Danger: Could cause memory unsafety bugs if used incorrectly! Do not expose!
|
||||
# Not suitable for values that have a different lifetime from the virtual DOM!
|
||||
# TODO: actually implement this for real! LOL
|
||||
enableVdomAllocator : Bool -> Effect {}
|
||||
enableVdomAllocator : Bool -> Task {} *
|
||||
|
||||
# Switch back from the virtual DOM allocator to the "normal"
|
||||
# allocator that is safe to use with long-lived values.
|
||||
# At the same time, drop the entire "old" virtual DOM arena.
|
||||
disableVdomAllocator : Effect {}
|
||||
disableVdomAllocator : Task {} *
|
|
@ -5,7 +5,6 @@ platform "client-side"
|
|||
imports [
|
||||
Html.Internal.Shared.{ App },
|
||||
Html.Internal.Client.{ PlatformState, initClientApp, dispatchEvent },
|
||||
Effect.{ Effect },
|
||||
]
|
||||
provides [main]
|
||||
|
||||
|
@ -27,18 +26,18 @@ ToHost state initData : {
|
|||
|
||||
# TODO: naming the type variables causes a type 'mismatch'
|
||||
# main : FromHost state initData -> Effect (ToHost state initData) where initData implements Decoding & Encoding
|
||||
main : FromHost _ _ -> Effect (ToHost _ _)
|
||||
main : FromHost _ _ -> Task (ToHost _ _) *
|
||||
main = \fromHost ->
|
||||
if fromHost.isInitEvent then
|
||||
initClientApp fromHost.initJson app
|
||||
|> Effect.map \platformState -> {
|
||||
|> Task.map \platformState -> {
|
||||
platformState: Box.box platformState,
|
||||
eventPreventDefault: Bool.false,
|
||||
eventStopPropagation: Bool.false,
|
||||
}
|
||||
else
|
||||
dispatchEvent (Box.unbox fromHost.eventPlatformState) fromHost.eventJsonList fromHost.eventHandlerId
|
||||
|> Effect.map \jsEventResult -> {
|
||||
|> Task.map \jsEventResult -> {
|
||||
platformState: Box.box jsEventResult.platformState,
|
||||
eventPreventDefault: jsEventResult.preventDefault,
|
||||
eventStopPropagation: jsEventResult.stopPropagation,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue