3

教育的な演習として、Python レクサーを Python で作成することに着手しました。最終的には、それ自体で実行できる Python の単純なサブセットを実装したいと考えているため、このレクサーを Python の合理的に単純なサブセットで記述し、インポートをできるだけ少なくしたいと考えています。

kaleidoscopeなど、 lexing を含むチュートリアルを見つけた場合は、単一の文字を先読みして、次に来るトークンを決定しますが、これは Python には不十分であると思います (1 つの理由として、1 つの文字を見ただけでは区別できない区切り文字または演算子、または識別子とキーワードの間; さらに、インデントの処理は、私には新しい獣のように見えます; とりわけ)。

私はこのリンクが非常に役立つことを発見しましたが、それを実装しようとすると、私のコードはすぐに多くのifステートメントとケースワークでかなり醜く見え始め、それを行う「正しい」方法ではないように思えました.

この種のコードをレックスするのに役立つ/教えてくれる良いリソースはありますか?

私はパーサー ジェネレーターを使用しているわけではありませんが、結果の 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 を使用して最初のパーサーを作成すると、後のプロジェクトで役立つスキルを学ぶことができます。

ply パーサーが機能するようになったら、教育用の演習として手動で作成できます。私の経験では、レクサーとパーサーを手作業で書くと、すぐに面倒になってしまいます。

于 2012-05-22T07:56:32.493 に答える
0

Python ベースの Python 実装である PyPy を見てみましょう。もちろん、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 に答える