Perl, 271 chars
@e=qw'die"Hello\x20World!\n" $i-- $i++ $a[$i]-- $a[$i]++ push@s,$p $a[$i]?$p=$s[-1]:pop@s print+chr$a[$i] $a[$i]=ord<>';
$/=\1;@k=(<>..Z,A..Z);
say"# $k[$_]: $e[$_]"for 0..8;say;
say"\$e{\"\Q$k[$_]\E\"}=sub{$e[$_]};"for 0..8;
say'$/=\1;@c=<>;&{$e{$c[$p++]}||sub{}}while$p<@c'
(The code above has some non-essential line breaks inserted for readability. All line breaks may be removed without affecting the behavior, and should not be counted for scoring purposes.)
Run with perl -M5.010
to enable the say
feature. Reads the input program from standard input (or from a file given as a command line argument), prints an interpreter to standard output. The output is also a Perl script, and should be executed the same way.
The (rudimentary) language spec is included as comments at the beginning of the interpreter, and is formatted as a list of characters and the Perl commands each of them will execute. In the spec, $p
stands for the program counter (which points to the next instruction to execute), @s
for the loop stack, $i
for the tape index and @a
for the tape.
Basically, the language executed by the interpreter is exactly what ugoren suggested in the comments: a simple variant of Brainf*ck, with an extra command that prints Hello World!
and ends the program. This extra command is mapped to the first character of the input program, while the other commands are mapped to subsequent letters. All unrecognized characters are ignored.
For example, using the input AAAAAA
, the output will look like this:
# A: die"Hello\x20World!\n"
# B: $i--
# C: $i++
# D: $a[$i]--
# E: $a[$i]++
# F: push@s,$p
# G: $a[$i]?$p=$s[-1]:pop@s
# H: print+chr$a[$i]
# I: $a[$i]=ord<>
$e{"A"}=sub{die"Hello\x20World!\n"};
$e{"B"}=sub{$i--};
$e{"C"}=sub{$i++};
$e{"D"}=sub{$a[$i]--};
$e{"E"}=sub{$a[$i]++};
$e{"F"}=sub{push@s,$p};
$e{"G"}=sub{$a[$i]?$p=$s[-1]:pop@s};
$e{"H"}=sub{print+chr$a[$i]};
$e{"I"}=sub{$a[$i]=ord<>};
$/=\1;@c=<>;&{$e{$c[$p++]}||sub{}}while$p<@c
Running the resulting code above with the same input produces the expected output:
Hello World!
whereas running the same code with the input EEEEEEFDCEEEEBGCFDCECEEEEBBGCEEFDCEHBG
(transliterated from this answer) prints:
abcdefghijklmnopqrstuvwxyz
Ps. To convert normal Brainf*ck code to the variant understood by this interpreter, you can use the transliteration:
tr/<>\-+[].,/B-I/
where B-I
should be replaced by the appropriate range of command letters as per the generated spec.
Notes on the BF implementation:
The tape is single-ended, with the read/write head starting at cell 0. Moving the head to a negative position might not cause an error, but will produce weird effects related to Perl's array indexing and should be considered undefined behavior.
Cell values do not wrap around. Technically, each cell is stored as a double, meaning that at some point the value will saturate and further inc/decrements will have no effect. You'll die of boredom before that happens, though.
Character I/O depends on Perl's I/O layers. By default, input values are unsigned bytes, and attempting to output values outside the range 0 – 255 gives a warning message. Passing the -CS
switch to Perl will enable Unicode I/O (encoded as UTF-8), though. Input at EOF yields a zero. To supply both a program and its input on stdin, separate them with a Ctrl+D character ("\cD"
or "\x05"
).
Edit: I later realized that there's a major difference between the language I implement above and real Brainf*ck: in my language, the loop condition is checked at the end of the loop, rather than at the beginning, which means that every loop executes at least once. In effect, loops in my language behave like do
–while
loops in C-ish languages, whereas those in normal BF behave like plain while
loops.
As it happens, the difference doesn't matter for the example program above, since all its loops run at least once anyway. What I'm not quite sure about is whether the proof of BF's Turing-completeness can still be made to apply to my variant of the language or not. It might be possible, but it's going to be tricky without any easy way to implement an if
statement.
I suppose I could fix my program to behave more like normal BF, but it would be a pain in the ass to do without a major redesign of the execution model. :(
hello_world
can be 2 bytes, any solution pretty much has to bake"Hello World!"
into the language definition (or be unnecessarily long to choose not to in the greater-than-that case). How about requiring that the input has at least 13 (string length + 1) distinct characters? (I think this is an interesting problem regardless of whether that change is made.) \$\endgroup\$