FS FormScript

Expressions

An expression reads values and produces a value. That is all it does.

forms
form loan version = "1.0" {
  field amount   { type = number }
  field term     { type = integer }
  field monthly  { type = number  value = pmt(0.05 / 12, term, amount) }
  field interest { type = number  value = amount * 0.05 }
  field label    { type = string  value = "Repay " + toString(monthly) + " a month" }
}

Every expression returns a value

This is the single most important fact about expressions in FormScript, and it is different from most languages. There are no exceptions.

forms-fragment
field a { type = number  value = 1 / 0 }
field b { type = string  value = "abc" + 1 }
field c { type = string  value = substring("hello", -1) }

None of those three is an error in the program. Each of them produces a value — the specification calls it the error value — and the field that holds it is invalid. The document still runs; the transaction simply does not commit.

So there is no try, no catch, and no way to write a program that stops halfway. A form that cannot be answered is a form that says so.

The operators

Operator What it does
+ adds numbers, or joins strings
- * / % arithmetic; / always gives a number
== != equality
< <= > >= ordering
and or ! logic, and it is three-valued
in is this value in that list
notIn is this value not in that list
matches does this string match that pattern
is between is this value between those two
?? the first operand that is not absent
? : a conditional

Arithmetic

forms-fragment
field total    { type = number value = price * quantity }
field share    { type = number value = total / count }
field remainder{ type = integer value = total % 3 }

/ always produces a number, even when both operands are integers: 6 / 2 is 3.0, a number, not an integer. That is deliberate — division that sometimes gives an integer and sometimes a decimal is a bug waiting to happen.

Strings

+ joins strings. It does not convert anything:

forms-fragment
field greeting { type = string value = "Hello, " + name }
field count    { type = string value = "You have " + toString(len(items)) + " items" }

"Hello, " + 36 is an error value, because a string and a number do not agree. You ask for the conversion: toString(36).

Comparison and logic

forms-fragment
field adult     { type = boolean value = age >= 18 }
field affordable{ type = boolean value = income > 10000 and credit == "good" }
field missing   { type = boolean value = not (first_name == "") }

and, or and ! are three-valued, which is the next section.

Lists and patterns

forms-fragment
field in_gb   { type = boolean value = country in ["GB", "IE", "FR"] }
field adult   { type = boolean value = age is between 18 and 65 }
field looks_ok{ type = boolean value = postcode matches "^[A-Z]{2}[0-9]{2}$" }

is between is sugar for age >= 18 and age <= 65.

Choosing

forms-fragment
field band { type = string value = income > 100000 ? "high" : "standard" }

? : is a conditional expression: if the condition is true, the first branch; if it is false, the second. What if it is unknown? Then the condition is not true and not false, and the specification says exactly what happens — section 6 covers it.

Falling back

forms-fragment
field name_or_blank { type = string value = name ?? "" }

?? yields its left operand unless that operand is absent, in which case it yields the right one. It is the one operator that treats absence as a thing you can ask about.

Reading an expression

The reference is a path: a name, or a name and a path into it.

forms-fragment
field a { type = string value = name }
field b { type = string value = address.street }
field c { type = string value = applicant.address.city }

The rules for a dotted path are in the specification (Spec-6.20): a dotted path names a field when the form has such a field, and is a member access on the value it starts at otherwise. In a document with a field address.street, the path address.street is that field.

There are also the $ names, which come from outside the form:

The name What it is
$form.output the output document
$form.valid whether the form is valid
$now the timestamp of the current transaction
$this the current element inside a comprehension
$env the environment the runtime was given

$this is the one you will meet in section 8.