PS。在哪里阅读解析理论?
答案 0 :(得分:11)
阅读Dragon Book总是一个好主意。但请注意,如果您的语言不是微不足道的话,那么实际上并没有“短暂”的方式。
答案 1 :(得分:11)
很有可能去龙书学习解析理论。但我不认为龙书和你对“理论”的含义有同样的看法。 Dragon Book描述了如何构建手写解析器,解析器生成器等,但你几乎肯定想要使用解析器生成工具。
有些人建议使用Bison和Flex(或者他们的旧版本Yacc和Lex)。 那些是旧的坚定者,但它们不是非常有用的工具。 他们的文档本身并不差,只是它在处理使用它们的 accidental complexity 方面没有多大帮助。 他们的内部数据没有很好地封装,很难用它们做任何先进的事情。例如,在phc中,我们仍然没有正确的行号,因为它非常困难。当我们修改语法以包含No-op语句时,它们会变得更好,但这是一个令人难以置信的黑客攻击,这是不必要的。
表面上看,Bison和Flex一起工作,但界面很尴尬。更糟糕的是,每个版本都有很多版本,只能与另一些特定版本完美搭配。而且,最后我至少检查了哪些版本的文档很差。
编写递归下降解析器很简单,但可能很乏味。 Antlr可以为您做到这一点,它似乎是一个非常好的工具集,有益于您在这个项目上学到的东西可以应用于许多其他语言和平台(Antlr非常便携)。还有很多现有的语法可供学习。
不清楚你在使用什么语言,但有些语言有很好的解析框架。特别是Haskell Parsec Library似乎非常优雅。如果您使用C ++,可能会使用Spirit。我发现很容易上手,但很难 - 但仍然可能 - 用它做高级的事情。这与我一般的C ++经验相符。我说我发现它很容易启动,但后来我已经编写了几个解析器,并研究了编译器类中的解析。
长话短说:Antlr,除非你有充分的理由。
答案 2 :(得分:5)
这取决于你的语言。一些非常简单的语言只需很少的解析,因此可以手工编码;其他语言使用PEG生成器,例如Rats!(PEG是解析器表达式语法,位于Regex和LR解析器之间)或传统的解析器生成器,例如Antlr和Yacc。较不正式的语言需要概率性技术,例如link grammars。
答案 3 :(得分:4)
写一个Recursive Descent Parser。这有时比YACC / BISON更容易,而且通常更直观。
答案 4 :(得分:3)
答案 5 :(得分:2)
YACC,有不同语言的各种实现。
祝你的语言好运; - )
答案 6 :(得分:2)
我使用GOLD Parsing System,因为对于像我这样的新手而言,它似乎比ANTLR更容易使用,同时仍然充分满足我的需求。该网站包含documentation(包括Writing Grammars的说明,只有工作的一半)以及software。
答案 7 :(得分:2)
您的语言的野牛定义是context-free grammar的形式。关于这个主题的维基百科很好,很可能是一个很好的起点。
答案 8 :(得分:1)
使用解析器生成器作为宿主语言是最快的方法,结合了Dragon Book或{C,ML}系列中的现代编译器构造等书中的解析理论。
如果使用C,则yacc
和GNU版本bison
是标准生成器。 Antlr广泛用于多种语言,据我所知,支持Java,C#和C ++。几乎所有语言中都有许多其他语言。
我个人最喜欢的是Menhir,这是OCaml的优秀解析器生成器。 ML风格的语言(Ocaml,标准ML等)方言通常非常适合构建编译器和解释器。
答案 9 :(得分:1)
对于没有编译理论背景的人来说,ANTLR最简单,因为:
ANTLRWORKS(可视化解析和AST调试)
The ANTLR book(不需要编译器理论背景)
lexer和parser只有一种语法。
答案 10 :(得分:0)
如果您对parsing expression grammars感到满意,那么编写自己的解析器可能会非常短。这是一个简单的Packrat解析器,它包含PEG的合理子集:
import functools
class peg_parse:
def __init__(self, grammar):
self.grammar = {k:[tuple(l) for l in rules] for k,rules in grammar.items()}
@functools.lru_cache(maxsize=None)
def unify_key(self, key, text, at=0):
if key not in self.grammar:
return (at + len(key), (key, [])) if text[at:].startswith(key) \
else (at, None)
rules = self.grammar[key]
for rule in rules:
l, res = self.unify_rule(rule, text, at)
if res is not None: return l, (key, res)
return (0, None)
def unify_line(self, parts, text, tfrom):
results = []
for part in parts:
tfrom, res = self.unify_key(part, text, tfrom)
if res is None: return tfrom, None
results.append(res)
return tfrom, results
它接受python字典形式的语法,其中非终结符为键,替代方案为数组的元素,每个替代方案都是一个表达式序列。下面是一个示例语法。
term_grammar = {
'expr': [
['term', 'add_op', 'expr'],
['term']],
'term': [
['fact', 'mul_op', 'term'],
['fact']],
'fact': [
['digits'],
['(','expr',')']],
'digits': [
['digit','digits'],
['digit']],
'digit': [[str(i)] for i in list(range(10))],
'add_op': [['+'], ['-']],
'mul_op': [['*'], ['/']]
}
以下是驱动程序:
import sys
def main(to_parse):
result = peg_parse(term_grammar).unify_key('expr', to_parse)
assert (len(to_parse) - result[0]) == 0
print(result[1])
if __name__ == '__main__': main(sys.argv[1])
可以这样调用:
python3 parser.py '1+2'
('expr',
[('term',
[('fact',
[('digits', [('digit', [('1', [])])])])]),
('add_op', [('+', [])]),
('expr',
[('term', [('fact', [('digits', [('digit', [('2', [])])])])])])])
解析表达式文法要特别注意:替代项的顺序很重要(与上下文无关语法不同,替代项是有序的选择,第一个选择首先被尝试,第二个选择仅在第一个没有选择时才尝试)比赛)。但是,它们可以表示所有已知的上下文无关文法。
另一方面,如果您决定使用上下文无关语法,则Earley Parser是最简单的语法之一。