0

在我的编译器课程中,我们的讲师告诉我们,我们将要实现的语言的语法需要在解析器中进行前瞻。使用 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
4

1 回答 1

1

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 的词法分析器真正做的就是用你所有的小正则表达式构建一个巨大的正则表达式,然后逐步匹配标记。

于 2013-10-14T00:02:20.500 回答