Lpmud Stack Compiler
		    User Manual

		    written by
	       square@cco.caltech.edu

Introduction
============

LSC is a high-level language operated through game-objects (e.g.monsters,
player-owned shops/castles) in lpmud to perform
actions which could be added, edited and initiated without altering
the existing lpc code inside the game-object, and without penalizing
the flexibility and the scope of the action which could be performed.
It will also help economizing the memory in a mud where complex npc's are
common.

Concepts
========

One of the philosophies behind all this things is that monsters should be
able to access and exploit as much as interactive users can. Furthermore,
monster should be able to take commands (not just commands by add_action()) to
affect their long term behavior. For example, a monster could be instructed
by a somewhat more influential player to "Once in a while check the who list
to see if Mr.? is on, if yes, search and kill him!... while on the other
hand hunt down monsters with equipments so that the next time I log on
you can give them to me" (of course the real codes are less English-like)

Also, if a player can tell other players to do things, why can't players do
the same to npc's, and why not npc's to npc's also?

It's like LPC provides a link for wizard to code the game, but not
hacking the system; LSC provides a link for players to make their own
decisions for game-objects, but not hacking the game. You can think of
LSC being something like:


   .--------------------------------------.
   |    LSC                               |<- players
   | .-------------------------.          |
   | |  Mudlib                 |          |
   | | .------.                |<-wizards |
   | | |Kernel|<-driver hacker |          |
   | | `------'                |          |
   | `----+--------------------'          |
   `------|-------------------------------'
          |
      .---+--.
      | Game |
      `------'

LSC commands are all linked up to some mudlib's functions that can
take care of many situations which player-programmers don't want to and
should not take care of.  Take users() as an LPC analogue of LSC commands.
We have users() to take care of all log-in users. Of course,
it can be done inside mudlib, but it'd be inefficient (and quite insecure,
too).  So with that in mind, i don't think player would need to code too
much and most of their codes will be tiny.

Furthermore, starting from LSC 3.0, everything that exists in LSC code
can be treated as "first class objects" a little like LISP. More 
operators (themselves objects, of course) would be added to support this
concept. Note that LSC objects mean something unrelated to LPC objects at all.
So after 3.0 functions could be passed, edited and returned, lsc code can be
investigated, matched, and substituted, etc.
[ NOTE: due to my heavy course load, 3.0 has never been realized *sigh* ]

Why Another Language?
=====================
* easy simulation of a "real" monster
If you want to write a monster which "looks" smarter, very often you have
to write a huge chunk of code doing your call_out/testing stuff, but all of
this now could be compressed into a compact LSC string.

varargs void catch_event(int type, mixed data) {
	string todo;
	... /* identifying events and thus setup todo string */
	Compile(todo);
        }
        ...
}

The Compile() will nicely pace the execution so you don't have to worry
about bogging the machine by large loop or deep recursion. It won't all of
a sudden execute a series of say() (unrealistic).

* convenience
Now it's not necessary to edit/save/update a monster to change its behavior.

* extensibility
new LSC operators could be defined within monster, all you need is to
write a function like:

int _<op_name>(lsc_ob) {
        mixed m,n;
        m = (mixed) lsc_ob->Pop();
	...
	if (ERROR) {
		lsc_ob->error(err_str);
		return -1;
	}
        ...
        lsc_ob->Push(...);
}

It should return 0 if successful or -1 otherwise.
Then your monster will have a new ability(operator).
Note: It's a plausible way to setup 'macro'.Refer to _list_proc() in code.
NOTE on 3.0: you should teach lsc to have an idea what this operator does,
otherwise the lsc object matching operator would have difficulty.

* background jobs capabilities
LSC object can also manage "pseudo-multi-tasking". It can "spawn" (just like
fork() in unix(tm)) a new child process. The specification will be covered in
later chapters. So a monster can on one hand waiting for a player to log on
and on the other hand performing some frequent routines.

* mapping capabilities
LSC has mapping now! It could be even more powerful than it's
counterpart in LPC because it can store functions i.e. {block}

* protect npc's from "seeing" errors
Very often, as a npc gets complicated, there will be a bug or two. When it
happens the heartbeat of the npc is halted (not sure if it's so in lp 3.0). It
is not nice. But by using this language the error is checked and the error
is notified in some way (depending on application).

* LISP-like features (3.0)
Extending the idea of functions inside variables, the concepts of LSC objects
is introduced so that lsc code can change and extend its code during execution.

--------------------------------------------------------------------------
More Concepts
=============

Each monster who would be "intelligent"(not quite yet) has to inherit a
lsc.c object. Each LSC object has the following simple structure:
	1. stack (Last-In, First-Out (LIFO) data structure)
	2. a hash table for storing "variables"
	NOTE: it's changed to mapping in lsc 2.0
	3. a compiler

If you have learned/heard the stack-oriented programming of Postscript
language (and FORTH, which I learned of it later), you'd probably know what
I'll be talking about.

You can think of each element inside the stack is a mixed variable, 
but it's not simply like that:
        each element of stack contains one of the following:
        n       :an integer, e.g. 1, 2, ....
        (str)   :string, the () is the identifier for strings
	obj	:object pointer

    these are just the basic data types which are exactly like C
    but there're a couple of "special strings" which serve different
    purposes. These are the ones which make this language much
    powerful:
        /var    :it's just a string with a '/' in front.
		:(could be thought of as a variable identifier)
        {block} :block is a segment of code of this language
		 NOTE: LSC 3.0 takes a new view that
		       { block } is a series of LSC objects.
        [array] :array.

There are also a set of commands (or stack operators, if you like) to do
something onto the stack (usually on the top of the stack)

To give you an idea how it works. Let's try an example,
call_other(lsc_ob, "Compile", "1 2 add say");

the Compile() inside lsc object will do 4 things:
	1. push integer 1 onto the stack
		stack: 1
		       ^top & also bottom element

	2. push integer 2 onto the stack
		stack: 1 2
                bottom-^ ^-top
		
	3. grap the first two elements of the stack and push the
	   sum onto the stack
		stack: 3

	4. grap the first element of the stack and say it.
		stack: [NULL]
		You see on the moniter:
			XXX says: 3

now let's try something interesting. I want to use variables:
call_other(lsc_ob, "Compile", "/one 1 def /two 2 def ... /* to be continued */

I think you can guess what it means and what'll be the result.
/one is a special string, it can be recognized by the def operation
so /one 1 def will initiate a search for "one" in the hash
table (mapping) if there exist such variable, substitute its value with 1,
otherwise setup a new variable named "one" and give it the value 1.
The history of the stack will look like:
	stack: "/one"
	stack: "/one" 1
	stack: [NULL]	/* after the def operation */
	stack: "/two"
	stack: "/two" 2
	stack: [NULL]	/* after another def operation */
After this operation, the hash_table (or mapping in 2.0) will look like:
	...
	"one", 1,
	...
	"two", 2,
	...
NOTE: the "/" is removed

Let's continue,

call_other(... "... /two 2 def one two add say");
for LSC, any data without special identifier e.g {, (, [, /, or integer
are assumed to be either operation or variable, so the "one" initiate a
search inside the hash_table (highest priority) and then LSC library function
(lowest priority, just like the "say" and "add" above)
Ha! the Compiler spots "one" in its database, so now what? Push the content
onto the stack, of course!
	stack: 1
then
	stack: 1 2

The rest is apparent.

So you've learned the basic behavior of LSC when treating simple data.
However, when dealing with { block }, the story is quite different.
Say I start up a monster anew (ie. forget about the definition above)
and issue the followings:
call_other(lsc_ob,"Compile","/one 1 def /two 2 def");
call_other(lsc_ob,"Compile","/three { one two add } def");

The behavior thus far will still be manageable. You might have guess
the result is:
	stack: [NULL]
	hash_table: /* the order is not important */
		...
		"one", 1,
		...
		"two", 2,
		...
		"three", "{ one two add }",
		...
That's right.
Ok... how about trying this after the declaration:
call_other(...,"Compile", "three");
	The stack will NOT look like:
		/* wrong */ stack: "{ one two add }"
		/* wrong */ stack: "{ 1 2 add }"
	But,
		/* correct */ stack: 3
Why? LSC has different behavior when dealing with { blocks } variables.
If it encounters a {block} inside a variable symbol, instead of pushing the whole
string onto the stack, it performs the action inside the block as if it were passed
to Compile().

So much about the nasty concepts. Here is a factorial variables, see if you
can figure out how it works (dup, le, pop, sub, mul & ifelse will be covered
in later chapters) :

/fact { dup
	dup 1 le
	{ pop pop 1 }
	{ 1 sub fact mul }
	ifelse
      } def


==========================================================================
			Standard LSC library operators
==========================================================================
Notation: Hereafter, "1st" element always means the top element of the
	  stack, i.e. the element most recently pushed onto the stack.
	  "2nd, 3rd,..." thus are impled.
ALL arguments an operator takes for execution will NOT be still in the
stack after the operation. (There are a few (very few) exceptions)

THE LIBRARY IS BY NO MEANS COMPLETE.
==========================================================================

BASICS:
-------

pop
pop will discard away 1st element. It'll invoke an error
"stack underflows" if the stack has nothing on it.

count
pushes the size of the stack.
so,
1 2 3
 count
=> 1 2 3 3
 count
=> 1 2 3 3 4

def
1st: any
2nd: variable ptr, i.e. string starts with '/'
def will use the 2nd to look for whether it is already defined.
If yes, replace the content of the variable by the 1st, otherwise
setup a variable and put the content with 1st.
Note: if 2nd(after removing the '/') is identical to one of
the operator's name, the operator will be replaced. That could be
a potential danger, but could be a very useful feature.
NOTE: to define an element of an array, use put operator

edef
same as def, but if the process running it is a child of some other
process, it'll look into its parent's hash_table(or mapping in 2.0).
No error is generated
if the process is itself a ROOT process.

undef
1st: variable ptr
undefine a variable

unedef
1st: variable ptr
undefine an external variable

exch
1st,2nd: any
exchange the position of the 1st and 2nd in the stack
(Note: it's one of the few ops which does not discard away the arguments)

dup
1st: any
it pushes a copy of 1st onto the stack.
(Another op which does not discard its arg)

ARITHMATIC:
-----------

add
1st,2nd: integer or (string) or [array] or { block }
add will push the sum of the 1st & 2nd onto the stack.
If 1st/2nd is a (string), add will concatenate and push the result as
(2nd_string+1st_string). 
When dealing with array, the 2 are joined together.
When dealing with {block}, they'll concatenate as
{ 2nd_block 1st_block }
NOTE: for convenience's sake, it's permissible to have
[2 3] 1 add
the result is
[2 3 1]

sub
1st,2nd: int or [array]
It pushes 2nd - 1st, or 2nd ^ 1st' (Boulean)

mul
1st,2nd: int or [array]
It pushes 1st*2nd or intersection [array] of 1st and 2nd if one of them is
an array.

div
1st,2nd: int
It pushes 2nd/1st.

neg
1st: int
It pushes -1st.

mod
1st, 2nd: int
It pushes 2nd % 1st.

CONDITIONALS:
-------------

logon
1st: (playername)
it pushes back the player name if 1st happens to be interactive, but
not necessarily inside the same environment.

present
1st: (itemname)
it pushes (inv) or (env) if it's present, either inside inventory or
environment.

and
1st, 2nd: any
pushes a 1 if 1st and 2nd is non-zero.

or
1st, 2nd: any
pushes a 1 if 1st or 2nd is non-zero.

not
1st: any
pushes !1st

eq
1st, 2nd: any
pushes 1st==2nd.

ne
1st, 2nd: any
pushes 1st!=2nd.

gt
1st, 2nd: int
pushes 2nd > 1st.

lt
1st, 2nd: int
pushes 2nd < 1st.

ge
1st, 2nd: int
pushes 2nd >= 1st.

le
1st, 2nd: int
pushes 2nd <= 1st.

if
1st: {block}
2nd: any
do the commands in 1st {block}, if 2nd is non-zero.

ifelse:
1st: {else_block}
2nd: {if_block}
3rd: any
do 2nd if 3rd is non-zero, 1st otherwise.

isdef
1st: /var
Push 1 if var is defined in hash_table(mapping in 2.0), 0 otherwise.

isedef
1st: /var
Push 1 if var is defined in parent's hash_table(or mapping in 2.0), 0 otherwise.
If the process is ROOT or ORPHANED, 0 is always pushed.

while
1st: {block}
2nd: {if-block}
the commands inside if-block is executed, if the 1st after the execution is
non-zero, block will be executed. The cycle goes on...
e.g. {1} {(hello) say} while will do infinite "hello"
Note: it's an error if 2nd is an empty block { }

continue
skip the rest of the code in the existing loop and go on with the next loop
(similar to C)
NOTE: be careful with the stack condition

exit
get out of the existing loop.
(similar to break; in C)
NOTE: it could be the major cause of flooding the stack


GAME:
-----
say
1st: any
It makes the monster say the 1st to the environment. It'll wait for
about 2 seconds before continuing.

randsay
1st: [array] of (string)/int/{block}
randomly says one of the elements in the array.

pause
pause the process until it's invoked by another Compile() call, with or
without an argument.

sleep
1st: int
sleeps for approximately 1st*2 seconds before continuing.

cmd
1st: (string)
make the npc do the string as if typed through a terminal.

randcmd
1st: [array] of (string)
randomly takes one of the strings as a command

present
1st: (obj name)
pushes an array of all objects matched (from env and inv) to
the stack. However, if no object is found, a 0 instead of [] is pushed.
NOTE: it's done by parse_command so adjective is permissible.

logon
1st: (playername)
pushes the player object if the player is on, or 0 otherwise

players_here
pushes an array of player objects in the env. If there's none, a zero
instead of [] is pushed.

livings_here
pushes an array of living objects (other than self) in the env. It behaves
the same as players_here

anything_here
similar to above... self-explanatory

health
1st: **object or (item name)
estimate the health of the object in percentile basis (0-100)
Note that it's just an ESTIMATE and is bound to have fluctuations.

inv
1st: **object or (object name)
pushes an array of visible objects in the object

MISCELLANY:
-----------
random
1st: int
Pushes random(1st);

load
1st: /varname
It pushes the content of varname as is, without executing (even if
it's a {block})

exec
1st: {block}
execute the block
So if I have
/i {(hello) say} def
then
/i load exec
is equivalent to just
i

randexec
1st: [array] of {block}
randomly exec one of the block in the array

length
1st: (string) or [array]
pushes the length/size of 1st

MULTI-TASKING:
--------------
spawn
1st: /varname (must contains {block} data ) or {block}
spawn will clone a new LSC object(its child), and make it do the command as
defined in {block} or in /varname.
- Each LSC has limited number of children. (should be dependent on some stats
  e.g. intelligent score)
- Each child is assigned a process id.
- The stack and hash table(mapping) of a new born child is EMPTY, but it can
  still access its parent's hash table(mapping) just like a global variable.
- When a child is done with all the code, it'll automatically self-destroy.
- Child process can't spawn.
- Child process can access the background job list of its parent by using
  the same command as parent's (for example, pause_proc, kill_proc)
- Children and parent is 'almost' transparent to each other. They don't
  interfere.
- The only way processes communicate is by using the common hash_table
  (mapping) i.e. the parent's.

list_proc
It's an example of 'macro' function in LSC, it'll provoke a series
of say to notify its multi-tasking condition.

pause_proc
1st: (process id) or process_object (if you know how) or int (index)
it pauses a process, except <ROOT>.
Note that it can be called inside a child, ie. a child can pause its
siblings

kill_proc
same as pause_proc, but it kills instead of pause.

ARRAY & MAPPING
---------------
array
1st: int
allocates an array with size 1st and pushes it onto the stack.

extract
1st,2nd: int
3rd: (string) or [array]
same as C 's 3rd[2nd..1st];
1st & 2nd could be negative, which means to count from the last.
so (abc) -1 -1 extract would push (c) into the stack
Be careful with the size, it assumes you know the size is not so
small that even -2 turns out to be meaningless (when size is just 1)

mapping
1st: int
allocates a mapping with "size" 1st and pushes it onto the stack

get
1st: int
2nd: (string) or [array] or mapping
gets the 1st-th element of the string/array and pushes it.
Note: 1st could be negative (counting from the last)

put
1st: int
2nd: [char]   or any
3rd: [string] or array or mapping
change the 1st-th element to 2nd.

aload
1st: array
it breaks the array apart, pushes the contain one by one, with the first at the bottom,
and lastly pushes the original array back onto the stack again

astore
1st: array
2nd, 3rd, .... (1+size of the array)th: any
it tries to pop the stack and fill the array.
so
1 2 3 [ 0 0 0 ] astore
=> [ 1 2 3 ]
NOTE: so you can see aload and astore are counterpart to each other.

member_array
1st: array
2nd: anything
Pushes the index of the first occurence of 2nd in array 1st. If the
item is not found, then -1 is pushed.