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