从Combine(Literal(' @')+' spec')更改为关键字(' @ spec')删除空格

时间:2015-01-20 16:23:49

标签: python pyparsing

为什么使用Combine(...)保留空格,而Keyword(...)删除空白?

我需要在匹配的令牌之后保留空格。

测试如下:

from pyparsing import *


def parse(string, refpattern):

    print refpattern.searchString(string)

    pattern = StringStart() \
        + SkipTo(refpattern)('previous') \
        + refpattern('ref') \
        + SkipTo(StringEnd())('rest')

    print pattern.parseString(string)


string = "With @ref to_something"

identifier = Combine(Word(alphas + '_', alphanums + '_') + Optional('.' + Word(alphas)))

pattern_without_space = (CaselessKeyword('@ref') | CaselessKeyword(r'\ref')).setParseAction(lambda s, l, t: ['ref']) \
    + White().suppress() + identifier

pattern_with_space = Combine((Literal('@') | Literal('\\')).suppress() + 'ref') + White().suppress() + identifier

parse(string, pattern_without_space)
parse(string, pattern_with_space)

将输出:

[['ref', 'to_something']]
['With', 'ref', 'to_something', '']
[['ref', 'to_something']]
['With ', 'ref', 'to_something', '']
#     ^ space i need is preserved here

1 个答案:

答案 0 :(得分:1)

使用|运算符替换(CaselessKeyword运算符)时会出现问题。请参阅以下示例:

from pyparsing import *

theString = 'This is @Foo Bar'
identifier = Combine(Word(alphas + '_', alphanums + '_') + Optional('.' + Word(alphas)))
def testParser(p):
  q = StringStart() + SkipTo(p)("previous") + p("body") + SkipTo(StringEnd())("rest")
  return q.parseString(theString)

def test7():
  p0 = (CaselessKeyword('@Foo') | Literal('@qwe')) + White().suppress() + identifier
  p1 = (CaselessKeyword('@Foo') | CaselessKeyword('@qwe')) + White().suppress() + identifier
  p2 = (Literal('@qwe') | CaselessKeyword('@Foo')) + White().suppress() + identifier
  p3 = (CaselessKeyword('@Foo')) + White().suppress() + identifier
  p4 = Combine((Literal('@') | Literal('\\')).suppress() + 'Foo') + White().suppress() + identifier
  print "p0:", testParser(p0)
  print "p1:", testParser(p1)
  print "p2:", testParser(p2)
  print "p3:", testParser(p3)
  print "p4:", testParser(p4)

test7()

输出结果为:

p0: ['This is', '@Foo', 'Bar', '']
p1: ['This is', '@Foo', 'Bar', '']
p2: ['This is', '@Foo', 'Bar', '']
p3: ['This is ', '@Foo', 'Bar', '']
p4: ['This is ', 'Foo', 'Bar', '']

也许这是一个错误?

更新:这是您定义自己的解析器以匹配@Foo\Foo作为关键字的方式:

from pyparsing import *
import string

class FooKeyWord(Token):
  alphas = string.ascii_lowercase + string.ascii_uppercase
  nums       = "0123456789"
  alphanums  = alphas + nums

  def __init__(self):
    super(FooKeyWord,self).__init__()
    self.identChars = alphanums+"_$"
    self.name = "@Foo"
  def parseImpl(self, instring, loc, doActions = True):
    if (instring[loc] in ['@', '\\'] and
         instring.startswith('Foo', loc+1) and
         (loc+4 >= len(instring) or instring[loc+4] not in self.identChars) and
         (loc == 0 or instring[loc-1].upper() not in self.identChars)):
         return loc+4, instring[loc] + 'Foo'
    raise ParseException(instring, loc, self.errmsg, self)

def test8():
  p = FooKeyWord() + White().suppress() + identifier
  q = StringStart() + SkipTo(p)("previous") + p("body") + SkipTo(StringEnd())("rest")
  print "with @Foo:", q.parseString("This is @Foo Bar")
  print "with \\Foo:", q.parseString("This is \\Foo Bar")

输出:

with @Foo: ['This is ', '@Foo', 'Bar', '']
with \Foo: ['This is ', '\\Foo', 'Bar', '']