Expressions are constructs denoting rules of computation of a value from other values by the application of operators. Expressions consist of operands and operators. Parentheses may be used to express specific associations of operators and operands. Dino is a dynamic-typed language. This means that a variable can store any Dino value.
All Dino values are first class values, i.e. they can be assigned to a variable, can be passed as a parameter of a function/class, and can be returned by functions. Operators require operands whose values are of given type and return the value of the result type. Most values have a representation in Dino. When a value representation is encountered in an expression during the expression evaluation, the new value is generated.
There are values of structured types, i.e. values which are
built from other values. The value of a structured type may be
mutable or immutable. A value or sub-value of a
mutable value can be changed. An immutable value can not be changed
after its generation. You can make a mutable value immutable as a
side effect by applying the operator final
(the table key is
also made immutable as a side effect of writing to the table). In all
cases, the operator returns the operand value as the result. If you
try to change an immutable value, exception immutable
is
generated. You can make a new mutable value as a side effect of
applying operator new
. The operator returns a new value
equal to the operand value.
Expr = final Expr
| new Expr
Structured value types are also shared value types. This
notion means that if two or more different variables (array elements
or table elements or keys) refer to the same value and the value is
changed through one variable, the value which is referred through the
other variables is changed too. There is no difference between the
notion "the same value" and the notion "equal values" for non-shared
type values. For the shared type operands, equality means that the
operands have the same structure (e.g. vectors with the same length)
and the corresponding element values are the same.
Examples:
new 5
new ['a', 'b', 'c']
new "abc"
new {"key0" : 10, "key1" : 20}
final 5
final ['a', 'b', 'c']
final "abc"
final {"key0" : 10, "key1" : 20}
Dino has the following types of values:
Expr = nil
Character
in the
section Vocabulary and Representation.
Expr = CHARACTER
Integer
in the section Vocabulary and
Representation. It is always stored as a 32-bit
integer value.
Expr = INTEGER
FloatingPointNumber
in section
Vocabulary and Representation. It is always
stored as an IEEE double (64-bit) floating point value.
Expr = FLOATINGPOINTNUMBER
:
. The
repetition value is converted into an integer value by
default. If the repetition value after the conversion is
not integer, exception optype
is generated. If
the repetition value is negative or zero, the element
value will be absent in the vector. Elements of vector
are accessed by their indexes. Indexes always starts with
0. Vectors in Dino are heterogenous, i.e. elements of a
vector may be of different types. A string represents an
immutable vector all of whose elements are characters in
the string. Elements of mutable vectors can be added to
or removed from the vector (see predefined functions
ins, insv, and del).
Expr = "[" ElistPartsList "]"
| STRING
ElistPartsList = [ Expr [":" Expr ] {"," Expr [":" Expr ] } ]
Examples:
"aaab"
['a', 'a', 'a', 'b']
[3 : 'a', 'b']
[3.0 : 'a', 'b']
["3" : 'a', 'b']
['a', 10, 10.0, "abcd", {}]
[]
{
and }
with optional element values
with a preceding :
. By default the element value
is equal to nil. It is not allowed to have
elements with equal keys in a table. If it is not true in
a table constructor, exception keyvalue
is
generated. Elements of tables are accessed by their keys.
Elements of mutable tables can be added to or removed from
the table correspondingly by assigning values and with the
aid of the function del. The side effect of the
table constructor execution is that the keys become
immutable.
Expr = "{" ElistPartsList "}"
Examples:
{'a', 'b', 10:[10]}
{'a' : nil, 'b' : nil, 10 : [10]}
{[10, 'a', {10}] : 10, [10] : {20:20}}
{}
type (expression)
.
Expr = char
| int
| float
| hide
| hideblock
| vector
| table
| func
| thread
| class
| func "(" ")"
| thread "(" ")"
| class "(" ")"
| type
There are the following type values:
type (nil)
to get it.char
.int
.float
.vector
.table
.func
.thread
.class
.func ()
.thread ()
.class ()
.hide
.hideblock
.type
.There is a special Dino construction called a designator. A designator refers for a vector or table element or for a declaration. If the designator refers to a vector or table element or for a variable declaration, it can stand in the left hand side of an assignment statement. If the designator stands in an expression, the corresponding value is used (vector/table element value, variable value, function, thread-function, or class). When the designator referring to table element stands up in the left hand side of an assignment statement, its key becomes immutable.
Expr = Designator
A designator referring to a vector element has the following syntax:
Designator = DesignatorOrCall "[" Expr "]"
DesignatorOrCall = Designator
| Call
The value of the construction before the brackets must be a vector.
Otherwise, the exception indexop
is generated. The value of
expression in the brackets (so called the index) is converted
to integer. If this is not possible, exception indextype
is
generated. If the index is negative or greater than or equal to the
vector length, the exception indexvalue
is generated. The
value of the designator will be the vector element value with given
index (the indexes starts with zero).
Examples:
vect [1]
vect ["1"]
vect [1.0]
A designator referring to a table element has the following syntax:
Designator = DesignatorOrCall "{" Expr "}"
The value of the construction before the figure brackets must be a
table. Otherwise, the exception keyop
is generated. The
value of expression in the figure brackets is called the key.
The value of the designator will be the table element value with the
key which is equal to given key. If the element with the given key is
absent in the table, exception keyvalue
is generated.
Examples:
tab {'c'}
tab {10}
tab {"1"}
tab {1.0}
The remaining forms of designator refer to a declaration. See section
Declarations and Scope Rules for a description on how they
work.
Designator = DesignatorOrCall "." IDENT
| IDENT
Examples:
value
value.f
One form of expression is the call of a function, thread-function, or
class. The value of the designator before the actual parameters
should be a function, thread-function, or class. Otherwise, the
exception callop
is generated. An instance of the block
corresponding to the body of the function, thread-function, or class
is created. The actual parameter values are assigned to the
corresponding formal parameters. If the corresponding function,
thread-function, or class has no default formal parameter
args
(see section Declarations), the remaining
actual parameter values are ignored. Otherwise, a vector whose
elements are the remaining parameter values is created and assigned to
the parameter args
. If there is no corresponding actual
parameter for a formal parameter, the default parameter value (see
section Declarations) or the value nil is assigned
to the formal parameter. Then statements in the block are executed.
If it is the call of a thread-function, a new execution thread is
created, and the statements of the block is executed in the new
thread. The value of call of the thread-function is the corresponding
thread. It is returned before starting the execution of statements in
the new thread.
Execution of the body is finished by reaching the block end or by execution of a return-statement. Finishing of the thread-function results in finishing the corresponding thread. The return-statement in a thread-function or in class should be without an expression. The call of a class returns the created object. A function call returns the value of the expression in the executed return-statement. Otherwise, the function call returns the value nil.
Expr = Call
Call = Designator ActualParameters
ActualParameters = "(" [ Expr { "," Expr } ] ")"
Examples:
f ()
f (10, 11, ni, [])
obj.objf ()
Expressions consist of operands and operators. The order in which
operators are executed in an expression is defined by their
priority and associativity of operators. That means
that the expression a op1 b op2 c
when the operator
op2
has higher priority than op1
is analogous to
a op1 (b op2 c)
. Dino operators have analogous priorities to
the ones in C language. The following Dino operators are placed in
the order of their priority (the higher the line on which the operator
is placed, the higher its priority).
! # ~ final new
* / %
+ -
@
<< >> >>>
< > <= >=
== != === !==
&
^
|
in
&&
||
:
?
All binary operators have left associativity in Dino. That means that
the expression a op1 b op2 c
when operators op1
and
op2
have the same priority is analogous to (a op1 b) op2
c
. Parentheses may be used to express specific associations of
operators and operands.
Expr = "(" Expr ")"
Most of the Dino operators require the operands to be of given types.
If an operand is not of given type, the conversion of it into the type
needed may be made. If after the possible conversions the operands
are still not of necessary types, exception optype
is
generated (when something about exceptions in this case is not
mentioned). The following conversions may be made by default:
syserrors.erange
may be
generated. In all remaining cases the results of conversion
coincide with the operand.syserrors.erange
may be generated.Logical operators produce the integer result 1 which means
true or 0 which means false. Logical `or'
||
and logical `and' &&
are short circuit
operators. That means that the second operand is evaluated depending
on the result of the first operand. When the operands of the
operators are evaluated, the arithmetic conversion is made.
If the first operand of logical `or' is nonzero (integer or floating point), the result will be 1. Otherwise, the second operand is evaluated. If the second operand is nonzero, the result will be 1. Otherwise, the result will be 0.
If the first operand of logical `and' is zero (integer or floating point), the result will be 0. Otherwise, the second operand is evaluated. If the second operand is nonzero, the result will be 1. Otherwise, the result will be 0.
Logical negation `!' makes impilict integer conversion of the operand. If the operand is zero (integer or floating point), the result will be 1. Otherwise, the result will be 0.
Operator in
checks that there is an element with the given
key (the first operand) in the given table (the second operand). If
the element is in the table, the result will be 1. Otherwise the
result will be 0. If the second operand is not a table, exception
keyop
is generated.
Expr = Expr "||" Expr
| Expr "&&" Expr
| Expr in Expr
| "!" Expr
Examples:
!(type (i) == int && type (a) == table && i >= 0 && i < #a)
k in t && t {k} == 0
0.0 || another_try
0 || another_try
The following operators work on integers (implicit integer conversion
is made) and return an integer result. Operators | ^ & ~
denote correspondingly bitwise or, bitwise exclusive or, bitwise and,
and bitwise negation of 32-bit integers.
Operators << >>> >>
denote correspondingly logical left bit
shift, logical right bit shift, and arithmetic (with sign extension)
right bit shift of given number (the first operand) by given number of
bits (the second operand). The value of the second operand must be
non-negative, otherwise the result is undefined.
Expr = Expr "|" Expr
| Expr "^" Expr
| Expr "&" Expr
| Expr "<<" Expr
| Expr ">>" Expr
| Expr ">>>" Expr
| "~" Expr
Examples:
(i >> shift) & mask
i & ~mask | (value << shift) & mask
i >>> 2
i << 2
All comparison operators return a logical value (integer 0 which means false or integer 1 which means true).
Operators equality ==
and inequality !=
may make
some conversion of the operands. If one of the two operands is
string, then the string conversion is applied to the other operand
before the comparison. Otherwise, standard arithmetic conversion is
applied to the operands. The operators do not generate exceptions
(but the conversions may). The operands are equal if they have the
same type and equal values (see section Types and Values).
For instances, functions and classes, the equality requires also the
same context.
Operator identity ===
or unidentity !==
returns 1
if the operands have (or not) the same value or 0 otherwise. The
operators never generate exceptions.
By default the arithmetic conversion is applied to the operands of
operators < > <= >=
. There is no exception if the operands
after the conversion are of integer or floating point type. So the
operands should be characters, integers, floating point numbers, or
strings representing integers or floating point numbers.
Expr = Expr "==" Expr
| Expr "!=" Expr
| Expr "===" Expr
| Expr "!==" Expr
| Expr "<" Expr
| Expr ">" Expr
| Expr "<=" Expr
| Expr ">=" Expr
Examples:
10 == 10
10 === 10
10 == 10.0
10 !== 10.0
10 <= 'c'
p != nil
'c' == "c"
10 < "20.0"
[10, 20] == [10, 20]
[10, 20] !== [10, 20]
The following operators return integer or floating point numbers.
Before operator execution, implicit arithmetic conversion is made on
the operands. The binary operators + - * / %
denote
correspondingly integer or floating point addition, subtraction,
multiplication, division, and evaluation of remainder. Unary operator
-
denotes arithmetic negation. The unary operator +
is given for symmetry and it returns simply the operand after the
conversion. It can be used for conversion of a string into an integer
or floating point number.
Expr = Expr "+" Expr
| Expr "-" Expr
| Expr "*" Expr
| Expr "/" Expr
| Expr "%" Expr
| "+" Expr
| "-" Expr
Examples:
+"0"
+"10."
+"1e1"
-i
(value + m - 1) / m * m
index % bound
The Dino conditional expression is analogous to the C language one.
Implicit arithmetic conversion is made for the first expression
followed by ?
. If the value of the expression is non zero
(integer or floating point), the second expression with following
:
is evaluated and it will be the result of the condition
expression. Otherwise, the third expression is evaluated and it
becomes the result.
The operator #
can be applied to a vector or a table. It returns
the length of the vector or the number of elements in the table.
The operator @
denotes concatenation of two vectors into a new
vector. Before the concatenation implicit string conversion of the
operands is made.
The remaining operators look like function calls. Operator
type
returns the expression type. Never is exception
generation possible during the operator evaluation.
The operator char
is used to conversion of a value into a
character. First, implicit integer conversion is applied to the
operand. The operand should be an integer after the conversion.
Otherwise, exception optype
will be generated. The integer
is transformed into the character with the corresponding code. If the
code is too big to be a character or is negative, exception
syserrors.erange
is generated.
The operator int
is used to conversion of a value into an
integer. Implicit integer conversion is applied to the operand. The
operand should be an integer after the conversion. Otherwise,
exception optype
will be generated. If the code is too big
to be an integer, exception syserrors.erange
is generated.
The operator float
is used to conversion of a value into
floating-point number. The first, implicit arithmetic conversion is
applied to the operand. The operand should be an integer or a
floating-point number after the conversion. Otherwise, exception
optype
will be generated. If the result integer is
transformed into the corresponding floating-point number. If the code
is too big or too small to be a floating-point number, exception
syserrors.erange
is generated.
The operator vector
is used for conversion of a value into a
vector. First, implicit string conversion is applied to the operand.
The optional second expression defines the format used only for the
string conversion of a character, an integer, a floating point number,
or a string. The second parameter value should be a string after
implicit string conversion. The format should not be given for a
table. The first operand should be a table or a vector after
conversion. The table is transformed into a new vector which consists
of pairs (one pair for each element in the table). The first element
of the pair is a key of the corresponding element, and the second one
is the element itself. The order of pairs in the result vector is
undefined.
The operator table
is used to conversion of a value into
table. First, string conversion is applied to the operand. The
operand should be a vector or a table after the conversion. The
vector is transformed into a new table whose elements are equal to the
vector elements that have integer keys equal to the corresponding
vector indexes.
If the operand of the operator func
is a block instance of
the body of a function, it returns the corresponding function.
Otherwise, it returns the value nil. The operator never
generates exceptions.
If the operand of the operator thread
is a thread, it returns
the corresponding thread-function. Otherwise, it returns the value
nil. The operator never generates exceptions.
If the operand of the operator class
is an object, it returns
the object's class. Otherwise, it returns the value nil.
The operator never generates exceptions.
Expr = Expr "?" Expr ":" Expr
| "#" Expr
| Expr "@" Expr
| type "(" Expr ")"
| char "(" Expr ")"
| int "(" Expr ")"
| float "(" Expr ")"
| vector "(" Expr ["," Expr] ")"
| table "(" Expr ")"
| func "(" Expr ")"
| thread "(" Expr ")"
| class "(" Expr ")"
Examples:
i < 10 ? i : 10
#{"a", 'b'}
#["a", 'b']
"concat this " @ "and this"
type (type)
type (10)
char (12)
vector (10)
vector (10, "%x")
vector ({"1":1, "2":2})
table ([1, 2, 3, 4])
func (context (obj))
thread (curr_thread)
class (c ())