|
19 | 19 | - [Basic example](#basic-example)
|
20 | 20 | - [Error handling](#error-handling-1)
|
21 | 21 | - [Overloading](#overloading)
|
| 22 | + - [AST functions \(advanced\)](#ast-functions-advanced) |
22 | 23 |
|
23 | 24 | <!-- /MarkdownTOC -->
|
24 | 25 |
|
@@ -279,3 +280,44 @@ jsonexpr::register_function(
|
279 | 280 | ```
|
280 | 281 |
|
281 | 282 | Note: It is possible to mix overloads with static (as above) and dynamic types (using one or more parameters of type `jsonexpr::json`). In such cases, functions with static types will always be preferred over functions with dynamic types.
|
| 283 | +
|
| 284 | +
|
| 285 | +#### AST functions (advanced) |
| 286 | +
|
| 287 | +A more advanced use case are "AST functions", which work at a lower level on the Abstract Syntax Tree (AST). The input of the C++ function is the set of parsed (but not yet evaluated) expressions fed as arguments to the *jsonexpr* function call. Possible use cases: |
| 288 | + - Short-circuiting; when it is not desirable to evaluate all function arguments before the call. Examples: shot-circuiting boolean logic; implement lazy evaluation for performance reasons; ... |
| 289 | + - Reflection; to inspect and modify the content of the provided expression. Examples: insert a call to a specific function on each argument; return the type of the AST node making up each argument; ... |
| 290 | +
|
| 291 | +In the example below, we implement a function that returns the first argument that does not evaluate to null, and does not evaluate the rest. |
| 292 | +```c++ |
| 293 | +jsonexpr::register_ast_function( |
| 294 | + funcs, "first_non_null", |
| 295 | + [](std::span<const jsonexpr::ast::node> args, const jsonexpr::variable_registry& vars, |
| 296 | + const jsonexpr::function_registry& funcs) -> jsonexpr::ast_function_result { |
| 297 | + for (const jsonexpr::ast::node& arg : args) { |
| 298 | + // Evaluate the current argument. |
| 299 | + const auto evaluated = jsonexpr::evaluate(arg, vars, funcs); |
| 300 | + if (!evaluated.has_value()) { |
| 301 | + return jsonexpr::unexpected(evaluated.error()); |
| 302 | + } |
| 303 | +
|
| 304 | + // Stop and return it if not null, otherwise continue with next argument. |
| 305 | + if (!evaluated.value().is_null()) { |
| 306 | + return evaluated.value(); |
| 307 | + } |
| 308 | + } |
| 309 | +
|
| 310 | + // No match found, return an error. |
| 311 | + // NB: 'jsonexpr::error' contains both a message and a location (as the range of characters |
| 312 | + // in the expression string), to help the user locate the actual part of the expression that is causing a problem. If the location is not specified (as we did here), the location |
| 313 | + // will automatically be set to the whole function call. |
| 314 | + return jsonexpr::unexpected(jsonexpr::error{.message = "all arguments were null"}); |
| 315 | + }); |
| 316 | +``` |
| 317 | + |
| 318 | +When used: |
| 319 | +``` |
| 320 | +first_non_null(null) -> error: all arguments were null |
| 321 | +first_non_null(null, 1) -> 1 |
| 322 | +first_non_null(1, 1+'abc') -> 1 (second argument was invalid, but no error since not evaluated) |
| 323 | +``` |
0 commit comments