Created
April 22, 2016 01:27
-
-
Save marcan/347faf7fa09802016d0c253699132539 to your computer and use it in GitHub Desktop.
Brainfuck interpreter in POSIX sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# Brainfuck interpreter implemented in pure POSIX sh builtins only (except I/O) | |
# Tested in bash and busybox ash (getchar uses a bash-specific read) | |
# === I/O === | |
getchar() { | |
# bashism | |
IFS= read -rN 1 a | |
if [ -z "$a" ]; then | |
echo $th | |
else | |
printf %d "'$a" | |
fi | |
} | |
putchar() { | |
printf "\x$(printf %x $1)" | |
} | |
# === Core BF interpreter: pure POSIX shell with builtins only from here on === | |
pl="" | |
pr="" | |
while read -r line; do | |
pl="$pl$line" | |
done <"$1" | |
th="0" | |
tl="" | |
tr="" | |
while :; do | |
case "$pl" in | |
'+'*) | |
th=$((th+1));; | |
'-'*) | |
th=$((th-1));; | |
'.'*) | |
putchar $th;; | |
','*) | |
th=$(getchar);; | |
'>'*) | |
tl="$th $tl" | |
set -- $tr | |
th=${1:-0} | |
shift | |
tr="$*" | |
;; | |
'<'*) | |
tr="$th $tr" | |
set -- $tl | |
th=${1:-0} | |
shift | |
tl="$*" | |
;; | |
'['*) | |
case "$th" in 0) | |
i=1 | |
while :; do | |
tmp="${pl#?}" | |
pr="${pl%"$tmp"}$pr" | |
pl="$tmp" | |
case "$pl" in | |
'['*) i=$((i+1));; | |
']'*) i=$((i-1));; | |
esac | |
case $i in 0) break; esac | |
done | |
esac | |
;; | |
']'*) | |
case "$th" in 0);; *) | |
i=1 | |
while :; do | |
tmp="${pr#?}" | |
pl="${pr%"$tmp"}$pl" | |
pr="$tmp" | |
case "$pl" in | |
']'*) i=$((i+1));; | |
'['*) i=$((i-1));; | |
esac | |
case $i in 0) break; esac | |
done | |
esac | |
;; | |
'') | |
break | |
esac | |
tmp="${pl#?}" | |
pr="${pl%"$tmp"}$pr" | |
pl="$tmp" | |
done |
Thanks for giving me the good idea.
Now I've just found if read
line is \n
-terminated or EOF
-terminated;
IFS= read -r && TERMINATED_WITH=LF || TERMINATED_WITH=EOF
That is, the read
's exit status would be 0 if \n
-terminated, 1 otherwise.
Also your code has:
putchar() {
printf "\x$(printf %x $1)"
}
but the fact is that the format %x
and \xHH
(where HH is hexadecimal) are not in POSIX; octal is available instead.
Thus, you could do this:
putchar(){
(
O="$(printf %o $1)"'
case O in
?) O=00"$O";;
??) O=0"$O";;
????*)
printf '%s' "$0: putchar: assertion failed: (length of O)<4" 1>&2
exit 10
;;
esac
printf '\'"$O"
)
}
putchar(){ ( O="$(printf %o $1)"' case O in ?) O=00"$O";; ??) O=0"$O";; ????*) printf '%s' "$0: putchar: assertion failed: (length of O)<4" 1>&2 exit 10 ;; esac printf '\'"$O" ) }
This is shorter;
printf '\'"$(printf %03o "$1")"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Isn't
read -r
POSIX?The convention is actually to line-buffer it, though. I understand not wanting to use external utilities, but you can implement I/O portably and properly handle EOF using built-ins if you line-buffer input; you can make it work with pure
bash
,dash
,yash
,ash
, andksh
. And if what I mentioned above is true, then the only non-POSIX built-in you need isprintf
(and you can make it compliant with POSIXprintf
). What is this licensed under?