@@ -0,0 +1,478 @@
10+From [Julien Beasley](https://github.com/zass30/Nock5KCSharp):
14+using System.Collections.Generic;
17+using System.Text.RegularExpressions;
19+namespace NockInterpreter
23+ static Dictionary<string, Noun> memocache = new Dictionary<string, Noun>();
25+ public static Noun Nock(Noun noun)
29+ if (memocache.TryGetValue(noun.ToString(), out cache_noun))
34+ if (Atom.IsAtom(noun))
35+ throw new Exception("Infinite loop nocking an atom: " + noun.ToString());
38+ Noun subject = noun.n1;
39+ if (Noun.IsCell(noun.n2))
41+ Cell formula = (Cell)noun.n2;
42+ if (Noun.IsAtom(formula.n1)) // we have lines 25-37 of spec
44+ Atom op = (Atom)formula.n1;
45+ Noun operands = formula.n2;
49+ case 0: // 25 :: *[a 0 b] /[b a]
50+ memocache[noun.ToString()] = fas(operands, subject);
51+ return memocache[noun.ToString()];
52+ case 1: // 26 :: *[a 1 b] b
53+ memocache[noun.ToString()] = operands;
54+ return memocache[noun.ToString()];
55+ case 2: // 27 :: *[a 2 b c] *[*[a b] *[a c]]
56+ if (Noun.IsCell(operands))
58+ Noun a = Nock(subject, operands.n1);
59+ Noun b = Nock(subject, operands.n2);
60+ noun = Noun.CreateNoun(a, b);
62+ // return Nock(Nock(subject, operands.n1), Nock(subject, operands.n2));
64+ throw new Exception("Atom after operand 2: " + operands.ToString());
65+ case 3: // 28 :: *[a 3 b] ?*[a b]
66+ memocache[noun.ToString()] = wut(Nock(subject, operands));
67+ return memocache[noun.ToString()];
68+ case 4: // 29 :: *[a 4 b] +*[a b]
69+ memocache[noun.ToString()] = lus(Nock(subject, operands));
70+ return memocache[noun.ToString()];
71+ case 5: // 30 :: *[a 5 b] =*[a b]
72+ memocache[noun.ToString()] = tis(Nock(subject, operands));
73+ return memocache[noun.ToString()];
74+ 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]
75+ if (Noun.IsCell(operands) && Noun.IsCell(operands.n2))
77+ Noun b = operands.n1;
78+ Noun c = operands.n2.n1;
79+ Noun d = operands.n2.n2;
80+ noun = Noun.CreateNoun("[" + subject + " 2 [0 1] 2 [1 " + c + " " + d + "] [1 0] 2 [1 2 3] [1 0] 4 4 " + b + "]");
82+ // return Nock(Noun.CreateNoun("[" + subject + " 2 [0 1] 2 [1 " + c + " " + d + "] [1 0] 2 [1 2 3] [1 0] 4 4 " + b + "]"));
84+ throw new Exception("Unhandled pattern for operand 6");
85+ case 7: // 33 :: *[a 7 b c] *[a 2 b 1 c]
86+ if (Noun.IsCell(operands))
88+ Noun b = operands.n1;
89+ Noun c = operands.n2;
90+ noun = Noun.CreateNoun("[" + subject + " 2 " + b + " 1 " + c + "]");
92+ // return Nock(Noun.CreateNoun("[" + subject + " 2 " + b + " 1 " + c + "]"));
94+ throw new Exception("Atom after operand 7: " + operands.ToString());
95+ case 8: // 34 :: *[a 8 b c] *[a 7 [[7 [0 1] b] 0 1] c]
96+ if (Noun.IsCell(operands))
98+ Noun b = operands.n1;
99+ Noun c = operands.n2;
100+ noun = Noun.CreateNoun("[" + subject + " 7 [[7 [0 1] " + b + "] 0 1] " + c + "]");
102+ // return Nock(Noun.CreateNoun("[" + subject + " 7 [[7 [0 1] " + b + "] 0 1] " + c + "]"));
104+ throw new Exception("Atom after operand 8: " + operands.ToString());
105+ case 9: // 35 :: *[a 9 b c] *[a 7 c 2 [0 1] 0 b]
106+ if (Noun.IsCell(operands))
108+ Noun b = operands.n1;
109+ Noun c = operands.n2;
110+ noun = Noun.CreateNoun("[" + subject + " 7 " + c + " 2 [0 1] 0 " + b + "]");
112+ // return Nock(Noun.CreateNoun("[" + subject + " 7 " + c + " 2 [0 1] 0 " + b + "]"));
114+ throw new Exception("Atom after operand 9: " + operands.ToString());
116+ if (Noun.IsCell(operands))
118+ if (Noun.IsCell(operands.n1)) // 36 :: *[a 10 [b c] d] *[a 8 c 7 [0 3] d]
120+ Noun b = operands.n1.n1;
121+ Noun c = operands.n1.n2;
122+ Noun d = operands.n2;
123+ noun = Noun.CreateNoun("[" + subject + " 8 " + c + " 7 [0 3] " + d + "]");
125+ // return Nock(Noun.CreateNoun("[" + subject + " 8 " + c + " 7 [0 3] " + d + "]"));
127+ else // 37 :: *[a 10 b c] *[a c]
129+ Noun c = operands.n2;
130+ noun = Noun.CreateNoun(subject, c);
132+ // return Nock(subject, c);
135+ throw new Exception("Atom after operand 10: " + operands.ToString());
137+ throw new Exception("Unknown operand: " + op.value);
140+ else // 23 :: *[a [b c] d] [*[a b c] *[a d]]
142+ memocache[noun.ToString()] = Noun.CreateNoun(Nock(subject, formula.n1), Nock(subject, formula.n2));
143+ return memocache[noun.ToString()];
147+ throw new Exception("Unhandled pattern");
150+ public static Noun Nock(string program)
152+ Noun noun = Noun.CreateNoun(program);
156+ public static Noun Nock(Noun n1, Noun n2)
158+ Noun noun = Noun.CreateNoun(n1, n2);
162+ private static Noun tis(Noun noun)
164+ if (Noun.IsAtom(noun.ToString()))
165+ throw new Exception("Infinite loop tising an atom: " + noun.ToString());
168+ Cell cell = (Cell)noun;
170+ if (cell.n1.ToString() == cell.n2.ToString())
171+ return Noun.CreateNoun("0");
173+ return Noun.CreateNoun("1");
177+ private static Noun lus(Noun noun)
179+ if (Noun.IsAtom(noun.ToString()))
181+ Atom a = (Atom)noun;
182+ int v = a.value + 1;
183+ return Noun.CreateNoun(v.ToString());
186+ throw new Exception("Infinite loop lusing a cell: " + noun.ToString());
189+ private static Noun wut(Noun noun)
191+ if (Noun.IsAtom(noun.ToString()))
192+ return Noun.CreateNoun("1");
194+ return Noun.CreateNoun("0");
197+ private static Noun fas(Noun n1, Noun n2)
199+ Noun noun = Noun.CreateNoun(n1, n2);
203+ private static Noun fas(Noun noun)
205+ if (Noun.IsAtom(noun.ToString()))
206+ throw new Exception("Infinite loop fasing an atom: " + noun.ToString());
209+ Cell c = (Cell)noun;
210+ // 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.
211+ if (Noun.IsCell(c.n1.ToString()))
212+ throw new Exception("Axis must be an atom: " + c.ToString());
215+ Atom a = (Atom)c.n1;
218+ else if (a.value >= 2)
220+ if (!Noun.IsCell(c.n2.ToString()))
222+ throw new Exception("Only a cell can have an axis of 2 or 3: " + c.n2.ToString());
226+ Cell c2 = (Cell)c.n2;
229+ else if (a.value == 3)
231+ else if (a.value % 2 == 0)
233+ int half = a.value / 2;
234+ return fas(Noun.CreateNoun("2", fas(Noun.CreateNoun(half.ToString(), c2))));
236+ else if (a.value % 2 == 1)
238+ int half = a.value / 2;
239+ return fas(Noun.CreateNoun("3", fas(Noun.CreateNoun(half.ToString(), c2))));
243+ throw new Exception("Infinite loop somewhere in fasing: " + c.n2.ToString());
248+ throw new Exception("Infinite loop somewhere in fasing: " + c.n2.ToString());
258+ // takes a program, returns a pair of nouns, stringified.
259+ public static Tuple<string, string> SplitCell(string program)
264+ // split the string right after the first space
265+ foreach (char c in program)
275+ // if we see a space, and our stack count is at 1, then we've found our split point
276+ if (stackCount == 1)
278+ string a = program.Substring(1, i - 1);
279+ string b = program.Substring(i + 1, program.Length - (i + 2));
281+ // to implement proper bracket closing, surround b with brackets if it isn't a cell and isn't an atom
282+ if (!IsCell(b) && !IsAtom(b))
284+ Tuple<string, string> tuple = new Tuple<string, string>(a, b);
290+ throw new Exception("Invalid char in cell: " + c);
293+ throw new Exception("Invalid cell: " + program);
296+ public static bool IsCell(string program)
298+ // check if cell is valid, as above but make sure no space after bracket
299+ // valid tokens are: space, int, [, ]
301+ // from int => space, ], int
302+ // from ] => space, ]
303+ // from space => int, [
305+ // stack count must always be nonzero
306+ // first and last elements must be [ and ]
308+ int i = 0; // i is the stack count for brackets.
310+ char s = '\0'; // s is the last seen character
311+ // split the string right after the first space
312+ foreach (char c in program)
321+ if (!(c == '[' || IsInt(c)))
326+ if (!(IsInt(c) || c == ' ' || c == ']'))
331+ if (!(c == ']' || c == ' '))
336+ if (!(c == '[' || IsInt(c)))
345+ if (i <= 0 && counter != program.Length) // stack count can't be zero unless it's the last character
349+ // We should end with stack count of zero
356+ public static bool IsInt(char c)
373+ public static bool IsValidChar(char c)
384+ public static bool IsAtom(string program)
387+ if (int.TryParse(program, out i))
395+ public static bool IsAtom(Noun noun)
397+ return IsAtom(noun.ToString());
400+ public static bool IsCell(Noun noun)
402+ return IsCell(noun.ToString());
405+ public static Noun CreateNoun(string program)
407+ if (IsAtom(program))
408+ return new Atom(program);
410+ return new Cell(program);
413+ public static Noun CreateNoun(Noun n1, Noun n2)
415+ return CreateNoun("[" + n1.ToString() + " " + n2.ToString() + "]");
418+ public static Noun CreateNoun(string p1, Noun n2)
420+ return CreateNoun("[" + p1 + " " + n2.ToString() + "]");
423+ public static Noun CreateNoun(Noun n1, string p2)
425+ return CreateNoun("[" + n1.ToString() + " " + p2 + "]");
428+ public static Noun CreateNoun(string p1, string p2)
430+ return CreateNoun("[" + p1 + " " + p2 + "]");
438+ public override string ToString()
440+ return value.ToString();
443+ public Atom(string program)
445+ if (IsAtom(program))
448+ bool result = int.TryParse(program, out i);
452+ throw new ArgumentException("Invalid Atom: " + program);
460+ public override string ToString()
462+ return "[" + n1.ToString() + " " + n2.ToString() + "]";
465+ public Cell(string program)
467+ if (IsCell(program))
469+ Tuple<string, string> split = SplitCell(program);
470+ n1 = CreateNoun(split.Item1);
471+ n2 = CreateNoun(split.Item2);
474+ throw new ArgumentException("Invalid Cell: " + program);