如何构建允许在上下文/状态对象上执行操作的pyparsing
程序?
我的程序示例如下:
load 'data.txt'
remove line 1
remove line 4
第一行应加载文件,第2行和第3行是对文件内容进行操作的命令。因此,我希望在执行完所有命令后文件的内容。
load_cmd = Literal('load') + filename
remove_cmd = Literal('remove line') + line_no
more_cmd = ...
def load_action(s, loc, toks):
# load file, where should I store it?
load_cmd.setParseAction(load_action)
def remove_line_action(s, loc, toks):
# remove line, how to obtain data to operate on? where to write result?
remove_line_cmd.setParseAction(remove_cmd)
# Is this the right way to define a whole program, i.e. not only one line?
program = load_cmd + remove_cmd | more_cmd |...
# How do I obtain the result?
program.scanString("""
load 'data.txt'
remove line 1
remove line 4
""")
答案 0 :(得分:3)
我已经写了一些这种命令解析风格的pyparsing示例,你可以在网上找到它们: http://pyparsing.wikispaces.com/file/view/simpleBool.py/451074414/simpleBool.py http://pyparsing.wikispaces.com/file/view/eval_arith.py/68273277/eval_arith.py
我还编写了一个简单的冒险风格的游戏处理器,它接受解析的命令结构并针对游戏" world"执行它们,它充当命令执行器。我在PyCon 2006上发表了这篇文章,但会议页面的链接已经陈旧 - 您现在可以在http://www.ptmcg.com/geo/python/confs/pyCon2006_pres2.html找到它(演示文稿是使用S5 - 鼠标在右下角查看导航按钮)。代码位于http://www.ptmcg.com/geo/python/confs/adventureEngine.py.txt,代码的UML图位于http://www.ptmcg.com/geo/python/confs/pyparsing_adventure.pdf。
我发现最好的一般模式与旧的模型 - 视图 - 控制器模式类似。
Model是您的虚拟机,它维护从命令到命令的上下文。在simple_bool
中,上下文只是推断的局部变量范围,因为每个解析的语句只是eval
ed。在eval_arith
中,此上下文保存在EvalConstant._vars
dict中,其中包含预定义和已解析变量的名称和值。在Adventure引擎中,上下文保存在Player对象(包含指向当前Room和Items集合的属性)中,该对象被传递给解析的命令对象以执行命令。
View是解析器本身。它提取每个命令的各个部分并组成命令类的实例。命令类的exec
方法的接口取决于您如何设置模型。但总的来说,您可以设想您定义的exec
方法将模型作为其中一个(如果不是唯一的)参数。
然后Controller是一个简单的循环,它实现了以下伪代码:
while not finished
read a command, assign to commandstring
parse commandstring, use parsed results to create commandobj (null if bad parse)
if commandobj is not null:
commandobj.exec(context)
finished = context.is_finished()
如果使用pyparsing实现解析器,则可以将Command类定义为此抽象类的子类:
class Command(object):
def __init__(self, s, l, t):
self.parameters = t
def exec(self, context):
self._do_exec(context)
定义每个命令时,相应的子类可以直接作为命令表达式的解析操作传递。例如,用于在迷宫中移动的简化GO命令看起来像:
goExpression = Literal("GO") + oneOf("NORTH SOUTH EAST WEST")("direction")
goExpression.setParseAction(GoCommand)
对于上面的抽象Command类,GoCommand类可能如下所示:
class GoCommand(Command):
def _do_exec(self, context):
if context.is_valid_move(self.parameters.direction):
context.move(self.parameters.direction)
else:
context.report("Sorry, you can't go " +
self.parameters.direction +
" from here.")
通过解析像" GO NORTH"这样的语句,你将得到的不是包含令牌的ParseResults" GO"和" NORTH",但是一个GoCommand实例,其参数包括一个命名标记" direction",给出GO命令的方向参数。
所以设计步骤是:
设计您的虚拟机及其命令界面
创建一个类来捕获虚拟机中的状态/上下文
设计您的命令及其相应的Command子类
为每个命令创建pyparsing解析器表达式
将Command子类作为解析操作附加到每个命令的pyparsing表达式
使用' |'
实现命令处理器循环
答案 1 :(得分:1)
我会做这样的事情:
cmdStrs = '''
load
remove line
add line
some other command
'''
def loadParse(val): print 'Load --> ' + val
def removeParse(val): print 'remove --> ' + val
def addLineParse(val): print 'addLine --> ' + val
def someOtherCommandParse(val): print 'someOther --> ' + val
commands = [ l.strip() for l in cmdStrs.split('\n') if l.strip() !='' ]
functions = [loadParse,
removeParse,
addLineParse,
someOtherCommandParse]
funcDict = dict( zip(commands, functions) )
program = '''
# This is a comment
load 'data.txt' # This is another comment
remove line 1
remove line 4
'''
for l in program.split('\n'):
l = l.strip().split('#')[0].strip() # remove comments
if l == '': continue
commandFound = False
for c in commands:
if c in l:
funcDict[c](l.split(c)[-1])
commandFound = True
if not commandFound:
print 'Error: Unknown command : ', l
当然,您可以将整个事物放在一个类中并使其成为一个对象,但是您可以看到一般结构。如果您有一个对象,那么您可以继续创建一个可以处理上下文/状态信息的版本。然后,上面的函数将只是成员函数。
为什么在学习Haskell之后我会感觉你是从Python开始的?一般人都会走另一条路。在Python中,您可以免费获得州。你不需要类。您可以使用类来处理同一程序中的多个状态:)。