trac_lang 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/examples/term.trl ADDED
@@ -0,0 +1,129 @@
1
+
2
+ ANSI Escape Codes
3
+ -----------------
4
+
5
+ #(PS,Loading examples/term.trl...)'
6
+
7
+ Here are some basic ANSI escape codes for controlling text appearance.
8
+
9
+ First we define the codes for effects:
10
+
11
+ #(DS,define-effect,(
12
+ #(EQ,<number>,-END-,,(
13
+ #(DS,clr-#(CS,effect-list),<number>)
14
+ #(define-effect,#(CS,effect-list,-END-))
15
+ ))
16
+ ))
17
+ #(scrub,define-effect)
18
+ #(SS,define-effect,<number>)'
19
+
20
+ #(DS,effect-list,(
21
+ 0 reset
22
+ 1 bright
23
+ 2 dim
24
+ 4 underscore
25
+ 5 blink
26
+ 7 reverse
27
+ 8 hidden))'
28
+
29
+ #(SS,effect-list,( ),##(CL,return))'
30
+
31
+ Notice how I segment the effect-list by spaces and newlines. That makes it
32
+ easy to write readable lists in your code.
33
+
34
+ Discard first empty segment: #(CS,effect-list)'
35
+
36
+ #(define-effect,#(CS,effect-list))'
37
+
38
+ Next we define the codes for colors, foreground and background:
39
+
40
+ #(DS,define-color,(
41
+ #(EQ,<number>,-END-,,(
42
+ #(define-colors,#(CS,color-list),<number>)
43
+ #(define-color,#(CS,color-list,-END-))
44
+ ))
45
+ ))
46
+ #(scrub,define-color)
47
+ #(SS,define-color,<number>)'
48
+
49
+ #(DS,define-colors,(
50
+ #(DS,clr-<name>-fg,3<number>)
51
+ #(DS,clr-<name>-bg,4<number>)
52
+ ))
53
+ #(scrub,define-colors)
54
+ #(SS,define-colors,<name>,<number>)'
55
+
56
+ #(DS,color-list,(
57
+ 0 black
58
+ 1 red
59
+ 2 green
60
+ 3 yellow
61
+ 4 blue
62
+ 5 magenta
63
+ 6 cyan
64
+ 7 white))
65
+
66
+ #(SS,color-list,( ),##(CL,return))'
67
+
68
+ Discard first empty segment: #(CS,color-list)'
69
+
70
+ #(define-color,#(CS,color-list))'
71
+
72
+ #(DD,define-effect,define-color)'
73
+
74
+
75
+ Now we define our script that will translate the names above into actual escape
76
+ codes.
77
+
78
+ #(DS,color,(
79
+ #(DS,list,<list>)
80
+ #(SS,list,##(CL,space))
81
+ #(DS,read,(
82
+ #(clr-<s>)
83
+ #(next,#(CS,list,END))
84
+ ))
85
+ #(SS,read,<s>)
86
+ #(DS,next,(
87
+ #(EQ,<s>,END,,(;#(read,<s>)))
88
+ ))
89
+ #(SS,next,<s>)
90
+ #(escape)[#(read,#(CS,list,reset))m
91
+ #(DD,list,read,next)
92
+ ))
93
+ #(sss,color,<list>)'
94
+
95
+ Usage:
96
+ (
97
+ #(color,bright red-fg blue-bg)
98
+
99
+ Defaults to reset: #(color)
100
+
101
+ The escape codes need to be embedded in print commands to take effect, like
102
+ so:
103
+
104
+ #(PS,#(color,green-fg)I am green!#(color) I am not!)
105
+
106
+ Using these you can make templates for creating forms:
107
+
108
+ #(DS,template,(
109
+ *label*Name:** *field*<name>**
110
+ *label*Occupation:** *field*<job>**
111
+ *label*Age:** *field*<age>**
112
+ ))
113
+
114
+ #(SS,template,**,*label*,*field*)
115
+
116
+ #(DS,form,##(CL,template,#(color),#(color,bright),#(color,reverse)))
117
+
118
+ #(SS,form,<name>,<job>,<age>)
119
+
120
+ Now you can fill out the form multiple times, and it will print with the
121
+ format you decided on. Notice that I used a neutral call on the template
122
+ when defining the form to protect the newlines in it.
123
+
124
+ )
125
+
126
+
127
+ #(PS,(success!
128
+ ))'
129
+
data/examples/util.trl ADDED
@@ -0,0 +1,366 @@
1
+
2
+ Basic Utilities
3
+
4
+ #(PS,Loading examples/util.trl...)'
5
+
6
+ Scrub Whitespace
7
+ ----------------
8
+ A problem with TRAC definitions is they're strings, and so if you add
9
+ whitespace to make them readable, your definition ends up with a lot of
10
+ extraneous whitespace. Carriage return and line feed are normally ignored if
11
+ they aren't protected, so they don't need to be removed, but spaces and tabs
12
+ aren't. So here are some scripts to help out:
13
+
14
+ scrub-char
15
+ scrubs a single character out of your script
16
+
17
+ #(DS,scrub-char,(#(SS,<f>,(<c>))#(DS,<f>,##(CL,<f>))))
18
+ #(SS,scrub-char,<f>,<c>)'
19
+
20
+ scrub
21
+ scrubs spaces and tabs out of your script
22
+
23
+ #(DS,scrub,(#(scrub-char,<f>,( ))#(scrub-char,<f>,( ))))
24
+ #(SS,scrub,<f>)'
25
+
26
+ Call these scripts on your definitions before you call SS, otherwise your
27
+ arguments will get messed up. To make that easier to do:
28
+
29
+ sss scrubs your script and then segments according to the args you pass
30
+
31
+ #(DS,sss,(#(scrub,<f>)#(SS,<f>,<args>)))
32
+ #(SS,sss,<f>,<args>)'
33
+
34
+ If you use this for a script that has multiple arguments, protect the argument
35
+ list with parentheses, otherwise TRAC will think your arguments are extra
36
+ arguments for sss, instead of for SS. Remember TRAC is only passing around
37
+ strings, not data structures.
38
+
39
+
40
+ Special Characters
41
+ ------------------
42
+ Another problem with TRAC is that some characters are special, and so can't be
43
+ used normally. This is especially true of the two editing characters - the "\"
44
+ and the "@". They can't be typed in, but since editing is not done when
45
+ reading a file, they can be entered in a file. So here are definitions for
46
+ each.
47
+
48
+ #(DS,backslash,\)'
49
+ #(DS,at,@)'
50
+
51
+ The meta character can be changed, so that a definition of the default meta
52
+ character single quote can be made.
53
+
54
+ #(CM,*)'
55
+ #(DS,quote,')*
56
+ #(CM,')*
57
+
58
+ Both the hash or number sign and the comma can easily be protected by
59
+ parentheses, so neither need a separate defintion. Here's how parentheses can
60
+ be defined:
61
+
62
+ #(DS,parens,(()))
63
+ #(DS,open-paren,##(CC,parens))
64
+ #(DS,close-paren,##(CC,parens))
65
+ #(DD,parens)'
66
+
67
+ However, even if you use a neutral call, unmatched parens can only be passed to
68
+ a TRAC primitive. You can't pass them to a form you define, because once the
69
+ form is expanded, the parens will no longer match.
70
+
71
+ Some important characters:
72
+
73
+ #(DS,return,(
74
+ ))'
75
+ #(DS,tab, )'
76
+ #(DS,space, )'
77
+ #(DS,bell,)'
78
+ #(DS,escape,)'
79
+ #(DS,backspace,)'
80
+
81
+ The following is the first control character, known as SOH. We will use it as
82
+ a delimiter sometimes, instead of using a printable character.
83
+
84
+ #(DS,.,)'
85
+
86
+
87
+ Character Classes
88
+ -----------------
89
+
90
+ Here are some useful character classes.
91
+
92
+ #(DS,lower,abcdefghijklmnopqrstuvwxyz)'
93
+ #(DS,upper,ABCDEFGHIJKLMNOPQRSTUVWXYZ)'
94
+ #(DS,digit,0123456789)'
95
+ #(DS,whitespace,##(return)##(tab)##(space))'
96
+
97
+ Here are some scripts for using the character classes. These were inspired by
98
+ TTM primitives that have the same names. See page 20 and 21 of TTM: An
99
+ Experimental and Interpretive Language.
100
+
101
+ https://github.com/Unidata/ttm/raw/master/ttm_interpretive_language_pr_07.pdf
102
+
103
+ #(DS,[TCL],(
104
+ #(EQ,(<c>),--,(Z),(
105
+ #(CR,class)
106
+ #(EQ,##(IN,class,(<c>),--),--,(F),(T))
107
+ ))
108
+ ))
109
+ #(sss,[TCL],(class,<c>,T,F,Z))
110
+ #(DS,TCL,(#([TCL],class,##(CC,form,--),(T),(F),(Z))))
111
+ #(SS,TCL,class,form,T,F,Z)'
112
+
113
+ TCL (Test Class) tests if the character pointed to by the form pointer is in
114
+ the class given. If returns T if so, F if not, and Z if the form pointer is at
115
+ the end of the form.
116
+
117
+ #(DS,[CCL],(
118
+ #([TCL],class,(<c>),(
119
+ (<c>)#([CCL],class,##(CC,form,--),form)
120
+ ),(
121
+ #(,##(CN,form,-1))
122
+ ))
123
+ ))
124
+ #(sss,[CCL],(class,<c>,form))
125
+ #(DS,CCL,(#([CCL],class,##(CC,form,--),form)))
126
+ #(SS,CCL,class,form)'
127
+
128
+ CCL (Call Class) returns all consecutive characters from the given form,
129
+ starting at the form pointer and going up to but not including the first
130
+ character that is not in the given class. In other words, it will return a
131
+ string of characters that are in the class given. If there are no characters
132
+ in the given class at the form pointer, or if the form pointer is at the end of
133
+ the form, nothing is returned.
134
+
135
+ #(DS,SCL,(#(,#(CCL,class,form))))
136
+ #(SS,SCL,class,form)'
137
+
138
+ SCL (Scan Class) will move the form pointer past all characters in the form
139
+ that are in the given character class.
140
+
141
+
142
+ Times
143
+ -----
144
+ Execute something a number of times. The action can be a simple string or
145
+ something more complicated.
146
+
147
+ #(DS,times,(
148
+ #(EQ,<n>,0,,(
149
+ <action>#(times,#(SU,<n>,1),(<action>)))
150
+ ))
151
+ )
152
+ #(sss,times,(<n>,<action>))'
153
+
154
+
155
+ Character Count
156
+ ---------------
157
+ Count the number of characters in a form. This is probably the most involved
158
+ thing that I wish was a primitive in TRAC. There isn't a code golf challenge
159
+ that doesn't require this, and if it was a primitive TRAC might actually have a
160
+ chance.
161
+
162
+ This really counts from the form pointer to the end, and sets the form pointer
163
+ at the end of the string as a side effect. So if you want the real length, you
164
+ have to call CR beforehand.
165
+
166
+ #(DS,count,(
167
+ #(EQ,##(CC,<f>,--),--,<tot>,(
168
+ #(count,<f>,#(AD,1,<tot>))
169
+ ))
170
+ ))
171
+ #(sss,count,(<f>,<tot>))'
172
+
173
+
174
+ Concatenate
175
+ -----------
176
+
177
+ Add given characters to the end of a given form. A very simple definition, but
178
+ useful when you only have one reference to the form you want to concatenate on.
179
+
180
+ #(DS,concat,(
181
+ #(DS,form,##(CL,form,args)(str))
182
+ #(SS,form,args)
183
+ ))
184
+ #(sss,concat,(form,args,str))'
185
+
186
+
187
+ Increment
188
+ ---------
189
+
190
+ Another very simple definition of something that's easy to do. But it's also
191
+ easy to forget that the form "num" needs to executed for the AD to work, in
192
+ which case you end up with one as your value, instead of what you want.
193
+
194
+ #(DS,inc,(#(DS,num,#(AD,#(num),1))))
195
+ #(SS,inc,num)'
196
+
197
+
198
+ Move To End
199
+ -----------
200
+ Move to the end of a form. This is relatively simple, but it sure would be
201
+ nice to have a primitive that could do this, the way CR can move to the start
202
+ of a form.
203
+
204
+ #(DS,end,(
205
+ #(EQ,##(CC,<form>,--),--,,(
206
+ #(end,<form>)
207
+ ))
208
+ ))
209
+ #(sss,end,<form>)'
210
+
211
+
212
+ At End Or Beginning?
213
+ --------------------
214
+ Test if you are at the end of a form. This uses a special trick of TRAC, when
215
+ the form pointer is at the end of a form, CN returns the end-of-form argument
216
+ even when you ask for zero characters. The same trick works at the beginning
217
+ of a form when you use negative zero. This trick is the whole reason that
218
+ negative zero exists in TRAC.
219
+
220
+ #(DS,end?,(#(EQ,#(CN,form,0,#(.)),#(.),(T),(F))))
221
+ #(SS,end?,form,T,F)'
222
+
223
+ #(DS,start?,(#(EQ,#(CN,form,-0,#(.)),#(.),(T),(F))))
224
+ #(SS,start?,form,T,F)'
225
+
226
+
227
+ Form Exists
228
+ -----------
229
+
230
+ This relies on a odd property of IN. If you search for the empty string in a
231
+ form using IN, if the form doesn't exist, the empty string is returned. If the
232
+ form does exist, searching for the empty string will fail, and so the
233
+ "not found" parameter will be returned. We can use this to create a test for
234
+ the existence of a form.
235
+
236
+ #(DS,exists?,(#(EQ,#(IN,form,,notfound),notfound,(T),(F))))
237
+ #(SS,exists?,form,T,F)'
238
+
239
+
240
+ Starts With
241
+ -----------
242
+
243
+ Tests if a given string starts with a given value. Notice that I have to
244
+ define a form with the string to search in and then delete it when I'm done
245
+ with it. I use the special character as the temporary name, so that I don't
246
+ overwrite an existing form.
247
+
248
+ #(DS,starts-with,(
249
+ #(DS,#(.),(string1))
250
+ #(EQ,#(IN,#(.),(string2),-),,#(DD,#(.))(T),(F))
251
+ ))
252
+ #(sss,starts-with,(string1,string2,T,F))
253
+
254
+
255
+ List Forms With Prefix
256
+ ----------------------
257
+
258
+ One way to organize the list of defined forms is to prefix the name of related
259
+ forms with a certain value. For instance, all the color code definitions in
260
+ the term.trl file are prefixed with "clr-". This script allows you to list the
261
+ only those forms. Notice I use the dot special character as a delimiter, so
262
+ you can use any printable character in your form names.
263
+
264
+ #(DS,list-prefix,(
265
+ #(DS,[names],#(LN,#(.)))
266
+ #(SS,[names],#(.))
267
+ #(DS,[delimiter])
268
+ #(DS,[next],(
269
+ #(EQ,<name>,#(.),,(
270
+ #(DS,[name],<name>)
271
+ #(EQ,#(IN,[name],prefix,#(.)),,(
272
+ #(CR,[name])
273
+ ##([delimiter])#([name])
274
+ #(DS,[delimiter],(<delimiter>))
275
+ ))
276
+ #([next],#(CS,[names],#(.)))
277
+ ))
278
+ ))
279
+ #(SS,[next],<name>)
280
+ #([next],#(CS,[names],#(.)))
281
+ #(DD,[next],[names],[name],[delimiter])
282
+ ))
283
+ #(sss,list-prefix,(prefix,<delimiter>))'
284
+
285
+
286
+ Get Segment By Index
287
+ --------------------
288
+
289
+ Returns a segment of a form by the one-based index of it. If the index is out
290
+ of range (below one or above the number of segments in the form), the default
291
+ is returned.
292
+
293
+ #(DS,segment,(
294
+ #(DS,[seg],(
295
+ #(EQ,(value),#(.),(default),(
296
+ #(GR,2,count,#(CR,form)(value),(
297
+ #([seg],#(SU,count,1),##(CS,form,#(.)))
298
+ ))
299
+ ))
300
+ ))
301
+ #(SS,[seg],count,value)
302
+ #(GR,1,index,(default),(
303
+ #([seg],index,#(CS,form,#(.)))#(CR,form)
304
+ ))
305
+ ))
306
+ #(sss,segment,(index,form,default))'
307
+
308
+
309
+ Logic - And, Or and Not
310
+ -----------------------
311
+
312
+ These work by passing partial function calls. For example, to test if a number
313
+ is between -1 and 1 you would do:
314
+ (
315
+ #(and,(GR,a,-1),(GR,1,a),between,not-between)
316
+ )
317
+ Any test that returns either a true or false value can be used, both built-in
318
+ tests and any user-defined tests as well. These can be nested if necessary,
319
+ although that can become unreadable very quickly. For example, test if "b" is
320
+ in the interval ]-1,1[ or the interval ]2,3[:
321
+ (
322
+ #(or,(and,(GR,b,-1),(GR,1,b)),(and,(GR,b,2),(GR,3,b)),true,false)
323
+ )
324
+ Both "and" and "or" are short-circuited, in other words, if the first test
325
+ determines what the result is, the second test is not executed.
326
+
327
+ #(DS,and,(
328
+ #(EQ,#(test1,T,F),T,(
329
+ #(EQ,#(test2,T,F),T,(true),(false))
330
+ ),(false))
331
+ ))
332
+ #(sss,and,(test1,test2,true,false))'
333
+
334
+ #(DS,or,(
335
+ #(EQ,#(test1,T,F),T,(true),(
336
+ #(EQ,#(test2,T,F),T,(true),(false))
337
+ ))
338
+ ))
339
+ #(sss,or,(test1,test2,true,false))'
340
+
341
+ And here's the implementation of not. Just pass it a partial test, same as you
342
+ do for "and" and "or".
343
+
344
+ #(DS,not,(#(test,(F),(T))))
345
+ #(SS,not,test,T,F)'
346
+
347
+
348
+ #(PS,(success!
349
+ ))'
350
+
351
+
352
+ Load Other Files
353
+ ----------------
354
+
355
+ #(DS,term,term.trl)
356
+ #(FB,term)'
357
+ #(DS,math,math.trl)
358
+ #(FB,math)'
359
+ #(DS,meta,meta.trl)
360
+ #(FB,meta)'
361
+ #(DS,ratio,ratio.trl)
362
+ #(FB,ratio)'
363
+ #(DS,struct,struct.trl)
364
+ #(FB,struct)'
365
+ #(DS,list,list.trl)
366
+ #(FB,list)'