我想解析一个包含以下内容的文件:
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
type)或不加引号(phrase2
type)。
所以这些短语是自然语言句子(所有可能的标点符号)......
但:
- 内部内容无关(即我不需要解析NLP含义中的Natural Language本身)
- 只需将其标记为 a
phrase1
或phrase2
类型:- 那些没有和带引号的,即如果短语,它放在
(
and;
或;
and;
or;
and)
or even between(
and之间)
加上引号,那么它是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;博士:
与 PEG.js 相关:
有没有应用规则的例子:
& expression
! expression
$ expression
& { predicate }
! { predicate }
一般问题:
- 用直观的模棱两可的语法处理如此复杂的字符串的可能方法是什么?这仍然不是自然语言,看起来它有一些正式的结构,只是有几个可选部分。其中一个想法是通过预处理来拆分字符串(借助正则表达式,在固定元素的位置,即“abbr -”“这是行尾”),然后为每个拆分部分创建一个单独的语法。但它似乎存在性能问题和可伸缩性问题(即 - 如果固定元素会发生一些变化 - 例如不再有
-
char 了。)
更新1:
我找到了解决匹配"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
包含在输出中呢?是否有任何通用规则可以根据规则中的表达式折叠结果?
更新2:
我创建了语法,以便它可以根据需要进行解析,尽管我还没有确定这种语法创建的清晰过程:
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 中可用表达式的示例)以及对语法本身的改进建议(我认为这与现在的理想语法相去甚远) ,我将不胜感激!