From 0a78720644e45bd27c4a57cbddf2d32aacb555c3 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Wed, 29 Jun 2022 00:59:59 -0500 Subject: Probably version ... whatever the next higher one is --- trainfuck.awk | 321 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100755 trainfuck.awk (limited to 'trainfuck.awk') diff --git a/trainfuck.awk b/trainfuck.awk new file mode 100755 index 0000000..df89821 --- /dev/null +++ b/trainfuck.awk @@ -0,0 +1,321 @@ +#!/bin/awk -f +# TRAINFUCK: CHOO CHOO MUTHAFUCKA -*- awk -*- +# Author: Case Duckworth +# License: WTFPL +# Version: #9 + +### Commentary: + +# LANGUAGE + +# trainfuck is not case-sensitive +# -- except for ALL ABOARD and END OF THE LINE +# ignore everything before ALL ABOARD +# ignore everything after END OF THE LINE +# (this means you can comment between these) +# bf tf +# + chug +# - chugga +# > choo +# < choo choo +# . click OR clickety +# , clack +# [ tickets please +# ] your ticket please +# syntax does NOT WRAP across line breaks +# anything else is an error and DERAILS the train + +### Code: +BEGIN { + # Configuration + BF_WIDTH = 42 + BF_PRINT = 1 + BF_PRINT_COMMENTS = 1 + BF_COMMENTS_COMPACT = 1 + BF_EXECUTE = 0 + BF_OUTPUT = "/dev/stderr" + BF_MODE = "trainfuck" + process_commandline() + # Constants + EXE_NAME = (EXE_NAME ? EXE_NAME : "trainfuck") + ERR_SYNTAX = 1 +} + +BEGIN { + # State variables and beginning output + ABOARD = 0 + FIRST_LINE = 1 + OFS = "\t" +} + +BF_MODE == "brainfuck" { + bf_program = bf_program $0 + next +} + +FIRST_LINE { + if (BF_PRINT_COMMENTS && first_line != "[") { + eprint("[") + } + eprint(first_line) + FIRST_LINE = 0 +} + +/^ALL ABOARD$/ { + if (! header && BF_PRINT_COMMENTS) { + eprint("]") + } + header++ + ABOARD = 1 + next +} + +/^END OF THE LINE$/ { + ABOARD = 0 + next +} + +ABOARD { + gsub(/[[:space:]]/, "", $0) + buf = buf tf_convert(toupper($0)) +} + +! ABOARD && BF_PRINT_COMMENTS { + printbuf() + eprint($0) + buf = "" +} + +END { + if (ABOARD) { + die("Didn't disembark from the train!", ERR_SYNTAX) + } + if (dead) { + exit dead + } + printbuf() + if (BF_EXECUTE) { + brainfuck(bf_program) + } +} + + +function brainfuck(buffer) +{ + split(buffer, bf, "") + i = 1 + c = 1 + for (n = 1; n < 30000; n++) { + tape[n] = 0 + } + while (c <= length(bf)) { + # print i, tape[i], c, bf[c] + if (bf[c] == "<" && c > 1) { + i-- + } else if (bf[c] == ">") { + i++ + } else if (bf[c] == "+") { + tape[i]++ + } else if (bf[c] == "-") { + tape[i]-- + } else if (bf[c] == ".") { + printf "%c", tape[i] + } else if (bf[c] == ",") { + tape[i] = char2number(getchar()) + } else if (bf[c] == "[") { + bracket = 1 + if (! tape[i]) { + while (c <= length(bf)) { + c++ + if (bf[c] == "[") { + bracket++ + } else if (bf[c] == "]") { + bracket-- + if (! bracket) { + break + } + } + } + if (c > length(bf)) { + die("Mismatched bracket: " c, ERR_SYNTAX) + } + } + } else if (bf[c] == "]") { + bracket = 1 + if (tape[i]) { + while (c) { + c-- + if (bf[c] == "]") { + bracket++ + } else if (bf[c] == "[") { + bracket-- + if (! bracket) { + break + } + } + } + if (! c) { + die("Mismatched bracket: " c, ERR_SYNTAX) + } + } + } + c++ + } +} + +function die(message, errnum) +{ + print(message) > "/dev/stderr" + dead = errnum +} + +function eprint(message, suppress_newline) +{ + printf("%s" (suppress_newline ? "" : "\n"), message) > BF_OUTPUT +} + +function getchar() +{ + system("stty raw") + cmd = "dd bs=1 count=1 2>/dev/null" + cmd | getline ch + close(cmd) + system("stty cooked") + return ch +} + +function printbuf(newline) +{ + suppress_newline = BF_PRINT_COMMENTS && BF_COMMENTS_COMPACT + for (ss = 1; ss <= length(buf); ss += BF_WIDTH) { + eprint(substr(buf, ss, BF_WIDTH), suppress_newline) + suppress_newline = 0 + } + bf_program = bf_program buf +} + +function process_commandline() +{ + for (a in ARGV) { + if (ARGV[a] == "--") { + delete ARGV[a] + break + } else if (ARGV[a] == "-h" || ARGV[a] == "--help") { + usage() + } else if (ARGV[a] == "-w") { + delete ARGV[a] + BF_WIDTH = ARGV[++a] + delete ARGV[a] + } else if (ARGV[a] == "-o") { + delete ARGV[a] + BF_OUTPUT = ARGV[++a] + delete ARGV[a] + } else if (ARGV[a] == "-c" || ARGV[a] == "--comments") { + BF_PRINT_COMMENTS = 1 + delete ARGV[a] + } else if (ARGV[a] == "-C" || ARGV[a] == "--no-comments") { + BF_PRINT_COMMENTS = 0 + delete ARGV[a] + } else if (ARGV[a] == "-z" || ARGV[a] == "--compact") { + BF_COMMENTS_COMPACT = 1 + delete ARGV[a] + } else if (ARGV[a] == "-Z" || ARGV[a] == "--no-compact") { + BF_COMMENTS_COMPACT = 0 + delete ARGV[a] + } else if (ARGV[a] == "-x" || ARGV[a] == "--execute") { + BF_EXECUTE = 1 + delete ARGV[a] + } else if (ARGV[a] == "-X" || ARGV[a] == "--no-execute") { + BF_EXECUTE = 0 + delete ARGV[a] + } else if (ARGV[a] == "-t" || ARGV[a] == "--trainfuck") { + BF_MODE = "trainfuck" + BF_MODE_FORCE = 1 + delete ARGV[a] + } else if (ARGV[a] == "-b" || ARGV[a] == "--brainfuck") { + BF_MODE = "brainfuck" + BF_MODE_FORCE = 1 + delete ARGV[a] + } + } + if (! BF_MODE_FORCE) { + if (ARGV[1] ~ /\.tf$/ || ARGV[1] ~ /\.trainfuck$/) { + BF_MODE = "trainfuck" + } else if (ARGV[1] ~ /\.bf$/ || ARGV[1] ~ /\.brainfuck$/) { + BF_MODE = "brainfuck" + } + } + if (BF_MODE == "brainfuck") { + BF_EXECUTE = 1 + } +} + +function tf_convert(t) +{ + if (! match(t, /CHUGGA|CHUG|CHOO|CLICK|CLICKETY|CLACK|TICKETSPLEASE|YOURTICKETPLEASE|$/)) { + die("Derailed at input line " FNR, ERR_SYNTAX) + } + pre = substr(t, 1, RSTART - 1) + tok = substr(t, RSTART, RLENGTH) + pst = substr(t, RSTART + RLENGTH) + if (tok == "CHUGGA") { # needs to be first + tok = "-" + return (pre tok tf_convert(pst)) + } + if (tok == "CHUG") { + tok = "+" + return (pre tok tf_convert(pst)) + } + if (tok == "CHOO") { + if (substr(pst, 1, 4) == "CHOO") { + tok = "<" + sub(/CHOO/, "", pst) + } else { + tok = ">" + } + return (pre tok tf_convert(pst)) + } + if (tok == "CLICK" || tok == "CLICKETY") { + tok = "." + return (pre tok tf_convert(pst)) + } + if (tok == "CLACK") { + tok = "," + return (pre tok tf_convert(pst)) + } + if (tok == "TICKETSPLEASE") { + tok = "[" + return (pre tok tf_convert(pst)) + } + if (tok == "YOURTICKETPLEASE") { + tok = "]" + return (pre tok tf_convert(pst)) + } +} + +function usage() +{ + eprint("TRAINFUCK: CHOO CHOO") + eprint("Usage: ") + eprint(EXE_NAME " -h|--help") + eprint(EXE_NAME " [-w WIDTH] [FLAGS...] INPUTFILE [-o OUTPUTFILE]") + eprint() + eprint("Parameters:") + eprint(" INPUTFILE\tThe trainfuck file to process.") + eprint("\t\tIf INPUTFILE ends in .tf or .trainfuck, it will be interpreted") + eprint("\t\tas a TRAINFUCK file; if it ends in .bf or .brainfuck, it will ") + eprint("\t\tbe interpreted as a BRAINFUCK file. Otherwise, the file will ") + eprint("\t\tbe assumed to be the default filetype (" BF_MODE ").") + eprint(" -w WIDTH\tSet the brainfuck output width (default: " BF_WIDTH ").") + eprint(" -o OUTPUTFILE\tWhere to send transpiled brainfuck (default: " BF_OUTPUT ").") + eprint() + eprint("FLAGS:") + eprint(" -h, --help\t\tShow this help") + eprint(" -c, --comments\t\tShow comments" (BF_PRINT_COMMENTS ? " (default)" : "")) + eprint(" -C, --no-comments\tHide comments" (BF_PRINT_COMMENTS ? "" : " (default)")) + eprint(" -z, --compact\t\tShow \"compact\" comments" (BF_COMMENTS_COMPACT ? " (default)" : "")) + eprint(" -Z, --no-compact\tShow \"wide\" comments" (BF_COMMENTS_COMPACT ? "" : " (default)")) + eprint(" -x, --execute\t\tExecute trainspiled brainfuck" (BF_EXECUTE ? " (default)" : "")) + eprint(" -X, --no-execute\tOnly trainspile, do not execute" (BF_EXECUTE ? "" : " (default)")) + exit +} -- cgit 1.4.1-21-gabe81