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