Skip to content

Commit 2d8b4cd

Browse files
committed
Added docs on AST functions
1 parent 6065df3 commit 2d8b4cd

File tree

1 file changed

+42
-0
lines changed

1 file changed

+42
-0
lines changed

README.md

+42
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- [Basic example](#basic-example)
2020
- [Error handling](#error-handling-1)
2121
- [Overloading](#overloading)
22+
- [AST functions \(advanced\)](#ast-functions-advanced)
2223

2324
<!-- /MarkdownTOC -->
2425

@@ -279,3 +280,44 @@ jsonexpr::register_function(
279280
```
280281
281282
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

Comments
 (0)