1 \
=== sorted
-array
=== /
2 \
Here are a few utility functions
useful for creating
and maintaining
3 \ the
deftype* method tables
. The keys array is
kept in sorted order
,
4 \
and the
methods array is
maintained in parallel
so that
an index into
5 \ one
corresponds to an index
in the
other.
7 \
Search a sorted array
for key
, returning the
index of where
it was
8 \ found
. If key is
not in the
array, return the
index where
it would
10 : array-find
{ a
-length a
-addr key
-- index found
? }
11 0 a
-length
( start
end )
14 2dup + 2 / dup
( start
end middle
middle )
15 cells
a-addr
+ @ ( start
end middle mid-val )
17 drop rot
( end middle start )
21 drop swap
( middle end )
24 key
> if ( start end middle )
27 -rot
2drop dup
( middle middle )
31 cells
a-addr
+ @ key
=
34 \
Create a new array, one
cell in length
, initialized the
provided value
35 : new-array { value
-- array }
36 cell allocate throw value
over ! ;
38 \
Resize a heap
-allocated
array to be
one cell longer, inserting value
39 \ at
idx, and shifting the
tail of the
array as necessary. Returns the
40 \
(possibly
new) array address
41 : array-insert
{ old
-array-length old
-array idx value
-- array }
42 old
-array old
-array-length
1+ cells
resize throw
44 a idx cells
+ dup
cell+ old
-array-length
idx - cells
cmove>
50 \
=== deftype* -- protocol
-enabled structs
=== /
51 \
Each type has MalTypeType% struct allocated on
the stack, with
52 \
mutable fields pointing
to all
class-shared resources
, specifically
53 \
the data needed
to allocate new instances
, and the table of protocol
54 \
methods that have
been extended
to the type.
55 \
Use 'deftype*' to define
a new type, and 'new' to create
new
56 \ instances
of that type.
60 \
cell% field
ref-count \
Ha, right
.
64 cell% 2 * field
MalTypeType-struct
65 cell% field
MalTypeType-methods
66 cell% field
MalTypeType-method
-keys
67 cell% field
MalTypeType-method
-vals
68 cell% field
MalTypeType-name
-addr
69 cell% field
MalTypeType-name
-len
70 end-struct MalTypeType%
72 : new ( MalTypeType -- obj
)
73 dup
MalTypeType-struct 2@ %allocate
throw ( MalTypeType obj ) \ create
struct
74 dup
-rot
mal-type ! ( obj ) \ set
struct's type pointer to this type
77 : deftype* ( struct-align struct-len -- MalTypeType )
78 MalTypeType% %allot ( s-a s-l MalTypeType )
79 dup 2swap rot ( MalTypeType s-a s-l MalTypeType )
80 MalTypeType-struct 2! ( MalTypeType ) \ store struct info
81 dup MalTypeType-methods 0 swap ! ( MalTypeType )
82 dup MalTypeType-method-keys nil swap ! ( MalTypeType )
83 dup MalTypeType-method-vals nil swap ! ( MalTypeType )
84 dup MalTypeType-name-len 0 swap ! ( MalTypeType )
87 : deftype ( struct-align struct-len R:type-name -- )
88 parse-name { orig-name-addr name-len }
89 \ parse-name uses temporary space, so copy into dictionary stack:
90 here { name-addr } name-len allot
91 orig-name-addr name-addr name-len cmove
93 \ allot and initialize type structure
95 name-addr mt MalTypeType-name-addr !
96 name-len mt MalTypeType-name-len !
97 \ ." Defining " mt MalTypeType-name-addr @ mt MalTypeType-name-len @ type cr
98 mt name-addr name-len nextname 1 0 const-does> ;
100 : type-name ( mal-type )
101 dup MalTypeType-name-addr @ ( mal-type name-addr )
102 swap MalTypeType-name-len @ ( name-addr name-len )
105 MalType% deftype MalDefault
107 \ nil type and instance to support extending protocols to it
108 MalType% deftype MalNil
109 MalNil new constant mal-nil
111 : not-object? ( obj -- bool )
118 \ === protocol methods === /
120 \ Used by protocol methods to find the appropriate implementation of
121 \ themselves for the given object, and then execute that implementation.
122 : execute-method { obj pxt -- }
124 ." Refusing to invoke protocol fn '"
125 pxt >name name>string type
126 ." ' on non-object: " obj .
129 obj mal-type @ dup MalTypeType-methods 2@ swap ( type methods method-keys )
130 dup 0= if \ No protocols extended to this type; check for a default
131 2drop drop MalDefault MalTypeType-methods 2@ swap
134 pxt array-find ( type idx found? )
135 dup 0= if \ No implementation found for this method; check for a default
136 2drop drop MalDefault dup MalTypeType-methods 2@ swap
137 pxt array-find ( type idx found? )
142 pxt >name name>string type
143 ." ' extended to type '"
144 obj mal-type @ type-name type
149 cells swap MalTypeType-method-vals @ + @ ( xt )
153 \ Extend a type with a protocol method. This mutates the MalTypeType
154 \ object that represents the MalType being extended.
155 : extend-method* { type pxt ixt -- type }
156 type MalTypeType-methods 2@ swap ( methods method-keys )
157 dup 0= if \ no protocols extended to this type
159 1 type MalTypeType-methods !
160 pxt new-array type MalTypeType-method-keys !
161 ixt new-array type MalTypeType-method-vals !
163 pxt array-find { idx found? }
164 found? if \ overwrite
165 ." Warning: overwriting protocol method implementation"
166 type MalTypeType-method-vals @ idx cells + ixt !
168 type MalTypeType-methods dup @ 1+ dup rot ! ( new-count )
169 1- dup type MalTypeType-method-keys @ idx pxt array-insert ( old-count new-array )
170 type MalTypeType-method-keys ! ( old-count )
171 type MalTypeType-method-vals @ idx ixt array-insert ( new-array )
172 type MalTypeType-method-vals !
179 \ def-protocol-method pr-str ...can be written:
180 \ : pr-str ( obj -- str ) [ latestxt ] literal execute-method ;
181 : def-protocol-method ( "name" -- )
183 does> ( ??? obj xt-ref -- ??? )
186 : extend ( type -- type pxt <noname...>)
187 parse-name find-name name>int ( type pxt )
191 : ;; ( type pxt <noname...> -- type )
192 [compile] ; ( type pxt ixt )
197 \ These whole-protocol names are only needed for 'satisfies
?':
199 def-protocol-method pr-str
202 MalList IPrintable extend
203 ' pr
-str
:noname
drop s" <unprintable>" ; extend
-method*
206 drop s" <unprintable>" ;;
210 \
=== Mal types and protocols
=== /
212 def
-protocol-method conj
( obj this
-- this
)
213 def
-protocol-method assoc
( k v this
-- this
)
214 def
-protocol-method get
( not-found k this
-- value
)
215 def
-protocol-method mal= ( a b
-- bool )
216 def
-protocol-method as-native
( obj -- )
217 def
-protocol-method to-list
( obj -- mal-list
)
227 cell% field
MalList/count
228 cell% field
MalList/start
231 : here
>MalList ( old
-here
-- mal-list
)
232 here
over - { bytes
} ( old
-here
)
233 MalList new bytes
( old
-here
mal-list
bytes )
234 allocate throw dup { target
} over MalList/start ! ( old
-here
mal-list
)
235 bytes cell / over MalList/count
! ( old
-here
mal-list
)
236 swap target
bytes cmove ( mal-list
)
237 0 bytes - allot \ pop list
contents from dictionary
stack
242 extend conj
{ elem old
-list
-- list
}
243 old
-list
MalList/count
@ 1+ { new-count
}
244 new-count
cells allocate throw { new-start }
247 old
-list
MalList/start @ new-start cell+ new-count
1- cells cmove
251 new-count
over MalList/count
!
252 new-start over MalList/start ! ;;
255 MalList new 0 over MalList/count
! constant
MalList/Empty
258 cell% field
MalVector/list
263 MalVector/list
@ to-list
;;
267 cell% field
MalMap/list
270 MalMap new MalList/Empty over MalMap/list
! constant
MalMap/Empty
273 extend conj
( kv map
-- map
)
274 MalMap/list
@ \ get
list
275 over MalList/start @ cell+ @ swap conj \ add value
276 swap MalList/start @ @ swap conj \ add key
277 MalMap new dup -rot
MalMap/list ! \ put
back in map
279 extend assoc
( k v map
-- map
)
280 MalMap/list @ \ get
list
282 MalMap new dup -rot
MalMap/list ! \ put
back in map
284 extend get
{ not-found k map
-- value
}
286 dup MalList/start @ { start }
287 MalList/count
@ { count
}
293 start over cells + @ k m
= if
294 start swap cells + cell+ @ -1 \ found
it ( value
-1 )
302 \
Examples of extending existing
protocol methods to existing
type
304 extend conj
( obj this
-- this
)
306 extend
as-native
;; ( obj -- obj )
310 extend conj
( item
nil -- mal-list )
311 drop MalList/Empty conj
;;
312 extend
as-native
drop 0 ;;
317 cell% field
MalInt/int
320 : MalInt. { int -- mal-int }
321 MalInt new dup MalInt/int int swap ! ;
324 extend
mal= ( other this
-- bool )
325 over mal-type @ MalInt = if
326 MalInt/int @ swap MalInt/int @ =
331 extend
as-native
( mal-int -- int )
336 cell% field
MalSymbol/sym
-addr
337 cell% field
MalSymbol/sym
-len
338 cell% field
MalSymbol/meta
341 : MalSymbol. { str
-addr str
-len
-- mal-sym
}
342 MalSymbol new { sym
}
343 str
-addr sym
MalSymbol/sym
-addr
!
344 str
-len sym
MalSymbol/sym
-len
!
345 MalMap/Empty sym
MalSymbol/meta
!
348 : unpack
-sym
( mal-string -- addr len
)
349 dup MalSymbol/sym
-addr
@
350 swap MalSymbol/sym
-len
@ ;
353 extend
mal= ( other this
-- bool )
354 over mal-type @ MalSymbol = if
355 unpack
-sym rot
unpack-sym str
=
359 ' as-native ' unpack-sym extend
-method*
363 cell% field
MalKeyword/str
-addr
364 cell% field
MalKeyword/str
-len
367 : unpack-keyword
( mal-keyword
-- addr len
)
368 dup MalKeyword/str
-addr
@
369 swap MalKeyword/str
-len
@ ;
372 extend
mal= ( other this
-- bool )
373 over mal-type @ MalKeyword = if
374 unpack-keyword rot
unpack-keyword str
=
378 ' as-native ' unpack-keyword extend
-method*
381 : MalKeyword. { str
-addr str
-len
-- mal-keyword
}
382 MalKeyword new { kw
}
383 str
-addr kw
MalKeyword/str
-addr
!
384 str
-len kw
MalKeyword/str
-len
!
388 cell% field
MalString/str
-addr
389 cell% field
MalString/str
-len
392 : MalString. { str
-addr str
-len
-- mal-str
}
393 MalString new { str
}
394 str
-addr str
MalString/str
-addr
!
395 str
-len str
MalString/str
-len
!
398 : unpack-str
( mal-string -- addr len
)
399 dup MalString/str
-addr
@
400 swap MalString/str
-len
@ ;
403 extend
mal= ( other this
-- bool )
404 over mal-type @ MalString = if
405 unpack-str rot
unpack-str str
=
409 ' as-native ' unpack-str extend
-method*
415 cell% field
MalFn/meta
418 : MalFn. { xt
-- mal-fn }
421 MalMap/Empty mal-fn MalFn/meta
!
431 cell% field
SpecialOp/xt
435 SpecialOp new swap over SpecialOp/xt
! ;