Format fixes in 1996/august
I am aware, however, of at least one possible bug. I've not addressed this or put it in bugs.md. I will first look at it a bit more as at first in linux I got a segfault but then I had the same problem under macOS - a freeze. I want to see if I can duplicate the crash again before I write about any bugs.
This commit is contained in:
1 parent
bedaa23460
commit
4e2492f734
2 files changed
+137
-94
No files matched your search
@@ -1,3 +1,6 @@
|
|||||||
august
|
august
|
||||||
august.oc
|
august.oc
|
||||||
test.oo
|
test.oo
|
||||||
|
fac.oo
|
||||||
|
parse.oo
|
||||||
|
prog.oc
|
||||||
+134
-94
@@ -1,65 +1,85 @@
|
|||||||
# Best of Show
|
# Best of Show
|
||||||
|
|
||||||
Lennart Augustsson
|
Lennart Augustsson
|
||||||
CR&T
|
CR&T
|
||||||
Stora Badhusgatan 18-20
|
Stora Badhusgatan 18-20
|
||||||
S-411 21 Goteborg
|
S-411 21 Goteborg
|
||||||
Sweden
|
Sweden
|
||||||
|
|
||||||
## To build:
|
## To build:
|
||||||
|
|
||||||
make all
|
```sh
|
||||||
|
make all
|
||||||
|
```
|
||||||
|
|
||||||
## To run:
|
## To run:
|
||||||
|
|
||||||
cat august.c test.oc | ./august > test.oo
|
```sh
|
||||||
./august < test.oo
|
cat august.c test.oc | ./august > test.oo
|
||||||
|
./august < test.oo
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
NOTE: Some compilers will compile this code into infinite loops!
|
||||||
|
If the above cat line does not execute in a very short amount
|
||||||
|
of time, then you may need to fix your compiler or use gcc.
|
||||||
|
|
||||||
|
It should print a `!` followed by a newline.
|
||||||
|
|
||||||
NOTE: Some compilers will compile this code into infinite loops!
|
|
||||||
If the above cat line does not execute in a very short amount
|
|
||||||
of time, then you may need to fix your compiler or use gcc.
|
|
||||||
|
|
||||||
## Try:
|
## Try:
|
||||||
|
|
||||||
cat august.c fac.oc | ./august > fac.oo
|
```sh
|
||||||
./august < fac.oo
|
cat august.c fac.oc | ./august > fac.oo
|
||||||
|
./august < fac.oo
|
||||||
|
|
||||||
cat august.c fac.oc | ./august | ./august
|
cat august.c fac.oc | ./august | ./august
|
||||||
|
|
||||||
cat august.c parse.oc | ./august > parse.oo
|
cat august.c parse.oc | ./august > parse.oo
|
||||||
cat parse.oo fac.oc | ./august | ./august
|
cat parse.oo fac.oc | ./august | ./august
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Judges' remarks:
|
## Judges' remarks:
|
||||||
|
|
||||||
This entry can feed on itself. If you C preprocess the source,
|
This entry can feed on itself. If you C pre-process the source,
|
||||||
you compile the interpreter:
|
you compile the interpreter:
|
||||||
|
|
||||||
make august.oc
|
```sh
|
||||||
|
make august.oc
|
||||||
|
```
|
||||||
|
|
||||||
We can now compile the interpreter as follows:
|
We can now compile the interpreter as follows:
|
||||||
|
|
||||||
cat august.c august.oc | ./august > august.oo
|
```sh
|
||||||
|
cat august.c august.oc | ./august > august.oo
|
||||||
|
```
|
||||||
|
|
||||||
and use the compiled interpreter to execute some previously
|
and use the compiled interpreter to execute some previously
|
||||||
compiled code:
|
compiled code:
|
||||||
|
|
||||||
cat august.oo test.oo | ./august
|
```sh
|
||||||
|
cat august.oo test.oo | ./august
|
||||||
|
```
|
||||||
|
|
||||||
And we can have the compiled interpreter interpret itself which
|
And we can have the compiled interpreter interpret itself which
|
||||||
in turn compiles the test program:
|
in turn compiles the test program:
|
||||||
|
|
||||||
cat august.oo august.oo fac.oo | ./august
|
```sh
|
||||||
|
cat august.oo august.oo fac.oo | ./august
|
||||||
|
```
|
||||||
|
|
||||||
And if you have lots of spare time, you can recurse one level deeper:
|
And if you have lots of spare time, you can recurse one level deeper:
|
||||||
|
|
||||||
cat august.oo august.oo august.oo fac.oo | ./august
|
```sh
|
||||||
|
cat august.oo august.oo august.oo fac.oo | ./august
|
||||||
|
```
|
||||||
|
|
||||||
We (the judges) recommend that you spend some time studying this
|
We (the judges) recommend that you spend some time studying this
|
||||||
entry as it surely will make the 'best of the IOCCC' list. It was
|
entry as it surely will make the 'best of the IOCCC' list. It was
|
||||||
very well received by those who attended the IOCCC BOF.
|
very well received by those who attended the IOCCC BOF.
|
||||||
|
|
||||||
## Author's remarks
|
## Author's remarks:
|
||||||
|
|
||||||
This program is a bytecode interpreter. This fact is not
|
This program is a bytecode interpreter. This fact is not
|
||||||
particularly obfuscated; any experienced C programmer can see that
|
particularly obfuscated; any experienced C programmer can see that
|
||||||
@@ -86,70 +106,88 @@ The compiler has absolutely no error checking, so things
|
|||||||
can go wrong. But this is what seasoned C programmers are used to.
|
can go wrong. But this is what seasoned C programmers are used to.
|
||||||
|
|
||||||
Assume that the interpreter has been compiled (source in prog.c and
|
Assume that the interpreter has been compiled (source in prog.c and
|
||||||
binary in prog) and that we have this in a file named test.oc:
|
binary in prog) and that we have this in a file named `test.oc`:
|
||||||
|
|
||||||
main() { putchar('!'); putchar('\n'); exit(0); }
|
```
|
||||||
|
main() { putchar('!'); putchar('\n'); exit(0); }
|
||||||
|
```
|
||||||
|
|
||||||
We can then compile by
|
We can then compile by
|
||||||
|
|
||||||
cat august.c test.oc | ./august > test.oo
|
```sh
|
||||||
|
cat august.c test.oc | ./august > test.oo
|
||||||
|
```
|
||||||
|
|
||||||
And run the compiled program by
|
And run the compiled program by
|
||||||
|
|
||||||
./august < test.oo
|
```sh
|
||||||
|
./august < test.oo
|
||||||
|
```
|
||||||
|
|
||||||
Or even simpler:
|
Or even simpler:
|
||||||
|
|
||||||
cat august.c test.oc | ./august | ./august
|
```sh
|
||||||
|
cat august.c test.oc | ./august | ./august
|
||||||
A larger example is included fac.oc.
|
```
|
||||||
|
|
||||||
The compiler for OC is naturally written in OC (where did you think
|
A larger example is included [fac.oc](fac.oc).
|
||||||
the byte code came from?). It is in the file parse.oc.
|
|
||||||
|
|
||||||
Hmm... So now we have an interpreter written in OC and a compiler
|
The compiler for OC is naturally written in OC (where did you think the byte
|
||||||
for OC, can we compile the interpreter? Yes, we can! The OC
|
code came from?). It is in the file [parse.oc](parse.oc).
|
||||||
compiler does not have a preprocessor (hey, what do you expect from
|
|
||||||
a program that is this small), so we need to use an external one.
|
|
||||||
To preprocess august.c use the command from the build file, but add
|
|
||||||
the -E flag and change 60000 to 40000, i.e.:
|
|
||||||
|
|
||||||
cc -E -DZ=40000 .... august.c > prog.oc
|
Hmm... So now we have an interpreter written in OC and a compiler for OC, can
|
||||||
|
we compile the interpreter? Yes, we can! The OC compiler does not have a
|
||||||
|
preprocessor (hey, what do you expect from a program that is this small), so we
|
||||||
|
need to use an external one. To pre-process [august.c](august.c) use the
|
||||||
|
command from the Makefile, but add the `-E` flag and change 60000 to 40000,
|
||||||
|
i.e.:
|
||||||
|
|
||||||
If prog.oc contain some junk line starting with a `#' (most likely
|
```sh
|
||||||
|
cc -E -DZ=40000 .... august.c > prog.oc
|
||||||
|
```
|
||||||
|
|
||||||
|
If `prog.oc` contain some junk line starting with a `#' (most likely
|
||||||
it does) then remove it.
|
it does) then remove it.
|
||||||
|
|
||||||
OK, we can now compile the interpreter:
|
OK, we can now compile the interpreter:
|
||||||
|
|
||||||
cat august.c prog.oc | ./august > prog.oo
|
```sh
|
||||||
|
cat august.c prog.oc | ./august > prog.oo
|
||||||
|
```
|
||||||
|
|
||||||
And we can run it:
|
And we can run it:
|
||||||
|
|
||||||
cat prog.oo test.oo | ./august
|
```sh
|
||||||
|
cat prog.oo test.oo | ./august
|
||||||
|
```
|
||||||
|
|
||||||
Here we have the interpreter interpreting another interpreter that runs
|
Here we have the interpreter interpreting another interpreter that runs
|
||||||
the program. Did you think that was too fast? Just throw in another
|
the program. Did you think that was too fast? Just throw in another
|
||||||
level of interpretation:
|
level of interpretation:
|
||||||
|
|
||||||
cat prog.oo prog.oo test.oo | ./august
|
```sh
|
||||||
|
cat prog.oo prog.oo test.oo | ./august
|
||||||
|
```
|
||||||
|
|
||||||
And another...
|
And another...
|
||||||
|
|
||||||
cat prog.oo prog.oo prog.oo test.oo | ./august
|
```sh
|
||||||
|
cat prog.oo prog.oo prog.oo test.oo | ./august
|
||||||
|
```
|
||||||
|
|
||||||
*****
|
---
|
||||||
|
|
||||||
Fitting the byte code for the compiler into the initial comment
|
Fitting the byte code for the compiler into the initial comment
|
||||||
was a challenge. I had to strip the compiler/interpreter of
|
was a challenge. I had to strip the compiler/interpreter of
|
||||||
a lot of functionality to bring down their sizes. The initial
|
a lot of functionality to bring down their sizes. The initial
|
||||||
version had a full set of binary operators as well as prefix &.
|
version had a full set of binary operators as well as prefix `&`.
|
||||||
Given a handful more bytes it would be easy to put them back.
|
Given a handful more bytes it would be easy to put them back.
|
||||||
|
|
||||||
Anyway, the bytecode from the compiler is first optimized in
|
Anyway, the bytecode from the compiler is first optimized in
|
||||||
an external optimizer (not supplied here); this brings down the
|
an external optimizer (not supplied here); this brings down the
|
||||||
size of it by about 30%. The remaining code is then LZ-compressed
|
size of it by about 30%. The remaining code is then LZ-compressed
|
||||||
(roughly) with a sliding window of 255 bytes. The most common
|
(roughly) with a sliding window of 255 bytes. The most common
|
||||||
occuring bytes are then encoded using " \n\t;{}" to avoid having
|
occuring bytes are then encoded using `" \n\t;{}"` to avoid having
|
||||||
them counted as significant. About 200 bytes of the interpreter
|
them counted as significant. About 200 bytes of the interpreter
|
||||||
handles the unpacking of the byte code (this is a lot, but it pays
|
handles the unpacking of the byte code (this is a lot, but it pays
|
||||||
off).
|
off).
|
||||||
@@ -161,75 +199,77 @@ coder), but decompression then takes up too much code. Maybe this
|
|||||||
would be a way to fit it all into the 1536 bytes, but I'm don't have
|
would be a way to fit it all into the 1536 bytes, but I'm don't have
|
||||||
time to try it.
|
time to try it.
|
||||||
|
|
||||||
*****
|
---
|
||||||
|
|
||||||
Execution starts at main(). Nothing can be used before it is
|
Execution starts at `main()`. Nothing can be used before it is
|
||||||
defined. getchar() and putchar() are predefined functions.
|
defined. `getchar()` and `putchar()` are predefined functions.
|
||||||
|
|
||||||
The following is a description of the OC grammar:
|
Below is a description of the OC grammar.
|
||||||
|
|
||||||
OC grammar
|
### OC grammar
|
||||||
==========
|
|
||||||
|
|
||||||
Terminals are in quotes, () is used for bracketing.
|
Terminals are in quotes, `()` is used for bracketing.
|
||||||
|
|
||||||
program: decl*
|
```
|
||||||
|
program: decl*
|
||||||
|
|
||||||
decl: vardecl
|
decl: vardecl
|
||||||
fundecl
|
fundecl
|
||||||
|
|
||||||
vardecl: type NAME ;
|
vardecl: type NAME ;
|
||||||
type NAME "[" INT "]" ;
|
type NAME "[" INT "]" ;
|
||||||
|
|
||||||
fundecl: type NAME "(" args ")" "{" body "}"
|
fundecl: type NAME "(" args ")" "{" body "}"
|
||||||
|
|
||||||
args: /*empty*/
|
args: /*empty*/
|
||||||
( arg "," )* arg
|
( arg "," )* arg
|
||||||
|
|
||||||
arg: type NAME
|
arg: type NAME
|
||||||
|
|
||||||
body: vardecl* stmt*
|
body: vardecl* stmt*
|
||||||
|
|
||||||
stmt: ifstmt
|
stmt: ifstmt
|
||||||
whilestmt
|
whilestmt
|
||||||
dowhilestmt
|
dowhilestmt
|
||||||
"return" expr ";"
|
"return" expr ";"
|
||||||
expr ";"
|
expr ";"
|
||||||
"{" stmt* "}"
|
"{" stmt* "}"
|
||||||
";"
|
";"
|
||||||
|
|
||||||
ifstmt: "if" "(" expr ")" stmt
|
ifstmt: "if" "(" expr ")" stmt
|
||||||
"if" "(" expr ")" stmt "else" stmt
|
"if" "(" expr ")" stmt "else" stmt
|
||||||
|
|
||||||
whilestmt: "while" "(" expr ")" stmt
|
whilestmt: "while" "(" expr ")" stmt
|
||||||
|
|
||||||
dowhilestmt: "do" stmt "while" "(" expr ")" ";"
|
dowhilestmt: "do" stmt "while" "(" expr ")" ";"
|
||||||
|
|
||||||
expr: expr binop expr
|
expr: expr binop expr
|
||||||
unop expr
|
unop expr
|
||||||
expr "[" expr "]"
|
expr "[" expr "]"
|
||||||
"(" expr ")"
|
"(" expr ")"
|
||||||
expr "(" exprs ")"
|
expr "(" exprs ")"
|
||||||
NAME
|
NAME
|
||||||
INT
|
INT
|
||||||
CHAR
|
CHAR
|
||||||
STRING
|
STRING
|
||||||
|
|
||||||
exprs: /*empty*/
|
exprs: /*empty*/
|
||||||
(expr ",")* expr
|
(expr ",")* expr
|
||||||
|
|
||||||
binop: "+" | "-" | "*" | "/" | "%" |
|
binop: "+" | "-" | "*" | "/" | "%" |
|
||||||
"=" |
|
"=" |
|
||||||
"<" | "==" | "!="
|
"<" | "==" | "!="
|
||||||
|
|
||||||
unop: "!" | "-" | "*"
|
unop: "!" | "-" | "*"
|
||||||
|
|
||||||
type: "int" stars
|
type: "int" stars
|
||||||
"char" stars
|
"char" stars
|
||||||
|
|
||||||
stars: "*"*
|
stars: "*"*
|
||||||
|
|
||||||
*****
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
Well, that is enough ranting for now.
|
Well, that is enough ranting for now.
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user