pyparsing 解析 c/cpp 枚举值作为用户定义的宏
pyparsing parse c/cpp enums with values as user defined macros
我有一个用例,我需要匹配值可以是用户定义的宏的枚举。
枚举示例
typedef enum
{
VAL_1 = -1
VAL_2 = 0,
VAL_3 = 0x10,
VAL_4 = **TEST_ENUM_CUSTOM(1,2)**,
}MyENUM;
我正在使用下面的代码,如果我不使用 VAL_4 中的格式,它就可以工作。我也需要 VAL_4 中的匹配格式。我是 pyparsing 的新手,欢迎任何帮助。
我的代码:
BRACE, RBRACE, EQ, COMMA = map(Suppress, "{}=,")
_enum = Suppress("enum")
identifier = Word(alphas, alphanums + "_")
integer = Word("-"+alphanums) **#I have tried to "_(,)" to this but is not matching.**
enumValue = Group(identifier("name") + Optional(EQ + integer("value")))
enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue) + Optional(COMMA))
enum = _enum + Optional(identifier("enum")) + LBRACE + enumList("names") + RBRACE + Optional(identifier("typedef"))
enum.ignore(cppStyleComment)
enum.ignore(cStyleComment)
谢谢
-普纳
只是向 integer
添加更多字符是错误的做法。即使是这个表达式:
integer = Word("-"+alphanums)
不是特别好,因为它会匹配“---”、“xyz”、“q--10-”和许多其他非整数字符串。
最好正确定义integer
。你可以这样做:
integer = Combine(Optional('-') + Word(nums))
但我发现对于这些在解析字符串中出现很多地方的低级表达式,Regex 是最好的:
integer = Regex(r"-?\d+") # Regex(r"-?[0-9]+") if you like more readable re's
然后为hex_integer也定义一个,
然后要添加宏,我们需要一个递归表达式,以处理宏的参数也是宏的可能性。
所以在这一点上,我们应该暂时停止编写代码,并进行一些设计。在解析器开发中,这种设计通常看起来像一个 BNF,您可以在其中用一种伪代码来描述您的解析器:
enum_expr ::= "typedef" "enum" [identifier]
"{"
enum_item_list
"}" [identifier] ";"
enum_item_list ::= enum_item ["," enum_item]... [","]
enum_item ::= identifier "=" enum_value
enum_value ::= integer | hex_integer | macro_expression
macro_expression ::= identifier "(" enum_value ["," enum_value]... ")"
注意 macro_expression 的递归:它用于定义 enum_value,但它包含 enum_value 作为其自身定义的一部分。在pyparsing中,我们使用一个Forward来设置这种递归。
看看下面的代码是如何实现 BNF 的。我以您发布的一些项目为基础,但宏表达式需要一些返工。底线是“不要只是不断地向整数添加字符以试图让某些东西起作用。”
LBRACE, RBRACE, EQ, COMMA, LPAR, RPAR, SEMI = map(Suppress, "{}=,();")
_typedef = Keyword("typedef").suppress()
_enum = Keyword("enum").suppress()
identifier = Word(alphas, alphanums + "_")
# define an enumValue expression that is recursive, so that enumValues
# that are macros can take parameters that are enumValues
enumValue = Forward()
# add more types as needed - parse action on hex_integer will do parse-time
# conversion to int
integer = Regex(r"-?\d+").addParseAction(lambda t: int(t[0]))
# or just use the signed_integer expression found in pyparsing_common
# integer = pyparsing_common.signed_integer
hex_integer = Regex(r"0x[0-9a-fA-F]+").addParseAction(lambda t: int(t[0], 16))
# a macro defined using enumValue for parameters
macro_expr = Group(identifier + LPAR + Group(delimitedList(enumValue)) + RPAR)
# use '<<=' operator to attach recursive definition to enumValue
enumValue <<= hex_integer | integer | macro_expr
# remaining enum expressions
enumItem = Group(identifier("name") + Optional(EQ + enumValue("value")))
enumList = Group(delimitedList(enumItem) + Optional(COMMA))
enum = (_typedef
+ _enum
+ Optional(identifier("enum"))
+ LBRACE
+ enumList("names")
+ RBRACE
+ Optional(identifier("typedef"))
+ SEMI
)
# this comment style includes cStyleComment too, so no need to
# ignore both
enum.ignore(cppStyleComment)
试试看:
enum.runTests([
"""
typedef enum
{
VAL_1 = -1,
VAL_2 = 0,
VAL_3 = 0x10,
VAL_4 = TEST_ENUM_CUSTOM(1,2)
}MyENUM;
""",
])
runTests 用于在开发期间测试和调试您的解析器。使用 enum.parseString(some_enum_expression)
或 enum.searchString(some_c_header_file_text)
获取实际解析结果。
使用即将发布的 pyparsing 3.0 版本中的新铁路图功能,这是此解析器的可视化表示:
我有一个用例,我需要匹配值可以是用户定义的宏的枚举。
枚举示例
typedef enum
{
VAL_1 = -1
VAL_2 = 0,
VAL_3 = 0x10,
VAL_4 = **TEST_ENUM_CUSTOM(1,2)**,
}MyENUM;
我正在使用下面的代码,如果我不使用 VAL_4 中的格式,它就可以工作。我也需要 VAL_4 中的匹配格式。我是 pyparsing 的新手,欢迎任何帮助。
我的代码:
BRACE, RBRACE, EQ, COMMA = map(Suppress, "{}=,")
_enum = Suppress("enum")
identifier = Word(alphas, alphanums + "_")
integer = Word("-"+alphanums) **#I have tried to "_(,)" to this but is not matching.**
enumValue = Group(identifier("name") + Optional(EQ + integer("value")))
enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue) + Optional(COMMA))
enum = _enum + Optional(identifier("enum")) + LBRACE + enumList("names") + RBRACE + Optional(identifier("typedef"))
enum.ignore(cppStyleComment)
enum.ignore(cStyleComment)
谢谢
-普纳
只是向 integer
添加更多字符是错误的做法。即使是这个表达式:
integer = Word("-"+alphanums)
不是特别好,因为它会匹配“---”、“xyz”、“q--10-”和许多其他非整数字符串。
最好正确定义integer
。你可以这样做:
integer = Combine(Optional('-') + Word(nums))
但我发现对于这些在解析字符串中出现很多地方的低级表达式,Regex 是最好的:
integer = Regex(r"-?\d+") # Regex(r"-?[0-9]+") if you like more readable re's
然后为hex_integer也定义一个,
然后要添加宏,我们需要一个递归表达式,以处理宏的参数也是宏的可能性。
所以在这一点上,我们应该暂时停止编写代码,并进行一些设计。在解析器开发中,这种设计通常看起来像一个 BNF,您可以在其中用一种伪代码来描述您的解析器:
enum_expr ::= "typedef" "enum" [identifier]
"{"
enum_item_list
"}" [identifier] ";"
enum_item_list ::= enum_item ["," enum_item]... [","]
enum_item ::= identifier "=" enum_value
enum_value ::= integer | hex_integer | macro_expression
macro_expression ::= identifier "(" enum_value ["," enum_value]... ")"
注意 macro_expression 的递归:它用于定义 enum_value,但它包含 enum_value 作为其自身定义的一部分。在pyparsing中,我们使用一个Forward来设置这种递归。
看看下面的代码是如何实现 BNF 的。我以您发布的一些项目为基础,但宏表达式需要一些返工。底线是“不要只是不断地向整数添加字符以试图让某些东西起作用。”
LBRACE, RBRACE, EQ, COMMA, LPAR, RPAR, SEMI = map(Suppress, "{}=,();")
_typedef = Keyword("typedef").suppress()
_enum = Keyword("enum").suppress()
identifier = Word(alphas, alphanums + "_")
# define an enumValue expression that is recursive, so that enumValues
# that are macros can take parameters that are enumValues
enumValue = Forward()
# add more types as needed - parse action on hex_integer will do parse-time
# conversion to int
integer = Regex(r"-?\d+").addParseAction(lambda t: int(t[0]))
# or just use the signed_integer expression found in pyparsing_common
# integer = pyparsing_common.signed_integer
hex_integer = Regex(r"0x[0-9a-fA-F]+").addParseAction(lambda t: int(t[0], 16))
# a macro defined using enumValue for parameters
macro_expr = Group(identifier + LPAR + Group(delimitedList(enumValue)) + RPAR)
# use '<<=' operator to attach recursive definition to enumValue
enumValue <<= hex_integer | integer | macro_expr
# remaining enum expressions
enumItem = Group(identifier("name") + Optional(EQ + enumValue("value")))
enumList = Group(delimitedList(enumItem) + Optional(COMMA))
enum = (_typedef
+ _enum
+ Optional(identifier("enum"))
+ LBRACE
+ enumList("names")
+ RBRACE
+ Optional(identifier("typedef"))
+ SEMI
)
# this comment style includes cStyleComment too, so no need to
# ignore both
enum.ignore(cppStyleComment)
试试看:
enum.runTests([
"""
typedef enum
{
VAL_1 = -1,
VAL_2 = 0,
VAL_3 = 0x10,
VAL_4 = TEST_ENUM_CUSTOM(1,2)
}MyENUM;
""",
])
runTests 用于在开发期间测试和调试您的解析器。使用 enum.parseString(some_enum_expression)
或 enum.searchString(some_c_header_file_text)
获取实际解析结果。
使用即将发布的 pyparsing 3.0 版本中的新铁路图功能,这是此解析器的可视化表示: