diff options
author | Case Duckworth | 2022-06-29 00:59:59 -0500 |
---|---|---|
committer | Case Duckworth | 2022-06-29 00:59:59 -0500 |
commit | 0a78720644e45bd27c4a57cbddf2d32aacb555c3 (patch) | |
tree | 29783dc9a534dbbfe172d5c84e66f5efcd9c43dc | |
parent | Recognize bf comments (diff) | |
download | trainfuck-0a78720644e45bd27c4a57cbddf2d32aacb555c3.tar.gz trainfuck-0a78720644e45bd27c4a57cbddf2d32aacb555c3.zip |
Probably version ... whatever the next higher one is
-rw-r--r-- | examples/add2.bf | 27 | ||||
-rw-r--r-- | examples/hello-world.bf | 45 | ||||
-rw-r--r-- | examples/helloworld.bf | 1 | ||||
-rw-r--r-- | examples/helloworld.trainfuck | 152 | ||||
-rw-r--r-- | examples/rot13.bf | 32 | ||||
-rwxr-xr-x | fucktrain | 4 | ||||
-rwxr-xr-x | trainfuck | 123 | ||||
-rwxr-xr-x | trainfuck.awk | 321 |
8 files changed, 433 insertions, 272 deletions
diff --git a/examples/add2.bf b/examples/add2.bf new file mode 100644 index 0000000..42361db --- /dev/null +++ b/examples/add2.bf | |||
@@ -0,0 +1,27 @@ | |||
1 | [ Add two values | ||
2 | from https://en.wikipedia.org/wiki/Brainfuck#Adding_two_values | ||
3 | ] | ||
4 | |||
5 | ++ Cell c0 = 2 | ||
6 | > +++++ Cell c1 = 5 | ||
7 | |||
8 | [ Start your loops with your cell pointer on the loop counter (c1 in our case) | ||
9 | < + Add 1 to c0 | ||
10 | > - Subtract 1 from c1 | ||
11 | ] End your loops with the cell pointer on the loop counter | ||
12 | |||
13 | At this point our program has added 5 to 2 leaving 7 in c0 and 0 in c1 | ||
14 | but we cannot output this value to the terminal since it is not ASCII encoded | ||
15 | |||
16 | To display the ASCII character "7" we must add 48 to the value 7 | ||
17 | We use a loop to compute 48 = 6 * 8 | ||
18 | |||
19 | ++++ ++++ c1 = 8 and this will be our loop counter again | ||
20 | [ | ||
21 | < +++ +++ Add 6 to c0 | ||
22 | > - Subtract 1 from c1 | ||
23 | ] | ||
24 | < . Print out c0 which has the value 55 which translates to "7"! | ||
25 | |||
26 | Finally print a newline: | ||
27 | ---------------------------------------------. | ||
diff --git a/examples/hello-world.bf b/examples/hello-world.bf new file mode 100644 index 0000000..dce24e2 --- /dev/null +++ b/examples/hello-world.bf | |||
@@ -0,0 +1,45 @@ | |||
1 | [ This program prints "Hello World!" and a newline to the screen, its | ||
2 | length is 106 active command characters. [It is not the shortest.] | ||
3 | |||
4 | This loop is an "initial comment loop", a simple way of adding a comment | ||
5 | to a BF program such that you don't have to worry about any command | ||
6 | characters. Any ".", ",", "+", "-", "<" and ">" characters are simply | ||
7 | ignored, the "[" and "]" characters just have to be balanced. This | ||
8 | loop and the commands it contains are ignored because the current cell | ||
9 | defaults to a value of 0; the 0 value causes this loop to be skipped. | ||
10 | |||
11 | from https://en.wikipedia.org/wiki/Brainfuck#Hello_World! | ||
12 | ] | ||
13 | ++++++++ Set Cell #0 to 8 | ||
14 | [ | ||
15 | >++++ Add 4 to Cell #1; this will always set Cell #1 to 4 | ||
16 | [ as the cell will be cleared by the loop | ||
17 | >++ Add 2 to Cell #2 | ||
18 | >+++ Add 3 to Cell #3 | ||
19 | >+++ Add 3 to Cell #4 | ||
20 | >+ Add 1 to Cell #5 | ||
21 | <<<<- Decrement the loop counter in Cell #1 | ||
22 | ] Loop until Cell #1 is zero; number of iterations is 4 | ||
23 | >+ Add 1 to Cell #2 | ||
24 | >+ Add 1 to Cell #3 | ||
25 | >- Subtract 1 from Cell #4 | ||
26 | >>+ Add 1 to Cell #6 | ||
27 | [<] Move back to the first zero cell you find; this will | ||
28 | be Cell #1 which was cleared by the previous loop | ||
29 | <- Decrement the loop Counter in Cell #0 | ||
30 | ] Loop until Cell #0 is zero; number of iterations is 8 | ||
31 | |||
32 | The result of this is: | ||
33 | Cell no : 0 1 2 3 4 5 6 | ||
34 | Contents: 0 0 72 104 88 32 8 | ||
35 | Pointer : ^ | ||
36 | |||
37 | >>. Cell #2 has value 72 which is 'H' | ||
38 | >---. Subtract 3 from Cell #3 to get 101 which is 'e' | ||
39 | +++++++..+++. Likewise for 'llo' from Cell #3 | ||
40 | >>. Cell #5 is 32 for the space | ||
41 | <-. Subtract 1 from Cell #4 for 87 to give a 'W' | ||
42 | <. Cell #3 was set to 'o' from the end of 'Hello' | ||
43 | +++.------.--------. Cell #3 for 'rl' and 'd' | ||
44 | >>+. Add 1 to Cell #5 gives us an exclamation point | ||
45 | >++. And finally a newline from Cell #6 | ||
diff --git a/examples/helloworld.bf b/examples/helloworld.bf deleted file mode 100644 index 8fa0f72..0000000 --- a/examples/helloworld.bf +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. | ||
diff --git a/examples/helloworld.trainfuck b/examples/helloworld.trainfuck deleted file mode 100644 index 1f68f11..0000000 --- a/examples/helloworld.trainfuck +++ /dev/null | |||
@@ -1,152 +0,0 @@ | |||
1 | TRAINFUCK BABEEE | ||
2 | CHOO CHOO MUTHERFUCKER | ||
3 | |||
4 | This example adapted from https://en.wikipedia.org/wiki/Brainfuck | ||
5 | |||
6 | This program prints "Hello World!" and a newline to the screen, its | ||
7 | length is 106 active command characters. | ||
8 | |||
9 | The program doesn't start until we have ALL ABOARD on a line by itself, so | ||
10 | we've got one better than an initial comment loop. | ||
11 | |||
12 | This file was handwritten, but soon(TM) we'll have a fucktrain command, | ||
13 | that'll translate to trainfuck from brainfuck. | ||
14 | |||
15 | ALL ABOARD | ||
16 | chug chug chug chug chug chug chug chug | ||
17 | END OF THE LINE | ||
18 | Set Cell #0 to 8 | ||
19 | (Also note: you can have comments betwen END OF THE LINE and ALL ABOARD) | ||
20 | ALL ABOARD | ||
21 | tickets please | ||
22 | choo | ||
23 | chug chug chug chug | ||
24 | END OF THE LINE | ||
25 | Add 4 to Cell #1; this will always set Cell #1 to 4 | ||
26 | ALL ABOARD | ||
27 | tickets please | ||
28 | END OF THE LINE | ||
29 | as the cell will be cleared by the loop | ||
30 | ALL ABOARD | ||
31 | choo | ||
32 | chug chug | ||
33 | END OF THE LINE | ||
34 | Add 2 to Cell #2 | ||
35 | ALL ABOARD | ||
36 | choo | ||
37 | chug chug chug | ||
38 | END OF THE LINE | ||
39 | Add 3 to Cell #3 | ||
40 | ALL ABOARD | ||
41 | choo | ||
42 | chug chug chug | ||
43 | END OF THE LINE | ||
44 | Add 3 to Cell #4 | ||
45 | ALL ABOARD | ||
46 | choo | ||
47 | chug | ||
48 | END OF THE LINE | ||
49 | Add 1 to Cell #5 | ||
50 | ALL ABOARD | ||
51 | choo choo | ||
52 | choo choo | ||
53 | choo choo | ||
54 | choo choo | ||
55 | chugga | ||
56 | END OF THE LINE | ||
57 | Decrement the loop counter in Cell #1 | ||
58 | ALL ABOARD | ||
59 | your ticket please | ||
60 | END OF THE LINE | ||
61 | Loop till Cell #1 is zero; number of iterations is 4 | ||
62 | ALL ABOARD | ||
63 | choo | ||
64 | chug | ||
65 | END OF THE LINE | ||
66 | Add 1 to Cell #2 | ||
67 | ALL ABOARD | ||
68 | choo | ||
69 | chug | ||
70 | END OF THE LINE | ||
71 | Add 1 to Cell #3 | ||
72 | ALL ABOARD | ||
73 | choo | ||
74 | chugga | ||
75 | END OF THE LINE | ||
76 | Subtract 1 from Cell #4 | ||
77 | ALL ABOARD | ||
78 | choo | ||
79 | choo | ||
80 | chug | ||
81 | END OF THE LINE | ||
82 | Add 1 to Cell #6 | ||
83 | ALL ABOARD | ||
84 | tickets pleasechoo choo | ||
85 | your ticket please | ||
86 | END OF THE LINE | ||
87 | Move back to the first zero cell you find; this will | ||
88 | ALL ABOARD | ||
89 | |||
90 | END OF THE LINE | ||
91 | be Cell #1 which was cleared by the previous loop | ||
92 | ALL ABOARD | ||
93 | choo choo | ||
94 | chugga | ||
95 | END OF THE LINE | ||
96 | Decrement the loop Counter in Cell #0 | ||
97 | ALL ABOARD | ||
98 | your ticket please | ||
99 | END OF THE LINE | ||
100 | Loop till Cell #0 is zero; number of iterations is 8 | ||
101 | ALL ABOARD | ||
102 | END OF THE LINE | ||
103 | The result of this is: | ||
104 | Cell No : 0 1 2 3 4 5 6 | ||
105 | Contents: 0 0 72 104 88 32 8 | ||
106 | Pointer : ^ | ||
107 | |||
108 | ALL ABOARD | ||
109 | choo | ||
110 | choo | ||
111 | click | ||
112 | END OF THE LINE | ||
113 | Cell #2 has value 72 which is 'H' | ||
114 | ALL ABOARD | ||
115 | choo | ||
116 | chugga chugga chugga click | ||
117 | END OF THE LINE | ||
118 | Subtract 3 from Cell #3 to get 101 which is 'e' | ||
119 | ALL ABOARD | ||
120 | chug chug chug chug chug chug chug click click chug chug chug click | ||
121 | END OF THE LINE | ||
122 | Likewise for 'llo' from Cell #3 | ||
123 | ALL ABOARD | ||
124 | choo | ||
125 | choo | ||
126 | click | ||
127 | END OF THE LINE | ||
128 | Cell #5 is 32 for the space | ||
129 | ALL ABOARD | ||
130 | choo choo | ||
131 | chugga click | ||
132 | END OF THE LINE | ||
133 | Subtract 1 from Cell #4 for 87 to give a 'W' | ||
134 | ALL ABOARD | ||
135 | choo choo | ||
136 | click | ||
137 | END OF THE LINE | ||
138 | Cell #3 was set to 'o' from the end of 'Hello' | ||
139 | ALL ABOARD | ||
140 | chug chug chug click chugga chugga chugga chugga chugga chugga click chugga chugga chugga chugga chugga chugga chugga chugga click | ||
141 | END OF THE LINE | ||
142 | Cell #3 for 'rl' and 'd' | ||
143 | ALL ABOARD | ||
144 | choo | ||
145 | choo | ||
146 | chug click | ||
147 | END OF THE LINE | ||
148 | Add 1 to Cell #5 gives us an exclamation point | ||
149 | ALL ABOARD | ||
150 | choo | ||
151 | chug chug click | ||
152 | END OF THE LINE | ||
diff --git a/examples/rot13.bf b/examples/rot13.bf new file mode 100644 index 0000000..bfcde73 --- /dev/null +++ b/examples/rot13.bf | |||
@@ -0,0 +1,32 @@ | |||
1 | [ ROT13 in Brainfuck | ||
2 | from https://en.wikipedia.org/wiki/Brainfuck#ROT13 | ||
3 | ] | ||
4 | |||
5 | -,+[ Read first character and start outer character reading loop | ||
6 | -[ Skip forward if character is 0 | ||
7 | >>++++[>++++++++<-] Set up divisor (32) for division loop | ||
8 | (MEMORY LAYOUT: dividend copy remainder divisor quotient zero zero) | ||
9 | <+<-[ Set up dividend (x minus 1) and enter division loop | ||
10 | >+>+>-[>>>] Increase copy and remainder / reduce divisor / Normal case: skip forward | ||
11 | <[[>+<-]>>+>] Special case: move remainder back to divisor and increase quotient | ||
12 | <<<<<- Decrement dividend | ||
13 | ] End division loop | ||
14 | ]>>>[-]+ End skip loop; zero former divisor and reuse space for a flag | ||
15 | >--[-[<->+++[-]]]<[ Zero that flag unless quotient was 2 or 3; zero quotient; check flag | ||
16 | ++++++++++++<[ If flag then set up divisor (13) for second division loop | ||
17 | (MEMORY LAYOUT: zero copy dividend divisor remainder quotient zero zero) | ||
18 | >-[>+>>] Reduce divisor; Normal case: increase remainder | ||
19 | >[+[<+>-]>+>>] Special case: increase remainder / move it back to divisor / increase quotient | ||
20 | <<<<<- Decrease dividend | ||
21 | ] End division loop | ||
22 | >>[<+>-] Add remainder back to divisor to get a useful 13 | ||
23 | >[ Skip forward if quotient was 0 | ||
24 | -[ Decrement quotient and skip forward if quotient was 1 | ||
25 | -<<[-]>> Zero quotient and divisor if quotient was 2 | ||
26 | ]<<[<<->>-]>> Zero divisor and subtract 13 from copy if quotient was 1 | ||
27 | ]<<[<<+>>-] Zero divisor and add 13 to copy if quotient was 0 | ||
28 | ] End outer skip loop (jump to here if ((character minus 1)/32) was not 2 or 3) | ||
29 | <[-] Clear remainder from first division if second division was skipped | ||
30 | <.[-] Output ROT13ed character from copy and clear it | ||
31 | <-,+ Read next character | ||
32 | ] End character reading loop | ||
diff --git a/fucktrain b/fucktrain index d382efa..7ade621 100755 --- a/fucktrain +++ b/fucktrain | |||
@@ -17,7 +17,9 @@ BEGIN { | |||
17 | gsub(/,/, "clack ", $0) | 17 | gsub(/,/, "clack ", $0) |
18 | gsub(/\[/, "tickets please ", $0) | 18 | gsub(/\[/, "tickets please ", $0) |
19 | gsub(/\]/, "your ticket please ", $0) | 19 | gsub(/\]/, "your ticket please ", $0) |
20 | 20 | if ($0) { | |
21 | |||
22 | } | ||
21 | } | 23 | } |
22 | 24 | ||
23 | END { | 25 | END { |
diff --git a/trainfuck b/trainfuck index 602fc92..22d1009 100755 --- a/trainfuck +++ b/trainfuck | |||
@@ -1,128 +1,15 @@ | |||
1 | #!/bin/awk -f | 1 | #!/bin/sh |
2 | # TRAINFUCK: CHOO CHOO MUTHAFUCKA -*- awk -*- | 2 | # TRAINFUCK: CHOO CHOO MUTHAFUCKA -*- sh -*- |
3 | # Author: Case Duckworth <acdw@acdw.net> | 3 | # Author: Case Duckworth <acdw@acdw.net> |
4 | # License: WTFPL | 4 | # License: WTFPL |
5 | # Version: #9 | 5 | # Version: #9 |
6 | 6 | ||
7 | ### Commentary: | 7 | ### Commentary: |
8 | 8 | ||
9 | # LANGUAGE | 9 | # shell wrapper around trainfuck.awk, which see. |
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 | 10 | ||
28 | ### Code: | 11 | ### Code: |
29 | BEGIN { | ||
30 | aboard = 0 | ||
31 | width = 30 | ||
32 | print "[" | ||
33 | } | ||
34 | |||
35 | /^ALL ABOARD$/ { | ||
36 | if (! header) { | ||
37 | print "]" | ||
38 | } | ||
39 | header++ | ||
40 | aboard = 1 | ||
41 | next | ||
42 | } | ||
43 | |||
44 | /^END OF THE LINE$/ { | ||
45 | aboard = 0 | ||
46 | next | ||
47 | } | ||
48 | |||
49 | aboard { | ||
50 | gsub(/[[:space:]]/, "", $0) | ||
51 | buf = buf proc(toupper($0)) | ||
52 | } | ||
53 | |||
54 | ! aboard { | ||
55 | printbuf() | ||
56 | if (header) { | ||
57 | gsub(/[-+<>.,\[\]]/, "", $0) | ||
58 | } | ||
59 | |||
60 | buf = "" | ||
61 | } | ||
62 | |||
63 | END { | ||
64 | if (DERAIL_ERR) { | ||
65 | print DERAIL_ERR | ||
66 | exit 9 | ||
67 | } | ||
68 | printbuf() | ||
69 | printf "\n" | ||
70 | } | ||
71 | |||
72 | |||
73 | function derail(err) | ||
74 | { | ||
75 | print "TRAIN DERAILED at input line", FNR | ||
76 | DERAIL_ERR = err | ||
77 | exit | ||
78 | } | ||
79 | 12 | ||
80 | function printbuf(newline) | 13 | TRAINFUCK=trainfuck.awk |
81 | { | ||
82 | for (ss = 1; ss <= length(buf); ss += width) { | ||
83 | printf "%s\n", substr(buf, ss, width) | ||
84 | } | ||
85 | } | ||
86 | 14 | ||
87 | function proc(t) | 15 | gawk -v EXE_NAME="$(basename $0)" -f "$TRAINFUCK" -- "$@" |
88 | { | ||
89 | if (! match(t, /CHUGGA|CHUG|CHOO|CLICK|CLICKETY|CLACK|TICKETSPLEASE|YOURTICKETPLEASE|$/)) { | ||
90 | derail("WTF") | ||
91 | } | ||
92 | pre = substr(t, 1, RSTART - 1) | ||
93 | tok = substr(t, RSTART, RLENGTH) | ||
94 | pst = substr(t, RSTART + RLENGTH) | ||
95 | if (tok == "CHUGGA") { # needs to be first | ||
96 | tok = "-" | ||
97 | return (pre tok proc(pst)) | ||
98 | } | ||
99 | if (tok == "CHUG") { | ||
100 | tok = "+" | ||
101 | return (pre tok proc(pst)) | ||
102 | } | ||
103 | if (tok == "CHOO") { | ||
104 | if (substr(pst, 1, 4) == "CHOO") { | ||
105 | tok = "<" | ||
106 | sub(/CHOO/, "", pst) | ||
107 | } else { | ||
108 | tok = ">" | ||
109 | } | ||
110 | return (pre tok proc(pst)) | ||
111 | } | ||
112 | if (tok == "CLICK" || tok == "CLICKETY") { | ||
113 | tok = "." | ||
114 | return (pre tok proc(pst)) | ||
115 | } | ||
116 | if (tok == "CLACK") { | ||
117 | tok = "," | ||
118 | return (pre tok proc(pst)) | ||
119 | } | ||
120 | if (tok == "TICKETSPLEASE") { | ||
121 | tok = "[" | ||
122 | return (pre tok proc(pst)) | ||
123 | } | ||
124 | if (tok == "YOURTICKETPLEASE") { | ||
125 | tok = "]" | ||
126 | return (pre tok proc(pst)) | ||
127 | } | ||
128 | } | ||
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 | } | ||