3

作为一个教育练习,我已经着手用 Python 编写一个 Python 词法分析器。最终,我想实现一个可以自行运行的简单 Python 子集,所以我希望这个词法分析器用一个相当简单的 Python 子集编写,并尽可能少地导入。

我发现的涉及词法分析的教程,例如kaleidoscope,向前看一个字符以确定接下来应该出现什么标记,但我担心这对于 Python 来说是不够的(一方面,只看一个你无法区分的字符分隔符或运算符,或在标识符和关键字之间;此外,处理缩进对我来说就像一头新野兽;除此之外)。

我发现这个链接很有帮助,但是,当我尝试实现它时,我的代码很快就开始看起来很丑陋,有很多if语句和案例,而且它似乎不是“正确”的方法.

是否有任何好的资源可以帮助/教我 lex 这种代码(我也想完全解析它,但首先是正确的?)?

我不喜欢使用解析器生成器,但我希望生成的 Python 代码使用 Python 的一个简单子集,并且是合理的自包含的,这样我至少可以梦想拥有一种可以自我解释的语言。(例如,根据我对这个示例的理解,如果我使用 ply,我将需要我的语言来解释 ply 包以及解释自身,我想这会使事情变得更复杂)。

4

4 回答 4

2

查看http://pyparsing.wikispaces.com/也许您发现它对您的任务很有用。

于 2012-05-22T07:52:15.927 回答
1

我过去曾在类似项目中使用过传统的flex/lexbison/yacc。我也使用过ply (python lex yacc),我发现这些技能可以从一种技能转移到另一种。

因此,如果您以前从未编写过解析器,我会使用 ply 编写您的第一个解析器,您将学到一些对以后项目有用的技能。

当你让你的层解析器工作时,你可以手工制作一个作为教育练习。根据我的经验,手工编写词法分析器和解析器很快就会变得非常混乱 - 因此解析器生成器的成功!

于 2012-05-22T07:56:32.493 回答
0

考虑查看 PyPy,一个基于 python 的 python 实现。它显然也有一个 python 解析器。

于 2012-05-22T07:54:00.030 回答
0

这个简单的基于正则表达式的词法分析器已经为我服务了几次,非常好:

#-------------------------------------------------------------------------------
# lexer.py
#
# A generic regex-based Lexer/tokenizer tool.
# See the if __main__ section in the bottom for an example.
#
# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
# Last modified: August 2010
#-------------------------------------------------------------------------------
import re
import sys


class Token(object):
    """ A simple Token structure.
        Contains the token type, value and position. 
    """
    def __init__(self, type, val, pos):
        self.type = type
        self.val = val
        self.pos = pos

    def __str__(self):
        return '%s(%s) at %s' % (self.type, self.val, self.pos)


class LexerError(Exception):
    """ Lexer error exception.

        pos:
            Position in the input line where the error occurred.
    """
    def __init__(self, pos):
        self.pos = pos


class Lexer(object):
    """ A simple regex-based lexer/tokenizer.

        See below for an example of usage.
    """
    def __init__(self, rules, skip_whitespace=True):
        """ Create a lexer.

            rules:
                A list of rules. Each rule is a `regex, type`
                pair, where `regex` is the regular expression used
                to recognize the token and `type` is the type
                of the token to return when it's recognized.

            skip_whitespace:
                If True, whitespace (\s+) will be skipped and not
                reported by the lexer. Otherwise, you have to 
                specify your rules for whitespace, or it will be
                flagged as an error.
        """
        # All the regexes are concatenated into a single one
        # with named groups. Since the group names must be valid
        # Python identifiers, but the token types used by the 
        # user are arbitrary strings, we auto-generate the group
        # names and map them to token types.
        #
        idx = 1
        regex_parts = []
        self.group_type = {}

        for regex, type in rules:
            groupname = 'GROUP%s' % idx
            regex_parts.append('(?P<%s>%s)' % (groupname, regex))
            self.group_type[groupname] = type
            idx += 1

        self.regex = re.compile('|'.join(regex_parts))
        self.skip_whitespace = skip_whitespace
        self.re_ws_skip = re.compile('\S')

    def input(self, buf):
        """ Initialize the lexer with a buffer as input.
        """
        self.buf = buf
        self.pos = 0

    def token(self):
        """ Return the next token (a Token object) found in the 
            input buffer. None is returned if the end of the 
            buffer was reached. 
            In case of a lexing error (the current chunk of the
            buffer matches no rule), a LexerError is raised with
            the position of the error.
        """
        if self.pos >= len(self.buf):
            return None
        else:
            if self.skip_whitespace:
                m = self.re_ws_skip.search(self.buf, self.pos)

                if m:
                    self.pos = m.start()
                else:
                    return None

            m = self.regex.match(self.buf, self.pos)
            if m:
                groupname = m.lastgroup
                tok_type = self.group_type[groupname]
                tok = Token(tok_type, m.group(groupname), self.pos)
                self.pos = m.end()
                return tok

            # if we're here, no rule matched
            raise LexerError(self.pos)

    def tokens(self):
        """ Returns an iterator to the tokens found in the buffer.
        """
        while 1:
            tok = self.token()
            if tok is None: break
            yield tok


if __name__ == '__main__':
    rules = [
        ('\d+',             'NUMBER'),
        ('[a-zA-Z_]\w+',    'IDENTIFIER'),
        ('\+',              'PLUS'),
        ('\-',              'MINUS'),
        ('\*',              'MULTIPLY'),
        ('\/',              'DIVIDE'),
        ('\(',              'LP'),
        ('\)',              'RP'),
        ('=',               'EQUALS'),
    ]

    lx = Lexer(rules, skip_whitespace=True)
    lx.input('erw = _abc + 12*(R4-623902)  ')

    try:
        for tok in lx.tokens():
            print(tok)
    except LexerError as err:
        print('LexerError at position %s' % err.pos)
于 2012-05-22T08:36:10.427 回答