Я новичок в мире лексизма и синтаксического анализа, поэтому я надеюсь, что эту проблему легко решить.Я пытаюсь проанализировать файл с группами токенов, которые относятся к разным типам, с помощью 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"
Я получил SyntaxError
для токена сразу после первого NEWLINE
,Каким-то образом синтаксический анализатор не знает, чтобы начать синтаксический анализ новой команды после того, как он видит шаблон, соответствующий 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
""")