-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtypes.qky
More file actions
204 lines (141 loc) · 5.62 KB
/
types.qky
File metadata and controls
204 lines (141 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
[ $ 'lookup-table.qky' ALEx-load ] now!
[ this ] is types.qky
( ------ Polimorphism ------
Copyright (C) 2021 Aleksander "olus2000" Sabak
https://github.com/olus2000 )
( This extension depends on the "lookup-table.qky" to be loaded.
This extension supports a simple object type system and polimorphic
words. Each type is identified by a type identifier and all objects
of a type are nests with its type identifier at the beginning,
followed by object's fields.
Object structure:
[ type-id field1 field2 ... ]
Simple types make objects that should only ever be interpreted as
data and never executed. A type id of a simple type should be a
number generated by the builder `type`. Example of a simple type:
type is 2D-point
[ 2D-point 2 1 ] is point-A
[ 2D-point 3 7 ] is point-B
Action types make objects with some sort of behavior when executed.
Their type id should be a nest performing that action based on
object's fields. Most actions should end with `]done[` to prevent
execution of object's fields. The standard Quackery system defines
two great examples of action types: `table` and `stack`. Another
one can be found in the "lookup-table.qky" extension.
To facilitate polimorphism this extension provides a new kind of
words: generic. Generic words are first declared without any
behavior and later can be implemented separately for each type.
Each generic word takes an object as its top parameter and decides
which behavior will be executed based on the object's type.
Example usage of generic words can be seen at the end of the file.
# Glossary:
type ( [ $ -- [ $ ):
Builder. Adds the next free simple type id to the built nest and
increments the counter in `type.id`. Should be used for defining
simple types.
Example usage:
type is 2D-vector
[ 2D-vector 4 5 ] is vec1
type.id ( -- [ ):
A stack holding a number not yet used as a simple type identifier
when generating types with `type`.
generic ( [ $ -- [ $ ):
Builder. Takes a name that follows it and defines a new generic
word with that name and extendable behavior. The top parameter of
that word has to be an object and action performed by a generic
word depends on the type of the object passed to it.
Example usage:
generic magnitude-square
generic.action ( ..a obj [ generic.action actions-lut ] -- ..b ):
An action type identifier of generic words. It's action is to
dispatch the actions-lut on the object passed as the argument
and `do` the corresponding action or `bail` if no action is
associated with the object's type.
<generic> ( -- new-generic ):
A constructor for objects representing generic words.
generic? ( obj -- ? ):
A predicate for generic words. Returns `true` if the object's type
id is `generic.action` and `false` otherwise. Returns `false` if
not passed a nest.
behavior ( [ $ -- [ $ ):
Builder. Expects three values to have already been built:
an action, a type and a generic word. It sets the behavior of the
generic word when acting on the type to be the action.
Example usage:
[ dup 1 peek 2 **
swap 2 peek 2 ** + ] 2D-vector magnitude-square behavior
lut.set-at ( value key lut -- ):
set-at
`set-at` for lookup tables is redefined to be a generic word with
behavior defined for lookup tables. )
[ stack 0 ] is type.id ( -- [ )
[ over
type.id share
swap put
1 type.id tally ] builds type ( [ $ -- [ $ )
[ dup nest? not if
[ $ 'Non-nest passed to a generic word.'
message put bail ]
dup 0 peek
]'[ do not if
[ $ 'Generic word undefined for this type.'
message put bail ]
do ]done[ ] is generic.action
( ..a obj [ generic.action actions-lut ] -- ..b )
[ ' generic.action nested
<lut> nested join ] is <generic> ( -- new-generic )
[ dup nest? not iff
[ drop false ] done
0 peek ' generic.action oats ] is generic? ( obj -- ? )
[ dup $ '' = if
[ $ '`generic` needs a name after it.'
message put bail ]
nextword dup
name? if
[ dup build share
generic? iff
drop ]done[ ]
( the following is literally code copied from `is`
if tere only was a way of reusing builder functionality... )
nested
namenest take
join
namenest put
<generic>
actiontable take
1 stuff
actiontable put ] builds generic ( [ $ -- [ $ )
[ over size 3 < if
[ $ '`behavior` needs a behavior, a type and a generic word.'
message put
bail ]
over dup take
over take
rot take
swap rot 1 peek
set-at ] builds behavior ( [ $ -- [ $ )
( ----- set-at as a generic ----- )
set-at is lut.set-at ( value key lut -- )
generic set-at
lut.set-at lut set-at behavior
( ----- example ----- )
[ drop $ '' ] now! ( comment this line to run the example code )
type is pair ( [ pair a b ] )
type is triple ( [ triple a b c ] )
generic sum ( obj -- n )
[ dup 1 peek
swap 2 peek + ] pair sum behavior ( pair -- n )
[ dup 1 peek
over 2 peek +
swap 3 peek + ] triple sum behavior ( triple -- n )
[ 0 over size
1 - times
[ over i
swap do + ]
nip ] table sum behavior ( table -- n )
say "The sum of pair 2 and 7 is "
' [ pair 2 7 ] sum echo cr
say "The sum of a triple 1, 4 and 6 is "
' [ triple 1 4 6 ] sum echo cr
say "The sum of a table of odd numbers less than 10 is "
' [ table 1 3 5 7 9 ] sum echo cr