summaryrefslogtreecommitdiff
path: root/docs/nock_implementations.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/nock_implementations.md')
-rw-r--r--docs/nock_implementations.md2525
1 files changed, 2525 insertions, 0 deletions
diff --git a/docs/nock_implementations.md b/docs/nock_implementations.md
new file mode 100644
index 0000000..37a1953
--- /dev/null
+++ b/docs/nock_implementations.md
@@ -0,0 +1,2525 @@
+Implementations
+===============
+
+We use a C implementation for our Nock interpreter. But building a Nock interpreter in another language is a fun exercise. Check out our community Nock implementations, shown below our official C implementation. (Note: the community implementations were written for a slightly older version of Nock, Nock 5K. The current version is Nock 4K.):
+
+Table of Contents
+-----------------
+
+* [C](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#C)
+
+* [Clojure](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#clojure)
+
+* [C#](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#c-sharp)
+
+* [Groovy](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#groovy)
+
+* [Haskell](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#haskell)
+
+* [JavaScript](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#javascript)
+
+* [Python](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#python)
+
+* [Ruby](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#ruby)
+
+* [Scala](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#scala)
+
+* [Scheme](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#scheme)
+
+* [Swift](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/nock/implementations/#swift)
+
+
+C Implementation
+----------------
+
+The actual production Nock interpreter. Note gotos for tail-call elimination, and manual reference counting. More about the C environment can be found in the [runtime system documentation](https://web.archive.org/web/20200919052443/https://urbit.org/docs/tutorials/vere/runtime/).
+
+ /\* \_n\_nock\_on(): produce .\*(bus fol). Do not virtualize.
+ \*/
+ static u3\_noun
+ \_n\_nock\_on(u3\_noun bus, u3\_noun fol)
+ {
+ u3\_noun hib, gal;
+
+ while ( 1 ) {
+ hib = u3h(fol);
+ gal = u3t(fol);
+
+ #ifdef U3\_CPU\_DEBUG
+ u3R->pro.nox\_d += 1;
+ #endif
+
+ if ( c3y == u3r\_du(hib) ) {
+ u3\_noun poz, riv;
+
+ poz = \_n\_nock\_on(u3k(bus), u3k(hib));
+ riv = \_n\_nock\_on(bus, u3k(gal));
+
+ u3a\_lose(fol);
+ return u3i\_cell(poz, riv);
+ }
+ else switch ( hib ) {
+ default: return u3m\_bail(c3\_\_exit);
+
+ case 0: {
+ if ( c3n == u3r\_ud(gal) ) {
+ return u3m\_bail(c3\_\_exit);
+ }
+ else {
+ u3\_noun pro = u3k(u3at(gal, bus));
+
+ u3a\_lose(bus); u3a\_lose(fol);
+ return pro;
+ }
+ }
+ c3\_assert(!"not reached");
+
+ case 1: {
+ u3\_noun pro = u3k(gal);
+
+ u3a\_lose(bus); u3a\_lose(fol);
+ return pro;
+ }
+ c3\_assert(!"not reached");
+
+ case 2: {
+ u3\_noun nex = \_n\_nock\_on(u3k(bus), u3k(u3t(gal)));
+ u3\_noun seb = \_n\_nock\_on(bus, u3k(u3h(gal)));
+
+ u3a\_lose(fol);
+ bus = seb;
+ fol = nex;
+ continue;
+ }
+ c3\_assert(!"not reached");
+
+ case 3: {
+ u3\_noun gof, pro;
+
+ gof = \_n\_nock\_on(bus, u3k(gal));
+ pro = u3r\_du(gof);
+
+ u3a\_lose(gof); u3a\_lose(fol);
+ return pro;
+ }
+ c3\_assert(!"not reached");
+
+ case 4: {
+ u3\_noun gof, pro;
+
+ gof = \_n\_nock\_on(bus, u3k(gal));
+ pro = u3i\_vint(gof);
+
+ u3a\_lose(fol);
+ return pro;
+ }
+ c3\_assert(!"not reached");
+
+ case 5: {
+ u3\_noun wim = \_n\_nock\_on(bus, u3k(gal));
+ u3\_noun pro = u3r\_sing(u3h(wim), u3t(wim));
+
+ u3a\_lose(wim); u3a\_lose(fol);
+ return pro;
+ }
+ c3\_assert(!"not reached");
+
+ case 6: {
+ u3\_noun b\_gal, c\_gal, d\_gal;
+
+ u3x\_trel(gal, &b\_gal, &c\_gal, &d\_gal);
+ {
+ u3\_noun tys = \_n\_nock\_on(u3k(bus), u3k(b\_gal));
+ u3\_noun nex;
+
+ if ( 0 == tys ) {
+ nex = u3k(c\_gal);
+ } else if ( 1 == tys ) {
+ nex = u3k(d\_gal);
+ } else return u3m\_bail(c3\_\_exit);
+
+ u3a\_lose(fol);
+ fol = nex;
+ continue;
+ }
+ }
+ c3\_assert(!"not reached");
+
+ case 7: {
+ u3\_noun b\_gal, c\_gal;
+
+ u3x\_cell(gal, &b\_gal, &c\_gal);
+ {
+ u3\_noun bod = \_n\_nock\_on(bus, u3k(b\_gal));
+ u3\_noun nex = u3k(c\_gal);
+
+ u3a\_lose(fol);
+ bus = bod;
+ fol = nex;
+ continue;
+ }
+ }
+ c3\_assert(!"not reached");
+
+ case 8: {
+ u3\_noun b\_gal, c\_gal;
+
+ u3x\_cell(gal, &b\_gal, &c\_gal);
+ {
+ u3\_noun heb = \_n\_nock\_on(u3k(bus), u3k(b\_gal));
+ u3\_noun bod = u3nc(heb, bus);
+ u3\_noun nex = u3k(c\_gal);
+
+ u3a\_lose(fol);
+ bus = bod;
+ fol = nex;
+ continue;
+ }
+ }
+ c3\_assert(!"not reached");
+
+ case 9: {
+ u3\_noun b\_gal, c\_gal;
+
+ u3x\_cell(gal, &b\_gal, &c\_gal);
+ {
+ u3\_noun seb = \_n\_nock\_on(bus, u3k(c\_gal));
+ u3\_noun pro;
+
+ u3t\_off(noc\_o);
+ pro = u3j\_kick(seb, b\_gal);
+ u3t\_on(noc\_o);
+
+ if ( u3\_none != pro ) {
+ u3a\_lose(fol);
+ return pro;
+ }
+ else {
+ if ( c3n == u3r\_ud(b\_gal) ) {
+ return u3m\_bail(c3\_\_exit);
+ }
+ else {
+ u3\_noun nex = u3k(u3at(b\_gal, seb));
+
+ u3a\_lose(fol);
+ bus = seb;
+ fol = nex;
+ continue;
+ }
+ }
+ }
+ }
+ c3\_assert(!"not reached");
+
+ case 10: {
+ u3\_noun p\_gal, q\_gal;
+
+ u3x\_cell(gal, &p\_gal, &q\_gal);
+ {
+ u3\_noun zep, hod, nex;
+
+ if ( c3y == u3r\_du(p\_gal) ) {
+ u3\_noun b\_gal = u3h(p\_gal);
+ u3\_noun c\_gal = u3t(p\_gal);
+ u3\_noun d\_gal = q\_gal;
+
+ zep = u3k(b\_gal);
+ hod = \_n\_nock\_on(u3k(bus), u3k(c\_gal));
+ nex = u3k(d\_gal);
+ }
+ else {
+ u3\_noun b\_gal = p\_gal;
+ u3\_noun c\_gal = q\_gal;
+
+ zep = u3k(b\_gal);
+ hod = u3\_nul;
+ nex = u3k(c\_gal);
+ }
+
+ u3a\_lose(fol);
+ return \_n\_hint(zep, hod, bus, nex);
+ }
+ }
+
+ case 11: {
+ u3\_noun ref = \_n\_nock\_on(u3k(bus), u3k(u3h(gal)));
+ u3\_noun gof = \_n\_nock\_on(bus, u3k(u3t(gal)));
+ u3\_noun val;
+
+ u3t\_off(noc\_o);
+ val = u3m\_soft\_esc(ref, u3k(gof));
+ u3t\_on(noc\_o);
+
+ if ( !\_(u3du(val)) ) {
+ u3m\_bail(u3nt(1, gof, 0));
+ }
+ if ( !\_(u3du(u3t(val))) ) {
+ //
+ // replace with proper error stack push
+ //
+ u3t\_push(u3nc(c3\_\_hunk, \_n\_mush(gof)));
+ return u3m\_bail(c3\_\_exit);
+ }
+ else {
+ u3\_noun pro;
+
+ u3z(gof);
+ u3z(fol);
+ pro = u3k(u3t(u3t(val)));
+ u3z(val);
+
+ return pro;
+ }
+ }
+ c3\_assert(!"not reached");
+ }
+ }
+ }
+
+
+
+Clojure
+-------
+
+From [Matt Earnshaw](https://web.archive.org/web/20200919052443/https://github.com/mattearnshaw/anock/blob/master/src/anock/core.clj):
+
+ (ns anock.core
+ (:import anock.NockException))
+
+ (declare atom? cell? cell)
+
+ (defn noun?
+ "A noun is an atom or a cell."
+ \[noun & ns\]
+ (if ns false
+ (or (atom? noun) (cell? noun))))
+
+ (defn atom?
+ "An atom is a natural number."
+ \[noun & ns\]
+ (if ns false
+ (and (integer? noun) (>= noun 0))))
+
+ (defn cell?
+ "A cell is an ordered pair of nouns."
+ \[noun\]
+ (cond
+ (atom? noun) false
+ (nil? noun) false
+ (not= 2 (count noun)) false
+ :else (and (noun? (first noun))
+ (noun? (second noun)))))
+
+ (defn tis
+ "= (pronounced 'tis') tests a cell for equality."
+ \[noun\]
+ (if (atom? noun) (throw (anock.NockException. "Cannot tis an atom."))
+ (let \[\[a b\] noun\]
+ (if (= a b) 0 1))))
+
+ (defn wut
+ "? (pronounced 'wut') tests whether a noun is a cell."
+ \[noun\]
+ (cond
+ (atom? noun) 1
+ (cell? noun) 0
+ :else (throw (anock.NockException. "Invalid noun."))))
+
+ (defn lus
+ "+ (pronounced 'lus') adds 1 to an atom."
+ \[noun\]
+ (if (atom? noun) (inc noun)
+ (throw (anock.NockException. "Can only lus atoms."))))
+
+ (defn fas
+ "/ (pronounced 'fas') is a tree address function."
+ \[noun\]
+ (if (atom? noun) (throw (anock.NockException. "Cannot fas an atom."))
+ (let \[\[a b\] (cell noun)\]
+ (assert (and (pos? a) (atom? a)) "Subject of fas must be a positive atom.")
+ (if (and (not (coll? b)) (or (= 2 a) (= 3 a)))
+ (throw (anock.NockException. (str "Cannot fas noun: " noun))))
+ (cond
+ (= 1 a) b
+ (= 2 a) (first b)
+ (= 3 a) (second b)
+ (even? a) (fas \[2 (fas \[(/ a 2) b\])\])
+ (odd? a) (fas \[3 (fas \[(/ (dec a) 2) b\])\])))))
+
+ (defn tar
+ "\* (pronounced 'tar') means Nock"
+ \[noun\]
+ (if (atom? noun) (throw (anock.NockException. "Cannot tar an atom."))
+ (try
+ (let \[noun (cell noun) \[x \[y z\]\] noun\]
+ (cond
+ (cell? y) (cell (tar \[x y\]) (tar \[x z\]))
+ (zero? y) (fas \[z x\])
+ (= 1 y) z
+ (= 3 y) (wut (tar \[x z\]))
+ (= 4 y) (lus (tar \[x z\]))
+ (= 5 y) (tis (tar \[x z\]))
+ :else (let \[\[p q\] z\]
+ (cond
+ (= 2 y) (tar \[(tar \[x p\]) (tar \[x q\])\])
+ (= 6 y) (tar \[x 2 \[0 1\] 2 \[1 (first q) (second q)\]
+ \[1 0\] 2 \[1 2 3\] \[1 0\] 4 4 p\])
+ (= 7 y) (tar \[x 2 p 1 q\])
+ (= 8 y) (tar \[x 7 \[\[7 \[0 1\] p\] 0 1\] q\])
+ (= 9 y) (tar \[x 7 q 2 \[0 1\] 0 p\])
+ (= 10 y) (if (cell? p)
+ (tar \[x 8 (second p) 7 \[0 3\] q\])
+ (tar \[x q\]))))))
+ (catch RuntimeException e
+ (throw (anock.NockException. (str "Cannot tar the noun " noun)))))))
+
+ (def nock tar)
+
+ ; Some convenience functions
+ (defn apply\* \[f x\]
+ (if (and (= 1 (count x)) (coll? (first x)))
+ (apply f x)
+ (f x)))
+
+ (defn bracket
+ "\[a b c\] -> \[a \[b c\]\]"
+ \[\[a & b :as c\]\]
+ (let \[b (vec b)\]
+ (cond
+ (and (noun? a) (apply noun? b)) (vec c)
+ (apply noun? b) (apply vector (bracket a) b)
+ (noun? a) \[a (apply\* bracket b)\]
+ :else \[(bracket a) (apply\* bracket b)\])))
+
+ (defn cell \[& nouns\]
+ (if (apply atom? nouns)
+ (throw (anock.NockException. "Cannot convert atom to cell."))
+ (apply\* bracket nouns)))
+
+
+
+C#
+--
+
+From [Julien Beasley](https://web.archive.org/web/20200919052443/https://github.com/zass30/Nock5KCSharp):
+
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Text.RegularExpressions;
+
+ namespace NockInterpreter
+ {
+ class Interpreter
+ {
+ static Dictionary<string, Noun> memocache = new Dictionary<string, Noun>();
+
+ public static Noun Nock(Noun noun)
+ {
+ Start:
+ Noun cache\_noun;
+ if (memocache.TryGetValue(noun.ToString(), out cache\_noun))
+ {
+ return cache\_noun;
+ }
+
+ if (Atom.IsAtom(noun))
+ throw new Exception("Infinite loop nocking an atom: " + noun.ToString());
+ else
+ {
+ Noun subject = noun.n1;
+ if (Noun.IsCell(noun.n2))
+ {
+ Cell formula = (Cell)noun.n2;
+ if (Noun.IsAtom(formula.n1)) // we have lines 25-37 of spec
+ {
+ Atom op = (Atom)formula.n1;
+ Noun operands = formula.n2;
+
+ switch (op.value)
+ {
+ case 0: // 25 :: \*\[a 0 b\] /\[b a\]
+ memocache\[noun.ToString()\] = fas(operands, subject);
+ return memocache\[noun.ToString()\];
+ case 1: // 26 :: \*\[a 1 b\] b
+ memocache\[noun.ToString()\] = operands;
+ return memocache\[noun.ToString()\];
+ case 2: // 27 :: \*\[a 2 b c\] \*\[\*\[a b\] \*\[a c\]\]
+ if (Noun.IsCell(operands))
+ {
+ Noun a = Nock(subject, operands.n1);
+ Noun b = Nock(subject, operands.n2);
+ noun = Noun.CreateNoun(a, b);
+ goto Start;
+ // return Nock(Nock(subject, operands.n1), Nock(subject, operands.n2));
+ }
+ throw new Exception("Atom after operand 2: " + operands.ToString());
+ case 3: // 28 :: \*\[a 3 b\] ?\*\[a b\]
+ memocache\[noun.ToString()\] = wut(Nock(subject, operands));
+ return memocache\[noun.ToString()\];
+ case 4: // 29 :: \*\[a 4 b\] +\*\[a b\]
+ memocache\[noun.ToString()\] = lus(Nock(subject, operands));
+ return memocache\[noun.ToString()\];
+ case 5: // 30 :: \*\[a 5 b\] =\*\[a b\]
+ memocache\[noun.ToString()\] = tis(Nock(subject, operands));
+ return memocache\[noun.ToString()\];
+ case 6: // 32 :: \*\[a 6 b c d\] \*\[a 2 \[0 1\] 2 \[1 c d\] \[1 0\] 2 \[1 2 3\] \[1 0\] 4 4 b\]
+ if (Noun.IsCell(operands) && Noun.IsCell(operands.n2))
+ {
+ Noun b = operands.n1;
+ Noun c = operands.n2.n1;
+ Noun d = operands.n2.n2;
+ noun = Noun.CreateNoun("\[" + subject + " 2 \[0 1\] 2 \[1 " + c + " " + d + "\] \[1
+ 0\] 2 \[1 2 3\] \[1 0\] 4 4 " + b + "\]");
+ goto Start;
+ // return Nock(Noun.CreateNoun("\[" + subject + " 2 \[0 1\] 2 \[1 " + c + " " + d +
+ "\] \[1 0\] 2 \[1 2 3\] \[1 0\] 4 4 " + b + "\]"));
+ }
+ throw new Exception("Unhandled pattern for operand 6");
+ case 7: // 33 :: \*\[a 7 b c\] \*\[a 2 b 1 c\]
+ if (Noun.IsCell(operands))
+ {
+ Noun b = operands.n1;
+ Noun c = operands.n2;
+ noun = Noun.CreateNoun("\[" + subject + " 2 " + b + " 1 " + c + "\]");
+ goto Start;
+ // return Nock(Noun.CreateNoun("\[" + subject + " 2 " + b + " 1 " + c +
+ "\]"));
+ }
+ throw new Exception("Atom after operand 7: " + operands.ToString());
+ case 8: // 34 :: \*\[a 8 b c\] \*\[a 7 \[\[7 \[0 1\] b\] 0 1\] c\]
+ if (Noun.IsCell(operands))
+ {
+ Noun b = operands.n1;
+ Noun c = operands.n2;
+ noun = Noun.CreateNoun("\[" + subject + " 7 \[\[7 \[0 1\] " + b + "\] 0 1\] " + c +
+ "\]");
+ goto Start;
+ // return Nock(Noun.CreateNoun("\[" + subject + " 7 \[\[7 \[0 1\] " + b + "\] 0 1\] " + c
+ + "\]"));
+ }
+ throw new Exception("Atom after operand 8: " + operands.ToString());
+ case 9: // 35 :: \*\[a 9 b c\] \*\[a 7 c 2 \[0 1\] 0 b\]
+ if (Noun.IsCell(operands))
+ {
+ Noun b = operands.n1;
+ Noun c = operands.n2;
+ noun = Noun.CreateNoun("\[" + subject + " 7 " + c + " 2 \[0 1\] 0 " + b +
+ "\]");
+ goto Start;
+ // return Nock(Noun.CreateNoun("\[" + subject + " 7 " + c + " 2 \[0 1\] 0 " + b +
+ "\]"));
+ }
+ throw new Exception("Atom after operand 9: " + operands.ToString());
+ case 10:
+ if (Noun.IsCell(operands))
+ {
+ if (Noun.IsCell(operands.n1)) // 36 :: \*\[a 10 \[b c\] d\] \*\[a 8 c 7 \[0 3\] d\]
+ {
+ Noun b = operands.n1.n1;
+ Noun c = operands.n1.n2;
+ Noun d = operands.n2;
+ noun = Noun.CreateNoun("\[" + subject + " 8 " + c + " 7 \[0 3\] " + d +
+ "\]");
+ goto Start;
+ // return Nock(Noun.CreateNoun("\[" + subject + " 8 " + c + " 7 \[0 3\] " + d +
+ "\]"));
+ }
+ else // 37 :: \*\[a 10 b c\] \*\[a c\]
+ {
+ Noun c = operands.n2;
+ noun = Noun.CreateNoun(subject, c);
+ goto Start;
+ // return Nock(subject, c);
+ }
+ }
+ throw new Exception("Atom after operand 10: " + operands.ToString());
+ default:
+ throw new Exception("Unknown operand: " + op.value);
+ }
+ }
+ else // 23 :: \*\[a \[b c\] d\] \[\*\[a b c\] \*\[a d\]\]
+ {
+ memocache\[noun.ToString()\] = Noun.CreateNoun(Nock(subject, formula.n1), Nock(subject, formula.n2));
+ return memocache\[noun.ToString()\];
+ }
+ }
+ }
+ throw new Exception("Unhandled pattern");
+ }
+
+ public static Noun Nock(string program)
+ {
+ Noun noun = Noun.CreateNoun(program);
+ return Nock(noun);
+ }
+
+ public static Noun Nock(Noun n1, Noun n2)
+ {
+ Noun noun = Noun.CreateNoun(n1, n2);
+ return Nock(noun);
+ }
+
+ private static Noun tis(Noun noun)
+ {
+ if (Noun.IsAtom(noun.ToString()))
+ throw new Exception("Infinite loop tising an atom: " + noun.ToString());
+ else
+ {
+ Cell cell = (Cell)noun;
+
+ if (cell.n1.ToString() == cell.n2.ToString())
+ return Noun.CreateNoun("0");
+ else
+ return Noun.CreateNoun("1");
+ }
+ }
+
+ private static Noun lus(Noun noun)
+ {
+ if (Noun.IsAtom(noun.ToString()))
+ {
+ Atom a = (Atom)noun;
+ int v = a.value + 1;
+ return Noun.CreateNoun(v.ToString());
+ }
+ else
+ throw new Exception("Infinite loop lusing a cell: " + noun.ToString());
+ }
+
+ private static Noun wut(Noun noun)
+ {
+ if (Noun.IsAtom(noun.ToString()))
+ return Noun.CreateNoun("1");
+ else
+ return Noun.CreateNoun("0");
+ }
+
+ private static Noun fas(Noun n1, Noun n2)
+ {
+ Noun noun = Noun.CreateNoun(n1, n2);
+ return fas(noun);
+ }
+
+ private static Noun fas(Noun noun)
+ {
+ if (Noun.IsAtom(noun.ToString()))
+ throw new Exception("Infinite loop fasing an atom: " + noun.ToString());
+ else
+ {
+ Cell c = (Cell)noun;
+ // If n1 isn't an atom, I assume we throw? This isn't defined in the spec. Confirmed by John B by email.
+ This spins forever.
+ if (Noun.IsCell(c.n1.ToString()))
+ throw new Exception("Axis must be an atom: " + c.ToString());
+ else
+ {
+ Atom a = (Atom)c.n1;
+ if (a.value == 1)
+ return c.n2;
+ else if (a.value >= 2)
+ {
+ if (!Noun.IsCell(c.n2.ToString()))
+ {
+ throw new Exception("Only a cell can have an axis of 2 or 3: " + c.n2.ToString());
+ }
+ else
+ {
+ Cell c2 = (Cell)c.n2;
+ if (a.value == 2)
+ return c2.n1;
+ else if (a.value == 3)
+ return c2.n2;
+ else if (a.value % 2 == 0)
+ {
+ int half = a.value / 2;
+ return fas(Noun.CreateNoun("2", fas(Noun.CreateNoun(half.ToString(), c2))));
+ }
+ else if (a.value % 2 == 1)
+ {
+ int half = a.value / 2;
+ return fas(Noun.CreateNoun("3", fas(Noun.CreateNoun(half.ToString(), c2))));
+ }
+ else
+ {
+ throw new Exception("Infinite loop somewhere in fasing: " + c.n2.ToString());
+ }
+ }
+ }
+ }
+ throw new Exception("Infinite loop somewhere in fasing: " + c.n2.ToString());
+ }
+ }
+ }
+
+ class Noun
+ {
+ public Noun n1;
+ public Noun n2;
+
+ // takes a program, returns a pair of nouns, stringified.
+ public static Tuple<string, string> SplitCell(string program)
+ {
+
+ int stackCount = 0;
+ int i = 0;
+ // split the string right after the first space
+ foreach (char c in program)
+ {
+ if (IsValidChar(c))
+ {
+ if (c == '\[')
+ stackCount++;
+ else if (c == '\]')
+ stackCount--;
+ else if (c == ' ')
+ {
+ // if we see a space, and our stack count is at 1, then we've found our split point
+ if (stackCount == 1)
+ {
+ string a = program.Substring(1, i - 1);
+ string b = program.Substring(i + 1, program.Length - (i + 2));
+
+ // to implement proper bracket closing, surround b with brackets if it isn't a cell and isn't an atom
+ if (!IsCell(b) && !IsAtom(b))
+ b = "\[" + b + "\]";
+ Tuple<string, string> tuple = new Tuple<string, string>(a, b);
+ return tuple;
+ }
+ }
+ }
+ else
+ throw new Exception("Invalid char in cell: " + c);
+ i++;
+ }
+ throw new Exception("Invalid cell: " + program);
+ }
+
+ public static bool IsCell(string program)
+ {
+ // check if cell is valid, as above but make sure no space after bracket
+ // valid tokens are: space, int, \[, \]
+ // from \[ => \[, int
+ // from int => space, \], int
+ // from \] => space, \]
+ // from space => int, \[
+
+ // stack count must always be nonzero
+ // first and last elements must be \[ and \]
+
+ int i = 0; // i is the stack count for brackets.
+ int counter = 0;
+ char s = '\\0'; // s is the last seen character
+ // split the string right after the first space
+ foreach (char c in program)
+ {
+ if (s == '\\0')
+ {
+ if (c != '\[')
+ return false;
+ }
+ else if (s == '\[')
+ {
+ if (!(c == '\[' || IsInt(c)))
+ return false;
+ }
+ else if (IsInt(s))
+ {
+ if (!(IsInt(c) || c == ' ' || c == '\]'))
+ return false;
+ }
+ else if (s == '\]')
+ {
+ if (!(c == '\]' || c == ' '))
+ return false;
+ }
+ else if (s == ' ')
+ {
+ if (!(c == '\[' || IsInt(c)))
+ return false;
+ }
+ s = c;
+ counter++;
+ if (c == '\[')
+ i++;
+ else if (c == '\]')
+ i--;
+ if (i <= 0 && counter != program.Length) // stack count can't be zero unless it's the last
+ character
+ return false;
+ }
+
+ // We should end with stack count of zero
+ if (i == 0)
+ return true;
+ else
+ return false;
+ }
+
+ public static bool IsInt(char c)
+ {
+ if (c == '0' ||
+ c == '1' ||
+ c == '2' ||
+ c == '3' ||
+ c == '4' ||
+ c == '5' ||
+ c == '6' ||
+ c == '7' ||
+ c == '8' ||
+ c == '9')
+ return true;
+ else
+ return false;
+ }
+
+ public static bool IsValidChar(char c)
+ {
+ if (c == ' ' ||
+ c == '\[' ||
+ c == '\]' ||
+ IsInt(c))
+ return true;
+ else
+ return false;
+ }
+
+ public static bool IsAtom(string program)
+ {
+ int i = 0;
+ if (int.TryParse(program, out i))
+ {
+ if (i >= 0)
+ return true;
+ }
+ return false;
+ }
+
+ public static bool IsAtom(Noun noun)
+ {
+ return IsAtom(noun.ToString());
+ }
+
+ public static bool IsCell(Noun noun)
+ {
+ return IsCell(noun.ToString());
+ }
+
+ public static Noun CreateNoun(string program)
+ {
+ if (IsAtom(program))
+ return new Atom(program);
+ else
+ return new Cell(program);
+ }
+
+ public static Noun CreateNoun(Noun n1, Noun n2)
+ {
+ return CreateNoun("\[" + n1.ToString() + " " + n2.ToString() + "\]");
+ }
+
+ public static Noun CreateNoun(string p1, Noun n2)
+ {
+ return CreateNoun("\[" + p1 + " " + n2.ToString() + "\]");
+ }
+
+ public static Noun CreateNoun(Noun n1, string p2)
+ {
+ return CreateNoun("\[" + n1.ToString() + " " + p2 + "\]");
+ }
+
+ public static Noun CreateNoun(string p1, string p2)
+ {
+ return CreateNoun("\[" + p1 + " " + p2 + "\]");
+ }
+ }
+
+ class Atom : Noun
+ {
+ public int value;
+
+ public override string ToString()
+ {
+ return value.ToString();
+ }
+
+ public Atom(string program)
+ {
+ if (IsAtom(program))
+ {
+ int i = 0;
+ bool result = int.TryParse(program, out i);
+ value = i;
+ }
+ else
+ throw new ArgumentException("Invalid Atom: " + program);
+ n1 = null;
+ n2 = null;
+ }
+ }
+
+ class Cell : Noun
+ {
+ public override string ToString()
+ {
+ return "\[" + n1.ToString() + " " + n2.ToString() + "\]";
+ }
+
+ public Cell(string program)
+ {
+ if (IsCell(program))
+ {
+ Tuple<string, string> split = SplitCell(program);
+ n1 = CreateNoun(split.Item1);
+ n2 = CreateNoun(split.Item2);
+ }
+ else
+ throw new ArgumentException("Invalid Cell: " + program);
+ }
+ }
+ }
+
+
+
+Groovy
+------
+
+From [Kohányi Róbert](https://web.archive.org/web/20200919052443/https://github.com/kohanyirobert/gnock/blob/master/gnock.groovy):
+
+ @Memoized
+ def i(def a) {
+ a.class in \[
+ byte, Byte,
+ char, Character,
+ short, Short,
+ int, Integer,
+ long, Long,
+ BigInteger
+ \] && a >= 0
+ }
+
+ @Memoized
+ def n(def a) {
+ def r
+ n(a, { r = it })
+ r
+ }
+
+ @TailRecursive
+ def n(def a, def r) {
+ if (a in List) {
+ if (a.size() == 1) {
+ r(a\[0\])
+ } else if (a.size() >= 2) {
+ n(a\[0\], { t ->
+ n(a.size() == 2 ? a\[1\] : a.tail(), { h ->
+ r(\[t, h\])
+ })
+ })
+ } else {
+ throw new IllegalStateException()
+ }
+ } else if (i(a)) {
+ r((BigInteger) a)
+ } else {
+ throw new IllegalStateException()
+ }
+ }
+
+ @Memoized
+ def wut(def a) {
+ i(a) ? 1 : 0
+ }
+
+ @Memoized
+ def lus(def a) {
+ if (wut(a) == 0) {
+ throw new IllegalStateException()
+ }
+ 1 + a
+ }
+
+ @Memoized
+ def tis(def a) {
+ if (wut(a) == 1) {
+ throw new IllegalStateException()
+ }
+ a\[0\] == a\[1\] ? 0 : 1
+ }
+
+ @Memoized
+ def fas(def a) {
+ def r
+ fas(a, { r = it })
+ r
+ }
+
+ @TailRecursive
+ def fas(def a, def r) {
+ if (wut(a) == 1) {
+ throw new IllegalStateException()
+ }
+ def h = a\[0\]
+ if (!i(h)) {
+ throw new IllegalStateException()
+ }
+ def t = a\[1\]
+ if (h == 0) {
+ throw new IllegalStateException()
+ } else if (h == 1) {
+ r(t)
+ } else {
+ if (i(t)) {
+ throw new IllegalStateException()
+ }
+ if (h == 2) {
+ r(t\[0\])
+ } else if (h == 3) {
+ r(t\[1\])
+ } else {
+ fas(\[h.intdiv(2), t\], { p ->
+ fas(\[2 + h.mod(2), p\], { q ->
+ r(q)
+ })
+ })
+ }
+ }
+ }
+
+ @Memoized
+ def tar(def a) {
+ def r
+ tar(a, { r = it})
+ r
+ }
+
+ @TailRecursive
+ def tar(def a, def r) {
+ if (wut(a) == 1) {
+ throw new IllegalStateException()
+ }
+ def s = a\[0\]
+ def f = a\[1\]
+ if (wut(f) == 1) {
+ throw new IllegalStateException()
+ }
+ def o = f\[0\]
+ def v = f\[1\]
+ if (wut(o) == 0) {
+ tar(\[s, o\], { p ->
+ tar(\[s, v\], { q ->
+ r(\[p, q\])
+ })
+ })
+ } else {
+ if (o == 0) {
+ r(fas(\[v, s\]))
+ } else if (o == 1) {
+ r(v)
+ } else if (o == 3) {
+ tar(\[s, v\], {
+ r(wut(it))
+ })
+ } else if (o == 4) {
+ tar(\[s, v\], {
+ r(lus(it))
+ })
+ } else if (o == 5) {
+ tar(\[s, v\], {
+ r(tis(it))
+ })
+ } else {
+ if (wut(v) == 1) {
+ throw new IllegalStateException()
+ }
+ def x = v\[0\]
+ def y = v\[1\]
+ if (o == 2) {
+ tar(\[s, x\], { p ->
+ tar(\[s, y\], { q ->
+ tar(\[p, q\], {
+ r(it)
+ })
+ })
+ })
+ } else if (o == 7) {
+ tar(n(\[s, 2, x, 1, y\]), {
+ r(it)
+ })
+ } else if (o == 8) {
+ tar(n(\[s, 7, \[\[7, \[0, 1\], x\], 0, 1\], y\]), {
+ r(it)
+ })
+ } else if (o == 9) {
+ tar(n(\[s, 7, y, 2, \[0, 1\], 0, x\]), {
+ r(it)
+ })
+ } else if (o == 10) {
+ if (wut(x) == 1) {
+ tar(\[s, y\], {
+ r(it)
+ })
+ } else {
+ tar(n(\[s, 8, x\[1\], 7, \[0, 3\], y\]), {
+ r(it)
+ })
+ }
+ } else {
+ if (wut(y) == 1) {
+ throw new IllegalStateException()
+ }
+ if (o == 6) {
+ tar(n(\[s, 2, \[0, 1\], 2, \[1, y\[0\], y\[1\]\], \[1, 0\], 2, \[1, 2, 3\], \[1, 0\], 4, 4, x\]), {
+ r(it)
+ })
+ } else {
+ throw new IllegalStateException()
+ }
+ }
+ }
+ }
+ }
+
+
+
+Haskell
+-------
+
+From [Steve Dee](https://web.archive.org/web/20200919052443/https://github.com/mrdomino/hsnock/blob/master/Language/Nock5K/Spec.hs):
+
+ module Language.Nock5K.Spec where
+ import Control.Monad.Instances
+
+ wut (a :- b) = return $ Atom 0
+ wut a = return $ Atom 1
+
+ lus (a :- b) = Left "+\[a b\]"
+ lus (Atom a) = return $ Atom (1 + a)
+
+ tis (a :- a') | a == a' = return $ Atom 0
+ tis (a :- b) = return $ Atom 1
+ tis a = Left "=a"
+
+ fas (Atom 1 :- a) = return a
+ fas (Atom 2 :- a :- b) = return a
+ fas (Atom 3 :- a :- b) = return b
+ fas (Atom a :- b) | a > 3 = do x <- fas $ Atom (a \`div\` 2) :- b
+ fas $ Atom (2 + (a \`mod\` 2)) :- x
+ fas a = Left "/a"
+
+ tar (a :- (b :- c) :- d) = do x <- tar (a :- b :- c)
+ y <- tar (a :- d)
+ return $ x :- y
+
+ tar (a :- Atom 0 :- b) = fas $ b :- a
+ tar (a :- Atom 1 :- b) = return b
+ tar (a :- Atom 2 :- b :- c) = do x <- tar (a :- b)
+ y <- tar (a :- c)
+ tar $ x :- y
+ tar (a :- Atom 3 :- b) = tar (a :- b) >>= wut
+ tar (a :- Atom 4 :- b) = tar (a :- b) >>= lus
+ tar (a :- Atom 5 :- b) = tar (a :- b) >>= tis
+
+ tar (a :- Atom 6 :- b :- c :- d) = tar (a :- Atom 2 :- (Atom 0 :- Atom 1) :-
+ Atom 2 :- (Atom 1 :- c :- d) :-
+ (Atom 1 :- Atom 0) :- Atom 2 :-
+ (Atom 1 :- Atom 2 :- Atom 3) :-
+ (Atom 1 :- Atom 0) :- Atom 4 :-
+ Atom 4 :- b)
+ tar (a :- Atom 7 :- b :- c) = tar (a :- Atom 2 :- b :- Atom 1 :- c)
+ tar (a :- Atom 8 :- b :- c) = tar (a :- Atom 7 :-
+ ((Atom 7 :- (Atom 0 :- Atom 1) :- b) :-
+ Atom 0 :- Atom 1) :- c)
+ tar (a :- Atom 9 :- b :- c) = tar (a :- Atom 7 :- c :- Atom 2 :-
+ (Atom 0 :- Atom 1) :- Atom 0 :- b)
+ tar (a :- Atom 10 :- (b :- c) :- d) = tar (a :- Atom 8 :- c :- Atom 7 :-
+ (Atom 0 :- Atom 3) :- d)
+ tar (a :- Atom 10 :- b :- c) = tar (a :- c)
+
+ tar a = Left "\*a"
+
+
+
+Hoon
+----
+
+ |= {sub/\* fol/\*}
+ ^- \*
+ ?< ?=(@ fol)
+ ?: ?=(^ -.fol)
+ \[$(fol \-.fol) $(fol +.fol)\]
+ ?+ fol
+ !!
+ {$0 b/@}
+ ?< =(0 b.fol)
+ ?: =(1 b.fol) sub
+ ?< ?=(@ sub)
+ \=+ \[now\=(cap b.fol) lat\=(mas b.fol)\]
+ $(b.fol lat, sub ?:(\=(2 now) \-.sub
+ +.sub))
+ ::
+ {$1
+ b/\*}
+ b.fol
+ ::
+ {$2
+ b/{^
+ \*}}
+ \=+ ben\=$(fol b.fol)
+ $(sub \-.ben, fol +.ben)
+ ::
+ {$3
+ b/\*}
+ \=+ ben\=$(fol b.fol)
+ .?(ben)
+ ::
+ {$4
+ b/\*}
+ \=+ ben\=$(fol b.fol)
+ ?> ?=(@ ben)
+ +(ben)
+ ::
+ {$5
+ b/\*}
+ \=+ ben\=$(fol b.fol)
+ ?> ?=(^ ben)
+ \=(\-.ben +.ben)
+ ::
+ {$6
+ b/\* c/\* d/\*}
+ $(fol \=>(fol \[2 \[0 1\] 2 \[1 c d\] \[1 0\] 2 \[1 2 3\] \[1 0\] 4
+ 4 b\]))
+ ::
+ {$7
+ b/\* c/\*} $(fol \=>(fol \[2 b 1 c\]))
+ {$8 b/\* c/\*} $(fol \=>(fol \[7 \[\[7 \[0 1\] b\] 0 1\] c\]))
+ {$9 b/\* c/\*} $(fol \=>(fol \[7 c 2 \[0 1\] 0 b\]))
+ {$10 @ c/\*} $(fol
+ c.fol)
+ {$10 {b/\* c/\*} d/\*} \=+($(fol c.fol)
+ $(fol d.fol))
+ \==
+
+
+
+JavaScript
+----------
+
+From [Joe Bryan](https://web.archive.org/web/20200919052443/https://github.com/joemfb/nock.js/blob/master/nock.js):
+
+ (function (self,
+ factory) {
+ 'use strict'
+
+ if (typeof define \===
+ 'function' && define.amd) {
+ define(\[\], factory)
+ } else if (typeof module
+ \=== 'object' && typeof module.exports \=== 'object') {
+ module.exports \= factory()
+ } else {
+ self.nock \= factory()
+ }
+ }(this, function () {
+ 'use strict'
+
+ /\*\*
+ \* Nock is a combinator interpreter on nouns. A noun is an atom or a cell.
+ \* An atom is an unsigned integer of any size; a cell is an ordered pair of nouns.
+ \*
+ \* @see http://urbit.org/docs/nock/definition/
+ \* @see https://media.urbit.org/whitepaper.pdf
+ \*/
+
+ var useMacros \= false
+
+ /\*
+ \* code conventions:
+ \*
+ \* \`n\` is a noun,
+ \* \`s\` is a subject noun,
+ \* \`f\` is a formula (or cell of formulas)
+ \*/
+
+ /\* operators \*/
+
+ /\*\*
+ \* wut (?): test for atom (1) or cell (0)
+ \*
+ \* ?\[a b\] 0
+ \* ?a 1
+ \*/
+ function wut (n) {
+ return typeof n \=== 'number' ? 1 : 0
+ }
+
+ /\*\*
+ \* lus (+): increment an atom
+ \*
+ \* +\[a b\] +\[a b\]
+ \* +a 1 + a
+ \*/
+ function lus (n) {
+ if (wut(n) \=== 0) throw
+ new Error('lus cell')
+ return 1 \+ n
+ }
+
+ /\*\*
+ \* tis (=): test equality
+ \*
+ \* =\[a a\] 0
+ \* =\[a b\] 1
+ \* =a =a
+ \*/
+ function tis (n) {
+ if (wut(n) \=== 1) throw
+ new Error('tis atom')
+ return deepEqual(n\[0\], n\[1\]) ? 0 :
+ 1
+ }
+
+ /\*\*
+ \* fas (/): resolve a tree address
+ \*
+ \* /\[1 a\] a
+ \* /\[2 a b\] a
+ \* /\[3 a b\] b
+ \* /\[(a + a) b\] /\[2 /\[a b\]\]
+ \* /\[(a + a + 1) b\] /\[3 /\[a b\]\]
+ \* /a /a
+ \*/
+ function fas (addr,
+ n) {
+ if (n \=== undefined)
+ throw new Error('invalid fas noun')
+ if (addr \=== 0)
+ throw new Error('invalid fas addr: 0')
+
+ if (addr \=== 1)
+ return n
+ if (addr \=== 2)
+ return n\[0\]
+ if (addr \=== 3)
+ return n\[1\]
+
+ return fas(2 \+ (addr %
+ 2), fas((addr
+ / 2)
+ | 0,
+ n))
+ }
+
+ /\* formulas \*/
+
+ /\*\*
+ \* slot (0): resolve a tree address
+ \*
+ \* \*\[a 0 b\] /\[b a\]
+ \*/
+ function slot (s, f) {
+ var p \= fas(f, s)
+
+ if (p \=== undefined)
+ throw new Error('invalid fas addr: ' \+ f)
+
+ return p
+ }
+
+ /\*\*
+ \* constant (1): return the formula regardless of subject
+ \*
+ \* \*\[a 1 b\] b
+ \*/
+ function constant (s, f) {
+ return f
+ }
+
+ /\*\*
+ \* evaluate (2): evaluate the product of second formula against the product of the first
+ \*
+ \* \*\[a 2 b c\] \*\[\*\[a b\] \*\[a c\]\]
+ \*/
+ function evaluate (s, f) {
+ return nock(nock(s, f\[0\]), nock(s, f\[1\]))
+ }
+
+ /\*\*
+ \* cell (3): test if the product is a cell
+ \*
+ \* \*\[a 3 b\] ?\*\[a b\]
+ \*/
+ function cell (s, f) {
+ return wut(nock(s, f))
+ }
+
+ /\*\*
+ \* incr (4): increment the product
+ \*
+ \* \*\[a 4 b\] +\*\[a b\]
+ \*/
+ function incr (s, f) {
+ return lus(nock(s, f))
+ }
+
+ /\*\*
+ \* eq (5): test for equality between nouns in the product
+ \*
+ \* \*\[a 5 b\] =\*\[a b\]
+ \*/
+ function eq (s, f) {
+ return tis(nock(s, f))
+ }
+
+ /\* macro-formulas \*/
+
+ /\*\*
+ \* ife (6): if/then/else
+ \*
+ \* \*\[a 6 b c d\] \*\[a 2 \[0 1\] 2 \[1 c d\] \[1 0\] 2 \[1 2 3\] \[1 0\] 4 4 b\]
+ \*/
+ function macroIfe (s, f) {
+ return nock(s, \[2, \[\[0, 1\], \[2,
+ \[\[1, \[f\[1\]\[0\],
+ f\[1\]\[1\]\]\], \[\[1,
+ 0\], \[2, \[\[1, \[2, 3\]\],
+ \[\[1, 0\], \[4, \[4, f\[0\]\]\]\]\]\]\]\]\]\]\])
+ }
+
+ function ife (s, f) {
+ var cond \= nock(s, f\[0\])
+
+ if (cond \=== 0)
+ return nock(s, f\[1\]\[0\])
+ if (cond \=== 1)
+ return nock(s, f\[1\]\[1\])
+
+ throw new Error('invalid ife conditional')
+ }
+
+ /\*\*
+ \* compose (7): evaluate formulas composed left-to-right
+ \*
+ \* \*\[a 7 b c\] \*\[a 2 b 1 c\]
+ \*/
+ function macroCompose (s, f) {
+ return nock(s, \[2, \[f\[0\],
+ \[1, f\[1\]\]\]\])
+ }
+
+ function compose (s, f) {
+ // alternate form:
+ // return nock(nock(s, f\[0\]), constant(s, f\[1\]))
+ return nock(nock(s, f\[0\]), f\[1\])
+ }
+
+ /\*\*
+ \* extend (8): evaluate the second formula against \[product of first, subject\]
+ \*
+ \* \*\[a 8 b c\] \*\[a 7 \[\[7 \[0 1\] b\] 0 1\] c\]
+ \*/
+ function macroExtend (s, f) {
+ return nock(s, \[7, \[\[\[7, \[\[0, 1\], f\[0\]\]\], \[0, 1\]\], f\[1\]\]\])
+ }
+
+ function extend (s, f) {
+ // alternate form:
+ // return nock(\[compose(s, \[\[0, 1\], f\[0\]\]), s\], f\[1\])
+ return nock(\[nock(s, f\[0\]), s\],
+ f\[1\])
+ }
+
+ /\*\*
+ \* invoke (9): construct a core and evaluate one of its arms against it
+ \*
+ \* \*\[a 9 b c\] \*\[a 7 c 2 \[0 1\] 0 b\]
+ \*/
+ function macroInvoke (s, f) {
+ return nock(s, \[7, \[f\[1\],
+ \[2, \[\[0, 1\], \[0,
+ f\[0\]\]\]\]\]\])
+ }
+
+ function invoke (s, f) {
+ var prod \= nock(s, f\[1\])
+ return nock(prod,
+ slot(prod, f\[0\]))
+ }
+
+ /\*\*
+ \* hint (10): skip first formula, evaluate second
+ \*
+ \* \*\[a 10 \[b c\] d\] \*\[a 8 c 7 \[0 3\] d\]
+ \* \*\[a 10 b c\] \*\[a c\]
+ \*/
+ function macroHint (s, f) {
+ if (wut(f\[0\]) \=== 0)
+ return nock(s, \[8, \[f\[0\]\[1\], \[7, \[\[0, 3\], f\[1\]\]\]\]\])
+ return nock(s, f\[1\])
+ }
+
+ function hint (s, f) {
+ if (wut(f\[0\]) \=== 0) {
+ if (wut(f\[0\]\[1\]) \===
+ 1) throw new Error('invalid hint')
+ nock(s, f\[0\]\[1\])
+ }
+ return nock(s, f\[1\])
+ }
+
+ /\* indexed formula functions \*/
+ var macroFormulas \= \[slot, constant, evaluate, cell, incr, eq,
+ macroIfe, macroCompose, macroExtend, macroInvoke, macroHint\]
+ var formulas \= \[slot, constant, evaluate, cell, incr, eq, ife,
+ compose, extend, invoke, hint\]
+
+ /\*\*
+ \* nock (\*)
+ \*
+ \* the nock function
+ \*
+ \* \*\[a \[b c\] d\] \[\*\[a b c\] \*\[a d\]\]
+ \* \*a \*a
+ \*/
+ function nock (s, f) {
+ if (wut(f\[0\]) \=== 0)
+ return \[nock(s, f\[0\]), nock(s, f\[1\])\]
+
+ var idx \= f\[0\]
+
+ if (idx \> 10)
+ throw new Error('invalid formula: ' \+ idx)
+
+ if (useMacros) return macroFormulas\[idx\](s, f\[1\])
+
+ return formulas\[idx\](s, f\[1\])
+ }
+
+ /\* construct a JS noun (group an array into pairs, associating right) \*/
+ function assoc (x) {
+ if (!x.length) return
+ x
+
+ if (x.length \=== 1)
+ return assoc(x\[0\])
+
+ return \[assoc(x\[0\]), assoc(x.slice(1))\]
+ }
+
+ /\* deep equality for arrays or primitives \*/
+ function deepEqual (a, b) {
+ if (a \=== b) return
+ true
+
+ if (!(Array.isArray(a) &&
+ Array.isArray(b))) return false
+ if (a.length !== b.length) return false
+
+ for (var i \=
+ 0; i < a.length; i++) {
+ if (!deepEqual(a\[i\], b\[i\])) return false
+ }
+
+ return true
+ }
+
+ /\* parse a hoon-serialized nock formula and construct a JS noun \*/
+ function parseNoun (x) {
+ if (Array.isArray(x)) return assoc(x)
+
+ if (typeof x \===
+ 'string') {
+ var str \= x.replace(/\[\\."'\]/g, '').split(' ').join(',')
+ return assoc(JSON.parse(str))
+ }
+
+ return x
+ }
+
+ function nockInterface () {
+ var args \= \[\].slice.call(arguments)
+ var subject, formula, noun
+
+ if (args.length \=== 1) {
+ formula \= parseNoun(args\[0\])
+ } else if (args.length \=== 2) {
+ subject \= parseNoun(args\[0\])
+ formula \= parseNoun(args\[1\])
+ } else {
+ noun \= assoc(args)
+ subject \= noun\[0\]
+ formula \= noun\[1\]
+ }
+
+ if (!formula) throw
+ new Error('formula required')
+
+ if (!subject) {
+ // !=(~)
+ subject \= \[1, 0\]
+ }
+
+ return nock(subject,
+ formula)
+ }
+
+ return {
+ nock: nockInterface,
+ \_nock: nock,
+ useMacros: function (arg) {
+ useMacros \= arg \=== undefined ||
+ arg
+ return this
+ },
+ util: {
+ assoc: assoc,
+ parseNoun: parseNoun,
+ deepEqual: deepEqual
+ },
+ operators: {
+ wut: wut,
+ lus: lus,
+ tis: tis,
+ fas: fas
+ },
+ formulas: {
+ slot: slot,
+ constant: constant,
+ evaluate: evaluate,
+ cell: cell,
+ incr: incr,
+ eq: eq,
+ macroIfe: macroIfe,
+ ife: ife,
+ macroCompose: macroCompose,
+ compose: compose,
+ macroExtend: macroExtend,
+ extend: extend,
+ macroInvoke: macroInvoke,
+ invoke: invoke,
+ macroHint: macroHint,
+ hint: hint
+ }
+ }
+ }))
+
+
+
+Python
+------
+
+From [James Tauber](https://web.archive.org/web/20200919052443/https://github.com/jtauber/pynock/blob/master/nock.py):
+
+ #!/usr/bin/env python3
+
+ # \[\]
+ def l(\*lst):
+ if len(lst) \== 1:
+ return(lst\[0\], 0)
+ if len(lst) \== 2:
+ return lst
+ else:
+ return (lst\[0\], l(\*lst\[1:\]))
+
+ \# \*
+ def nock(noun):
+ return tar(noun)
+
+ \# ?
+ def wut(noun):
+ if isinstance(noun, int):
+ return 1
+ else:
+ return 0
+
+
+ \# +
+ def lus(noun):
+ if isinstance(noun, int):
+ return 1 \+ noun
+ else:
+ return noun
+
+
+ \# =
+ def tis(noun):
+ if noun\[0\] \==
+ noun\[1\]:
+ return 0
+ else:
+ return 1
+
+
+ \# /
+ def slot(noun):
+ if noun\[0\] \==
+ 1:
+ return noun\[1\]
+ elif noun\[0\] \==
+ 2:
+ return noun\[1\]\[0\]
+ elif noun\[0\] \==
+ 3:
+ return noun\[1\]\[1\]
+ elif noun\[0\] % 2 \== 0:
+ return slot((2, slot((noun\[0\] //
+ 2, noun\[1\]))))
+ elif noun\[0\] % 2 \== 1:
+ return slot((3, slot(((noun\[0\] \- 1) //
+ 2, noun\[1\]))))
+
+
+ def tar(noun):
+ if isinstance(noun\[1\]\[0\], int):
+ if noun\[1\]\[0\] \== 0:
+ return slot((noun\[1\]\[1\],
+ noun\[0\]))
+ elif noun\[1\]\[0\] \== 1:
+ return noun\[1\]\[1\]
+ elif noun\[1\]\[0\] \== 2:
+ return nock((nock((noun\[0\],
+ noun\[1\]\[1\]\[0\])), nock((noun\[0\],
+ noun\[1\]\[1\]\[1\]))))
+ elif noun\[1\]\[0\] \== 3:
+ return wut(nock((noun\[0\],
+ noun\[1\]\[1\])))
+ elif noun\[1\]\[0\] \== 4:
+ return lus(nock((noun\[0\],
+ noun\[1\]\[1\])))
+ elif noun\[1\]\[0\] \== 5:
+ return tis(nock((noun\[0\],
+ noun\[1\]\[1\])))
+ elif noun\[1\]\[0\] \== 6:
+ return nock(l(noun\[0\],
+ 2, (0, 1), 2,
+ l(1, noun\[1\]\[1\]\[1\]\[0\], noun\[1\]\[1\]\[1\]\[1\]), (1, 0), 2,
+ l(1, 2, 3),
+ (1, 0), 4, 4,
+ noun\[1\]\[1\]\[0\]))
+ elif noun\[1\]\[0\] \== 7:
+ return nock(l(noun\[0\],
+ 2, noun\[1\]\[1\]\[0\],
+ 1, noun\[1\]\[1\]\[1\]))
+ elif noun\[1\]\[0\] \== 8:
+ return nock(l(noun\[0\],
+ 7, l(l(7, (0, 1), noun\[1\]\[1\]\[0\]), 0, 1), noun\[1\]\[1\]\[1\]))
+ elif noun\[1\]\[0\] \== 9:
+ return nock(l(noun\[0\],
+ 7, noun\[1\]\[1\]\[1\],
+ l(2, (0, 1),
+ (0, noun\[1\]\[1\]\[0\]))))
+ elif noun\[1\]\[0\] \== 10:
+ if isinstance(noun\[1\]\[1\]\[0\],
+ int):
+ return nock((noun\[0\],
+ noun\[1\]\[1\]\[1\]))
+ else:
+ return nock(l(noun\[0\],
+ 8, noun\[1\]\[1\]\[0\]\[1\], 7, (0,
+ 3), noun\[1\]\[1\]\[1\]\[0\]))
+ else:
+ return (nock((noun\[0\], noun\[1\]\[0\])), nock((noun\[0\], noun\[1\]\[1\])))
+
+
+
+Ruby
+----
+
+From [T.J. Corcoran](https://web.archive.org/web/20200919052443/https://github.com/TJamesCorcoran/nock/blob/master/nock.rb):
+
+ def str\_to\_tree(str)
+ arr \= \[\]
+ str.scan(/\\+|\\=|\\?|\\/|\\\*|\\\[|\\\]|\\d+/).each
+ do |token|
+ end
+ end
+
+ def max\_depth(arr)
+ ret \= arr.is\_a?(Array) ?
+ \[max\_depth(arr\[0\]), max\_depth(arr\[1\])\].max \+ 1
+ : 1
+ ret
+ end
+
+ def pp(arr)
+ depth \= max\_depth(arr)
+ space \= 128
+ 1.up\_to(8) do |depth|
+ space \= space / 2
+ min \= 2 \*\* (depth \-
+ 1)
+ max \= (2 \*\* depth)
+ \- 1
+ min.upto(max) { |axis|
+ end
+
+ end
+
+ def norm(arr)
+ return arr unless arr.is\_a?(Array)
+ while arr.size \> 2
+ size \= arr.size
+ arr\[size \- 2\] \= \[ arr\[size
+ \- 2\],
+ arr.pop \]
+ end
+ arr \= arr.map { |x| norm(x) }
+ end
+
+ def wut(arr)
+ arr.is\_a?(Array) ?
+ YES : NO
+ end
+
+ def lus(atom)
+ raise "not an atom" unless atom.is\_a?(Fixnum)
+ atom \+ 1
+ end
+
+ def tis(arr)
+ raise "not pair" unless arr.is\_a?(Array) && arr.size \== 2
+ ( arr\[0\] \==
+ arr\[1\] ) ? YES :
+ NO
+ end
+
+ def slot(axis, arr, allow\_error
+ \= true)
+ raise "axis on atom"
+ unless arr.is\_a?(Array)
+ return arr if axis \==
+ 1
+ return arr\[0\] if axis \== 2
+ return arr\[1\] if axis \== 3
+ return slot(2, slot(axis/2, arr))
+ if (axis %2) \== 0
+ return slot(3, slot(axis/2, arr))
+ end
+
+
+ def nock(arr)
+ raise "error: nocking an atom"
+ unless arr.is\_a?(Array)
+
+ oper \= slot(4, arr)
+ a \= slot(2, arr)
+ b \= slot(5, arr)
+
+ if oper.is\_a?(Array)
+ return \[ nock( \[ a, \[b, c\]\]), nock( \[a,
+ d\]) \]
+ end
+
+
+ case oper
+ when 0 then
+ slot(b,a )
+
+ when 1 then
+ b
+
+ when 2 then
+ b\_prime \= slot(2, b)
+ c \= slot(3,b)
+ nock( \[ nock(\[a, b\_primce\]), nock(\[a, c\]) \])
+
+ when 3 then
+ wut(nock(\[a, b\]))
+
+ when 4 then
+ lus(nock(\[a, b\]))
+
+ when 5 then
+ tis(nock(\[a, b\]))
+
+ when 6 then
+ b\_prime \= slot(2, b)
+ c \= slot(6,b)
+ d \= slot(7,b)
+ nock( norm(\[a, 2, \[0, 1\], 2,
+ \[1, c, d\], \[1, 0\], 2,
+ \[1, 2, 3\], \[1,
+ 0\], 4, 4, b\]) )
+
+ when 7 then
+ b\_prime \= slot(2, b)
+ c \= slot(3,b)
+ nock( norm (\[a, 2, b\_prime, 1, c\]))
+
+ when 8 then
+ b\_prime \= slot(2, b)
+ c \= slot(3,b)
+ nock( norm (\[a, 7, \[\[7, \[0, 1\], b\_prime\],
+ 0, 1\], c\]))
+
+ when 9 then
+ b\_prime \= slot(2, b)
+ c \= slot(3,b)
+ nock( norm (\[a, 7, c, 2, \[0, 1\],
+ 0, b\_prime\]))
+
+ when 10 then
+ if wut(slot(2,b)) \== TRUE
+ b\_prime \= slot(4, b)
+ c \= slot(5, b)
+ d \= slot(3, b)
+ c \= slot(3,b)
+ nock( norm (\[a, 8, c, 7, \[0, 3\], d\]))
+ else
+ b\_prime \= slot(2, b)
+ c \= slot(3, b)
+ nock( norm (\[a, 10, \[b, c\]\]))
+ end
+ else
+ raise "error: unknown opcode
+ #{oper.inspect}"
+ end
+ end
+
+
+
+Scala
+-----
+
+From [Steve Randy Waldman](https://web.archive.org/web/20200919052443/https://github.com/swaldman/nnnock/blob/master/src/main/scala/com/mchange/sc/v1/nnnock/package.scala):
+
+ package object nnnock {
+
+ sealed trait Noun;
+ case class Atom( value :
+ Int ) extends Noun;
+ case class Cell( head :
+ Noun, tail : Noun ) extends
+ Noun;
+ implicit def toAtom( value :
+ Int ) : Atom \= Atom( value );
+ implicit def toInt( atom :
+ Atom ) : Int \= atom.value;
+
+ def nock( a :
+ Noun ) : Noun \= \*(a)
+
+
+ object Cell {
+ private def apply( nl :
+ List\[Noun\] ) : Cell
+ \= {
+ nl match {
+ case a
+ :: b :: Nil \=> Cell(a, b);
+ case a
+ :: b :: c :: Nil
+ \=> Cell(a, Cell(b, c));
+ case a
+ :: b :: c :: tail
+ \=> Cell(a, this.apply( b
+ :: c :: tail ) );
+ }
+ }
+ def apply(a : Noun, b : Noun, tail : Noun\*) :
+ Cell \=
+ apply( a :: b :: tail.toList );
+ }
+
+ def ?(
+ noun : Noun ) : Noun
+ \= noun match {
+ case \_
+ : Cell \=> 0;
+ case \_
+ : Atom \=> 1;
+ }
+
+ @tailrec def plus( noun : Noun ) : Noun
+ \= noun match {
+ case a
+ : Atom \=> 1 + a;
+ case c
+ : Cell \=> plus( c ); //intentional endless spin
+ }
+
+ def heq( noun :
+ Noun ) : Atom \= noun
+ match {
+ case Cell( a :
+ Atom, b : Atom ) \=>
+ if ( a == b ) 0 else 1;
+ case Cell( a :
+ Cell, b : Atom ) \=>
+ 1;
+ case Cell( a :
+ Atom, b : Cell ) \=>
+ 1;
+ case Cell( a :
+ Cell, b : Cell ) \=>
+ if ((heq( Cell( a.head, b.head ) ) | heq( Cell( a.tail, b.tail ) )) == 0) 0 else 1;
+ case a
+ : Atom \=> heq( a ); //intentional endless spin
+ }
+
+ def /(
+ noun : Noun ) : Noun
+ \= noun match {
+ case Cell(Atom(1), a) \=> a;
+ case Cell(Atom(2), Cell(a, b)) \=> a;
+ case Cell(Atom(3), Cell(a, b)) \=> b;
+ case Cell(Atom(value),
+ b ) \=> {
+ val a \= value / 2;
+ val num \= if ( value % a
+ == 0 ) 2 else 3;
+ /(Cell(num, /(Cell(a, b))));
+ }
+ case a \=> /( a ); //intentional endless spin
+ }
+
+
+ def \*(
+ noun : Noun ) : Noun
+ \= noun match {
+ case Cell( a, Cell(Cell(b, c), d) )
+ \=> Cell( \*(Cell(a,b,c)),
+ \*(Cell(a,d)) );
+ case Cell( a, Cell(Atom(value), tail) ) \=>
+ {
+ (value, tail) match {
+ case (0, b) \=> /(
+ Cell(b, a) );
+ case (1, b) \=> b;
+ case (2, Cell(b, c)) \=> \*(
+ Cell( \*( Cell(a,b) ), \*( Cell(a,c) ) ) );
+ case (3, b) \=> ?( \*(
+ Cell(a,b) ) );
+ case (4, b) \=> plus( \*(
+ Cell(a,b) ) );
+ case (5, b) \=> heq( \*(
+ Cell(a,b) ) );
+ case (6, Cell(b, Cell(c, d))) \=> \*(
+ Cell(a,2,Cell(0,1),2,Cell(1,c,d),Cell(1,0),2,Cell(1,2,3),Cell(1,0),4,4,b) ); //wtf?
+ case (7, Cell(b, c)) \=> \*(
+ Cell(a,2,b,1,c) );
+ case (8, Cell(b, c)) \=> \*(
+ Cell(a,7,Cell(Cell(7,Cell(0,1),b),0,1),c) );
+ //wtf2
+ case (9, Cell(b, c)) \=> \*(
+ Cell(a,7,c,2,Cell(0,1),0,b) );
+ case (10, Cell(Cell(b,c),d))
+ \=> \*( Cell(a,8,c,7,Cell(0,3),d) );
+ case (10, Cell(b, c)) \=> \*(
+ Cell(a,c) );
+ case \_ \=> \*( noun ); //intentional endless spin
+ }
+ }
+ case a \=> \*( a ); //intentional endless spin
+ }
+ }
+
+
+
+Scheme
+------
+
+From [Kohányi Róbert](https://web.archive.org/web/20200919052443/https://github.com/kohanyirobert/snock/blob/master/snock.ss):
+
+ (import (rnrs (6)))
+
+ (define (i a) a)
+
+ (define (n a r)
+ (cond
+ ((list? a)
+ (let ((l (length a)))
+ (cond
+ ((equal? l 0) (raise 1))
+ ((equal? l 1) (r (car a)))
+ (else
+ (let ((t (cond
+ ((equal? l 2) (cadr a))
+ (else (cdr a)))))
+ (n (car a)
+ (lambda (p) (n t
+ (lambda (q) (r (cons p q)))))))))))
+ ((fixnum? a) (r a))
+ (else (raise 2))))
+
+ (define (wut a)
+ (cond
+ ((fixnum? a) 1)
+ (else 0)))
+
+ (define (lus a)
+ (cond
+ ((equal? (wut a) 0) (raise 3))
+ (else (+ 1 a))))
+
+ (define (tis a)
+ (cond
+ ((equal? (wut a) 1) (raise 4))
+ ((equal? (car a) (cdr a)) 0)
+ (else 1)))
+
+ (define (fas a r)
+ (cond
+ ((equal? (wut a) 1) (raise 5))
+ (else
+ (let ((h (car a))
+ (t (cdr a)))
+ (cond
+ ((not (fixnum? h)) (raise 6))
+ ((equal? h 0) (raise 7))
+ ((equal? h 1) (r t))
+ ((fixnum? t) (raise 8))
+ ((equal? h 2) (r (car t)))
+ ((equal? h 3) (r (cdr t)))
+ (else
+ (fas (cons (div h 2) t)
+ (lambda (p) (fas (cons (+ 2 (mod h 2)) p)
+ (lambda (q) (r q)))))))))))
+
+ (define (tar a r)
+ (cond
+ ((equal? (wut a) 1) (raise 9))
+ (else
+ (let ((s (car a))
+ (f (cdr a)))
+ (cond
+ ((equal? (wut f) 1) (raise 10))
+ (else
+ (let ((o (car f))
+ (v (cdr f)))
+ (cond
+ ((equal? (wut o) 0) (tar (cons s o)
+ (lambda (p) (tar (cons s v)
+ (lambda (q) (r (cons p q)))))))
+ ((equal? o 0) (r (fas (cons v s) i)))
+ ((equal? o 1) (r v))
+ ((equal? o 3) (tar (cons s v)
+ (lambda (p) (r (wut p)))))
+ ((equal? o 4) (tar (cons s v)
+ (lambda (p) (r (lus p)))))
+ ((equal? o 5) (tar (cons s v)
+ (lambda (p) (r (tis p)))))
+ ((equal? (wut v) 1) (raise 11))
+ (else
+ (let ((x (car v))
+ (y (cdr v)))
+ (cond
+ ((equal? o 2) (tar (cons s x)
+ (lambda (p) (tar (cons s y)
+ (lambda (q) (tar (cons p q)
+ (lambda (u) (r u))))))))
+ ((equal? o 7) (tar (n (list (list s) 2 (list x) 1 (list y)) i)
+ (lambda (p) (r p))))
+ ((equal? o 8) (tar (n (list (list s) 7 (list (list 7 (list 0 1) (list x)) 0 1) (list y)) i)
+ (lambda (p) (r p))))
+ ((equal? o 9) (tar (n (list (list s) 7 (list y) 2 (list 0 1) 0 (list x)) i)
+ (lambda (p) (r p))))
+ ((equal? o 10) (cond
+ ((equal? (wut x) 1) (tar (cons s y)
+ (lambda (p) (r p))))
+ (else (tar (n (list (list s) 8 (list (cdr x)) 7 (list 0 3) (list y)) i)
+ (lambda (p) (r p))))))
+ ((equal? (wut y) 1) (raise 12))
+ ((equal? o 6) (tar (n (list
+ (list s)
+ 2
+ (list 0 1)
+ 2
+ (list 1 (list (car y)) (list (cdr y)))
+ (list 1 0)
+ 2
+ (list 1 2 3)
+ (list 1 0)
+ 4
+ 4
+ (list x))
+ i)
+ (lambda (p) (r p))))
+ (else (raise 13)))))))))))))
+
+
+
+Swift
+-----
+
+ import Foundation
+ // 1 :: A noun is an atom or a cell.
+ // 2 :: An atom is a natural number.
+ // 3 :: A cell is an ordered pair of nouns.
+ // 4
+ // 5 :: nock(a) \*a
+ // 6 :: \[a b c\] \[a \[b c\]\]
+ // 7
+ // 8 :: ?\[a b\] 0
+ // 9 :: ?a 1
+ // 10 :: +\[a b\] +\[a b\]
+ // 11 :: +a 1 + a
+ // 12 :: =\[a a\] 0
+ // 13 :: =\[a b\] 1
+ // 14 :: =a =a
+ // 15
+ // 16 :: /\[1 a\] a
+ // 17 :: /\[2 a b\] a
+ // 18 :: /\[3 a b\] b
+ // 19 :: /\[(a + a) b\] /\[2 /\[a b\]\]
+ // 20 :: /\[(a + a + 1) b\] /\[3 /\[a b\]\]
+ // 21 :: /a /a
+ // 22
+ // 23 :: \*\[a \[b c\] d\] \[\*\[a b c\] \*\[a d\]\]
+ // 24
+ // 25 :: \*\[a 0 b\] /\[b a\]
+ // 26 :: \*\[a 1 b\] b
+ // 27 :: \*\[a 2 b c\] \*\[\*\[a b\] \*\[a c\]\]
+ // 28 :: \*\[a 3 b\] ?\*\[a b\]
+ // 29 :: \*\[a 4 b\] +\*\[a b\]
+ // 30 :: \*\[a 5 b\] =\*\[a b\]
+ // 31
+ // 32 :: \*\[a 6 b c d\] \*\[a 2 \[0 1\] 2 \[1 c d\] \[1 0\] 2 \[1 2 3\] \[1 0\] 4 4 b\]
+ // 33 :: \*\[a 7 b c\] \*\[a 2 b 1 c\]
+ // 34 :: \*\[a 8 b c\] \*\[a 7 \[\[7 \[0 1\] b\] 0 1\] c\]
+ // 35 :: \*\[a 9 b c\] \*\[a 7 c 2 \[0 1\] 0 b\]
+ // 36 :: \*\[a 10 \[b c\] d\] \*\[a 8 c 7 \[0 3\] d\]
+ // 37 :: \*\[a 10 b c\] \*\[a c\]
+ // 38
+ // 39 :: \*a \*a
+
+ // 1 :: A noun is an atom or a cell.
+ public indirect enum Noun:
+ IntegerLiteralConvertible, ArrayLiteralConvertible, Equatable, Hashable, CustomStringConvertible
+ {
+ // 2 :: An atom is a natural number.
+ public typealias ATOM = UIntMax
+
+ case Atom(ATOM)
+ // 3 :: A cell is an ordered pair of nouns.
+ case Cell(Noun,
+ Noun)
+ case Invalid
+
+ public static var YES: Noun {
+ return .Atom(0) }
+ public static var NO: Noun {
+ return .Atom(1) }
+
+ public init(\_ noun: Noun) { self = noun }
+
+ // 6 :: \[a b c\] \[a \[b c\]\]
+ public init(\_ nouns: \[Noun\]) {
+ self = .Invalid
+ if nouns.count > 0 {
+ var reverse = nouns.reverse().generate()
+ self = reverse.next()!
+ while let n = reverse.next() {
+ self = .Cell(n, self)
+ }
+ }
+ }
+
+ // protocol IntegerLiteralConvertible
+ public typealias IntegerLiteralType = ATOM
+ public init(integerLiteral value: IntegerLiteralType) {
+ self = .Atom(value)
+ }
+
+ // protocol ArrayLiteralConvertible
+ public typealias Element = Noun
+ public init(arrayLiteral elements: Element...) {
+ self = Noun(elements)
+ }
+
+ // Array subscript
+ public subscript(axis: ATOM) -> Noun {
+ return fas(.Cell(.Atom(axis), self))
+ }
+
+ // protocol Hashable
+ public var hashValue: Int {
+ //return self.description.hashValue
+ switch self {
+ case let .Atom(a):
+ return a.hashValue
+ case let .Cell(a, b):
+ return (5381 + 31 &\* a.hashValue) &+ b.hashValue
+ default:
+ abort()
+ }
+ }
+
+ // protocol CustomStringConvertible
+ public var description: String {
+ return describe()
+ }
+
+ private func describe(depth:
+ Int = 0) -> String {
+ var sub = ""
+ let next = depth+1
+ switch self {
+ case .Invalid:
+ return "\[%INVALID%\]"
+ case let .Atom(atom):
+ return "\\(atom)"
+ case let .Cell(.Cell(a, b), c):
+ sub = "\[\\(a.describe(next)) \\(b.describe(next))\]
+ \\(c.describe(next))"
+ case let .Cell(a, b):
+ sub = "\\(a.describe(next)) \\(b.describe(next))"
+ }
+ return depth == 0 ? "\[\\(sub)\]" : sub
+ }
+ }
+
+ // protocol Equatable
+ public func == (left: Noun, right: Noun) -> Bool
+ {
+ switch (left, right) {
+ case let (.Atom(lhs), .Atom(rhs)): return lhs == rhs
+ case let (.Cell(lp, lq), .Cell(rp, rq)): return lp == rp && lq == rq
+ case (.Invalid, .Invalid): return
+ true
+ default: return false
+ }
+ }
+
+ public func wut(noun:
+ Noun) -> Noun
+ {
+ switch noun {
+ // 8 :: ?\[a b\] 0
+ case .Cell:
+ return Noun.YES
+ case .Atom:
+ // 9 :: ?a 1
+ return Noun.NO
+ default:
+ //return wut(noun)
+ abort()
+ }
+ }
+
+ public func lus(noun:
+ Noun) -> Noun
+ {
+ if case let .Atom(a) = noun {
+ // 11 :: +a 1 + a
+ return .Atom(1+a)
+ }
+ // 10 :: +\[a b\] +\[a b\]
+ //return lus(noun)
+ abort()
+ }
+
+ public func tis(noun:
+ Noun) -> Noun
+ {
+ if case let .Cell(a, b) = noun {
+ // 12 :: =\[a a\] 0
+ // 13 :: =\[a b\] 1
+ return (a == b) ? Noun.YES : Noun.NO
+ }
+ // 14 :: =a =a
+ //return tis(noun)
+ abort()
+ }
+
+ public func fas(noun:
+ Noun) -> Noun
+ {
+ switch noun {
+ // 16 :: /\[1 a\] a
+ case let .Cell(1, a):
+ return a
+ // 17 :: /\[2 a b\] a
+ case let .Cell(2, .Cell(a, \_)):
+ return a
+ // 18 :: /\[3 a b\] b
+ case let .Cell(3, .Cell(\_, b)):
+ return b
+ // 19 :: /\[(a + a) b\] /\[2 /\[a b\]\]
+ // 20 :: /\[(a + a + 1) b\] /\[3 /\[a b\]\]
+ case let .Cell(.Atom(axis), tree):
+ let inner = Noun.Atom(axis / 2)
+ let outer = Noun.Atom(2 + (axis % 2))
+ return fas(.Cell(outer, fas(.Cell(inner, tree))))
+ default:
+ // 21 :: /a /a
+ //return fas(noun)
+ abort()
+ }
+ }
+
+ public func tar(noun:
+ Noun) -> Noun
+ {
+ switch noun {
+ case let .Cell(a, formula):
+ switch formula {
+ // 23 :: \*\[a \[b c\] d\] \[\*\[a b c\] \*\[a d\]\]
+ case let .Cell(.Cell(b, c), d):
+ return .Cell(tar(\[a, b, c\]), tar(\[a, d\]))
+
+ // 25 :: \*\[a 0 b\] /\[b a\]
+ case let .Cell(0, b):
+ return fas(\[b, a\])
+
+ // 26 :: \*\[a 1 b\] b
+ case let .Cell(1, b):
+ return b
+
+ // 27 :: \*\[a 2 b c\] \*\[\*\[a b\] \*\[a c\]\]
+ case let .Cell(2, .Cell(b, c)):
+ return tar(\[tar(\[a, b\]), tar(\[a, c\])\])
+
+ // 28 :: \*\[a 3 b\] ?\*\[a b\]
+ case let .Cell(3, b):
+ return wut(tar(\[a, b\]))
+
+ // 29 :: \*\[a 4 b\] +\*\[a b\]
+ case let .Cell(4, b):
+ return lus(tar(\[a, b\]))
+
+ // 30 :: \*\[a 5 b\] =\*\[a b\]
+ case let .Cell(5, b):
+ return tis(tar(\[a, b\]))
+
+ // 32 :: \*\[a 6 b c d\] \*\[a 2 \[0 1\] 2 \[1 c d\] \[1 0\] 2 \[1 2 3\] \[1 0\] 4 4 b\]
+ case let .Cell(6, .Cell(b, .Cell(c, d))):
+ return tar(\[a, 2, \[0, 1\],
+ 2, \[1, c, d\], \[1, 0\], 2,
+ \[1, 2, 3\], \[1,
+ 0\], 4, 4, b\])
+
+ // 33 :: \*\[a 7 b c\] \*\[a 2 b 1 c\]
+ case let .Cell(7, .Cell(b, c)):
+ return tar(\[a, 2, b, 1, c\])
+
+ // 34 :: \*\[a 8 b c\] \*\[a 7 \[\[7 \[0 1\] b\] 0 1\] c\]
+ case let .Cell(8, .Cell(b, c)):
+ return tar(\[a, 7, \[\[7, \[0, 1\], b\],
+ 0, 1\], c\])
+
+ // 35 :: \*\[a 9 b c\] \*\[a 7 c 2 \[0 1\] 0 b\]
+ case let .Cell(9, .Cell(b, c)):
+ return tar(\[a, 7, c, 2, \[0, 1\],
+ 0, b\])
+
+ // 36 :: \*\[a 10 \[b c\] d\] \*\[a 8 c 7 \[0 3\] d\]
+ case let .Cell(10, .Cell(.Cell(\_, c), d)):
+ return tar(\[a, 8, c, 7, \[0, 3\], d\])
+
+ // 37 :: \*\[a 10 b c\] \*\[a c\]
+ case let .Cell(10, .Cell(\_, c)):
+ return tar(\[a, c\]);
+
+ default:
+ //return tar(noun)
+ abort()
+ }
+ default:
+ //return tar(noun)
+ abort()
+ }
+ }
+
+
+ public var nock\_functions = Dictionary<Noun, Noun -> Noun\>()
+
+ public func dao(formula:
+ Noun) -> Noun\->Noun
+ {
+ if let cached = nock\_functions\[formula\] {
+ return cached
+ }
+
+ let compiler = { () -> Noun -> Noun
+ in
+ switch formula {
+ case let .Cell(.Cell(b, c), d): // Distribution
+ let (p, q) = (dao(.Cell(b, c)), dao(d))
+ return { a in .Cell(p(a), q(a)) }
+
+ case let .Cell(0, b): // Axis
+ return { a in fas(.Cell(b, a)) }
+
+ case let .Cell(1, b): // Just
+ return { \_ in b }
+
+ case let .Cell(2, .Cell(b, c)): //
+ Fire
+ let (f, g) = (dao(b), dao(c))
+ return { a in dao(g(a))(f(a)) }
+
+ case let .Cell(3, b): // Depth
+ let f = dao(b)
+ return { a in wut(f(a)) }
+
+ case let .Cell(4, b): // Bump
+ let f = dao(b)
+ return { a in lus(f(a)) }
+
+ case let .Cell(5, b): // Same
+ let f = dao(b)
+ return { a in tis(f(a)) }
+
+ case let .Cell(6, .Cell(b, .Cell(c, d))): //
+ If
+ let (p, q, r) = (dao(b), dao(c), dao(d))
+ return { a in
+ switch p(a) {
+ case Noun.self.YES:
+ return q(a)
+ case Noun.self.NO:
+ return r(a)
+ default:
+ return tar(.Cell(a, formula))
+ }
+ }
+
+ case let .Cell(7, .Cell(b, c)): //
+ Compose
+ let (f, g) = (dao(b), dao(c))
+ return { a in g(f(a)) }
+
+ case let .Cell(8, .Cell(b, c)): //
+ Push
+ let (f, g) = (dao(b), dao(c))
+ return { a in g(.Cell(f(a), a)) }
+
+ case let .Cell(9, .Cell(b, c)): //
+ Call
+ let f = dao(c)
+ return { a in
+ let core = f(a)
+ let arm = dao(fas(.Cell(b, core)))
+ return arm(core)
+ }
+
+ case let .Cell(10, .Cell(.Cell(\_, c), d)): //
+ Hint
+ let ignore = dao(c)
+ let f = dao(d)
+ return { a in ignore(a); return f(a) }
+
+ case let .Cell(10, .Cell(\_, c)): // Hint
+ let f = dao(c)
+ return { a in f(a) }
+
+ default:
+ return { a in tar(.Cell(a, formula)) }
+ }
+ }
+
+ let r = compiler()
+ nock\_functions\[formula\] = r
+ return r
+ }
+
+ public func nock(n: Noun) -> Noun
+ {
+ if case let .Cell(a, b) = n {
+ let f = dao(b)
+ return f(a)
+ }
+ return nock(n)
+ }