为我的语言编写解析器的最短方法是什么?

时间:2009-10-04 13:25:40

标签: parsing theory interpreter

PS。在哪里阅读解析理论?

11 个答案:

答案 0 :(得分:11)

阅读Dragon Book总是一个好主意。但请注意,如果您的语言不是微不足道的话,那么实际上并没有“短暂”的方式。

答案 1 :(得分:11)

摘要:最短的可能是Antlr。

很有可能去龙书学习解析理论。但我不认为龙书和你对“理论”的含义有同样的看法。 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)

道格拉斯·克罗克福德有一个平易近人的example of a parser written in JavaScript

答案 5 :(得分:2)

YACC,有不同语言的各种实现。

祝你的语言好运; - )

答案 6 :(得分:2)

我使用GOLD Parsing System,因为对于像我这样的新手而言,它似乎比ANTLR更容易使用,同时仍然充分满足我的需求。该网站包含documentation(包括Writing Grammars的说明,只有工作的一半)以及software

答案 7 :(得分:2)

尝试Bison进行解析,Flex进行lexing

您的语言的野牛定义是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是最简单的语法之一。