对上下文进行操作的Pyparsing

时间:2014-07-13 09:42:47

标签: python pyparsing

如何构建允许在上下文/状态对象上执行操作的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
""")

2 个答案:

答案 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中,您可以免费获得州。你不需要类。您可以使用类来处理同一程序中的多个状态:)。