Implement `defined`
We need to make a special case here, because defined cannot be implemented as a function - any argument passed to a function is evaluated before passing it in, and evaluating undefined variables simply yields null. I can see two ways in which this could be done.
The first one is to make defined a very restricted special case in the parser, which emits a separate kind of expression, let's say CheckDefinedE p VarName; and the interpreter would then simply take the varname and check it against the current scope. This is easy to implement, but not very featureful: e.g., {% if foo.bar is defined %} will not be possible this way.
The second solution would be more involved. First, we would need a way to mark functions as macros; the operations semantics would then be as follows:
- function: evaluate all arguments, pass results of evaluation to function and return output.
- macro: pass unevaluated arguments to function, evaluate output and return.
Thus, a macro would decide on its own whether it wants to evaluate its arguments.
Armed with this, we would then have to implement a function that, instead of completely evaluating an expression, merely checks for definedness. This is thin ice though, there are plenty of cases where the answer isn't obvious, e.g., in {{ defined(foo(bar)) }}, is it enough for foo to be a function, and for bar to be defined? Or do all identifiers that foo closes over also need to be defined? And if so, how are we going to check this?
Relevant Jinja documentation: http://jinja.pocoo.org/docs/2.10/templates/#defined