Merge branch 'status' into fast-jets

This commit is contained in:
Alex Shelkovnykov 2023-10-13 10:09:07 -06:00
commit 9f147bcb2c
27 changed files with 4488 additions and 1609 deletions

View File

@ -36,7 +36,7 @@ jobs:
-A clippy::missing_safety_doc
- name: Build
run: cargo build --release --verbose
run: cargo build --release --verbose --features check_all
- name: Run tests
run: cargo test --verbose -- --test-threads=1

View File

@ -1,7 +1,6 @@
:: A small pill that runs a snasphot of azimuth state against a few
:: tens of thousands of logs. Requires naive-cradle.hoon,
:: mainnet.azimuth-snapshot, and log.jam from this scaffolding
:: directory.
:: A small pill that runs a snasphot of azimuth state against a few tens of
:: thousands of logs. Requires naive-cradle.hoon,
:: mainnet.azimuth-snapshot, and log.jam from this scaffolding directory.
::
/+ naive=naive-cradle, orig-naive=naive, ethereum, dice
/* snap %azimuth-snapshot /lib/mainnet/azimuth-snapshot
@ -236,7 +235,6 @@
++ swp :: naive rev bloq order
~/ %swp
|= [a=bloq b=@]
~> %sham.%swp
(rep a (flop (rip a b)))
::
++ met :: measure
@ -312,7 +310,6 @@
++ lent :: length
~/ %lent
|= a=(list)
~> %sham.%lent
^- @
=+ b=0
|-
@ -322,7 +319,6 @@
++ slag :: suffix
~/ %slag
|* [a=@ b=(list)]
~> %sham.%slag
|- ^+ b
?: =(0 a) b
?~ b ~
@ -331,7 +327,6 @@
++ snag :: index
~/ %snag
|* [a=@ b=(list)]
~> %sham.%snag
|- ^+ ?>(?=(^ b) i.b)
?~ b
~_ leaf+"snag-fail"
@ -349,7 +344,6 @@
++ flop :: reverse
~/ %flop
|* a=(list)
~> %sham.%flop
=> .(a (homo a))
^+ a
=+ b=`_a`~
@ -360,7 +354,6 @@
++ welp :: concatenate
~/ %welp
=| [* *]
~> %sham.%welp
|@
++ $
?~ +<-
@ -371,7 +364,6 @@
++ reap :: replicate
~/ %reap
|* [a=@ b=*]
~> %sham.%reap
|- ^- (list _b)
?~ a ~
[b $(a (dec a))]

View File

@ -1,4 +1,6 @@
:: A trivial working pill which requires no jets
:: A trivial working pill which requires no jets. Requires cradle.hoon
:: from this scaffolding directory.
::
/+ cradle
!.
=/ core

View File

@ -1,3 +1,6 @@
:: This file is just tiny.hoon from urbit/urbit (but with sham jets). Required
:: to run baby.hoon as an Arvo.
::
!.
=> %a50
~% %a.50 ~ ~
@ -206,7 +209,6 @@
++ swp :: naive rev bloq order
~/ %swp
|= [a=bloq b=@]
~> %sham.%swp
(rep a (flop (rip a b)))
::
++ met :: measure
@ -282,7 +284,6 @@
++ lent :: length
~/ %lent
|= a=(list)
~> %sham.%lent
^- @
=+ b=0
|-
@ -292,7 +293,6 @@
++ slag :: suffix
~/ %slag
|* [a=@ b=(list)]
~> %sham.%slag
|- ^+ b
?: =(0 a) b
?~ b ~
@ -301,7 +301,6 @@
++ snag :: index
~/ %snag
|* [a=@ b=(list)]
~> %sham.%snag
|- ^+ ?>(?=(^ b) i.b)
?~ b
~_ leaf+"snag-fail"
@ -319,7 +318,6 @@
++ flop :: reverse
~/ %flop
|* a=(list)
~> %sham.%flop
=> .(a (homo a))
^+ a
=+ b=`_a`~
@ -330,7 +328,6 @@
++ welp :: concatenate
~/ %welp
=| [* *]
~> %sham.%welp
|@
++ $
?~ +<-
@ -341,7 +338,6 @@
++ reap :: replicate
~/ %reap
|* [a=@ b=*]
~> %sham.%reap
|- ^- (list _b)
?~ a ~
[b $(a (dec a))]

View File

@ -1,6 +1,9 @@
:: This file is a combination of tiny.hoon and naive.hoon from urbit/urbit
:: (but with sham jets). Required to run azimuth-pill.hoon as an Arvo.
::
!.
!=
:: begin cradle.hoon
:: begin tiny.hoon
=>
=> %a50
~% %a.50 ~ ~
@ -209,7 +212,6 @@
++ swp :: naive rev bloq order
~/ %swp
|= [a=bloq b=@]
~> %sham.%swp
(rep a (flop (rip a b)))
::
++ met :: measure
@ -285,7 +287,6 @@
++ lent :: length
~/ %lent
|= a=(list)
~> %sham.%lent
^- @
=+ b=0
|-
@ -295,7 +296,6 @@
++ slag :: suffix
~/ %slag
|* [a=@ b=(list)]
~> %sham.%slag
|- ^+ b
?: =(0 a) b
?~ b ~
@ -304,7 +304,6 @@
++ snag :: index
~/ %snag
|* [a=@ b=(list)]
~> %sham.%snag
|- ^+ ?>(?=(^ b) i.b)
?~ b
~_ leaf+"snag-fail"
@ -322,7 +321,6 @@
++ flop :: reverse
~/ %flop
|* a=(list)
~> %sham.%flop
=> .(a (homo a))
^+ a
=+ b=`_a`~
@ -333,7 +331,6 @@
++ welp :: concatenate
~/ %welp
=| [* *]
~> %sham.%welp
|@
++ $
?~ +<-
@ -344,7 +341,6 @@
++ reap :: replicate
~/ %reap
|* [a=@ b=*]
~> %sham.%reap
|- ^- (list _b)
?~ a ~
[b $(a (dec a))]

View File

@ -0,0 +1,874 @@
:: This file aims to approach hoon.hoon, as the pieces necessary to run a live
:: ship with Ares as Serf are written. Required to run toddler.hoon as an Arvo.
::
!.
=> %a50
~% %a.50 ~ ~
|%
::
:: Types
::
+$ cord @t
+$ knot @ta
+$ term @tas
+$ char @t
+$ ship @p
+$ life @ud
+$ rift @ud
+$ pass @
+$ bloq @
+$ step _`@u`1
+$ bite $@(bloq [=bloq =step])
+$ octs [p=@ud q=@]
+$ dime [p=@ta q=@]
+$ pint [p=[p=@ q=@] q=[p=@ q=@]]
+$ spot [p=path q=pint]
+$ mold $~(* $-(* *))
++ unit |$ [item] $@(~ [~ u=item])
++ list |$ [item] $@(~ [i=item t=(list item)])
++ lest |$ [item] [i=item t=(list item)]
+$ tape (list @tD)
+$ path (list knot)
+$ coin $~ [%$ %ud 0]
$% [%$ p=dime]
[%blob p=*]
[%many p=(list coin)]
==
+$ tone $% [%0 product=*]
[%1 block=*]
[%2 trace=(list [@ta *])]
==
++ pair
|$ [head tail]
[p=head q=tail]
++ trel
|$ [first second third]
[p=first q=second r=third]
++ qual
|$ [first second third fourth]
[p=first q=second r=third s=fourth]
+$ tank
$+ tank
$~ leaf/~
$@ cord
$% [%leaf p=tape]
[%palm p=(qual tape tape tape tape) q=(list tank)]
[%rose p=(trel tape tape tape) q=(list tank)]
==
+$ tang (list tank)
+$ toon $% [%0 p=*]
[%1 p=*]
[%2 p=tang]
==
++ tree |$ [node] $@(~ [n=node l=(tree node) r=(tree node)])
++ gate $-(* *)
::
++ map
|$ [key value]
$| (tree (pair key value))
|=(a=(tree (pair)) ?:(=(~ a) & ~(apt by a)))
::
++ set
|$ [item]
$| (tree item)
|=(a=(tree) ?:(=(~ a) & ~(apt in a)))
::
++ jug |$ [key value] (map key (set value))
::
:: Basic arithmetic
::
++ add :: unsigned addition
~/ %add
|= [a=@ b=@]
~> %sham.%add
^- @
?: =(0 a) b
$(a (dec a), b +(b))
::
++ dec :: decrement
~/ %dec
|= a=@
~> %sham.%dec
~_ leaf+"decrement-underflow"
?< =(0 a)
=+ b=0
|- ^- @
?: =(a +(b)) b
$(b +(b))
::
++ div :: divide
~/ %div
|: [a=`@`1 b=`@`1]
~> %sham.%div
^- @
~_ leaf+"divide-by-zero"
?< =(0 b)
=+ c=0
|-
?: (lth a b) c
$(a (sub a b), c +(c))
::
++ dvr :: divide w/remainder
~/ %dvr
|: [a=`@`1 b=`@`1]
~> %sham.%dvr
^- [p=@ q=@]
[(div a b) (mod a b)]
::
++ gte :: greater or equal
~/ %gte
|= [a=@ b=@]
~> %sham.%gte
^- ?
!(lth a b)
::
++ gth :: greater
~/ %gth
|= [a=@ b=@]
~> %sham.%gth
^- ?
!(lte a b)
::
++ lte :: less or equal
~/ %lte
|= [a=@ b=@]
~> %sham.%lte
|(=(a b) (lth a b))
::
++ lth :: less
~/ %lth
|= [a=@ b=@]
~> %sham.%lth
^- ?
?& !=(a b)
|-
?| =(0 a)
?& !=(0 b)
$(a (dec a), b (dec b))
== == ==
::
++ mod :: modulus
~/ %mod
|: [a=`@`1 b=`@`1]
~> %sham.%mod
^- @
?< =(0 b)
(sub a (mul b (div a b)))
::
++ mul :: multiply
~/ %mul
|: [a=`@`1 b=`@`1]
~> %sham.%mul
^- @
=+ c=0
|-
?: =(0 a) c
$(a (dec a), c (add b c))
::
++ sub :: subtract
~/ %sub
|= [a=@ b=@]
~> %sham.%sub
~_ leaf+"subtract-underflow"
:: difference
^- @
?: =(0 b) a
$(a (dec a), b (dec b))
::
:: Tree addressing
::
++ cap :: index in head or tail
~/ %cap
|= a=@
~> %sham.%cap
^- ?(%2 %3)
?- a
%2 %2
%3 %3
?(%0 %1) !!
* $(a (div a 2))
==
::
++ mas :: axis within head/tail
~/ %mas
|= a=@
~> %sham.%mas
^- @
?- a
?(%2 %3) 1
?(%0 %1) !!
* (add (mod a 2) (mul $(a (div a 2)) 2))
==
::
:: List logic
::
++ snoc :: append to end of list
|* [a=(list) b=*]
(weld a ^+(a [b]~))
::
++ flop :: reverse
~/ %flop
|* a=(list)
=> .(a (homo a))
^+ a
=+ b=`_a`~
|-
?~ a b
$(a t.a, b [i.a b])
::
++ homo :: homogenize
|* a=(list)
^+ =< $
|@ ++ $ ?:(*? ~ [i=(snag 0 a) t=$])
--
a
::
++ lent :: length
~/ %lent
|= a=(list)
^- @
=+ b=0
|-
?~ a b
$(a t.a, b +(b))
::
++ reap :: replicate
~/ %reap
|* [a=@ b=*]
|- ^- (list _b)
?~ a ~
[b $(a (dec a))]
::
++ roll :: left fold
~/ %roll
|* [a=(list) b=_=>(~ |=([* *] +<+))]
|- ^+ ,.+<+.b
?~ a
+<+.b
$(a t.a, b b(+<+ (b i.a +<+.b)))
::
++ scag :: prefix
~/ %scag
|* [a=@ b=(list)]
|- ^+ b
?: |(?=(~ b) =(0 a)) ~
[i.b $(b t.b, a (dec a))]
::
++ slag :: suffix
~/ %slag
|* [a=@ b=(list)]
|- ^+ b
?: =(0 a) b
?~ b ~
$(b t.b, a (dec a))
::
++ snag :: index
~/ %snag
|* [a=@ b=(list)]
|- ^+ ?>(?=(^ b) i.b)
?~ b
~_ leaf+"snag-fail"
!!
?: =(0 a) i.b
$(b t.b, a (dec a))
::
++ turn :: transform
~/ %turn
|* [a=(list) b=gate]
=> .(a (homo a))
^- (list _?>(?=(^ a) (b i.a)))
|-
?~ a ~
[i=(b i.a) t=$(a t.a)]
::
++ weld :: concatenate
~/ %weld
|* [a=(list) b=(list)]
=> .(a ^.(homo a), b ^.(homo b))
|- ^+ b
?~ a b
[i.a $(a t.a)]
::
++ welp :: concatenate
~/ %welp
=| [* *]
|@
++ $
?~ +<-
+<-(. +<+)
+<-(+ $(+<- +<->))
--
::
:: Bit arithmetic
::
++ bex :: binary exponent
~/ %bex
|= a=bloq
~> %sham.%bex
^- @
?: =(0 a) 1
(mul 2 $(a (dec a)))
++ can :: assemble
~/ %can
|= [a=bloq b=(list [p=step q=@])]
~> %sham.%can
^- @
?~ b 0
(add (end [a p.i.b] q.i.b) (lsh [a p.i.b] $(b t.b)))
::
++ cat :: concatenate
~/ %cat
|= [a=bloq b=@ c=@]
~> %sham.%cat
(add (lsh [a (met a b)] c) b)
::
++ cut :: slice
~/ %cut
|= [a=bloq [b=step c=step] d=@]
~> %sham.%cut
(end [a c] (rsh [a b] d))
::
++ end :: tail
~/ %end
|= [a=bite b=@]
~> %sham.%end
=/ [=bloq =step] ?^(a a [a *step])
(mod b (bex (mul (bex bloq) step)))
::
++ fil :: fill bloqstream
~/ %fil
|= [a=bloq b=step c=@]
=| n=@ud
=. c (end a c)
=/ d c
|- ^- @
?: =(n b)
(rsh a d)
$(d (add c (lsh a d)), n +(n))
::
++ lsh :: left-shift
~/ %lsh
|= [a=bite b=@]
~> %sham.%lsh
=/ [=bloq =step] ?^(a a [a *step])
(mul b (bex (mul (bex bloq) step)))
::
++ met :: measure
~/ %met
|= [a=bloq b=@]
~> %sham.%met
^- @
=+ c=0
|-
?: =(0 b) c
$(b (rsh a b), c +(c))
::
++ rap :: assemble variable
~/ %rap
|= [a=bloq b=(list @)]
~> %sham.%rap
^- @
?~ b 0
(cat a i.b $(b t.b))
::
++ rep :: assemble fixed
~/ %rep
|= [a=bite b=(list @)]
~> %sham.%rep
=/ [=bloq =step] ?^(a a [a *step])
=| i=@ud
|- ^- @
?~ b 0
%+ add $(i +(i), b t.b)
(lsh [bloq (mul step i)] (end [bloq step] i.b))
::
++ rev :: reverse block order
~/ %rev
|= [boz=bloq len=@ud dat=@]
~> %sham.%rev
^- @
=. dat (end [boz len] dat)
%+ lsh
[boz (sub len (met boz dat))]
(swp boz dat)
::
++ rip :: disassemble
~/ %rip
|= [a=bite b=@]
~> %sham.%rip
^- (list @)
?: =(0 b) ~
[(end a b) $(b (rsh a b))]
::
++ rsh :: right-shift
~/ %rsh
|= [a=bite b=@]
~> %sham.%rsh
=/ [=bloq =step] ?^(a a [a *step])
(div b (bex (mul (bex bloq) step)))
::
++ swp :: naive rev bloq order
~/ %swp
|= [a=bloq b=@]
(rep a (flop (rip a b)))
::
:: Modular arithmetic
::
++ fe :: modulo bloq
|_ a=bloq
++ dif :: difference
|=([b=@ c=@] (sit (sub (add out (sit b)) (sit c))))
++ inv |=(b=@ (sub (dec out) (sit b))) :: inverse
++ net |= b=@ ^- @ :: flip byte endianness
=> .(b (sit b))
?: (lte a 3)
b
=+ c=(dec a)
%+ con
(lsh c $(a c, b (cut c [0 1] b)))
$(a c, b (cut c [1 1] b))
++ out (bex (bex a)) :: mod value
++ rol |= [b=bloq c=@ d=@] ^- @ :: roll left
=+ e=(sit d)
=+ f=(bex (sub a b))
=+ g=(mod c f)
(sit (con (lsh [b g] e) (rsh [b (sub f g)] e)))
++ ror |= [b=bloq c=@ d=@] ^- @ :: roll right
=+ e=(sit d)
=+ f=(bex (sub a b))
=+ g=(mod c f)
(sit (con (rsh [b g] e) (lsh [b (sub f g)] e)))
++ sum |=([b=@ c=@] (sit (add b c))) :: wrapping add
++ sit |=(b=@ (end a b)) :: enforce modulo
--
::
:: Bit logic
::
++ con :: binary or
~/ %con
|= [a=@ b=@]
~> %sham.%con
=+ [c=0 d=0]
|- ^- @
?: ?&(=(0 a) =(0 b)) d
%= $
a (rsh 0 a)
b (rsh 0 b)
c +(c)
d %+ add d
%+ lsh [0 c]
?& =(0 (end 0 a))
=(0 (end 0 b))
==
==
::
++ dis :: binary and
~/ %dis
|= [a=@ b=@]
~> %sham.%dis
=| [c=@ d=@]
|- ^- @
?: ?|(=(0 a) =(0 b)) d
%= $
a (rsh 0 a)
b (rsh 0 b)
c +(c)
d %+ add d
%+ lsh [0 c]
?| =(0 (end 0 a))
=(0 (end 0 b))
==
==
::
++ mix :: binary xor
~/ %mix
|= [a=@ b=@]
~> %sham.%mix
^- @
=+ [c=0 d=0]
|-
?: ?&(=(0 a) =(0 b)) d
%= $
a (rsh 0 a)
b (rsh 0 b)
c +(c)
d (add d (lsh [0 c] =((end 0 a) (end 0 b))))
==
::
:: Hashes
::
++ mug :: mug with murmur3
~/ %mug
|= a=*
~> %sham.%mug
|^ ?@ a (mum 0xcafe.babe 0x7fff a)
=/ b (cat 5 $(a -.a) $(a +.a))
(mum 0xdead.beef 0xfffe b)
::
++ mum
|= [syd=@uxF fal=@F key=@]
=/ wyd (met 3 key)
=| i=@ud
|- ^- @F
?: =(8 i) fal
=/ haz=@F (muk syd wyd key)
=/ ham=@F (mix (rsh [0 31] haz) (end [0 31] haz))
?.(=(0 ham) ham $(i +(i), syd +(syd)))
--
::
++ muk :: standard murmur3
~% %muk ..muk ~
=+ ~(. fe 5)
|= [syd=@ len=@ key=@]
=. syd (end 5 syd)
=/ pad (sub len (met 3 key))
=/ data (welp (rip 3 key) (reap pad 0))
=/ nblocks (div len 4) :: intentionally off-by-one
=/ h1 syd
=+ [c1=0xcc9e.2d51 c2=0x1b87.3593]
=/ blocks (rip 5 key)
=/ i nblocks
=. h1 =/ hi h1 |-
?: =(0 i) hi
=/ k1 (snag (sub nblocks i) blocks) :: negative array index
=. k1 (sit (mul k1 c1))
=. k1 (rol 0 15 k1)
=. k1 (sit (mul k1 c2))
=. hi (mix hi k1)
=. hi (rol 0 13 hi)
=. hi (sum (sit (mul hi 5)) 0xe654.6b64)
$(i (dec i))
=/ tail (slag (mul 4 nblocks) data)
=/ k1 0
=/ tlen (dis len 3)
=. h1
?+ tlen h1 :: fallthrough switch
%3 =. k1 (mix k1 (lsh [0 16] (snag 2 tail)))
=. k1 (mix k1 (lsh [0 8] (snag 1 tail)))
=. k1 (mix k1 (snag 0 tail))
=. k1 (sit (mul k1 c1))
=. k1 (rol 0 15 k1)
=. k1 (sit (mul k1 c2))
(mix h1 k1)
%2 =. k1 (mix k1 (lsh [0 8] (snag 1 tail)))
=. k1 (mix k1 (snag 0 tail))
=. k1 (sit (mul k1 c1))
=. k1 (rol 0 15 k1)
=. k1 (sit (mul k1 c2))
(mix h1 k1)
%1 =. k1 (mix k1 (snag 0 tail))
=. k1 (sit (mul k1 c1))
=. k1 (rol 0 15 k1)
=. k1 (sit (mul k1 c2))
(mix h1 k1)
==
=. h1 (mix h1 len)
|^ (fmix32 h1)
++ fmix32
|= h=@
=. h (mix h (rsh [0 16] h))
=. h (sit (mul h 0x85eb.ca6b))
=. h (mix h (rsh [0 13] h))
=. h (sit (mul h 0xc2b2.ae35))
=. h (mix h (rsh [0 16] h))
h
--
::
:: Noun Ordering
::
++ dor :: tree order
~/ %dor
|= [a=* b=*]
~> %sham.%dor
^- ?
?: =(a b) &
?. ?=(@ a)
?: ?=(@ b) |
?: =(-.a -.b)
$(a +.a, b +.b)
$(a -.a, b -.b)
?. ?=(@ b) &
(lth a b)
::
++ gor :: mug order
~/ %gor
|= [a=* b=*]
~> %sham.%gor
^- ?
=+ [c=(mug a) d=(mug b)]
?: =(c d)
(dor a b)
(lth c d)
::
++ mor :: more mug order
~/ %mor
|= [a=* b=*]
~> %sham.%mor
^- ?
=+ [c=(mug (mug a)) d=(mug (mug b))]
?: =(c d)
(dor a b)
(lth c d)
::
:: Unsigned powers
::
++ pow :: unsigned exponent
~/ %pow
|= [a=@ b=@]
?: =(b 0) 1
|- ?: =(b 1) a
=+ c=$(b (div b 2))
=+ d=(mul c c)
?~ (dis b 1) d (mul d a)
::
:: Set logic
::
++ in
~/ %in
=| a=(tree) :: (set)
~> %sham.%in
|@
++ apt
=< $
~/ %apt
=| [l=(unit) r=(unit)]
~> %sham.%apt
|. ^- ?
?~ a &
?& ?~(l & (gor n.a u.l))
?~(r & (gor u.r n.a))
?~(l.a & ?&((mor n.a n.l.a) $(a l.a, l `n.a)))
?~(r.a & ?&((mor n.a n.r.a) $(a r.a, r `n.a)))
==
::
++ del
~/ %del
|* b=*
~> %sham.%del
|- ^+ a
?~ a
~
?. =(b n.a)
?: (gor b n.a)
a(l $(a l.a))
a(r $(a r.a))
|- ^- [$?(~ _a)]
?~ l.a r.a
?~ r.a l.a
?: (mor n.l.a n.r.a)
l.a(r $(l.a r.l.a))
r.a(l $(r.a l.r.a))
::
++ put
~/ %put
|* b=*
~> %sham.%put
|- ^+ a
?~ a
[b ~ ~]
?: =(b n.a)
a
?: (gor b n.a)
=+ c=$(a l.a)
?> ?=(^ c)
?: (mor n.a n.c)
a(l c)
c(r a(l r.c))
=+ c=$(a r.a)
?> ?=(^ c)
?: (mor n.a n.c)
a(r c)
c(l a(r l.c))
--
::
:: Map logic
::
++ by
~/ %by
=| a=(tree (pair)) :: (map)
~> %sham.%by
=* node ?>(?=(^ a) n.a)
|@
++ del
~/ %del
|* b=*
~> %sham.%del
|- ^+ a
?~ a
~
?. =(b p.n.a)
?: (gor b p.n.a)
a(l $(a l.a))
a(r $(a r.a))
|- ^- [$?(~ _a)]
?~ l.a r.a
?~ r.a l.a
?: (mor p.n.l.a p.n.r.a)
l.a(r $(l.a r.l.a))
r.a(l $(r.a l.r.a))
::
++ apt
=< $
~/ %apt
=| [l=(unit) r=(unit)]
~> %sham.%apt
|. ^- ?
?~ a &
?& ?~(l & &((gor p.n.a u.l) !=(p.n.a u.l)))
?~(r & &((gor u.r p.n.a) !=(u.r p.n.a)))
?~ l.a &
&((mor p.n.a p.n.l.a) !=(p.n.a p.n.l.a) $(a l.a, l `p.n.a))
?~ r.a &
&((mor p.n.a p.n.r.a) !=(p.n.a p.n.r.a) $(a r.a, r `p.n.a))
==
::
++ get
~/ %get
|* b=*
~> %sham.%get
=> .(b `_?>(?=(^ a) p.n.a)`b)
|- ^- (unit _?>(?=(^ a) q.n.a))
?~ a
~
?: =(b p.n.a)
`q.n.a
?: (gor b p.n.a)
$(a l.a)
$(a r.a)
::
++ put
~/ %put
|* [b=* c=*]
~> %sham.%put
|- ^+ a
?~ a
[[b c] ~ ~]
?: =(b p.n.a)
?: =(c q.n.a)
a
a(n [b c])
?: (gor b p.n.a)
=+ d=$(a l.a)
?> ?=(^ d)
?: (mor p.n.a p.n.d)
a(l d)
d(r a(l r.d))
=+ d=$(a r.a)
?> ?=(^ d)
?: (mor p.n.a p.n.d)
a(r d)
d(l a(r l.d))
--
::
:: Jug logic
::
++ ju
=| a=(tree (pair * (tree))) :: (jug)
|@
++ del
|* [b=* c=*]
^+ a
=+ d=(get b)
=+ e=(~(del in d) c)
?~ e
(~(del by a) b)
(~(put by a) b e)
::
++ get
|* b=*
=+ c=(~(get by a) b)
?~(c ~ u.c)
::
++ put
|* [b=* c=*]
^+ a
=+ d=(get b)
(~(put by a) b (~(put in d) c))
--
::
:: Parsing
::
++ ne
|_ tig=@
++ d (add tig '0')
--
::
:: Atom printing
::
++ co
!:
~% %co ..co ~
=< |_ lot=coin
++ rend
^- tape
~+
?. ?=(%$ -.lot) !!
=+ [yed=(end 3 p.p.lot) hay=(cut 3 [1 1] p.p.lot)]
|- ^- tape
?+ yed !!
%ud
::
=/ gam=tape
((ox-co [10 3] |=(a=@ ~(d ne a))) q.p.lot)
?: =(0 q.p.lot)
['0' ~]
gam
==
--
=| rep=tape
|%
:: +em-co: format in numeric base
::
:: in .bas, format .min digits of .hol with .par
::
:: - .hol is processed least-significant digit first
:: - all available digits in .hol will be processed, but
:: .min digits can exceed the number available in .hol
:: - .par handles all accumulated output on each call,
:: and can edit it, prepend or append digits, &c
:: - until .hol is exhausted, .par's sample is [| digit output],
:: subsequently, it's [& 0 output]
::
++ em-co
|= [[bas=@ min=@] par=$-([? @ tape] tape)]
|= hol=@
^- tape
?: &(=(0 hol) =(0 min))
rep
=/ [dar=@ rad=@] (dvr hol bas)
%= $
min ?:(=(0 min) 0 (dec min))
hol dar
rep (par =(0 dar) rad rep)
==
::
:: +ox-co: format '.'-separated digit sequences in numeric base
::
:: in .bas, format each digit of .hol with .dug,
:: with '.' separators every .gop digits.
::
:: - .hol is processed least-significant digit first
:: - .dug handles individual digits, output is prepended
:: - every segment but the last is zero-padded to .gop
::
++ ox-co
|= [[bas=@ gop=@] dug=$-(@ @)]
%+ em-co
[(pow bas gop) 0]
|= [top=? seg=@ res=tape]
%+ weld
?:(top ~ `tape`['.' ~])
%. seg
%+ em-co(rep res)
[bas ?:(top 0 gop)]
|=([? b=@ c=tape] [(dug b) c])
--
::
:: Formatting functions
::
++ scow
~/ %scow
|= mol=dime
~> %sham.%scow
~(rend co %$ mol)
--

View File

@ -0,0 +1,98 @@
:: A working pill that grows towards arvo.hoon. Requires playpen.hoon from this
:: scaffolding directory.
::
/+ playpen
!.
=/ core
=> playpen
!=
=>
|%
+$ card (cask)
++ cask |$ [a] (pair mark a)
+$ goof [mote=term =tang]
+$ mark @tas
+$ ovum [=wire =card]
+$ vere [[non=@ta rev=path] kel=wynn]
+$ wire path
+$ wasp
:: %crud: reroute $ovum with $goof
:: %wack: iterate entropy
:: %wyrd: check/record runtime kelvin stack
::
$% [%crud =goof =ovum]
[%wack p=@uvJ]
[%wyrd p=vere]
==
+$ weft [lal=@tas num=@ud]
+$ wynn (list weft)
:: mutually recursive Ackermann functions
:: test turning %spot hints on/off
++ ack
:: re-enable %spot hints
!:
|= [m=@ud n=@ud]
:: %memo hint
~+
?~ m +(n)
?~ n
(ack (dec m) 1)
(ack (dec m) $(n (dec n)))
++ slow
|= x=@ud
!:
(slow-help x 0)
++ slow-help
|= [x=@ud y=@ud]
!.
?: .= x y
x
$(y .+(y))
-- =>
::
|%
++ load !!
++ peek _~
++ wish !!
++ poke
|= [now=@da ovo=ovum]
^- ^
::
?. ?=(?(%crud %wack %wyrd) p.card.ovo)
~> %slog.[0 leaf+(scow %ud (slow (bex 23)))]
[~ ..poke]
::
=/ buz
~> %mean.'pith: bad wasp'
;;(wasp card.ovo)
?+ -.buz
~> %slog.[0 leaf+(scow %ud (ack 2 1))]
[~ ..poke]
::
%crud
=/ tang tang.goof.buz
|-
?~ tang
[~ ..poke]
~> %slog.[0 -.tang]
$(tang +.tang)
==
--
::
|= [now=@da ovo=ovum]
^- *
.(+> +:(poke now ovo))
::
|%
++ aeon
^- *
=> *[arvo=* epic=*]
!=
|- ^- *
?@ epic arvo
%= $
epic +.epic
arvo .*([arvo -.epic] [%9 2 %10 [6 %0 3] %0 2])
==
--
[%pill %toddler [aeon .*(playpen core) ~] ~ ~]

29
rust/ares/Cargo.lock generated
View File

@ -20,11 +20,13 @@ dependencies = [
"either",
"ibig",
"intmap",
"lazy_static",
"libc",
"memmap",
"murmur3",
"num-derive",
"num-traits",
"signal-hook",
"static_assertions",
]
@ -230,9 +232,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.6.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "funty"
@ -378,9 +380,9 @@ dependencies = [
[[package]]
name = "num-traits"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
@ -569,6 +571,25 @@ dependencies = [
"serde",
]
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "static_assertions"
version = "1.1.0"

View File

@ -9,20 +9,25 @@ edition = "2018"
# [patch.crates-io]
# ibig = { path = "../ibig-rs" }
# Please keep these alphabetized
[dependencies]
ares_macros = { path = "../ares_macros" }
bitvec = "1.0.0"
either = "1.6.1"
libc = "0.2.126"
murmur3 = { git = "https://github.com/tloncorp/murmur3", rev = "7878a0f" }
memmap = "0.7.0"
intmap = "1.1.0"
num-traits = "0.2"
num-derive = "0.3"
criterion = "0.4"
static_assertions = "1.1.0"
ibig = { path = "../ibig-rs" }
assert_no_alloc = "1.1.2"
# use this when debugging requires allocation (e.g. eprintln)
# assert_no_alloc = {version="1.1.2", features=["warn_debug"]}
bitvec = "1.0.0"
criterion = "0.4"
either = "1.9.0"
ibig = { path = "../ibig-rs" }
intmap = "1.1.0"
lazy_static = "1.4.0"
libc = "0.2.126"
memmap = "0.7.0"
murmur3 = { git = "https://github.com/tloncorp/murmur3", rev = "7878a0f" }
num-derive = "0.3"
num-traits = "0.2"
signal-hook = "0.3"
static_assertions = "1.1.0"
[build-dependencies]
cc = "1.0.79"
@ -37,6 +42,10 @@ opt-level = 3
[profile.dev.package."*"]
opt-level = 3
# run with e.g. 'cargo build --features check_forwarding,check_acyclic'
[features]
check_all = [ "check_acyclic", "check_forwarding", "check_junior" ]
check_acyclic = []
check_forwarding = []
check_junior = []
sham_hints = []

View File

@ -104,7 +104,7 @@ impl<T: Copy> MutHamt<T> {
mug >>= 5;
match (*stem).entry(chunk) {
None => {
let new_leaf_buffer = stack.struct_alloc(1);
let new_leaf_buffer = stack.struct_alloc::<(Noun, T)>(1);
*new_leaf_buffer = (*n, t);
(*stem).bitmap |= chunk_to_bit(chunk);
(*stem).typemap &= !chunk_to_bit(chunk);
@ -524,7 +524,7 @@ impl<T: Copy + Preserve> Preserve for Hamt<T> {
};
*(stem.buffer.add(idx) as *mut Entry<T>) = Entry { stem: new_stem };
assert!(traversal_depth <= 5); // will increment
traversal_stack[traversal_depth - 1].unwrap().1 = position + 1;
traversal_stack[traversal_depth - 1] = Some((stem, position + 1));
traversal_stack[traversal_depth] = Some((new_stem, 0));
traversal_depth += 1;
continue 'preserve;

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,38 @@
pub mod cold;
pub mod hot;
pub mod math;
pub mod warm;
pub mod bits;
pub mod form;
pub mod hash;
pub mod math;
pub mod nock;
pub mod text;
pub mod tree;
use crate::interpreter::Context;
use crate::jets::bits::*;
use crate::jets::form::*;
use crate::jets::hash::*;
use crate::jets::math::*;
use crate::jets::nock::*;
use crate::jets::text::*;
use crate::jets::tree::*;
use crate::mem::NockStack;
use crate::noun::{self, Noun};
use crate::noun::{self, Noun, Slots};
use ares_macros::tas;
use std::cmp;
crate::gdb!();
/// Return Err if the computation crashed or should punt to Nock
pub type Jet = fn(&mut NockStack, Noun) -> Result<Noun, JetErr>;
pub type Result = std::result::Result<Noun, JetErr>;
pub type Jet = fn(&mut Context, Noun) -> Result;
/// Only return a deterministic error if the Nock would have deterministically crashed.
/**
* Only return a deterministic error if the Nock would have deterministically
* crashed.
*/
#[derive(Debug, PartialEq)]
pub enum JetErr {
Punt, // Retry with the raw nock
@ -21,15 +40,9 @@ pub enum JetErr {
NonDeterministic, // Other error
}
impl From<()> for JetErr {
fn from(_: ()) -> Self {
JetErr::NonDeterministic
}
}
impl From<noun::Error> for JetErr {
fn from(_err: noun::Error) -> Self {
Self::NonDeterministic
Self::Deterministic
}
}
@ -39,32 +52,45 @@ impl From<JetErr> for () {
pub fn get_jet(jet_name: Noun) -> Option<Jet> {
match jet_name.as_direct().ok()?.data() {
tas!(b"dec") => Some(jet_dec),
tas!(b"add") => Some(jet_add),
tas!(b"sub") => Some(jet_sub),
tas!(b"mul") => Some(jet_mul),
tas!(b"dec") => Some(jet_dec),
tas!(b"div") => Some(jet_div),
tas!(b"mod") => Some(jet_mod),
tas!(b"dvr") => Some(jet_dvr),
tas!(b"lth") => Some(jet_lth),
tas!(b"lte") => Some(jet_lte),
tas!(b"gth") => Some(jet_gth),
tas!(b"gte") => Some(jet_gte),
tas!(b"gth") => Some(jet_gth),
tas!(b"lte") => Some(jet_lte),
tas!(b"lth") => Some(jet_lth),
tas!(b"mod") => Some(jet_mod),
tas!(b"mul") => Some(jet_mul),
tas!(b"sub") => Some(jet_sub),
//
tas!(b"cap") => Some(jet_cap),
tas!(b"mas") => Some(jet_mas),
//
tas!(b"lent") => Some(jet_lent),
//
tas!(b"bex") => Some(jet_bex),
tas!(b"can") => Some(jet_can),
tas!(b"cat") => Some(jet_cat),
tas!(b"cut") => Some(jet_cut),
tas!(b"end") => Some(jet_end),
tas!(b"lsh") => Some(jet_lsh),
tas!(b"met") => Some(jet_met),
tas!(b"rap") => Some(jet_rap),
tas!(b"rep") => Some(jet_rep),
tas!(b"rev") => Some(jet_rev),
tas!(b"rip") => Some(jet_rip),
tas!(b"rsh") => Some(jet_rsh),
//
tas!(b"con") => Some(jet_con),
tas!(b"dis") => Some(jet_dis),
tas!(b"mix") => Some(jet_mix),
tas!(b"end") => Some(jet_end),
tas!(b"cat") => Some(jet_cat),
tas!(b"cut") => Some(jet_cut),
tas!(b"can") => Some(jet_can),
tas!(b"rep") => Some(jet_rep),
tas!(b"rip") => Some(jet_rip),
tas!(b"met") => Some(jet_met),
//
tas!(b"mug") => Some(jet_mug),
tas!(b"rev") => Some(jet_rev),
//
tas!(b"scow") => Some(jet_scow),
//
tas!(b"mink") => Some(jet_mink),
_ => {
// eprintln!("Unknown jet: {:?}", jet_name);
None
@ -81,3 +107,288 @@ pub fn get_jet_test_mode(_jet_name: Noun) -> bool {
*/
false
}
pub mod util {
use super::*;
use crate::noun::Error::NotRepresentable;
use crate::noun::{Atom, Cell, DirectAtom, IndirectAtom, Noun, D};
use bitvec::prelude::{BitSlice, Lsb0};
use ibig::UBig;
use std::result;
/**
* Currently, only addresses indexable by the first 48 bits are reachable by
* modern 64-bit CPUs.
*/
const MAX_BIT_LENGTH: usize = (1 << 47) - 1;
/// Performs addition that returns None on Noun size overflow
pub fn checked_add(a: usize, b: usize) -> result::Result<usize, JetErr> {
a.checked_add(b)
.filter(|x| x <= &MAX_BIT_LENGTH)
.ok_or(JetErr::NonDeterministic)
}
/// Performs addition that returns None on Noun size overflow
pub fn checked_sub(a: usize, b: usize) -> result::Result<usize, JetErr> {
a.checked_sub(b).ok_or(JetErr::NonDeterministic)
}
pub fn checked_left_shift(bloq: usize, a: usize) -> result::Result<usize, JetErr> {
let res = a << bloq;
// Catch overflow
if (res >> bloq) < a || res > MAX_BIT_LENGTH {
Err(JetErr::NonDeterministic)
} else {
Ok(res)
}
}
/// Convert length in bits to length in 64-bit words
pub fn bits_to_word(a: usize) -> result::Result<usize, JetErr> {
checked_add(a, 63).map(|x| x >> 6)
}
/// Convert length as bite to length in 64-bit words
pub fn bite_to_word(bloq: usize, step: usize) -> result::Result<usize, JetErr> {
bits_to_word(checked_left_shift(bloq, step)?)
}
pub fn slot(noun: Noun, axis: u64) -> Result {
noun.slot(axis).map_err(|_e| JetErr::Deterministic)
}
/// Extract a bloq and check that it's computable by the current system
pub fn bloq(a: Noun) -> result::Result<usize, JetErr> {
let bloq = a.as_direct()?.data() as usize;
if bloq >= 47 {
Err(JetErr::NonDeterministic)
} else {
Ok(bloq)
}
}
/// Extract the bloq and step from a bite
pub fn bite(a: Noun) -> result::Result<(usize, usize), JetErr> {
if let Ok(cell) = a.as_cell() {
let bloq = bloq(cell.head())?;
let step = cell.tail().as_direct()?.data() as usize;
Ok((bloq, step))
} else {
bloq(a).map(|x| (x, 1_usize))
}
}
/** In a bloq space, copy from `from` for a span of `step`, to position `to`.
*
* Note: unlike the vere version, this sets the bits instead of XORing
* them. If we need the XOR version, we could use ^=.
*/
pub unsafe fn chop(
bloq: usize,
from: usize,
step: usize,
to: usize,
dest: &mut BitSlice<u64, Lsb0>,
source: &BitSlice<u64, Lsb0>,
) -> result::Result<(), JetErr> {
let from_b = checked_left_shift(bloq, from)?;
let to_b = checked_left_shift(bloq, to)?;
let mut step_b = checked_left_shift(bloq, step)?;
let end_b = checked_add(from_b, step_b)?;
if from_b >= source.len() {
return Ok(());
}
if end_b > source.len() {
step_b -= end_b - source.len();
}
dest[to_b..to_b + step_b].copy_from_bitslice(&source[from_b..from_b + step_b]);
Ok(())
}
/// Subtraction
pub fn sub(stack: &mut NockStack, a: Atom, b: Atom) -> noun::Result<Atom> {
if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) {
let a_small = a.data();
let b_small = b.data();
if a_small < b_small {
Err(NotRepresentable)
} else {
Ok(Atom::new(stack, a_small - b_small))
}
} else {
let a_big = a.as_ubig(stack);
let b_big = b.as_ubig(stack);
if a_big < b_big {
Err(NotRepresentable)
} else {
let a_big = a.as_ubig(stack);
let b_big = b.as_ubig(stack);
let res = UBig::sub_stack(stack, a_big, b_big);
Ok(Atom::from_ubig(stack, &res))
}
}
}
/// Binary exponent
pub fn bex(stack: &mut NockStack, arg: usize) -> Atom {
unsafe {
if arg < 63 {
DirectAtom::new_unchecked(1 << arg).as_atom()
} else {
let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, (arg + 7) >> 3);
dest.set(arg, true);
atom.normalize_as_atom()
}
}
}
/// Measure the number of bloqs in an atom
pub fn met(bloq: usize, a: Atom) -> usize {
if unsafe { a.as_noun().raw_equals(D(0)) } {
0
} else if bloq < 6 {
(a.bit_size() + ((1 << bloq) - 1)) >> bloq
} else {
let bloq_word = bloq - 6;
(a.size() + ((1 << bloq_word) - 1)) >> bloq_word
}
}
pub fn rip(stack: &mut NockStack, bloq: usize, step: usize, atom: Atom) -> Result {
let len = (met(bloq, atom) + step - 1) / step;
let mut list = D(0);
for i in (0..len).rev() {
let new_atom = unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(stack, step << bloq);
chop(bloq, i * step, step, 0, new_slice, atom.as_bitslice())?;
new_indirect.normalize_as_atom()
};
list = Cell::new(stack, new_atom.as_noun(), list).as_noun();
}
Ok(list)
}
/// Binary OR
pub fn con(stack: &mut NockStack, a: Atom, b: Atom) -> Atom {
let new_size = cmp::max(a.size(), b.size());
unsafe {
let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, new_size);
let a_bit = a.as_bitslice();
dest[..a_bit.len()].copy_from_bitslice(a_bit);
*dest |= b.as_bitslice();
atom.normalize_as_atom()
}
}
pub mod test {
use super::*;
use crate::hamt::Hamt;
use crate::mem::{unifying_equality, NockStack};
use crate::noun::{Atom, Noun, D, T};
use assert_no_alloc::assert_no_alloc;
use ibig::UBig;
pub fn init_stack() -> NockStack {
NockStack::new(8 << 10 << 10, 0)
}
#[allow(non_snake_case)]
pub fn A(stack: &mut NockStack, ubig: &UBig) -> Noun {
Atom::from_ubig(stack, ubig).as_noun()
}
pub fn assert_noun_eq(stack: &mut NockStack, mut a: Noun, mut b: Noun) {
let eq = unsafe { unifying_equality(stack, &mut a, &mut b) };
assert!(eq, "got: {}, need: {}", a, b);
}
pub fn assert_jet(stack: &mut NockStack, jet: Jet, sam: Noun, res: Noun) {
let mut context = Context {
stack,
newt: None,
cache: &mut Hamt::<Noun>::new(),
};
let sam = T(context.stack, &[D(0), sam, D(0)]);
let jet_res = assert_no_alloc(|| jet(&mut context, sam).unwrap());
assert_noun_eq(stack, jet_res, res);
}
pub fn assert_jet_ubig(stack: &mut NockStack, jet: Jet, sam: Noun, res: UBig) {
let res = A(stack, &res);
assert_jet(stack, jet, sam, res);
}
pub fn assert_nary_jet_ubig(stack: &mut NockStack, jet: Jet, sam: &[Noun], res: UBig) {
let sam = T(stack, sam);
assert_jet_ubig(stack, jet, sam, res);
}
pub fn assert_jet_err(stack: &mut NockStack, jet: Jet, sam: Noun, err: JetErr) {
let mut context = Context {
stack,
newt: None,
cache: &mut Hamt::<Noun>::new(),
};
let sam = T(context.stack, &[D(0), sam, D(0)]);
let jet_res = jet(&mut context, sam);
assert!(
jet_res.is_err(),
"with sample: {}, expected err: {:?}, got: {:?}",
sam,
err,
&jet_res
);
let jet_err = jet_res.unwrap_err();
assert_eq!(
jet_err, err,
"with sample: {}, expected err: {:?}, got: {:?}",
sam, err, jet_err
);
}
}
#[cfg(test)]
mod tests {
use super::test::{init_stack, A};
use super::*;
use ibig::ubig;
#[test]
fn test_met() {
let s = &mut init_stack();
let a = A(s, &ubig!(0xdeadbeef12345678fedcba9876543210))
.as_atom()
.unwrap();
assert_eq!(met(0, a), 128);
assert_eq!(met(1, a), 64);
assert_eq!(met(2, a), 32);
assert_eq!(met(3, a), 16);
assert_eq!(met(4, a), 8);
assert_eq!(met(5, a), 4);
assert_eq!(met(6, a), 2);
assert_eq!(met(7, a), 1);
assert_eq!(met(8, a), 1);
let a = D(0x7fffffffffffffff).as_atom().unwrap();
assert_eq!(met(0, a), 63);
assert_eq!(met(1, a), 32);
assert_eq!(met(2, a), 16);
assert_eq!(met(3, a), 8);
assert_eq!(met(4, a), 4);
assert_eq!(met(5, a), 2);
assert_eq!(met(6, a), 1);
assert_eq!(met(7, a), 1);
}
}
}

713
rust/ares/src/jets/bits.rs Normal file
View File

@ -0,0 +1,713 @@
/** Bit arithmetic & logic jets
*/
use crate::interpreter::Context;
use crate::jets::util::*;
use crate::jets::JetErr::*;
use crate::jets::Result;
use crate::noun::{DirectAtom, IndirectAtom, Noun, D};
use std::cmp;
crate::gdb!();
/*
* Bit arithmetic
*/
pub fn jet_bex(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?.as_direct()?.data() as usize;
Ok(bex(context.stack, arg).as_noun())
}
pub fn jet_can(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let bloq = bloq(slot(arg, 2)?)?;
let original_list = slot(arg, 3)?;
let mut len = 0usize;
let mut list = original_list;
loop {
if unsafe { list.raw_equals(D(0)) } {
break;
}
let cell = list.as_cell()?;
let item = cell.head().as_cell()?;
let step = item.head().as_direct()?.data() as usize;
len = checked_add(len, step)?;
list = cell.tail();
}
if len == 0 {
Ok(D(0))
} else {
unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, len)?);
let mut pos = 0;
let mut list = original_list;
loop {
if list.raw_equals(D(0)) {
break;
}
let cell = list.as_cell()?;
let item = cell.head().as_cell()?;
let step = item.head().as_direct()?.data() as usize;
let atom = item.tail().as_atom()?;
chop(bloq, 0, step, pos, new_slice, atom.as_bitslice())?;
pos += step;
list = cell.tail();
}
Ok(new_indirect.normalize_as_atom().as_noun())
}
}
}
pub fn jet_cat(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let bloq = bloq(slot(arg, 2)?)?;
let a = slot(arg, 6)?.as_atom()?;
let b = slot(arg, 7)?.as_atom()?;
let len_a = met(bloq, a);
let len_b = met(bloq, b);
let new_len = bite_to_word(bloq, checked_add(len_a, len_b)?)?;
if new_len == 0 {
Ok(a.as_noun())
} else {
unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(context.stack, new_len);
chop(bloq, 0, len_a, 0, new_slice, a.as_bitslice())?;
chop(bloq, 0, len_b, len_a, new_slice, b.as_bitslice())?;
Ok(new_indirect.normalize_as_atom().as_noun())
}
}
}
pub fn jet_cut(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let bloq = bloq(slot(arg, 2)?)?;
let start = slot(arg, 12)?.as_direct()?.data() as usize;
let run = slot(arg, 13)?.as_direct()?.data() as usize;
let atom = slot(arg, 7)?.as_atom()?;
if run == 0 {
return Ok(D(0));
}
let new_indirect = unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, run)?);
chop(bloq, start, run, 0, new_slice, atom.as_bitslice())?;
new_indirect.normalize_as_atom()
};
Ok(new_indirect.as_noun())
}
pub fn jet_end(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let (bloq, step) = bite(slot(arg, 2)?)?;
let a = slot(arg, 3)?.as_atom()?;
if step == 0 {
Ok(D(0))
} else if step >= met(bloq, a) {
Ok(a.as_noun())
} else {
unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, step)?);
chop(bloq, 0, step, 0, new_slice, a.as_bitslice())?;
Ok(new_indirect.normalize_as_atom().as_noun())
}
}
}
pub fn jet_lsh(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let (bloq, step) = bite(slot(arg, 2)?)?;
let a = slot(arg, 3)?.as_atom()?;
let len = met(bloq, a);
if len == 0 {
return Ok(D(0));
}
let new_size = bits_to_word(checked_add(a.bit_size(), checked_left_shift(bloq, step)?)?)?;
unsafe {
let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(context.stack, new_size);
chop(bloq, 0, len, step, dest, a.as_bitslice())?;
Ok(atom.normalize_as_atom().as_noun())
}
}
pub fn jet_met(_context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let bloq = bloq(slot(arg, 2)?)?;
let a = slot(arg, 3)?.as_atom()?;
Ok(D(met(bloq, a) as u64))
}
pub fn jet_rap(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let bloq = bloq(slot(arg, 2)?)?;
let original_list = slot(arg, 3)?;
let mut len = 0usize;
let mut list = original_list;
loop {
if unsafe { list.raw_equals(D(0)) } {
break;
}
let cell = list.as_cell()?;
len = checked_add(len, met(bloq, cell.head().as_atom()?))?;
list = cell.tail();
}
if len == 0 {
Ok(D(0))
} else {
unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, len)?);
let mut pos = 0;
let mut list = original_list;
loop {
if list.raw_equals(D(0)) {
break;
}
let cell = list.as_cell()?;
let atom = cell.head().as_atom()?;
let step = met(bloq, atom);
chop(bloq, 0, step, pos, new_slice, atom.as_bitslice())?;
pos += step;
list = cell.tail();
}
Ok(new_indirect.normalize_as_atom().as_noun())
}
}
}
pub fn jet_rep(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let (bloq, step) = bite(slot(arg, 2)?)?;
let original_list = slot(arg, 3)?;
let mut len = 0usize;
let mut list = original_list;
loop {
if unsafe { list.raw_equals(D(0)) } {
break;
}
let cell = list.as_cell()?;
len = checked_add(len, step)?;
list = cell.tail();
}
if len == 0 {
Ok(D(0))
} else {
unsafe {
let (mut new_indirect, new_slice) =
IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, len)?);
let mut pos = 0;
let mut list = original_list;
loop {
if list.raw_equals(D(0)) {
break;
}
let cell = list.as_cell()?;
let atom = cell.head().as_atom()?;
chop(bloq, 0, step, pos, new_slice, atom.as_bitslice())?;
pos += step;
list = cell.tail();
}
Ok(new_indirect.normalize_as_atom().as_noun())
}
}
}
pub fn jet_rev(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let boz = slot(arg, 2)?.as_atom()?.as_direct()?.data();
if boz >= 64 {
return Err(NonDeterministic);
}
let boz = boz as usize;
let len = slot(arg, 6)?.as_atom()?.as_direct()?.data();
let dat = slot(arg, 7)?.as_atom()?;
let bits = len << boz;
/* 63 is the maximum number of bits for a direct atom */
let mut output = if dat.is_direct() && bits < 64 {
unsafe { DirectAtom::new_unchecked(0).as_atom() }
} else {
unsafe { IndirectAtom::new_raw(context.stack, ((bits + 7) / 8) as usize, &0).as_atom() }
};
let src = dat.as_bitslice();
let dest = output.as_bitslice_mut();
let len = len as usize;
let total_len = len << boz;
for (start, end) in (0..len).map(|b| (b << boz, (b + 1) << boz)) {
dest[start..end].copy_from_bitslice(&src[(total_len - end)..(total_len - start)]);
}
Ok(unsafe { output.normalize() }.as_noun())
}
pub fn jet_rip(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let (bloq, step) = bite(slot(arg, 2)?)?;
let atom = slot(arg, 3)?.as_atom()?;
rip(context.stack, bloq, step, atom)
}
pub fn jet_rsh(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let (bloq, step) = bite(slot(arg, 2)?)?;
let a = slot(arg, 3)?.as_atom()?;
let len = met(bloq, a);
if step >= len {
return Ok(D(0));
}
let new_size = bits_to_word(checked_sub(a.bit_size(), checked_left_shift(bloq, step)?)?)?;
unsafe {
let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(context.stack, new_size);
chop(bloq, step, len - step, 0, dest, a.as_bitslice())?;
Ok(atom.normalize_as_atom().as_noun())
}
}
/*
* Bit logic
*/
pub fn jet_con(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let a = slot(arg, 2)?.as_atom()?;
let b = slot(arg, 3)?.as_atom()?;
Ok(con(context.stack, a, b).as_noun())
}
pub fn jet_dis(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let a = slot(arg, 2)?.as_atom()?;
let b = slot(arg, 3)?.as_atom()?;
let new_size = cmp::max(a.size(), b.size());
unsafe {
let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(context.stack, new_size);
let a_bit = a.as_bitslice();
dest[..a_bit.len()].copy_from_bitslice(a_bit);
*dest &= b.as_bitslice();
Ok(atom.normalize_as_atom().as_noun())
}
}
pub fn jet_mix(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let a = slot(arg, 2)?.as_atom()?;
let b = slot(arg, 3)?.as_atom()?;
let new_size = cmp::max(a.size(), b.size());
unsafe {
let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(context.stack, new_size);
let a_bit = a.as_bitslice();
dest[..a_bit.len()].copy_from_bitslice(a_bit);
*dest ^= b.as_bitslice();
Ok(atom.normalize_as_atom().as_noun())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jets::util::test::{assert_jet, assert_jet_ubig, init_stack, A};
use crate::mem::NockStack;
use crate::noun::{Noun, D, T};
use ibig::ubig;
fn atoms(s: &mut NockStack) -> (Noun, Noun, Noun, Noun, Noun) {
(atom_0(s), atom_24(s), atom_63(s), atom_96(s), atom_128(s))
}
fn atom_0(_stack: &mut NockStack) -> Noun {
D(0)
}
fn atom_24(_stack: &mut NockStack) -> Noun {
D(0x876543)
}
fn atom_63(_stack: &mut NockStack) -> Noun {
D(0x7fffffffffffffff)
}
fn atom_96(stack: &mut NockStack) -> Noun {
A(stack, &ubig!(0xfaceb00c15deadbeef123456))
}
fn atom_128(stack: &mut NockStack) -> Noun {
A(stack, &ubig!(0xdeadbeef12345678fedcba9876543210))
}
/*
* Bit arithmetic
*/
#[test]
fn test_bex() {
let s = &mut init_stack();
assert_jet(s, jet_bex, D(0), D(1));
assert_jet(s, jet_bex, D(5), D(32));
assert_jet(s, jet_bex, D(62), D(0x4000000000000000));
assert_jet_ubig(
s,
jet_bex,
D(256),
ubig!(_0x10000000000000000000000000000000000000000000000000000000000000000),
);
}
#[test]
fn test_can() {
let s = &mut init_stack();
let (a0, _a24, _a63, _a96, a128) = atoms(s);
let bloq0 = D(0);
let bloq3 = D(3);
let bloq4 = D(4);
let sam = T(s, &[bloq0, D(0)]);
assert_jet(s, jet_can, sam, D(0));
let sam = T(s, &[bloq3, D(0)]);
assert_jet(s, jet_can, sam, D(0));
let run1 = T(s, &[D(0), a0]);
let run2 = T(s, &[D(1), a0]);
let run3 = T(s, &[D(2), a0]);
let sam = T(s, &[bloq0, run1, run2, run3, D(0)]);
assert_jet(s, jet_can, sam, D(0));
let sam = T(s, &[bloq3, run1, run2, run3, D(0)]);
assert_jet(s, jet_can, sam, D(0));
let run1 = T(s, &[D(1), a128]);
let run2 = T(s, &[D(3), a0]);
let sam = T(s, &[bloq3, run1, run2, D(0)]);
assert_jet(s, jet_can, sam, D(0x10));
let run1 = T(s, &[D(3), a0]);
let run2 = T(s, &[D(1), a128]);
let sam = T(s, &[bloq3, run1, run2, D(0)]);
assert_jet(s, jet_can, sam, D(0x10000000));
let run1 = T(s, &[D(8), D(0xfe)]);
let run2 = T(s, &[D(4), D(0xa)]);
let run3 = T(s, &[D(0), D(0xbbbb)]);
let run4 = T(s, &[D(1), D(0)]);
let run5 = T(s, &[D(1), D(0)]);
let run6 = T(s, &[D(1), D(1)]);
let run7 = T(s, &[D(1), D(1)]);
let sam = T(s, &[bloq0, run1, run2, run3, run4, run5, run6, run7, D(0)]);
assert_jet(s, jet_can, sam, D(0xcafe));
let run1 = T(s, &[D(1), D(0xfe)]);
let run2 = T(s, &[D(1), D(0xca)]);
let sam = T(s, &[bloq4, run1, run2, D(0)]);
assert_jet(s, jet_can, sam, D(0xca00fe));
}
#[test]
fn test_cat() {
let s = &mut init_stack();
let (a0, a24, _a63, _a96, a128) = atoms(s);
let bloq0 = D(0);
let bloq3 = D(3);
let bloq4 = D(4);
let sam = T(s, &[bloq0, a0, a0]);
assert_jet(s, jet_cat, sam, D(0));
let sam = T(s, &[bloq3, a0, a0]);
assert_jet(s, jet_cat, sam, D(0));
let sam = T(s, &[bloq0, a24, a128]);
let res = A(s, &ubig!(_0xdeadbeef12345678fedcba9876543210876543));
assert_jet(s, jet_cat, sam, res);
let sam = T(s, &[bloq3, a24, a128]);
let res = A(s, &ubig!(_0xdeadbeef12345678fedcba9876543210876543));
assert_jet(s, jet_cat, sam, res);
let sam = T(s, &[bloq4, a24, a128]);
let res = A(s, &ubig!(_0xdeadbeef12345678fedcba987654321000876543));
assert_jet(s, jet_cat, sam, res);
}
#[test]
fn test_cut() {
let s = &mut init_stack();
let (_a0, a24, _a63, a96, a128) = atoms(s);
let run = T(s, &[D(0), D(0)]);
let sam = T(s, &[D(0), run, a24]);
assert_jet(s, jet_cut, sam, D(0));
let run = T(s, &[D(0), D(5)]);
let sam = T(s, &[D(0), run, a24]);
assert_jet(s, jet_cut, sam, D(0x3));
let run = T(s, &[D(4), D(6)]);
let sam = T(s, &[D(3), run, a96]);
assert_jet(s, jet_cut, sam, D(0xb00c15deadbe));
let run = T(s, &[D(4), D(1)]);
let sam = T(s, &[D(4), run, a24]);
assert_jet(s, jet_cut, sam, D(0));
let run = T(s, &[D(2), D(10)]);
let sam = T(s, &[D(4), run, a128]);
let res = A(s, &ubig!(0xdeadbeef12345678fedcba98));
assert_jet(s, jet_cut, sam, res);
}
#[test]
fn test_end() {
let s = &mut init_stack();
let (a0, a24, _a63, a96, a128) = atoms(s);
let sam = T(s, &[a0, a24]);
assert_jet(s, jet_end, sam, D(0x1));
let sam = T(s, &[D(3), a24]);
assert_jet(s, jet_end, sam, D(0x43));
let sam = T(s, &[D(7), a24]);
assert_jet(s, jet_end, sam, a24);
let sam = T(s, &[D(6), a128]);
let res = A(s, &ubig!(0xfedcba9876543210));
assert_jet(s, jet_end, sam, res);
let bit = T(s, &[D(0), D(5)]);
let sam = T(s, &[bit, a24]);
assert_jet(s, jet_end, sam, D(0x3));
let bit = T(s, &[D(4), D(6)]);
let sam = T(s, &[bit, a96]);
assert_jet(s, jet_end, sam, a96);
}
#[test]
fn test_lsh() {
let s = &mut init_stack();
let (a0, a24, _a63, a96, a128) = atoms(s);
let sam = T(s, &[a0, a24]);
assert_jet(s, jet_lsh, sam, D(0x10eca86));
let sam = T(s, &[D(3), a24]);
assert_jet(s, jet_lsh, sam, D(0x87654300));
let sam = T(s, &[D(7), a24]);
let res = A(s, &ubig!(_0x87654300000000000000000000000000000000));
assert_jet(s, jet_lsh, sam, res);
let sam = T(s, &[D(6), a128]);
let res = A(
s,
&ubig!(_0xdeadbeef12345678fedcba98765432100000000000000000),
);
assert_jet(s, jet_lsh, sam, res);
let bit = T(s, &[D(0), D(5)]);
let sam = T(s, &[bit, a24]);
assert_jet(s, jet_lsh, sam, D(0x10eca860));
let bit = T(s, &[D(4), D(6)]);
let sam = T(s, &[bit, a96]);
let res = A(
s,
&ubig!(_0xfaceb00c15deadbeef123456000000000000000000000000),
);
assert_jet(s, jet_lsh, sam, res);
}
#[test]
fn test_met() {
let s = &mut init_stack();
let (a0, a24, _a63, _a96, a128) = atoms(s);
let sam = T(s, &[a0, a0]);
assert_jet(s, jet_met, sam, D(0));
let sam = T(s, &[a0, a24]);
assert_jet(s, jet_met, sam, D(24));
let sam = T(s, &[D(3), a24]);
assert_jet(s, jet_met, sam, D(3));
let sam = T(s, &[D(1), a128]);
assert_jet(s, jet_met, sam, D(64));
}
#[test]
fn test_rap() {
let s = &mut init_stack();
let bloq0 = D(0);
let bloq2 = D(2);
let bloq3 = D(3);
let empty_list = D(0);
let zero_list = T(s, &[D(0), D(0), D(0)]);
let test_list = T(s, &[D(0xe), D(0xf), D(0xa), D(0xc), D(0)]);
let wide_list = T(s, &[D(0xafe), D(0xc), D(0)]);
let sam = T(s, &[bloq0, empty_list]);
assert_jet(s, jet_rap, sam, D(0));
let sam = T(s, &[bloq0, zero_list]);
assert_jet(s, jet_rap, sam, D(0));
let sam = T(s, &[bloq3, zero_list]);
assert_jet(s, jet_rap, sam, D(0));
let sam = T(s, &[bloq0, test_list]);
assert_jet(s, jet_rap, sam, D(0xcafe));
let sam = T(s, &[bloq2, test_list]);
assert_jet(s, jet_rap, sam, D(0xcafe));
let sam = T(s, &[bloq2, wide_list]);
assert_jet(s, jet_rap, sam, D(0xcafe));
let sam = T(s, &[bloq3, test_list]);
let res = A(s, &ubig!(0xc0a0f0e));
assert_jet(s, jet_rap, sam, res);
let sam = T(s, &[bloq3, wide_list]);
assert_jet(s, jet_rap, sam, D(0xc0afe));
}
#[test]
fn test_rep() {
let s = &mut init_stack();
let (a0, a24, a63, a96, a128) = atoms(s);
let sam = T(s, &[D(0), D(0)]);
assert_jet(s, jet_rep, sam, D(0));
let bit = T(s, &[D(3), D(2)]);
let sam = T(s, &[bit, a0, a24, a63, a96, a128, D(0)]);
let res = A(s, &ubig!(0x32103456ffff65430000));
assert_jet(s, jet_rep, sam, res);
}
#[test]
fn test_rev() {
let s = &mut init_stack();
let (_a0, a24, _a63, _a96, _a128) = atoms(s);
let sam = T(s, &[D(0), D(60), a24]);
assert_jet(s, jet_rev, sam, D(0xc2a6e1000000000));
let test = 0x1234567890123u64;
let sam = T(s, &[D(3), D(7), D(test)]);
assert_jet(s, jet_rev, sam, D(test.swap_bytes() >> 8));
}
#[test]
fn test_rip() {
let s = &mut init_stack();
let (_a0, _a24, _a63, _a96, a128) = atoms(s);
let sam = T(s, &[D(0), D(0)]);
assert_jet(s, jet_rip, sam, D(0));
let bit = T(s, &[D(1), D(2)]);
let sam = T(s, &[bit, a128]);
#[rustfmt::skip]
let res = T(
s,
&[
D(0x0), D(0x1), D(0x2), D(0x3), D(0x4), D(0x5), D(0x6), D(0x7),
D(0x8), D(0x9), D(0xa), D(0xb), D(0xc), D(0xd), D(0xe), D(0xf),
D(0x8), D(0x7), D(0x6), D(0x5), D(0x4), D(0x3), D(0x2), D(0x1),
D(0xf), D(0xe), D(0xe), D(0xb), D(0xd), D(0xa), D(0xe), D(0xd),
D(0x0),
],
);
assert_jet(s, jet_rip, sam, res);
}
#[test]
fn test_rsh() {
let s = &mut init_stack();
let (a0, a24, _a63, a96, a128) = atoms(s);
let sam = T(s, &[a0, a24]);
assert_jet(s, jet_rsh, sam, D(0x43b2a1));
let sam = T(s, &[D(3), a24]);
assert_jet(s, jet_rsh, sam, D(0x8765));
let sam = T(s, &[D(7), a24]);
assert_jet(s, jet_rsh, sam, D(0));
let sam = T(s, &[D(2), a128]);
let res = A(s, &ubig!(0xdeadbeef12345678fedcba987654321));
assert_jet(s, jet_rsh, sam, res);
let sam = T(s, &[D(6), a128]);
let res = A(s, &ubig!(0xdeadbeef12345678));
assert_jet(s, jet_rsh, sam, res);
let bit = T(s, &[D(0), D(5)]);
let sam = T(s, &[bit, a24]);
assert_jet(s, jet_rsh, sam, D(0x43b2a));
let bit = T(s, &[D(4), D(6)]);
let sam = T(s, &[bit, a96]);
assert_jet(s, jet_rsh, sam, D(0));
}
/*
* Bit logic
*/
#[test]
fn test_con() {
let s = &mut init_stack();
let (a0, a24, a63, a96, a128) = atoms(s);
let sam = T(s, &[a0, a0]);
assert_jet(s, jet_con, sam, D(0));
let sam = T(s, &[a24, a96]);
let res = A(s, &ubig!(0xfaceb00c15deadbeef977557));
assert_jet(s, jet_con, sam, res);
let sam = T(s, &[a96, a128]);
let res = A(s, &ubig!(0xdeadbeeffafef67cffdebfbeff563656));
assert_jet(s, jet_con, sam, res);
let sam = T(s, &[a24, a63]);
assert_jet(s, jet_con, sam, a63);
let sam = T(s, &[a0, a128]);
assert_jet(s, jet_con, sam, a128);
let sam = T(s, &[a128, a0]);
assert_jet(s, jet_con, sam, a128);
}
#[test]
fn test_dis() {
let s = &mut init_stack();
let (a0, a24, a63, a96, a128) = atoms(s);
let sam = T(s, &[a0, a0]);
assert_jet(s, jet_dis, sam, D(0));
let sam = T(s, &[a24, a96]);
assert_jet(s, jet_dis, sam, D(0x22442));
let sam = T(s, &[a96, a128]);
let res = A(s, &ubig!(0x1204100814dca89866103010));
assert_jet(s, jet_dis, sam, res);
let sam = T(s, &[a24, a63]);
assert_jet(s, jet_dis, sam, a24);
let sam = T(s, &[a0, a128]);
assert_jet(s, jet_dis, sam, a0);
let sam = T(s, &[a128, a0]);
assert_jet(s, jet_dis, sam, a0);
}
#[test]
fn test_mix() {
let s = &mut init_stack();
let (a0, a24, a63, a96, a128) = atoms(s);
let sam = T(s, &[a0, a0]);
assert_jet(s, jet_mix, sam, D(0));
let sam = T(s, &[a24, a96]);
let res = A(s, &ubig!(0xfaceb00c15deadbeef955115));
assert_jet(s, jet_mix, sam, res);
let sam = T(s, &[a96, a128]);
let res = A(s, &ubig!(0xdeadbeefe8fae674eb02172699460646));
assert_jet(s, jet_mix, sam, res);
let sam = T(s, &[a24, a63]);
let res = A(s, &ubig!(0x7fffffffff789abc));
assert_jet(s, jet_mix, sam, res);
let sam = T(s, &[a0, a128]);
assert_jet(s, jet_mix, sam, a128);
let sam = T(s, &[a128, a0]);
assert_jet(s, jet_mix, sam, a128);
}
}

140
rust/ares/src/jets/form.rs Normal file
View File

@ -0,0 +1,140 @@
/** Formatting jets
*/
use crate::interpreter::Context;
use crate::jets::util::slot;
use crate::jets::Result;
use crate::noun::Noun;
crate::gdb!();
pub fn jet_scow(context: &mut Context, subject: Noun) -> Result {
let aura = slot(subject, 12)?.as_direct()?;
let atom = slot(subject, 13)?.as_atom()?;
util::scow(context.stack, aura, atom)
}
pub mod util {
use crate::jets;
use crate::jets::JetErr;
use crate::mem::NockStack;
use crate::noun::{Atom, Cell, DirectAtom, D, T};
use ares_macros::tas;
use num_traits::identities::Zero;
pub fn scow(
stack: &mut NockStack,
aura: DirectAtom, // XX: technically this should be Atom?
atom: Atom,
) -> jets::Result {
match aura.data() {
tas!(b"ud") => {
if atom.as_bitslice().first_one().is_none() {
return Ok(T(stack, &[D(b'0' as u64), D(0)]));
}
let mut root = D(0);
let mut lent = 0;
if atom.direct().is_some() {
let mut n = atom.as_direct()?.data();
while n != 0 {
root = T(stack, &[D(b'0' as u64 + (n % 10)), root]);
n /= 10;
lent += 1;
}
} else {
let mut n = atom.as_indirect()?.as_ubig(stack);
while !n.is_zero() {
root = T(stack, &[D(b'0' as u64 + (&n % 10u64)), root]);
n /= 10u64;
lent += 1;
}
}
unsafe {
let mut list = root.as_cell()?;
lent -= 1;
while lent > 2 {
if lent % 3 == 0 {
let (cell, memory) = Cell::new_raw_mut(stack);
(*memory).head = D(b'.' as u64);
(*memory).tail = list.tail();
(*(list.to_raw_pointer_mut())).tail = cell.as_noun();
list = list.tail().as_cell()?;
}
list = list.tail().as_cell()?;
lent -= 1;
}
Ok(root)
}
}
_ => Err(JetErr::Punt),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jets::util::test::{assert_jet, assert_jet_err, init_stack, A};
use crate::jets::JetErr;
use crate::noun::{Noun, D, T};
use ares_macros::tas;
use ibig::ubig;
// Rust can't handle implicit conversions from u8 to u64
#[allow(non_snake_case)]
fn B(b: u8) -> Noun {
D(b as u64)
}
#[test]
fn test_scow() {
let s = &mut init_stack();
let aura = D(tas!(b"ud"));
let sam = T(s, &[aura, D(0)]);
let res = T(s, &[B(b'0'), D(0)]);
assert_jet(s, jet_scow, sam, res);
let sam = T(s, &[aura, D(100)]);
let res = T(s, &[B(b'1'), B(b'0'), B(b'0'), D(0)]);
assert_jet(s, jet_scow, sam, res);
let big = A(s, &ubig!(100));
let sam = T(s, &[aura, big]);
let res = T(s, &[B(b'1'), B(b'0'), B(b'0'), D(0)]);
assert_jet(s, jet_scow, sam, res);
let sam = T(s, &[aura, D(1000)]);
let res = T(s, &[B(b'1'), B(b'.'), B(b'0'), B(b'0'), B(b'0'), D(0)]);
assert_jet(s, jet_scow, sam, res);
let big = A(s, &ubig!(1000));
let sam = T(s, &[aura, big]);
let res = T(s, &[B(b'1'), B(b'.'), B(b'0'), B(b'0'), B(b'0'), D(0)]);
assert_jet(s, jet_scow, sam, res);
let sam = T(s, &[aura, D(9876543210)]);
let res = T(
s,
&[
B(b'9'),
B(b'.'),
B(b'8'),
B(b'7'),
B(b'6'),
B(b'.'),
B(b'5'),
B(b'4'),
B(b'3'),
B(b'.'),
B(b'2'),
B(b'1'),
B(b'0'),
D(0),
],
);
assert_jet(s, jet_scow, sam, res);
let bad_aura = D(tas!(b"ux"));
let sam = T(s, &[bad_aura, D(0)]);
assert_jet_err(s, jet_scow, sam, JetErr::Punt);
}
}

View File

@ -0,0 +1,68 @@
/** Hash jets
*/
use crate::interpreter::Context;
use crate::jets::util::*;
use crate::jets::Result;
use crate::mug::mug;
use crate::noun::Noun;
crate::gdb!();
pub fn jet_mug(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
Ok(mug(context.stack, arg).as_noun())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jets::util::test::{assert_jet, init_stack, A};
use crate::mem::NockStack;
use crate::noun::{Noun, D, T};
use ibig::ubig;
fn atoms(s: &mut NockStack) -> (Noun, Noun, Noun, Noun, Noun) {
(atom_0(s), atom_24(s), atom_63(s), atom_96(s), atom_128(s))
}
fn atom_0(_stack: &mut NockStack) -> Noun {
D(0)
}
fn atom_24(_stack: &mut NockStack) -> Noun {
D(0x876543)
}
fn atom_63(_stack: &mut NockStack) -> Noun {
D(0x7fffffffffffffff)
}
fn atom_96(stack: &mut NockStack) -> Noun {
A(stack, &ubig!(0xfaceb00c15deadbeef123456))
}
fn atom_128(stack: &mut NockStack) -> Noun {
A(stack, &ubig!(0xdeadbeef12345678fedcba9876543210))
}
#[test]
fn test_mug() {
let s = &mut init_stack();
let (a0, a24, a63, a96, a128) = atoms(s);
assert_jet(s, jet_mug, a0, D(0x79ff04e8));
assert_jet(s, jet_mug, a24, D(0x69d59d90));
assert_jet(s, jet_mug, a63, D(0x7a9f252e));
assert_jet(s, jet_mug, a96, D(0x2aa4c8fb));
assert_jet(s, jet_mug, a128, D(0x44fb2c0c));
let sam = T(s, &[a128, a128]);
assert_jet(s, jet_mug, sam, D(0x61c0ea5c));
let sam = T(s, &[a96, a128]);
assert_jet(s, jet_mug, sam, D(0x20fb143f));
let sam = T(s, &[a0, a0]);
assert_jet(s, jet_mug, sam, D(0x192f5588));
let sam = T(s, &[a0, a24, a63, a96, a128]);
let sam = T(s, &[sam, a0, a24, a63, a96, a128]);
let sam = T(s, &[sam, a0, a24, a63, a96, a128]);
assert_jet(s, jet_mug, sam, D(0x7543cac7));
}
}

File diff suppressed because it is too large Load Diff

375
rust/ares/src/jets/nock.rs Normal file
View File

@ -0,0 +1,375 @@
/** Virtualization jets
*/
use crate::interpreter::Context;
use crate::jets::util::slot;
use crate::jets::Result;
use crate::noun::Noun;
crate::gdb!();
// XX: interpret should accept optional scry function and potentially produce blocked
pub fn jet_mink(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
// mink sample = [nock scry_namespace]
// = [[subject formula] scry_namespace]
let v_subject = slot(arg, 4)?;
let v_formula = slot(arg, 5)?;
let _scry = slot(arg, 3)?;
util::mink(context, v_subject, v_formula)
}
pub mod util {
use crate::interpreter::{interpret, Context, NockErr, Tone};
use crate::jets;
use crate::jets::form::util::scow;
use crate::jets::util::rip;
use crate::jets::JetErr;
use crate::mem::NockStack;
use crate::noun::{tape, Cell, Noun, D, T};
use ares_macros::tas;
use std::result;
const LEAF: Noun = D(tas!(b"leaf"));
const ROSE: Noun = D(tas!(b"rose"));
pub fn mink(context: &mut Context, subject: Noun, formula: Noun) -> jets::Result {
// XX: no partial traces; all of our traces go down to the "home road"
match interpret(context, subject, formula) {
Ok(res) => Ok(T(context.stack, &[D(0), res])),
Err(err) => match err {
Tone::Blocked(block) => Ok(T(context.stack, &[D(1), block])),
Tone::Error(err, trace) => match err {
NockErr::Deterministic => Ok(T(context.stack, &[D(2), trace])),
NockErr::NonDeterministic => Err(JetErr::NonDeterministic),
},
},
}
}
/** Consume $tone, produce $toon
*/
// XX: should write a jet_mook wrapper for this function
pub fn mook(context: &mut Context, tone: Cell, flop: bool) -> result::Result<Cell, JetErr> {
let tag = tone.head().as_direct()?;
let original_list = tone.tail();
match tag.data() {
x if x < 2 => return Ok(tone),
x if x > 2 => return Err(JetErr::Deterministic),
_ => {}
}
if unsafe { original_list.raw_equals(D(0)) } {
return Ok(tone);
} else if original_list.atom().is_some() {
return Err(JetErr::Deterministic);
}
// XX: trim traces longer than 1024 frames
unsafe {
let mut res = D(0);
let mut list = original_list;
// Unused if flopping
let (mut new_cell, mut new_memory) = Cell::new_raw_mut(context.stack);
let mut memory = new_memory;
// loop guaranteed to run at least once
loop {
if list.raw_equals(D(0)) {
break;
} else if !flop && res.raw_equals(D(0)) {
res = new_cell.as_noun();
} else if !flop {
(new_cell, new_memory) = Cell::new_raw_mut(context.stack);
(*memory).tail = new_cell.as_noun();
memory = new_memory
}
let cell = list.as_cell()?;
let trace = cell.head().as_cell()?;
let tag = trace.head().as_direct()?;
let dat = trace.tail();
let tank: Noun = match tag.data() {
tas!(b"mean") => {
if let Ok(atom) = dat.as_atom() {
let tape = rip(context.stack, 3, 1, atom)?;
T(context.stack, &[LEAF, tape])
} else {
let tone = mink(context, dat, dat.as_cell()?.head())?.as_cell()?;
if !tone.head().raw_equals(D(0)) {
let tape = tape(context.stack, "####");
T(context.stack, &[LEAF, tape])
} else {
// XX: need to check that this is actually a tank
// return leaf+"mean.mook" if not
tone.tail()
}
}
}
tas!(b"spot") => {
let stack = &mut context.stack;
let spot = dat.as_cell()?;
let pint = spot.tail().as_cell()?;
let pstr = pint.head().as_cell()?;
let pend = pint.tail().as_cell()?;
let colo = T(*stack, &[D(b':' as u64), D(0)]);
let trel = T(*stack, &[colo, D(0), D(0)]);
let smyt = smyt(stack, spot.head())?;
let aura = D(tas!(b"ud")).as_direct()?;
let str_lin = scow(stack, aura, pstr.head().as_atom()?)?;
let str_col = scow(stack, aura, pstr.tail().as_atom()?)?;
let end_lin = scow(stack, aura, pend.head().as_atom()?)?;
let end_col = scow(stack, aura, pend.tail().as_atom()?)?;
let mut list = end_col.as_cell()?;
loop {
if list.tail().atom().is_some() {
break;
}
list = list.tail().as_cell()?;
}
// "{end_col}]>"
let p4 = T(*stack, &[D(b']' as u64), D(b'>' as u64), D(0)]);
(*list.tail_as_mut()) = p4;
list = end_lin.as_cell()?;
loop {
if list.tail().atom().is_some() {
break;
}
list = list.tail().as_cell()?;
}
// "{end_lin} {end_col}]>"
let p3 = T(*stack, &[D(b' ' as u64), end_col]);
(*list.tail_as_mut()) = p3;
list = str_col.as_cell()?;
loop {
if list.tail().atom().is_some() {
break;
}
list = list.tail().as_cell()?;
}
// "{str_col}].[{end_lin} {end_col}]>"
let p2 = T(
*stack,
&[D(b']' as u64), D(b'.' as u64), D(b'[' as u64), end_lin],
);
(*list.tail_as_mut()) = p2;
list = str_lin.as_cell()?;
loop {
if list.tail().atom().is_some() {
break;
}
list = list.tail().as_cell()?;
}
// "{str_lin} {str_col}].[{end_lin} {end_col}]>"
let p1 = T(*stack, &[D(b' ' as u64), str_col]);
(*list.tail_as_mut()) = p1;
// "<[{str_lin} {str_col}].[{end_lin} {end_col}]>"
let tape = T(*stack, &[D(b'<' as u64), D(b'[' as u64), str_lin]);
let finn = T(*stack, &[LEAF, tape]);
T(*stack, &[ROSE, trel, smyt, finn, D(0)])
}
_ => {
let tape = rip(context.stack, 3, 1, tag.as_atom())?;
T(
context.stack,
&[
D(tas!(b"m")),
D(tas!(b"o")),
D(tas!(b"o")),
D(tas!(b"k")),
D(tas!(b".")),
tape,
],
)
} // XX: TODO
// %hand
// %hunk
// %lose
};
if flop {
res = T(context.stack, &[tank, res]);
} else {
(*memory).head = tank;
}
list = cell.tail();
}
if !flop {
(*memory).tail = D(0);
}
let toon = Cell::new(context.stack, D(2), res);
Ok(toon)
}
}
pub fn smyt(stack: &mut NockStack, path: Noun) -> jets::Result {
let lash = D(tas!(b"/"));
let zero = D(0);
let sep = T(stack, &[lash, zero]);
let trel = T(stack, &[sep, sep, zero]);
let tank = smyt_help(stack, path)?;
Ok(T(stack, &[ROSE, trel, tank]))
}
fn smyt_help(stack: &mut NockStack, path: Noun) -> jets::Result {
// XX: switch to using Cell:new_raw_mut
if unsafe { path.raw_equals(D(0)) } {
return Ok(D(0));
}
let cell = path.as_cell()?;
let tail = smyt_help(stack, cell.tail())?;
let trip = rip(stack, 3, 1, cell.head().as_atom()?)?;
let head = T(stack, &[LEAF, trip]);
Ok(T(stack, &[head, tail]))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jets::util::test::{assert_jet, init_stack};
use crate::mem::NockStack;
use crate::noun::{D, T};
use crate::serf::TERMINATOR;
use std::sync::Arc;
#[test]
fn init() {
// This needs to be done because TERMINATOR is lazy allocated, and if you don't
// do it before you call the unit tests it'll get allocated on the Rust heap
// inside an assert_no_alloc block.
//
// Also Rust has no primitive for pre-test setup / post-test teardown, so we
// do it in a test that we rely on being called before any other in this file,
// since we're already using single-threaded test mode to avoid race conditions
// (because Rust doesn't support test order dependencies either).
let _ = Arc::clone(&TERMINATOR);
}
#[test]
fn test_mink_success() {
let sack = &mut init_stack();
let subj = D(0);
let form = T(sack, &[D(1), D(53)]);
let nock = T(sack, &[subj, form]);
let scry = D(0);
let samp = T(sack, &[nock, scry]);
let rest = T(sack, &[D(0), D(53)]);
assert_jet(sack, jet_mink, samp, rest);
}
#[test]
fn test_mink_zapzap() {
let sack = &mut init_stack();
let subj = D(0);
let form = T(sack, &[D(0), D(0)]);
let nock = T(sack, &[subj, form]);
let scry = D(0);
let samp = T(sack, &[nock, scry]);
let rest = T(sack, &[D(2), D(0)]);
assert_jet(sack, jet_mink, samp, rest);
}
#[test]
fn test_mink_trace() {
let sack = &mut init_stack();
let subj = D(0);
let scry = D(0);
// != !: ?~ 0 !! 53
// [ 11
// [1.953.460.339 1 [1.953.719.668 0] [1 9] [1 22]]
// [ 8
// [11 [1.953.460.339 1 [1.953.719.668 0] [1 13] [1 14]] [1 0]]
// [ 6
// [5 [1 0] [0 2]]
// [7 [0 3] [11 [1.953.460.339 1 [1.953.719.668 0] [1 16] [1 18]] [0 0]]]
// [7 [0 3] [11 [1.953.460.339 1 [1.953.719.668 0] [1 20] [1 22]] [1 53]]]
// ]
// ]
// ]
//
// 1.953.719.668 = 'test'
// 1.953.460.339 = 'spot'
//
// All of this below is because of "two-phase borrow checks"
// https://stackoverflow.com/questions/60686259/mutable-borrow-in-function-argument
let hint_spot = D(1953460339);
let hint_path = T(sack, &[D(1953719668), D(0)]);
let hint_dyn = D(1);
let hint_row = D(1);
let make_hint = |sack: &mut NockStack, col_start: u64, col_end: u64| {
let start = T(sack, &[hint_row, D(col_start)]);
let end = T(sack, &[hint_row, D(col_end)]);
T(sack, &[hint_spot, hint_dyn, hint_path, start, end])
};
let sss3s1 = T(sack, &[D(0), D(3)]);
let sss3s2s1 = make_hint(sack, 20, 22);
let sss3s2s2 = T(sack, &[D(1), D(53)]);
let sss3s2 = T(sack, &[D(11), sss3s2s1, sss3s2s2]);
let sss3 = T(sack, &[D(7), sss3s1, sss3s2]);
let sss2s1 = sss3s1;
let sss2s2s1 = make_hint(sack, 16, 18);
let sss2s2s2 = T(sack, &[D(0), D(0)]);
let sss2s2 = T(sack, &[D(11), sss2s2s1, sss2s2s2]);
let sss2 = T(sack, &[D(7), sss2s1, sss2s2]);
let sss1s1 = T(sack, &[D(1), D(0)]);
let sss1s2 = T(sack, &[D(0), D(2)]);
let sss1 = T(sack, &[D(5), sss1s1, sss1s2]);
let ss2 = T(sack, &[D(6), sss1, sss2, sss3]);
let ss1s1 = make_hint(sack, 13, 14);
let ss1s2 = sss1s1;
let ss1 = T(sack, &[D(11), ss1s1, ss1s2]);
let s2 = T(sack, &[D(8), ss1, ss2]);
let s1 = make_hint(sack, 9, 22);
let form = T(sack, &[D(11), s1, s2]);
let nock = T(sack, &[subj, form]);
let samp = T(sack, &[nock, scry]);
// trace
// [%2 trace=~[[~.spot [[1.953.719.668 0] [1 16] 1 18]] [~.spot [[1.953.719.668 0] [1 9] 1 22]]]]
let ttt2t1 = T(sack, &[D(1), D(9)]);
let ttt2t2 = T(sack, &[D(1), D(22)]);
let ttt2 = T(sack, &[hint_path, ttt2t1, ttt2t2]);
let ttt1t1 = T(sack, &[D(1), D(16)]);
let ttt1t2 = T(sack, &[D(1), D(18)]);
let ttt1 = T(sack, &[hint_path, ttt1t1, ttt1t2]);
let tt2 = T(sack, &[hint_spot, ttt2]);
let tt1 = T(sack, &[hint_spot, ttt1]);
let t1 = T(sack, &[tt1, tt2, D(0)]);
let rest = T(sack, &[D(2), t1]);
assert_jet(sack, jet_mink, samp, rest);
}
}

View File

@ -0,0 +1,58 @@
/** Text processing jets
*/
use crate::interpreter::Context;
use crate::jets::util::slot;
use crate::jets::Result;
use crate::noun::{Noun, D};
crate::gdb!();
pub fn jet_lent(_context: &mut Context, subject: Noun) -> Result {
let tape = slot(subject, 6)?;
util::lent(tape).map(|x| D(x as u64))
}
pub mod util {
use crate::jets::JetErr;
use crate::noun::Noun;
pub fn lent(tape: Noun) -> Result<usize, JetErr> {
let mut len = 0usize;
let mut list = tape;
loop {
if let Some(atom) = list.atom() {
if atom.as_bitslice().first_one().is_none() {
break;
} else {
return Err(JetErr::Deterministic);
}
}
let cell = list.as_cell()?;
// don't need checked_add or indirect atom result: 2^63-1 atoms would be 64 ebibytes
len += 1;
list = cell.tail();
}
Ok(len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jets::util::test::{assert_jet, assert_jet_err, init_stack};
use crate::jets::JetErr;
use crate::noun::{D, T};
#[test]
fn test_lent() {
let s = &mut init_stack();
assert_jet(s, jet_lent, D(0), D(0));
let sam = T(s, &[D(1), D(2), D(3), D(0)]);
assert_jet(s, jet_lent, sam, D(3));
let sam = T(s, &[D(3), D(2), D(1), D(0)]);
assert_jet(s, jet_lent, sam, D(3));
assert_jet_err(s, jet_lent, D(1), JetErr::Deterministic);
let sam = T(s, &[D(3), D(2), D(1)]);
assert_jet_err(s, jet_lent, sam, JetErr::Deterministic);
}
}

View File

@ -0,0 +1,82 @@
/** Tree jets
*/
use crate::interpreter::Context;
use crate::jets::util::*;
use crate::jets::JetErr::*;
use crate::jets::Result;
use crate::noun::{Noun, D};
crate::gdb!();
pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
let tom = arg.as_atom()?;
let met = met(0, tom);
unsafe {
if met < 2 {
Err(Deterministic)
} else if *(tom.as_bitslice().get_unchecked(met - 2)) {
Ok(D(3))
} else {
Ok(D(2))
}
}
}
pub fn jet_mas(context: &mut Context, subject: Noun) -> Result {
let stack = &mut context.stack;
let arg = slot(subject, 6)?;
let tom = arg.as_atom()?;
let met = met(0, tom);
if met < 2 {
Err(Deterministic)
} else {
let c = bex(stack, met - 1);
let d = bex(stack, met - 2);
let e = sub(stack, tom, c)?;
Ok(con(stack, e, d).as_noun())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jets::util::test::{assert_jet, assert_jet_err, init_stack};
use crate::jets::JetErr;
use crate::noun::D;
#[test]
fn test_cap() {
let s = &mut init_stack();
assert_jet_err(s, jet_cap, D(0), JetErr::Deterministic);
assert_jet_err(s, jet_cap, D(1), JetErr::Deterministic);
assert_jet(s, jet_cap, D(2), D(2));
assert_jet(s, jet_cap, D(3), D(3));
assert_jet(s, jet_cap, D(4), D(2));
assert_jet(s, jet_cap, D(5), D(2));
assert_jet(s, jet_cap, D(6), D(3));
assert_jet(s, jet_cap, D(7), D(3));
assert_jet(s, jet_cap, D(8), D(2));
}
#[test]
fn test_mas() {
let s = &mut init_stack();
assert_jet_err(s, jet_mas, D(0), JetErr::Deterministic);
assert_jet_err(s, jet_mas, D(1), JetErr::Deterministic);
assert_jet(s, jet_mas, D(2), D(1));
assert_jet(s, jet_mas, D(3), D(1));
assert_jet(s, jet_mas, D(4), D(2));
assert_jet(s, jet_mas, D(5), D(3));
assert_jet(s, jet_mas, D(6), D(2));
assert_jet(s, jet_mas, D(7), D(3));
assert_jet(s, jet_mas, D(8), D(4));
}
}

View File

@ -1,5 +1,7 @@
extern crate num_derive;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate static_assertions;
pub mod interpreter;
pub mod jets;

View File

@ -1,9 +1,10 @@
use ares::interpreter::interpret;
use ares::hamt::Hamt;
use ares::interpreter::{interpret, Context};
use ares::jets::cold::Cold;
use ares::jets::hot::Hot;
use ares::jets::warm::Warm;
use ares::mem::NockStack;
use ares::noun::IndirectAtom;
use ares::noun::{IndirectAtom, Noun};
use ares::serf::serf;
use ares::serialization::{cue, jam};
use memmap::Mmap;
@ -27,7 +28,11 @@ fn main() -> io::Result<()> {
if filename == "see gdb! definition in lib.rs about this" {
ares::interpreter::use_gdb();
ares::jets::use_gdb();
ares::jets::bits::use_gdb();
ares::jets::hash::use_gdb();
ares::jets::math::use_gdb();
ares::jets::nock::use_gdb();
ares::jets::tree::use_gdb();
ares::mem::use_gdb();
ares::mug::use_gdb();
ares::newt::use_gdb();
@ -47,9 +52,6 @@ fn main() -> io::Result<()> {
let f = File::open(filename)?;
let in_len = f.metadata()?.len();
let mut stack = NockStack::new(8 << 10 << 10, 0);
let mut cold = Cold::new(&mut stack);
let mut warm = Warm::new();
let hot = Hot::init(&mut stack);
let jammed_input = unsafe {
let in_map = Mmap::map(&f)?;
let word_len = (in_len + 7) >> 3;
@ -63,15 +65,16 @@ fn main() -> io::Result<()> {
let input_cell = input
.as_cell()
.expect("Input must be jam of subject/formula pair");
let result = interpret(
&mut stack,
&mut None,
&mut cold,
&mut warm,
hot,
input_cell.head(),
input_cell.tail(),
);
let mut context = Context {
stack: &mut stack,
newt: None,
cache: &mut Hamt::<Noun>::new(),
cold: &mut Cold::new(&mut stack);
warm: &mut Warm::new();
hot: &mut Hot::init(&mut stack);
};
let result =
interpret(&mut context, input_cell.head(), input_cell.tail()).expect("nock failed");
if let Ok(atom) = result.as_atom() {
println!("Result: {}", atom);
}

View File

@ -1,7 +1,9 @@
use crate::assert_acyclic;
use crate::assert_no_forwarding_pointers;
use crate::assert_no_junior_pointers;
use crate::noun::{Atom, Cell, CellMemory, IndirectAtom, Noun, NounAllocator};
use crate::snapshot::pma::{pma_in_arena, pma_malloc_w};
use assert_no_alloc::permit_alloc;
use either::Either::{self, Left, Right};
use ibig::Stack;
use libc::{c_void, memcmp};
@ -58,22 +60,22 @@ impl NockStack {
* The initial frame is a west frame. When the stack is initialized, a number of slots is given.
* We add three extra slots to store the previous frame, stack, and allocation pointer. For the
* initial frame, the previous allocation pointer is set to the beginning (low boundary) of the
* arena, the previous frame pointer is set to NULL, and the previous stack pointer is set to XX */
* arena, the previous frame pointer is set to NULL, and the previous stack pointer is set to NULL */
/** Size is in 64 bit words.
/** size is in 64-bit (i.e. 8-byte) words.
* top_slots is how many slots to allocate to the top stack frame.
*/
pub fn new(size: usize, top_slots: usize) -> NockStack {
let memory = MmapMut::map_anon(size << 3).expect("Mapping memory for nockstack failed");
let start = memory.as_ptr() as *const u64;
// Here, frame_pointer < alloc_pointer, so the initial frame is West
let frame_pointer = unsafe { start.add(top_slots + RESERVED) } as *mut u64;
let frame_pointer = unsafe { start.add(RESERVED + top_slots) } as *mut u64;
let stack_pointer = frame_pointer;
let alloc_pointer = unsafe { start.add(size) } as *mut u64;
unsafe {
*frame_pointer = ptr::null::<u64>() as u64; // "frame pointer" from "previous" frame
*frame_pointer.sub(STACK) = ptr::null::<u64>() as u64; // "stack pointer" from "previous" frame
*frame_pointer.sub(ALLOC) = start as u64; // "alloc pointer" from "previous" frame
*frame_pointer.sub(FRAME + 1) = ptr::null::<u64>() as u64; // "frame pointer" from "previous" frame
*frame_pointer.sub(STACK + 1) = ptr::null::<u64>() as u64; // "stack pointer" from "previous" frame
*frame_pointer.sub(ALLOC + 1) = ptr::null::<u64>() as u64; // "alloc pointer" from "previous" frame
};
NockStack {
start,
@ -86,6 +88,11 @@ impl NockStack {
}
}
/** Current frame pointer of this NockStack */
pub fn get_frame_pointer(&self) -> *const u64 {
self.frame_pointer
}
/** Checks if the current stack frame has West polarity */
#[inline]
pub fn is_west(&self) -> bool {
@ -351,6 +358,10 @@ impl NockStack {
}
unsafe fn copy(&mut self, noun: &mut Noun) {
assert_acyclic!(*noun);
assert_no_forwarding_pointers!(*noun);
assert_no_junior_pointers!(self, *noun);
self.pre_copy();
assert!(self.stack_is_empty());
let noun_ptr = noun as *mut Noun;
@ -438,7 +449,10 @@ impl NockStack {
}
}
// Set saved previous allocation pointer its new value after this allocation
assert_acyclic!(*noun);
assert_no_forwarding_pointers!(*noun);
assert_no_junior_pointers!(self, *noun);
}
pub unsafe fn struct_is_in<T>(&self, ptr: *const T, count: usize) {
@ -570,7 +584,6 @@ impl NockStack {
},
}
}
assert_acyclic!(*noun);
}
pub unsafe fn frame_pop(&mut self) {
@ -582,6 +595,18 @@ impl NockStack {
self.stack_pointer = prev_stack_ptr;
self.alloc_pointer = prev_alloc_ptr;
if self.frame_pointer.is_null()
|| self.stack_pointer.is_null()
|| self.alloc_pointer.is_null()
{
permit_alloc(|| {
panic!(
"serf: frame_pop: null NockStack pointer f={:p} s={:p} a={:p}",
self.frame_pointer, self.stack_pointer, self.alloc_pointer
);
});
}
self.pc = false;
}
@ -720,6 +745,88 @@ impl NockStack {
unsafe { self.stack_pointer == self.alloc_pointer.add(RESERVED) }
}
}
pub fn no_junior_pointers(&self, noun: Noun) -> bool {
unsafe {
if let Ok(c) = noun.as_cell() {
let mut fp: *mut u64;
let mut sp = self.stack_pointer;
let mut ap = self.alloc_pointer;
let mut pfp = *(self.prev_frame_pointer_pointer());
let mut psp = *(self.prev_stack_pointer_pointer());
let mut pap = *(self.prev_alloc_pointer_pointer());
let mut dbg_stack = Vec::new();
// Detemine range
let (rlo, rhi) = loop {
if psp.is_null() {
psp = ((self.start as u64) + ((self.size << 3) as u64)) as *mut u64;
}
let (lo, hi) = if sp < ap { (ap, psp) } else { (psp, ap) };
let ptr = c.to_raw_pointer() as *mut u64;
if ptr >= lo && ptr < hi {
break if sp < ap { (sp, ap) } else { (ap, sp) };
} else {
fp = pfp;
sp = psp;
ap = pap;
if sp < ap {
pfp = *(fp.sub(FRAME + 1)) as *mut u64;
psp = *(fp.sub(STACK + 1)) as *mut u64;
pap = *(fp.sub(ALLOC + 1)) as *mut u64;
} else {
pfp = *(fp.add(FRAME)) as *mut u64;
psp = *(fp.add(STACK)) as *mut u64;
pap = *(fp.add(ALLOC)) as *mut u64;
}
}
};
dbg_stack.push(c.head());
dbg_stack.push(c.tail());
while let Some(n) = dbg_stack.pop() {
if let Ok(a) = n.as_allocated() {
let ptr = a.to_raw_pointer();
if ptr >= rlo && ptr < rhi {
eprintln!(
"\rserf: Noun {:x} has Noun {:x} in junior of range {:p}-{:p}",
(noun.raw << 3),
(n.raw << 3),
rlo,
rhi
);
return false;
}
if let Some(c) = a.cell() {
dbg_stack.push(c.tail());
dbg_stack.push(c.head());
}
}
}
true
} else {
true
}
}
}
}
#[cfg(feature = "check_junior")]
#[macro_export]
macro_rules! assert_no_junior_pointers {
( $x:expr, $y:expr ) => {
assert_no_alloc::permit_alloc(|| {
assert!($x.no_junior_pointers($y));
})
};
}
#[cfg(not(feature = "check_junior"))]
#[macro_export]
macro_rules! assert_no_junior_pointers {
( $x:expr, $y:expr ) => {};
}
pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Noun) -> bool {
@ -746,6 +853,13 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou
* senior noun, *never vice versa*, to avoid introducing references from more senior frames
* into more junior frames, which would result in incorrect operation of the copier.
*/
assert_acyclic!(*a);
assert_acyclic!(*b);
assert_no_forwarding_pointers!(*a);
assert_no_forwarding_pointers!(*b);
assert_no_junior_pointers!(stack, *a);
assert_no_junior_pointers!(stack, *b);
// If the nouns are already word-equal we have nothing to do
if (*a).raw_equals(*b) {
return true;
@ -792,7 +906,6 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou
) == 0
{
let (_senior, junior) = senior_pointer_first(stack, x_as_ptr, y_as_ptr);
// unify
if x_as_ptr == junior {
*x = *y;
} else {
@ -842,8 +955,14 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou
}
}
stack.frame_pop();
assert_acyclic!(*a);
assert_acyclic!(*b);
assert_no_forwarding_pointers!(*a);
assert_no_forwarding_pointers!(*b);
assert_no_junior_pointers!(stack, *a);
assert_no_junior_pointers!(stack, *b);
(*a).raw_equals(*b)
}
@ -864,17 +983,19 @@ unsafe fn senior_pointer_first(
};
loop {
if low_pointer.is_null() || high_pointer.is_null() {
// we found the bottom of the stack; check entirety of the stack
low_pointer = stack.start;
high_pointer = stack.start.add(stack.size);
}
match (
a < high_pointer && a >= low_pointer,
b < high_pointer && b >= low_pointer,
) {
(true, true) => {
// both pointers are in the same frame, pick arbitrarily (lower in mem)
if a < b {
break (a, b);
} else {
break (b, a);
};
break lower_pointer_first(a, b);
}
(true, false) => break (b, a), // a is in the frame, b is not, so b is senior
(false, true) => break (a, b), // b is in the frame, a is not, so a is senior
@ -887,30 +1008,23 @@ unsafe fn senior_pointer_first(
alloc_pointer = *(frame_pointer.sub(ALLOC + 1)) as *const u64;
frame_pointer = *(frame_pointer.sub(FRAME + 1)) as *const u64;
// both pointers are in the PMA, pick arbitrarily (lower in mem)
if frame_pointer.is_null() {
if a < b {
break (a, b);
} else {
break (b, a);
}
break lower_pointer_first(a, b);
};
// previous allocation pointer
high_pointer = alloc_pointer;
// "previous previous" stack pointer. this is the other boundary of the previous allocation arena
low_pointer = *(frame_pointer.add(STACK)) as *const u64;
continue;
} else if stack_pointer > alloc_pointer {
stack_pointer = *(frame_pointer.add(STACK)) as *const u64;
alloc_pointer = *(frame_pointer.add(ALLOC)) as *const u64;
frame_pointer = *(frame_pointer.add(FRAME)) as *const u64;
// both pointers are in the PMA, pick arbitrarily (lower in mem)
if frame_pointer.is_null() {
if a < b {
break (a, b);
} else {
break (b, a);
}
break lower_pointer_first(a, b);
};
// previous allocation pointer
@ -925,6 +1039,14 @@ unsafe fn senior_pointer_first(
}
}
fn lower_pointer_first(a: *const u64, b: *const u64) -> (*const u64, *const u64) {
if a < b {
(a, b)
} else {
(b, a)
}
}
impl NounAllocator for NockStack {
unsafe fn alloc_indirect(&mut self, words: usize) -> *mut u64 {
self.indirect_alloc(words)

View File

@ -1,4 +1,6 @@
use crate::assert_acyclic;
use crate::assert_no_forwarding_pointers;
use crate::assert_no_junior_pointers;
use crate::mem::*;
use crate::noun::{Allocated, Atom, DirectAtom, Noun};
use either::Either::*;
@ -119,7 +121,11 @@ pub fn mug_u32(stack: &mut NockStack, noun: Noun) -> u32 {
if let Some(mug) = get_mug(noun) {
return mug;
}
assert_acyclic!(noun);
assert_no_forwarding_pointers!(noun);
assert_no_junior_pointers!(stack, noun);
stack.frame_push(0);
unsafe {
*(stack.push()) = noun;
@ -171,6 +177,11 @@ pub fn mug_u32(stack: &mut NockStack, noun: Noun) -> u32 {
unsafe {
stack.frame_pop();
}
assert_acyclic!(noun);
assert_no_forwarding_pointers!(noun);
assert_no_junior_pointers!(stack, noun);
get_mug(noun).expect("Noun should have a mug once it is mugged.")
}

View File

@ -58,6 +58,7 @@ use either::Either;
use std::io::{Read, Write};
use std::os::unix::prelude::FromRawFd;
use std::ptr::copy_nonoverlapping;
use std::slice::from_raw_parts_mut;
crate::gdb!();
@ -82,7 +83,9 @@ impl Newt {
fn write_noun(&mut self, stack: &mut NockStack, noun: Noun) {
let atom = jam(stack, noun);
let size = atom.size() << 3;
let mut buf = vec![0u8; size + 5];
// XX: checked add?
let buf = unsafe { from_raw_parts_mut(stack.struct_alloc::<u8>(size + 5), size + 5) };
buf[0] = 0u8;
buf[1] = size as u8;
buf[2] = (size >> 8) as u8;
buf[3] = (size >> 16) as u8;
@ -104,10 +107,14 @@ impl Newt {
);
},
};
self.output.write_all(&buf).unwrap();
self.output.write_all(buf).unwrap();
}
/** Send %ripe, the first event. */
/** Send %ripe, the first event.
*
* eve = event number
* mug = mug of Arvo after above event
*/
pub fn ripe(&mut self, stack: &mut NockStack, eve: u64, mug: u64) {
let version = T(
stack,
@ -127,7 +134,11 @@ impl Newt {
self.write_noun(stack, live);
}
/** Send %slog, pretty-printed debug output. */
/** Send %slog, pretty-printed debug output.
*
* pri = debug priority
* tank = output as tank
*/
pub fn slog(&mut self, stack: &mut NockStack, pri: u64, tank: Noun) {
let slog = T(stack, &[D(tas!(b"slog")), D(pri), tank]);
self.write_noun(stack, slog);
@ -145,19 +156,30 @@ impl Newt {
self.write_noun(stack, peek);
}
/** Send %peek %bail, unsuccessfully scried. */
/** Send %peek %bail, unsuccessfully scried.
*
* dud = goof
*/
pub fn peek_bail(&mut self, stack: &mut NockStack, dud: Noun) {
let peek = T(stack, &[D(tas!(b"peek")), D(tas!(b"bail")), dud]);
self.write_noun(stack, peek);
}
/** Send %play %done, successfully replayed events. */
/** Send %play %done, successfully replayed events.
*
* mug = mug of Arvo after full replay
*/
pub fn play_done(&mut self, stack: &mut NockStack, mug: u64) {
let play = T(stack, &[D(tas!(b"play")), D(tas!(b"done")), D(mug)]);
self.write_noun(stack, play);
}
/** Send %play %bail, failed to replay events. */
/** Send %play %bail, failed to replay events.
*
* eve = last good event number
* mug = mug of Arvo after above event
* dud = goof when trying next event
*/
pub fn play_bail(&mut self, stack: &mut NockStack, eve: u64, mug: u64, dud: Noun) {
let play = T(
stack,
@ -166,7 +188,12 @@ impl Newt {
self.write_noun(stack, play);
}
/** Send %work %done, successfully ran event. */
/** Send %work %done, successfully ran event.
*
* eve = new event number
* mug = mug of Arvo after above event
* fec = list of effects
*/
pub fn work_done(&mut self, stack: &mut NockStack, eve: u64, mug: u64, fec: Noun) {
let work = T(
stack,
@ -175,7 +202,13 @@ impl Newt {
self.write_noun(stack, work);
}
/** Send %work %swap, successfully replaced failed event. */
/** Send %work %swap, successfully replaced failed event.
*
* eve = new event number
* mug = mug of Arvo after above event
* job = event performed instead of the one given to serf by king
* fec = list of effects
*/
pub fn work_swap(&mut self, stack: &mut NockStack, eve: u64, mug: u64, job: Noun, fec: Noun) {
let work = T(
stack,
@ -184,7 +217,10 @@ impl Newt {
self.write_noun(stack, work);
}
/** Send %work %bail, failed to run event. */
/** Send %work %bail, failed to run event.
*
* lud = list of goof
*/
pub fn work_bail(&mut self, stack: &mut NockStack, lud: Noun) {
let work = T(stack, &[D(tas!(b"work")), D(tas!(b"bail")), lud]);
self.write_noun(stack, work);

View File

@ -1,6 +1,6 @@
use crate::mem::{word_size_of, NockStack};
use bitvec::prelude::{BitSlice, Lsb0};
use either::Either;
use either::{Either, Left, Right};
use ibig::{Stack, UBig};
use intmap::IntMap;
use std::fmt;
@ -44,7 +44,9 @@ pub const NO: Noun = D(1);
#[macro_export]
macro_rules! assert_acyclic {
( $x:expr ) => {
assert_no_alloc::permit_alloc(|| {
assert!(crate::noun::acyclic_noun($x));
})
};
}
@ -61,8 +63,8 @@ pub fn acyclic_noun(noun: Noun) -> bool {
fn acyclic_noun_go(noun: Noun, seen: &mut IntMap<()>) -> bool {
match noun.as_either_atom_cell() {
Either::Left(_atom) => true,
Either::Right(cell) => {
Left(_atom) => true,
Right(cell) => {
if seen.get(cell.0).is_some() {
false
} else {
@ -82,6 +84,42 @@ fn acyclic_noun_go(noun: Noun, seen: &mut IntMap<()>) -> bool {
}
}
#[cfg(feature = "check_forwarding")]
#[macro_export]
macro_rules! assert_no_forwarding_pointers {
( $x:expr ) => {
assert_no_alloc::permit_alloc(|| {
assert!(crate::noun::no_forwarding_pointers($x));
})
};
}
#[cfg(not(feature = "check_forwarding"))]
#[macro_export]
macro_rules! assert_no_forwarding_pointers {
( $x:expr ) => {};
}
pub fn no_forwarding_pointers(noun: Noun) -> bool {
let mut dbg_stack = Vec::new();
dbg_stack.push(noun);
while !dbg_stack.is_empty() {
if let Some(noun) = dbg_stack.pop() {
if unsafe { noun.raw & FORWARDING_MASK == FORWARDING_TAG } {
return false;
} else if let Ok(cell) = noun.as_cell() {
dbg_stack.push(cell.tail());
dbg_stack.push(cell.head());
}
} else {
break;
}
}
true
}
/** Test if a noun is a direct atom. */
fn is_direct_atom(noun: u64) -> bool {
noun & DIRECT_MASK == DIRECT_TAG
@ -186,6 +224,11 @@ impl DirectAtom {
pub fn as_bitslice_mut(&mut self) -> &mut BitSlice<u64, Lsb0> {
BitSlice::from_element_mut(&mut self.0)
}
pub fn as_bytes(&self) -> &[u8] {
let bytes: &[u8; 8] = unsafe { std::mem::transmute(&self.0) };
&bytes[..]
}
}
impl fmt::Display for DirectAtom {
@ -227,6 +270,16 @@ pub fn T<A: NounAllocator>(allocator: &mut A, tup: &[Noun]) -> Noun {
Cell::new_tuple(allocator, tup).as_noun()
}
/// Create $tape Noun from ASCII string
pub fn tape<A: NounAllocator>(allocator: &mut A, text: &str) -> Noun {
// XX: Needs unit tests
let mut res = D(0);
for c in text.bytes().rev() {
res = T(allocator, &[D(c as u64), res])
}
res
}
/** An indirect atom.
*
* Indirect atoms represent atoms above DIRECT_MAX as a tagged pointer to a memory buffer
@ -444,6 +497,9 @@ impl IndirectAtom {
}
}
// XX: Need a version that either:
// a) allocates on the NockStack directly for creating a tape (or even a string?)
// b) disables no-allocation, creates a string, utilitzes it (eprintf or generate tape), and then deallocates
impl fmt::Display for IndirectAtom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x")?;
@ -480,7 +536,7 @@ impl Cell {
(self.0 << 3) as *const CellMemory
}
unsafe fn to_raw_pointer_mut(&mut self) -> *mut CellMemory {
pub unsafe fn to_raw_pointer_mut(&mut self) -> *mut CellMemory {
(self.0 << 3) as *mut CellMemory
}
@ -575,6 +631,28 @@ impl fmt::Display for Cell {
}
}
impl Slots for Cell {}
impl private::RawSlots for Cell {
fn raw_slot(&self, axis: &BitSlice<u64, Lsb0>) -> Result<Noun> {
let mut noun: Noun = self.as_noun();
// Panic because all of the logic to guard against this is in Noun::RawSlots, Noun::Slots
let mut cursor = axis.last_one().expect("raw_slow somehow by-passed 0 check");
while cursor != 0 {
cursor -= 1;
// Returns Err if axis tried to descend through atom
if axis[cursor] {
noun = noun.as_cell()?.tail();
} else {
noun = noun.as_cell()?.head();
}
}
Ok(noun)
}
}
/**
* Memory representation of the contents of a cell
*/
@ -648,9 +726,9 @@ impl Atom {
pub fn as_either(&self) -> Either<DirectAtom, IndirectAtom> {
if self.is_indirect() {
unsafe { Either::Right(self.indirect) }
unsafe { Right(self.indirect) }
} else {
unsafe { Either::Left(self.direct) }
unsafe { Left(self.direct) }
}
}
@ -678,24 +756,40 @@ impl Atom {
}
}
pub fn direct(&self) -> Option<DirectAtom> {
if self.is_direct() {
unsafe { Some(self.direct) }
} else {
None
}
}
pub fn indirect(&self) -> Option<IndirectAtom> {
if self.is_indirect() {
unsafe { Some(self.indirect) }
} else {
None
}
}
pub fn size(&self) -> usize {
match self.as_either() {
Either::Left(_direct) => 1,
Either::Right(indirect) => indirect.size(),
Left(_direct) => 1,
Right(indirect) => indirect.size(),
}
}
pub fn bit_size(&self) -> usize {
match self.as_either() {
Either::Left(direct) => direct.bit_size(),
Either::Right(indirect) => indirect.bit_size(),
Left(direct) => direct.bit_size(),
Right(indirect) => indirect.bit_size(),
}
}
pub fn data_pointer(&self) -> *const u64 {
match self.as_either() {
Either::Left(_direct) => (self as *const Atom) as *const u64,
Either::Right(indirect) => indirect.data_pointer(),
Left(_direct) => (self as *const Atom) as *const u64,
Right(indirect) => indirect.data_pointer(),
}
}
@ -710,6 +804,14 @@ impl Atom {
pub fn as_noun(self) -> Noun {
Noun { atom: self }
}
pub fn as_bytes(&self) -> &[u8] {
if self.is_direct() {
unsafe { self.direct.as_bytes() }
} else {
unsafe { self.indirect.as_bytes() }
}
}
}
impl fmt::Display for Atom {
@ -750,8 +852,8 @@ impl Allocated {
pub unsafe fn forwarding_pointer(&self) -> Option<Allocated> {
match self.as_either() {
Either::Left(indirect) => indirect.forwarding_pointer().map(|i| i.as_allocated()),
Either::Right(cell) => cell.forwarding_pointer().map(|c| c.as_allocated()),
Left(indirect) => indirect.forwarding_pointer().map(|i| i.as_allocated()),
Right(cell) => cell.forwarding_pointer().map(|c| c.as_allocated()),
}
}
@ -765,9 +867,17 @@ impl Allocated {
pub fn as_either(&self) -> Either<IndirectAtom, Cell> {
if self.is_indirect() {
unsafe { Either::Left(self.indirect) }
unsafe { Left(self.indirect) }
} else {
unsafe { Either::Right(self.cell) }
unsafe { Right(self.cell) }
}
}
pub fn cell(&self) -> Option<Cell> {
if self.is_cell() {
unsafe { Some(self.cell) }
} else {
None
}
}
@ -868,17 +978,57 @@ impl Noun {
pub fn as_either_atom_cell(&self) -> Either<Atom, Cell> {
if self.is_cell() {
unsafe { Either::Right(self.cell) }
unsafe { Right(self.cell) }
} else {
unsafe { Either::Left(self.atom) }
unsafe { Left(self.atom) }
}
}
pub fn as_either_direct_allocated(&self) -> Either<DirectAtom, Allocated> {
if self.is_direct() {
unsafe { Either::Left(self.direct) }
unsafe { Left(self.direct) }
} else {
unsafe { Either::Right(self.allocated) }
unsafe { Right(self.allocated) }
}
}
pub fn atom(&self) -> Option<Atom> {
if self.is_atom() {
unsafe { Some(self.atom) }
} else {
None
}
}
pub fn cell(&self) -> Option<Cell> {
if self.is_cell() {
unsafe { Some(self.cell) }
} else {
None
}
}
pub fn direct(&self) -> Option<DirectAtom> {
if self.is_direct() {
unsafe { Some(self.direct) }
} else {
None
}
}
pub fn indirect(&self) -> Option<IndirectAtom> {
if self.is_indirect() {
unsafe { Some(self.indirect) }
} else {
None
}
}
pub fn allocated(&self) -> Option<Allocated> {
if self.is_allocated() {
unsafe { Some(self.allocated) }
} else {
None
}
}
@ -950,8 +1100,8 @@ impl Noun {
if allocated.get_metadata() & (1 << 32) == 0 {
allocated.set_metadata(allocated.get_metadata() | (1 << 32));
match allocated.as_either() {
Either::Left(indirect) => indirect.size() + 2,
Either::Right(cell) => {
Left(indirect) => indirect.size() + 2,
Right(cell) => {
word_size_of::<CellMemory>()
+ cell.head().mass_wind(inside)
+ cell.tail().mass_wind(inside)
@ -973,7 +1123,7 @@ impl Noun {
if let Ok(allocated) = self.as_allocated() {
if inside(allocated.to_raw_pointer()) {
allocated.set_metadata(allocated.get_metadata() & !(1 << 32));
if let Either::Right(cell) = allocated.as_either() {
if let Right(cell) = allocated.as_either() {
cell.head().mass_unwind(inside);
cell.tail().mass_unwind(inside);
}
@ -1010,6 +1160,23 @@ impl fmt::Display for Noun {
}
}
impl Slots for Noun {}
impl private::RawSlots for Noun {
fn raw_slot(&self, axis: &BitSlice<u64, Lsb0>) -> Result<Noun> {
match self.as_either_atom_cell() {
Right(cell) => cell.raw_slot(axis),
Left(_atom) => {
if axis.last_one() == Some(0) {
Ok(*self)
} else {
// Axis tried to descend through atom
Err(Error::NotCell)
}
}
}
}
}
/**
* An allocation object (probably a mem::NockStack) which can allocate a memory buffer sized to
* a certain number of nouns
@ -1024,3 +1191,44 @@ pub trait NounAllocator: Sized {
/** Allocate memory for a cell */
unsafe fn alloc_cell(&mut self) -> *mut CellMemory;
}
/**
* Implementing types allow component Nouns to be retreived by numeric axis
*/
pub trait Slots: private::RawSlots {
/**
* Retrieve component Noun at given axis, or fail with descriptive error
*/
fn slot(&self, axis: u64) -> Result<Noun> {
if axis == 0 {
// 0 is not allowed as an axis
Err(Error::NotRepresentable)
} else {
self.raw_slot(BitSlice::from_element(&axis))
}
}
/**
* Retrieve component Noun at axis given as Atom, or fail with descriptive error
*/
fn slot_atom(&self, atom: Atom) -> Result<Noun> {
self.raw_slot(atom.as_bitslice())
}
}
/**
* Implementation methods that should not be made available to derived crates
*/
mod private {
use crate::noun::{BitSlice, Lsb0, Noun, Result};
/**
* Implementation of the Slots trait
*/
pub trait RawSlots {
/**
* Actual logic of retreiving Noun object at some axis
*/
fn raw_slot(&self, axis: &BitSlice<u64, Lsb0>) -> Result<Noun>;
}
}

View File

@ -1,21 +1,174 @@
use crate::interpreter::{interpret, raw_slot};
use crate::hamt::Hamt;
use crate::interpreter;
use crate::interpreter::{inc, interpret, Tone};
use crate::jets::cold::Cold;
use crate::jets::hot::Hot;
use crate::jets::warm::Warm;
use crate::jets::nock::util::mook;
use crate::jets::text::util::lent;
use crate::mem::NockStack;
use crate::mug::mug_u32;
use crate::newt::Newt;
use crate::noun::{Noun, D, T};
use crate::snapshot::{self, Snapshot};
use crate::noun::{Cell, Noun, Slots, D, T};
use crate::snapshot::double_jam::DoubleJam;
use crate::snapshot::Snapshot;
use ares_macros::tas;
use signal_hook;
use signal_hook::consts::SIGINT;
use std::fs::create_dir_all;
use std::io;
use std::path::PathBuf;
use std::thread::sleep;
use std::time;
use std::result::Result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
crate::gdb!();
struct Context {
epoch: u64,
event_num: u64,
snapshot: DoubleJam,
arvo: Noun,
mug: u32,
stack: NockStack,
newt: Newt,
cache: Hamt<Noun>,
// XX: persistent memo cache
cold: Cold,
warm: Warm,
hot: Hot,
}
impl Context {
pub fn new(snap_path: &PathBuf) -> Self {
// TODO: switch to Pma when ready
// let snap = &mut snapshot::pma::Pma::new(snap_path);
let mut snapshot = DoubleJam::new(snap_path);
let mut stack = NockStack::new(256 << 10 << 10, 0);
let newt = Newt::new();
let cache = Hamt::<Noun>::new();
let cold = Cold::new(stack);
let warm = Warm::new();
let hot = Hot::init(stack);
let (epoch, event_num, arvo) = snapshot.load(&mut stack).unwrap_or((0, 0, D(0)));
let mug = mug_u32(&mut stack, arvo);
Context {
epoch,
event_num,
snapshot,
arvo,
mug,
stack,
newt,
cache,
cold,
warm,
hot,
}
}
//
// Getters
//
pub fn epoch(&self) -> u64 {
self.epoch
}
pub fn event_num(&self) -> u64 {
self.event_num
}
pub fn arvo(&self) -> Noun {
self.arvo
}
pub fn stack_as_mut(&mut self) -> &mut NockStack {
&mut self.stack
}
pub fn for_interpreter(&mut self) -> interpreter::Context {
self.cache = Hamt::<Noun>::new();
interpreter::Context {
stack: &mut self.stack,
newt: Some(&mut self.newt),
cache: &mut self.cache,
cold: &mut self.cold,
warm: &mut self.warm,
hot: &mut self.hot,
}
}
//
// Setters
//
pub fn event_update(&mut self, new_event_num: u64, new_arvo: Noun) {
// XX: assert event numbers are continuous
self.arvo = new_arvo;
self.event_num = new_event_num;
self.snapshot.save(&mut self.stack, &mut self.arvo);
self.mug = mug_u32(&mut self.stack, self.arvo);
}
//
// Snapshot functions
//
pub fn sync(&mut self) {
self.snapshot
.sync(&mut self.stack, self.epoch, self.event_num);
}
//
// Newt functions
//
pub fn next(&mut self) -> Option<Noun> {
self.newt.next(&mut self.stack)
}
pub fn ripe(&mut self) {
self.newt
.ripe(&mut self.stack, self.event_num, self.mug as u64);
}
pub fn live(&mut self) {
self.newt.live(&mut self.stack);
}
pub fn peek_done(&mut self, dat: Noun) {
self.newt.peek_done(&mut self.stack, dat);
}
pub fn play_done(&mut self) {
self.newt.play_done(&mut self.stack, self.mug as u64);
}
pub fn play_bail(&mut self, dud: Noun) {
self.newt
.play_bail(&mut self.stack, self.event_num, self.mug as u64, dud);
}
pub fn work_done(&mut self, fec: Noun) {
self.newt
.work_done(&mut self.stack, self.event_num, self.mug as u64, fec);
}
pub fn work_swap(&mut self, job: Noun, fec: Noun) {
self.newt
.work_swap(&mut self.stack, self.event_num, self.mug as u64, job, fec);
}
pub fn work_bail(&mut self, lud: Noun) {
self.newt.work_bail(&mut self.stack, lud);
}
}
#[allow(dead_code)]
const LOAD_AXIS: u64 = 4;
const PEEK_AXIS: u64 = 22;
@ -23,12 +176,20 @@ const POKE_AXIS: u64 = 23;
#[allow(dead_code)]
const WISH_AXIS: u64 = 10;
// Necessary because Arc::new is not const
lazy_static! {
pub static ref TERMINATOR: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
}
/**
* This is suitable for talking to the king process. To test, change the arg_c[0] line in
* u3_lord_init in vere to point at this binary and start vere like normal.
*/
pub fn serf() -> io::Result<()> {
sleep(time::Duration::from_secs(0));
// Register SIGINT signal hook to set flag first time, shutdown second time
signal_hook::flag::register_conditional_shutdown(SIGINT, 1, Arc::clone(&TERMINATOR))?;
signal_hook::flag::register(SIGINT, Arc::clone(&TERMINATOR))?;
let snap_path_string = std::env::args()
.nth(2)
.ok_or(io::Error::new(io::ErrorKind::Other, "no pier path"))?;
@ -36,114 +197,203 @@ pub fn serf() -> io::Result<()> {
snap_path.push(".urb");
snap_path.push("chk");
create_dir_all(&snap_path)?;
// TODO: switch to Pma when ready
// let snap = &mut snapshot::pma::Pma::new(snap_path);
let snap = &mut snapshot::double_jam::DoubleJam::new(snap_path);
let stack = &mut NockStack::new(96 << 10 << 10, 0);
let mut cold = Cold::new(stack);
let mut warm = Warm::new();
let hot = Hot::init(stack);
let newt = &mut Newt::new();
let (_epoch, mut event_number, mut arvo) = snap.load(stack).unwrap_or((0, 0, D(0)));
let mug = mug_u32(stack, arvo);
newt.ripe(stack, event_number, mug as u64);
let mut context = Context::new(&snap_path);
context.ripe();
// Can't use for loop because it borrows newt
while let Some(writ) = newt.next(stack) {
let tag = raw_slot(writ, 2).as_direct().unwrap();
while let Some(writ) = context.next() {
// XX: probably want to bookend this logic frame_push / frame_pop
// preserve jet state and persistent cache, lose everything else
let tag = slot(writ, 2)?.as_direct().unwrap();
match tag.data() {
tas!(b"live") => {
let inner = raw_slot(writ, 6);
match inner.as_direct().unwrap().data() {
let inner = slot(writ, 6)?.as_direct().unwrap();
match inner.data() {
tas!(b"cram") => eprintln!("cram"),
tas!(b"exit") => eprintln!("exit"),
tas!(b"save") => {
// XX what is eve for?
eprintln!("save");
snap.sync(stack, 0, event_number);
context.sync();
}
tas!(b"meld") => eprintln!("meld"),
tas!(b"pack") => eprintln!("pack"),
_ => eprintln!("unknown live"),
}
newt.live(stack);
context.live();
}
tas!(b"peek") => {
let sam = raw_slot(writ, 7);
let res = slam(stack, newt, &mut cold, &mut warm, hot, arvo, PEEK_AXIS, sam);
newt.peek_done(stack, res);
let sam = slot(writ, 7)?;
let res =
slam(&mut context, PEEK_AXIS, sam).expect("peek error handling unimplemented");
context.peek_done(res);
}
tas!(b"play") => {
let run = if event_number == 0 {
let lit = slot(writ, 7)?;
if context.epoch() == 0 && context.event_num() == 0 {
// apply lifecycle to first batch
let lit = raw_slot(writ, 7);
let sub = T(stack, &[D(0), D(3)]);
let lyf = T(stack, &[D(2), sub, D(0), D(2)]);
let gat =
interpret(stack, &mut Some(newt), &mut cold, &mut warm, hot, lit, lyf);
arvo = raw_slot(gat, 7);
false
play_life(&mut context, lit);
} else {
true
play_list(&mut context, lit);
};
// do we need to assert something here?
// event_number = raw_slot(writ, 6).as_direct().unwrap().data();
let mut lit = raw_slot(writ, 7);
while let Ok(cell) = lit.as_cell() {
if run {
let ovo = cell.head();
let res =
slam(stack, newt, &mut cold, &mut warm, hot, arvo, POKE_AXIS, ovo)
.as_cell()
.unwrap();
arvo = res.tail();
}
event_number += 1;
lit = cell.tail();
}
snap.save(stack, &mut arvo);
newt.play_done(stack, 0);
}
tas!(b"work") => {
let ovo = raw_slot(writ, 7);
let res = slam(stack, newt, &mut cold, &mut warm, hot, arvo, POKE_AXIS, ovo)
.as_cell()
.unwrap();
let fec = res.head();
arvo = res.tail();
snap.save(stack, &mut arvo);
event_number += 1;
newt.work_done(stack, event_number, 0, fec);
// XX: what is in slot 6? it's mil_w in Vere Serf
let job = slot(writ, 7)?;
work(&mut context, job);
}
_ => panic!("got message with unknown tag {}", tag),
};
clear_interrupt();
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn slam(
stack: &mut NockStack,
newt: &mut Newt,
cold: &mut Cold,
warm: &mut Warm,
hot: Hot,
core: Noun,
axis: u64,
ovo: Noun,
) -> Noun {
let pul = T(stack, &[D(9), D(axis), D(0), D(2)]);
let sam = T(stack, &[D(6), D(0), D(7)]);
let fol = T(stack, &[D(8), pul, D(9), D(2), D(10), sam, D(0), D(2)]);
let sub = T(stack, &[core, ovo]);
interpret(stack, &mut Some(newt), cold, warm, hot, sub, fol)
fn burn(context: &mut Context, subject: Noun, formula: Noun) -> Result<Noun, Tone> {
let burn_context = &mut context.for_interpreter();
interpret(burn_context, subject, formula)
}
fn slam(context: &mut Context, axis: u64, ovo: Noun) -> Result<Noun, Tone> {
let arvo = context.arvo();
let pul = T(context.stack_as_mut(), &[D(9), D(axis), D(0), D(2)]);
let sam = T(context.stack_as_mut(), &[D(6), D(0), D(7)]);
let fol = T(
context.stack_as_mut(),
&[D(8), pul, D(9), D(2), D(10), sam, D(0), D(2)],
);
let sub = T(context.stack_as_mut(), &[arvo, ovo]);
burn(context, sub, fol)
}
fn goof(context: &mut Context, trace: Noun) -> Noun {
let tone = Cell::new(context.stack_as_mut(), D(2), trace);
let mook_context = &mut context.for_interpreter();
let tang = mook(mook_context, tone, false)
.expect("serf: goof: +mook crashed on bail")
.tail();
// XX: noun::Tone or noun::NockErr should use a bail enum system similar to u3m_bail motes;
// might be able to replace NockErr with mote and map determinism to individual motes;
// for, always set to %exit
T(mook_context.stack, &[D(tas!(b"exit")), tang])
}
/** Run slam, process stack trace to tang if error */
fn soft(context: &mut Context, ovo: Noun) -> Result<Noun, Noun> {
match slam(context, POKE_AXIS, ovo) {
Ok(res) => Ok(res),
Err(Tone::Error(_, trace)) => Err(goof(context, trace)),
Err(Tone::Blocked(_)) => panic!("soft: blocked err handling unimplemented"),
}
}
fn play_life(context: &mut Context, eve: Noun) {
let sub = T(context.stack_as_mut(), &[D(0), D(3)]);
let lyf = T(context.stack_as_mut(), &[D(2), sub, D(0), D(2)]);
match burn(context, eve, lyf) {
Ok(gat) => {
let eved = lent(eve).expect("serf: play: boot event number failure") as u64;
let arvo = slot(gat, 7).expect("serf: play: lifecycle didn't return initial Arvo");
context.event_update(eved, arvo);
context.play_done();
}
Err(Tone::Error(_, trace)) => {
let goof = goof(context, trace);
context.play_bail(goof);
}
Err(Tone::Blocked(_)) => {
panic!("play: blocked err handling unimplemented")
}
}
}
fn play_list(context: &mut Context, mut lit: Noun) {
let mut eve = context.event_num();
while let Ok(cell) = lit.as_cell() {
let ovo = cell.head();
match soft(context, ovo) {
Ok(res) => {
let arvo = res
.as_cell()
.expect("serf: work: +slam returned atom")
.tail();
eve += 1;
context.event_update(eve, arvo);
}
Err(goof) => {
return context.play_bail(goof);
}
}
lit = cell.tail();
}
context.play_done();
}
fn work(context: &mut Context, job: Noun) {
match soft(context, job) {
Ok(res) => {
let cell = res.as_cell().expect("serf: work: +slam returned atom");
let fec = cell.head();
let eve = context.event_num();
context.event_update(eve + 1, cell.tail());
context.work_done(fec);
}
Err(goof) => {
work_swap(context, job, goof);
}
}
}
fn work_swap(context: &mut Context, job: Noun, goof: Noun) {
// TODO: on decryption failure in aes_siv, should bail as fast as
// possible, without rendering stack trace or injecting crud event. See
// c3__evil in vere.
clear_interrupt();
// crud = [+(now) [%$ %arvo ~] [%crud goof ovo]]
let job_cell = job.as_cell().expect("serf: work: job not a cell");
let job_now = job_cell.head().as_atom().expect("serf: work: now not atom");
let now = inc(context.stack_as_mut(), job_now).as_noun();
let wire = T(context.stack_as_mut(), &[D(0), D(tas!(b"arvo")), D(0)]);
let crud = T(
context.stack_as_mut(),
&[now, wire, D(tas!(b"crud")), goof, job_cell.tail()],
);
match soft(context, crud) {
Ok(res) => {
let cell = res.as_cell().expect("serf: work: crud +slam returned atom");
let fec = cell.head();
let eve = context.event_num();
context.event_update(eve + 1, cell.tail());
context.work_swap(crud, fec);
}
Err(goof_crud) => {
work_bail(context, &[goof_crud, goof]);
}
}
}
fn work_bail(context: &mut Context, goofs: &[Noun]) {
let lest = T(context.stack_as_mut(), goofs);
let lud = T(context.stack_as_mut(), &[lest, D(0)]);
context.work_bail(lud);
}
fn slot(noun: Noun, axis: u64) -> io::Result<Noun> {
noun.slot(axis)
.map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis"))
}
fn clear_interrupt() {
(*TERMINATOR).store(false, Ordering::Relaxed);
}

View File

@ -110,6 +110,7 @@ impl Snapshot for DoubleJam {
}
fn save(&mut self, _stack: &mut NockStack, noun: &mut Noun) {
// XX: I don't think this needs to be mut
self.noun = *noun;
}