Expressions may be nested within matched pairs of parenthesis; which can be (), [] or {} characters. The only String operator is concatenation for which an exclamation point (!) is used. Real arithmetic operations only use the signed operators.
Integer variables in Gilda are not declared with a signed or unsigned
attribute. The only operations where the sign of the operand matters are
multiplication and division. Operators (_* and _/) are used to distinguish
between signed and unsigned operands.
Real operators limited to unary plus and minus and binary:
You do not need to track whether an integer variable is signed or unsigned and no explicit casting is needed between the two.
Operator Description Precedence ~ E Signed unary complement 1 + E - E Signed unary plus and minus 1 E ^ E Exponentiation; signed real or unsigned integer 2 E * E E / E Signed Multiplication and division 3 E % E Unsigned integer or real IEEE remainder 3 E _* E E _/ E Unsigned integer Multiplication and division 3 E \\ E E // E Logical shift left and right 3 E << E E >> E Circular shift left and right 3 E + E E - E Signed addition and subtraction 4 E /\ E Logical And 5 E \/ E E -- E Logical Or and Exclusive Or 6
Gilda expressions differs from other languages you are probably familiar with where the type of an operation is determined by its operands. Instead, the type of an operator in Gilda determines the type of the operand. In turn the operator's type is determined by its context.
This difference is best described with a trivial assignment, S = A + B. With bottom up type assignment the type of the addition is determined by the types of A and B and the sum is assigned to S. The types of the variables can all be different; which is ambiguous with bottom up assignment. To disambiguate you need to memorize a set of rules for every type combination.
To sort this out you can either figure out the rules for implicit type casting or you can code using explicit type cast functions. With implicit casts people reading the code not only have to know the rules, but also have to reference the variable declarations to understand what the code is doing. The rules differ for different languages and they can also differ between compilers for the same language. What could possibly go wrong?
To simplify things Gilda uses top down type casting. In the assignment example, the type of the addition is the type of the result, S. Then if A or B have different types they are implicitly cast to the type of the operator; which is the type of S. There is no set of type casting rules needed to read or compensate for when writing code.
Conditions and Print command arguments use untyped expressions. Their type is determined by the types of variables they reference or type of functions they use. Untyped expressions with mixed type operands are not allowed and in this case type casting functions are required.
An untyped expression containing only constants is evaluated as a constant expression. Constant expressions are also used when declaring an initial value for a variable. Quoted text is evaluated as a String type and Integer constants are treated as Cells. Real numbers are not allowed in constant expressions.
Some conversions require explicit use of a type cast function. Implicit type casting is automatic and does not require the use of a function. Converstion that are not listed in the table are not permitted.
Implicit Excplicit Integer <=> Integer single <=> double Integer => string string => Integer Integer <=> Enumeration
Note that unsigned multiplication and integer exponentiation truncate the upper portion. Overflow on signed multiplication is undefined and should be avoided. You can check for overflow by using one of these methods:
Min is the smallest negative value and Max is the largest positive value. Safe signed multiplication of N by a constant K: K = 0 | 1 ==> all pass K = -1 ==> assert N ~= Min else ==> assert N = {Min / K} to {Max / K} Safe signed multiplication of N by a variable V: For smaller types, cast to a bigger type: Nw = word{ N }; Sign extend Parcel N to a Word Vw = word{ V }; Sign extend Parcel V to a Word Pw = Nw * Nv; Signed product as a Word. P = parcel{ Pw }; A signed Parcel product : Assert that the sign of a Parcel product : and the upper Parcel bits all match. : assert -parcel{ word{ Pw // 16 }} &ERROR: Signed product overflow. = (P // 15 ) /\ 1 fault For Cell types this will work, but involves a division. You might be better off using the previous method with a 128 bit product. Un = abs( N ) Uv = abs( V ) Up = Un _* Uv; Unsigned product of absolute values S = -{(N -- V) // 63}; 0 if positive result or -1 if negative P = (Up -- S) - S; A signed 64 bit product IF Uv: IF a dividend, assert Un = Up _/ Uv fault; Unsigned product overflowed. assert Up _<= Max - S fault; Unsigned product is out of range. .
The exponentiation operator (^) is unsigned for integer arguments and signed for real arguments. Note that a signed real base may not have a fractional exponent. In this case the result would be complex so a fault exception is raised instead. Also, zero to the zero power is one.
Unsigned integer exponentiation is computed in log2 time using the following algorithm:
Power = if Exponent /\ 1, Base, 1; Initial value. IF Exponent // 1: IF Exponent is 2 or more, DO always: DO over each bit in the Exponent, Exponent //= 1; Shift out the low bit. Base _*= Base; Square the base. IF Exponent /\ 1: IF the low bit is set, Power _*= Base; Scale Power by the Base. UNDO IF Exponent = 1; UNDO IF no more Exponent bits. . - .