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
endAttribute 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.
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:
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
| Category | Operator | Name | Command | Example |
|---|---|---|---|---|
| arithmetic calculations | + | addition | co_add |
|
- | subtraction | co_subtract |
| |
* | multiplication | co_multiply |
| |
/ | division | co_divide |
| |
- | unary minus | co_unary_minus |
| |
| boolean logic | and | boolean and | co_and |
|
or | boolean or | co_or |
| |
xor | boolean exclusive or | co_xor |
| |
not | boolean not (inversion) | co_not |
| |
| comparisons | =r | equal reference | N/A | foo =r bar |
#r | unequal reference | N/A | foo #r bar | |
=v | equal value | co_is_equal_to |
| |
#v | unequal value | co_is_equal_to and co_not |
| |
> | greater than | co_compare_to and co_is_equal_to |
| |
>= | greater than or equal | co_compare_to and co_is_equal_to and co_not |
| |
< | less than | co_compare_to and co_is_equal_to |
| |
<= | less than or equal | co_compare_to and co_is_equal_to and co_not |
| |
| append | & | append | co_append |
|
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
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
| Priority | Operators | Description |
|---|---|---|
| 1 | or xor | boolean or and exclusive or |
| 2 | and | boolean and |
| 3 | =r #r =v #v > >= < <= | comparisons |
| 4 | + - & | arithmetic addition and subtraction, and appending |
| 5 | * / | arithmetic multiplication and division |
| 6 | not - | boolean not and unary minus |
| 7 | . | dot operator (e.g. a.b) |
| 8 | () iif then else | parenthesized expression and iif then else operator (also called ternary operator) |
The syntax of an expression is shown below:
Table 9.3. Expression syntax
| Production | Syntax | Links |
|---|---|---|
expression | and_expression ( ( "or" | "xor" ) and_expression ) * | Chapter 9, Expressions and operators |
and_expression | compare_expression ( "and" compare_expression ) * | |
compare_expression | add_expression comparison_operator add_expression | |
comparison_operator | "=r" | "=v" | "#r" | "#v" | ">" | ">=" | "<" | "<=" | |
add_expression | multiply_expression ( ( "+" | "-" | "&" ) multiply_expression ) * | |
multiply_expression | sign_expression ( ( "*" | "/" ) sign_expression ) * | |
sign_expression | ( "not" | "-" | "+" ) ? expression_path | |
expression_path | expression_value ( "." attribute_id_or_command_output ) * | |
expression_value |
| |
attribute_id_or_command_output |
| |
script_output | RSE_script_selector ( "." output_argument_id ) ? input_assignment_list ? | |
input_assignment_list | "(" input_assignment ( ";" ? input_assignment ) * ")" | |
input_assignment |
( input_argument_id "=" ) ? expression
remark: |
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 serviceInstructions that use expressions:
assignments
flow control
loops
events
error handling
testing