Boolean values - e.g. true
and false
.
Integer types, usually corresponding to machine register widths.
int
is a 32-bit signed integer, with values ranging from
-2147483648 to 2147483647 (inclusive).
uint
is an unsigned 32-bit integer, with values ranging from
0 to 4294967295 (inclusive).
Integer types can also be written with a suffix representing the bit width, for example
int32
, int64
, uint16
etc.
Currently supported bit widths are 16, 32, and 64.
32-bit floating point IEEE 754 format number.
Winter does not provide any exceptions from the IEEE 754 standard however.
64-bit double-precision floating point IEEE 754 format number.
An alias (another name) for float
or double
, depending on the runtime configuration option
real_is_double
.
A Unicode character.
A string of Unicode characters.
A string of Unicode characters.
A fixed-length array of N items of some type, for example:
array<int, 10>
Is an array of 10 integers.
A variable-length array items of some type, for example:
varray<int>
Is an array of integers. The variable-length aspect means the length of the varray is not known at compile time, but only at run time.
A fixed-length vector of N items of some type, for example:
vector<float, 4>
Is an array of 4 floats. Vectors are similar to small arrays. They are designed to be stored in vector registers and be operated on by SIMD instructions when possible.
The number of elements must be > 0 and < 128.
A tuple (short ordered list) of types, for example
tuple<int, float>
Is a tuple with the first element type int and the second float.
A function that takes zero or more arguments and returns a value.
function<arg0, arg1... argN, return_type>
For example, the square function, which maps a float to a float, would have type
function<float, float>
A type of which values are not operable on by the program. Used for data (for example pointers) being passed through the program.
In general, all Winter values can be be written with literals, e.g. can be written directly in the program.
false
and true
.
Int literals can be written in hexadecimal format (base 16) or decimal format (base 10).
Hexadecimal int literals have a 0x
prefix, and may contain the lower or upper case
letters a
to f
, for example
0xDEADBEEF
Int literals may have an i
or u
suffix followed by the desired bit width for the value,
for example
100i16
is 32-bit signed integer with value 100,
1152921504606846976u64
is a 64-bit unsigned integer with value 1152921504606846976.
The bit width must be 16, 32, or 64.
These have a similar syntax to C++:
[sign] [digits] [.digits] [ { e | E }[sign]digits]f
For example
1.23f
1.23e45f
1.23e-45f
Floating-point literals without an f
suffix are single-precision if the floating_point_literals_default_to_double configuration option is false.
These have a similar syntax to C++:
[sign] [digits] [.digits] [ { e | E }[sign]digits]d
For example
1.23d
1.23e45d
1.23e-45d
Floating-point literals without a d
suffix are double-precision if the
floating_point_literals_default_to_double configuration option is true.
By default floating_point_literals_default_to_double is true, so by default
floating-point literals are double-precision.
Character literals are enclosed with single quotes, like so:
'a'
Various escape sequences are also allowed, much like C++:
'\''
is a single-quote character
'\\'
is a single backslash character
'\n'
is a newline character
'\r'
is a carriage return character
'\t'
is a tab character
Unicode characters can also be written with the numeric code point:
'\u{7FFF}'
Where the hexadecimal value in the curly braces gives the Unicode code point.
Character literals are enclosed with double quotes, like so:
"hello"
The same escape sequences beginning with the backslash character, as for char literals are also allowed in strings, for example
"hello \n\n \t \u{7FFF}"
An array literal is written using square brackets, with an a
suffix.
[1, 2, 3]a
NOTE: Syntax for this may change.
A varray (variable-length array) literal is written using square brackets, with a va
suffix.
[1, 2, 3]va
NOTE: Syntax for this may change.
A vector literal is written using square brackets, with a v
suffix.
[1, 2, 3]v
NOTE: Syntax for this may change.
An tuple literal is written using square brackets, with an t
suffix.
[1, 2, 3]t
Tuple literals can also be written using parentheses containing more than one element, for example
(1, 2)
A structure value can be created using the name of the structure as a function, followed by a comma-separated list of values to use for structure members.
For example
struct Complex { float re, float im }
def addComplex(Complex a, Complex b) : Complex(a.re + b.re, a.im + b.im)
In this example addComplex uses a constructor/literal call to construct a new Complex value and return it from the function.
If expressions are written like so:
if cond then a else b
for example
if x < 1 then y + 2 else z * 2
Let blocks introduce one or more named variables into an expression, for example
let
a = 5
in
a + 10
will return the value 15.
Multiple let clauses can be listed after each other separated just by whitespace, for example
let
a = 5
b = 10
in
a + b
The type of the introduced variables can also be explictly written, for example
let
float x = 1.0f
in
x + 2.0f
The right side of the =
can be an arbitrary expression, for example
let
a = f(1, 2) + 3
in
a + b
Functions are defined using the def
keyword, for example
def square(float x) float : x*x
the parts of the definition are, from left to right:
- The
def
keyword that begins the definition - The name of the function, in this case 'square'
(float x)
: A comma-separated list of arguments with their types preceding them, enclosed by parenthesesfloat
: The return type. Expliclty writing the return type like this is optional. If not explicitly written it will be deduced by the compiler.:
A colon character- The function body expression, in this case
x*x
An example of leaving off the function return type:
def square(float x) : x*x
Struct values bundle a few values together into a single value.
They are similar to C++ structs.
struct Complex { float re, float im }
Structs are defined with the struct
keyword, followed by the name of the structure.
Then inside the curly braces, a comma-separated list of structure members is written.
A structure element can be accessed by writing a dot, and then the name of the structure member, for example
struct Complex { float re, float im }
def getRealPart(Complex c) : c.re
A tuple element can be accessed using square bracket indexing syntax:
pair = (a, b)
pair[0] # returns a
The expression in the square brackets must be computable at compile time.
Named constants can be used to define constant values in a program. Unlike other variables they can only be written at file scope, not in a function.
N = 1000
MY_CONST = f(a, b) + 5
The right side of the named constant definition can be an arbitrary expression.
The names need not be in upper case.
Named constants can also be defined with an explicit type, for example
int N = 1000
This behaves much like the ternary conditional operator in C++:
cond ? a : b
Returns a if cond is true, otherwise b.
These functions are mostly the same as in the C and C++ standard libraries. They never throw any kind of exception however, or set any kind of flag.
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
(float, float) -> float, (double, double)-> double, (vector<float, N>, vector<float, N>) -> vector<float, N>, (vector<double, N>, vector<double, N>) -> vector<double, N>
(float, float) -> float, (double, double)-> double, (vector<float, N>, vector<float, N>) -> vector<float, N>, (vector<double, N>, vector<double, N>) -> vector<double, N>
Same semantics as the C/C++ function fmod.
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
float -> int, double -> int, vector<float, N> -> vector<int, N>, vector<double, N> -> vector<int, N>
float -> float, double -> double, vector<float, N> -> vector<float, N>, vector<double, N> -> vector<double, N>
sign(x)
:
if x is < 0.0, returns -1.0,
if x = -0.0, returns -0.0
if x = +0.0, returns +0.0
if x is > 0.0 returns 1.0
int -> float, vector<int, N> -> vector<float, N>
Returns the closest representable float to the input value.
int -> double, vector<int, N> -> vector<double, N>
Returns the closest representable double to the input value.
int -> real, vector<int, N> -> vector<real, N>
Returns the closest representable real (may be float or double) to the input value.
int32 -> int64, vector<int32, N> -> vector<int64, N>
Returns a 64-bit integer with the same value as the input value.
array<e, N> -> int64, varray<e, N> -> int64, tuple<...> -> int64, vector<e, N> ->int64
Returns the length of the array or num elements of the vector or tuple.
Returns the i-th element of the array, vector or tuple. For example
elem([10, 20, 30]a, 1)
returns 20.