
                                Welcome to Y ("why") v 0.71 pre-beta. 

Y is a stack-oriented FORTH-type programming language derived from 
Wouter van Oortmerssen's "FALSE".
Like FALSE, Y is cryptic to the extreme. (But it is much more powerful. 
Virtually all of the example programs  in "Kernighan & Ritchie - 
Programming in C" can be done in Y in a fraction of time and code. :-) )

This stuff is provided as-is, I won't take any responsibility for damaged software, 
hardware, or brains. Since I'm still developing (well, something like
that...), this is a very early beta version. Y looks stable and the
small Y programs I tested worked fine, but this  doesn't have to mean
anything.

                                                               Thomas Fischbacher
                                                               dexam@another.gun.de

THIS IS A PRE-BETA VERSION! 

This stuff comes _without_ a makefile.

If you want to compile the Y interpreter for your machine:
* translate main.cpp, class_Y_program.cpp
* create two object files from class_Y_program_Keywords.cpp: one with #define NOCHECK, one without. 
* Link all four files to executable. Add math library and some sugar, let it boil for half an hour, serve.



Copyright:

All files included in this package are (C) 1995 by T.Fischbacher. You
may freely distribute it as long as you don't make any changes. The
full archive has to be distributed, don't leave files out.
(If you want to make changes, ask me first.) You may not use class
Y_program in your own code without my permission. (Though you most
probably will get it if you ask me.)
No money may be charged for this package or any of its contents.



What is it for?

Y is what you get when you cross a programmable pocket calculator with
a programming language like C.
For some, Y is merely a toy. (But a very powerful one.)
For others, it's just another cryptic unixlike command which probably
can save you much time in special situations.

There are many good programming languages for doing large projects,
but what do you use if you want a quick solution for those small but
nasty everyday's problems which can't be managed with common commands?
One day, I had recieved an ascii data file which must have gone
through some noise-producing 7/8-Bit-gate. About twenty per cent of
the characters had their highest bit set, so the text was totally
corrupted. With Y, it took me less than twenty keystrokes to
solve the problem by writing a binary filter. Here it is:
(^$1_=~#127&,)%



Concepts:

The main paradigm is that ONE action is caused by ONE character. 
(I ask you: do you prefer a text editor where you only have to press A-s
to save a file, or one where you have to type something like 
"<ESC> save-file" or ":w!" every time?)
As you can see, Y is extremely low in redundancy. Therefore, you will 
probably never get "syntax errors". (Except if you don't match your braces 
or something like that.) Virtually any error is a "real" error.
As every Emacs user knows, there are more things
you want to do than characters in ASCII. In Y, this problem is solved
by using multiple codepages. The "Y" command in Y is for switching between
them. (Even cheap pocket calculators have a MODE key nowadays, right?)


How it works:

Write a program with your favourite text editor, and start the Y
interpreter giving the program name as an argument. That's it.
Alternatively, you may give programs as command-line arguments. 
(See interpreter options)


Portability:

Ought to run/compile on any 32-Bit comuter/os. (Sorry, no 64 bit support...) 
Properly written Y programs should be highly portable.
Won't run with DOS and its WINDY graphical interface, because of
brain-damaged DOS memory-management. Those half-eaten-fruit-computers
also will have difficulties, since they don't know command lines.
I have tested Y under HP-UX and AmigaOS and compiled it on a RiscPC,
and would be glad to hear your experiences with Y running under
other OSes. (OS/2, Linux, RiscOS, for example.)



What I want from you:

FEEDBACK!



Design of the Source code:

"class Y_program" is an object oriented interface to procedural
code. It provides means to load, step, and run Y programs. Why so, if,
for example, "keywords" certainly are "objects" in the sense of the OO
paradigm and therefore ought to deserve an own class?
Y itself won't change much, therefore, a smaller but more static
implementation serves well. Having a function for any keyword doesn't
seem a good idea, because this would be extremely confusing.
* #defines, casting:
Yes, they are extremely bad C(++) style, no question. Why so?
You'll see that all casts are neccessary since the Y programming
language itself doesn't discern datatypes, and many Y functions are mapped
to C functions. 
And about the #defines: they serve to control compilation only. At
least, they ought to. :-)
* Using C I/O functions:
Since many of the Y commands are 1:1 mapped onto C functions, the Y interpreter 
also uses ANSI-C-functions for loading programs, printing error messages, etc.


Plans for the future:

I see Y as a "model language". Extremely simple, but powerful enough
for virtually all "small things". You may have noticed that
do_keyword() is a virtual function, allowing other classes to derive
from Y_program and provide their own codepages. (Such codepages should
have numbers >=1000. If you want to write one, ask me and I'll give
you a number. This will prohibit number conflicts.)
At the moment I'm writing a "player"-subclass plus some additional framework
to implement some C-robots like game with Y. =8-) 
Did you see how easily multiple Y programs can be stepped parallel (if
they don't perform "waiting actions" like getting chars from stdin)?
I also plan to write a biosphere simulator in which "animals" are
controlled by Y programs. (Mail me for more info.)
And then there are a lot of other things I'd like to implement in Y.
(A crossing between a matrix calculator and a renderer, for example.)
And there will be a book about it. :-)



The Language:
-------------
Numbers:  anything composed of 0123456789 starting with non-0
          (value will be placed on stack)

Strings:  anything between quotes. "This, for example."
          (start memory address will be placed on stack, strings are automatically 0-terminated)

Comments: anything between {}. {Like here}
          For god's sake, use them in your code!

Variables: `a'-`z'; 26*16 all-purpose long-words of memory may be used.
            a,b,c,d,... put addresses of Long-word varbase+0, varbase+16,varbase+32,
             varbase+48,... on stack.
            Use in combination with `V',`;',`:'
            initialized at program start:
             c: argc: number of arguments
             v: argv: start address of array of pointers to arguments
             i/o/e: std(in/out/err) standard i/o file handles

Subroutines: code enclosed in [] isn't executed when read by the interpreter;
             its memory start address is placed on stack instead. May be nested.
             See `!'

!:           get memory address from stack and execute subroutine there.

if:          "?": conditional subroutine call. Execute subroutine
             starting at first stack value only if second stack value is not 0.

ifthenelse: Of course you can use the same technique as in FALSE
            (doubling truthvalues, negating), but for more convenience
            (it is extremely nasty to have to keep the stack position of
             else-truthflag in the then-branch in mind), a special
             ifthenelse has been added: 
            "J" takes two start addresses and a truthvalue from stack.
            (Order on stack: truthval, true-branch, false-branch)
            true-branch is executed if and only if truthval !=0;
            otherwise, false-branch is executed. Sorry, I wanted to
            use another symbol for this, but 'J' was the last one left. 

A word about '?', 'J' and '!' subroutine calls: 
Y performs proper tail recursion. :-)

Loops:       (#): Anything enclosed between () is performed in a loop.
             `#' reads topmost stack value and, if =0, exits innermost loop
             Loops may be nested.
             Loops are purely iterative.

':           Escape-character: place Ascii value of next character in sourcecode
             on stack.

"Y": change codepage.
Before call: topmost stack item: number of new codepage
After call:  topmost stack item: number of previous codepage


ALWAYS REMEMBER: for the sake of portability, never make any
system-dependent assumptions in your programs. never.
There are machines on which long words have to be 4n-aligned;
MSB/LSB first is also very machine dependent.

--------------------------------------------------------------------------------
CODEPAGES:

Entry Format:

command: <Stack contents before execution> -> <Stack contents after execution> % Description  [>> (C-equivalent)]

Stack contents notation: rightmost is topmost

Odd codepages provide the same functions as their even counterparts, but don't perform checks.
(stack over/underflow, division by zero)

//+ Codepage 0: Basic things, memory management, I/O
---------------

STACK MANIPULATORS:   @$\%N              @ ROT  - $ DUP  - \ SWAP - % DROP - N n-th
ARITHMETICS:          +-*/M_             + ADD  - - SUB  - * MULS - / DIVS - M - MOD - _ NEG
LOGIC:                &|~                & AND  - | OR   - ~ NOTSET
COMPARISON:           ><=                > MORE - < LESS - = EQUAL
STANDARD I/O          `.,^B              ` PRINT STRING - . PRINT INT - , PRINT CHAR - ^ GETCHAR - B FLUSH
MEMORY MANAGEMENT:    AF                 A ALLOC - F FREE
MEMORY MANIPULATION:  ;:                 ; PEEK - : POKE
FILE COMMANDS:        OCRWETSLGPI        O OPEN - C CLOSE - R READ - W WRITE - E EOF - T TELL - S SEEK
                                         L FLUSH - G GET - P PUT - I INPUT
MISC:                 QKDUV              Q QUIT - K KOMMANDLINE - D DICE - U TIME - V 4*+




STACK MANIPULATORS:

@: <s3> <s2> <s1>              -> <s2> <s1> <s3>         % ROT: rotate topmost three stack elements
$: <s1>                        -> <s1> <s1>              % DUP: duplicate topmost element
\: <s2> <s1>                   -> <s1> <s2>              % SWAP: exchange topmost elements
%: <s1>                        ->                        % DROP: kill topmost stack element
N: <s_n> <s_n-1> ... <s2> <s1> -> ... <s2> <s_[s1+2]>    % N-th: copy n-th stack element to top. 0N <=> $

ARITHMETICS:

+: <s2> <s1>                   -> < <s1>+<s2> >          % ADD: add integers             >> +
-: <s2> <s1>                   -> < <s1>-<s2> >          % SUB: subtract integers        >> -
*: <s2> <s1>                   -> < <s1>*<s2> >          % MULS: multiplicate signed     >> *
/: <s2> <s1>                   -> < <s1>/<s2> >          % DIVS: divide signed           >> /
M: <s2> <s1>                   -> < <s1> MOD <s2> >      % MOD: Modulo                   >> %
_: <s1>                        -> < -<s1> >              % NEG: unary minus              >> -

LOGIC:

&: <s2> <s1>                   -> < <s1> AND <s2> >      % AND: bit-wise AND             >> &
|: <s2> <s1>                   -> < <s1> OR <s2> >       % OR: bit-wise OR               >> |
~: <s1>                        -> < NOT <s1> >           % NOTSET: 1 if s1 is zero, 0 otherwise. >> !  (not ~ !!!)

COMPARISON:

>: <s2> <s1>                   -> < <s2> > <s1> ? 1:0 >  % GREATER: 1 if s2>s1, 0 otherwise >> >
<: <s2> <s1>                   -> < <s2> < <s1> ? 1:0 >  % SMALLER: 1 if s2<s1, 0 otherwise >> <
=: <s2> <s1>                   -> < <s2> = <s1> ? 1:0 >  % EQUAL:   1 if s2=s1, 0 otherwise >> ==

STANDARD I/O:

`: <s1>                        ->                        % PRINT STRING: output 0-terminated string starting at memory address s1
.: <s1>                        ->                        % PRINT INT: output signed integer
,: <s1>                        ->                        % PRINT CHAR: output char with Ascii code s1
^:                             -> <s1>                   % GETCHAR: read a single keystroke's ascii value. -1 if EndOfFile
B:                             ->                        % FLUSH: Flush standard-output-buffer

MEMORY MANAGEMENT:

A: <s1>                        -> <s2>                   % ALLOCATE: allocate s1 bytes of aligned memory,        >> calloc
                                                           place start address on stack, or zero, if error.
F: <s1>                        ->                        % FREE: free previously allocated memory starting at s1 >> free

MEMORY MANIPULATION:

;: <s1>                        -> <s2>                   % PEEK: read memory value (->s2) at memory position s1
:: <s2> <s1>                   ->                        % POKE: write value s2 to memory position s1
Z: <s1>                        ->                        % SIZE_SET: set peek/poke width to byte(1),word(2),longword(4)
V: see under MISC


FILE I/O:

O: <s2> <s1>                   -> <s3>                   % OPEN: s2=filename-string; s1=mode; s3=file handle     >> fopen
                                                           mode: 0=read,1=write,2=read+write,3=append
C: <s1>                        ->                        % CLOSE: s1=file handle                                 >> fclose
R: <s3> <s2> <s1>              -> <s4>                   % READ: read max. s1 bytes to memory area at s2 from file with handle s1
                                                           s4=number of bytes that have been read.               >> fread
W: <s3> <s2> <s1>              -> <s4>                   % WRITE: counterpart to READ, identical in arguments.   >> fwrite
E: <s1>                        -> <s2>                   % EOF: if file w. handle s1 is at end position, s2=1, 0 otherwise >> feof
T: <s1>                        -> <s2>                   % TELL: s2=read/write position in file w. handle s1     >> ftell
S: <s3> <s2> <s1>              -> <s4>                   % SEEK: seek r/w position s2 in file w. handle s3, s4=0 if success, 1 if error
                                                           s1: 0: seek from beginning; 1: from current pos; 2: from end    >> fseek
L: <s1>                        ->                        % FLUSH: flush buffer of file s1                        >> fflush
G: <s1>                        -> <s2>                   % GET: read single character s2 from file s1. Like ^.   >> fgetc
P: <s2> <s1>                   -> <s3>                   % PUT: write ascii s1 to file s2. return -1 if error, otherwise, return s1
                                                                                                                 >> fputc
I: <s3> <s2> <s1>              -> <s4>                   % INPUT: read line from file s3, max. chars s2, to mem location s1
                                                                                                                 >> fgets

MISC. STUFF:

Q:                             ->                        % QUIT: quit program
K: <s1>                        -> <s2>                   % KOMMAND-LINE: execute string s1 as shell command. s2=return-value
                                                                                                                 >> system
D: <s1>                        -> (<s2>)                 % DICE: s1>1: generate random number between 1 and s1
                                                                 s1<0: set random seed to -s1. No value returned in this case!
                                                                 s1=0: use timer to set random seed.
                                                                 s1=1: simply return a C-rand() random number.   >> RAND

U:                             -> <s1>                   % TIME ("Uhr" :-) ): s2=number of seconds since 1.1.1970 00:00:00
V: <s2> <s1>                   -> <s3>                   % ? VAR ?: essentially a shortcut for 4*+
                                                           Useful for accessing s1-th-longword (start address s2)
                                                           or for accessing `higher variables':
                                                           a <=> a0V; a1V...a15V can also be used. See "variables".
//-

//+ Codepage 2: single-precision floating point math
---------------

STACK MANIPULATORS:   @$\%N
Peek/Poke:            ;:Z
FLOAT ARITHMETICS:    +-*/M_   + ADD - - SUB - * MULS - / DIVS - M MOD - _ NEG
FLOAT COMPARISON:     ><=H     > MORE - < LESS - = EQUAL - H SET-COMPARISON-PRECISION
STANDARD I/O          `.,^B    ` PRINT STRING - . PRINT FLOAT - , PRINT CHAR - ^ GETFLOAT - B FLUSH
CONVERSION:           FI       F INT->FLOAT - I FLOAT->INT
FUNCTIONS             RSCTEP   R ROOT - S SIN - C COS - T TAN - E EXP - P POW
                      ALD      A ARCTAN - L LN - D DECIMAL LG
                      G        G GAMMA FUNCTION (as stirling's formula)


STACK MANIPULATORS:  As usual
Peek/Poke:           As usual

FLOAT ARITHMETICS:

+: <s2> <s1>                   -> < <s1>+<s2> >          % ADD: add floats             >> +
-: <s2> <s1>                   -> < <s1>-<s2> >          % SUB: subtract floats        >> -
*: <s2> <s1>                   -> < <s1>*<s2> >          % MULS: multiplicate signed     >> *
/: <s2> <s1>                   -> < <s1>/<s2> >          % DIVS: divide signed           >> /
M: <s2> <s1>                   -> < <s1> MOD <s2> >      % MOD: float modulo             >> %
_: <s1>                        -> < -<s1> >              % NEG: unary minus              >> -


FLOAT COMPARISON:

>: <s2> <s1>                   -> < <s2> > <s1> ? 1:0 >  % GREATER: 1 if s2>s1, 0 otherwise >> >
<: <s2> <s1>                   -> < <s2> < <s1> ? 1:0 >  % SMALLER: 1 if s2<s1, 0 otherwise >> <
=: <s2> <s1>                   ->                        % returns 1 if |s2-s1| < float-compare-tolerance, 0 otherwise
H  <s1>                        ->                        % sets float-compare-tolerance to s1


STANDARD I/O:

`: <s1>                        ->                        % PRINT STRING: output 0-terminated string starting at memory address s1
.: <s1>                        ->                        % PRINT FLOAT: output float
,: <s1>                        ->                        % PRINT CHAR: output char with Ascii code s1
^:                             -> <s1>                   % GETFLOAT: scanf("%f",&x); read a float onto stack
B:                             ->                        % FLUSH: Flush standard-output-buffer


CONVERSION:

F: <s1>                        -> <s2>                   % Convert int to float
I: <s2>                        -> <s2>                   % Convert float to int


FUNCTIONS:

R: <s1>                        -> <s2>                   % s2=sqrt(s1)
S: <s1>                        -> <s2>                   % s2=sin(s1)
C: <s1>                        -> <s2>                   % s2=cos(s1)
T: <s1>                        -> <s2>                   % s2=tan(s1)
E: <s1>                        -> <s2>                   % s2=exp(s1)
P: <s1> <s2>                   -> <s3>                   % s3=s1^s2
A: <s1>                        -> <s2>                   % s2=atan(s1)
L: <s1>                        -> <s2>                   % s2=ln(s1)
D: <s1>                        -> <s2>                   % s2=log10(s1)
G: <s1>                        -> <s2>                   % s2=gamma(s1+1) ~ s1!
                                                         % implemented as stirling's formula
                                                         % n! ~ (n/e)^n sqrt(2pi n) (1+1/12n+1/288n^2)

//-






