-
Notifications
You must be signed in to change notification settings - Fork 0
Flxble.Templating: The language
The flxble's templating language is a dynamically-typed ML-like (F#-ish) language and consists of:
- Values .. The normal forms.
- Expressions .. Things which are not values but evaluate to values.
- Statements .. Instructions which the engine executes. Statements are not expressions nor values.
There are 10 kinds of values.
- Boolean values
- Integer values
- Float values
- String values
- Null values .. Indicates an error. Can contain an error message.
- Date values .. Represents a date time.
- TimeSpan values .. The difference between two date values.
- Array values
- Record values .. A hashtable.
- Function values
An expression evaluates to a value. If there is an error, it will evaluate to a Null value containing the error message. If there is an error before evaluating the expression (e.g. when the argument of a function call raises an error), it will be passed to outer expressions so you can always see the error message explaining the root cause.
A literal expression creates a value.
- Boolean literals ...
true,false. - Integer literals ...
0,-1,42, etc. - Float literals ...
4.2,1.2e+3,inf,nan, etc. - String literals ...
"Lorem ipsum", etc. - Null literals ...
null,()
Arrays, Records, and Functions have their own expressions to create their values and will be introduced later.
Dates and TimeSpans do not have literal expressions nor their own expressions. Instead, you can use the following functions:
Creates a new Date value.
def d = date.new {
year = 2019,
month = 1,
day = 1,
hour = 12,
minute = 34,
second = 56,
millisecond = 789
}The fields year, month, and day are required. The other fields are optional and will be 0 if omitted.
def t = timespan.new {
day = 1,
hour = 23,
minute = 45,
second = 6,
millisecond = 789
}All the fields are optional and will be 0 if omitted.
A valid variable has a form of [a-zA-z_]+[a-zA-z0-9_]*. e.g. foo1, _foo_bar.
Variables can be defined by Let-expressions and Define-statements.
A variable evaluates to the corresponding value. If not defined, it evaluates to Null value with an error message (the variable '<name>' does not exist).
A let expression creates a local variable to be used inside a scope.
let <variable_name> = <expression> in <expression>e.g. let x = 41 in let y = 1 in x + y evaluates to 42.
A lambda abstraction creates a function value.
fun <arg_name>+ -> <expression> e.g. fun x y -> x + y evaluates to a function that computes the sum of the two input values.
let <variable_name> <arg_name>+ = <expression>is a syntax sugar of
let <variable_name> = fun <arg_name>+ -> expressionAn application expression applies arguments to a function.
<expression> <expression>+e.g. (fun x y -> x + y) 21 21 and let f x y = x + y in f 21 21 both evaluate to 42.
Application expression supports partial application; for example, (fun x y -> x + y) 21 evaluates to a new function that adds 21 to the input value:
let f x y = x + y in
let g y = f 21 in
g 21A binary operator expression is just like the application expression but in an infix form.
e.g. 1 + 2 evaluates to 3 by the definition of +.
You can use an operator as a function by enclosing it by `.
e.g. `+` 1 2 evaluates to 3.
An array expression creates an array value.
[ <expression>, <expression>, ... , <expression> ]e.g. [1+2, 3+4, 5+6] evaluates to [3,7,11].
A record expression creates a record value.
{ <field_name> = <expression>, ..., <field_name> = <expression> }e.g. { a = 1 + 2, b = true && false } evaluates to { a = 3, b = false }.
A record expression creates a new record value from an existing record value.
The original record value will not be changed.
{ <expression> with <field_name> = <expression>, ..., <field_name> = <expression> }e.g. if we have def x = { a = 3, b = false }, then { x with c = 42.0 } evaluates to { a = 3, b = false, c = 42.0 }.
An indexer access expression returns the value associated with the specified key.
<expression>.[<expression>]e.g. [1,2,3].[0] evaluates to 1, and { a = 1, b = "foo" }.["b"] evaluates to "foo".
A Date value accepts the following keys: year, month, day, hour, minute, second, millisecond.
Similarly, a TimeSpan value accepts the following keys: day, hour, minute, second, millisecond.
They all return an integer value.
e.g. You can get the current year in an integer value with date.now.year.
A member access expression is just a syntax sugar of an indexer access expression with a string key.
<expression>.<field_name>e.g. { a = 1, b = "foo" }.b evaluates to "foo".
An if expression first evaluates the condition, then returns one of the two expressions.
if <expression> then <expression> else <expression>e.g. if the_ultimate_anwser = 42 then "foo" else "bar" evaluates to "foo".