on bdf9204 help sign in
363 lines 9.1 kB history raw
1---
2navhome: '/docs/'
3next: True
4sort: 9
5title: Javascript
6---
8# Javascript
10From [Joe Bryan](https://github.com/joemfb/nock.js/blob/master/nock.js):
12 (function (self, factory) {
13 'use strict'
15 if (typeof define === 'function' && define.amd) {
16 define([], factory)
17 } else if (typeof module === 'object' && typeof module.exports === 'object') {
18 module.exports = factory()
19 } else {
20 self.nock = factory()
21 }
22 }(this, function () {
23 'use strict'
25 /**
26 * Nock is a combinator interpreter on nouns. A noun is an atom or a cell.
27 * An atom is an unsigned integer of any size; a cell is an ordered pair of nouns.
28 *
29 * @see http://urbit.org/docs/nock/definition/
30 * @see http://media.urbit.org/whitepaper.pdf
31 */
33 var useMacros = false
35 /*
36 * code conventions:
37 *
38 * `n` is a noun,
39 * `s` is a subject noun,
40 * `f` is a formula (or cell of formulas)
41 */
43 /* operators */
45 /**
46 * wut (?): test for atom (1) or cell (0)
47 *
48 * ?[a b] 0
49 * ?a 1
50 */
51 function wut (n) {
52 return typeof n === 'number' ? 1 : 0
53 }
55 /**
56 * lus (+): increment an atom
57 *
58 * +[a b] +[a b]
59 * +a 1 + a
60 */
61 function lus (n) {
62 if (wut(n) === 0) throw new Error('lus cell')
63 return 1 + n
64 }
66 /**
67 * tis (=): test equality
68 *
69 * =[a a] 0
70 * =[a b] 1
71 * =a =a
72 */
73 function tis (n) {
74 if (wut(n) === 1) throw new Error('tis atom')
75 return deepEqual(n[0], n[1]) ? 0 : 1
76 }
78 /**
79 * fas (/): resolve a tree address
80 *
81 * /[1 a] a
82 * /[2 a b] a
83 * /[3 a b] b
84 * /[(a + a) b] /[2 /[a b]]
85 * /[(a + a + 1) b] /[3 /[a b]]
86 * /a /a
87 */
88 function fas (addr, n) {
89 if (n === undefined) throw new Error('invalid fas noun')
90 if (addr === 0) throw new Error('invalid fas addr: 0')
92 if (addr === 1) return n
93 if (addr === 2) return n[0]
94 if (addr === 3) return n[1]
96 return fas(2 + (addr % 2), fas((addr / 2) | 0, n))
97 }
99 /* formulas */
101 /**
102 * slot (0): resolve a tree address
103 *
104 * *[a 0 b] /[b a]
105 */
106 function slot (s, f) {
107 var p = fas(f, s)
109 if (p === undefined) throw new Error('invalid fas addr: ' + f)
111 return p
112 }
114 /**
115 * constant (1): return the formula regardless of subject
116 *
117 * *[a 1 b] b
118 */
119 function constant (s, f) {
120 return f
121 }
123 /**
124 * evaluate (2): evaluate the product of second formula against the product of the first
125 *
126 * *[a 2 b c] *[*[a b] *[a c]]
127 */
128 function evaluate (s, f) {
129 return nock(nock(s, f[0]), nock(s, f[1]))
130 }
132 /**
133 * cell (3): test if the product is a cell
134 *
135 * *[a 3 b] ?*[a b]
136 */
137 function cell (s, f) {
138 return wut(nock(s, f))
139 }
141 /**
142 * incr (4): increment the product
143 *
144 * *[a 4 b] +*[a b]
145 */
146 function incr (s, f) {
147 return lus(nock(s, f))
148 }
150 /**
151 * eq (5): test for equality between nouns in the product
152 *
153 * *[a 5 b] =*[a b]
154 */
155 function eq (s, f) {
156 return tis(nock(s, f))
157 }
159 /* macro-formulas */
161 /**
162 * ife (6): if/then/else
163 *
164 * *[a 6 b c d] *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
165 */
166 function macroIfe (s, f) {
167 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]]]]]]]]]]])
168 }
170 function ife (s, f) {
171 var cond = nock(s, f[0])
173 if (cond === 0) return nock(s, f[1][0])
174 if (cond === 1) return nock(s, f[1][1])
176 throw new Error('invalid ife conditional')
177 }
179 /**
180 * compose (7): evaluate formulas composed left-to-right
181 *
182 * *[a 7 b c] *[a 2 b 1 c]
183 */
184 function macroCompose (s, f) {
185 return nock(s, [2, [f[0], [1, f[1]]]])
186 }
188 function compose (s, f) {
189 // alternate form:
190 // return nock(nock(s, f[0]), constant(s, f[1]))
191 return nock(nock(s, f[0]), f[1])
192 }
194 /**
195 * extend (8): evaluate the second formula against [product of first, subject]
196 *
197 * *[a 8 b c] *[a 7 [[7 [0 1] b] 0 1] c]
198 */
199 function macroExtend (s, f) {
200 return nock(s, [7, [[[7, [[0, 1], f[0]]], [0, 1]], f[1]]])
201 }
203 function extend (s, f) {
204 // alternate form:
205 // return nock([compose(s, [[0, 1], f[0]]), s], f[1])
206 return nock([nock(s, f[0]), s], f[1])
207 }
209 /**
210 * invoke (9): construct a core and evaluate one of it's arms against it
211 *
212 * *[a 9 b c] *[a 7 c 2 [0 1] 0 b]
213 */
214 function macroInvoke (s, f) {
215 return nock(s, [7, [f[1], [2, [[0, 1], [0, f[0]]]]]])
216 }
218 function invoke (s, f) {
219 var prod = nock(s, f[1])
220 return nock(prod, slot(prod, f[0]))
221 }
223 /**
224 * hint (10): skip first formula, evaluate second
225 *
226 * *[a 10 [b c] d] *[a 8 c 7 [0 3] d]
227 * *[a 10 b c] *[a c]
228 */
229 function macroHint (s, f) {
230 if (wut(f[0]) === 0) return nock(s, [8, [f[0][1], [7, [[0, 3], f[1]]]]])
231 return nock(s, f[1])
232 }
234 function hint (s, f) {
235 if (wut(f[0]) === 0) {
236 if (wut(f[0][1]) === 1) throw new Error('invalid hint')
237 nock(s, f[0][1])
238 }
239 return nock(s, f[1])
240 }
242 /* indexed formula functions */
243 var macroFormulas = [slot, constant, evaluate, cell, incr, eq, macroIfe, macroCompose, macroExtend, macroInvoke, macroHint]
244 var formulas = [slot, constant, evaluate, cell, incr, eq, ife, compose, extend, invoke, hint]
246 /**
247 * nock (*)
248 *
249 * the nock function
250 *
251 * *[a [b c] d] [*[a b c] *[a d]]
252 * *a *a
253 */
254 function nock (s, f) {
255 if (wut(f[0]) === 0) return [nock(s, f[0]), nock(s, f[1])]
257 var idx = f[0]
259 if (idx > 10) throw new Error('invalid formula: ' + idx)
261 if (useMacros) return macroFormulas[idx](s, f[1])
263 return formulas[idx](s, f[1])
264 }
266 /* construct a JS noun (group an array into pairs, associating right) */
267 function assoc (x) {
268 if (!x.length) return x
270 if (x.length === 1) return assoc(x[0])
272 return [assoc(x[0]), assoc(x.slice(1))]
273 }
275 /* deep equality for arrays or primitives */
276 function deepEqual (a, b) {
277 if (a === b) return true
279 if (!(Array.isArray(a) && Array.isArray(b))) return false
280 if (a.length !== b.length) return false
282 for (var i = 0; i < a.length; i++) {
283 if (!deepEqual(a[i], b[i])) return false
284 }
286 return true
287 }
289 /* parse a hoon-serialized nock formula and construct a JS noun */
290 function parseNoun (x) {
291 if (Array.isArray(x)) return assoc(x)
293 if (typeof x === 'string') {
294 var str = x.replace(/[\."']/g, '').split(' ').join(',')
295 return assoc(JSON.parse(str))
296 }
298 return x
299 }
301 function nockInterface () {
302 var args = [].slice.call(arguments)
303 var subject, formula, noun
305 if (args.length === 1) {
306 formula = parseNoun(args[0])
307 } else if (args.length === 2) {
308 subject = parseNoun(args[0])
309 formula = parseNoun(args[1])
310 } else {
311 noun = assoc(args)
312 subject = noun[0]
313 formula = noun[1]
314 }
316 if (!formula) throw new Error('formula required')
318 if (!subject) {
319 // !=(~)
320 subject = [1, 0]
321 }
323 return nock(subject, formula)
324 }
326 return {
327 nock: nockInterface,
328 _nock: nock,
329 useMacros: function (arg) {
330 useMacros = arg === undefined || arg
331 return this
332 },
333 util: {
334 assoc: assoc,
335 parseNoun: parseNoun,
336 deepEqual: deepEqual
337 },
338 operators: {
339 wut: wut,
340 lus: lus,
341 tis: tis,
342 fas: fas
343 },
344 formulas: {
345 slot: slot,
346 constant: constant,
347 evaluate: evaluate,
348 cell: cell,
349 incr: incr,
350 eq: eq,
351 macroIfe: macroIfe,
352 ife: ife,
353 macroCompose: macroCompose,
354 compose: compose,
355 macroExtend: macroExtend,
356 extend: extend,
357 macroInvoke: macroInvoke,
358 invoke: invoke,
359 macroHint: macroHint,
360 hint: hint
361 }
362 }
363 }))