on master help sign in
LICENSEnew file+7
since splitsnapshots
unifiedsplit
@@ -0,0 +1,7 @@
1+Copyright 2023 Ilia Kostiuchenko
2+
3+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
README.md+8−1
@@ -6,10 +6,17 @@
6
6
7### Demo
7### Demo
8
8
9−[![Demo](https://img.youtube.com/vi/KV49E-D-SOY/default.jpg)](https://youtu.be/KV49E-D-SOY)
9+[![DEMO](https://img.youtube.com/vi/tynCzifvIzE/0.jpg)](https://www.youtube.com/watch?v=tynCzifvIzE)
10+
11+
12+## Getting started
13+
14+The easiest way of using this library is by using the [airlock template](https://github.com/ilyakooo0/airlock-template).
10
15
11### FFI
16### FFI
12
17
18+This is for setting up this library from scratch.
19+
13Urbit subscriptions require [server-side event (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
20Urbit subscriptions require [server-side event (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
14Unfortunately, Elm does not provide a native implementation.
21Unfortunately, Elm does not provide a native implementation.
15This means that we will need to use [JS FFI (aka ports)](https://guide.elm-lang.org/interop/ports.html).
22This means that we will need to use [JS FFI (aka ports)](https://guide.elm-lang.org/interop/ports.html).
elm.json+11−2
@@ -3,9 +3,18 @@
3 "name": "ilyakooo0/airlock",
3 "name": "ilyakooo0/airlock",
4 "summary": "Urbit airlock",
4 "summary": "Urbit airlock",
5 "license": "MIT",
5 "license": "MIT",
6− "version": "1.0.0",
6+ "version": "1.1.1",
7 "exposed-modules": [
7 "exposed-modules": [
8− "Ur", "Ur.Cmd", "Ur.Sub", "Ur.Constructor", "Ur.Deconstructor", "Ur.Phonemic", "Ur.Da", "Ur.Uw", "Ur.Run", "Ur.Types"
8+ "Ur",
9+ "Ur.Cmd",
10+ "Ur.Sub",
11+ "Ur.Constructor",
12+ "Ur.Deconstructor",
13+ "Ur.Phonemic",
14+ "Ur.Da",
15+ "Ur.Uw",
16+ "Ur.Run",
17+ "Ur.Types"
9 ],
18 ],
10 "elm-version": "0.19.0 <= v < 0.20.0",
19 "elm-version": "0.19.0 <= v < 0.20.0",
11 "dependencies": {
20 "dependencies": {
example/src/Sink.elm+1−2
@@ -14,7 +14,6 @@ import Ur.Constructor as C
14import Ur.Deconstructor as D
14import Ur.Deconstructor as D
15import Ur.Run
15import Ur.Run
16import Ur.Sub
16import Ur.Sub
17−import Ur.Types exposing (Noun)
18import Widget
17import Widget
19import Widget.Icon as Icon
18import Widget.Icon as Icon
20import Widget.Material as Material
19import Widget.Material as Material
@@ -56,7 +55,7 @@ main =
56 , app = "journal"
55 , app = "journal"
57 , path = [ "sync" ]
56 , path = [ "sync" ]
58 , deconstructor =
57 , deconstructor =
59− D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b )))
58+ D.list (D.cell D.bigint D.cord)
60 |> D.map GotListings
59 |> D.map GotListings
61 }
60 }
62 ]
61 ]
example/src/Vanilla.elm+10−9
@@ -46,9 +46,9 @@ main =
46 , success =
46 , success =
47 D.cell D.ignore
47 D.cell D.ignore
48 (D.cell (D.const D.cord "jrnl")
48 (D.cell (D.const D.cord "jrnl")
49− (D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b ))))
49+ (D.list (D.cell D.bigint D.cord))
50− |> D.map GotListings
51 )
50 )
51+ |> D.map (\( (), ( (), listings ) ) -> GotListings listings)
52 }
52 }
53 ]
53 ]
54 |> Ur.Cmd.cmd
54 |> Ur.Cmd.cmd
@@ -177,14 +177,15 @@ type JournalUpdate
177 | Delete BigInt
177 | Delete BigInt
178
178
179
179
180−decodeJournalUpdate : D.Deconstructor (JournalUpdate -> a) a
180+decodeJournalUpdate : D.Deconstructor JournalUpdate
181decodeJournalUpdate =
181decodeJournalUpdate =
182− D.cell D.ignore <|
183− D.oneOf
182+ D.oneOf
184− [ (D.cell (D.const D.cord "add") <| D.cell D.bigint D.cord) |> D.map Add
183+ [ (D.cell (D.const D.cord "add") <| D.cell D.bigint D.cord) |> D.map (\( (), ( id, txt ) ) -> Add id txt)
185− , (D.cell (D.const D.cord "edit") <| D.cell D.bigint D.cord) |> D.map Edit
184+ , D.cell (D.const D.cord "edit") (D.cell D.bigint D.cord) |> D.map (\( (), ( id, txt ) ) -> Edit id txt)
186− , D.cell (D.const D.cord "del") D.bigint |> D.map Delete
185+ , D.cell (D.const D.cord "del") D.bigint |> D.map (\( (), id ) -> Delete id)
187− ]
186+ ]
187+ |> D.cell D.ignore
188+ |> D.map (\( (), upd ) -> upd)
188
189
189
190
190view : Model -> Document Msg
191view : Model -> Document Msg
example/urbit/app/journal.hoon+1−1
@@ -21,7 +21,7 @@
21
21
22%- agent:dbug
22%- agent:dbug
23=/ state *state-0
23=/ state *state-0
24−=* entries (tap:j-orm journal.stat)
24+=* entries (tap:j-orm journal.state)
25=/ snik (sink ~[/sync])
25=/ snik (sink ~[/sync])
26=/ sink (snik entries)
26=/ sink (snik entries)
27^- agent:gall
27^- agent:gall
example/urbit/lib/sink.hoon+1−1
@@ -21,6 +21,6 @@
21 ++ paths pats
21 ++ paths pats
22 ++ flush
22 ++ flush
23 ^- card:agent:gall
23 ^- card:agent:gall
24− [%give %fact pats %noun !>(^-((clog) [%flush (extract stat)]))]
24+ [%give %fact pats %noun !>(^-((clog) [%flush stat]))]
25 --
25 --
26--
26--
src/BitWriter.elm+5−5
@@ -14,17 +14,17 @@ import Bytes.Encode as BE
14
14
15empty : BitWriter
15empty : BitWriter
16empty =
16empty =
17− BitWriter { running = Nothing, collected = BE.sequence [], offset = 0 }
17+ BitWriter { running = Nothing, collected = [], offset = 0 }
18
18
19
19
20run : BitWriter -> Bytes
20run : BitWriter -> Bytes
21run (BitWriter { running, collected }) =
21run (BitWriter { running, collected }) =
22 case running of
22 case running of
23 Nothing ->
23 Nothing ->
24− BE.encode collected
24+ BE.encode (collected |> List.reverse |> BE.sequence)
25
25
26 Just { value } ->
26 Just { value } ->
27− BE.sequence [ collected, BE.unsignedInt8 value ] |> BE.encode
27+ BE.sequence (BE.unsignedInt8 value :: collected |> List.reverse) |> BE.encode
28
28
29
29
30type BitWriter
30type BitWriter
@@ -35,7 +35,7 @@ type BitWriter
35 , length : Int
35 , length : Int
36 }
36 }
37 , offset : Int
37 , offset : Int
38− , collected : BE.Encoder
38+ , collected : List BE.Encoder
39 }
39 }
40
40
41
41
@@ -68,7 +68,7 @@ bit b (BitWriter { running, collected, offset }) =
68 if length == 7 then
68 if length == 7 then
69 BitWriter
69 BitWriter
70 { running = Nothing
70 { running = Nothing
71− , collected = BE.sequence [ collected, BE.unsignedInt8 newValue ]
71+ , collected = BE.unsignedInt8 newValue :: collected
72 , offset = newOffset
72 , offset = newOffset
73 }
73 }
74
74
src/Ur.elm+2−2
@@ -77,7 +77,7 @@ scry :
77 , agent : Agent
77 , agent : Agent
78 , path : Path
78 , path : Path
79 , error : msg
79 , error : msg
80− , success : D.Deconstructor (msg -> msg) msg
80+ , success : D.Deconstructor msg
81 }
81 }
82 -> Cmd msg
82 -> Cmd msg
83scry args =
83scry args =
@@ -91,7 +91,7 @@ scryTask :
91 , agent : Agent
91 , agent : Agent
92 , path : Path
92 , path : Path
93 , error : msg
93 , error : msg
94− , success : D.Deconstructor (msg -> msg) msg
94+ , success : D.Deconstructor msg
95 }
95 }
96 -> Task a msg
96 -> Task a msg
97scryTask { url, agent, path, error, success } =
97scryTask { url, agent, path, error, success } =
src/Ur/Deconstructor.elm+206−226
@@ -6,24 +6,16 @@ module Ur.Deconstructor exposing
6 , int, signedInt, bigint
6 , int, signedInt, bigint
7 , float32, float64
7 , float32, float64
8 , cord, tape
8 , cord, tape
9− , bytes, sig, ignore, tar, lazy
9+ , bytes, sig, ignore, tar, lazy, debug
10 )
10 )
11
11
12{-| This module provides an API to deconstruct `Noun`s into arbitrary Elm data structures.
12{-| This module provides an API to deconstruct `Noun`s into arbitrary Elm data structures.
13
13
14−The principal (and types) are very similar to `Url.Parser` from `elm/url`.
15−
16You would parse a `[%edit @ cord]` like this:
14You would parse a `[%edit @ cord]` like this:
17
15
18 type alias Edit = {id: Int, text : String}
16 type alias Edit = {id: Int, text : String}
19
17
20− (D.cell (D.const D.cord "edit") (D.cell D.int D.cord)) |> D.map Edit
18+ (D.cell (D.const D.cord "edit") (D.cell D.bigint D.cord)) |> D.map (\( (), ( id, txt ) ) -> Edit id txt)
21−
22−When you `map` a `Deconstructor` the function you pass to `map` will accept exactly the number of arguments
23−that "captured" values in exacly the order they occured in the `Deconstructor`.
24−
25−In our case we `map` the `Edit` type, which accepts exaclty two arguments because there are two `Deconstructor`s
26−that "capture" a value: `D.int` and `D.cord`.
27
19
28@docs Deconstructor
20@docs Deconstructor
29
21
@@ -63,7 +55,7 @@ that "capture" a value: `D.int` and `D.cord`.
63
55
64# Miscellaneous
56# Miscellaneous
65
57
66−@docs bytes, sig, ignore, tar, lazy
58+@docs bytes, sig, ignore, tar, lazy, debug
67
59
68-}
60-}
69
61
@@ -74,33 +66,31 @@ import Bytes exposing (Bytes, Endianness(..))
74import Bytes.Decode as BD
66import Bytes.Decode as BD
75import Bytes.Encode as BE
67import Bytes.Encode as BE
76import Bytes.Extra
68import Bytes.Extra
77−import Ur.Jam exposing (cue)
69+import Ur.Jam exposing (cue, isSig)
78import Ur.Types exposing (..)
70import Ur.Types exposing (..)
79
71
80
72
81{-| A `Noun` deconstructor.
73{-| A `Noun` deconstructor.
82-}
74-}
83−type Deconstructor a b
75+type alias Deconstructor a =
84− = Deconstructor (Noun -> a -> Maybe b)
76+ Noun -> Maybe a
85
77
86
78
87{-| Executes a `Deconstructor` on a `Noun`.
79{-| Executes a `Deconstructor` on a `Noun`.
88-}
80-}
89−run : Deconstructor (a -> a) a -> Noun -> Maybe a
81+run : Deconstructor a -> Noun -> Maybe a
90−run (Deconstructor f) noun =
82+run f noun =
91− f noun identity
83+ f noun
92
84
93
85
94{-| Executes a deconstructor on a `jam`med `Noun`.
86{-| Executes a deconstructor on a `jam`med `Noun`.
95-}
87-}
96−runBytes : Deconstructor (a -> a) a -> Bytes -> Maybe a
88+runBytes : Deconstructor a -> Bytes -> Maybe a
97−runBytes (Deconstructor f) bs =
89+runBytes f bs =
98− cue bs
99− |> Maybe.andThen
90+ cue bs |> Maybe.andThen f
100− (\noun -> f noun identity)
101
91
102
92
103−{-| Asserts that the value at the current position should be exactly equal to the second argument.
93+{-| Asserts that the value should be exactly equal to the second argument.
104
94
105The first argument is a `Deconstructor` for the given type.
95The first argument is a `Deconstructor` for the given type.
106
96
@@ -109,104 +99,41 @@ The second argument is the value to compare with.
109This is useful to match on `term`s when there are multiple possible cases in a head-tagged union.
99This is useful to match on `term`s when there are multiple possible cases in a head-tagged union.
110
100
111-}
101-}
112−const : Deconstructor (a -> a) a -> a -> Deconstructor c c
102+const : Deconstructor a -> a -> Deconstructor ()
113−const (Deconstructor f) value =
103+const f value noun =
114− Deconstructor
104+ case f noun of
115− (\noun c ->
105+ Just a ->
116− case f noun identity of
117− Just a ->
118− if a == value then
106+ if a == value then
119− Just c
107+ Just ()
120
108
121− else
109+ else
122− Nothing
110+ Nothing
123
111
124− Nothing ->
112+ Nothing ->
125− Nothing
113+ Nothing
126− )
127
114
128
115
129−{-| Extracts a `cord` at the current location.
116+{-| Extracts a `BigInt`.
130-}
117-}
131−cord : Deconstructor (String -> a) a
118+bigint : Deconstructor BigInt
132−cord =
119+bigint x =
133− Deconstructor
134− (\x f ->
135− case x of
120+ case x of
136− Atom bs ->
121+ Atom bs ->
137− BD.decode (BD.string (Bytes.width bs)) bs
122+ BigInt.Bytes.decode bs |> Just
138− |> Maybe.map f
139−
140− Cell _ ->
141− Nothing
142− )
143
123
144−
124+ Cell _ ->
145−{-| Extracts a `tape` at the current location.
125+ Nothing
146−-}
147−tape : Deconstructor (String -> a) a
148−tape =
149− list cord |> fmap String.concat
150
126
151
127
152−{-| Extracts a 32-bit unsigned `Int` at the given location.
128+{-| Extracts a 32-bit signed `Int`.
153
129
154−If the `Atom` at the current location is larger than 32 bits the the behaviour is undefined.
130+If the `Atom` is larger than 32 bits the the behaviour is undefined.
155
131
156-}
132-}
157−int : Deconstructor (Int -> a) a
158−int =
159− Deconstructor
160− (\x f ->
161− case x of
162− Atom bs ->
163− (case Bytes.width bs of
164− 1 ->
165− BD.decode BD.unsignedInt8 bs
166−
167− 2 ->
168− BD.decode (BD.unsignedInt16 LE) bs
169−
170− 3 ->
171− BD.decode (BD.unsignedInt32 LE) (BE.encode (BE.sequence [ BE.bytes bs, BE.unsignedInt8 0 ]))
172−
173− 4 ->
174− BD.decode (BD.unsignedInt32 LE) bs
175−
176− _ ->
177− Nothing
178− )
179− |> Maybe.map f
180−
181− Cell _ ->
182− Nothing
183− )
184−
185−
186−{-| Extracts a `BigInt` at the current location.
187−-}
188−bigint : Deconstructor (BigInt -> a) a
189−bigint =
190− Deconstructor
191− (\x f ->
192− case x of
193− Atom bs ->
194− BigInt.Bytes.decode bs |> f |> Just
195−
196− Cell _ ->
197− Nothing
198− )
199−
200−
201−{-| Extracts a 32-bit signed `Int` at the given location.
202−
203−If the `Atom` at the current location is larger than 32 bits the the behaviour is undefined.
204−
205−-}
206−signedInt : Deconstructor (Int -> a) a
133+signedInt : Deconstructor Int
207signedInt =
134signedInt =
208 int
135 int
209− |> fmap
136+ |> map
210 (\i ->
137 (\i ->
211 if Bitwise.and 1 i == 1 then
138 if Bitwise.and 1 i == 1 then
212 -(Bitwise.shiftRightBy 1 (1 + i))
139 -(Bitwise.shiftRightBy 1 (1 + i))
@@ -216,105 +143,85 @@ signedInt =
216 )
143 )
217
144
218
145
219−{-| Extracts a 32-bit `Float` at the given location.
146+{-| Extracts a 32-bit `Float`.
220-}
147-}
221−float32 : Deconstructor (Float -> a) a
148+float32 : Deconstructor Float
222−float32 =
149+float32 x =
223− Deconstructor
224− (\x f ->
225− case x of
150+ case x of
226− Atom bs ->
151+ Atom bs ->
227− BD.decode (BD.float32 LE) bs
152+ BD.decode (BD.float32 LE) bs
228− |> Maybe.map f
153+
229−
230− Cell _ ->
154+ Cell _ ->
231− Nothing
155+ Nothing
232− )
233
156
234
157
235−{-| Extracts a 64-bit `Float` at the given location.
158+{-| Extracts a 64-bit `Float`.
236-}
159-}
237−float64 : Deconstructor (Float -> a) a
160+float64 : Deconstructor Float
238−float64 =
161+float64 x =
239− Deconstructor
240− (\x f ->
241− case x of
162+ case x of
242− Atom bs ->
163+ Atom bs ->
243− BD.decode (BD.float64 LE) bs
164+ BD.decode (BD.float64 LE) bs
244− |> Maybe.map f
165+
245−
246− Cell _ ->
166+ Cell _ ->
247− Nothing
167+ Nothing
248− )
249
168
250
169
251−{-| Extracts the raw `Byte`s of an `Atom` at the current location.
170+{-| Extracts the raw `Byte`s of an `Atom`.
252-}
171-}
253−bytes : Deconstructor (Bytes -> a) a
172+bytes : Deconstructor Bytes
254−bytes =
173+bytes n =
255− Deconstructor
256− (\n f ->
257− case n of
174+ case n of
258− Atom bs ->
175+ Atom bs ->
259− f bs |> Just
176+ Just bs
260
177
261− Cell _ ->
178+ Cell _ ->
262− Nothing
179+ Nothing
263− )
264
180
265
181
266−{-| Asserts the the `Atom` at the current position should be exactly `sig` (`~`).
182+{-| Asserts the the `Atom` should be exactly `sig` (`~`).
267-}
183-}
268−sig : Deconstructor a a
184+sig : Deconstructor ()
269−sig =
185+sig n =
270− Deconstructor
271− (\n a ->
272− case n of
186+ case n of
273− Atom b ->
187+ Atom b ->
274− if (Bytes.Extra.toByteValues b |> List.filter (\x -> x /= 0)) == [] then
188+ if (Bytes.Extra.toByteValues b |> List.filter (\x -> x /= 0)) == [] then
275− Just a
189+ Just ()
276
190
277− else
191+ else
278− Nothing
192+ Nothing
279
193
280− Cell _ ->
194+ Cell _ ->
281− Nothing
195+ Nothing
282− )
283
196
284
197
285−{-| Extracts a sig-terminated list of arbitrary elements at the current position.
198+{-| Extracts a sig-terminated list of arbitrary elements.
286
199
287The first argument is a `Deconstructor` of the elements of the list.
200The first argument is a `Deconstructor` of the elements of the list.
288
201
289-}
202-}
290−list : Deconstructor (a -> a) a -> Deconstructor (List a -> b) b
203+list : Deconstructor a -> Deconstructor (List a)
291−list (Deconstructor f) =
204+list f n =
292− Deconstructor
205+ let
293− (\n g ->
206+ go n_ =
294− let
295− go n_ =
296− case n_ of
207+ case n_ of
297− Atom _ ->
208+ Atom _ ->
298− Just []
209+ Just []
299
210
300− Cell ( el, tail ) ->
211+ Cell ( el, tail ) ->
301− Maybe.map2 (\a b -> a :: b) (f el identity) (go tail)
212+ Maybe.map2 (\a b -> a :: b) (f el) (go tail)
302− in
213+ in
303− go n |> Maybe.map g
214+ go n
304− )
305
215
306
216
307−alt : Deconstructor a b -> Deconstructor a b -> Deconstructor a b
217+alt : Deconstructor a -> Deconstructor a -> Deconstructor a
308−alt (Deconstructor f) (Deconstructor g) =
218+alt f g n =
309− Deconstructor
310− (\n a ->
311− case f n a of
219+ case f n of
312− Just x ->
220+ Just x ->
313− Just x
221+ Just x
314
222
315− Nothing ->
223+ Nothing ->
316− g n a
224+ g n
317− )
318
225
319
226
320{-| Try to execute all of the `Deconstructor`s in order until one succeeds.
227{-| Try to execute all of the `Deconstructor`s in order until one succeeds.
@@ -322,77 +229,150 @@ alt (Deconstructor f) (Deconstructor g) =
322This is especially useful for deconstructing head-tagged unions from Hoon.
229This is especially useful for deconstructing head-tagged unions from Hoon.
323
230
324-}
231-}
325−oneOf : List (Deconstructor a b) -> Deconstructor a b
232+oneOf : List (Deconstructor a) -> Deconstructor a
326oneOf l =
233oneOf l =
327 case l of
234 case l of
328 [] ->
235 [] ->
329− Deconstructor (\_ _ -> Nothing)
236+ always Nothing
330
237
331 x :: xs ->
238 x :: xs ->
332 alt x (oneOf xs)
239 alt x (oneOf xs)
333
240
334
241
335−{-| Extract the raw `Noun` at the current position.
242+{-| Ignore any value.
336
243
337−Always succeeds.
244+This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
338
245
339-}
246-}
340−tar : Deconstructor (Noun -> a) a
247+ignore : Deconstructor ()
341−tar =
248+ignore =
342− Deconstructor (\noun f -> Just (f noun))
249+ always (Just ())
343
250
344
251
345−{-| Ignore any value at the current position.
252+{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
346
253
347−This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
254+Accepts two arbitrary `Deconstructor`s that form a Cell.
348
255
349-}
256-}
350−ignore : Deconstructor a a
257+cell : Deconstructor a -> Deconstructor b -> Deconstructor ( a, b )
351−ignore =
258+cell l r noun =
352− Deconstructor (\_ f -> Just f)
259+ case noun of
260+ Cell ( lhs, rhs ) ->
261+ l lhs |> Maybe.andThen (\a -> r rhs |> Maybe.map (\b -> ( a, b )))
353
262
263+ Atom _ ->
264+ Nothing
354
265
355−{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
356
266
357−Accepts two arbitrary `Deconstructor`s that form a Cell.
267+{-| Maps (applies) a function to all of the values deconstructed.
268+
269+This is useful when you want to create a data type with extracted values as fields.
358
270
359-}
271-}
360−cell : Deconstructor a b -> Deconstructor b c -> Deconstructor a c
272+lazy : (() -> Deconstructor a) -> Deconstructor a
361−cell (Deconstructor l) (Deconstructor r) =
273+lazy f noun =
362− Deconstructor
274+ f () noun
363− (\noun a ->
364− case noun of
365− Cell ( lhs, rhs ) ->
366− l lhs a |> Maybe.andThen (\b -> r rhs b)
367
275
368− Atom _ ->
276+
369− Nothing
277+{-| Maps over the result of the deconstructor.
370− )
278+-}
279+map : (a -> b) -> Deconstructor a -> Deconstructor b
280+map g f noun =
281+ f noun |> Maybe.map g
371
282
372
283
373−{-| "Lazily" applies a deconstructor.
284+{-| Extracts a `cord`.
374−This is useful when you are defining a recursive `Deconstructor` which needs to call itself.
375-}
285-}
376−lazy : (() -> Deconstructor a b) -> Deconstructor a b
286+cord : Deconstructor String
377−lazy f =
287+cord x =
378− Deconstructor
379− (\noun a ->
380− case f () of
288+ case x of
381− Deconstructor g ->
289+ Atom bs ->
382− g noun a
290+ BD.decode (BD.string (Bytes.width bs)) bs
383− )
384
291
292+ Cell _ ->
293+ Nothing
385
294
386−{-| Maps (applies) a function to all of the values deconstructed.
387
295
388−This is useful when you want to create a data type with extracted values as fields.
296+{-| Extracts a `tape`.
297+-}
298+tape : Deconstructor String
299+tape =
300+ list cord |> map String.concat
301+
302+
303+{-| Extracts a 32-bit unsigned `Int`.
304+
305+If the `Atom` is larger than 32 bits the the behaviour is undefined.
389
306
390-}
307-}
391−map : a -> Deconstructor a b -> Deconstructor (b -> c) c
308+int : Deconstructor Int
392−map a (Deconstructor f) =
309+int x =
393− Deconstructor (\noun g -> f noun a |> Maybe.map g)
310+ case x of
311+ Atom bs ->
312+ case Bytes.width bs of
313+ 1 ->
314+ BD.decode BD.unsignedInt8 bs
315+
316+ 2 ->
317+ BD.decode (BD.unsignedInt16 LE) bs
318+
319+ 3 ->
320+ BD.decode (BD.unsignedInt32 LE) (BE.encode (BE.sequence [ BE.bytes bs, BE.unsignedInt8 0 ]))
321+
322+ 4 ->
323+ BD.decode (BD.unsignedInt32 LE) bs
324+
325+ _ ->
326+ Nothing
327+
328+ Cell _ ->
329+ Nothing
394
330
395
331
396−fmap : (a -> b) -> Deconstructor (a -> c) c -> Deconstructor (b -> c) c
332+{-| Extract the raw `Noun`.
397−fmap f (Deconstructor g) =
333+
398− Deconstructor (\noun h -> g noun (f >> h))
334+Always succeeds.
335+
336+-}
337+tar : Deconstructor Noun
338+tar =
339+ Just
340+
341+
342+{-| Debug a deconstructor.
343+If the deconstructor from the second argument fails then the failed nock will be printed to the console.
344+-}
345+debug : (String -> String) -> Deconstructor a -> Deconstructor a
346+debug log f noun =
347+ case f noun of
348+ Just x ->
349+ Just x
350+
351+ Nothing ->
352+ let
353+ _ =
354+ log (prettyNoun noun)
355+ in
356+ Nothing
357+
358+
359+prettyNoun : Noun -> String
360+prettyNoun noun =
361+ let
362+ go isRhs n =
363+ case n of
364+ Atom a ->
365+ if isSig a then
366+ "~"
367+
368+ else
369+ "@"
370+
371+ Cell ( lhs, rhs ) ->
372+ if isRhs then
373+ go False lhs ++ " " ++ go True rhs
374+
375+ else
376+ "[" ++ go False lhs ++ " " ++ go True rhs ++ "]"
377+ in
378+ go False noun
src/Ur/NounDiff.elm+21−61
@@ -11,29 +11,17 @@ type Patch
11 | Diff DelDiff InsDiff
11 | Diff DelDiff InsDiff
12
12
13
13
14−deconstructPatch : D.Deconstructor (Patch -> c) c
14+deconstructPatch : () -> D.Deconstructor Patch
15−deconstructPatch =
15+deconstructPatch () =
16 D.oneOf
16 D.oneOf
17 [ D.cell (D.const D.cord "cell")
17 [ D.cell (D.const D.cord "cell")
18 (D.cell
18 (D.cell
19− (D.lazy (\() -> deconstructPatch_))
19+ (D.lazy deconstructPatch)
20− (D.lazy (\() -> deconstructPatch_))
20+ (D.lazy deconstructPatch)
21 )
21 )
22− |> D.map PatchCell
22+ |> D.map (\( (), ( a, b ) ) -> PatchCell a b)
23− , D.cell (D.const D.cord "diff") (D.cell deconstructDel deconstructIns) |> D.map Diff
23+ , D.cell (D.const D.cord "diff") (D.cell (D.lazy deconstructDel) (D.lazy deconstructIns))
24− ]
24+ |> D.map (\( (), ( a, b ) ) -> Diff a b)
25−
26−
27−deconstructPatch_ : D.Deconstructor (Patch -> c) c
28−deconstructPatch_ =
29− D.oneOf
30− [ D.cell (D.const D.cord "cell")
31− (D.cell
32− deconstructPatch
33− deconstructPatch
34− )
35− |> D.map PatchCell
36− , D.cell (D.const D.cord "diff") (D.cell deconstructDel deconstructIns) |> D.map Diff
37 ]
25 ]
38
26
39
27
@@ -93,31 +81,17 @@ type DelDiff
93 | DelCell DelDiff DelDiff
81 | DelCell DelDiff DelDiff
94
82
95
83
96−deconstructDel : D.Deconstructor (DelDiff -> c) c
84+deconstructDel : () -> D.Deconstructor DelDiff
97−deconstructDel =
85+deconstructDel () =
98 D.oneOf
86 D.oneOf
99− [ D.cell (D.const D.cord "ignore") D.ignore |> D.map Ignore
87+ [ D.cell (D.const D.cord "ignore") D.ignore |> D.map (\( (), () ) -> Ignore)
100− , D.cell (D.const D.cord "hole") D.int |> D.map DelHole
88+ , D.cell (D.const D.cord "hole") D.int |> D.map (\( (), x ) -> DelHole x)
101 , D.cell (D.const D.cord "cell")
89 , D.cell (D.const D.cord "cell")
102 (D.cell
90 (D.cell
103− deconstructDel_
91+ (D.lazy deconstructDel)
104− deconstructDel_
92+ (D.lazy deconstructDel)
105 )
93 )
106− |> D.map DelCell
94+ |> D.map (\( (), ( lhs, rhs ) ) -> DelCell lhs rhs)
107− ]
108−
109−
110−deconstructDel_ : D.Deconstructor (DelDiff -> c) c
111−deconstructDel_ =
112− D.oneOf
113− [ D.const D.cord "ignore" |> D.map Ignore
114− , D.cell (D.const D.cord "hole") D.int |> D.map DelHole
115− , D.cell (D.const D.cord "cell")
116− (D.cell
117− (D.lazy (\() -> deconstructDel))
118− (D.lazy (\() -> deconstructDel))
119− )
120− |> D.map DelCell
121 ]
95 ]
122
96
123
97
@@ -127,29 +101,15 @@ type InsDiff
127 | InsCell InsDiff InsDiff
101 | InsCell InsDiff InsDiff
128
102
129
103
130−deconstructIns : D.Deconstructor (InsDiff -> c) c
131−deconstructIns =
132− D.oneOf
133− [ D.cell (D.const D.cord "hole") D.int |> D.map InsHole
134− , D.cell (D.const D.cord "atom") D.bytes |> D.map InsAtom
135− , D.cell (D.const D.cord "cell")
136− (D.cell
137− deconstructIns_
138− deconstructIns_
139− )
140− |> D.map InsCell
141− ]
142−
143−
144−deconstructIns_ : D.Deconstructor (InsDiff -> c) c
104+deconstructIns : () -> D.Deconstructor InsDiff
145−deconstructIns_ =
105+deconstructIns () =
146 D.oneOf
106 D.oneOf
147− [ D.cell (D.const D.cord "hole") D.int |> D.map InsHole
107+ [ D.cell (D.const D.cord "hole") D.int |> D.map (\( (), x ) -> InsHole x)
148− , D.cell (D.const D.cord "atom") D.bytes |> D.map InsAtom
108+ , D.cell (D.const D.cord "atom") D.bytes |> D.map (\( (), bs ) -> InsAtom bs)
149 , D.cell (D.const D.cord "cell")
109 , D.cell (D.const D.cord "cell")
150 (D.cell
110 (D.cell
151− (D.lazy (\() -> deconstructIns))
111+ (D.lazy deconstructIns)
152− (D.lazy (\() -> deconstructIns))
112+ (D.lazy deconstructIns)
153 )
113 )
154− |> D.map InsCell
114+ |> D.map (\( (), ( a, b ) ) -> InsCell a b)
155 ]
115 ]
src/Ur/Requests.elm+1−1
@@ -98,7 +98,7 @@ toNoun eventId req =
98 C.cell (C.cord app) (C.listOf C.cord path)
98 C.cell (C.cord app) (C.listOf C.cord path)
99
99
100 Unsubscribe subId ->
100 Unsubscribe subId ->
101− C.cell (C.cord "usubscribe") <|
101+ C.cell (C.cord "unsubscribe") <|
102 C.cell (C.int eventId) (C.int subId)
102 C.cell (C.int eventId) (C.int subId)
103
103
104 Poke { ship, agent, mark, noun } ->
104 Poke { ship, agent, mark, noun } ->
src/Ur/Run.elm+10−12
@@ -31,7 +31,7 @@ type alias SubDict msg =
31 Dict
31 Dict
32 -- (ship, agent, path)
32 -- (ship, agent, path)
33 ( String, String, List String )
33 ( String, String, List String )
34− { deconstructor : D.Deconstructor (msg -> msg) msg
34+ { deconstructor : D.Deconstructor msg
35 , number : Int
35 , number : Int
36 , sink : Bool
36 , sink : Bool
37 }
37 }
@@ -178,7 +178,7 @@ update inp msg model =
178 processUrSubs
178 processUrSubs
179 model.eventId
179 model.eventId
180 model.subscriptions
180 model.subscriptions
181− (inp.urbitSubscriptions model.app |> (\(Ur.Sub.Internal.Sub x) -> x))
181+ (inp.urbitSubscriptions appModel |> (\(Ur.Sub.Internal.Sub x) -> x))
182
182
183 ( eventId_, cmds, urReqs ) =
183 ( eventId_, cmds, urReqs ) =
184 processCmd subsResult.eventId appCmds
184 processCmd subsResult.eventId appCmds
@@ -211,12 +211,10 @@ update inp msg model =
211 Ok string ->
211 Ok string ->
212 case
212 case
213 D.runBytes
213 D.runBytes
214− (D.cell D.int (D.cell D.cord D.tar)
214+ (D.cell D.int (D.cell D.cord D.tar))
215− |> D.map (\a b c -> ( a, b, c ))
216− )
217 (Ur.Uw.decode string)
215 (Ur.Uw.decode string)
218 of
216 of
219− Just ( messageId, messageType, rest ) ->
217+ Just ( messageId, ( messageType, rest ) ) ->
220 let
218 let
221 ( eventId, ackReqs ) =
219 ( eventId, ackReqs ) =
222 tag model.eventId [ Ack messageId ]
220 tag model.eventId [ Ack messageId ]
@@ -248,7 +246,7 @@ update inp msg model =
248 of
246 of
249 Just { deconstructor, sink } ->
247 Just { deconstructor, sink } ->
250 if sink then
248 if sink then
251− case D.run (D.cell D.ignore (D.cell D.ignore deconstructSink)) rest of
249+ case D.run (D.cell D.ignore (D.cell D.ignore deconstructSink) |> D.map (\( (), ( (), s ) ) -> s)) rest of
252 Just (Flush noun) ->
250 Just (Flush noun) ->
253 ( { model | sinks = Dict.insert messageId noun model.sinks }
251 ( { model | sinks = Dict.insert messageId noun model.sinks }
254 , case D.run deconstructor noun of
252 , case D.run deconstructor noun of
@@ -284,7 +282,7 @@ update inp msg model =
284 ( model, Cmd.none )
282 ( model, Cmd.none )
285
283
286 else
284 else
287− case D.run (D.cell D.ignore (D.cell D.ignore deconstructor)) rest of
285+ case D.run (D.cell D.ignore (D.cell D.ignore deconstructor) |> D.map (\( (), ( (), m ) ) -> m)) rest of
288 Just subMsg ->
286 Just subMsg ->
289 ( model_, pureCmd (AppMsg subMsg) )
287 ( model_, pureCmd (AppMsg subMsg) )
290
288
@@ -315,7 +313,7 @@ update inp msg model =
315 NeedsActivation ->
313 NeedsActivation ->
316 let
314 let
317 ( eventId, reqs ) =
315 ( eventId, reqs ) =
318− [ Poke { ship = "~zod", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
316+ [ Poke { ship = "", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
319 |> tag model.eventId
317 |> tag model.eventId
320 in
318 in
321 ( { model | eventId = eventId }
319 ( { model | eventId = eventId }
@@ -351,11 +349,11 @@ type Sink
351 | Drain Patch
349 | Drain Patch
352
350
353
351
354−deconstructSink : D.Deconstructor (Sink -> c) c
352+deconstructSink : D.Deconstructor Sink
355deconstructSink =
353deconstructSink =
356 D.oneOf
354 D.oneOf
357− [ D.cell (D.const D.cord "flush") D.tar |> D.map Flush
355+ [ D.cell (D.const D.cord "flush") D.tar |> D.map (\( (), n ) -> Flush n)
358− , D.cell (D.const D.cord "drain") deconstructPatch |> D.map Drain
356+ , D.cell (D.const D.cord "drain") (D.lazy deconstructPatch) |> D.map (\( (), patch ) -> Drain patch)
359 ]
357 ]
360
358
361
359
src/Ur/Sub.elm+4−7
@@ -1,11 +1,8 @@
1−module Ur.Sub exposing
2− ( Sub, subscribe, none, batch
1+module Ur.Sub exposing (Sub, subscribe, none, batch, sink)
3− , sink
4− )
5
2
6{-| This module is conceptually similar to `Platform.Sub`, but allows you to subscribe to Urbit channels.
3{-| This module is conceptually similar to `Platform.Sub`, but allows you to subscribe to Urbit channels.
7
4
8−@docs Sub, subscribe, none, batch
5+@docs Sub, subscribe, none, batch, sink
9
6
10-}
7-}
11
8
@@ -30,7 +27,7 @@ type alias Sub msg =
30 }
27 }
31
28
32-}
29-}
33−subscribe : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor (msg -> msg) msg } -> Sub msg
30+subscribe : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor msg } -> Sub msg
34subscribe { ship, app, path, deconstructor } =
31subscribe { ship, app, path, deconstructor } =
35 Dict.singleton ( ship, app, path )
32 Dict.singleton ( ship, app, path )
36 { deconstructor = deconstructor
33 { deconstructor = deconstructor
@@ -41,7 +38,7 @@ subscribe { ship, app, path, deconstructor } =
41
38
42{-| Creates a %sink subscription.
39{-| Creates a %sink subscription.
43-}
40-}
44−sink : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor (msg -> msg) msg } -> Sub msg
41+sink : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor msg } -> Sub msg
45sink { ship, app, path, deconstructor } =
42sink { ship, app, path, deconstructor } =
46 Dict.singleton ( ship, app, path )
43 Dict.singleton ( ship, app, path )
47 { deconstructor = deconstructor
44 { deconstructor = deconstructor
src/Ur/Sub/Internal.elm+1−1
@@ -10,7 +10,7 @@ type Sub msg
10 (Dict
10 (Dict
11 -- key is (ship, app, path)
11 -- key is (ship, app, path)
12 ( Ship, Agent, Path )
12 ( Ship, Agent, Path )
13− { deconstructor : D.Deconstructor (msg -> msg) msg
13+ { deconstructor : D.Deconstructor msg
14 , sink : Bool
14 , sink : Bool
15 }
15 }
16 )
16 )
src/Ur/Uw.elm+7−7
@@ -29,24 +29,24 @@ decode string =
29 -- 0w
29 -- 0w
30 |> List.drop 2
30 |> List.drop 2
31
31
32− go : List Char -> BW.BitWriter -> BW.BitWriter
32+ go : List Char -> List Int -> BW.BitWriter -> BW.BitWriter
33− go cs writer =
33+ go cs append writer =
34 case cs of
34 case cs of
35 [] ->
35 [] ->
36− writer
36+ BW.bits append writer
37
37
38 '.' :: rest ->
38 '.' :: rest ->
39− go rest writer
39+ go rest append writer
40
40
41 c :: rest ->
41 c :: rest ->
42 case Dict.get c charToBits of
42 case Dict.get c charToBits of
43 Just bits ->
43 Just bits ->
44− go rest writer |> BW.bits bits
44+ go rest (bits ++ append) writer
45
45
46 Nothing ->
46 Nothing ->
47− go rest writer
47+ go rest [] writer
48 in
48 in
49− BW.run (go chars BW.empty)
49+ BW.run (go chars [] BW.empty)
50
50
51
51
52{-| -}
52{-| -}
tests/Test/Urbit.elm+11−11
@@ -77,7 +77,7 @@ tests =
77 Expect.equal
77 Expect.equal
78 (Just ( 1, 2 ))
78 (Just ( 1, 2 ))
79 (D.runBytes
79 (D.runBytes
80− (D.cell D.int D.int |> D.map Tuple.pair)
80+ (D.cell D.int D.int)
81 (Bytes.fromByteValues [ 0x31, 0x12 ])
81 (Bytes.fromByteValues [ 0x31, 0x12 ])
82 )
82 )
83 )
83 )
@@ -96,7 +96,7 @@ tests =
96 Expect.equal
96 Expect.equal
97 (Just ( 4, [ 1, 2, 3 ] ))
97 (Just ( 4, [ 1, 2, 3 ] ))
98 (D.runBytes
98 (D.runBytes
99− (D.cell D.int (D.list D.int) |> D.map Tuple.pair)
99+ (D.cell D.int (D.list D.int))
100 (Bytes.fromByteValues [ 0x61, 0xC6, 0x21, 0x43, 0x0B ])
100 (Bytes.fromByteValues [ 0x61, 0xC6, 0x21, 0x43, 0x0B ])
101 )
101 )
102 )
102 )
@@ -105,7 +105,7 @@ tests =
105 Expect.equal
105 Expect.equal
106 (Just ( 8, 11 ))
106 (Just ( 8, 11 ))
107 (D.runBytes
107 (D.runBytes
108− (D.cell D.float32 D.float32 |> D.map Tuple.pair)
108+ (D.cell D.float32 D.float32)
109 (Bytes.fromByteValues [ 0x01, 0x1F, 0x00, 0x00, 0x20, 0x08, 0x7C, 0x00, 0x00, 0x98, 0x20 ])
109 (Bytes.fromByteValues [ 0x01, 0x1F, 0x00, 0x00, 0x20, 0x08, 0x7C, 0x00, 0x00, 0x98, 0x20 ])
110 )
110 )
111 )
111 )
@@ -114,7 +114,7 @@ tests =
114 Expect.equal
114 Expect.equal
115 (Just ( 8, "hi" ))
115 (Just ( 8, "hi" ))
116 (D.runBytes
116 (D.runBytes
117− (D.cell D.int D.cord |> D.map Tuple.pair)
117+ (D.cell D.int D.cord)
118 (Bytes.fromByteValues [ 0x41, 0x10, 0x3C, 0x5A, 0x1A ])
118 (Bytes.fromByteValues [ 0x41, 0x10, 0x3C, 0x5A, 0x1A ])
119 )
119 )
120 )
120 )
@@ -123,7 +123,7 @@ tests =
123 Expect.equal
123 Expect.equal
124 (Just ( 8, "hi" ))
124 (Just ( 8, "hi" ))
125 (D.runBytes
125 (D.runBytes
126− (D.cell D.int D.tape |> D.map Tuple.pair)
126+ (D.cell D.int D.tape)
127 (Bytes.fromByteValues [ 0x41, 0x30, 0x38, 0x3A, 0x78, 0x5A ])
127 (Bytes.fromByteValues [ 0x41, 0x30, 0x38, 0x3A, 0x78, 0x5A ])
128 )
128 )
129 )
129 )
@@ -134,8 +134,8 @@ tests =
134 (Just "hi")
134 (Just "hi")
135 (D.runBytes
135 (D.runBytes
136 (D.oneOf
136 (D.oneOf
137− [ D.cell (D.const D.cord "tape") D.tape
137+ [ D.cell (D.const D.cord "tape") D.tape |> D.map (\((), t) -> t)
138− , D.cell (D.const D.cord "cord") D.cord
138+ , D.cell (D.const D.cord "cord") D.cord |> D.map (\((), c) -> c)
139 ]
139 ]
140 )
140 )
141 (Bytes.fromByteValues [ 0x01, 0x9F, 0x2E, 0x0C, 0xAE, 0x1C, 0x1C, 0x1D, 0x3C, 0x2D ])
141 (Bytes.fromByteValues [ 0x01, 0x9F, 0x2E, 0x0C, 0xAE, 0x1C, 0x1C, 0x1D, 0x3C, 0x2D ])
@@ -147,8 +147,8 @@ tests =
147 (Just "hi")
147 (Just "hi")
148 (D.runBytes
148 (D.runBytes
149 (D.oneOf
149 (D.oneOf
150− [ D.cell (D.const D.cord "tape") D.tape
150+ [ D.cell (D.const D.cord "tape") D.tape |> D.map (\((), t) -> t)
151− , D.cell (D.const D.cord "cord") D.cord
151+ , D.cell (D.const D.cord "cord") D.cord |> D.map (\((), c) -> c)
152 ]
152 ]
153 )
153 )
154 (Bytes.fromByteValues [ 0x01, 0x7F, 0xEC, 0x4D, 0x8E, 0x0C, 0x1E, 0x2D, 0x0D ])
154 (Bytes.fromByteValues [ 0x01, 0x7F, 0xEC, 0x4D, 0x8E, 0x0C, 0x1E, 0x2D, 0x0D ])
@@ -160,7 +160,7 @@ tests =
160 Expect.equal
160 Expect.equal
161 (Just ( 8, -8 ))
161 (Just ( 8, -8 ))
162 (D.runBytes
162 (D.runBytes
163− (D.cell D.signedInt D.signedInt |> D.map Tuple.pair)
163+ (D.cell D.signedInt D.signedInt)
164 (Bytes.fromByteValues [ 0xC1, 0x20, 0xE4, 0x01 ])
164 (Bytes.fromByteValues [ 0xC1, 0x20, 0xE4, 0x01 ])
165 )
165 )
166 )
166 )
@@ -190,7 +190,7 @@ tests =
190 (\( f, i, ui ) ->
190 (\( f, i, ui ) ->
191 Expect.equal (Just ( f, i, ui ))
191 Expect.equal (Just ( f, i, ui ))
192 (D.run
192 (D.run
193− (D.cell D.float64 (D.cell D.signedInt D.int) |> D.map (\a b c -> ( a, b, c )))
193+ (D.cell D.float64 (D.cell D.signedInt D.int) |> D.map (\(a, (b, c)) -> ( a, b, c )))
194 (C.cell (C.float64 f) (C.cell (C.signedInt i) (C.int ui)))
194 (C.cell (C.float64 f) (C.cell (C.signedInt i) (C.int ui)))
195 )
195 )
196 )
196 )