diff options
Diffstat (limited to 'trainfuck.awk')
-rwxr-xr-x | trainfuck.awk | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/trainfuck.awk b/trainfuck.awk new file mode 100755 index 0000000..df89821 --- /dev/null +++ b/trainfuck.awk | |||
@@ -0,0 +1,321 @@ | |||
1 | #!/bin/awk -f | ||
2 | # TRAINFUCK: CHOO CHOO MUTHAFUCKA -*- awk -*- | ||
3 | # Author: Case Duckworth <acdw@acdw.net> | ||
4 | # License: WTFPL | ||
5 | # Version: #9 | ||
6 | |||
7 | ### Commentary: | ||
8 | |||
9 | # LANGUAGE | ||
10 | |||
11 | # trainfuck is not case-sensitive | ||
12 | # -- except for ALL ABOARD and END OF THE LINE | ||
13 | # ignore everything before ALL ABOARD | ||
14 | # ignore everything after END OF THE LINE | ||
15 | # (this means you can comment between these) | ||
16 | # bf tf | ||
17 | # + chug | ||
18 | # - chugga | ||
19 | # > choo | ||
20 | # < choo choo | ||
21 | # . click OR clickety | ||
22 | # , clack | ||
23 | # [ tickets please | ||
24 | # ] your ticket please | ||
25 | # syntax does NOT WRAP across line breaks | ||
26 | # anything else is an error and DERAILS the train | ||
27 | |||
28 | ### Code: | ||
29 | BEGIN { | ||
30 | # Configuration | ||
31 | BF_WIDTH = 42 | ||
32 | BF_PRINT = 1 | ||
33 | BF_PRINT_COMMENTS = 1 | ||
34 | BF_COMMENTS_COMPACT = 1 | ||
35 | BF_EXECUTE = 0 | ||
36 | BF_OUTPUT = "/dev/stderr" | ||
37 | BF_MODE = "trainfuck" | ||
38 | process_commandline() | ||
39 | # Constants | ||
40 | EXE_NAME = (EXE_NAME ? EXE_NAME : "trainfuck") | ||
41 | ERR_SYNTAX = 1 | ||
42 | } | ||
43 | |||
44 | BEGIN { | ||
45 | # State variables and beginning output | ||
46 | ABOARD = 0 | ||
47 | FIRST_LINE = 1 | ||
48 | OFS = "\t" | ||
49 | } | ||
50 | |||
51 | BF_MODE == "brainfuck" { | ||
52 | bf_program = bf_program $0 | ||
53 | next | ||
54 | } | ||
55 | |||
56 | FIRST_LINE { | ||
57 | if (BF_PRINT_COMMENTS && first_line != "[") { | ||
58 | eprint("[") | ||
59 | } | ||
60 | eprint(first_line) | ||
61 | FIRST_LINE = 0 | ||
62 | } | ||
63 | |||
64 | /^ALL ABOARD$/ { | ||
65 | if (! header && BF_PRINT_COMMENTS) { | ||
66 | eprint("]") | ||
67 | } | ||
68 | header++ | ||
69 | ABOARD = 1 | ||
70 | next | ||
71 | } | ||
72 | |||
73 | /^END OF THE LINE$/ { | ||
74 | ABOARD = 0 | ||
75 | next | ||
76 | } | ||
77 | |||
78 | ABOARD { | ||
79 | gsub(/[[:space:]]/, "", $0) | ||
80 | buf = buf tf_convert(toupper($0)) | ||
81 | } | ||
82 | |||
83 | ! ABOARD && BF_PRINT_COMMENTS { | ||
84 | printbuf() | ||
85 | eprint($0) | ||
86 | buf = "" | ||
87 | } | ||
88 | |||
89 | END { | ||
90 | if (ABOARD) { | ||
91 | die("Didn't disembark from the train!", ERR_SYNTAX) | ||
92 | } | ||
93 | if (dead) { | ||
94 | exit dead | ||
95 | } | ||
96 | printbuf() | ||
97 | if (BF_EXECUTE) { | ||
98 | brainfuck(bf_program) | ||
99 | } | ||
100 | } | ||
101 | |||
102 | |||
103 | function brainfuck(buffer) | ||
104 | { | ||
105 | split(buffer, bf, "") | ||
106 | i = 1 | ||
107 | c = 1 | ||
108 | for (n = 1; n < 30000; n++) { | ||
109 | tape[n] = 0 | ||
110 | } | ||
111 | while (c <= length(bf)) { | ||
112 | # print i, tape[i], c, bf[c] | ||
113 | if (bf[c] == "<" && c > 1) { | ||
114 | i-- | ||
115 | } else if (bf[c] == ">") { | ||
116 | i++ | ||
117 | } else if (bf[c] == "+") { | ||
118 | tape[i]++ | ||
119 | } else if (bf[c] == "-") { | ||
120 | tape[i]-- | ||
121 | } else if (bf[c] == ".") { | ||
122 | printf "%c", tape[i] | ||
123 | } else if (bf[c] == ",") { | ||
124 | tape[i] = char2number(getchar()) | ||
125 | } else if (bf[c] == "[") { | ||
126 | bracket = 1 | ||
127 | if (! tape[i]) { | ||
128 | while (c <= length(bf)) { | ||
129 | c++ | ||
130 | if (bf[c] == "[") { | ||
131 | bracket++ | ||
132 | } else if (bf[c] == "]") { | ||
133 | bracket-- | ||
134 | if (! bracket) { | ||
135 | break | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | if (c > length(bf)) { | ||
140 | die("Mismatched bracket: " c, ERR_SYNTAX) | ||
141 | } | ||
142 | } | ||
143 | } else if (bf[c] == "]") { | ||
144 | bracket = 1 | ||
145 | if (tape[i]) { | ||
146 | while (c) { | ||
147 | c-- | ||
148 | if (bf[c] == "]") { | ||
149 | bracket++ | ||
150 | } else if (bf[c] == "[") { | ||
151 | bracket-- | ||
152 | if (! bracket) { | ||
153 | break | ||
154 | } | ||
155 | } | ||
156 | } | ||
157 | if (! c) { | ||
158 | die("Mismatched bracket: " c, ERR_SYNTAX) | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | c++ | ||
163 | } | ||
164 | } | ||
165 | |||
166 | function die(message, errnum) | ||
167 | { | ||
168 | print(message) > "/dev/stderr" | ||
169 | dead = errnum | ||
170 | } | ||
171 | |||
172 | function eprint(message, suppress_newline) | ||
173 | { | ||
174 | printf("%s" (suppress_newline ? "" : "\n"), message) > BF_OUTPUT | ||
175 | } | ||
176 | |||
177 | function getchar() | ||
178 | { | ||
179 | system("stty raw") | ||
180 | cmd = "dd bs=1 count=1 2>/dev/null" | ||
181 | cmd | getline ch | ||
182 | close(cmd) | ||
183 | system("stty cooked") | ||
184 | return ch | ||
185 | } | ||
186 | |||
187 | function printbuf(newline) | ||
188 | { | ||
189 | suppress_newline = BF_PRINT_COMMENTS && BF_COMMENTS_COMPACT | ||
190 | for (ss = 1; ss <= length(buf); ss += BF_WIDTH) { | ||
191 | eprint(substr(buf, ss, BF_WIDTH), suppress_newline) | ||
192 | suppress_newline = 0 | ||
193 | } | ||
194 | bf_program = bf_program buf | ||
195 | } | ||
196 | |||
197 | function process_commandline() | ||
198 | { | ||
199 | for (a in ARGV) { | ||
200 | if (ARGV[a] == "--") { | ||
201 | delete ARGV[a] | ||
202 | break | ||
203 | } else if (ARGV[a] == "-h" || ARGV[a] == "--help") { | ||
204 | usage() | ||
205 | } else if (ARGV[a] == "-w") { | ||
206 | delete ARGV[a] | ||
207 | BF_WIDTH = ARGV[++a] | ||
208 | delete ARGV[a] | ||
209 | } else if (ARGV[a] == "-o") { | ||
210 | delete ARGV[a] | ||
211 | BF_OUTPUT = ARGV[++a] | ||
212 | delete ARGV[a] | ||
213 | } else if (ARGV[a] == "-c" || ARGV[a] == "--comments") { | ||
214 | BF_PRINT_COMMENTS = 1 | ||
215 | delete ARGV[a] | ||
216 | } else if (ARGV[a] == "-C" || ARGV[a] == "--no-comments") { | ||
217 | BF_PRINT_COMMENTS = 0 | ||
218 | delete ARGV[a] | ||
219 | } else if (ARGV[a] == "-z" || ARGV[a] == "--compact") { | ||
220 | BF_COMMENTS_COMPACT = 1 | ||
221 | delete ARGV[a] | ||
222 | } else if (ARGV[a] == "-Z" || ARGV[a] == "--no-compact") { | ||
223 | BF_COMMENTS_COMPACT = 0 | ||
224 | delete ARGV[a] | ||
225 | } else if (ARGV[a] == "-x" || ARGV[a] == "--execute") { | ||
226 | BF_EXECUTE = 1 | ||
227 | delete ARGV[a] | ||
228 | } else if (ARGV[a] == "-X" || ARGV[a] == "--no-execute") { | ||
229 | BF_EXECUTE = 0 | ||
230 | delete ARGV[a] | ||
231 | } else if (ARGV[a] == "-t" || ARGV[a] == "--trainfuck") { | ||
232 | BF_MODE = "trainfuck" | ||
233 | BF_MODE_FORCE = 1 | ||
234 | delete ARGV[a] | ||
235 | } else if (ARGV[a] == "-b" || ARGV[a] == "--brainfuck") { | ||
236 | BF_MODE = "brainfuck" | ||
237 | BF_MODE_FORCE = 1 | ||
238 | delete ARGV[a] | ||
239 | } | ||
240 | } | ||
241 | if (! BF_MODE_FORCE) { | ||
242 | if (ARGV[1] ~ /\.tf$/ || ARGV[1] ~ /\.trainfuck$/) { | ||
243 | BF_MODE = "trainfuck" | ||
244 | } else if (ARGV[1] ~ /\.bf$/ || ARGV[1] ~ /\.brainfuck$/) { | ||
245 | BF_MODE = "brainfuck" | ||
246 | } | ||
247 | } | ||
248 | if (BF_MODE == "brainfuck") { | ||
249 | BF_EXECUTE = 1 | ||
250 | } | ||
251 | } | ||
252 | |||
253 | function tf_convert(t) | ||
254 | { | ||
255 | if (! match(t, /CHUGGA|CHUG|CHOO|CLICK|CLICKETY|CLACK|TICKETSPLEASE|YOURTICKETPLEASE|$/)) { | ||
256 | die("Derailed at input line " FNR, ERR_SYNTAX) | ||
257 | } | ||
258 | pre = substr(t, 1, RSTART - 1) | ||
259 | tok = substr(t, RSTART, RLENGTH) | ||
260 | pst = substr(t, RSTART + RLENGTH) | ||
261 | if (tok == "CHUGGA") { # needs to be first | ||
262 | tok = "-" | ||
263 | return (pre tok tf_convert(pst)) | ||
264 | } | ||
265 | if (tok == "CHUG") { | ||
266 | tok = "+" | ||
267 | return (pre tok tf_convert(pst)) | ||
268 | } | ||
269 | if (tok == "CHOO") { | ||
270 | if (substr(pst, 1, 4) == "CHOO") { | ||
271 | tok = "<" | ||
272 | sub(/CHOO/, "", pst) | ||
273 | } else { | ||
274 | tok = ">" | ||
275 | } | ||
276 | return (pre tok tf_convert(pst)) | ||
277 | } | ||
278 | if (tok == "CLICK" || tok == "CLICKETY") { | ||
279 | tok = "." | ||
280 | return (pre tok tf_convert(pst)) | ||
281 | } | ||
282 | if (tok == "CLACK") { | ||
283 | tok = "," | ||
284 | return (pre tok tf_convert(pst)) | ||
285 | } | ||
286 | if (tok == "TICKETSPLEASE") { | ||
287 | tok = "[" | ||
288 | return (pre tok tf_convert(pst)) | ||
289 | } | ||
290 | if (tok == "YOURTICKETPLEASE") { | ||
291 | tok = "]" | ||
292 | return (pre tok tf_convert(pst)) | ||
293 | } | ||
294 | } | ||
295 | |||
296 | function usage() | ||
297 | { | ||
298 | eprint("TRAINFUCK: CHOO CHOO") | ||
299 | eprint("Usage: ") | ||
300 | eprint(EXE_NAME " -h|--help") | ||
301 | eprint(EXE_NAME " [-w WIDTH] [FLAGS...] INPUTFILE [-o OUTPUTFILE]") | ||
302 | eprint() | ||
303 | eprint("Parameters:") | ||
304 | eprint(" INPUTFILE\tThe trainfuck file to process.") | ||
305 | eprint("\t\tIf INPUTFILE ends in .tf or .trainfuck, it will be interpreted") | ||
306 | eprint("\t\tas a TRAINFUCK file; if it ends in .bf or .brainfuck, it will ") | ||
307 | eprint("\t\tbe interpreted as a BRAINFUCK file. Otherwise, the file will ") | ||
308 | eprint("\t\tbe assumed to be the default filetype (" BF_MODE ").") | ||
309 | eprint(" -w WIDTH\tSet the brainfuck output width (default: " BF_WIDTH ").") | ||
310 | eprint(" -o OUTPUTFILE\tWhere to send transpiled brainfuck (default: " BF_OUTPUT ").") | ||
311 | eprint() | ||
312 | eprint("FLAGS:") | ||
313 | eprint(" -h, --help\t\tShow this help") | ||
314 | eprint(" -c, --comments\t\tShow comments" (BF_PRINT_COMMENTS ? " (default)" : "")) | ||
315 | eprint(" -C, --no-comments\tHide comments" (BF_PRINT_COMMENTS ? "" : " (default)")) | ||
316 | eprint(" -z, --compact\t\tShow \"compact\" comments" (BF_COMMENTS_COMPACT ? " (default)" : "")) | ||
317 | eprint(" -Z, --no-compact\tShow \"wide\" comments" (BF_COMMENTS_COMPACT ? "" : " (default)")) | ||
318 | eprint(" -x, --execute\t\tExecute trainspiled brainfuck" (BF_EXECUTE ? " (default)" : "")) | ||
319 | eprint(" -X, --no-execute\tOnly trainspile, do not execute" (BF_EXECUTE ? "" : " (default)")) | ||
320 | exit | ||
321 | } | ||