I'm creating a python-based DSL just for fun. Right now, it just compiles text into an AST that executes python code directly. I mean if I write:
a = int(read("input something: "))
b = a**2
It translates this to a tree like:
Program:
VariableSet a:
Call:
VariableGet int
Call:
VariableGet read
Literal("input something: ")
VariableSet b:
BinaryExpession **:
VariableGet a
Literal(2)
Each of these nodes will be executed with a code like the following:
class VariableSet(object):
def __init__(self, name, expr):
self.name = name
self.expr = expr
def __call__(self, scope):
value = self.expr(scope)
scope.put(self.name, value)
return value
It does work very well. I wanted that, when some instruction throws an exception, that the exception stack traceback points to the DSL source code, not the python AST code. Right now, all I see is:
Traceback (most recent call last):
File "parser.py", line 164, in <module>
parse(data)(scope)
File "/home/juanplopes/Projects/my-parser/ast.py", line 158, in __call__
result = expr(scope)
File "/home/juanplopes/Projects/my-parser/ast.py", line 63, in __call__
value = self.expr(scope)
File "/home/juanplopes/Projects/my-parser/ast.py", line 81, in __call__
return self.method(scope)(*[arg(scope) for arg in self.args])
File "/home/juanplopes/Projects/my-parser/ast.py", line 81, in __call__
return self.method(scope)(*[arg(scope) for arg in self.args])
File "parser.py", line 153, in read
raise Exception('some exception')
Exception: some exception
Is there a way to customize how tracebacks are generated in python?
Yes, but it's ugly to do so.
You might want to look at how it's done in jinja2
.
Jinja is a templating engine. It handles tracebacks so that they point at the template errors.
Maybe you could transpose this to your code so that it points to the DSL errors.