about summary refs log tree commit diff stats
path: root/trainfuck.awk
diff options
context:
space:
mode:
Diffstat (limited to 'trainfuck.awk')
-rwxr-xr-xtrainfuck.awk321
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:
29BEGIN {
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
44BEGIN {
45 # State variables and beginning output
46 ABOARD = 0
47 FIRST_LINE = 1
48 OFS = "\t"
49}
50
51BF_MODE == "brainfuck" {
52 bf_program = bf_program $0
53 next
54}
55
56FIRST_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
78ABOARD {
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
89END {
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
103function 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
166function die(message, errnum)
167{
168 print(message) > "/dev/stderr"
169 dead = errnum
170}
171
172function eprint(message, suppress_newline)
173{
174 printf("%s" (suppress_newline ? "" : "\n"), message) > BF_OUTPUT
175}
176
177function 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
187function 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
197function 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
253function 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
296function 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}