Разбор командных строк, определенных в строках с PLY - PullRequest
0 голосов
/ 25 мая 2018

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

1 Ответ

0 голосов
/ 25 мая 2018

Проблема была вызвана тем, что первое правило является особенным: именно здесь запускается парсер.Так как первое вышеприведенное правило не может объединить две команды (найденные в двух смежных строках), оно терпит неудачу.

Я исправил его, добавив новое корневое правило выше 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])

(я также добавил в класс поле commands для хранения проанализированных команд)

Это может обрабатывать несколько команд, которые «объединяются» вместе, как это найдено в моем входном файле.

...