问题描述
{ARGUMENT1,ARGUMENT2,ARGUMENT3,ARGUMENT4}
此结构可以隐藏在更多的花括号中。
{SOME,RANDOM,STUFF {ARGUMENT1,ARGUMENT4},SOME,MORE,STUFF }
目前,我能够为要提取的ARGUMENT1,ARGUMENT4
提取词法,但前提是要唯一匹配。
{SOME,STUFF }{Argument1,Argument2,Argument3,Argument4}
ARGUMENT1,Argument4
我如何只收到以下信息:
ARGUMENT1,ARGUMENT4
Argument1,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,Argument4
的构造。
显然,这些C文件无法编译,但由于它们包含在其他文件中,因此无需编译。
感谢您的所有投入!
解决方法
您的描述并不十分清楚。您的示例似乎表明您要查找不包含任何子列表的支撑列表。这就是我要解决的问题。
请注意,通常不建议尝试在lexer中完成所有这些工作。 Lexers通常应该返回简单的原子标记,将其留给解析器的语法来完成将标记放在一起成为有用结构的工作。但是,如果我的用例正确无误,则可以使用lexer做到这一点。
您的代码根据深度计数器在碰到大括号时是否为0来决定是否返回CCODE令牌。但这显然不是您想要的:您不在乎括号的嵌套深度;相反,当遇到闭合括号时,您想知道它是否是最里面的括号。您不需要堆栈,因为您只需要最后一个打开的括号读取的位置,并且只在未关闭括号时才需要。因此,每次看到开口撑杆时,都要设置最后一个开口撑杆位置,当看到闭合撑杆时,请检查是否设置了最后一个开口撑杆位置。如果是,则可以返回该位置之后的字符串,并将最后一个右括号位置设置为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',102)
LexToken(TEXT,'OF',110)
LexToken(TEXT,'CURLY',113)
LexToken(TEXT,'BRACES',119)
LexToken(CCODE,'I WANT TO SEE THIS AGAIN',152)