bbc-basic: Use distinct error numbers for all errors.
[jackhill/mal.git] / bbc-basic / types
1 REM Types library for mal in BBC BASIC
2
3 REM This library should be the only thing that understands the
4 REM implementation of mal data types in BBC BASIC. All other
5 REM code should use routines in this library to access them.
6
7 REM As far as other code is concerned, a mal object is just an
8 REM opaque 32-bit integer, which might be a pointer, or might not.
9
10 REM Following the 8-bit BASIC implementation, we currently have two
11 REM arrays, Z%() containing most objects and S$() containing strings
12 REM (referenced from Z%()).
13
14 REM See ../basic/mem.in.bas for data representations.
15
16 DEF PROCtypes_init
17 LOCAL i%
18 DIM Z%(10000), S$(10000)
19 FOR i% = 0 TO 15
20 Z%(i%) = 0
21 NEXT i%
22 Z%(0) = 32: REM nil
23 Z%(2) = 1+32: REM false
24 Z%(4) = 1+32: Z%(5) = 1: REM true
25 Z%(6) = 6+32: REM empty list
26 Z%(12) = 8+32: REM empty hashmap
27 next_Z% = 16
28 next_S% = 0
29 ENDPROC
30
31 DEF FNtype_of(val%)
32 =Z%(val%) AND 31
33
34 DEF FNmalloc(size%)
35 LOCAL val%
36 val% = next_Z%
37 next_Z% += size%
38 =val%
39
40 REM ** Nil **
41
42 DEF FNis_nil(val%)
43 =FNtype_of(val%) = 0
44
45 DEF FNnil
46 =0
47
48 REM ** Boolean **
49
50 DEF FNis_boolean(val%)
51 =FNtype_of(val%) = 1
52
53 DEF FNalloc_boolean(bval%)
54 IF bval% THEN =4
55 =2
56
57 DEF FNunbox_boolean(val%)
58 IF NOT FNis_boolean(val%) THEN ERROR &40E80911, "Not a boolean"
59 IF Z%(val% + 1) THEN =TRUE
60 =FALSE
61
62 REM ** Integers **
63
64 DEF FNis_int(val%)
65 =FNtype_of(val%) = 2
66
67 DEF FNalloc_int(ival%)
68 LOCAL val%
69 val% = FNmalloc(2)
70 Z%(val%) = 2+32
71 Z%(val% + 1) = ival%
72 =val%
73
74 DEF FNunbox_int(val%)
75 IF NOT FNis_int(val%) THEN ERROR &40E80912, "Not an integer"
76 =Z%(val% + 1)
77
78 REM ** Symbols **
79
80 DEF FNis_symbol(val%)
81 =FNtype_of(val%) = 5
82
83 DEF FNalloc_symbol(sval$)
84 LOCAL s%
85 s% = next_S%
86 next_S% += 1
87 S$(s%) = sval$
88 val% = FNmalloc(2)
89 Z%(val%) = 5+32
90 Z%(val% + 1) = s%
91 =val%
92
93 DEF FNunbox_symbol(val%)
94 IF NOT FNis_symbol(val%) THEN ERROR &40E80915, "Not a symbol"
95 =S$(Z%(val% + 1))
96
97 REM ** Lists **
98
99 DEF FNempty
100 =6
101
102 DEF FNalloc_pair(car%, cdr%)
103 LOCAL val%
104 val% = FNmalloc(3)
105 Z%(val% + 0) = 6+32
106 Z%(val% + 2) = car%
107 Z%(val% + 1) = cdr%
108 =val%
109
110 DEF FNis_empty(val%)
111 =val% = FNempty
112
113 DEF FNis_list(val%)
114 =FNtype_of(val%) = 6
115
116 DEF FNlist_car(val%)
117 IF NOT FNis_list(val%) THEN ERROR &40E80916, "Can't get car of non-list"
118 IF Z%(val% + 1) = 0 THEN ERROR &40E80920, "Can't get car of empty list"
119 =Z%(val% + 2)
120
121 DEF FNlist_cdr(val%)
122 IF NOT FNis_list(val%) THEN ERROR &40E80916, "Can't get cdr of non-list"
123 IF Z%(val% + 1) = 0 THEN ERROR &40E80920, "Can't get cdr of empty list"
124 =Z%(val% + 1)
125
126 DEF FNlist_len(val%)
127 LOCAL i%
128 WHILE NOT FNis_empty(val%)
129 val% = FNlist_cdr(val%)
130 i% += 1
131 ENDWHILE
132 = i%
133
134 DEF PROClist_to_array(val%, a%())
135 REM a%() must already be correctly dimensioned.
136 LOCAL i%
137 WHILE NOT FNis_empty(val%)
138 a%(i%) = FNlist_car(val%)
139 val% = FNlist_cdr(val%)
140 i% += 1
141 ENDWHILE
142 ENDPROC
143
144 REM ** Core functions **
145
146 DEF FNis_corefn(val%)
147 =FNtype_of(val%) = 9
148
149 DEF FNalloc_corefn(fn%)
150 LOCAL val%
151 val% = FNmalloc(2)
152 Z%(val% + 0) = 32 + 9
153 Z%(val% + 1) = fn%
154 =val%
155
156 DEF FNunbox_corefn(val%)
157 IF NOT FNis_corefn(val%) THEN ERROR &40E80919, "Not a core function"
158 =Z%(val% + 1)
159
160 REM ** Hashmaps **
161
162 REM To defer implementing mal strings for a bit, hashmap keys are
163 REM currently BASIC strings rather than arbitrary values.
164
165 DEF FNempty_hashmap
166 =12
167
168 DEF FNalloc_hashmap_entry(key$, val%, next%)
169 LOCAL entry%
170 LOCAL s%
171 s% = next_S%
172 next_S% += 1
173 S$(s%) = key$
174 entry% = FNmalloc(4)
175 Z%(entry% + 0) = 32 + 8
176 Z%(entry% + 1) = next%
177 Z%(entry% + 2) = s%
178 Z%(entry% + 3) = val%
179 =entry%
180
181 DEF FNis_hashmap(val%)
182 =FNtype_of(val%) = 8
183
184 DEF FNhashmap_get(map%, key$)
185 IF NOT FNis_hashmap(map%) THEN ERROR &40E80918, "Can't get item from a non-hashmap"
186 IF map% = FNempty_hashmap THEN =FNnil
187 IF S$(Z%(map% + 2)) = key$ THEN =Z%(map% + 3)
188 =FNhashmap_get(Z%(map% + 1), key$)