如何使用pyparsing LineStart?
How to use pyparsing LineStart?
我正在尝试使用 pyparsing 从文档中的评论中解析 key:value 对。一个键从一行的开头开始,然后是一个值。值可以在以空格开头的多行上继续。
import pyparsing as pp
instring = """
-- This is (a) #%^& comment
/*
name1: val
name2: val2 with $*&#@) junk
name3: val3: with @)(*% multi-
line: content
*/
"""
comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug()
identifier = pp.Word(pp.alphanums + "_").setDebug()
meta1 = pp.LineStart() + identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd())
meta2 = pp.LineStart() + pp.White() + pp.SkipTo(pp.LineEnd())
metaval = meta1 + pp.ZeroOrMore(meta2)
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.OneOrMore(metaval) + pp.Literal("*/")
if __name__ == "__main__":
p = metalist.parseString(instring)
print(p)
失败:
Matched {Empty SkipTo:(LineEnd) Empty} -> ['This is (a) #%^& comment']
File "C:\Users\user\py3\lib\site-packages\pyparsing.py", line 2305, in parseImpl
raise ParseException(instring, loc, self.errmsg, self)
pyparsing.ParseException: Expected start of line (at char 32), (line:4, col:1)
pyparsing whitespace match issues 的答案是
LineStart has always been difficult to work with, but ...
如果解析器位于第 4 行第 1 列(第一个 key:value 对),那么为什么找不到行首?识别以无空格开头的行和以空格开头的行的正确 pyparsing 语法是什么?
我认为我对 LineStart
的困惑是,对于 LineEnd
,我可以寻找一个 '\n'
字符,但是 [=13= 没有单独的字符].所以在 LineStart
中,我查看当前解析器位置是否位于 '\n'
之后;或者如果它当前 on a '\n'
,移动过去并继续。不幸的是,我在一个弄乱了报告位置的地方实现了这个,所以你会得到那些读起来像 "failed to find a start of line on line X col 1," 的奇怪错误,这听起来确实应该是一个成功匹配的行首。另外,我想我需要重新审视这个隐式的换行符跳过,或者就此而言,LineStart
的所有空格跳过。
目前,我已经通过稍微扩展您的行起始表达式使您的代码正常工作,如:
LS = pp.Optional(pp.LineEnd()) + pp.LineStart()
并将 meta1 和 meta2 中的 LineStart 引用替换为 LS:
comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug()
identifier = pp.Word(pp.alphanums + "_").setDebug()
meta1 = LS + identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd())
meta2 = LS + pp.White() + pp.SkipTo(pp.LineEnd())
metaval = meta1 + pp.ZeroOrMore(meta2)
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.OneOrMore(metaval) + pp.Literal("*/")
如果 LineStart
的这种情况让您感到不舒服,您可以尝试另一种策略:使用解析时条件仅接受从第 1 列开始的标识符:
comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug()
identifier = pp.Word(pp.alphanums + "_").setName("identifier")
identifier.addCondition(lambda instring,loc,toks: pp.col(loc,instring) == 1)
meta1 = identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd()).setDebug()
meta2 = pp.White().setDebug() + pp.SkipTo(pp.LineEnd()).setDebug()
metaval = meta1 + pp.ZeroOrMore(meta2, stopOn=pp.Literal('*/'))
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.LineEnd() + pp.OneOrMore(metaval) + pp.Literal("*/")
此代码完全取消了 LineStart
,同时我弄清楚了我希望这个特定令牌执行的操作。我还必须修改 metaval
中的 ZeroOrMore
重复,这样 */
就不会被意外处理为继续的评论内容。
感谢您对此的耐心等待 - 我不想快速推出补丁 LineStart
更改,然后发现我忽略了其他兼容性或其他边缘情况,这些情况只会让我回到当前的 less - 在这个 class 上的状态非常好。但在发布 2.1.10 之前,我会努力澄清这种行为。
我正在尝试使用 pyparsing 从文档中的评论中解析 key:value 对。一个键从一行的开头开始,然后是一个值。值可以在以空格开头的多行上继续。
import pyparsing as pp
instring = """
-- This is (a) #%^& comment
/*
name1: val
name2: val2 with $*&#@) junk
name3: val3: with @)(*% multi-
line: content
*/
"""
comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug()
identifier = pp.Word(pp.alphanums + "_").setDebug()
meta1 = pp.LineStart() + identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd())
meta2 = pp.LineStart() + pp.White() + pp.SkipTo(pp.LineEnd())
metaval = meta1 + pp.ZeroOrMore(meta2)
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.OneOrMore(metaval) + pp.Literal("*/")
if __name__ == "__main__":
p = metalist.parseString(instring)
print(p)
失败:
Matched {Empty SkipTo:(LineEnd) Empty} -> ['This is (a) #%^& comment']
File "C:\Users\user\py3\lib\site-packages\pyparsing.py", line 2305, in parseImpl
raise ParseException(instring, loc, self.errmsg, self)
pyparsing.ParseException: Expected start of line (at char 32), (line:4, col:1)
pyparsing whitespace match issues 的答案是
LineStart has always been difficult to work with, but ...
如果解析器位于第 4 行第 1 列(第一个 key:value 对),那么为什么找不到行首?识别以无空格开头的行和以空格开头的行的正确 pyparsing 语法是什么?
我认为我对 LineStart
的困惑是,对于 LineEnd
,我可以寻找一个 '\n'
字符,但是 [=13= 没有单独的字符].所以在 LineStart
中,我查看当前解析器位置是否位于 '\n'
之后;或者如果它当前 on a '\n'
,移动过去并继续。不幸的是,我在一个弄乱了报告位置的地方实现了这个,所以你会得到那些读起来像 "failed to find a start of line on line X col 1," 的奇怪错误,这听起来确实应该是一个成功匹配的行首。另外,我想我需要重新审视这个隐式的换行符跳过,或者就此而言,LineStart
的所有空格跳过。
目前,我已经通过稍微扩展您的行起始表达式使您的代码正常工作,如:
LS = pp.Optional(pp.LineEnd()) + pp.LineStart()
并将 meta1 和 meta2 中的 LineStart 引用替换为 LS:
comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug()
identifier = pp.Word(pp.alphanums + "_").setDebug()
meta1 = LS + identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd())
meta2 = LS + pp.White() + pp.SkipTo(pp.LineEnd())
metaval = meta1 + pp.ZeroOrMore(meta2)
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.OneOrMore(metaval) + pp.Literal("*/")
如果 LineStart
的这种情况让您感到不舒服,您可以尝试另一种策略:使用解析时条件仅接受从第 1 列开始的标识符:
comment1 = pp.Literal("--") + pp.originalTextFor(pp.SkipTo(pp.LineEnd())).setDebug()
identifier = pp.Word(pp.alphanums + "_").setName("identifier")
identifier.addCondition(lambda instring,loc,toks: pp.col(loc,instring) == 1)
meta1 = identifier + pp.Literal(":") + pp.SkipTo(pp.LineEnd()).setDebug()
meta2 = pp.White().setDebug() + pp.SkipTo(pp.LineEnd()).setDebug()
metaval = meta1 + pp.ZeroOrMore(meta2, stopOn=pp.Literal('*/'))
metalist = pp.ZeroOrMore(comment1) + pp.Literal("/*") + pp.LineEnd() + pp.OneOrMore(metaval) + pp.Literal("*/")
此代码完全取消了 LineStart
,同时我弄清楚了我希望这个特定令牌执行的操作。我还必须修改 metaval
中的 ZeroOrMore
重复,这样 */
就不会被意外处理为继续的评论内容。
感谢您对此的耐心等待 - 我不想快速推出补丁 LineStart
更改,然后发现我忽略了其他兼容性或其他边缘情况,这些情况只会让我回到当前的 less - 在这个 class 上的状态非常好。但在发布 2.1.10 之前,我会努力澄清这种行为。