diff --git a/2001/herrmann1/Makefile b/2001/herrmann1/Makefile index 49e7cf5a9..2f886fa65 100644 --- a/2001/herrmann1/Makefile +++ b/2001/herrmann1/Makefile @@ -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 '' /\\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" diff --git a/2001/herrmann1/README.md b/2001/herrmann1/README.md index 9dfb867be..7e41962ac 100644 --- a/2001/herrmann1/README.md +++ b/2001/herrmann1/README.md @@ -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. diff --git a/2001/herrmann1/herrmann1.sh b/2001/herrmann1/herrmann1.sh index 330397f71..8af2b39c6 100755 --- a/2001/herrmann1/herrmann1.sh +++ b/2001/herrmann1/herrmann1.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash name=herrmann1