-
Notifications
You must be signed in to change notification settings - Fork 104
Error Messages
Debugging is by far the most annoying and troublesome aspect of programming - this is a fact that virtually everyone in the world is well aware of. Indeed, this knowledge dates all the way back to the times of the Ancient Sumerians, to those olden days when woolly mammoths still existed in Alaska and when cavemen ran around hunting dinosaurs. In a modern society such as ours today, such fundamental knowledge can surely be left unquestioned and taken to be common sense. The purpose of this page, then, is not to question or challenge this assumption, but rather to give an account and overview of the new and improved error messages in Source1920, built and implemented with this assumption in mind.
Source was always intended to be beginner-friendly, something that anyone - even a complete newbie to CS - would be able to quickly grasp. A large part of this beginner-friendliness came through the error messages in the output console. To be more precise: the error messages in Source have always been truncated; such concepts as "stack traces" and "having more than one error message at a time" were virtually unheard of. Truly, the Source error messages were no more explicit in describing an error's location than merely giving its line number, and these messages only ever appeared in complete solitude - never in groups. There's no denying that it has worked out well. After all, which programmer would, when running his program and expecting to see the computed output, be pleased to be greeted instead by a massive red wall of jargon and gibberish?
... Well, as it turns out, there are indeed many such programmers around. You see, there's a reason compilers and runtime environments give error messages in the first place. This is a universal constant across all reputable programming language implementations, from much-beloved PHP to blazing fast Java. Why do you think massive walls of stack trace text exist? Why is it that error messages in most languages are designed to appear in multiples rather than one-at-a-time? Yes, dear reader - the answer is that, at the very core of it all, error messages are meant to help us debug; to help us with that ever-so-annoying aspect of programming that no one wants to do, but which everyone has to anyway. This is the true purpose of error messages. And for us to strip all this away, to cast it aside in the pursuit of beginner-friendliness? It cannot be said to be "wrong" or "bad", but it is certainly not without detriment.
As a CS1101S student, I was one of the fortunate few to have been graced with, as a dear friend of mine once put it, "prior experience". This perhaps meant that I was somewhat ahead of the curve when I started out with the module. Make no mistake - this was not by my own merit, but rather by the circumstances I had been placed in prior to matriculation. That is either way not important. The fact of the matter is this: Source is great for beginners. That said, not everyone is a beginner. These non-beginners amongst us, which may possibly include you, dear reader, are naturally obligated to extend and provide help to their notably less non-beginner friends. Such help may come in many forms, but, for the purposes of this discussion, I will focus on just one: help with debugging faulty programs.
You see, as a CS1101S student, I often had friends come to me and ask me how to fix their buggy code. Naturally, the first question I'd ask them is something along the lines of, "what error messages do you encounter?" Imagine my dismay, then, when, due to the intrinsic built-in beginner-friendliness of Source, I am provided with only a single, vague, error message, with no stack trace to interpret it in the context of, and with a location bearing no greater precision than a mere line number. I understand that many are out there who can operate perfectly well under such stressful conditions - I am, unfortunately, not one such person. It all started out fine at first, but with higher-order functions giving rise to extremely long lines and with hidden builtin functions giving their own errors under questionable use-cases, well, my ability to help my friends out took a severe tumble. That feeling of powerlessness and helplessness in the face of despair and adversity - it is truly a terrible feeling that pierces my heart to its core. I do not believe I am alone in these sentiments.
With all of this backstory in mind, then; with this deep understanding of the pain felt when trying to debug with vague error messages; you, dear reader, can see for yourself why it is so terribly important that verbose error messages be a thing.
Please make no mistake, though - beginner-friendliness is still key. After all, the vast majority of CS1101S students are, in fact, beginners. But, what if we were to allow each individual user to decide for himself or herself? What if everyone were given a choice? Would that not be empowering? Would that not give rise to a more equal society, bringing us one step closer to that utopian society that our great forefathers always dreamt of?
The toggle that we have chosen to use here is a very simple, yet very powerful tool. Just like with David and the mighty Goliath, a single line of code can make a huge difference. A sample of said line of code is provided below (please do not touch):
"enable verbose";
Legend has it that, by prepending this simple line of code to any Source program, the error messages given as a result (should the program fail to run to completion) will be of a more... verbose quality. This toggle is implemented with a similar mindset to Javascript's "use strict";
imperative, in which a specific string is inserted as the first line of a program to give rise to different behavior in the parser and interpreter. The purpose of using a string, as opposed to implementing it in the form of, say, a function call, is to ensure compatibility: any interpreter prepared to deal with the imperative will be able to do so, while any interpreter not prepared will simply read the value as a string. In this way, we ensure that a valid Source program remains also a valid Javascript program.
The use of the aforementioned toggle will, as mentioned, result in more verbose and elaborate error messages. No examples will be provided here; no programmer is perfect, so you will likely run into these error messages on your own in due time. That said, there are three major changes to the verbose error messages that one should be informed of:
- Column numbers are now shown in addition to line numbers
- All error messages include an extended message that goes in-depth into what should be done to resolve the error
- Some error messages come with example code which performs a similar function to the original code but which does not result in an error (these are personalized and written using the same variable names and values as the original code, allowing for easier implementation and fixing)
Of course, the fact that you're on this repository and reading this wiki page suggests that you're interested in making your own contributions to the error messages of the Academy. If this is the case, then there are some things that I should tell you regarding the existing implementations, so as to make your learning process a little easier.
All errors in Source extend from a single superclass, which requires that each error have, among other things, two functions named explain()
and elaborate()
. The former will always be called whenever the error occurs, with irregard to verbosity; the latter will only be called when verbose mode is enabled. As such, you should build your explain()
methods to return standalone descriptions of errors that are short and sweet, and which do not require additional elaboration - anything else should be left to the elaborate()
methods.
The js-slang interpreter currently works by using two things called a lexer and a parser. The lexer's role is to take the source code and to transform it into a JSON syntax tree of sorts, in which the entire program's functionality is encoded. This tree is then fed to the parser, which executes the instructions described in the tree, simulating the running of the program itself. At each step of the way, the parser reduces and simplifies the tree; when the tree has reached a state beyond which no further reduction is possible, we are left with our output, which is then passed to the frontend for display.
The lexer and parser are not part of the same system - they are rather two separate dependencies that communicate through a common interface. This interface, unfortunately, does not include error messages. As such, both the lexer and parser are capable of raising their own exceptions. While all of these still have to be displayed to the user in the end, there are some nuanced differences in the ways these are handled within the program.
Lexer errors can be likened to syntax or compile-time errors - problems with the code which can be detected before the program is even run. These errors are handled in the rules
subdirectory of the source code, with a few stragglers located within the main index
file.
These are all located and handled within interpreter-errors
. As each error can be invoked in a variety of different ways, please take care to ensure that your error messages remain universally accurate. Alternatively, you could include checks to determine the exact context in which the error occurred, then decide what to do from there.