Sentinel: Introduction To Sentinel Language – Part-5

Share At:

Abstract Network Animation. Hd1080 Seamless Stock Footage Video (100%  Royalty-free) 16066732 | Shutterstock

Sentinel Language Specification

This is the specification for the Sentinel policy language.

The Sentinel language is designed with policy enforcement in mind. It is dynamically typed and garbage collected and has explicit support for rule construction representing boolean logic.

The language is designed to be easy to learn and use by non-programmers. It is expected to be embedded within applications.

Source code representation

Source code is Unicode text encoded in UTF-8. A “character” referenced in this document refers to a Unicode code point. Each code point is distinct: upper and lower case letters are different characters.

The underscore character _ (U+005F) is considered a “letter”.

letter        = unicode_letter | "_" .
decimal_digit = "0" … "9" .
octal_digit   = "0" … "7" .
hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .

Declarations and Scope

A declaration binds an identifier to a value. An identifier is declared at the point it is first assigned. An assignment is considered a first assignment if the identifier hasn’t already been previously declared in the current scope or any parent scopes.

The scope of an identifier is the extent of source text in which the identifier denotes the specified value. Sentinel is lexically scoped using blocks.

An identifier declared in a block may be redeclared in an inner block. While the identifier of the inner declaration is in scope, it denotes the value assigned in the inner declaration.

Blocks

A block is a possibly empty sequence of statements within matching brace brackets.

Block = "{" StatementList "}" .
StatementList = { Statement ";" } .

Blocks nest and affect scoping.

In addition to explicit blocks in the source code, there are implicit blocks:

  1. The universe block encompasses all source text.
  2. Each program has a program block containing all source text for that program.
  3. Each “any”, “all”, and “for” statements is considered to be in its own implicit block. “if” does not create an implicit block.

Lexical Elements

Comments

Comments are sections of source text used for documentation.

# Single line comment
// Single line comment
/* multi
line
    comment */

Three forms of comments are supported: two single-line forms and one multi-line form. A single-line comment begins with the token // or #. Everything between the starting token and the end of the line is ignored.

A multi-line comment begins with the token /* and ends with the token */. Everything between /* and */ is ignored.

A comment may not start inside a string literal or inside another comment.

A multi-line comment containing no newlines acts like a space.

Identifiers

Identifiers name program entities such as rules, variables, and functions. An identifier is a sequence of one or more letters and digits. The first character in an identifier must be a letter.

identifier = letter { letter | unicode_digit } .
a
_a
_A
αβ

Keywords

The following keywords are reserved and may not be used as identifiers:

all
any
as
break
case
continue
default
else
empty
filter
for
func
if
import
map
param
return
rule
when

Operators and Delimiters

The following character sequences represent operators, delimiters, and other special tokens:

+
-
*
/
%
+=
-=
*=
/=
%=
==
<
>
=
!
!=
<=
>=
( )
[ ]
{ }
,
.
:
;
and
contains
else
in
is
matches
not
or
xor

As a special case, else is both a keyword and operator, depending on the context. See Else Operator for more details

Integer Literals

An integer literal is a sequence of digits representing an integer constant. An optional prefix sets a non-decimal base: 0 for octal, 0x or 0X for hexadecimal. In hexadecimal literals, letters a-f and A-F represents values 10 through 15.

Integers are signed 64-bit values (-9223372036854775808 to 9223372036854775807).

int_lit     = decimal_lit | octal_lit | hex_lit .
decimal_lit = ( "1" … "9" ) { decimal_digit } .
octal_lit   = "0" { octal_digit } .
hex_lit     = "0" ( "x" | "X" ) hex_digit { hex_digit } .
42
0600
0xBadFace
170141183460469231731687303715884105727

Floating-point Literals

A floating-point literal is a decimal representation of a floating-point constant. It has an integer part, a decimal point, a fractional part, and an exponent part. The integer and fractional part comprise decimal digits; the exponent part is an e or E followed by an optionally signed decimal exponent. One of the integer part or the fractional part may be elided; one of the decimal point or the exponent may be elided.

Floating-point numbers are IEEE-754 64-bit floating numbers.

float_lit = decimals "." [ decimals ] [ exponent ] |
            decimals exponent |
            "." decimals [ exponent ] .
decimals  = decimal_digit { decimal_digit } .
exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals .
0.
72.40
072.40  // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5

String Literals

String literals are character sequences between double quotes, as in “bar”. Within the quotes, any character may appear except newline and unescaped double quote. The text between the quotes forms the value of the literal.

A multi-character sequence beginning with a backslash encode values in various formats.

Backslash escapes allow arbitrary values to be encoded as ASCII text. There are four ways to represent the integer value as a numeric constant: \x followed by exactly two hexadecimal digits; \u followed by exactly four hexadecimal digits; \U followed by exactly eight hexadecimal digits, and a plain backslash \ followed by exactly three octal digits. In each case the value of the literal is the value represented by the digits in the corresponding base.

After a backslash, certain single-character escapes represent special values:

\a   U+0007 alert or bell
\b   U+0008 backspace
\f   U+000C form feed
\n   U+000A line feed or newline
\r   U+000D carriage return
\t   U+0009 horizontal tab
\v   U+000b vertical tab
\\   U+005c backslash
\"   U+0022 double quote

The three-digit octal (\nnn) and two-digit hexadecimal (\xnn) escapes represent individual bytes of the resulting string; all other escapes represent the (possibly multi-byte) UTF-8 encoding of individual characters. Thus inside a string literal \377 and \xFF represent a single byte of value 0xFF=255, while ÿ, \u00FF, \U000000FF and \xc3\xbf represent the two bytes 0xc3 0xbf of the UTF-8 encoding of character U+00FF.

A string value is a (possibly empty) sequence of bytes. Strings are immutable: once created, it is impossible to change the contents of a string.

The length of a string s (its size in bytes) can be discovered using the built-in function length. An individual character (of type string) can be accessed by integer indices 0 through length(s)-1.

string_lit       = `"` { unicode_value | byte_value } `"` .
unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value       = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value   = `\` "x" hex_digit hex_digit .
little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
                           hex_digit hex_digit hex_digit hex_digit .
escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | `"` ) .
`abc`                // same as "abc"
`\n
\n`                  // same as "\\n\n\\n"
"\n"
"\""                 // same as `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800"             // illegal: surrogate half
"\U00110000"         // illegal: invalid Unicode code point

Implicit Line Joining

Expressions can be split over more than one line. For example:

a or
b or # This is a comment
c

Implicitly continued lines can have trailing comments. Blank continued lines are allowed.

Whitespace

Whitespace is needed to separate tokens, but no distinction is made between the number and combination of whitespace characters. Whitespace characters can be space (U+0020), horizontal tabs (U+0009), carriage returns (U+000D), and newlines (U+000A).

a or b

a or      b

a or
    b

rule { a or b }

rule {
  a or b }

rule {
  a or
  b
}

Semicolons

The formal grammar uses semicolons “;” as terminators in a number of productions. It is idiomatic Sentinel source to omit semicolons in most cases.

A semicolon is automatically inserted into the token stream immediately after a line’s final token if that token is:

  • An identifier
  • An integer, float, or string literal
  • The keyword breakcontinue, or return
  • The delimiter )], or }

Variables

A variable is a storage location for holding a value.

A variable has a dynamic type, which is the concrete type of the value assigned to the variable at run time. The dynamic type may vary during execution and change as a result of further assignments.

A variable’s value is retrieved by referring to the variable in an expression; it is the most recent value assigned to the variable. If a variable has not yet been declared (initially assigned), it is an error.

x = 7          // x is type int
x = "foo"      // x is now type string

x = y          // error if y is not previously assigned

Undefined

The value denoted by the keyword undefined represents undefined behavior or values. It can be created directly using the keyword undefined. It is also returned as a result of expressions in specified cases.

undefined is a valid operand for any operations. Only undefined or true will result in true. All other operations result in undefined. An exception is if undefined is not reached as a result of short-circuit operations.

undefined OR true       = true
undefined OR false      = undefined
undefined OR undefined  = undefined
undefined AND true      = undefined
undefined AND false     = undefined
undefined AND undefined = undefined
undefined XOR true      = undefined
undefined XOR false     = undefined
undefined XOR undefined = undefined

// Short-circuit examples
false OR true OR undefined   = true
false OR undefined OR true   = true
true AND false AND undefined = false
true AND undefined AND false = undefined

// Non-logical operators
undefined + 5 = undefined
-undefined    = undefined
!undefined    = undefined

If the result of the main rule is undefined, it is treated as false but is indicative of erroneous logic.


Operand

Operands denote the elementary values in an expression. An operand may be a literal, an identifier denoting a variable, rule, or function, or a parenthesized expression.

Operand     = Literal | OperandName | MethodExpr | "(" Expression ")" .
Literal     = BasicLit | CompositeLit | FunctionLit | RuleLit .
BasicLit    = int_lit | float_lit | string_lit .
OperandName = identifier | QualifiedIdent.

Primary Expressions

Primary expressions are the operands for unary and binary expressions.

PrimaryExpr =
    Operand |
    PrimaryExpr Selector |
    PrimaryExpr Index |
    PrimaryExpr Slice |
    PrimaryExpr Arguments |
    PrimaryExpr ( "is empty" | "is not empty" ).

Selector  = "." identifier .
Index     = "[" Expression "]" .
Slice     = "[" [ Expression ] ":" [ Expression ] "]" |
            "[" [ Expression ] ":" Expression ":" Expression "]" .
Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
x
2
(s + ".txt")
f(3.1415, true)
m["foo"]
s[i : j + 1]
obj.color
f.p[i].x()
x is empty
x is not empty

Null

The reserved word null denote the singleton value null. Null represents the explicit absense of a value. Behavior of null within expressions is specified explicitly for each expression.


Booleans

Boolean Literals

The reserved words true and false denote objects that represent the boolean values true and false, respectively. These are boolean literals.

BoolLit = "true" | "false" .

Boolean Expressions

Boolean expressions are expressions that must result in a boolean value. Any other value type becomes the undefined value.

BoolExpr = Expression .

List Literals

A list literal denotes a list, which is an integer indexed collection of values.

ListLit     = "[" [ ElementList [ "," ] ] "]" .
ElementList = Element { "," Element } .
Element     = Expression | LiteralValue .

A list may contain zero or more values. The number of values in a list is its length.

A list has a set of indices. An empty list has an empty set of indices. A non-empty list has the index set {0...n - 1} where n is the length of the list. Attempting to access a list using an index that is not a member of its set of indices results in the undefined value.


Map Literals

A map literal denotes a map, which is an unordered group of elements indexed by a set of unique keys.

MapLit           = "{" [ KeyedElementList [ "," ] ] "}" .
KeyedElementList = KeyedElement { "," KeyedElement } .
KeyedElement     = Element ":" Element .

Keys can only be a boolean, numeric, or string type.

The value of a non-existent key is the undefined value.


Function Literals

A function literal represents a function.

FunctionLit    = "func" Function .
Function       = Parameters FunctionBody .
FunctionBody   = Block .
Parameters     = "(" [ IdentifierList [ "," ] ] ")" .
IdentifierList = identifier { "," identifier } .
func(a, b) { return b }

Function literals are only allowed in the file scope. They may refer to variables defined in surrounding blocks.

A function must terminate with a return statement. If a function has no meaningful return value, it should return undefined.


Rule Expressions

A rule is an expression that is evaluated lazily and the result is memoized.

If the optional “when” predicate is present, the rule is evaluated only when the “when” boolean expression results in true. If the predicate is false, the rule is not evaluated and returns true. The predicate is evaluated when the rule would be evaluated; it is also lazy and memoized in the same way.

RuleExpr = "rule" [ "when" BoolExpr ] "{" Expr "}" .
rule { x is y }

rule {
    x == "value" or
    y == "other"
}

rule when x is y { y > 42 }

rule { map ["a", "b", "c"] as _, id {
    { "id": id }
}}

Selectors

For a primary expression x, the selector expression x.f denotes the field f of the value x. The identifier f is called the selector. The type of the selector expression is the type of the selector.

As a special case, selectors can be reserved words and keyword operators, but cannot be any other non-identifier element.

Selectors are used to access data from imports. The first primary expression x in x.f denotes the import name. The field f is the selector to access data from the import.

Selectors may also be used to access map data with static keys. They are syntactic sugar over index expressions. math.pi is equivalent to math["pi"] and exhibit the same limitations and behavior as an index expression. Selectors cannot, however, be used to assign values to map data.

Selectors on undefined result in undefined.

math.pi
time.pst.hour

Slice Expressions

Slice expressions construct a substring or list from a string or list.

The primary expression

a[low : high]

constructs a substring or list. The indices low and high select which elements of operand a appear in the result. The result has indices starting at 0 and length equal to high - low.

a = [1, 2, 3, 4, 5]
b = a[1:4]           // [2, 3, 4]

For convenience, any of the indices may be omitted. A missing low index defaults to zero; a missing high index defaults to the length of the sliced operand:

a[2:]  // same as a[2 : length(a)]
a[:3]  // same as a[0 : 3]
a[:]   // same as a[0 : length(a)]

The indices are in range if 0 <= low <= high <= length(a), otherwise they are out of range. If the indices are out of range at run time, the result is the undefined value.

If a is the value null, the result is the undefined value.

If a is any other value type, it is an error.


Calls

Given an expression f where f is a function value:

f(a1, a2, … an)

calls f with arguments a1, a2, ... an. The type of the expression is the result of f. The arguments are evaluated left to right. Arguments are passed by value.


Operators

Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .

binary_op  = logical_op | set_op | rel_op | add_op | mul_op | else_op .
logical_op = "and" | "or" | "xor" .
set_op     = ["not"] ( "contains" | "in" ).
rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" |
             "is" | "is not" | "matches" | "not matches" .
add_op     = "+" | "-" .
mul_op     = "*" | "/" | "%" .
else_op    = "else" .
unary_op   = "+" | "-" | "!" | "not" .

Operator Precedence

Unary operators have the highest precedence.

Precedence    Operator
    6          *  /  %
    5          +  -
    4          else
    3          ==  !=  <  <=  >  >= "is" "is not" "matches" "contains" "in"
    2          and
    1          or  xor

Binary operators of the same precedence associate from left to right. For instance, x / y z is the same as (x / y) z.

Arithmetic Operators

Arithmetic operators apply to numeric values and yield a result of the same type when both operands are the same.

Arithmetic operations between integer and floating-point types result in a floating-point value with the integer operand treated as a floating-point value for purposes of calculation.

Arithmetic operations between numeric values (integer, floating-point) and non-numeric values (strings, lists) are not permitted.

All five supported arithmetic operators (+, -, *, /, %) apply to both integer and floating-point types; + also applies to strings.

+    sum                    integers, floats, strings, lists
*    difference             integers, floats
*    product                integers, floats
/    quotient               integers, floats
%    remainder              integers, floats

Integer operators

For two integer values x and y, the integer quotient q = x / y and remainder r = x % y satisfy the following relationships:

x = q*y + r  and  |r| < |y|

with x / y truncated towards zero.

 x     y     x / y     x % y
 5     3       1         2
-5     3      -1        -2
 5    -3      -1         2
-5    -3       1        -2

As an exception to this rule, if the dividend x is the most negative value for the int type of x (-9223372036854775808), the quotient q = x / -1 is equal to x (and r = 0).

If the divisor is a constant, it must not be zero. If the divisor is zero at run time, an error occurs.

Integer overflow

For signed integers, the operations +-, and * may legally overflow and the resulting value exists and is deterministically defined by the signed integer representation, the operation, and its operands. No exception is raised as a result of overflow.

Floating-point operators

For floating-point numbers, +x is the same as x, while -x is the negation of x. The result of a floating-point division by zero is not specified beyond the IEEE-754 standard.

String Concatenation

Strings can be concatenated using the + operator or the += assignment operator:

x = "hi"
y = "hello"
x = x + ", " + y     // "hi, hello"
x += " and good bye" // "hi, hello and good bye"

String addition creates a new string by concatenating the operands.

List Concatenation

Lists can be concatenated using the + operator or the += assignment operator:

x = [1, 2]
x = x + [2, 3]    // [1, 2, 2, 3]
x += [4]          // [1, 2, 2, 3, 4]

List addition creates a new list by concatenating the operands.

Comparison Operators

Comparison operators compare two operands and yield a boolean value.

==       equal
!=       not equal
<        less
<=       less or equal
>        greater
>=       greater or equal
"is"     equal
"is not" not equal

In any comparison, the two operands must be equivalent types. The only exception are integers and floats, which are considered numeric and may be compared. Any comparison between other non-matching types results in the undefined value.

The equality operators ==!=is, and is not apply to operands that are comparable. The ordering operators <<=>, and >= apply to operands that are ordered. The behavior of is with == and is not with != is identical. These terms and the result of the comparisons are defined as follows:

  • Boolean values are comparable. Two boolean values are equal if they are either both true or both false.
  • Integer values are comparable and ordered, in the usual way.
  • Floating point values are comparable and ordered, as defined by the IEEE-754 standard.
  • An integer compared with floating point value treats the integer as the converted floating point value.
  • String values are comparable and ordered, lexically byte-wise.
  • Lists are comparable. Lists are equal if they are of equal length and their corresponding elements are comparable and equal.
  • Maps are comparable. Maps are equal if they are of equal length and both their corresponding keys and values are comparable and equal.

If either operand is the undefined value, the result of the expression is the undefined value.

Emptiness Comparisons

Checking the emptiness of an object can be achieved by using one of the emptiness expressions, is empty or is not empty. Although similar to Comparison Operators in that they yield a boolean value and read as though a comparison is taking place, both is empty and is not empty are evaluated as a multi-word expression.

An emptiness comparison can only be performed against collections, strings or undefined, with the same rules as the built-in length function.

"" is empty             // true
"foo" is empty          // false
[] is empty             // true
[1] is empty            // false
{} is empty             // true
{"a": "b"} is empty     // false

"" is not empty         // false
"foo" is not empty      // true
[] is not empty         // false
[1] is not empty        // true
{} is not empty         // false
{"a": "b"} is not empty // true

undefined is empty      // undefined
undefined is not empty  // undefined

Logical Operators

Logical operators apply to boolean values and yield a boolean result.

Logical operators are evaluated left-to-right and perform short-circuit logic. The right-hand side is not guaranteed to be evaluated if a short-circuit can be performed.

and    conditional AND    p and q  is  "if p then q else false"
or     conditional OR     p or q   is  "if p then true else q"
xor    conditional XOR    p xor q  is  "if p and not q or not p and q then true"
!      NOT                !p       is  "not p"
not    NOT                !p       is  "not p"

Set Operators

The set operators contains and in test for set inclusion for lists and maps, and substring inclusion for strings.

Set operators may be negated by prefixing the operator with notnot contains and not in. This is equivalent to wrapping the binary expression in a unary not but results in a more readable form.

contains tests if the left-hand collection contains the right-hand value. For lists, this tests equality of any value. For maps, this tests equality of any key. For strings, this tests for substring existence.

in tests if the right-hand collection contains the left-hand value. The behavior is equivalent to contains.

The collection must be a list, map, or string. If it is the undefined value, the result is the undefined value. For any other value, the result is an error.

[1, 2, 3] contains 2            // true
[1, 2, 3] contains 5            // false
[1, 2, 3] contains "value"      // false
[1, 2, 3] not contains "value"  // true

{ "a": 1, "b": 2 } contains "a"     // true
{ "a": 1, "b": 2 } contains "c"     // false
{ "a": 1, "b": 2 } contains 2       // false
{ "a": 1, "b": 2 } not contains 2   // true

"test" contains "est"     // true
"test" contains "best"    // false
"test" in "testing"       // true
"best" in "testing"       // false

Matches Operator

The matches operator tests if a string matches a regular expression.

The matches operators may be negated by prefixing the operator with notnot matches. This is equivalent to wrapping the binary expression in a unary not but results in a more readable form.

The left-hand value is the string to test. The right-hand value is a string value representing a regular expression.

The syntax of the regular expression is the same general syntax used by Python, Ruby, and other languages. More precisely, it is the syntax accepted by RE2. The regular expression is not anchored by default; any anchoring must be explicitly specified.

"test"   matches "e"            // true
"test"   matches "^e"           // false
"TEST"   matches "test"         // false
"TEST"   matches "(?i)test"     // true
"ABC123" matches "[A-Z]+\\d+"   // true
"test"   not matches "e"        // false

If either operand is undefined, the result is the undefined value. For any other non-string value, the result is an error.

Else Operator

Else operators can be used to provide a default value for an expression that may evaluate to the undefined value. Else operators return their left-hand value unless it is undefined, otherwise they return their right-hand value.

foo() else 42
foo.bar else ""
config["bad-key"] else null

Quantifier Expressions (any, all, filter, map)

Quantifiers are expressions that apply a predicate to a collection (list or map). The purpose of the quantifier is to produce a result based on its particular type.

any and all expressions are existential and universal quantifiers, respectively. any expressions are equivalent to a chain of or and all expressions are equivalent to a chain of and. Both expressions implement short-circuiting equivalent to logical operators.

The map expression is an apply-to-all quantifier and returns a new list for all values in the input collection. The output list will be the same length as the input collection.

The filter expression is a subset quantifier and returns the subset of all values in the input collection for which the supplied predicate applies. This will be zero or more elements, up to the length of the input.

The body of a quantifier expression is a boolean expression.

any returns the boolean value true if any value in the collection expression results in the body expression evaluating to true. If the body expression evaluates to false for all values, the any expression returns false.

all returns the boolean true if all values in the collection expression result in the body expression evaluating to true. If any value in the collection expression result in the body expression evaluating to false, the all expression returns false.

For empty collections, any returns false and all returns true.

map always returns a list, regardless of if the input collection is a list itself; maps (as in the data type) also return lists when processed through map. Each element is the result of the supplied expression body.

filter returns a list or map, the same type as its input collection. Only the elements for which the expression body evaluated to true will be returned. If an iteration of the expression body results in undefined, the entire result set is undefined.

When the collection is a list, the first identifier is assigned the index and the second identifier is assigned the value. If only one identifier is specified, it is assigned the value.

When the collection is a map, the first identifier is assigned the key and the second identifier is assigned the value. If only one identifier is specified, it is assigned the key.

QuantExpr = QuantOp Expression "as" [ identifier "," ] identifier "{" BoolExpr "}" .
QuantOp   = "all" | "any" | "filter" | "map" .
all group.tasks as t { t.driver is "vmware" } // true if every iterations is true
any group.tasks as t { t.driver is "vmware" } // true if any iteration is true
map group.tasks as t { { "driver": t.driver } } // [{"driver": "vmware"}, ...]
filter group.tasks as t { t.driver is "vmware" } // subset of tasks that is true

Statements

Expression Statements

Function call expressions may exist in the statement context.

ExpressionStmt = Expression .

Assignments

Assignments set the value of an expression to the value of another expression.

Assignment = AssignExpr assign_op Expression .
AssignExpr = identifier | IndexExpr .
assign_op  = [ add_op | mul_op ] "=" .

The assignment x op= y is equivalent to x = x op (y) for supported values of op.

a = 1
b[12] = 42
c["key"] = "value"

a *= 12
b[12] += 8

Assignments to lists and maps can be carried out using index expressions, with the operands of the index expression being evaluated alongside the right hand side expression in a right-to-left (RHS, LHS) order.

In addition to the rules detailed in the section describing their use, the following rules apply when assigning to maps or lists using index expressions:

  • The variable must exist and be a list or map. Attempts to use an index assignment on an unknown variable, or a variable that not a list or map, results in a runtime error.
  • Assigning to a list or map index that already exists overwrites that index’s data with evaluated right hand side’s value.
  • Assigning to an unknown key in a map creates a key for that map with the evaluated right hand side’s value assigned to that key.
  • Attempting to assign a value to a list index that is out of range results in a runtime error.

If Statements

If statements specify the conditional execution of two branches according to the value of a boolean expression. If the expression evaluates to true, the “if” branch is executed, otherwise, if present, the “else” branch is executed.

IfStmt = "if" BoolExpr Block [ "else" ( IfStmt | Block ) ] .
if x < y {
    return x
} else if x > z {
    return z
} else {
    return y
}

Case Statements

Case statements specify a selection control mechanism to conditionally execute a branch based on expression equality.

Each clause that wishes to provide an expression must use the “when” keyword. Multiple expressions can be provided, separated with a comma (“,”).

There can be one, optional, “else” clause within a case statement. The “else” clause is executed if all other clauses failed to run.

The clauses are evaluated in the order they are listed, with the first successful evaluation being executed.

case statement can optionally provide an expression. If no expression is provided, it is the equivalent of writing case true {.

CaseStmt = "case" [ Expression ] "{" { CaseWhenClause } "}" .
CaseWhenClause = CaseWhenCase ":" StatementList .
CaseWhenCase = "when" Expression { "," Expression } | "else" .
case x {
when y, z:
    return true
else:
    return false
}

case {
when x > 42:
    return true
else:
    return false
}

For Statements

for statement specifies repeated execution of a block.

The expression must evaluate to a list or a map.

When iterating over a list, the first identifier is assigned the index and the second identifier is assigned the value. If only one identifier is specified, it is assigned the value.

When iterating over a map, the first identifier is assigned the key and the second identifier is assigned the value. If only one identifier is specified, it is assigned the key.

ForStmt = "for" Expressions "as" [ identifier "," ] identifier Block .
for [1, 2, 3] as v { count += v } // basic sum

for [1, 2, 3] as idx, v {
    if idx > 1 { count += v }
}

data = { "a": 12, "b": 32 }
for data as k    { count += data[k] } // sum map values
for data as k, v { count += v }

Break Statements

break statement terminates execution of the innermost for statement within the same function.

BreakStmt = "break" .
// Will only print "1"
for [1, 2, 3] as v {
    print(v)
    break
}

Continue Statements

continue statement begins the next iteration of the innermost for loop. The for loop must be within the same function.

ContinueStmt = "continue" .
// Will print 1, 3
for [1, 2, 3] as v {
    if v == 2 {
        continue
    }

    print(v)
    break
}

Return Statements

A return statement in a function F terminates the execution of F and provides the result value.

ReturnStmt = "return" Expression .

A function must terminate with a return statement.

The return statement can be invoked at any time during a function to terminate execution.


Built-in Functions

Built-in functions are predeclared. They are called like any other function.

Length

The built-in function length returns the length of a collection of string.

The length of a string is the number of bytes in the string. It is not necessarilly the number of characters.

The length of a collection is the number of elements in that collection.

The length of undefined is undefined.

Collections

List Append

The built-in function append appends a value to the end of a list in-place.

Appending to a non-list value results in an immediate fail.

The return value of append is always the undefined value.

append([1,2], 3)      // [1, 2, 3]
append(1, 3)          // error()
append(undefined, 3)  // error()
append([], undefined) // [undefined] (a list containing `undefined`)

Map Delete

The built-in function delete deletes elements from a map by key. The map is modified in-place.

Deleting a key that does not exist does nothing.

Calling delete for a non-map value results in an immediate fail.

The return value of delete is always the undefined value.

data = { "a": 2, "b": 3 }
delete(data, "a")            // data is now { "b": 3 }
delete(data, "c")            // data is unchanged
delete(1, "a")               // error()
delete(undefined, "b")       // error()

Keys and Values

The built-in function keys and values return the keys and values of a map, respectively. The return value is a list. Both functions return values in an unspecified order.

The keys or values of undefined is undefined.

data = { "a": 2, "b": 3 }
keys(data)       // ["b", "a"]
values(data)     // [2, 3]

Range

The built-in function range returns a list of numbers in a range.

There are three ways to call this function:

range(end)
range(start, end)
range(start, end, step)

The start is inclusive, the end is exclusive.

If start is not provided, it defaults to 0. If step is not provided, it defaults to 1.

range(5)           // [0,1,2,3,4]
range(1, 5)        // [1,2,3,4]
range(1, 5, 2)     // [1,3]
range(0, -3, -1)   // [0,-1,-2]

Type Conversion

The built-in functions intfloatstring, and bool convert a value to a value of that type according to the rules below.

For int:

  • Integer values are unchanged
  • String values are converted according to the syntax of integer literals
  • Float values are rounded down to their nearest integer value
  • Boolean values are converted to 1 for true, and 0 for false

For float:

  • Float values are unchanged
  • Integer values are converted to the nearest equivalent floating point value
  • String values are converted according to the syntax of float literals
  • Boolean values are converted to 1.0 for true, and 0.0 for false

For string:

  • String values are unchanged
  • Integer values are converted to the base 10 string representation
  • Float values are converted to a string formatted xxx.xxx with a precision of 6. This is equivalent to %f for C’s sprintf.
  • Boolean values are converted to "true" for true, and "false" for false

For bool:

  • The following string values convert to true"1""t""T""TRUE""true", and "True"
  • The following string values convert to false"0""f""F""FALSE""false", and "False"
  • Any non-zero integer or float value converts to true
  • Any zero integer or float value converts to false

For any other unspecified type, the result is the undefined value.

Printing

The built-in function print can be used to output formatted debug information. The runtime may omit print function calls depending on its execution mode. The runtime may choose where the output of a print function goes.

print is a variadic function, accepting one or more values to be printed; values are separated by a single whitespace character (" ").

The return value of print is always true to allow it to be used in boolean expressions.

print("hello")                    // "hello"
print("hello", "world")           // "hello world"
print("The", "number", "is", 42)  // "The number is 42"
print([1, 2, 3])                  // "[1, 2, 3]"
one_is_zero = rule { 1 == 0 }
print(one_is_zero)                // "false"

Errors

The built-in function error is equivalent to print but immediately causes execution to halt and the main rule to evaluate to false. The program execution is considered to have failed.

Happy Learning !!!


Share At:
0 0 votes
Article Rating
Subscribe
Notify of
guest
4 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Create Account
1 day ago

The point of view of your article has taught me a lot, and I already know how to improve the paper on gate.oi, thank you. https://www.gate.io/ja/signup/XwNAU

gateio
18 days ago

At the beginning, I was still puzzled. Since I read your article, I have been very impressed. It has provided a lot of innovative ideas for my thesis related to gate.io. Thank u. But I still have some doubts, can you help me? Thanks.

gate io
20 days ago

At the beginning, I was still puzzled. Since I read your article, I have been very impressed. It has provided a lot of innovative ideas for my thesis related to gate.io. Thank u. But I still have some doubts, can you help me? Thanks.

creek gate io
28 days ago

I am sorting out relevant information about gate io recently, and I saw your article, and your creative ideas are of great help to me. However, I have doubts about some creative issues, can you answer them for me? I will continue to pay attention to your reply. Thanks.

Back To Top

Contact Us