From ca8a67a583ad39bdb4cf36d635536e099af21bdf Mon Sep 17 00:00:00 2001 From: polwex Date: Tue, 7 Oct 2025 02:24:42 +0700 Subject: solved NOCK issue, fucking bytecode man. but solid pill working!! --- docs/nock/nock-for-everyday-coders-1.md | 589 +++++++ docs/nock/nock-for-everyday-coders-2.md | 334 ++++ docs/nock/nock_implementations.md | 2525 +++++++++++++++++++++++++++++++ docs/nock_implementations.md | 2525 ------------------------------- vere/build.zig | 10 + vere/pkg/noun/nock.c | 69 +- vere/pkg/noun/vortex.c | 6 + vere/pkg/vere/solid_boot_test.c | 121 +- vere/pkg/vere/test_bisect_lifecycle.c | 74 + vere/pkg/vere/test_op8.c | 49 + vere/pkg/vere/test_opcodes.c | 78 + vere/pkg/vere/test_simple_lifecycle.c | 42 + zod/.urb/log/0i0/data.mdb | Bin 12832768 -> 12832768 bytes zod/.urb/log/0i0/lock.mdb | Bin 8192 -> 8192 bytes zod/.urb/log/0i0/vere.txt | 2 +- zod/.vere.lock | 2 +- 16 files changed, 3862 insertions(+), 2564 deletions(-) create mode 100644 docs/nock/nock-for-everyday-coders-1.md create mode 100644 docs/nock/nock-for-everyday-coders-2.md create mode 100644 docs/nock/nock_implementations.md delete mode 100644 docs/nock_implementations.md create mode 100644 vere/pkg/vere/test_bisect_lifecycle.c create mode 100644 vere/pkg/vere/test_op8.c create mode 100644 vere/pkg/vere/test_opcodes.c create mode 100644 vere/pkg/vere/test_simple_lifecycle.c diff --git a/docs/nock/nock-for-everyday-coders-1.md b/docs/nock/nock-for-everyday-coders-1.md new file mode 100644 index 0000000..694d4d8 --- /dev/null +++ b/docs/nock/nock-for-everyday-coders-1.md @@ -0,0 +1,589 @@ +Nock for Everyday Coders, Part 1: Basic Functions and Opcodes +By ~timluc-miptev + +Series Outline +Part 1: Basic Functions and Opcodes +Part 2: The Rest of Nock and Some Real-World Code +Interlude: Loose Ends and FAQ +Part 3: Design Patterns and Real Programs +Table of Contents +Intro +Getting Started and Confusing Points +Nock's Simplest Functions, 0 and 1 +4, the Incrementing Function +Cell-Maker (aka the Distribution Rule) +3 and 5, the "Is This a Cell?" and "Equality Test" Functions +2, the "Subject-Altering" Function +Summary and First Hoon Connections +End of Part 1 +tldr;/Intro +For Whom? +Nock is not super complex, and most normal programmers can learn the basics of it rapidly. The mental model you gain from Nock turns out to be very useful in learning Hoon and understanding Urbit. + +The Urbit docs generally suggest not worrying about Nock, but it's very simple and small, so I think most normal programmers will feel more comfortable in Hoon if they learn its basics. + +By "normal programmer", I mean: a person of somewhat above-average IQ (programming is a pretty self-selecting profession) who works relatively high up the modern tech stack, abstracted from low-level stuff. His/her day job likely consists of a lot of React/JS/SQL/Rails/Java etc, and he/she is not really familiar with assembly/OS stuff. That describes me and most programmers I know. + +Why Is This Necessary? +The official Nock docs on the Urbit site are accurate, thorough, and well-explained. However, they take a little while to get to the practical examples, and probably lose a fair number of people along the way. I'm not trying to be original; I'm just a translator. + +If I say something here and you're like "wait, isn't that already in the Nock docs?", the answer is yes, it is; I'm just changing the order for clarity/teaching purposes, in ways that were helpful to me when I learned. + +Goals by the End of the Series +to explain Nock clearly in terms most programmers will relate to +impart a feeling of confidence with very basic Nock +give a knowledge of Nock’s idioms and big "wins" so that they carry over to Hoon learning +Getting Started--Confusing Points +When people first look at Nock, they see the definition page, which, tbh, is fairly intimidating. + +I'm talking about lines like this in the spec: + +/[(a + a) b] /[2 /[a b]] +The problem is, the programmer already probably knows that Nock code he's seen looks more like the below--just lists of numbers, with no symbols: + +[6 + [5 [0 6] [1 0]] + [0 7] + [9 4 [[0 2] [2 [0 6] [0 5]] [4 0 7]]] +] +So what gives? Which one is the "real" Nock? + +Phases of Nock +We are looking at two different things in the examples above: + +Pseudocode for how to interpret +Nock code to be interpreted (the lists of numbers) +The symbols and spec are pseudocode, not real Nock code. They could just as easily be written in English, and they will never be written down as actual Nock code and given to an interpreter. They represent what an interpreter should do to turn Nock code into interpreter instructions. + +The lists of numbers are the actual Nock code. This is what you feed to an interpreter to get some result. + +What Is a Nock Interpreter? +An interpreter can be a computer program, or it can be a human manually expanding Nock code into results. In both cases, the program and human have to know the Nock pseudocode in order to do the right thing with incoming Nock code. + +So a Nock interpreter is any entity that takes Nock code as input, and gives a noun as output. A noun can be: + +:: a number +782 +:: a cell (pair with two elements) +[782 9872] +:: each element can itself be a pair +[782 [9872 89728]] +:: the above can be written as +[782 9872 89728] +How to Run Nock Code +We will be expanding Nock pseudocode manually in the examples that follow, in effect acting as our own interpreter. + +If we want to check that we're getting the right results from our manual interpretation, the Urbit dojo has a Hoon function that runs Nock code (i.e. a Nock interpreter). To use it: + +Start up a dojo session (see here for how to create a fake ship) +At the prompt, we can execute Nock with .*(NOCK_SUBJECT, NOCK_FORMULA) +Subject, Formula? +Let's keep this simple: + +subject = an argument to a function +formula = the function +That's it. We'll see below how this works, going really slowly with examples. + +Evaluating Our First Nock Code +OK, so the interpreter takes two arguments, a "subject" and a "formula". Both are nouns (a number or a cell). Let's run some insanely simple Nock code in the Dojo: + +~zod:dojo> .*(42 [0 1]) +42 +In the above, 42 is our subject. [0 1] is our formula. + +Formulas are always cells, and the first element of the cell is a number that you can think of as the name of the function. + +In this case, our function name is 0, which is the memory slot function. It is always followed by 1 number, in this case 1, which is the number of the memory slot to fetch in the subject. + +Whenever we look at Nock code, we want to ask: + +What is the subject (function argument)? In this case, it's 42. +What is the formula (function)? In this case, it's [0 1]. +What value does that formula (function) produce when called on this subject (argument)? In this case, the return value is 42. +Why is the return value 42? How does this formula work? + +Nock's Simplest Functions, 0 and 1 +The two most basic Nock functions are 0 and 1. The goal here is to get strong intuitions of what they do, how they handle edge cases, and how this relates to the Nock spec/pseudocode. + +Prerequisite +Note: Before getting started, you should understand how Nock/Hoon handle addresses in binary trees. I have put 3 resources below + +If you already understand why memory slot 5 of + +[['apple' %pie] [0b1101 0xdad]] +is %pie, you are good to go and can skip this explanation. + +Binary Tree Explanations +official Hoon documents on binary trees +the Tree Addressing sectiion +My explanation: Every noun in Nock can be thought of as a tree, which means we can give an exact number to access any position in the tree. This means that, no matter how big our subject (argument) is, we can yank a value out of any part of it. + +How do you say which slot number you want from the tree? + +1 is the tree root +The head of every node n is 2n +the tail is 2n+1 +Let's take an example tree to illustrate. In Nock cell form, the tree is: + +[[4 5] [6 14 15]] +Drawn as ASCII art (this is not real Nock code), the tree looks like: + + 1 + 2 3 +4 5 6 7 + 14 15 +So + +1 is the address of the whole tree, [[4 5] [6 14 15]] +2 is the address of the left branch, [4 5] +3 is the address of the right branch, [6 14 15] +15 is the value 15 +Let's play around now in the dojo Nock interpreter so that we can confirm this. In each example, our subject (argument) will be the tree [[4 5] [6 14 15]]. + +:: formula (function) [0 1]: get the whole tree +~zod:dojo> .*([[4 5] [6 14 15]] [0 1]) +[[4 5] 6 14 15] + +:: formula (function) [0 2]: get the left branch +~zod:dojo> .*([[4 5] [6 14 15]] [0 2]) +[4 5] + +:: formula (function) [0 7]: get the subtree in slot 7 +~zod:dojo> .*([[4 5] [6 14 15]] [0 7]) +[14 15] +0, the "Memory Slot" Function +The pseudocode for the 0 opcode is as follows: + +*[a 0 b] /[b a] +/[b a] is pseudocode. In English, it means "a is a binary tree. Get the memory slot numbered b. + +So if a were the tree [9 10], and b were 1, we'd get the memory slot 1 in the tree [9 10], which is just the tree itself. + +Written in pseudocode, that's /[1 [9 10]]. We can also do /[2 [9 10]], which grabs memory slot 2, aka 9. + +Let's look at some examples. I've put them first in Dojo form so that you can see how they run in that interpreter, and then I show the "human interpreter" in pseudocode below that. + +Example 1 +:: get memory slot 1 +~zod:dojo> .*([50 51] [0 1]) +[50 51] +:: PSEUDOCODE -- replaces the Dojo's () with [] +*[[50 51] [0 1]] +:: *[a 0 b] -> a = [50 51], b = 1 +/[1 [50 51]] +[50 51] +Example 2 +~zod:dojo> .*([50 51] [0 2]) +50 +:: PSEUDOCODE -- replaces the Dojo's () with [] +*[[50 51] [0 2]] +:: *[a 0 b] -> a = [50 51], b = 2 +/[2 [50 51]] +50 +Example 3 -- A Crash! +~zod:dojo> .*([50 51] [0 [0 1]]) +ford: %ride failed to execute: +:: PSEUDOCODE +*[[50 51] [0 [0 1]]] +/[[0 1] [50 51]] +:: can't evaluate this--a memory slot must be a number like 2, not a cell like [0 1] +CRASH +Summary of 0 +0 is everywhere in Nock, because "get something from a memory slot" really means "store stuff in a place and get it whenever I want," which is another name for creating variables. These memory slot numbers take the place of variable names. + +If you're familiar with assembly or C, they're conceptually similar to memory pointers or registers in how Nock uses them. Keep in mind though, Nock is functional/immutable, so it doesn't update memory locations: it creates copies of data structures with altered values. + +We've also seen that the memory slot function can't take just anything as the memory slot to fetch: it must receive an atom (number). + +1, the "Quoter" Function +This is another really simple function. You can think of it as a "quoter": it just returns any value passed to it exactly as it is. It ignores the subject, and just quotes the value after it. Let's look at a couple examples. + +~zod:dojo> .*([20 30] [1 67]) +67 +:: [1 2 587] is same as [1 [2 [587]]] +~zod:dojo> .*([20 30] [1 [2 [587]) +[2 587] +~zod:dojo> +It doesn't matter how much information is after the 1: 1 is a dumb function that just returns it all. + +The pseudocode for 1 is: + +*[a 1 b] b +In English, this means: "ignore the subject a, and just return everything after the 1 exactly as it is. + +Let's look at our first example, .*([20 30] [1 67]). The subject a is [20 30], so we ignore that. What's after the 1? 67, so we return that. + +In the second example, "everything after the 1" is longer, but the same rule applies: the Nock interpreter just returns it exactly as it is, after stripping out unnecessary brackets. + +4, the Incrementing Function +0 and 1 are simple functions that don't have any nested behavior. Now we're going to move to a function that does have nested children. + +Here are code examples of opcode 4, and then we'll look at its pseudocode and break down the examples. + +:: example 1 +~zod:dojo> .*(50 [4 0 1]) +51 + +::example 2 +~zod:dojo> .*(50 [4 4 0 1]) +52 + +:: example 3 +~zod:dojo> .*([100 150] [4 4 0 3]) +152 + +:: example 4 +~zod:dojo> .*(50 [4 1 98]) +99 + +::example 5 +~zod:dojo> .*(50 [4 1 [0 2]]) +ford: %ride failed to execute: +Here's 4's pseudocode, juxtaposed with that of 0 and 1: + +:: 4 +*[a 4 b] +*[a b] + +:: 0 +*[a 0 b] /[b a] + +:: 1 +*[a 1 b] b +In English, this says "when we have subject a, function 4, and Nock code b, first evaluate [a b] as [subject formula], and then add 1 to the result." + +If we contrast with 0 and 1, we see that the right side has a * symbol. This symbol means "evaluate the expression again in the Nock interpreter." 0 and 1 did not have this symbol, and that's why they couldn't evaluate nested Nock expressions. + +Example 1 Breakdown +.*(50 [4 0 1]) +Let's start by translating the dojo's .*(subject formula) to pseudocode of the form *[a b], and then expand it line by line: + +:: change to pseudocode +*[50 [4 0 1]] +:: move the 4 to the outside as + ++*[50 [0 1]] +:: expand the 0 opcode to the memory slot operator "/" ++/[1 50] +:: grabs the memory slot ++(50) +:: evaluate +51 +The [a b] part of +*[a b] expands to: + +[50 [0 1]] +OK, this we know how to handle! It's just our 0 function, and it wants the value in memory slot 1 of the subject. That's 50. + +So now we that *[a b] = 50, and we just have to add 1 to it (the + in +*[a b]). That is 51, which is exactly what the interpreter gave us. + +Example 2 Breakdown +This one is similar, we just have an extra 4. We again start by translating the dojo's .*(subject formula) to pseudocode, and then expand + +*[50 [4 4 0 1]] +:: the first 4 moves outside as a '+' ++*[50 [4 0 1]] +:: 2nd 4 becomes a '+'...OK, we have the 0 function again! +++*[50 [0 1]] +++/[1 50] +++(50) ++(51) +52 +Example 3 Breakdown +Here we again see lots of 4s applied consecutively, and we also see how we can yank values out of a more complicated subject and manipulate them. Notice how the subject is a cell, not an atom. The rest is the same as in Example 2. + +a (the subject) = [100 150] +*[[100 150] [4 4 0 3]] ++*[[100 150] [4 0 3]] +:: Now we've extracted all the increments ("+" signs) +:: So we just grab the value at memory slot 3 ([0 3] command) +++*[[100 150] [0 3]] +++/[3 [100 150]] +++(150) ++(151) +152 +Example 4 Breakdown +In the below example, we see how we can use the quote/constant function 1 to generate the value 98 and increment it. We ignore the subject 50 completely. + +*[50 [4 1 98]] +:: formula is the "quoter" function: [1 98] ++*[50 [1 98]] ++(98) +99 +Example 5 Breakdown +Just as opcode 0 had values it couldn't handle (non-atoms), so opcode 4 needs the nested value inside it to evaluate to an atom. + +*[50 [4 1 [0 2]]] +:: OK cool, the nested value is a 1 opcode, let's see what happens ++*[50 [1 [0 2]] +:: 1 ignores the subject (50) and just returns [0 2] ++([0 2]) +:: oh crap, [0 2] isn't an atom, how can we increment it??? We can't so we crash +ford: %ride failed to execute: +Summary +In these examples, we've seen that function 4 can be called as many times in a row as we want. At the end of those calls, it always ends up incrementing a number that either is yanked from the subject (memory slot function 0) or quoted as it is (quote function 1). + +the Cell-Maker (aka the Distribution Rule) +The Nock interpreter is allowed to return nouns, which are atoms (positive numbers) or cells (pairs of nouns). What have our functions/opcodes been returning so far? + +0: atoms or cells, depending what's in the memory slot that we yoink +1: atoms or cells, depending on what we quote +4: just atoms +But what if my subject was [51 67 89], and I wanted to increment every value and return that as [52 68 90]? How can I do that when it's a cell, and 4 only seems able to return atoms? + +The answer is something that the Nock docs call the "Distribution Rule" or "implicit cons" (hello, fellow LISPers!), but that I find easiest to think of as the "Cell-Maker Rule." + +A Quick Detour into Nock Formulas +We haven't talked much yet about what values are legal to feed into the Nock interpreter (the .*(subject formula) function in the dojo). So far, we've only been using formulas that start with numbers (our functions/opcodes 0/1/4). + +Let's now fully solidify our understanding of what's a legal formula by taking a quick look at the 3 possible cases (the format is`.(subject formula)`*): + +:: formula is a cell starting with an atom--works; we knew this +~zod:dojo> .*(50 [0 1]) +50 + +:: formula is just an atom (0)--error, not valid +~zod:dojo> .*(50 0) +ford: %ride failed to execute: + +:: now we do: .*(subject [cell1 cell2]) +:: so...formula is a cell that starts with a cell...wtf, this works? +~zod:dojo> .*(50 [[0 1] [1 203]]) +[50 203] +The Cell-Maker in All His Glory +So apparently a formula cell can start with a cell. The Cell-Maker rule is (from the Nock docs): + +*[subject [formula-x formula-y]] +=> [*[subject formula-x] *[subject formula-y]] +In our example above, formula-x is [0 1], and formula-y is [1 203]. They each evaluate individually against the subject, and the end result is a cell. + +Using Our New Powers +We can make as many cells in a row as we want! + +~zod:dojo> .*(50 [[0 1] [1 203] [0 1] [1 19] [1 76]]) +[50 203 50 19 76] +We can put any operation inside each cell! + +:: [19 20] is our subject, just to be clear +:: the rest is formulas you've seen before +~zod:dojo> .*([19 20] [[0 1] [1 76] [4 4 0 3]]) +[[19 20] 76 22] +If we take the returned collection [[19 20] 76 22] in order, we can write in English how they connect to our collection of formulas that we passed: + +[19 20]: [0 1], get memory slot 1 +76: [1 76], return the quoted value 76 +22: [4 4 0 3], increment twice the value in memory slot 3 (20) +So we can pass one small subject ([19 20]) and make an arbitrarily long collection of values from it, using any functions we want. Cell-Maker ftw! + +3 and 5, the "Is This a Cell?" and "Equality Test" Functions +Now we come to functions/opcodes 3 and 5, which are pretty straightforward after we've seen how 4 and the Cell-Maker work. Functions 3 and 5, like 4, allow nested evaluation. Let's put all their pseudocode definitions together to compare: + +:: function/opcode 3 +*[a 3 b] ?*[a b] +:: function/opcode 4 +*[a 4 b] +*[a b] +:: function/opcode 5 +*[a 5 b c] =[*[a b] *[a c]] +First of all, notice how the right side of all these "equations" has the evaluation operator, *. This means that these functions can have nested formulas, since they keep evaluating all the way down. + +There are some new pseudocode symbols here that we need to translate into English. We already know +: "increment the value after this". Now we also see: + +?: "check whether the value after this is a cell. Return 0 if yes, 1 if no" +=: "first run the function in b with subject a as the argument, and same for the function in c. If the results are equal, return 0, if not, return 1. +Dojo + Pseudocode Examples of 3, the "Is-This-A-Cell?" Function +Example 1 -- not a cell + +~zod:dojo> .*(50 [3 0 1]) +1 +:: PSEUDOCODE OF THE ABOVE +*[50 [3 0 1]] +?*[50 [0 1]] +::get memory slot 1 of the subject +?(50) +:: is 50 a cell? No, so return 1 +1 +Example 2 -- yes, this is a cell + +~zod:dojo> .*([50 51] [3 0 1]) +0 +:: PSEUDOCODE OF THE ABOVE +*[[50 51] [3 0 1]] +?*[[50 51] [0 1]] +::get memory slot 1 of the subject: [50 51] +?([50 51]) +:: is [50 51] a cell? Yes, so return 0 +0 +Example 3 -- nested evaluation with one of the other 3/4/5 functions/opcodes + +~zod:dojo> .*([50 51] [4 4 3 0 1]) +2 +:: PSEUDOCODE OF THE ABOVE +*[[50 51] [4 4 3 0 1]] ++*[[50 51] [4 3 0 1]] +:: whatever comes out of the 3 function, we're gonna increment twice +++*[[50 51] [3 0 1]] +:: down to just fetching memory slot 1 +++?*[[50 51] [0 1]] +++?([50 51]) +:: is [50 51] a cell? Yes, so return 0 +++(0) ++(1) +2 +Example 4 -- check whether multiple things are cells using Cell-Maker + +~zod:dojo> .*([[50 51] 52] [[3 0 2] [3 0 3]]) +[0 1] +*[[[50 51] 52] [[3 0 2] [3 0 3]]] +?[*[[50 51] 52] [0 2]] + *[[50 51] [0 3]]] +:: yank memory slots 2 and 3 +?*[[50 51] 52] +:: first is a cell, second is not +[0 1] +Dojo + Pseudocode Examples of 5, the "Equals" Function +Because 5 compares the results of 2 formulas, it always makes 2 inner evaluations of the subject. It's similar to Cell-Maker in this way. + +Example 1 -- compare two equal values + +~zod:dojo> .*([50 51] [5 [0 2] [0 2]]) +0 +:: PSEUDOCODE +*[[50 51] [5 [0 2] [0 2]]] +:: factor out the = +=[*[[50 51] [0 2]] *[[50 51] [0 2]]] +:: get memory slot 2 twice +=(50 50) +0 +Example 2 -- compare two unequal values + +~zod:dojo> .*([50 51] [5 [0 2] [0 3]]) +1 +:: PSEUDOCODE +*[[50 51] [5 [0 2] [0 3]]] +:: factor out the = +=[*[[50 51] [0 2]] *[[50 51] [0 3]]] +:: get memory slot 2 and memory slot 3 +=(50 51) +1 +Example 3 -- compare two values, one of which comes from a nested function + +~zod:dojo> .*([50 51] [5 [4 0 2] [0 3]]) +0 +:: PSEUDOCODE +*[[50 51] [5 [4 0 2] [0 3]]] +:: factor out the = +=[*[[50 51] [4 0 2]] + *[[50 51] [0 3]]] +:: factor out the + +=[+*[[50 51] [0 2]] + *[[50 51] [0 3]]] +:: get memory slots 2 and 3 +=[+50 51] +:: evaluate the + +=([51 51]) +0 +Example 4 -- 5 knows how to compare cells, not just atoms. Erotic. + +~zod:dojo> .*([99 99] [5 [1 [99 99]] [0 1]]) +0 +:: PSEUDOCODE +*[[99 99] [5 [1 [99 99]] [0 1]]] +=[*[[99 99] [1 [99 99]]] + *[[99 99] [0 1]]] +:: first cell is the result of quoter function (ignores subject) +:: second cell is the result of fetching memory slot 1 in the subject +=[[99 99] [99 99]] +:: true +0 +2, the "Subject-Altering" and "Stored Procedure" Function +In all our examples so far, the subject has been defined at the start when we call the interpreter, and never changes. But what if we want a different subject? + +Motivation, or "Why Would I Want a Different Subject?" +A different subject? Why would we want that? Here's an easy example. Say you found the following piece of Nock code on the interwebz: + +[8 [1 0] 8 [1 6 [5 [0 7] 4 0 6] [0 6] 9 2 [0 2] [4 0 6] 0 7] 9 2 0 1] +This is the code for a Nock function that expects a subject that is an atom, and decrements that subject by 1. You could actually enter it in the Dojo right now: + +~zod:dojo> .*(100 [8 [1 0] 8 [1 6 [5 [0 7] 4 0 6] [0 6] 9 2 [0 2] [4 0 6] 0 7] 9 2 0 1]) +99 +It works! You do not have to understand the code right now; just try entering different numbers instead of 100 to see the program working. + +But now imagine that I have a Nock program with a different subject + +~zod:dojo> .*([50 51] some-formula) +And somewhere inside some-formula, I want to decrement the number 51. I can't pass [50 51] as the subject to my decrementer code above though, because that's a cell, and it expects just an atom (a number). + +Subject Altering to the Rescue +The function/opcode 2 is designd to handle this problem for us. Here's the pseudocode, and then I'll explain what the pseudocode means. + +:: PSEUDOCODE +*[a 2 b c] *[*[a b] *[a c]] +2 expects 2 formulas after the subject a: b and c. With those, it: + +runs formula b against the subject to set up a new environment/subject derived from the subject +runs formula c against the subject to prepare a 2nd function. +run that 2nd function against the new environment/subject from step (1) +Note that the pseudocode for 2 has nested *s. + +*[*[a b] *[a c]] +The 2 inner *s run steps (1) and (2), and the outer one, around the whole expression, runs step (3). + +Examples +Example 1 — change the subject, have step (2) just call a constant value +~zod:dojo> .*([50 51] [2 [0 3] [1 [4 0 1]]]) +52 +:: PSEUDOCODE +*[[50 51] [2 [0 3] [1 [4 0 1]]]] +:: separate b and c to each run against the subject (steps 1 and 2) +*[*[[50 51] [0 3]] *[[50 51] [1 [4 0 1]]]] +:: after steps 1 and 2, we have a new subject, 51! +:: note how we're back in normal *[subject formula] form +*[51 [4 0 1]] +:: apply the 4 function as we're used to ++*[51 [0 1]] +:: grab 51 from memory slot 1 ++(51) +52 +Example 2 — grab a block of code from the subject in step (2), then run it in step (3). +Think of this as grabbing a "stored procedure" from the subject. + +~zod:dojo> .*([[4 0 1] 51] [2 [0 3] [0 2]]]) +52 +:: PSEUDOCODE, subject is [[4 0 1] 51] +*[[[4 0 1] 51] [2 [0 3] [0 2]]]] +*[*[[[4 0 1] 51] [0 3] + *[[[4 0 1] 51][0 2]] +:: step 1 gets memory slot 3, step 2 grabs memory slot 2 +*[51 [4 0 1]] +:: looks like a normal 4 opcode to me! ++*[51 [0 1]] +:: grab memory slot 1 ++(51) +52 +Example 3: Back to Our Motivating Case +Remember our decrementing block of code that we couldn't use when the subject was [50 51], instead of just an atom? Opcode 2 makes handling that issue a piece of cake. + +We simply use 2 to transform our subject into an atom, and use 1 to quote the decrement block of code before it evaluates in step (3). + +~zod:dojo> .*([50 51] [2 [0 2] [1 [8 [1 0] 8 [1 6 [5 [0 7] 4 0 6] [0 6] 9 2 [0 2] [4 0 6] 0 7] 9 2 0 1]]]) +49 +:: PSEUDOCODE (subject is [50 51]) +:: I substitute "decrement-formula" for that block of Nock code for clarity +*[[50 51] [2 [0 2] [1 decrement-formula]]] +*[*[[50 51] [0 2]] *[[50 51] [1 decrement-formula]]] +:: 1, the quoting function, just returns the decrement-formula, like in Example 1 +*[50 decrement-formula] +::decrement-formula has the atom subject that it wants now! +49 +Summary and First Hoon Connections +For those who know a bit of Hoon, Example 2 above is similar-ish to calling an arm that produces a gate, and then running the gate. Most of Hoon runs this type of stored-procedure + subject-altering Nock, and it all uses opcode 2 at its base. + +And for those who know Hoon, [[4 0 1] 51] should already be looking a lot like [battery payload]...that's not a coincidence. + +We're starting to see the first glimpses of how Hoon's cores, arms and subjects/subject mutations flow out of the fundamental structure of Nock. + +We also see how Hoon/Nock lend themselves well to throwing around chunks of code, and adjusting the subject as necessary to create the correct subject/environment against which to run that code. + +End of Part 1 +You now have seen all the fundamental functions/opcodes in Nock. In Part 2, I'll introduce the remaining functions, which no longer need pseudocode: we have enough scaffolding now to build the rest of Nock from Nock itself. Instead, these new opcodes will just be shortcuts/macros/code expansions of opcodes 1-5. + +If you already feel like you're comfortable with Nock now, you can skip my Part 2, and jump directly to the section of the Nock Explanation titled Sugar to see the rest of the opcodes explained. + +We'll also start to connect Nock to Hoon, and see how most of the fundamental (and slightly weird) features of Hoon flow directly from Nock's structure, and make a lot more sense in combination with it. diff --git a/docs/nock/nock-for-everyday-coders-2.md b/docs/nock/nock-for-everyday-coders-2.md new file mode 100644 index 0000000..bc6dc37 --- /dev/null +++ b/docs/nock/nock-for-everyday-coders-2.md @@ -0,0 +1,334 @@ +Nock for Everyday Coders, Part 2 +The Rest of Nock and Some Real-World Code +By ~timluc-miptev + +Series Outline +Part 1: Basic Functions and Opcodes +Part 2: The Rest of Nock and Some Real-World Code +Interlude: Loose Ends and FAQ +Part 3: Design Patterns and Real Programs +A word of encouragement: we're done with the hard part now. Every Nock function we learn in this section will be built from pieces in Part 1. + +None of these new functions are necessary to make Nock work. All of them except Nock 10 and 11 are syntatic sugar that is part of the Nock definition, and must be built into every correct Nock implementation. If you have seen macros or code expansions in other languages, that's another word for what's happening here. + +Nock 10 makes it easier to replace a memory value somewhere in a tree, and Nock 11 allows passing hints to the interpreter. + +One point of clarity: this syntactic sugar/code-expansion system is not extensible. This means that you can't invent your own Nock opcodes and still have that language be Nock; you're making a higher-level language on top of Nock at that point. + +In fact, that's not a bad way to think of Hoon: it's a higher-level language that adds missing syntatic sugar/macros and human-readable names to Nock. (That's not the whole story, but it's a decent mental peg to initially hang Hoon on.) + +Nock is intentionally very, very small, such that you can always walk through and analyze what is happening in a block of code if you know Nock's opcodes. + +Table of Contents +An Opening Note about the 1 Function +6, "If/Else" +7, the "Composition" Opcode +8, the "Variable Adder" Opcode +9, Create a Core and Run One of Its Arms +10, Replace a Memory Slot +Real Nock Code +An Opening Note about the 1 Function +You're going to see the 1 opcode (the "Quoter") appear in a lot of examples below for a simple reason: the 6-9 opcodes expect formulas in a lot of places, not just atoms. And, as we've seen already, formulas have to be cells. + +Whenever a formula is required, but you really just want to return a number, you use the quoter function. + +Example 1: Increment the Number 5 +:: we just want to run 4 on the number 5, but 4 expects a formula after it, so we use [1 5] +~zod:dojo> .*(0 [4 1 5]) +6 +Example 2: Compare a Memory Slot to a Number +:: we grab memory slot 2 +:: then it has to be compared to the result of a formula +:: so we just use the formula [1 23] to return 23 +~zod:dojo> .*([23 45] [5 [0 2] [1 23]]) +0 +6, "If/Else" +We're into the "Sugar" part of Nock now. This means that all the functions in Part 2 will use only * and Nock code in their pseudocode. For example, here is the code for 6, the "If/Else" function. + +*[a 6 b c d] *[a *[[c d] 0 *[[2 3] 0 *[a 4 4 b]]]] +English Definition (Really Simple) +The English definition of what's happening is simple/boring (refer to the left side of the definition above): + +evaluate formula b against subject a (*[a b]) to see whether it's 0 or 1 +If b equals 0 ("true"), run formula c against a. +If b equals 1 ("false"), run formula d against a. If b is not equal to 0 or 1, the code crashes, for reasons we'll see in the code explanation. +Code Expansion Explanation (Way More Fun) +OK, so that's the English version. The code explanation (the right side of the definition) is really fun now that we know the basic Nock opcodes. + +The pseuodocode has 4 nested [subject formula]s, so I'm going to unwrap those to the bottom, and then build it up again. The layers are, in order: + +*[a ...], i.e. subject a evaluated against that long formula starting with *[[c d]... +subject [c d] evaluated against the formula starting with [[2 3... +subject [2 3] evaluated against the lowest level formula +finally, subject a evalauted against formula [4 4 b] +Step 4 +Remember, our English explanation was "see if *[a b] is true or false, and do different actions depending on that. So (4) is that check. + +Let's say we have a as 59, and b just the quoted value 0 (true) + +*[a 4 4 b] +:: a: 59 +:: b: [1 0] +*[59 4 4 [1 0]] +:: substitute out the two increment operators +++*[59 [1 0]] +:: ignore subject, return quoted value 0 +++(0) +2 +Summary: because *[a b] evaluated to 0 ("true"), we get the number 2. If *[a b] had been "false", we'd get 3 (because we'd evaluate ++(1). + +What the heck? Why are we getting 2 or 3 back? How does that help us?? Well... + +Nock is going to use that returned 2 or 3 to represent a memory slot, and executes the code in that memory slot. + +Step 3 +Now we go up to the next level, (3), with the subject [2 3] evaluated against our return value from (4). Let's imagine that 2 had been returned: + +remember, "result-of-step-4" was 2 +*[[2 3] 0 result-of-step-4] +*[[2 3] 0 2] +:: get memory slot 2 +2 +OK, so now we are getting feel. It grabs memory slot 2 if *[a b] was true, and memory slot 3 if *[a b] was false. + +Wait, isn't this redundant? We are just using our 2 or 3 generated in step (4) to generate a 2 or a 3. Seems dumb. + +The answer is that we are making sure to crash if `[a b]` yields a non-true/false value*. If *[a b] returned 10 (for example), we'd have the following code in step (3): + +*[[2 3] 0 10] +:: lol idiot there's no slot 10 +CRASH!! +This is exactly what we want: the program crashes unless we are doing a boolean test that returns a 0 or 1 (converted to a 2 or 3) in step (4). + +Step 2 +We now have our validated 2 or 3 to plug into step (2). Let's imagine c is the simple formula [0 1] and d is [1 203] + +:: if step 3 returned "2" (true) +*[[[0 1] [1 203]] 0 2] +[0 1] +Not much to see here, we just grab memory slot 2 or 3 depending on whether our initial b was true or false. + +Step 1 +And now we're back at the top level, where we just use whichever formula we yoinked in step (2) and run it against a + +*[a formula-from-step-2] +:: let's say we returned formula [0 1] +:: our original a, from step 4, was 59 +*[59 [0 1] +59 +Example Code Expansion of 6 +~zod:dojo> .*(1 [6 [0 1] [0 1] [4 0 1]]) +:: PSEUDOCODE +:: expansion: *[a *[[c d] 0 *[[2 3] 0 *[a 4 4 b]]]] +*[1 *[[[0 1] [4 0 1]] 0 *[[2 3] 0 *[1 4 4 [0 1]]]]] +:: factor out the 4 opcodes +*[1 *[[[0 1] [4 0 1]] 0 *[[2 3] 0 ++*[1 [0 1]]]]] +:: b evaluates to 1 (yank memory slot 1) +*[1 *[[[0 1] [4 0 1]] 0 *[[2 3] 0 ++(1)]]] +:: evaluate the two increments +*[1 *[[[0 1] [4 0 1]] 0 *[[2 3] 0 3]]] +:: get memory slot 3 from [2 3] +*[1 *[[[0 1] [4 0 1]] 0 3]] +:: [0 3] means get memory slot 3 from the subject (formula [4 0 1]) +*[1 [4 0 1]] +:: factor out the 4 opcode ++*[1 [0 1]] ++(1) +2 +Summary of 6 +Now I'm going to make you a little sad. Most Nock interpreters don't do this whole awesome code expansion. They just see 6, and implement an if/else check with a crash if *[a b] isn't a boolean. + +However, the pattern of storing chunks of code in memory and pulling them out when you want them is the most important part of Nock. In fact, those of you who know Hoon are probably already seeing the makings of something that begins with "c" and rhymes with "zor"... + +7, the "Composition" Opcode +7 is so simple we barely need to spend time on it. All it does is create a new subject/environment (using 2), and immediately runs a formula against that new subject. Let's compare it to 2: + +::opcode 7 +*[a 7 b c] *[*[a b] c] + +:: opcode 2 +*[a 2 b c] *[*[a b] *[a c]] +Literally exactly the same except with c instead of *[a c]. This means that you can write a "subject-changing" formula in b, and then run a simple function against that in c. + +Example: Opcode 2 vs Opcode 7 for increment +:: using opcode 2 +~zod:dojo> .*([23 45] [2 [0 3] [1 4 0 1]]) +46 + +:: using opcode 7 -- we get to remove the quoter opcode "1" +:: wow amazing /sarcasm +~zod:dojo> .*([23 45] [7 [0 3] [4 0 1]]) +46 +7 is clearly trivial, so why does it exist? It allows clean expression of function composition: this is really c(b(a)), where a (the subject) is the initial argument, and b and c are functions. This pattern comes up a ton, and it's nice to not use 1s everywhere, I guess. + +8, the "Variable Adder" Opcode +8 is what you want if you're ever writing Nock and think "how can I add a new variable to the subject?" The new variable can be based on either the existing subject, or be a new value you add on. + +*[a 8 b c] *[[*[a b] a] c] +This is saying to run *[a b], and then make that the head of a new subject, with the old subject a as the new subject's tail. + +Example 1: Add Variable as a Copied Value from an Existing Subject +In English, the below code first yanks the variable from memory slot 3, copies it to the head of a new subject, and then increments the value in that new head. + +~zod:dojo> .*([67 39] [8 [0 3] [4 0 2]]) +40 +:: PSEUDOCODE +*[[*[[67 39] [0 3]] [67 39]] [4 0 2]] +:: yanks mem slot 3 and pins it to the front of the old subject +*[[39 [67 39]] [4 0 2]] ++*[[39 [67 39]] [0 2]] ++(39) +40 +Example 2: Add Variable as a New Value +~zod:dojo> .*([67 39] [8 [1 0] [4 0 2]]) +1 +:: PSEUDOCODE +*[[*[[67 39] [1 0]] [67 39]] [4 0 2]] +*[[0 [67 39]] [4 0 2]] ++*[[0 [67 39]] [0 2]] ++(0) +1 +Why do we want this? In Example 1, we pin a copy of a value so that we can manipulate it without changing the original. In Example 2, we add a 0 to the front; maybe we want to increment it until some condition is met? + +The above should be starting to feel very Hoon-ish: we're a minor code transform away from pinning new values to the head of a payload. + +9, Create a Core and Run a Stored Procedure Arm inside It +We're almost at full Hoon now, although still at a very low/raw level. 9 looks a little complicated... + +*[a 9 b c] *[*[a c] 2 [0 1] 0 b] +...but in English, this is just saying + +Use formula c to make a new subject from a (*[a c]) +Grab the formula located at memory slot b in that new subject +Run that formula against the new subject (*[a c]) +The fact that the above description has the words "make a new subject" tells us right away that it's syntactic sugar for opcode 2, and that's indeed what we see in the pseudocode. + +Example: An Incrementer Arm +The initial expression here likely looks very cryptic, but if you follow the pseudocode, it's pretty clear. + +To stay oriented, remember that, if we're thinking of 9 as *[a 9 b c], then + +a: 45 +b: 2 +c: [[1 4 0 3] 0 1] +~zod:dojo> .*(45 [9 2 [1 4 0 3] 0 1]) +46 +:: PSEUDOCODE +*[45 [9 2 [1 4 0 3] [0 1]]] +*[*[45 [1 4 0 3] [0 1]] + 2 [0 1] 0 2] +:: new subject is [[4 0 3] 45] +:: that is a formula to increment mem slot 3 in the head; 45 in the tail +*[[[4 0 3] 45] 2 [0 1] 0 2] +:: now we expand opcode 2 +*[*[[[4 0 3] 45] 0 1] + *[[[4 0 3] 45] 0 2]] +:: mem slot 2 of the subject becomes the new formula +*[[[4 0 3] 45] 4 0 3] ++*[[[4 0 3] 45] 0 3] +:: grab mem slot 3 ++(45) + +46 +We start above with a subject that is not a core; it's just the atom 45. The code for c is then: + +[[1 4 0 3] [0 1]] +This formula uses the Cell Maker (Distribution Rule) to insert [4 0 3] as the head of the new subject, and puts mem slot 1 of the old subject as the tail, so we get a new subject of [[4 0 3] 45]. + +Then essentially what we do is use b (2 here) to select mem slot 2 from that new subject. Mem slot 2 is a formula: [4 0 3].We run that formula against the new subject. + +Key Point/Possible Confusion +When I first say 9, I thought: "why didn't they just use 2 to make the transformed subject? Why is there an extra step to use [0 1] to pull the new *[a c] subject? Why can't we do *[a 2 c [0 b]]? + +The answer is that we actually use the *[a c] subject twice: + +we extract from it the formula located at memory slot b +then we run that formula against the *[a c] subject +If we just did *[a 2 c [0 b]], then [0 b] would try to look up the b memory slot in a, NOT *[a c]. So 9 gives us one extra step to set up the core itself. + +How to Think of 9 +I like to think of 9 as having two parts: + +c: our "set up the subject" formula. This makes a new subject +b: the memory slot in our new subject where an arm is And then we run the arm located at b against the new subject. +You can think of this as setting up a subject, pulling a stored procedure from it, and then running that procedure against the subject. + +10, Replace a Memory Slot +Before explaining 10, we need to introduce a new operator, #. # is the "edit" operator. It has the form + +#[mem-slot new-val target-tree] +It replaces the memory slot mem-slot in target-tree with new-val. + +:: Example +#[2 [4 5] [99 88 77]] +[[4 5] 88 77] +Pseudocode for 10 + +*[a 10 [b c] d] #[b *[a c] *[a d]] +In English, this first calculates *[a c] and *[a d], and then replaces memory slot b in the latter with the result of the former. + +Example +~zod:dojo> .*(50 [10 [2 [0 1]] [1 8 9 10]]) +[50 9 10] +:: PSEUDOCODE +*[50 [10 [2 [0 1]] [1 8 9 10]]] +#[2 *[50 0 1] *[50 1 8 9 10]] +#[2 /[1 50] [8 9 10]] +#[2 50 [8 9 10]] +:: expanded as far as we can, now do the edit +:: replace mem slot 2 of [8 9 10] with the value 50 +[50 9 10] +11: Save for Later +I cover 11 in the Interlude, because it's not part of strict Nock semantics. You can go there to read about it. + +Hoon Time! +If "grab a chunk of code from a subject and then run it against the subject" sounds a lot like getting an arm from a core, that's because it is. + +The "new subjects" created by *[a c] are cores, and the things selected from memory slots by b are arms. + +Real Nock Code +To finish this off and take your new powers for a spin, let's look at some real Nock code from the wild. I got the below example from the Dojo. It's a mold: a function that takes a noun and returns it if it's the correct type, and crashes if not. The mold here checks whether the input noun is a boolean (0 or 1). + +(To see more examples of basic molds, go to the Hoon Tutorial 2.3). + +A Boolean Mold +~zod:dojo> =boolean-mold ? + +:: below output shortened for our purposes here +~zod:dojo> boolean-mold +< 1.toz ... > + +:: grab the code for the battery +~zod:dojo> -.boolean-mold +[6 [5 [1 0] 0 6] [1 0] 6 [5 [1 1] 0 6] [1 1] 0 0] +So we + +assign the mold-gate ? to the face boolean-mold +take a look at what's inside it...looks like the head is an arm...let's print out its source code! +print out that source code by calling the head +Code Walkthrough +So, we know the below code is a gate that evaluates its sample, and returns it if it's a boolean, and crashes otherwise. Let's see how that works. + +[6 [5 [1 0] 0 6] [1 0] 6 [5 [1 1] 0 6] [1 1] 0 0] +We start with 6, which means this is an if-else. The true/false test is the next element: + +[5 [1 0] 0 6] +This compares the quoted value 0 (from [1 0]) with the value at memory slot 6 in the subject ([0 6]). + +What is the subject and what is at mem slot 6? This code is the arm of a gate, so the subject is that gate/core: [battery sample payload]. We are looking at the battery right now, and so [0 6] yanks the head of the tail...the sample! + +We know the sample will be a noun that we're testing for booleanness, so this code starts by seeing whether the sample is the value 0. If it is, the next element is [1 0], so we return 0 if the sample is 0. + +Otherwise, we run the 2nd branch of the if-else: + +[6 [5 [1 1] 0 6] [1 1] 0 0] +This is also an opcode 6 if-else. once again, it compares something to the value at mem slot 6 (the sample), but this time it checks whether that is the value 1. If it is, it runs the formula [1 1] to return 1. + +If not, it means our input was neither 0 nor 1 and is not a boolean, so we run the formula [0 0], which always crashes (there is no memory slot 0). This is exactly what we want--crash if the sample is not a boolean! + +Summary +In Part 2, we've seen how to use all the remaining opcodes, which build Nock up to a slightly more expressive level. We also saw how Hoon cores start to arise pretty naturally out of the Nock primitives, especially 8 and 9. Then we walked through real production Nock code to show that everything we've learned so far works exactly as expected in the wild. + +In Part 3, you'll learn how to write real programs in Nock, and compose those programs to make new ones. + +Finally, hopefully you now see that, whatever its other limitations, Nock is not particularly obscurantist, and is fairly straightforward to parse, once you understand its syntax and idioms. diff --git a/docs/nock/nock_implementations.md b/docs/nock/nock_implementations.md new file mode 100644 index 0000000..37a1953 --- /dev/null +++ b/docs/nock/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 memocache = new Dictionary(); + + 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 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 tuple = new Tuple(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 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\>() + + 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) + } diff --git a/docs/nock_implementations.md b/docs/nock_implementations.md deleted file mode 100644 index 37a1953..0000000 --- a/docs/nock_implementations.md +++ /dev/null @@ -1,2525 +0,0 @@ -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 memocache = new Dictionary(); - - 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 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 tuple = new Tuple(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 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\>() - - 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) - } diff --git a/vere/build.zig b/vere/build.zig index eaf3b48..a940b2f 100644 --- a/vere/build.zig +++ b/vere/build.zig @@ -637,6 +637,16 @@ fn buildBinary( .file = "pkg/vere/solid_boot_test.c", .deps = vere_test_deps, }, + .{ + .name = "opcodes-test", + .file = "pkg/vere/test_opcodes.c", + .deps = vere_test_deps, + }, + .{ + .name = "bisect-test", + .file = "pkg/vere/test_bisect_lifecycle.c", + .deps = vere_test_deps, + }, .{ .name = "ivory-boot-test", .file = "pkg/vere/ivory_boot_test.c", diff --git a/vere/pkg/noun/nock.c b/vere/pkg/noun/nock.c index eef211b..87fcf2c 100644 --- a/vere/pkg/noun/nock.c +++ b/vere/pkg/noun/nock.c @@ -22,7 +22,7 @@ // along with some other debugging info # undef VERBOSE_BYTECODE -#if 0 +#if 1 // Retained for debugging purposes. static u3_noun _n_nock_on(u3_noun bus, u3_noun fol); @@ -118,7 +118,7 @@ _n_hint(u3_noun zep, case c3__memo: { u3z(hod); -#if 0 +#if 1 return _n_nock_on(bus, nex); #else { @@ -168,9 +168,9 @@ _n_nock_on(u3_noun bus, u3_noun fol) u3R->pro.nox_d += 1; #endif - // Trace first 30 opcodes + // Trace first 200 opcodes static c3_w opcode_count = 0; - if ( opcode_count < 30 ) { + if ( opcode_count < 200 ) { if ( c3y == u3du(hib) ) { u3l_log("[C-Nock:%u] cell-cell formula", opcode_count); } else { @@ -213,8 +213,29 @@ _n_nock_on(u3_noun bus, u3_noun fol) u3_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))); + static c3_w op2_debug = 0; + u3_noun c_gal = u3t(gal); + u3_noun b_gal = u3h(gal); + + if (op2_debug == 0) { + u3l_log("[Op2 Debug] Computing formula (tail gal):"); + u3l_log(" c_gal mug: 0x%x", u3r_mug(c_gal)); + } + + u3_noun nex = _n_nock_on(u3k(bus), u3k(c_gal)); + + if (op2_debug == 0) { + u3l_log(" nex mug: 0x%x", u3r_mug(nex)); + u3l_log("[Op2 Debug] Computing subject (head gal):"); + u3l_log(" b_gal mug: 0x%x", u3r_mug(b_gal)); + } + + u3_noun seb = _n_nock_on(bus, u3k(b_gal)); + + if (op2_debug == 0) { + u3l_log(" seb mug: 0x%x", u3r_mug(seb)); + op2_debug = 1; + } u3a_lose(fol); bus = seb; @@ -372,35 +393,15 @@ _n_nock_on(u3_noun bus, u3_noun fol) } 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(u3k(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(u3nt(c3__hunk, ref, gof)); - return u3m_bail(c3__exit); - } - else { - u3_noun pro; - - u3z(ref); - u3z(gof); - u3z(fol); - pro = u3k(u3t(u3t(val))); - u3z(val); + // Simplified opcode 11: just ignore the hint and evaluate q_gal + // This matches OCaml's implementation + u3_noun q_gal = u3t(gal); + u3_noun pro; - return pro; - } + // Ignore the hint (head of gal), just evaluate q_gal + pro = _n_nock_on(bus, u3k(q_gal)); + u3a_lose(fol); + return pro; } u3_assert(!"not reached"); } diff --git a/vere/pkg/noun/vortex.c b/vere/pkg/noun/vortex.c index e4b55d9..f9c21d9 100644 --- a/vere/pkg/noun/vortex.c +++ b/vere/pkg/noun/vortex.c @@ -58,11 +58,17 @@ u3v_life(u3_noun eve) } } + // Check slot 3 before lifecycle + u3_noun eve_slot3 = u3r_at(3, eve); + u3l_log("u3v_life: eve slot 3 (before lifecycle) mug: 0x%x", u3r_mug(eve_slot3)); + u3l_log("u3v_life: calling u3n_nock_on(eve, [2 [0 3] [0 2]])..."); u3_noun gat = u3n_nock_on(eve, lyf); u3l_log("u3v_life: u3n_nock_on returned successfully"); + u3l_log("u3v_life: gate mug: 0x%x", u3r_mug(gat)); u3_noun cor = u3k(u3x_at(7, gat)); + u3l_log("u3v_life: slot 7 (kernel) mug: 0x%x", u3r_mug(cor)); u3z(gat); u3l_log("u3v_life: completed successfully"); diff --git a/vere/pkg/vere/solid_boot_test.c b/vere/pkg/vere/solid_boot_test.c index 26a0f07..8598125 100644 --- a/vere/pkg/vere/solid_boot_test.c +++ b/vere/pkg/vere/solid_boot_test.c @@ -258,21 +258,136 @@ _setup(void) u3C.wag_w |= u3o_hashless; u3m_boot_lite(1 << 28); // 256MB loom for solid pill + u3l_log("=== Testing ivory.pill first for comparison ==="); + u3_noun ivory_jammed = u3m_file("/home/y/code/urbit/vere/ocaml/ivory.pill"); + u3l_log("ivory_jammed is_atom: %u", u3a_is_atom(ivory_jammed)); + u3_noun ivory = u3ke_cue(ivory_jammed); + u3l_log("ivory (after cue) is_atom: %u", u3a_is_atom(ivory)); + u3l_log(""); + + u3l_log("=== Now testing solid.pill ==="); u3l_log("Loading solid.pill from OCaml directory..."); u3_noun solid_jammed = u3m_file("/home/y/code/urbit/vere/ocaml/solid.pill"); + u3l_log(" solid_jammed is_atom: %u", u3a_is_atom(solid_jammed)); + u3l_log(" solid_jammed mug: 0x%x", u3r_mug(solid_jammed)); u3l_log("Cuing solid.pill..."); u3_noun pil = u3ke_cue(solid_jammed); // Don't free ivory_jammed - it's managed by u3m_file u3l_log("solid_pil is_atom: %u", u3a_is_atom(pil)); + u3l_log("solid_pil mug: 0x%x", u3r_mug(pil)); + + if (u3a_is_cell(pil)) { + u3l_log("solid_pil is a CELL!"); + u3_noun h = u3h(pil); + u3_noun t = u3t(pil); + u3l_log(" head is_atom: %u", u3a_is_atom(h)); + u3l_log(" tail is_atom: %u", u3a_is_atom(t)); + } + + // Parse solid pill structure [%pill %solid [bot mod use]] + u3_noun pill_tag, rest; + if ( c3n == u3r_cell(pil, &pill_tag, &rest) ) { + printf("*** fail: pill is not a cell\n"); + exit(1); + } + + // Structure is flat 4-tuple like mars.c line 1631: [typ bot mod use] + u3_noun typ, bot, mod, use; + if ( c3n == u3r_qual(rest, &typ, &bot, &mod, &use) ) { + printf("*** fail: cannot extract [typ bot mod use]\n"); + exit(1); + } + + if ( c3y == u3a_is_atom(typ) ) { + c3_c* typ_c = u3r_string(typ); + u3l_log("Pill type: %s", typ_c); + c3_free(typ_c); + } + + u3l_log("Bot events: %llu", (unsigned long long)u3qb_lent(bot)); + u3l_log("Mod events: %llu", (unsigned long long)u3qb_lent(mod)); + u3l_log("Use events: %llu", (unsigned long long)u3qb_lent(use)); + + // Add boot event to use list (mars.c lines 1785-1789) + { + u3_noun wir = u3nq(c3__d, c3__term, '1', u3_nul); + u3_noun ven = u3nc(c3__fake, 0); // [%fake ~zod] + u3_noun cad = u3nt(c3__boot, c3n, ven); // [%boot lit venue] + use = u3nc(u3nc(wir, cad), use); + } + + // Add system events to mod list (mars.c lines 1764-1789) + // Note: prepend in reverse order so wyrd comes first + { + u3_noun cad, wir = u3nt(u3_blip, c3__arvo, u3_nul); + + // Version negotiation (wyrd) - prepended LAST so it's FIRST + u3_noun ver = u3nq(c3__vere, u3i_string("live"), u3i_string("3.5"), u3_nul); + u3_noun sen = u3i_string("0v1s.vu178"); + u3_noun kel = u3nl(u3nc(c3__zuse, 409), + u3nc(c3__lull, 321), + u3nc(c3__arvo, 235), + u3nc(c3__hoon, 136), + u3nc(c3__nock, 4), + u3_none); + cad = u3nt(c3__wyrd, u3nc(sen, ver), kel); + mod = u3nc(u3nc(wir, cad), mod); // transfer [wir], wyrd is now at head + + wir = u3nt(u3_blip, c3__arvo, u3_nul); // recreate wir + + // Verbosity (verb) + cad = u3nt(c3__verb, u3_nul, c3n); // verbose = no + mod = u3nc(u3nc(u3k(wir), cad), mod); + + // Identity (whom) - use fake ship ~zod + cad = u3nc(c3__whom, 0); + mod = u3nc(u3nc(u3k(wir), cad), mod); + + // Entropy (wack) - prepended FIRST so it's LAST + c3_w eny_w[16]; + for (int i = 0; i < 16; i++) { + eny_w[i] = 0xdeadbeef; // Simple test entropy + } + cad = u3nc(c3__wack, u3i_words(16, eny_w)); + mod = u3nc(u3nc(wir, cad), mod); // transfer [wir], wack is now at head + } + + u3l_log("After adding system events:"); + u3l_log(" Bot events: %llu", (unsigned long long)u3qb_lent(bot)); + u3l_log(" Mod events: %llu", (unsigned long long)u3qb_lent(mod)); + u3l_log(" Use events: %llu", (unsigned long long)u3qb_lent(use)); + + // Build event list exactly like _mars_boot_make (lines 1903-1922) + struct timeval tim_u; + gettimeofday(&tim_u, 0); + u3_noun now = u3_time_in_tv(&tim_u); + u3_noun bit = u3qc_bex(48); // 1/2^16 seconds + u3_noun eve = u3kb_flop(u3k(bot)); + + { + u3_noun lit = u3qb_weld(u3k(mod), u3k(use)); + u3_noun i, t = lit; + + while ( u3_nul != t ) { + u3x_cell(t, &i, &t); + now = u3ka_add(now, u3k(bit)); + eve = u3nc(u3nc(u3k(now), u3k(i)), eve); + } + + u3z(lit); + } + + u3_noun all_events = u3kb_flop(eve); + u3z(now); u3z(bit); - // Boot with solid pill - u3l_log("Booting with solid.pill from OCaml..."); - if ( c3n == u3v_boot_lite(pil) ) { + u3l_log("Booting with %llu events...", (unsigned long long)u3qb_lent(all_events)); + if ( c3n == u3v_boot(all_events) ) { printf("*** fail: solid boot failed\n"); exit(1); } u3l_log("✓ solid boot completed!"); + u3l_log("Kernel mug: 0x%x", u3r_mug(u3A->roc)); } /* _test_lily(): test small noun parsing. diff --git a/vere/pkg/vere/test_bisect_lifecycle.c b/vere/pkg/vere/test_bisect_lifecycle.c new file mode 100644 index 0000000..3aa26d1 --- /dev/null +++ b/vere/pkg/vere/test_bisect_lifecycle.c @@ -0,0 +1,74 @@ +/// Bisect ivory pill to find where lifecycle diverges + +#include "noun.h" +#include "vere.h" + +static u3_noun lifecycle_formula; + +static void test_lifecycle(const char* name, u3_noun subject) { + u3l_log("Testing: %s", name); + u3l_log(" Subject mug: 0x%x", u3r_mug(subject)); + + // Check if subject has valid structure + if (c3n == u3a_is_cell(subject)) { + u3l_log(" SKIP: subject is atom\n"); + return; + } + + u3_noun slot2 = u3h(subject); + if (c3n == u3a_is_cell(slot2)) { + u3l_log(" SKIP: slot 2 is atom (not a formula)\n"); + return; + } + + // Try to run lifecycle + u3_noun result = u3n_nock_on(u3k(subject), u3k(lifecycle_formula)); + u3l_log(" Result mug: 0x%x\n", u3r_mug(result)); + u3z(result); +} + +static void walk_tree(const char* prefix, u3_noun subject, c3_w depth, c3_w max_depth) { + if (depth >= max_depth) return; + + // Test current node + test_lifecycle(prefix, subject); + + // Walk children if cell + if (c3y == u3a_is_cell(subject)) { + char buf[256]; + u3_noun h = u3h(subject); + u3_noun t = u3t(subject); + + snprintf(buf, sizeof(buf), "%s.2", prefix); + walk_tree(buf, h, depth + 1, max_depth); + + snprintf(buf, sizeof(buf), "%s.3", prefix); + walk_tree(buf, t, depth + 1, max_depth); + } +} + +int main(int argc, char* argv[]) +{ + u3C.wag_w |= u3o_hashless; + u3m_boot_lite(1 << 28); // 256MB loom + + // Load ivory pill + u3l_log("Loading ivory.pill from OCaml directory..."); + u3_noun ivory_jammed = u3m_file("/home/y/code/urbit/vere/ocaml/ivory.pill"); + u3l_log("Cuing ivory.pill..."); + u3_noun pil = u3ke_cue(ivory_jammed); + + u3_noun arvo_core = u3t(pil); + u3l_log("Ivory core mug: 0x%x\n", u3r_mug(arvo_core)); + + u3l_log("Walking tree and testing lifecycle at each node..."); + u3l_log("Format: slot path (e.g. '.2.3' = slot 6)\n"); + + // Create lifecycle formula: [2 [0 3] [0 2]] + lifecycle_formula = u3nt(2, u3nc(0, 3), u3nc(0, 2)); + + // Walk tree up to depth 5 + walk_tree("core", arvo_core, 0, 5); + + return 0; +} diff --git a/vere/pkg/vere/test_op8.c b/vere/pkg/vere/test_op8.c new file mode 100644 index 0000000..2819e49 --- /dev/null +++ b/vere/pkg/vere/test_op8.c @@ -0,0 +1,49 @@ +/// Test opcode 8 (extend) + +#include "noun.h" +#include "vere.h" + +int main(int argc, char* argv[]) +{ + u3C.wag_w |= u3o_hashless; + u3m_boot_lite(1 << 20); // 1MB loom + + // Test: *[[42 99] [8 [1 123] [0 1]]] + // This should compute: *[[123 [42 99]] [0 1]] = [123 [42 99]] + + u3_noun subject = u3nc(42, 99); + u3_noun formula = u3nc(8, u3nc(u3nc(1, 123), u3nc(0, 1))); + + u3l_log("Subject: [42 99]"); + u3l_log("Subject mug: 0x%x", u3r_mug(subject)); + u3l_log("Formula: [8 [1 123] [0 1]]"); + u3l_log("Formula mug: 0x%x\n", u3r_mug(formula)); + + u3l_log("This should compute:"); + u3l_log(" 1. Evaluate [1 123] on [42 99] -> 123"); + u3l_log(" 2. Extend subject: [123 [42 99]]"); + u3l_log(" 3. Evaluate [0 1] on [123 [42 99]] -> [123 [42 99]]\n"); + + u3_noun result = u3n_nock_on(subject, formula); + + u3l_log("Result mug: 0x%x", u3r_mug(result)); + if (c3y == u3a_is_cell(result)) { + u3_noun h = u3h(result); + u3_noun t = u3t(result); + if (c3y == u3a_is_atom(h) && c3y == u3a_is_cell(t)) { + u3_noun th = u3h(t); + u3_noun tt = u3t(t); + if (c3y == u3a_is_atom(th) && c3y == u3a_is_atom(tt)) { + u3l_log("Result: [%u [%u %u]]", h, th, tt); + } else { + u3l_log("Result: cell (unexpected structure)"); + } + } else { + u3l_log("Result: cell (unexpected structure)"); + } + } else { + u3l_log("Result: atom"); + } + + return 0; +} diff --git a/vere/pkg/vere/test_opcodes.c b/vere/pkg/vere/test_opcodes.c new file mode 100644 index 0000000..e7e8781 --- /dev/null +++ b/vere/pkg/vere/test_opcodes.c @@ -0,0 +1,78 @@ +/// Consolidated opcode tests + +#include "noun.h" +#include "vere.h" + +static void test_name(const char* name) { + u3l_log("\n=== %s ===", name); +} + +static void test_op2_simple() { + test_name("Opcode 2: Simple lifecycle [0 1] -> 99"); + u3_noun formula_slot2 = u3nc(0, 1); + u3_noun payload = 99; + u3_noun subject = u3nc(formula_slot2, payload); + u3_noun formula = u3nt(2, u3nc(0, 3), u3nc(0, 2)); + + u3_noun result = u3n_nock_on(subject, formula); + u3l_log("Result: %u, mug: 0x%x", result, u3r_mug(result)); +} + +static void test_op7_compose() { + test_name("Opcode 7: Compose with [0 1]"); + u3_noun formula_slot2 = u3nt(7, u3nc(0, 3), u3nc(0, 1)); + u3_noun payload = u3nc(42, 99); + u3_noun subject = u3nc(formula_slot2, payload); + u3_noun formula = u3nt(2, u3nc(0, 3), u3nc(0, 2)); + + u3_noun result = u3n_nock_on(subject, formula); + u3l_log("Result mug: 0x%x", u3r_mug(result)); + u3z(result); +} + +static void test_op8_extend() { + test_name("Opcode 8: Extend subject"); + u3_noun subject = u3nc(42, 99); + u3_noun formula = u3nc(8, u3nc(u3nc(1, 123), u3nc(0, 1))); + + u3_noun result = u3n_nock_on(subject, formula); + if (c3y == u3a_is_cell(result)) { + u3_noun h = u3h(result); + u3_noun t = u3t(result); + if (c3y == u3a_is_atom(h) && c3y == u3a_is_cell(t)) { + u3_noun th = u3h(t); + u3_noun tt = u3t(t); + if (c3y == u3a_is_atom(th) && c3y == u3a_is_atom(tt)) { + u3l_log("Result: [%u [%u %u]], mug: 0x%x", h, th, tt, u3r_mug(result)); + } + } + } + u3z(result); +} + +static void test_op9_invoke() { + test_name("Opcode 9: Invoke core arm"); + u3_noun core = u3nc(u3nc(0, 3), 42); + u3_noun formula = u3nc(9, u3nc(2, u3nc(0, 1))); + + u3_noun result = u3n_nock_on(core, formula); + if (c3y == u3a_is_atom(result)) { + u3l_log("Result: %u, mug: 0x%x", result, u3r_mug(result)); + } + u3z(result); +} + +int main(int argc, char* argv[]) +{ + u3C.wag_w |= u3o_hashless; + u3m_boot_lite(1 << 20); + + u3l_log("Testing Nock opcodes..."); + test_op2_simple(); + test_op7_compose(); + test_op8_extend(); + test_op9_invoke(); + u3l_log("\nAll tests complete."); + + return 0; +} diff --git a/vere/pkg/vere/test_simple_lifecycle.c b/vere/pkg/vere/test_simple_lifecycle.c new file mode 100644 index 0000000..3fd92aa --- /dev/null +++ b/vere/pkg/vere/test_simple_lifecycle.c @@ -0,0 +1,42 @@ +/// Test simple lifecycle formula + +#include "noun.h" +#include "vere.h" + +int main(int argc, char* argv[]) +{ + u3C.wag_w |= u3o_hashless; + u3m_boot_lite(1 << 20); // 1MB loom + + // Subject: [[7 [0 3] [0 1]] [42 99]] + // Formula: [7 [0 3] [0 1]] - compose: *[payload [0 1]] returns payload + u3_noun formula_slot2 = u3nt(7, u3nc(0, 3), u3nc(0, 1)); + u3_noun payload = u3nc(42, 99); + u3_noun subject = u3nc(formula_slot2, payload); + + // Lifecycle formula: [2 [0 3] [0 2]] + u3_noun formula = u3nt(2, u3nc(0, 3), u3nc(0, 2)); + + u3l_log("Subject: [[7 [0 3] [0 1]] [42 99]]"); + u3l_log("Subject mug: 0x%x", u3r_mug(subject)); + u3l_log("Formula: [2 [0 3] [0 2]]"); + u3l_log("Formula mug: 0x%x\n", u3r_mug(formula)); + + u3l_log("Slot 2 (formula): [7 [0 3] [0 1]] (mug=0x%x)", u3r_mug(formula_slot2)); + u3l_log("Slot 3 (payload): [42 99] (mug=0x%x)\n", u3r_mug(payload)); + + u3l_log("Running nock..."); + u3l_log("This should compute: *[[42 99] [7 [0 3] [0 1]]]"); + u3l_log("Which is: *[99 [0 1]] = 99\n"); + + u3_noun result = u3n_nock_on(subject, formula); + + u3l_log("Result mug: 0x%x", u3r_mug(result)); + if (c3y == u3a_is_atom(result)) { + u3l_log("Result: %u (atom)", result); + } else { + u3l_log("Result: cell"); + } + + return 0; +} diff --git a/zod/.urb/log/0i0/data.mdb b/zod/.urb/log/0i0/data.mdb index 368dd24..116dd96 100644 Binary files a/zod/.urb/log/0i0/data.mdb and b/zod/.urb/log/0i0/data.mdb differ diff --git a/zod/.urb/log/0i0/lock.mdb b/zod/.urb/log/0i0/lock.mdb index 423e462..d70edc5 100644 Binary files a/zod/.urb/log/0i0/lock.mdb and b/zod/.urb/log/0i0/lock.mdb differ diff --git a/zod/.urb/log/0i0/vere.txt b/zod/.urb/log/0i0/vere.txt index cc829d4..6b8d268 100644 --- a/zod/.urb/log/0i0/vere.txt +++ b/zod/.urb/log/0i0/vere.txt @@ -1 +1 @@ -4.0-476771a4b6 \ No newline at end of file +4.0-3c019f88b2 \ No newline at end of file diff --git a/zod/.vere.lock b/zod/.vere.lock index 2b5d6c8..6f6c200 100644 --- a/zod/.vere.lock +++ b/zod/.vere.lock @@ -1 +1 @@ -788685 +1130697 -- cgit v1.2.3