PLY 解析 C 文件以获得大括号构造
PLY Parse C Files for curly brace construct
我想用 PLY 解析一些 C 代码。
我要提取的内容如下:
{ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4}
这个结构可以隐藏在更多的花括号中。
{SOME, RANDOM, STUFF {ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4}, SOME, MORE, RANDOM, STUFF }
目前我可以对我想提取的结构进行 lex ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4
但前提是它是唯一匹配的。
{SOME, RANDOM, STUFF {ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4}, SOME, MORE, RANDOM, STUFF }{Argument1, Argument2, Argument3, Argument4}
这是我当前方法失败的地方,因为上面示例的词法分析输出将是:
ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4}, SOME, MORE, RANDOM, STUFF }{Argument1, Argument2, Argument3, Argument4
如何才能只收到以下内容:
ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4
Argument1, Argument2, Argument3, Argument4
简短说明:
我确实有一个条件词法分析器,它搜索左花括号以保存其位置。
对于每个新的左括号,我都会增加一个计数器。
对于每个右括号,我都会减少计数器。
如果计数器为零,我开始将 t.value
设置为从最新的左大括号到下一个右大括号的所有元素。
我想这应该适用于示例字符串中的多个命中。
在我看来,我未能从 ccode
状态切换回 initial
状态。
现在来看我的实际代码(在这个例子中我省略了花括号中的逗号,使我的编程更简单):
import ply.lex as lex
import ply.yacc as yacc
# Declare the state
states = (
('ccode', 'exclusive'),
)
tokens = [
'TEXT',
'CCODE'
]
# this saves all rbrace positions
# to get the inner curly brace construct you want to use first element
# text lib call should always be the inner curly brace construct
rbrace_positions = []
def t_ANY_TEXT(t):
r'\w+'
t.value = str(t.value)
return t
# Match the first {. Enter ccode state.
def t_ccode(t):
r'\{'
t.lexer.code_start = t.lexer.lexpos # Record the starting position
print(t.lexer.code_start)
t.lexer.level = 1 # Initial brace level
t.lexer.begin('ccode') # Enter 'ccode' state
def t_lbrace(t):
r'\{'
t.lexer.level += 1
def t_rbrace(t):
r'\}'
t.lexer.level -= 1
# Rules for the ccode state
def t_ccode_lbrace(t):
r'\{'
t.lexer.current_lbrace = t.lexer.lexpos
t.lexer.level += 1
def t_ccode_rbrace(t):
r'\}'
rbrace_positions.append(t.lexer.lexpos)
t.lexer.level -= 1
# If closing brace, return the code fragment
if t.lexer.level == 0:
t.value = t.lexer.lexdata[t.lexer.current_lbrace:rbrace_positions[0]-1]
t.type = "CCODE"
t.lexer.lineno += t.value.count('\n')
t.lexer.begin('INITIAL')
for _ in reversed(rbrace_positions):
rbrace_positions.pop()
return t
# C or C++ comment (ignore)
def t_ccode_comment(t):
r'(/\*(.|\n)*?\*/)|(//.*)'
pass
# C string
def t_ccode_string(t):
r'\"([^\\n]|(\.))*?\"'
# C character literal
def t_ccode_char(t):
r'\'([^\\n]|(\.))*?\''
# Any sequence of non-whitespace characters (not braces, strings)
def t_ccode_nonspace(t):
r'[^\s\{\}\'\"]+'
# Ignored characters (whitespace)
t_ccode_ignore = " \t\n"
# For bad characters, we just skip over it
def t_ccode_error(t):
t.lexer.skip(1)
def t_error(t):
t.lexer.skip(1)
lexer = lex.lex()
data = '''{ I DONT WANT TO RECEIVE THIS
{THIS IS WHAT I WANT TO SEE}
AS WELL AS I DONT WANT TO RECEIVE THIS}
OUTSIDE OF CURLY BRACES
{I WANT TO SEE THIS AGAIN}
'''
lexer.input(data)
for tok in lexer:
print(tok)
数据只是一个简单示例的测试字符串。
但是在我的 C 源文件中有一些我想提取的结构 Argument1, Argument2, Argument3, Argument4
。
显然那些 C 文件不会编译,但没有必要,因为它们包含在其他一些文件中。
感谢您的所有意见!
你的描述不是很清楚。你的例子似乎表明你想找到一个不包含任何子列表的大括号列表。这就是我要解决的问题。
请注意,通常不建议尝试在词法分析器中完成所有这些工作。词法分析器通常应该 return 简单的原子标记,将其留给解析器的语法来完成将标记组合成有用结构的工作。但是,如果我的用例正确,则可以使用词法分析器执行此操作。
你的代码决定是否 return 一个 CCODE 标记是基于当深度计数器到达右括号时它是否为 0。但这显然不是您想要的:您不关心大括号嵌套的深度;相反,当遇到右大括号时,您想知道它是否是最里面的大括号。你不需要一个堆栈,因为你只需要最后一次打开大括号读取的位置,而且你只需要在它未关闭时使用它。因此,每次看到左大括号时,都会设置最后一个大括号的位置,而当看到右大括号时,则检查是否设置了最后一个大括号的位置。如果是,您可以 return 从该位置开始的字符串并将最后一个左大括号位置设置为 None
。如果未设置,则继续扫描。
这是一个基于您的代码的简化示例:
import ply.lex as lex
# Declare the state
states = (
('ccode', 'exclusive'),
)
tokens = [
'TEXT',
'CCODE'
]
# Changed from t_ANY_TEXT because otherwise you get all the text inside
# braces as well. Perhaps that's what you wanted but it makes the output less
# clear.
def t_TEXT(t):
r'\w+'
t.value = str(t.value)
return t
# Match the first {. Enter ccode state.
def t_ccode(t):
r'\{'
t.lexer.current_open = t.lexer.lexpos # Record the starting position
t.lexer.level = 1 # Initial brace level
t.lexer.begin('ccode') # Enter 'ccode' state
# t_lbrace and t_rbrace deleted because they never match
# Rules for the ccode state
def t_ccode_lbrace(t):
r'\{'
t.lexer.current_open = t.lexer.lexpos
t.lexer.level += 1
def t_ccode_rbrace(t):
r'\}'
t.lexer.level -= 1
if t.lexer.level == 0:
t.lexer.begin('INITIAL')
if t.lexer.current_open is not None:
t.value = t.lexer.lexdata[t.lexer.current_open:t.lexer.lexpos - 1]
t.type = "CCODE"
t.lexer.current_open = None
return t
# C or C++ comment (ignore)
def t_ccode_comment(t):
r'(/\*(.|\n)*?\*/)|(//.*)'
# C string
def t_ccode_string(t):
r'\"([^\\n]|(\.))*?\"'
# C character literal
def t_ccode_char(t):
r'\'([^\\n]|(\.))*?\''
# Any sequence of non-whitespace characters (not braces, strings)
def t_ccode_nonspace(t):
r'''[^\s{}'"]+''' # No need to escape inside a character class
# Ignored characters (whitespace)
t_ccode_ignore = " \t\n"
# For bad characters, we just skip over it
def t_ccode_error(t):
t.lexer.skip(1)
def t_error(t):
t.lexer.skip(1)
lexer = lex.lex()
data = '''{ I DONT WANT TO RECEIVE THIS
{THIS IS WHAT I WANT TO SEE}
AS WELL AS I DONT WANT TO RECEIVE THIS}
OUTSIDE OF CURLY BRACES
{I WANT TO SEE THIS AGAIN}
'''
lexer.input(data)
for tok in lexer:
print(tok)
样本运行:
$ python3 nested_brace.py
LexToken(CCODE,'THIS IS WHAT I WANT TO SEE',1,58)
LexToken(TEXT,'OUTSIDE',1,102)
LexToken(TEXT,'OF',1,110)
LexToken(TEXT,'CURLY',1,113)
LexToken(TEXT,'BRACES',1,119)
LexToken(CCODE,'I WANT TO SEE THIS AGAIN',1,152)
我想用 PLY 解析一些 C 代码。 我要提取的内容如下:
{ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4}
这个结构可以隐藏在更多的花括号中。
{SOME, RANDOM, STUFF {ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4}, SOME, MORE, RANDOM, STUFF }
目前我可以对我想提取的结构进行 lex ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4
但前提是它是唯一匹配的。
{SOME, RANDOM, STUFF {ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4}, SOME, MORE, RANDOM, STUFF }{Argument1, Argument2, Argument3, Argument4}
这是我当前方法失败的地方,因为上面示例的词法分析输出将是:
ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4}, SOME, MORE, RANDOM, STUFF }{Argument1, Argument2, Argument3, Argument4
如何才能只收到以下内容:
ARGUMENT1, ARGUMENT2, ARGUMENT3, ARGUMENT4
Argument1, Argument2, Argument3, Argument4
简短说明:
我确实有一个条件词法分析器,它搜索左花括号以保存其位置。
对于每个新的左括号,我都会增加一个计数器。
对于每个右括号,我都会减少计数器。
如果计数器为零,我开始将 t.value
设置为从最新的左大括号到下一个右大括号的所有元素。
我想这应该适用于示例字符串中的多个命中。
在我看来,我未能从 ccode
状态切换回 initial
状态。
现在来看我的实际代码(在这个例子中我省略了花括号中的逗号,使我的编程更简单):
import ply.lex as lex
import ply.yacc as yacc
# Declare the state
states = (
('ccode', 'exclusive'),
)
tokens = [
'TEXT',
'CCODE'
]
# this saves all rbrace positions
# to get the inner curly brace construct you want to use first element
# text lib call should always be the inner curly brace construct
rbrace_positions = []
def t_ANY_TEXT(t):
r'\w+'
t.value = str(t.value)
return t
# Match the first {. Enter ccode state.
def t_ccode(t):
r'\{'
t.lexer.code_start = t.lexer.lexpos # Record the starting position
print(t.lexer.code_start)
t.lexer.level = 1 # Initial brace level
t.lexer.begin('ccode') # Enter 'ccode' state
def t_lbrace(t):
r'\{'
t.lexer.level += 1
def t_rbrace(t):
r'\}'
t.lexer.level -= 1
# Rules for the ccode state
def t_ccode_lbrace(t):
r'\{'
t.lexer.current_lbrace = t.lexer.lexpos
t.lexer.level += 1
def t_ccode_rbrace(t):
r'\}'
rbrace_positions.append(t.lexer.lexpos)
t.lexer.level -= 1
# If closing brace, return the code fragment
if t.lexer.level == 0:
t.value = t.lexer.lexdata[t.lexer.current_lbrace:rbrace_positions[0]-1]
t.type = "CCODE"
t.lexer.lineno += t.value.count('\n')
t.lexer.begin('INITIAL')
for _ in reversed(rbrace_positions):
rbrace_positions.pop()
return t
# C or C++ comment (ignore)
def t_ccode_comment(t):
r'(/\*(.|\n)*?\*/)|(//.*)'
pass
# C string
def t_ccode_string(t):
r'\"([^\\n]|(\.))*?\"'
# C character literal
def t_ccode_char(t):
r'\'([^\\n]|(\.))*?\''
# Any sequence of non-whitespace characters (not braces, strings)
def t_ccode_nonspace(t):
r'[^\s\{\}\'\"]+'
# Ignored characters (whitespace)
t_ccode_ignore = " \t\n"
# For bad characters, we just skip over it
def t_ccode_error(t):
t.lexer.skip(1)
def t_error(t):
t.lexer.skip(1)
lexer = lex.lex()
data = '''{ I DONT WANT TO RECEIVE THIS
{THIS IS WHAT I WANT TO SEE}
AS WELL AS I DONT WANT TO RECEIVE THIS}
OUTSIDE OF CURLY BRACES
{I WANT TO SEE THIS AGAIN}
'''
lexer.input(data)
for tok in lexer:
print(tok)
数据只是一个简单示例的测试字符串。
但是在我的 C 源文件中有一些我想提取的结构 Argument1, Argument2, Argument3, Argument4
。
显然那些 C 文件不会编译,但没有必要,因为它们包含在其他一些文件中。
感谢您的所有意见!
你的描述不是很清楚。你的例子似乎表明你想找到一个不包含任何子列表的大括号列表。这就是我要解决的问题。
请注意,通常不建议尝试在词法分析器中完成所有这些工作。词法分析器通常应该 return 简单的原子标记,将其留给解析器的语法来完成将标记组合成有用结构的工作。但是,如果我的用例正确,则可以使用词法分析器执行此操作。
你的代码决定是否 return 一个 CCODE 标记是基于当深度计数器到达右括号时它是否为 0。但这显然不是您想要的:您不关心大括号嵌套的深度;相反,当遇到右大括号时,您想知道它是否是最里面的大括号。你不需要一个堆栈,因为你只需要最后一次打开大括号读取的位置,而且你只需要在它未关闭时使用它。因此,每次看到左大括号时,都会设置最后一个大括号的位置,而当看到右大括号时,则检查是否设置了最后一个大括号的位置。如果是,您可以 return 从该位置开始的字符串并将最后一个左大括号位置设置为 None
。如果未设置,则继续扫描。
这是一个基于您的代码的简化示例:
import ply.lex as lex
# Declare the state
states = (
('ccode', 'exclusive'),
)
tokens = [
'TEXT',
'CCODE'
]
# Changed from t_ANY_TEXT because otherwise you get all the text inside
# braces as well. Perhaps that's what you wanted but it makes the output less
# clear.
def t_TEXT(t):
r'\w+'
t.value = str(t.value)
return t
# Match the first {. Enter ccode state.
def t_ccode(t):
r'\{'
t.lexer.current_open = t.lexer.lexpos # Record the starting position
t.lexer.level = 1 # Initial brace level
t.lexer.begin('ccode') # Enter 'ccode' state
# t_lbrace and t_rbrace deleted because they never match
# Rules for the ccode state
def t_ccode_lbrace(t):
r'\{'
t.lexer.current_open = t.lexer.lexpos
t.lexer.level += 1
def t_ccode_rbrace(t):
r'\}'
t.lexer.level -= 1
if t.lexer.level == 0:
t.lexer.begin('INITIAL')
if t.lexer.current_open is not None:
t.value = t.lexer.lexdata[t.lexer.current_open:t.lexer.lexpos - 1]
t.type = "CCODE"
t.lexer.current_open = None
return t
# C or C++ comment (ignore)
def t_ccode_comment(t):
r'(/\*(.|\n)*?\*/)|(//.*)'
# C string
def t_ccode_string(t):
r'\"([^\\n]|(\.))*?\"'
# C character literal
def t_ccode_char(t):
r'\'([^\\n]|(\.))*?\''
# Any sequence of non-whitespace characters (not braces, strings)
def t_ccode_nonspace(t):
r'''[^\s{}'"]+''' # No need to escape inside a character class
# Ignored characters (whitespace)
t_ccode_ignore = " \t\n"
# For bad characters, we just skip over it
def t_ccode_error(t):
t.lexer.skip(1)
def t_error(t):
t.lexer.skip(1)
lexer = lex.lex()
data = '''{ I DONT WANT TO RECEIVE THIS
{THIS IS WHAT I WANT TO SEE}
AS WELL AS I DONT WANT TO RECEIVE THIS}
OUTSIDE OF CURLY BRACES
{I WANT TO SEE THIS AGAIN}
'''
lexer.input(data)
for tok in lexer:
print(tok)
样本运行:
$ python3 nested_brace.py
LexToken(CCODE,'THIS IS WHAT I WANT TO SEE',1,58)
LexToken(TEXT,'OUTSIDE',1,102)
LexToken(TEXT,'OF',1,110)
LexToken(TEXT,'CURLY',1,113)
LexToken(TEXT,'BRACES',1,119)
LexToken(CCODE,'I WANT TO SEE THIS AGAIN',1,152)