trac_lang 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ ))'
@@ -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
+ ))'
@@ -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
+ ))'