A few questions about implementing exceptions in my interpreter
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
A few questions about implementing exceptions in my interpreter
The first is that if the exception is not handled, it should be printed to stderr, and that includes the exception's name. The problem is that in my language, nothing has a fixed, global name, including classes and functions. The only way you can give them a "name" is by putting them in variables.
The second, which also has to do with printing an unhandled exception, is how do I get line, column, and filename info? Once the code is converted to an AST, it has no concept of filenames, lines, or columns.
The third, and the last one that has to do with printing an unhandled exception, is how do I get a backtrace?
Finally, there are some exceptions that are thrown by the interpreter itself (caused by doing things like reading non-existent variables or importing a file that's not there). Do the exceptions these throw have to be in the global scope all the time? Having to initialize variables in the global scope instead of leaving it empty seems like extra trouble. I solved this with the builtin types (like integers and strings) by creating keywords that evaluate to the types.
The first is that if the exception is not handled, it should be printed to stderr, and that includes the exception's name. The problem is that in my language, nothing has a fixed, global name, including classes and functions. The only way you can give them a "name" is by putting them in variables.
This is incorrect, you can use typeid with C++ exception classes. Derive your own class from std::exception, catch it, and use typeid().name() to get exception class. Just make sure you catch it by reference (catch std::exception& e), not by value. Also std::exception has very handy what() method for passing error messages.
The second, which also has to do with printing an unhandled exception, is how do I get line, column, and filename info? Once the code is converted to an AST, it has no concept of filenames, lines, or columns.
There are more or less standard built-in macros(es?)
Code:
__FILE__
__FUNCTION__
__LINE__
that are supported by g++/msvc compilers. Use them when you throw exception.
Quote:
Originally Posted by MTK358
The third, and the last one that has to do with printing an unhandled exception, is how do I get a backtrace?
That's platform-dependent, and you should REALLY google for tutorials - in last 10 years somebody most likely wrote at least one linux backtrace tutorial. I wouldn't bother. Unless it is a segfault, backtrace is not necesarry, and if it IS a segfault, then you'll simply need to make coredump and examine it later within debugger.
Quote:
Originally Posted by MTK358
Do the exceptions these throw have to be in the global scope all the time?
Sentence doesn't make much sense. Anyway, that's up to you to decide - the answer would require analysis of your interpreter. The easiest way is to handle interpreter exceptions would be to introduce additional exception class (derived from std::exception) for interpreter exceptions, and try to catch it before catching std::exception - simply use several catch blocks.
You seem to have completely misunderstood my question.
I'm not talking about C++, but about my interpreted language.
I asked how to get a backtrace of the interpreted program, not the C++ interpreter. I also asked how to get the line and column info of the spot in the interpreted program where the exception was thrown, not what line and column a C++ exception was thrown at.
I asked how to get a backtrace of the interpreted program, not the C++ interpreter.
Well, in this case I don't really see the problem.
Your interpreter most likely has a "stack" or similar structure that stores return addresses. Get a copy of "stack" when exception is thrown, walk through the structure, and you'll get a backtrace.
Quote:
Originally Posted by MTK358
I also asked how to get the line and column info of the spot in the interpreted program where the exception was thrown, not what line and column a C++ exception was thrown at.
You won't be able to get column unless you're talking about syntax errors - exeption gets thrown by statement, not by character. To get line information, you obviously will have to store it within interpreter - line number for every statement. 2..4 extra bytes per statement will do the trick. You're the author, so you can add information you need. If you think it takes too much space, then you could try searching for a way to compress it.
As I already said, more detailed answer would require complete analysis of your interpreter which is beyond the scope of this forum.
Well, in this case I don't really see the problem.
Your interpreter most likely has a "stack" or similar structure that stores return addresses. Get a copy of "stack" when exception is thrown, walk through the structure, and you'll get a backtrace.
Actually, it currently just uses a set of recursive AST-walking functions, not any kind of VM (but I am interested in how to do a bytecode VM. The problem is that when I try to desrch about it, almost all the results are about JVM).
Quote:
Originally Posted by SigTerm
You won't be able to get column unless you're talking about syntax errors - exeption gets thrown by statement, not by character. To get line information, you obviously will have to store it within interpreter - line number for every statement. 2..4 extra bytes per statement will do the trick. You're the author, so you can add information you need. If you think it takes too much space, then you could try searching for a way to compress it.
As I already said, more detailed answer would require complete analysis of your interpreter which is beyond the scope of this forum.
I considered putting line/column info into each AST node, but it doesn't seem worth it. I'll just leave that alone for now.
Line-numbers are simply stored as a field in the bytecode record! Yup, every bytecode has the line-number it came from. (I mean, do I really care about 4 extra bytes per instruction?)
(In the same vein, feel free to put line/column numbers and whatever else you'd like to keep good 'n handy, into an AST node! We are not still in the days when we had to split compilers into "core-loads" and carefully record them onto magnetic drums...)
The interpreter maintains an exception-handler stack. (This, like all other such stacks, is not "in program-visible 'memory,'" but is a separate data-structure.) When an exception occurs, the topmost entry is the active handler. (If an exception is occurring within an exception handler, the stack is popped until we find a handler that we can use.)
Just to clarify ... the p-code execution routine within the interpreter is wrapped in a try..except block. If an exception occurs while executing an instruction, we wind up here. At this point the interpreter captures the error-information and puts itself into an EXCEPTION_OCCURRED state. From the point-of-view of the implementation language, "the exception is over now." The interpreter now searches its stack in order to figure out what the user program wants to do with the exception.
If there is no usable stack-entry, the user program either didn't prepare to handle exceptions or the exception occurred within its outermost exception-handling routine. (Either way, "You're dead, Jim...") The interpreter puts itself into an ERROR_STOP state and returns to its caller.
Since my language implements "try..finally," there's one more twist of weirdness .. an opcode called DO_FINALLY. While compiling exception-handling statements we remember how many nested "try..finally" blocks we're in, and we generate that many copies of this instruction before, say, "return." This opcode looks for a "finally" handler that hasn't been tripped yet, pops the stack until it gets there, then sends us off into that code. The finally-block ends with an END_FINALLY instruction, which sends us back to the instruction following DO_FINALLY. (When either of these instructions are executing, we know that "at the moment, everything is okay... nothing, ahh, exceptional is happening.")
It seems to work well enough, although once I saw it misbehave most oddly. That interpreter's been in service now for about fifteen years...
Last edited by sundialsvcs; 05-12-2011 at 06:01 PM.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.