Skip to content

Commit 97281d5

Browse files
committed
Update features.md, mention varlength arrays
1 parent 8d06136 commit 97281d5

File tree

5 files changed

+215
-14
lines changed

5 files changed

+215
-14
lines changed

docs/features.md

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,160 @@ foo1.field <- ^foo2
118118

119119
These approaches have a drawback: there is no way to check if a variable is initialized without attempting to access it, which terminates the program. Use of a separate flag variable is recommended.
120120

121+
## A note on variable length array types
122+
123+
In Soodocode, all arrays must have a fixed length. The DECLARE statement automatically initializes an array, so it is not possible to DECLARE a variable with an unknown length and leave it uninitialized. However, there are a few places when variable-length array types can be used.
124+
125+
### In function parameters
126+
127+
```js
128+
PROCEDURE arr(x: ARRAY OF INTEGER)
129+
OUTPUT LENGTH(x)
130+
ENDPROCEDURE
131+
DECLARE y: ARRAY[1:10] OF INTEGER
132+
CALL arr(y)
133+
```
134+
Here, the parameter "x" can accept an array of any length, because when the function is actually running, it has a known length.
135+
136+
This can also be thought of as the function being generic:
137+
138+
```ts
139+
function arr<const T extends number[]>(x:T){
140+
console.log(x.length);
141+
}
142+
const y = Array<number>(10).fill(0);
143+
arr<number[] & {length: 10;}>(y);
144+
```
145+
146+
Functions may return an array of arbitrary length, however, this is difficult to use.
147+
148+
```js
149+
//returns an array with a random length
150+
FUNCTION arr() RETURNS ARRAY OF INTEGER
151+
DECLARE out: ARRAY[1:ROUND(RAND(100), 0)] OF INTEGER
152+
RETURN out
153+
ENDFUNCTION
154+
155+
OUTPUT LENGTH(arr()) //we can call the function, but how do we store the return value?
156+
DECLARE x: ARRAY OF INTEGER //not allowed, because DECLARE allocates and initializes the array
157+
```
158+
159+
Variable-length arrays are also not allowed in record types, because record types automatically initialize their fields.
160+
161+
```js
162+
TYPE foo
163+
DECLARE field: ARRAY OF INTEGER
164+
ENDTYPE
165+
DECLARE x: foo //not allowed: this tries to initialize x.field
166+
```
167+
168+
There are two workarounds.
169+
170+
### Varlength arrays behind pointers
171+
172+
Pointers do not do any automatic initialization, so variable-length arrays can be safely stored behind a pointer.
173+
174+
```js
175+
//returns an array with a random length
176+
FUNCTION arr() RETURNS ARRAY OF INTEGER
177+
DECLARE out: ARRAY[1:ROUND(RAND(100), 0)] OF INTEGER
178+
RETURN out
179+
ENDFUNCTION
180+
181+
//pointer to array of integer (unknown length)
182+
TYPE arrayWrapper = ^ARRAY OF INTEGER
183+
DECLARE x: arrayWrapper
184+
185+
x <- ^arr() //Store a pointer to the return value
186+
187+
OUTPUT LENGTH(x^)
188+
FOR i <- 1 TO LENGTH(x^)
189+
OUTPUT (x^)[i]
190+
NEXT i
191+
192+
x^ <- arr() //This also works
193+
```
194+
195+
### Varlength arrays behind class fields
196+
197+
At first glance, it looks like class fields are automatically initialized, but there is a way to avoid initializing them.
198+
199+
```js
200+
CLASS bar
201+
PUBLIC field: ARRAY OF INTEGER
202+
PUBLIC PROCEDURE NEW()
203+
ENDPROCEDURE
204+
ENDCLASS
205+
206+
DECLARE x: bar; x <- NEW bar()
207+
OUTPUT x.field //initialized?
208+
```
209+
210+
The trick is to lazily initialize class fields: if the first assignment to a field occurs in the constructor, it never needs to be initialized. Therefore, the following code works:
211+
212+
```js
213+
FUNCTION arr() RETURNS ARRAY OF INTEGER
214+
DECLARE out: ARRAY[1:ROUND(RAND(100), 0)] OF INTEGER
215+
RETURN out
216+
ENDFUNCTION
217+
218+
219+
CLASS bar
220+
PUBLIC field: ARRAY OF INTEGER
221+
PUBLIC PROCEDURE NEW()
222+
field <- arr()
223+
//field never needed to be initialized
224+
ENDPROCEDURE
225+
ENDCLASS
226+
227+
DECLARE x: bar; x <- NEW bar()
228+
OUTPUT LENGTH(x.field)
229+
```
230+
231+
(If the field is not assigned to in the constructor, it will be automatically initialized after the constructor runs, which will cause an error)
232+
233+
### Varlength arrays behind class fields and pointers
234+
235+
```js
236+
FUNCTION arr(x: INTEGER) RETURNS ARRAY OF INTEGER
237+
DECLARE out: ARRAY[1:x] OF INTEGER
238+
RETURN out
239+
ENDFUNCTION
240+
241+
TYPE pArray = ^ARRAY OF INTEGER
242+
CLASS bar
243+
PUBLIC field: ARRAY OF INTEGER
244+
PUBLIC PROCEDURE NEW(len: INTEGER)
245+
//The field is assigned to before it is used, so it does not need to be initialized
246+
field <- arr(len)
247+
ENDPROCEDURE
248+
ENDCLASS
249+
250+
DECLARE x: bar
251+
DECLARE y: pArray
252+
253+
x <- NEW bar(1)
254+
y <- ^x.field //Create a pointer to x.field
255+
256+
FOR i <- 1 TO LENGTH(x.field) //Loop through x.field
257+
OUTPUT x.field[i]
258+
NEXT i
259+
260+
FOR i <- 1 TO LENGTH(y^) //Loop through the pointer
261+
OUTPUT (y^)[i]
262+
NEXT i
263+
264+
//Replace x.field with an array of different type
265+
x.field <- arr(2)
266+
267+
//The pointer should still be pointing to x.field
268+
FOR i <- 1 TO LENGTH(y^)
269+
OUTPUT (y^)[i]
270+
NEXT i
271+
```
272+
273+
This was difficult to implement, but it is supported.
274+
121275
# Expressions
122276

123277
## Operators

programs/demos/demos.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@
3030
[![Image of output color](./output-color.png)](./output-color.sc)
3131

3232
[![Image of pointer name](./pointer-names.png)](./pointer-names.sc)
33+
34+
[![Image of varlength arrays](./varlength-arrays.png)](./varlength-arrays.sc)

programs/demos/varlength-arrays.png

467 KB
Loading

programs/demos/varlength-arrays.sc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//Function that returns an array of arbitrary length
2+
FUNCTION arr(x: INTEGER) RETURNS ARRAY OF INTEGER
3+
DECLARE out: ARRAY[1:x] OF INTEGER
4+
RETURN out
5+
ENDFUNCTION
6+
7+
TYPE pArray = ^ARRAY OF INTEGER
8+
CLASS bar
9+
PUBLIC field: ARRAY OF INTEGER
10+
PUBLIC PROCEDURE NEW(len: INTEGER)
11+
//The field is assigned to before it is used, so it does not need to be initialized
12+
field <- arr(len)
13+
ENDPROCEDURE
14+
ENDCLASS
15+
16+
DECLARE x: bar
17+
DECLARE y: pArray
18+
19+
x <- NEW bar(1)
20+
y <- ^x.field //Create a pointer to x.field
21+
22+
FOR i <- 1 TO LENGTH(x.field) //Loop through x.field
23+
OUTPUT x.field[i]
24+
NEXT i
25+
26+
FOR i <- 1 TO LENGTH(y^) //Loop through the pointer
27+
OUTPUT (y^)[i]
28+
NEXT i
29+
30+
//Replace x.field with an array of different type
31+
x.field <- arr(2)
32+
33+
//The pointer should still be pointing to x.field
34+
FOR i <- 1 TO LENGTH(y^)
35+
OUTPUT (y^)[i]
36+
NEXT i

spec/src/full.spec.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -459,15 +459,15 @@ OUTPUT amogus(x)`,
459459
`different length`,
460460
],
461461
varlength_array_behind_pointer_simple: [
462-
`FUNCTION foo(x: INTEGER) RETURNS ARRAY OF INTEGER
462+
`FUNCTION arr(x: INTEGER) RETURNS ARRAY OF INTEGER
463463
DECLARE out: ARRAY[1:x] OF INTEGER
464464
RETURN out
465465
ENDFUNCTION
466466
467467
TYPE arrayWrapper = ^ARRAY OF INTEGER
468468
DECLARE x: arrayWrapper
469469
470-
x <- ^foo(1)
470+
x <- ^arr(1)
471471
OUTPUT LENGTH(x^)
472472
FOR i <- 1 TO LENGTH(x^)
473473
OUTPUT (x^)[i]
@@ -476,44 +476,43 @@ NEXT i
476476
["1", "0"]
477477
],
478478
varlength_array_behind_pointer_complex: [
479-
`FUNCTION foo(x: INTEGER) RETURNS ARRAY OF INTEGER
479+
`FUNCTION arr(x: INTEGER) RETURNS ARRAY OF INTEGER
480480
DECLARE out: ARRAY[1:x] OF INTEGER
481481
RETURN out
482482
ENDFUNCTION
483483
484484
TYPE arrayWrapper = ^ARRAY OF INTEGER
485485
DECLARE x: arrayWrapper
486486
487-
x <- ^foo(1)
487+
x <- ^arr(1)
488488
OUTPUT LENGTH(x^)
489489
FOR i <- 1 TO LENGTH(x^)
490490
OUTPUT (x^)[i]
491491
NEXT i
492492
493-
x <- ^foo(2)
493+
x <- ^arr(2)
494494
OUTPUT LENGTH(x^)
495495
FOR i <- 1 TO LENGTH(x^)
496496
OUTPUT (x^)[i]
497497
NEXT i
498498
499-
x^ <- foo(3)
499+
x^ <- arr(3)
500500
OUTPUT LENGTH(x^)
501501
FOR i <- 1 TO LENGTH(x^)
502502
OUTPUT (x^)[i]
503503
NEXT i`,
504504
["1", "0", "2", "0", "0", "3", "0", "0", "0"]
505505
],
506506
varlength_array_in_class_complex: [
507-
`FUNCTION foo(x: INTEGER) RETURNS ARRAY OF INTEGER
507+
`FUNCTION arr(x: INTEGER) RETURNS ARRAY OF INTEGER
508508
DECLARE out: ARRAY[1:x] OF INTEGER
509509
RETURN out
510510
ENDFUNCTION
511511
512512
CLASS bar
513513
PUBLIC field: ARRAY OF INTEGER
514514
PUBLIC PROCEDURE NEW(len: INTEGER)
515-
field <- foo(len)
516-
//field never needed to be initialized
515+
field <- arr(len)
517516
ENDPROCEDURE
518517
ENDCLASS
519518
@@ -524,14 +523,16 @@ FOR i <- 1 TO LENGTH(x.field)
524523
OUTPUT x.field[i]
525524
NEXT i
526525
527-
x.field <- foo(2)
526+
x.field <- arr(2)
528527
FOR i <- 1 TO LENGTH(x.field)
529528
OUTPUT x.field[i]
530529
NEXT i`,
531530
["0", "0", "0"]
532531
],
533532
varlength_array_in_class_very_complex: [
534-
`FUNCTION foo(x: INTEGER) RETURNS ARRAY OF INTEGER
533+
`
534+
//Function that returns an array of arbitrary length
535+
FUNCTION arr(x: INTEGER) RETURNS ARRAY OF INTEGER
535536
DECLARE out: ARRAY[1:x] OF INTEGER
536537
RETURN out
537538
ENDFUNCTION
@@ -540,24 +541,32 @@ TYPE pArray = ^ARRAY OF INTEGER
540541
CLASS bar
541542
PUBLIC field: ARRAY OF INTEGER
542543
PUBLIC PROCEDURE NEW(len: INTEGER)
543-
field <- foo(len)
544-
//field never needed to be initialized
544+
//The field is assigned to before it is used, so it does not need to be initialized
545+
field <- arr(len)
545546
ENDPROCEDURE
546547
ENDCLASS
547548
548549
DECLARE x: bar
549550
DECLARE y: pArray
550551
551552
x <- NEW bar(1)
553+
//Create a pointer to x.field
552554
y <- ^x.field
555+
556+
//Loop through x.field
553557
FOR i <- 1 TO LENGTH(x.field)
554558
OUTPUT x.field[i]
555559
NEXT i
560+
561+
//Loop through the pointer
556562
FOR i <- 1 TO LENGTH(y^)
557563
OUTPUT (y^)[i]
558564
NEXT i
559565
560-
x.field <- foo(2)
566+
//Replace x.field with an array of different type
567+
x.field <- arr(2)
568+
569+
//The pointer should still be pointing to x.field
561570
FOR i <- 1 TO LENGTH(y^)
562571
OUTPUT (y^)[i]
563572
NEXT i`,

0 commit comments

Comments
 (0)