模糊语法和PEG.js的问题(未找到示例)

时间:2014-07-09 17:02:03

标签: regex parsing ambiguity peg

我想用以下内容的行解析文件:

simple word abbr -8. (012) word, simple phrase, one another phrase - (simply dummy text of the printing; Lorem Ipsum : "Lorem" - has been the industry's standard dummy text, ever since the 1500s!; "It is a long established!"; "Sometimes by accident, sometimes on purpose (injected humour and the like)"; "sometimes on purpose") This is the end of the line

所以现在解释部分(不是所有的空格都被描述,因为这里有标记):

  • simple word是由空格分隔的一个或多个单词(短语)
  • abbr -是字符串的固定部分(从不更改)
  • 8 - 可选号码
  • . - 始终包含
  • word, simple phrase, one another phrase - 用逗号分隔的一个或多个单词或短语
  • - ( - 固定部分,始终包含
  • simply dummy text of the printing; Lorem Ipsum : "Lorem" - has been the industry's standard dummy text, ever since the 1500s!; - (可选)由;
  • 分隔的一个或多个短语
  • "It is a long established!"; "Sometimes by accident, sometimes on purpose (injected humour and the like)"; "sometimes on purpose" - (可选)一个或多个引号";分隔的短语
  • ) This is the end of the line - 始终包含

在最坏的情况下,子句中没有短语,但这种情况并不常见:应该有一个短语而不增加引号(phrase1类型)或使用它们(phrase2类型)。

所以这些短语是自然语言句子(可能所有的标点符号)......

BUT:

  • 内部内容无关紧要(即我不需要在NLP意义上解析自然语言本身)
  • 只需要将其标记为phrase1phrase2类型:
    • 那些没有和带引号的词,即如果短语位于(;;;;之间, )或甚至()之间加上引号,然后是phrase2类型
    • 否则,如果短语的开头或结尾没有引号,虽然它可以包含短语中的所有标记,但它是phrase1类型

由于为这样的输入编写正则表达式(PCRE)是一种过度杀伤,所以我查看了解析方法(EBNF或类似方法)。我最终得到了一个PEG.js解析器生成器。我创建了一个基本的语法变体(即使不处理子句中不同短语的部分):

start = term _ "abbr" _ "-" .+
term = word (_? word !(_ "abbr" _ "-"))+
word = letters:letter+ {return letters.join("")}
letter = [A-Za-z]
_ "whitespace"
  = [ \t\n\r]*

或(差异仅在" abbr -""_ "abbr" _ "-""):

start = term " abbr -" .+
term = word (_? word !(" abbr -"))+
word = letters:letter+ {return letters.join("")}
letter = [A-Za-z]
_ "whitespace"
  = [ \t\n\r]*

但即便是这个简单的语法也无法解析字符串的开头。错误是:

  • Parse Error Expected [A-Za-z] but " " found.
  • Parse Error Expected "abbr" but "-" found.

因此看起来问题在于模糊性:"abbr"term用作word令牌。虽然我定义了我认为有意义的规则!(" abbr -"),但如果下一个子字符串不是word种,则只会消耗下一个" abbr -"令牌。

我没有找到任何好的例子来解释PEG.js的以下表达,在我看来这是上述问题的可能解决方案[来自:http://pegjs.majda.cz/documentation]

  • & expression
  • ! expression
  • $ expression
  • & { predicate }
  • ! { predicate }

TL; DR:

与PEG.js相关:

  • 是否有任何应用规则的示例:

    • & expression
    • ! expression
    • $ expression
    • & { predicate }
    • ! { predicate }

一般问题:

  • 用直观的模​​糊语法处理这些复杂字符串的可行方法是什么?这仍然不是一种自然语言,看起来它有一些正式的结构,只有几个可选部分。其中一个想法是通过预处理来分割字符串(在正则表达式的帮助下,在固定元素的位置,即“abbr - ”“)这是行的结尾”)然后为每个分裂的部分创建一个单独的语法。但它似乎存在性能问题和可伸缩性问题(即 - 如果固定元素会稍微改变一下 - 例如,将不再有-字符。)

UPDATE1:

我找到了解决问题的规则,以匹配"abbr -"歧义:

term = term:(word (!" abbr -" _? word))+ {return term.join("")}

但结果看起来很奇怪:

[
   "simple, ,word",
   " abbr -",
   [
      "8",
      ...
   ],
   ...
]

如果删除谓词:term = term:(word (!" abbr -" _? word))+

[
   [
      "simple",
      [
         [
            undefined,
            [
               " "
            ],
            "word"
         ]
      ]
   ],
   " abbr -",
   [
      "8",
      ".",
      " ",
      "(",
      ...
   ],
   ...
]

我期待的是:

[
   [
      "simple word"
   ],
   " abbr -",
   [
      "8",
      ".",
      " ",
      "(",
      ...
   ],
   ...
]

或至少:

[
   [
      "simple",
      [
         " ",
         "word"
      ]
   ],
   " abbr -",
   [
      "8",
      ".",
      " ",
      "(",
      ...
   ],
   ...
]

表达式是分组的,所以为什么它在如此多的嵌套级别中分开,甚至undefined都包含在输出中?是否有任何通用规则根据规则中的表达式折叠结果?

UPDATE2:

我创建了语法,以便根据需要进行解析,尽管我还没有确定这种语法创建的明确过程:

start
  = (term:term1 (" abbr -" number "." _ "("number:number") "{return number}) terms:terms2 ((" - (" phrases:phrases ")" .+){return phrases}))

//start //alternative way = looks better
//  = (term:term1 " abbr -" number "." _ "("number:number") " terms:terms2 " - (" phrases:phrases ")" .+){return {term: term, number: number, phrases:phrases}}

term1
  = term1:(
    start_word:word
        (rest_words:(
          rest_word:(
            (non_abbr:!" abbr -"{return non_abbr;})
            (space:_?{return space[0];}) word){return rest_word.join("");})+{return rest_words.join("")}
        )) {return term1.join("");}


terms2
  = terms2:(start_word:word (rest_words:(!" - (" ","?" "? word)+){rest_words = rest_words.map(function(array) {
    return array.filter(function(n){return n != null;}).join("");
}); return start_word + rest_words.join("")})

phrases
//  = ((phrase_t:(phrase / '"' phrase '"') ";"?" "?){return phrase_t})+
  = (( (phrase:(phrase2 / phrase1) ";"?" "?) {return phrase;})+)

phrase2
  = (('"'pharse2:(phrase)'"'){return {phrase2: pharse2}})

phrase1
  = ((pharse1:phrase){return {phrase1: pharse1}})

phrase
  = (general_phrase:(!(';' / ')' / '";' / '")') .)+ ){return general_phrase.map(function(array){return array[1]}).join("")}

word = letters:letter+ {return letters.join("")}
letter = [A-Za-z]
number = digits:digit+{return digits.join("")}
digit = [0-9]
_ "whitespace"
  = [ \t\n\r]*

可以在PEG.js作者的网站上进行测试:[http://pegjs.majda.cz/online]或者在PEG.js Web-IDE上进行测试:[http://peg.arcanis.fr/]

如果有人对前面的问题有答案(即消除语法歧义的一般方法,PEG.js中可用表达式的例子)以及对语法本身的改进建议(这是我认为远离理想现在的语法),我非常感谢!

1 个答案:

答案 0 :(得分:3)

  

那么为什么它会在如此多的嵌套级别中分离,甚至未定义也包含在输出中?

如果查看documentation for PEG.js,您将看到几乎每个操作员都将其操作数的结果收集到一个数组中。 undefined运算符返回!

$运算符会绕过所有这些嵌套,只是为您提供匹配的实际字符串,例如:[a-z]+会给出一个字母数组,但$[a-z]+会给出一串字母

我认为大多数解析都遵循以下模式:"给我一切,直到我看到这个字符串"。你应该先用首先使用!在PEG中表达这一点,以确保你没有点击终止字符串,然后只取下一个字符。例如,要使所有事情都达到" abbr - ":

(!" abbr -" .)+

如果终止字符串是单个字符,您可以使用[^]作为简短形式,例如:[^x]+是一种较短的说法(!"x" .)+

解析逗号/分号分隔的短语而不是逗号/分号终止的短语有点令人讨厌,但将它们视为可选的终止符似乎可行(使用某些trim)。

start = $(!" abbr -" .)+ " abbr -" $num "." [ ]? "(012)" 
  phrase_comma+ "- (" noq_phrase_semi+ q_phrase_semi+ ")"
  $.*
phrase_comma    =      p:$[^-,]+    [, ]* { return p.trim() }
noq_phrase_semi = !'"' p:$[^;]+     [; ]* { return p.trim() }
q_phrase_semi   =  '"' p:$[^"]+ '"' [; ]* { return p }
num = [0-9]+

给出

[
    "simple word",
    " abbr -",
    "8",
    ".",
    " ",
    "(012)",
    [
        "word",
        "simple phrase",
        "one another phrase"
    ],
    "- (",
    [
        "simply dummy text of the printing",
        "Lorem Ipsum : \"Lorem\" - has been the industry's standard dummy text, ever since the 1500s!"
    ],
    [
        "It is a long established!",
        "Sometimes by accident, sometimes on purpose (injected humour and the like)",
        "sometimes on purpose"
    ],
    ")",
    " This is the end of the line"
]