Added public files
Roughly added all public files. Probably missed some, though.
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().
+