TOC PREV NEXT

2  Simple Arithmetic Expressions

2.1  Constants and Literals

The simplest expression is a constant, which can be given either by the symbolic name of the constant, or by a literal. By default, the symbolic names of constants supported are PI, pi, E, e, true, false, i, j, NaN, Infinity, PositiveInfinity, NegativeInfinity, MaxUnsignedByte, MinUnsignedByte, MaxShort, MinShort, MaxInt, MinInt, MaxLong, MinLong, MaxFloat, MinFloat, MaxDouble, MinDouble. For example,
PI/2.0

is a valid expression that refers to the symbolic name "PI" and the literal "2.0." The constants i and j are the imaginary number with value equal to the square root of -1. The constant NaN is "not a number," which for example is the result of dividing 0.0/0.0. The constant Infinity is the result of dividing 1.0/0.0. The constants that start with "Max" and "Min" are the maximum and minimum values for their corresponding types.
Numerical values without decimal points, such as "10" or "-3" are integers (type int). Numerical values with decimal points, such as "10.0" or "3.14159" are of type double. Numerical values followed by "f" or "F" are of type float. Numerical values without decimal points followed by the character "l" (el) or "L" are of type long. long. Numerical values without decimal points followed by the character "s" or "S" are of type short. Unsigned integers followed by "ub" or "UB" are of type , unsignedByte, as in "5ub". An unsignedByte has a value between 0 and 255; note that it not quite the same as the Java byte, which has a value between -128 and 127. Numbers of type int, long, short or unsignedByte can be specified in decimal, octal, or hexadecimal. Numbers beginning with a leading "0" are octal numbers. Numbers beginning with a leading "0x" are hexadecimal numbers. For example, "012" and "0xA" are both equal to the integer 10.
A complex is defined by appending an "i" or a "j" to a double for the imaginary part. This gives a purely imaginary complex number which can then leverage the polymorphic operations in the Token classes to create a general complex number. Thus 2 + 3i will result in the expected complex number. You can optionally write this 2 + 3*i.
Literal string constants are also supported. Anything between double quotes, "...", is interpreted as a string constant. The following built-in string-valued constants are defined:
Table 1: String-valued constants defined in the expression language
Variable name Meaning Property name Example under Windows
PTII The directory in which Ptolemy II is installed ptolemy.ptII.dir c:\tmp
HOME The user home directory user.home c:\Documents and Settings\\you
CWD The current working directory user.dir c:\ptII
TMPDIR The temporary directory java.io.tmpdir c:\Documents and Settings\\you\Local  Settings\Temp\
The value of these variables is the value of the Java virtual machine property, such as user.home. The properties user.dir and user.home are standard in Java. Their values are platform dependent; see the documentation for the java.lang.System.getProperties() method for details. Note that user.dir and user.home are usually not readable in unsigned applets, in which case, attempts to use these variables in an expression will result in an exception. Vergil will display all the Java properties if you invoke JVM Properties in the View menu of a Graph Editor.
The ptolemy.ptII.dir property is set automatically when Vergil or any other Ptolemy II executable is started up. You can also set it when you start a Ptolemy II process using the java command by a syntax like the following:
java -Dptolemy.ptII.dir=${PTII} classname

where classname is the full class name of a Java application. The constants() utility function returns a record with all the globally defined constants. If you open the expression evaluator and invoke this function, you will see that its value is something like:
CLASSPATH = "xxxxxxCLASSPATHxxxxxx", CWD = "/Users/Ptolemy/ptII",
E = 2.718281828459, HOME = "/Users/cxh", Infinity = Infinity,
MaxDouble = 1.7976931348623E308, MaxFloat = 3.4028234663853E38,
MaxInt = 2147483647, MaxLong = 9223372036854775807L,
MaxShort = 32767s, MaxUnsignedByte = 255ub, MinDouble = 4.9E-324,
MinFloat = 1.4012984643248E-45, MinInt = -2147483648,
MinLong = -9223372036854775808L, MinShort = -32768s,
MinUnsignedByte = 0ub, NaN = NaN, NegativeInfinity = -Infinity,
PI = 3.1415926535898, PTII = "/Users/Ptolemy/ptII",
PositiveInfinity = Infinity,
TMPDIR = "/var/folders/7f/7f-o2nyjFgewH67h0keKu++++TI/-Tmp-/",
boolean = false, complex = 0.0 + 0.0i, double = 0.0,
e = 2.718281828459, false = false, fixedpoint = fix(0,2,2),
float = 0.0f, general = present, i = 0.0 + 1.0i, int = 0,
j = 0.0 + 1.0i, long = 0L, matrix = [], nil = nil,
null = object(null), object = object(null), pi = 3.1415926535898,
scalar = present, short = 0s, string = "", true = true,
unknown = present, unsignedByte = 0ub, xmltoken = null

2.2  Variables

Expressions can contain identifiers that are references to variables within the scope of the expression. For example,
PI*x/2.0

is valid if "x" a variable in scope. In the expression evaluator, the variables that are in scope include the built-in constants plus any assignments that have been previously made. For example,
>> x = pi/2
1.5707963267949
>> sin(x)
1.0

In the context of Ptolemy II models, the variables in scope include all parameters defined at the same level of the hierarchy or higher. So for example, if an actor has a parameter named "x" with value 1.0, then another parameter of the same actor can have an expression with value "PI*x/2.0", which will evaluate to π/2.
Consider a parameter P in actor X which is in turn contained by composite actor Y. The scope of an expression for P includes all the parameters contained by X and Y, plus those of the container of Y, its container, etc. That is, the scope includes any parameters defined above in the hierarchy.
You can add parameters to actors (composite or not) by right clicking on the actor, selecting "Configure" and then clicking on "Add", or by dragging in a parameter from the utilities library. Thus, you can add variables to any scope, a capability that serves the same role as the "let" construct in many functional programming languages.
Occasionally, it is desirable to access parameters that are not in scope. The expression language supports a limited syntax that permits access to certain variables out of scope. In particular, if in place of a variable name x in an expression you write A::x, then instead of looking for x in scope, the interpreter looks for a container named A in the scope and a parameter named x in A. This allows reaching down one level in the hierarchy from either the current container or any of its containers.

2.3  Operators

The arithmetic operators are +, -, *, /, ^, and %. Most of these operators operate on most data types, including arrays, records, and matrices. The ôperator computes "to the power of" or exponentiation where the exponent can only be an int, short, or an unsignedByte.
The unsignedByte, short, int and long types can only represent integer numbers. Operations on these types are integer operations, which can sometimes lead to unexpected results. For instance, 1/2 yields 0 if 1 and 2 are integers, whereas 1.0/2.0 yields 0.5. The exponentiation operator "^" when used with negative exponents can similarly yield unexpected results. For example, 2^-1 is 0 because the result is computed as 1/(2^1).
The % operation is a modulo or remainder operation. The result is the remainder after division. The sign of the result is the same as that of the dividend (the left argument). For example,
>> 3.0 % 2.0
1.0
>> -3.0 % 2.0
-1.0
>> -3.0 % -2.0
-1.0
>> 3.0 % -2.0
1.0

The magnitude of the result is always less than the magnitude of the divisor (the right argument). Note that when this operator is used on doubles, the result is not the same as that produced by the remainder() function (see Table 10). For instance,
>> remainder(-3.0, 2.0)
1.0

The remainder() function calculates the IEEE 754 standard remainder operation. It uses a rounding division rather than a truncating division, and hence the sign can be positive or negative, depending on complicated rules (see 8.0.4). For example, counter intuitively,
>> remainder(3.0, 2.0)
-1.0

When an operator involves two distinct types, the expression language has to make a decision about which type to use to implement the operation. If one of the two types can be converted without loss into the other, then it will be. For instance, int can be converted losslessly to double, so 1.0/2 will result in 2 being first converted to 2.0, so the result will be 0.5. Among the scalar types, unsignedByte can be converted to anything else, short can be converted to int, int can be converted to double, float can be converted to double and double can be converted to complex. Note that long cannot be converted to double without loss, nor vice versa, so an expression like 2.0/2L yields the following error message:
Error evaluating expression "2.0/2L"
in .Expression.evaluator
Because:
divide method not supported between ptolemy.data.DoubleToken '2.0'
and ptolemy.data.LongToken '2L' because the types are incomparable.

Just as long cannot be cast to double, int cannot be cast to float and vice versa.
All scalar types have limited precision and magnitude. As a result of this, arithmetic operations are subject to underflow and overflow.
The bitwise operators are &, |, # and ~. They operate on boolean, unsignedByte, short, int and long (but not fixedpoint, float, double orcomplex). The operator & is bitwise AND, ~ is bitwise NOT, and | is bitwise OR, and # is bitwise XXOR ( exclusive or, after MATLAB).
The relational operators are <, <=, >, >=, == and !=. They return type boolean. Note that these relational operators check the values when possible, irrespective of type. So, for example,
1 == 1.0

returns true. If you wish to check for equality of both type and value, use the equals() method, as in
>> 1.equals(1.0)
false

Boolean-valued expressions can be used to give conditional values. The syntax for this is
boolean ? value1 : value2

If the boolean is true, the value of the expression is value1; otherwise, it is value2. The logical boolean operators are &&, ||, !, & and |. They operate on type boolean and return type boolean. The difference between logical && and and logical & is that & evaluates all the operands regardless of whether their value is now irrelevant. Similarly for logical || and |. This approach is borrowed from Java. Thus, for example, the expression false && x will evaluate to false irrespective of whether x is defined. On the other hand, false & x will throw an exception.
The << and >> operators performs arithmetic left and right shifts respectively. The >>> operator performs a logical right shift, which does not preserve the sign. They operate on unsignedByte, short, int, and long.

2.4  Comments

In expressions, anything inside /*...*/ is ignored, so you can insert comments.
TOC PREV NEXT