import dot from 'dot-access';
const OPERATORS = {
'==': (x,y)=>(x===y),
'!=': (x,y)=>(x!=y),
'>': (x,y)=>(x>y),
'>=': (x,y)=>(x>=y),
'<': (x,y)=>(x<y),
'<=': (x,y)=>(x<=y),
'-': (x,y)=>(x-y),
'+': (x,y)=>(x+y)
};
/**
* SymbolParser is used for populating a tree with
* keyword symbols.
*/
class SymbolParser {
constructor() {
this.SYMBOLS = {
SWAP: '@',
STEP: '->',
TEMPLATE: '^',
EVALUATE:'=',
DIRECTIVE:'@$'
}
}
_stringToArgs(str, scope) {
return (str.substring(str.indexOf('(') + 1, str.length - 1)).split(',').
map(function (val) {
if (typeof val === 'string')
if (!val[0] === "'") {
val = scope.resolve(val);
}else{
val = val.replace(/'/g, "");
}
return val;
});
}
_clean(value) {
for (var key in this.SYMBOLS)
if (this.SYMBOLS.hasOwnProperty(key)) {
if (this.startsWith(this.SYMBOLS[key], value))
value = value.replace(this.SYMBOLS[key], '');
}
return value;
}
evaluate(str, scope) {
var exp = str.split(' ');
if(exp.length === 1)
return (scope.resolve(exp[0]));
if(!OPERATORS.hasOwnProperty(exp[1]))
throw new Error('evaluate(): Unknown operator: '+exp[1]+' !');
return OPERATORS[exp[1]](scope.resolve(exp[0]), scope.resolve(exp[2]));
}
endsWith(searchString, subjectString, position) {
if (position === undefined || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.indexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
}
startsWith(searchString, str, position) {
position = position || 0;
return str.indexOf(searchString, position) === position;
}
hasKeySymbol(key) {
var flag = false;
for (var sym in this.SYMBOLS)
if (this.SYMBOLS.hasOwnProperty(sym))
if (this.startsWith(this.SYMBOLS[sym], key))
flag = true;
return flag;
}
applyStep(key, value, scope, newKey, newTree) {
if (this.startsWith(this.SYMBOLS.STEP, key))
newTree[newKey] = this.parse(value, scope);
}
applySwap(key, value, scope, newKey, newTree) {
var isCallable = false;
var args;
if (this.startsWith(this.SYMBOLS.SWAP, key)) {
if (value[value.length - 1] === ')') isCallable = true;
if (isCallable)
args = this._stringToArgs(value, scope);
var target = scope.resolve((isCallable)?
value.substring(0, value.indexOf('(')) : value);
if (typeof target === 'function') {
var paths = value.split('.');
paths.pop();
target = (args)?
target.bind.apply(target, [null].concat(args)):
target.bind(scope.resolve(paths.join('.')));
}
return newTree[newKey] = target;
}
}
applyDirectiveSwap(key, template, scope, newKey, newTree) {
if(this.startsWith(this.SYMBOLS.DIRECTIVE. key)) {
}
}
applyTemplate(key, template, scope, newKey, newTree) {
if (this.startsWith(this.SYMBOLS.TEMPLATE, key))
newTree[newKey] = template.replace(/\{\{([\w\.\-]*)\}\}/g, function (s, k) {
return scope.resolve(k);
});
}
applyEval(key, value, scope, newKey, newTree) {
if(this.startsWith(this.SYMBOLS.EVALUATE, key))
newTree[newKey] = this.evaluate(value, scope);
}
parseObjectLike(schema, scope) {
if (Array.isArray(schema))
return this.parseArray(schema, scope);
if (typeof schema === 'object')
return this.parseObject(schema, scope);
return schema;
}
parseArray(schema, scope) {
return schema.map(function (scheme, key) {
if (scheme.type)
scheme.key = key;
return this.parse(scheme, scope);
}.bind(this))
}
parseObject(tree, scope) {
var newTree = {};
var newKey;
for (var key in tree)
if (tree.hasOwnProperty(key)) {
if (this.hasKeySymbol(key)) {
newKey = this._clean(key);
this.applyStep(key, tree[key], scope, newKey, newTree);
this.applySwap(key, tree[key], scope, newKey, newTree);
this.applyTemplate(key, tree[key], scope, newKey, newTree);
this.applyEval(key, tree[key], scope, newKey, newTree);
} else {
newTree[key] = tree[key];
}
}
return newTree;
}
/**
* @param {Object|Array} tree
* @param {Scope} scope
* @returns {*}
*/
parse(tree, scope) {
return this.parseObjectLike(tree, scope);
}
}
export default SymbolParser