Merge remote-tracking branch 'origin/next' into philip/aquarium

This commit is contained in:
Philip Monk 2019-02-22 16:21:03 -08:00
commit 55b708d1b7
No known key found for this signature in database
GPG Key ID: B66E1F02604E44EC
15 changed files with 401 additions and 122 deletions

View File

@ -1 +1 @@
315634d99b0a0d7760267eacc0b32465e03ea981
2d571aa1681506e85aa66a3715d0f1bc0298aeba

View File

@ -83,16 +83,6 @@ function barMass(urb) {
}
Promise.resolve(urbit)
// XX temporary
// send ctrl-x to select dojo
//
.then(function(){
return urbit.expect(/talk\[\] /)
.then(function() {
return urbit.pty.write("\x18")
})
.then(function() { return urbit })
})
.then(actions.safeBoot)
.then(function(){
return barMass(urbit);

View File

@ -620,7 +620,6 @@
-.userspace-ova.pil
[//http/0v1n.2m9vh %born ~]
[//http/0v1n.2m9vh %live 8.080 `8.445]
[//term/1 %belt %ctl `@c`%x]
==
=. this abet-pe:initted
=. init-cache

View File

@ -169,6 +169,7 @@
++ poke-helm-tlon-init-stream (wrap poke-tlon-init-stream):from-helm
++ poke-helm-automass (wrap poke-automass):from-helm
++ poke-helm-cancel-automass (wrap poke-cancel-automass):from-helm
++ poke-helm-bonk (wrap poke-bonk):from-helm
++ poke-hood-sync (wrap poke-sync):from-kiln
++ poke-kiln-commit (wrap poke-commit):from-kiln
++ poke-kiln-info (wrap poke-info):from-kiln

7
gen/hood/bonk.hoon Normal file
View File

@ -0,0 +1,7 @@
:: Helm: bonk ames
::
:::: /hoon/bonk/hood/gen
::
/? 310
:- %say
|=({^ ~ ~} helm-bonk+~)

View File

@ -99,12 +99,12 @@
^- part
:* %drum
%2
~ :: sys
(deft-fish our) :: eel
(deft-apes our) :: ray
~ :: fur
~ :: bin
== ::
sys=~
eel=(deft-fish our)
ray=(deft-apes our)
fur=~
bin=~
==
::
::
++ en-gill :: gill to wire
@ -245,7 +245,9 @@
::
++ se-adit :: update servers
^+ .
%+ roll ~(tap in ray)
:: ensure dojo connects after talk
=* dojo-on-top aor
%+ roll (sort ~(tap in ray) dojo-on-top)
=< .(con +>)
|: $:{wel/well:gall con/_..se-adit} ^+ con
=. +>.$ con

View File

@ -37,7 +37,8 @@
=+ sez=(fall (~(get by hoc) ost) $:session)
=> |% :: arvo structures
++ card ::
$% {$conf wire dock $load ship term} ::
$% [%bonk wire ~] ::
{$conf wire dock $load ship term} ::
{$flog wire flog:dill} ::
[%mint wire our=ship p=ship q=safe:rights:jael]
{$nuke wire ship} ::
@ -106,6 +107,12 @@
|= ~
abet:(emit %rest way.mass-timer.sez nex.mass-timer.sez)
::
++ poke-bonk
|= ~
~& .^((unit @da) %a /(scot %p our)/time/(scot %da now)/(scot %p our))
%- %- slog :_ ~ .^(tank %b /(scot %p our)/timers/(scot %da now))
abet:(emit %bonk /bonk ~)
::
++ take-wake-automass
|= [way=wire ~]
=. nex.mass-timer.sez (add now tim.mass-timer.sez)

150
lib/number-to-words.hoon Normal file
View File

@ -0,0 +1,150 @@
:: |number-to-words: conversion of unsigned integers to a tape
::
:: returns a unit because not all numbers can always be represented
::
|%
++ numbers
|%
++ ten 10
++ one-hundred 100
++ one-thousand (pow 10 3)
++ one-million (pow 10 6)
++ one-billion (pow 10 9)
++ one-trillion (pow 10 12)
++ one-quadrillion (pow 10 15)
++ one-quintillion (pow 10 18)
++ one-sextillion (pow 10 21)
++ one-septillion (pow 10 24)
++ one-octillion (pow 10 27)
++ one-nonillion (pow 10 30)
++ one-decillion (pow 10 33)
++ one-undecillion (pow 10 36)
++ one-duodecillion (pow 10 39)
++ one-tredecillion (pow 10 42)
++ one-quattuordecillion (pow 10 45)
++ one-quindecillion (pow 10 48)
++ one-sexdecillion (pow 10 51)
++ one-septendecillion (pow 10 54)
++ one-octodecillion (pow 10 57)
++ one-novemdecillion (pow 10 60)
++ one-vigintillion (pow 10 63)
++ max (pow 10 66)
--
++ eng-us
|%
++ to-words
|= num=@u
^- (unit tape)
=+ numbers
?: (gte num max)
~
:- ~
|-
^- tape
:: 0-19
?: =(num 0) "zero"
?: =(num 1) "one"
?: =(num 2) "two"
?: =(num 3) "three"
?: =(num 4) "four"
?: =(num 5) "five"
?: =(num 6) "six"
?: =(num 7) "seven"
?: =(num 8) "eight"
?: =(num 9) "nine"
?: =(num 10) "ten"
?: =(num 11) "eleven"
?: =(num 12) "twelve"
?: =(num 13) "thirteen"
?: =(num 14) "fourteen"
?: =(num 15) "fifteen"
?: =(num 16) "sixteen"
?: =(num 17) "seventeen"
?: =(num 18) "eighteen"
?: =(num 19) "nineteen"
:: 20-99
::
:: tpl: tens place
:: rem: ones place
:: sfx: suffix
::
=/ tpl (div num ten)
=/ rem (mod num ten)
=/ sfx
?: |(=(rem 0) (gte tpl 10))
~
['-' $(num rem)]
?: =(tpl 2) (weld "twenty" sfx)
?: =(tpl 3) (weld "thirty" sfx)
?: =(tpl 4) (weld "forty" sfx)
?: =(tpl 5) (weld "fifty" sfx)
?: =(tpl 6) (weld "sixty" sfx)
?: =(tpl 7) (weld "seventy" sfx)
?: =(tpl 8) (weld "eighty" sfx)
?: =(tpl 9) (weld "ninety" sfx)
:: 100-max
::
:: num-break: repeated pattern from 100 on
::
=/ num-break
::
:: min: minimum to qualify for this break
:: str: english word for this break
::
|= [min=@u str=tape]
=/ rem (mod num min)
;: weld
^$(num (div num min))
[' ' str]
?: =(rem 0)
~
%+ weld
?:((lth rem one-hundred) " and " ", ")
^$(num rem)
==
::
?: (lth num one-thousand)
(num-break one-hundred "hundred")
?: (lth num one-million)
(num-break one-thousand "thousand")
?: (lth num one-billion)
(num-break one-million "million")
?: (lth num one-trillion)
(num-break one-billion "billion")
?: (lth num one-quadrillion)
(num-break one-trillion "trillion")
?: (lth num one-quintillion)
(num-break one-quadrillion "quadrillion")
?: (lth num one-sextillion)
(num-break one-quintillion "quintillion")
?: (lth num one-septillion)
(num-break one-sextillion "sextillion")
?: (lth num one-octillion)
(num-break one-septillion "septillion")
?: (lth num one-nonillion)
(num-break one-octillion "octillion")
?: (lth num one-decillion)
(num-break one-nonillion "nonillion")
?: (lth num one-undecillion)
(num-break one-decillion "decillion")
?: (lth num one-duodecillion)
(num-break one-undecillion "undecillion")
?: (lth num one-tredecillion)
(num-break one-duodecillion "duodecillion")
?: (lth num one-quattuordecillion)
(num-break one-tredecillion "tredecillion")
?: (lth num one-quindecillion)
(num-break one-quattuordecillion "quattuordecillion")
?: (lth num one-sexdecillion)
(num-break one-quindecillion "quindecillion")
?: (lth num one-septendecillion)
(num-break one-sexdecillion "sexdecillion")
?: (lth num one-octodecillion)
(num-break one-septendecillion "septendecillion")
?: (lth num one-novemdecillion)
(num-break one-octodecillion "octodecillion")
?: (lth num one-vigintillion)
(num-break one-novemdecillion "novemdecillion")
(num-break one-vigintillion "vigintillion")
--
--

View File

@ -778,11 +778,20 @@
=. q.q.ovo
:- %userspace
:- %|
:~ hoon+`pit
zuse+`bud
:: hoon-cache+`p.niz
:~ hoon+&+pit
zuse+&+bud
:+ %caches %|
%+ turn
%+ sort vanes
|=([a=[lab=@tas *] b=[lab=@tas *]] (aor lab.a lab.b))
|=([label=@tas =vane] [(cat 3 %vane- label) %& worm.vane])
q.q.ovo
dot+`.
:+ %vases %|
%+ turn
%+ sort vanes
|=([a=[lab=@tas *] b=[lab=@tas *]] (aor lab.a lab.b))
|=([label=@tas =vane] [(cat 3 %vane- label) %& vase.vane])
dot+&+.
==
[[~ ovo] +>.$]
:: add entropy
@ -916,4 +925,3 @@
|= txt/@
q:(slap bud (ream txt))
--

View File

@ -6,7 +6,7 @@
=, ames
:: this number needs to be below 8
::
=+ protocol-version=0
=+ protocol-version=1
|%
+= move [p=duct q=(wind note:able gift:able)] :: local move
:: |pact: internal packet structures
@ -1331,6 +1331,8 @@
::
%pito
:_ fox(tim `p.bon)
%- flop
^- (list move)
:- [gad.fox %pass /ames %b %wait p.bon]
?~ tim.fox ~
[gad.fox %pass /ames %b %rest u.tim.fox]~
@ -1461,6 +1463,13 @@
%barn
:_ fox(gad hen)
[%bock ~]~
::
%bonk
:_ fox
?~ tim.fox
~& %ames-bonk-e
~
[%pito u.tim.fox]~
::
%hear
(~(gnaw am [our now fox ski]) %good p.kyz q.kyz)
@ -1508,7 +1517,14 @@
[[%wine who " has sunk"]~ fox]
::
%vega
[~ fox]
:: re-initialize our cryptosuite B cores
::
=/ =wund
%+ turn
val.ton.fox
|= [=life =ring *]
[life ring (nol:nu:crub:crypto ring)]
[~ fox(val.ton wund)]
::
%wake
(~(wake am [our now fox ski]) hen)
@ -1536,11 +1552,28 @@
?. =(our his)
~
``[%noun !>(pals:~(um am [our now fox ski]))]
?: ?=([%time ~] tyl)
?. =(our his)
~
``[%noun !>(tim.fox)]
~
::
++ wegh
^- mass
:+ %ames %|
:~ dot+&+fox
:~ :+ %town %|
=> ton.fox
:~ wund+&+val
deed+&+law
fast+&+seh
them+&+hoc
==
:+ %corn %|
=> zac.fox
:~ incoming+&+nys
complete+&+olz
neighbor+&+wab
==
dot+&+fox
==
--

View File

@ -25,48 +25,38 @@
|%
:: %entry-points
::
:: +born: handle urbit restart
:: +born: urbit restarted; refresh :next-wake and store wakeup timer duct
::
++ born
^+ [moves state]
:: store this duct for setting unix wakeup timers
::
=. unix-duct.state duct
:: process any elapsed timers and clear and reset :next-wake
::
=> notify-clients
set-wake(next-wake.state ~)
++ born set-unix-wake(next-wake.state ~, unix-duct.state duct)
:: +crud: error report; hand off to %dill to be printed
::
++ crud
|= [p=@tas q=tang]
^+ [moves state]
[[duct %slip %d %flog %crud p q]~ state]
:: +rest: cancel the timer at :date, resetting :next-wake if needed
:: +rest: cancel the timer at :date, then adjust unix wakeup
:: +wait: set a new timer at :date, then adjust unix wakeup
::
++ rest
|= date=@da
^+ [moves state]
::
=. timers.state (unset-timer [date duct])
set-wake
++ rest |=(date=@da set-unix-wake(timers.state (unset-timer [date duct])))
++ wait |=(date=@da set-unix-wake(timers.state (set-timer [date duct])))
:: +vega: learn of a kernel upgrade
::
++ vega
[moves state]
:: +wait: set a new timer at :date, resetting :next-wake if needed
::
++ wait
|= date=@da
^+ [moves state]
=. timers.state (set-timer [date duct])
set-wake
:: +wake: unix says we should wake up; notify clients and set :next-wake
++ vega [moves state]
:: +wake: unix says wake up; process the elapsed timer and set :next-wake
::
++ wake
^+ [moves state]
=> notify-clients
set-wake(next-wake.state ~)
::
?~ timers.state ~|(%behn-wake-no-timer !!)
:: if unix woke us too early, retry by resetting the unix wakeup timer
::
?: (gth date.i.timers.state now)
~? debug=%.n [%behn-wake-too-soon `@dr`(sub date.i.timers.state now)]
set-unix-wake(next-wake.state ~)
:: pop first timer, tell vane it has elapsed, and adjust next unix wakeup
::
=< set-unix-wake
(emit-vane-wake(timers.state t.timers.state) duct.i.timers.state)
:: +wegh: produce memory usage report for |mass
::
++ wegh
@ -82,28 +72,10 @@
::+|
::
++ event-core .
:: +notify-clients: wake up vanes whose timers have expired
:: +emit-vane-wake: produce a move to wake a vane; assumes no prior moves
::
:: When we return the list moves to clients, we flop them so they're in
:: the same order as they were in :timers.
::
++ notify-clients
=* timers timers.state
^+ event-core
::
?~ timers
=. moves (flop moves)
event-core
::
?: (gth date.i.timers now)
=. moves (flop moves)
event-core
::
=. moves [[duct.i.timers %give %wake ~] moves]
=> .(timers t.timers)
=. moves (flop moves)
event-core
:: +set-wake: set or unset a unix timer to wake us when next timer expires
++ emit-vane-wake |=(=^duct event-core(moves [duct %give %wake ~]~))
:: +emit-doze: set new unix wakeup timer in state and emit move to unix
::
:: We prepend the unix %doze event so that it is handled first. Arvo must
:: handle this first because the moves %behn emits will get handled in
@ -113,30 +85,40 @@
:: back into %behn and emits a second %doze, the second %doze would be
:: handled by unix first which is incorrect.
::
++ set-wake
^+ [moves state]
++ emit-doze
|= =date=(unit @da)
^+ event-core
:: make sure we don't try to wake up in the past
::
=? date-unit ?=(^ date-unit) `(max now u.date-unit)
::
%_ event-core
next-wake.state date-unit
moves [[unix-duct.state %give %doze date-unit] moves]
==
:: +set-unix-wake: set or unset next unix wakeup timer based on :i.timers
::
++ set-unix-wake
=< [moves state]
^+ event-core
::
=* next-wake next-wake.state
=* timers timers.state
=* unix-duct unix-duct.state
:: if no timers, cancel existing wakeup timer or no-op
::
?~ timers
?~ next-wake
[moves state]
:_ state(next-wake ~)
[[unix-duct %give %doze ~] moves]
event-core
(emit-doze ~)
:: if :next-wake is in the past or not soon enough, reset it
::
?^ next-wake
?: &((gte date.i.timers u.next-wake) (lte now u.next-wake))
[moves state]
:_ state(next-wake `date.i.timers)
[[unix-duct %give %doze `date.i.timers] moves]
event-core
(emit-doze `date.i.timers)
:: there was no unix wakeup timer; set one
::
:_ state(next-wake `date.i.timers)
[[unix-duct %give %doze `date.i.timers] moves]
(emit-doze `date.i.timers)
:: +set-timer: set a timer, maintaining the sort order of the :timers list
::
++ set-timer
@ -149,6 +131,7 @@
:: ignore duplicates
::
?: =(t i.timers)
~? debug=%.n [%behn-set-duplicate t]
timers
:: timers at the same date form a fifo queue
::
@ -160,11 +143,12 @@
::
++ unset-timer
=* timers timers.state
|= [t=timer]
|= t=timer
^+ timers
:: if we don't have this timer, no-op; for debugging, add a printf here
:: if we don't have this timer, no-op
::
?~ timers
~? debug=%.n [%behn-unset-missing t]
~
?: =(i.timers t)
t.timers

View File

@ -1258,6 +1258,7 @@
^- (unit @tas)
?+ sep ~& [%ap-vain sep]
~
%bonk `%a
%build `%f
%cash `%a
%conf `%g

View File

@ -2123,7 +2123,7 @@
==
=+ cuz=[block-number.place log-index.place]
::
?: =(event.log changed-dns:azimuth-events)
?: =(i.topics.log changed-dns:azimuth-events)
=+ ^- [pri=tape sec=tape ter=tape]
%+ decode-results data.log
~[%string %string %string]

View File

@ -245,6 +245,7 @@
== == == ::
++ task :: in request ->$
$% {$barn ~} :: new unix process
{$bonk ~} :: reset the timer
{$crud p/@tas q/(list tank)} :: error with trace
{$hear p/lane q/@} :: receive packet
{$halo p/lane q/@ r/ares} :: hole with trace
@ -7331,79 +7332,79 @@
^- (unit (pair ship diff-point))
~? ?=(~ mined.log) %processing-unmined-event
::
?: =(event.log owner-changed)
?: =(i.topics.log owner-changed)
=+ ^- [who=@ wer=address]
(decode-topics topics.log ~[%uint %address])
`[who %owner wer]
::
?: =(event.log activated)
?: =(i.topics.log activated)
=/ who=@
(decode-topics topics.log ~[%uint])
`[who %activated who]
::
?: =(event.log spawned)
?: =(i.topics.log spawned)
=+ ^- [pre=@ who=@]
(decode-topics topics.log ~[%uint %uint])
`[pre %spawned who]
::
?: =(event.log escape-requested)
?: =(i.topics.log escape-requested)
=+ ^- [who=@ wer=@]
(decode-topics topics.log ~[%uint %uint])
`[who %escape `wer]
::
?: =(event.log escape-canceled)
?: =(i.topics.log escape-canceled)
=/ who=@ (decode-topics topics.log ~[%uint])
`[who %escape ~]
::
?: =(event.log escape-accepted)
?: =(i.topics.log escape-accepted)
=+ ^- [who=@ wer=@]
(decode-topics topics.log ~[%uint %uint])
`[who %sponsor & wer]
::
?: =(event.log lost-sponsor)
?: =(i.topics.log lost-sponsor)
=+ ^- [who=@ pos=@]
(decode-topics topics.log ~[%uint %uint])
`[who %sponsor | pos]
::
?: =(event.log changed-keys)
?: =(i.topics.log changed-keys)
=/ who=@ (decode-topics topics.log ~[%uint])
=+ ^- [enc=octs aut=octs sut=@ud rev=@ud]
%+ decode-results data.log
~[[%bytes-n 32] [%bytes-n 32] %uint %uint]
`[who %keys rev (pass-from-eth enc aut sut)]
::
?: =(event.log broke-continuity)
?: =(i.topics.log broke-continuity)
=/ who=@ (decode-topics topics.log ~[%uint])
=/ num=@ (decode-results data.log ~[%uint])
`[who %continuity num]
::
?: =(event.log changed-management-proxy)
?: =(i.topics.log changed-management-proxy)
=+ ^- [who=@ sox=address]
(decode-topics topics.log ~[%uint %address])
`[who %management-proxy sox]
::
?: =(event.log changed-voting-proxy)
?: =(i.topics.log changed-voting-proxy)
=+ ^- [who=@ tox=address]
(decode-topics topics.log ~[%uint %address])
`[who %voting-proxy tox]
::
?: =(event.log changed-spawn-proxy)
?: =(i.topics.log changed-spawn-proxy)
=+ ^- [who=@ sox=address]
(decode-topics topics.log ~[%uint %address])
`[who %spawn-proxy sox]
::
?: =(event.log changed-transfer-proxy)
?: =(i.topics.log changed-transfer-proxy)
=+ ^- [who=@ tox=address]
(decode-topics topics.log ~[%uint %address])
`[who %transfer-proxy tox]
::
:: warn about unimplemented events, but ignore
:: the ones we know are harmless.
~? ?! .= event.log
~? ?! .= i.topics.log
:: OwnershipTransferred(address,address)
0x8be0.079c.5316.5914.1344.cd1f.d0a4.f284.
1949.7f97.22a3.daaf.e3b4.186f.6b64.57e0
[%unimplemented-event event.log]
[%unimplemented-event i.topics.log]
~
::
++ apply-point-diff
@ -7751,11 +7752,12 @@
::
++ decode-topics
:: tox: list of hex words
|* [tox=(list @t) tys=(list etyp)]
=- (decode-arguments - tys)
%+ roll tox
|= [top=@t tos=@t]
(cat 3 tos (rsh 3 2 top))
|* [tox=(lest @ux) tys=(list etyp)]
=- (decode-arguments (crip -) tys)
%+ render-hex-bytes (mul 32 (lent tox))
%+ roll `(list @ux)`tox
|= [top=@ux tos=@]
(cat 8 top tos)
::
++ decode-results
:: rex: string of hex bytes with leading 0x.
@ -7886,14 +7888,14 @@
fro=(unit block)
tob=(unit block)
adr=(list address)
top=(list octs)
top=(list ?(@ux (list @ux)))
==
[%eth-get-filter-logs fid=@ud]
$: %eth-get-logs
fro=(unit block)
tob=(unit block)
adr=(list address)
top=(list octs)
top=(list ?(@ux (list @ux)))
==
[%eth-get-filter-changes fid=@ud]
[%eth-send-raw-transaction dat=@ux]
@ -7922,8 +7924,16 @@
::
address=@ux
data=@t
event=@ux
topics=(list @t)
:: event data
::
:: For standard events, the first topic is the event signature
:: hash. For anonymous events, the first topic is the first
:: indexed argument.
:: Note that this does not support the "anonymous event with
:: zero topics" case. This has dubious usability, and using
:: +lest instead of +list saves a lot of ?~ checks.
::
topics=(lest @ux)
==
::
:: data for eth_call.
@ -8033,8 +8043,8 @@
(turn adr.req (cork address-to-hex tape))
::
?~ top.req ~
:^ ~ 'topics' %a
(turn `(list octs)`top.req :(cork render-hex-bytes prefix-hex tape))
:+ ~ 'topics'
(topics-to-json top.req)
==
::
%eth-get-filter-logs
@ -8060,8 +8070,8 @@
(turn adr.req (cork address-to-hex tape))
::
?~ top.req ~
:^ ~ 'topics' %a
(turn `(list octs)`top.req :(cork render-hex-bytes prefix-hex tape))
:+ ~ 'topics'
(topics-to-json top.req)
==
::
%eth-get-filter-changes
@ -8104,6 +8114,23 @@
%label s+l.dob
==
::
++ topics-to-json
|= tos=(list ?(@ux (list @ux)))
^- json
:- %a
=/ ttj
;: cork
(cury render-hex-bytes 32)
prefix-hex
tape:enjs:format
==
%+ turn tos
|= t=?(@ (list @))
?@ t
?: =(0 t) ~
(ttj `@`t)
a+(turn t ttj)
::
:: parsing responses
::
::TODO ++ parse-response |= json ^- response
@ -8143,11 +8170,12 @@
address+(cu hex-to-num so)
data+so
::
::TODO doesn't account for the anonymous event case, which has no hash.
=- topics+(cu - (ar so))
|= r=(list @t)
^- (lest @ux)
?> ?=([@t *] r)
[(hex-to-num i.r) t.r]
:- (hex-to-num i.r)
(turn t.r hex-to-num)
==
--
::

View File

@ -0,0 +1,69 @@
:: tests for number-to-words
::
/+ number-to-words, *test
::
|%
++ test-eng-us
=+ eng-us:number-to-words
;: weld
%+ expect-eq
!> `(unit tape)``"zero"
!> (to-words 0)
::
%+ expect-eq
!> `(unit tape)``"one"
!> (to-words 1)
::
%+ expect-eq
!> `(unit tape)``"twelve"
!> (to-words 12)
::
%+ expect-eq
!> `(unit tape)``"twenty"
!> (to-words 20)
::
%+ expect-eq
!> `(unit tape)``"ninety-nine"
!> (to-words 99)
::
%+ expect-eq
!> `(unit tape)``"one hundred"
!> (to-words 100)
::
%+ expect-eq
!> `(unit tape)``"one hundred and eleven"
!> (to-words 111)
::
%+ expect-eq
!> `(unit tape)``"one thousand"
!> (to-words 1.000)
::
%+ expect-eq
!> `(unit tape)``"one thousand, one hundred and eleven"
!> (to-words 1.111)
::
%+ expect-eq
!> `(unit tape)``"one million and one"
!> (to-words 1.000.001)
::
%+ expect-eq
!> `(unit tape)``"one trillion"
!> (to-words (pow 10 12))
::
%+ expect-eq
!>
^- (unit tape)
:- ~
;: weld
"eighteen quintillion, four hundred and forty-six quadrillion, seven "
"hundred and forty-four trillion, seventy-three billion, seven "
"hundred and nine million, five hundred and fifty-one thousand, six "
"hundred and sixteen"
==
!> (to-words 18.446.744.073.709.551.616)
::
%+ expect-eq
!> `(unit tape)``"one vigintillion"
!> (to-words (pow 10 63))
==
--