0

我是 PEGjs 的新手,我正在尝试编写一个 PEGjs 语法,将 RegEx 转换(\s*[\(])|(\s*[\)])|(\"[^\(\)]+?\")|([^\(\)\s]+)为语法。

基本上我想做的是转换测试输入

(App= smtp AND "SPort" != 25) OR (App= pop3 AND "SPort" != 110) OR (App = imap AND "SPort" != 143) AND (App= imap OR "SPort" != 143)

为如下的json格式

{
  "eventTypes": [
    "All"
  ],
  "condition": {
    "operator": "and",
    "terms": [
      {
        "operator": "or",
        "terms": [
          {
            "operator": "or",
            "terms": [
              {
                "operator": "and",
                "terms": [
                  {
                    "name": "App",
                    "operator": "equals",
                    "value": "smtp"
                  },
                  {
                    "name": "Sport",
                    "operator": "notEquals",
                    "value": "25"
                  }
                ]
              },
              {
                "operator": "and",
                "terms": [
                  {
                    "name": "App",
                    "operator": "equals",
                    "value": "pop3"
                  },
                  {
                    "name": "Sport",
                    "operator": "notEquals",
                    "value": "110"
                  }
                ]
              }
            ]
          },
          {
            "operator": "and",
            "terms": [
              {
                "name": "App",
                "operator": "equals",
                "value": "imap"
              },
              {
                "name": "Sport",
                "operator": "notEquals",
                "value": "143"
              }
            ]
          }
        ]
      },
      {
        "operator": "or",
        "terms": [
          {
            "name": "App",
            "operator": "equals",
            "value": "imap"
          },
          {
            "name": "Sport",
            "operator": "notEquals",
            "value": "143"
          }
        ]
      }
    ]
  }
}

我已经编写了一些复杂的 javascript 代码来将示例输入转换为 JSON 格式显示,但是代码有点复杂并且不容易长期维护,所以我想尝试一下语法解析器。由于我是语法世界的新手,我寻求一些帮助或指导来实现上述语法,以便我可以根据需要进行增强/编写?

您可以在此处查看正则表达式的输出

编辑

Javascript解决方案:

 var str = '((Application = smtp AND "Server Port" != 25) AND (Application = smtp AND "Server Port" != 25)) OR (Application = pop3 AND "Server Port" != 110) OR (Application = imap AND "Server Port" != 143) AND (Application = imap OR "Server Port" != 143)';

var final = str.replace(/\((?!\()/g,"['")        //replace ( with [' if it's not preceded with (
           .replace(/\(/g,"[")               //replace ( with [
           .replace(/\)/g,"']")              //replace ) with '] 
           .replace(/\sAND\s/g,"','AND','")  //replace AND with ','AND','
           .replace(/\sOR\s/g,"','OR','")    //replace OR with ','OR','
           .replace(/'\[/g,"[")              //replace '[ with [
           .replace(/\]'/g,"]")              //replace ]' with ]
           .replace(/"/g,"\\\"")             //escape double quotes
           .replace(/'/g,"\"");              //replace ' with "
console.log(JSON.parse("["+final+"]"))
4

1 回答 1

1

据我所知,你无法得到你想要的结果,因为它需要一个无限循环。具体来说,给定以下输入:

A OR B OR C

您要求此输出:

(A OR B) OR C

要获得此结果,您需要有这样的规则:

BOOL = left:( BOOL / Expression ) "OR" right:( Expression )

这会创建一个无限循环,因为 BOOL 永远无法解决。BOOL 无法解析,因为 BOOL 中的第一条规则是匹配 BOOL。然而,我们可以得到

A OR ( B OR C )

因为

BOOL = left:( Expression ) "OR" right:( BOOL / Expression )

不会创建无限循环。这是因为我们可以在递归回 BOOL 之前开始匹配某些东西。这有点令人兴奋,我知道,但相信我……你必须有一些东西让 PegJS 开始匹配,然后才能递归。

如果这是可以接受的,那么我相信这个语法会让你非常接近所需的输出:

// Our top-level rule is Expression
Expression
  = BOOL
  / SubExpression
  / Comparison
  / Term

// A sub expression is just an expression wrapped in parentheses
// Note that this does not cause an infinite loop because the first term is always "("
SubExpression
  = _ "(" _ innards: Expression _ ")" _ { return innards; }

Comparison
  = name:Term _ operator:("=" / "!=") _ value:Term {
      return {
        name: name,
        operator: operator === '=' ? 'equals' : 'notEquals',
        value: value,
      };
    }

BOOL = AND / OR

// We separate the AND and OR because we want AND to take precendence over OR
AND
  = _ left:( OR / SubExpression / Comparison ) _ "AND" _ right:( AND / OR / SubExpression / Comparison ) _ {
    return {
      operator: 'and',
      terms: [ left, right ]
    }
  }

OR
  = _ left:( SubExpression / Comparison ) _ "OR" _ right:( OR / SubExpression / Comparison ) _ {
    return {
      operator: 'or',
      terms: [ left, right ]
    }
  }

Term
  = '"'? value:$( [0-9a-zA-Z]+ ) '"'? {
      return value;
    }

Integer "integer"
  = _ [0-9]+ { return parseInt(text(), 10); }

_ "whitespace"
  = [ \t\n\r]*

鉴于您的意见,我们会得到:

{
   "operator": "and",
   "terms": [
      {
         "operator": "or",
         "terms": [
            {
               "operator": "and",
               "terms": [
                  {
                     "name": "App",
                     "operator": "equals",
                     "value": "smtp"
                  },
                  {
                     "name": "SPort",
                     "operator": "notEquals",
                     "value": "25"
                  }
               ]
            },
            {
               "operator": "or",
               "terms": [
                  {
                     "operator": "and",
                     "terms": [
                        {
                           "name": "App",
                           "operator": "equals",
                           "value": "pop3"
                        },
                        {
                           "name": "SPort",
                           "operator": "notEquals",
                           "value": "110"
                        }
                     ]
                  },
                  {
                     "operator": "and",
                     "terms": [
                        {
                           "name": "App",
                           "operator": "equals",
                           "value": "imap"
                        },
                        {
                           "name": "SPort",
                           "operator": "notEquals",
                           "value": "143"
                        }
                     ]
                  }
               ]
            }
         ]
      },
      {
         "operator": "or",
         "terms": [
            {
               "name": "App",
               "operator": "equals",
               "value": "imap"
            },
            {
               "name": "SPort",
               "operator": "notEquals",
               "value": "143"
            }
         ]
      }
   ]
}
于 2018-04-06T22:16:13.730 回答