Fixes in 1992/buzzard.2/README.md
Typo/spelling fixes and formatting fixes.
This commit is contained in:
1 parent
b2ff5f15fa
commit
85ddf1a25a
1 file changed
+164
-160
+164
-160
@@ -10,29 +10,32 @@
|
||||
|
||||
make all
|
||||
|
||||
### To run
|
||||
## To run:
|
||||
|
||||
First:
|
||||
First:
|
||||
|
||||
make first
|
||||
|
||||
Second:
|
||||
Second:
|
||||
|
||||
echo help | cat third help.th - | ./first
|
||||
cat third demo5.th | ./first
|
||||
|
||||
Third:
|
||||
Third:
|
||||
|
||||
cat third help.th - | ./first
|
||||
|
||||
NOTE: Wait until Ok is printed and the type:
|
||||
2 3 + . cr <-- yes you should really type the 2 letters: cr
|
||||
NOTE: Wait until Ok is printed and the type:
|
||||
|
||||
Forth:
|
||||
2 3 + . cr
|
||||
|
||||
Yes you should really type the 2 letters: cr.
|
||||
|
||||
Fourth:
|
||||
|
||||
Sorry, this is third!
|
||||
|
||||
### Alternate code
|
||||
## Alternate code:
|
||||
|
||||
An alternate version of this entry, `buzzard.2.alt.c`, is provided. This alternate code
|
||||
does not contain a fix that the author notes below.
|
||||
@@ -45,191 +48,192 @@ Use `buzzard.2.alt` as you would `buzzard.2` above.
|
||||
|
||||
## Judges' comments
|
||||
|
||||
Have fun!
|
||||
Have fun!
|
||||
|
||||
## Author's comments
|
||||
|
||||
What it does:
|
||||
What it does:
|
||||
|
||||
first implements a relatively primitive stack machine. How
|
||||
primitive? It supplies 13 visible primitives: 3 arithmetic,
|
||||
1 comparison, 2 memory-access, 2 character I/O, 3 primitives
|
||||
for defining new words, 1 tokenizing, and 1 special stack
|
||||
operation. (There are also three internal operations for
|
||||
the stack machine: 'push this integer', 'call this code',
|
||||
and 'compile a call to this code'.)
|
||||
`first` implements a relatively primitive stack machine. How
|
||||
primitive? It supplies 13 visible primitives: 3 arithmetic,
|
||||
1 comparison, 2 memory-access, 2 character I/O, 3 primitives
|
||||
for defining new words, 1 tokenizing, and 1 special stack
|
||||
operation. (There are also three internal operations for
|
||||
the stack machine: 'push this integer', 'call this code',
|
||||
and 'compile a call to this code'.)
|
||||
|
||||
It is very difficult to accomplish anything with this set
|
||||
of primitives, but they do have an interesting property.
|
||||
It is very difficult to accomplish anything with this set
|
||||
of primitives, but they do have an interesting property.
|
||||
|
||||
This--what this interesting property is, or in other words
|
||||
what first is good for--is the major obfuscation; there are
|
||||
also minor source obfuscations, as well as some design tricks
|
||||
that are effectively obfuscations. Details on the obfuscations
|
||||
are below, and the interesting property is discussed much
|
||||
further down.
|
||||
This--what this interesting property is, or in other words
|
||||
what first is good for--is the major obfuscation; there are
|
||||
also minor source obfuscations, as well as some design tricks
|
||||
that are effectively obfuscations. Details on the obfuscations
|
||||
are below, and the interesting property is discussed much
|
||||
further down.
|
||||
|
||||
|
||||
How to run it:
|
||||
### How to run it:
|
||||
|
||||
first expects you to first enter the names of the 13 primitives,
|
||||
separated by whitespace--it doesn't care what you name them, but
|
||||
if all the names aren't unique, you won't be able to use some of
|
||||
them. After this you may type any sequence of valid first input.
|
||||
Valid first input is defined as any sequence of whitespace-delimited
|
||||
tokens which consist of primitives, new words you've defined, and
|
||||
integers (as parsed by "%d"). Invalid input behaves unpredictably,
|
||||
but gives no warning messages. A sample program, demo1.1st, is
|
||||
included, but it only works on ASCII systems.
|
||||
`first` expects you to first enter the names of the 13 primitives,
|
||||
separated by whitespace--it doesn't care what you name them, but
|
||||
if all the names aren't unique, you won't be able to use some of
|
||||
them. After this you may type any sequence of valid first input.
|
||||
Valid first input is defined as any sequence of whitespace-delimited
|
||||
tokens which consist of primitives, new words you've defined, and
|
||||
integers (as parsed by `"%d"`). Invalid input behaves unpredictably,
|
||||
but gives no warning messages. A sample program, `demo1.1st`, is
|
||||
included, but it only works on ASCII systems.
|
||||
|
||||
Do not expect to be able to do anything interesting with first.
|
||||
Do not expect to be able to do anything interesting with first.
|
||||
|
||||
To do something interesting, you need to feed first the file
|
||||
third first. In unix, you can do
|
||||
To do something interesting, you need to feed first the file
|
||||
third first. In unix, you can do
|
||||
|
||||
% cat third help.th - | first
|
||||
% cat third help.th - | ./first
|
||||
|
||||
to do this. Hopefully most operating systems will provide a
|
||||
way to do this. It may take some time for this to complete
|
||||
(I seem to remember it taking several minutes on an 8086 PC);
|
||||
THIRD will prompt you when it is finished. The file third has
|
||||
not been obfuscated, due to sheer kindness on the author's part.
|
||||
to do this. Hopefully most operating systems will provide a
|
||||
way to do this. It may take some time for this to complete
|
||||
(I seem to remember it taking several minutes on an 8086 PC);
|
||||
THIRD will prompt you when it is finished. The file third has
|
||||
not been obfuscated, due to sheer kindness on the author's part.
|
||||
|
||||
For more information on what you can do once you've piped
|
||||
THIRD into first, type 'help' and consult FORTH manuals for
|
||||
further reference. Six sample THIRD programs are included
|
||||
in the files demo[1-6].th. buzzard.2.README has more
|
||||
information.
|
||||
For more information on what you can do once you've piped
|
||||
THIRD into first, type 'help' and consult FORTH manuals for
|
||||
further reference. Six sample THIRD programs are included
|
||||
in the files demo[1-6].th. buzzard.2.README has more
|
||||
information.
|
||||
|
||||
Keep in mind that you are still running first, and
|
||||
are for the most part limited by first's tokenizer
|
||||
(notably, unknown words will attempt to be parsed as
|
||||
integers.) It is possible to build a new parser that
|
||||
parses by hand, reading a single character at a time;
|
||||
however, such a parser cannot easily use the existing
|
||||
dictionary, and so would have to implement its own,
|
||||
thus requiring reimplementing all of first and third
|
||||
a second time--I did not care to tackle this project.
|
||||
Keep in mind that you are still running first, and
|
||||
are for the most part limited by first's tokenizer
|
||||
(notably, unknown words will attempt to be parsed as
|
||||
integers.) It is possible to build a new parser that
|
||||
parses by hand, reading a single character at a time;
|
||||
however, such a parser cannot easily use the existing
|
||||
dictionary, and so would have to implement its own,
|
||||
thus requiring reimplementing all of first and third
|
||||
a second time--I did not care to tackle this project.
|
||||
|
||||
|
||||
Compiling:
|
||||
### Compiling:
|
||||
|
||||
first is reasonably portable. You may need to adjust the
|
||||
size of the buffers on smaller machines; m[] needs to be
|
||||
at least 2000 long, though.
|
||||
`first` is reasonably portable. You may need to adjust the
|
||||
size of the buffers on smaller machines; `m[]` needs to be
|
||||
at least 2000 long, though.
|
||||
|
||||
I say first is portable mainly because it uses native types.
|
||||
Unlike FORTH, which traditionally allows byte and multi-byte
|
||||
operations, all operations are performed on C 'int's. That
|
||||
means first code is only as portable as the same code would
|
||||
be in C. As in C, the result of dividing -1 by 2 is machine
|
||||
(or rather compiler) dependent.
|
||||
I say `first` is portable mainly because it uses native types.
|
||||
Unlike FORTH, which traditionally allows byte and multi-byte
|
||||
operations, all operations are performed on C `int`s. That
|
||||
means first code is only as portable as the same code would
|
||||
be in C. As in C, the result of dividing -1 by 2 is machine
|
||||
(or rather compiler) dependent.
|
||||
|
||||
How is first obfuscated?
|
||||
### How is first obfuscated?
|
||||
|
||||
first is obfuscated in several ways. Some minor obfuscations
|
||||
like &w[&m[1]][s] for s+m[w+1] were in the original source
|
||||
but are no longer because, apparently, ANSI doesn't allow it
|
||||
(gcc -ansi -pedantic doesn't mind it, though.)
|
||||
Other related obfuscations are still present. The top of the
|
||||
stack is cached in a variable, which increases performance
|
||||
massively if the compiler can figure out to keep it in a register;
|
||||
it also obfuscates the code. (Unfortunately, the top of stack
|
||||
is a global variable and neither gcc nor most bundled compilers
|
||||
seem to register allocate it.)
|
||||
`first` is obfuscated in several ways. Some minor obfuscations
|
||||
like `&w[&m[1]][s]` for `s+m[w+1]` were in the original source
|
||||
but are no longer because, apparently, ANSI doesn't allow it
|
||||
(gcc -ansi -pedantic doesn't mind it, though.)
|
||||
Other related obfuscations are still present. The top of the
|
||||
stack is cached in a variable, which increases performance
|
||||
massively if the compiler can figure out to keep it in a register;
|
||||
it also obfuscates the code. (Unfortunately, the top of stack
|
||||
is a global variable and neither gcc nor most bundled compilers
|
||||
seem to register allocate it.)
|
||||
|
||||
More significant are the design obfuscations. m[0] is the
|
||||
"dictionary pointer", used when compiling words, and m[1] is
|
||||
the return stack index. Both are used as integer offsets into
|
||||
m. Both are kept in m, instead of as separate pointers,
|
||||
because they are then accessible to first programs, which is a
|
||||
crucial property of first. Similarly the way words are stored
|
||||
in the dictionary is not obvious, so it can be difficult to
|
||||
follow exactly what the compiler words are doing.
|
||||
More significant are the design obfuscations. `m[0]` is the
|
||||
"dictionary pointer", used when compiling words, and `m[1]` is
|
||||
the return stack index. Both are used as integer offsets into
|
||||
m. Both are kept in m, instead of as separate pointers,
|
||||
because they are then accessible to first programs, which is a
|
||||
crucial property of first. Similarly the way words are stored
|
||||
in the dictionary is not obvious, so it can be difficult to
|
||||
follow exactly what the compiler words are doing.
|
||||
|
||||
Assuming you've waded through all that, you still have
|
||||
to penetrate the most significant obfuscation. Traditionally,
|
||||
the question is whether a reader can answer the question "what
|
||||
will this do when I run it". A reader who has deciphered first
|
||||
to this point may think they know the answer to this question,
|
||||
but they may not know the answer to the more important question,
|
||||
"what will this program do when given the right input?" FORTH
|
||||
afficianados, and especially FORTH implementors, may recognize
|
||||
the similarity of the internal compiler format to many FORTH
|
||||
interal representations, and, being aware that FORTH interpreters
|
||||
can often by self-compiling, may be suspicious that this program
|
||||
can compile FORTH, or a significant subset of it, or at least be
|
||||
capable of doing so if fed the right input. Of course, the name
|
||||
"THIRD" should be a dead giveaway, if the name "first" wasn't.
|
||||
(These numbers were largely chosed because they were five letters
|
||||
long, like "FORTH", and would not require truncation to five
|
||||
letters, which would be a dead giveaway. Besides, THIRD represents
|
||||
a step backwards, in more ways than one.)
|
||||
Assuming you've waded through all that, you still have
|
||||
to penetrate the most significant obfuscation. Traditionally,
|
||||
the question is whether a reader can answer the question "what
|
||||
will this do when I run it". A reader who has deciphered first
|
||||
to this point may think they know the answer to this question,
|
||||
but they may not know the answer to the more important question,
|
||||
"what will this program do when given the right input?" FORTH
|
||||
aficionados, and especially FORTH implementers, may recognize
|
||||
the similarity of the internal compiler format to many FORTH
|
||||
internal representations, and, being aware that FORTH interpreters
|
||||
can often by self-compiling, may be suspicious that this program
|
||||
can compile FORTH, or a significant subset of it, or at least be
|
||||
capable of doing so if fed the right input. Of course, the name
|
||||
"THIRD" should be a dead giveaway, if the name "first" wasn't.
|
||||
(These numbers were largely chosen because they were five letters
|
||||
long, like "FORTH", and would not require truncation to five
|
||||
letters, which would be a dead giveaway. Besides, THIRD represents
|
||||
a step backwards, in more ways than one.)
|
||||
|
||||
|
||||
What exactly is first, then?
|
||||
### What exactly is `first`, then?
|
||||
|
||||
first is a tiny interpreter which implements a sufficient
|
||||
pseudo-subset of FORTH to allow it to bootstrap a relatively
|
||||
complete version of FORTH (based loosely on forth79), which
|
||||
I call THIRD. Complete relative to what, I'm not sure.
|
||||
`first` is a tiny interpreter which implements a sufficient
|
||||
pseudo-subset of FORTH to allow it to bootstrap a relatively
|
||||
complete version of FORTH (based loosely on forth79), which
|
||||
I call THIRD. Complete relative to what, I'm not sure.
|
||||
|
||||
I believe first is close to the smallest amount of code possible
|
||||
to get this effect *using forth-style primitives*, and still have
|
||||
some efficiency (it is possible to get by without multiplication
|
||||
if you have addition, obviously). In the design file, design,
|
||||
I give a justification for why each primitive in first was included.
|
||||
I believe first is close to the smallest amount of code possible
|
||||
to get this effect *using forth-style primitives*, and still have
|
||||
some efficiency (it is possible to get by without multiplication
|
||||
if you have addition, obviously). In the design file, design,
|
||||
I give a justification for why each primitive in first was included.
|
||||
|
||||
THIRD is sorta slow, because first has so few primitives that
|
||||
many things that are primitives in FORTH (like swap) take a
|
||||
significant amount of time in THIRD.
|
||||
THIRD is sorta slow, because first has so few primitives that
|
||||
many things that are primitives in FORTH (like swap) take a
|
||||
significant amount of time in THIRD.
|
||||
|
||||
When you get the 'Ok.' message from third, try out some sample
|
||||
FORTH code (first has no way of knowing if keyboard input is
|
||||
waiting, so it can't actually prompt you in a normal way. It
|
||||
only prints 'Ok.' after you define a word).
|
||||
|
||||
2 3 + . cr ( add 2 and 3, and print it and a newline.)
|
||||
|
||||
and THIRD responds
|
||||
|
||||
5
|
||||
|
||||
Now try:
|
||||
|
||||
: test 11 1 do i . loop cr ;
|
||||
test
|
||||
|
||||
and THIRD responds
|
||||
|
||||
1 2 3 4 5 6 7 8 9 10
|
||||
When you get the 'Ok.' message from third, try out some sample
|
||||
FORTH code (first has no way of knowing if keyboard input is
|
||||
waiting, so it can't actually prompt you in a normal way. It
|
||||
only prints 'Ok.' after you define a word).
|
||||
|
||||
|
||||
When in THIRD, you can see how much space you're currently
|
||||
using by typing
|
||||
|
||||
here .
|
||||
|
||||
The number THIRD replies is the number of machine words (ints)
|
||||
that the dictionary (the first code) takes up, plus the
|
||||
512 ints for the return stack. If you compile the basic
|
||||
THIRD system without the help word (strings take up one
|
||||
int per character in the string!), you should find that
|
||||
you're using around 1000 ints (plus the return stack).
|
||||
|
||||
Thus THIRD gives you a relatively complete FORTH system in
|
||||
less than 700 chars of C source + about 1000 ints of
|
||||
memory--and it's portable too (you could copy over the
|
||||
THIRD memory dump to another machine, in theory). If the
|
||||
above numbers seem to you to be mixing apples and oranges
|
||||
(C source and compiled THIRD code), note that you should
|
||||
in theory be able to stick the compiled THIRD code into
|
||||
the C source.
|
||||
2 3 + . cr ( add 2 and 3, and print it and a newline.)
|
||||
|
||||
|
||||
Software Construction Company gets credit for rekindling
|
||||
my interest in FORTH and thus indirectly inspiring me
|
||||
to write this program.
|
||||
and THIRD responds
|
||||
|
||||
5
|
||||
|
||||
Now try:
|
||||
|
||||
: test 11 1 do i . loop cr ;
|
||||
test
|
||||
|
||||
and THIRD responds
|
||||
|
||||
1 2 3 4 5 6 7 8 9 10
|
||||
|
||||
|
||||
When in THIRD, you can see how much space you're currently
|
||||
using by typing
|
||||
|
||||
here .
|
||||
|
||||
The number THIRD replies is the number of machine words (ints)
|
||||
that the dictionary (the first code) takes up, plus the
|
||||
512 ints for the return stack. If you compile the basic
|
||||
THIRD system without the help word (strings take up one
|
||||
int per character in the string!), you should find that
|
||||
you're using around 1000 ints (plus the return stack).
|
||||
|
||||
Thus THIRD gives you a relatively complete FORTH system in
|
||||
less than 700 chars of C source + about 1000 ints of
|
||||
memory--and it's portable too (you could copy over the
|
||||
THIRD memory dump to another machine, in theory). If the
|
||||
above numbers seem to you to be mixing apples and oranges
|
||||
(C source and compiled THIRD code), note that you should
|
||||
in theory be able to stick the compiled THIRD code into
|
||||
the C source.
|
||||
|
||||
Software Construction Company gets credit for rekindling
|
||||
my interest in FORTH and thus indirectly inspiring me
|
||||
to write this program.
|
||||
|
||||
## Copyright and CC BY-SA 4.0 License:
|
||||
|
||||
|
||||
Reference in new issue
Block a user