LPC (programming language)
From Wikipedia, the free encyclopedia
LPC | |
---|---|
Paradigm | prototype-based |
Designed by | Lars Pensjö |
Developer | Lars Pensjö and others |
Major implementations | LPC |
Dialects | Amylaar, MudOS, LDMud, DGD |
Influenced by | C, C++, Lisp, Perl |
Influenced | Pike |
The LPC programming language is an object-oriented programming language derived from C and developed originally by Lars Pensjö to facilitate MUD building on LPMuds. It has been used not only for mud game creation, but also has evolved into the Pike programming language used for various internet servers, multimedia applications and system administration tasks. LPC stands for 'Lars Pensjö C'.
LPC syntax places it in the family of C-like programming languages, with C and C++ its strongest influences.
Contents |
[edit] Basic structure
Almost everything in LPC is an object. However, LPC does not precisely use the concept of a class (MudOS has something called a class, but it is really a struct). Instead, LPC objects are blueprint objects and clones of blueprint objects. You can treat a blueprint object much as you would a class in other object-oriented languages, with exceptions such as not being able to specify functions or variables that belong only to the blueprint object (i.e. class methods and attributes), and the blueprint object being subject to interaction in exactly the same fashion as its clones.
Each object has variables (attributes) and functions (methods). The variables store the object's state; the functions are executable routines that can be called in the object. An object is uniquely defined by the name of the file in which it comes from, plus, if a clone, a numeric identifier. In a typical implementation, a clone of the file /lib/weapon.c which is the third clone created in the current run session will be /lib/weapon#3. The blueprint object corresponding to /lib/weapon.c is simply /lib/weapon. In a MUD game, common objects include rooms, weapons, armor, and NPCs. Most mudlibs define inheritable objects for such common things. In the LPMud 2.4.5 mudlib, for example, the parent object for all rooms is /lib/room.
[edit] A simple room in a Typical Mudlib
Because LPC is generally used to code MUDs, 'rooms' are often created as objects that store information describing a particular scene along with exits that point to other room objects. This is the most common usage of LPC. Other uses that are not game related are possible.
The following example shows a very simple room that leverages functions defined in the mudlib object /lib/room. However, not all mudlibs define or determine rooms in the same way. The following is typical of a traditional room, but is not the only method of defining a room.
inherit "/lib/room"; void create() { ::create(); set_short("a simple room"); set_long("A simple room in a simple building."); set_description("This is a simple room in a simple building. It is very nice."); add_exit("north", "/realms/descartes/north_room"); }
The first line tells the object to inherit functionality from the /lib/room object. This example assumes the /lib/room object defines functions for ::create(), set_short(), set_long(), set_description(), and add_exit().
This example contains a single function, create(). Most drivers call or can be set to call create() to allow an object to initialize itself with startup values. In this case, the example calls functions that set up the basic room attributes in the inherited /lib/room. The functions you call here are highly dependent on the mudlib you are using since the mudlib defines the actual room parent.
[edit] Data types
[edit] Common data types
- object
- Any LPC object, including both blueprints and clones. Available in all known drivers. Has no literal form as such.
object obj = new("/std/weapon");
- int
- A 32-bit integer. Available in all known drivers. Literal form is the bare number.
int number = 23;
- string
- A variable length character string. You do not need to do any form of memory management or bounds management; LPC handles that for you. Available in all known drivers. Literal form is the character string enclosed in double-quotes.
string text = "Hello, world!";
- array, declared as <type> *
- An array of another data type. In most drivers, you define an array by type with the * modifier, as in object *, string *, int *. Typically, only one * can be applied to a declaration, so if you wanted an array of arrays, you would declare it as mixed *. LPC arrays are generally "rubberband" arrays that can have their length modified after allocation. Arrays are almost universally available, though the preferred declaration syntax differs in MudOS. Usual literal form is the list of values, separated by commas (optional after the last value), enclosed in ({ and }) boundary markers.
int * numbers = ({ 1, 2, 3 }); string * words = ({ "foo", "bar", "baz" }); mixed * stuff = ({ 1, "green", 48.2 });
- mapping
- A hash map (associative array) of keys to values. Supported by most drivers. Usual literal form is a ([ boundary marker, followed by zero or more key-value pairs with a colon between the key and value and a comma following the value (optional on the last key-value pair), and a ]) boundary marker. More details are available in the Wikipedia article on associative arrays.
mapping map = ([ "hello" : 1, "world" : 2, ]);
- float
- A floating-point numeric value, generally "single-precision" rather than "double-precision". Supported by most drivers.
float number = 3.1415;
- mixed
- Used to designate variables that may hold values of different types. Effectively, this disables compile-time typechecking on the variable. LPC has strong dynamic typing; you would work with mixed type variables by checking type information at runtime. Available in all known drivers.
mixed value = random(2) ? "hello" : 1;
[edit] Less common data types
- status
status flag = 1;
- closure
- The most common function-pointer data type, mainly known to be supported by the Amylaar LPMud and LDMud drivers. The basic literal syntax for obtaining a closure of a function is a hash mark, followed by a single quote, followed by the function name. Construction of lambda closures is much more complex.
closure func = #'some_local_function;
- symbol
- Used by drivers supporting lambda closure construction (mainly the Amylaar LPMud and LDMud drivers) for binding to variable namespace within lambda closures. Literal syntax is a single quote followed by the symbol name.
symbol var = 'x;
- quoted array
- Also used by drivers supporting lambda closure construction, this is a special type with no explicit declaration form, used for designating arrays so that they are not interpreted as closure instructions. Has no literal form as such, being obtained by sending arrays through the quote() function.
mixed quoted_array = quote(({ 1, 2, 3 }));
- <type> array
- The form of array declaration preferred under MudOS. The <type> * form is usually also available.
int array numbers = ({ 1, 2, 3 });
- class <name>
- Unique to MudOS; behaves like a C struct. It is not a class in any object-oriented sense of the word.
class example { int number; string name; }; class example instance = new(class example); instance->number = 23; instance->name = "Bob";
- function
- The MudOS function-pointer type, similar to closure, which is usually supported as an alias. Anonymous functions may be specified within (: :) boundaries, using $1, $2, etc. for arguments. LDMud also supports a variant of this syntax for its closure values.
function op = (: return sqrt($1 * $1 + $2 * $2); :);
[edit] Pseudo-types and type modifiers
- void
- Used as the declaration type for the return value of a function, specifies that the function will not return a value. Available in all known drivers.
- varargs
- A type modifier for function declarations; specifies that the function's argument list is intended to be variable-length. Supported by most drivers. Some drivers support an extension to this behavior where a variable in the argument list of a varargs function can itself receive the varargs modifier, causing it to act as a "catch-all" variable for multiple arguments in the actual argument list.
- private
- A type modifier for object variable and function declarations; specifies that the affected entity should not be available in the namespace of any inheritors or, in the case of functions, be callable by other objects. Supported by most drivers.
- static
- A type modifier for object variable and function declarations. For variables, specifies that they should not be serialized. For functions, specifies that they should not be callable by other objects. Supported by most drivers, but often deprecated in favor of more sensibly designed type modifiers.
- protected
- A type modifier for function declarations; specifies that the function should not be callable by other objects, though it still appears in inheritors' function namespace, unlike the behavior of private. Supported by many drivers.
- nosave
- A type modifier for object variable declarations; specifies that the variable should not be serialized. Supported by many drivers; in such drivers, static for variables is generally deprecated in favor of nosave.
- nomask
- A type modifier for function declarations; specifies that it should not be permitted to override the function or otherwise obscure it in the function namespace. Supported by most drivers.
- public
- Generally the default behavior, allowing unrestricted access to a variable or function, it can also usually be applied as a type modifier that prevents public access to the variable or function from being overridden later. Supported by most drivers.
- virtual
- A type modifier for inherits; specifies that when a module occurs more than once in the inheritance tree, only one instance of its variables should be maintained. Supported by many drivers.
Most drivers also support applying type modifiers to inherit statements, causing the inherited object to behave with respect to its inheritor as if the type modifier were applied to its functions and/or variables, as appropriate.
Where the term 'object variable' is used above, this means a variable which is an element of an object (i.e. an attribute), as opposed to a local variable (exists only within a function or block) or a global variable (nonexistent in LPC -- if someone speaks of a global variable in reference to LPC, they probably mean an object variable).
[edit] Passing values
Primitive LPC types (int, string, status, float, etc.) are passed by value. Data structure types (object, array, mapping, class) are passed by reference.
This feature can be powerful, but it can also lead to security holes. In most mud systems, the people building the world are generally less trusted than the staff running the game. If an object passes a mapping with sensitive values like access control information, an author of another object can modify that and thus increase their access rights. Mudlib developers and game admins should thus be careful when passing complex types to lower access objects.
[edit] Function types
LPC environments generally categorize functions into several major types according to how they are implemented:
- lfun
An lfun, or "local function", is defined by a blueprint object. (Clones have the same function set as their blueprint.) They are written in LPC. Functions in a given object can call other functions within the same object using the syntax function_name(), while functions in other objects are usually called with the syntax object->function_name(). Lfuns defined in objects one is inheriting can be called with the syntax ::function_name() or module_name::function_name().
- efun
An efun, or "external function", is defined by the driver. Efuns are written in C and compiled statically into the driver, so they generally run much faster than other function types, but are more difficult to write and lack flexibility. They are available in the namespace of all functions written in LPC, so, for example, the efun this_player() can be called with the syntax this_player(), or efun::this_player() if one needs to bypass an lfun or simul_efun.
DGD uses the term kfuns, "kernel functions", rather than efuns. DGD kfuns are mostly identical with efuns in other implementations.
- simul_efun
A simul_efun, "simulated external function", is written in LPC within the driver environment and placed in a special object whose functions mimic efuns for purposes of syntax. So a simul_efun some_function() is available as some_function() in the namespace of all LPC functions.
DGD does not precisely have simul_efuns, but rather has an auto object that acts as if it is automatically inherited by all other objects. This partially mimics the behavior of simul_efuns in other implementations.
[edit] The Master Object
LPC implementations generally have a master object, which is a specific LPC object that is loaded first by the LPC driver and which essentially controls what will happen past that point. The master object will typically tell the driver where the simul_efun object is, preload any objects which need to be present at startup, define what functions will be called when an object is loaded, and otherwise configure the driver's operation. The driver will refer back to the master object when it needs to interact with a central point of reference for the running environment, such as for accepting network connections, handling errors, and validating attempts to perform privileged operations.
[edit] See also
[edit] External links
- LPC Basics by George Reese on Apr 23rd, 1993
- NannyMUD LPC by Mats H. Carlberg in March in 1998
- LPC Tutorial by Ronny Wikh on Jul 8th, 2003
- LPMuds.net A resource for MUDs that use LPC.