bbc-basic: Adjust error message from FNenv_get to match tests.
[jackhill/mal.git] / bbc-basic / types
CommitLineData
49c172c0 1REM > types library for mal in BBC BASIC
ca23e632
BH
2
3REM This library should be the only thing that understands the
4REM implementation of mal data types in BBC BASIC. All other
5REM code should use routines in this library to access them.
6
7REM As far as other code is concerned, a mal object is just an
8REM opaque 32-bit integer, which might be a pointer, or might not.
9
10REM Following the 8-bit BASIC implementation, we currently have two
11REM arrays, Z%() containing most objects and S$() containing strings
4b811acc
BH
12REM (referenced from Z%()). Unlike that implementation, we use a
13REM two-dimensional array where each object is a whole row. This
14REM is inefficient but should make memory management simpler.
ca23e632 15
2ec0dbee
BH
16REM S%() holds reference counts for the strings in S$(). At present
17REM these are all 0 or 1.
18
7f6d61fc
BH
19REM Z%(x,0) holds the type of an object. High-order bits contain flags.
20
21REM sS%() is a shadow stack, used to keep track of which mal values might
22REM be referenced from local variables at a given depth of the BASIC call
23REM stack. It grows upwards. sSP% points to the first unused word. sFP%
24REM points to the start of the current shadow stack frame. The first word
25REM of each shadow stack frame is the saved value of sFP%. The rest are
26REM mal values.
6d1a36e8
BH
27
28REM Types are:
29REM 0 nil
30REM 1 boolean
31REM 2 integer
68f0184e 32REM 4 string
6d1a36e8
BH
33REM 5 symbol
34REM 6 list (each object is a cons cell)
35REM 8 hash-map (each object is one entry)
36REM 9 core function
37REM 10 mal function
38REM 13 environment
7b6e74a6 39REM 15 free block
6d1a36e8
BH
40
41REM Formats of individual objects are defined below.
ca23e632
BH
42
43DEF PROCtypes_init
5256663e
BH
44 REM Arbitrarily use half of BASIC's heap as the mal heap, with a bit
45 REM more for strings. Each heap entry is sixteen bytes.
2ec0dbee
BH
46 DIM Z%((HIMEM-LOMEM)/32,3)
47 DIM S$((HIMEM-LOMEM)/64), S%((HIMEM-LOMEM)/64)
7f6d61fc
BH
48 DIM sS%((HIMEM-LOMEM)/64)
49 Z%(1,0) = 1: REM false
50 Z%(2,0) = 1: Z%(2,1) = TRUE: REM true
51 Z%(3,0) = 6: REM empty list
52 Z%(5,0) = 8: REM empty hashmap
4b811acc 53 next_Z% = 6
ca23e632 54 next_S% = 0
7f6d61fc
BH
55 sSP% = 1
56 sFP% = 0
7b6e74a6
BH
57 F% = 0
58 GCtoggle% = 0
129c2ac6 59 SF% = 0
ca23e632
BH
60ENDPROC
61
62DEF FNtype_of(val%)
4b811acc 63=Z%(val%,0) AND 31
ca23e632 64
7f6d61fc 65DEF PROCgc_enter
5411931a 66 REM PRINT ;sFP%;
7f6d61fc
BH
67 sS%(sSP%) = sFP%
68 sFP% = sSP%
69 sSP% += 1
5411931a 70 REM PRINT " >>> ";sFP%
7f6d61fc 71ENDPROC
a6b84652 72
7b6e74a6
BH
73DEF FNgc_save
74=sFP%
75
7f6d61fc 76DEF PROCgc_exit
5411931a 77 REM PRINT ;sS%(sFP%);" <<< ";sFP%
7f6d61fc
BH
78 sSP% = sFP%
79 sFP% = sS%(sFP%)
a6b84652
BH
80ENDPROC
81
7b6e74a6
BH
82DEF PROCgc_restore(oldFP%)
83 sFP% = oldFP%
84 sSP% = sFP% + 1
5411931a 85 REM PRINT "!!! ";sFP%
7b6e74a6
BH
86ENDPROC
87
7f6d61fc
BH
88DEF FNref_local(val%)
89 sS%(sSP%) = val%
90 sSP% += 1
91=val%
92
93DEF FNgc_exit(val%)
94 PROCgc_exit
7b6e74a6
BH
95 val% = FNref_local(val%)
96=val%
97
d9dcadde
BH
98DEF PROCgc_keep_only2(val1%, val2%)
99 PROCgc_exit
100 PROCgc_enter
101 val1% = FNref_local(val1%)
102 val2% = FNref_local(val2%)
103ENDPROC
104
7b6e74a6 105DEF FNmalloc(type%)
15119738 106 LOCAL val%
90d169ce
BH
107 REM If the heap is full, collect garbage first.
108 IF F% = 0 AND next_Z% > DIM(Z%(),1) THEN PROCgc
7b6e74a6
BH
109 IF F% <> 0 THEN
110 val% = F%
111 F% = Z%(val%,1)
112 ELSE
113 val% = next_Z%
114 next_Z% += 1
115 ENDIF
116 Z%(val%,0) = type% OR GCtoggle%
7f6d61fc 117=FNref_local(val%)
15119738 118
f1e3f09f
BH
119DEF FNsalloc(s$)
120 LOCAL val%
129c2ac6
BH
121 IF SF% <> 0 THEN
122 val% = SF%
123 SF% = S%(val%)
124 ELSE
125 val% = next_S%
126 next_S% += 1
127 ENDIF
f1e3f09f 128 S$(val%) = s$
f1e3f09f
BH
129=val%
130
7b6e74a6 131DEF PROCfree(val%)
129c2ac6 132 CASE FNtype_of(val%) OF
68f0184e 133 WHEN 4 : PROCfree_string(val%)
129c2ac6
BH
134 WHEN 5 : PROCfree_symbol(val%)
135 WHEN 8 : PROCfree_hashmap(val%)
136 ENDCASE
7b6e74a6
BH
137 Z%(val%,0) = 15
138 Z%(val%,1) = F%
139 Z%(val%,2) = 0
140 Z%(val%,3) = 0
141 F% = val%
142ENDPROC
143
129c2ac6
BH
144DEF PROCsfree(val%)
145 S$(val%) = ""
146 S%(val%) = SF%
147 SF% = val%
148ENDPROC
149
7b6e74a6 150DEF PROCgc
5411931a 151 REM PRINT "** START GC **"
7b6e74a6
BH
152 GCtoggle% = GCtoggle% EOR &100
153 PROCgc_markall
154 PROCgc_sweep
5411931a 155 REM PRINT "** FINISH GC **"
7b6e74a6
BH
156ENDPROC
157
158DEF PROCgc_markall
159 LOCAL sp%, fp%
160 fp% = sFP%
5411931a 161 REM PRINT ">>marking...";
7b6e74a6
BH
162 FOR sp% = sSP% - 1 TO 0 STEP -1
163 IF sp% = fp% THEN
164 fp% = sS%(sp%)
5411931a 165 REM PRINT " / ";
7b6e74a6
BH
166 ELSE PROCgc_mark(sS%(sp%))
167 ENDIF
168 NEXT sp%
5411931a 169 REM PRINT
7b6e74a6
BH
170ENDPROC
171
172DEF PROCgc_mark(val%)
173 IF (Z%(val%,0) AND &100) <> GCtoggle% THEN
5411931a 174 REM PRINT " ";val%;
7b6e74a6
BH
175 Z%(val%,0) = Z%(val%,0) EOR &100
176 CASE FNtype_of(val%) OF
177 WHEN 6 : PROCgc_mark_list(val%)
178 WHEN 8 : PROCgc_mark_hashmap(val%)
179 WHEN 10 : PROCgc_mark_fn(val%)
940092c6 180 WHEN 12 : PROCgc_mark_atom(val%)
7b6e74a6
BH
181 WHEN 13 : PROCgc_mark_environment(val%)
182 ENDCASE
183 ENDIF
184ENDPROC
185
186DEF PROCgc_sweep
187 LOCAL val%
5411931a 188 REM PRINT ">>sweeping ...";
7b6e74a6
BH
189 FOR val% = 6 TO next_Z% - 1
190 IF FNtype_of(val%) <> 15 AND (Z%(val%,0) AND &100) <> GCtoggle% THEN
5411931a 191 REM PRINT " ";val%;
7b6e74a6
BH
192 PROCfree(val%)
193 ENDIF
194 NEXT val%
5411931a 195 REM PRINT
7b6e74a6
BH
196ENDPROC
197
ca23e632
BH
198REM ** Nil **
199
c32c31b3
BH
200DEF FNis_nil(val%)
201=FNtype_of(val%) = 0
202
ca23e632
BH
203DEF FNnil
204=0
205
3ab6b58b
BH
206REM ** Boolean **
207
604e6260 208REM Z%(x,1) = TRUE or FALSE
6d1a36e8 209
3ab6b58b
BH
210DEF FNis_boolean(val%)
211=FNtype_of(val%) = 1
212
213DEF FNalloc_boolean(bval%)
4b811acc
BH
214 IF bval% THEN =2
215=1
3ab6b58b
BH
216
217DEF FNunbox_boolean(val%)
9809aa65 218 IF NOT FNis_boolean(val%) THEN ERROR &40E80911, "Not a boolean"
604e6260 219=Z%(val%,1)
3ab6b58b 220
b4033eb7
BH
221DEF FNis_truish(val%)
222 IF FNis_nil(val%) THEN =FALSE
604e6260 223 IF FNis_boolean(val%) THEN =FNunbox_boolean(val%)
b4033eb7
BH
224=TRUE
225
ca23e632
BH
226REM ** Integers **
227
6d1a36e8
BH
228REM Z%(x,1) = integer value
229
ca23e632
BH
230DEF FNis_int(val%)
231=FNtype_of(val%) = 2
232
233DEF FNalloc_int(ival%)
234 LOCAL val%
7b6e74a6 235 val% = FNmalloc(2)
4b811acc 236 Z%(val%,1) = ival%
ca23e632
BH
237=val%
238
239DEF FNunbox_int(val%)
9809aa65 240 IF NOT FNis_int(val%) THEN ERROR &40E80912, "Not an integer"
4b811acc 241=Z%(val%,1)
ca23e632 242
68f0184e
BH
243REM ** Strings **
244
245DEF FNis_string(val%)
246=FNtype_of(val%) = 4
247
248DEF FNalloc_string(sval$)
249 LOCAL val%
250 val% = FNmalloc(4)
251 Z%(val%,1) = FNsalloc(sval$)
252=val%
253
254DEF PROCfree_string(val%)
255 PROCsfree(Z%(val%,1))
256ENDPROC
257
258DEF FNunbox_string(val%)
259 IF NOT FNis_string(val%) THEN ERROR &40E80914, "Not a string"
260=S$(Z%(val%,1))
261
ca23e632
BH
262REM ** Symbols **
263
6d1a36e8
BH
264REM Z%(x,1) = index in S$() of the value of the symbol
265
ca23e632
BH
266DEF FNis_symbol(val%)
267=FNtype_of(val%) = 5
268
269DEF FNalloc_symbol(sval$)
f1e3f09f 270 LOCAL val%
7b6e74a6 271 val% = FNmalloc(5)
f1e3f09f 272 Z%(val%,1) = FNsalloc(sval$)
ca23e632
BH
273=val%
274
129c2ac6
BH
275DEF PROCfree_symbol(val%)
276 PROCsfree(Z%(val%,1))
277ENDPROC
278
ca23e632 279DEF FNunbox_symbol(val%)
9809aa65 280 IF NOT FNis_symbol(val%) THEN ERROR &40E80915, "Not a symbol"
4b811acc 281=S$(Z%(val%,1))
ca23e632
BH
282
283REM ** Lists **
284
6d1a36e8
BH
285REM Z%(x,1) = index in Z%() of next pair
286REM Z%(x,2) = index in Z%() of first element
287
288REM The empty list is a distinguished value, which happens to have
289REM both elements nil.
290
ca23e632 291DEF FNempty
4b811acc 292=3
ca23e632
BH
293
294DEF FNalloc_pair(car%, cdr%)
295 LOCAL val%
7b6e74a6 296 val% = FNmalloc(6)
7f6d61fc
BH
297 Z%(val%,2) = car%
298 Z%(val%,1) = cdr%
299=val%
a6b84652 300
ca23e632
BH
301DEF FNis_empty(val%)
302=val% = FNempty
303
304DEF FNis_list(val%)
305=FNtype_of(val%) = 6
306
7b6e74a6
BH
307DEF PROCgc_mark_list(val%)
308 IF NOT FNis_empty(val%) THEN
309 PROCgc_mark(Z%(val%,1))
310 PROCgc_mark(Z%(val%,2))
311 ENDIF
312ENDPROC
313
ca23e632 314DEF FNlist_car(val%)
9809aa65 315 IF NOT FNis_list(val%) THEN ERROR &40E80916, "Can't get car of non-list"
4b811acc 316 IF Z%(val%,1) = 0 THEN ERROR &40E80920, "Can't get car of empty list"
7f6d61fc 317=FNref_local(Z%(val%,2))
ca23e632
BH
318
319DEF FNlist_cdr(val%)
9809aa65 320 IF NOT FNis_list(val%) THEN ERROR &40E80916, "Can't get cdr of non-list"
4b811acc 321 IF Z%(val%,1) = 0 THEN ERROR &40E80920, "Can't get cdr of empty list"
7f6d61fc 322=FNref_local(Z%(val%,1))
c2d58701 323
86f8c24f
BH
324DEF FNalloc_list2(val0%, val1%)
325 =FNalloc_pair(val0%, FNalloc_pair(val1%, FNempty))
326
327DEF FNalloc_list3(val0%, val1%, val2%)
328 =FNalloc_pair(val0%, FNalloc_pair(val1%, FNalloc_pair(val2%, FNempty)))
329
717da462
BH
330DEF FNlist_len(val%)
331 LOCAL i%
332 WHILE NOT FNis_empty(val%)
333 val% = FNlist_cdr(val%)
334 i% += 1
335 ENDWHILE
336= i%
337
59c39631 338DEF FNlist_nth(val%, n%)
1588b9d4 339 WHILE n% > 0
59c39631 340 IF FNis_empty(val%) THEN ERROR &40E80923, "Subscript out of range"
82805c27 341 val% = FNlist_cdr(val%)
59c39631
BH
342 n% -= 1
343 ENDWHILE
82805c27 344 IF FNis_empty(val%) THEN ERROR &40E80923, "Subscript out of range"
7b6e74a6 345=FNlist_car(val%)
59c39631 346
717da462
BH
347DEF PROClist_to_array(val%, a%())
348 REM a%() must already be correctly dimensioned.
349 LOCAL i%
350 WHILE NOT FNis_empty(val%)
7f6d61fc 351 a%(i%) = FNref_local(FNlist_car(val%))
717da462
BH
352 val% = FNlist_cdr(val%)
353 i% += 1
354 ENDWHILE
355ENDPROC
356
ff0d66d4
BH
357DEF FNarray_to_list(a%())
358 LOCAL i%, val%
7f6d61fc 359 PROCgc_enter
ff0d66d4
BH
360 val% = FNempty
361 IF DIM(a%(), 1) = 0 THEN =val%
362 FOR i% = DIM(a%(), 1) - 1 TO 0 STEP -1
363 val% = FNalloc_pair(a%(i%), val%)
364 NEXT i%
7f6d61fc 365=FNgc_exit(val%)
ff0d66d4 366
717da462
BH
367REM ** Core functions **
368
6d1a36e8
BH
369REM Z%(x,1) = index of function in FNcore_call
370
717da462
BH
371DEF FNis_corefn(val%)
372=FNtype_of(val%) = 9
373
374DEF FNalloc_corefn(fn%)
375 LOCAL val%
7b6e74a6 376 val% = FNmalloc(9)
4b811acc 377 Z%(val%,1) = fn%
717da462
BH
378=val%
379
380DEF FNunbox_corefn(val%)
9809aa65 381 IF NOT FNis_corefn(val%) THEN ERROR &40E80919, "Not a core function"
4b811acc 382=Z%(val%,1)
717da462 383
6d1a36e8
BH
384REM ** Hash-maps **
385
386REM Z%(x,1) = index in Z%() of next element
387REM Z%(x,2) = index in S$() of value
388REM Z%(x,3) = index in Z%() of value
c2d58701
BH
389
390REM To defer implementing mal strings for a bit, hashmap keys are
391REM currently BASIC strings rather than arbitrary values.
392
393DEF FNempty_hashmap
4b811acc 394=5
c2d58701
BH
395
396DEF FNalloc_hashmap_entry(key$, val%, next%)
397 LOCAL entry%
7b6e74a6 398 entry% = FNmalloc(8)
4b811acc 399 Z%(entry%,1) = next%
f1e3f09f 400 Z%(entry%,2) = FNsalloc(key$)
4b811acc 401 Z%(entry%,3) = val%
c2d58701
BH
402=entry%
403
404DEF FNis_hashmap(val%)
405=FNtype_of(val%) = 8
406
7b6e74a6
BH
407DEF PROCgc_mark_hashmap(val%)
408 PROCgc_mark(Z%(val%,1))
409 PROCgc_mark(Z%(val%,3))
410ENDPROC
411
129c2ac6
BH
412DEF PROCfree_hashmap(val%)
413 PROCsfree(Z%(val%,2))
414ENDPROC
415
c2d58701 416DEF FNhashmap_get(map%, key$)
9809aa65 417 IF NOT FNis_hashmap(map%) THEN ERROR &40E80918, "Can't get item from a non-hashmap"
c2d58701 418 IF map% = FNempty_hashmap THEN =FNnil
7f6d61fc 419 IF S$(Z%(map%,2)) = key$ THEN =FNref_local(Z%(map%,3))
4b811acc 420=FNhashmap_get(Z%(map%,1), key$)
dbe45187 421
61df4dc6
BH
422DEF FNhashmap_contains(map%, key$)
423 IF NOT FNis_hashmap(map%) THEN ERROR &40E80918, "Can't get item from a non-hashmap"
424 IF map% = FNempty_hashmap THEN =FALSE
425 IF S$(Z%(map%,2)) = key$ THEN =TRUE
426=FNhashmap_get(Z%(map%,1), key$)
427
1204264c
BH
428REM ** Functions **
429
24951719 430REM Z%(x,0) AND &80 = is_macro flag
6d1a36e8
BH
431REM Z%(x,1) = index in Z%() of ast
432REM Z%(x,2) = index in Z%() of params
433REM Z%(x,3) = index in Z%() of env
434
1204264c
BH
435DEF FNis_fn(val%)
436=FNtype_of(val%) = 10
437
24951719
BH
438DEF FNis_nonmacro_fn(val%)
439=FNtype_of(val%) = 10 AND (Z%(val%, 0) AND &80) = &00
440
441DEF FNis_macro(val%)
442=FNtype_of(val%) = 10 AND (Z%(val%, 0) AND &80) = &80
443
9462d98d 444DEF FNalloc_fn(ast%, params%, env%)
1204264c 445 LOCAL val%
7b6e74a6 446 val% = FNmalloc(10)
9462d98d
BH
447 Z%(val%,1) = ast%
448 Z%(val%,2) = params%
4b811acc 449 Z%(val%,3) = env%
1204264c
BH
450=val%
451
24951719
BH
452DEF PROCmake_macro(val%)
453 IF NOT FNis_fn(val%) THEN ERROR &40E8091A, "Not a function"
454 Z%(val%, 0) = Z%(val%, 0) OR &80
455ENDPROC
456
7b6e74a6
BH
457DEF PROCgc_mark_fn(val%)
458 PROCgc_mark(Z%(val%,1))
459 PROCgc_mark(Z%(val%,2))
460 PROCgc_mark(Z%(val%,3))
461ENDPROC
462
9462d98d 463DEF FNfn_ast(val%)
1204264c 464 IF NOT FNis_fn(val%) THEN ERROR &40E8091A, "Not a function"
7f6d61fc 465=FNref_local(Z%(val%,1))
1204264c 466
9462d98d 467DEF FNfn_params(val%)
1204264c 468 IF NOT FNis_fn(val%) THEN ERROR &40E8091A, "Not a function"
7f6d61fc 469=FNref_local(Z%(val%,2))
1204264c
BH
470
471DEF FNfn_env(val%)
472 IF NOT FNis_fn(val%) THEN ERROR &40E8091A, "Not a function"
7f6d61fc 473=FNref_local(Z%(val%,3))
1204264c 474
940092c6
BH
475REM ** Atoms **
476
477REM Z%(x,1) = index in Z% of current referent
478
479DEF FNis_atom(val%)
480=FNtype_of(val%) = 12
481
482DEF FNalloc_atom(contents%)
483 LOCAL val%
484 val% = FNmalloc(12)
485 Z%(val%,1) = contents%
486=val%
487
488DEF PROCgc_mark_atom(val%)
489 PROCgc_mark(Z%(val%,1))
490ENDPROC
491
492DEF FNatom_deref(val%)
493=FNref_local(Z%(val%,1))
494
495DEF PROCatom_reset(val%, contents%)
496 Z%(val%,1) = contents%
497ENDPROC
498
dbe45187
BH
499REM ** Environments **
500
6d1a36e8
BH
501REM Z%(x,1) = index in Z% of hash-map
502REM Z%(x,2) = index in Z% of outer environment
503
dbe45187
BH
504DEF FNis_environment(val%)
505=FNtype_of(val%) = 13
506
507DEF FNalloc_environment(outer%)
508 LOCAL val%
7b6e74a6 509 val% = FNmalloc(13)
4b811acc
BH
510 Z%(val%,1) = FNempty_hashmap
511 Z%(val%,2) = outer%
dbe45187
BH
512=val%
513
7b6e74a6
BH
514DEF PROCgc_mark_environment(val%)
515 PROCgc_mark(Z%(val%,1))
516 PROCgc_mark(Z%(val%,2))
517ENDPROC
518
dbe45187
BH
519DEF FNenvironment_data(val%)
520 IF NOT FNis_environment(val%) THEN ERROR &40E8091D, "Not an environment"
7f6d61fc 521=FNref_local(Z%(val%,1))
dbe45187
BH
522
523DEF PROCenvironment_set_data(val%, data%)
524 IF NOT FNis_environment(val%) THEN ERROR &40E8091D, "Not an environment"
4b811acc 525 Z%(val%,1) = data%
dbe45187
BH
526ENDPROC
527
528DEF FNenvironment_outer(val%)
529 IF NOT FNis_environment(val%) THEN ERROR &40E8091D, "Not an environment"
7f6d61fc 530=FNref_local(Z%(val%,2))
dd3d5ad7
BH
531
532REM Local Variables:
533REM indent-tabs-mode: nil
534REM End: