解析在 PLY 行上定义的命令字符串
Parsing command strings defined on lines with PLY
我是词法分析领域的新手,所以我希望这是一个容易解决的问题。我正在尝试使用 Python 的 PLY:
来解析包含属于不同类型的令牌组的文件
STRING STRING QUANTITY STRING STRING # TypeA
STRING STRING STRING STRING STRING STRING # TypeB
STRING STRING QUANTITY QUANTITY QUANTITY # TypeC
每一行都应该是我的程序可以理解的一种命令。例如,我们调用第一行TypeA
、第二行TypeB
中定义的类型,依此类推。由于每行应该有一个命令,因此每行末尾的 NEWLINE
标记表示命令结束。我成功地使用以下词法分析器对文件进行了标记化:
# top level tokens
tokens = [
'QUANTITY',
'STRING',
'NEWLINE'
]
# number, possibly in exponential notion, e.g. -1.5e-3.0, or SI suffix, e.g. 'k'
t_QUANTITY = r'[+-]?(\d+\.\d*|\d*\.\d+|\d+)([eE][+-]?\d*\.?\d*|[GMkmunpf])?'
# any group of 2 or more alphanumeric characters, with the first being a letter
t_STRING = r'[a-zA-Z_][a-zA-Z_0-9]*'
# ignore spaces and tabs
t_ignore = ' \t'
# ignore comments
t_ignore_COMMENT = r'\#.*'
# detect new lines
def t_newline(t):
r'\n+'
# generate newline token
t.type = "NEWLINE"
return t
我想编写一个解析器,将每个匹配的命令解析为不同的对象。我最终应该得到一个已解析对象的列表。
我尝试构建以下规则:
def p_command(self, p):
'''command : tokens NEWLINE
| NEWLINE'''
print("found command:", list(p))
def p_tokens(self, p):
'''tokens : type_a_tokens
| type_b_tokens
| type_c_tokens'''
p[0] = p[1]
def p_type_a_tokens(self, p):
'''type_a_tokens : STRING STRING QUANTITY STRING STRING'''
p[0] = "TypeA"
def p_type_b_tokens(self, p):
'''type_b_tokens : STRING STRING STRING STRING STRING STRING'''
p[0] = "TypeB"
def p_type_c_tokens(self, p):
'''type_c_tokens : STRING STRING QUANTITY QUANTITY QUANTITY'''
p[0] = "TypeC"
我在第一个 NEWLINE
之后立即得到一个 SyntaxError
作为令牌。不知何故,解析器在看到与 p_type_a_tokens
.
匹配的模式后不知道开始解析新命令
任何人都可以阐明什么应该是一组非常简单的解析规则吗?虽然 PLY 的文档通常非常好,但到目前为止我发现的所有示例都是针对计算器或编程语言的,其中换行符等内容不适用。
完整来源:
from ply import lex, yacc
class InputParser(object):
# top level tokens
tokens = [
'QUANTITY',
'STRING',
'NEWLINE'
]
t_QUANTITY = r'[+-]?(\d+\.\d*|\d*\.\d+|\d+)([eE][+-]?\d*\.?\d*|[GMkmunpf])?'
t_STRING = r'[a-zA-Z_][a-zA-Z_0-9]*'
# ignored characters
t_ignore = ' \t'
# ignore comments
t_ignore_COMMENT = r'\#.*'
def __init__(self, **kwargs):
self.lexer = lex.lex(module=self, **kwargs)
self.parser = yacc.yacc(module=self, **kwargs)
# detect new lines
def t_newline(self, t):
r'\n+'
# generate newline token
t.type = "NEWLINE"
# error handling
def t_error(self, t):
# anything that gets past the other filters
print("Illegal character '%s' on line %i at position %i" %
(t.value[0], self.lexer.lineno))
# skip forward a character
t.lexer.skip(1)
# match commands on their own lines
def p_command(self, p):
'''command : tokens NEWLINE
| NEWLINE'''
print("found command:", list(p))
p[0] = p[1]
def p_tokens(self, p):
'''tokens : type_a_tokens
| type_b_tokens
| type_c_tokens'''
p[0] = p[1]
def p_type_a_tokens(self, p):
'''type_a_tokens : STRING STRING QUANTITY STRING STRING'''
print("found type a")
p[0] = "TypeA"
def p_type_b_tokens(self, p):
'''type_b_tokens : STRING STRING STRING STRING STRING STRING'''
print("found type b")
p[0] = "TypeB"
def p_type_c_tokens(self, p):
'''type_c_tokens : STRING STRING QUANTITY QUANTITY QUANTITY'''
print("found type c")
p[0] = "TypeC"
def p_error(self, p):
if p:
error_msg = "syntax error '%s'" % p.value
else:
error_msg = "syntax error at end of file"
print(error_msg)
def parse(self, text):
self.parser.parse(text, lexer=self.lexer)
if __name__ == "__main__":
parser = InputParser()
parser.parse("""
a b 5.5 c d # TypeA
e f 1.6 g h # TypeA
i j k l m n # TypeB
# empty line
o p -1 2.0 3e4 # TypeC
""")
问题是由于第一条规则很特殊:这是解析器的起点。由于上面的第一条规则不能组合两个命令(在两个相邻的行上找到),所以它失败了。
我通过在 p_command
之上添加一个新的根规则来修复它,它可以采用单个 command
(当文件只包含一个命令时)或命令列表(command_list
):
def p_command_list(self, p):
'''command_list : command
| command_list command'''
if len(p) == 3:
self.commands.append(p[2])
else:
self.commands.append(p[1])
(我还在 class 中添加了一个 commands
字段来保存解析的命令)
这可以处理 "merged" 多个命令,正如在我的输入文件中找到的那样。
我是词法分析领域的新手,所以我希望这是一个容易解决的问题。我正在尝试使用 Python 的 PLY:
来解析包含属于不同类型的令牌组的文件STRING STRING QUANTITY STRING STRING # TypeA
STRING STRING STRING STRING STRING STRING # TypeB
STRING STRING QUANTITY QUANTITY QUANTITY # TypeC
每一行都应该是我的程序可以理解的一种命令。例如,我们调用第一行TypeA
、第二行TypeB
中定义的类型,依此类推。由于每行应该有一个命令,因此每行末尾的 NEWLINE
标记表示命令结束。我成功地使用以下词法分析器对文件进行了标记化:
# top level tokens
tokens = [
'QUANTITY',
'STRING',
'NEWLINE'
]
# number, possibly in exponential notion, e.g. -1.5e-3.0, or SI suffix, e.g. 'k'
t_QUANTITY = r'[+-]?(\d+\.\d*|\d*\.\d+|\d+)([eE][+-]?\d*\.?\d*|[GMkmunpf])?'
# any group of 2 or more alphanumeric characters, with the first being a letter
t_STRING = r'[a-zA-Z_][a-zA-Z_0-9]*'
# ignore spaces and tabs
t_ignore = ' \t'
# ignore comments
t_ignore_COMMENT = r'\#.*'
# detect new lines
def t_newline(t):
r'\n+'
# generate newline token
t.type = "NEWLINE"
return t
我想编写一个解析器,将每个匹配的命令解析为不同的对象。我最终应该得到一个已解析对象的列表。
我尝试构建以下规则:
def p_command(self, p):
'''command : tokens NEWLINE
| NEWLINE'''
print("found command:", list(p))
def p_tokens(self, p):
'''tokens : type_a_tokens
| type_b_tokens
| type_c_tokens'''
p[0] = p[1]
def p_type_a_tokens(self, p):
'''type_a_tokens : STRING STRING QUANTITY STRING STRING'''
p[0] = "TypeA"
def p_type_b_tokens(self, p):
'''type_b_tokens : STRING STRING STRING STRING STRING STRING'''
p[0] = "TypeB"
def p_type_c_tokens(self, p):
'''type_c_tokens : STRING STRING QUANTITY QUANTITY QUANTITY'''
p[0] = "TypeC"
我在第一个 NEWLINE
之后立即得到一个 SyntaxError
作为令牌。不知何故,解析器在看到与 p_type_a_tokens
.
任何人都可以阐明什么应该是一组非常简单的解析规则吗?虽然 PLY 的文档通常非常好,但到目前为止我发现的所有示例都是针对计算器或编程语言的,其中换行符等内容不适用。
完整来源:
from ply import lex, yacc
class InputParser(object):
# top level tokens
tokens = [
'QUANTITY',
'STRING',
'NEWLINE'
]
t_QUANTITY = r'[+-]?(\d+\.\d*|\d*\.\d+|\d+)([eE][+-]?\d*\.?\d*|[GMkmunpf])?'
t_STRING = r'[a-zA-Z_][a-zA-Z_0-9]*'
# ignored characters
t_ignore = ' \t'
# ignore comments
t_ignore_COMMENT = r'\#.*'
def __init__(self, **kwargs):
self.lexer = lex.lex(module=self, **kwargs)
self.parser = yacc.yacc(module=self, **kwargs)
# detect new lines
def t_newline(self, t):
r'\n+'
# generate newline token
t.type = "NEWLINE"
# error handling
def t_error(self, t):
# anything that gets past the other filters
print("Illegal character '%s' on line %i at position %i" %
(t.value[0], self.lexer.lineno))
# skip forward a character
t.lexer.skip(1)
# match commands on their own lines
def p_command(self, p):
'''command : tokens NEWLINE
| NEWLINE'''
print("found command:", list(p))
p[0] = p[1]
def p_tokens(self, p):
'''tokens : type_a_tokens
| type_b_tokens
| type_c_tokens'''
p[0] = p[1]
def p_type_a_tokens(self, p):
'''type_a_tokens : STRING STRING QUANTITY STRING STRING'''
print("found type a")
p[0] = "TypeA"
def p_type_b_tokens(self, p):
'''type_b_tokens : STRING STRING STRING STRING STRING STRING'''
print("found type b")
p[0] = "TypeB"
def p_type_c_tokens(self, p):
'''type_c_tokens : STRING STRING QUANTITY QUANTITY QUANTITY'''
print("found type c")
p[0] = "TypeC"
def p_error(self, p):
if p:
error_msg = "syntax error '%s'" % p.value
else:
error_msg = "syntax error at end of file"
print(error_msg)
def parse(self, text):
self.parser.parse(text, lexer=self.lexer)
if __name__ == "__main__":
parser = InputParser()
parser.parse("""
a b 5.5 c d # TypeA
e f 1.6 g h # TypeA
i j k l m n # TypeB
# empty line
o p -1 2.0 3e4 # TypeC
""")
问题是由于第一条规则很特殊:这是解析器的起点。由于上面的第一条规则不能组合两个命令(在两个相邻的行上找到),所以它失败了。
我通过在 p_command
之上添加一个新的根规则来修复它,它可以采用单个 command
(当文件只包含一个命令时)或命令列表(command_list
):
def p_command_list(self, p):
'''command_list : command
| command_list command'''
if len(p) == 3:
self.commands.append(p[2])
else:
self.commands.append(p[1])
(我还在 class 中添加了一个 commands
字段来保存解析的命令)
这可以处理 "merged" 多个命令,正如在我的输入文件中找到的那样。