Errors
How Venutian reports a problem in your source, and how attempt and rescue catch a fault while a program runs.
Every failure in Venutian gets one of two treatments: a diagnostic if the toolchain caught the problem before your program ran, or a catchable fault if it happened while your program was running. Neither ever shows you a raw interpreter traceback.
Toolchain diagnostics
venutian run, venutian build, venutian check and every other command that reads your source report a problem the same way, whether it is a lexing, parsing, or semantic analysis error. A diagnostic carries the message, the file, the line and column, and, where the toolchain has one, a suggestion. When it can, it prints the offending source line with a ^ under the exact column:
VENUTIAN ERROR V2001
'use "does_not_exist.ven"' could not find that file.
source: bad_use.ven
line: 1
column: 1
Checked: /full/resolved/path/to/does_not_exist.ven
Run any command with --diagnostics-format json to get the same information as structured JSON, for an editor or a script to read directly.
attempt and rescue
Inside a running program, attempt and rescue catch a fault. A raise inside the attempt block, or a fault the runtime itself raises (dividing by zero, indexing past the end of a List, and the like), unwinds to the nearest matching rescue:
shape ValidationError
message: Text
shape TimeoutError
seconds: Integer
make risky(n)
when n < 0
raise ValidationError("must be positive")
when n > 100
raise TimeoutError(30)
give n * 2
make try_it(n)
attempt
give risky(n)
rescue err: ValidationError
give "validation: " + err.message
rescue err: TimeoutError
give "timeout after " + as_text(err.seconds) + "s"
rescue err
give "unknown: " + as_text(err)
print try_it(5)
print try_it(-1)
print try_it(200)
10
validation: must be positive
timeout after 30s
A rescue clause with a : Type filter only catches a value of that shape, or of a shape it inherits from, exactly the way choose's is name: Type pattern matches (see types and shapes). Only the last rescue clause in an attempt may skip the type filter; an earlier untyped rescue would catch everything and make every clause after it unreachable, so the compiler refuses to build a program written that way.
A raised value keeps its real runtime type through the catch, so rescue err: Animal matches a raised Dog. The stack is restored to the exact depth it had when the attempt was entered, even when the raise happens in the middle of an expression, so no stale value from before the fault is left for code after the rescue to trip over.
What raise takes
raise takes any Venutian value: a shape instance built for the purpose, as above, or plain Text. A built in runtime fault, like dividing by zero, raises Text carrying the fault's message, so your own raise and a built in fault land in exactly the same rescue.
What stays uncaught
An attempt/rescue block only ever sees a raise from code that runs inside it, directly or through a function it calls. A toolchain diagnostic never reaches a rescue: your program has not started running yet when one of those is reported, so there is nothing an attempt block could be wrapped around. An uncaught runtime fault ends the program and renders through the same diagnostic format as a toolchain error, so you see one consistent shape of error message no matter when the problem was actually found.