Skip to content

Commit

Permalink
Add try-catch-finally and fix jqlang#1608
Browse files Browse the repository at this point in the history
We can't really fix the issues with try-catch syntax, but we can
introduce `try` as a function-like syntax, using `try(exp; handler)`
in preference to `(try exp catch (handler))`.

While we're at it, we can use the new `UNWINDING` opcode to implement
try-catch-finally as `try(exp; handler; finally)`.
  • Loading branch information
nicowilliams committed Dec 30, 2019
1 parent 480a739 commit e8c874b
Show file tree
Hide file tree
Showing 4 changed files with 1,095 additions and 1,024 deletions.
34 changes: 22 additions & 12 deletions docs/content/manual/manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ sections:
values.
examples:
- program: 'try error("\($__loc__)") catch .'
- program: 'try(error("\($__loc__)"); .)'
input: 'null'
output: ['"{\"file\":\"<top-level>\",\"line\":1}"']

Expand Down Expand Up @@ -2154,25 +2154,35 @@ sections:
input: '{}'
output: [42]

- title: try-catch
- title: try-catch-finally
body: |
Errors can be caught by using `try EXP catch EXP`. The first
expression is executed, and if it fails then the second is
executed with the error message. The output of the handler,
if any, is output as if it had been the output of the
expression to try.
Errors can be caught and handled by using any of `EXP?`,
`try(EXP)`, `try(EXP; EXP)`, or `try(EXP; EXP; EXP)`.
The `try EXP` form uses `empty` as the exception handler.
In the second and third forms, the second `EXP` is a handler
that will run when an error is handled.
In the first two forms errors are suppressed, as if `empty`
had been used as the handler in the second or third forms.
In the third form, the third `EXP` runs once in all cases,
after the first `EXP` is exhausted and the handler has run if
an error was produced. The input to the third, "finally"
expression will be `true` if no error was caught, or an array
containing the error if an error was caught.
The older `try Exp catch Exp` syntax was problematic and is
now obsolete, and may be removed in future versions of jq.
examples:
- program: 'try .a catch ". is not an object"'
- program: 'try(.a; ". is not an object")'
input: 'true'
output: ['". is not an object"']
- program: '[.[]|try .a]'
- program: '[.[]|try(.a)]'
input: '[{}, true, {"a":1}]'
output: ['[null, 1]']
- program: 'try error("some exception") catch .'
- program: 'try(error("some exception"); .)'
input: 'true'
output: ['"some exception"']

Expand All @@ -2187,7 +2197,7 @@ sections:
# Repeat an expression until it raises "break" as an
# error, then stop repeating without re-raising the error.
# But if the error caught is not "break" then re-raise it.
try repeat(exp) catch .=="break" then empty else error;
try(repeat(exp); if .=="break" then empty else error end);
jq has a syntax for named lexical labels to "break" or "go (back) to":
Expand Down
6 changes: 6 additions & 0 deletions src/builtin.jq
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,12 @@ def JOIN($idx; stream; idx_expr; join_expr):
def IN(s): any(s == .; .);
def IN(src; s): any(src == s; .);

def _try_finally(e; h; f):
. as $dot |
unwinding |
if .==false then $dot|try(e; h)
else f
end;

def coeval(program; args; options): [program, ., args, options] | coeval;
def eval(program; args; options):
Expand Down
Loading

0 comments on commit e8c874b

Please sign in to comment.