Adding error handling support
This commit is contained in:
parent
d73a5be3f5
commit
e2e058c8ac
14
examples/errors.spf
Normal file
14
examples/errors.spf
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
blop x = blop #SyntaxError
|
||||||
|
|
||||||
|
# afficher y; #UnknownVariable
|
||||||
|
|
||||||
|
# texte z;
|
||||||
|
# afficher z; #UninitializedVariable
|
||||||
|
|
||||||
|
# texte a;
|
||||||
|
# texte a; #AlreadyDefined
|
||||||
|
|
||||||
|
# entier b = "test"; #IncompatibleType
|
||||||
|
|
||||||
|
# liste c = [0, 1, 2];
|
||||||
|
# afficher c[10]; #IndexError
|
@ -1,5 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from modules.errors import SPFUnknownVariable, SPFUninitializedVariable, SPFAlreadyDefined, SPFIncompatibleType, SPFIndexError
|
||||||
|
|
||||||
trace_format = '\033[1m -> '
|
trace_format = '\033[1m -> '
|
||||||
reset_format = '\033[0m'
|
reset_format = '\033[0m'
|
||||||
|
|
||||||
@ -11,14 +13,16 @@ class Variables:
|
|||||||
"booléen": bool,
|
"booléen": bool,
|
||||||
"liste": list }
|
"liste": list }
|
||||||
|
|
||||||
def __init__(self, typ, value = None):
|
def __init__(self, typ, value=None):
|
||||||
assert typ in self.types.keys(), "Ce type de variable est inconnu"
|
assert typ in self.types.keys(), "Ce type de variable est inconnu"
|
||||||
self.type = typ
|
self.type = typ
|
||||||
assert self.checkType(value, typ), f"Le type n'est pas équivalent: { value } n'est pas {typ}"
|
if not self.checkType(value, typ):
|
||||||
self.value = value if (value is not None) else self.default(typ)
|
raise SPFIncompatibleType(value, self.type)
|
||||||
|
self.value = value
|
||||||
|
|
||||||
def set(self, value):
|
def set(self, value):
|
||||||
assert self.checkType(value, self.type), f"Le type n'est pas équivalent: {value} n'est pas {self.type}"
|
if not self.checkType(value, self.type):
|
||||||
|
raise SPFIncompatibleType(value, self.type)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -49,19 +53,24 @@ class Variables:
|
|||||||
self.trace = trace
|
self.trace = trace
|
||||||
|
|
||||||
def get(self, name):
|
def get(self, name):
|
||||||
assert name in self.variables, f"la variable {name} n'éxiste pas"
|
if name not in self.variables:
|
||||||
|
raise SPFUnknownVariable(name)
|
||||||
|
if self.variables[name].value == None:
|
||||||
|
raise SPFUninitializedVariable(name)
|
||||||
if self.trace:
|
if self.trace:
|
||||||
print(f"{trace_format}accède {name}{reset_format}", file=sys.stderr)
|
print(f"{trace_format}accède {name}{reset_format}", file=sys.stderr)
|
||||||
return self.variables[name].value
|
return self.variables[name].value
|
||||||
|
|
||||||
def declare(self, typ, name, value=None):
|
def declare(self, typ, name, value=None):
|
||||||
assert name not in self.variables, f"la variable {name} existe déjà"
|
if name in self.variables:
|
||||||
|
raise SPFAlreadyDefined(name)
|
||||||
self.variables[name] = self.Variable(typ, value)
|
self.variables[name] = self.Variable(typ, value)
|
||||||
if self.trace:
|
if self.trace:
|
||||||
print(f"{trace_format}déclare {name} = {value}{reset_format}", file=sys.stderr)
|
print(f"{trace_format}déclare {name} = {value}{reset_format}", file=sys.stderr)
|
||||||
|
|
||||||
def assign(self, name, value):
|
def assign(self, name, value):
|
||||||
assert name in self.variables, f"la variable {name} n'éxiste pas"
|
if name not in self.variables:
|
||||||
|
raise SPFUnknownVariable(name)
|
||||||
self.variables[name].set(value)
|
self.variables[name].set(value)
|
||||||
if self.trace:
|
if self.trace:
|
||||||
print(f"{trace_format}modifie {name} = {value}{reset_format}", file=sys.stderr)
|
print(f"{trace_format}modifie {name} = {value}{reset_format}", file=sys.stderr)
|
||||||
|
44
modules/errors.py
Normal file
44
modules/errors.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# args
|
||||||
|
# 0) variable name
|
||||||
|
# 1) list of lines of the stack trace
|
||||||
|
|
||||||
|
class SPFException(Exception):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = "Une erreur est survenue"
|
||||||
|
self.errorline = None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return (f"[ligne {self.errorline}] " if self.errorline else "") + f"{self.msg}"
|
||||||
|
|
||||||
|
class SPFSyntaxError(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = "Une erreur de syntaxe est survenue"
|
||||||
|
|
||||||
|
class SPFUnknownVariable(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"la variable `{args[0]}` n'est pas déclarée"
|
||||||
|
|
||||||
|
class SPFUninitializedVariable(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"la variable `{args[0]}` n'est pas initialisée"
|
||||||
|
|
||||||
|
class SPFAlreadyDefined(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"la variable `{args[0]}` est déjà déclarée"
|
||||||
|
|
||||||
|
class SPFIncompatibleType(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"`{args[0]}` n'est pas de type `{args[1]}`"
|
||||||
|
|
||||||
|
class SPFIndexError(SPFException):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.msg = f"La liste `{args[0]}` ne posède pas d'élèment d'indexe {args[1]}"
|
||||||
|
|
||||||
|
|
40
spf.py
40
spf.py
@ -8,6 +8,7 @@ import lark
|
|||||||
import sys
|
import sys
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from modules.Variables import Variables
|
from modules.Variables import Variables
|
||||||
|
from modules.errors import *
|
||||||
|
|
||||||
class SPFInterpreter(lark.visitors.Interpreter):
|
class SPFInterpreter(lark.visitors.Interpreter):
|
||||||
def __init__(self, trace=False):
|
def __init__(self, trace=False):
|
||||||
@ -24,11 +25,19 @@ class SPFInterpreter(lark.visitors.Interpreter):
|
|||||||
type = el.children[0].value
|
type = el.children[0].value
|
||||||
name = el.children[1].value
|
name = el.children[1].value
|
||||||
old = self.variables.variables.copy()
|
old = self.variables.variables.copy()
|
||||||
|
try:
|
||||||
self.variables.declare(type, name)
|
self.variables.declare(type, name)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
target = self.visit_children(el.children[2])[0]
|
target = self.visit_children(el.children[2])[0]
|
||||||
for i in target:
|
for i in target:
|
||||||
|
try:
|
||||||
self.variables.assign(name, i)
|
self.variables.assign(name, i)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
self.visit_children(el.children[3])
|
self.visit_children(el.children[3])
|
||||||
self.variables.variables = old.copy()
|
self.variables.variables = old.copy()
|
||||||
|
|
||||||
@ -40,20 +49,33 @@ class SPFInterpreter(lark.visitors.Interpreter):
|
|||||||
|
|
||||||
def append(self, el):
|
def append(self, el):
|
||||||
(_, toadd, var) = self.visit_children(el);
|
(_, toadd, var) = self.visit_children(el);
|
||||||
|
try:
|
||||||
var_val = self.variables.get(var.value)
|
var_val = self.variables.get(var.value)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
var_val.append(toadd)
|
var_val.append(toadd)
|
||||||
|
|
||||||
def declaration(self, el):
|
def declaration(self, el):
|
||||||
type = el.children[0].value
|
type = el.children[0].value
|
||||||
name = el.children[1].value
|
name = el.children[1].value
|
||||||
value = self.visit_children(el.children[3])[0] if len(el.children) >= 3 else None
|
value = self.visit_children(el.children[3])[0] if len(el.children) >= 3 else None
|
||||||
|
try:
|
||||||
self.variables.declare(type, name, value)
|
self.variables.declare(type, name, value)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
def assignation(self, el):
|
def assignation(self, el):
|
||||||
name = el.children[0].value
|
name = el.children[0].value
|
||||||
assert el.children[1].value == "=" and el.children[2].data == "expression", "Unexpected"
|
assert el.children[1].value == "=" and el.children[2].data == "expression", "Unexpected"
|
||||||
value = self.visit_children(el.children[2])[0]
|
value = self.visit_children(el.children[2])[0]
|
||||||
|
try:
|
||||||
self.variables.assign(name, value)
|
self.variables.assign(name, value)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
def expression(self, el):
|
def expression(self, el):
|
||||||
return self.visit_children(el)[0]
|
return self.visit_children(el)[0]
|
||||||
@ -108,7 +130,6 @@ class SPFInterpreter(lark.visitors.Interpreter):
|
|||||||
|
|
||||||
def priority(self, el):
|
def priority(self, el):
|
||||||
result = self.visit_children(el)
|
result = self.visit_children(el)
|
||||||
print(result)
|
|
||||||
if len(result) < 2:
|
if len(result) < 2:
|
||||||
return result[0]
|
return result[0]
|
||||||
elif result[0].type == "SIZE_OP":
|
elif result[0].type == "SIZE_OP":
|
||||||
@ -120,13 +141,22 @@ class SPFInterpreter(lark.visitors.Interpreter):
|
|||||||
|
|
||||||
def list_get(self, el):
|
def list_get(self, el):
|
||||||
result = self.visit_children(el)
|
result = self.visit_children(el)
|
||||||
|
try:
|
||||||
return result[0][result[1] - 1] # Index start at 1 (like lua)
|
return result[0][result[1] - 1] # Index start at 1 (like lua)
|
||||||
|
except IndexError:
|
||||||
|
e = SPFIndexError(result[0], result[1])
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
def finalterm(self, el):
|
def finalterm(self, el):
|
||||||
return self.visit_children(el)[0]
|
return self.visit_children(el)[0]
|
||||||
|
|
||||||
def variable(self, el):
|
def variable(self, el):
|
||||||
|
try:
|
||||||
return self.variables.get(el.children[0].value)
|
return self.variables.get(el.children[0].value)
|
||||||
|
except SPFException as e:
|
||||||
|
e.errorline = el.meta.line
|
||||||
|
raise e
|
||||||
|
|
||||||
def test(self,el):
|
def test(self,el):
|
||||||
old = self.variables.variables.copy()
|
old = self.variables.variables.copy()
|
||||||
@ -167,11 +197,16 @@ def main():
|
|||||||
args = arg_parser.parse_args()
|
args = arg_parser.parse_args()
|
||||||
|
|
||||||
with open("spf.lark") as grammar:
|
with open("spf.lark") as grammar:
|
||||||
spf_parser = lark.Lark(grammar, parser="lalr", strict=True, debug=True)
|
spf_parser = lark.Lark(grammar, parser="lalr", strict=True, debug=True, propagate_positions=True)
|
||||||
|
|
||||||
with open(args.spf_file) as spf_input:
|
with open(args.spf_file) as spf_input:
|
||||||
program = spf_input.read()
|
program = spf_input.read()
|
||||||
|
try:
|
||||||
parsed = spf_parser.parse(program)
|
parsed = spf_parser.parse(program)
|
||||||
|
except lark.UnexpectedInput as u:
|
||||||
|
e = SPFSyntaxError()
|
||||||
|
e.errorline = u.line
|
||||||
|
raise e
|
||||||
|
|
||||||
if args.pretty:
|
if args.pretty:
|
||||||
print(parsed.pretty())
|
print(parsed.pretty())
|
||||||
@ -183,7 +218,6 @@ def main():
|
|||||||
if args.dump:
|
if args.dump:
|
||||||
interpreter.dump()
|
interpreter.dump()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user