Pyparsing 与左侧可选
Pyparsing with Optional at left
我有这样的东西
IDENTIFIER = Word(alphas + '_', alphanums + '_') #words
GENERIC_TYPE = Regex('[a-zA-Z_]+[a-zA-Z0-9_]*(\<[a-zA-Z0-9_]+\>)?') #List<string> or int
AMF = Keyword('public') | Keyword('private') | Keyword('protected') #method modifier
SFMF = Optional(Keyword('static')) & Optional(Keyword('final')) #static and final modifiers
对于这个例子:
res = (Optional(AMF) +
SFMF +
IDENTIFIER).parseString('Method')
print(res)
它打印:['Method']
但如果我添加 Optional(GENERIC_TYPE)
:
res = (Optional(AMF) +
SFMF +
Optional(GENERIC_TYPE) +
IDENTIFIER).parseString(text)
print(res)
它为 text='int Method'
打印 ['int', 'Method']
但为 'final Method'
(或仅 'Method'
)引发异常:
pyparsing.ParseException: Expected W:(abcd...,abcd...) (at char 12), (line:1, col:13)
看起来 pyparsing 没有看到 Optional 的东西,因为如果 GENERIC_TYPE 是可选的(就像它之前的很多东西一样)它应该更进一步并解析 IDENTIFIER 部分。
更新:
问题似乎出在解析逻辑上。如果有两个相同的模式,其中一个是 Optional,那么解析器不会检查它是否与第二个有关。例如:
m = Optional('M') + Literal('M')
m.parseString('M')
解析器将 'M' 匹配到第一部分,然后遗漏了非可选文字部分。
所以现在的问题是我能否解析它以便它与第二个匹配。它可能不在字符串或行的末尾,所以我不能使用它。
我会说,"GENERIC_TYPEs have to be followed by an IDENTIFIER"。因此,要解决语法问题,请将 res
重写为:
res = (Optional(AMF) +
SFMF +
Optional(GENERIC_TYPE + FollowedBy(IDENTIFIER)) +
IDENTIFIER).parseString(text)
你也可以这样写:
res = (Optional(AMF) +
SFMF +
(GENERIC_TYPE + IDENTIFIER | IDENTIFIER)).parseString(text)
Pyparsing 不会像正则表达式那样进行任何前瞻,您必须明确地将其包含在语法定义中。
此外,由于 IDENTIFIER 将匹配任何字符串,您可能希望定义一个匹配所有语言关键字的表达式,例如 'keyword',然后将 IDENTIFIER 定义为:
keyword = MatchFirst(map(Keyword,"public private protected static final".split()))
IDENTIFIER = ~keyword + Word(alphas + '_', alphanums + '_')
最后,您可能希望 GENERIC_TYPE 处理的不仅仅是简单的 container<type>
定义,例如 Map<String,String>
、Map<String,List<String>>
甚至 Map<String,Map<String,Map<String,Map<String,Map<String,String>>>>>
。
这将解析所有这些:
GENERIC_TYPE = Group(IDENTIFIER + nestedExpr('<', '>', content=delimitedList(IDENTIFIER)))
我有这样的东西
IDENTIFIER = Word(alphas + '_', alphanums + '_') #words
GENERIC_TYPE = Regex('[a-zA-Z_]+[a-zA-Z0-9_]*(\<[a-zA-Z0-9_]+\>)?') #List<string> or int
AMF = Keyword('public') | Keyword('private') | Keyword('protected') #method modifier
SFMF = Optional(Keyword('static')) & Optional(Keyword('final')) #static and final modifiers
对于这个例子:
res = (Optional(AMF) +
SFMF +
IDENTIFIER).parseString('Method')
print(res)
它打印:['Method']
但如果我添加 Optional(GENERIC_TYPE)
:
res = (Optional(AMF) +
SFMF +
Optional(GENERIC_TYPE) +
IDENTIFIER).parseString(text)
print(res)
它为 text='int Method'
打印 ['int', 'Method']
但为 'final Method'
(或仅 'Method'
)引发异常:
pyparsing.ParseException: Expected W:(abcd...,abcd...) (at char 12), (line:1, col:13)
看起来 pyparsing 没有看到 Optional 的东西,因为如果 GENERIC_TYPE 是可选的(就像它之前的很多东西一样)它应该更进一步并解析 IDENTIFIER 部分。
更新:
问题似乎出在解析逻辑上。如果有两个相同的模式,其中一个是 Optional,那么解析器不会检查它是否与第二个有关。例如:
m = Optional('M') + Literal('M')
m.parseString('M')
解析器将 'M' 匹配到第一部分,然后遗漏了非可选文字部分。
所以现在的问题是我能否解析它以便它与第二个匹配。它可能不在字符串或行的末尾,所以我不能使用它。
我会说,"GENERIC_TYPEs have to be followed by an IDENTIFIER"。因此,要解决语法问题,请将 res
重写为:
res = (Optional(AMF) +
SFMF +
Optional(GENERIC_TYPE + FollowedBy(IDENTIFIER)) +
IDENTIFIER).parseString(text)
你也可以这样写:
res = (Optional(AMF) +
SFMF +
(GENERIC_TYPE + IDENTIFIER | IDENTIFIER)).parseString(text)
Pyparsing 不会像正则表达式那样进行任何前瞻,您必须明确地将其包含在语法定义中。
此外,由于 IDENTIFIER 将匹配任何字符串,您可能希望定义一个匹配所有语言关键字的表达式,例如 'keyword',然后将 IDENTIFIER 定义为:
keyword = MatchFirst(map(Keyword,"public private protected static final".split()))
IDENTIFIER = ~keyword + Word(alphas + '_', alphanums + '_')
最后,您可能希望 GENERIC_TYPE 处理的不仅仅是简单的 container<type>
定义,例如 Map<String,String>
、Map<String,List<String>>
甚至 Map<String,Map<String,Map<String,Map<String,Map<String,String>>>>>
。
这将解析所有这些:
GENERIC_TYPE = Group(IDENTIFIER + nestedExpr('<', '>', content=delimitedList(IDENTIFIER)))