|
1 | 1 | # Soodocode features list
|
2 | 2 |
|
| 3 | +This list is incomplete. |
| 4 | + |
3 | 5 | # Syntax
|
4 | 6 |
|
5 | 7 | ## Literals
|
@@ -71,248 +73,6 @@ A record type is a user-defined composite data type, made up of zero or more fie
|
71 | 73 |
|
72 | 74 | Initializing a variable of type (record type) causes all its fields to be initialized automatically.
|
73 | 75 |
|
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 |
| - |
316 | 76 | # Expressions
|
317 | 77 |
|
318 | 78 | ## Operators
|
|
0 commit comments