Expressions
Operators and how tightly they bind, arithmetic with no hidden conversions, comparisons, the conditional, assignment, casts, calls and members, literals, and C++ written inside an expression.
Comparisons of records, arrays, sets and pointers are on Types; is, as and declared operators on Objects.
module exe expressions_power;
var
LT: bool = true;
LF: bool = false;
LA: int32 = 6;
LU: uint32 = 4;
LC: char = 'a';
LNeg: float64 = -3.75;
LN8: int8 = 126;
begin
println("shl before + {}", 1 + 2 shl 1);
println("* before shl {}", 2 * 3 shl 1);
println("& before = {}", LA & 2 = 2);
println("not before = {}", not LT = LF);
LC++;
println("char step {}", LC);
println("truncates {}", int32(LNeg));
println("mixed sign {}", uint32(LA) + LU);
println("test a bit {}", (LA & 1) <> 0);
LN8 += 1;
println("compound {}", LN8);
end.
shl before + 5
* before shl 12
& before = true
not before = true
char step b
truncates -3
mixed sign 10
test a bit false
compound 127
[Heap] Allocs: 0, Frees: 0, Leaked: 0
Binding power
From loosest to tightest:
| Level | Operators |
|---|---|
| assignment (right associative) | := and the compound forms |
| conditional (right associative) | ? : |
| or | or || |
| and | and && |
| comparison | = <> < > <= >= <=> in is |
| adding | + - | xor |
| shift | shl shr << >> |
| multiplying | * / div mod % & as |
| prefix | the prefix operators and the C-style cast |
| call | ( ) |
| postfix | index, member, ++ -- |
| deref | the postfix ^ |
|and&sit with+and*(Pascal placement), not below the comparisons as in C++:LA | LB = 3is(LA | LB) = 3, andLA & 2 = 2is(LA & 2) = 2.- The shifts bind tighter than
+and looser than*:1 + 2 shl 1is 5 and2 * 3 shl 1is 12. notbinds tighter than a comparison:not LT = LFis(not LT) = LF.- Comparisons do not chain:
LN < LM < 3is PR0038. - One spelling per operator: C++'s
==and!=are PR0039, naming PaskalCPP's=and<>, and the C++ word operators (bitand,compland the others) are PR0040 with the PaskalCPP form. =is always equality: an expression statement whose operator is=(LN = 5;) is PR0048, since it compares and assigns nothing.^after an operand is the deref, never C++'s xor; bitwise xor is the wordxor.
Two levels sit where C++ does not put them: | and & bind tighter than a comparison, and the shifts bind tighter than + and -. When in doubt, parenthesize.
Arithmetic
+ - * work on numbers; / is always real division (7 / 2 is 3.5); div and mod are integer division and remainder, and % is the same as mod. div truncates toward zero, so -7 div 2 is -3 and -7 mod 2 is -1.
A result has the type C++ gives it (the usual arithmetic conversions): int8 + int8 is int32, / of two integers float64, - or ~ of an int8 int32, a shift the promoted left operand's type, ++ / -- the operand's own type.
No hidden conversions
Each operand must convert to the result's type with no hidden conversion: LA + LU (int32 with uint32) is SM0200 ('int32' does not convert implicitly to 'uint32'); write the cast on one side, uint32(LA) + LU.
/converts both integer operands to float64 under the same rule, so an int64 or uint64 operand needsfloat64(...).- The result then converts where it goes:
LN := LA + LBwith int8 operands and an int8 target is SM0200;int8(LN + 5)stores it. bool,charandwcharare no arithmetic operands:LC + 1is SM0250;int32(LC) + 1is the number. Comparisons,inand++/--of a char stay legal:LC++steps 'a' to 'b'.- A
string/wstringtakes+(concatenation), comparisons with the string family of its width, andin; any other operator is SM0250. - An opaque C/C++ operand keeps the result opaque, judged by clang.
Constant folding
A constant expression is folded and judged when the program is compiled: an overflow is SM0256 (constant overflows 'int32': 2147483647 + 1), a zero divisor SM0257 (5 div 0), the minimum div -1 SM0258, and a constant shift count outside 0 .. bits - 1 SM0259 (shift count 32 outside 0..31).
Bitwise and logical
shl shr << >> | & xor ~ take integers: 1 shl 4 is 16, 6 & 3 is 2, 5 xor 3 is 6, ~LN of 5 is -6. A float operand is SM0250, and so is & on two bools.
xor also takes two bools: then it is logical and its result a bool (LT xor LF is true, LT xor LT false).
The result of a bitwise operator on integers is a number: LB := LN & 1 is SM0200 ('int32' does not convert implicitly to 'bool'). A bool from it is a comparison: (LA & 1) <> 0.
and or not (and && || !) take bool and give bool; and / or evaluate the right side only when it decides the value. An int32 operand is SM0250.
Comparisons
= <> < > <= >= give bool; <=> gives C++'s ordering, compared with 0: (LA <=> LB) < 0. Integers, floats, chars and strings compare.
- Two numbers compared meet at one type, each converting to it:
LA < LU(int32 with uint32) is SM0200, since C++ compares that pair unsigned. x in Stests membership of a set variable or of a set literal written in place:3 in [1, 2, 3],LN in [2..6].
The conditional
C ? A : B is A or B and evaluates only that branch; it is right associative (C1 ? A : C2 ? B : C). C is a bool: LN ? 1 : 2 is SM0263.
Assignment
Assignment is an expression: := and the compound forms += -= *= /= %= &= |= <<= >>=, right associative, so LA := LB := 4 sets both.
- A compound assignment's value converts to the target's type:
LN8 += 1is fine,LN += LA(int32 into int8) is SM0200;/=on an integer target is SM0250 (writediv). - A constant cannot be assigned (SM0255).
Conversions and casts
No hidden conversions: a value converts implicitly only when every value it can hold is exactly representable in the target (integer widening, float32 to float64, a constant that fits). An int32 variable into an int8 is SM0200, an int32 argument for an int8 parameter SM0404. An opaque C/C++ value is accepted; clang judges it.
- A type cast
T(x)(a builtin type or an alias of one) converts as C++ does: a float truncates toward zero (int32(3.75)is 3,int32(-3.75)is -3) and a narrower integer wraps (int16(70000)is 4464). - A cast takes one value:
int32(1, 2)is PR0041. A type named as a value (LN := TSmall;) is SM0269. - A C-style cast
(T)xis a cast when the parenthesized text is plainly a type ((int)LF,(int32_t)-1);(a name)is a cast only when an operand follows, so(LN) - 1is a subtraction.
Prefix and postfix operators
Prefix not - + address of and the C++ forms ! ~ * & ++ --; postfix ^ ++ --. Prefix operators chain: not not LB, **LPP, LPP^^.
address of (and &) takes a variable, a field, an element or a deref; a value that is not stored has none: address of Five() is SM0254.
Calls and members
A call ( ), an index [ ] and a member . / -> chain on any value: a call result (Make().Y), a nested record (LQ.P.X), a record through a pointer (LQP->P.X), C++ members (LV.size()). After . and -> any keyword is a name, so a C++ member named type or end is reached as written.
Only a routine or a routine value is called: LM(1) with an int32 LM is SM0400. A member a record lacks is SM0009 ('TQ' has no member 'Z').
Primaries
true, false, nil, integer, float, string and char literals, ( ), self (the object a method runs on) and parent.<member>.
Set literals
[e, a..b, ...] holds elements and ranges of one ordinal family (integers of any width are one); [] is the empty set. A float element is SM0266, an int32 and a char in one literal SM0266, a range whose low bound exceeds its high bound SM0267.
Record literals
T(F: v, G: w) builds a record value: the fields it names, in declaration order, each once; a field it leaves out starts at zero. Literals nest and pass as arguments. A field the record lacks is SM0414, a field given twice SM0415, fields out of order SM0416 ('X' comes before 'Y' in 'TP').
C++ in expressions
C++ is written in an expression as in C++: qualified names with template arguments (std::max<int>(3, 9)), new / delete, nullptr, this, sizeof, braced init ({1, 2}) and lambdas.
- Template arguments after a qualified name are taken unless an operand follows the
>(std::numbers::pi_v<float64> > 3.0); the one token<>is an empty list only before({::>,);](std::less<>()); an unqualifiedname<...>is a comparison. [ ... ]is a lambda when(or{follows its], else a set literal.- A group that never closes is PR0063 at its opener (
'(' is never closed). A C++ mistake is clang's, reported at its .pcpp line: see Diagnostics.