Previous: Introduction
Simple Transforms
sUTL is transforms. It doesn't have statements, or expressions, or functions, or classes or objects. Only transforms.
You can think about a transform as follows:
source --> transform --> result
ie: the source is transformed by the transform to produce the result.
But it's a little more complicated than that. The full picture is given by the evaluate function.
Evaluate
To run a sUTL program, we evaluate a transform. Conceptually, evaluate is a function outside of sUTL; it is the sUTL interpreter (and will turn up in the host language in various guises).
The evaluate function works logically as follows:
evaluate ( source, transform, library, scope ) -> result
The arguments to this function are all JSON data structures.
- source: This is the input JSON data structure.
- transform: This is sUTL transform itself.
- scope: This is data that the transform can draw upon which is local to the transform. For simple transforms it is the same as the source, but for nested transforms it can be radically different. You can think of the scope as your program's variables.
- library: This is a dictionary of library transforms that can be drawn upon by the transform.
Literal Transforms
The most basic kind of transform is a literal transform. A literal transform is like a literal in other languages.
All the primitive elements of JSON data structures function as literal transforms. These are things like the number 3, or the string "Hello World". For example, here is the number 3 as a literal:
You'll notice that the transform 3 evaluates to the result 3, no matter what the source is. All literal transforms behave in this way.
The complete list of literal transforms is the list of simple JSON datatypes: that is,
- strings
- numbers
- booleans
- null
Strings
Numbers
Booleans
null
General Dictionary Transforms
The general dictionary transform is a transform that is applied to any dictionary which isn't covered by any specific dictionary transforms (see other sections). The transform will look like this:
transform = {
"arg1": ?,
"arg2": ?,
...
"argn": ?
}
It is evaluated as follows:
evaluate ( source, transform, scope, library ) = {
"arg1": evaluate ( source, transform["arg1"], scope, library ),
"arg2": evaluate ( source, transform["arg2"], scope, library ),
...
"argn": evaluate ( source, transform["argn"], scope, library )
}
An example: Say we have the following transform:
transform = {
"name": "Fred",
"occupation": "literalist"
}
Then evaluate would behave as follows, for any source, scope, and library:
evaluate ( source, transform, scope, library )
= {
"name": evaluate ( source, transform["name"], scope, library )
"occupation": evaluate ( source, transform["occupation"], scope, library )
}
= {
"name": "Fred",
"occupation": "literalist"
}
What does this mean? It means that dictionaries that only contain more dictionaries and simple transforms evaluate as literals. This is true if they contain lists, too, see below.
You can try it here:
General List Transform
This is a transform that is applied to any list which isn't covered by any specific list transforms (see elsewhere). The transform looks like this:
transform = [
?, ?, ... ?
]
It is evaluated as follows:
evaluate ( source, transform, scope, library ) = [
evaluate ( source, transform[0], scope, library ),
evaluate ( source, transform[1], scope, library ),
...
evaluate ( source, transform[n-1], scope, library )
]
An example: Say we have the following transform:
transform = [ 1, 2, 3, 4, 5 ]
Then evaluate would behave as follows, for any source, scope, and library:
evaluate ( source, transform, scope, library )
= [
evaluate ( source, transform[0], scope, library ),
evaluate ( source, transform[1], scope, library ),
...
evaluate ( source, transform[n-1], scope, library )
]
= [
1, 2, 3, 4, 5
]
So this transform means that unless something special is going on, an array is transformed to itself. If it contains more complex transforms then they will be evaluated, so it may not always be literal.
You can try it here:
Concatenate Transform
Here's the first transform that really lets you do something! This transform lets you join lists together.
The Concatenate Transform lets you join multiple lists and items into one list. It's a special form of the general list transform. All elements of the list are evaluated as before, and then if the first element of the list is "&&", then it is processed as a concatenate transform.
transform = [ "&&", elem1, elem2, ..., elemn ]
evaluates to
evaluate ( source, transform, scope, library )
= concatenate ( tail ( transform ) )
The function tail just means we've removed the first element from the list.
The function concatenate examines each element of the list. If the element is itself not a list, it is wrapped in a list. Then, all elements (which are all lists) are concatenated into one big list, and the result is returned.
eg: transform = [ "&&", [1, 2], 3, [4, 5] ]
evaluates to
evaluate ( source, transform, scope, library )
= concatenate ( tail ( transform ) )
= concatenate ( tail ( [ "&&", [1, 2], 3, [4, 5] ] ) )
= concatenate ( [ [1, 2], 3, [4, 5] ] )
= [ 1, 2, 3, 4, 5 ]
Try it here:
Quote Transform
The quote transform is really simple. It provides a way of stopping some data being evaluated like a transform.
It works like this:
{
":": <data>
}
evaluates to
<data>
Example: Imagine you have an array that looks like a concatenation transform, but you don't want it evaluated, you want it to stay intact. If you provide
transform = [ "&&", [1, 2], [3] ]
as a transform, it'll evaluate to
result = [1, 2, 3]
But if you wrap it in the quote transform like this:
transform = {":": [ "&&", [1, 2], [3] ]}
it'll transform to this:
result = [ "&&", [1, 2], [3] ]
The name "quote" comes from LISP, where a quote character is used.
This transform seems trivial, but is important for "escaping" data so it isn't accidentally evaluated, and also for keeping transforms intact while you pass them into other transforms, to be used later. You'll see it over and over in contexts where one compound transform uses a separate transform to do something.
Note: you can use a single quote instead of a colon for the quote transform, like this:
{
"'": <data>
}
I think it's a little harder to read, but really it makes no difference. It may please you a little more if you're a LISPer.
Next: Paths