summaryrefslogtreecommitdiff
path: root/chapter17b/README.md
blob: 1b3a41af8cf9dd1e5e61a7197a28d5dfe6cbe3aa (plain)
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
# Chapter 17b: Syntax Sugar

English | [日本語](README.ja.md)

[Previous: Chapter 17a](../chapter17a/README.md) |
[Next: Chapter 18](../chapter18/README.md)


## Table of Contents

1. [Pre/Post-increment](#prepost-increment)
2. [Operator assignment](#operator-assignment)
3. [var/val](#var--val)
4. [Trinary](#trinary)
5. [For Loops](#for-loops)
6. [Memory effects](#memory-effects)

Chapter 17a established the permissions checked by assignments. This chapter
adds shorter ways to write assignments and control flow without changing those
permissions. Chapter 18 then introduces functions and calls.

## 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
on local variables with semantics similar to other languages. The supported
operators are `+=`, `-=`, `*=`, `/=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.
Bitwise and shift assignments require integer operands; `>>=` preserves the sign
while `>>>=` shifts in zeros. The assignment evaluates its right-hand side once,
stores the result narrowed to the variable's type, and yields that stored value.
Assignments associate right-to-left, as in `x |= y <<= 2`.



## var & val

`var` and `val` declare variables and infer their types; `var` infers a
reassignable binding while `val` infers a fixed binding.  Both preserve access
permissions from the initializing expression.  Neither makes a writable object
read-only.  The two independent permissions come from [Chapter
17a](../chapter17a/README.md).

```cpp
struct Point { int x; };
val p = new Point; // Fixed binding to a writable new object
p.x = 3;           // Allowed
// p = new Point;  // Error: fixed binding
Point view = p;    // Explicit read-only view
var q = view;      // Reassignable binding, still read-only access
// q.x = 4;        // Error: cannot restore write permission by inference
```

The initializer is required for type inference.  Primitives infer their broad
numeric type (`int` or `flt`); references retain their access permissions.  A
reassignable inferred reference allows null so it can later hold the empty
value.  Explicit types retain their declared width and nullability.  Function
parameters still require explicit types.

## 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;
```