Skip to content
Permalink
Fetching contributors…
Cannot retrieve contributors at this time
352 lines (297 sloc) 7.57 KB
/* global define */
(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/theory/whitepaper#-nock
*/
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 it's 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
}
}
}))
You can’t perform that action at this time.