使用命名值使用 pyparsing 获取字符串中的标记位置

Get token position in string with pyparsing, using named values

我试图在使用 pyparsing 时获取字符串中的标记位置。我想报告 C 文件中包含防护问题的位置:

import pyparsing as pp

m = None
n = None

#a sample C header file
lines = "\
#ifndef HEADER_FILE_H\n\
#define HEADER_FILE_H 1\n\
\n\
\n\
/* code is here */\n\
\n\
#endif /* HEADER_FILE_H */\
"

LBRACE,RBRACE,LBRACK,RBRACK,LT,GT,LPAREN,RPAREN,DQ,SEMI = map(pp.Suppress,'{}[]<>()";')
CIDENT = pp.Word(pp.alphanums + "_")  #any C identifier
LCOMMENT = pp.Suppress("/*")
RCOMMENT = pp.Suppress("*/")

last_line = lines.split("\n")[-1]  #get last line

pound = pp.Literal("#") + pp.Suppress(pp.Optional(pp.White(" \t")))
ifndef = pound + pp.Literal("ifndef")
ifnotdefined = pound + pp.Literal("if") + pp.Literal("!defined")
define = pound + pp.Literal("define")
endif = pound + pp.Literal("endif")
comment = pp.Optional(LCOMMENT + CIDENT("guardname_endif") + RCOMMENT)("guard_end_comment")

includeguardifndef = pp.Or([ifndef, ifnotdefined]) + pp.Optional(LPAREN) + CIDENT("guardname_ifndef_val") + pp.Optional(RPAREN)
includeguard = define + CIDENT("guardname_define_val") + pp.Optional(pp.Literal("1")("guard_is_one"))
includeguard_top = includeguardifndef + includeguard
includeguardendif = endif("includeguardendif") + comment

try:
   m = includeguard_top.parseString(lines)
except pp.ParseException:
   pass

try:
   n = includeguardendif.parseString(last_line)
except pp.ParseException:
   pass

print(m)
print(n)

现在当我得到我的比赛"m"时,我可以得到m.guardname_define_value,最终我想得到像m.guardname_define_value.pos这样的东西,这是比赛的位置"lines".

我到达了这个 question,这让我快到了,但我不知道如何仍然使用标记获得命名范围?我不想使用幻数来获得比赛结束时的位置。

我对正则表达式并不陌生,但我是 pyparsing 的新手,我对它的强大和清晰感到非常惊讶。真的很享受。如果对我上面所做的有什么建议,我也会采纳。

  1. 主要问题是获取带有命名结果的标记位置,而不是使用幻数。例如:我不想做一些像 m.guardname_define_value[0][0] 这样神秘的事情来获得位置
  2. 我是否需要像上面那样 try/catch for pp.ParseException?如果我不这样做,我会得到一个例外。我真的不在乎匹配是否失败,我只是检查 None 的结果。
  3. 认为 nestedExpr 可能会做我想做的事情,但更进一步,我可以自动让它寻找神奇匹配的 #ifdef ... #endif? (如果 opener 是“#ifdef”而 closer 是“#endif”?)
  4. "search for anything"又不过分贪心的正确说法是什么?即这个正则表达式:".*(FOO).*" 会消耗并丢弃任何东西,直到它找到并捕获 FOO,然后再消耗并丢弃它之后的任何东西,我很难复制它。

谢谢。

这是您的示例代码,稍作修改。 (我真的不喜欢反斜杠,在您的脚本中嵌入文本示例时,您可能会发现使用三重引号更容易让人眼前一亮。)值得注意的是,我展示了 locatedExpr 的用法。这可能是你真正想看到的。另外,请参阅此 SO 问题:Pyparsing: get token location in results name

import pyparsing as pp

#a sample C header file
lines = """
#ifndef HEADER_FILE_H
#define HEADER_FILE_H 1


/* code is here */

#endif /* HEADER_FILE_H */
"""

LBRACE,RBRACE,LBRACK,RBRACK,LT,GT,LPAREN,RPAREN,DQ,SEMI = map(pp.Suppress,'{}[]<>()";')
CIDENT = pp.Word(pp.alphas + "_", pp.alphanums + "_")  #any C identifier
LCOMMENT = pp.Suppress("/*")
RCOMMENT = pp.Suppress("*/")


def make_directive(s, pound=pp.Literal("#")):
    return pp.Combine(pound + s, adjacent=False)

ifndef = make_directive("ifndef")
ifnotdefined = make_directive("if") + pp.Literal("!defined")
define = make_directive("define")
endif = make_directive("endif")

comment = pp.Optional(LCOMMENT
                      + CIDENT("guardname_endif") 
                      + RCOMMENT)("guard_end_comment")


includeguardifndef = ((ifndef | ifnotdefined) 
                      + pp.Optional(LPAREN) 
                      + CIDENT("guardname_ifndef_val") 
                      + pp.Optional(RPAREN))
includeguard = (define 
                + CIDENT("guardname_define_val") 
                + pp.Optional(pp.Literal("1")("guard_is_one")))
includeguard_top = includeguardifndef + includeguard
includeguardendif = endif("includeguardendif") + comment

# parse the header
parser = includeguard_top + pp.SkipTo(includeguardendif).suppress() + includeguardendif
print(parser.parseString(lines).dump())

# parse the header, with locns
loc = pp.locatedExpr
parser = loc(includeguard_top) + pp.SkipTo(includeguardendif).suppress() + loc(includeguardendif)
print(parser.parseString(lines).dump())

打印:

['#ifndef', 'HEADER_FILE_H', '#define', 'HEADER_FILE_H', '1', '#endif', 'HEADER_FILE_H']
- guard_end_comment: ['HEADER_FILE_H']
- guard_is_one: '1'
- guardname_define_val: 'HEADER_FILE_H'
- guardname_endif: 'HEADER_FILE_H'
- guardname_ifndef_val: 'HEADER_FILE_H'
- includeguardendif: '#endif'

[[1, '#ifndef', 'HEADER_FILE_H', '#define', 'HEADER_FILE_H', '1', 46], [69, '#endif', 'HEADER_FILE_H', 95]]
[0]:
  [1, '#ifndef', 'HEADER_FILE_H', '#define', 'HEADER_FILE_H', '1', 46]
  - guard_is_one: '1'
  - guardname_define_val: 'HEADER_FILE_H'
  - guardname_ifndef_val: 'HEADER_FILE_H'
  - locn_end: 46
  - locn_start: 1
  - value: ['#ifndef', 'HEADER_FILE_H', '#define', 'HEADER_FILE_H', '1']
[1]:
  [69, '#endif', 'HEADER_FILE_H', 95]
  - guard_end_comment: ['HEADER_FILE_H']
  - guardname_endif: 'HEADER_FILE_H'
  - includeguardendif: '#endif'
  - locn_end: 95
  - locn_start: 69
  - value: ['#endif', 'HEADER_FILE_H']