Exit code string output

You must write a program or function that, when given a nonempty string S of N printable ASCII characters, outputs a program that will exit with exit code C, where C is the ASCII codepoint at position 0 in S. This program you write will additionally output a program P, such that, when run, it exits with exit code C′, where C′ is the ASCII codepoint at position 1 in S. Program P will output another program P′. This process repeats until there are no characters left in S. After this is done, you must output nothing, followed by an optional newline; and should exit with exit code 0.

The characters between 0x20 and 0x7e inclusive.

Some more rules:

• Self-modifying programs are not allowed: you must output the source to STDOUT (or, return value initially)
• You may not read your own source code.

The shortest such program in bytes will win.

For some rudimentary testing, this ruby script can be used. (The first argument is the way you invoke the script, the second is the program, and the third is the input string.)

Hypothetical Example

Say the program is FOO. When given the string "ABC", it outputs BARA. This program exits with code 65 and outputs BARB. This in turn exits with code 66 and ouputs BARC. This program exits with code 67 and outputs BAR!. This outputs nothing, and exits with code 0.

• Would doing this count as exit codes in Forth? Change the parameter to see other OS errors. 0 is Success. tio.run/nexus/… – mbomb007 May 10 '17 at 14:04
• @mbomb007 I don't know much about Forth. Is that how one would conventionally use an "error code" in Forth? – Conor O'Brien May 10 '17 at 14:11
• It depends if your goal is to have OS-level error codes. If only the number matters, you can just do something like 33 throw to throw an arbitrary number. You use negatives for OS-level, and the offset is -512. Idk much either, but I'm looking here: complang.tuwien.ac.at/forth/gforth/Docs-html/… – mbomb007 May 10 '17 at 14:52

Python 2, 126101 94 bytes

In the process of making this, I found that Python code may not contain literal NUL bytes.

lambda i,s='''i=%r;s=%r
try:print s%%(i[1:],s,i[0])
except:0
exit(ord(%r))''':s%(i[1:],s,i[0])


Try it online (shows the exit code in the Debug info)

Note that each of the non-empty programs below has a trailing linefeed.

For input Hello, the above outputs:

i='ello';s='i=%r;s=%r\ntry:print s%%(i[1:],s,i[0])\nexcept:print s%%(0,s,"\\0")*(i>0)\nexit(ord(%r))'
try:print s%(i[1:],s,i[0])
except:0
exit(ord('H'))


which prints

...

which prints

i='o';s='i=%r;s=%r\ntry:print s%%(i[1:],s,i[0])\nexcept:print s%%(0,s,"\\0")*(i>0)\nexit(ord(%r))'
try:print s%(i[1:],s,i[0])
except:0
exit(ord('l'))


which prints

i='';s='i=%r;s=%r\ntry:print s%%(i[1:],s,i[0])\nexcept:print s%%(0,s,"\\0")*(i>0)\nexit(ord(%r))'
try:print s%(i[1:],s,i[0])
except:0
exit(ord('o'))


which prints nothing (the empty program)

which prints nothing and exits with code 0.

Python 3, 77 bytes

p='exit(0)'
for c in input()[::-1]:p='print(%r);exit(ord(%r))'%(p,c)
print(p)


This code takes input from STDIN and outputs the first program to STDOUT.

If the input is ABCDE, the results are

 0 print('print(\'print(\\\'print("print(\\\\\\\'exit(0)\\\\\\\');exit(ord(\\\\\\\'E\\\\\\\'))");exit(ord(\\\\\\\'D\\\\\\\'))\\\');exit(ord(\\\'C\\\'))\');exit(ord(\'B\'))');exit(ord('A'))
65 print('print(\'print("print(\\\'exit(0)\\\');exit(ord(\\\'E\\\'))");exit(ord(\\\'D\\\'))\');exit(ord(\'C\'))');exit(ord('B'))
66 print('print("print(\'exit(0)\');exit(ord(\'E\'))");exit(ord(\'D\'))');exit(ord('C'))
67 print("print('exit(0)');exit(ord('E'))");exit(ord('D'))
68 print('exit(0)');exit(ord('E'))
69 exit(0)
0


where each line contains the exit code and the output of the previously executed program (the first line is the first program).

• Your answer is like mine... just that you did it in the opposite direction... I'm facepalming myself for that. – Leaky Nun May 1 '17 at 17:24
• 67 bytes – mbomb007 May 1 '17 at 17:30
• @LeakyNun yeah, but I did not use your answer as a starting point, if that's what you mean – vaultah May 1 '17 at 18:06
• I'm just saying I should have thought of that. – Leaky Nun May 1 '17 at 18:07
• @vaultah You going to golf yours to 67 bytes, or should I post it as a separate answer? – mbomb007 May 1 '17 at 21:58

Python 3, 115108 100 bytes

i=input()
r="%s"
k=""
for c in i:r%="print(%s\"%%s%s\");exit(%i)"%(k,k,ord(c));k+=k+"\\"
print(r%"")


Try it online!

For the input Hello, the program prints:

print("print(\"print(\\\"print(\\\\\\\"print(\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\");exit(111)\\\\\\\");exit(108)\\\");exit(108)\");exit(101)");exit(72)


The program above prints:

print("print(\"print(\\\"print(\\\\\\\"\\\\\\\");exit(111)\\\");exit(108)\");exit(108)");exit(101)


and exits with code 72.

Try it online!

The program above prints

print("print(\"print(\\\"\\\");exit(111)\");exit(108)");exit(108)


and exits with code 101.

Try it online!

The program above prints:

print("print(\"\");exit(111)");exit(108)


and exits with code 108.

Try it online!

The program above prints:

print("");exit(111)


and exits with code 108.

Try it online!

The program above prints nothing and exits with code 111.

Try it online!

The empty program prints nothing and exits with code 0.

Try it online!

• A TIO link for the empty program? That's dedication for you! – Neil May 1 '17 at 19:23

C, 156 bytes

char*s="char*s=%c%s%c;n=%d;char*t=%c%s%c;main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}";main(i,t)char**t;{printf(s,34,s,34,0,34,t[1],34);}


Try it online! (Open the debug-tab to see the exit code.)

Takes input as a command line argument.

For input "ABC", this outputs the program

char*s="char*s=%c%s%c;n=%d;char*t=%c%s%c;main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}";n=0;char*t="ABC";main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}


which returns 65 and outputs

char*s="char*s=%c%s%c;n=%d;char*t=%c%s%c;main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}";n=1;char*t="ABC";main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}


which returns 66 and outputs

char*s="char*s=%c%s%c;n=%d;char*t=%c%s%c;main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}";n=2;char*t="ABC";main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}


which returns 67 and outputs

char*s="char*s=%c%s%c;n=%d;char*t=%c%s%c;main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}";n=3;char*t="ABC";main(){n<strlen(t)&&printf(s,34,s,34,n+1,34,t,34);return t[n];}


which outputs nothing and returns 0.

• @mbomb007 Thanks, it's fixed now (and it got shorter in the process). – Steadybox May 1 '17 at 16:46
• Gotta love it when that happens. – mbomb007 May 1 '17 at 16:49

Python 2, 67 bytes

Based on this answer, but modified to use Python 2, with a trivial program 0 to print nothing and exit.

p=0
for c in input()[::-1]:p='print %r;exit(ord(%r))'%(p,c)
print p


Try it online

RPL, 73 bytes

With hp8 code page.

Turn on your HP48 or similar, or fire droid48. Do not forget to -52 SF for better visualization of the stack. I assume you have already pushed the string, e.g. "ABC", on the stack. Then key in the following function:

→ x«{LAST}x{DUP NUM 3ROLLD 2OVER SIZE DUP{SUB 2SWAP PUT}{4DROPN}IFTE}+ +»


(For convenience I suggest to push the α key twice before typing anything, hence locking alpha input mode. Latter just use the DEL key to cancel the automatically inserted closing delimiters. Simply use the ENTER key to validate. Do not forget the space after the stab operator.)

This function immediately pushes on the stack a self-modifying program, under the form of a list. (But the function above does not modify itself). As the L in RPL originally stands for LISP, pressing the EVAL key will indeed evaluate this program. It returns the exit code on stack level two, and leaves itself, modified (yes, here it is questionable), for a latter EVAL. So press EVAL repeatedly until the program finally stops to drop itself on stack level one. The final exit code 0 thus appears on level one, with past exit codes above. If you forgot to -52 SF, you can navigate in the stack after each EVAL by pressing the ▴ key (leave this navigation mode with the ON key). The above function does accept strings with 0x0 chars inside, to create such strings 0 CHR and + are your friends. The self modification consists of removing the used char from the embedded string (the SUB 2 SWAP PUT branch). Hence the dropped program is shorter after each EVAL. The 4 DROPN branch ensures the output nothing instruction from the OP is respected, dropping among others the program itself. Of course all this assumes your note a -55 SF adept. Users of -55 SF shall be banned. Forever.

I assume an RPL/2 solution exists, and could feature a real unix exit code, but afaik RPL/2 has limited introspection, and cannot evaluate lists.

• I don't think using self-modifying code counts as a valid quine according to our consensus, since it has access to its own source code. I'll ask the OP in a comment. See related meta posts: What counts as a proper quine?; Does using SMBF count as a cheating quine? <- this one is the one that applies – mbomb007 May 1 '17 at 21:39
• Only the generated program is self-modifying, not the function that answers the challenge. But I agree, this is questionable ! Added some edits to highlight this. – Nacre May 1 '17 at 23:37

sed, 467 461 bytes

Character codes are hard:

s:^:Y:
:b
s:ZY[ (2<FPZdnx]:0_Y:
s:ZY[ )3=GQ[eoy]:1_Y:
s:ZY[ *4>HR\fpz]:2_Y:
s:ZY[]!+5?ISgq{]:3_Y:
s:ZY[",6@JT^hr|]:4_Y:
s:ZY[-#7AKU_is}]:5_Y:
s:ZY[$.8BLVjt~]:6_Y: s:ZY[%/9CMWaku]:7_Y: s:ZY[&0:DNXblv]:8_Y: s:ZY['1;EOYcmw]:9_Y: s:Y[ -']:3Z&: s:Y[(-1]:4Z&: s:Y[2-9:;]:5Z&: s:Y[<=>?@A-E]:6Z&: s:Y[F-O]:7Z&: s:Y[P-Y]:8Z&: s:Y[]Z\-abc]:9Z&: s:Y[d-m]:10Z&: s:Y[n-w]:11Z&: s:Y[xyz{-~]:12Z&: tb s/([^_]+)_Y$/ q\1/
:
s/[/\]/\\&/g
s/([^_]+)_ (.*)/ s\/^\/\2\/;q\1/
/^\S/b


Try it online!

Otherwise, the logic is rather straightforward: (1) escape special characters (there're two), (2) wrap in an additional s/^/…/;q\1 layer, (3) repeat.

Here's the output for hello:

 s/^/s\/^\/s\\\/^\\\/s\\\\\\\/^\\\\\\\/q111\\\\\\\/;q108\\\/;q108\/;q101/;q104


And a little script that I used:

#!/bin/bash
set -uo pipefail
IFS=$'\n' P=$(echo $1 | sed -rf q.sed) echo$P

echo $1 | od -An -tuC for char in$(echo $1 | sed 's:.:&\n:g'); do P=$(echo | sed $P) printf ' %3d'$?
done

• You don't need to worry about newlines, as the post says you'll only be given characters between 0x20 and 0x7E. Nice solution! :) – Conor O'Brien May 14 '17 at 14:38
• @ConorO'Brien Oh, right. Thanks! – eush77 May 14 '17 at 14:48

PowerShell, 172 156 bytes.

param($i)$s=@'
if($i='{0}'){{$s=@'
{1}'@
$s-f($i-replace'^.'-replace"'","''"),"$sn"$host.setshouldexit($i[0]) }} exit '@$s-f$i.replace("'","''"),"$sn"


The h3l}'{l0 input will result the next output

if($i='h3l}''{l0'){$s=@'
if($i='{0}'){{$s=@'
{1}'@
$s-f($i-replace'^.'-replace"'","''"),"$sn"$host.setshouldexit($i[0]) }} exit '@$s-f($i-replace'^.'-replace"'","''"),"$sn"
$host.setshouldexit($i[0])
}
exit


Which in the own turn will output

if($i='3l}''{l0'){$s=@'
if($i='{0}'){{$s=@'
{1}'@
$s-f($i-replace'^.'-replace"'","''"),"$sn"$host.setshouldexit($i[0]) }} exit '@$s-f($i-replace'^.'-replace"'","''"),"$sn"
$host.setshouldexit($i[0])
}
exit


The last run will output nothing and exit code will be 0.

if($i=''){$s=@'
if($i='{0}'){{$s=@'
{1}'@
$s-f($i-replace'^.'-replace"'","''"),"$sn"$host.setshouldexit($i[0]) }} exit '@$s-f($i-replace'^.'-replace"'","''"),"$sn"
$host.setshouldexit($i[0])
}
exit