**************************************************************************
** title:      Expressions
** author:     Brian Frank
** created:    9 May 07
** copyright:  Copyright (c) 2007, Brian Frank and Andy Frank
** license:    Licensed under the Academic Free License version 3.0
**************************************************************************

Operator Precedence [#precedence]
*********************************
Fantom's expression syntax is very similar to C, Java, C# and company.
Operators in order of precedence:

  - Primary: '(x)   x.y   x.y()   x->y()   x?.y   x?.y()   x?->y()   x[y]'
  - Unary: '++x   --x   x++   x--  !x   +x   -x   (T)x   &x'
  - Multiplicative: '*   /   %'
  - Additive: '+   -'
  - Range: '..   ..<'
  - Relational: '<   <=   >=   >   <=>   is   isnot   as'
  - Equality: '==   !=   ===   !=='
  - Conditional And: '&&'
  - Conditional Or: '||'
  - If Expr: 'x?t:f   x?:y'
  - Assignment: '=   *=   /=   %=   +=   -='
  - Collection Add: ','

Shortcut Operators [#shortcuts]
*******************************
Fantom is a pure OO language in that everything is an object you can call
methods on - even value-types such as 'Bool' and 'Int'.  As such almost
all the operators are really just method calls.  We call these operators
the *shortcut operators* because they are just syntax sugar for calling
a specific method:

   a + b     =>  a.plus(b)
   a - b     =>  a.minus(b)
   a * b     =>  a.mult(b)
   a / b     =>  a.div(b)
   a % b     =>  a.mod(b)
   a[b]      =>  a.get(b)
   a[b] = c  =>  a.set(b, c)
   -a        =>  a.negate()
   ++a, a++  =>  a = a.increment()
   --a, a--  =>  a = a.decrement()
   a == b    =>  a.equals(b)
   a != b    =>  !a.equals(b)
   a <=> b   =>  a.compare(b)
   a > b     =>  a.compare(b) > 0
   a >= b    =>  a.compare(b) >= 0
   a < b     =>  a.compare(b) < 0
   a <= b    =>  a.compare(b) <= 0
   a,b,c     =>  it.add(a).add(b).add(c)

For example say we have two variables 'a' and 'b' both of type 'Int'.
Then the expression 'a+b' is really just syntax sugar for calling
`sys::Int.plus` as 'a.plus(b)'.  See [Method Operators]`Methods#operators`
for a detailed discussion.

Prefix and Postfix Operators [#prefixPostfix]
=============================================
The '++' and '--' operators can be prefix or postfix just like C
family languages.  Both of these operators assign the result of
the call to 'increment' or 'decrement' to the operand variable.
If prefix then the expression evaluates to the assignment.  If
postfix then the expression evaluates to the value of the operand
before 'increment' or 'decrement' is assigned.

Equality Operators [#equality]
==============================
The equality operators '==' and '!=' both make use of the `sys::Obj.equals`
virtual method.  Most types override this method to compare value equality.
If 'equals' is not overridden, then the default behavior is to compare
reference equality.

Relational Operators [#relational]
==================================
The relational operators like '<' and '>' all use the `sys::Obj.compare`
virtual method.  Many types with the notation of ordering will override
this method to return -1, 0, or 1.  If 'compare' is not overridden, then
the default implementation will compare the result of the operands 'toStr'
method.

The compiler translates the numeric return into a boolean condition based
on which operator was used.  The special '<=>' operator returns the 'Int'
value of -1, 0, 1 directly.  You will commonly use the '<=>' operator
for custom sorts with a closure:

  people.sort |Person a, Person b->Int| { return a.age <=> b.age }

If that code doesn't make any sense to you, then don't worry - just keep
reading until we cover [closures]`Closures`.

Comparisons with Null [#comparisonsWithNull]
============================================
The equality and relational operators have special handling if either
operand is 'null' such that a 'NullErr' exception is never raised.
For equality a non-null and null are never equal, but two nulls are
always equal.  For relational operators, null is always less than a non-null
object.  Special handling for null does *not* apply if the 'equals'
or 'compare' method is used as a normal method call.  Nor does this
special handling apply for other shortcut operators.

Same Operators [#same]
**********************
The '===' and '!==' operators are called the *same* and *not same*
operators.  These operators are used to check if two variables
reference the same object instance in memory.  Unlike the '==' and '!='
shortcut operators, the same and not same operators do not result
in the 'equals' method call.  These operators are not allowed to be
used against value-types.

Conditional Operators [#cond]
*****************************
The conditional '!', '&&', and '||' operators are used with boolean
expressions.  Use '&&' to perform a logical 'and' and '||' to
perform a logical 'or'.  Both of these operators are short circuiting
in that the second test is skipped if the first test is conclusive ('false'
for '&&' and 'true' for '||').  The '!' operator performs a logical 'not'.
Code examples for the conditional operators:

  t := true
  f := false
  t && f  => evaluates to false
  t && t  => evaluates to true
  f || t  => evaluates to true
  !t      => evaluates to false

Ternary Operator [#ternary]
***************************
The ternary operator combines three expressions as a convenient way
to assign a value based on an if/else condition:

  condExpr ? trueExpr : falseExpr

The 'condExpr' must evaluate to a boolean.  If 'condExpr' evaluates
to 'true' then the whole expression evaluates to 'trueExpr', otherwise
to 'falseExpr'.  Examples:

  3 > 4 ? "yes" : "no"  => evaluates to "no"
  6 > 4 ? "yes" : "no"  => evaluates to "yes"

Fantom also supports use of a 'throw' statement as one of the results
of a ternary operation:

  val := isValid(key) ? map[key] : throw ArgErr("invalid key")

Null Convenience Operators [#nullConvenience]
*********************************************
Fantom supports several of the operators found in Groovy to make
working with null more convenient:
  - **Elvis Operator** 'x ?: y'  (look at it sideways as a "smiley" face)
  - **Safe Invoke** 'x?.y'
  - **Safe Dynamic Invoke** 'x?->y'

Elvis Operator [#elvis]
=======================
The elvis operator evaluates the left hand side.  If it is non-null
then it is result of the whole expression.  If it is null, then the
result of the whole expression is the right hand side expression.
The right hand side expression is short circuited if the left hand
side evaluates to non-null.  It is similar to how you might use the
ternary operator:

  // hard way
  file != null ? file : defaultFile

  // easy way
  file ?: defaultFile

The elvis operator may not be used on a non-nullable type since
by definition it will not be null.

Like the [ternary]`#ternary` operator the elvis operator may use
a 'throw' statement as the right hand side of the expression:

  val := map[key] ?: throw ArgErr("key not found")

Safe Invoke [#safeInvoke]
=========================
The safe invoke operators are designed to short circuit if
the target of method call or field access is null.  If short
circuited, then the whole expression evaluates to null.  It
is quite useful to skip checking a bunch of values for null
during a call chain:

  // hard way
  Str? email := null
  if (userList != null)
  {
    user := userList.findUser("bob")
    if (user != null) email = user.email
  }

  // easy way
  email := userList?.findUser("bob")?.email

If at any point in a null-safe call chain we detect null, then
the whole expression is short circuited and the expression evaluates
to null.  You can use '?->' as a null-safe version of the
[dynamic invoke operator]`Methods#dynamicInvoke`.

The safe invoke operator may not be used on a non-nullable type
since by definition it will not be null.  The result of a safe
invoke is always nullable:

  x := str.size   =>  x is typed as Int
  x := str?.size  =>  x is typed as Int?

Type Checking [#typeChecking]
*****************************
The cast operator is used to perform a type conversion.  The cast syntax uses
parenthesis like C languages - such as '(Int)x'.  If a type cast fails
at runtime, then a `sys::CastErr` exception is raised.

The 'is', 'isnot', and 'as' operators are used check an object's type at
runtime:
  - 'is' operator returns a 'Bool' if the operand implements the
    specified type (like Java's 'instanceof' operator).  If target is
    null, then evaluates to false.
  - 'isnot' operator is semantically equivalent to '!(x is Type)'.
    If target is null then evaluates to true.
  - The 'as' operator returns the object cast to the specified type
    or 'null' it not an instance of that type (like C#):

  Obj obj := 123
  obj is Str     =>  evaluates to false
  obj is Num     =>  evaluates to true
  obj isnot Str  =>  evaluates to true
  obj isnot Num  =>  evaluates to false
  obj as Float   =>  evaluates to null
  obj as Int     =>  evaluates to 6 (expr is typed as Int)

Nullability of types is not considered when using the 'is', 'isnot',
and 'as' operators.  For example these two expressions are considered
equivalent:

  obj is Str
  obj is Str?

The 'as' operator by definition returns a nullable type.  For example
the following expression evaluates to 'Str?', not 'Str':

  x := obj as Str  => x is typed as Str?

Indexing [#indexing]
********************
Depending on how it is used, the '[]' operator maps to three different shortcuts:

   a[b]      =>  a.get(b)
   a[b] = c  =>  a.set(b, c)
   a[b]      =>  a.getRange(b) if b is Range

Typically 'a[b]' is a shortcut for calling 'a.get(b)'.  For example the
`sys::List.get` method allows you to lookup a list item by it's 'Int'
index.  Whenever a class supports a 'get' operator method which is
annotated with the '@Operator' facet you can use '[]' as a shortcut.
Consider this code:

  list := ["a", "b", "c"]
  list.get(2)
  list[2]
  list.get("2")  // error
  list["2"]      // error

The expression 'list[2]' is exactly the same code as 'list.get(2)'.  The
last two lines result in a compiler error because we are attempting
to pass a 'Str' when an 'Int' is expected.

When the indexing shortcut is used on the left hand side of an assignment
such as 'a[b] = c' then the index operator maps to 'a.set(b, c)'.  For example
these two lines of code have identical behavior:

  map.set("tmj", "Too Much Joy")
  map["tmj"] = "Too Much Joy"

If the '[]' operator is used with a `sys::Range` index, then we
map to the 'a.getRange(b)' method which performs a slice.  Slicing is
used to create sub-strings and sub-lists.  Some example code which
creates sub-strings:

  s := "abcd"
  s[0..2]  => "abc"
  s[3..3]  => "d"
  s[0..<2] => "ab"

  start := 0; end := 2
  s[start..<end] => "ab"

We use '..' to specify an inclusive end index, and '..<' to specify
an exclusive end index.  Also note how we can use any arbitrary expression
with the range operators to define compact slice expressions.

By convention Fantom APIs which support integer indexing allow the use of negative
integers to index from the end of the list.  For example '-1' can be used to
index the last item of a list (or the last character of a string).  Using
negative indexes works with all three shortcuts:

  list := ["a", "b", "c", "d"]
  list[-2]           =>  evaluates to "c"
  list[-1] = "last"  =>  replaces list[3] with "last"
  list[1..-1]        =>  evaluates to ["b", "c", "last"]

Use of negative indexes applies to most methods on 'List' and 'Str'
which take an index argument.

Bitwise Operators [#bitwise]
****************************
Fantom doesn't have bitwise operators, instead normal method
calls are used:

  ~a      =>   a.not
  a & b   =>   a.and(b)
  a | b   =>   a.or(b)
  a ^ b   =>   a.xor(b)
  a << b  =>   a.shiftl(b)
  a >> b  =>   a.shiftr(b)

Serialization Expressions [#serialization]
******************************************
Fantom supports three expression constructs which are designed to make the
programming language a true superset of the [serialization]`Serialization`
syntax:
  - Simples
  - It-Blocks
  - Collections

Simples [#simples]
==================
[Simples]`Serialization#simple` are special serializable types which serialize
via a string represenation.  Fantom allows the use of a simple
expression:

   <type>(<str>)

   // for example:
   Version("3.2")

   // is syntax sugar for
   Version.fromStr("3.2")

To use this expression, the type must have a static method called 'fromStr' which
takes a 'Str' parameter and returns an instance of itself.  The method may contain
additional parameters if they have default values.  The type does not have to
implement the `sys::Serializable` facet to use this expression (although it does
if you want to serialize it).  Simple expressions are a subset of [construction
calls]`Methods#constructionCalls`.

It-Blocks [#itBlocks]
=========================
[It-blocks]`Closures#itBlocks` enable you write compound expressions - they are
typically used to initialize an instance.  This feature is a clean a superset
of how [complex]`Serialization#syntaxComplex` types are serialized.  An example
it-block expression:

  Address
  {
    street = "123 Happy Lane"
    city   = "Houston"
    state  = "TX"
  }

  // is syntax sugar for (note: can't actually use it as param name)
  Address.make.with |Address it|
  {
    it.street = "123 Happy Lane"
    it.city   = "Houston"
    it.state  = "TX"
  }

Collections [#collections]
==========================
It-blocks may also be used to initialize a collection if 'it' supports a
method called "add".  Any expression inside an it-block suffixed with a
comma is assumed to be a call to 'it.add':

  a,       =>  it.add(a)
  a, b     =>  it.add(a).add(b)
  a, b, c  =>  it.add(a).add(b).add(c)

Note the comma operator chains the calls to 'add', therefore the 'add'
method must be annotated with the '@Operator' facet and return the
chainable type 'This'.

Here is an FWT example:

  Menu
  {
    text = "File"
    MenuItem { text = "Open"; onSelect=#open.func },
    MenuItem { text = "Save"; onSelect=#save.func },
  }

  // is syntax sugar for (note: can't actually use it as param name)
  Menu.make.with |Menu it|
  {
    it.text = "File"
    it.add(MenuItem { text = "Open"; onSelect=#open.func })
     .add(MenuItem { text = "Save"; onSelect=#save.func })
  }

Advanced Operators [#advances]
******************************
Fantom has a couple other operators which will be discussed later:

  - `Closures` are expressions which create a new function inside
    a method body.

  - [Call]`Functions#calling` operator '()' is used to invoke a function variable.

  - [Dynamic invoke]`Methods#dynamicInvoke` operator '->' is used to call
    a method without compile time type checking.

  - [Field storage]`Fields#accessors` operator '&' is used to access a field's
    raw storage without going through its getter/setter methods.