Python PLY: получение синтаксической ошибки в каждой строке ввода - PullRequest
0 голосов
/ 28 февраля 2019

Работая над написанием компилятора для конструкции цикла for C. Однако я все еще застрял на предварительной задаче синтаксического анализа начальной части программы на C, а именно заголовочных файлов, которые необходимо включить, и основной функции.

Вот мой код:

import ply.lex as lex
import ply.yacc as yacc
tokens = ('HASH','INCLUDE','HEADER_FILE','MAIN','FLOW_OPEN','FLOW_CLOSE','SEMI_COLON','TYPE','SMALL_OPEN','SMALL_CLOSE','OTHERS')

t_HASH = r'\#'
t_INCLUDE = r'include'
t_HEADER_FILE = r'<stdio.h>'
t_MAIN = r'main' 
t_FLOW_OPEN = r'{'
t_FLOW_CLOSE = r'}'
t_SMALL_OPEN = r'\('
t_SMALL_CLOSE = r'\)'
t_SEMI_COLON = r';'
t_OTHERS = r'[a-zA-Z][a-zA-Z]*'
t_TYPE = r'int|void'

def t_error(token):
    print(f'Illegal character: {token.value}')

def t_whitespace(t):
    r'\s+'
    pass

def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)

lexer = lex.lex()
#Building the parser

def p_expression_start(p):
    'expression : header body'

def p_header(p):
    'header : HASH INCLUDE HEADER_FILE'

def p_body(p):
    'body : main rest'

def p_main(p):
    'main : TYPE MAIN SMALL_OPEN SMALL_CLOSE'

def p_rest(p):
    'rest : FLOW_OPEN st FLOW_CLOSE'

def p_st(p):
    ''''
        st : OTHERS st
            | end
        '''
def p_end(p): #Empty production
    'end : SEMI_COLON' 

def p_error(p):
    print("Syntax error in input!")

parser = yacc.yacc(method='LALR',debug=True)

with open(r'forparsing.txt','r') as file:
    while True:
        try:
            line = next(file)
            print('Parsing')
            parser.parse(line)
        except:
            print('Finished')
            break

И ввод, который я даю:

# include <stdio.h>
void main()
{
 abc;
 }

Но при запуске программы я получаю синтаксическую ошибку в каждой строке.Что может быть не так здесь.Насколько я понимаю, синтаксический анализатор не может извлечь начальный символ из заданного ввода, но я не знаю, как это исправить.В общем, как мне отладить синтаксические ошибки с PLY?

1 Ответ

0 голосов
/ 28 февраля 2019

Ни одна из ваших строк ввода не является синтаксически действительной сама по себе.Они формируют синтаксически допустимую программу только при анализе в целом.Поэтому вам нужно вызывать parse один раз для строки, содержащей всю программу, а не один раз для каждой строки.

Это можно сделать, просто вызвав file.read() в своем коде обработки файлов вместо использования while loop.


Синтаксическая ошибка, с которой вы сталкиваетесь после исправления, связана с тем, как перекрывающиеся лексические правила обрабатываются в PLY.В здравомыслящих генераторах лексеров побеждает правило, дающее самое длинное совпадение, и, если оба дают одинаковое совпадение, побеждает то, которое стоит первым в коде.Однако в PLY побеждает тот, у кого самое длинное регулярное выражение.Из-за этого поведения нельзя использовать отдельные правила для сопоставления идентификаторов и ключевых слов с помощью PLY.В этом случае правило t_OTHERS используется, даже если, скажем, t_INCLUDE также совпадает.

Вместо в документации PLY рекомендуется следующий способ сопоставления идентификаторов и ключевых слов:

Чтобы обработать зарезервированные слова, вы должны написать одно правило для соответствия идентификатору и выполнить специальный поиск имени в функции, подобной этой:

 reserved = {
    'if' : 'IF',
    'then' : 'THEN',
    'else' : 'ELSE',
    'while' : 'WHILE',
    ...
 }

 tokens = ['LPAREN','RPAREN',...,'ID'] + list(reserved.values())

 def t_ID(t):
     r'[a-zA-Z_][a-zA-Z_0-9]*'
     t.type = reserved.get(t.value,'ID')    # Check for reserved words
     return t

Этот подход значительно сокращает количествоправила регулярных выражений, которые могут немного ускорить процесс.

Примечание. Следует избегать написания отдельных правил для зарезервированных слов.Например, если вы напишите правила, подобные этим,

 t_FOR   = r'for'
 t_PRINT = r'print'

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

Опять же, следует отметить, что ни одна из упомянутых проблем не существует в генераторах лексеров, которые используют правило максимального жука.


В общем, как отлаживать проблемы синтаксических ошибок с PLY?

Первым шагом будет изменение p_error для вывода некоторой полезной информации (например, какой тип токенав какой строке вызвала синтаксическая ошибка) вот так:

def p_error(p):
    if p == None:
        token = "end of file"
    else:
        token = f"{p.type}({p.value}) on line {p.lineno}"

    print(f"Syntax error: Unexpected {token}")
...