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/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)'
|