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