Next Previous Contents

5. Expressions

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.

5.1 Types and Values

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:

5.2 Designators

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

5.3 Calls

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 ()

5.4 Operators

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:

Logical operators

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

Bit operators

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

Comparison operators

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]

Arithmetic operators

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 

Miscellaneous operators

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 ())


Next Previous Contents