Skip to content

Instantly share code, notes, and snippets.

@makenowjust
Created April 20, 2014 07:07
Show Gist options
  • Save makenowjust/11107311 to your computer and use it in GitHub Desktop.
Save makenowjust/11107311 to your computer and use it in GitHub Desktop.
a brainf*ck interpreter written in pure Makefile (GNU Make)
# basic numbers
n0:=
n1:=+
n2:=+ +
# number operators
add=$(1) $(2)
incr=+ $(1)
sub=$(strip $(wordlist $(words $(call add,$(2),$(n1))),10000,$(1)))
decr=$(wordlist 2,10000,$(1))
mul=$(if $(2),$(1) $(call mul,$(1),$(call decr,$(2))),)
lt =$(if $(2),$(if $(1),$(call lt,$(call decr,$(1)),$(call decr,$(2))),true),)
# more numbers
n4 :=$(call add,$(n2),$(n2))
n16 :=$(call mul,$(n4),$(n4))
n32 :=$(call add,$(n16),$(n16))
n33 :=$(call incr,$(n32))
n64 :=$(call mul,$(n16),$(n4))
n128:=$(call add,$(n64),$(n64))
n127:=$(call decr,$(n128))
n256:=$(call mul,$(n16),$(n16))
n255:=$(call sub,$(n256),$(n1))
# to char
comma:=,
chars:=! " \# $$ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \\ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
to_char=$(if $(call lt,$(n32),$(1)),$(word $(words $(call sub,$(1),$(n32))),$(chars)),$(if $(filter $(words $(1)),32), ,\$(words $(1))))
current_buffer=buffer$(words $(pt))
# commands
# +
define cmd_incr
$(if $(filter $(words $($(current_buffer))),255),\
$(eval $(current_buffer):=),\
$(eval $(current_buffer)+=+))
endef
# -
define cmd_decr
$(if $($(current_buffer)),\
$(eval $(current_buffer):=$(call decr,$($(current_buffer)))),\
$(eval $(current_buffer):=$(n255)))
endef
# >
define cmd_next
$(eval pt+=+)
endef
# <
define cmd_prev
$(if $(pt),\
$(eval pt:=$(wordlist 2,10000,$(pt))),\
$(error buffer under flow!))
endef
# .
define cmd_write
$(if $(filter $(words $($(current_buffer))),10),\
$(info $(output))$(eval output:=),\
$(eval output:=$(output)$(call to_char,$($(current_buffer)))))
endef
# ,
define cmd_read
$(if $(USE_SHELL),\
$(eval $(current_buffer):=$(shell $(read_command))),\
$(error read command (",") is not supported))
endef
define read_command
OLDIFS="$$IFS";
IFS="";
read -n1 x;
if [[ "$$x" = ' ' ]]; then
x=32;
elif [[ "$$x" = $$'
' ]]; then
x=10;
else
x="'$$x";
fi;
IFS="$$OLDIFS";
for i in `seq 1 $$(printf %d $$x)`; do
echo -n "+ ";
done;
endef
# [
define cmd_loop_start
$(if $($(current_buffer)),\
$(eval loops_length+=+)$(eval loops_$(words $(loops_length)):=$(pc)),\
$(call loop_skip,$(zero)))
endef
define loop_skip
$(if $(filter $(current_source),]),\
$(if $(filter $(words $(1)),1),,\
$(eval pc+=+)$(call loop_skip,$(call decr,$(1)))),\
$(if $(filter $(current_source),[),\
$(eval pc+=+)$(call loop_skip,$(call incr,$(1))),\
$(if $(current_source),\
$(eval pc+=+)$(call loop_skip,$(1)),\
$(error missing end of brace))))
endef
# ]
define cmd_loop_end
$(if $(loops_length),\
$(eval pc:=$(call decr,$(loops_$(words $(loops_length)))))$(eval loops_length:=$(call decr,$(loops_length)))$(eval loops:=$(wordlist 1,10000,$(loops))),\
$(error missing start of brace))
endef
echo:=,[.,]
hello:=++++[>+<++++]>++ +++++++. ---. +++++++.. +++.
source:=$(or $(SRC),$(hello))
# convert source to list
source:=$(strip \
$(subst +, + ,\
$(subst -, - ,\
$(subst >, > ,\
$(subst <, < ,\
$(subst ., . ,\
$(subst $(comma), $(comma) ,\
$(subst [, [ ,\
$(subst ], ] ,$(source))))))))))
# execute
current_source=$(word $(words $(pc)),$(source))
char_is=$(filter $(current_source),$(1))
pc:=+
step=$(strip \
$(if $(call char_is,+),$(cmd_incr),\
$(if $(call char_is,-),$(cmd_decr),\
$(if $(call char_is,>),$(cmd_next),\
$(if $(call char_is,<),$(cmd_prev),\
$(if $(call char_is,.),$(cmd_write),\
$(if $(call char_is,$(comma)),$(cmd_read),\
$(if $(call char_is,[),$(cmd_loop_start),\
$(if $(call char_is,]),$(cmd_loop_end),)))))))))
run=$(strip $(if $(current_source),$(step)$(eval pc+=+)$(call run),))
$(call run)
$(info $(output))
all:
@echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment