50 lines
1.2 KiB
Bash
50 lines
1.2 KiB
Bash
#!/usr/bin/env bash
|
|
# BrainF*** interpreter in bash
|
|
if (( ! $# )); then
|
|
printf >&2 'Usage: %s program-file\n' "$0"
|
|
exit 1
|
|
fi
|
|
|
|
# load the program
|
|
exec 3<"$1"
|
|
program=()
|
|
while IFS= read -r line <&3; do
|
|
mapfile -t instr < <(tr -cd '[]<>.,+-' <<<"$line" | sed $'s/./&\\\n/g')
|
|
program+=("${instr[@]}")
|
|
done
|
|
exec 3<&-
|
|
|
|
# parse loops
|
|
loops=()
|
|
matches=()
|
|
for pc in "${!program[@]}"; do
|
|
instr=${program[pc]}
|
|
if [[ $instr == '[' ]]; then
|
|
loops=("$pc" "${loops[@]}")
|
|
elif [[ $instr == ']' ]]; then
|
|
matches[$pc]=${loops[0]}
|
|
matches[${loops[0]}]=$pc
|
|
loops=(${loops[@]:1})
|
|
fi
|
|
done
|
|
|
|
# execute program
|
|
memory=(0)
|
|
mp=0
|
|
pc=0
|
|
while (( pc < ${#program[@]} )); do
|
|
instr=${program[pc]}
|
|
(( pc+=1 ))
|
|
mem=${memory[mp]}
|
|
case "$instr" in
|
|
'[') if (( ! mem )); then (( pc=${matches[pc-1]}+1 )); fi;;
|
|
']') if (( mem )); then (( pc=${matches[pc-1]}+1 )); fi;;
|
|
+) memory[mp]=$(( (mem + 1) % 256 ));;
|
|
-) memory[mp]=$(( (mem - 1) % 256 ));;
|
|
'>') (( mp+=1 )); if (( mp >= ${#memory[@]} )); then memory+=(0); fi;;
|
|
'<') (( mp-=1 )); if (( mp < 0 )); then memory=(0 "${memory[@]}"); mp=0; fi;;
|
|
.) printf %b $(printf '\\%03o' "$mem");;
|
|
,) read -n1 c; memory[mp]=$(LC_CTYPE=C printf '%d' "'$c");;
|
|
esac
|
|
done
|