# SeExpr Language Reference#

This section provides an in-depth view of the SeExpr programming language, from its syntax to a reference guide of its built-in functions. It is largely inspired from the SeExpr User Documentation page (SeExpr: User Documentation).

It may look quite theoretical but it is essential to master the language and debug expressions.

## Language Structure#

SeExpr language is largely inspired from C and, like in many programming languages, SeExpr expression programs consist in a list of statements that are executed sequentially and can be separated by empty lines.

Statements are followed by a semicolon ; to mark the end of the statement. But one of the main differences with C or Python is that SeExpr programs are meant to return a value.

Unlike C, SeExpr does not have any return statement, so it uses a syntax trick to indicate which statement defines the return value: the last statement of a program defines the return value of the expression when it is not followed by a semicolon.

Thereby, a SeExpr program is structured like this:

statement1;
statement2;

statement3;
...
statementN;
return_statement


Danger

Adding other statements after a return statement will result in a "failed to compile" error.

Warning

When ending the last statement with a semicolon, the expression does not return any value.

Like in Python, you can add comments to your program using the # character. Any character following # to the end of the line are ignored when compiling the program.

SeExpr does not have a syntax for multi-line comments, so if you wish to comment several lines, each line should start with #.

statement1;   # commenting statement 1
#statement2;  line is ignored
statement3;

# since there isn't any
# ways to comment a block
# of code, you must instead
# comment each line
Statement3;
...
statementN;
return_statement


### Statements and Expressions#

In this section, expression means is a sequence of operators and their operand, that specifies a computation, and it has nothing to do with the Clarisse expression system.

Expression evaluation may produce a result (ex: evaluation of 2+2 produces the result 4), may generate side-effects (ex: evaluation of printf("Hello") sends the characters 'H', 'e', 'l', 'l', 'o' to the log system), and may designate objects or functions.

Most statements in a typical SeExpr program are expression statements, such as variable assignments or function calls.

Except for the return value statement, any expression followed by a semicolon is a statement.

Identifier[index] = value;  # Unsupported by the language!

variable = expression; # Assignment expression statement

function(arg1, … , argN); # Function call expression statement


Each SeExpr program must end with a return value statement. Any expression that returns a value can be used as return statement. In this case, the expression is not followed by a semicolon.

Primary expressions, such as constant values (number, vector or character string) and declared identifiers (variable names) can be used as return value statements.

x = 3.14;   # Declared identifier used as return value statement
x

x = 0.14;   # Operator expression used as return value statement
y = 3;
x + y

3.14    # Constant number used as return value statement

[0, 1, 2]   # Constant vector used as return value statement

"Hello world"   # Constant string used as return value statement

snoise(T)   # Function call used as return value statement


Because variable assignment does not return a value, assignment expression cannot be used as return value statement. Same goes for function calls that do not return values like printf, for instance.

SeExpr expressions are formed either using the operator syntax or the function call syntax. Both syntax use operands as input parameter that can be expressions themselves (in that case, the expressions must return a value):

operator op1    # Expression using a unary operator syntax
# Ex: !x

op1 operator op2    # Expression using a binary operator syntax
# Ex: x + y

op1 operator op2 operator op3   # Expression using a ternary operator syntax
# Ex: z ? x : y

function( op1 , op2 , … , opN ) # Expression using a function call syntax
# Ex: snoise(T)
# Ex: get_name()
# Ex: dot(u, v)

Identifier[index] = value;  # Unsupported by the language!


When using expressions as operands, it is recommended to use parenthesis () around the expression to isolate the operand in order to make sure expressions are executed in the desired order:

op1 operator ( op2 operator op3 )   # Ex: x + (y * z)

op1 operator function( … )  # Ex: x + snoise(T)

function( op1 , op2 operator op3 , … )  # Ex: dot( [0,1,2], u + v )


Note

Each function defines its own number of operands (commonly referred to as arguments) and some functions take no argument as input parameter.

Finally, SeExpr defined another kind of statement, the conditional statement, that is used to choose between one of several lists of statements depending on the value of an expression:

if ( expression ) { # Statements enclosed within the braces following the if
statement       # keyword are executed only if expression is true.
statement
# more code
statement
} else {            # If expression is false, only the statements enclosed within
statement       # the braces after the else keyword are executed.
statement
# more code
statement
}


## Types and variables#

SeExpr supports 2 basic value types:

Type Description
FP Floating-point number either in decimal or integer form. Ex: 3.14 or 7
STRING String of characters. Ex: "Hello world"

In addition, numbers can be packed into vectors (up to 16 numbers) to encode complex objects like 3D positions, 3D vectors, colors, quaternions and 4x4 matrices.

Since SeExpr v3, a standardized notation has been introduced to the language, FP[n], to describe vector types of dimension n.

Types are never used in SeExpr program. They are used to document language features but variable declaration do not require explicit typing.

Instead the type of the variable is implicitly deduced from the initialization expression that is always required when declaring a new variable. Therefore, the assignment operator is always used to declare a new variable:

variable_name = expression; # Declares a new variable variable_name
# and initialize its value with the result value of expression.
# Ex: x = 3.14;
# Ex: z = x + y;
# Ex: x = snoise(T);


Variable names follow the C-like identifier rules: an arbitrarily long sequence of digits, underscores, lowercase and uppercase Latin letters.

A valid identifier must begin with a non-digit character and they are case-sensitive.

Variable names can be any valid identifier as long as it does not conflict with reserved names that are used as keywords for the language.

### Numbers and Vectors#

Vectors (points, colors, or 3D vectors) are collections of N floating-point numbers (N being the dimension of the vector, a 3D vector is made of 3 numbers called components).

SeExpr allows you to directly create and manipulate vectors.

#### Examples#

[1, 2, 3]   # Returns the 3D vector (color, position, …) whose components are
# 1 in the 1st axis, 2 in the 2nd axis and 3 in the 3rd axis.

v = [1, 0, 0];  # Assigns the 3D vector [1,0,0] to variable v

x = 1;
y = 2;
v = [x, y, 3];  # Floating-point variables can be used when assigning a vector


To use the value from a vector component, you must use the [ ] operator right after the vector identifier:

identifier[index]   # Reads the index-th component of identifier


Warning, unlike C, SeExpr does not support component assignment using the [ ] operator:

Identifier[index] = value;  # Unsupported by the language!


Vectors may be intermixed with scalars (simple floating point numbers). If a scalar is used in a vector context, it is replicated into the three components (ex: 0.5 becomes [0.5, 0.5, 0.5]):

dot(1, [2,3,4]) # Same as dot([1,1,1], [2,3,4]) and returns 9


If a vector is used in a scalar context, only the first component is used. One of the benefits of this is that all the functions that are defined to work with scalars automatically extended to vectors:

v = [1,2,3];
w = [0,1,2];
v[w]    # Vector w is used in a scalar context so only w[0] is used.
# Same as v[w[0]].


### Strings#

SeExpr supports character strings natively. String are defined like in C using double-quote characters to enclose the string characters.

str = "hello World";    # Assigns the string Hello World to variable str

str = "";   # Assigns an empty string to variable str


Unlike C, strings do not support the [ ] operator. Strings can be concatenated using the + operator:

h = "Hello";
w = "World";
h + " " + w # Returns the string "Hello World"


### More Examples#

my_var = 3; # Declares the variable my_var as a
# floating-point number and assigns
# the number 3 to it.

_my_var = 3.14; # Assigns the value 3.14 to my_var

my_var = "Hello world"; # Assigns "Hello World" to my_var

my_var = x + 3.14;  # Adds 3.14 to value of the variable x and
# assigns the result to m_var

my_var = snoise(T); # Assigns the result of snoise(T)


## Alias Types and Constants#

To ease the readability of the constants and functions descriptions, we are introducing new types that simply are aliasing the basic types.

For instance, instead of writing FP[3] for a 3-component floating-point vector representing a color, we use the alias type COLOR.

Alias Types Description
INT Defined as FP, represents a number in integer format.
COLOR Defined as FP[3], represents a color either in RGB or HSL format.
VECTOR Defined as FP[3], represents a 3D vector of components X, Y and Z.

SeExpr defines some constants for math expressions (like e and π) as well as for controlling the behavior of built-in functions:

Math Constants Description
FP E = 2.71828... Exponential constant
FP PI = 3.14159... Trigonometric constant
Interpolation Constants Description
INT linear = 0 Linear falloff shape used to control remap and midhsi functions
INT smooth = 1 Smooth falloff shape used to control remap and midhsi functions
INT gaussian = 2 Gaussian falloff shape used to control remap and midhsi functions

## Keywords Reference#

This section covers the reserved keywords, used by the language, other than operators and function names. As you can see, SeExpr reserves a very few keywords:

Keyword Syntax Usage
if if ( expr ) { … } Conditional statement that executes the following braces enclosed sequence of statements { … } when the expression expr is true.
else if ( expr ) { … } else { … } Optional conditional statement, always used after a if statement, that executes the following braces enclosed sequence of statements { … } when the expression expr is false.
local Reserved, do not use
global Reserved, do not use
def Reserved, do not use