Skip to content

Commit d2ff096

Browse files
committed
Update documentation, create notes.md
1 parent 239c11c commit d2ff096

File tree

3 files changed

+249
-242
lines changed

3 files changed

+249
-242
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,5 @@ Usage: `soodocode file.sc`
8686

8787
## More information
8888
For a full list of features, see [features.md](docs/features.md)
89+
90+
For some discussion of advanced language features, see [notes.md](docs/notes.md)

docs/features.md

Lines changed: 2 additions & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Soodocode features list
22

3+
This list is incomplete.
4+
35
# Syntax
46

57
## Literals
@@ -71,248 +73,6 @@ A record type is a user-defined composite data type, made up of zero or more fie
7173

7274
Initializing a variable of type (record type) causes all its fields to be initialized automatically.
7375

74-
## A note on the DECLARE statement
75-
76-
The DECLARE statement works quite differently than many other languages.
77-
78-
In most other languages, you can declare an array or record variable, then assign an array or record to it, like this Typescript code:
79-
80-
```ts
81-
let x: number[];
82-
x = [1, 2, 3];
83-
let y: { field: number };
84-
y = { field: 123 };
85-
```
86-
87-
However, pseudocode does not have array or object literals. The only way to get an array or object is from the DECLARE statement, which automatically allocates an array or object.
88-
89-
```js
90-
DECLARE x: ARRAY[1:3] OF INTEGER
91-
x[1] <- 1 //this is already accessible
92-
DECLARE y: Recordtype
93-
y.field <- 123 //y is already initialized
94-
```
95-
96-
This initialization is also done recursively.
97-
98-
```js
99-
TYPE Record
100-
DECLARE field: INTEGER
101-
ENDTYPE
102-
DECLARE x: ARRAY[1:10] OF Record //allocates the array, and also 10 records
103-
x[0].field <- 50; //you can immediately write to the fields, the first declare statement already created all of the objects.
104-
```
105-
106-
For comparison, in Typescript:
107-
```ts
108-
type Record = {
109-
field?: number;
110-
};
111-
const x = new Array<Record>(10);
112-
x[0] = {};
113-
x[0].field = 50; //in Typescript, you need to assign the object first before writing to a slot in that object
114-
```
115-
116-
## A note on recursive types
117-
118-
Recursive record types without indirection are not allowed.
119-
120-
```js
121-
TYPE Foo
122-
DECLARE field: Foo //recursive without indirection
123-
ENDTYPE
124-
125-
DECLARE foo: Foo
126-
//Because there is no way to say "foo <- Foo { }" like in Rust, the declare statement automatically initializes the fields
127-
//that requires initializing foo.field,
128-
//which requires initializing foo.field.field...
129-
```
130-
131-
To work around this, you can use a class, like this:
132-
133-
```js
134-
CLASS Foo
135-
PUBLIC field: Foo //recursive without indirection
136-
ENDCLASS
137-
138-
DECLARE foo: Foo
139-
//this variable is currently uninitialized, because it can be assigned to with foo <- NEW foo()
140-
141-
foo <- NEW foo()
142-
//foo.field is now uninitialized
143-
foo.field <- NEW foo()
144-
//foo.field.field is still uninitialized
145-
//This does not create an infinite loop
146-
```
147-
148-
Alternatively, use a pointer, like this:
149-
150-
```js
151-
TYPE pFoo = ^Foo
152-
TYPE Foo
153-
DECLARE field: ^Foo //recursive with indirection
154-
ENDTYPE
155-
DECLARE foo1, foo2: Foo
156-
//foo1.field is now uninitialized
157-
158-
foo1.field <- ^foo2
159-
```
160-
161-
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.
162-
163-
## A note on variable length array types
164-
165-
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.
166-
167-
### In function parameters
168-
169-
```js
170-
PROCEDURE foo(x: ARRAY OF INTEGER)
171-
OUTPUT LENGTH(x)
172-
ENDPROCEDURE
173-
DECLARE y: ARRAY[1:10] OF INTEGER
174-
CALL foo(y)
175-
```
176-
Here, the parameter "x" can accept an array of any length, because when the function is actually running, it has a known length.
177-
178-
This can also be thought of as the function being generic:
179-
180-
```ts
181-
function arr<const T extends number[]>(x:T){
182-
console.log(x.length);
183-
}
184-
const y = Array<number>(10).fill(0);
185-
arr<number[] & {length: 10;}>(y);
186-
```
187-
188-
Functions may return an array of arbitrary length, however, this is difficult to use.
189-
190-
```js
191-
//returns an array with a random length
192-
FUNCTION arr() RETURNS ARRAY OF INTEGER
193-
DECLARE out: ARRAY[1:ROUND(RAND(100), 0)] OF INTEGER
194-
RETURN out
195-
ENDFUNCTION
196-
197-
OUTPUT LENGTH(arr()) //we can call the function, but how do we store the return value?
198-
DECLARE x: ARRAY OF INTEGER //not allowed, because DECLARE allocates and initializes the array
199-
```
200-
201-
Variable-length arrays are also not allowed in record types, because record types automatically initialize their fields.
202-
203-
```js
204-
TYPE foo
205-
DECLARE field: ARRAY OF INTEGER
206-
ENDTYPE
207-
DECLARE x: foo //not allowed: this tries to initialize x.field
208-
```
209-
210-
There are two workarounds.
211-
212-
### Varlength arrays behind pointers
213-
214-
Pointers do not do any automatic initialization, so variable-length arrays can be safely stored behind a pointer.
215-
216-
```js
217-
//returns an array with a random length
218-
FUNCTION arr() RETURNS ARRAY OF INTEGER
219-
DECLARE out: ARRAY[1:ROUND(RAND(100), 0)] OF INTEGER
220-
RETURN out
221-
ENDFUNCTION
222-
223-
//pointer to array of integer (unknown length)
224-
TYPE arrayWrapper = ^ARRAY OF INTEGER
225-
DECLARE x: arrayWrapper
226-
227-
x <- ^arr() //Store a pointer to the return value
228-
229-
OUTPUT LENGTH(x^)
230-
FOR i <- 1 TO LENGTH(x^)
231-
OUTPUT (x^)[i]
232-
NEXT i
233-
234-
x^ <- arr() //This also works
235-
```
236-
237-
### Varlength arrays behind class fields
238-
239-
At first glance, it looks like class fields are automatically initialized, but there is a way to avoid initializing them.
240-
241-
```js
242-
CLASS bar
243-
PUBLIC field: ARRAY OF INTEGER
244-
PUBLIC PROCEDURE NEW()
245-
ENDPROCEDURE
246-
ENDCLASS
247-
248-
DECLARE x: bar; x <- NEW bar()
249-
OUTPUT x.field //initialized?
250-
```
251-
252-
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:
253-
254-
```js
255-
FUNCTION arr() RETURNS ARRAY OF INTEGER
256-
DECLARE out: ARRAY[1:ROUND(RAND(100), 0)] OF INTEGER
257-
RETURN out
258-
ENDFUNCTION
259-
260-
261-
CLASS bar
262-
PUBLIC field: ARRAY OF INTEGER
263-
PUBLIC PROCEDURE NEW()
264-
field <- arr()
265-
//field never needed to be initialized
266-
ENDPROCEDURE
267-
ENDCLASS
268-
269-
DECLARE x: bar; x <- NEW bar()
270-
OUTPUT LENGTH(x.field)
271-
```
272-
273-
(If the field is not assigned to in the constructor, it will be automatically initialized after the constructor runs, which will cause an error)
274-
275-
### Varlength arrays behind class fields and pointers
276-
277-
```js
278-
FUNCTION arr(length: INTEGER) RETURNS ARRAY OF INTEGER
279-
DECLARE out: ARRAY[1:length] OF INTEGER
280-
RETURN out
281-
ENDFUNCTION
282-
283-
TYPE pArray = ^ARRAY OF INTEGER
284-
CLASS bar
285-
PUBLIC field: ARRAY OF INTEGER
286-
PUBLIC PROCEDURE NEW(len: INTEGER)
287-
field <- arr(len)
288-
ENDPROCEDURE
289-
ENDCLASS
290-
291-
DECLARE x: bar
292-
DECLARE y: pArray
293-
294-
x <- NEW bar(1)
295-
y <- ^x.field //Create a pointer to x.field
296-
297-
FOR i <- 1 TO LENGTH(x.field) //Loop through x.field
298-
OUTPUT x.field[i]
299-
NEXT i
300-
301-
FOR i <- 1 TO LENGTH(y^) //Loop through the pointer
302-
OUTPUT (y^)[i]
303-
NEXT i
304-
305-
//Replace x.field with an array of different type
306-
x.field <- arr(2)
307-
308-
//The pointer should still be pointing to x.field
309-
FOR i <- 1 TO LENGTH(y^)
310-
OUTPUT (y^)[i]
311-
NEXT i
312-
```
313-
314-
This was difficult to implement, but it is supported.
315-
31676
# Expressions
31777

31878
## Operators

0 commit comments

Comments
 (0)