19
2
This is Weekly Challenge #2. Theme: Translation
Write a program or function that takes in source code for a program in Prelude and outputs code for an equivalent program in Befunge-93. For the program to be equivalent, it should, for any given input, produce the same output as the Prelude program, and halt if and only if the Prelude program halts.
Input language: Prelude
Python interpreter:
#!/usr/bin/python
import sys
NUMERIC_OUTPUT = True
NUMERIC_INPUT = True
try:
filename = sys.argv[1]
except:
print "Usage:", sys.argv[0], "<filename>"
raise SystemExit
try:
inFile = file(filename)
except:
print "Error when opening", filename
raise SystemExit
# code is kept as a list of voices, each voice a string
code = []
firstLine = True
eof = False
while not eof:
# read batch of lines
batch = []
while 1:
line = inFile.readline()
if line == '': eof = True
if line == '' or line.rstrip() == '*':
break
batch.append(line.rstrip())
maxLen = max([len(b) for b in batch])
batch = [b + ' '*(maxLen - len(b)) for b in batch]
if firstLine:
code = batch
firstLine = False
else:
if len(batch) != len(code):
print "Error in the program: number of voices changes"
raise SystemExit
for i in range(len(batch)):
code[i] += batch[i]
class Stack:
def __init__(self):
self.data = []
def push(self, value):
if self.data or value != 0: self.data.append(value)
def drop(self):
if self.data: self.data.pop()
def top(self):
if self.data: return self.data[-1]
return 0
def pop(self):
value = self.top()
self.drop()
return value
numVoices = len(code)
numInstructions = len(code[0])
stacks = [Stack() for x in range(numVoices)]
topValues = [0 for x in range(numVoices)]
# establish loop couplings
loopStack = []
loops = {}
for cp in range(numInstructions):
curr = [voice[cp] for voice in code]
if curr.count('(') + curr.count(')') > 1:
print "Error in the program: More than one bracket; position", cp
raise SystemExit
if '(' in curr:
loopStack.append((cp, curr.index('(')))
if ')' in curr:
if not loopStack:
print "Error in the program: extraneous closing bracket; position", cp
raise SystemExit
openingPosition, openingVoice = loopStack.pop()
loops[openingPosition] = cp
loops[cp] = openingPosition, openingVoice
if loopStack:
print "Error in the program: not enough closing brackets"
raise SystemExit
# now, actually execute the program
cp = 0 # code pointer
while cp < numInstructions:
# technically we're supposed to shuffle our voices to make sure to perform IO
# in random order, but screw that for now
next_cp = cp+1 # can be modified by ( )
for voice in range(numVoices):
i = code[voice][cp] # current instruction
if i == '^':
stacks[voice].push(topValues[(voice-1) % numVoices])
elif i == 'v' or i == 'V':
stacks[voice].push(topValues[(voice+1) % numVoices])
elif i == '+':
stacks[voice].push(stacks[voice].pop() + stacks[voice].pop())
elif i == '-':
b = stacks[voice].pop()
a = stacks[voice].pop()
stacks[voice].push(a-b)
elif i == '#':
stacks[voice].drop()
elif i == '?':
if NUMERIC_INPUT:
try: num = int(raw_input())
except ValueError: num = 0
stacks[voice].push(num)
else:
char = sys.stdin.read(1)
if not char: char = '\0'
stacks[voice].push(ord(char))
elif i == '!':
if NUMERIC_OUTPUT:
print stacks[voice].pop()
else:
sys.stdout.write(chr(stacks[voice].pop()))
elif i == '(':
if stacks[voice].top() == 0:
next_cp = loops[cp] + 1
elif i == ')':
openingPosition, openingVoice = loops[cp]
if topValues[openingVoice] != 0:
next_cp = openingPosition + 1
elif i in '0123456789':
stacks[voice].push(int(i))
topValues = [stacks[i].top() for i in range(numVoices)]
cp = next_cp
A Prelude program consists of a number of "voices" which execute instructions simultaneously. The instructions for each voice are on a separate line. Each voice has a separate stack, which is initialized with an infinite amount of zeroes. Execution begins at the leftmost column, and advances one column to the right each tick, except when influenced by )
or (
instructions. The program terminates when the last column is reached.
Prelude spec for this challenge:
Digits 0-9 Push onto the stack a number from 0 to 9. Only single-digit
numeric literals can be used.
^ Push onto the stack the top value of the stack of the above
voice.
v Push onto the stack the top value of the stack of the below
voice.
# Remove the top value from the stack.
+ Pop the top two integers from the stack and push their sum.
- Pop the top two integers from the stack, subtract the topmost
from the second, and push the result.
( If the top of the stack is 0, jump to the column after the
matching `)` after the current column executes.
) If the top of the stack is not 0, jump to the column after
the matching `(` after the current column executes.
? Read an integer from STDIN.
! Pop one value from the stack and print it to STDOUT as an
integer.
<space> No-op
Notes
v
and^
act cyclically, so thev
on the bottom voice will copy the stack element of the top voice, and^
on the top voice will copy from the bottom voice. Corollary: Bothv
and^
duplicate the top of the stack in a single-voice program.- A
(
and its matching)
may be located on different lines. However, a)
will always look at the stack of the voice where the corresponding(
was placed, not the stack where the)
itself is placed. - The values produced by the
^
andv
instructions operate on the values present prior to the completion of any other operations in the same column. ?
and!
operate differently from the specification found on esolangs.org, so be be sure to test with the slightly modified interpreter provided in this post.
Input is guaranteed to have:
- Matching parentheses
- No more than one parenthesis in a column
- Same number of characters on each line
- At least one line
- No column with more than one I/O (
!
or?
) instruction - One linefeed character after the instructions for each voice
- No characters other than the ones mentioned above
Output language: Befunge-93
Befunge is a stack-based language whose program counter (PC; a pointer to the current instruction) moves freely on a two-dimensional grid. It start in the top left corner, moving to the right. The playfield is toroidal, i.e. PC movement wraps around both edges. Befunge also has a stack which is initialised to an infinite number of zeroes. Befunge has the following operations:
Digits 0-9 Push onto the stack a number from 0 to 9. Only single-digit
numeric literals can be used.
+ Pop the top two integers from the stack and push their sum.
- Pop the top two integers from the stack, subtract the topmost
from the second, and push the result.
* Pop the top two integers from the stack and push their product.
/ Pop the top two integers from the stack, divide the second by
the topmost, and push the result. Rounds down.
% Pop the top two integers from the stack, divide the second by
the topmost, and push the remainder.
! Pop a value, push 1 if it's zero, push 0 otherwise.
` Pop the top two integers from the stack, push 1 if the second
is greater than the topmost, push 0 otherwise.
> Set PC direction to right.
< Set PC direction to left.
^ Set PC direction to up.
v Set PC direction to down.
? Set PC direction randomly.
_ Pop a value, if it's 0 set PC direction to right, left otherwise.
| Pop a value, if it's 0 set PC direction to down, up otherwise.
" Toggle string mode. While in string mode, push each character's
ASCII value instead of executing it.
: Duplicate top stack element.
\ Swap top two stack elements.
$ Pop and discard top stack element.
. Pop a value and output it as an integer. Whitespace not included.
, Pop a value and output the corresponding ASCII character.
# Jump over the next instruction.
g Pop y, pop x, push the character at coordinate (x,y).
p Pop y, pop x, pop v, set character at coordinate (x,y) to v.
& Read an integer from STDIN and push it.
~ Read a character from STDIN and push it ASCII value.
@ End program.
You may assume the following characteristics of the Befunge-93 compiler/interpreter:
- Integers are unlimited-precision.
- It allows grids of any size.
- Grid coordinates (for
g
andp
) are 0-based.
Scoring
In order to prevent submissions which simply produce a Prelude interpreter in Befunge and hardcode the Prelude source into it, the goal will be to minimise the size of the resulting Befunge source code.
Below are provided a number of Prelude programs. Your translator will be run on all of these. Your score is the sum of the sizes of the Befunge programs, provided all of them are valid.
Your translator should not be optimised specifically towards these test cases (e.g. by hardcoding handwritten Befunge programs for them). If I suspect any answer of doing so, I reserve the right to change inputs or create additional ones.
Sample Inputs
Print n-1
down to 0
:
?(1-^!)
Logical AND:
? (0)
?(0 )
1 !
Logical OR:
? (0)
? (0)
1 1 !
Check parity of input (i.e. modulo 2) of nonnegative number:
?(1-)
^ v
v1-^^-!
Square the input:
^
^+ !
?(1-)
Print the nth Fibonacci number, where n = 0
corresponds to 0 and n = 1
corresponds to 1:
0 v+v!
1 ^
?(1-)
Signum:
1) v # - !
vv (##^v^+)
?(# ^ ##
Division for non-negative inputs:
1 (# 1) v # - 1+)
vv (##^v^+)
? v-(0 # ^ #
?
1+ 1-!
Of course, your program must exhibit the same behavior for all cases, even if the sample program's behavior for negative numbers is not specified.
Finally, your translator should not be unreasonably long:
- It must be contained inside a Stack Exchange post
- It should process the sample inputs in under 10 minutes on a typical desktop computer.
Note that a numeric input for Prelude or Befunge is given as an optional minus sign followed by one or more decimal digits, followed by a newline. Other input is undefined behavior.
You may write your translator in any language. Shortest translated Befunge code wins.
Leaderboard
- Sp3000: 16430 bytes
I don't understand: "Push onto the stack the top value on the stack of the above voice." Doesn't it has to be: "Push onto the stack the top value of the stack of the above voice." – Def – 2015-01-11T07:59:18.803
It says prelude executes voices simultaneously, does that mean they're really executed on a separate thread or can I just executed the first commands on all the voices (top to bottom) then the second commands and so on. – Def – 2015-01-11T08:08:17.953
@Deformyer I changed it from "on" to "of", but I thought "top value on the stack" wasn't wrong either. As for simultaneity, no you don't need to actually interpret them in parallel. What's important is that they all act on the previous state of the stacks, and no operation in a given column can affect any other operation in that column. – Martin Ender – 2015-01-11T11:35:47.097
Don't several of the test cases violate "No column with more than one I/O (! or ?) instruction?" – Fuwjax – 2015-01-14T00:06:23.930
@proudhaskeller The
1
is inside a loop, so it may not be pushed. A 0 can come from the infinite amount of 0s that start out on the stacks. – feersum – 2015-01-14T02:45:48.603@Fuwjax, haskeller Fixed simultaneous I/O. – feersum – 2015-01-14T02:46:06.953
@proudhaskeller what do you mean by the interpreter rejecting? Matching is done as if all of the parentheses were collapsed into one row. – feersum – 2015-01-14T02:49:36.870
@feersum i think it fails if the column of the closing paren is smaller than the column of the opening one – proud haskeller – 2015-01-14T05:48:04.710
@proudhaskeller Yes, looping affects all of the voices, so they always stay together on one column. Can you give a specific example of your interpreter error? – feersum – 2015-01-14T08:49:00.927
@feersum
(\n)
for example – proud haskeller – 2015-01-14T13:51:04.643@proudhaskeller That violates the rule
No more than one parenthesis in a column
– feersum – 2015-01-14T14:29:17.933@feersum sorry, the spaces were removed.
\s\s(\n)
.\s
is a space. – proud haskeller – 2015-01-14T14:29:55.933Could the winning criterion be clarified? I suspect it's intended to be the size of the program when stored as a text file with no trailing spaces on any line. But I can imagine other reasonable meanings of "size" for Befunge programs, like the rectangular area of the program or the number of non-whitespace characters. – Runer112 – 2015-01-14T15:48:47.203
@proudhaskeller The opening paren has to be to the left of the closing one. – feersum – 2015-01-14T23:06:34.027
@Runer112 Your first interpretation is correct. – feersum – 2015-01-14T23:07:21.073
I've been thinking about how to do this. I started trying to make a Prelude interpreter in Befunge. Keeping track of a variable number of stacks would be really hard. – KSFT – 2015-01-17T18:42:25.700