在javascript中根据AND OR逻辑分割一个字符串 [英] splitting a string based on AND OR logic in javascript

查看:129
本文介绍了在javascript中根据AND OR逻辑分割一个字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是分割一个包含逻辑操作的字符串。
例如,这里是我的示例字符串:

  var rule =device2.temperature> 20 || device2 .humidity> 68&& device3.temperature> 10

我需要解析该字符串在某种程度上,我可以轻松地操作我的逻辑,我不知道哪种方法会更好。



PS:请记住,这些规则字符串可以有10或更多不同的条件组合,如4个AND和6个OR。

解决方案

Overkill:



小心,没有完全测试。可能仍然包含错误

而且,代码不检查wether语法是否有效,只会引发一些明显的错误。

  var parse =(function(){

function parse(){
var cache = {};

/
function raw(v){return cache [v] ||(cache [v] = Function(return+ v))}这个可能和eval一样邪恶,

//解析字符串并将其转换为运算符令牌或函数
函数parseStrings(v,prop,symbol,number,string){
if(!prop&& amp {
throw new Error(意外/未处理的符号,v);
} else {
var w;
switch(prop){
// keywords
casetrue:
casefalse:
casenull:
w = r aw(v);
break;
}
tokens.push(
w ||
〜unary.indexOf(prop)&& v ||
prop&& parse.fetch v)||
number&& raw(number)||
string&&raw(string)||
symbol
);
}
}

var tokens = [];
for(var i = 0; i< arguments.length; ++ i){
var arg = arguments [i];
switch(typeof arg){
casenumber:
caseboolean:
tokens.push(raw(arg));
break;

casefunction:
tokens.push(arg);
break;

casestring:
//滥用str.replace()作为RegEx.forEach()
arg.replace(matchTokens,parseStrings);
break; $($)


$ b for(var i = tokens.lastIndexOf((),j; i> = 0; i = tokens.lastIndexOf(()) {
j = tokens.indexOf(),i);
if(j> 0){
tokens.splice(i,j + 1-i,process(tokens.slice (i + 1,j)));
} else {
throw new Error(mismatching parantheses)
}
}
if(tokens.indexOf ))> = 0)throw new Error(mismatching parantheses);

返回进程(令牌);
}

//组合令牌和函数,直到单个函数为止
函数进程(令牌){
//一元运算符,如
一元。 forEach(o => {
var i = -1;
while((i = tokens.indexOf(o,i + 1))> = 0){
if(( o ===+|| o === - )&& typeof令牌[i-1] ===function)continue;
tokens.splice(i,2,parse [unaryMapping [o] || o](令牌[i + 1]));
}
})
//二进制运算符
binary.forEach(o => {
for(var i = tokens.lastIndexOf(o); i> = 0; i = tokens.lastIndexOf(o)){
tokens.splice(i-1,3,parse [o ](令牌[i-1],令牌[i + 1]));
}
})

//三元运算符
for(var i = tokens.lastIndexOf(?),j; i> = 0; i = tokens.lastIndexOf(?)){
if(tokens [i + 2] ===:){
令牌.splice(i-1,5,parse.ternary(令牌[i-1],令牌[i + 1],令牌[i + 3]));
} else {
throw new Error(unexpected symbol)
}
}

if(tokens.length!== 1){
throw new Error(unparsed tokens left);
}
return tokens [0];
}

var unary =!,〜,+, - ,typeof.split(,);
var unaryMapping = {//以避免与二进制运算符的冲突
+:加号,
- :减
}
var二进制=**,*,/,%,+, - ,<,>>>>,<,& !=,===,!==,&,^,|,&&,||.split(,);
var matchTokens = /([az$_][\.a-z0-9$_]*)|([+\\--)/\\~^]=*|[\(\ ):::::::::::::::::::::::::::::::::::::::::::::::::::: \\ [\s\S] | [^])+ [] | ['](?: \\ [\s\S] | [^'])+ [' ])| \S / gi;

(function(){
var def = {value:null};
var odp =(k,v)=> {def.value = v; Object.defineProperty(parse,k,def)};

unary.forEach(o => {
var k = unaryMapping [o] || o ;
k in parse || odp(k,Function(a,return function(ctx){return+ o +(a(ctx))}));
})

//大多数浏览器不支持这种语法,所以我手动实现这个方法
odp(**,(a,b)=>(ctx)=>数学.pow(a(ctx),b(ctx)));
binary.forEach(o => {
o in parse || odp(o,Function(a,b函数(ctx){return a(ctx)+ o +b(ctx)}));
});

od p(ternary,(c,t,e)=> ctx => c(ctx)?t(ctx):e(ctx));

odp(fetch key => {
var a = key.split(。);
return ctx => {
//获取路径,如devices.2.temperature
// ctx [devices] [2] [temperature]; $($ v $ null);
for(var i = 0,v = ctx / * || window * /; i if(v == null)return void 0;
v = v [a [i]];
}
return v;
}
});

/ *一些糖* /
var aliases = {
或:||,
和:&&
not:!
}
for(var name in aliases)odp(name,parse [aliases [name]]);
})();

返回解析;
})();

和您的代码:

  var data = {
device2:{
temperature:18,
humidity:70
},

device3:{
温度:15,
湿度:75
}
};

//返回一个函数,期望上下文工作(可选)。
// aka。 (在上下文中,对象是device2定义的)
var rule = parse(device2.temperature> 20 || device2.humidity> 68&& device3.temperature> 10);
console.log(您的规则已解决:,规则(数据));

糖:

  var rule1 = parse(device2.temperature> 20); 
var rule2 = parse(device2.humidity> 68&& device3.temperature> 10);

//部分/组合规则到新的
//只`和`(a&& b),`或`(a || b),`加号+ value),`minus`(-value)和'not',(!value)命名别名
var rule3 = parse.or(rule1,rule2);
//但是你可以访问所有这样的运算符
var rule3 = parse ['||'](rule1,rule2);
//或者你可以组合函数和字符串
var rule3 = parse(rule1,||,rule2);

console.log((,rule1(data),||,rule2(data),)=,rule3(data));

//三元运算符和字符串('和'支持)
var example = parse(rule1,?'device2:'+ device2.temperature:'device3:'+ device3.temperature );
console.log(example(data))

还有什么要知道:



代码处理运算符优先级,并支持圆括号。

如果无法获取路径,它的特定函数返回undefined(不在此抛出错误)
访问路径中的数组键: parse(devices.2.temperature)提取设备[2] .temperature



未实现:



解析数组和解析函数调用以及关于值修改的所有内容。这个引擎做一些计算,它期望一些Value in,并给你一个值,没有更多的。


My problem is to split a string which contains a logical operation. For example, here is my sample string:

var rule = "device2.temperature > 20 || device2.humidity>68 && device3.temperature >10"

I need to parse that string in a way that I can easily operate my logic and I am not sure which approach would be better.

PS: Please keep in mind that those rule strings can have 10 or more different condition combinations, like 4 ANDs and 6 ORs.

解决方案

Overkill:

beware, not fully tested. may still contain errors
And, code doesn't check wether syntax is valid, only throws on a few obvious errors.

var parse = (function(){    

    function parse(){
        var cache = {};

        //this may be as evil as eval, so take care how you use it.
        function raw(v){ return cache[v] || (cache[v] = Function("return " + v)) }

        //parses Strings and converts them to operator-tokens or functions
        function parseStrings(v, prop, symbol, number, string){
            if(!prop && !symbol && !number && !string){
                throw new Error("unexpected/unhandled symbol", v);
            }else{
                var w;
                switch(prop){
                    //keywords
                    case "true":
                    case "false":
                    case "null":
                        w = raw( v );
                        break;
                }
                tokens.push( 
                    w || 
                    ~unary.indexOf(prop) && v ||
                    prop && parse.fetch(v) || 
                    number && raw( number ) || 
                    string && raw( string ) ||
                    symbol
                );
            }
        }       

        var tokens = [];
        for(var i = 0; i < arguments.length; ++i){
            var arg = arguments[i];
            switch(typeof arg){
                case "number":
                case "boolean":
                    tokens.push(raw( arg ));
                    break;

                case "function":
                    tokens.push( arg );
                    break;

                case "string":
                    //abusing str.replace() as kind of a RegEx.forEach()
                    arg.replace(matchTokens, parseStrings);
                    break;
            }
        }

        for(var i = tokens.lastIndexOf("("), j; i>=0; i = tokens.lastIndexOf("(")){
            j = tokens.indexOf(")", i);
            if(j > 0){
                tokens.splice(i, j+1-i, process( tokens.slice( i+1, j ) ));
            }else{
                throw new Error("mismatching parantheses")
            }
        }
        if(tokens.indexOf(")") >= 0) throw new Error("mismatching parantheses");

        return process(tokens);
    }

    //combines tokens and functions until a single function is left
    function process(tokens){
        //unary operators like
        unary.forEach(o => {
            var i = -1;
            while((i = tokens.indexOf(o, i+1)) >= 0){
                if((o === "+" || o === "-") && typeof tokens[i-1] === "function") continue;
                tokens.splice( i, 2, parse[ unaryMapping[o] || o ]( tokens[i+1] ));
            }
        })
        //binary operators
        binary.forEach(o => {
            for(var i = tokens.lastIndexOf(o); i >= 0; i = tokens.lastIndexOf(o)){
                tokens.splice( i-1, 3, parse[ o ]( tokens[i-1], tokens[i+1] ));
            }
        })

        //ternary operator
        for(var i = tokens.lastIndexOf("?"), j; i >= 0; i = tokens.lastIndexOf("?")){
            if(tokens[i+2] === ":"){
                tokens.splice(i-1, 5, parse.ternary(tokens[i-1], tokens[i+1], tokens[i+3] ));
            }else{
                throw new Error("unexpected symbol")
            }
        }

        if(tokens.length !== 1){
            throw new Error("unparsed tokens left");
        }
        return tokens[0];
    }

    var unary = "!,~,+,-,typeof".split(",");
    var unaryMapping = {    //to avoid collisions with the binary operators
        "+": "plus",
        "-": "minus"
    }
    var binary = "**,*,/,%,+,-,<<,>>,>>>,<,<=,>,>=,==,!=,===,!==,&,^,|,&&,||".split(",");
    var matchTokens = /([a-z$_][\.a-z0-9$_]*)|([+\-*/!~^]=*|[\(\)?:]|[<>&|=]+)|(\d+(?:\.\d*)?|\.\d+)|(["](?:\\[\s\S]|[^"])+["]|['](?:\\[\s\S]|[^'])+['])|\S/gi;

    (function(){
        var def = { value: null };
        var odp = (k,v) => { def.value = v; Object.defineProperty(parse, k, def) };

        unary.forEach(o => {
            var k = unaryMapping[o] || o;
            k in parse || odp(k, Function("a", "return function(ctx){ return " + o + "(a(ctx)) }"));
        })

        //most browsers don't support this syntax yet, so I implement this manually
        odp("**", (a,b) => (ctx) => Math.pow(a(ctx), b(ctx)));
        binary.forEach(o => {
            o in parse || odp(o, Function("a,b", "return function(ctx){ return a(ctx) "+o+" b(ctx) }"));
        });

        odp("ternary", (c,t,e) => ctx => c(ctx)? t(ctx): e(ctx));

        odp("fetch", key => {
            var a = key.split(".");
            return ctx => {
                //fetches a path, like devices.2.temperature
                //does ctx["devices"][2]["temperature"];
                for(var i=0, v = ctx /*|| window*/; i<a.length; ++i){
                    if(v == null) return void 0;
                    v = v[a[i]];
                }
                return v;
            }
        });

        /* some sugar */
        var aliases = {
            "or": "||",
            "and": "&&",
            "not": "!"
        }
        for(var name in aliases) odp(name, parse[aliases[name]]);
    })();

    return parse;
})();

and your code:

var data = {
    device2: {
        temperature: 18,
        humidity: 70
    },

    device3: {
        temperature: 15,
        humidity: 75
    }
};

//you get back a function, that expects the context to work on (optional).
//aka. (in wich context/object is `device2` defined?)
var rule = parse("device2.temperature > 20 || device2.humidity>68 && device3.temperature >10");
console.log("your rule resolved:", rule(data));

sugar:

var rule1 = parse("device2.temperature > 20");
var rule2 = parse("device2.humidity>68 && device3.temperature >10");

//partials/combining rules to new ones
//only `and` (a && b), `or` (a || b), `plus` (+value), `minus` (-value) and 'not', (!value) have named aliases
var rule3 = parse.or(rule1, rule2);
//but you can access all operators like this
var rule3 = parse['||'](rule1, rule2);
//or you can combine functions and strings 
var rule3 = parse(rule1, "||", rule2);

console.log( "(", rule1(data), "||", rule2(data), ") =", rule3(data) );

//ternary operator and Strings (' and " supported)
var example = parse(rule1, "? 'device2: ' + device2.temperature : 'device3: ' + device3.temperature");
console.log( example(data) )

What else to know:

Code handles operator precedence and supports round brackets

If a Path can't be fetched, it the particular function returns undefined (no Errors thrown here)
Access to Array-keys in the paths: parse("devices.2.temperature") fetches devices[2].temperature

not implemented:

parsing Arrays and parsing function-calls and everything around value modification. This engine does some computation, it expects some Value in, and gives you a value out. No more, no less.

这篇关于在javascript中根据AND OR逻辑分割一个字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆