2014-04-09 07:53:30 -07:00
|
|
|
^title Error Handling
|
2014-04-14 21:23:46 -07:00
|
|
|
^category language
|
2014-04-09 07:53:30 -07:00
|
|
|
|
2015-01-01 20:45:15 -08:00
|
|
|
Errors come in a few fun flavors.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
|
|
|
|
## Syntax errors
|
|
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
The first errors you're likely to run into are syntax errors. These include
|
|
|
|
|
simple bugs where your code doesn't follow the language's grammar, like:
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-09-22 07:59:54 -07:00
|
|
|
:::wren
|
2015-01-01 16:02:02 -08:00
|
|
|
1 + * 2
|
|
|
|
|
|
2015-10-18 15:56:52 -07:00
|
|
|
Wren detects these errors as soon as it tries to read your code. When it hits
|
|
|
|
|
one, you get a friendly error message, like:
|
2015-01-01 16:02:02 -08:00
|
|
|
|
|
|
|
|
:::text
|
2015-10-18 15:56:52 -07:00
|
|
|
[main line 1] Error on '*': Unexpected token for expression.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Some slightly more "semantic" errors fall into this bucket too. Things like
|
|
|
|
|
using a variable that hasn't been defined, or declaring two variables with the
|
|
|
|
|
same name in the same scope. So if you do:
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-09-22 07:59:54 -07:00
|
|
|
:::wren
|
2015-01-01 16:02:02 -08:00
|
|
|
var a = "once"
|
|
|
|
|
var a = "twice"
|
|
|
|
|
|
|
|
|
|
Wren tells you:
|
|
|
|
|
|
|
|
|
|
:::text
|
2015-10-18 15:56:52 -07:00
|
|
|
[main line 2] Error on 'a': Top-level variable is already defined.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Note that it does this before it executes *any* code. Unlike some other
|
|
|
|
|
scripting languages, Wren tries to help you find your errors as soon as
|
|
|
|
|
possible when it can.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
If it starts running your code, you can be sure you don't have any errors
|
|
|
|
|
related to syntax or variable scope.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
|
|
|
|
## Runtime errors
|
|
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Alas, just fixing all of the "compile-time" errors doesn't mean your code does
|
|
|
|
|
what you want. Your program may still have errors that can't be detected
|
|
|
|
|
statically. Since they can't be found until your code is run, they're called
|
|
|
|
|
"runtime" errors.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Most runtime errors come from the VM itself. They arise from code trying to
|
|
|
|
|
perform an operation that the VM can't do. The most common error is a "method
|
|
|
|
|
not found" one. If you call a method on an object and its class (and all of its
|
|
|
|
|
superclasses) don't define that method, there's nothing Wren can do:
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-09-22 07:59:54 -07:00
|
|
|
:::wren
|
2015-01-01 16:02:02 -08:00
|
|
|
class Foo {}
|
|
|
|
|
|
2015-07-10 09:18:22 -07:00
|
|
|
var foo = Foo.new()
|
2015-01-01 16:02:02 -08:00
|
|
|
foo.someRandomMethod
|
|
|
|
|
|
|
|
|
|
If you run this, Wren will print:
|
|
|
|
|
|
|
|
|
|
:::text
|
|
|
|
|
Foo does not implement method 'someRandomMethod'.
|
|
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Then it stops executing code. Unlike some other languages, Wren doesn't keep
|
|
|
|
|
plugging away after a runtime error has occurred. A runtime error implies
|
|
|
|
|
there's a bug in your code and it wants to draw your attention to it. To help
|
|
|
|
|
you out, it prints a stack trace showing where in the code the error occurred,
|
|
|
|
|
and all of the method calls that led to it.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Another common runtime error is passing an argument of the wrong type to a
|
|
|
|
|
method. For example, lists are indexed using a number. If you try to pass some
|
|
|
|
|
other type, it's an error:
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-09-22 07:59:54 -07:00
|
|
|
:::wren
|
2015-01-01 16:02:02 -08:00
|
|
|
var list = ["a", "b", "c"]
|
|
|
|
|
list["1"]
|
|
|
|
|
|
|
|
|
|
This exits with:
|
|
|
|
|
|
|
|
|
|
:::text
|
|
|
|
|
Subscript must be a number or a range.
|
2015-10-18 15:56:52 -07:00
|
|
|
[main line 2] in (script)
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
These are the most two common kinds of runtime errors, but there are others.
|
|
|
|
|
Stuff like out of bounds errors on lists, calling a function with the wrong
|
|
|
|
|
number of arguments, etc.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
|
|
|
|
## Handling runtime errors
|
|
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Most of the time, runtime errors indicate a bug in your code and the best
|
|
|
|
|
solution is to fix the bug. However, sometimes it's useful to be able to handle
|
|
|
|
|
them at, uh, runtime.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
To keep the language simpler, Wren does not have exception handling. Instead,
|
|
|
|
|
it takes advantage of [fibers](fibers.html) for handling errors. When a runtime
|
|
|
|
|
error occurs, the current fiber is aborted. Normally, Wren will also abort any
|
|
|
|
|
fibers that invoked that one, all the way to the main fiber, and then exit the
|
|
|
|
|
VM.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
However, you can run a fiber using the `try` method. If a runtime error occurs
|
|
|
|
|
in the called fiber, the error is captured and the `try` method returns the
|
|
|
|
|
error message as a string.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
|
|
|
|
For example, if you run this program:
|
|
|
|
|
|
2015-09-22 07:59:54 -07:00
|
|
|
:::wren
|
2015-07-10 09:18:22 -07:00
|
|
|
var fiber = Fiber.new {
|
2015-01-01 16:02:02 -08:00
|
|
|
123.badMethod
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-26 23:08:36 -08:00
|
|
|
var error = fiber.try()
|
2015-09-15 07:46:09 -07:00
|
|
|
System.print("Caught error: " + error)
|
2015-01-01 16:02:02 -08:00
|
|
|
|
|
|
|
|
It prints:
|
|
|
|
|
|
|
|
|
|
:::text
|
2015-01-02 14:12:53 -08:00
|
|
|
Caught error: Num does not implement method 'badMethod'.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
The called fiber can no longer be used, but any other fibers can proceed as
|
|
|
|
|
usual. When a fiber has been aborted because of a runtime error, you can also
|
|
|
|
|
get the error from the fiber object. Continuing the above example:
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-09-22 07:59:54 -07:00
|
|
|
:::wren
|
2015-09-15 07:46:09 -07:00
|
|
|
System.print(fiber.error)
|
2015-01-01 16:02:02 -08:00
|
|
|
|
|
|
|
|
This also prints:
|
|
|
|
|
|
|
|
|
|
:::text
|
|
|
|
|
Num does not implement method 'badMethod'.
|
|
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
If you have a chain of fiber calls and a runtime error occurs, it will walk the
|
|
|
|
|
chain looking for a `try` call, so this can also be used to capture runtime
|
|
|
|
|
errors generated in fibers that are invoked by the one you called `try` on.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
|
|
|
|
## Creating runtime errors
|
|
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Most runtime errors come from within the Wren VM, but you may want to be able
|
|
|
|
|
to cause your own runtime errors to occur. This can be done by calling the
|
|
|
|
|
`abort()` static method on `Fiber`:
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-09-22 07:59:54 -07:00
|
|
|
:::wren
|
2015-01-01 16:02:02 -08:00
|
|
|
Fiber.abort("Something bad happened")
|
|
|
|
|
|
|
|
|
|
You must pass in an error message, and it must be a string.
|
|
|
|
|
|
|
|
|
|
## Failures
|
|
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
The last flavor of errors is the highest-level one. All of the above errors
|
|
|
|
|
indicate *bugs*—places where the code itself is incorrect. But some
|
|
|
|
|
errors indicate that the code simply couldn't accomplish its task for
|
|
|
|
|
unforeseeable reasons. We'll call these "failures".
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
Consider a program that reads in a string of input from the user and parses it
|
|
|
|
|
to a number. Many strings are not valid numbers, so this parsing can fail. The
|
|
|
|
|
only way the program could prevent that failure is by validating the string
|
|
|
|
|
before its parsed, but validating that a string is a number is pretty much the
|
|
|
|
|
same thing as parsing it.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
For cases like this where failure can occur and the program *will* want to
|
2015-02-26 23:08:36 -08:00
|
|
|
handle it, fibers and `try()` are too coarse-grained to work with. Instead,
|
|
|
|
|
these operations will indicate failure by *returning* some sort of error
|
|
|
|
|
indication.
|
2015-01-01 16:02:02 -08:00
|
|
|
|
2015-01-03 23:27:02 -08:00
|
|
|
For example, a method for parsing a number could return a number on success and
|
|
|
|
|
`null` to indicate parsing failed. Since Wren is dynamically typed, it's easy
|
|
|
|
|
and natural for a method to return different types of values.
|