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 \ === protocol methods === /
113 \ Used by protocol methods to find the appropriate implementation of
114 \ themselves for the given object, and then execute that implementation.
115 : execute-method { obj pxt -- }
116 obj mal-type @ dup MalTypeType-methods 2@ swap ( type methods method-keys )
117 dup 0= if \ No protocols extended to this type; check for a default
118 2drop drop MalDefault MalTypeType-methods 2@ swap
120 dup 0= if ." No protocols extended to this type or MalDefault" 1 throw endif
122 pxt array-find ( type idx found? )
123 dup 0= if \ No implementation found for this method; check for a default
124 2drop drop MalDefault dup MalTypeType-methods 2@ swap
125 dup 0= if ." No implementation found for this method, and no protocols extended to MalDefault" 1 throw endif
126 pxt array-find ( type idx found? )
131 pxt >name name>string type
132 ." ' extended to type '"
133 obj mal-type @ type-name type
138 cells swap MalTypeType-method-vals @ + @ ( xt )
142 \ Extend a type with a protocol method. This mutates the MalTypeType
143 \ object that represents the MalType being extended.
144 : extend-method* { type pxt ixt -- type }
145 type MalTypeType-methods 2@ swap ( methods method-keys )
146 dup 0= if \ no protocols extended to this type
148 1 type MalTypeType-methods !
149 pxt new-array type MalTypeType-method-keys !
150 ixt new-array type MalTypeType-method-vals !
152 pxt array-find { idx found? }
153 found? if \ overwrite
154 ." Warning: overwriting protocol method implementation"
155 type MalTypeType-method-vals @ idx cells + ixt !
157 type MalTypeType-methods dup @ 1+ dup rot ! ( new-count )
158 1- dup type MalTypeType-method-keys @ idx pxt array-insert ( old-count new-array )
159 type MalTypeType-method-keys ! ( old-count )
160 \ cr ." before: " MalList MalTypeType-method-vals @ @ . cr
161 type MalTypeType-method-vals @ idx ixt array-insert ( new-array )
162 type MalTypeType-method-vals !
163 \ cr ." after: " MalList MalTypeType-method-vals @ @ . cr
170 \ def-protocol-method pr-str ...can be written:
171 \ : pr-str ( obj -- str ) [ latestxt ] literal execute-method ;
172 : def-protocol-method ( "name" -- )
174 does> ( ??? obj xt-ref -- ??? )
177 : extend ( type -- type pxt <noname...>)
178 parse-name find-name name>int ( type pxt )
182 : ;; ( type pxt <noname...> -- type )
183 [compile] ; ( type pxt ixt )
188 \ These whole-protocol names are only needed for 'satisfies
?':
190 def-protocol-method pr-str
193 MalList IPrintable extend
194 ' pr
-str
:noname
drop s" <unprintable>" ; extend
-method*
197 drop s" <unprintable>" ;;
201 \
=== Mal types and protocols
=== /
204 cell% field
MalList/car
205 cell% field
MalList/cdr
208 : MalList/conj
{ val coll -- list
}
210 val over MalList/car
! ( list
)
211 coll over MalList/cdr
! ( list
)
215 cell% field
MalArray/count
216 cell% field
MalArray/start
219 : here
>MalArray ( old
-here
-- mal-array )
220 here
over - { bytes
} ( old
-here
)
221 MalArray new bytes
( old
-here
mal-array bytes
)
222 allocate throw dup { target
} over MalArray/start ! ( old
-here
mal-array )
223 bytes
cell / over MalArray/count
! ( old
-here
mal-array )
224 swap target
bytes cmove ( mal-array )
225 0 bytes - allot \ pop
array contents
from dictionary
stack
228 def
-protocol
-method conj
( obj this
-- this
)
229 def
-protocol
-method assoc
( k v this
-- this
)
230 def
-protocol
-method get
( not-found k this
-- value
)
231 def
-protocol
-method mal= ( a b
-- bool )
232 def
-protocol
-method as-native
( obj -- )
233 def
-protocol
-method invoke
( argv argc
mal-fn -- ... )
236 cell% field
MalVector/list
240 cell% field
MalMap/list
243 MalMap new mal-nil over MalMap/list
! constant
MalMap/Empty
246 extend conj
( kv map
-- map
)
247 MalMap/list
@ \ get
list
248 over MalList/cdr
@ MalList/car
@ conj \ add value
249 swap MalList/car
@ conj \ add key
250 MalMap new MalMap/list ! \ put
back in map
252 extend assoc
( k v map
-- map
)
253 MalMap/list @ \ get
list
255 MalMap new dup -rot
MalMap/list ! \ put
back in map
257 extend get
( not-found k map
-- value
)
259 MalMap/list @ \ get
list
262 swap MalList/car
@ k
mal= if
263 MalList/car
@ -1 \ found
it
275 \
Examples of extending existing protocol
methods to existing
type
277 extend conj
( obj this
-- this
)
279 extend
as-native
;; ( obj -- obj )
283 ' conj ' MalList/conj extend
-method*
284 extend
as-native
drop 0 ;;
288 ' conj ' MalList/conj extend
-method*
293 cell% field
MalInt/int
296 : MalInt. { int -- mal-int }
297 MalInt new dup MalInt/int int swap ! ;
300 extend
as-native
( mal-int -- int )
305 cell% field
MalSymbol/sym
-addr
306 cell% field
MalSymbol/sym
-len
307 cell% field
MalSymbol/meta
310 : MalSymbol. { str
-addr str
-len
-- mal-sym
}
311 MalSymbol new { sym
}
312 str
-addr sym
MalSymbol/sym
-addr
!
313 str
-len sym
MalSymbol/sym
-len
!
314 MalMap/Empty sym
MalSymbol/meta
!
317 : unpack
-sym
( mal-string -- addr len
)
318 dup MalSymbol/sym
-addr
@
319 swap MalSymbol/sym
-len
@ ;
322 extend
mal= ( other this
-- bool )
323 over mal-type @ MalSymbol = if
324 unpack
-sym rot
unpack-sym str
=
328 ' as-native ' unpack-sym extend
-method*
332 cell% field
MalKeyword/str
-addr
333 cell% field
MalKeyword/str
-len
336 : unpack-keyword
( mal-keyword
-- addr len
)
337 dup MalKeyword/str
-addr
@
338 swap MalKeyword/str
-len
@ ;
341 extend
mal= ( other this
-- bool )
342 over mal-type @ MalKeyword = if
343 unpack-keyword rot
unpack-keyword str
=
347 ' as-native ' unpack-keyword extend
-method*
348 extend invoke
{ argv argc kw
-- val }
349 argc
1 > if argv
cell+ @ else mal-nil endif \
not-found
355 : MalKeyword. { str
-addr str
-len
-- mal-keyword
}
356 MalKeyword new { kw
}
357 str
-addr kw
MalKeyword/str
-addr
!
358 str
-len kw
MalKeyword/str
-len
!
362 cell% field
MalString/str
-addr
363 cell% field
MalString/str
-len
366 : MalString. { str
-addr str
-len
-- mal-str
}
367 MalString new { str
}
368 str
-addr str
MalString/str
-addr
!
369 str
-len str
MalString/str
-len
!
372 : unpack-str
( mal-string -- addr len
)
373 dup MalString/str
-addr
@
374 swap MalString/str
-len
@ ;
377 extend
mal= ( other this
-- bool )
378 over mal-type @ MalString = if
379 unpack-str rot
unpack-str str
=
383 ' as-native ' unpack-str extend
-method*
389 cell% field
MalFn/meta
392 : MalFn. { xt
-- mal-fn }
395 MalMap/Empty mal-fn MalFn/meta
!
399 extend invoke
( ... mal-fn -- ... )
400 MalFn/xt
@ execute ;;