4
\$\begingroup\$

Given a file named hello_world that contains 2-100 bytes of printable ASCII, create a Turing-complete language in which hello_world is a valid program that prints "Hello World!". The output should be two files: a compiler or interpreter, and a text file with the language spec.

Rules

  • Shortest code wins. Length of (any of the) output doesn't matter.
  • The generated compiler/interpreter must tokenize hello_world into at least two tokens, neither of which are ignored, and can't care about the filename.
  • Your program can't use external resources (no cloning its own compiler).
  • The generated code can be in any language, and can compile to any language.
\$\endgroup\$
7
  • 7
    \$\begingroup\$ I doubt if you'll get anything interesting. It's just too hard. Most likely, any solution will just be a way to cheat your rules (e.g. "Compile" the first character to print "Hello, world", the second to a comment delimiter, map all else to BrainF**k). \$\endgroup\$
    – ugoren
    Commented Nov 18, 2012 at 19:14
  • \$\begingroup\$ That's basically what I'm picturing--although I'd consider treating something as a comment delimiter to be ignoring it. It's still non-trivial because you have to avoid collisions between the arbitrary input file and your BrainF**k syntax. And of course writing the shortest possible program that outputs an interpreter isn't, as far as a I know, a solved problem even without the additional constraints. \$\endgroup\$
    – histocrat
    Commented Nov 18, 2012 at 19:24
  • 2
    \$\begingroup\$ Given that 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\$
    – Kevin Reid
    Commented Nov 18, 2012 at 21:11
  • \$\begingroup\$ Also, this definition encourages the language spec to be written gratuitously tersely. How about allowing the spec to be derived from a separate template file whose length is not counted? To avoid gimmicks, the requirement would be that no information in the template file is used in the generation of the interpreter/compiler. \$\endgroup\$
    – Kevin Reid
    Commented Nov 18, 2012 at 21:13
  • \$\begingroup\$ I'm not saying the challenge is trivial. I'm saying it's too hard. You'll get an answer only IF someone finds a trick, that follows your rules to the letter, but makes it uninteresting. \$\endgroup\$
    – ugoren
    Commented Nov 19, 2012 at 5:43

2 Answers 2

3
\$\begingroup\$

Let's give it a try (and let everyone else copy it)

Tcl

set f [open hello_world rb]
set h [split [read $f] {}]
for {set i 0} {$i<256} {incr i} {if {[set c [format %c $i]] ni $h} break}
set f [open i w]
puts $f [string map [list @1 [lindex $h 0] @2 [lindex $h 1] @3 $c] {set s 0;set i 0
set f [open [lindex $argv0] rb]
set c [split [read $f] {}]
foreach d $c {incr i
if {$d eq @1 && $s==0} {puts -nonewline "Hello ";incr s}
if {$d eq @2 && $s==1} {puts "World!"}
if {$d eq @3} {eval [join [lrange $c $i end] {}];break}}}]
puts [open d wb] [string map [list @1 [list [lindex $h 0]] @2 [list [lindex $h 1]] @3 [list $c]] {At the beginning the state is 0. Commands:
@1: If the state is 0, prints "Hello " without newline, increment the state.
@2: If the state is 1, prints "World!" with newline, flushes stdout and increases the state.
@3: Treats the rest of the file as Tcl.}]
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Your use of eval is forbidden by the rule "Your program can't use external resources (no cloning its own compiler)." (Make no mistake; this is not an easy challenge.) \$\endgroup\$
    – breadbox
    Commented Aug 1, 2013 at 20:00
  • 3
    \$\begingroup\$ I don't clone the compiler, nor do I use external resources. About (ab)using the interpreter... well, I may do that. \$\endgroup\$ Commented Aug 1, 2013 at 20:02
2
\$\begingroup\$

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 dowhile 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. :(

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.