Search code examples
pythonparsingyacclexerply

ply.yacc error: 'ERROR: no rules of the form p_rulename are defined'


I am coding a parser to c-minus language. Lexer is ready and working properly, so I began develop parser but I can't pass from the first part: i am receiving an error that don't let me move forward, because I can se whats is right and what is wrong, I only see this error reproduced below. I try to change parser builder but still don't work.

This code below is Lexer that is working. It builds a lexer that identifies all symbols of grammar.

reserved = {
    'else' : 'ELSE',
    'if' : 'IF',
    'int' : 'INT',
    'return' : 'RETURN',
    'void' : 'VOID',
    'while' : 'WHILE'
}

tokens = [
    'ID',
    'NUM',
    'PLUS',
    'MINUS',
    'MULT',
    'DIV',
    'LESS',
    'LESSOREQUAL',
    'GREAT',
    'GREATOREQUAL',
    'DOUBLEEQUAL',
    'NOTEQUAL',
    'EQUAL',
    'SEMICOLON',
    'COLON',
    'LPAREN',
    'RPAREN',
    'LBRACKET',
    'RBRACKET',
    'LKEY',
    'RKEY',
    'COMENT'
] + list(reserved.values())

def t_COMENT(t):
    r'/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/'
    return t

def t_ID(t):
    r'[a-zA-Z]+'
    return t

def t_NUM(t):
    r'[0-9]+'
    return t

def t_PLUS(t):
    r'\+'
    return t

def t_MINUS(t):
    r'\-'
    return t

def t_MULT(t):
    r'\*'
    return t

def t_DIV(t):
    r'\/'
    return t

def t_LESS(t):
    r'\<'
    return t

def t_LESSOREQUAL(t):
    r'\<\='
    return t

def t_GREAT(t):
    r'\>'
    return t

def t_GREATOREQUAL(t):
    r'\>\='
    return t

def t_DOUBLEEQUAL(t):
    r'\=\='
    return t

def t_NOTEQUAL(t):
    r'\!\='
    return t

def t_EQUAL(t):
    r'\='
    return t

def t_SEMICOLON(t):
    r'\;'
    return t

def t_COLON(t):
    r'\,'
    return t

def t_LPAREN(t):
    r'\('
    return t

def t_RPAREN(t):
    r'\)'
    return t

def t_LBRACKET(t):
    r'\['
    return t

def t_RBRACKET(t):
    r'\]'
    return t

def t_LKEY(t):
    r'\{'
    return t

def t_RKEY(t):
    r'\}'
    return t

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

def t_error(t):
    print("ERROR: Illegal character '{0}' at line {1}".format(t.value[0], t.lineno))
    t.lexer.skip(1)

t_ignore  = ' \t'

This code below is Parser that is still in development. But I can't test none of the functions I am creating because the error that is ocurring.

import ply.yacc as yacc
import lexer

tokens = lexer.tokens

class Parser():

    def p_program(p):
        'program: declaration_list'
        p[0] = p[1]

    def p_declaration_list(p):
        '''declaration_list: declaration_list declaration
                           | declaration'''
        p[0] = (0, (p[1], 0))

Main:

import ply.yacc as yacc
import ply.lex as lex
from tabulate import tabulate
import sys
from lexer import *
from parser import Parser

lexer = lex.lex()

with open(sys.argv[1], 'r') as f:
    lexer.input(f.read())
    tok_array = [[tok.type, tok.value, tok.lexpos, tok.lineno] for tok in lexer]
    print(tabulate(tok_array, headers=['Tipo','Valor','Posição','Linha']),'\n')

print('passou aqui 1')
parser = yacc.yacc()

with open(sys.argv[1], 'r') as f:
    parser.input(f.read())
    tok_array = [[tok.type, tok.value, tok.lexpos, tok.lineno] for tok in parser]
    print(tabulate(tok_array, headers=['Tipo','Valor','Posição','Linha']),'\n')

Below is the complete error:

ERROR: no rules of the form p_rulename are defined
Traceback (most recent call last):
  File "main.py", line 16, in <module>
    parser = yacc.yacc()
  File "/home/tlunafar/.local/lib/python3.8/site-packages/ply/yacc.py", line 3323, in yacc
    raise YaccError('Unable to build parser')
ply.yacc.YaccError: Unable to build parser
```

Here is the program in c-minus that i am testing:
```
int gcd(int u) {
    if (v == 0) return u;
    &
    else return gcd(v, u-u/v*v);
    /* comment */
}
```

Where exactly is this error? Can anybody show me?

Solution

  • If you put the parser definitions into a class or you try to build the parser from a different module, you need to use the module= parameter to tell yacc where the rules are. Otherwise, it can't find them and you get an error saying that no rules were found. So instead of parser = yacc.yacc(), you need:

    parser = yacc.yacc(module=Parser)
    

    Note that all of the parser rules need to be in the same namespace; that includes the definition of tokens. So you'll need to put that inside the class:

    class Parser():
        tokens = lexer.tokens
        # ...
    

    Also, Ply insists that productions be written with whitespace on both sides of the colon, so you'll have to fix that. And there are a number of other errors; notably, parsers are not called the same way as lexers; they don't return a generator of tokens. Generally, you only call the parser once to parse the entire input. The details are in the Ply manual.