2018-11-17 13:01:37 -06:00
|
|
|
+++
|
|
|
|
title = "UCG Expressions"
|
|
|
|
weight = 3
|
|
|
|
sort_by = "weight"
|
|
|
|
in_search_index = true
|
|
|
|
+++
|
2018-11-18 14:23:35 -06:00
|
|
|
Ucg expressions can reference a bound name, do math, concatenate lists or
|
|
|
|
strings, copy and modify a struct, or format a string.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
Symbols
|
|
|
|
-------
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
Many ucg expressions or statements use a symbol. A symbol might be used as
|
|
|
|
either a name for a binding or a name for a field. Symbols must start with an
|
|
|
|
ascii letter and can contain any ascii letter, number, `_`, or `-` characters.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
Selectors
|
|
|
|
------
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
A UCG selector references a bound value by name. They can descend into tuples
|
|
|
|
or lists or refer to symbols in imported files. They start with a symbol followed
|
|
|
|
optionally by a list of other symbols or numbers separated by `.` characters to
|
|
|
|
reference subfields or indexes in a list.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
You can reference a field in a tuple by putting the field name after a dot.
|
|
|
|
Lists are always 0 indexed. You can index into a list by referencing the index
|
|
|
|
after a `.`.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
let tuple = {
|
|
|
|
inner = {
|
|
|
|
field = "value",
|
|
|
|
},
|
|
|
|
list = [1, 2, 3],
|
|
|
|
};
|
|
|
|
|
|
|
|
// reference the field in the inner tuple in our tuple defined above.
|
|
|
|
tuple.inner.field;
|
|
|
|
|
|
|
|
// reference the field in the list contained in our tuple defined above.
|
|
|
|
tuple.list.0;
|
|
|
|
```
|
|
|
|
|
|
|
|
### The environment Selector
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
There is a special selector in ucg for obtaining a value from the environment.
|
|
|
|
The `env` selector references the environment variables in environment at the
|
|
|
|
time of the build. You reference an environment variable just like it was in a
|
|
|
|
tuple. Attempting to reference a variable that doesn't exist will be a compile
|
|
|
|
error.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
let env_name = env.DEPLOY_ENV;
|
|
|
|
```
|
|
|
|
|
|
|
|
Binary Operators
|
|
|
|
----------
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
UCG has a number of binary infix operators. Some work only on numeric values and others
|
2018-11-17 13:01:37 -06:00
|
|
|
work on more than one type.
|
|
|
|
|
|
|
|
### Numeric Operators
|
|
|
|
|
|
|
|
ucg supports the following numeric operators, `+`, `-`, `*`, `/` Each one is type safe
|
|
|
|
and infers the types from the values they operate on. The operators expect both the
|
|
|
|
left and right operands to be of the same type.
|
|
|
|
|
|
|
|
```
|
|
|
|
1 + 1;
|
|
|
|
1.0 - 1.0;
|
|
|
|
```
|
|
|
|
|
|
|
|
### Concatenation
|
|
|
|
|
|
|
|
The `+` operator can also do concatenation on strings and lists. As with the numeric
|
|
|
|
version both sides must be the same type, either string or list.
|
|
|
|
|
|
|
|
```
|
|
|
|
"Hello " + "World"; // "Hello World"
|
|
|
|
[1, 2] + [3]; // [1, 2, 3]
|
|
|
|
```
|
|
|
|
|
|
|
|
### Comparison Operators
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
UCG supports the comparison operators `==`, `!=`, `>=`, `<=`, `<`, and `>`.
|
|
|
|
They all expect both sides to be of the same type.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
The `>`, `<`, `>=`, and `>=` operators are only supported on numeric types
|
|
|
|
(i.e. int, and float).
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
1 > 2; // result is false
|
|
|
|
2 < 3; // result is true
|
|
|
|
10 > "9"; // This is a compile error.
|
|
|
|
(1+2) == 3;
|
|
|
|
```
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
The equality operators `==` and `!=` are supported for all types and will
|
|
|
|
perform deep equal comparisons on complex types.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
let tpl1 = {
|
|
|
|
foo = "bar",
|
|
|
|
one = 1
|
|
|
|
};
|
|
|
|
let tpl2 = {
|
|
|
|
foo = "bar",
|
|
|
|
one = 1
|
|
|
|
};
|
|
|
|
tpl1 == tpl2; // returns true
|
|
|
|
let tpl2 = {
|
|
|
|
foo = "bar",
|
|
|
|
one = 1
|
|
|
|
duck = "quack",
|
|
|
|
};
|
|
|
|
tpl1 == tpl3; // returns false
|
|
|
|
```
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
Because tuples are an ordered set both tuples in a comparison must have their
|
|
|
|
fields in the same order to compare as equal.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
#### Operator Precedence
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
UCG binary operators follow the typical operator precedence for math. `*` and
|
|
|
|
`/` are higher precendence than `+` and `-` which are higher precedence than
|
|
|
|
any of the comparison operators.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
Copy Expressions
|
|
|
|
----------------
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
UCG expressions have a special copy expression for tuples. These faciliate a
|
|
|
|
form of data reuse as well as a way to get a modified version of a tuple. Copy
|
|
|
|
expressions start with a selector referencing a tuple followed by braces `{}`
|
|
|
|
with `name = value` pairs separated by commas. Trailing commas are allowed.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
Copied expressions can change base fields in the copied tuple or add new
|
|
|
|
fields. If you are changing the value of a base field in the copy then the new
|
|
|
|
value must be of the same type as the base field's value. This allows you to
|
|
|
|
define a base "type" of sorts and ensure that any modified fields stay the
|
|
|
|
same.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
let base = {
|
|
|
|
field1 = "value1",
|
|
|
|
field2 = 100,
|
|
|
|
field3 = 5.6,
|
|
|
|
};
|
|
|
|
|
|
|
|
let overridden = base{
|
|
|
|
field1 = "new value"
|
|
|
|
};
|
|
|
|
|
|
|
|
let expanded = base{
|
|
|
|
field2 = 200,
|
|
|
|
field3 = "look ma a new field",
|
|
|
|
};
|
|
|
|
|
|
|
|
let bad = base{
|
|
|
|
field1 = 300, // Error!!! must be a string.
|
|
|
|
};
|
|
|
|
|
|
|
|
```
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
There is a special selector that can be used in a copy expression to refer to
|
|
|
|
the base tuple in a copy called `self`. `self` can only be used in the body of
|
|
|
|
the copy.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
let nestedtpl = {
|
|
|
|
field1 = "value1",
|
|
|
|
inner = {
|
|
|
|
field2 = 2
|
|
|
|
inner = {
|
|
|
|
field3 = "three",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let copiedtpl = nestedtpl{
|
|
|
|
inner = self.inner{
|
|
|
|
inner = self.inner{
|
|
|
|
field4 = 4,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
Format Expressions
|
|
|
|
----------
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
UCG has a format expression that has a limited form of string templating. A
|
|
|
|
format expression starts with a string followed by the `%` operator and a list
|
|
|
|
of arguments in parentheses separated by commas. Trailing commas are allowed.
|
|
|
|
The format string should have `@` characters in each location where a value
|
|
|
|
should be placed. Any primitive value can be used as an argument.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
"https://@:@/" % (host, port)
|
|
|
|
```
|
|
|
|
|
|
|
|
Conditionals
|
|
|
|
----------
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
UCG supports a limited conditional expression called a select. A select
|
|
|
|
expression starts with the `select` keyword and is followed by a an expression
|
|
|
|
resolving to a string naming the field to select, an expression resolving to
|
|
|
|
the default value, and finally a tuple literal to select the field from. If the
|
|
|
|
field selected is not in the tuple then the default value will be used.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
let want = "baz";
|
|
|
|
|
|
|
|
// field default
|
|
|
|
select want, "quux", {
|
|
|
|
baz = "foo",
|
|
|
|
fuzz = "bang",
|
|
|
|
}; // result will be "foo"
|
|
|
|
|
|
|
|
// field default
|
|
|
|
select "quack", "quux", {
|
|
|
|
baz = "foo",
|
|
|
|
fuzz = "bang",
|
|
|
|
}; // result will be "quux"
|
|
|
|
```
|
|
|
|
|
|
|
|
Macros
|
|
|
|
-----
|
|
|
|
|
2018-11-18 14:23:35 -06:00
|
|
|
Macros look like functions but they are resolved at compile time and
|
|
|
|
configurations don't execute so they never appear in output. Macros do not
|
|
|
|
close over their environment so they can only reference values defined in their
|
|
|
|
arguments. They can't refer to bindings or other macros defined elsewhere. They
|
|
|
|
are useful for constructing tuples of a certain shape or otherwise promoting
|
|
|
|
data reuse. You define a macro with the `macro` keyword followed by the
|
|
|
|
arguments in parentheses, a `=>`, and then a tuple literal.
|
2018-11-17 13:01:37 -06:00
|
|
|
|
|
|
|
```
|
|
|
|
let mymacro = macro (arg1, arg2) => {
|
|
|
|
host = arg1,
|
|
|
|
port = arg2,
|
|
|
|
connstr = "couchdb://@:@" % (arg1, arg2),
|
|
|
|
}
|
|
|
|
|
|
|
|
let my_dbconf = mymacro("couchdb.example.org", "9090");
|
|
|
|
|
|
|
|
let my_dbhost = dbconf.host;
|
|
|
|
```
|
|
|
|
|
2018-11-17 22:28:25 -06:00
|
|
|
Next: <a href="/reference/statements">Statements</a>
|