Inconsistency with lower & upper
ginger decided that there is no difference between functions, tests and filters. Unfortunately, there is a clash for the tests and filters lower and upper:
In jinja2:
|upper returns an uppercased string which is always truthy
is upper returns true only if the string is already uppercased
The same goes for lower respectively
This blocks #18
As discussed, potential solutions:
- Extend the AST to track how exactly a
CallEwas invoked (function call, filter, test, macro invocation), and then hand that information to functions, which can then change their behavior accordingly. This would allow us to write agfnLowerthat acts as an "all-lowercase" test when invoked throughis, and as a "make lowercase" function otherwise. This is terrible. - Abandon the "tests and filters and functions and macros are the same thing" paradigm and put them in separate namespaces. Breaks the API though, and it would be a real shame, because for use cases where 100% jinja compatibility isn't needed, this interchangeability is actually super convenient (write a macro, use it as a filter, holy cow, that's awesome!)
- Add some magic to
isthat mangles the test name to prefer some kind of specially-named builtin (ideally one that you cannot produce from within the template). E.g.,foobar is lowerwould then parse intoCallE (VarE "is-lower") [(Nothing, VarE "foobar")](note theis-prefix on the variable name). This would work, but I'm really reluctant to add this kind of hard-to-explain magic.
So all the actual solutions kind of suck.
Additionally, I can't really think of a reasonable use case where you'd say "oh, hmm, I need to check whether everything in this string is lowercase" - the only situations I can think of are ones where you'd want to actually convert the string to lowercase anyway.
So I'm inclined to say that we should just accept this incompatibility (is lower doing something somewhat unintuitive, and deviating from jinja), document it as such, and call it a day.
On further thought, I believe option 3 might not be so bad after all.
The thing is that there are more tests in Jinja2, and many of them do make sense in Ginger, like for example the is number test.
So here's how I think it should work:
- The
issyntax construct should take care of prependingis_to the function name, thusfoo is barshould be desugared tois_bar(foo). This also implies thatissyntax is going to be a lot more restrictive than filter or function call syntax: only literal function names are allowed for the filter name, so unlike filters or functions, you cannot use arbitrary expressions as tests. This isn't going to be a huge problem, because Jinja2 has the same restriction. It also means that theisconstruct should have very high precedence, i.e.,foo + bar is evenshould parse asfoo + (bar is even), not(foo + bar) is even. - Built-in tests will be exposed to the template with the
is_prefix added. This also exposes them as plain old functions, true to the "tests and filters and functions are all the same thing" paradigm, but it avoids name clashes between things likelowerandis_lower. - Tests, in this approach, can also be user-defined - as long as the
is_prefix is added, it will work just fine.