Arcee:又一个 Parser Generator 轮子

项目地址:tdkihrr/arcee

为什么要做这样一个东西呢?不是有Antlr吗,Python下不是也有相应的bind吗?人类为什么又要再做一遍已经成熟了的东西呢?

答案是不爽!

之前刷 EOPL ,想用 Python 改写其中的玩具语言,重写了三四个后,感觉很别扭。教材里自带了一个parser,所以不用考虑解释器前端的东西,但我用Python改写时,由于没有可口的前端,写起来很不爽,每次写完后端,都只能自己用 Python 手敲一遍AST,真的很麻烦,所以我就萌生了自己写一个 parser generator 的想法。

所以,就有 Arcee 。

使用方法:

Install

$ pip install Arcee

Example

It's really readable.

grammar:

KEYWORDS        : let, if, zero, -
NUMBER          : \d+(\.\d*)?
ASSIGN          : =
SUBTRACTION     : -
RIGHT_BRACKET   : (
COLON           : ,
LETF_BRACKET    : )
ID              : [A-Za-z]+
SKIP            : [ \\t]+

program : expression ;
expression : zeroexp
    | diffexp
    | ifexp
    | varexp
    | letexp
    | constexp
    ;
constexp : $NUMBER ;
diffexp : '-' '(' expression ',' expression ')' ;
zeroexp : 'zero' '(' expression ')' ;
ifexp : 'if' expression 'then' expression 'else' expression ;
varexp : $ID ;
letexp : 'let' $ID '=' expression 'in' expression ;
$ arcee grammar > result.py

result.py has three parts:

Token

from collections import namedtuple

Token = namedtuple('Token', ['type', 'value', 'line', 'column']) Program = namedtuple('Program', ['expression']) # ...

Lexer

import re

def tokenize(code):
    pass # ...

Parser

class Parser:
    def __init__(self, token_list): pass # ... def parse_expression(self): if xxx: self.parse_constexp() elif yyy: self.parse_diffexp() #... def parse_constexp(self): pass def parse_diffexp(self): pass def parse_zeroexp(self): pass def parse_ifexp(self): pass def parse_varexp(self): pass def parse_letexp(self): pass

You can parse input such as:

input = '''let a = 0 in if zero(a) then -(a, 1) else -(a, 2)'''

tokens = list(tokenize(input)) parser = Parser(tokens) parser.parse_program()

result is:

result = Program(
    expression=Expression(
        nonterminal=Letexp(
            ID=Token(type='ID', value='a', line=2, column=4), expression1=Expression( nonterminal=Constexp( NUMBER=Token(type='NUMBER', value='0', line=2, column=8))), expression2=Expression( nonterminal=Ifexp( expression1=Expression( nonterminal=Zeroexp( expression=Expression( nonterminal=Varexp( ID=Token(type='ID', value='a', line=2, column=21))))), expression2=Expression( nonterminal=Diffexp( expression1=Expression( nonterminal=Varexp( ID=Token(type='ID', value='a', line=2, column=31))), expression2=Expression( nonterminal=Constexp( NUMBER=Token(type='NUMBER', value='1', line=2, column=34))))), expression3=Expression( nonterminal=Diffexp( expression1=Expression( nonterminal=Varexp( ID=Token(type='ID', value='a', line=2, column=44))), expression2=Expression( nonterminal=Constexp( NUMBER=Token(type='NUMBER', value='2', line=2, column=47))))))))))

Now, you can use this ast to do what you like.

这个轮子目前还有一点小问题,不过自己用的话还是没问题。由于工作缘故,估计是要去学 JavaScript 了,这个东西估计不会再更新了(也许哪天还会的。。。),到时估计就是重写一个 npm 包吧,这个再说。

猜你喜欢

转载自www.cnblogs.com/tdkihrr/p/9573397.html