1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
# Chapter 17: Syntax Sugar
## Pre/Post-increment
Allow `arg++` and `arg--` with the usual meanings; the value is updated and the
expression is the pre-update value. Greedy match is used so `arg---arg` parses
as `(arg--)-arg`.
Also allows `s.fld++` and `ary[idx]++`, and their `--` variants.
Allow pre-increment for identifiers only, for now: `--pc`.
## Operator assignment
Along the same lines as post-increment, we now allow `op=` assignment
with semantics similar to other languages.
`x += y`
## var & val
`var` can be used in the `type` position to indicate a "variable" (mutable
value), whose type will be inferred from the required initalizing expression.
`val` is the same as `var`, except it is a "value" (not mutable).
The inferred value is the `glb` of the peephole type, which means `var` and
`val` will not infer types like `u8` or `f32`, instead inferring `int` and
`flt` respectively. Reference types will always infer as nullable, so e.g.
`var s = new S;`
infers as
`S s? = new S;`
## Mutability
Typed primitive fields are always mutable. Typed reference fields without an
initializer must be mutable to get a value. Initialized reference variables
are immutable by default and can be made mutable with a leading `!`. `var` and
`val` keep their current sense and can be used to make any field mutable or
immutable. Fields are always mutable during construction, but will become
immutable at the end of either constructor.
`int x; x=3; x++; // OK, primitive so mutable`
`int x =3; x++; // Ok, primitive so mutable, despite initializer`
Assume in the next examples this struct exists:
`struct S { int x; };`
Then:
`S s; s = new S ; s.x++; // OK, no initializer so s.x is mutable`
`S s; s = new S{x=3}; s.x++; // OK, no initializer so s.x is mutable`
`S s = new S ; s.x++; // Error, initializer so s.x is immutable`
`S s = new S{x=3}; s.x++; // Error, initializer so s.x is immutable`
Leading `!` makes mutable:
`S !s = new S; s.x++; // Ok, has '!' so mutable`
'var' is variable:
`var s = new S; s.x++; // Ok, has var so mutable`
'val' is a "value": not mutable through this reference, but may be mutable
through other references.
`val s = new S; s.x++; // Error, val so s.x is immutable`
### Reference variables with an initializer are deeply immutable
Initialized reference variables are deeply immutable, but a mutable version of the same
reference can exist. This works for `val` and normal type declarations: `val s
= new S;` and `S s = new S` both make `s` an immutable reference to a `struct S`.
```cpp
struct Bar { int x; }
Bar !bar = new Bar;
bar.x = 3; // Ok, bar is mutable
struct Foo { Bar !bar; int y; }
Foo !foo = new Foo;
foo.bar = bar; // Ok bar is mutable
foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
val xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
print(xfoo.bar.x); // Ok to read through xfoo, prints 4
foo.bar.x++; // Bumps to 5
print(xfoo.bar.x); // Ok to read through xfoo, prints 5
```
## Trinary
Allow `pred ? e_true : e_false`. Also allow `pred ? e_true`, where the false result
is the zero type version of the true result.
## For Loops
Allow C/C++ style `for` loops:
`for( init; test; next ) body`
Example:
```cpp
int sum=0;
for( int i=0; i<arg; i++ )
sum += i;
return sum;
```
Any of `init`, `test` and `next` can be empty. `init` allows for declaration
of a new variable, with scope limited to the `for` expression.
```cpp
int sum=0;
for( int i=0; i<arg; i++ )
sum += i;
return i; // ERROR: Undefined name 'i'
```
A broken `find` call:
```cpp
for( int i=0; i<ary#; i++ )
if( ary[i]==e )
break;
do_stuff(i); // ERROR: undefined name 'i'
```
An example `find` call:
```cpp
for( int i=0; i<ary#; i++ )
if( ary[i]==e )
return i;
return -1;
```
|