Here's a single model, where both formatters and predicates are the
same operation: filtering. This model is nice because it has only
one definition of expression and assigns only one meaning to |.
Definitions:
A JSON value is as the standard says: null, true, false, number,
string, list, object.
A string is a special case of a JSON value.
A substitution identifies a single value that may or may not be
expanded into the output.
A directive is a literal, substitution, or a section.
A section identifies a sequence of directives that may or may not be
expanded into the output.
Filters are functions from an *arbitrary* JSON value -> JSON value
(null is included in both domain and range!). This implies they can
be chained. And it also implies that they may modify any other value.
An expression has the form name ("|" name)*.
To evaluate the expression, the leading name is looked up
in the value stack (same as current JSON template) to produce
an initial value. Then each .name is applied, replacing the value
with the result of looking that name up in value, which must
be a JSON map. Then each |name is applied, replacing
the value with the result if applying the named filter to the value.
The expression a|b|c|d would be written d(c(b(a))) in many
programming languages. The special leading name "@" means
the top element on the value stack.
A substitution has the form "{" expr "}". In the output, it is
replaced by the result of evaluating expr.
A section has the form
"{.section" expr "}" true-body ("{.or}" false-body)? "{.end}"
In the output, it is replaced by the result of the following procedure.
First, expr is evaluated to produce a value v.
If v is not false or empty, v is pushed on the value stack, true-body
is executed, and v is popped off. Otherwise, false-body is executed
if present.
A repeated section has the form
"{.repeated section" expr "}" body ("{.or}" false-body)? "{.end}"
[Yes there's also an alternates-with but it doesn't matter here.]
In the output, it is replaced by the result of the following procedure.
First, expr is evaluated to produce a value v, which must be an array.
For each element of v, that element is pushed on the stack, body is
executed, and the element is popped off. If v is empty, false-body
is executed if present.
As a result of this definition, one style of filters is as "predicates"
which return either their input or false. Using them can enable or
disable sections appropriately:
{.section num|>1}
There are {num|english} people in group {name}
{.or}
{.section num|==1}
There is one person in group {name}
{.or}
There are no people on group {name}. How sad.
{.end}
{.end}
It is also possible to use filters to transform the data before iterating
over it, for example to select only the public fields from a data structure
definition:
{.repeated section fields|public?}
Field {name} has type {type}.
{.end}
Or to order an array in a certain way:
{.repeated section people|sort-by-phone-number}
{name} {phone-number}
{.end}
Or perhaps to create a potentially large data structure on the fly:
{.section x|primes-up-to-10000}
{...@} is prime.
{.end}
Again, the primes are: {x|primes-up-to-10000}.
In this implementation, evaluation of expressions has just
one behavior: a|b|c|d means take a, pipe it through b, c, and d,
and use the result as the value of the expression, no matter
what the context.
Neither sections nor substitutions have predicates or formatters.
Data has filters. Instead of 4 cases, there is 1.
Predicates are an idiom, not a built-in concept.
Russ
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "JSON
Template" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/json-template?hl=en
-~----------~----~----~----~------~----~------~--~---