使用 Python 的 PLY 库更改词法规则



在我的编译器课上,我们的讲师告诉我们,我们将要实现的语言的语法将需要在解析器中提前查看。 使用像flex这样的工具,这可以通过foo/x轻松完成。

我目前正在尝试使用 PLY 库在 Python 中做一个示例程序,看看 Python 是否适合该项目。 我正在尝试实现FORTRAN的do循环的简单版本:

-- Spaces are ignored in FORTRAN
DO 5 I=1,10   -- Loop
DO 5 I=1.10   -- Assignment (DO5I = 1.10)

目前,我的想法是匹配DO关键字,向前看,看看其余的输入是否与循环匹配。 如果是,则返回DO令牌。 否则,我想"倒带"输入并进入标识符规则。 像这样:

def t_do(t):
    'do'
    if re.match(do_loop_regex, t.lexer.lexdata[t.lexer.lexpos:]):
        return t
    else:
        t.rewind() # this is what I need to figure out
        return t_identifier(t)
def t_identifier(t):
    '[A-Z_][A-Z0-9_]*'
    return t

使用 Ply 是可能的,但需要一些数据处理和令牌构建。

import re
from ply import lex
tokens = ('LOOP','ASSIGNMENT')
literals = '=,'
re_float = r'(d+.d+)'
re_int = r'(d+)'
re_ident = r'([A-Za-z]w*)'
re_expr = '(%s)' % '|'.join([re_float, re_int, re_ident])
re_loop = 'DO%s%s=%s,%s' % (re_int, re_ident, re_expr, re_expr)
@lex.TOKEN(re_loop)
def t_LOOP(t):
    return t
re_assignment = '%s=%s' % (re_ident, re_expr)
@lex.TOKEN(re_assignment)
def t_ASSIGNMENT(t):
    return t
def t_newline(t):
    r'n+'
    t.lineno += len(t.value)    # count newlines
def t_error(t):
    print "syntax error at %s, line# %d" % (t.value, t.lineno)
DATA = """-- Spaces are ignored in FORTRAN
DO 5 I=1,10   -- Loop
DO 5 I=1.10   -- Assignment (DO5I = 1.10)"""
def preprocess(data):
    re_spaces=re.compile('s*')
    re_comment=re.compile('--.*$')
    lines = []
    for line in data.split('n'):       # split into lines
        line = re_spaces.sub('', line)
        line = re_comment.sub('', line)
        if not line: continue           # skip blank lines
        line = line.upper()
        lines.append(line)
    return 'n'.join(lines)+'n'
print re_assignment
lexer = lex.lex()
lexer.input(preprocess(DATA))
while True:
    tok = lexer.token()
    if not tok: break
    print tok

首先,您必须手动去除注释、空格并强制为大写。 然后你构建一个解析器,它基本上实现了你的语法内联。 我认为它最终会变得无法控制。 如果是我,我认为实现我自己的词法分析器会容易得多,它只进行逐行正则表达式匹配。 Ply的词法分析器真正做的是用你所有的小正则表达式构建一个巨大的正则表达式,然后逐步匹配标记。

最新更新