diff --git a/bin/solid.pill b/bin/solid.pill
index 1f07b5c380..5ee94ee674 100644
--- a/bin/solid.pill
+++ b/bin/solid.pill
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9701b98374a28ae99961871d4204856720e9ee6c3f769c389e6faf38392b95e7
-size 12715787
+oid sha256:a4573b45b3e9074b05f31697e8d849d090c3e8edb4cc1498a765630dd9c41a1e
+size 12458357
diff --git a/pkg/arvo/sys/zuse.hoon b/pkg/arvo/sys/zuse.hoon
index 29ed011ca8..49aafbbb63 100644
--- a/pkg/arvo/sys/zuse.hoon
+++ b/pkg/arvo/sys/zuse.hoon
@@ -5836,6 +5836,7 @@
|* wil/(pole fist)
|= jon/json
?. ?=({$a *} jon) ~
+ ?. =((lent wil) (lent p.jon)) ~
=+ raw=((at-raw wil) p.jon)
?.((za raw) ~ (some (zp raw)))
::
diff --git a/pkg/arvo/tests/sys/zuse/format.hoon b/pkg/arvo/tests/sys/zuse/format.hoon
new file mode 100644
index 0000000000..1ef96550f3
--- /dev/null
+++ b/pkg/arvo/tests/sys/zuse/format.hoon
@@ -0,0 +1,693 @@
+/+ *test
+=, format
+|%
+:: split a cord on newlines
+::
+++ test-to-wain
+ ;: weld
+ :: basic usage
+ ::
+ %+ expect-eq
+ !> ~['hello' 'world']
+ !> (to-wain 'hello\0aworld')
+ :: string with no newlines
+ ::
+ %+ expect-eq
+ !> ~['hey']
+ !> (to-wain 'hey')
+ :: empty string works fine
+ ::
+ %+ expect-eq
+ !> ~['']
+ !> (to-wain '')
+ :: leading/trailing/consecutive newlines all work fine
+ ::
+ %+ expect-eq
+ !> ~['' 'hi' '' '' 'there' '']
+ !> (to-wain '\0ahi\0a\0a\0athere\0a')
+ ==
+:: join a list of lines (cords) into a single cord
+::
+++ test-of-wain
+ ;: weld
+ :: basic usage
+ ::
+ %+ expect-eq
+ !> 'hey\0athere\0aworld!'
+ !> (of-wain ~['hey' 'there' 'world!'])
+ :: empty list
+ ::
+ %+ expect-eq
+ !> ''
+ !> (of-wain ~)
+ :: single list
+ ::
+ %+ expect-eq
+ !> 'hey'
+ !> (of-wain ~['hey'])
+ :: list with empties
+ ::
+ %+ expect-eq
+ !> 'hey\0a\0athere'
+ !> (of-wain ~['hey' '' 'there'])
+ ==
+:: join a list of lines (tapes) into a single cord.
+::
+:: Appends an extra newline - this matches unix conventions of a
+:: trailing newline. Also see #1, #2
+::
+++ test-of-wall
+ ;: weld
+ :: basic usage
+ ::
+ %+ expect-eq
+ !> "hey\0athere\0aworld!\0a"
+ !> (of-wall ~["hey" "there" "world!"])
+ :: empty list
+ ::
+ %+ expect-eq
+ !> ""
+ !> (of-wall ~)
+ :: single list
+ ::
+ %+ expect-eq
+ !> "hey\0a"
+ !> (of-wall ~["hey"])
+ :: list with empties
+ ::
+ %+ expect-eq
+ !> "hey\0a\0athere\0a"
+ !> (of-wall ~["hey" "" "there"])
+ ==
+:: encoding and decoding of beams <-> paths
+
+:: (a beam is a fully-qualified file reference. ship, desk, version,
+:: path)
+::
+++ test-beam
+ =/ b=beam [[p=~zod q=%home r=[%ud p=12]] s=/hoon/zuse/sys]
+ =/ p=path /~zod/home/12/sys/zuse/hoon
+ ;: weld
+ :: proper encode
+ ::
+ %+ expect-eq
+ !> p
+ !> (en-beam b)
+ :: proper decode
+ ::
+ %+ expect-eq
+ !> (some b)
+ !> (de-beam p)
+ :: proper round trip
+ ::
+ %+ expect-eq
+ !> (some b)
+ !> (de-beam (en-beam b))
+ :: path too short
+ ::
+ %+ expect-eq
+ !> ~
+ !> (de-beam /~zod/home)
+ :: invalid ship
+ ::
+ %+ expect-eq
+ !> ~
+ !> (de-beam /'~zodisok'/home/12/sys/zuse/hoon)
+ :: invalid desk
+ ::
+ %+ expect-eq
+ !> ~
+ !> (de-beam /~zod/12/12/sys/zuse/hoon)
+ :: invalid case
+ ::
+ %+ expect-eq
+ !> ~
+ !> (de-beam /~zod/home/~zod/sys/zuse/hoon)
+ ==
+:: example values used in test
+::
+++ ex
+ |%
+ ++ nul `json`~
+ ++ tru `json`[%b &]
+ ++ num `json`[%n ~.12]
+ ++ str `json`[%s 'hey']
+ ++ frond `json`(frond:enjs 'foo' num)
+ ++ obj `json`(pairs:enjs ~[['foo' num] ['bar' str]])
+ --
+:: functions for creating `json` values
+::
+++ test-enjs
+ =, enjs
+ ;: weld
+ :: numbers
+ ::
+ %+ expect-eq
+ !> num:ex
+ !> (numb 12)
+ %+ expect-eq
+ !> num:ex
+ !> (numb 0xc)
+ %+ expect-eq
+ !> [%n '0']
+ !> (numb 0)
+ :: strings
+ ::
+ %+ expect-eq
+ !> str:ex
+ !> (tape "hey")
+ %+ expect-eq
+ :: uses of-wall, so adds the trailing newline
+ ::
+ !> [%s 'hi\0athere\0a']
+ !> (wall ~["hi" "there"])
+ :: objects
+ ::
+ %+ expect-eq
+ !> [%o (molt ~[['foo' num:ex]])]
+ !> (frond 'foo' num:ex)
+ =+ props=~[['foo' num:ex] ['bar' tru:ex]]
+ %+ expect-eq
+ !> [%o (molt props)]
+ !> (pairs props)
+ :: time - stored as integer number of milliseconds since the unix epoch
+ ::
+ %+ expect-eq
+ !> [%n '1000']
+ !> (time ~1970.1.1..0.0.1)
+ :: ship - store ship identity as a string
+ ::
+ %+ expect-eq
+ !> [%s 'zod']
+ !> (ship ~zod)
+ ==
+:: dejs - recursive processing of `json` values
+::
+:: This version crashes when used on improper input. Prefer using
+:: dejs-soft (also tested below) which returns units instead.
+::
+:: decoding from null, booleans, numbers, strings
+::
+++ test-dejs-primitives
+ =, dejs
+ ;: weld
+ :: null
+ ::
+ %+ expect-eq
+ !> ~
+ !> (ul `json`~)
+ :: booleans
+ ::
+ :: bo extracts as-is, bu negates it
+ ::
+ %+ expect-eq
+ !> &
+ !> (bo tru:ex)
+ %+ expect-eq
+ !> |
+ !> (bu tru:ex)
+ %- expect-fail
+ |. (bo num:ex)
+ %- expect-fail
+ |. (bu num:ex)
+ :: integers
+ ::
+ :: as @
+ ::
+ %+ expect-eq
+ !> 12
+ !> (ni num:ex)
+ %- expect-fail
+ |. (ni tru:ex)
+ :: as cord
+ ::
+ %+ expect-eq
+ !> '12'
+ !> (no num:ex)
+ %- expect-fail
+ |. (no tru:ex)
+ :: timestamp - ms since the unix epoch
+ ::
+ %+ expect-eq
+ !> ~1970.1.1..00.00.01
+ !> (di [%n ~.1000])
+ %- expect-fail
+ |. (di tru:ex)
+ :: strings
+ ::
+ :: string as tape
+ ::
+ %+ expect-eq
+ !> "hey"
+ !> (sa str:ex)
+ %- expect-fail
+ |. (sa tru:ex)
+ :: string as cord
+ ::
+ %+ expect-eq
+ !> 'hey'
+ !> (so str:ex)
+ %- expect-fail
+ |. (so tru:ex)
+ :: string with custom parser
+ ::
+ %+ expect-eq
+ !> ' '
+ !> ((su (just ' ')) [%s ' '])
+ %- expect-fail
+ |. ((su (just ' ')) tru:ex)
+ ==
+:: decoding arrays
+::
+++ test-dejs-arrays
+ =, dejs
+ ;: weld
+ :: ar - as list
+ ::
+ %+ expect-eq
+ !> ~[1 2 3]
+ !> ((ar ni) [%a ~[[%n '1'] [%n '2'] [%n '3']]])
+ %- expect-fail
+ |. ((ar ni) str:ex)
+ %- expect-fail
+ |. ((ar ni) [%a ~[str:ex]])
+ :: at - as tuple
+ ::
+ :: handlers must match exactly
+ ::
+ %+ expect-eq
+ !> [1 'hey']
+ !> ((at ~[ni so]) [%a ~[[%n '1'] [%s 'hey']]])
+ :: too few or many handlers crash
+ ::
+ %- expect-fail
+ |. ((at ~[ni so]) [%a ~])
+ %- expect-fail
+ |. ((at ~[ni so]) [%a ~[[%n '1'] [%s 'hey'] [%b &]]])
+ :: a nested error will crash
+ ::
+ %- expect-fail
+ |. ((at ~[ni]) [%a ~[[%s 'hey']]])
+ ==
+:: decoding objects
+::
+++ test-dejs-objects
+ =, dejs
+ ;: weld
+ :: of - single-property objects
+ ::
+ %+ expect-eq
+ !> ['foo' 12]
+ !> ((of ~[['foo' ni]]) frond:ex)
+ %+ expect-eq
+ !> ['foo' 12]
+ !> ((of ~[['bar' so] ['foo' ni]]) frond:ex)
+ %- expect-fail
+ :: the handler needs to apply properly to the value
+ ::
+ |. ((of ~[['foo' ni]]) num:ex)
+ %- expect-fail
+ :: the key of the frond needs to exist in the handler list
+ ::
+ |. ((of ~[['bar' so]]) frond:ex)
+ %- expect-fail
+ :: an object with multiple properties is an error
+ ::
+ |. ((of ~[['bar' so] ['foo' ni]]) obj:ex)
+ :: ot - exact-shape objects to tuple
+ ::
+ %+ expect-eq
+ !> [12 'hey']
+ !> ((ot ~[['foo' ni] ['bar' so]]) obj:ex)
+ %- expect-fail
+ :: it checks it's called on an actual object
+ ::
+ |. ((ot ~[['foo' ni]]) num:ex)
+ %- expect-fail
+ :: missing property on the object
+ ::
+ |. ((ot ~[['foo' ni] ['baz' so]]) obj:ex)
+ :: ou - object to tuple, with optional properties. value handlers
+ ::
+ :: are passed (unit json)
+ ::
+ %+ expect-eq
+ !> [12 14]
+ !> ((ou ~[['foo' (uf 14 ni)] ['baz' (uf 14 ni)]]) obj:ex)
+ :: om - simple object as map
+ ::
+ %+ expect-eq
+ !> (molt ~[['foo' num:ex] ['bar' str:ex]])
+ !> ((om same) obj:ex)
+ :: op - object to map, but run a parsing function on the keys
+ ::
+ %+ expect-eq
+ !> (molt ~[[12 num:ex] [14 str:ex]])
+ !> ((op dem same) (pairs:enjs ~[['12' num:ex] ['14' str:ex]]))
+ ==
+:: decoder transformers
+::
+++ test-dejs-transformers
+ =, dejs
+ ;: weld
+ :: cu - decode, then transform
+ ::
+ %+ expect-eq
+ !> 11
+ !> ((cu dec ni) [%n ~.12])
+ :: ci - decode, then assert a transformation succeeds
+ ::
+ %+ expect-eq
+ !> 12
+ !> ((ci some ni) num:ex)
+ %- expect-fail
+ |. ((ci |=(* ~) ni) num:ex)
+ :: mu - decode if not null
+ ::
+ %+ expect-eq
+ !> ~
+ !> ((mu ni) nul:ex)
+ %+ expect-eq
+ !> (some 12)
+ !> ((mu ni) num:ex)
+ :: pe - add prefix to decoded value
+ ::
+ %+ expect-eq
+ !> ['a' 12]
+ !> ((pe 'a' ni) num:ex)
+ :: uf - defaults for empty (unit json)
+ ::
+ %+ expect-eq
+ !> 'nah'
+ !> ((uf 'nah' ni) ~)
+ %+ expect-eq
+ !> 12
+ !> ((uf 'nah' ni) (some num:ex))
+ :: un - dangerous ensure a (unit json)
+ ::
+ %+ expect-eq
+ !> 12
+ !> ((un ni) (some num:ex))
+ %- expect-fail
+ |. ((un ni) ~)
+ ==
+:: various unit/collection helpers
+::
+++ test-dejs-helpers
+ =, dejs
+ =+ all=`(list (unit @))`~[(some 1) (some 2) (some 3)]
+ =+ nall=`(list (unit @))`~[(some 1) ~ (some 3)]
+ ;: weld
+ :: za - are all units in this list full?
+ ::
+ %+ expect-eq
+ !> &
+ !> (za ~)
+ %+ expect-eq
+ !> &
+ !> (za all)
+ %+ expect-eq
+ !> |
+ !> (za nall)
+ :: zl - collapse (list (unit)) -> (unit (list))
+ ::
+ %+ expect-eq
+ !> (some ~[1 2 3])
+ !> (zl all)
+ %+ expect-eq
+ !> ~
+ !> (zl nall)
+ %+ expect-eq
+ !> (some ~)
+ !> (zl ~)
+ :: zp - force unwrap a (list (unit)) as tuple
+ ::
+ %+ expect-eq
+ !> [1 2 3]
+ !> (zp all)
+ %- expect-fail
+ |. (zp nall)
+ %- expect-fail
+ |. (zp ~)
+ :: zm - collapse a (map @tas (unit *)) -> (unit (map @tas *))
+ ::
+ %+ expect-eq
+ !> (some (molt ~[['a' 1] ['b' 2]]))
+ !> (zm (molt ~[['a' (some 1)] ['b' (some 2)]]))
+ %+ expect-eq
+ !> ~
+ !> (zm (molt ~[['a' `(unit @)`(some 1)] ['b' ~]]))
+ %+ expect-eq
+ !> (some ~)
+ !> (zm ~)
+ ==
+::
+:: dejs-soft recursive processing of `json` values
+::
+:: These functions return units, which will be nil if the input
+:: doesn't match the defined structure.
+::
+++ test-dejs-soft-primitives
+ =, dejs-soft
+ ;: weld
+ :: null
+ ::
+ %+ expect-eq
+ !> `~
+ !> (ul `json`~)
+ :: booleans
+ ::
+ :: bo extracts as-is, bu negates it
+ ::
+ %+ expect-eq
+ !> `&
+ !> (bo tru:ex)
+ %+ expect-eq
+ !> `|
+ !> (bu tru:ex)
+ %+ expect-eq
+ !> ~
+ !> (bo num:ex)
+ %+ expect-eq
+ !> ~
+ !> (bu num:ex)
+ :: integers
+ :: as @
+ ::
+ %+ expect-eq
+ !> `12
+ !> (ni num:ex)
+ %+ expect-eq
+ !> ~
+ !> (ni tru:ex)
+ :: as cord
+ ::
+ %+ expect-eq
+ !> `'12'
+ !> (no num:ex)
+ %+ expect-eq
+ !> ~
+ !> (no tru:ex)
+ :: timestamp - ms since the unix epoch
+ ::
+ %+ expect-eq
+ !> `~1970.1.1..00.00.01
+ !> (di [%n ~.1000])
+ %+ expect-eq
+ !> ~
+ !> (di tru:ex)
+ :: string as tape
+ ::
+ %+ expect-eq
+ !> `"hey"
+ !> (sa str:ex)
+ %+ expect-eq
+ !> ~
+ !> (sa tru:ex)
+ :: string as cord
+ ::
+ %+ expect-eq
+ !> `'hey'
+ !> (so str:ex)
+ %+ expect-eq
+ !> ~
+ !> (so tru:ex)
+ :: string with custom parser
+ ::
+ %+ expect-eq
+ !> `' '
+ !> ((su (just ' ')) [%s ' '])
+ %+ expect-eq
+ !> ~
+ !> ((su (just ' ')) tru:ex)
+ ==
+:: decoding arrays
+::
+++ test-dejs-soft-arrays
+ =, dejs-soft
+ ;: weld
+ :: ar - as list
+ ::
+ %+ expect-eq
+ !> `~[1 2 3]
+ !> ((ar ni) [%a ~[[%n '1'] [%n '2'] [%n '3']]])
+ %+ expect-eq
+ !> ~
+ !> ((ar ni) str:ex)
+ %+ expect-eq
+ !> ~
+ !> ((ar ni) [%a ~[str:ex]])
+ :: at - as tuple
+ ::
+ :: handlers must match exactly
+ ::
+ %+ expect-eq
+ !> `[1 'hey']
+ !> ((at ~[ni so]) [%a ~[[%n '1'] [%s 'hey']]])
+ :: too few or many handlers won't match
+ ::
+ %+ expect-eq
+ !> ~
+ !> ((at ~[ni so]) [%a ~])
+ %+ expect-eq
+ !> ~
+ !> ((at ~[ni so]) [%a ~[[%n '1'] [%s 'hey'] [%b &]]])
+ :: a nested failure to match will propagate upwards
+ ::
+ %+ expect-eq
+ !> ~
+ !> ((at ~[ni]) [%a ~[[%s 'hey']]])
+ ==
+:: decoding objects
+::
+++ test-dejs-soft-objects
+ =, dejs-soft
+ ;: weld
+ :: of - single-property objects
+ ::
+ %+ expect-eq
+ !> `['foo' 12]
+ !> ((of ~[['foo' ni]]) frond:ex)
+ %+ expect-eq
+ !> `['foo' 12]
+ !> ((of ~[['bar' so] ['foo' ni]]) frond:ex)
+ %+ expect-eq
+ !> ~
+ :: the handler needs to apply properly to the value
+ ::
+ !> ((of ~[['foo' ni]]) num:ex)
+ %+ expect-eq
+ !> ~
+ :: the key of the frond needs to exist in the handler list
+ ::
+ !> ((of ~[['bar' so]]) frond:ex)
+ %+ expect-eq
+ !> ~
+ :: an object with multiple properties is an error
+ ::
+ !> ((of ~[['bar' so] ['foo' ni]]) obj:ex)
+ :: ot - exact-shape objects to tuple
+ ::
+ %+ expect-eq
+ !> `[12 'hey']
+ !> ((ot ~[['foo' ni] ['bar' so]]) obj:ex)
+ %+ expect-eq
+ !> ~
+ :: missing property on the object
+ ::
+ !> ((ot ~[['foo' ni] ['baz' so]]) obj:ex)
+ :: om - simple object as map
+ ::
+ %+ expect-eq
+ !> `(molt ~[['foo' num:ex] ['bar' str:ex]])
+ !> ((om some) obj:ex)
+ :: op - object to map, but run a parsing function on the keys
+ ::
+ %+ expect-eq
+ !> `(molt ~[[12 num:ex] [14 str:ex]])
+ !> ((op dem some) (pairs:enjs ~[['12' num:ex] ['14' str:ex]]))
+ ==
+:: decoder transformers
+::
+++ test-dejs-soft-transformers
+ =, dejs-soft
+ ;: weld
+ :: cu - decode, then transform
+ ::
+ %+ expect-eq
+ !> `11
+ !> ((cu dec ni) [%n ~.12])
+ :: ci - decode, then transform, adapting the transformer to return a
+ :: unit
+ ::
+ %+ expect-eq
+ !> `12
+ !> ((ci some ni) num:ex)
+ %+ expect-eq
+ !> ~
+ !> ((ci |=(* ~) ni) num:ex)
+ :: mu - decode if not null
+ ::
+ %+ expect-eq
+ !> `~
+ !> ((mu ni) nul:ex)
+ %+ expect-eq
+ !> `(some 12)
+ !> ((mu ni) num:ex)
+ :: pe - add prefix to decoded value
+ ::
+ %+ expect-eq
+ !> `['a' 12]
+ !> ((pe 'a' ni) num:ex)
+ ==
+:: various unit/collection helpers
+::
+++ test-dejs-soft-helpers
+ =, dejs-soft
+ =+ all=`(list (unit @))`~[(some 1) (some 2) (some 3)]
+ =+ nall=`(list (unit @))`~[(some 1) ~ (some 3)]
+ ;: weld
+ :: za - are all units in this list full?
+ ::
+ %+ expect-eq
+ !> &
+ !> (za ~)
+ %+ expect-eq
+ !> &
+ !> (za all)
+ %+ expect-eq
+ !> |
+ !> (za nall)
+ :: zl - collapse (list (unit)) -> (unit (list))
+ ::
+ %+ expect-eq
+ !> (some ~[1 2 3])
+ !> (zl all)
+ %+ expect-eq
+ !> ~
+ !> (zl nall)
+ %+ expect-eq
+ !> (some ~)
+ !> (zl ~)
+ :: zp - force unwrap a (list (unit)) as tuple
+ ::
+ %+ expect-eq
+ !> [1 2 3]
+ !> (zp all)
+ %- expect-fail
+ |. (zp nall)
+ %- expect-fail
+ |. (zp ~)
+ :: zm - collapse a (map @tas (unit *)) -> (unit (map @tas *))
+ ::
+ %+ expect-eq
+ !> (some (molt ~[['a' 1] ['b' 2]]))
+ !> (zm (molt ~[['a' (some 1)] ['b' (some 2)]]))
+ %+ expect-eq
+ !> ~
+ !> (zm (molt ~[['a' `(unit @)`(some 1)] ['b' ~]]))
+ %+ expect-eq
+ !> (some ~)
+ !> (zm ~)
+ ==
+--
diff --git a/pkg/arvo/tests/sys/zuse/html.hoon b/pkg/arvo/tests/sys/zuse/html.hoon
index be144b750a..9d4b6facf9 100644
--- a/pkg/arvo/tests/sys/zuse/html.hoon
+++ b/pkg/arvo/tests/sys/zuse/html.hoon
@@ -1,13 +1,15 @@
-:: tests for html
+:: tests for html
+::
/+ *test
+=, html
=, de-xml:html
=, en-xml:html
|%
-:: de-xml takes a cord but en-xml returns a tape?
+:: de-xml takes a cord but en-xml returns a tape?
::
++ test-de-xml
;: weld
- :: Basic use
+ :: Basic use
::
%+ expect-eq
!> ^- manx +:(de-xml:html (crip "
My first webpageWelcome!
Hello, world! We are on the web.\0a"))
@@ -23,14 +25,15 @@
;script(src "http://unsafely.tracking.you/cookiemonster.js");
==
==
- :: CDATA sections
+ :: CDATA sections
::
%+ expect-eq
!> ^- manx
+:(de-xml:html (crip ""))
!> ^- manx
;elem: text
- :: comments
+ :: comments
+ ::
%+ expect-eq
!> ^- manx
;elem: text
@@ -39,7 +42,7 @@
!> ^- manx
;elem;
!> +:(de-xml:html (crip ""))
- :: entities
+ :: entities
::
%+ expect-eq
!> ^- manx
@@ -59,13 +62,13 @@
::
++ test-en-xml
;: weld
- :: Entities
+ :: Entities
::
%+ expect-eq
!> ">"
!> %- en-xml:html
;elem: >
- :: Basic use
+ :: Basic use
::
%+ expect-eq
!> %- en-xml:html
@@ -82,11 +85,788 @@
==
==
!> "My first webpageWelcome!
Hello, world!\0aWe are on the web.\0a"
- :: Attributes
+ :: Attributes
::
%+ expect-eq
!> "Submit"
!> %- en-xml:html
;input(type "submit"): Submit
==
+:: JSON encoding/decoding
+::
+++ from-code-points
+ |= points=(list @)
+ (tufa `(list @c)`points)
+++ from-code-point
+ |= point=@
+ (tuft point)
++$ json-parse-spec [name=tape input=cord expected=json]
++$ json-parse-rejection-spec [input=cord name=tape]
+:: For checking a large list of examples against expected values.
+:: It also nicely formats any failures.
+::
+++ run-parse-specs
+ :: legend tells of a man who made the Kessel run in less than 12
+ :: parse-specs...
+ |= specs=(list json-parse-spec)
+ %- zing
+ %- turn
+ :- specs
+ |= spec=json-parse-spec
+ ^- tang
+ =+ result=(expect-eq !>(`expected.spec) !>((de-json:html input.spec)))
+ ?~ result ~
+ `tang`[[%leaf "in {name.spec}:"] result]
+:: Checks that a list of examples all fail to parse
+::
+++ run-parse-rejection-specs
+ |= specs=(list json-parse-rejection-spec)
+ %- zing
+ %- turn
+ :- specs
+ |= spec=json-parse-rejection-spec
+ ^- tang
+ =+ result=(expect-eq !>(~) !>((de-json:html input.spec)))
+ ?~ result ~
+ `tang`[[%leaf "in {name.spec}:"] result]
+:: example values used in tests
+::
+++ ex
+ |%
+ ++ two `json`[%n '2']
+ ++ tru `json`[%b &]
+ --
+:: encoding naked values
+::
+++ test-en-json-basics
+ ;: weld
+ %+ expect-eq
+ !> "true"
+ !> (en-json [%b &])
+ %+ expect-eq
+ !> "false"
+ !> (en-json [%b |])
+ %+ expect-eq
+ !> "null"
+ !> (en-json ~)
+ %+ expect-eq
+ !> "123.45"
+ !> (en-json [%n '123.45'])
+ ==
+:: encoding strings, with proper escaping rules
+::
+++ test-en-json-strings
+ :: A less-confusing representation of theses strings are included in comments
+ ::
+ :: Things get confusing with hoon string literal escapes. The
+ :: version included as a comment is if you opened the json output
+ :: in a simple text editor.
+ ::
+ ;: weld
+ :: "hello"
+ ::
+ %+ expect-eq
+ !> "\"hello\""
+ !> (en-json [%s 'hello'])
+ :: it escapes quotes
+ :: "he said \"wow\""
+ ::
+ %+ expect-eq
+ !> "\"he said \\\"wow\\\"\""
+ !> (en-json [%s 'he said "wow"'])
+ :: it escapes backslashes
+ :: "Delete C:\\Windows\\System32"
+ ::
+ %+ expect-eq
+ !> "\"Delete C:\\\\Windows\\\\System32\""
+ !> (en-json [%s 'Delete C:\\Windows\\System32'])
+ :: it uses \n for newlines
+ :: "hello\nworld"
+ ::
+ %+ expect-eq
+ !> "\"hello\\nworld\""
+ !> (en-json [%s 'hello\0aworld'])
+ :: it uses \u encoding for control characters (0x1f and below)
+ :: "ding!\u0007"
+ ::
+ %+ expect-eq
+ !> "\"ding!\\u0007\""
+ !> (en-json [%s 'ding!\07'])
+ :: it supports null bytes
+ ::
+ %+ expect-eq
+ !> "\"null\\u0000byte\\u0000separator\""
+ !> (en-json [%s 'null\00byte\00separator'])
+ :: inline unicode characters
+ ::
+ %+ expect-eq
+ !> "\"lmao 🤣\""
+ !> (en-json [%s 'lmao 🤣'])
+ ==
+:: encoding arrays
+::
+++ test-en-json-arrays
+ ;: weld
+ :: empty array
+ ::
+ %+ expect-eq
+ !> "[]"
+ !> (en-json [%a ~])
+ :: 1 element
+ ::
+ %+ expect-eq
+ !> "[2]"
+ !> (en-json [%a ~[two:ex]])
+ :: multiple elements are comma-separated
+ ::
+ %+ expect-eq
+ !> "[2,2,2]"
+ !> (en-json [%a ~[two:ex two:ex two:ex]])
+ ==
+:: encoding basic objects
+::
+++ test-en-json-objects
+ :: opening curly braces are escaped to avoid urbit string literal
+ :: interpolation
+ ::
+ ;: weld
+ :: empty object
+ ::
+ %+ expect-eq
+ !> "\{}"
+ !> (en-json [%o ~])
+ :: one property
+ ::
+ %+ expect-eq
+ !> "\{\"foo\":2}"
+ !> (en-json [%o (molt ~[['foo' two:ex]])])
+ :: multiple properties are comma-separated
+ ::
+ %+ expect-eq
+ !> "\{\"foo\":2,\"bar\":true}"
+ !> (en-json [%o (molt ~[['foo' two:ex] ['bar' tru:ex]])])
+ :: object keys use same encoding logic as strings
+ ::
+ %+ expect-eq
+ :: {"\u0007\"\n\\":true}
+ ::
+ !> "\{\"\\u0007\\\"\\n\\\\\":true}"
+ !> (en-json [%o (molt ~[['\07"\0a\\' tru:ex]])])
+ ==
+:: object encoding stress-test
+::
+++ test-en-json-complex-structure
+ %+ expect-eq
+ :: [{}, 4, [[], [{foo: {"4": 4, "true": true}}]]]
+ ::
+ !> "[\{},4,[[],[\{\"foo\":\{\"4\":4,\"true\":true}}]]]"
+ !> %- en-json:html
+ :- %a
+ :~ [%o ~]
+ [%n '4']
+ :- %a
+ :~ [%a ~]
+ :- %a
+ :~ %+ frond:enjs:format
+ 'foo'
+ (pairs:enjs:format ~[['4' [%n '4']] ['true' [%b &]]])
+ ==
+ ==
+ ==
+:: decoding naked values
+::
+++ test-de-json-simple-values
+ =, html
+ ;: weld
+ %+ expect-eq
+ !> `~
+ !> (de-json 'null')
+ %+ expect-eq
+ !> `[%b &]
+ !> (de-json 'true')
+ %+ expect-eq
+ !> `[%b |]
+ !> (de-json 'false')
+ ==
+:: The following parser test suite (test-de-json-bad-examples and
+:: test-en-json-suite) is adapted from https://github.com/nst/JSONTestSuite/
+:: (Copyright (c) 2016 Nicolas Seriot) under the terms of the MIT license.
+::
+:: These are all inputs that should be rejected by a valid json parser
+::
+++ test-de-json-bad-examples
+ %- run-parse-rejection-specs
+ :~
+ ['[1 true]' "n_array_1_true_without_comma"]
+ ['[aÂ]' "n_array_a_invalid_utf8"]
+ ['["": 1]' "n_array_colon_instead_of_comma"]
+ ['[""],' "n_array_comma_after_close"]
+ ['[,1]' "n_array_comma_and_number"]
+ ['[1,,2]' "n_array_double_comma"]
+ ['["x",,]' "n_array_double_extra_comma"]
+ ['["x"]]' "n_array_extra_close"]
+ ['["",]' "n_array_extra_comma"]
+ ['["x"' "n_array_incomplete"]
+ ['[x' "n_array_incomplete_invalid_value"]
+ ['[3[4]]' "n_array_inner_array_no_comma"]
+ ['[ˇ]' "n_array_invalid_utf8"]
+ ['[1:2]' "n_array_items_separated_by_semicolon"]
+ ['[,]' "n_array_just_comma"]
+ ['[-]' "n_array_just_minus"]
+ ['[ , ""]' "n_array_missing_value"]
+ ['["a",\0a4\0a,1,' "n_array_newlines_unclosed"]
+ ['[1,]' "n_array_number_and_comma"]
+ ['[1,,]' "n_array_number_and_several_commas"]
+ ['["\0b"\\f]' "n_array_spaces_vertical_tab_formfeed"]
+ ['[*]' "n_array_star_inside"]
+ ['[""' "n_array_unclosed"]
+ ['[1,' "n_array_unclosed_trailing_comma"]
+ ['[1,\0a1\0a,1' "n_array_unclosed_with_new_lines"]
+ ['[{}' "n_array_unclosed_with_object_inside"]
+ ['[fals]' "n_incomplete_false"]
+ ['[nul]' "n_incomplete_null"]
+ ['[tru]' "n_incomplete_true"]
+ ['[++1234]' "n_number_++"]
+ ['[+1]' "n_number_+1"]
+ ['[+Inf]' "n_number_+Inf"]
+ ['[-01]' "n_number_-01"]
+ ['[-1.0.]' "n_number_-1.0."]
+ ['[-NaN]' "n_number_-NaN"]
+ ['[.-1]' "n_number_.-1"]
+ ['[.2e-3]' "n_number_.2e-3"]
+ ['[0.1.2]' "n_number_0.1.2"]
+ ['[1 000.0]' "n_number_1_000"]
+ ['[1eE2]' "n_number_1eE2"]
+ ['[Inf]' "n_number_Inf"]
+ ['[NaN]' "n_number_NaN"]
+ ['[1]' "n_number_U+FF11_fullwidth_digit_one"]
+ ['[1+2]' "n_number_expression"]
+ ['[0x1]' "n_number_hex_1_digit"]
+ ['[0x42]' "n_number_hex_2_digits"]
+ ['[Infinity]' "n_number_infinity"]
+ ['[0e+-1]' "n_number_invalid+-"]
+ ['[-123.123foo]' "n_number_invalid-negative-real"]
+ ['[123Â]' "n_number_invalid-utf-8-in-bigger-int"]
+ ['[1e1Â]' "n_number_invalid-utf-8-in-exponent"]
+ ['[0Â]' "n_number_invalid-utf-8-in-int"]
+ ['[-Infinity]' "n_number_minus_infinity"]
+ ['[-foo]' "n_number_minus_sign_with_trailing_garbage"]
+ ['[- 1]' "n_number_minus_space_1"]
+ ['[-012]' "n_number_neg_int_starting_with_zero"]
+ ['[-.123]' "n_number_neg_real_without_int_part"]
+ ['[-1x]' "n_number_neg_with_garbage_at_end"]
+ ['[1ea]' "n_number_real_garbage_after_e"]
+ ['[1eÂ]' "n_number_real_with_invalid_utf8_after_e"]
+ ['[.123]' "n_number_starting_with_dot"]
+ ['[1.2a-3]' "n_number_with_alpha"]
+ ['[1.8011670033376514H-308]' "n_number_with_alpha_char"]
+ ['[012]' "n_number_with_leading_zero"]
+ ['["x", truth]' "n_object_bad_value"]
+ ['{[: "x"}' "n_object_bracket_key"]
+ ['{"x", null}' "n_object_comma_instead_of_colon"]
+ ['{"x"::"b"}' "n_object_double_colon"]
+ ['{üá®üá≠}' "n_object_emoji"]
+ ['{"a":"a" 123}' "n_object_garbage_at_end"]
+ ['{key: \'value\'}' "n_object_key_with_single_quotes"]
+ ['{"π":"0",}' "n_object_lone_continuation_byte_in_key_and_trailing_comma"]
+ ['{"a" b}' "n_object_missing_colon"]
+ ['{:"b"}' "n_object_missing_key"]
+ ['{"a" "b"}' "n_object_missing_semicolon"]
+ ['{"a":' "n_object_missing_value"]
+ ['{"a"' "n_object_no-colon"]
+ ['{1:1}' "n_object_non_string_key"]
+ ['{9999E9999:1}' "n_object_non_string_key_but_huge_number_instead"]
+ ['{null:null,null:null}' "n_object_repeated_null_null"]
+ ['{"id":0,,,,,}' "n_object_several_trailing_commas"]
+ ['{\'a\':0}' "n_object_single_quote"]
+ ['{"id":0,}' "n_object_trailing_comma"]
+ ['{"a":"b"}/**/' "n_object_trailing_comment"]
+ ['{"a":"b"}/**//' "n_object_trailing_comment_open"]
+ ['{"a":"b"}//' "n_object_trailing_comment_slash_open"]
+ ['{"a":"b"}/' "n_object_trailing_comment_slash_open_incomplete"]
+ ['{"a":"b",,"c":"d"}' "n_object_two_commas_in_a_row"]
+ ['{a: "b"}' "n_object_unquoted_key"]
+ ['{"a":"a' "n_object_unterminated-value"]
+ ['{ "foo" : "bar", "a" }' "n_object_with_single_string"]
+ ['{"a":"b"}#' "n_object_with_trailing_garbage"]
+ [' ' "n_single_space"]
+ ['["\\uD800\\"]' "n_string_1_surrogate_then_escape"]
+ ['["\\uD800\\u"]' "n_string_1_surrogate_then_escape_u"]
+ ['["\\uD800\\u1"]' "n_string_1_surrogate_then_escape_u1"]
+ ['["\\uD800\\u1x"]' "n_string_1_surrogate_then_escape_u1x"]
+ ['[é]' "n_string_accentuated_char_no_quotes"]
+ ['["\\x00"]' "n_string_escape_x"]
+ ['["\\\\\\"]' "n_string_escaped_backslash_bad"]
+ ['["\\\09"]' "n_string_escaped_ctrl_char_tab"]
+ ['["\\üåÄ"]' "n_string_escaped_emoji"]
+ ['["\\"]' "n_string_incomplete_escape"]
+ ['["\\u00A"]' "n_string_incomplete_escaped_character"]
+ ['["\\uD834\\uDd"]' "n_string_incomplete_surrogate"]
+ ['["\\uD800\\uD800\\x"]' "n_string_incomplete_surrogate_escape_invalid"]
+ ['["\\uÂ"]' "n_string_invalid-utf-8-in-escape"]
+ ['["\\a"]' "n_string_invalid_backslash_esc"]
+ ['["\\uqqqq"]' "n_string_invalid_unicode_escape"]
+ ['["\\Â"]' "n_string_invalid_utf8_after_escape"]
+ ['[\\u0020"asd"]' "n_string_leading_uescaped_thinspace"]
+ ['[\\n]' "n_string_no_quotes_with_bad_escape"]
+ ['"' "n_string_single_doublequote"]
+ ['[\'single quote\']' "n_string_single_quote"]
+ ['abc' "n_string_single_string_no_double_quotes"]
+ ['["\\' "n_string_start_escape_unclosed"]
+ ['["new' "n_string_unescaped_newline"]
+ ['line"]' "n_string_unescaped_newline"]
+ ['["\09"]' "n_string_unescaped_tab"]
+ ['"\\UA66D"' "n_string_unicode_CapitalU"]
+ ['""x' "n_string_with_trailing_garbage"]
+ ['[⁠]' "n_structure_U+2060_word_joined"]
+ ['Ôªø' "n_structure_UTF8_BOM_no_data"]
+ ['<.>' "n_structure_angle_bracket_."]
+ ['[]' "n_structure_angle_bracket_null"]
+ ['[1]x' "n_structure_array_trailing_garbage"]
+ ['[1]]' "n_structure_array_with_extra_array_close"]
+ ['["asd]' "n_structure_array_with_unclosed_string"]
+ ['aå' "n_structure_ascii-unicode-identifier"]
+ ['[True]' "n_structure_capitalized_True"]
+ ['1]' "n_structure_close_unopened_array"]
+ ['{"x": true,' "n_structure_comma_instead_of_closing_brace"]
+ ['[][]' "n_structure_double_array"]
+ [']' "n_structure_end_array"]
+ ['Ôª{}' "n_structure_incomplete_UTF8_BOM"]
+ ['Â' "n_structure_lone-invalid-utf-8"]
+ ['[' "n_structure_lone-open-bracket"]
+ ['["a\00a"]' "n_string_unescaped_crtl_char"]
+ ['["\\00"]' "n_string_backslash_00"]
+ ==
+:: TODO: de-json is accepting a slew of number formats it shouldn't.
+::
+:: Tracking issue here: https://github.com/urbit/urbit/issues/1775
+:: Re-enable this test by removing the disable- prefix
+::
+++ disable-test-reject-invalid-numbers
+ %- run-parse-rejection-specs
+ :~
+ ['123\00' "n_multidigit_number_then_00"]
+ ['[1.]' "n_number_real_without_fractional_part"]
+ ['[2.e+3]' "n_number_2.e+3"]
+ ['[2.e-3]' "n_number_2.e-3"]
+ ['[2.e3]' "n_number_2.e3"]
+ ['[9.e+]' "n_number_9.e+"]
+ ['[0.3e+]' "n_number_0.3e+"]
+ ['[0.3e]' "n_number_0.3e"]
+ ['[0.e1]' "n_number_0.e1"]
+ ['[0E+]' "n_number_0_capital_E+"]
+ ['[0E]' "n_number_0_capital_E"]
+ ['[0e+]' "n_number_0e+"]
+ ['[0e]' "n_number_0e"]
+ ['[1.0e+]' "n_number_1.0e+"]
+ ['[1.0e-]' "n_number_1.0e-"]
+ ['[1.0e]' "n_number_1.0e"]
+ ['[-2.]' "n_number_-2."]
+ ==
+:: these are all inputs that should be accepted by a valid parser
+::
+++ test-en-json-suite
+ =+ frond=frond:enjs:format
+ =+ pairs=pairs:enjs:format
+ %- run-parse-specs
+ :~
+ :* "y_array_arraysWithSpaces"
+ '[[] ]'
+ [%a ~[[%a ~]]]
+ ==
+ :* "y_array_empty-string"
+ '[""]'
+ [%a ~[[%s '']]]
+ ==
+ :* "y_array_empty"
+ '[]'
+ [%a ~]
+ ==
+ :* "y_array_ending_with_newline"
+ '["a"]\0a'
+ [%a ~[[%s 'a']]]
+ ==
+ :* "y_array_false"
+ '[false]'
+ [%a ~[[%b |]]]
+ ==
+ :* "y_array_heterogeneous"
+ '[null, 1, "1", {}]'
+ [%a ~[~ [%n '1'] [%s '1'] [%o ~]]]
+ ==
+ :* "y_array_null"
+ '[null]'
+ [%a ~[~]]
+ ==
+ :* "y_array_with_1_and_newline"
+ '[1\0a]'
+ [%a ~[[%n '1']]]
+ ==
+ :* "y_array_with_leading_space"
+ ' [1]'
+ [%a ~[[%n '1']]]
+ ==
+ :* "y_array_with_several_null"
+ '[1,null,null,null,2]'
+ [%a ~[[%n '1'] ~ ~ ~ [%n '2']]]
+ ==
+ :* "y_array_with_trailing_space"
+ '[2] '
+ [%a ~[[%n '2']]]
+ ==
+ :* "y_number"
+ '[123e65]'
+ [%a ~[[%n '123e65']]]
+ ==
+ :* "y_number_0e+1"
+ '[0e+1]'
+ [%a ~[[%n '0e+1']]]
+ ==
+ :* "y_number_0e1"
+ '[0e1]'
+ [%a ~[[%n '0e1']]]
+ ==
+ :* "y_number_after_space"
+ '[ 4]'
+ [%a ~[[%n '4']]]
+ ==
+ :* "y_number_double_close_to_zero"
+ '[-0.0000000000000000000000000000000000000000000000000000000\
+ /00000000000000000000001]'
+ [%a ~[[%n '-0.0000000000000000000000000000000000000000000000\
+ /00000000000000000000000000000001']]]
+ ==
+ :* "y_number_int_with_exp"
+ '[20e1]'
+ [%a ~[[%n '20e1']]]
+ ==
+ :* "y_number_minus_zero"
+ '[-0]'
+ [%a ~[[%n '-0']]]
+ ==
+ :* "y_number_negative_int"
+ '[-123]'
+ [%a ~[[%n '-123']]]
+ ==
+ :* "y_number_negative_one"
+ '[-1]'
+ [%a ~[[%n '-1']]]
+ ==
+ :* "y_number_negative_zero"
+ '[-0]'
+ [%a ~[[%n '-0']]]
+ ==
+ :* "y_number_real_capital_e"
+ '[1E22]'
+ [%a ~[[%n '1E22']]]
+ ==
+ :* "y_number_real_capital_e_neg_exp"
+ '[1E-2]'
+ [%a ~[[%n '1E-2']]]
+ ==
+ :* "y_number_real_capital_e_pos_exp"
+ '[1E+2]'
+ [%a ~[[%n '1E+2']]]
+ ==
+ :* "y_number_real_exponent"
+ '[123e45]'
+ [%a ~[[%n '123e45']]]
+ ==
+ :* "y_number_real_fraction_exponent"
+ '[123.456e78]'
+ [%a ~[[%n '123.456e78']]]
+ ==
+ :* "y_number_real_neg_exp"
+ '[1e-2]'
+ [%a ~[[%n '1e-2']]]
+ ==
+ :* "y_number_real_pos_exponent"
+ '[1e+2]'
+ [%a ~[[%n '1e+2']]]
+ ==
+ :* "y_number_simple_int"
+ '[123]'
+ [%a ~[[%n '123']]]
+ ==
+ :* "y_number_simple_real"
+ '[123.456789]'
+ [%a ~[[%n '123.456789']]]
+ ==
+ :* "y_object"
+ '{"asd":"sdf", "dfg":"fgh"}'
+ (pairs ~[['asd' [%s 'sdf']] ['dfg' [%s ['fgh']]]])
+ ==
+ :* "y_object_basic"
+ '{"asd":"sdf"}'
+ (frond ['asd' [%s 'sdf']])
+ ==
+ :: duplicated keys, it takes the latest one.
+ ::
+ :* "y_object_duplicated_key"
+ '{"a":"b","a":"c"}'
+ (frond ['a' [%s 'c']])
+ ==
+ :* "y_object_duplicated_key_and_value"
+ '{"a":"b","a":"b"}'
+ (frond ['a' [%s 'b']])
+ ==
+ :* "y_object_empty"
+ '{}'
+ [%o ~]
+ ==
+ :* "y_object_empty_key"
+ '{"":0}'
+ (frond ['' [%n '0']])
+ ==
+ :* "y_object_extreme_numbers"
+ '{ "min": -1.0e+28, "max": 1.0e+28 }'
+ (pairs ~[['min' [%n '-1.0e+28']] ['max' [%n '1.0e+28']]])
+ ==
+ =/ long=@t 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+ :* "y_object_long_strings"
+ '{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], \
+ /"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}'
+ (pairs ~[['id' [%s long]] ['x' [%a ~[(frond ['id' [%s long]])]]]])
+ ==
+ :* "y_object_simple"
+ '{"a":[]}'
+ (frond 'a' [%a ~])
+ ==
+ :* "y_object_string_unicode"
+ '{"title":"\\u041f\\u043e\\u043b\\u0442\\u043e\\u0440\\u0430 \
+ /\\u0417\\u0435\\u043c\\u043b\\u0435\\u043a\\u043e\\u043f\\u0430" }'
+ (frond 'title' [%s 'Полтора Землекопа'])
+ ==
+ :* "y_object_with_newlines"
+ '{\0a"a": "b"\0a}'
+ (frond 'a' [%s 'b'])
+ ==
+ :* "y_string_allowed_escapes"
+ '["\\"\\\\\\/\\b\\f\\n\\r\\t"]'
+ [%a ~[[%s '"\\/\08\0c\0a\0d\09']]]
+ ==
+ :* "y_string_backslash_and_u_escaped_zero"
+ '["\\\\u0000"]'
+ [%a ~[[%s '\\u0000']]]
+ ==
+ :* "y_string_backslash_doublequotes"
+ '["\\""]'
+ [%a ~[[%s '"']]]
+ ==
+ :* "y_string_comments"
+ '["a/*b*/c/*d//e"]'
+ [%a ~[[%s 'a/*b*/c/*d//e']]]
+ ==
+ :* "y_string_double_escape_a"
+ '["\\\\a"]'
+ [%a ~[[%s '\\a']]]
+ ==
+ :* "y_string_double_escape_n"
+ '["\\\\n"]'
+ [%a ~[[%s '\\n']]]
+ ==
+ :* "y_string_escaped_control_character"
+ '["\\u0012"]'
+ [%a ~[[%s '\12']]]
+ ==
+ :* "y_string_in_array_with_leading_space"
+ '[ "asd"]'
+ [%a ~[[%s 'asd']]]
+ ==
+ :* "y_string_nonCharacterInUTF-8_U+10FFFF"
+ '[""]'
+ [%a ~[[%s (from-code-point 0x10.ffff)]]]
+ ==
+ :* "y_string_nonCharacterInUTF-8_U+FFFF"
+ '[""]'
+ [%a ~[[%s (from-code-point 0xffff)]]]
+ ==
+ :* "y_string_null_escape"
+ '["\\u0000"]'
+ [%a ~[[%s '\00']]]
+ ==
+ :* "y_string_one-byte-utf-8"
+ '["\\u002c"]'
+ [%a ~[[%s '\2c']]]
+ ==
+ :* "y_string_pi"
+ '["π"]'
+ [%a ~[[%s 'π']]]
+ ==
+ :* "y_string_reservedCharacterInUTF-8_U+1BFFF"
+ '[""]'
+ [%a ~[[%s (from-code-point 0x1.bfff)]]]
+ ==
+ :* "y_string_simple_ascii"
+ '["asd "]'
+ [%a ~[[%s 'asd ']]]
+ ==
+ :* "y_string_space"
+ '" "'
+ [%s ' ']
+ ==
+ :* "y_string_three-byte-utf-8"
+ '["\\u0821"]'
+ [%a ~[[%s (from-code-point 0x821)]]]
+ ==
+ :* "y_string_two-byte-utf-8"
+ '["\\u0123"]'
+ [%a ~[[%s (from-code-point 0x123)]]]
+ ==
+ :* "y_string_u+2028_line_sep"
+ '["
"]'
+ [%a ~[[%s (from-code-point 0x2028)]]]
+ ==
+ :* "y_string_u+2029_par_sep"
+ '["
"]'
+ [%a ~[[%s (from-code-point 0x2029)]]]
+ ==
+ :* "y_string_unicode_2"
+ '["⍂㈴⍂"]'
+ [%a ~[[%s '⍂㈴⍂']]]
+ ==
+ :* "y_string_unicode_U+2064_invisible_plus"
+ '["\\u2064"]'
+ [%a ~[[%s (from-code-point 0x2064)]]]
+ ==
+ :* "y_string_unicode_escaped_double_quote"
+ '["\\u0022"]'
+ [%a ~[[%s (from-code-point 0x22)]]]
+ ==
+ :* "y_string_utf8"
+ '["€𝄞"]'
+ [%a ~[[%s '€𝄞']]]
+ ==
+ :* "y_structure_lonely_false"
+ 'false'
+ [%b |]
+ ==
+ :* "y_structure_lonely_int"
+ '42'
+ [%n '42']
+ ==
+ :* "y_structure_lonely_negative_real"
+ '-0.1'
+ [%n '-0.1']
+ ==
+ :* "y_structure_lonely_null"
+ 'null'
+ ~
+ ==
+ :* "y_structure_lonely_string"
+ '"asd"'
+ [%s 'asd']
+ ==
+ :* "y_structure_lonely_true"
+ 'true'
+ [%b &]
+ ==
+ :* "y_structure_string_empty"
+ '""'
+ [%s '']
+ ==
+ :* "y_structure_trailing_newline"
+ '["a"]\0a'
+ [%a ~[[%s 'a']]]
+ ==
+ :* "y_structure_true_in_array"
+ '[true]'
+ [%a ~[[%b &]]]
+ ==
+ :* "y_structure_whitespace_array"
+ ' [] '
+ [%a ~]
+ ==
+ ==
+:: TODO: de-json is rejecting or dropping unicode escape sequences
+::
+:: Tracking issue here: https://github.com/urbit/urbit/issues/1776
+:: Re-enable this test by removing the disable- prefix
+::
+++ disable-test-parse-unicode-escape-sequences
+ =+ frond=frond:enjs:format
+ =+ pairs=pairs:enjs:format
+ %- run-parse-specs
+ :~
+ :* "y_string_with_del_character"
+ '["a\7fa"]'
+ [%a ~[[%s 'a\7fa']]]
+ ==
+ :* "y_string_unicode_U+FDD0_nonchar"
+ '["\\uFDD0"]'
+ [%a ~[[%s (from-code-point 0xfdd0)]]]
+ ==
+ :* "y_string_unicode_U+FFFE_nonchar"
+ '["\\uFFFE"]'
+ [%a ~[[%s (from-code-point 0xfffe)]]]
+ ==
+ :* "y_string_unicode_U+10FFFE_nonchar"
+ '["\\uDBFF\\uDFFE"]'
+ [%a ~[[%s (crip (from-code-points ~[0xdbff 0xdffe]))]]]
+ ==
+ :* "y_string_unicode_U+1FFFE_nonchar"
+ '["\\uD83F\\uDFFE"]'
+ [%a ~[[%s (crip (from-code-points ~[0xd83f 0xdffe]))]]]
+ ==
+ :* "y_string_unicode_U+200B_ZERO_WIDTH_SPACE"
+ '["\\u200B"]'
+ [%a ~[[%s (from-code-point 0x200b)]]]
+ ==
+ :* "y_string_uEscape"
+ '["\\u0061\\u30af\\u30EA\\u30b9"]'
+ [%a ~[[%s (crip (from-code-points ~[0x61 0x30af 0x30ea 0x30b9]))]]]
+ ==
+ :* "y_string_uescaped_newline"
+ '["new\\u000Aline"]'
+ [%a ~[[%s 'new\0aline']]]
+ ==
+ :* "y_string_unescaped_char_delete"
+ '["\7f"]'
+ [%a ~[[%s '\7f']]]
+ ==
+ :* "y_string_unicode"
+ '["\\uA66D"]'
+ [%a ~[[%s (from-code-point 0xa66d)]]]
+ ==
+ :* "y_string_unicodeEscapedBackslash"
+ '["\\u005C"]'
+ [%a ~[[%s (from-code-point 0x5c)]]]
+ ==
+ :* "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF"
+ '["\\uD834\\uDd1e"]'
+ [%a ~[[%s (crip (from-code-points ~[0xd834 0xdd1e]))]]]
+ ==
+ :* "y_string_last_surrogates_1_and_2"
+ '["\\uDBFF\\uDFFF"]'
+ [%a ~[[%s (crip (from-code-points ~[0xdbff 0xdfff]))]]]
+ ==
+ :* "y_string_nbsp_uescaped"
+ '["new\\u00A0line"]'
+ [%a ~[[%s (crip "new{(from-code-points ~[0xa0])}line")]]]
+ ==
+ :* "y_string_escaped_noncharacter"
+ '["\\uFFFF"]'
+ [%a ~[[%s (from-code-point 0xffff)]]]
+ ==
+ :* "y_string_escaped_null"
+ '"foo\\u0000bar"'
+ [%s 'foo\00bar']
+ ==
+ :* "y_object_escaped_null_in_key"
+ '{"foo\\u0000bar": 42}'
+ (frond ['foo\00bar' [%n '42']])
+ ==
+ :* "y_string_1_2_3_bytes_UTF-8_sequences"
+ '["\\u0060\\u012a\\u12AB"]'
+ [%a ~[[%s '`Īካ']]]
+ ==
+ :* "y_string_accepted_surrogate_pair"
+ '["\\uD801\\udc37"]'
+ [%a ~[[%s '𐐷']]]
+ ==
+ :* "y_string_accepted_surrogate_pairs"
+ '["\\ud83d\\ude39\\ud83d\\udc8d"]'
+ [%a ~[[%s '😹💍']]]
+ ==
+ ==
--