用于分析Python的资源
作为一个教育练习,我开始用Python编写Python lexer。 最后,我想实现一个可以自己运行的简单Python子集,所以我希望这个lexer可以用一个相当简单的Python子集编写,并尽可能少地导入 例如,我发现的涉及词法分析的教程,只需向前看一个字符,就可以确定下一个应该是什么标记,但我担心这对于Python来说是不够的(一方面,仅仅看一个字符,您无法区分分隔符或运算符,或者标识符和关键字;此外,处理缩进在我看来就像一个新的野兽;除此之外) 我发现这是非常有用的,但是,当我尝试实现它时,我的代码很快开始看起来非常丑陋,有很多用于分析Python的资源,python,lexical-analysis,Python,Lexical Analysis,作为一个教育练习,我开始用Python编写Python lexer。 最后,我想实现一个可以自己运行的简单Python子集,所以我希望这个lexer可以用一个相当简单的Python子集编写,并尽可能少地导入 例如,我发现的涉及词法分析的教程,只需向前看一个字符,就可以确定下一个应该是什么标记,但我担心这对于Python来说是不够的(一方面,仅仅看一个字符,您无法区分分隔符或运算符,或者标识符和关键字;此外,处理缩进在我看来就像一个新的野兽;除此之外) 我发现这是非常有用的,但是,当我尝试实现它时
if
语句和案例,而且这似乎不是一种“正确”的方式
有没有什么好的资源可以帮助我/教我这类代码(我也想完全解析它,但首先要做的是正确的?)
我不想在上面使用解析器生成器,但我希望生成的Python代码使用Python的一个简单子集,并且也是合理的自包含的,这样我至少可以梦想拥有一种能够解释自身的语言。(例如,从我的理解来看,如果我使用ply,我将需要我的语言来解释ply包以及解释它本身,我认为这会使事情变得更复杂).看看也许你发现它对你的任务有用。考虑看看PyPy,一个基于python的python实现。它显然也有一个python解析器。我过去在类似的项目中使用过传统的语法和语法。我也使用过(python lex yacc),我发现这些技能可以从一个转换到另一个 因此,如果您以前从未编写过解析器,我将使用ply编写您的第一个解析器,您将为以后的项目学习一些有用的技能
当你的ply解析器开始工作时,你可以手工制作一个,作为一个教育练习。根据我的经验,手工编写词法分析器和语法分析器会很快变得非常混乱-因此解析器生成器成功了!这个简单的基于正则表达式的词法分析器已经为我服务了好几次,非常好:
#-------------------------------------------------------------------------------
# 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)
#-------------------------------------------------------------------------------
#lexer.py
#
#基于regex的通用Lexer/tokenizer工具。
#有关示例,请参见底部的if _; main __;部分。
#
#伊莱·本德斯基(eliben@gmail.com)
#此代码位于公共域中
#最后修改日期:2010年8月
#-------------------------------------------------------------------------------
进口稀土
导入系统
类标记(对象):
“”“一个简单的令牌结构。
包含令牌类型、值和位置。
"""
定义初始值(自身、类型、值、位置):
self.type=type
self.val=val
self.pos=pos
定义(自我):
返回“%s(%s)”(self.type、self.val、self.pos)
类LexeError(异常):
“”“Lexer错误异常。
销售时点情报系统:
输入行中发生错误的位置。
"""
定义初始(自我,位置):
self.pos=pos
类Lexer(对象):
“”“一个简单的基于正则表达式的lexer/tokenizer。
请参见下面的用法示例。
"""
def uuu init uuuu(self、rules、skip_whitespace=True):
“”“创建一个lexer。
规则:
规则列表。每个规则都是`正则表达式,类型`
pair,其中使用的正则表达式是'regex'
要识别标记,“type”是类型
识别时要返回的令牌的名称。
跳过空白:
如果为True,则将跳过空白(\s+)而不是
由lexer报告。否则,你必须
指定空白的规则,否则将被删除
标记为错误。
"""
#所有正则表达式都连接成一个正则表达式
#具有命名组。因为组名称必须有效
#Python标识符,但
#用户是任意字符串,我们自动生成组
#命名并将其映射到令牌类型。
#
idx=1
regex_parts=[]
self.group_type={}
对于正则表达式,键入规则:
groupname='组%s'%idx
regex_parts.append(“(?P%s)”(组名,regex))
self.group\u type[groupname]=类型
idx+=1
self.regex=re.compile('|'.join(regex_部分))
self.skip_空格=skip_空格
self.re_ws_skip=re.compile('\S')
def输入(自身、buf):
“”“使用缓冲区作为输入初始化lexer。
"""
self.buf=buf
self.pos=0
def令牌(自身):
“”“返回在中找到的下一个令牌(令牌对象)。”
输入缓冲区。如果
已到达缓冲区。
如果出现词法分析错误(当前的
缓冲区不匹配任何规则),将使用
错误的位置。
"""
如果self.pos>=len(self.buf):
一无所获
其他:
如果self.skip_空白:
m=self.re_ws_skip.search(self.buf,self.pos)
如果m:
self.pos=m.start()
其他:
一无所获
m=self.regex.match(self.buf,self.pos)
如果m:
groupname=m.lastgroup
tok_type=self.group_type[groupname]
tok=令牌(tok_类型,m.group(groupname),self.pos)
self.pos=m.end()
返回tok
#如果我们在这里,没有匹配的规则
提升杆错误(自身位置)
def令牌(自身):
“”“返回在缓冲区中找到的标记的迭代器。”