Improved error checking and messages
This commit is contained in:
parent
16d700b310
commit
0ac3c1a0a3
40
sly/yacc.py
40
sly/yacc.py
@ -103,6 +103,7 @@ class YaccSymbol:
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
class YaccProduction:
|
class YaccProduction:
|
||||||
|
__slots__ = ('_slice', '_namemap', '_stack')
|
||||||
def __init__(self, s, stack=None):
|
def __init__(self, s, stack=None):
|
||||||
self._slice = s
|
self._slice = s
|
||||||
self._namemap = { }
|
self._namemap = { }
|
||||||
@ -147,7 +148,8 @@ class YaccProduction:
|
|||||||
if name in self._namemap:
|
if name in self._namemap:
|
||||||
return self._slice[self._namemap[name]].value
|
return self._slice[self._namemap[name]].value
|
||||||
else:
|
else:
|
||||||
raise AttributeError(f'No symbol {name}')
|
nameset = '{' + ', '.join(self._namemap) + '}'
|
||||||
|
raise AttributeError(f'No symbol {name}. Must be one of {nameset}.')
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
if name[0:1] == '_' or name not in self._namemap:
|
if name[0:1] == '_' or name not in self._namemap:
|
||||||
@ -389,7 +391,7 @@ class Grammar(object):
|
|||||||
if term in self.Precedence:
|
if term in self.Precedence:
|
||||||
raise GrammarError(f'Precedence already specified for terminal {term!r}')
|
raise GrammarError(f'Precedence already specified for terminal {term!r}')
|
||||||
if assoc not in ['left', 'right', 'nonassoc']:
|
if assoc not in ['left', 'right', 'nonassoc']:
|
||||||
raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'")
|
raise GrammarError(f"Associativity of {term!r} must be one of 'left','right', or 'nonassoc'")
|
||||||
self.Precedence[term] = (assoc, level)
|
self.Precedence[term] = (assoc, level)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@ -1562,6 +1564,8 @@ class ParserMetaDict(dict):
|
|||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
if key in self and callable(value) and hasattr(value, 'rules'):
|
if key in self and callable(value) and hasattr(value, 'rules'):
|
||||||
value.next_func = self[key]
|
value.next_func = self[key]
|
||||||
|
if not hasattr(value.next_func, 'rules'):
|
||||||
|
raise GrammarError(f'Redefinition of {key}. Perhaps an earlier {key} is missing @_')
|
||||||
super().__setitem__(key, value)
|
super().__setitem__(key, value)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
@ -1659,11 +1663,10 @@ class Parser(metaclass=ParserMeta):
|
|||||||
Build the grammar from the grammar rules
|
Build the grammar from the grammar rules
|
||||||
'''
|
'''
|
||||||
grammar_rules = []
|
grammar_rules = []
|
||||||
fail = False
|
errors = ''
|
||||||
# Check for non-empty symbols
|
# Check for non-empty symbols
|
||||||
if not rules:
|
if not rules:
|
||||||
cls.log.error('no grammar rules are defined')
|
raise YaccError('No grammar rules are defined')
|
||||||
return False
|
|
||||||
|
|
||||||
grammar = Grammar(cls.tokens)
|
grammar = Grammar(cls.tokens)
|
||||||
|
|
||||||
@ -1672,8 +1675,7 @@ class Parser(metaclass=ParserMeta):
|
|||||||
try:
|
try:
|
||||||
grammar.set_precedence(term, assoc, level)
|
grammar.set_precedence(term, assoc, level)
|
||||||
except GrammarError as e:
|
except GrammarError as e:
|
||||||
cls.log.error(str(e))
|
errors += f'{e}\n'
|
||||||
fail = True
|
|
||||||
|
|
||||||
for name, func in rules:
|
for name, func in rules:
|
||||||
try:
|
try:
|
||||||
@ -1682,21 +1684,17 @@ class Parser(metaclass=ParserMeta):
|
|||||||
try:
|
try:
|
||||||
grammar.add_production(prodname, syms, pfunc, rulefile, ruleline)
|
grammar.add_production(prodname, syms, pfunc, rulefile, ruleline)
|
||||||
except GrammarError as e:
|
except GrammarError as e:
|
||||||
cls.log.error(str(e))
|
errors += f'{e}\n'
|
||||||
fail = True
|
|
||||||
except SyntaxError as e:
|
except SyntaxError as e:
|
||||||
cls.log.error(str(e))
|
errors += f'{e}\n'
|
||||||
fail = True
|
|
||||||
try:
|
try:
|
||||||
grammar.set_start(getattr(cls, 'start', None))
|
grammar.set_start(getattr(cls, 'start', None))
|
||||||
except GrammarError as e:
|
except GrammarError as e:
|
||||||
cls.log.error(str(e))
|
errors += f'{e}\n'
|
||||||
fail = True
|
|
||||||
|
|
||||||
undefined_symbols = grammar.undefined_symbols()
|
undefined_symbols = grammar.undefined_symbols()
|
||||||
for sym, prod in undefined_symbols:
|
for sym, prod in undefined_symbols:
|
||||||
cls.log.error(f'%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym)
|
errors += '%s:%d: Symbol %r used, but not defined as a token or a rule\n' % (prod.file, prod.line, sym)
|
||||||
fail = True
|
|
||||||
|
|
||||||
unused_terminals = grammar.unused_terminals()
|
unused_terminals = grammar.unused_terminals()
|
||||||
if unused_terminals:
|
if unused_terminals:
|
||||||
@ -1723,16 +1721,15 @@ class Parser(metaclass=ParserMeta):
|
|||||||
|
|
||||||
infinite = grammar.infinite_cycles()
|
infinite = grammar.infinite_cycles()
|
||||||
for inf in infinite:
|
for inf in infinite:
|
||||||
cls.log.error('Infinite recursion detected for symbol %r', inf)
|
errors += 'Infinite recursion detected for symbol %r\n' % inf
|
||||||
fail = True
|
|
||||||
|
|
||||||
unused_prec = grammar.unused_precedence()
|
unused_prec = grammar.unused_precedence()
|
||||||
for term, assoc in unused_prec:
|
for term, assoc in unused_prec:
|
||||||
cls.log.error('Precedence rule %r defined for unknown symbol %r', assoc, term)
|
errors += 'Precedence rule %r defined for unknown symbol %r\n' % (assoc, term)
|
||||||
fail = True
|
|
||||||
|
|
||||||
cls._grammar = grammar
|
cls._grammar = grammar
|
||||||
return not fail
|
if errors:
|
||||||
|
raise YaccError('Unable to build grammar.\n'+errors)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def __build_lrtables(cls):
|
def __build_lrtables(cls):
|
||||||
@ -1786,8 +1783,7 @@ class Parser(metaclass=ParserMeta):
|
|||||||
raise YaccError('Invalid parser specification')
|
raise YaccError('Invalid parser specification')
|
||||||
|
|
||||||
# Build the underlying grammar object
|
# Build the underlying grammar object
|
||||||
if not cls.__build_grammar(rules):
|
cls.__build_grammar(rules)
|
||||||
raise YaccError('Invalid grammar')
|
|
||||||
|
|
||||||
# Build the LR tables
|
# Build the LR tables
|
||||||
if not cls.__build_lrtables():
|
if not cls.__build_lrtables():
|
||||||
|
Loading…
Reference in New Issue
Block a user