fix Makefile, README.md and bash script for 2001/herrmann1
This commit is contained in:
1 parent
7621291e2f
commit
bad0cd868f
3 files changed
+556
-469
No files matched your search
+258
-169
@@ -1,18 +1,9 @@
|
||||
#!/usr/bin/env make
|
||||
#
|
||||
# IOCCC 2001 entry Makefile
|
||||
# IOCCC 2001 winning entry - herrmann1
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# An Historical Makefile NOTE:
|
||||
#
|
||||
# The orig.Makefile, if it exits, is the original Makefile before this IOCCC
|
||||
# year directory was reworked so that entries are in separate directories.
|
||||
# This also applies to any orig.ansi.mk and orig.common.mk files.
|
||||
#
|
||||
# Several Makefiles were modified / formed in 2022, and as such it may have
|
||||
# elements that are not historically compatible with way entries were built.
|
||||
#
|
||||
# Regarding individual entries:
|
||||
#
|
||||
# IOCCC winning entry code may not work on your system. Even with standards,
|
||||
@@ -45,192 +36,290 @@
|
||||
# See: http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
|
||||
SHELL= /bin/sh
|
||||
CAT= cat
|
||||
RM= rm
|
||||
SED= sed
|
||||
TRUE= true
|
||||
#CINCLUDE= -I ${HOMEBREW_PREFIX}/include/SDL2
|
||||
|
||||
# optimization
|
||||
#############################
|
||||
# shell used by this Makefile
|
||||
#############################
|
||||
#
|
||||
# Most compiles will safely use -O2. Some can use only -O.
|
||||
# A few compilers have broken optimizers and thus you may
|
||||
# not want anything.
|
||||
SHELL= bash
|
||||
|
||||
#######################
|
||||
# common tool locations
|
||||
#######################
|
||||
#
|
||||
include ../../var.mk
|
||||
|
||||
# Set X11_LIBDIR to the directory where the X11 library resides
|
||||
#
|
||||
#X11_LIBDIR= /usr/X11R6/lib
|
||||
#X11_LIBDIR= /usr/X11/lib
|
||||
X11_LIBDIR= /opt/X11/lib
|
||||
|
||||
# Set X11_INCLUDEDIR to the directory where the X11 include files reside
|
||||
#
|
||||
#X11_INCDIR= /usr/X11R6/include
|
||||
#X11_INCDIR= /usr/X11/include
|
||||
X11_INCDIR= /opt/X11/include
|
||||
|
||||
# Common C compiler warnings to silence
|
||||
#
|
||||
CSILENCE=
|
||||
#CSILENCE= -Wno-implicit-int
|
||||
|
||||
# Common C compiler warning flags
|
||||
#
|
||||
#CWARN= ${CSILENCE}
|
||||
#CWARN= -Wall ${CSILENCE}
|
||||
#CWARN= -Wall -Wextra ${CSILENCE}
|
||||
CWARN= -Wall -Wextra -pedantic ${CSILENCE}
|
||||
|
||||
# Compiler standard
|
||||
#
|
||||
#CSTD=
|
||||
#CSTD= -ansi
|
||||
CSTD= -std=gnu99
|
||||
|
||||
# Compiler bit architecture
|
||||
#
|
||||
# Some entries require 32-bitness:
|
||||
# ARCH= -m32
|
||||
#
|
||||
# Some entries require 64-bitness:
|
||||
# ARCH= -m64
|
||||
#
|
||||
# By default we assume nothing:
|
||||
#
|
||||
ARCH=
|
||||
|
||||
# Defines that are needed to compile
|
||||
#
|
||||
CDEFINE=
|
||||
#CDEFINE= -DIOCCC
|
||||
|
||||
# Include files that are needed to compile
|
||||
#
|
||||
CINCLUDE=
|
||||
#CINCLUDE= -include stdlib.h
|
||||
#CINCLUDE= -include stdio.h
|
||||
#CINCLUDE= -include stdlib.h -include stdio.h
|
||||
#CINCLUDE= -I ${X11_INCDIR} -I ${X11_INCDIR}/X11
|
||||
#CINCLUDE= `${SDL2_CONFIG} --cflags`
|
||||
|
||||
# Optimization
|
||||
#
|
||||
# Most compiles will safely use -O2. Some can use only -O1 or -O.
|
||||
# A few compilers have broken optimizers or this entry make break
|
||||
# under those buggy optimizers and thus you may not want anything.
|
||||
# Reasonable compilers will allow for -O3.
|
||||
#
|
||||
#OPT=
|
||||
#OPT= -O
|
||||
OPT= -O2
|
||||
#OPT= -O1
|
||||
#OPT= -O2
|
||||
OPT= -O3
|
||||
|
||||
# flags for ANSI compiles
|
||||
# Default flags for ANSI C compilation
|
||||
#
|
||||
# NOTE: Some ANSI compilers make use of -Xa to turn on ANSI mode,
|
||||
# others such as gcc may want -ansi, others (Compaq Tru64 Unix)
|
||||
# want -std1, and others may want nothing at all.
|
||||
# Adjust the CFLAGS line as needed.
|
||||
#
|
||||
# NOTE: Some compilers cannot optimize, or optimize some entries
|
||||
# incorrectly. You might want to turn on -O to see if your
|
||||
# compiler can handle them.
|
||||
#
|
||||
#
|
||||
#CFLAGS= -Xa ${OPT}
|
||||
#CFLAGS= -std1 $(OPT)
|
||||
CFLAGS= -ansi ${OPT}
|
||||
CFLAGS= ${CSTD} ${CWARN} ${ARCH} ${CDEFINE} ${CINCLUDE} ${OPT}
|
||||
|
||||
# NOTE: On some systems, the library path does not include the location
|
||||
# of the X11 libraries. You might need to use a -L/path/to/X11/lib/dir
|
||||
# in order to compile with these libs.
|
||||
# Libraries needed to build
|
||||
#
|
||||
X11CCFLAGS= -I/usr/X11R6/include -I/usr/X11R6/include/X11 -I/usr/include/X11
|
||||
#X11CCFLAGS=
|
||||
X11LDFLAGS= -L/usr/X11R6/lib -Wl,-R/usr/X11R6/lib
|
||||
#X11LDFLAGS=
|
||||
LIBS=
|
||||
#LIBS= -lm
|
||||
#LIBS= -L ${X11_LIBDIR} -lX11
|
||||
#LIBS= `${SDL2_CONFIG} --libs`
|
||||
|
||||
# ANSI compiler
|
||||
#
|
||||
# Set CC to the name of your ANSI compiler.
|
||||
#
|
||||
# Some entries seem to need gcc. If you have gcc, set
|
||||
# both CC and MAY_NEED_GCC to gcc.
|
||||
#
|
||||
# If you do not have gcc, set CC to the name of your ANSI compiler, and
|
||||
# set MAY_NEED_GCC to either ${CC} (and hope for the best) or to just :
|
||||
# to disable such programs.
|
||||
# C compiler to use
|
||||
#
|
||||
CC= cc
|
||||
MAY_NEED_GCC= gcc
|
||||
#CC=clang
|
||||
#CC=gcc
|
||||
|
||||
# winners that compile under ANSI C
|
||||
# Compiler add-ons or replacements for clang only
|
||||
#
|
||||
WINNERS= anonymous bellard bellard.otccex cheong coupard \
|
||||
ctk dgbeards herrmann1 herrmann2 jason kev ollinger rosten \
|
||||
schweikh westley williams
|
||||
ALT_NAMES= westley.orig
|
||||
DATA_FILES= anonymous_files bellard_files herrmann1_files herrmann2_files
|
||||
|
||||
|
||||
all: ${WINNERS}
|
||||
|
||||
# Most likely to amaze
|
||||
ifeq ($(CC),clang)
|
||||
#
|
||||
anonymous: anonymous.c
|
||||
magic='${MAY_NEED_GCC} -O1'; \
|
||||
X='-DA(X)=#X'; \
|
||||
warning='-pedantic -Dprocessor'; \
|
||||
$$magic $$warning -Dmagic= $$X "-DX=A($$magic \"$$X\")" \
|
||||
-o anonymous anonymous.c
|
||||
|
||||
anonymous_files: anonymous.ten.c
|
||||
|
||||
# Best abuse of the rules
|
||||
#CSILENCE+=
|
||||
#
|
||||
bellard: bellard.c
|
||||
${CC} -rdynamic ${CFLAGS} bellard.c -o bellard -ldl
|
||||
|
||||
bellard_files: bellard.otccex.c
|
||||
|
||||
# Best short program
|
||||
CWARN+= -Weverything
|
||||
#
|
||||
cheong: cheong.c
|
||||
${CC} ${CFLAGS} cheong.c -o cheong
|
||||
endif
|
||||
|
||||
# Most obfuscated sound
|
||||
# Specific add-ons or replacements for gcc only
|
||||
#
|
||||
coupard: coupard.c
|
||||
${CC} ${CFLAGS} -o coupard coupard.c
|
||||
|
||||
# Worst Driver
|
||||
ifeq ($(CC),gcc)
|
||||
#
|
||||
ctk: ctk.c
|
||||
${CC} ${CFLAGS} -o ctk ctk.c
|
||||
|
||||
# Worst AI
|
||||
#CSILENCE+=
|
||||
#
|
||||
dgbeards: dgbeards.c
|
||||
${CC} ${CFLAGS} -DE=break -DF=char -DK=case -DP=int -DR=return \
|
||||
-DI=0xFFFF dgbeards.c -o dgbeards
|
||||
|
||||
# Best abuse of the C preprocessor
|
||||
#CWARN+=
|
||||
#
|
||||
herrmann1: herrmann1.c herrmann1.sh
|
||||
@echo "NOTE: $@ must be built/run using the script $@.sh"
|
||||
${SHELL} ./herrmann1.sh
|
||||
endif
|
||||
|
||||
herrmann1_files: herrmann1.gcd herrmann1.sh herrmann1.times2
|
||||
|
||||
# Most eye-crossing
|
||||
##############################
|
||||
# Special flags for this entry
|
||||
##############################
|
||||
#
|
||||
# This entry requires the GCC -include feature.
|
||||
ENTRY= herrmann1
|
||||
PROG= ${ENTRY}
|
||||
#
|
||||
herrmann2: herrmann2.c
|
||||
${MAY_NEED_GCC} ${CFLAGS} herrmann2.c -o herrmann2 \
|
||||
-include /usr/include/stdlib.h -include /usr/include/stdio.h \
|
||||
-include /usr/include/time.h -include /usr/include/unistd.h
|
||||
|
||||
herrmann2_files: herrmann2.cup herrmann2.ioccc
|
||||
|
||||
# Best Of Show
|
||||
OBJ= ${PROG}.o
|
||||
DATA= herrmann1.gcd herrmann1.sh herrmann1.times2
|
||||
TARGET= ${PROG}
|
||||
#
|
||||
jason: jason.c
|
||||
${CC} ${CFLAGS} -o jason jason.c
|
||||
ALT_OBJ=
|
||||
ALT_TARGET=
|
||||
|
||||
# Best Curses Game
|
||||
|
||||
#################
|
||||
# build the entry
|
||||
#################
|
||||
#
|
||||
kev: kev.c
|
||||
${CC} ${CFLAGS} -DPORT=5455 -DSPEED=50 -o kev kev.c \
|
||||
-lcurses -ltermcap \
|
||||
-Dl_="socket(il.sin_family=AF_INET,SOCK_STREAM,0)"
|
||||
|
||||
# Best primal ASCII graphics
|
||||
#
|
||||
ollinger: ollinger.c
|
||||
${CC} ${CFLAGS} ollinger.c -o ollinger
|
||||
|
||||
# Best abuse of the user
|
||||
#
|
||||
rosten: rosten.c
|
||||
${CC} ${CFLAGS} rosten.c ${X11CCFLAGS} ${X11LDFLAGS} -pedantic -lX11 \
|
||||
-o rosten
|
||||
|
||||
# Best one-liner
|
||||
#
|
||||
schweikh: schweikh.c
|
||||
${CC} ${CFLAGS} schweikh.c -o schweikh
|
||||
|
||||
# Best position-independant code
|
||||
#
|
||||
westley: westley.c
|
||||
${CC} ${CFLAGS} westley.c -o westley
|
||||
|
||||
westley.orig: westley.orig.c
|
||||
${CC} ${CFLAGS} westley.orig.c -o westley.orig
|
||||
|
||||
# Best position-independant code
|
||||
#
|
||||
williams: williams.c
|
||||
${CC} williams.c ${X11CCFLAGS} ${X11LDFLAGS} -lX11 -o williams
|
||||
|
||||
everything: ${WINNERS} ${ALT_NAMES} ${DATA_FILES}
|
||||
|
||||
love:
|
||||
@echo 'not war?'
|
||||
|
||||
haste:
|
||||
${MAKE} waste
|
||||
|
||||
waste:
|
||||
@echo 'waste'
|
||||
|
||||
clean:
|
||||
${RM} -f *.o a.out core ${WINNERS}
|
||||
|
||||
clobber: clean
|
||||
${RM} -f ${WINNERS}
|
||||
${RM} -f ${ALT_NAMES}
|
||||
|
||||
nuke: clobber
|
||||
all: data ${TARGET}
|
||||
@${TRUE}
|
||||
|
||||
install: all
|
||||
${CAT} ${WINNERS} ${ALT_NAMES} > /dev/null
|
||||
.PHONY: all alt data everything clean clobber install \
|
||||
love haste waste maker easter_egg sandwich \
|
||||
supernova deep_magic magic charon pluto
|
||||
|
||||
${PROG}: ${PROG}.c ${PROG}.sh
|
||||
${SHELL} ./herrmann1.sh
|
||||
|
||||
# alternative executable
|
||||
#
|
||||
alt: data ${ALT_TARGET}
|
||||
@${TRUE}
|
||||
|
||||
# data files
|
||||
#
|
||||
data: ${DATA}
|
||||
@${TRUE}
|
||||
|
||||
# both all and alt
|
||||
#
|
||||
everything: all alt
|
||||
|
||||
|
||||
###############
|
||||
# utility rules
|
||||
###############
|
||||
#
|
||||
clean:
|
||||
${RM} -f ${OBJ} ${ALT_OBJ}
|
||||
@-if [ -f indent.c ]; then \
|
||||
echo ${RM} -f indent.c; \
|
||||
${RM} -f indent.c; \
|
||||
fi
|
||||
|
||||
clobber: clean
|
||||
${RM} -f ${TARGET} ${ALT_TARGET}
|
||||
@-if [ -e sandwich ]; then \
|
||||
${RM} -f sandwich; \
|
||||
echo 'ate sandwich'; \
|
||||
fi
|
||||
|
||||
install:
|
||||
@echo "Dr. Spock says that is not logical!"
|
||||
@${TRUE}
|
||||
|
||||
# you may be able to misunderstand the source by reading indent.c
|
||||
#
|
||||
indent.c: ${PROG}.c
|
||||
@if which "${GINDENT}" >/dev/null 2>&1; then \
|
||||
echo ${RM} -f $@; \
|
||||
${RM} -f $@; \
|
||||
echo "${GINDENT} < $< > $@"; \
|
||||
${GINDENT} < $< > $@; \
|
||||
elif which "${INDENT}" >/dev/null 2>&1; then \
|
||||
echo ${RM} -f $@; \
|
||||
${RM} -f $@; \
|
||||
echo "${INDENT} < $< > $@"; \
|
||||
${INDENT} < $< > $@; \
|
||||
else \
|
||||
echo "no intent tool found, ident $< yourself, sorry"; \
|
||||
echo "exit 1"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
||||
##################
|
||||
# 133t hacker rulz
|
||||
##################
|
||||
#
|
||||
love:
|
||||
@echo 'not war?'
|
||||
@${TRUE}
|
||||
|
||||
haste:
|
||||
$(MAKE) waste
|
||||
@${TRUE}
|
||||
|
||||
waste:
|
||||
@echo 'haste'
|
||||
@${TRUE}
|
||||
|
||||
maker:
|
||||
@echo 'Participate in a maker faire'
|
||||
@${TRUE}
|
||||
|
||||
easter_egg:
|
||||
@echo "you expected to often misunderstand this $${RANDOM} magic"
|
||||
@echo "chongo '<was here>' /\\oo/\\"
|
||||
@echo "Eggy"
|
||||
|
||||
sandwich: easter_egg
|
||||
@if [ `id -u` -eq 0 ]; then \
|
||||
echo 'Okay.'; \
|
||||
echo "$${RANDOM}`date +%s`$${RANDOM}" > $@; \
|
||||
else \
|
||||
echo 'What? Make it yourself.'; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
# Understand the history of the Homebrew Computer Club
|
||||
# as well as the West Coast Computer Faire and
|
||||
# you might be confused different. :-)
|
||||
#
|
||||
supernova: sandwich
|
||||
@-if [ -r .code_anal ]; then \
|
||||
${RM} -f .code_anal_v6; \
|
||||
else \
|
||||
echo "planet deniers, like some members of the IAU, are so cute when they try to defend their logic"; \
|
||||
fi
|
||||
@echo "A $@ helps ${MAKE} the elements that help form planets"
|
||||
@${TRUE}
|
||||
|
||||
deep_magic: supernova
|
||||
@-if [ -r .code_anal ]; then \
|
||||
ccode_analysis --deep_magic 7889066cdbdf09b0408021754586e895be641cc9be29e7eafd3ca65359541f17 \
|
||||
--FNV1a_hash_512_bit -v2 "${PROG}" "${PROG}.c"; \
|
||||
else \
|
||||
echo "Wrong! Do it again!"; \
|
||||
sleep 2; \
|
||||
echo "Wrong! Do it again!"; \
|
||||
sleep 2; \
|
||||
echo "Wrong! Do it again!"; \
|
||||
fi
|
||||
@${TRUE}
|
||||
|
||||
magic: deep_magic
|
||||
@-if [ -r .code_anal ]; then \
|
||||
ccode_analysis --level 20003 --mode 5717175 --FNV1a_hash_512_bit -v2 "${PROG}" "${PROG}.c"; \
|
||||
else \
|
||||
echo "If you don't eat yer meat, you can't have any pudding!"; \
|
||||
echo "How can you have any pudding if you don't eat yer meat?!"; \
|
||||
fi
|
||||
@${TRUE}
|
||||
|
||||
# The IOCCC resident astronomer states
|
||||
#
|
||||
charon: magic
|
||||
@echo "$@ is a dwarf planet"
|
||||
@echo "dwarf is a type of planet"
|
||||
@echo "therefore IAU, $@ is a planet"
|
||||
|
||||
pluto: charon
|
||||
${MAKE} charon
|
||||
@echo "$@ is a dwarf planet"
|
||||
@echo "dwarf is a type of planet"
|
||||
@echo "therefore, $@ is a planet"
|
||||
@echo "having lots of planets is good feature because supernovae can help ${MAKE} a lot of them"
|
||||
+297
-299
@@ -1,4 +1,4 @@
|
||||
Best abuse of the C preprocessor:
|
||||
# Best abuse of the C preprocessor:
|
||||
|
||||
Immanuel Herrmann
|
||||
University of Freiburg, Germany
|
||||
@@ -8,356 +8,354 @@ Best abuse of the C preprocessor:
|
||||
|
||||
http://pcpool.mathematik.uni-freiburg.de/~immi/
|
||||
|
||||
# To build:
|
||||
|
||||
Judges' Comments:
|
||||
```sh
|
||||
make
|
||||
```
|
||||
|
||||
To build:
|
||||
### To run:
|
||||
|
||||
Running and building are combined for this entry.
|
||||
```sh
|
||||
./herrmann1.sh 'prg=file'
|
||||
```
|
||||
|
||||
Try:
|
||||
./herrmann1.sh 'prg=herrmann1.times2'
|
||||
-or-
|
||||
./herrmann1.sh 'prg=herrmann1.gcd'
|
||||
### Try:
|
||||
|
||||
There are some clear hints in the source code as to the
|
||||
purpose of this program. Perhaps the only way to figure out what is
|
||||
going on is to look at the differences between the runs of the C
|
||||
preprocessor! Computer Science students should recognize what is going
|
||||
on.
|
||||
```sh
|
||||
./herrmann1.sh 'prg=herrmann1.times2'
|
||||
```
|
||||
|
||||
-or-
|
||||
|
||||
Selected Author's Comments:
|
||||
```sh
|
||||
./herrmann1.sh 'prg=herrmann1.gcd'
|
||||
```
|
||||
|
||||
Many cool things have been done with the preprocessor,
|
||||
e. g. calculation of prime numbers or the tower of Hanoi. However,
|
||||
up to now, no one proved that the preprocessor is Turing complete,
|
||||
and I wondered why. Well, the answer is simply that the
|
||||
preprocessor is _not_ Turing complete, at least not if the program
|
||||
is preprocessed only once. This is true even if the program is
|
||||
allowed to include itself. (The reason being that for a given
|
||||
program, the preprocessor has only a finite number of states, plus
|
||||
a stack consisting of the places which the file has been included
|
||||
from. This is only a push-down automaton.)
|
||||
## Judges' comments:
|
||||
|
||||
Well, this entry _is_ a Turing machine implemented in the
|
||||
preprocessor; so of course, it has to be preprocessed several
|
||||
times. Here are the main features:
|
||||
There are some clear hints in the source code as to the
|
||||
purpose of this program. Perhaps the only way to figure out what is
|
||||
going on is to look at the differences between the runs of the C
|
||||
preprocessor! Computer Science students should recognize what is going
|
||||
on.
|
||||
|
||||
- Easy to program: Write your own Turing programs in a nice
|
||||
syntax.
|
||||
## Author's comments:
|
||||
|
||||
- Easy to debug: (some) errors in the Turing machine program will
|
||||
be reported nicely. (That's something which is not often seen in
|
||||
IOCCC programs.)
|
||||
Many cool things have been done with the preprocessor,
|
||||
e. g. calculation of prime numbers or the tower of Hanoi. However,
|
||||
up to now, no one proved that the preprocessor is Turing complete,
|
||||
and I wondered why. Well, the answer is simply that the
|
||||
preprocessor is _not_ Turing complete, at least not if the program
|
||||
is preprocessed only once. This is true even if the program is
|
||||
allowed to include itself. (The reason being that for a given
|
||||
program, the preprocessor has only a finite number of states, plus
|
||||
a stack consisting of the places which the file has been included
|
||||
from. This is only a push-down automaton.)
|
||||
|
||||
- Ultra-fast: The resulting C program will print the output in
|
||||
time O(n), where n is the length of the output. This means for
|
||||
example that you can solve NP hard problems in polynomial time!
|
||||
;-)
|
||||
Well, this entry _is_ a Turing machine implemented in the
|
||||
preprocessor; so of course, it has to be preprocessed several
|
||||
times. Here are the main features:
|
||||
|
||||
- And best of all: During the preprocessing, watch the animated
|
||||
Turing machine running over the tape and doing its work in your
|
||||
terminal!
|
||||
- Easy to program: Write your own Turing programs in a nice
|
||||
syntax.
|
||||
|
||||
- Easy to debug: (some) errors in the Turing machine program will
|
||||
be reported nicely. (That's something which is not often seen in
|
||||
IOCCC programs.)
|
||||
|
||||
- Ultra-fast: The resulting C program will print the output in
|
||||
time O(n), where n is the length of the output. This means for
|
||||
example that you can solve NP hard problems in polynomial time!
|
||||
;-)
|
||||
|
||||
How to use it
|
||||
=============
|
||||
- And best of all: During the preprocessing, watch the animated
|
||||
Turing machine running over the tape and doing its work in your
|
||||
terminal!
|
||||
|
||||
For the impatient
|
||||
-----------------
|
||||
### How to use it
|
||||
|
||||
The info files "herrmann1.turing" and "herrmann1.gcd" are sample
|
||||
programs for the Turing machine. For example, type
|
||||
For the impatient
|
||||
-----------------
|
||||
|
||||
./herrmann1.sh 'prg=herrmann1.times2'
|
||||
The info files "herrmann1.turing" and "herrmann1.gcd" are sample
|
||||
programs for the Turing machine. For example, type
|
||||
|
||||
and watch the animated Turing machine multiply 8 by two (in
|
||||
unary representation). Here's a screen shot during compilation:
|
||||
```sh
|
||||
./herrmann1.sh 'prg=herrmann1.times2'
|
||||
```
|
||||
|
||||
/*
|
||||
*
|
||||
* State: st2.
|
||||
*
|
||||
* _ _ _ _ _ * _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
* ... O O I I I I I O I I I I I O O ...
|
||||
* ~ ~ ~ ~ ~ * ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
||||
*
|
||||
*
|
||||
*/
|
||||
and watch the animated Turing machine multiply 8 by two (in
|
||||
unary representation). Here's a screen shot during compilation:
|
||||
|
||||
(As on real videos, you'll see a bit of "white noise" at the
|
||||
beginning and at the end.;-) After compilation has terminated,
|
||||
type ./prog to see the final result. The output will be
|
||||
/*
|
||||
*
|
||||
* State: st2.
|
||||
*
|
||||
* _ _ _ _ _ * _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
* ... O O I I I I I O I I I I I O O ...
|
||||
* ~ ~ ~ ~ ~ * ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
Final tape:
|
||||
(As on real videos, you'll see a bit of "white noise" at the
|
||||
beginning and at the end.;-) After compilation has terminated,
|
||||
type ./prog to see the final result. The output will be
|
||||
|
||||
*
|
||||
..OOIIIIIIIIIIIIIIIIOOOO..
|
||||
*
|
||||
Final tape:
|
||||
|
||||
(Note that the last frame of the animation is _not_ the final
|
||||
result.)
|
||||
*
|
||||
..OOIIIIIIIIIIIIIIIIOOOO..
|
||||
*
|
||||
|
||||
(Note that the last frame of the animation is _not_ the final
|
||||
result.)
|
||||
|
||||
In the previous example, the Turing machine started on the default
|
||||
tape of the program "times2.turing". To provide your own tape, type
|
||||
(for example)
|
||||
|
||||
./build prg=times2.turing tape="O O O I I I O O O"
|
||||
In the previous example, the Turing machine started on the default
|
||||
tape of the program "times2.turing". To provide your own tape, type
|
||||
(for example)
|
||||
|
||||
(The O's and I's are letters, not digits.) You might prefer to
|
||||
provide only a finite portion of the tape, like I did above. In
|
||||
this case, the remainder of the tape is filled with O's. ;-)
|
||||
./build prg=times2.turing tape="O O O I I I O O O"
|
||||
|
||||
If you just type
|
||||
./build
|
||||
and run the program, you'll get a usage message (and the return
|
||||
code will be 2).
|
||||
(The O's and I's are letters, not digits.) You might prefer to
|
||||
provide only a finite portion of the tape, like I did above. In
|
||||
this case, the remainder of the tape is filled with O's. ;-)
|
||||
|
||||
Note: To get a really nice animation, you'll need a terminal which
|
||||
is able to scroll fast enough. (xterm is not very good. The
|
||||
kde terminal "konsole" or the text console under linux are much
|
||||
better. Simply try some out.)
|
||||
If you just type:
|
||||
|
||||
```
|
||||
./build
|
||||
```
|
||||
|
||||
and run the program, you'll get a usage message (and the return
|
||||
code will be 2).
|
||||
|
||||
Writing your own programs
|
||||
-------------------------
|
||||
Note: To get a really nice animation, you'll need a terminal which
|
||||
is able to scroll fast enough. (xterm is not very good. The
|
||||
kde terminal "konsole" or the text console under linux are much
|
||||
better. Simply try some out.)
|
||||
|
||||
Now, let's assume you want to write a program which adds 1 to a
|
||||
number in unary representation. (That is, a row of I's is
|
||||
expected, and one more I is appended to the row.) Let's call it
|
||||
"plus1.turing". Here's what it could look like:
|
||||
### Writing your own programs
|
||||
|
||||
/* Search beginning of number. */
|
||||
#define start_O O, right, start
|
||||
/* Number found. Move one step to the left. */
|
||||
#define start_I I, left, st2
|
||||
/* Write a I and stop */
|
||||
#define st2_O I, left, stop
|
||||
Now, let's assume you want to write a program which adds 1 to a
|
||||
number in unary representation. (That is, a row of I's is
|
||||
expected, and one more I is appended to the row.) Let's call it
|
||||
"plus1.turing". Here's what it could look like:
|
||||
|
||||
Some explanations:
|
||||
- The tape alphabet is always O and I.
|
||||
- The states can be anything starting with "st". "start" and
|
||||
"stop" are the start and stop states, respectively.
|
||||
- to define a state, use two lines: one for each letter on the
|
||||
tape. You may omit one of the lines if the Turing machine will
|
||||
never enter the state reading that letter. (In the example, the
|
||||
line defining st2_I has been left out.)
|
||||
- The order of the lines is irrelevant.
|
||||
- You may use c-style comments. (Would you have guessed it?)
|
||||
/* Search beginning of number. */
|
||||
#define start_O O, right, start
|
||||
/* Number found. Move one step to the left. */
|
||||
#define start_I I, left, st2
|
||||
/* Write a I and stop */
|
||||
#define st2_O I, left, stop
|
||||
|
||||
Now, the program can be used with
|
||||
Some explanations:
|
||||
- The tape alphabet is always O and I.
|
||||
- The states can be anything starting with "st". "start" and
|
||||
"stop" are the start and stop states, respectively.
|
||||
- to define a state, use two lines: one for each letter on the
|
||||
tape. You may omit one of the lines if the Turing machine will
|
||||
never enter the state reading that letter. (In the example, the
|
||||
line defining st2_I has been left out.)
|
||||
- The order of the lines is irrelevant.
|
||||
- You may use c-style comments. (Would you have guessed it?)
|
||||
|
||||
./herrmann1.sh 'prg=plus1.turing' 'tape="I I I"'
|
||||
Now, the program can be used with
|
||||
|
||||
But what happens if no tape is provided? Up to now, the Turing
|
||||
machine would start on an empty tape (that is, a tape filled with
|
||||
O's). For many programs, this doesn't make much sense (as is
|
||||
the case for "plus1.turing"). So you might want to provide a
|
||||
default tape by adding a line like
|
||||
```sh
|
||||
./herrmann1.sh 'prg=plus1.turing' 'tape="I I I"'
|
||||
```
|
||||
|
||||
#define tape O O O O O I I I I
|
||||
But what happens if no tape is provided? Up to now, the Turing
|
||||
machine would start on an empty tape (that is, a tape filled with
|
||||
O's). For many programs, this doesn't make much sense (as is
|
||||
the case for "plus1.turing"). So you might want to provide a
|
||||
default tape by adding a line like
|
||||
|
||||
to your program.
|
||||
#define tape O O O O O I I I I
|
||||
|
||||
to your program.
|
||||
|
||||
### Debugging
|
||||
|
||||
When writing long programs, the most common errors are to forget
|
||||
to define a state or to misspell one. Let's assume you forgot the
|
||||
line
|
||||
|
||||
#define start_I I, left, st2
|
||||
|
||||
Remove it and type
|
||||
|
||||
./herrmann1 'prg=plus1.turing'
|
||||
|
||||
The output will be
|
||||
|
||||
Undefined state start.
|
||||
|
||||
Tape:
|
||||
|
||||
Debugging
|
||||
---------
|
||||
*
|
||||
..OOOOOOOOIIIIOO..
|
||||
*
|
||||
|
||||
When writing long programs, the most common errors are to forget
|
||||
to define a state or to misspell one. Let's assume you forgot the
|
||||
line
|
||||
and the return code will be 1. As you can see, the Turing machine
|
||||
moved over the O's, but when it arrived at the I (still in state
|
||||
start), the error occurred.
|
||||
|
||||
#define start_I I, left, st2
|
||||
Note that other kinds of errors can cause compilation to fail.
|
||||
If this happens, the build script will write the error message of
|
||||
gcc into a file called "error" and then exit.
|
||||
|
||||
Remove it and type
|
||||
### What the build script does
|
||||
|
||||
./herrmann1 'prg=plus1.turing'
|
||||
In principle, the build script simply runs the preprocessor over
|
||||
and over again, until nothing changes anymore; then, the program
|
||||
is compiled. After each preprocessing step, the script outputs the
|
||||
beginning of the program; this is where the animation takes place
|
||||
(except in the first and last few steps).
|
||||
|
||||
However, there's a technical problem to overcome: Some compilers
|
||||
(including gcc) insert a lot of white space during the
|
||||
preprocessing. This is bad for two reasons. First, the animated
|
||||
picture won't look right. And second, after some time, the file
|
||||
will grow too big for your hard disk. (The amount of white space
|
||||
grows exponentially.) So after each preprocessing, the first thing
|
||||
the build script does is remove all the unnecessary white space
|
||||
using sed.
|
||||
|
||||
(gcc would even insert #line directives if I didn't suppress
|
||||
them with the -P option.)
|
||||
|
||||
### Some more details
|
||||
|
||||
- The arguments passed to the build script are passed on to gcc
|
||||
with a "-D" before. I had a slight technical problem doing this
|
||||
(especially with the spaces that can appear within the tape
|
||||
argument). That made the build script a bit longer.
|
||||
|
||||
- It would be enough to pass the defines in the first
|
||||
preprocessing. The program then "remembers" them. However, it
|
||||
doesn't matter if they are passed in all preprocessings. (This
|
||||
doesn't sound very impressive, but it has required some extra
|
||||
lines of code.)
|
||||
|
||||
- During the preprocessing, intermediate files "herrmann1-1.c" and
|
||||
"herrmann1-2.c" are generated.
|
||||
|
||||
- Also, a file "error" is generated. This file is removed after
|
||||
the compilation, except if gcc produced an error; then it contains
|
||||
the error message. (I can't let gcc output all its messages
|
||||
directly, because the warnings would clobber the animation. I
|
||||
could have made the compile script output the error file in case
|
||||
of an error, but I didn't want to make it yet longer.)
|
||||
|
||||
The output will be
|
||||
### Some technical details / obfuscations
|
||||
|
||||
Undefined state start.
|
||||
- The program is compressed. In the first three preprocessing
|
||||
passes, it uncompresses itself and initializes the
|
||||
"variables". So if you want to understand how it works, the best
|
||||
thing is to preprocess it three times and then look at it
|
||||
again. (However, I wouldn't have wasted three passes if they
|
||||
weren't needed anyway to initialize the tape in certain
|
||||
cases. Find out why.)
|
||||
|
||||
Tape:
|
||||
- The first problem I had to solve when writing this program was
|
||||
to write a preprocessor program which outputs itself. Of course,
|
||||
each program which doesn't contain any preprocessor directives
|
||||
is such a program, but that's not what I needed. In fact, to do
|
||||
this, the program has to include itself, but even then, it's not
|
||||
easy.
|
||||
|
||||
*
|
||||
..OOOOOOOOIIIIOO..
|
||||
*
|
||||
- The next problem to solve was how to pass results of a
|
||||
calculation of one preprocessing to the next one. This is done
|
||||
by outputting #defines with the result. But there's a problem
|
||||
with this: it is impossible to output a "#define result xxx",
|
||||
where xxx depends on the previous value of result. Try it if you
|
||||
don't believe it. And find out what I did instead.
|
||||
|
||||
and the return code will be 1. As you can see, the Turing machine
|
||||
moved over the O's, but when it arrived at the I (still in state
|
||||
start), the error occurred.
|
||||
- What's a useful way to store information, especially the content
|
||||
of the tape? Of course, a "#define tape OOIOOIOIIIIO" contains
|
||||
the information, but then, how do you access the information?
|
||||
(For example, you won't be able to write preprocessor code which
|
||||
replaces the first O by I.) My answer looks very much like the
|
||||
way a stack would have been implemented in lambda calculus. (The
|
||||
tape is implemented as two stacks, to be exact.) And still,
|
||||
there were some technical problems to solve. Look at the program
|
||||
for more details.
|
||||
|
||||
Note that other kinds of errors can cause compilation to fail.
|
||||
If this happens, the build script will write the error message of
|
||||
gcc into a file called "error" and then exit.
|
||||
|
||||
|
||||
|
||||
|
||||
What the build script does
|
||||
==========================
|
||||
|
||||
In principle, the build script simply runs the preprocessor over
|
||||
and over again, until nothing changes anymore; then, the program
|
||||
is compiled. After each preprocessing step, the script outputs the
|
||||
beginning of the program; this is where the animation takes place
|
||||
(except in the first and last few steps).
|
||||
|
||||
However, there's a technical problem to overcome: Some compilers
|
||||
(including gcc) insert a lot of white space during the
|
||||
preprocessing. This is bad for two reasons. First, the animated
|
||||
picture won't look right. And second, after some time, the file
|
||||
will grow too big for your hard disk. (The amount of white space
|
||||
grows exponentially.) So after each preprocessing, the first thing
|
||||
the build script does is remove all the unnecessary white space
|
||||
using sed.
|
||||
|
||||
(gcc would even insert #line directives if I didn't suppress
|
||||
them with the -P option.)
|
||||
|
||||
|
||||
Some more details
|
||||
-----------------
|
||||
|
||||
- The arguments passed to the build script are passed on to gcc
|
||||
with a "-D" before. I had a slight technical problem doing this
|
||||
(especially with the spaces that can appear within the tape
|
||||
argument). That made the build script a bit longer.
|
||||
|
||||
- It would be enough to pass the defines in the first
|
||||
preprocessing. The program then "remembers" them. However, it
|
||||
doesn't matter if they are passed in all preprocessings. (This
|
||||
doesn't sound very impressive, but it has required some extra
|
||||
lines of code.)
|
||||
|
||||
- During the preprocessing, intermediate files "herrmann1-1.c" and
|
||||
"herrmann1-2.c" are generated.
|
||||
|
||||
- Also, a file "error" is generated. This file is removed after
|
||||
the compilation, except if gcc produced an error; then it contains
|
||||
the error message. (I can't let gcc output all its messages
|
||||
directly, because the warnings would clobber the animation. I
|
||||
could have made the compile script output the error file in case
|
||||
of an error, but I didn't want to make it yet longer.)
|
||||
|
||||
|
||||
|
||||
|
||||
Some technical details / obfuscations
|
||||
====================================
|
||||
|
||||
- The program is compressed. In the first three preprocessing
|
||||
passes, it uncompresses itself and initializes the
|
||||
"variables". So if you want to understand how it works, the best
|
||||
thing is to preprocess it three times and then look at it
|
||||
again. (However, I wouldn't have wasted three passes if they
|
||||
weren't needed anyway to initialize the tape in certain
|
||||
cases. Find out why.)
|
||||
|
||||
- The first problem I had to solve when writing this program was
|
||||
to write a preprocessor program which outputs itself. Of course,
|
||||
each program which doesn't contain any preprocessor directives
|
||||
is such a program, but that's not what I needed. In fact, to do
|
||||
this, the program has to include itself, but even then, it's not
|
||||
easy.
|
||||
|
||||
- The next problem to solve was how to pass results of a
|
||||
calculation of one preprocessing to the next one. This is done
|
||||
by outputting #defines with the result. But there's a problem
|
||||
with this: it is impossible to output a "#define result xxx",
|
||||
where xxx depends on the previous value of result. Try it if you
|
||||
don't believe it. And find out what I did instead.
|
||||
|
||||
- What's a useful way to store information, especially the content
|
||||
of the tape? Of course, a "#define tape OOIOOIOIIIIO" contains
|
||||
the information, but then, how do you access the information?
|
||||
(For example, you won't be able to write preprocessor code which
|
||||
replaces the first O by I.) My answer looks very much like the
|
||||
way a stack would have been implemented in lambda calculus. (The
|
||||
tape is implemented as two stacks, to be exact.) And still,
|
||||
there were some technical problems to solve. Look at the program
|
||||
for more details.
|
||||
|
||||
- There are many other interesting things to look at, e.g. the way
|
||||
to access the information in the user-provided program, the way
|
||||
a non-defined state is detected, the way the picture is
|
||||
produced, etc. If you want a real challenge, try to understand
|
||||
what the line "w j k(P ## x)" does.
|
||||
|
||||
- Due to the fact that our alphabet has only 53 ;-) letters, I had
|
||||
to use many variables more than once.
|
||||
|
||||
- I couldn't refrain from doing a little bit of nasty stuff like
|
||||
|
||||
#define a 0&0
|
||||
#if !a
|
||||
|
||||
Of course, it is a bit more subtle, and even useful.
|
||||
|
||||
- The layout of the source code..., well, let's call it the
|
||||
"modern art of programming" - a mixture of program-like
|
||||
indenting and modern art. There are even some comments...
|
||||
|
||||
|
||||
|
||||
|
||||
Compatibility
|
||||
=============
|
||||
|
||||
- It works with many versions of gcc. I tested it with versions
|
||||
2.7.2.1, 2.8.1, 2.95.2, and ecgs-2.91.66. However, an old version
|
||||
of gcc produced "internal compiler errors" (obviously not my
|
||||
fault).
|
||||
|
||||
- The only warnings I get tell me that I'm undefining __FILE__ and
|
||||
that preprocessing directives are not recognized within macro
|
||||
args. My answer to this is "yes, I know this and it's all
|
||||
right". With "-Wall", I also get some warnings about "/*"
|
||||
inside comments (Well, _I_ like some well-placed "/*/*/".) as
|
||||
well as some suggestions where to place some more parentheses
|
||||
(as if I didn't know the operator precedence).
|
||||
|
||||
- Different versions of cc all produced only rubbish. Apparently,
|
||||
the preprocessors didn't really work. (I don't feel guilty
|
||||
there, either. Even with very simple programs, they produced
|
||||
mysterious output.)
|
||||
|
||||
- For Mac-Users: It works with the C compiler of Codewarrior (an
|
||||
old version). This compiler even leaves out all that unnecessary
|
||||
white space, so that no clean-up is needed. (It would be useful
|
||||
to write a kind of script which does the equivalent to the build
|
||||
script, so that one does not have to choose "preprocess" from
|
||||
the menu over and over again. I leave this as an exercise to the
|
||||
experienced Mac programmer.)
|
||||
|
||||
- In the build script, I passed the -P option to gcc to suppress
|
||||
the insertion of #line directives. If your compiler doesn't
|
||||
support this option (and does insert #line directives), you
|
||||
should change the call to sed to remove them, too: Add
|
||||
|
||||
-e "/^#line/ d"
|
||||
|
||||
to the sed parameters. Or, if your #line directives look like
|
||||
|
||||
# 15 "filename"
|
||||
|
||||
add
|
||||
|
||||
-e "/^# [0-9]/ d"
|
||||
|
||||
instead.
|
||||
|
||||
- First, I had "-ansi -pedantic" turned on, but on some systems,
|
||||
the stdio.h is not compatible to that, so I turned it back off.
|
||||
|
||||
- The build script probably needs a bash to run in. So perhaps you
|
||||
have to add "#!/bin/bash" or something like this at the
|
||||
beginning.
|
||||
|
||||
- On SunOS, I had to remove the "-q" option from diff in the build
|
||||
script.
|
||||
- There are many other interesting things to look at, e.g. the way
|
||||
to access the information in the user-provided program, the way
|
||||
a non-defined state is detected, the way the picture is
|
||||
produced, etc. If you want a real challenge, try to understand
|
||||
what the line "w j k(P ## x)" does.
|
||||
|
||||
- Due to the fact that our alphabet has only 53 ;-) letters, I had
|
||||
to use many variables more than once.
|
||||
|
||||
- I couldn't refrain from doing a little bit of nasty stuff like
|
||||
|
||||
#define a 0&0
|
||||
#if !a
|
||||
|
||||
Of course, it is a bit more subtle, and even useful.
|
||||
|
||||
- The layout of the source code..., well, let's call it the
|
||||
"modern art of programming" - a mixture of program-like
|
||||
indenting and modern art. There are even some comments...
|
||||
|
||||
### Compatibility
|
||||
|
||||
- It works with many versions of gcc. I tested it with versions
|
||||
2.7.2.1, 2.8.1, 2.95.2, and ecgs-2.91.66. However, an old version
|
||||
of gcc produced "internal compiler errors" (obviously not my
|
||||
fault).
|
||||
|
||||
- The only warnings I get tell me that I'm undefining __FILE__ and
|
||||
that preprocessing directives are not recognized within macro
|
||||
args. My answer to this is "yes, I know this and it's all
|
||||
right". With "-Wall", I also get some warnings about "/*"
|
||||
inside comments (Well, _I_ like some well-placed "/*/*/".) as
|
||||
well as some suggestions where to place some more parentheses
|
||||
(as if I didn't know the operator precedence).
|
||||
|
||||
- Different versions of cc all produced only rubbish. Apparently,
|
||||
the preprocessors didn't really work. (I don't feel guilty
|
||||
there, either. Even with very simple programs, they produced
|
||||
mysterious output.)
|
||||
|
||||
- For Mac-Users: It works with the C compiler of Codewarrior (an
|
||||
old version). This compiler even leaves out all that unnecessary
|
||||
white space, so that no clean-up is needed. (It would be useful
|
||||
to write a kind of script which does the equivalent to the build
|
||||
script, so that one does not have to choose "preprocess" from
|
||||
the menu over and over again. I leave this as an exercise to the
|
||||
experienced Mac programmer.)
|
||||
|
||||
- In the build script, I passed the -P option to gcc to suppress
|
||||
the insertion of #line directives. If your compiler doesn't
|
||||
support this option (and does insert #line directives), you
|
||||
should change the call to sed to remove them, too: Add
|
||||
|
||||
-e "/^#line/ d"
|
||||
|
||||
to the sed parameters. Or, if your #line directives look like
|
||||
|
||||
# 15 "filename"
|
||||
|
||||
add
|
||||
|
||||
-e "/^# [0-9]/ d"
|
||||
|
||||
instead.
|
||||
|
||||
- First, I had "-ansi -pedantic" turned on, but on some systems,
|
||||
the stdio.h is not compatible to that, so I turned it back off.
|
||||
|
||||
- The build script probably needs a bash to run in. So perhaps you
|
||||
have to add "#!/bin/bash" or something like this at the
|
||||
beginning.
|
||||
|
||||
- On SunOS, I had to remove the "-q" option from diff in the build
|
||||
script.
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
name=herrmann1
|
||||
|
||||
|
||||
Reference in new issue
Block a user