trac_lang 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +58 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/README.trl +633 -0
- data/examples/golf.trl +113 -0
- data/examples/list.trl +125 -0
- data/examples/math.trl +351 -0
- data/examples/meta.trl +254 -0
- data/examples/ratio.trl +107 -0
- data/examples/struct.trl +176 -0
- data/examples/term.trl +129 -0
- data/examples/util.trl +366 -0
- data/exe/trac_lang +74 -0
- data/lib/trac_lang.rb +16 -0
- data/lib/trac_lang/bindings.rb +53 -0
- data/lib/trac_lang/block.rb +46 -0
- data/lib/trac_lang/decimal.rb +79 -0
- data/lib/trac_lang/dispatch.rb +421 -0
- data/lib/trac_lang/executor.rb +97 -0
- data/lib/trac_lang/expression.rb +58 -0
- data/lib/trac_lang/form.rb +253 -0
- data/lib/trac_lang/immediate_read.rb +52 -0
- data/lib/trac_lang/octal.rb +88 -0
- data/lib/trac_lang/parser.rb +114 -0
- data/lib/trac_lang/version.rb +4 -0
- data/trac_lang.gemspec +42 -0
- metadata +177 -0
data/examples/meta.trl
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
|
2
|
+
Meta Programming
|
3
|
+
|
4
|
+
#(PS,Loading examples/meta.trl...)'
|
5
|
+
|
6
|
+
Anonymous forms
|
7
|
+
---------------
|
8
|
+
|
9
|
+
We want to be able different classes of anonymous forms. The following does
|
10
|
+
that, creating a class of anonymous forms with the given name. We first save a
|
11
|
+
special prefix for the name of all the forms in the class. Next we create the
|
12
|
+
creating form, that will create members of the class. It creates the member
|
13
|
+
and then returns its name. We can use the name like a function pointer to call
|
14
|
+
the form wherever we want. We next create a test form, that checks if the
|
15
|
+
given name is a name of a form in our class. We both test if the name is the
|
16
|
+
name of a form and if the name starts with the form prefix. The last form we
|
17
|
+
create is for cleaning up. When called it will delete all the forms of the
|
18
|
+
given class.
|
19
|
+
|
20
|
+
#(DS,anonymous,(
|
21
|
+
#(DS,name-prefix,name..)
|
22
|
+
#(DS,name,(
|
23
|
+
#(DS,name-seq,#(AD,1,#(name-seq)))
|
24
|
+
#(DS,#(name-prefix)#(name-seq),(value))
|
25
|
+
#(SS,#(name-prefix)#(name-seq),args)
|
26
|
+
#(name-prefix)#(name-seq)
|
27
|
+
))
|
28
|
+
#(SS,name,args,value)
|
29
|
+
#(DS,name-current,(#(name-prefix)#(name-seq)))
|
30
|
+
#(DS,name?,(
|
31
|
+
#(and,(exists?,(obj)),(starts-with,(obj),#(name-prefix)),(T),(F))
|
32
|
+
))
|
33
|
+
#(SS,name?,obj,T,F)
|
34
|
+
#(DS,name-gc,(
|
35
|
+
#(DD,#(list-prefix,#(name-prefix),(,)))
|
36
|
+
))
|
37
|
+
))
|
38
|
+
#(sss,anonymous,name)'
|
39
|
+
|
40
|
+
We'll use lambda to define anonymous functions.
|
41
|
+
|
42
|
+
#(anonymous,lambda)'
|
43
|
+
|
44
|
+
It can be used as follows:
|
45
|
+
(
|
46
|
+
#(lambda,n,(#(AD,1,n))) => successor function
|
47
|
+
|
48
|
+
#(lambda?,x,if-so,if-not) => test if something is a lambda
|
49
|
+
|
50
|
+
#(lambda-gc) => deletes all lambdas
|
51
|
+
)
|
52
|
+
|
53
|
+
We'll use data to define anonymous pieces of data.
|
54
|
+
|
55
|
+
#(anonymous,data)'
|
56
|
+
|
57
|
+
We use it similarly to lambda:
|
58
|
+
(
|
59
|
+
#(data,(,),(1,2,3,4)) => array of numbers
|
60
|
+
|
61
|
+
#(data?,formname,if-so,if-not) => test if something is a data form
|
62
|
+
|
63
|
+
#(data-gc) => delete all data forms
|
64
|
+
)
|
65
|
+
|
66
|
+
Curry
|
67
|
+
-----
|
68
|
+
|
69
|
+
Currying is to take a script that takes multiple arguments, fill in some of its
|
70
|
+
arguments and return a script that will take the rest of the necessary
|
71
|
+
arguments and can be called at a later time.
|
72
|
+
|
73
|
+
#(DS,curry,(#(lambda,(<y>),##(CL,<f>,<x>,<y>))))
|
74
|
+
#(SS,curry,<f>,<x>)'
|
75
|
+
|
76
|
+
Usage:
|
77
|
+
(
|
78
|
+
#(DS,func,#(curry,multi-arg-func,(a,b,c)))
|
79
|
+
...
|
80
|
+
#(#(func),(some,more,args))
|
81
|
+
|
82
|
+
Or another more concrete example:
|
83
|
+
|
84
|
+
We need to redefine primitives to curry them:
|
85
|
+
#(DS,mult,(#(ML,a,b)))
|
86
|
+
#(SS,mult,a,b)
|
87
|
+
|
88
|
+
Now we define something that prints an action for each number 1 through 10.
|
89
|
+
|
90
|
+
#(DS,1-10,(
|
91
|
+
#(EQ,count,10,,(
|
92
|
+
#(PS, #(action,#(AD,1,count)))
|
93
|
+
#(1-10,#(AD,1,count))
|
94
|
+
))
|
95
|
+
))
|
96
|
+
#(sss,1-10,(action,count))
|
97
|
+
|
98
|
+
Print ten even numbers
|
99
|
+
#(1-10,#(curry,mult,2))
|
100
|
+
|
101
|
+
Print ten multiples of five
|
102
|
+
#(1-10,#(curry,mult,5))
|
103
|
+
)
|
104
|
+
|
105
|
+
Currying doesn't have to be that formal with TRAC. Since TRAC is a string
|
106
|
+
language, you can define pieces of functions and then assemble them when
|
107
|
+
necessary. In the following, we define a sum function that takes two
|
108
|
+
lambdas, a test and a func.
|
109
|
+
|
110
|
+
#(DS,sum,(
|
111
|
+
#(#(test,start),total,(
|
112
|
+
#(sum,#(AD,1,start),test,func,#(AD,total,#(func,start)))
|
113
|
+
))
|
114
|
+
))
|
115
|
+
#(sss,sum,(start,test,func,total))'
|
116
|
+
|
117
|
+
You see here how we would sum up all the squares from one to five. Notice that
|
118
|
+
the first lambda is simply the first three arguments of the GR primitive. When
|
119
|
+
it is replaced in the definition of sum above, if GR is true, the running total
|
120
|
+
if returned, otherwise sum is called again. The GR primitive has been
|
121
|
+
effectively curried, changed into a test that takes a single argument.
|
122
|
+
(
|
123
|
+
#(sum,1,#(lambda,n,(GR,n,5)),#(lambda,n,(#(ML,n,n))))
|
124
|
+
)
|
125
|
+
|
126
|
+
|
127
|
+
Combinators
|
128
|
+
-----------
|
129
|
+
|
130
|
+
TRAC can't support real combinators, since it doesn't have truly anonymous
|
131
|
+
functions. But with a little creativity, you can create something that looks
|
132
|
+
an awful lot like the Y combinator, and does the same kind of work.
|
133
|
+
|
134
|
+
Of course these combinators aren't necessary for TRAC. TRAC already supports
|
135
|
+
recursion, so there's no need to create a script that can make scripts
|
136
|
+
recursive. However, this does show the power of TRAC and it also helps clear
|
137
|
+
up some of the mystique of these combinators.
|
138
|
+
|
139
|
+
U Combinator
|
140
|
+
------------
|
141
|
+
The U combinator takes as an argument the name of a script whose only argument
|
142
|
+
is a script name.
|
143
|
+
|
144
|
+
#(DS,U,(#(<f>,<f>)))
|
145
|
+
#(SS,U,<f>)'
|
146
|
+
|
147
|
+
Using the U combinator we can create a recursive function from a special sort
|
148
|
+
of function. Here's an example for calculating the factorial.
|
149
|
+
|
150
|
+
(
|
151
|
+
#(DS,fact,(
|
152
|
+
#(lambda,<n>,(
|
153
|
+
#(EQ,<n>,0,1,(
|
154
|
+
#(ML,<n>,#(#(U,<f>),#(SU,<n>,1)))
|
155
|
+
))
|
156
|
+
))
|
157
|
+
))
|
158
|
+
#(sss,fact,<f>)
|
159
|
+
#(DS,!,(#(#(U,fact),<n>)))
|
160
|
+
#(SS,!,<n>)
|
161
|
+
)
|
162
|
+
|
163
|
+
How does the U combinator work? If you turn trace on and trace through the
|
164
|
+
operation of ! you get a good idea of what's happening. The "fact" script
|
165
|
+
creates an anonymous function which does the work of testing the end condition
|
166
|
+
and setting up the multiplication necessary. The U combinator passes a script
|
167
|
+
to itself, allowing it to call itself without direct recursion. The doubling
|
168
|
+
in the U combinator is what makes the recursion work. It passes a copy of the
|
169
|
+
script to be called to itself.
|
170
|
+
|
171
|
+
The U combinator must be called inside the script that's being made recursive,
|
172
|
+
which isn't as neat as we would like. If we factor the U combinator out of
|
173
|
+
our script, we get the famous Y combinator.
|
174
|
+
|
175
|
+
|
176
|
+
Y Combinator
|
177
|
+
------------
|
178
|
+
|
179
|
+
This version of the Y combinator uses the U combinator in its definition. Pass
|
180
|
+
in a script that takes a single argument and returns the name of a script.
|
181
|
+
Looking at it this way and comparing it to the factorial definition above helps
|
182
|
+
make it understandable. The outer U corresponds to the U in the definition of
|
183
|
+
!, while the inner U corresponds to the U in the definition of fact. The
|
184
|
+
definition of an anonymous function is necessary because that's the only real
|
185
|
+
way TRAC has of passing a function around as an object. As a result this
|
186
|
+
creates a lot of identical anonymous functions.
|
187
|
+
|
188
|
+
#(DS,Y,(
|
189
|
+
#(U,(
|
190
|
+
#(lambda,<x>,(
|
191
|
+
#(<f>,(#(U,<x>)))
|
192
|
+
))
|
193
|
+
))
|
194
|
+
))
|
195
|
+
#(sss,Y,<f>)'
|
196
|
+
|
197
|
+
|
198
|
+
Y Combinator w/o U
|
199
|
+
------------------
|
200
|
+
|
201
|
+
Here's the closest TRAC is going to get to having a real Y combinator. We take
|
202
|
+
the version of Y above and expand the definition of the U combinator. If
|
203
|
+
you've seen the Y combinator in other forms, you will see the resemblance here.
|
204
|
+
|
205
|
+
#(DS,Y,(
|
206
|
+
#(#(lambda,<x>,(
|
207
|
+
#(<f>,(#(<x>,<x>)))
|
208
|
+
)),#(lambda,<x>,(
|
209
|
+
#(<f>,(#(<x>,<x>)))
|
210
|
+
))
|
211
|
+
)
|
212
|
+
))
|
213
|
+
#(sss,Y,<f>)'
|
214
|
+
|
215
|
+
What's interesting is how the Y combinator is thought of as an airy abstraction
|
216
|
+
in computing, but here it's being defined in a language which is nothing but
|
217
|
+
strings, which is about as concrete as you can get.
|
218
|
+
|
219
|
+
Here's how you would create the factorial function using the Y combinator. We
|
220
|
+
create a script that takes an anonymous function as an argument and returns a
|
221
|
+
new anonymous function. The anonymous function returned takes a single integer
|
222
|
+
argument and returns one if the input was zero, otherwise multiplies the input
|
223
|
+
by the output of the script passed in.
|
224
|
+
|
225
|
+
(
|
226
|
+
#(DS,fact,(
|
227
|
+
#(lambda,<n>,(
|
228
|
+
#(EQ,<n>,0,1,(
|
229
|
+
#(ML,<n>,#(<f>,#(SU,<n>,1)))
|
230
|
+
))
|
231
|
+
))
|
232
|
+
))
|
233
|
+
#(sss,fact,<f>)
|
234
|
+
#(DS,!,(#(#(Y,fact),<n>)))
|
235
|
+
#(SS,!,<n>)
|
236
|
+
)
|
237
|
+
|
238
|
+
Here's how you would count characters in a form using the Y combinator.
|
239
|
+
|
240
|
+
(
|
241
|
+
#(DS,count,(
|
242
|
+
#(lambda,<s>,(
|
243
|
+
#(EQ,#(CC,<s>,**),**,0,(
|
244
|
+
#(AD,1,#(<f>,<s>))
|
245
|
+
))
|
246
|
+
))
|
247
|
+
))
|
248
|
+
#(sss,count,<f>)
|
249
|
+
#(DS,*,(#(#(Y,count),<s>)))
|
250
|
+
#(SS,*,<s>)
|
251
|
+
)
|
252
|
+
|
253
|
+
#(PS,(success!
|
254
|
+
))'
|
data/examples/ratio.trl
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
|
2
|
+
Definition of rational numbers
|
3
|
+
|
4
|
+
#(PS,Loading examples/ratio.trl...)'
|
5
|
+
|
6
|
+
Basics
|
7
|
+
-------------------------------------------------------------------------------
|
8
|
+
|
9
|
+
The format of our rational numbers will be:
|
10
|
+
|
11
|
+
numerator/denominator
|
12
|
+
|
13
|
+
To work with them we will need a way of extracting the numerator and
|
14
|
+
denominator from them. So we need our anonymous data form. We use the default
|
15
|
+
values for the segment form to make 0/1 the default rational. The default
|
16
|
+
denominator of one also means that whole numbers will be interpreted correctly,
|
17
|
+
and so can be mixed in with rationals without a problem.
|
18
|
+
|
19
|
+
#(DS,numr,(#(segment,1,#(data,/,x),0)))
|
20
|
+
#(SS,numr,x)'
|
21
|
+
|
22
|
+
#(DS,dnmr,(#(segment,2,#(data,/,x),1)))
|
23
|
+
#(SS,dnmr,x)'
|
24
|
+
|
25
|
+
Often we are going to need to pass a rational into a form as a separate
|
26
|
+
numerator and denominator. So it will be useful to have a function that
|
27
|
+
changes a rational into a set of two parameters. The following will replace
|
28
|
+
the slash in a rational with a comma. I could use CL to call the data and
|
29
|
+
put the comma in the space between segments, but that wouldn't give me my
|
30
|
+
default denominator of one which makes it possible to mix rationals and
|
31
|
+
integers. Also notice that I use the data-current call here, so that I don't
|
32
|
+
have to duplicate the creation of the temporary data form.
|
33
|
+
|
34
|
+
#(DS,argr,(#(segment,1,#(data,/,x),0),#(segment,2,#(data-current),1)))
|
35
|
+
#(SS,argr,x)'
|
36
|
+
|
37
|
+
Next we need a way of reducing fractions to standard form.
|
38
|
+
|
39
|
+
First we have something to reduce the faction by a given value.
|
40
|
+
|
41
|
+
#(DS,reduceby,(#(DV,#(numr,x),by)/#(DV,#(dnmr,x),by)))
|
42
|
+
#(SS,reduceby,x,by)'
|
43
|
+
|
44
|
+
Next we have a function to normalize the sign of a fraction. Notice that if
|
45
|
+
the denominator is zero, we change the fraction to 0/0, our form for NaN.
|
46
|
+
|
47
|
+
#(DS,reducesign,(#(sgn?,denom,numer/denom,0/0,(#(-,numer)/#(-,denom)))))
|
48
|
+
#(SS,reducesign,numer,denom)'
|
49
|
+
|
50
|
+
We need a GCD that's works the same with negative and positive numbers.
|
51
|
+
|
52
|
+
#(DS,gcd+,(#(gcd,#(abs,x),#(abs,y))))
|
53
|
+
#(SS,gcd+,x,y)'
|
54
|
+
|
55
|
+
Finally we combine all these functions into one that will put all our rationals
|
56
|
+
in normal form.
|
57
|
+
|
58
|
+
#(DS,reduce,(#(reducesign,#(argr,#(reduceby,x,#(gcd+,#(argr,x)))))))
|
59
|
+
#(SS,reduce,x)'
|
60
|
+
|
61
|
+
|
62
|
+
Arithmetic
|
63
|
+
-------------------------------------------------------------------------------
|
64
|
+
|
65
|
+
#(DS,[addr],(#(reduce,#(op,#(ML,<a>,<d>),#(ML,<b>,<c>))/#(ML,<b>,<d>))))
|
66
|
+
#(SS,[addr],op,<a>,<b>,<c>,<d>)'
|
67
|
+
|
68
|
+
#(DS,addr,(#([addr],AD,#(argr,x),#(argr,y))))
|
69
|
+
#(SS,addr,x,y)'
|
70
|
+
|
71
|
+
#(DS,subr,(#([addr],SU,#(argr,x),#(argr,y))))
|
72
|
+
#(SS,subr,x,y)'
|
73
|
+
|
74
|
+
#(DS,mulr,(#(reduce,#(ML,#(numr,x),#(numr,y))/#(ML,#(dnmr,x),#(dnmr,y)))))
|
75
|
+
#(SS,mulr,x,y)'
|
76
|
+
|
77
|
+
#(DS,divr,(#(reduce,#(ML,#(numr,x),#(dnmr,y))/#(ML,#(numr,y),#(dnmr,x)))))
|
78
|
+
#(SS,divr,x,y)'
|
79
|
+
|
80
|
+
#(DS,rcpr,(#(dnmr,x)/#(numr,x)))
|
81
|
+
#(SS,rcpr,x)'
|
82
|
+
|
83
|
+
|
84
|
+
Tests
|
85
|
+
-------------------------------------------------------------------------------
|
86
|
+
|
87
|
+
#(DS,grr,(#(GR,#(ML,#(numr,x),#(dnmr,y)),#(ML,#(numr,y),#(dnmr,x)),(T),(F))))
|
88
|
+
#(SS,grr,x,y,T,F)'
|
89
|
+
|
90
|
+
#(DS,eqnr,(#(and,(eqn,#(numr,x),#(numr,y)),(eqn,#(dnmr,x),#(dnmr,y)),(T),(F))))
|
91
|
+
#(SS,eqnr,x,y,T,F)'
|
92
|
+
|
93
|
+
#(DS,NaN?,(#(eqn,#(dnmr,x),0,(T),(F))))
|
94
|
+
#(SS,NaN?,x,T,F)'
|
95
|
+
|
96
|
+
|
97
|
+
Now we can use rational numbers, and even mix integers in with them.
|
98
|
+
(
|
99
|
+
#(subr,1,1/3) => 2/3
|
100
|
+
#(mulr,5/7,14) => 10/1
|
101
|
+
#(grr,5/7,2/3,true,false) => true
|
102
|
+
#(rcpr,2) => 1/2
|
103
|
+
)
|
104
|
+
|
105
|
+
|
106
|
+
#(PS,(success!
|
107
|
+
))'
|
data/examples/struct.trl
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
|
2
|
+
Data Structures
|
3
|
+
|
4
|
+
#(PS,Loading examples/struct.trl...)'
|
5
|
+
|
6
|
+
Stack
|
7
|
+
-----
|
8
|
+
|
9
|
+
Here's an implementation of a stack in TRAC.
|
10
|
+
|
11
|
+
Our first definition is push. The complicated expression at the start is
|
12
|
+
checking if our stack exists yet or if it is empty. If either is true, set the
|
13
|
+
stack to the value given. Otherwise, add the value to the top of the stack.
|
14
|
+
Because of the error checking, we don't have to create a stack before using it.
|
15
|
+
We can just push something onto it to create it.
|
16
|
+
|
17
|
+
#(DS,push,(
|
18
|
+
#(or,(not,(exists?,name)),(EQ,##(CL,name),),(
|
19
|
+
#(DS,name,(value))
|
20
|
+
),(
|
21
|
+
#(DS,name,(value)#(.)##(CL,name,#(.)))
|
22
|
+
))
|
23
|
+
#(SS,name,#(.))
|
24
|
+
))
|
25
|
+
#(sss,push,(name,value))'
|
26
|
+
|
27
|
+
Now, pop a value off the top of the stack. If the stack is empty, return the
|
28
|
+
ifempty parameter. Here we see the form pointer in action. The CS call moves
|
29
|
+
the form pointer after the end of the first segment, so the CL call in the next
|
30
|
+
line only returns the rest of the stack. This works because DS and SS return
|
31
|
+
the empty string, so the return from CS is the return from the pop function.
|
32
|
+
If the stack doesn't exist, this will have the side-effect of creating an empty
|
33
|
+
stack.
|
34
|
+
|
35
|
+
#(DS,pop,(
|
36
|
+
#(CS,name,ifempty)
|
37
|
+
#(DS,name,##(CL,name,#(.)))
|
38
|
+
#(SS,name,#(.))
|
39
|
+
))
|
40
|
+
#(sss,pop,(name,ifempty))'
|
41
|
+
|
42
|
+
Finally, peek at the top of the stack without removing the value.
|
43
|
+
|
44
|
+
#(DS,peek,(
|
45
|
+
#(CS,name,ifempty)
|
46
|
+
#(CR,name)
|
47
|
+
))
|
48
|
+
#(sss,peek,(name,ifempty))'
|
49
|
+
|
50
|
+
|
51
|
+
Queue
|
52
|
+
-----
|
53
|
+
|
54
|
+
A queue is very similar to a stack, we just add things to the end of the string
|
55
|
+
instead of the beginning. Dequeue is just a synonym for pop, and peek works on
|
56
|
+
both stacks and queues.
|
57
|
+
|
58
|
+
#(DS,enqueue,(
|
59
|
+
#(or,(not,(exists?,name)),(EQ,##(CL,name),),(
|
60
|
+
#(DS,name,(value))
|
61
|
+
),(
|
62
|
+
#(DS,name,##(CL,name,#(.))#(.)(value))
|
63
|
+
))
|
64
|
+
#(SS,name,#(.))
|
65
|
+
))
|
66
|
+
#(sss,enqueue,(name,value))'
|
67
|
+
|
68
|
+
#(DS,dequeue,##(CL,pop,name,ifempty))
|
69
|
+
#(SS,dequeue,name,ifempty)'
|
70
|
+
|
71
|
+
|
72
|
+
Hash
|
73
|
+
----
|
74
|
+
|
75
|
+
A hash piggybacks on the form dictionary. The hash name and key are mashed
|
76
|
+
together to make the name of the form that holds the corresponding value. From
|
77
|
+
this we can easily define all the typical hash methods.
|
78
|
+
|
79
|
+
#(DS,put,(#(DS,name:key,value)))
|
80
|
+
#(SS,put,name,key,value)'
|
81
|
+
|
82
|
+
#(DS,get,(#(name:key)))
|
83
|
+
#(SS,get,name,key)'
|
84
|
+
|
85
|
+
#(DS,contains,(#(exists?,name:key,(T),(F))))
|
86
|
+
#(SS,contains,name,key,T,F)'
|
87
|
+
|
88
|
+
#(DS,keys,(#(list-prefix,name:,delimiter)))
|
89
|
+
#(SS,keys,name,delimiter)'
|
90
|
+
|
91
|
+
#(DS,delete-hash,(#(DD,#(list-prefix,name:,(,)))))
|
92
|
+
#(SS,delete-hash,name)'
|
93
|
+
|
94
|
+
An array is just a special kind of hash that uses numbers for keys, and a set
|
95
|
+
is just a hash where you ignore the values set.
|
96
|
+
|
97
|
+
There's one other trick you can do with hashes. By creating the following two
|
98
|
+
functions, you can make a hash into a primitive object, with methods as well as
|
99
|
+
data.
|
100
|
+
|
101
|
+
#(DS,define,(
|
102
|
+
#(DS,name:method,(action))
|
103
|
+
#(SS,name:method,args)
|
104
|
+
))
|
105
|
+
#(sss,define,(name,method,args))'
|
106
|
+
|
107
|
+
#(DS,call,(#(CL,name:method,args)))
|
108
|
+
#(SS,call,name,method,args)'
|
109
|
+
|
110
|
+
A hash with such methods isn't a true object - it doesn't have a defining class
|
111
|
+
and there's no such thing as inheritance. But it's still an example of data
|
112
|
+
and functions bound together in one object.
|
113
|
+
|
114
|
+
|
115
|
+
Case
|
116
|
+
----
|
117
|
+
|
118
|
+
Here's the case statement, so TRAC can be routed to different actions based on
|
119
|
+
the value of something. It may seem strange to have a control structure in the
|
120
|
+
example file for data structures, but really, control structures *are* data
|
121
|
+
structures. The case statement is just a special kind of hash with a default
|
122
|
+
value.
|
123
|
+
|
124
|
+
#(DS,case,(#(DS,case-prefix,name)))
|
125
|
+
#(SS,case,name)'
|
126
|
+
|
127
|
+
#(DS,when,(
|
128
|
+
#(DS,#(case-prefix):(value),(action))
|
129
|
+
#(scrub,#(case-prefix):(value))
|
130
|
+
))
|
131
|
+
#(SS,when,value,action)'
|
132
|
+
|
133
|
+
#(DS,default,(
|
134
|
+
#(DS,#(case-prefix):default,(action))
|
135
|
+
#(sss,#(case-prefix):default,value)
|
136
|
+
))
|
137
|
+
#(sss,default,action)'
|
138
|
+
|
139
|
+
#(DS,end-case,(
|
140
|
+
#(DS,[end-case],(
|
141
|
+
#(DS,prefix,(
|
142
|
+
#(exists?,prefix:(value),(
|
143
|
+
#(prefix:(value))
|
144
|
+
),(
|
145
|
+
#(prefix:default,(value))
|
146
|
+
))
|
147
|
+
))
|
148
|
+
#(SS,prefix,value)
|
149
|
+
))
|
150
|
+
#(SS,[end-case],prefix)
|
151
|
+
#([end-case],#(case-prefix))
|
152
|
+
))
|
153
|
+
#(sss,end-case,name)'
|
154
|
+
|
155
|
+
These are defining words - in other words, they define pieces of code, so they
|
156
|
+
should be used at the same level as you use DS and other defining words. The
|
157
|
+
example will make things more clear.
|
158
|
+
|
159
|
+
(
|
160
|
+
Defining the code to run:
|
161
|
+
|
162
|
+
#(case,execute-option)
|
163
|
+
#(when,d,(#(delete-file)))
|
164
|
+
#(when,g,(#(get-file)))
|
165
|
+
#(when,q,(#(HL)))
|
166
|
+
#(default,(#(PS,#(help-text))))
|
167
|
+
#(end-case)
|
168
|
+
|
169
|
+
Actually calling the code:
|
170
|
+
|
171
|
+
#(execute-option,#(RC))
|
172
|
+
)
|
173
|
+
|
174
|
+
|
175
|
+
#(PS,(success!
|
176
|
+
))'
|