Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/doc/LPC/.synonym b/doc/LPC/.synonym
new file mode 100644
index 0000000..300916b
--- /dev/null
+++ b/doc/LPC/.synonym
@@ -0,0 +1,26 @@
+array arrays
+vererbung inheritance
+erben inheritance
+integer integers
+mapping mappings
+modulo operators
+operatoren operators
+increment operators
+decrement operators
+string strings
+case switch
+private modifiers
+public modifiers
+static modifiers
+nomask modifiers
+protected modifiers
+nosave modifiers
+virtual modifiers
+struct structs
+strong_types pragma
+strict_types pragma
+save_types pragma
+pedantic pragma
+range_check pragma
+rttc pragma
+rtt_checks pragma
diff --git a/doc/LPC/alists b/doc/LPC/alists
new file mode 100644
index 0000000..5550de7
--- /dev/null
+++ b/doc/LPC/alists
@@ -0,0 +1,53 @@
+CONCEPT
+ alists
+
+LAST UPDATE
+ 2 Mar 92 21:10:21 GMT
+
+AUTHOR
+ From: amylaar@mcshh.hanse.de (Joern Rennecke)
+ Subject: general documentation on alists
+
+DESCRIPTION
+ Alists provide a fast and convenient way to access data
+ associatively.
+
+ Alists are implemented as arrays of arrays, the first being
+ the array holding the keys, the others arrays holding
+ associated data. An empty alist is an array of empty arrays.
+
+ Note that the the dimensions of the arrays are used the other
+ way than in lisp to allow for faster searching.
+
+ Keys have to be of type integer, string or object. Types can
+ be mixed.
+
+ The search functions return an undefined value when another
+ list is given in place of a presorted key list.
+
+ A list with non-numeric keys retrieved by restore_object() has
+ to be readjusted by using order_alist(), especially after
+ reboot.
+
+ Deleting an entry can safely be done with exclude_array as
+ long as all associated data lists are treated like the key
+ array; index finding for such purposes can be done with assoc.
+
+ Typical applications: holding administrary information about
+ wizards, list of visitors in a pub, list of customers having
+ some sort of credit, information remembered about items etc.
+
+NOTE
+ The main use of alists, storing data associatively, is now
+ better performed by mappings. Alists are needed for more
+ extreme situations only.
+
+ Alists are available only if the driver is compiled with
+ alist support. In that case, __ALISTS__ is defined.
+
+HISTORY
+ LDMud 3.3 made alists an optional efun.
+
+SEE ALSO
+ mappings(LPC), order_alist(E), insert_alist(E), assoc(E),
+ transpose_array(E)
diff --git a/doc/LPC/arrays b/doc/LPC/arrays
new file mode 100644
index 0000000..883dddb
--- /dev/null
+++ b/doc/LPC/arrays
@@ -0,0 +1,116 @@
+CONCEPT
+ arrays
+
+DESCRIPTION
+ There is support for arrays. The arrays can't be declared, but
+ should be allocated dynamically with the function 'allocate()'
+ (see efun/allocate).
+
+ Arrays are stored by reference, so all assignments of whole
+ arrays will just copy the address. The array will be
+ deallocated when no variable points to it any longer.
+
+ When a variable points to an array, items can be accessed with
+ indexing: 'arr[3]' as an example. The name of the array being
+ indexed can be any expression, even a function call:
+ 'func()[2]'. It can also be another array, if this array has
+ pointers to arrays:
+
+ arr = allocate(2);
+ arr[0] = allocate(3);
+ arr[1] = allocate(3);
+
+ Now 'arr[1][2]' is a valid value.
+
+ The 'sizeof()' function (in true C a compiler-directive, not a
+ function) will give the number of elements in an array (see
+ efun/sizeof).
+
+NOTE
+ Nowadays it is most of the time preferable to use an array
+ constructor, a list surrounded by '({' and '})',
+ e.g. ({ 1, "xx", 2 }) will construct a new array with size 3,
+ initialized with 1, "xx" and 2 respectively.
+
+
+OPERATIONS
+
+ INDEXING
+ There are several very useful operations defined on arrays.
+ The most used is the indexing:
+ a=({ 0,1,2,3 });
+ return a[2]; // this will return 2
+ You also can count from the end of the array. Use <1 to specify
+ the last element in the array:
+ a=({ 0,1,2,3 });
+ return a[<3]; // this will return 1
+ With indexing you can also create sub-arrays:
+ a=({ 0,1,2,3,4,5,6,7 });
+ return a[3..5]; // this will return ({ 3,4,5 })
+ return a[2..<2]; // this will return ({ 2,3,4,5,6 })
+ return a[<5..<3]; // this will return ({ 3,4,5 })
+ return a[<6..5]; // this will return ({ 2,3,4,5 })
+ return a[3..3]; // this will return ({ 3 })
+ return a[3..2]; // this will return ({ })
+ return a[3..0]; // this will return ({ })
+ return a[5..100]; // this will return ({ 5,6,7 })
+ [x..] is interpreted as [x..<1]
+
+ ADDING
+ You can add two arrays. The result is one array with the elements
+ of both the former arrays:
+ a=({ 0,1 });
+ b=({ "a","b" });
+ return a+b; // this will return ({ 0,1,"a","b" })
+ return b+a; // this will return ({ "a","b",0,1 })
+
+ SUBTRACTING
+ You can erase all elements of one array that occur in another
+ array:
+ a=({ 0,1,2,3,4,5,6,7 });
+ b=({ 7,2,5,8,1,9 });
+ return a-b; // this will return ({ 0,3,4,6 })
+ return b-a; // this will return ({ 8,9 })
+
+ INTERJUNCTION
+ Use the &-operator to create the interjunction of two arrays:
+ a=({ 5,2,8,1,9,4 })
+ b=({ 1,6,7,3,4,5 })
+ return a&b; // this will return ({ 1,4,5 })
+
+ ASSIGNING
+ Assigning can also be done to sub-arrays and is thus very powerful:
+ a=({ 0,1,2,3,4,5,6,7 });
+ a[<4..<3]=({ 8,9 });
+ return a; // this will return ({ 0,1,2,3,8,9,6,7 })
+
+ a=({ 0,1,2,3,4,5,6,7 });
+ a[2..5]=({ });
+ return a; // this will return ({ 0,1,6,7 })
+
+ a=({ 0,1,2,3,4 });
+ a[3..2]=({ 8,9 });
+ return a; // this will return ({ 0,1,2,8,9,3,4 })
+
+ a=({ 0,1,2,3,4 });
+ a[3..0]=({ 8,9 });
+ return a; // this will return ({ 0,1,2,8,9,1,2,3,4 })
+ // this is quite funny but true ;-)
+ // WARNING: If done unintentionally and
+ // within a loop, you can quickly cause
+ // the game to run out of memory!
+
+ GENERAL
+ Of course for any of the operators explained above you can use
+ the combined form of assigning and operating; that means the
+ operators +=, -= and &= work.
+
+
+TIPS
+ If you want to make sure that no element is more than once in an
+ array you can use the following:
+ a = m_indices(mkmapping(a));
+ This creates a mapping out of the array and recreates the array
+ at once. The elements in the array can be shuffled by this
+ procedure.
+
diff --git a/doc/LPC/block b/doc/LPC/block
new file mode 100644
index 0000000..29eb382
--- /dev/null
+++ b/doc/LPC/block
@@ -0,0 +1,48 @@
+NAME
+ block
+
+DESCRIPTION
+ A block is a special statment, that begins with '{', contains
+ a list of statements, and ends with '}'.
+
+ The block may define local variables. If for a variable no
+ initialisation is given, the variable is initialised to 0 every
+ time the block is entered. Otherwise, the initialisation
+ expression is evaluated and its result assigned to the variable
+ everytime the block is entered.
+
+ Example definitions are:
+
+ int i;
+ int j = 3;
+ int k = 3 * j, l;
+
+ Here, i and l are both initialised to 0; j is initialised
+ to 3, and k is initialised to 9 (3 * j).
+
+ Local variables defined in a block are visible only until the
+ end of the block. Definitions in an inner block hide definitions in
+ outer blocks.
+
+HISTORY
+ Up to 3.2.7 local variables were visible (from their point of
+ definition) in the whole function. That is, code like
+
+ do {
+ int res;
+
+ res = ...
+ } while (res == 5);
+ write(res);
+
+ was perfectly legal. It is no longer, as 'res' ceases to exist
+ with the closing '}' of the while().
+
+ Up to 3.5.0 you could get this old behaviour back with the
+ #pragma no_local_scopes and switch it off again with
+ #pragma local_scopes.
+
+ Since 3.5.0 it is not possible to disable the local scope behaviour.
+
+ Up to 3.2.8, local variables could not be initialised in their
+ definition.
diff --git a/doc/LPC/closure_guide b/doc/LPC/closure_guide
new file mode 100644
index 0000000..438ba46
--- /dev/null
+++ b/doc/LPC/closure_guide
@@ -0,0 +1,730 @@
+Closure Guide for LPC
+
+Table of Contents
+
+ 1 Indroduction, Overview and Efun-Closures
+
+ 2 Lfun-, Inline and Lambda-Closures
+ 2.1 Lfun-Closures
+ 2.2 Inline-Closures
+ 2.3 Lambda-Closures
+ 2.3.1 Advantages of Lambda-Closures
+ 2.3.2 Free Variables in Lambda-Closure Constructs
+ 2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
+ 2.4 Closures with Strange Names
+ 2.5 Operator-Closures
+ 2.6 Variable-Closures
+
+ 3 Examples
+ 3.1 Lfun-Closure
+ 3.2 Lambda-Closure
+
+1 Introduction, Overview and Efun-Closures
+
+ A closure is a pointer to a function. That means that it is data like an
+ int or a string are. It may be assigned to a variable or given to anoth-
+ er function as argument.
+
+ To create a closure that points to an efun like write() you can write
+ the name of the efun prepended with "hash-tick": #'. #'write is a clo-
+ sure that points to the efun write().
+
+ I very often put parentheses around such a closure-notation because
+ otherwise my editor gets confused by the hashmark: (#'write). This is
+ especially of interest within lambda-closures (see below).
+
+ A closure can be evaluated (which means that the function it points to
+ is called) using the efuns funcall() or apply(), which also allow to
+ give arguments to the function. Example:
+
+ funcall(#'write,"hello");
+
+ This will result in the same as write("hello"); alone. The string
+ "hello" is given as first (and only) argument to the function the clo-
+ sure #'write points to.
+
+ The return value of the function the closure points to is returned by
+ the efun funcall() or apply(). (Since write() always returns 0 the re-
+ turn value of the example above will be 0.)
+
+ What are closures good for? With closures you can make much more univer-
+ sally usable functions. A good example is the function filter().
+ It gets an array and a closure as arguments. Then it calls the function
+ the closure points to for each element of the array:
+
+ filter(({ "bla","foo","bar" }),#'write);
+
+ This will call write("bla"), write("foo") and write("bar") in any order;
+ the order is undefined.
+ (In the current implementation the given closure is evaluated for all
+ elements from the first to the last, so the output will be "blafoobar".)
+
+ Furthermore the efun filter() examines the return value of each
+ call of the function the closure points to (the return value of the
+ write()s). If the value is true (not 0) then this element is put into
+ another array which filter() builds up. If the return value is
+ false (== 0) then this element is _not_ put into this array. When all
+ calls are done the slowly built up array is returned. Thus,
+ filter() filters from the given array all elements that the given
+ closure evaluates "true" for and returns an array of those. (The array
+ given to filter() itself is _not_ changed!)
+
+ A more sensical example for filterwould be this:
+
+ x = filter(users(),#'query_is_wizard);
+
+ users() is an efun that gets no arguments and returns an array of all
+ logged in players (wizards and players). query_is_wizard() is a
+ simul_efun that gets an object as first (and only) argument and returns
+ true (1) if this object is a wizard and 0 otherwise.
+
+ So, for each element of the array returned by users() the function
+ query_is_wizard() is called and only those for which 1 was returned are
+ collected into the result and then put into the variable x.
+
+ We now have all logged in wizards stored as array in the variable x.
+
+ Another example: We want to filter out all numbers that are greater than
+ 42 from the array a of integers:
+
+ x = filter(({ 10,50,30,70 }),#'>,42);
+
+ (x will now be ({ 50,70 }).)
+
+ Here two things are new: first: we create a closure that points to an
+ operator; second: we use the possibility to give extra arguments to
+ filter().
+
+ Like all efuns the usual operators can be pointed to with a closure by
+ prepending #' to them. funcall(#'>,4,5) is exactly the same as (4>5).
+
+ The extra arguments given as third to last argument (as many as you
+ like) to filter() are given as second to last argument to the
+ function pointed to by the closure each time it is called.
+
+ Thus we now call (({ 10,50,30,70 })[0]>42), (({ 10,50,30,70 })[1]>42) ...
+ (which is (10>42), (50>42) ...) and return an array of all elements this
+ returns true for and store it into x.
+
+ If you want to create a closure to an efun of which you have the name
+ stored in a string you can create such an efun-closure with the efun
+ symbol_function():
+
+ symbol_function("write") // this will return #'write
+
+ funcall(symbol_function("write"),"foobar"); // == write("foobar");
+
+ This function does not very often occur in normal code but it is very
+ useful for tool-programming (eg the robe uses symbol_function() to allow
+ you call any efun you give).
+
+2 Lfun- and Lambda-Closures
+
+ Very often the possibilities closures to efuns offer are not sufficient
+ for the purpose one has. In nearly all cases three possibilities exist in
+ such cases: use an lfun- or inline-closure, or a lambda-closure.
+
+2.1 Lfun-Closures
+
+ The first possibility is rather easy: like with the efun-closures you
+ can create a pointer to a function in the same object you are by using
+ the #' to prepend it to a function name of a function declared above.
+ Example:
+
+ status foo(int x) {
+ return ((x*2) > 42);
+ }
+
+ int *bar() {
+ return filter(({ 10,50,30,70 }),#'foo);
+ }
+
+ Thus, #'foo is used like there was an efun of this name and doing the
+ job that is done in foo().
+
+2.2 Inline Closure
+
+ Inline closures are a variant of lfun closures, the difference being
+ that the function text is written right where the closure is used,
+ enclosed in a pair of '(:' and ':)'. The compiler will then take care
+ of creating a proper lfun and lfun-closure. The arguments passed to
+ such an inline closure are accessible by position: $1 would be the
+ first argument, $2 the second, and so on. With this, the
+ above example would read:
+
+ int * bar() {
+ return filter(({ 10,50,30,70 }), (: ($1 * 2) > 42 :));
+ }
+
+ or alternatively:
+
+ int * bar() {
+ return filter(({ 10,50,30,70 }), (: return ($1 * 2) > 42; :));
+ }
+
+ The difference between the two versions is that in the first form the text
+ of the inline closure must be an expression only, whereas in the second
+ form any legal statement is allowed. The compiler distinguishes the two
+ forms by the last character before the ':)': if it's a ';' or '}', the
+ compiler treats the closure as statement(s), otherwise as expression.
+
+ Inline closures may also nested, so that the following (not very useful)
+ example is legal, too:
+
+ return filter( ({ 10, 50, 30, 70 })
+ , (: string *s;
+ s = map(users(), (: $1->query_name() :));
+ return s[random(sizeof(s))] + ($1 * 2);
+ :));
+
+ The notation of inline closures is modelled after the MudOS functionals,
+ but there are a few important differences in behaviour.
+
+
+2.3 Lambda-Closures
+
+ Lambda-Closures take the idea of 'define it where you use it' one step
+ further. On first glance they may look like inline closures with an uglier
+ notation, but they offer a few increased possibilities. But first things
+ first.
+
+ The efun lambda() creates a function temporarily and returns a closure
+ pointing to this function. lambda() therefor gets two arrays as
+ arguments, the first is a list of all arguments the function shall expect
+ and the second array is the code of the function (in a more or less
+ complicated form; at least not in C- or LPC-syntax). The closure #'foo
+ from the example above could be notated as lambda-closure:
+
+ lambda(({ 'x }),({ (#'>),
+ ({ (#'*),'x,2 }),
+ 42
+ }))
+
+ Now, the first argument is ({ 'x }), an array of all arguments the
+ function shall expect: 1 argument (called 'x) is expected. Notice the
+ strange notation for this argument with one single leading tick. Like
+ The hash-tick to denote closures the leading tick is used to denote
+ things called "symbols". They do not differ much from strings and if
+ you do not want to have a deeper look into closures you can leave it
+ this way.
+
+ The second argument is an array. The first element of such an array
+ must be an efun- or an lfun-closure, the further elements are the
+ arguments for the function this closure points to. If such an argu-
+ ment is an array, it is treated alike; the first element must be a
+ closure and the remaining elements are arguments (which of course
+ also might be arrays ...).
+
+ This leads to a problem: sometimes you want to give an array as an
+ argument to a function. But arrays in an array given to lambda() are
+ interpreted as code-arrays. To allow you to give an array as an argu-
+ ment within an array given to lambda(), you can use the function
+ quote() to make your array to a quoted array (a quoted array is for
+ an array what a symbol is for a string):
+
+ lambda(0,({ (#'sizeof),
+ quote(({ 10,50,30,70 }))
+ }))
+
+ For array constants, you can also use a single quote to the same
+ effect:
+
+ lambda(0,({ (#'sizeof),
+ '({ 10,50,30,70 })
+ }))
+
+ This lambda-closure points to a function that will return 4 (it will
+ call sizeof() for the array ({ 10,50,30,70 })). Another thing: if
+ we want to create a function that expects no arguments, we can give
+ an empty array as first argument to lambda() but we can give 0 as
+ well to attain this. This is just an abbreviation.
+
+ Lambda-closure constructs can become quite large and hard to read. The
+ larger they become the harder the code is to read and you should avoid
+ extreme cases. Very often the possibility to use an lfun or an inline
+ instead of a large lambda shortens the code dramatically. Example:
+
+ status foo(object o) {
+ return environment(o)->query_level()>WL_APPRENTICE;
+ }
+
+ x=filter(a,#'foo);
+
+ does the same as
+
+ x=filter(a,lambda(({ 'o }),
+ ({ (#'>),
+ ({ (#'call_other),
+ ({ (#'environment),'o }),
+ "query_level"
+ }),
+ WL_APPRENTICE
+ })));
+
+ (Note that the syntax with the arrow "->" for call_other()s cannot be
+ used, #'-> does not exist. You have to use #'call_other for this and
+ give the name of the lfun to be called as a string.)
+
+ This example also demonstrates the two disadvantages of lambda closures.
+ First, they are very difficult to read, even for a simple example like
+ this. Second, the lambda closure is re-created everytime the
+ filter() is executed, even though the created code is always the
+ same.
+
+ 'Why use lambdas at all then?' you may ask now. Well, read on.
+
+
+2.3.1 Advantages of Lambda Closures
+
+ The advantages of lambdas stem from the fact that they are created
+ at runtime from normal arrays.
+
+ This means that the behaviour of a lambda can be made dependant on data
+ available only at runtime. For example:
+
+ closure c;
+ c = lambda(0, ({#'-, ({ #'time }), time() }) );
+
+ Whenever you now call this closure ('funcall(c)') it will return the
+ elapsed time since the closure was created.
+
+ The second advantage of lambdas is that the arrays from which they
+ are compiled can be constructed at runtime. Imagine a customizable prompt
+ which can be configured to display the time, the environment, or both:
+
+ mixed code;
+
+ code = ({ "> " });
+ if (user_wants_time)
+ code = ({ #'+, ({ #'ctime }), code });
+ if (user_wants_environment)
+ code = ({ #'+, ({#'to_string, ({#'environment, ({#'this_player }) }) })
+ , code });
+ set_prompt(lambda(0, code));
+
+
+2.3.2 Free Variables in Lambda-Closure Constructs
+
+ You can use local variables in lambda constructs without declaring
+ them, just use them. The only limitation is that you at first have
+ to assign something to them. Give them as symbols like you do with
+ the arguments. This feature does not make much sense without the use
+ of complexer flow controlling features described below.
+
+ The closure #'= is used to assign a value to something (like the
+ LPC-operator = is).
+
+2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
+
+ There are some special closures that are supposed to be used only
+ within a lambda construct. With them you can create nearly all code
+ you can with regular LPC-code like loops and conditions.
+
+ #'? acts like the "if" statement in LPC. The first argument is the
+ condition, the second is the code to be executed if the condition
+ returns true. The following arguments can also be such couples of
+ code-arrays that state a condition and a possible result. If at
+ the end there is a single argument, it is used as the else-case
+ if no condition returned true.
+
+ lambda(({ 'x }),({ (#'?), // if
+ ({ (#'>),'x,5 }), // (x > 5)
+ ({ (#'*),'x,2 }), // result is x * 2;
+ ({ (#'<),'x,-5 }), // else if (x < -5)
+ ({ (#'/),'x,2 }), // result is x/2;
+ 'x // else result is x;
+ }))
+
+ #'?! is like the #'? but it negates all conditions after evaluation
+ and thus is like an ifnot in LPC (if there were one).
+
+ #', (which looks a bit strange) is the equivalent of the comma-operator
+ in LPC and says: evaluate all arguments and return the value of
+ the last. It is used to do several things inside a lambda-closure.
+
+ lambda(({ 'x }),({ (#',), // two commas necessary!
+ // one for the closure and one as
+ // delimiter in the array
+ ({ (#'write),"hello world!" }),
+ ({ (#'say),"Foobar." })
+ }))
+
+ #'while acts like the LPC statement "while" and repeats executing one
+ code-array while another returns true.
+ #'while expects two or more arguments: the condition as first
+ argument, then the result the whole expression shall have after
+ the condition turns false (this is in many cases of no interest)
+ and as third to last argument the body of the loop.
+
+ lambda(0,({ (#',), // several things to do ...
+ ({ (#'=),'i,0 }), // i is a local variable of this
+ // lambda-closure and is
+ // initialized with 0 now.
+ ({ (#'while),
+ ({ (#'<),'i,10 }), // condition: i < 10
+ 42, // result is not interesting,
+ // but we must give one
+ ({ (#'write),'i }), // give out i
+ ({ (#'+=),'i,1 }) // increase i
+ })
+ }))
+
+ The function this closure points to will give out the
+ numbers from 0 to 9 and then return 42.
+
+ #'do is like the do-while statement in LPC and is very much like the
+ #'while. The difference is that #'while tests the condition al-
+ ready before the body is evaluated for the first time, this means
+ that the body might not be evaluated even once. #'do evaluates
+ the body first and then the condition, thus the body is evaluated
+ at least one time.
+ Furthermore, the arguments for #'do are changed in order. #'do
+ expects as first to last but two the body of the loop, then the
+ condition (as last-but-one'th argument) and the result value as
+ last argument. So #'do must have at least two arguments: the
+ condition and the result.
+
+ lambda(0,({ (#',), // several things to do ...
+ ({ (#'=),'i,0 }), // i is a local variable of this
+ // lambda-closure and is initialized
+ // with 0 now.
+ ({ (#'do),
+ ({ (#'write),'i }), // give out i
+ ({ (#'+=),'i,1 }) // increase i
+ ({ (#'<),'i,10 }), // condition: i < 10
+ 42 // result is not interesting
+ })
+ }))
+
+ NOTE: There is no #'for in LPC, you should use #'while for this.
+
+ #'foreach is like the foreach() statement in LPC. It evaluates one or
+ more bodies repeatedly for every value in a giving string, array
+ or mapping. The result of the closure is 0.
+
+ #'foreach expects two or more arguments:
+ - a single variable symbol, or an array with several variable
+ symbols
+ - the value to iterate over
+ - zero or more bodes to evaluate in each iteration.
+
+ The single values retrieved from the given value are assigned
+ one after another to the variable(s), then the bodies are executed
+ for each assignment.
+
+ lambda(0, ({#'foreach, 'o, ({#'users})
+ , ({#'call_other, 'o, "die" })
+ }));
+
+ lambda(0, ({#'foreach, ({'k, 'v}), ({ ...mapping...})
+ , ({#'printf, "%O:%O\n", 'k, 'v })
+ }));
+
+ #'return gets one argument and acts like the "return" statement in LPC
+ in the function that is created by lambda(). It aborts the
+ execution of this function and returns the argument.
+
+ lambda(0,({ (#'while),// loop
+ 1, // condition is 1 ==> endles loop
+ 42, // return value (which will never be used)
+ ({ (#'write),"grin" })
+ ({ (#'?!), // ifnot
+ ({ (#'random),10 }), // (random(10))
+ ({ (#'return),100 }) // return 100;
+ })
+ }))
+
+ This function will enter an endles loop that will in each
+ turn give out "grin" and if random(10) returns 0 (which will
+ of course happen very soon) it will leave the function with
+ "return 100". The value 42 that is given as result of the
+ loop would be returned if the condition would evaluate to 0
+ which cannot be. (1 is never 0 ;-)
+
+ #'break is used like the "break" statement in LPC and aborts the exe-
+ cution of loops and switches.
+ It must not appear outside a loop or switch of the lambda
+ closure itself, it cannot abort the execution of the function
+ the closure points to!
+
+ lambda(0,({ (#'?),
+ ({ (#'random),2 }),
+ ({ (#'break) }), // this will cause the error
+ // "Unimplemented operator break
+ // for lambda()"
+ "random was false!"
+ }));
+
+ You can use ({ #'return,0 }) instead of ({ #'break }) in such
+ cases.
+
+ #'continue is used like the "continue" statement in LPC and jumps to
+ the end of the current loop and continues with the loop
+ condition.
+
+ #'default may be used within a #'switch-construct but be careful!
+ To call symbol_function("default") (which is done usually
+ by tools that allow closure-creation) might crash the
+ driver! So please do only use it within your LPC-files.
+ (NOTE: This driver bug is fixed somewhere below 3.2.1@131.)
+
+ #'.. may be used within a #'switch-construct but is not implemented
+ yet (3.2.1@131). But #'[..] works well instead of it.
+
+ #'switch is used to create closures which behave very much like the
+ switch-construct in LPC. To understand the following you
+ should already know the syntax and possibilities of the
+ latter one (which is mightier than the C-version of switch).
+
+ I will confront some LPC versions and the corresponding clo-
+ sure versions below.
+
+ LPC: Closure:
+ switch (x) { lambda(0,({ (#'switch), x,
+ case 5: ({ 5 }),
+ return "five"; ({ (#'return),"five" }),
+ (#',),
+ case 6..9: ({ 6, (#'[..]), 9 }),
+ return "six to nine"; ({ (#'return),
+ "six to nine" }),
+ (#',),
+ case 1: ({ 1 }),
+ write("one"); ({ (#'write),"one" }),
+ // fall through (#',),
+ case 2: ({ 2,
+ case 10: 10 }),
+ return "two or ten"; ({ (#'return),
+ "two or ten" }),
+ (#',),
+ case 3..4: ({ 3, (#'[..]), 4 }),
+ write("three to four"); ({ (#'write),
+ "three to four" }),
+ break; // leave switch (#'break),
+ default: ({ (#'default) }),
+ write("something else"); ({ (#'write),
+ "something else" }),
+ break; (#'break)
+ } }))
+
+ #'&& evaluates the arguments from the first on and stops if one evalu-
+ ates to 0 and returns 0. If none evaluates to 0 it returns the
+ result of the last argument.
+
+ #'|| evaluates the arguments from the first on and stops if one evalu-
+ ates to true (not 0) and returns it. If all evaluate to 0 it
+ returns 0.
+
+ #'catch executes the closure given as argument, but catches any
+ runtime error (see catch(E)). Optionally the symbols 'nolog,
+ 'publish and 'reserve may be given as additional arguments to
+ modify the behaviour of the catch.
+
+ #'sscanf acts similar to how a funcall would, but passes the third
+ and following arguments as lvalues, that is, values which can
+ be assigned to.
+
+ #'= and the #'<op>= variants are also special because the first
+ argument has to be an lvalue.
+
+2.4 Closures with Strange Names
+
+ #'negate is the unary minus that returns -x for the argument x.
+
+ map(({ 1,2,3 }),#'negate)
+
+ This returns ({ -1,-2,-3 }).
+
+ #'[ is used for the things that in LPC are done with
+ the []-operator (it indexes an array or a mapping).
+
+ lambda(0,({ #'[,quote(({ 10,50,30,70 })),2 })) ==> 30
+ lambda(0,({ #'[,([ "x":10;50, "y":30;70 ]),"x",1 })) ==> 50
+
+ #'[< is the same as #'[ but counts the elements from the
+ end (like indexing with [] and the "<").
+
+ #'[..] returns a subarray of the argument from the one
+ given index to the other given index, both counted from the
+ beginning of the array.
+
+ #'[..<]
+ #'[<..]
+ #'[<..<] same as above, but the indexes are counted from the end,
+
+ lambda(0,({ #'[..<],
+ quote(({ 0,1,2,3,4,5,6,7 })),2,3
+ }))
+
+ This will return ({ 2,3,4,5 }).
+ #'[..
+ #'[<.. same as above, but only the first index is given, the
+ subarray will go till the end of the original (like with
+ [x..]).
+
+ #'({ is used to create arrays (as with ({ }) in LPC). All arguments
+ become the elements of the array.
+
+ lambda(0,({ #'({,
+ ({ (#'random),10 }),
+ ({ (#'random),50 }),
+ ({ (#'random),30 }),
+ ({ (#'random),70 })
+ }))
+
+ This returns ({ random(10),random(50),random(30),random(70) }).
+
+ #'([ is used to create mappings out of single entries (with seve-
+ ral values) like the ([ ]) in LPC. Very unusual is the fact
+ that this closure gets arrays as argument that are not eval-
+ uated, although they are not quoted.
+
+ lambda(0,({ #'([,
+ ({ "x",1,2,3 }),
+ ({ "y",4,5,6 })
+ }));
+
+ This returns ([ "x": 1;2;3,
+ "y": 4;5;6 ]).
+
+ However, the elements of the arrays are evaluated as lambda
+ expressions, so if you want to create a mapping from values
+ evaluated at call time, write them as lambda closures:
+
+ lambda(0, ({ #'([, ({ 1, ({ #'ctime }) }) }) )
+
+ will return ([ 1: <result of ctime() at call time ]).
+
+ Arrays can be put into the mapping by quoting:
+
+ lambda(0, ({ #'([, ({ 1, '({ 2 }) }) }) )
+
+ will return ([ 1: ({ 2 }) ])
+
+
+ #'[,] is nearly the same as #'[. The only difference
+ shows up if you want to index a mapping with a width
+ greater than 1 (with more than just one value per
+ key) directly with funcall(). Example:
+ funcall(#'[,([ 0:1;2, 3:4;5 ]),0,1)
+ This will not work. Use #'[,] and it will
+ work. If you want to use it in a lambda closure you
+ do not have to use #'[,] and #'[ will
+ do fine. On the other hand, #'[,] cannot
+ work with arrays, so in nearly all cases use #'[
+ and just in the described special case, use
+ #'[,].
+ This is a strange thing and I deem it a bug, so it
+ might change in the future.
+
+2.5 Operator-Closures
+
+ Most of the closures that are used for things which are done by opera-
+ tors are in fact not operator-closures but efun-closures. But there are
+ a few which do not have the state of efun-closures but are called
+ "operator-closures". #'return is an example, a complete list of them is
+ given below.
+
+ These closures cannot be called directly using funcall() or apply() (or
+ other efuns like filter()), but must appear only in lambda-con-
+ structs.
+
+ funcall(#'return,4); // does not work! This will raise an
+ // Uncallable-closure error.
+ funcall(lambda(0, // this is a correct example
+ ({ (#'return),4 })
+ ));
+
+ All operator-closures:
+ #'&&
+ #'||
+ #',
+ #'?
+ #'?!
+ #'=
+ #'<op>=
+ #'++
+ #'--
+ #'break
+ #'catch
+ #'continue
+ #'default
+ #'do
+ #'foreach
+ #'return
+ #'sscanf
+ #'switch
+ #'while
+ #'({
+ #'([
+
+ #'.. is very likely to be an operator closure too, but since it is
+ not implemented yet, I cannot say for sure.
+
+2.6 Variable-Closures
+
+ All object-global variables might be "closured" by prepending a #' to
+ them to allow access and/or manipulation of them. So if your object has
+ a global variable x you can use #'x within a closure.
+
+ Normally you will treat those expressions like lfun-closures: put them
+ into an array to get the value:
+
+ object.c:
+ int x;
+ int foo() {
+ return lambda(0,({ (#'write),({ (#'x) }) }));
+ }
+
+ Anybody who now calls object->foo() will get a closure which will, when
+ evaluated, write the actual value of object's global variable x.
+
+ Variable closures do not accept arguments.
+
+3 Examples
+
+ In this section I will give and explain some examples coming out of
+ praxis. If the explanation seems to be in some cases too detailed this
+ can be explained by the trial to allow the reader to read the examples
+ section first ;-)
+
+3.1 Lfun-Closure
+
+ An item with a complex long-description like a watch that shall always
+ show the actual time will usually base upon the complex/item-class and
+ give an lfun-closure as argument to the set_long()-method.
+
+ watch.c:
+ inherit "complex/item";
+
+ string my_long() {
+ return ("The watch is small and has a strange otherworldly"
+ " aura about it.\n"
+ "The current time is: "+ctime()+".\n");
+ }
+
+ void create() {
+ set_short("a little watch");
+ set_id(({ "watch","little watch" }));
+ set_long(#'my_long); // the lfun-closure to the lfun my_long()
+ }
+
+3.2 Lambda-Closure
+
+ The example from 3.1 can also be written using a lambda-closure.
+
+ watch.c:
+ inherit "complex/item";
+
+ void create() {
+ set_short("a little watch");
+ set_id(({ "watch","little watch" }));
+ set_long(lambda(0,({ (#'+),
+ "The watch is small and has a strange"
+ " otherworldly aura about it.\n"
+ "The current time is: ",
+ ({ (#'+),
+ ({ (#'ctime) }),
+ ".\n"
+ })
+ })));
+ }
diff --git a/doc/LPC/closures b/doc/LPC/closures
new file mode 100644
index 0000000..ba5d804
--- /dev/null
+++ b/doc/LPC/closures
@@ -0,0 +1,331 @@
+CONCEPT
+ closures
+
+NOTE
+ This is the official man page concerning closures. If you find
+ it hard to read maybe the Closure Guide is an easier introduction
+ for you: See closure_guide(LPC) and closures-example(LPC).
+
+DESCRIPTION
+ Closures provide a means of creating code dynamically and
+ passing pieces of code as parameters, storing them in
+ variables. One might think of them as a very advanced form of
+ process_string(). However, this falls short of what you can
+ actually do with them.
+
+ The simplest kind of closures are efuns, lfuns or operators.
+ For example, #'this_player is an example of a closure. You can
+ assign it to a variable as in
+
+ closure f;
+ object p;
+ f = #'this_player;
+
+ and later use either the funcall() or apply() efun to evaluate
+ it. Like
+
+ p = funcall(f);
+
+ or
+
+ p = apply(f);
+
+ In both cases there p will afterwards hold the value of
+ this_player(). Of course, this is only a rather simple
+ application.
+
+ Inline closures are a variant of lfun closures, the difference
+ being that the function text is written right where the
+ closure is used. Since they are pretty powerful by
+ themselves, inline closures have their own manpage.
+
+ More useful instances of closures can be created
+ using the lambda() efun. It is much like the lambda function
+ in LISP. For example, you can do the following:
+
+ f = lambda( ({ 'x }), ({ #'environment, 'x }) );
+
+ This will create a lambda closure and assign it to f. The
+ first argument to lambda is an array describing the arguments
+ (symbols) passed to the closure upon evaluation by funcall()
+ or apply(). You can now evaluate f, for example by means of
+ funcall(f,this_object()). This will result in the following
+ steps:
+
+ 1. The value of this_object() will be bound to symbol x.
+ 2. environment(x) evaluates to environment(this_object())
+ and is returned as the result of the funcall().
+
+ One might wonder why there are two functions, funcall() and
+ apply(), to perform the seemingly same job, namely evaluating
+ a closure. Of course there is a subtle difference. If the last
+ argument to apply() is an array, then each of its elements
+ gets expanded to an additional paramater. The obvious use
+ would be #'call_other as in:
+
+ mixed eval(object ob,string func,mixed *args) {
+ return apply(#'call_other,ob,func,args);
+ }
+
+ This will result in calling
+ ob->func(args[0],args[1],...,args[sizeof(args)-1]). Using
+ funcall() instead of apply() would have given us
+ ob->func(args).
+
+ Of course, besides efuns there are closures for operators,
+ like #'+, '-, #'<, #'&&, etc.
+
+ Well, so far closures have been pretty much limited despite
+ their obvious flexibility. This changes now with the
+ introduction of conditional and loop operators. For example,
+ try:
+
+ closure max;
+ max = lambda( ({ 'x, 'y }),
+ ({ #'? ,({ #'>, 'x, 'y }), 'x, 'y }) );
+ return funcall(max,7,3);
+
+ The above example will return 7. What happened? Of course #'?
+ is the conditional operator and its 'syntax' is as follows:
+
+ ({ #'?, cond1, val1, cond2, val2, ..., condn, valn,
+ valdefault });
+
+ It evaluates cond1, cond2, ..., condn successively until it
+ gets a nonzero result and then returns the corresponding
+ value. If there is no condition evaluating to a nonzero
+ result, valdefault gets returned. If valdefault is omitted, 0
+ gets returned. #'?! works just like #'?, except that the !
+ operator is applied to conditions before testing. Therefore,
+ while #'? is somewhat like an if statement, #'?! resembles an
+ if_not statement if there were one.
+
+ There are also loops:
+
+ ({ #'do, loopbody1, ..., loopbodyN, loopcond, loopresult })
+
+ will evaluate the loopbodies until loopcond evaluates to 0 and
+ then return the value of loopresult. Symbols may be used as
+ variables, of course.
+
+ ({ #'while, loopcond, loopresult, loopbody1, ..., loopbodyN })
+
+ works similar but evaluates loopcond before the loopbodies.
+
+ The foreach() loop also exists:
+
+ ({ #'foreach, 'var, expr, loopbody1, ..., loopbodyN })
+ ({ #'foreach, ({ 'var1, ..., 'varN}) , expr
+ , loopbody1, ..., loopbodyN })
+
+
+ Now on to a couple of tricky things:
+
+ a) How do I write down an array within a lambda closure to
+ avoid interpretation as a subclosure?
+
+ ({ #'member, ({ "abc", "xyz" }), 'x }) will obviously
+ result in an error as soon as lambda() tries to interpret
+ "abc" as a closure operator. The solution is to quote the
+ array, as in: ({ #'member, '({ "abc", "xyz" }), 'x }).
+ Applying lambda() to this will not result in an error.
+ Instead, the quote will be stripped from the array and the
+ result regarded as a normal array literal. The same can be
+ achieved by using the efun quote(), e.g.:
+
+ ({ #'member, quote( ({ "abc", "xyz" }), 'x ) })
+
+ b) Isn't it a security risk to pass, say, a closure to the
+ master object which then evaluates it with all the
+ permissions it got?
+
+ Luckily, no. Each closure gets upon compilation bound to
+ the object defining it. That means that executing it first
+ sets this_object() to the object that defined it and then
+ evaluates the closure. This also allows us to call lfuns
+ which might otherwise be undefined in the calling object.
+
+ There is however, a variant of lambda(), called
+ unbound_lambda(), which works similar but does not allow
+ the use of lfuns and does not bind the closure to the
+ defining object. The drawback is that trying to evaluate it
+ by apply() or funcall() will result in an error. The
+ closure first needs to be bound by calling bind_lambda().
+ bind_lambda() normally takes one argument and transforms an
+ unbound closure into a closure bound to the object
+ executing the bind_lambda().
+
+ Privileged objects, like the master and the simul_efun
+ object (or those authorized by the privilege_violation()
+ function in the master) may also give an object as the
+ second argument to bind_lambda(). This will bind the
+ closure to that object. A sample application is:
+
+ dump_object(ob)
+ // will dump the variables of ob to /dump.o
+ {
+ closure save;
+ save = unbound_lambda( ({ }),
+ ({ #'save_object, "/open/dump" }) );
+ bind_lambda(save,ob);
+ funcall(save);
+ }
+
+ bind_lambda() can also be used with efun closures.
+
+ c) It might be an interesting application to create closures
+ dynamically as an alternative to writing LPC code to a file
+ and then loading it. However, how do I avoid doing exactly
+ that if I need symbols like 'x or 'y?
+
+ To do that one uses the quote() efun. It takes a string as
+ its argument and transforms it into a symbol. For example,
+ writing quote("x") is exactly the same as writing 'x.
+
+ d) How do I test if a variable holds a closure?
+
+ Use the closurep() efun which works like all the other type
+ testing efuns. For symbols there is also symbolp()
+ available.
+
+ e) That means, I can do:
+ if (closurep(f)) return funcall(f); else return f; ?
+
+ Yes, but in the case of funcall() it is unnecessary. If
+ funcall() gets only one argument and it is not a closure it
+ will be returned unchanged. So return funcall(f); would
+ suffice.
+
+ f) I want to use a function in some object as a closure. How do I do
+ that?
+
+ There are several ways. If the function resides in
+ this_object(), just use #'func_name. If not, or if you want
+ to create the function dnynamically, use the efun
+ symbol_function(). It takes a string as it first and an
+ object as its second argument and returns a closure which
+ upon evaluation calls the given function in the given
+ object (and faster than call_other(), too, if done from
+ inside a loop, since function search will be done only when
+ calling symbol_function().
+
+ g) Can I create efun closures dynamically, too?
+
+ Yes, just use symbol_function() with a single argument.
+ Most useful for marker objects and the like. But
+ theoretically a security risk if not used properly and from
+ inside a security relevant object. Take care, however,
+ that, if there is a simul_efun with the same name, it will
+ be preferred as in the case of #'function. Use the efun::
+ modifier to get the efun if you need it.
+
+ h) Are there other uses of closures except using them to store
+ code?
+
+ Lots. For example, you can use them within almost all of
+ the efuns where you give a function as an argument, like
+ filter(), sort_array() or walk_mapping().
+ sort_array(array,#'>) does indeed what is expected. Another
+ application is set_prompt(), where a closure can output
+ your own prompt based on the current time and other stuff
+ which changes all the time.
+
+ Finally, there are some special efun/operator closures:
+
+ #'[ : indexes an array.
+ #'[< : does the same, but starting at the end.
+ #'[..] : gets an array and two numbers
+ and returns a sub-array.
+ #'[..<] : same as above but the second index is
+ interpreted as counted from the left end.
+ #'[<..] and
+ #'[<..<] : should be clear now.
+ #'[.. : takes only one index and returns the sub-
+ array from this index to the end.
+ #'[<.. : same as above but the index is interpreted
+ as counted from the left end.
+ #'({ : puts all arguments into an array.
+ #'([ : gets an unquoted (!) array which must include
+ at least one element as argument and returns a mapping of
+ the width of the given array's size with one entry that
+ contains the first element as key and the other elements
+ as values to the key.
+
+ #'negate is for unary minus.
+ #', may be followed by any number of closures,
+ e.g.: ({ (#',),
+ ({#'= 'h, 'a, }), ({#'=, 'a, 'b }), ({#'=, 'b, 'h }) })
+ will swap 'a and 'b when compiled and executed.
+
+
+ Procedural elements:
+ ====================
+
+ definition of terms:
+ <block> : zero or more values to be evaluated.
+ <test> : one value to be evaluated as branch or loop condition.
+ <result> : one value to be evaluated at the end of the
+ execution of the form; the value is returned.
+ <lvalue> : local variable/parameter, global variable, or an
+ indexed lvalue.
+ useded EBNF operators:
+ { } iteration
+ [ ] option
+
+ forms:
+ ({#', <body> <result>})
+ ({#'? { <test> <result> } [ <result> ] })
+ ({#'?! { <test> <result> } [ <result> ] })
+ ({#'&& { test } })
+ ({#'|| { test } })
+ ({#'while <test> <result> <body>}) loop while test
+ evaluates non-zero.
+ ({#'do <body> <test> <result>}) loop till test
+ evaluates zero.
+ ({#'= { <lvalue> <value> } }) assignment
+ other assignment
+ operators work, too.
+
+ lisp similars:
+ #', progn
+ #'? cond
+ #'&& and
+ #'|| or
+ #'while do /* but lisp has more syntactic candy here */
+ #'= setq
+
+ A parameter / local variable 'foo' is referenced as 'foo , a
+ global variable as ({#'foo}) . In lvalue positions
+ (assignment), you need not enclose global variable closures in
+ arrays.
+
+ Call by reference parameters are given with ({#'&, <lvalue>})
+
+ Some special efuns:
+ #'[ indexing
+ #'[< indexing from the end
+ #'negate unary -
+
+ Unbound lambda closures
+ =======================
+
+ These closures are not bound to any object. They are created
+ with the efun unbound_lambda() . They cannot contain
+ references to global variables, and all lfun closures are
+ inserted as is, since there is no native object for this
+ closure. You can bind and rebind unbound lambda closures to
+ an object with efun bind_lambda() You need to bind it before
+ it can be called. Ordinary objects can obly bind to
+ themselves, binding to other objects causes a privilege
+ violation(). The point is that previous_object for calls done
+ from inside the closure will reflect the object doing
+ bind_lambda(), and all object / uid based security will also
+ refer to this object.
+
+
+AUTHOR
+ MacBeth, Amylaar, Hyp
+
+SEE ALSO
+ closures-abstract(LPC), closures-example(LPC), closure_guide(LPC)
+ inline-closures(LPC)
diff --git a/doc/LPC/closures-abstract b/doc/LPC/closures-abstract
new file mode 100644
index 0000000..6b1d932
--- /dev/null
+++ b/doc/LPC/closures-abstract
@@ -0,0 +1,82 @@
+Procedural elements:
+====================
+
+definition of terms:
+ <block> : zero or more values to be evaluated.
+ <test> : one value to be evaluated as branch or loop condition.
+ <result> : one value to be evaluated at the end of the execution of
+ the form; the value is returned.
+ <lvalue> : local variable/parameter, global variable, or an indexed
+ lvalue.
+ <expression>: one value to be evaluated.
+ <integer> : an integer constant
+ <string> : a string constant, or 0.
+used EBNF operators:
+{ } iteration
+[ ] option
+| alternative
+
+forms:
+ ({#', <body> <result>})
+ ({#'? { <test> <result> } [ <result> ] })
+ ({#'?! { <test> <result> } [ <result> ] })
+ ({#'&& { test } })
+ ({#'|| { test } })
+ ({#'while <test> <result> <body>...}) loop while test evaluates non-zero.
+ ({#'do <body> <test> <result>}) loop till test evaluates zero.
+ ({#'foreach <var> <expr> <body>...}) loop over all values of <expr>.
+ ({#'foreach ({ <var>...<var> }) <expr> <body>...})
+ ({#'= { <lvalue> <value> } }) assignment
+ other assignment operators work too.
+ case_label: <integer> | <string> | #'default
+ generalized_case_label: case_label | <integer> #'.. <integer>
+ case_label_list: case_label | ({ { generalized_case_label } })
+ case_delimiter: #', | #'break
+ ({#'switch <expression> { case_label_list <result> case_delimiter } })
+ Evaluate expression, then evaluate the result form labeled with
+ the value equal to the value evaluated from expression.
+ If no matching label exists, the value of #'switch is 0.
+ ({#'catch, <body> [, 'nolog ] [, 'publish ], [, 'reserve, <expr> })
+ Evaluates the <body> and catches any runtime error. If the symbol
+ 'nolog is also given, a caught error is not logged. If the
+ symbol 'publish is also given, master::runtime_error() is
+ called for the caught error. If the symbol 'reserve with a
+ following expression is given, this value is used as the
+ computing reserve.
+
+
+lisp similars:
+ #', progn
+ #'? cond
+ #'&& and
+ #'|| or
+ #'while do /* but lisp has more syntactic candy here */
+ #'= setq
+
+A parameter / local variable 'foo' is referenced as 'foo , a global
+variable as ({#'foo}) . In lvalue positions (assignment), you need not
+enclose global variable closures in arrays.
+
+Call by reference parameters are given with ({#'&, <lvalue>})
+
+Some special efuns:
+#'[ indexing
+#'[< indexing from the end
+#'negate unary -
+
+Unbound lambda closures
+=======================
+
+These closures are not bound to any object. They are created with the efun
+unbound_lambda() . They cannot contain references to global variables, and
+all lfun closures are inserted as is, since there is no native object for
+this closure.
+You can bind and rebind unbound lambda closures to an object with efun
+bind_lambda() You need to bind it before it can be called. Ordinary objects
+can obly bind to themselves, binding to other objects causes a privilege
+violation().
+The point is that previous_object for calls done from inside the closure
+will reflect the object doing bind_lambda(), and all object / uid based
+security will also refer to this object.
+
+
diff --git a/doc/LPC/closures-example b/doc/LPC/closures-example
new file mode 100644
index 0000000..f50fd35
--- /dev/null
+++ b/doc/LPC/closures-example
@@ -0,0 +1,202 @@
+CONCEPT
+ closures example
+
+DESCRIPTION
+ This document contains small examples of the usage of
+ (lambda-)closures. For technical details see the closures(LPC)
+ doc. For hints when to use which type of closure, see the end
+ of this doc.
+
+
+ Many Muds use 'details' to add more flavour. 'Details' are
+ items which can be looked at, but are not implemented as own
+ objects, but instead simulated by the environment.
+ Lets assume that the function
+
+ AddDetail(string keyword, string|closure desc)
+
+ adds the detail 'keyword' to the room, which, when look at,
+ returns the string 'desc' resp. the result of the execution of
+ closure 'desc' as the detail description to the player.
+
+ Now imagine that one wants to equip a room with magic runes,
+ which read as 'Hello <playername>!\n" when looked at.
+ Obviously
+
+ AddDetail("runes", sprintf( "Hello %s!\n"
+ , this_player()->QueryName()));
+
+ is not sufficient, as the 'this_player()' is executed to early
+ and just once: for the player loading the room.
+
+ The solution is to use closures. First, the solution using
+ lfun-closures:
+
+ private string _detail_runes () {
+ return sprintf("Hello %s!\n", this_player()->QueryName());
+ }
+ ...
+ AddDetail("runes", #'_detail_runes);
+
+ or with an inline closure:
+
+ AddDetail("runes"
+ , (: sprintf("Hello %s!\n", this_player()->QueryName()) :)
+ );
+
+
+ Simple? Here is the same code, this time as lambda-closure:
+
+ AddDetail( "runes"
+ , lambda(0
+ , ({#'sprintf, "Hello %s!\n"
+ , ({#'call_other, ({#'this_player})
+ , "QueryName" })
+ })
+ ));
+
+ Why the extra ({ }) around '#'this_player'? #'this_player
+ alone is just a symbol, symbolizing the efun this_player(),
+ but call_other() needs an object as first argument. Therefore,
+ the #'this_player has to be interpreted as function to
+ evaluate, which is enforced by enclosing it in ({ }). The same
+ reason also dictates the enclosing of the whole #'call_other
+ expression into ({ }).
+ Note also the missing #'return: it is not needed. The result
+ of a lambda-closure is the last value computed.
+
+
+ Another example: Task is to reduce the HP of every living in a
+ room by 10, unless the result would be negative.
+ Selecting all livings in a room is simply
+
+ filter(all_inventory(room), #'living)
+
+ The tricky part is to reduce the HP. Again, first the
+ lfun-closure solution:
+
+ private _reduce_hp (object liv) {
+ int hp;
+ hp = liv->QueryHP();
+ if (hp > 10)
+ liv->SetHP(hp-10);
+ }
+ ...
+
+ map( filter(all_inventory(room), #'living)
+ , #'_reduce_hp)
+
+ or as an inline closure:
+
+ map( filter(all_inventory(room), #'living)
+ , (: int hp;
+ hp = liv->QueryHP();
+ if (hp > 10)
+ liv->SetHP(hp - 10);
+ :) );
+
+ Both filter() and map() pass the actual array item
+ being filtered/mapped as first argument to the closure.
+
+ Now, the lambda-closure solution:
+
+ map( filter(all_inventory(room), #'living)
+ , lambda( ({ 'liv })
+ , ({'#, , ({#'=, 'hp, ({#'call_other, 'liv, "QueryHP" }) })
+ , ({#'?, ({#'>, 'hp, 10 })
+ , ({#'call_other, 'liv, "SetHP"
+ , ({#'-, 'hp, 10 })
+ })
+ })
+ })
+ ) // of lambda()
+ );
+
+ It is worthy to point out how local variables like 'hp' are
+ declared in a lambda-closure: not at all. They are just used
+ by writing their symbol 'hp . Same applies to the closures
+ parameter 'liv .
+ The lambda-closure solution is not recommended for three
+ reasons: it is complicated, does not use the powers of
+ lambda(), and the lambda() is recompiled every time this
+ statement is executed!
+
+
+ So far, lambda-closures seem to be just complicated, and in
+ fact: they are. Their powers lie elsewhere.
+
+ Imagine a computation, like for skill resolution, which
+ involves two object properties multiplied with factors and
+ then added.
+ The straightforward solution would be a function like:
+
+ int Compute (object obj, string stat1, int factor1
+ , string stat2, int factor2)
+ {
+ return call_other(obj, "Query"+stat1) * factor1
+ + call_other(obj, "Query"+stat2) * factor2;
+ }
+
+ Each call to Compute() involves several operations (computing
+ the function names and resolving the call_other()s) which in
+ fact need to be done just once. Using lambda-closures, one can
+ construct and compile a piece of code which behaves like a
+ Compute() tailored for a specific stat/factor combination:
+
+ closure ConstructCompute (object obj, string stat1, int factor1
+ , string stat2, int factor2)
+ {
+ mixed code;
+
+ // Construct the first multiplication.
+ // The symbol_function() creates a symbol for the
+ // lfun 'Query<stat1>', speeding up later calls.
+ // Note again the extra ({ }) around the created symbol.
+
+ code = ({#'*, ({ symbol_function("Query"+stat1, obj) })
+ , factor1 });
+
+ // Construct the second multiplication, and the addition
+ // of both terms.
+
+ code = ({#'+, code
+ , ({#'*, ({ symbol_function("Query"+stat2, obj) })
+ , factor2 })
+ });
+
+ // Compile the code and return the generated closure.
+ return lambda(0, code);
+ }
+
+ Once the closure is compiled,
+
+ str_dex_fun = ConstructCompute(obj, "Str", 10, "Dex", 90);
+
+ it can be used with a simple 'funcall(str_dex_fun)'.
+
+
+DESCRIPTION -- When to use which closure?
+ First, a closure is only then useful if it needn't to live any
+ longer than the object defining it. Reason: when the defining
+ object gets destructed, the closure will vanish, too.
+
+ Efun-, lfun- and inline closures should be used where useful, as they
+ mostly do the job and are easy to read. The disadvantage of lfun- and
+ inline closures is that they make a replace_program() impossible
+ - but since such objects tend to not being replaceable at all, this is
+ no real loss.
+
+ Lambda closures are needed if the actions of the closure are
+ heavily depending on some data available only at runtime, like
+ the actual inventory of a certain player.
+ If you use lfun-closures and find yourself shoving around
+ runtime data in arguments or (gasp!) global variables, it is
+ time to think about using a lambda-closure, compiling the
+ value hard into it.
+ The disadvantages of lambda closures are clear: they are damn
+ hard to read, and each lambda() statement requires extra time to
+ compile the closure.
+
+
+SEE ALSO
+ closures(LPC), closure_guide(LPC), closures-abstract(LPC)
diff --git a/doc/LPC/closures2 b/doc/LPC/closures2
new file mode 100644
index 0000000..fc52894
--- /dev/null
+++ b/doc/LPC/closures2
@@ -0,0 +1,369 @@
+Closures provide a means of creating code dynamically and passing pieces
+of code as parameters, storing them in variables. One might think of them
+as a very advanced form of process_string(). However, this falls short of
+what you can actually do with them.
+
+The simplest kind of closures are efuns, lfuns or operators. For
+example, #'this_player is an example of a closure. You can assign it
+to a variable as in
+
+ closure f;
+ object p;
+ f = #'this_player;
+
+and later use either the funcall() or apply() efun to evaluate it. Like
+
+ p = funcall(f);
+
+or
+ p = apply(f);
+
+In both cases there p will afterwards hold the value of this_player().
+Of course, this is only a rather simple application. More useful
+instances of closures can be created using the lambda() efun. It is much
+like the lambda function in LISP. For example, you can do the following:
+
+ f = lambda( ({ 'x }), ({ #'environment, 'x }) );
+
+This will create a lambda closure and assign it to f. The first argument
+to lambda is an array describing the arguments (symbols) passed to the
+closure upon evaluation by funcall() or apply(). You can now evaluate f,
+for example by means of funcall(f,this_object()). This will result in
+the following steps:
+
+ 1. The value of this_object() will be bound to symbol x.
+ 2. environment(x) evaluates to environment(this_object())
+ and is returned as the result of the funcall().
+
+One might wonder why there are two functions, funcall() and apply(), to
+perform the seemingly same job, namely evaluating a closure. Of course
+there is a subtle difference. If the last argument to apply() is an array,
+then each of its elements gets expanded to an additional paramater. The
+obvious use would be #'call_other as in:
+
+ mixed eval(object ob,string func,mixed *args)
+ {
+ return apply(#'call_other,ob,func,args);
+ }
+
+This will result in calling ob->func(args[0],args[1],...,args[sizeof(args)-1]).
+Using funcall() instead of apply() would have given us ob->func(args).
+
+Of course, besides efuns there are closures for operators, like #'+,
+#'-, #'<, #'&&, etc.
+
+Well, so far closures have been pretty much limited despite their
+obvious flexibility. This changes now with the introduction of
+conditional and loop operators. For example, try:
+
+ closure max;
+ max = lambda( ({ 'x, 'y }), ({ #'? ,({ #'>, 'x, 'y}), 'x, 'y }) });
+ return funcall(max,7,3);
+
+The above example will return 7. What happened? Of course #'? is the
+conditional operator and its 'syntax' is as follows:
+
+ ({ #'?, cond1, val1, cond2, val2, ..., condn, valn, valdefault });
+
+It evaluates cond1, cond2, ..., condn successively until it gets a
+nonzero result and then returns the corresponding value. If there is no
+condition evaluating to a nonzero result, valdefault gets returned. If
+valdefault is omitted, 0 gets returned. #'?! works just like #'?, except
+that the ! operator is applied to conditions before testing. Therefore,
+while #'? is somewhat like an if statement, #'?! resembles an if_not
+statement if there were one.
+
+ There are also loops:
+ ({ #'do, loopbody, loopcond, loopresult })
+ will evaluate loopbody until loopcond evaluates to 0 and
+ then return the value of loopresult. Symbols my be used
+ as variables, of course.
+
+ ({ #'while, loopcond, loopresult, loopbody })
+ works similar but evaluates loopcond before loopbody.
+
+There are, however, some questions open:
+
+a) How do I write down an array within a lambda closure to avoid
+ interpretation as a subclosure?
+ ({ #'member_array, 'x, ({ "abc", "xyz }) }) will obviously result in
+ an error as soon as lambda() tries to interpret "abc" as a closure
+ operator. The solution is to quote the array, as in:
+ ({ #'member_array, 'x, '({ "abc", "xyz }) }). Applying lambda() to
+ this will not result in an error. Instead, the quote will be stripped
+ from the array and the result regarded as a normal array literal. The
+ same can be achieved by using the efun quote(), e.g.:
+ ({ #'member_array, 'x, quote( ({ "abc", "xyz }) ) })
+b) Isn't it a security risk to pass, say, a closure to the master object
+ which then evaluates it with all the permissions it got?
+ Luckily, no. Each closure gets upon compilation bound to the object
+ defining it. That means that executing it first sets this_object()
+ to the object that defined it and then evaluates the closure. This
+ also allows us to call lfuns which might otherwise be undefined in
+ the calling object.
+ There is however, a variant of lambda(), called unbound_lambda(),
+ which works similar but does not allow the use of lfuns and does not
+ bind the closure to the defining object. The drawback is that trying
+ to evaluate it by apply() or funcall() will result in an error. The
+ closure first needs to be bound by calling bind_lambda().
+ bind_lambda() normally takes one argument and transforms an unbound
+ closure into a closure bound to the object executing the
+ bind_lambda().
+ Privileged objects, like the master and the simul_efun object (or
+ those authorized by the privilege_violation() function in the master)
+ may also give an object as the second argument to bind_lambda(). This
+ will bind the closure to that object. A sample application is:
+
+ dump_object(ob)
+ // will dump the variables of ob to /dump.o
+ {
+ closure save;
+ save = unbound_lambda( ({ }), ({ #'save_object, "/open/dump" }) );
+ bind_lambda(save,ob);
+ funcall(save);
+ }
+
+ bind_lambda() can also be used with efun closures.
+
+c) It might be an interesting application to create closures dynamically
+ as an alternative to writing LPC code to a file and then loading it.
+ However, how do I avoid doing exactly that if I need symbols like 'x
+ or 'y?
+ To do that one uses the quote() efun. It takes a string as its
+ argument and transforms it into a symbol. For example, writing
+ quote("x") is exactly the same as writing 'x.
+
+d) How do I test if a variable holds a closure?
+ Use the closurep() efun which works like all the other type testing
+ efuns. For symbols there is also symbolp() available.
+
+e) That means, I can do:
+ if (closurep(f)) return funcall(f); else return f; ?
+ Yes, but in the case of funcall() it is unnecessary. If funcall()
+ gets only one argument and it is not a closure it will be returned
+ unchanged. So return funcall(f); would suffice.
+
+f) I want to use a function in some object as a closure. How do I do
+ that?
+ There are several ways. If the function resides in this_object(),
+ just use #'func_name. If not, or if you want to create the function
+ dnynamically, use the efun symbol_function(). It takes a string as
+ it first and an object as its second argument and returns a closure
+ which upon evaluation calls the given function in the given object
+ (and faster than call_other(), too, if done from inside a loop,
+ since function search will be done only when calling symbol_function().
+
+g) Can I create efun closures dynamically, too?
+ Yes, just use symbol_function() with a single argument. Most useful
+ for marker objects and the like. But theoretically a security risk
+ if not used properly and from inside a security relevant object.
+ Take care, however, that, if there is a simul_efun with the same
+ name, it will be preferred as in the case of #'function. Use the
+ efun:: modifier to get the efun if you need it.
+
+h) Are there other uses of closures except using them to store code?
+ Lots. For example, you can use them within almost all of the
+ efuns where you give a function as an argument, like filter(),
+ sort_array() or walk_mapping(). sort_array(array,#'>) does indeed
+ what is expected. Another application is set_prompt(), where a
+ closure can output your own prompt based on the current time and other
+ stuff which changes all the time.
+
+Finally, there are some special efun/operator closures:
+
+#'[ indexes an array.
+#'[< does the same, but starting at the end.
+#'negate is for unary minus.
+#', may be followed by any number of closures,
+e.g.: ({ #', ({#'= 'h, 'a, }), ({#'=, 'a, 'b }), ({#'=, 'b, 'h }) })
+will swap 'a and 'b when compiled and executed.
+
+------------
+An example from Amylaar:
+
+#I have tested the replace_program() functionality with one example, which I
+#include below. The room is commonly known as /room/orc_valley.c .
+#A prerequisite to make this work is to have valued properties in room.c .
+#The property C_EXTRA_RESET, if defined, is evaluated at reset time in
+#the reset() of the used room.c .
+#Moreover, you need a function to query an unprocessed property to use
+#orc_valley.c from fortress.c (That is, don't do an automatic funcall there.)
+#If you can't supply such a function, you have to set the property "get_orc"
+#at the end of orc_valley.c 's extra_reset() with:
+# add_prop("get_orc", lambda(0, get_orc) );
+#which will set the property to a function that returns the function that
+#is in the variable get_orc at the time you do the add_prop() call.
+#
+#Back to fortress.c : Assume you have the function successfully queried and
+#stored in the variable get_orc. Now you can compute your extra_reset()
+#function by:
+# get_orc = lambda( 0, ({#'funcall, get_orc, 8, 40}) );
+#which creates the usual 8 orcs with an a_chat chance of 40.
+#
+#Here comes the orc_valley.c source:
+#
+# ----------- cut here ------- cut here -------- cut here ------------
+#
+##include "room.h"
+##include "/sys/stdproperties.h"
+##undef EXTRA_RESET
+##define EXTRA_RESET extra_reset();
+#
+#extra_reset() {
+# closure get_orc;
+#
+# replace_program("room/room"); /* Must come first. */
+# get_orc = lambda( ({'num_orcs, 'chat_chance}),
+# ({#'?!, ({#'present, "orc", ({#'previous_object}) }),
+# ({#'do,
+# ({#'=, 'orc, ({#'clone_object, "obj/monster"}) }),
+# ({#'=, 'i, 9}),
+# ({#'do,
+# ({#'call_other, 'orc, "set_level",
+# ({#'+, ({#'random, 2}), 1}) }),
+# ({#'call_other, 'orc,
+# ({#'[,
+# quote(({"set_aggressive", "set_ac", "set_short",
+# "set_al", "set_ep", "set_hp", "set_race",
+# "set_alias", "set_name"})), ({#'-=, 'i, 1}) }),
+# ({#'[,
+# quote(({1, 0, "An orc", -60, 1014, 30, "orc",
+# "dirty crap", "orc"})), 'i}) }),
+# 'i, 0}),
+# ({#'call_other, 'orc, "load_a_chat", 'chat_chance,
+# quote(({ "Orc says: Kill 'em!\n",
+# "Orc says: Bloody humans!\n",
+# "Orc says: Stop 'em!\n",
+# "Orc says: Get 'em!\n",
+# "Orc says: Let's rip out his guts!\n",
+# "Orc says: Kill 'em before they run away!\n",
+# "Orc says: What is that human doing here!\n",
+# })) }),
+# ({#'=, 'n, ({#'*, ({#'random, 3}), 5}) }),
+# ({#'=, 'weapon, ({#'clone_object, "obj/weapon"}) }),
+# ({#'=, 'i, 5}),
+# ({#'do,
+# ({#'call_other, 'weapon,
+# ({#'[,
+# quote(({ "set_alt_name", "set_weight", "set_value",
+# "set_class", "set_name"})), ({#'-=, 'i, 1}) }),
+# ({#'[,
+# quote(({ "knife", 1, 8, 5, "knife",
+# "knife", 1, 15, 7, "curved knife",
+# "axe", 2, 25, 9, "hand axe", })),
+# ({#'+, 'n, 'i}) }) }),
+# 'i, 0}),
+# ({#'transfer, 'weapon, 'orc}),
+# ({#'command,
+# ({#'+, "wield ",
+# ({#'call_other, 'weapon, "query_name"}) }), 'orc}),
+# ({#'move_object, 'orc, ({#'previous_object}) }),
+# ({#'-=, 'num_orcs, 1}), 0})
+# })
+# );
+# add_prop("get_orc", get_orc);
+# get_orc = lambda( 0, ({#'funcall, get_orc, 2, 50}) );
+# add_prop(C_EXTRA_RESET, get_orc);
+# funcall(get_orc);
+#}
+#
+#TWO_EXIT("room/slope", "east",
+# "room/fortress", "north",
+# "The orc valley",
+# "You are in the orc valley. This place is inhabited by orcs.\n" +
+# "There is a fortress to the north, with lot of signs of orcs.\n", 1)
+#
+# ----------- cut here ------- cut here -------- cut here ------------
+#
+Procedural elements:
+====================
+
+definition of terms:
+ <block> : zero or more values to be evaluated.
+ <test> : one value to be evaluated as branch or loop condition.
+ <result> : one value to be evaluated at the end of the execution of the
+ form; the value is returned.
+ <lvalue> : local variable/parameter, global variable, or an indexed lvalue.
+useded EBNF operators:
+{ } iteration
+[ ] option
+
+forms:
+ ({#', <body> <result>})
+ ({#'? { <test> <result> } [ <result> ] })
+ ({#'?! { <test> <result> } [ <result> ] })
+ ({#'&& { test } })
+ ({#'|| { test } })
+ ({#'while <test> <result> <body>}) loop while test evaluates non-zero.
+ ({#'do <body> <test> <result>}) loop till test evaluates zero.
+ ({#'= { <lvalue> <value> } }) assignment
+ other assignment operators work too.
+
+lisp similars:
+ #', progn
+ #'? cond
+ #'&& and
+ #'|| or
+ #'while do /* but lisp has more syntactic candy here */
+ #'= setq
+
+A parameter / local variable 'foo' is referenced as 'foo , a global
+variable as ({#'foo}) . In lvalue positions (assignment), you need not
+enclose global variable closures in arrays.
+
+Call by reference parameters are given with ({#'&, <lvalue>})
+
+Some special efuns:
+#'[ indexing
+#'[< indexing from the end
+#'negate unary -
+
+Unbound lambda closures
+=======================
+
+These closures are not bound to any object. They are created with the efun
+unbound_lambda() . They cannot contain references to global variables, and
+all lfun closures are inserted as is, since there is no native object for
+this closure.
+You can bind and rebind unbound lambda closures to an object with efun
+bind_lambda() You need to bind it before it can be called. Ordinary objects
+can obly bind to themselves, binding to other objects causes a privilege
+violation().
+The point is that previous_object for calls done from inside the closure
+will reflect the object doing bind_lambda(), and all object / uid based
+security will also refer to this object.
+
+
+The following is mostly vapourware.
+Well, another application would be that some things in the driver can be,
+sort of, microprogrammed.
+The master object could set some hooks in inaugurate_master(), like creating
+the code for move_object(), using a primitive low_move_object() or
+__move_object() or such. All calls of init(), exit(), etc. can thus be
+controlled on mudlib level.
+The driver would do an implicit bind_lambda() to the victim when the closure
+is used.
+
+e.g.
+({#'?, ({#'=, 'ob, ({#'first_inventory, 'destination}) }),
+ ({#'do,
+ ({#'call_other, 'ob, "init"}),
+ ({#'=, 'ob, ({#'next_inventory, 'ob}) }), 0 })
+})
+
+or
+
+({#'filter_objects, ({#'all_inventory, 'destination}), "init"})
+/* Won't show init failures due to move/destruct */
+
+is equivalent to
+
+if (ob = first_inventory(destination) ) {
+ do {
+ ob->init();
+ } while(ob = next_inventory(ob) );
+}
+
+and it's speed is mainly determined by the call_other. Thus, it shouldn't be
+noticably slower than the current C code in move_object().
+
diff --git a/doc/LPC/comments b/doc/LPC/comments
new file mode 100644
index 0000000..a3b0fd0
--- /dev/null
+++ b/doc/LPC/comments
@@ -0,0 +1,44 @@
+NAME
+ comments
+
+SYNTAX
+ /* block comment text */
+ // line comment text <end of line>
+
+
+DESCRIPTION
+ Comments are used to stored arbitrary text in the LPC program
+ source. It is a good idea if some if this text explains the
+ deeper intentions behind the actual LPC statements.
+
+ There are block comments and line comments.
+
+ Block comments start with a '/*' and end with a '*/'. They cannot
+ be nested, so
+
+ /* this /* is */ illegal */
+
+ will treat '/* this /* is */' as the comment.
+
+ Line comments start with '//' and continue until the unescaped(!)
+ end of the line (as in the new C standard).
+
+ It is not possible to next block and line comments within
+ each other. Meaning: '//' within /* ... */ has no special meaning,
+ neither does '/*' or '*/' have after a //.
+
+EXAMPLES
+ /* Simple block comment */
+
+ /* Block comments can
+ span several lines */
+
+ // Simple line comment
+
+ // Line comments can \
+ span several lines, too!
+
+ //#define LONG_MACRO The unique behaviour \
+ or line comments regarding the end of line \
+ can be used for example to comment out a \
+ large macro with just to keystrokes.
diff --git a/doc/LPC/do-while b/doc/LPC/do-while
new file mode 100644
index 0000000..8f69b2a
--- /dev/null
+++ b/doc/LPC/do-while
@@ -0,0 +1,15 @@
+NAME
+ do-while
+
+SYNTAX
+ do { statement } while(expr);
+
+DESCRIPTION
+ Execute 'statment' until 'expr' evaulates to 0.
+
+ A 'break' in the 'statement' will terminate the loop. A
+ 'continue' will continue the execution from the beginning of
+ the loop.
+
+SEE ALSO
+ for(LPC), foreach(LPC), while(LPC), if(LPC), switch(LPC)
diff --git a/doc/LPC/ed0 b/doc/LPC/ed0
new file mode 100644
index 0000000..0edac7f
--- /dev/null
+++ b/doc/LPC/ed0
@@ -0,0 +1,39 @@
+NAME
+ ed0
+
+DESCRIPTION
+ When in 'ed', the prompt is ':'.
+
+ Ed has two modes, command mode and insert mode. The insert
+ mode has no prompt. You exit the insert mode by typing a
+ single '.' on a line.
+
+ All commands have the following syntax:
+
+ X,Ycmd
+
+ or
+
+ Xcmd
+
+ For example:
+
+ 1,10p
+ Will print line 1 to 10.
+ 1,5d
+ Will delete line 1 to 5.
+ 8p
+ Will print line 8.
+ A '.' is the "current line". The current line is the last line
+ referenced. If you want to print last line + 10 more:
+ .,.+10p
+
+NOTE
+ These manpages seem a bit antique, though still correct. For a
+ better detailed help, invoke ed and use the ``h'' command.
+ Also you could look into the man page for ed(1) on you nearest
+ Unix box. And for a bit of fun you can try the good old
+ quiz(6), invoke as ``quiz function ed-command''.
+
+SEE ALSO
+ ed1(LPC), ed(E)
diff --git a/doc/LPC/ed1 b/doc/LPC/ed1
new file mode 100644
index 0000000..25f3ac2
--- /dev/null
+++ b/doc/LPC/ed1
@@ -0,0 +1,32 @@
+NAME
+ ed1
+
+DESCRIPTION
+ Commands that use a line range:
+ If no line is given, then curent line is printed.
+
+ p Print line.
+ d Delete line.
+ l Print line with control characters.
+ r file Read in a file after the line specified.
+ s Substitute patterns. See special documentation.
+ z Print 10 lines.
+ a Start insert mode after specified line. Exit with
+ '.'<return>.
+ i Start insert mode before specified line. Exit with
+ '.'<return>.
+
+ Commands used without line specification:
+
+ q Quit. Won't work if file is changed.
+ Q Quit and discard all changes if not saved.
+ w Write the file out.
+ w file Write the file out with name 'file'.
+ e file Edit a file.
+ !cmd Give a game command. For example "say Wait, I am busy".
+
+ As line numbers '.' is current line, and '$' is last line of
+ file. Thus '1,$p' will always print all of the file.
+
+SEE ALSO
+ ed2(LPC)
diff --git a/doc/LPC/ed2 b/doc/LPC/ed2
new file mode 100644
index 0000000..85d520e
--- /dev/null
+++ b/doc/LPC/ed2
@@ -0,0 +1,25 @@
+NAME
+ ed2
+
+DESCRIPTION
+ Substitutions are very advanced.
+
+ First a simple example:
+
+ s/apa/bepa/
+ This will substitue the 'apa' in current line to 'bepa'.
+ If an 'p' is appended, you will also immediately see the result.
+
+ 1,$s/apa/bepa/
+ Same, but all lines in file. Only first occurence on every line.
+
+ Any character can used instead of '/':
+ s!apa!bepa!g
+ The 'g' specifies that all occurences of apa on this line are
+ changed to bepa.
+
+ The pattern that are supposed to be replaced, can be a regular
+ expression. See ed3 about that.
+
+SEE ALSO
+ ed3(LPC)
diff --git a/doc/LPC/ed3 b/doc/LPC/ed3
new file mode 100644
index 0000000..380c008
--- /dev/null
+++ b/doc/LPC/ed3
@@ -0,0 +1,30 @@
+NAME
+ ed3
+
+DESCRIPTION
+ Searching is done with:
+ /hello/
+ Find first line in of after current line.
+ Just // will repeat the search.
+
+ The patterns are regular expressions, where some characters
+ have a special meaning:
+ . Match any character.
+ x* Match any numbers of x (0 or more).
+ [abc] Match 'a', 'b' or 'c'.
+ [0-9] Match any digit 0 - 9.
+ [a-z] Match any lowercase letter.
+ \x Match 'x' where 'x' can be any character except '('
+ and ')'.
+
+EXAMPLE
+ s/ab.d/ABCD/
+ Substitute any string 'abXd' against 'ABCD' where X can be any
+ character.
+
+NOTE
+ This only half the truth, there is lots more magic in the
+ regexps.
+
+SEE ALSO
+ regexp(E), ed4(LPC)
diff --git a/doc/LPC/ed4 b/doc/LPC/ed4
new file mode 100644
index 0000000..2d5303c
--- /dev/null
+++ b/doc/LPC/ed4
@@ -0,0 +1,12 @@
+NAME
+ ed4
+
+DESCRIPTION
+ How to copy from a standard file.
+
+ Enter ed. Then do 'r /room/vill_green.c'. Now you have
+ something in the buffer. Change it into what you want it to
+ be. Then 'w /players/peter/hall.c'. Or 'w hall.c'.
+
+SEE ALSO
+ ed5(LPC)
diff --git a/doc/LPC/ed5 b/doc/LPC/ed5
new file mode 100644
index 0000000..9156ff3
--- /dev/null
+++ b/doc/LPC/ed5
@@ -0,0 +1,47 @@
+NAME
+ ed5
+
+DESCRIPTION
+ = : prints current line
+ a : append lines
+ c : change, that is, delate, than insert
+ d : delete line(s)
+ E <filename> : discard current buffer and edit the file named
+ <filename>
+ e <filename> : like e, but refuse if file has been changed
+ f : print current filename
+ f <filename> : set filename
+ i : insert line(s)
+ j : with no or one argument: join line with following line
+ j : with two arguments : join line range given
+ k<letter> : set mark <letter> to current line. <letter> must
+ be in the range [a-z] . The mark can be used
+ thereinafter as a line address, with a leading slash.
+ (e.g. ka to set mark a, /ap to print marked line)
+ l : print line with control characters
+ <start>,<end>m<dest> : move block (lines from start to end)
+ behind line # dest
+ <line>m<dest> : move single line
+ m<dest> : move current line
+ M : remove Ctrl-M (CR) characters.
+ p : print line
+ P : print line
+ Q : quit, discarding the buffer
+ q : the same, but refuse if file has been changed since last
+ write
+ r : read in file. If no adress is given, at the last insert
+ position, if also nothing was inserted, at the end of the
+ buffer
+ s : substitute
+ <start>,<end>t<dest> : copy block ( lines from start to end )
+ behind line position dest
+ <line>t<dest> : copy single line
+ t<dest> : copy current line
+ w : write file
+ W : write file
+ x : write file if buffer has been changed since last change,
+ then quit
+ z : show approx. a screenful of lines
+
+SEE ALSO
+ ed6(LPC)
diff --git a/doc/LPC/ed6 b/doc/LPC/ed6
new file mode 100644
index 0000000..8b80a70
--- /dev/null
+++ b/doc/LPC/ed6
@@ -0,0 +1,32 @@
+NAME
+ ed6
+
+DESCRIPTION
+ This is the list of extended ed commands that Xio unearthed
+ somewhere, thanks!
+
+ a) never use 1,$p to print out an editfile, because you will
+ be thrown out 'cause of too much text transfereed to you.
+
+ b) $: jump to end of file.
+
+ c) ?anything? and ?? : search from bottom to up. (like '/'
+ from beginning to end of file. (also with substitutions,
+ try out..)
+
+ d) ( g/xxx/p search global xxx and print corresponding lines,
+ /xxx/s/new/old/p : search xxx, substitute new to old in this
+ line and print out. (try this concatenations with other
+ commands)
+
+ e) 1,nmx ( see ed5 ) , but also: 1,ntx : don't move the lines,
+ but make a copy of them.
+
+ f) x,y w name : save lines x to y to file name (if you don't
+ know the line numbers : '=' current line number)
+
+ g) s/$/text/p : append text to the end of current LINE and
+ print line
+
+ h) s/^/text/p : insert text at beginning og current LINE and
+ print line
diff --git a/doc/LPC/efuns b/doc/LPC/efuns
new file mode 100644
index 0000000..ca5b224
--- /dev/null
+++ b/doc/LPC/efuns
@@ -0,0 +1,16 @@
+CONCEPT
+ efuns
+
+DESCRIPTION
+ Efuns are "system calls" in the LPC driver, the C program
+ which compiles and executes the LPC programs of the mudlib.
+ These are hardcoded functions which perform basic tasks which
+ would be ineffective or impossible to be implemented in the
+ mudlib.
+
+ There are efuns for accessing the underlying filesystem, for
+ creating, moving and destructing objects, for writing
+ messages to users, for manipulation of LPC data types.
+
+SEE ALSO
+ efun(E), lfuns(LPC), applied(A), master(M)
diff --git a/doc/LPC/escape b/doc/LPC/escape
new file mode 100644
index 0000000..8518749
--- /dev/null
+++ b/doc/LPC/escape
@@ -0,0 +1,81 @@
+CONCEPT
+ character escape codes
+
+DESCRIPTION
+ Character escape codes are used to represent some common
+ special characters that would be awkward or impossible to
+ enter in the source program directly. The escape characters
+ come in two varieties: 'character escapes', which can be
+ used to represent some particular formatting and special
+ characters, and 'numeric escapes', which allow a character to
+ be specified by its numeric encoding.
+
+ Escapes begin always with a backslash '\'. If the following
+ characters could not be treated as a valid escape the backslash
+ is merely ignored.
+
+ The following character escapes are available in LPC (the code
+ may differ from platform to platform):
+
+ \a Code 007 Bell
+ \b Code 008 Backspace
+ \e Code 027 Escape
+ \f Code 012 Formfeed
+ \n Code 010 Newline
+ \r Code 013 Carriage-Return
+ \t Code 009 Tabulator
+ \\ Code 092 Backslash itself
+ \' Code 039 Single quote
+ \" Code 034 Double quote
+
+ The single quote may appear without preceding backslash in character
+ constants, and the double quote may appear without a backslash in
+ string constants.
+
+ The numeric escapes could be used to express a character directly
+ by its code in binary, octal, decimal or hexadecimal notation.
+
+ \0b Beginning of binary notation
+ \0o Beginning of octal notation
+ \0x Beginning of hexadecimal notation
+ \x Beginning of hexadecimal notation
+
+ A backslash followed by a digit ([0-9]) which does not map to one
+ of the above starts an escape in decimal notation.
+
+ A numeric escape terminates when N digits have been used up or
+ when the first character that is not a valid digit in that
+ notation is encountered. N is 2 for hexadeximals, 3 for
+ decimals and octals and 8 for binarys.
+
+ If the specified code is greater than 255 a warning is issued and
+ the value modulo 256 is used.
+
+EXAMPLES
+ Put a newline at the end of user output
+ "You enter.\n"
+
+ Alert the user
+ "Beeep.\a Wake up\n"
+
+ Put a double quote in a string
+ "You say \"hello\"\n"
+
+ Write the line from above
+ "\"You say \\\"hello\\\"\\n\""
+
+ Put a single quote in a string
+ "You say 'hello'\n"
+
+ Some forms to write "abcde"
+ "abcde"
+ "ab\99de" (with c's code being 99)
+ "ab\099de"
+ "ab\x63de" (99 = 0x63)
+ "ab\0x63de"
+
+ The following string consists of two characters
+ "\0111" (\011 and 1)
+
+ The following string consists of three characters
+ "\0o090" (\000 and 9 and 0)
diff --git a/doc/LPC/for b/doc/LPC/for
new file mode 100644
index 0000000..e7a988d
--- /dev/null
+++ b/doc/LPC/for
@@ -0,0 +1,36 @@
+NAME
+ for
+
+SYNTAX
+ for(init; expr2; expr3) statement;
+
+DESCRIPTION
+ Execute <init> once. Then, while <expr2> returns a non-zero
+ value, execute <statement>. Every time <statement> has been
+ executed, or a 'continue' statement has been executed, execute
+ <expr3> before next loop.
+
+ <init> is usually a series of one or more expressions (remember
+ that assignments are expressions, too), separated by commas.
+ Additionally it is also allowed to define new local variables
+ here and assign them an initial value. The scope of such variables
+ is the whole for statement.
+
+ Examples for legal <init> expressions are:
+
+ for (i = 0; ...
+ for (i = 0, j = 0; ...
+ for (i = 0, int j = i; ...
+ for (int j = 4; ...
+
+ Illegal <init> expressions are:
+
+ for (int i; ... : no value assigned
+ for (int i += 4; ... : only plain assignments allowed
+
+ A 'break' in the 'statement' will terminate the loop. A
+ 'continue' will continue the execution from the beginning of
+ the loop.
+
+SEE ALSO
+ foreach(LPC), if(LPC), do-while(LPC), while(LPC), switch(LPC)
diff --git a/doc/LPC/foreach b/doc/LPC/foreach
new file mode 100644
index 0000000..7e6060b
--- /dev/null
+++ b/doc/LPC/foreach
@@ -0,0 +1,132 @@
+NAME
+ foreach
+
+SYNTAX
+ foreach (<var> : <expr>) <statement>;
+ foreach (<var>, <var2>, ... ,<varN> : <expr>) <statement>;
+
+ foreach (<var> : <expr1> .. <expr2>) <statement>;
+ foreach (<var>, <var2>, ... ,<varN> : <expr1>..<expr2> ) <statement>;
+
+ /* MudOS compatibility only - not for new code: */
+ foreach (<var> in <expr>) <statement>;
+ foreach (<var>, <var2>, ... ,<varN> in <expr>) <statement>;
+
+DESCRIPTION
+ The instruction evaluates its range specification - either a
+ simple <expr> which can yield an array, a struct, a string, a
+ mapping or an integer, or an integer range <expr1> through
+ <expr2> - and executes <statement> once for each value in the
+ range. The respective value is assigned to <var> right before
+ <statement> is executed.
+
+ A 'break' in the <statement> will terminate the loop. A
+ 'continue' will continue the execution from the beginning of
+ the loop.
+
+ Every <var> specification can declare a new local variable, whose
+ scope is the whole foreach() statement.
+
+
+ The normal form (one <expr>):
+
+ <expr> is evaluated and has to yield an array, a struct, a
+ string or a mapping (or reference to the former), or an
+ integer.
+
+ If <expr> is a array, struct, or string, the values of
+ <expr> (in case of the string, the integer values of the
+ characters) are then assigned one by one in order of
+ occurence to <var>, and <statement> is executed for every
+ assignment.
+
+ If <expr> is a mapping, the keys are assigned one by one
+ to <var>, and the values for each key are assigned in
+ order to <var2>..<varN>. If there are more values than
+ variable, the extraneous values are ignored. Due to the
+ nature of mappings, a specific order of the keys can not
+ be guaranteed.
+
+ If <expr> evaluates to a reference to an array, mapping, or
+ string, the loop will assign references to the values into
+ the variables. This allows the loop body to change the contents
+ of the original data.
+
+ If <expr> evalutes to an integer, the loop will count up <var>
+ from 0 to <expr>-1, basically implementing a count loop.
+
+ If there are more variables than necessary, the unneeded ones
+ are not changed.
+
+
+ The ranged form (<expr1> .. <expr2>):
+
+ <expr1> and <expr2> are evaluated and must yield integers.
+ The loop will count up <var> from <expr1> to <expr2>, basically
+ implementing a counted loop.
+
+ If <expr1> is less than <expr2>, the loop will terminate at once.
+
+ If there are more than variable, the unneeded ones are not
+ changed.
+
+
+
+ WHAT HAPPENS IF <expr> IS CHANGED IN THE LOOP?
+
+ If <expr> yields an array or struct:
+ - assignments to single elements or to array ranges effect
+ the values assigned to the variable:
+ a = ({1, 2, 3})
+ foreach(x : a) { a[1..2] = ({4, 5}); write(x+" "); }
+ will write ("1 4 5 ").
+ - operations which implicitely copy the array or struct (this
+ includes range assignments which change the size) don't
+ have an effect on the loop.
+
+ If <expr> yields a mapping, the loop will run over the indices
+ the mapping had at the begin of the loop. Deleted indices are silently
+ skipped, new indices ignored, but changes of the data of existing
+ indices are acknowledged.
+
+ If <expr> yields a string, the value used at the start of the loop
+ remains.
+
+
+WARNING
+ The additional syntax forms using "in" as keyword are meant
+ to make re-engineering of MudOS objects easier. Do not use them
+ for newly written code, as they may not be available in future.
+
+
+EXAMPLES
+ // Call quit() in all interactive users
+ foreach(o : users()) o->quit();
+ foreach(object o : users()) o->quit();
+
+ // Print the contents of a mapping <m>
+ foreach(key, value : m) printf("%O:%O\n", key, value);
+ foreach(mixed key, mixed value : m) printf("%O:%O\n", key, value);
+
+ // Don't change the content of a string: s remains "FOOBAR".
+ s = "FOOBAR";
+ foreach(i : s) i += 32;
+
+ // Do change the content of a string: s will become "foobar".
+ s = "FOOBAR";
+ foreach(i : &s) i += 32;
+
+ // Count from 0 to 5
+ foreach(i : 6) printf("%d\n", i);
+
+ // Count from 1 to 6
+ foreach(i : 1 .. 6) printf("%d\n", i);
+
+HISTORY
+ LDMud 3.3.44 introduced the use of references, the loop over
+ an integer expression, and the loop over an integer range.
+ LDMud 3.3.266 added support for structs.
+
+
+SEE ALSO
+ for(LPC)
diff --git a/doc/LPC/functions b/doc/LPC/functions
new file mode 100644
index 0000000..50c39a0
--- /dev/null
+++ b/doc/LPC/functions
@@ -0,0 +1,174 @@
+CONCEPT
+ functions
+
+DESCRIPTION
+ Functions are named blocks of code which are be called with
+ a number of argument values, and which return a result value
+ to the caller.
+
+ Functions are defined in an object and are also known as
+ "local funs" or short "lfuns".
+
+
+ DEFINING A FUNCTION
+
+ A function definition takes the form
+
+ <modifiers> <type> name ( <arguments> ) {
+ statements...
+ }
+
+ The parts in detail:
+ - <modifiers> can be any one of "static", "private", "public"
+ and "protected" (see modifiers(LPC)), optionally combined
+ with "varargs" (see varargs(LPC)) and/or "nomask".
+ If not specified, the function behaves as if it was
+ specified as "public", but this visibility can be restricted
+ in derived object through non-public inheritance.
+ - <type> is the type of the result returned by the function.
+ If specified as "void", the function is compiled to return
+ the value 0 under all circumstances. If not specified, the
+ type is assumed to be "mixed", furthermore typechecking is
+ disabled for this function.
+ - name is the name of the function, e.g. "short", or "Nice_Try",
+ under which it is made known.
+ - <arguments> is a list of variable definitions in the
+ normal '<type> <name>' style, separated by comma.
+ Examples: () : no argument taken
+ (int a) : takes on integer argument
+ (mixed a, object *b): takes two arguments, one
+ arbitrary type, one array of objects.
+ - { statements... } defines the code for this function. This
+ is a normal block (see block(LPC)) and as such can define
+ its own local variables.
+
+
+ DECLARING A FUNCTION
+
+ A function declaration makes the name and type of a function known
+ to the compiler with the assertion that the code for this function
+ will be provided "elsewhere".
+
+ The form is:
+
+ <modifiers> <type> name ( <arguments> );
+
+ Typical uses are:
+ - to declare in advance functions which are called before they
+ can be defined; for example if the create() function of an object
+ calls other functions which are defined after the create().
+ - to declare functions which will be provided by an inheriting
+ object.
+
+ Calling a declared but undefined function results in a runtime error.
+
+
+ CALLING A FUNCTION
+
+ Functions in other objects are called with the call_other() efun,
+ which can be shortened to '->':
+
+ ob->fun(a, b, c)
+ call_other(ob, "fun", a, b, c)
+
+ Note: See the entry H_DEFAULT_METHOD in hooks(C) for a modification
+ of this behaviour.
+
+ Functions in the same object are called just by writing their name,
+ followed by the arguments in parenthesis:
+
+ short()
+ compute(a)
+ do_that(a, "foo")
+
+ Array function arguments can be 'flattened' with the '...' operator.
+ For example:
+
+ mixed * m = ({ "foo", "bar" });
+ fun(m..., 1);
+
+ will be executed as:
+
+ fun("foo", "bar", 1);
+
+
+ If the number of values passed to the function does not match the
+ number of expected arguments (and if type checking is not enabled), the
+ driver will perform the necessary adaption at call time: excess
+ values are ignored, missing values are substituted by the number 0.
+ The values passed to the called function are massaged by the driver
+ to match the argument list
+
+
+ FUNCTIONS AND INHERITANCE
+
+ A "public" or "protected" (== "static") function defined in one
+ object is also visible in all inheriting objects. The exception from
+ this rule is when an inheriting child redefines ("overloads") the
+ inherited function with its own. When compiling with type checking,
+ the argument list of the redefined function has to match the
+ original one.
+
+ When a function is called, the driver looks for the function first
+ in the object called, and if not found there, then in the inherited
+ objects.
+
+ To explicitely call an inherited function (useful when a redefining
+ functions wants to use the original one), the "::" operator is used:
+
+ ::create()
+ ::compute(a)
+
+ The named function is searched only in the inherited objects, and
+ the first found is used.
+
+
+ If the function is inherited from several objects and a specific
+ one is to be called, the "::" can be extended to contain the
+ partial or full name of the inherited object:
+
+ inherit "/obj/cooker";
+ inherit "/obj/container";
+
+ tainer::create()
+ container::create()
+ "tainer"::create()
+ "container"::create()
+ "obj/container"::create()
+ "/obj/container"::create()
+
+ all call the create() in the container inherit. Note that the
+ name given to the :: operator is matched against the ends of
+ the inherited names.
+
+
+ One special form of this call is
+
+ efun::find_object()
+
+ which bypasses any redefinition of an efun (here find_object())
+ and directly calls the efun itself. This is only possible for
+ efun-redefinitions which do not use the "nomask" modifier.
+
+
+ Additionally, a call to a function inherited from several objects
+ can be instructed to call _all_ inherited functions through the
+ use of the wildcards "*" (match any number of arbitrary characters)
+ and "?" (match one arbitrary character):
+
+ inherit "/obj/cooker";
+ inherit "/obj/container";
+
+ "*"::create()
+ "co*"::create()
+ "*er"::create()
+
+ all call both inherited create()s. The function called this way
+ must not take arguments, and the single results from all calls are
+ combined into one array used as final result. If there is no such
+ function inherited at all, the statement will just return
+ an empty array.
+
+SEE ALSO
+ types(LPC), modifiers(LPC), varargs(LPC), references(LPC),
+ call_other(E), simul_efun(C), call_out(E)
diff --git a/doc/LPC/if b/doc/LPC/if
new file mode 100644
index 0000000..33bbff2
--- /dev/null
+++ b/doc/LPC/if
@@ -0,0 +1,39 @@
+NAME
+ if
+
+SYNTAX
+ if (expr1) statement1;
+ else if (expr2) statement2;
+ ...
+ else if (exprN) statementN;
+ else statementX;
+
+DESCRIPTION
+ The if() statement implements the conditional execution of statements.
+ The expressions 'expr1' .. 'exprN' are evaluate in the order they
+ appear until one of the expressions returns non-0 ('true'). At that
+ point, the statement associated with the expression is executed, and
+ the program continues after the if() statement. If none of the
+ expressions evaluate to 'true', the statementX in the 'else'-branch
+ is executed.
+
+ Both the 'else if' branches and the 'else' branch are optional, and
+ there can be any number of 'else if' branches - but there must be one
+ 'if' branch, and the branches must be in the order given above.
+
+ Any 'else' or 'else if' always relates to the immediately preceeding
+ 'if' resp. 'else if' conditional. This means that
+
+ if (a)
+ if (b) do_b;
+ else do_c;
+
+ is interpreted as
+
+ if (a) {
+ if (b) do_b;
+ else do_c;
+ }
+
+SEE ALSO
+ for(LPC), foreach(LPC), do-while(LPC), while(LPC), switch(LPC)
diff --git a/doc/LPC/inherit b/doc/LPC/inherit
new file mode 100644
index 0000000..0e4c0f2
--- /dev/null
+++ b/doc/LPC/inherit
@@ -0,0 +1,9 @@
+NAME
+ inherit
+
+DESCRIPTION
+ The concept of inheritance and the inherit statement are
+ explained in detail in the man page for ``inheritance''.
+
+SEE ALSO
+ inheritance(C)
diff --git a/doc/LPC/inheritance b/doc/LPC/inheritance
new file mode 100644
index 0000000..23fb804
--- /dev/null
+++ b/doc/LPC/inheritance
@@ -0,0 +1,181 @@
+KONZEPT
+ inheritance
+
+BESCHREIBUNG
+ Mittels Vererbung kann man das Verhalten und/oder die implementierten
+ Methoden eines Objektes in ein neues Objekt hineinerben.
+
+ 1. Wozu ist Vererbung gut
+ 1.1. Erben von Implementationen: Strukturieren von Bibliotheken
+ Mit Vererbung der Implementation koennen aufeinander aufbauende und gut
+ wart-/nutzbare Strukturen geschaffen werden:
+
+ /std/thing/description beinhaltet
+ - wie ein Objekt aussieht/welche Details es gibt
+ - wie man das Objekt finden/identifizieren kann
+ /std/thing/moving beinhaltet
+ - wie man ein Objekt bewegen kann
+ /std/thing erbt
+ - /std/thing/description und
+ - /std/thing/moving
+ - damit den Code aus beiden Objekten (und noch andere)
+
+ Sowohl thing/description als auch thing/moving sind nicht als
+ eigenstaendige Objekte sinnvoll nutzbar, sind abstrakt.
+
+ 1.2. Erben von Schnittstellen und Verhalten
+ Durch das Erben von /std/thing werden Schnittstelle und auch
+ Verhalten von /std/thing geerbt:
+
+ -- keks.c --
+ inherit "/std/thing";
+ --
+
+ Dadurch koennen jetzt andere Objekte, wie NPCs oder Spieler mit dem
+ Keks-Objekt so interagieren, als wenn es ein /std/thing waere.
+
+ Morgengrauen stellt eine grosse Bibliothek von miteinander sinnvoll
+ agierenden Objekten unter /std zur Verfuegung, Die dort verfuegbaren
+ Objekte sind groesstenteils selbsterklaerend, wie /std/npc,
+ /std/armour oder /std/weapon.
+
+ Das Keks-Objekt muss erweitert werden, wenn es sich vom normalen
+ Ding unterscheiden soll. Typischerweise geschieht das durch
+ Ueberschreiben der Initialisierungmethode namens "create".
+
+ 2. Ueberschreiben/Erweitern von Verhalten/Methoden
+ 2.1. Ueberschreiben
+ Um das Verhalten von geerbten Methoden zu erweitern, muss diese
+ ueberschrieben werden:
+
+ -- keks.c --
+ ...
+ void create() {
+ SetProp(P_NAME, "Keks");
+ SetProp(P_GENDER, MALE);
+ AddId(({"keks"}));
+ }
+ --
+
+ Allerdings wuerde jetzt der normale Code von "create" in /std/thing
+ nicht mehr ausgefuehrt werden. Mit dem 'scope'-Operator :: wird
+ innerhalb einer ueberschriebenen Methode auf deren bisherige
+ Implementation zugegriffen:
+
+ -- keks.c --
+ ...
+ void create() {
+ ::create();
+ SetProp(P_NAME, "Keks");
+ SetProp(P_GENDER, MALE);
+ AddId(({"keks"}));
+ }
+ --
+
+ Auf geerbte globale Variablen kann normal zugegriffen werden, wenn
+ diese nicht vor direktem Zugriff durch erbende Objekte geschuetzt
+ wurden. Also in ihrer Sichtbarkeit veraendert wurde.
+
+ Ueberschreiben von Methoden in den davon erbenden Objekten kann durch
+ das Schluesselwort 'nomask' verboten werden.
+
+ 2.2. Sichtbarkeit von Methoden und Variablen
+ Es ist moeglich, Methoden und Variablen mit einem Modifikator fuer
+ ihre Sichtbarkeit zu versehen, der auch bei der Vererbung zum
+ Tragen kommt:
+ - 'public' Methoden sind von aussen/innen in Eltern/Kind zugreifbar
+ - 'protected' Methoden sind nur von innen in Eltern/Kind zugreifbar
+ - 'private' Methoden sind nur von innen in Eltern zugreifbar
+ (also nur im definierenden Objekt selbst)
+
+ 2.3 Laufzeit-Polymorphie (vielgestaltes Verhalten)
+ Methoden werden in LPC immer erst zum Zeitpunkt ihres Aufrufs
+ gebunden, also nicht schon beim Laden. Beispielsweise koennen
+ wir unseren Keks essbar machen:
+
+ -- keks.c --
+ ...
+ void create() {
+ ...
+ AddCmd("iss&@ID", "action_essen", "Was willst du essen?");
+ }
+
+ int action_essen() {
+ if(this_player()->eat_food(1, 0,
+ "Du bekommst "+QueryPronoun(WEN)+
+ " nicht mehr hineingestopft.\n")>0) {
+ write("Du isst "+name(WEN,1)+".\n");
+ say(this_player()->Name(WER)+" isst "+name(WEN)+".\n");
+ remove();
+ }
+ return 1;
+ }
+ --
+
+ und jetzt in einem davon erbenden Kruemelkeks diesen beim Entfernen
+ im "remove" kruemeln lassen:
+
+ -- kruemelkeks.c --
+ inherit "/doc/beispiele/inherit/keks.c";
+ ...
+ varargs int remove(int silent) {
+ if(!silent && living(environment()))
+ tell_object(environment(), Name(WER,1)+
+ " kruemelt ein letztes Mal.\n");
+ return ::remove(silent);
+ }
+ --
+
+ Trotzdem wir "action_essen" nicht modifiziert haben, wird von dieser
+ Methode in einem Kruemelkeks immer automatisch die aktuelle und
+ kruemelende "remove" aufgerufen.
+
+ 3. Multiple Inheritance
+ In LPC ist multiple Vererbung moeglich. Das heisst, man kann von
+ mehreren Objekten erben. Das kann zu Konflikten fuehren, zum Beispiel
+ wenn eine Methode in zwei Objekten von denen geerbt wurde vorkommt.
+
+ Diese Konflikte sollte man dann "per Hand" loesen in dem man diese
+ spezielle Methode ueberschreibt und mittels des scope-Operators die
+ gewuenschte(n) geerbt(en) Methode(n) aufruft:
+
+ inherit "/std/weapon";
+ inherit "/std/lightsource";
+
+ void create() {
+ weapon::create();
+ lightsource::create();
+ ...
+ }
+
+ void init() {
+ weapon::init();
+ lightsource::init();
+ // oder sehr generell und unkontrolliert:
+ // "*"::init();
+ }
+
+ Schwerwiegende Konflikte koennen auftreten, wenn eine gleichnamige
+ Variable aus verschiedenen Objekten geerbt wird. Die Variablen
+ existieren im letztlich erbenden Objekt zweimal und die verschiedenen
+ geerbten Methoden greifen auf ihre jeweilige Variante zu.
+
+ Beispiel ist die sog. "Masche" oder der "Diamond of Death":
+
+ A (Ursprungsobjekt mit globaler Variable x)
+ / \
+ B C (B, C erben von A und manipulieren beide x)
+ \ /
+ D (D erbt von B und A)
+
+ Mit dem Schluesselwort 'virtual' kann die Doppelung beim Erben
+ in B und C unterbunden werden.
+
+SIEHE AUCH:
+ inherit
+ private, protected, public, nomask
+ virtual
+ objekte, oop
+ /doc/beispiele/inherit
+
+2.Feb 2008 Gloinson
diff --git a/doc/LPC/initialisation b/doc/LPC/initialisation
new file mode 100644
index 0000000..61f3c2b
--- /dev/null
+++ b/doc/LPC/initialisation
@@ -0,0 +1,93 @@
+CONCEPT
+
+ VARIABLE INITIALIZATION
+
+DESCRIPTION
+ Global variables, like their local counterparts, can be defined
+ with an initial value:
+
+ int * a = ({ 3, 4 });
+
+ The initialization value can be any legal LPC expression,
+ including function calls. The code for the initializations is
+ collected in a compiler-generated function __INIT() which is
+ called even before the create-hook is applied on the object.
+
+ During initialization, blueprints and clones are treated
+ slightly differently:
+
+ Blueprint variables are always all initialized using __INIT().
+
+ For clones the programmer can select whether the clone's
+ variables should also be initialized with __INIT(), or if they
+ should be assigned from the current blueprint values
+ ('shared with the blueprint'). The latter method is useful for
+ example if blueprints and clones shall share arrays or
+ mappings.
+
+ The selection is performed with the two pragmas
+ 'init_variables' and 'share_variables'. The status of this
+ pragma at the point of the first variable definition counts,
+ and is applied to all variables in the program.
+
+ The clone initialization method is evaluated per-program, i.e.
+ if an inherited program defines 'share_variables' and the child
+ program doesn't, only the inherited variables are initialized
+ from the blueprint, and all others from __INIT().
+
+ The default setting for the pragma is configured into the
+ driver, but can also be chosen when starting the driver.
+
+EXAMPLE
+ For the object
+
+ ----------
+ inherit "a";
+ int a = 4;
+ int b;
+ ----------
+
+ the compiler executes the equivalent of these __INIT() functions:
+
+ #pragma share_variables:
+
+ unknown __INIT()
+ {
+ "a"::__INIT();
+ if (clonep(this_object()))
+ {
+ a = (blueprint of this_object())->a;
+ }
+ else
+ {
+ a = 4;
+ }
+ return 1;
+ }
+
+
+ #pragma init_variables:
+
+ unknown __INIT()
+ {
+ "a"::__INIT();
+ a = 4;
+ return 1;
+ }
+
+
+ In either case the variable 'b' (in fact all variables) are
+ set to '0' as part of the loading/cloning process before the
+ driver performs the specific initialisation.
+
+WARNING
+ Do not call __INIT() yourself, overload, or use it directly in
+ any other way. The implementation of the variable
+ initialization may change at any time.
+
+HISTORY
+ Before LDMud 3.3.378, the choice between sharing and initializing
+ variables was a fixed configuration choice of the driver.
+
+SEE ALSO
+ pragma(LPC), create(H), invocation(D)
diff --git a/doc/LPC/inline-closures b/doc/LPC/inline-closures
new file mode 100644
index 0000000..3a2ecdd
--- /dev/null
+++ b/doc/LPC/inline-closures
@@ -0,0 +1,131 @@
+CONCEPT
+ inline closures
+
+SYNTAX
+ function <returntype> ( <arguments> ) : <context> { <code> }
+
+ (: <statements> ; <expr>, ... , <expr> :)
+
+
+DESCRIPTION
+ Inline closures are a way to program closures which are
+ compiled at the time an object is loaded, but can access
+ values from their enclosing function at runtime.
+
+ Example:
+
+ closure factory (int arg) {
+ return function int (int val) { return val * arg; };
+ }
+
+ closure f1 = factory(2);
+ closure f2 = factory(3);
+ funcall(f1, 3) -> will yield 6.
+ funcall(f2, 3) -> will yield 9.
+
+ The closure here 'inherits' the current value of the local
+ variable 'arg' at the time the closure is created. These
+ values are called the "context" of the closures - they are
+ stored in a special set of variables in the closure.
+
+ One specific feature of the closure context is that it can be
+ changed from within the closure, and that these changes remain
+ permanent:
+
+ closure factory (int arg) {
+ return function int (int val) { return val * arg++; };
+ }
+
+ closure f = factory(2);
+ funcall(f, 3) -> will yield 6.
+ funcall(f, 3) -> will now yield 9!
+
+ But changes of the closure context will not reflect on the
+ local variable it was copied from and vice versa.
+
+ In addition to the implicite context inherited from the
+ defining function, additional context variables can be defined
+ in the closure:
+
+ closure factory (int arg) {
+ return function int (int val) : int x = 2 * arg
+ { return val * x; };
+ }
+
+ closure f = factory(2);
+ funcall(f, 3) -> will yield 12.
+
+ It is possible to define multiple context variables with and
+ without initialisation:
+
+ closure factory (int arg) {
+ return function int (int val) : int y, x = 2 * arg;
+ int z
+ { return val * x; };
+ }
+
+ These explicite context variables are useful when the closures
+ needs to keep a state, or to improve performance:
+
+ mapping m = ...;
+ closure slow (int arg) {
+ return function mixed () { return m[arg]; }
+ }
+ closure fast (int arg) {
+ return function mixed () : mixed val = m[arg] { return val; }
+ }
+
+ In the above example, the fast() function executes the lookup
+ m[arg] only once when the inline closure is created; the
+ slow() function on the other hand returns a closures which
+ looks up m[arg] every time it is called. A second effect is
+ that the results of the slow closure change when m changes;
+ the result of the fast closure is always the same.
+
+
+ In the definition of an inline closure, some elements are
+ optional:
+
+ <returntype> defaults to 'mixed'
+ ( <arguments> ) defaults to '(mixed $1 ... mixed $9)'
+ : <context> no default
+
+
+ The special (: :) form is meant for simple expressions (and
+ MudOS compatibility). The form
+
+ (: <statements> ; <expr>, ..., <expr> :)
+
+ is the shorthand notation for
+
+ function { <statements>; return <expr>, ..., <expr>; }
+
+ For example the two statements
+
+ sort_array(arr, function { return $1 < $2; } )
+ sort_array(arr, (: $1 < $2 :) )
+
+ do the same. The example also demonstrates that both the <statements>
+ and the <expr> part in this form are optional.
+
+
+
+NOTES
+ The macro __LPC_INLINE_CLOSURES__ is defined when the
+ inline closures as described here are available. If not
+ defined, the driver implements a more restricted version
+ ('(: :)' syntax only, no context variables) for backwards
+ compatibility.
+
+ Inline closures are not to be confused with inline functions
+ known from other languages.
+
+HISTORY
+ LDMud 3.2.7 implemented the older, restricted form of inline
+ closures.
+ LDMud 3.3.271 implemented the full form of inline closures.
+ LDMud 3.3.275 re-allowed statements in the (: :) form.
+
+SEE ALSO
+ closures-abstract(LPC), closures-example(LPC), closure_guide(LPC)
+ closures(LPC)
diff --git a/doc/LPC/integers b/doc/LPC/integers
new file mode 100644
index 0000000..edbd848
--- /dev/null
+++ b/doc/LPC/integers
@@ -0,0 +1,40 @@
+NAME
+ integers
+
+DESCRIPTION
+ int is the fastest data type in LPC.
+
+ An integer contains a whole number between
+ -2147483648 and 2147483647.
+
+ Operations with integers:
+ + plus & AND << SHIFT right
+ - minus | OR >> SHIFT left
+ * multiply ^ XOR
+ / divide (whole) ~ NOT = Assign value
+ % divide (modulo) += add -= subtract
+
+ The result of a boolean expression is also an integer:
+ 0 (false)
+ 1 (true)
+
+ Boolean operators are:
+ ! (not)
+ == (equal)
+ || (or)
+ && (and)
+
+FUNCTIONS
+ int to_int(string)
+ int to_int(float)
+ int sscanf(string str, string fmt, mixed var1, mixed var2, ...)
+ void printf(string format, ...)
+
+REMARKS
+ Single characters in strings are also integers.
+
+SEE ALSO
+ arrays, mappings, operators, strings, types
+
+LAST CHANGED
+ Wednesday, 7th May, 2003, by Amaryllis
diff --git a/doc/LPC/lfuns b/doc/LPC/lfuns
new file mode 100644
index 0000000..a9c559d
--- /dev/null
+++ b/doc/LPC/lfuns
@@ -0,0 +1,31 @@
+CONCEPT
+ lfuns
+
+DESCRIPTION
+ A lfun is a LPC function within an object which is public and can
+ be called by other objects. In OO terms, lfuns are "methods"
+ which you can send "messages" to.
+
+ Calling lfuns is done by using the efun call_other(), which
+ takes as arguments the object in which the lfun is to be called,
+ the name of the lfun to be called in the object, and additional
+ and optional arguments.
+
+ An example looks like this:
+
+ call_other(drink, "QueryShort");
+
+ This call may also be written as
+
+ drink->QueryShort();
+
+ This means call_other(object, "function", args...) can also be
+ written as object->function(args...). The second form is
+ preferred as it is easier to read.
+
+ Some lfuns have a special meaning for the LPC driver, because
+ they are applied by the interpreter instead from an LPC object.
+ To distinguish those, they are called ``applied lfuns''.
+
+SEE ALSO
+ efuns(LPC), efun(E), applied(A), master(M), call_other(E)
diff --git a/doc/LPC/lpc b/doc/LPC/lpc
new file mode 100644
index 0000000..c03df0f
--- /dev/null
+++ b/doc/LPC/lpc
@@ -0,0 +1,9 @@
+NAME
+ lpc
+
+DESCRIPTION
+ This directory contains man pages about various aspects of the
+ LPC language as it is provided by Amylaars parser/driver.
+
+SEE ALSO
+ concepts(C), driver(D), efun(E), applied(A), master(M)
diff --git a/doc/LPC/mappings b/doc/LPC/mappings
new file mode 100644
index 0000000..381e3c8
--- /dev/null
+++ b/doc/LPC/mappings
@@ -0,0 +1,474 @@
+CONCEPT
+ mappings
+
+LAST UPDATE
+ Mon, 15 Mar 1999
+
+DESCRIPTION
+
+ A step-by-step introduction to mappings:
+ ----------------------------------------
+
+ 1. What is a mapping?
+
+ A mapping is a datatype which allows to store data associated to a key.
+ In other languages they are also known as 'dictionaries' or 'alists'.
+ There are also alists in LPC but they are not a separate datatype but are
+ implemented on top of arrays. Alists are the predecessors of mappings.
+ The keys and the values can be of any type. But most common datatypes
+ for keys are strings, integers and objects. Others like arrays, mappings
+ or closures aren't a good choice because comparision between i.e. arrays
+ often returns false even if they equal in content. This is because the
+ driver compares i.e. two arrays by their internal pointers and not by
+ their content. The reason for this is simple: speed.
+
+ Mappings are allways treated as references when passing them to
+ functions. This means when you pass a mapping to another object and this
+ object modifies the mapping the modification will take place in a global
+ scope - visible to all objects holding this mapping in a variable.
+
+
+ 2. What are mappings good for?
+
+ The term 'dictionary' probably describes the use of a mapping best.
+ Opposed to arrays mappings don't have a specific order. They provide a
+ mechanism to create a set of associations between values. Such an
+ association consists of a unique key and data that is identified by the
+ key. Think of a dictionary where you have a word and a definition of
+ it. You use the word to lookup its definition.
+
+ Mappings can be used i.e. to hold aliases for commands. The key would
+ then be the name of the alias and the data the command(s) behind an
+ alias. Or they can be used for the exits of a room. The keys would be
+ the directions where one can go to and the associated data would be the
+ file names of the rooms. But mappings can also be used as a kind of a
+ sparse array. A sparse array is an array where most of the elements
+ aren't used (occupied by 0). I.e. if you want to store values at the
+ position 0, 13 and 37642 of an array you would have to create an array
+ with a size of at least 37643. This costs a lot of memory so a mapping
+ would be more useful because you would then use the numbers 0, 13 and
+ 37642 as a key and not as an index to a position (actually the keys of a
+ mapping are sometimes called indices but this is just because the way
+ data is accessed in a mapping is similar to arrays: by the [] operator).
+ This also allows to query all occupied positions of a sparse array by
+ querying for all the keys of the mapping opposed to an array where you
+ have to iterate over all elements.
+
+
+ 3. How do I create a mapping?
+
+ There are several ways to do so. The most convenient is the following:
+
+ mapping map;
+ map = ([ key0: value00; ...; value0n,
+ ... : ... ; ...; ... ,
+ keyn: valuen0; ...; valuenn ]);
+
+ As you can see, a key may have more than one value assigned. But the
+ amount of values per key must always be equal. It is even possible to
+ have mappings without any values!
+ Another method is to use the efun mkmapping(). This efun gets two
+ arguments with the first beeing an array of keys and the following beeing
+ arrays of values:
+
+ mapping map;
+ map = mkmapping (({ key0 , ..., keyn }),
+ ({ value00, ..., value0n }),
+ ({ ... , ..., ... }),
+ ({ valuen0, ..., valuenn }));
+
+ If the efun only gets one argument, then this argument will be taken as
+ an array of keys and a mapping without values will be returned.
+
+ An empty mapping can be created by using the above described methods by
+ simply ommitting the keys and values:
+
+ mapping map;
+ map = ([]);
+ or:
+ map = mkmapping(({}), ({}));
+
+ Or by using the efun m_allocate(). This efun gets as first
+ argument the amount of keys which will be added soon and an optional
+ second argument specifying the width of the mapping:
+
+ map = m_allocate(n, width);
+
+ The value <n> may be a bit confusing since mappings shrink and grow
+ dynamically. This value just tells the driver how 'long' this mapping is
+ going to be so proper memory allocations will be performed to reduce
+ the overhead of memory reallocation. I.e. if you want to read in a file
+ and store the read data in a mapping you probably know the amount of
+ keys. So you allocate a mapping with this efun and tell the driver how
+ much memory should be allocated by specifing a proper <n> value.
+ Thus causing a speedup when adding the read data to the mapping
+ afterwards. The <width> just specifies how many values per key this
+ mapping is going to have. If no width is given, 1 will be taken as
+ default.
+
+ An empty mapping created with '([])' will always have a width of 1. To
+ create empty mappings with other widths, write it as
+
+ map = ([:width ]);
+
+ <width> can be any expression returning an integer value (including
+ function calls), and in fact this notation is just a fancy way of
+ writing
+
+ map = m_allocate(0, width);
+
+
+
+ 4. How can I modify the data of a mapping?
+
+ Adding a new key is similiar to modifying the associated data of an
+ existing key:
+
+ map += ([ key: value0; ...; valuen ]);
+
+ Or in case only a single value should be modified:
+
+ map[key, n] = valuen;
+
+ If <n> is out of range or if <key> doesn't exists and <n> is greater
+ than 0 an "Illegal index" error will be reported. If <n> is equal to 0 or
+ the mapping only has a single value per key one can abbreviate it with:
+
+ map[key] = value;
+
+ If there is no <key> (and <n> is equal to 0 or not specified at all) a
+ new one will be added automatically.
+
+ Deletion of a key is done with the -= operator or the efun
+ m_delete(). A mapping can only be substracted by one without any values:
+
+ map -= ([ key ]);
+ or:
+ map -= ([ key0, ..., keyn ]);
+
+ The efun takes a mapping as first and a key as second argument:
+
+ m_delete(map, key);
+
+ The efun m_delete() returns the mapping but because mappings are
+ handled as references there is no need of an assignment like:
+
+ map = m_delete(map, key);
+
+
+ 5. How can I access the data stored in a mapping?
+
+ This can be done by:
+
+ valuen = map[key, n];
+
+ Or in case of a mapping with just one value per key:
+
+ value0 = map[key];
+
+ If there is no <key> in the mapping and <n> is 0 or not specified at
+ all (which is the same) a 0 will be returned or if <n> is greater than 0
+ an "Illegal index" error will be reported.
+
+
+ 6. How can I test for the existance of a key?
+
+ A return value of 0 is sufficient for most applications but sometimes
+ the ambiguity between an existing value of 0 and a nonexisting key can
+ lead to a problem. Therefore one can use the efun member() or
+ mapping_contains() to check if there actually is a key in the mapping:
+
+ if (member(map, key)) {
+ ...
+ }
+ or:
+ if (mapping_contains(&value0, ..., &valuen, map, key)) {
+ ...
+ }
+
+ This also shows how one can retrieve all values associated to a key
+ from a mapping in a single step. The '&' is the reference operator which
+ is neccesary to let the efun store the values in the variables.
+
+ In case of mappings with no values, the efun member() and
+ mapping_contains() are equal in their behaviour and their way of calling
+ because mapping_contains() won't get any reference variables to store the
+ values in (obviously, because there aren't any).
+
+ Also normally member() is known to return the postion of an element in
+ a list (i.e. a character in a string or data in an array) and if an
+ element couldn't be found -1 is returned. But in the case of mappings
+ there are no such things as order and postion. So member() only returns 0
+ or 1.
+
+
+ 7. How can I copy a mapping?
+
+ A mapping can be copied with the + operator or by the efun
+ copy_mapping():
+
+ newmap = ([]) + map;
+ or:
+ newmap = copy_mapping(map);
+
+ A mapping should only be copied when it is neccesary to get an own copy
+ of it that must not be shared by other objects.
+
+
+ 8. How can I get all keys of a mapping?
+
+ The efun m_indices() gets a mapping as argument and returns an array
+ holding all keys defined in this mapping:
+
+ keys = m_indices(map);
+
+
+ 9. How can I get all the values of a mapping?
+
+ The efun m_values() gets a mapping as argument and returns an array
+ holding all the first (second, ...) values of it.
+
+ values0 = m_values(map); returns the first values
+ values0 = m_values(map, 0); dito
+ values1 = m_values(map, 1); returns the second values
+ etc
+
+
+ 10. How can I determine the size of a mapping?
+
+ Because a mapping is a kind of rectangle it has two sizes: a length and
+ a width. There are three different efuns to query these values. The first
+ two are the efuns sizeof(), which returns the amount of key-value
+ associations (the length of a mapping), and widthof(), which returns the
+ number of values per key (the width). The third is the efun get_type_info().
+ get_type_info() is meant to be a function to identify a datatype. Its
+ return value is an array of two numerical values. The first specifies
+ the datatype of the argument and the second is a datatype dependend
+ value. In the case of a mapping the first value is T_MAPPING (which is a
+ value defined in <lpctypes.h>) and the second the amount of values per
+ key (a.k.a. columns or the width of the mapping - actually it would be
+ correct to say that the width of a mapping is the amount of columns plus
+ one for the keys but this is uncommon).
+
+
+ 11. What is the best method to iterate over a mapping?
+
+ First of all the main purpose of a mapping is not meant to be a set of
+ data to iterate over. Afterall the keys in a mapping have no specific but
+ a random order (at least on the LPC side). But still it is possible and
+ sometimes even neccesary to do so.
+
+ If all key-value associations should be processed then one should use
+ walk_mapping(). If all keys of a mapping should be processed to create a
+ new mapping being a subset of the given one, then filter_mapping() should
+ be used. If all keys are going to be processed and to create a new
+ mapping with the same set of keys as the given mapping, then one would
+ use map_mapping(). But in the case of an iteration that should/can stop
+ even if not all data is processed it is probably wise to iterate over the
+ mapping by first querying for the keys and then to iterate over them with
+ a for() or a while() loop and querying the values by 'hand'.
+
+ The efun walk_mapping() gets a mapping as first argument and the name
+ of a function as second one. All the following arguments are treated as
+ extras which will be passed to the function specified with the 2nd
+ argument. Instead of a string for the name of a function a closure can be
+ used, too. Nothing will be returned:
+
+ ...
+ walk_mapping(map, "func", xarg0, ..., xargn);
+ ...
+
+ void func(mixed key, mixed value0, ..., mixed valuen,
+ mixed xarg0, ..., mixed xargn) {
+ ...
+ }
+
+ func() will be called for all key-value associations and gets as first
+ argument the key. The next arguments are the values behind the key and
+ are passed as references. The rest of the passed arguments are those
+ specified as extras. Because the values are passed as references (opposed
+ to copies) it is possible to modify them from inside func() by simply
+ assigning new value to the variables <value0>, ..., <valuen>.
+
+ The efun filter_mapping() calls a function for each key in a mapping
+ and creates a new mapping which only contains key-value associations for
+ which the called function returned true (not equal 0 that is). The first
+ argument is the mapping to iterate over and the second is a function name
+ given as a string or a closure:
+
+ ...
+ submap = filter_mapping(map, "func", xarg0, ..., xargn);
+ ...
+
+ int func(mixed key, mixed xarg0, ..., mixed xargn) {
+ ...
+ }
+
+ func() gets as first argument the key and the others are those passed
+ as extras to filter_mapping().
+
+ The efun map_mapping() gets a mapping as first argument and a string as
+ a function name (or again a closure) as second argument. Any additional
+ arguments are again used as extras that will be passed to the iteration
+ function. This efun returns a new mapping with the same keys as the given
+ one. The values returned by the function that is invoked for each key
+ will be used as the associated data behind each key of the new mapping:
+
+ ...
+ newmap = map_mapping(map, "func", xarg0, ..., xargn);
+ ...
+
+ mixed func(mixed key, mixed xarg0, ..., mixed xargn) {
+ ...
+ }
+
+ func() gets as first argument the key and the others are those passed
+ as extras to map_mapping().
+
+ Because a function can only return a single value (even when it is an
+ array) it restricts the use of map_mapping() to only allow creation of
+ mappings with a single value per key.
+
+
+ 12. Is it possible to join/intersect/cut mappings with another?
+
+ Joining mappings is only possible, if they have the same width (amount
+ of values per key). One can use the + and += operator:
+
+ map = map1 + map2 + ... + mapn;
+ map += map1 + map2 + ... + mapn;
+
+ Intersection of two mappings is only possible by using
+ filter_mapping(). There is no efun or operator which features this. The
+ 'easiest' way may be the following function:
+
+ mapping intersect_mapping(mapping map1, mapping map2) {
+ closure cl;
+
+ cl = lambda(({ 'key }), ({ #'member, map2, 'key }));
+ return filter_mapping(map1, cl, map2);
+ }
+
+ This function returns a new mapping which consists of all key-value
+ associations of <map1> for which an equal key could be found in
+ <map2>. This function uses a closure which returns 0 or 1 depending on
+ wether a key from <map1> is contained in <map2> or not.
+
+ Cutting out all key-value associations of a mapping for which a key
+ could be found in another mapping can be done by using the - and -=
+ operator:
+
+ mapping cut_mapping(mapping map1, mapping map2) {
+ return map1 - mkmapping(m_indices(map2));
+ }
+
+ Because a maping can only be substracted by one without any values we
+ first have to create such by using m_indices() and mkmapping().
+
+
+ 13. What are those mappings without any values (besides keys) good for?
+
+ Because the way how the driver searches for a key in a mapping is
+ rather fast, those mappings can be used as a set of elements with a fast
+ method for testing if an element is contained in the set. This technique
+ is called hashing (further explanation would lead too far) which is
+ faster than searching for values in array (which is done in a linear
+ fashion).
+
+ Another (maybe more pratical) use of these mappings are to create a
+ array of unique values out of an array with several equal values:
+
+ uniques = m_indices(mkmapping(array));
+
+ mkmapping() uses <array> to create a mapping without any values but
+ just keys. And because a mapping can only have unique keys all multiple
+ values in <array> are taken as one. The call of m_indices() then returns
+ an array of these unique keys. Actually we only make use of those
+ mappings temporarily.
+
+
+ 14. How can I convert an alist into a mapping and vice versa?
+
+ There are no special efuns which handle such conversions. But it can be
+ done by the following functions:
+
+ mapping alist_to_mapping(mixed *alist) {
+ return apply(#'mkmapping, alist);
+ }
+
+ The efun apply() takes a closure and an array of values and passes each
+ element of the array as an argument to the closure. Because an alist
+ consists of an array of arrays with the first beeing the list of keys and
+ the others the values associated to each key passing them as arguments to
+ the efun closure #'mkmapping via apply() causes the creation of a mapping
+ out of an alist.
+
+ mixed *mapping_to_alist(mapping map) {
+ mixed *alist;
+ symbol *vars;
+ string var;
+ closure cl;
+ int width;
+
+ width = get_type_info(map)[1];
+ alist = allocate(width + 1);
+ vars = allocate(width + 2);
+ for (var = "a"; width; var[0]++, width--) {
+ alist[width] = ({});
+ vars[width] = quote(var);
+ }
+ alist[0] = ({});
+ vars[0] = 'key;
+ vars[<1] = 'alist;
+ cl = lambda(vars, ({ #'=, 'alist, ({ #'insert_alist }) + vars }));
+ walk_mapping(map, cl, &alist);
+ return alist;
+ }
+
+ This function is a bit more complicated than the other and detailed
+ description would lead too far of the topic. This function has one
+ restriction: it can only turn a mappings with up to 26 values per key
+ into an alist. But this should be sufficient for probably all
+ applications which use mappings.
+
+ And Hyps further comment on this:
+ The function mapping_to_alist() is also not that
+ clever because insert_alist() allways creates a new
+ alist. A second (optional) argument to m_values() to
+ specify the value column would be better. Besides
+ this, the conversion of a mapping into an alist could
+ be done by to_array().
+
+ 15. Dirty Mappings
+
+ 'Dirty mappings' are nothing the LPC programmer directly is involved
+ with, however, as it relates to the way mappings are implemented
+ internally by the gamedriver. However, as this term shows up in
+ various driver statistics, it is explained here.
+
+ There are two fundamental approaches to implement mappings:
+
+ 1. Store all data entries in an array-like structure, in sorted order.
+ 2. Store all data in a hashtable, each entry allocaed separately.
+
+ Method 1 is very space efficient, as it doesn't need much overhead
+ per entry; however, insertions and deletions of entries are
+ relatively slow as all other entries need to be moved.
+ Method 2 is very fast as nothing needs to be moved in memory,
+ however it has a large overhead.
+
+ The gamedriver uses a hybrid method: at the basis is a mapping
+ implementation based on arrays. However the driver uses a hash table
+ in addition to handle all the ongoing insertions and deletions.
+ Every once in a while, the contents of the hash table are sorted
+ into the base array, reasoning that any entry surviving for longer
+ time in the hash table is worth keeping in a more space-efficient
+ manner. 'Dirty' mappings are such mappings with both an array and a
+ hash part, 'clean' mappings are those with just an array part.
+
+HISTORY
+ The ([:width ]) notation was added in LDMud 3.2.9/3.3.208 .
+
+SEE ALSO
+ alists(LPC), closures(LPC), structs(LPC), mkmapping(E),
+ walk_mapping(E)
diff --git a/doc/LPC/modifiers b/doc/LPC/modifiers
new file mode 100644
index 0000000..a30904e
--- /dev/null
+++ b/doc/LPC/modifiers
@@ -0,0 +1,182 @@
+CONCEPT
+ modifiers
+
+DESCRIPTION
+ A modifier changes the syntactic and/or semantic behaviour of
+ an object-global variable or a function in an object.
+ The existing modifiers are described below.
+ To use a modifier just prepend it to the declaration. If several
+ modifiers are to be used their order does not matter:
+
+ private int bar; // example for a variable
+ protected nomask int foo() { return 3; } // example for a function
+
+ For functions:
+ ~~~~~~~~~~~~~~
+ private -- such functions can only be called with an internal
+ call from within this file. Not even inheriting
+ objects can call these functions. You can nevertheless
+ build an lfun-closure with #' out of a private function
+ (but you cannot save and restore it).
+ protected -- such functions can be called from within the object,
+ or from inheriting objects; but in neither case
+ with call_other(). It is possible to create #' closures
+ or use symbol_function() from within the object.
+ Its use is preferred over the older "static".
+ static -- such functions can be called from within the object
+ in either way (internal call or with call_other()).
+ Inheriting objects can call such functions.
+ But it is not possible to call static functions from
+ other objects via call_other().
+ The use of 'static' in new code is not recommended.
+ Note that an add_action() is treated like a call
+ from within the object except the player who got the
+ add_action() was forced (thus it is a simple way to
+ secure an add_action() against forces, although this
+ method has the severe disadvantages of raising an error
+ at the force so better use the security system).
+ Also efuns like call_out() or input_to() can call
+ these functions if given as a string.
+ public -- this is the default type. Such functions can be called
+ from within the file as well as from inheriting objects
+ and other objects via call_other().
+ To declare a function public only results in the
+ impossibility to change the accessibility at the
+ inherit statement (see below). No error will occur,
+ only the type will not be modified by the inherit
+ statement.
+ nomask -- such functions cannot be overridden by inheriting
+ objects. If you have the fun foo() defined in your
+ object and inherit an object which also has declared
+ a function foo() and this nomask, you will get an
+ compile error if you try to load your object.
+ Furthermore a shadow will fail if it tries to shadow
+ a nomask declared function.
+ varargs -- this changes the syntax of the function in a way that
+ not all of the arguments in the declaration must be
+ given at the call. This is often very usefull if some
+ of the arguments shall be omitable (the omitted
+ arguments are set to 0 if the function is called with
+ fewer arguments than specified).
+ This is mainly within the object really necessary;
+ call_other()s usually (that is if they do not have a
+ certain pragma ('man pragma')) do not need the called
+ function to be declared varargs to omit any arguments,
+ but it is good style to use this modifier to document
+ the code by this.
+ deprecated - Whenever this function is called, a warning is issued.
+ Usually this is done at compile-time. Exceptions are
+ call_others and symbol_function() which warn at run-time.
+
+ For object-global variables:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ private -- such variables can only be accessed from within the
+ same object. Not even inheriting objects can access
+ private variables.
+ It is a good style to declare all internal variables
+ private to prevent inheriting objects from accessing
+ the variables directly without using functions.
+ nosave -- such variables are neither stored with save_object()
+ nor restored with restore_object(). This can be very
+ useful if you want a room to use save_object() and
+ restore_object() to save your own defined variables
+ but not the hundreds of variables inherited from a
+ room-class (e.g. /complex/room). You then use the modifier
+ at the inherit statement (see below).
+ Note that nosave and private do not overlap in any
+ way. They are absolutely independant.
+ static -- the old name for 'nosave'. Its use is deprecated.
+ public -- declares the variable public. It cannot be declared
+ private or static by inheriting. No error will occur,
+ only the type will not be modified by the inherit
+ statement.
+ deprecated - Whenever this variable is used, a warning is issue.
+ Usually this is done at compile-time, but
+ symbol_variable() warns at run-time.
+
+ It is no good style to let inheriting objects have access to
+ internal variables so declare them as private and offer functions
+ to query and change the variables if possible.
+
+ It is also possible to redeclare all variables and/or functions
+ of an inherited object for the own object at the inheriting
+ statement:
+
+ private functions nosave variables inherit "complex/room";
+ public variables inherit "complex/room";
+ private functions inherit "complex/room";
+
+ To redeclare a function or a variable declared public in the
+ inherited object to be private or protected is not possible.
+
+ There also exists a modifier explicitly for the inherit statement:
+
+ virtual -- inherits the given object virtually. This only makes
+ sense in a complex inherit tree.
+ If an object is inherited normally (not virtually)
+ twice somewhere in the inherit tree the intern
+ variables exist twice. If inherited virtually they
+ exist only once.
+ Example:
+ A inherits B and C.
+ B inherits D.
+ C inherits D.
+ If the inheritance of D is virtual in B and C
+ D's variables exist only once in A. If A changes
+ D's variables via functions of B this also changes
+ the variables of D as known by C.
+
+ virtual: non-virtual:
+ A A
+ / \ / \
+ B C B C
+ \ / | |
+ D D D
+
+
+ To simplify the adoption of existing code, LPC allows to specify
+ a default visibility for functions and variables, using a syntax
+ similar to the inherit syntax:
+
+ default private;
+
+ All variables and functions are by default private.
+
+ default private variables public functions;
+
+ All variables are by default private, but functions are public.
+
+ Only the modifiers 'private', 'public' and 'protected' (and 'static'
+ for functions only) are allowed here.
+
+ The default visibility thus set affects only variables/functions with
+ no explicite visibility:
+
+ default private;
+
+ int private_var;
+ public int public_var;
+
+ The definition is valid from the point of the 'default' statement
+ until the end of the file, or until the next 'default' statement:
+
+ default private;
+
+ int private_var;
+
+ default public;
+
+ int public_var;
+
+ Note that this default visibility does not affect inherits.
+
+
+HISTORY
+ The modifier 'static' for variables was renamed to 'nosave'
+ with LDMud 3.2.8. 'static' is still recognized as an alias.
+
+ The default visibility was added in LDMud 3.2.9 as experimental
+ feature.
+
+SEE ALSO
+ closures(LPC), inheritance(LPC), functions(LPC), types(LPC)
diff --git a/doc/LPC/operators b/doc/LPC/operators
new file mode 100644
index 0000000..18406a3
--- /dev/null
+++ b/doc/LPC/operators
@@ -0,0 +1,213 @@
+NAME
+ operators
+
+DESCRIPTION
+
+ These are the operators availailable in LPC. They are listed
+ in the order of precedence (low priority first):
+
+
+ expr1 , expr2 Evaluate 'expr1' and then 'expr2'. The
+ returned value is the result of 'expr2'. The
+ returned value of 'expr1' is thrown away.
+
+ var = expr Evaluate 'expr', and assign the value to
+ 'var'. The new value of 'var' is the result.
+
+ var += expr Assign the value of 'expr' + 'var' to 'var'.
+ This is mostly equivalent to "var = var + expr".
+
+ var -= expr Similar to '+=' above.
+ var &= expr
+ var |= expr
+ var ^= expr
+ var <<= expr
+ var >>= expr
+ var >>>= expr
+ var *= expr
+ var %= expr
+ var /= expr
+ var &&= expr
+ var ||= expr
+
+ expr1 ? expr2 : expr3
+ Evaluates 'expr1' and branches according to
+ its truth value. If it is true, the 'expr2' is
+ evaluated and returned as result, else
+ 'expr3'.
+
+ expr1 || expr2 The result is true if 'expr1' or 'expr2' is
+ true. 'expr2' is not evaluated if 'expr1' was
+ true.
+
+ expr1 && expr2 The result is true i 'expr1' and 'expr2' is
+ true. 'expr2' is not evaluated if 'expr1' was
+ false.
+
+ expr1 | expr2 The result is the bitwise or of 'expr1' and
+ 'expr2'.
+ For arrays, the union set is computed: all elements
+ from <expr1> plus all those from <expr2> which
+ are not in <expr1>.
+
+ expr1 ^ expr2 The result is the bitwise xor of 'expr1' and
+ 'expr2'.
+ For arrays, the symmetric difference is computed:
+ all elements from <expr1> which are not in <expr2>,
+ plus all those from <expr2> which are not in <expr1>.
+
+ expr1 & expr2 The result is the bitwise and of 'expr1' and
+ 'expr2'.
+
+ For arrays and strings, the intersection set
+ (all elements resp. characters from expr1 which
+ which are also in the expr2) is computed.
+ Note: "aab" & "a" -> "aa"
+ but ({ 'a','a','b' }) & ({ 'a' }) -> ({ 'a' })
+ Eventually the array behaviour will be changed
+ to match the string behaviour.
+
+ Intersecting an array with a mapping is equivalent
+ to intersecting the array with the indices of the
+ mapping: array & mapping = array & m_indices(mapping)
+
+ Mappings can be intersected with another mapping
+ or an array. The resulting mapping holds all
+ those entries from the first mapping, which are
+ also mentioned in the second mapping (as index)
+ resp. in the array.
+
+ expr1 == expr2 Compare values. Valid for strings, numbers,
+ objects and closures.
+
+ expr1 != expr1 Compare values. Valid for strings, numbers,
+ objects and closures.
+
+ expr1 > expr2 Valid for strings and numbers.
+
+ expr1 >= expr2 Valid for strings and numbers.
+
+ expr1 < expr2 Valid for strings and numbers.
+
+ expr1 <= expr2 Valid for strings and numbers.
+
+ expr1 << expr2 Shift 'expr1' left by 'expr2' bits; the sign
+ bit is not preserved.
+
+ expr1 >> expr2 Shift 'expr1' right by 'expr2' bits.
+ This shift preserves the sign of 'expr1'.
+
+ expr1 >>> expr2 Shift 'expr1' right by 'expr2' bits.
+ This shift does not preserve the sign of 'expr1',
+ instead it shifts in 0 bits.
+
+ expr1 + expr2 Add 'expr1' and 'expr2'. If numbers, then
+ arithmetic addition is used. If one of the
+ expressions are a string, then that string is
+ concatenated with the other value.
+ If the expressions are arrays, the result is
+ the right array appended to the left.
+ If the expressions are mappings of equal width,
+ the result is merger of the two mappings. If one
+ key exists in both mappings, the element from the
+ right mapping appears in the result. If the two
+ mappings are of different width, the result is
+ <expr1> if non-empty, and <expr2> otherwise.
+
+ expr1 - expr2 Subtract 'expr2' from 'expr1'. Valid for
+ numbers, strings, arrays, mappings.
+ For arrays and strings, all occurences of the
+ elements resp. characters in 'expr2' are removed
+ from 'expr1', and the result is returned.
+ For mapping, all occurances of elemens in 'expr1'
+ which have a matching key in 'expr2' are removed, and
+ the result is returned.
+
+ expr1 * expr2 Multiply 'expr1' with 'expr2'.
+ If strings or arrays are multiplied with a number
+ (zero or positive), the result is a repetition of the
+ original string or array.
+
+ expr1 % expr2 The modulo operator of numeric arguments.
+
+ expr1 / expr2 Integer division.
+
+ ++ var Increment the value of variable 'var', and
+ return the new value.
+
+ -- var Decrement the value of variable 'var', and
+ return the new value.
+
+ - var Compute the negative value of 'var'.
+
+ ! var Compute the logical 'not' of an integer.
+
+ ~ var The boolean 'not' of an integer.
+
+ ( type ) var Return the value of <var> converted to <type>.
+ <type> can be 'string', 'int', 'object', 'float'
+ or 'int*'. <var> must be of a specific type
+ for a conversion to take place; if <var> is 'mixed'
+ or unknown, the cast is purely declarative.
+ Also, if the declared type of <var> is that of <type>,
+ the value is not changed.
+
+ NB. The literal number 0 is of unknown type, as
+ it doubles as 'not initialized' for strings, objects,
+ and arrays.
+
+ The operator acts like the efuns
+ to_string(), to_int(), to_object(), to_float()
+ and to_array(). It is advisable to use the
+ efuns directly instead of the cast.
+
+ ({ type }) var <var> is now assumed to have the type <type>.
+ This is purely declarative, the actual value
+ of <var> is not changed.
+
+ var ++ Increment the value of variable 'var', and
+ return the old value.
+
+ var -- Decrement the value of variable 'var', and
+ return the old value.
+
+ expr1[expr2] The array or mapping given by 'expr1' is
+ indexed by 'expr2'.
+
+ expr1[expr2..expr3] Extracts a
+ piece from an array or string.
+ expr2 or expr3 may be omitted, default is the begin
+ or end of expr1.
+ Negative numbers for expr2 or expr3
+ mean ``count from before the beginning'', i.e.
+ foo[-2..-1] is an empty array or string.
+ foo[<2..<1] gives the 2nd and last element of
+ the array resp. chars of the string.
+
+ expr1->name(...) The symbolic form of call_other(). 'expr1'
+ gives either an object or a string which is
+ used as the file_name of an object, and calls
+ the function 'name' in this object.
+
+ ident::name(...)
+ Call the inherited function 'name' with the
+ given parameters in the parent 'ident'.
+ 'ident' may be given as string containing the
+ full pathname, or as identifier containing the
+ pure basename.
+ If 'ident' is omitted, the last inherited
+ function of this 'name' is called.
+
+ ({ }) Array constructor.
+ ([ ]) Mapping constructor.
+
+NOTE
+ The closure operators are not described here.
+
+HISTORY
+ LDMud 3.2.9 added '>>>', '>>>=', '&&=' and '||='.
+ LDMud 3.2.10 extended '&' to mappings.
+ LDMud 3.3 extended '|' and '^' to arrays.
+
+SEE ALSO
+ arrays(LPC), alists(LPC), mappings(LPC), closures(LPC)
diff --git a/doc/LPC/pragma b/doc/LPC/pragma
new file mode 100644
index 0000000..d26f326
--- /dev/null
+++ b/doc/LPC/pragma
@@ -0,0 +1,116 @@
+NAME
+ pragma
+
+DESCRIPTION
+ The preprocessor directive #pragma can be used to select
+ several compilation options. Multiple options can be selected
+ in one #pragma directive by separating them with commas.
+
+ no_clone: The blueprint object can't be cloned.
+ no_inherit: The program can't be inherited.
+ no_shadow: The program can't be shadowed (similar to declaring
+ all functions as 'nomask').
+
+ init_variables: Clone variables are initialized by __INIT().
+ share_variables: Clone variables are initialized from the
+ blueprint.
+
+ weak_types: no type checking (this is the default).
+ strict_types: all functions must be declared with argument
+ prototypes, and the return values of call_other() must
+ be casted.
+ strong_types: all functions must be declared with complete
+ types of return value and parameters.
+ save_types: the declaration data is kept after compilation and
+ checked at runtime. This is important for type-safe
+ inheritance.
+
+ rtt_checks: runtime checks during execution of this program will be
+ enabled. The interpreter will check for correct datatypes of
+ arguments on function calls. (Later it will include checks
+ upon assignments.)
+ Don't confuse this with strong/strict_types, they only
+ check at compile time.
+ strong_types/strict_types is seriously recommended.
+ This pragma implicitly enables save_types as well.
+ no_rtt_checks: disable runtime type checks for this program (default).
+
+ pedantic: Certain warnings are treated as errors:
+ - failure to pass enough arguments to simul efuns
+ sloppy: Turns off pedantic (the default).
+
+ range_check: Use of questionable ranges (ranges of negative sizes,
+ or with bounds beyond the array's size) cause a runtime
+ warning.
+ no_range_check: Turns off range_check (the default).
+
+ warn_deprecated: Use of deprecated efuns or indexing operations
+ causes the compiler to issue a warning (the default).
+ no_warn_deprecated: Turns off warn_deprecated.
+
+ warn_empty_casts: A cast of a value to its own type generates
+ a warning (the default). Exception are casts to type
+ 'mixed'.
+ no_warn_empty_casts: Turns off warn_empty_casts.
+
+ warn_missing_return: Warn if a value-returning function is missing
+ a return statement (the default). If possible, the driver
+ will try to detect this at compile time; otherwise a runtime
+ warning will be generated when the function is executed.
+ The check applies only to functions with a declared return
+ type other than 'void'.
+ no_warn_missing_return: Turn off warn_missing_return.
+
+ warn_function_inconsistent: If an inherited function is
+ overloaded with inconsistent return types or arguments,
+ a warning is generated; or if pragma_pedantic is in effect,
+ an error. By default this is active.
+ no_warn_function_inconsistent: An inherited function can
+ be overloaded with inconsistent return types or arguments,
+ as long as pragma_pedantic is not in effect.
+
+ This pragma is meant to easen the adaption of legacy
+ mudlib code - in general one should fix the warnings,
+ not turn them off.
+
+ When an object is compiled with type testing (#pragma
+ strict_types), all types are saved of the arguments for that
+ function during compilation. If the #pragma save_types is
+ specified, then the types are saved even after compilation, to
+ be used when the object is inherited.
+
+ The following two pragmas are available if the driver was
+ compiled with DEBUG and TRACE_CODE options:
+
+ set_code_window: Sets an offset to the current program writing
+ position. Use this BEFORE a piece of code where you
+ want to check to what bytecodes it is compiled.
+ show_code_window: shows some bytes starting at or near the
+ last point set_code_window was called.
+
+EXAMPLES
+ #pragma strict_types
+ #pragma no_clone, no_inherit
+
+HISTORY
+ LDMud 3.2.7 added local_scopes, no_local_scopes, no_clone
+ and no_inherit.
+ LDMud 3.2.8 added weak_types, pedantic and sloppy.
+ LDMud 3.2.9 allowed to specify multiple pragmas in one directive.
+ LDMud 3.2.9 added (no_)warn_deprecated.
+ LDMud 3.2.10 added (no_)warn_empty_casts.
+ Starting with LDMud 3.2.10, #pragma xxx_types in an included file are
+ no longer valid only until the end of the file, but remain active
+ when processing returns to the including file.
+ LDMud 3.2.11 added (no_)warn_function_inconsistent.
+ LDMud 3.3.378 added init_variables, share_variables.
+ LDMud 3.3.357 added (no_)warn_missing_return.
+ LDMud 3.3.646 added (no_)range_check.
+ LDMud 3.5.0 removed combine_strings and no_combine_strings.
+ LDMud 3.5.0 removed local_scopes and no_local_scopes.
+ LDMud 3.5.0 removed verbose_errors (making its behaviour mandatory).
+ LDMud 3.5.0 enabled warn_deprecated by default.
+
+SEE ALSO
+ inheritance(LPC), initialisation(LPC), objects(C),
+ operators(LPC)
diff --git a/doc/LPC/preprocessor b/doc/LPC/preprocessor
new file mode 100644
index 0000000..61593f0
--- /dev/null
+++ b/doc/LPC/preprocessor
@@ -0,0 +1,23 @@
+NAME
+ preprocessor
+
+DESCRIPTION
+ The LPC driver understands the following preprocessor
+ directives:
+
+ #include, #define, #if, #ifdef, #ifndef, #else, #elif,
+ #endif, #undef
+ same as in ANSI C
+
+ #line <num>
+ line numbers start at <num> with the next line
+
+ #echo
+ the rest of the line is printed to the error output
+ (stderr), thus can be captured into a log file
+
+ #pragma
+ see the separate man page
+
+SEE ALSO
+ pragma(LPC), predefined(D)
diff --git a/doc/LPC/references b/doc/LPC/references
new file mode 100644
index 0000000..c1d60f0
--- /dev/null
+++ b/doc/LPC/references
@@ -0,0 +1,49 @@
+CONCEPT
+ references
+
+DESCRIPTION
+ Call by reference can be used to have a function that passes
+ more than one value to the caller, without using arrays that
+ have to be unpacked thereinafter.
+ There is nothing special to declare in the calling function,
+ you simply do an assignment to a parameter of the function.
+ The caller has to pass references explicitely; this is done by
+ prefixing an lvalue with '&' .
+ To pass a reference to an element of an array, you have to
+ enclose the indexed lvalue in round brackets.
+
+EXAMPLE
+
+ void assign(mixed destination, mixed source) {
+ destination = source;
+ }
+
+ void extract_number(int destination, string source) {
+ sscanf(source, "%d", destination);
+ }
+
+ void test() {
+ int i;
+ float f;
+ mixed *a;
+
+ extract_number(&i, "42 palantirs");
+ assign(&f, 3.141592653589793);
+ assign(&a, ({ i, f }));
+ assign(&(a[<0..<1]), ({1,2,3,"sink","x","y","x"}));
+ assign(&(a[5][0]), 'w');
+ assign(&(a[5][<1]), 'g');
+ printf("%O", a));
+ }
+
+ ({ /* sizeof() == 9 */
+ 42,
+ 3.14159,
+ 1,
+ 2,
+ 3,
+ "wing",
+ "x",
+ "y",
+ "x"
+ })
diff --git a/doc/LPC/strings b/doc/LPC/strings
new file mode 100644
index 0000000..15820ef
--- /dev/null
+++ b/doc/LPC/strings
@@ -0,0 +1,65 @@
+NAME
+ strings
+
+DESCRIPTION
+ A string is a data type in LPC.
+
+ It consists of several ASCII characters (letters), numbers
+ and special characters. ASCII characters may contain of
+ values between -128 and 127 (e.g. 'A' (65) or '5' (53)), but
+ 0 is not allowed (because strings are 'null terminated',
+ which means that marks the end of a string).
+
+ Notations for strings:
+ "abc" + "def" "ghi" + 5 + "jkl" + to_string( ({'m','n'}) )
+ == "abcdefghi5jklmn"
+
+ Special characters must be escaped with a leading '\':
+ \n (new line)
+ \t (tabulator)
+ \b (backspace)
+ ...
+
+ The single characters can be accessed like an array:
+ string s;
+ int i,j;
+
+ s="test";
+
+ s[0] (ASCII Code (value) of the first character) ='t'
+ s[<1] (last character) ='t'
+ s[0..0] (string composed of the first char) ="t"
+ s[1..<2] (string from second to character before last) ="es"
+ s[2..] (string from third to last character) ="st"
+ ...
+
+ String manipulations:
+ s[0]=s[0]-32 ("test" -> "Test")
+ s[1..<2]="o" ("test" -> "tot")
+ ...
+
+FUNCTIONS
+ int strlen(string str)
+ int member(string s, int elem)
+ int strstr (string str, string str2, int pos)
+ int to_int(string)
+ mixed *to_array(string)
+ string to_string(mixed)
+ string upperstring(string str)
+ string lowerstring(string str)
+ string lower_case(string str)
+ string capitalize(string str)
+ string break_string(string str, int width, int space, int leave_lfs)
+ string sprintf(string fmt, ...)
+ int sscanf(string str, string fmt, mixed var1, mixed var2, ...)
+ string *new_explode(string str, string del)
+ string *explode(string str, string del);
+ string implode(mixed *arr, string del)
+ string *regexplode (string text, string pattern)
+ string *regexp(string *list, string pattern)
+
+SEE ALSO
+ arrays, integers, mappings, operators, types
+
+LAST CHANGED
+ 12. Mar 2004 Gloinson
diff --git a/doc/LPC/structs b/doc/LPC/structs
new file mode 100644
index 0000000..217b0a6
--- /dev/null
+++ b/doc/LPC/structs
@@ -0,0 +1,227 @@
+CONCEPT
+ structs
+
+INTRODUCTION
+ structs are, next to arrays and mappings, a way to group a
+ collection of value together.
+
+ A struct holds a fixed number of values, called 'members', and
+ allows to access them by their given name. The name is resolved
+ when the LPC code is compiled, making struct member access as fast
+ as array member access.
+
+ structs are passed by reference.
+
+
+DEFINITION
+ A new struct type has to be defined at the top level of an
+ object. For example
+
+ struct Foo {
+ int one, *two;
+ struct Bar three;
+ };
+
+ defines the new struct 'Foo' with three members: integer 'one',
+ integer array 'two', and struct Bar 'three'
+
+ It is possible to 'inherit' structs from each other. Given above
+ definition of struct Foo, the following definition
+
+ struct Quux (Foo) {
+ int four;
+ };
+
+ is equivalent to the definition
+
+ struct Quux {
+ int one, *two;
+ struct Bar three;
+ int four;
+ };
+
+
+ The usual visibility modifiers apply, e.g.
+
+ protected struct Bang {...};
+
+
+ struct definitions are promoted through inheritance like functions,
+ with the difference that all structs live in the same flat namespace.
+ This means: a struct defined in a program is visible in _all_
+ inherited programs, regardless of how deep the inheritance is
+ nested. This also means that in one program there must not be
+ two structs, inherited or not, with the same name.
+
+
+ To declare a struct without defining it, write:
+
+ struct Quux;
+
+ This notation is useful if you have two structs referencing
+ each other:
+
+ struct Quux;
+
+ struct Bar {
+ struct Quux quux;
+ };
+ struct Quux {
+ struct Bar bar;
+ };
+
+
+USAGE
+ To use a struct, its definition must be visible - either because it
+ is defined in the object compiled, or it has been inherited.
+ (Note: #include'ing structs does not what you think it does: in
+ LPC it constructs a new struct type whereever it is included).
+
+
+ A variable to hold a struct is defined like this:
+
+ struct Foo var;
+
+ and similar for function arguments:
+
+ void fun (struct Foo arg)
+
+
+ Just writing 'struct Foo var' however does not _create_ a struct,
+ it just creates a variable capable of holding one. To assign a value
+ to the variable upon creation, assign it with a struct value, either
+ from another variable or from a literal struct:
+
+ struct Foo var = (<Foo>);
+
+
+ Literal structs are written using (<>) as delimiters:
+
+ (<Foo>)
+ creates an empty instance of struct Foo
+
+ (<Foo> 1, ({ 2 }), bar)
+ creates an instance of struct Foo, and assigns 1 to member
+ 'one', ({ 2 }) to member 'two', and the content of variable
+ bar to member 'three'.
+
+ (<Foo> two: ({ 2 }) )
+ creates an instance of struct Foo which is all empty except
+ for member 'two' which is assigned the value ({ 2 }).
+
+ It is not possible to use both named and unnamed initializers
+ in the same literal.
+
+
+ A struct member is accessed using the -> operator:
+
+ struct Foo var = ...;
+
+ var->one = 1;
+
+
+ It is possible to compute struct lookups at runtime:
+
+ struct Foo bar = ...;
+ string member = "one";
+
+ bar->(member) = 1; // sets bar->one to 1
+ bar->(0) = 1; // sets bar->one to 1
+
+
+ When using struct values held in variables/expressions of type
+ 'mixed', the 'mixed' value should to be casted to the struct
+ value. The cast can be omitted if the looked-up member exists
+ in only one struct (and its children) known to the compiler:
+
+ struct Foo { int one; };
+ struct Bar { int two; };
+ struct Baz { int two; };
+ mixed var;
+
+ var->one // looks up Foo->one
+ (struct Foo)var->one // looks up Foo->one
+ var->two // ERROR: ambiguous lookup
+ (struct Bar)var->one // looks up Bar->one
+
+
+USAGE IN CLOSURES
+ The #'(< operator can be used in lambda closures to create a
+ struct; the type of the struct is given by the 'template'
+ struct passed as first argument. The content of the template
+ struct is irrelevant, so an empty struct suffices. For
+ example, to create an instance of struct Foo:
+
+ ({ #'(<, (<Foo>), 1, ({ 2 }), (<Bar>) })
+
+ The order of the member values is the order in which they
+ appear in the struct definition.
+
+ To access a struct member in a lambda closure, use the #'->
+ operator with the name of the member as double-quoted symbol
+ or literal string:
+
+ ({ #'->, struct-expression, ''one })
+ ({ #'->, struct-expression, "one" })
+
+
+MISCELLANEOUS
+ Internally structs can be identified by the ID string
+ returned from get_type_info(). This string contains the name
+ of the struct, the name of the program its type was defined in,
+ and the ID number of the program. However, do not rely on
+ a particular format of this string!
+
+ Support for structs is signaled by the macro __LPC_STRUCTS__.
+
+ Since structs are tied to the program they are defined in,
+ re-compiling a program creates new struct types which are
+ in principle incompatible to the old ones. However, the LPC
+ compiler checks if the newly compiled structs have the same
+ structure as their older counterparts of the same name
+ (and defining program). If the structures conform, the existing
+ older struct types are used instead of the new ones. This way
+ an accidental of for example /std/types.c doesn't break
+ the whole mud.
+
+
+EXAMPLES
+ Suppose we have two objects: a monster, and a monster
+ coordinate tracker, and we want to use a struct to store the
+ coordinate:
+
+ -- monster_coordinate.c --
+ struct Coordinate { int x; int y; };
+
+ -- monster_tracker.c --
+ inherit "monster_coordinate";
+
+ void track (struct Coordinate coord) { ... }
+
+ -- monster.c --
+ inherit "monster_coordinate";
+
+ int move (..) {
+ ...
+ "monster_tracker"->track( (<Coordinate> my_x, my_y) );
+ }
+
+ Note that using '#include "monster_coordinate.c"' instead of inherit
+ won't work. While the objects would compile, the first call to
+ track() would cause a runtime error of the type
+
+ Illegal type to struct->(): struct Coordinate (/monster.c #234),
+ expected struct Coordinate
+ (/monster_tracker.c #552)
+
+
+HISTORY
+ structs were fully implemented first in LDMud 3.3.246.
+ The implementation was revised in LDMud 3.3.344.
+ The reactivation of unchanged structs in object updates was
+ implemented in LDMud 3.3.417.
+
+
+SEE ALSO
+ mappings(LPC), get_type_info(E), structp(E), to_mapping(E),
+ to_struct(E), struct_info(E), baseof(E)
diff --git a/doc/LPC/switch b/doc/LPC/switch
new file mode 100644
index 0000000..9bca596
--- /dev/null
+++ b/doc/LPC/switch
@@ -0,0 +1,93 @@
+NAME
+ switch
+
+SYNTAX
+ switch (expr) block
+
+DESCRIPTION
+ Branch to the case label in statement that matches expr.
+ If no matching case label is found (by value or by type),
+ branch to the default label in statement.
+
+ A case label has the form
+
+ case expr_n :
+
+ where expr_n must be constant, or the form
+
+ case expr_n1 .. expr_n2 :
+
+ where expr_n1 and expr_n2 must be numeric constants and
+ expr_n1 < expr_n2.
+
+ Either all case labels have to be strings or all have to be
+ numeric. Only 0 is special: it is allowed in a switch
+ statement where all other labels are strings.
+
+ A default label has the form
+
+ default :
+
+ The default label defaults to the end of statement if not
+ given explicitly.
+
+ Whenever a 'break' statement is executed inside 'statement' a
+ branch to the end of the switch statement is performed.
+
+EXAMPLE
+ Typical usage:
+
+ switch(random(100)) {
+ case 0 .. 22 : write("Nothing happens"); break;
+ case 23 .. 27 :
+ write("You are surrounded by a golden glow");
+ this_player()->heal_self(random(3));
+ break;
+ case 28 .. 32 :
+ write("The water was poisoned!\n");
+ this_player()->add_exp(this_player()->hit_player(random(4)));
+ break;
+ case 33 : write("You hear a voice whispering: "+random_hint());
+ /* fall through */
+ case 34 :
+ write("While you didn't pay attention, a water demon "
+ "snatches a coin out of your purse!\n");
+ this_player()->add_money(-1);
+ break;
+ default : write "You hear some strange noises\n"; break;
+ case 42 : return;
+ case 99 : write("It tastes good.\n";
+ }
+
+NOTE
+ In C, the grammar for switch() is
+
+ switch (expr) statement
+
+ allowing constructs like
+
+ switch (expr)
+ while (expr2)
+ {
+ case 1: ...
+ case 2: ...
+ }
+
+ In LPC a switch has to be followed by a block that contains the
+ case labels directly. In contrast to C the group of statements
+ following a case label have their own lexical scope so that
+ variable declarations may not cross case labels.
+
+HISTORY
+ LDMud 3.2.10 constrained the grammar to require a block for the
+ switch() body, not just a statement. This differs from the C
+ syntax, but was necessary as the compiler didn't handle
+ the statement case correctly.
+ LDMud 3.3 allowed to pass values of the wrong type to switch(), the
+ driver would in that case use the default case. Before, values of
+ the wrong type caused a runtime error.
+ LDMud 3.3.718 disallowed case labels in inner blocks and variable
+ declarations that cross case labels.
+
+SEE ALSO
+ for(LPC), foreach(LPC), do-while(LPC), if(LPC), while(LPC)
diff --git a/doc/LPC/types b/doc/LPC/types
new file mode 100644
index 0000000..903a1be
--- /dev/null
+++ b/doc/LPC/types
@@ -0,0 +1,136 @@
+CONCEPT
+ types
+
+DESCRIPTION
+
+ Variables can have the following types:
+
+ o int An integer. Normally full 32 bits signed, yielding a
+ range of at least -2,147,483,648 to 2,147,483,647. The
+ exact available range is given by the predefined
+ macros __INT_MIN__ and __INT_MAX__.
+
+ Integer values can be specified in decimal, in
+ sedecimal when preceeded by '0x' (e.g. 0x11), binary
+ when preceeded by '0b' (e.g. 0b00010001), octal when
+ preceeded by '0o' (e.g. 0o21) and as character
+ yielding the charset value for the character as the number
+ to use (e.g. '0' yields 48 on ASCII machines).
+
+ Character values are enclosed in single-quotes ('),
+ with the sequence ''' returning the single-quote
+ itself. Instead of the literal character an
+ escape-sequence can be written between the
+ single-quotes:
+ \N : the character code N in decimal
+ \0xN : the character code N in sedecimal
+ \xN : the character code N in sedecimal
+ \0oN : the character code N in octal
+ \0bN : the character code N in binary
+ \a : BEL (0x07)
+ \b : Backspace (0x08)
+ \t : Tab (0x09)
+ \e : Escape (0x1b)
+ \n : Newline (0x0a)
+ \f : Formfeed (0x0c)
+ \r : Carriage Return (0x0d)
+ \<other character>: the given character
+
+ o status OUTDATED - status was planned to be an optimized
+ boolean format, but this was never actually
+ implemented. status does work; however, since it
+ is only an alias for type 'int', just use int.
+
+ o string Strings in lpc are true strings, not arrays of characters
+ as in C (and not pointers to strings). Strings are
+ mutable -- that is, the contents of a string can be
+ modified as needed.
+
+ The text of a string is written between double-quotes
+ ("). A string can written over several lines when the
+ lineends are escaped (like a macro), however a better
+ solution is to write one string per line and let the
+ gamedriver concatenate them.
+
+ String text typically consists of literal characters,
+ but escape-sequences can be used instead of
+ characters:
+ \<CR> : Carriage Return (0x0d)
+ \<CR><LF> : ignored
+ \<LF> : ignored
+ \<LF><CR> : ignored
+
+ \N : the character code N in decimal
+ \0xN : the character code N in sedecimal
+ \xN : the character code N in sedecimal
+ \0oN : the character code N in octal
+ \0bN : the character code N in binary
+ \a : BEL (0x07)
+ \b : Backspace (0x08)
+ \t : Tab (0x09)
+ \e : Escape (0x1b)
+ \n : Newline (0x0a)
+ \f : Formfeed (0x0c)
+ \r : Carriage Return (0x0d)
+ \" : The double quote (")
+ \<other character>: the given character
+
+ Adjacent string literals are automatically
+ concatenated by the driver when the LPC program is
+ compiled. String literals joined with '+' are
+ concatenated by the LPC compiler as well.
+
+ o object Pointer to an object. Objects are always passed by
+ reference.
+
+ o array Pointer to a vector of values, which could also
+ be an alist. Arrays take the form ({ n1, n2, n3 })
+ and may contain any type or a mix of types. Arrays
+ are always passed by reference. Note that the size
+ of arrays in LPC, unlike most programming languages,
+ CAN be changed at run-time.
+
+ o mapping An 'associative array' consisting of values indexed by
+ keys. The indices can be any kind of datatype.
+ Mappings take the form ([ key1: value1, key2: value2 ]).
+ By default, mappings are passed by reference.
+
+ o closure References to executable code, both to local
+ functions, efuns and to functions compiled at
+ run-time ("lambda closures").
+
+ o symbol Identifier names, which in essence are quoted strings.
+ They are used to compute lambda closures, e.g. instead
+ of ({..., 'ident, ... }) you can write declare a
+ 'symbol' variable foo, compute a value for it, and then
+ create the closure as ({ ..., foo, ... })
+
+ o float A floating point number in the absolute range
+ __FLOAT_MIN__ to __FLOAT_MAX__ (typically 1e-38 to 1e+38).
+ Floating point numbers are signified by a '.'
+ appearing, e.g. '1' is integer 1, but '1.' is
+ floating-point 1 .
+
+ o mixed A variable allowed to take a value of any type (int,
+ string, object, array, mapping, float or closure).
+
+ o struct A collection of values. See structs(LPC).
+
+ o union A range of types, either of which the variable
+ can contain at runtime. See unions(LPC).
+
+ All uninitialized variables have the value 0.
+
+ The type of a variable is really only for documentation. Unless
+ you define #pragma strong_types or rtt_checks, variables can
+ actually be of any type and has no effect at all on the program.
+ However, it's extremely bad style to declare one type but use
+ another, so please try to avoid this.
+
+ A pointer to a destructed object will always have the value 0.
+
+
+SEE ALSO
+ alists(LPC), arrays(LPC), mappings(LPC), closures(LPC), structs(LPC),
+ unions(LPC), typeof(E), get_type_info(E), inheritance(LPC),
+ pragma(LPC), modifiers(LPC), escape(LPC)
diff --git a/doc/LPC/unions b/doc/LPC/unions
new file mode 100644
index 0000000..65a841d
--- /dev/null
+++ b/doc/LPC/unions
@@ -0,0 +1,30 @@
+CONCEPT
+ unions
+
+DESCRIPTION
+ Unions types are a declaration at compile time that a variable,
+ function parameter or function return value can be one of
+ several types.
+
+ Except for type checks using #pragma rtt_checks they have no
+ impact at runtime. There is no runtime union type, the concrete
+ value type is one of the possibilities of the union type.
+
+ Union types have no type names for themselves, they are declared
+ anonymously with the declaration of the variable, function
+ parameter or return type:
+
+ int|string var;
+ int|float fun(object|closure f);
+
+ When using union types as array member types they must be
+ enclosed with < >:
+
+ <int|string>* arr; /* An array of ints and strings. */
+ int*|string* arr; /* Either an array of ints or
+ an array of strings. */
+
+ /* There must be a whitespace between two consecutive <
+ to be not confused with the << operator: */
+ < <int|string>*|object >* arr;
+
diff --git a/doc/LPC/varargs b/doc/LPC/varargs
new file mode 100644
index 0000000..ef21837
--- /dev/null
+++ b/doc/LPC/varargs
@@ -0,0 +1,75 @@
+CONCEPT
+ varargs
+
+DESCRIPTION
+ A function uses "varargs", short for "variable arguments", if
+ it intentionally may be called with less or more arguments
+ than formally specified.
+
+ The proper order to define a function call is:
+
+ [ modifier ] [ varargs ] [ return type ] function( args...)
+
+ Any other order will result in an error.
+
+
+ Given a function definition like this:
+
+ void fun (string arg1, int arg2, int arg3)
+
+ fun() has to be called with exactly three parameters: one
+ string and two integers.
+
+
+ If the function is defined as
+
+ varargs void fun (string arg1, int arg2, int arg3)
+
+ it is possible to call the function with just arg1, or arg1
+ and arg2. The remaining unspecified arguments (arg2 and arg3,
+ resp. arg3) are in these cases assumed to be 0.
+
+
+ To pass more arguments than specified, the functions last
+ parameter must be defined as following:
+
+ void fun (string arg1, int arg2, varargs int * arg3)
+
+ This allows fun() to be called with two or more arguments.
+ The arguments, except those assigned to the other parameters,
+ in this case arg1 and arg2, and collected into an array which
+ is then passed as arg3. For example
+
+ fun("foo", 1) -> arg3 == ({ })
+ fun("foo", 1, 2) -> arg3 == ({ 2 })
+ fun("foo", 1, 2, 3) -> arg3 == ({ 2, 3 })
+
+ The type of the varargs argument has to be an array of the
+ expected type (int*, object*, string*, ...); in this example,
+ only integers are allowed. To accept arguments of any kind,
+ define the parameter as 'varargs mixed' or 'varargs mixed*'.
+
+ To 'flatten' the received argument array in your own function
+ calls, use the efun apply(); e.g.:
+
+ apply(#'call_out, "bar", 1, arg3)
+
+ or the 'flatten arguments' operator:
+
+ call_out("bar", 1, arg3...)
+
+ The two varargs variants can of course be combined:
+
+ varargs void fun (string arg1, int arg2, varargs int* arg3)
+
+ defines a function which may be called with any number of
+ arguments.
+
+
+HISTORY
+ The possibility to pass more arguments than formally specified
+ was introduced in 3.2.1@132. Before, the excess arguments were
+ silently ignored.
+
+SEE ALSO
+ pragma(LPC), apply(E), modifiers(LPC)
diff --git a/doc/LPC/while b/doc/LPC/while
new file mode 100644
index 0000000..adc4708
--- /dev/null
+++ b/doc/LPC/while
@@ -0,0 +1,15 @@
+NAME
+ while
+
+SYNTAX
+ while (expr) statement;
+
+DESCRIPTION
+ While 'expr' evaluates to non 0, execute statement.
+
+ A 'break' in the 'statement' will terminate the loop. A
+ 'continue' will continue the execution from the beginning of
+ the loop.
+
+SEE ALSO
+ for(LPC), foreach(LPC), do-while(LPC), if(LPC), switch(LPC)