Python PLY:在每一行输入中获取语法错误

时间:2019-02-28 19:19:34

标签: python compiler-construction ply

正在为C的for循环结构编写编译器。但是,Im仍然停留在解析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 :(得分:1)

您自己的输入行在语法上均无效。它们仅在整体上解析时才能形成语法上有效的程序。因此,您需要使用包含整个程序的字符串调用parse一次,而不是每行一次。

您可以通过在文件处理代码中调用file.read()而不是使用while循环来实现此目的。


解决此问题后遇到的语法错误是由于在PLY中处理重叠词法规则的方式引起的。在理智的词法生成器中,产生最长匹配项的规则将获胜;如果两者产生相同的匹配项,则该规则中的第一位将获胜。但是,在PLY中,正则表达式最长的那个赢了。由于这种行为,您不能使用单独的规则来使用PLY匹配标识符和关键字。在这种情况下,即使t_OTHERS也匹配,也会使用t_INCLUDE规则。

相反,the PLY documentation建议采用以下方式来匹配标识符和关键字:

  

要处理保留字,您应该编写一条规则以匹配   标识符并在像这样的函数中进行特殊的名称查找:

 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'
     

这些规则将针对包含这些单词的标识符触发   作为前缀,例如“忘记”或“已打印”。这可能不是   你想要的。

同样,应该指出的是,使用最大munch规则的词法生成器中没有提到的任何问题。


  

通常,如何调试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}")