Form Programming Language Logo


ormpl

Language Specification 1.0

Introduction

This language specification document is here to be a comprehensive document covering all features of form 1.0 for future and current reference, as such it outlines all language features in notable detail with examples for explanative and documentation purposes.


Constants

Integer Constants

Integers are numbers with no fractional part, or in other words whole numbers by default integer constants are assumed to be unsigned.

unsigned constant

46

int constant

+46

Integer constants can also have a specified type suffix. The type suffix represents the type of the constant, it is upto you to ensure the specified type is large enough to hold the given integer constant.

46ll

The possible suffix are "u" (unsigned), "ul" (unsigned long), "ull" (unsigned long long), "l" (long), "ll" (long long).

The exact sizes of the above types is platform dependant.

You can also specify integers using hexadecimal.

0x00FF (representing the number 255 of type short (16 bit integer))

The default type of the hexadecimal constant depends on the number of hex characters specified. Like number constants you can also specify a type using a suffix.

0xD6ll (representing the number 214 of type long long)

Float Constants

Floats are numbers with an integral and fractional parts. There are a number ways of specifying a floating point number as a constant.

Number form

5.6

Representing number 5.6 type double.

Scientific notation

5e-5

Representing number 0.00005 type double.

Number with exponent

15.7e2

Representing number 1570 type double.

Float constants can also specify a type with a suffix, the possible suffix are "l" (long double), "f" (float), "lf" (double), "d" (double), "ld" (long double).

5.6f

Representing number 5.6 type float.

Scientific notation

5e-5f

Representing number 0.00005 type float.

Number with exponent

15.7e2f

Representing number 1570 type float.

Bool Constants

There are two boolean constants true and false both are of type bool.

Structure Construction

Structures values can be build using special construction syntax. A structure can be made by wrapping 1 or more expressions in curly brackets.

{ 18, -15.0e2f }

Representing a structure of type {unsigned, float}.

If all of the values in the structure constructor are constants then the whole structure is a constant.

List Construction

List values can be build using special construction syntax. A list can be made by wrapping 1 or more expressions in square brackets. All of the expressions in the list construction must be of the same type.

[ 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100 ]

Representing a list of type [unsigned].

If all of the values in the list constructor are constants then the whole list is a constant.

Character Constants

Characters are integers constant made to represent printable characters of different byte sizes.

'0'

Representing character ASCII character 'q' (number 48) of type char.

In addition to the printable character there are a number of special escape characters that require 2 characters to represent.

'\'' (single quote), '\"' (double quote), '\a' (alert), '\b' (backspace), '\f' (formfeed), '\n' (newline), '\r' (carriage return), '\t' (tab), '\v' (vertical tab), '\0' (zero) are the escape characters

You can also specify character constant using hexadecimal numbers for example the following are valid characters:

'\x54' (hexadecimal character), '\u0054' (unicode 16 bit character, int16), '\U00000054' (unicode 32 bit character, int32)

If you follow a backslash with any other character, that character is used without further processing for example '\\' (backslash character), '\e' (e character).

Character constants can also specify a type with a suffix, the possible suffix are "w" (short), "l" (short), "ll" (int). This is needed with character codes longer than 8 bits as char is assumed for all character constants. For example:

'\u0054'w, '\u0054'l, '\U00000054'll

The preceeding zeros can be omitted giving us.

'\u54'w, '\u54'l, '\U54'll

String Constant

String constants are constants representing lists of character, so by default they are of type [char]. For example:

"This is a string!!!\n"

The above represents the character list, [char], containing the string "This is a string!!!\n". The above is equivilent to the following made with list construction.

[ 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 's', 't', 'r', 'i', 'n', 'g', '!', '!', '!', '\n' ]

All of the possible options for character constants also apply to the characters that make up a string constant.

String constants can also specify a type with a suffix, the possible suffix are "w" (short), "l" (short), "ll" (int). This is needed with character codes longer than 8 bits as char is assumed for all string constants. For example:

"\u54\u55"w, "\u54\u55"l, "\U54\U55"ll


Comments

There are two kinds of comments in form, single line and multi-line comments. Multi-line comments can be nested.

<> single line comment <* Multi-line comment <* Nested multi-line comment*> *>


Variables

Variables are easy to define and are used in all form code. Variables can either be instanced specifying a type followed by a name, for example:

unsigned var

This gives us a variable named var of the type unsigned, set to 0. All variables are, by default, initialized to zero or a valid initialization state in the case of more complex types (see Types).

The type of a variable can be inferred from context.

i = +4

This gives us a variable named i of the type int, set to 4.


Functions/Constraints

Functions are the main program building block, functions are called using constraints.

Functions take a type, returning a possibly specified type. The function construction is as follows.

<this-type> -> <constraint-type> [-> <optional-return-type>] : <> function block ;

Note that <this-type> and <constraint-type> do not have to represent real types but instead only names. However if specified <optional-return-type> must be a true type. If the types are not real types <this-type> and the return type are infered (unless <optional-return-type> is specified). If <constraint-type> represents a true type, this is the return type of the function and <optional-return-type> must be omited. For example the main function for every program must be specified as follows.

mutable [[char]] -> main -> int : <> program code return +0;

You are only required to specify the return type of a function if (a) the function is recursive or (b) the function throw a compile time exception before a return statement is evaluated by the compiler (unlikely).

This means that the majority of functions can omit the return type. For example the following function returns a long integer.

{long a, long b} -> min : if this.a < this.b : return this.a return this.b;

All functions are called using what is called a constraint. For example to call min from within main looks like this.

mutable [[char]] -> main -> int : smaller = {50l, 18l} -> min return +0;

Constraints can be used to construct function call chains by combining functions together with function composition and backtracking (see backtracking). A very simple example of function composition might be.

int -> cvar : <> some code return +0; cvar -> cvar2 : <> some code return +0; mutable [[char]] -> main -> int : +17 -> cvar2 return +0;

Here +17 (int) is constraint to type cvar2, function composition is used to first call function int -> cvar followed by cvar -> cvar2 to constrain +17 (int) to type cvar2.

this

this represents the variable passed into a function/closure. For example.

mutable {unsigned a, unsigned b} -> add_this : "mutable {unsigned a, unsigned b} -> add_this\n" -> stdio.print this.a += 1 this.b += 1 return this.a + this.b;

mutable

In form functions come in two kinds mutable and immutable, functions are immutable by default. Mutable functions are defined by their side effects, for example opening a file/writting to file. Function that are immutable cannot call mutable functions. The main function must be marked as mutable.

mutable [[char]] -> main -> int : "printing to screen is a mutable call\n" -> stdio.print return +0;

backtracking

Constraints can be used to construct function call chains by combining functions together with backtracking and function composition (see Functions/Constraints). Function that do not return or call the default exception call throw initiate backtracking. Backtracking can be used with function compostion to allow alternative "paths" to a value that covers the failure path.

int -> cvar : <> int -> cvar [1] if this > 0 && this < 4 : return +0; <> function doesn't return, backtracking initiated (alternatively call throw) ; int -> cvar : <> some code return +0; cvar -> cvar2 : <> int -> cvar [2] return +0; mutable [[char]] -> main -> int : +17 -> cvar2 return +0;

Here, like before, +17 (int) is constraint to type cvar2, function composition is used to first call function int -> cvar (the first found by the compiler). +17 is not greater than 0 and less than 4 so backtracking is initiated so then int -> cvar (the second found by the compiler) is called, this returns and is followed by cvar -> cvar2 to constrain +17 (int) to type cvar2

Note that if int -> cvar (the second found by the compiler) doesn't return (or calls the default throw exception), backtracking propagates meaning we now backtrack from [[char]] -> main -> int, testing other containing calls.

mem

An immutable functions can be decared as mem. mem functions implement memoization. Because immutable functions do not have side effects a call to an immutable function with the same parameters will always return the same return value. This means that calls to a mem function (both the input and output values) are stored, then, each time the mem function is called we search the stored inputs and return the stored outputs (if found), otherwise the function is called.

This can be used to optimise certain expensive function calls with a search through input/output values, this may represent a considerable speed improvement for expensive to call functions. An example use could be:

int -> fib -> int mem : if this < +2 : return this; return ((this - +1) -> fib) + ((this - +2) -> fib);

Function Templates

Function can be templated, that is they are allowed to accept any type. To specify a template parameter simply write %<identifier> in place of any type appearing in the function definition. You can have any number of these function template types, in a single function definition. The form runtime is responsible for instantiating a function that accepts the correct types.

{%T a, %T b} -> min : if this.a < this.b : return this.a return this.b;

In this very simple example, note a and b are of the same type. T is bound to the type of a and b and can be used like any other type in the template function.

Note because all functions are types and types can be templated (see Templates), function can have any number of type parameters. For example a function accepting type parameters might look like:

{%T a, %T b} -> add<:%U:> : <> function code... return <:U:>(this.a + this.b);

Here function add is taking a and b of the same type T, the function adds the two values together then converts them to template parameter type U.


Closures

Closures are small function objects (sometimes called functors) that can contain state. They can be passed to or returned from functions and called with a similar syntax to constraints. As an example of closure use the following to sorts the list in reverse order.

mutable [[char]] -> main -> int : <> sort in reverse order lst = {[8, 5, 3, 4, 2, 8, 9, 6, 7, 2, 9], [#] const {unsigned a, unsigned b} -> bool : return this.a > this.b;} -> stdlib.merge_sort return +0;

Closures can be copied/compared etc just like other values, see Closure Operators. For example.

mutable {} -> closure_test : "closure_test enter\n" -> stdio.print [-] mutable unsigned -> unsigned clsr_test = [#] mutable unsigned -> unsigned : "[#] mutable unsigned -> unsigned clsr_test\n" -> stdio.print return this + 1; <> f == 6 {/clsr_test, unsigned f/} = (5 -> [~] clsr_test);

Here [-] mutable unsigned -> unsigned is the closure type. [#] mutable unsigned -> unsigned : <*closure function body*> ; is the closure declaration, note both the input and output types must be declared as real types. 5 -> [~] clsr_test is the closure call. Note that the closure call always returns a structure containing the closure passed to the function followed by the closure return type aka {<closure-type>, <return-type>}.

Closures can contain variables (aka state), this state can be modified in the closure call, note the closure returned from the function must be copied to another closure in order to retain these state changes. For example.

mutable {} -> closure_test : "closure_test enter\n" -> stdio.print var1 = 2 [-] unsigned -> unsigned clsr_test = [#] unsigned -> unsigned => k = var1, int l = 0 : k++ l++ return this + k + 1; <> f == 8 {/clsr_test, unsigned f/} = (5 -> [~] clsr_test);

Note k and l are variables within the closure, k is assigned the value 2 from unsigned variable var1, making k's type unsigned, and int l is assigned 0.

A closure can be declared with either/both it's input/output types absent, these are called generic closures. It is recommended that the input and output types be present. For example.

mutable {} -> closure_test : "closure_test enter\n" -> stdio.print [-] mutable '% -> '% clsr_test = [#] mutable unsigned -> unsigned : "[#] mutable unsigned -> unsigned clsr_test\n" -> stdio.print return this + 1; <> g == 8 {/clsr_test, unsigned g/} = (7 -> <:{[-] mutable '% -> '%, unsigned}:>[~] clsr_test);

Here [-] mutable '% -> '% is the closure type, note both the input/output types are absent, this is called a generic closure. [#] mutable unsigned -> unsigned : <*closure function body*> ; is the closure declaration, this is the same as before. 7 -> <:{[-] mutable '% -> '%, unsigned}:>[~] clsr_test is the closure call. Note this time we "cast" the closure call to <:{[-] mutable '% -> '%, unsigned}:> this is so that the output type of clsr_test is known, we known the input type to be unsigned because of the 7 passed to clsr_test.


Types

Integer

As previously mentioned, integers are whole numbers.

Integers can be either signed or unsigned.

The possible signed integer types, in byte size order, are signed char, short, int, long, long long.

The possible unsigned integer types, in byte size order, are char, unsigned short, unsigned, unsigned long, unsigned long long.

char and signed char are 8 bits (1 byte)

It is by no mean guranteed that the type are of different sizes, however each of the types in the above lists are atleast a big as the preceding type. However typical sizes for the above types are listed here.

short (typically 16bit), int (typically system word size), long (typically 64bit), long long (size of long unless hardware support for larger type avaliable)

unsigned short (typically 16bit), unsigned int (typically system word size), unsigned long (typically 64bit), unsigned long long (size of unsigned long unless hardware support for larger type avaliable)

Examples:

unsigned i int k = -7 char chr = '\0'

By default integer variables are initialized to zero.

Float

As previously mentioned, Floats are numbers with an integral and fractional parts.

The possible floating point number types are, in byte size order, float, double and long double.

Like integers it is by no mean guranteed that the type are different, all types depend on the hardware/software support for that type.

The more bytes that represent the number the higher the precision the number can represent.

Examples:

float flt double dbl = -15.0e-2 long double ldbl = -7.0l

By default floating point variables are initialized to zero.

Bool

Boolean variable represent either true or false, they are used to control many of the languages conditional and loop statements.

There is one boolean type bool.

Examples:

bool b = true bool found

By default boolean variables are initialized to false.

Structure

Structures are an ordered set of types that are used throughout form.

Structures are denoted with the curly brackets containing a list of types, structures can contain any type including other structures.

Member variables, or simply members, of a structure can be named or unnamed.

Examples:

{unsigned, float} strct1 = {18, -789.54f} {int v1, char v2} strct2 {double, {unsigned, unsigned}} strct3

By default structure variables are initialized to a valid initialization state.

To access a member of a structure you can used either the [] operator with compile time constant unsigned integer index, the member . operator followed by the name of the member or .{} operator specifying the variable name with compile time constant [char]. For example

strct2.v1 = +5 strct2[0] = +5 strct2.{"v1"} = +5

All of the above set member v1 to +5.

Union

Unions (sometimes discriminated unions) are used to represent one of a possible range of types, you can change the type contained in a union at runtime.

Unions are denoted with the <|...|> brackets containing a list of types, unions can contain any type including other unions.

Member variables, or simply members, of a union can be named or unnamed.

<|unsigned, float|> unn1 = 18 <|int v1, char v2|> unn2 <|double, {unsigned, unsigned}|> unn3

To access a member of a union you can used either the [] operator with compile time constant unsigned integer index, the member . operator followed by the name of the member or .{} operator specifying the variable name with compile time constant [char]. For example

unn2.v1 = +5 unn2[0] = +5 unn2.{"v1"} = +5

All of the above set member v1 to +5.

By accessing one of the member of the union the type contained in the union is changed to the type of the accessed member.

To access an integer indicating the contained type in a union you need to call inbuilt function type. For example:

unn2.v1 = -11 unn2 -> type <> type returns 1 here<

By default union variables are initialized to a valid initialization state, this state represents non of the possible types, only by accessing one of the member of the union is the type contain in the union set.

If the union is uninitialized inbuilt function type returns zero, otherwise type returns the index of the type contained plus 1.

You are however allowed to set/initialize the union to a particular type simply by assigning that type to the union, if the union contains 2 or more of a particular type the union is set to the first of that type. For example

<|unsigned, float|> unn1 = 18.0f <> initialize to float, type is 2 here <|unsigned, double, int, int, int, float|> unn2 unn2 = +5 <> set to first int, type is 3 here

List

Lists are a growable list of contiguous data of one specific type.

Lists are denoted with the [] brackets containing a list of types, list can contain any type including other lists.

Examples:

[int] lst1 = [-3, +11, -1, +0, -5, +6] [unsigned, unsigned] lst2 [{unsigned, unsigned}] lst3 [{int}] lst4 [[char]] lst5 = ["this", "is", "a", "list", "of", "strings"]

In a list type definition, lists that have two or more types listed between the square brackets contain a structure that contains all types listed. For example, the types of lst2 and lst3 are equivalent here, however the types of lst1 and lst4 are not.

You can use the [] operator to access any of the elements of a list. For example:

lst1[0] = 5 <> set the zeroth element of the list to +5 lst1[3] = -78 <> set the third element of the list to -78

There are many inbuilt function dedicated to the use of list, see Inbuilt Functions.

Optional

Optional can be used to denote a variable that may or may not be set.

Optional are denoted with a ? following the type you want an optional for, you can have an optional of any type.

Examples:

int? opt1 = -3 {unsigned, unsigned}? opt2

By default optional variables are not set, but are initialized to a valid state.

To test if an optional has been set you can use the ? operator.

?opt1 <> returns true ?opt2 <> returns false

To access the variable contained within the optional you can use the ^ operator. Trying to access a optional that has not been set is undefined. For example.

^opt1 = -10 <> get the variable in option 1 and set it to -10 ^opt2 <> undefined behavior, opt2 not yet set

To set an optional simply assign to the optional with a value of the contained type.

opt2 = {2, 6}

Closure

Closures are small function objects (sometimes functors) that can contain state. They can be passed to or returned from functions and called with a similar syntax to constraints. See Closures for some examples of thier use. Like function they can be declared mutable, otherwise immutable. They can also be declared nothrow, this indicates that a closure doesn't backtrack (see backtracking), or throw any other exception.

Closure types have the following construction [-] <mutable> <nothrow> <input-type> -> <output-type>

For example the following closure type, [-] unsigned -> unsigned, is an immutable closure that takes an unsigned integer, returning an unsigned integer, the closure is assumed to throw.

Closures can contain variables (aka state), you do not need to declare this in the closures type. For complete examples see Closures.

A closure can be declared with either/both it's input/output types absent, these are called generic closures. For example.

mutable {} -> closure_test : "closure_test enter\n" -> stdio.print [-] mutable '% -> '% clsr_test = [#] mutable unsigned -> unsigned : "[#] mutable unsigned -> unsigned clsr_test\n" -> stdio.print return this + 1; <> g == 8 {/clsr_test, unsigned g/} = (7 -> <:{[-] mutable '% -> '%, unsigned}:>[~] clsr_test);

Closure types like this have the following construction [-] <mutable> <nothrow> '% -> '%, where '% replace the <input-type> and <output-type> respectively. It is recommended that the input and output types be present.

def

New types can be defined using the def keyword, you can define any type. For example.

def my_int int def new_strct_type {unsigned mmbr1, unsigned mmbr2} def big_strct_type { unsigned unsgn1, unsigned unsgn2, float flt1, float flt2, char chr, double dbl, [unsigned, unsigned] lst} def two_strings {[char] a, [char] b} def strng [char] def three_strings {strng a, strng b, strng c}

Templates

Types can be parameterized, these are called templates. The following are examples of template types.

def my_data<:T, U:> {int, unsigned, T, T, U} def vertex<:T:> {T x, T y, T z} def optional_type<:T:> T? def point_list<:T:> [vertex<:T:>] def float_vertex_list [vertex<:float:>] def double_vertex_list [vertex<:double:>]


Block

Blocks are used throughout form, variables declared within a block are local to that block.

All of the following constructs contain blocks, if, elseif, else, case, for, while, do...when, for <type>, set, catch, with (3 blocks). However you can simply declare a block independantly, it has the following construction.

: <> code to be executed ;


Conditionals

Conditional flow control can be used to optionally run code depending on conditional statements.

if

if is the most common conditional statement and is used throughout form code.

if <conditional statement> : <> code run if the above conditional statement is true ;

else if

elseif can be used to extend an if with further conditional blocks.

The only conditional block to be run is the first if/elseif block who's associated conditional statement is found to be true.

if <conditional statement 1> : <> code run if conditional statement 1 is true elseif <conditional statement 2> : <> code run if conditional statement 2 is true elseif <conditional statement 3> : <> code run if conditional statement 3 is true <> more elseif control flow... ;

else

else can be used to extend an if/elseif, the else block must appear last.

The else block is run if no if/elseif conditional statement is true.

if <conditional statement 1> : <> code run if conditional statement 1 is true elseif <conditional statement 2> : <> code run if conditional statement 2 is true <> more elseif control flow... else <> code run if no if/elseif conditional statement is true ;

switch...case

A switch...case statement takes a value and a relevate matching case statement block is run if the value is equal(==) to one of the case's statements. An optional default block can be added, this is run if the value doesn't match any of the case statements. For example.

d = 10 switch d : case 1 : <> case run if d == 1 case 2 : <> case run if d == 2 case 5 : <> case run if d == 5 case 0, 10 : <> case run if d == 0 or d == 10 case 5, 15 : <> case run if d == 5 or d == 15 case 9, 19 : <> case run if d == 9 or d == 19 case 10, 20 : <> case run if d == 10 or d == 20 <> further case control flow default <> optional default block, run if non of the above case statements match d ;


Loops

Loops are used to repeat a block depending upon a conditional statement.

for

for loops have to following construction.

for <loop initilization>, <conditional statement>, <loop increment statement> : <> block repeated while conditional statement is true ;

for loops are useful as they gather all of the loop control statements into one place making loop control easier.

<loop initilization> is run prior to loop entry, and can be used to initialize all loop control variables.

<conditional statement> is run at the beginning of the loop, the block is run if conditional statement is found to be true.

<loop increment statement> is run at the end of the loop, and can be used to increment the loop control variables before the conditional statement test.

Because of this for loops are easily made that repeat the centre block some number of times using a control variable. For example the following loop, executes the for block 10 times:

for i = 0, i < 10, ++i : "this is printed 10 times\n" -> stdio.print;

while

while loops have to following construction.

while <conditional statement> : <> block repeated while conditional statement is true ;

while are your basic loop, as a simple example the following executes the while block 10 times.

i = 0 while i < 10: "this is printed 10 times\n" -> stdio.print ++i;

do...when

do...when loops have to following construction.

do <> do...when block when <conditional statement>

do...when loops are distict from the other loops as the do...when loop tests the condition statement at the end of the loop, this means that the body of the loop is executed atleast once before the condition is tested. As a simple example the following runs the do...when block 10 times.

i = 0 do "this is printed 10 times\n" -> stdio.print ++i when i < 10

break/continue

break statements can be used to immediately exit the encasing loop of any of the main loops (for, while, do...when) and for <type>, see below. For example:

i = 0 while i < 10: "this is printed 7 times\n" -> stdio.print if i < 7 : break; "this is printed 6 times\n" -> stdio.print ++i;

continue statements can be used to jump to the next loop in any of the main loops (for, while, do...when) and for <type> see below.

For while and do...when loops continue jumps to the condition.

For for loops continue jumps to the loop increment statement.

For for <type> continue jumps to the next item to be processed, see below.

For example:

for i = 0, i < 10, ++i : if i == 7 : continue; "this is printed 9 times\n" -> stdio.print;


for <type>

for <type> has the following construction.

for <variable-name> in <expression> : <> for <type> block ;

Optionally <variable-name> may be preceeded by keyword ref, this signifies that no copied are made from the value returned from expression and that any modifications made to <variable-name> are made to the variable returned from <expression>.

<expression> must resolve to one of the following types; list, structure or union

for <type> does a different action depending on the type returned.

for <list>

The list is looped over with each element of the list bound to <variable-name>, either copied (default) or by reference (ref), see above.

For example:

str = "This is printed to stdout.\n" for x in str : <:int:>x -> stdio.putchar;

for <structure>

The code contained in the for <type> block is run for each of the members of the structure.

For example:

strct = {'$', 'c', '\n'} for x in strct : <:int:>x -> stdio.putchar;

for <union>

The code contained in the for <type> block is run for the type the union currently contains. If the union is uninitialized then for <type> block is skipped.

For example:

<|char, signed char|> unn = 'f' for x in unn : <:int:>x -> stdio.putchar;


return

return is used to end a function/closure immediately and return the value in the following expression. If no expression follows the return keyword then the empty structure {} is returned. All return statements in a function/closure must return the same type, if the return type is specified the value returned must be of that type or convertable to that type.

long -> abs -> unsigned long : if this < 0 : <> long converted to unsigned long on return return -this return this;


set...yield

set...yield expression can be used much like functions. set opens up a block, all yield statement return a value to the set expression. All yield expressions within the same set block must yield the same type. If no value is yield within the set block then the value returned is the default initilization for the type found in the yield expressions. For example.

mutable [[char]] -> main -> int : int a = +5 int b = -3 <> yield the smaller of a or b int rslt = set : if a < b : yield a; yield b; return +0;


Casts

There are two kinds of cast.

Standard Cast

Standard cast can be used to convert any of integer, floating point and boolean types between each other. For example.

mutable [[char]] -> main -> int : <> float converted to int, a == 5 (float truncated) int a = <:int:>5.6f return +0;

Standard cast can be used to cast any type to a type that has the same bit compatible layout, this is most useful for structure or unions. For example.

def a_strct {unsigned a, unsigned b} mutable [[char]] -> main -> int : {unsigned a, unsigned b} avar1 = {4, 13} tmp = <:a_strct:>avar1 return +0;

Bit Cast

Bit Casts can be used to bit-convert any of integer, floating point and boolean types between each other. The types do not have to be the same size.

mutable [[char]] -> main -> int : <> float bit-converted to int, a's bit pattern == 5.6f bit pattern int a = as<:int:>5.6f return +0;


Exceptions

We have already seen backtracking, however there are 2 other kind of exceptions in form.

Null exception

You can throw a null exception with the expression throw null. Null exceptions can only be caught with a catch all block, they are intended to be used only in the case of unrecoverable errors. See catch.

Count exception

You can throw a count exception with the expression throw , where expression returns an unsigned value. When a Count exception is examined by a catch block the count it contains is decremented by 1. Count exceptions are caught by a catch block once the count they are initialized with reaches zero or the exception is caught by a catch all block.


catch

catch blocks are used to catch exceptions, there are 3 kinds of exception within form. backtracking, null and count exceptions. Each have different rules regarding catch.

A catch block has the following construction.

catch <all> : <> code that we want to catch exceptions from... ; <> if code is found to throw and catch is made code resumes here...

There are two kinds of catch block; The standard catch block and the catch all block.

Backtracking exceptions are always caught by a catch block, code resumes after the catch block (see backtracking).

Null exceptions are only caught by a catch all block, code resumes after the catch all block (see Exceptions).

Count exceptions are caught by a catch all block, code resumes after the catch all block OR once the count they contain reaches 0 (count is decremented at every catch block they pass through) (see Exceptions).


with

with blocks are used to help with the correct cleanup of objects at runtime, they are especially useful due to the need for many systems to use exceptions. A with block is actually split into 3 blocks, like so.

with : <> block one... ; <> block two... ; <> block three... ;

with blocks are different from normal blocks as the variables declared in block one are avaliable in all three of the blocks.

Also so long as block two is entered, block three will always be executed, even if the code within block two throws an exception, returns, yields or otherwise exits of any kind (however not including system calls that exit/terminate the program).

This has the desirable property that block one can be used to initialize variables that might need cleaning up, block two can be used the execute code that might throw/return/yield and block three can be used to clean up the variables initialized in block one. Because block three always get executed the variables in block one will always be correctly cleaned up. The is best shown with an example.

<> open a file for writing ensure it is closed with : <> initialize fle = stdio.fopen("openfile.txt", "w") ; <> code that writes to the file, but could throw... ; <> this code always runs, close the file stdio.fclose(fle) ;


sizeof

sizeof <type> is used to get the byte size of any type in the language, for example.

include "memory" as memory mutable [[char]] -> main -> int : unsigned data_a = 48 unsigned data_b = 79 {<:stddef.ptr_t:>&data_a, <:stddef.ptr_t:>&data_b, <:stddef.size_t:>sizeof unsigned} -> memory.memcpy return +0;


let

let construction is: let <identifier> = <expression>

When <identifier> is found in code the <identifier> is substituted for the specified <expression>. Brackets are implied around <expression>. let can be used to name constants, or reduce on code where a particular expression is repeated many times. For example.

<> pi let M_PI = 3.14159265358979323846 <> char maximum value let CHAR_MAX = 255 let x_plus_one = ++x

Because every kind of code construction in form is an expression you can include ifs, loop etc in let expressions if needed.

<> pi let MIN_AB = set : if a < b : yield a; yield b;


import

Because the form runtime lives within an environment, this environment must be interfaced with. The import construct can be used to import a system library, the library must have functions within it corresponding with the expected interface. To import a system library: (example relates to window dll system files)

import "frstdio.dll" as stdiolib

To call a function from within the library can be done as so, note both the input variable and the cast to int (<:int:>) before the function name, stdiolib.fn_fclose, is required as this is how form knows the input type and return type of fn_fclose.

extern inline mutable FILE -> fclose : return this -> <:int:>stdiolib.fn_fclose;


include

include constructs are used to gain access to functions, def and let definitions in other form source (.frs) or form module (.fmdl) files.

Note form is not recursive in this regard, the included functions, def and let definitions are local to the file doing the inclusion.

include "stdio"

Note there is no need to write ".frs" or ".fmdl", form will find one of "stdio.frs" or "stdio.fmdl". You can now use any functions, def or let that are declared extern within "stdio". See extern.

as

You can name the library at the include statement, this means that when accessing any extern definitions in stdio (for example) you will need to preceed all access with the chosen name. For example

include "stdio" as stdio mutable [[char]] -> main -> int : stdio.FILE fle = {"myfile.txt", "r"} -> stdio.fopen <> code using fle... fle -> stdio.fclose return +0;


extern

The extern keyword can preceed a functions, def or let definition. This signifies that the functions, def or let definition can be used by anyone that include the library. See include.


Assignment expansion

Any time that you can assign a structure to a variable it is possible to use assignment expansion to instead assign to multiple variables. For example.

mutable [[char]] -> main -> int : unsigned a unsigned b {/a, b/} = {6, 11} return +0;

a and b do not necessarily need to be declared here, as the type can be deducted from the structure on the right hand side. You could also use this to swap variable.

mutable [[char]] -> main -> int : unsigned a = 11 unsigned b = 6 {/a, b/} = {b, a} return +0;

Intermediate variables are used here making swapping like this expensive if used on complex types. Assignment expansion can be used recursively with structures containing other structures. For example.

mutable [[char]] -> main -> int : {/a, {/b, c/}, d/} = {6, {11, 'a'}, "hello!"} return +0;

Any time that you do assignment expansion you can choose to discard any of the values copied using an underscore in place of the variable being assigned to. For example:

mutable [[char]] -> main -> int : {/_, b, _/} = {6, {11, 'a'}, "hello!"} return +0;

Here the values 6 and "hello!" simply discarded.


Operators

Integer Operators

Both a and b are of the same integer types, the integer types are signed char, short, int, long, long long, char, unsigned short, unsigned, unsigned long, unsigned long long. c is of any type.

All types returned from these operators are of the same integer types as those passed in unless otherwise noted.

Suffix/postfix increment and decrement

a++, a--

v1 = v2++

Value of a is returned before 1 is added/subtracted from a.

Prefix increment and decrement

++a, --a

v1 = ++v2

1 is added/subtracted from a then returns a.

Unary plus and minus

+a, -a

v1 = -v2

Unary plus is no-op. Unary minus swaps the sign (-/+) of a.

Logical NOT and bitwise complement

!a ~a

v1 = ~v2

Logical NOT converts to bool (integers values are false if zero, otherwise true) then negates the value. Complement inverts (swaps 1 to 0, and vice-versa) all of the bits of a.

Casts

<:type:>a, as<:type:>a

v1 = <:long:>v2

Standard Cast, if possible, converts a to type. Bit Cast does bit copy from a to type.

Address-of

&a

v1 = &v2

Returns the address of a. a must be a variable.

Multiplication, division and modulus

a*b, a/b, a%b

v1 = v2 * v3

Applies the selected operation to a and b.

Addition and subtraction

a+b, a-b

v1 = v2 + v3

Applies the selected operation to a and b.

Bitwise left shift and right shift

a<<b, a>>b

v1 = v2 << v3

Left and right bit shift moves the bits in a, b count to the left or right respectively.

Less than, Greater than, Less than or equal to, Greater than or equal to

a<b, a<=b, a>b, a>=b

v1 = v2 < v3

Ordered comparison of a with b, returns bool

Equals, Not equals

a==b, a!=b

v1 = v2 == v3

Equals/not equals comparison of a with b, returns bool

Bitwise AND

a&b

v1 = v2 & v3

Applies the & operation to the bits of a and b.

Bitwise XOR

a^b

v1 = v2 ^ v3

Applies the ^ operation to the bits of a and b.

Bitwise OR

a|b

v1 = v2 | v3

Applies the | operation to the bits of a and b.

Logical AND

a&&b

v1 = v2 && v3

Logical AND first evaluates a to bool. If a is true, then evaluate b to bool. If b is true then returns true. If either a or b is false returns false. Note b is only evaluated if a is true.

Logical OR

a||b

v1 = v2 || v3

Logical OR first evaluates a to bool. If a is false, then evaluate b to bool. If b is false then returns false. If either a or b is true returns true. Note b is only evaluated if a is false.

Assignment

a=b

v1 = v2

Copies value in b into a, a must be a variable.

Compound assignments

a*=b, a/=b, a%=b, a+=b, a-=b, a<<=b, a>>=b, a&=b, a^=b, a|=b

v1 *= v2

Applies the relevant operation between a and b then stores the value in a, a must be a variable.

Comma

a,c; c,a

v1, v2

In operation a,c executes a then c, retuns c.

Float Operators

Both a and b are of the same floating point types, the floating point types are float, double and long double. c is of any type.

All types returned from these operators are of the same floating point types as those passed in unless otherwise noted.

Suffix/postfix increment and decrement

a++, a--

v1 = v2++

Value of a is returned before 1 is added/subtracted from a.

Prefix increment and decrement

++a, --a

v1 = ++v2

1 is added/subtracted from a then returns a.

Unary plus and minus

+a, -a

v1 = -v2

Unary plus is no-op. Unary minus swaps the sign (-/+) of a.

Logical NOT

!a

v1 = !v2

Logical NOT converts to bool (floating point values are false if zero, otherwise true) then negates the value.

Casts

<:type:>a, as<:type:>a

v1 = <:long:>v2

Standard Cast, if possible, converts a to type. Bit Cast does bit copy from a to type.

Address-of

&a

v1 = &v2

Returns the address of a. a must be a variable.

Multiplication, division

a*b, a/b

v1 = v2 * v3

Applies the selected operation to a and b.

Addition and subtraction

a+b, a-b

v1 = v2 + v3

Applies the selected operation to a and b.

Less than, Greater than, Less than or equal to, Greater than or equal to

a<b, a<=b, a>b, a>=b

v1 = v2 < v3

Ordered comparison of a with b, returns bool

Equals, Not equals

a==b, a!=b

v1 = v2 == v3

Equals/not equals comparison of a with b, returns bool

Logical AND

a&&b

v1 = v2 && v3

Logical AND first evaluates a to bool. If a is true, then evaluate b to bool. If b is true then returns true. If either a or b is false returns false. Note b is only evaluated if a is true.

Logical OR

a||b

v1 = v2 || v3

Logical OR first evaluates a to bool. If a is false, then evaluate b to bool. If b is false then returns false. If either a or b is true returns true. Note b is only evaluated if a is false.

Assignment

a=b

v1 = v2

Copies value in b into a, a must be a variable.

Compound assignments

a*=b, a/=b, a+=b, a-=b

v1 *= v2

Applies the relevant operation between a and b then stores the value in a, a must be a variable.

Comma

a,c; c,a

v1, v2

In operation a,c executes a then c, retuns c.

Bool Operators

Both a and b are both of bool type. c is of any type.

All results from these operations are of type bool, unless otherwise noted.

Logical NOT and bitwise complement

!a ~a

v1 = ~v2

Logical NOT returns the negated value in a. Complement inverts (swaps 1 to 0, and vice-versa) all of the bits of a.

Casts

<:type:>a, as<:type:>a

v1 = <:long:>v2

Standard Cast, if possible, converts a to type. Bit Cast does bit copy from a to type.

Address-of

&a

v1 = &v2

Returns the address of a. a must be a variable.

Less than, Greater than, Less than or equal to, Greater than or equal to

a<b, a<=b, a>b, a>=b

v1 = v2 < v3

Ordered comparison of a with b

Equals, Not equals

a==b, a!=b

v1 = v2 == v3

Equals/not equals comparison of a with b

Bitwise AND

a&b

v1 = v2 & v3

Applies the & operation to the bits of a and b.

Bitwise XOR

a^b

v1 = v2 ^ v3

Applies the ^ operation to the bits of a and b.

Bitwise OR

a|b

v1 = v2 | v3

Applies the | operation to the bits of a and b.

Logical AND

a&&b

v1 = v2 && v3

Logical AND first evaluates a. If a is true, then evaluate b. If b is true then returns true. If either a or b is false returns false. Note b is only evaluated if a is true.

Logical OR

a||b

v1 = v2 || v3

Logical OR first evaluates a. If a is false, then evaluate b. If b is false then returns false. If either a or b is true returns true. Note b is only evaluated if a is false.

Assignment

a=b

v1 = v2

Copies value in b into a, a must be a variable.

Compound assignments

a&=b, a^=b, a|=b

v1 &= v2

Applies the relevant operation between a and b then stores the value in a, a must be a variable.

Comma

a,c; c,a

v1, v2

In operation a,c executes a then c, retuns c.

Structure Operators

Where a and b are expressions, a and b are of the same structure type unless otherwise noted. c is of any type.

Index

a[]

v1 = v2[5]

Returns the member of a at index given by value in square brackets, index start at 0. Index passed must be compile time constant.

Member access

a.b, a.{"b"}

v1 = v2.mmbr

Returns the member of a named "b". "b" must be a valid member of a.

Cast

<:type:>a

v1 = <:{unsigned, float}:>v2

Standard Cast, if possible, converts a to type. Type conversion can only be made if the type you are casting to is bit compatible with the type we are casting from.

Address-of

&a

v1 = &v2

Returns the address of a. a must be a variable.

Less than, Greater than, Less than or equal to, Greater than or equal to

a<b, a<=b, a>b, a>=b

v1 = v2 < v3

Ordered comparison of a with b, returns bool

Equals, Not equals

a==b, a!=b

v1 = v2 == v3

Equals/not equals comparison of a with b, returns bool

Assignment

a=b

v1 = v2

Copies value in b into a, a must be a variable. See Assignment Expansion.

Comma

a,c; c,a

v1, v2

In operation a,c executes a then c, retuns c.

Union Operators

Where a and b are expressions, a and b are of the same union type unless otherwise noted. c is of any type.

Index

a[]

v1 = v2[5]

Returns the member of a at index given by value in square brackets, index start at 0. Index passed must be compile time constant. If the union already contains the member at index, that is returned. Otherwise the union is cleared and the contained member is changed to the member at index and that is returned.

Member access

a.b, a.{"b"}

v1 = v2.mmbr

Returns the member of a named "b". "b" must be a valid member of a. If the union already contains the member b, that is returned. Otherwise the union is cleared and the contained member is changed to b and that is returned.

Cast

<:type:>a

v1 = <:<|unsigned, float|>:>v2

Standard Cast, if possible, converts a to type. Type conversion can only be made if the type you are casting to is bit compatible with the type we are casting from.

Index

?a

v1 = ?v2

If the union a contains a member return true, otherwise false.

Address-of

&a

v1 = &v2

Returns the address of a. a must be a variable.

Less than, Greater than, Less than or equal to, Greater than or equal to

a<b, a<=b, a>b, a>=b

v1 = v2 < v3

Ordered comparison of a with b, returns bool

Equals, Not equals

a==b, a!=b

v1 = v2 == v3

Equals/not equals comparison of a with b, returns bool

Assignment

a=b

v1 = v2

Copies value in b into a, a must be a variable.

Comma

a,c; c,a

v1, v2

In operation a,c executes a then c, retuns c.

List Operators

Where a and b are expressions, a and b are of the same list type unless otherwise noted. c is of any type. listof is a value of the type that the list contains. count is a value of type unsigned. {idx1, idx2} represents a structure containing two unsigned values.

Index

a[]

v1 = v2[5]

Returns the list item at index. The index must be in the range 0 to (size-1).

Head

!a

v1 = !v2

Returns a copy of the first item in list. The list must not be empty.

Tail

+a

v1 = +v2

Returns a list containing all but the first item in list.

Question mark

?a

v1 = ?v2

Returns true if the list contains items, otherwise false.

Cast

<:type:>a

v1 = <:[unsigned, float]:>v2

Standard Cast, if possible, converts a to type. Type conversion can only be made if the type you are casting to is bit compatible with the type we are casting from.

Address-of

&a

v1 = &v2

Returns the address of a. a must be a variable.

List beginning

a*count

v1 = v2 * v3

Returns a list containing count items from the front of the list. List must contain count number of items.

List end

a/count

v1 = v2 / v3

Returns a list containing count items from the back of the list. List must contain count number of items.

List range

a*{idx1, idx2}

v1 = v2 * {1, 7}

Returns a list containing items from list a in the range [idx1, idx2). idx1 and idx2 must be valid indexes.

List concatenation

a+b, a+listof, listof+a

v1 = v2 + v3

Returns a list containing all of the items in a concaternated with b. Or returns a list concaternated with listof appended to the front or back.

List remove

a-listof

v1 = v2 - v3

Returns a list containing all of the items in a, except those that equal to (==) listof.

Less than, Greater than, Less than or equal to, Greater than or equal to

a<b, a<=b, a>b, a>=b

v1 = v2 < v3

Ordered comparison of a with b, returns bool

Equals, Not equals

a==b, a!=b

v1 = v2 == v3

Equals/not equals comparison of a with b, returns bool

Assignment

a=b

v1 = v2

Copies value in b into a, a must be a variable.

Comma

a,c; c,a

v1, v2

In operation a,c executes a then c, retuns c.

Optional Operators

Where a and b are expressions, a and b are of the same optional type unless otherwise noted. c is of any type.

Indirection

^a

v1 = ^v2

Returns the value in the optional a. Optional must be set, otherwise undefined.

Question mark

?a

v1 = ?v2

If the optional has been set returns true, otherwise false.

Cast

<:type:>a

v1 = <:unsigned?:>v2

Standard Cast, if possible, converts a to type. Type conversion can only be made if the type you are casting to is bit compatible with the type we are casting from.

Address-of

&a

v1 = &v2

Returns the address of a. a must be a variable.

Less than, Greater than, Less than or equal to, Greater than or equal to

a<b, a<=b, a>b, a>=b

v1 = v2 < v3

Ordered comparison of a with b, returns bool

Equals, Not equals

a==b, a!=b

v1 = v2 == v3

Equals/not equals comparison of a with b, returns bool

Assignment

a=b

v1 = v2

Copies value in b into a, a must be a variable.

Comma

a,c; c,a

v1, v2

In operation a,c executes a then c, retuns c.

Closure Operators

Cast

<:type:>a

v1 = <:[-] mutable unsigned -> unsigned:>v2

Standard Cast, if possible, converts a to type. Type conversion can only be made if the type you are casting to is bit compatible with the type we are casting from.

Address-of

&a

v1 = &v2

Returns the address of a. a must be a variable.

Closure Call

a -> [~] expr

v1 = v2 -> [~] v3

See Closures.

Less than, Greater than, Less than or equal to, Greater than or equal to

a<b, a<=b, a>b, a>=b

v1 = v2 < v3

Ordered comparison of a with b, returns bool

Equals, Not equals

a==b, a!=b

v1 = v2 == v3

Equals/not equals comparison of a with b, returns bool

Assignment

a=b

v1 = v2

Copied value in b into a, a must be a variable.

Comma

a,c; c,a

v1, v2

In operation a,c executes a then c, retuns c.


Operator Precedence


Operator Name Resolution order
Postfix operators
0 a++ a--
a[]
a.mmbr a.{"mmbr"}
Suffix/postfix increment and decrement
Index
Member access
Outward
Prefix operators
1 ++a --a
+a -a
!a ~a
^a
?a
<:type:>a as<:type:>a
&a
Prefix increment and decrement
Unary plus and minus
Logical NOT and bitwise complement
Indirection
Question mark
Casts
Address-of
Outward
2 a -> func
a -> [~] expr
a -> typeof expr
Function Call
Closure Call
Type Call
Left-to-right
3 a*b a/b a%b Multiplication, division and modulus Left-to-right
4 a+b a-b Addition and subtraction Left-to-right
5 a<>b Bitwise left shift and right shift Left-to-right
6 a<b a<=b a>b a>=b Relational operators <, <=, > and >= Left-to-right
7 a==b a!=b For relational operators == and != Left-to-right
8 a&b Bitwise AND Left-to-right
9 a^b Bitwise XOR (exclusive or) Left-to-right
10 a|b Bitwise OR (inclusive or) Left-to-right
11 a&&b Logical AND Left-to-right
12 a||b Logical OR Left-to-right
13 a=b
a*=b a/=b a%=b
a+=b a-=b
a<<=b a>>=b
a&=b a^=b a|=b
Assignment
Compound assignments
*Right-to-left
Right-to-left
14 a,b Comma Left-to-right

Operators that appear at the top in the above list are of higher precendence, aka they are resolved before operators with a lower precedence. For example a && b || c && d is resolved as ((a && b) || (c && d)) as the && operator has higher precedence than the || operator.

The resolution order is ment to signify the order of evaluation for operators within a particular group. For example a + b - c is ordered Left-to-right and so is resolved as (((a) + b) - c), whereas the following is resolved Right-to-left a += b *= c and so is resolved as (a += (b *= (c)))

For postfix and prefix operators, the resolution order doesn't matter as the resolution order is always from the variable/value outward, but note that postfix operators always have higher precedence than prefix operators. So !--a.b++ is resolved as (!(--(((a).b)++))).

* Note, depending upon context, assignment maybe resolved Right-to-left or Left-to-right, meaning e1 = e2 can be resolved as (e1) = e2 or e1 = (e2) for any kind of e1 or e2, this applies to assignment and expansion assignment (See Assignment Expansion). However multiple assignments aka e1 = e2 = e3 are always resolved Right-to-left, meaning e1 = e2 = e3 is always e1 = (e2 = e3) but e2 and e3 may be resolved in any order.


Inbuilt Functions

clear

definition

%T[1] -> clear

desciption

The function accepts structure, union, list or optional types. This returns a value of the type passed in set to the default initialized state.

parameters

[1] parameters of type structure, union, list or optional

return

value of the type passed set to the default initialized state

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> clear lst1 to empty list lst1 = lst1 -> clear return +0;


erase

definition

{[%T][1], unsigned[2]} -> erase

desciption

The function takes a list[1] and an index[2], erasing the element at position index[2] from the list[1]. Index[2] must be a valid index into the list[1].

parameters

[1] a list

[2] index of the element to be erased from the list

return

a list with the element erased

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> erase 0 lst1 = {lst1, 0} -> erase <> erase 3 lst1 = {lst1, 2} -> erase <> erase 4 lst1 = {lst1, 2} -> erase return +0;


erase

definition

{[%T][1], unsigned[2], unsigned[3]} -> erase

desciption

The function takes a list[1] and a start index[2] and end index[3] representing the range [index[2], index[3]), erasing the elements starting at position index[2] ending at 1 before index[3] from the list[1]. Both indexes[2][3] must be a valid indexes into the list[1].

parameters

[1] a list

[2] the start index

[3] the end index

return

a list with the elements erased

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> erase 0 lst1 = {lst1, 0, 1} -> erase <> erase [2, 3, 4] lst1 = {lst1, 1, 4} -> erase return +0;


insert

definition

{[%T][1], %T[2], unsigned[3]} -> insert

desciption

The function inserts element[2] into list[1] at index[3]. Index[3] must be a valid index into the given list[1].

parameters

[1] a list

[2] the element to be inserted

[3] the index to insert the element into the list

return

a list with the element inserted

example

mutable [[char]] -> main -> int : [unsigned, unsigned] lst1 <> lst1 == [{3, 4}] lst1 = {lst1, {3, 4}, 0} -> insert <> lst1 == [{3, 4}, {6, 9}] lst1 = {lst1, {6, 9}, 1} -> insert <> lst1 == [{0, 2}, {3, 4}, {6, 9}] lst1 = {lst1, {0, 2}, 0} -> insert <> lst1 == [{0, 2}, {3, 4}, {5, 5}, {6, 9}] lst1 = {lst1, {5, 5}, 2} -> insert return +0;


insert

definition

{[%T][1], [%T][2], unsigned[3]} -> insert

desciption

The function inserts the elements in list[2] into list[1] at index[3]. Index[3] must be a valid index into the given list[1].

parameters

[1] a list

[2] a second list who's elements are to be added to list [1]

[3] the index to insert the elements

return

a list with the elements inserted

example

mutable [[char]] -> main -> int : [unsigned, unsigned] lst1 <> lst1 == [{0, 2}, {3, 4}, {5, 5}, {6, 9}] lst1 = {lst1, [{0, 2}, {3, 4}, {5, 5}, {6, 9}], 0} -> insert <> lst1 == [{0, 2}, {3, 4}, {0, 0}, {2, 2}, {5, 5}, {6, 9}] lst1 = {lst1, [{0, 0}, {2, 2}], 2} -> insert return +0;


push_front

definition

{[%T][1], %T[2]} -> push_front

desciption

The function inserts element[2] into list[1] before the first element of list[1].

parameters

[1] a list

[2] the element to be inserted

return

a list with the element inserted

example

mutable [[char]] -> main -> int : [unsigned, unsigned] lst1 <> lst1 == [{3, 4}] lst1 = {lst1, {3, 4}} -> push_front <> lst1 == [{5, 8}, {3, 4}] lst1 = {lst1, {5, 8}} -> push_front <> lst1 == [{0, 2}, {5, 8}, {3, 4}] lst1 = {lst1, {0, 2}} -> push_front <> lst1 == [{5, 5}, {0, 2}, {5, 8}, {3, 4}] lst1 = {lst1, {5, 5}} -> push_front return +0;


push_back

definition

{[%T][1], %T[2]} -> push_back

desciption

The function inserts element[2] into list[1] after the last element of list[1].

parameters

[1] a list

[2] the element to be inserted

return

a list with the element inserted

example

mutable [[char]] -> main -> int : [unsigned, unsigned] lst1 <> lst1 == [{3, 4}] lst1 = {lst1, {3, 4}} -> push_back <> lst1 == [{3, 4}, {5, 8}] lst1 = {lst1, {5, 8}} -> push_back <> lst1 == [{3, 4}, {5, 8}, {0, 2}] lst1 = {lst1, {0, 2}} -> push_back <> lst1 == [{3, 4}, {5, 8}, {0, 2}, {5, 5}] lst1 = {lst1, {5, 5}} -> push_back return +0;


pop_front

definition

[%T][1] -> pop_front

desciption

The function erases the element at index 0 from the list[1]. The list[1] must be atleast size 1.

parameters

[1] a list

return

a list with the first element erased

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> lst1 == [1, 2, 3, 4] lst1 = lst1 -> pop_front return +0;


pop_back

definition

[%T][1] -> pop_back

desciption

The function erases the element at index (size - 1) from the list[1]. The list must be atleast size 1.

parameters

[1] a list

return

a list with the last element erased

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> lst1 == [0, 1, 2, 3] lst1 = lst1 -> pop_back return +0;


reverse

definition

[%T][1] -> reverse

desciption

The function reverses the elements in list[1].

parameters

[1] a list

return

a list with the elements reversed

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> lst1 == [4, 3, 2, 1, 0] lst1 = lst1 -> reverse return +0;


reserve

definition

{[%T][1], unsigned[2]} -> reserve

desciption

The function increases or decreases the memory block allocated to the list[1] for holding elements. If the number requested is less than the size of the list[1] then the internal memory block size (reserve size) is made to fit the number of elements in the list[1]. Otherwise the internal memory block is increased in size upto the specified count element size[2].

parameters

[1] a list

[2] new reserve size

return

a list with an internal memory block of the requested size (or larger).

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> internally allocated memory set to atleast 20 lst1 = {lst1, 20} -> reserve return +0;


resize

definition

{[%T][1], unsigned[2]} -> resize

desciption

The function changes the number of elements held in list[1]. If the size[2] specified is larger than the current size the new elements will be set to their default initilization data. If the size[2] specified is smaller then the excess elements with be erased.

parameters

[1] a list

[2] the new size

return

a list with the requested number of elements

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> lst1 == [0, 1, 2] lst1 = {lst1, 3} -> resize <> lst1 == [0, 1, 2, 0, 0, 0] lst1 = {lst1, 6} -> resize return +0;


resize

definition

{[%T][1], unsigned[2], %T[3]} -> resize

desciption

The function changes the number of elements held in list[1]. If the size[2] specified is larger than the current size the new elements will be set to the initializer value[3]. If the size[2] specified is smaller then the excess elements with be erased.

parameters

[1] a list

[2] the new size

[3] value to set new elements to

return

a list with the requested number of elements

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> lst1 == [0, 1, 2] lst1 = {lst1, 3, 5} -> resize <> lst1 == [0, 1, 2, 3, 3, 3] lst1 = {lst1, 6, 3} -> resize return +0;


swap

definition

{[%T][1], unsigned[2], unsigned[3]} -> swap

desciption

The function swaps the elements at index[2] and index[3] in list[1]. Index[2] and index[3] must be valid indexes into the list[1].

parameters

[1] a list

[2] index of first element to be swapped

[3] index of second element to be swapped

return

a list with element at index[2] swapped with element at index[3]

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> lst1 == [0, 3, 2, 1, 4] lst1 = {lst1, 1, 3} -> swap return +0;


swap

definition

{%T[1], %T[2]} -> swap

desciption

The function returns the value[1] with value[2] swapped.

parameters

[1] first value

[2] second value

return

{%T, %T} structure, of value[2] followed by value[1]

example

mutable [[char]] -> main -> int : unsigned a = 25 unsigned b = 50 <> a == 50, b == 25 {/a, b/} = {a, b} -> swap return +0;


size

definition

%T[1] -> size

desciption

If the value[1] passed to size is of type structure the size returned is the number of elements in that structure. If the value[1] passed to size is of type list the size returned is the number of elements in the list. All other types the size returned is 1.

parameters

[1] value

return

unsigned, the (counting) size of the given value[1]

example

mutable [[char]] -> main -> int : lst1 = [0, 1, 2, 3, 4] <> sze == 5 sze = lst1 -> size strct1 = {6, 11, 87} <> sze == 3 sze = strct1 -> size <> sze == 1 sze = 45.7f -> size return +0;


type

definition

%T[1] -> type

desciption

The function takes a union[1] and returns the type (unsigned) representing the currently held value in the union[1], type returns 0 if the union[1] is uninitialized.

parameters

[1] a union

return

unsigned, number of the currently held value in the union[1]

example

mutable [[char]] -> main -> int : <|float, unsigned|> unn <> num == 0 num = unn -> type unn = 78 <> num == 2 num = unn -> type return +0;