bot18
Version:
A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f
426 lines (413 loc) • 13.3 kB
JavaScript
var debug = require('debug')('codemap')
function PlainObject() {}
PlainObject.prototype = Object.create(null);
isObject = function (val) {
return Object.prototype.toString.call(val) === '[object Object]';
}
isArray = function (val) {
return Object.prototype.toString.call(val) === '[object Array]';
}
function shallowCopy (obj) {
var ret = new PlainObject();
Object.keys(obj).forEach(function (k) {
ret[k] = obj[k];
});
return ret;
}
function named (name, fn) {
return Object.defineProperty(function () {
return fn.apply(this, arguments)
}, '_name', {value: name})
}
codemap.named = named;
codemap.container = named.bind(null, 'container');
codemap.alter = named.bind(null, 'alter');
module.exports = codemap;
var globalId = module.exports.globalId = 0;
function codemap(rootMap) {
var app = {
merge: function merge(a, b) {
if (isObject(a) && isObject(b)) {
Object.keys(b).forEach(function (i) {
if (isObject(a[i]) && isObject(b[i])) {
a[i] = app.merge(a[i], b[i]);
} else if (isArray(a[i]) && isArray(b[i])) {
a[i] = a[i].concat(b[i]);
} else {
a[i] = b[i];
}
});
} else if (isArray(a) && isArray(b)) {
a = a.concat(b);
} else {
a = b;
}
return a;
},
plainObject: function plainObject() {
return new PlainObject();
},
pathObject: function(path) {
app.merge(path, {
get: function get(p) {
return app.get(p, path.map._ns);
},
exists: function exists(p) {
return app.exists(p, path.map._ns);
},
set: function set(p, val) {
return app.set(p, val, path.map._ns);
},
clear: function clear(p) {
return app.clear(p, path.map._ns);
}
});
path.get.exists = path.exists;
return path;
},
parsePath: function parsePath(p, map) {
var alterMatch = p.match(/^@(?:(.+):)?([^\[]+)(\[(\-?\d*)\])?$/);
if (alterMatch) {
var prefix = !alterMatch[1] && map._folder ? map._folder + '.' : '';
return app.pathObject({
p: p,
ns: alterMatch[1] || map._ns,
pointer: prefix + alterMatch[2],
op: 'alter',
weight: alterMatch[3] ? parseInt(alterMatch[3].replace(/\[|\]/g, ''), 10) : 0,
value: map[p],
map: map
});
}
var pushMatch = p.match(/^(?:(.+):)?([^\[]+)\[(\-?\d*)\]$/);
if (pushMatch) {
var prefix = !pushMatch[1] && map._folder ? map._folder + '.' : '';
return app.pathObject({
p: p,
ns: pushMatch[1] || map._ns,
pointer: prefix + pushMatch[2],
op: 'push',
weight: pushMatch[3] ? parseInt(pushMatch[3].replace(/\[|\]/g, ''), 10) : 0,
value: map[p],
map: map
});
}
var mergeMatch = p.match(/^(?:(.+):)?([^\{]+)\{(\-?\d*)\}$/);
if (mergeMatch) {
var prefix = !mergeMatch[1] && map._folder ? map._folder + '.' : '';
return app.pathObject({
p: p,
ns: mergeMatch[1] || map._ns,
pointer: prefix + mergeMatch[2],
op: 'merge',
weight: mergeMatch[3] ? parseInt(mergeMatch[3].replace(/\{|\}/g, ''), 10) : 0,
value: map[p],
map: map
});
}
if (p.charAt(0) === '_') return null;
var setMatch = p.match(/^(?:(.+):)?(.*)$/);
if (!setMatch) {
var err = new Error('invalid path `' + p + '`');
throw err;
}
var prefix = !setMatch[1] && map._folder ? map._folder + '.' : '';
return app.pathObject({
p: p,
ns: setMatch[1] || map._ns,
pointer: prefix + setMatch[2],
op: 'set',
value: map[p],
map: map
});
},
parseMap: function parseMap(map) {
if (map['_maps']) {
map['_maps'].forEach(function (map) {
app.parseMap(map);
});
}
Object.keys(map).forEach(function (p) {
var parsed = app.parsePath(p, map);
if (parsed) {
//debug('adding path cache', parsed)
app.addPathCache(parsed);
}
});
},
addPathCache: function addPathCache(parsed) {
var p = parsed.ns ? parsed.ns + ':' + parsed.pointer : parsed.pointer;
if (typeof app._pathCache[p] === 'undefined') {
app._pathCache[p] = [];
}
parsed.id = globalId++;
app._pathCache[p].push(parsed);
return p;
},
getPathCache: function getPathCache(p) {
return app._pathCache[p] || [];
},
clearCache: function clearCache(p) {
if (typeof p === 'undefined') {
app._valCache = app.plainObject();
}
else if (typeof p === 'string') {
delete app._valCache[p];
}
},
addValCache: function addValCache(p, val) {
app._valCache[p] = val;
},
getValCache: function getValCache(p) {
return app._valCache[p];
},
clear: function clear(p, defaultNs) {
debug('CLEAR', p, defaultNs)
if (!defaultNs) defaultNs = rootMap._ns;
var map = {_ns: defaultNs};
var parsed = app.parsePath(p, map);
if (parsed) {
var key = parsed.ns ? parsed.ns + ':' + parsed.pointer : parsed.pointer;
app.clearCache(key);
}
else {
var err = new Error('invalid path `' + p + '`');
throw err;
}
},
set: function set(p, val, defaultNs) {
debug('SET', p, val, defaultNs)
if (!defaultNs) defaultNs = rootMap._ns;
var map = {_ns: defaultNs};
map[p] = val;
var parsed = app.parsePath(p, map);
if (parsed) {
var key = app.addPathCache(parsed);
app.clearCache(key);
app.validatePathCache();
}
else {
var err = new Error('invalid path `' + p + '`');
throw err;
}
},
exists: function exists(p, defaultNs) {
if (!defaultNs) defaultNs = rootMap._ns;
if (defaultNs && p.indexOf(':') === -1) {
p = defaultNs + ':' + p;
}
debug('EXISTS', p);
var paths = app.getPathCache(p);
if (paths.length) {
debug('EXISTS', p, 'was found in path cache');
return true;
}
debug('EXISTS', p, 'was not found');
return false;
},
get: function get(p, defaultNs) {
if (!defaultNs) defaultNs = rootMap._ns;
if (defaultNs && p.indexOf(':') === -1) {
p = defaultNs + ':' + p;
}
debug('GET', p)
var cached = app.getValCache(p);
if (typeof cached !== 'undefined') {
debug('GET', p, 'was cached')
return cached;
}
var paths = app.getPathCache(p);
if (!paths.length) {
var err = new Error('path `' + p + '` is undefined');
err.path = p;
throw err;
}
var val = null;
// debug(JSON.stringify(paths, null, 2))
paths.forEach(function (path) {
var tmp = app.getValue(path);
//debug('get value', path.p, ' === ', tmp);
if (typeof tmp === 'undefined') {
var _err = new Error('undefined value for `' + p + '`');
_err.path = path;
throw _err;
}
switch (path.op) {
case 'set':
val = tmp;
break;
case 'push':
if (!val) val = [];
if (!isArray(val)) {
var _err2 = new Error('cannot push to non-array `' + p + '`');
_err2.val = val;
_err2.tmp = tmp;
_err2.path = path;
throw _err2;
}
debug('concat', val, tmp)
val = val.concat(tmp);
break;
case 'merge':
if (!val) val = app.plainObject();
if (!isObject(val) || !isObject(tmp)) {
var _err3 = new Error('cannot merge non-object-literal `' + p + '`');
_err3.val = val;
_err3.tmp = tmp;
_err3.path = path;
throw _err3;
}
val = app.merge(val, tmp);
break;
case 'alter':
if (typeof tmp === 'function' && (tmp.name === 'alter' || tmp._name === 'alter')) {
val = tmp.call(app, val);
} else val = tmp;
break;
}
});
//debug('val', val);
app.addValCache(p, val);
return val;
},
getValue: function getValue(path) {
if (isArray(path.value)) {
//debug('resolving array', path.value)
return path.value.map(function (val) {
var pathCopy = shallowCopy(path);
pathCopy.value = val;
//debug('path Copy', pathCopy)
return app.getValue(pathCopy);
});
}
var pointerValue = app.isPointer(path);
if (pointerValue) {
return path.get(pointerValue);
}
return typeof path.value === 'function' && (path.value.name === 'container' || path.value._name === 'container')
? path.value.call(app, path.get, path.set, path.clear)
: path.value;
},
validatePathCache: function validatePathCache() {
Object.keys(app._pathCache).forEach(function (p) {
var hasSet = false;
var hasMerge = false;
var hasPush = false;
var hasAlter = false;
var paths = app._pathCache[p];
// order paths by op
paths.sort(function (a, b) {
if (a.op === 'set' && b.op !== 'set') return -1;
if (b.op === 'set' && a.op !== 'set') return 1;
if (a.op === 'alter' && b.op !== 'alter') return 1;
if (b.op === 'alter' && a.op !== 'alter') return -1;
if (a.weight < b.weight) return -1;
if (b.weight < a.weight) return 1;
if (a.id < b.id) return -1;
if (b.id < a.id) return 1;
return 0;
});
paths.forEach(function (path) {
if (typeof path.value === 'undefined') {
var err = new Error('undefined value for `' + p + '`');
err.path = path;
throw err;
}
switch (path.op) {
case 'set':
if (hasSet) {
var _err4 = new Error('cannot set path twice `' + p + '`');
_err4.path = p;
_err4.paths = app._pathCache[p];
throw _err4;
}
hasSet = true;
break;
case 'push':
if (hasMerge) {
var _err5 = new Error('cannot push and merge to same path `' + p + '`');
_err5.path = p;
_err5.paths = app._pathCache[p];
throw _err5;
}
hasPush = true;
break;
case 'merge':
if (hasPush) {
var _err6 = new Error('cannot push and merge to same path `' + p + '`');
_err6.path = p;
_err6.paths = app._pathCache[p];
throw _err6;
}
hasMerge = true;
break;
case 'alter':
hasAlter = true;
break;
}
var pointerValue = app.isPointer(path);
if (pointerValue && typeof app._pathCache[pointerValue] === 'undefined') {
var _err7 = new Error('cannot point to undefined path `' + pointerValue + '`');
_err7.path = p;
_err7.paths = app._pathCache[p];
_err7.pointer = pointerValue;
throw _err7;
}
});
if (hasAlter && !(hasSet || hasMerge || hasPush)) {
var err = new Error('cannot alter undefined path `' + p + '`');
err.path = p;
err.paths = app._pathCache[p];
throw err;
}
});
},
isPointer: function isPointer(path) {
if (typeof path.value !== 'string') return false;
var pointerMatch = path.value.match(/^#(?:(.+):)?(.*)$/);
if (pointerMatch) {
var ns = pointerMatch[1] || path.ns;
var prefix = ns ? ns + ':' : '';
return prefix + pointerMatch[2];
}
return false;
},
export: function _export() {
var ret = app.plainObject();
var pathStrings = [];
Object.keys(rootMap).forEach(function (k) {
var path = app.parsePath(k, rootMap);
if (path && (!path.ns || path.ns === rootMap._ns)) {
var p = path.ns ? path.ns + ':' + path.pointer : path.pointer;
if (pathStrings.indexOf(p) === -1) {
pathStrings.push(p);
}
}
});
pathStrings.sort(function (a, b) {
if (a.length < b.length) return -1;
if (a.length > b.length) return 1;
return 0;
});
pathStrings.forEach(function (p) {
var parts = p.split('.');
var current = ret;
parts.forEach(function (part, idx) {
if (typeof current[part] === 'undefined') current[part] = app.plainObject();
if (idx == parts.length - 1) {
current[part] = app.get(p);
} else {
current = current[part];
}
});
});
return ret;
}
};
app._pathCache = app.plainObject();
app.get.exists = app.exists;
app.clearCache();
app.parseMap(rootMap);
app.validatePathCache();
return app;
}