Chapter 9. Expressions and operators

Introduction

An expression is a part of an instruction used to determine or evaluate a single object.

The simplest form of an expression consists of a literal or an object reference, as shown below:

// simple expressions consisting of a literal
"123" // string
123   // integer
'a'   // character
yes   // yes_no

// simple expressions consisting of an object reference
c_identifier  // constant
v_name        // variable
i_index       // input argument
o_result      // output argument
a_city        // attribute

An expression is often used in assignment instructions. Thus, in the following instruction, an expression consisting of the string "Hello" is assigned to variable greeting of type string:

var string greeting = "Hello"

A more complex expression can be defined by repeatedly getting the value of an attribute or the result of a command from an intermediate result of the expression. This can be done as follows:

  • getting an object's attribute value

    The general syntax to get attribute values in an expression is:

    object_reference ( "." attribute_id ) *

    As we can see, the . (dot) operator, followed by the attribute's identifier, is used to access attribute values.

    Example:

    Suppose the following type has been defined:

    type product
    
       attribute identifier type:positive32 end
       attribute name type:string end
       attribute price_in_cents type:positive32 end
       
       command convert_to_XML
          out result type:string end
       end
    
    end

    Attribute values can then be accessed in expressions as follows:

    // first create a 'product' object
    const product orange = fa_product_factory.co_create ( &
       identifier = 100 &
       name = "orange" &
       price_in_cents = 50 )
    
    // now access atrributes
    const string       name  = c_orange.a_name         // use dot operator to access attribute 'name' of orange
    const string_value value = c_orange.a_name.a_value // use dot operator twice to access the string value of attribute 'name' of orange
  • getting the result of an object's command

    The general syntax is:

    object_reference ( "." command_id ( "." output_argument_id ) ? input_assignment_list ? ) *

    command_id is the command's identifier.

    output_argument_id is used to define the output argument taken into account in case the command has more than one output argument.

    input_assignment_list is the list of input argument assignments in the form input_argument_id = expression, enclosed between parenthesis. Input arguments that have default values can be omitted if the default value is suitable.

    Example:

    const string XML_code = c_orange.co_convert_to_XML // use dot operator to access command 'convert_to_XML' of orange

    If a command has input arguments, then a value must be specified for each input argument that doesn't have a default value.

    For example, the expression in the following assignment first applies command convert_to_XML to the product object, and then command replace_all is applied to the expression's intermediate result of convert_to_XML, in order to replace tag name by tag description in the XML string:

    const string modified_XML_code = c_orange.co_convert_to_XML.co_replace_all ( &
       to_replace = "name" &
       replace_by = "description" )

Both ways (i.e. getting attributes, and getting results of commands) can be combined, as follows:

const zero_positive32 character_count = c_orange.co_convert_to_XML.a_item_count

There is no limit for the complexity of an expression, but well written programs rarely surpass 5 levels of nesting.

Type of an expression

The resulting type of an expression is always the type of the last object evaluated.

Consider again the following instruction:

const zero_positive32 character_count = c_orange.co_convert_to_XML.a_item_count

In this case the first type encountered during the evaluation of the expression is product (the type of c_orange). Then, after evaluating co_convert_to_XML, the intermediate result is of type string (because co_convert_to_XML returns a string object), and the final result is an object of type zero_positive32 (because that's the type of attribute item_count in type string). Hence, the type of the entire expression is zero_positive32.

Because Obix is a statically typed language, the compiler always checks the type compatibility of an expression assigned to an object reference.

For more information about static typing please refer to:

Expression operators

Expressions are often used to do arithmetic calculations.

Suppose, for example, we want to compute 3 * 4 + 5 * 17, and assign the result to variable result.

A multiplication on an integer object is done in Obix with command multiply, and two numbers can be added with command add. Thus, the code looks like this:

var positive32 result = 3.multiply(4).add(5.multiply(17))

It is important to note that the mathematical precedence rule 'multiplication before addition' requires us to write

3.multiply(4).add(5.multiply(17))

instead of

3.multiply(4).add(5).multiply(17)

which would indeed compute (3 * 4 + 5) * 17.

The above code shows the 'pure OO way' to do it. Obviously, this is quite error-prone, because it is a bit cumbersome to write and the mathematical precedence rules have to be observed and programmed manually. Moreover, the expression

3.multiply(4).add(5.multiply(17))

is certainly less readable than

3 * 4 + 5 * 17

Therefore, Obix provides expression operators, and the precedence rules (e.g. multiplication before addition) are built into the language.

The benefit is that the above instruction can now simply be rewritten as:

var positive32 result = 3 * 4 + 5 * 17

The compiler implicitly converts this instruction into the first one, so that we don't need anymore to care about it.

An expression operator is a symbol used as a replacement (or shortcut) for a command.

For example, the expression operator * (multiply) is a shortcut for command multiply, which means that

3 * 4

is equivalent to

3.multiply(4)

The following table lists the operators defined in Obix.

Table 9.1. Expression operators

CategoryOperatorNameCommandExample
arithmetic calculations+additionco_add

foo + bar

equal to:
foo.add(bar)

-subtractionco_subtract

foo - bar

equal to:
foo.subtract(bar)

*multiplicationco_multiply

foo * bar

equal to:
foo.multiply(bar)

/divisionco_divide

foo / bar

equal to:
foo.divide(bar)

-unary minusco_unary_minus

-foo

equal to:
foo.unary_minus

boolean logicandboolean andco_and

foo and bar

equal to:
foo.and(bar)

orboolean orco_or

foo or bar

equal to:
foo.or(bar)

xorboolean exclusive orco_xor

foo xor bar

equal to:
foo.xor(bar)

notboolean not (inversion)co_not

not foo

equal to:
foo.not

comparisons=requal referenceN/Afoo =r bar
#runequal referenceN/Afoo #r bar
=vequal valueco_is_equal_to

foo =v bar

equal to:
foo.is_equal_to(bar)

#vunequal valueco_is_equal_to and co_not

foo #v bar

equal to:
foo.is_equal_to(bar).not

>greater thanco_compare_to and co_is_equal_to

foo > bar

equal to:
foo.compare_to(bar).is_equal_to(ty_comparator_result.greater)

>=greater than or equalco_compare_to and co_is_equal_to and co_not

foo >= bar

equal to:
foo.compare_to(bar).is_equal_to(ty_comparator_result.less).not

<less thanco_compare_to and co_is_equal_to

foo < bar

equal to:
foo.compare_to(bar).is_equal_to(ty_comparator_result.less)

<=less than or equalco_compare_to and co_is_equal_to and co_not

foo <= bar

equal to:
foo.compare_to(bar).is_equal_to(ty_comparator_result.greater).not

append&appendco_append

foo & bar

equal to:
foo.append(bar)


Expression operators are typically used with scalar types (e.g. integers, strings, booleans), but there usage is not limited to these types. They can be used with any other type, as long as the command represented by the operator exists in the type.

For example, suppose we create a new type complex_number with commands add and multiply. If c1 and c2 are variables of type complex_number, then

c1 = c1.add(c1.multiply(c2))

can more simply be written as

c1 = c1 + c1 * c2

Expression operator precedence

The following table lists the precedences of Obix's expression operators. A higher priority means prioritized evaluation. For example, a multiplication is evaluated before an addition.

Table 9.2. Expression operators precedence

PriorityOperatorsDescription
1or xorboolean or and exclusive or
2andboolean and
3=r #r =v #v > >= < <=comparisons
4+ - &arithmetic addition and subtraction, and appending
5* /arithmetic multiplication and division
6not -boolean not and unary minus
7.dot operator (e.g. a.b)
8() iif then elseparenthesized expression and iif then else operator (also called ternary operator)

Syntax

The syntax of an expression is shown below:

Table 9.3. Expression syntax

ProductionSyntaxLinks
expressionand_expression ( ( "or" | "xor" ) and_expression ) *Chapter 9, Expressions and operators
and_expressioncompare_expression ( "and" compare_expression ) * 
compare_expressionadd_expression comparison_operator add_expression 
comparison_operator"=r" | "=v" | "#r" | "#v" | ">" | ">=" | "<" | "<=" 
add_expressionmultiply_expression ( ( "+" | "-" | "&" ) multiply_expression ) * 
multiply_expressionsign_expression ( ( "*" | "/" ) sign_expression ) * 
sign_expression( "not" | "-" | "+" ) ? expression_path 
expression_pathexpression_value ( "." attribute_id_or_command_output ) * 
expression_value

-> literal
-> object_reference
-> script_output
-> "(" expression ")"
-> "iif" expression "then" expression "else" expression "end" "if" ?
-> "this"

 
attribute_id_or_command_output 
script_outputRSE_script_selector ( "." output_argument_id ) ? input_assignment_list ? 
input_assignment_list"(" input_assignment ( ";" ? input_assignment ) * ")" 
input_assignment ( input_argument_id "=" ) ? expression

remark: input_argument_id = can only be omitted if the called script has exactly one input argument

 

Examples

Example 9.1. Expression examples

service expression_examples

   command examples
      script
         // arithmetic addition
         var positive32 b = 3.add(4)  // without operator
         var positive32 a = 3 + 4     // with '+' operator
         check script a =v 7          // check result
         check script a =v b

         // operator precedence
         check script 1 + 2 * 3 =v 7   // multiplication before additon
         check script (1 + 2) * 3 =v 9 // parenthesis used to change precedence
         check script 1 + 2 * 3   =v 1.add(2.multiply(3))
         check script (1 + 2) * 3 =v 1.add(2).multiply(3)
         
         // boolean operators
         const yes_no the_sun_shines = yes
         const yes_no it_rains = no
         const yes_no weather_is_fine = the_sun_shines and not it_rains
         check script weather_is_fine
         
         // comparisons

         check script 7 =v 7 and 7 > 6 and 7 >= 7 and 7 < 8 and 7 <= 7

         const positive32 seven1 = 7
         const positive32 seven2 = 7
         check script seven1 =v seven2  // values are equal
         check script seven1 #r seven2  // but references are not
         
         // check if "foo_bar" is a valid prefixed identifier
         const attribute_check_error prefixed_identifier_error = ty_prefixed_id.a_value.check ( "foo_bar".a_value )
         const yes_no valid = prefixed_identifier_error =r void
         check script valid =v yes
         
         // check if slash is used as file path separator
         const yes_no slash_used = se_file_system.a_file_path_separator =v "/"  // check value of service attribute
         const string message = iif slash_used then "slash is used" else "slash is NOT used" end if
         if slash_used then
            check script message =v "slash is used"
         else
            check script message =v "slash is NOT used"
         end if
         
         // create product object
         const product my_book = fa_product_factory.co_create ( &
            identifier = 200 &
            name = "Think positiv!" &
            price_in_cents = 1050 )
		    
         // check attributes
         check script my_book.a_name =v "Think positiv!"
         check script my_book.a_name.a_item_count =v 14

         // display number of characters in XML string with tag "name" replaced with "description"
         const zero_positive32 XML_character_count = my_book.co_convert_to_XML.co_replace_all ( &
            to_replace = "name" &
            replace_by = "description" ).a_item_count
         console.message ( """XML of {{my_book.to_string}} contains {{XML_character_count.to_string}} characters""" )

      end script
   end command

end service

See also

Instructions that use expressions: