Declarations and Conversions
HyperScript uses variables for storing values. All variables have an identifier (name) , and a data type.
Data type specifies the internal storage format of the data. Variables can be declared with any of the available data types, and variables can easily be converted from one data type to another. Undeclared variables are created automatically with the list data type when they are initially assigned a value.
Vectors can be used to store indexed collections of the same data type, while lists and structures can be aggregates of arbitrary data types.
All variables may use subscript [ ] brackets to reference individual element values. Subscript indices start at zero.
Every name (identifier) in HyperScript has one of the following fundamental data types associated with it:
boolean str char uchar binary hex octal byte ubyte short ushort long ulong float double int handle
This data type determines what operations can be applied to the name, and how such operations are interpreted.
Declarations
A declaration is used to associate a data type with a name. For example:
char ch;
str s;
int count = 1;
float pi = 3.141592;
str name = "Hyper";
str seasons = { "winter", "spring", "summer", "fall" };
list Date = { int d, int m, int y };
int day ( str rDate ) { day = rDate->d; };
list WAFER = {}; /* Initialized to empty */
Some of these examples show that a declaration can also be a definition. For Date, it is a structure definition, and for day, it is a function definition.
A name can be undeclared (removed) using the undef statement. For example:
undef pi; /* pi is now undefined */
The variable, pi, must be declared or initialized to be used again.
Initialization
Some of these declarations assign values. For example, pi is assigned the value 3.141592. If no value was assigned, then pi would have been initialized to zero. All declared variables except those of type char, str, and list are initialized to zero if no value is assigned.
Variables can be initialized to empty by assigning to '{}'. In the example above, WAFER is initialized to empty.
Conversions
Conversions are performed implicitly when mixing types in assignments and expressions. There are rules for how types are implicitly converted when mixing with other types. When you want to be sure about how the types are converted, use explicit type conversion.
In addition, variables can be converted from one data type to another.
Explicit Conversion
Explicit type conversion, or typecasting, can be specified a number of ways. For example, the following equivalent statements all convert the variable x to type int:
i = (int) x; i = int x; i = int (x); i = (int) (x);
If x is a list or vector, then the conversion operation will typecast each element of x to type int.
In most cases, HyperScript will correctly perform the appropriate type conversions implicitly, but explicit conversion is occasionally necessary. For example:
put ( "1.2" + 3 );
produces "1.23" because the implicit conversion rules (see below) promote 3 to type str.
Thus, the following:
float f = "1.2" + 3;
assigns to f the value 1.23, which might not be what was intended. To be sure, use explicit conversion as follows:
float f = float ( "1.2" ) + 3;
Implicit Conversion
Integral and floating-point types can be mixed freely in assignments and expressions because type conversions are performed implicitly with the intent to minimize information loss. However, not all conversions will preserve values and information can be lost if you are not careful.
This section explains the conversion rules and what to watch out for.
Promotions
Implicit type conversions that preserve values are called promotions. In a binary expression where the operands are not the same data type, one of the arguments must be converted to match the data type of the other operand. Rules are applied in the following order when dealing with mixed operands:
- If one of the operands is a string, the other operand is converted to a string.
- If one of the operands is a float, the other operand is converted to a float.
- If one of the operands is an integer, the other operand is converted to an integer.
Integral Conversions
An integer can be converted to another integer type. A conversion is simply a copy of as many bits from the source as will fit into the destination. High-order bits are thrown away if necessary. For example:
ubyte ub = 1023; /* binary 0x000003ff becomes binary 0xff, or 255 */
The boolean type also can be converted to its integer equivalent.
Floating-Point Conversions
A floating-point value can be converted into another floating-point type. If the source value can be exactly represented in the destination type, the result is the original floating-point value. If the source value is between two adjacent destination values, the result is one of those values. Otherwise, the conversion produces inf if the source value is greater than the maximum positive number, -inf if the source value is less than the minimum negative number, and zero if the absolute value of the source is less than the smallest possible fractional decimal. For example:
float f = 4.028234e+38; /* largest float value */ double d = f; /* OK, d == f */ float f2 = d; /* OK, f2 == d */ double d3 = 1.7976931348623157e+308; /* largest double value */ float f3 = d3; /* inf */ double d4 = 4.940656458412465E-324; /* smallest double fraction */ float f4 = d4; /* zero */
Floating-Integral Conversions
When converting a floating-point value to an integer the fractional part is truncated. If the truncated value cannot be represented in the destination type, the high order bits are discarded. For example:
int i = 2.6; /* i becomes 2 */ byte b = 300.7; /* b becomes 44 */
The last example makes sense when we examine what conversions take place. First, 300.7 is truncated to 300. Since 300 is too big to fit into a byte, the high order bits are discarded, and we end up with the value, 44.
Conversions from integer to floating-point are performed as the hardware allows. For example:
int i = float ( 1234567890 );
assigns i the value, 1234567936.
String-Numeric Conversions
Strings and numbers (integers or floating-point) can be used freely together in assignments and in certain expressions. For example:
int i = "10"; /* i becomes 10 */ str s = 10; /* s becomes "10" */ float f = "1.2"; /* f becomes 1.2 */
An expression involving addition of strings and numbers converts the numbers to strings and performs string concatenation. For example:
str s = 10 + "1.2"; /* s becomes "101.2" */ float f = 10 + "1.2"; /* f becomes 101.2 */
Note that string subtraction is valid, as in:
float f = 101.2 - "1.2"; /* f becomes 10 */
Strings and numbers cannot be mixed in expressions where string operations are undefined. For example:
float f = "1.2" * 2; /* UNDEFINED: string multiplication */
Data Type Conversions
Variable data types can easily be converted from one type to another, following the conversion rules explained above. For example:
int i = { 1, 2, 3, 4, 5 };
float i; /* i becomes { 1., 2., 3., 4., 5. } */
str i; /* i becomes { "1", "2", "3", "4", "5" } */
Converting between the str data type and the char and uchar data types provides the means for individual character string manipulation.
str a = "abc"; /* Start with a string of three characters */ char a; /* Convert to a vector of characters */ a = a + 23; /* Add 23 to each of the values */ str a; /* Convert back to a str. The value of 'a' is now "xyz" */
The conversion of "abc" to "xyz" can also be done more compactly:
a = str ( char ( char ("abc" ) + 23 ) );