mirror of
https://github.com/urbit/ares.git
synced 2024-12-24 22:01:49 +03:00
Merge branch 'status' into fast-jets
This commit is contained in:
commit
9f147bcb2c
2
.github/workflows/ares-shared.yml
vendored
2
.github/workflows/ares-shared.yml
vendored
@ -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
|
||||
|
@ -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))]
|
||||
|
@ -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
|
||||
|
@ -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))]
|
||||
|
@ -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))]
|
||||
|
874
hoon/scaffolding/playpen.hoon
Normal file
874
hoon/scaffolding/playpen.hoon
Normal 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)
|
||||
--
|
98
hoon/scaffolding/toddler.hoon
Normal file
98
hoon/scaffolding/toddler.hoon
Normal 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
29
rust/ares/Cargo.lock
generated
@ -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"
|
||||
|
@ -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_acyclic=[]
|
||||
sham_hints=[]
|
||||
check_all = [ "check_acyclic", "check_forwarding", "check_junior" ]
|
||||
check_acyclic = []
|
||||
check_forwarding = []
|
||||
check_junior = []
|
||||
sham_hints = []
|
||||
|
@ -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
@ -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
713
rust/ares/src/jets/bits.rs
Normal 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
140
rust/ares/src/jets/form.rs
Normal 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);
|
||||
}
|
||||
}
|
68
rust/ares/src/jets/hash.rs
Normal file
68
rust/ares/src/jets/hash.rs
Normal 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
375
rust/ares/src/jets/nock.rs
Normal 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);
|
||||
}
|
||||
}
|
58
rust/ares/src/jets/text.rs
Normal file
58
rust/ares/src/jets/text.rs
Normal 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);
|
||||
}
|
||||
}
|
82
rust/ares/src/jets/tree.rs
Normal file
82
rust/ares/src/jets/tree.rs
Normal 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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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.")
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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>;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user