Skip to content

Commit

Permalink
Add file name and line number info to exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
dgelessus committed Jul 14, 2017
1 parent ec0254b commit d180159
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 51 deletions.
17 changes: 16 additions & 1 deletion rezparser/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,22 @@


class RezParserError(Exception):
__slots__ = ()
__slots__ = ("message", "filename", "lineno")

def __init__(self, message, *, filename=None, lineno=None):
full_message = str(message)

if filename is not None:
full_message += f", in file {filename!r}"

if lineno is not None:
full_message += f", on line {lineno}"

super().__init__(full_message)

self.message = message
self.filename = filename
self.lineno = lineno


class Token(ply.lex.LexToken):
Expand Down
48 changes: 43 additions & 5 deletions rezparser/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ def lexpos(self, lexpos):
else:
self.lexer.lexpos = lexpos

@property
def filename(self):
if self.lexer is None:
return self._filename
else:
return self.lexer.filename

@filename.setter
def filename(self, filename):
if self.lexer is None:
self._filename = filename
else:
self.lexer.filename = filename

def __init__(self, lexer=None):
super().__init__()

Expand All @@ -66,7 +80,12 @@ def token(self):
return self.lexer.token()

def clone(self):
cloned = NoOpLexer(self.lexer.clone())
cloned = NoOpLexer(None if self.lexer is None else self.lexer.clone())
for attr in ("_lineno", "_lexpos", "_filename"):
try:
setattr(cloned, attr, getattr(self, attr))
except AttributeError:
pass
cloned.input(self.tokens)
return cloned

Expand Down Expand Up @@ -233,7 +252,12 @@ class RezLexer(object):
tokens += tuple("FUN_" + fun[2:].upper() for fun in rez_functions)

def t_error(self, t):
raise LexError(t)
try:
newline_pos = t.value.index("\n", 1)
except ValueError:
newline_pos = len(t.value)

raise LexError(repr(t.value[:newline_pos]), filename=self.filename, lineno=self.lineno)

_pp = r"(?m:^)[ \t]*\#[ \t]*"
_id = r"[A-Za-z_][A-Za-z0-9_]*"
Expand Down Expand Up @@ -360,7 +384,7 @@ def t_IDENTIFIER(self, t):
@ply.lex.TOKEN(r"\$\$"+_id)
def t_REZ_FUNCTION(self, t):
if t.value.lower() not in RezLexer.rez_functions:
raise LexError("Unknown Rez function: " + t.value)
raise LexError("Unknown Rez function: " + t.value, filename=self.filename, lineno=self.lineno)

t.type = "FUN_" + t.value[2:].upper()

Expand Down Expand Up @@ -443,11 +467,23 @@ def lexpos(self):
def lexpos(self, lexpos):
self.lexer.lexpos = lexpos

def __init__(self, *, _lexer=None, **kwargs):
@property
def filename(self):
try:
return self.lexer.filename
except AttributeError:
return None

@filename.setter
def filename(self, filename):
self.lexer.filename = filename

def __init__(self, *, filename="<input>", _lexer=None, **kwargs):
super().__init__()

if _lexer is None:
self.lexer = ply.lex.lex(module=self, lextab="_table_lexer", **kwargs)
self.filename = filename
else:
self.lexer = _lexer

Expand All @@ -462,4 +498,6 @@ def token(self):
return self.lexer.token()

def clone(self):
return RezLexer(_lexer=self.lexer.clone())
new_lexer = RezLexer(_lexer=self.lexer.clone())
new_lexer.filename = self.filename
return new_lexer
32 changes: 20 additions & 12 deletions rezparser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ class RezParser(object):

start = "Start symbol must be set manually"

def p_error(self, p):
raise ParseError(p)
def p_error(self, t):
raise ParseError(t, filename=t.lexer.filename, lineno=t.lineno)

def p_empty(self, p):
"""empty : """
Expand Down Expand Up @@ -132,7 +132,11 @@ def p_intlit(self, p):
"""

if p[1].startswith("'"):
value = int.from_bytes(_unescape_string(p[1][1:-1]), "big")
try:
unescaped = _unescape_string(p[1][1:-1])
except ValueError as e:
raise ParseError(str(e), filename=p[-1].lexer.filename, lineno=p[-1].lineno)
value = int.from_bytes(unescaped, "big")
elif p[1].startswith("$"):
value = int(p[1][1:], 16)
elif p[1].startswith("0X") or p[1].startswith("0x"):
Expand Down Expand Up @@ -514,7 +518,11 @@ def p_single_string(self, p):
if p[1].startswith("$"):
p[0] = ast.StringLiteral(value=bytes.fromhex(p[1][2:-1]))
else:
p[0] = ast.StringLiteral(value=_unescape_string(p[1][1:-1]))
try:
unescaped = _unescape_string(p[1][1:-1])
except ValueError as e:
raise ParseError(str(e), filename=p[-1].lexer.filename, lineno=p[-1].lineno)
p[0] = ast.StringLiteral(value=unescaped)
else:
p[0] = p[1]

Expand Down Expand Up @@ -972,13 +980,13 @@ def p_array_field(self, p):
for mod in p[1]:
mod = mod.lower()
if mod in seen:
raise ParseError(f"Duplicate attribute {mod!r}")
raise ParseError(f"Duplicate attribute {mod!r}", filename=p[-1].lexer.filename, lineno=p[-1].lineno)
seen.add(mod)

if mod == "wide":
wide = True
else:
raise ParseError(f"Unsupported modifier {mod!r} for type array")
raise ParseError(f"Unsupported modifier {mod!r} for type array", filename=p[-1].lexer.filename, lineno=p[-1].lineno)

if len(p) > 9:
p[0] = ast.ArrayField(wide=wide, label=None, count=p[4], fields=p[7])
Expand Down Expand Up @@ -1038,29 +1046,29 @@ def p_field(self, p):
for mod in modifiers:
mod = mod.lower()
if mod in seen:
raise ParseError(f"Duplicate attribute {mod!r}")
raise ParseError(f"Duplicate attribute {mod!r}", filename=p[-1].lexer.filename, lineno=p[-1].lineno)
seen.add(mod)

if mod == "key":
is_key = True
elif mod == "unsigned":
if typename not in ("bitstring", "byte", "integer", "longint"):
raise ParseError(f"Unsupported modifier {mod!r} for type {typename!r}")
raise ParseError(f"Unsupported modifier {mod!r} for type {typename!r}", filename=p[-1].lexer.filename, lineno=p[-1].lineno)

signed = False
elif mod in ("binary", "octal", "decimal", "hex", "literal"):
if mod == "hex" and typename == "string":
# Special case: hex is allowed on string
pass
elif typename not in ("bitstring", "byte", "integer", "longint"):
raise ParseError(f"Unsupported modifier {mod!r} for type {typename!r}")
raise ParseError(f"Unsupported modifier {mod!r} for type {typename!r}", filename=p[-1].lexer.filename, lineno=p[-1].lineno)

if base is None:
base = mod
else:
raise ParseError(f"Duplicate base modifier {mod!r} (base was previously set to {base!r}")
raise ParseError(f"Duplicate base modifier {mod!r} (base was previously set to {base!r}", filename=p[-1].lexer.filename, lineno=p[-1].lineno)
else:
raise ParseError(f"Invalid modifier: {mod!r}")
raise ParseError(f"Invalid modifier: {mod!r}", filename=p[-1].lexer.filename, lineno=p[-1].lineno)

if typename == "int":
typename = "integer"
Expand All @@ -1087,7 +1095,7 @@ def p_field(self, p):
elif typename == "rect":
fieldtype = ast.RectFieldType()
else:
raise ParseError(f"Unknown field type {typename!r}")
raise ParseError(f"Unknown field type {typename!r}", filename=p[-1].lexer.filename, lineno=p[-1].lineno)

if value_or_symconsts is None:
value = None
Expand Down
Loading

0 comments on commit d180159

Please sign in to comment.