mirror of
https://github.com/urbit/shrub.git
synced 2024-12-21 09:51:36 +03:00
196 lines
5.3 KiB
Haskell
196 lines
5.3 KiB
Haskell
module Nock where
|
|
|
|
import ClassyPrelude
|
|
|
|
type Atom = Integer
|
|
|
|
type Noun = Tree Atom
|
|
data Tree a = A !a
|
|
| C !(Tree a) !(Tree a)
|
|
deriving (Eq, Ord, Read, Functor)
|
|
|
|
data Fern a = FernA !a
|
|
| FernF [Fern a]
|
|
|
|
toFern :: Tree a -> Fern a
|
|
toFern = \case
|
|
A a -> FernA a
|
|
C h t -> case toFern t of
|
|
a@FernA{} -> FernF [toFern h, a]
|
|
FernF fs -> FernF (toFern h : fs)
|
|
|
|
instance Show a => Show (Fern a) where
|
|
show = \case
|
|
FernA a -> show a
|
|
FernF xs -> "[" <> intercalate " " (map show xs) <> "]"
|
|
|
|
instance Show a => Show (Tree a) where
|
|
show = show . toFern
|
|
|
|
yes, no :: Noun
|
|
yes = A 0
|
|
no = A 1
|
|
|
|
-- | Tree address
|
|
type Axis = Atom
|
|
|
|
data Nock
|
|
= NC Nock Nock -- ^ ^: autocons
|
|
| N0 Axis -- ^ 0, axis: tree addressing
|
|
| N1 Noun -- ^ 1, const
|
|
| N2 Nock Nock -- ^ 2, compose: compute subject, formula; apply
|
|
| N3 Nock -- ^ 3, is cell
|
|
| N4 Nock -- ^ 4, succ
|
|
| N5 Nock Nock -- ^ 5, eq
|
|
| N6 Nock Nock Nock -- ^ 6, if
|
|
| N7 Nock Nock -- ^ 7, then: =>
|
|
| N8 Nock Nock -- ^ 8, push: =+
|
|
| N9 Axis Nock -- ^ 9, invoke
|
|
| N10 (Axis, Nock) Nock -- ^ 10, edit
|
|
| N11 Hint Nock -- ^ 11, hint
|
|
| N12 Nock Nock -- ^ 12, scry
|
|
deriving (Eq, Ord, Read)
|
|
|
|
data Hint = Tag Atom
|
|
| Assoc Atom Nock
|
|
deriving (Eq, Ord, Read, Show)
|
|
|
|
instance Show Nock where
|
|
show = show . nockToNoun
|
|
|
|
nockToNoun :: Nock -> Noun
|
|
nockToNoun = go
|
|
where
|
|
go = \case
|
|
NC f g -> C (go f) (go g)
|
|
N0 a -> C (A 0) (A a)
|
|
N1 n -> C (A 1) n
|
|
N2 f g -> C (A 2) (C (go f) (go g))
|
|
N3 f -> C (A 3) (go f)
|
|
N4 f -> C (A 4) (go f)
|
|
N5 f g -> C (A 5) (C (go f) (go g))
|
|
N6 f g h -> C (A 5) (C (go f) (C (go g) (go h)))
|
|
N7 f g -> C (A 7) (C (go f) (go g))
|
|
N8 f g -> C (A 8) (C (go f) (go g))
|
|
N9 a f -> C (A 9) (C (A a) (go f))
|
|
N10 (a, f) g -> C (A 10) (C (C (A a) (go f)) (go g))
|
|
N11 (Tag a) f -> C (A 11) (C (A a) (go f))
|
|
N11 (Assoc a f) g -> C (A 11) (C (C (A a) (go f)) (go g))
|
|
N12 f g -> C (A 12) (C (go f) (go g))
|
|
|
|
nounToNock :: Noun -> Nock
|
|
nounToNock = go
|
|
where
|
|
go = \case
|
|
A{} -> error "nounToNock: atom"
|
|
C n@C{} m -> NC (go n) (go m)
|
|
C (A op) n -> case op of
|
|
0 | (A a) <- n -> N0 a
|
|
1 -> N1 n
|
|
2 | (C m o) <- n -> N2 (go m) (go o)
|
|
3 -> N3 (go n)
|
|
4 -> N4 (go n)
|
|
5 | (C m o) <- n -> N5 (go m) (go o)
|
|
6 | (C m (C o p)) <- n -> N6 (go m) (go o) (go p)
|
|
7 | (C m o) <- n -> N7 (go m) (go o)
|
|
8 | (C m o) <- n -> N8 (go m) (go o)
|
|
9 | (C (A a) m) <- n -> N9 a (go m)
|
|
10 | (C (C (A a) m) o) <- n -> N10 (a, (go m)) (go o)
|
|
11 | (C (C (A a) m) o) <- n -> N11 (Assoc a (go m)) (go o)
|
|
| (C (A a) m) <- n -> N11 (Tag a) (go m)
|
|
12 | (C m o) <- n -> N12 (go m) (go o)
|
|
_ -> error ("nockToNoun: invalid " <> show op <> " " <> show n)
|
|
|
|
-- | Nock interpreter
|
|
nock :: Noun -> Nock -> Noun
|
|
nock n = \case
|
|
NC f g -> C (nock n f) (nock n g)
|
|
N0 a -> axis a n
|
|
N1 n' -> n'
|
|
N2 sf ff -> nock (nock n sf) (nounToNock (nock n ff))
|
|
N3 f -> case nock n f of
|
|
C{} -> yes
|
|
A{} -> no
|
|
N4 f -> case nock n f of
|
|
C{} -> error "nock: cannot increment cell"
|
|
A a -> A (a + 1)
|
|
N5 f g -> if nock n f == nock n g then yes else no
|
|
N6 f g h -> case nock n f of
|
|
(A 0) -> nock n g
|
|
(A 1) -> nock n h
|
|
_ -> error "nock: invalid test value"
|
|
N7 f g -> nock (nock n f) g
|
|
N8 f g -> nock (C (nock n f) n) g
|
|
N9 a f -> let c = nock n f in nock c (nounToNock (axis a c))
|
|
N10 (a, f) g -> edit a (nock n f) (nock n g)
|
|
N11 _ f -> nock n f
|
|
N12{} -> error "nock: scrying is not allowed"
|
|
|
|
|
|
-- Axis logic
|
|
|
|
data Dir = L | R
|
|
deriving (Eq, Ord, Enum, Read, Show)
|
|
type Path = [Dir]
|
|
|
|
-- some stuff from hoon.hoon
|
|
|
|
cap :: Axis -> Dir
|
|
cap = \case
|
|
2 -> L
|
|
3 -> R
|
|
a | a <= 1 -> error "cap: bad axis"
|
|
| otherwise -> cap (div a 2)
|
|
|
|
mas :: Axis -> Axis
|
|
mas = \case
|
|
2 -> 1
|
|
3 -> 1
|
|
a | a <= 1 -> error "mas: bad axis"
|
|
| otherwise -> (mod a 2) + 2 * mas (div a 2)
|
|
|
|
capMas :: Axis -> (Dir, Axis)
|
|
capMas = \case
|
|
2 -> (L, 1)
|
|
3 -> (R, 1)
|
|
a | a <= 1 -> error "capMas: bad axis"
|
|
| otherwise -> (d, (mod a 2) + 2 * r)
|
|
where
|
|
(d, r) = capMas (div a 2)
|
|
|
|
peg :: Axis -> Axis -> Axis
|
|
peg a = \case
|
|
1 -> a
|
|
2 -> a * 2
|
|
3 -> a * 2 + 1
|
|
b -> (mod b 2) + 2 * peg a (div b 2)
|
|
|
|
axis :: Axis -> Tree a -> Tree a
|
|
axis 1 n = n
|
|
axis (capMas -> (d, r)) (C n m) = case d of
|
|
L -> axis r n
|
|
R -> axis r m
|
|
axis a _ = error ("bad axis: " ++ show a)
|
|
|
|
edit :: Axis -> Tree a -> Tree a -> Tree a
|
|
edit 1 v n = v
|
|
edit (capMas -> (d, r)) v (C n m) = case d of
|
|
L -> C (edit r v n) m
|
|
R -> C n (edit r v m)
|
|
edit a _ _ = error ("bad edit: " ++ show a)
|
|
|
|
-- Write an axis as a binary number; e.g. 5 as 101.
|
|
-- The rule is: after droping the 1 in the msb, you read from left to right.
|
|
-- 0 becomes L and 1 becomes R. So 5 becomes [L,R]
|
|
toPath :: Axis -> Path
|
|
toPath = \case
|
|
1 -> []
|
|
(capMas -> (d, r)) -> d : toPath r
|
|
|
|
toAxis :: Path -> Axis
|
|
toAxis = foldl' step 1
|
|
where
|
|
step r = \case
|
|
L -> 2 * r
|
|
R -> 2 * r + 1
|