Updates to 1992/buzzard.2

Rename buzzard.2.README buzzard.2.README.md (perhaps buzzard.2.md would
be better?).

Format fixes in README.md.
This commit is contained in:
Cody Boone Ferguson committed 2023-04-09 10:55:03 -07:00
1 parent 6cb76a5b78
commit ddf8c5be37
2 files changed
+156 -134

No files matched your search

+144 -129
View File
@@ -1,58 +1,73 @@
# Best Language Tool:
Sean Barrett
Software Construction Company
430 Southwest Parkway, #1906
College Station, TX 77840
USA
Sean Barrett
Software Construction Company
430 Southwest Parkway, #1906
College Station, TX 77840
USA
## To build:
make all
```sh
make all
```
## To run:
First:
make first
```sh
make first
```
Second:
echo help | cat third help.th - | ./first
cat third demo5.th | ./first
```sh
echo help | cat third help.th - | ./first
cat third demo5.th | ./first
```
Third:
cat third help.th - | ./first
```sh
cat third help.th - | ./first
```
NOTE: Wait until Ok is printed and the type:
NOTE: Wait until 'Ok' is printed and the type:
2 3 + . cr
Yes you should really type the 2 letters: cr.
```
2 3 + . cr
```
Yes you should really type the 2 letters: `cr`.
Fourth:
Sorry, this is third!
```
Sorry, this is third!
```
## 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.
An alternate version of this entry, [buzzard.2.alt.c](buzzard.2.alt.c), is
provided. This alternate code does not contain a fix that the author notes
below.
To compile this alternate version:
make alt
```sh
make alt
```
Use `buzzard.2.alt` as you would `buzzard.2` above.
## Judges' comments
## Judges' comments:
Have fun!
## Author's comments
## 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,
@@ -88,152 +103,152 @@ included, but it only works on ASCII systems.
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
[third](third) first. In unix, you can do
% cat third help.th - | ./first
```sh
% 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](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](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.md](buzzard.2.README.md) 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:
`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](https://en.wikipedia.org/wiki/Forth_(programming_language), 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?
`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.)
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.
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.)
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.)
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 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 be 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?
`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).
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.)
```sh
2 3 + . cr
```
and THIRD responds
(which adds 2 and 3, and print the result and a newline.)
5
THIRD responds:
```
5
```
Now try:
: test 11 1 do i . loop cr ;
test
```
: test 11 1 do i . loop cr ;
test
```
and THIRD responds
1 2 3 4 5 6 7 8 9 10
```
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
When in `THIRD`, you can see how much space you're currently
using by typing:
here .
```
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).
The number THIRD replies with 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.
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.
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:
@@ -3,11 +3,16 @@ buzzard.2.design description of FIRST (design documentation of THIRD)
third implementation of THIRD in FIRST
help.th online listing of THIRD primitives
FIRST demos: use 'first < {demo}'
# FIRST demos:
Use `first < {demo}`.
demo1.1st prints Hello World! assuming ASCII
THIRD demos: use 'cat third {demo} | first'
# THIRD demos:
Use `cat third {demo} | first`.
demo1.th prints Hello World! regardless of character set
demo2.th demonstrates a simple loop
@@ -16,7 +21,9 @@ demo4.th recursive factorial calculating on the way up
demo5.th recursive factorial calculating on the way down
demo6.th demonstrates switching from compiler to execution mode
Interactive THIRD: use 'cat third - | first'.
# Interactive THIRD:
To include the primitive on-line help, use
'cat third help.th - | first'.
Use `cat third - | first`.
To include the primitive on-line help, use
`cat third help.th - | first`.