Register to post in forums, or Log in to your existing account
 

Play RetroMUD
Post new topic  Reply to topic     Home » Forums » CMUD General Discussion Goto page Previous  1, 2, 3  Next
Fang Xianfu Posted: Wed Apr 18, 2007 2:49 am
Windows Script Host
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Mon Jun 18, 2007 6:23 am   
 
Fang Xianfu wrote:
I was told in an IRC channel that LuaCOM might be able to do it, but I have no idea how to go about it.


LuaCOM implements a COM interface to Lua - that is, given a Lua script you can create and access COM objects. There are some examples on my forum. However it doesn't make a COM interface that gives you the Lua script engine.

In other words, a Lua script "requires" LuaCOM - to get access to COM (eg. Excel spreadsheets). You don't (somehow) load LuaCOM dll to get an interface into running Lua scripts.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Mon Jun 18, 2007 5:03 pm   
 
Quote:
For example, when I added Lua to MUSHclient, I made my own "print" function

That's very cool. I definitely need to look into this more and figure out how to integrate Lua with Delphi code.

Btw, great to hear from you Nick!
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Mon Jun 18, 2007 9:34 pm   
 
Thanks, Zugg! :)

A quick search seems to indicate you can interface Delphi with Lua, for example:

http://www.matrix44.de/lua/

Here is a quick example of the sort of "glue" routine I used. First, taking a look at how the normal (C++) code adds a timer:

Code:

long CMUSHclientDoc::AddTimer(const char * TimerName,
                              short Hour,
                              short Minute,
                              double Second,
                              const char * ResponseText,
                              long Flags,
                              const char * ScriptName)
{
  // stuff to add a timer here

 return eOK;  // success!

}


To make the Lua glue, we basically pull from the Lua stack all the arguments by number (1 to 7), and then call the original routine:

Code:

//----------------------------------------
//  world.AddTimer
//----------------------------------------
static int L_AddTimer (lua_State *L)
  {
  CMUSHclientDoc *pDoc = doc (L);
  lua_pushnumber (L, pDoc->AddTimer (
                  luaL_checkstring (L, 1),  // TimerName
                  luaL_checknumber (L, 2),  // Hour
                  luaL_checknumber (L, 3),  // Minute
                  luaL_checknumber (L, 4),  // Second
                  luaL_checkstring (L, 5),  // ResponseText 
                  luaL_checknumber (L, 6),  // Flags
                  luaL_optstring (L, 7, "")   // ScriptName - optional
                  ));
  return 1;  // number of result fields
  } // end of L_AddTimer


Each of those things (like checkstring) obtains the appropriate argument, and checks its type, in the same call. Once we have all the arguments we can call the original AddTimer, which returns a single result. That single result (success code) is pushed onto the Lua stack, and we return 1 to indicate a single result has been returned.

I'm not sure exactly how that would fit in with Delphi, but I am sure you can make something work.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Mon Jun 18, 2007 10:18 pm   
 
Yep, I also just found that same web site, along with another one http://www.noeska.com/dlua/default.aspx that has an example of calling Lua from Delphi. In their example they show how to assign a Delphi routine to a Lua function (print in this case) and also how to handle Lua arrays from Delphi. So it looks like I have everything I need.

I assume you distribute the LUA.DLL and LUALIB.DLL files with MUSHClient? Do these DLLs need to be "registered" with Windows, and have you run into any conflicts with people having different versions of LUA on their computer? Or do you just put the DLLs in the same directory as MUSHclient and let Windows figure it out?
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Mon Jun 18, 2007 10:53 pm   
 
No, they don't need to be registered. Unlike the whole COM mess, where you have to register objects blah blah, Lua is designed to easily integrate with your programs with no fuss. There is no Lua.ini file for example. It doesn't use the Registry at all.

Recently they have integrated lua.dll with lualib.dll, so I just distribute a single dll, namely lua5.1.dll, which is standard Lua with a couple of minor tweaks to the compile options, such as allowing 64-bit integers for some operations, so converting from floats to integers works over a larger range of numbers. My changes are documented here: http://www.gammon.com.au/forum/?id=7795

I had Lua integrated into the executable for a while, but that causes major problems if you try to load a separate DLL (eg. LuaCOM) - LuaCOM uses the address space of the lua5.1.dll and the internal Lua used a different address space, and the program didn't stay running for long.

By forcing everything to go to the standard DLL, you avoid these problems. The DLL itself is 194 Kb.

"Standard" Lua (and its libraries) are pretty powerful - you can load DLLs for example, thus letting users integrate other things, like COM objects (using LuaCOM), TCP/IP (using luasocket), ODBC and MySQL database access.

In fact, I was worried it was too powerful - someone writing a plugin, using Lua, could do all sorts of things, so I made a Lua "sandbox" which is code which is executed after the engine is activated, but before user code is run. Using Lua itself, it can downgrade itself to disallow some operations. For example, the simple statement:

Code:

io = nil


This effectively removes the entire IO library (open/close, read/write files). The sort of thing I did was remove the ability to load DLLs, unless the user specifically permitted it (same with file IO, etc.).

The other thing you can do is write your own libraries and add them into your Lua address space. They aren't part of the DLL, they are part of that particular Lua script engine instance, inside the client. In essence, any function that has a C interface can be added in (like L_AddTimer above).

Stuff you might do there is bitwise operations (which Lua doesn't directly support, such as bitwise and, or, xor). You might give the users a combo box, list box, dialog box, message box, and so on, for user interface. You might do stuff like hashing, base64 encoding, interface to PCRE regular expressions. Really, the sky is the limit.

Lua's internal regular expression matching is very fast. I did some timings here:

http://www.gammon.com.au/forum/?id=6034

Compared to PCRE, which I generally regard as fast and efficient, Lua was actually twice as fast on my test case, if you compiled the PCRE expression each time. Even precompiling the PCRE string, Lua came out slightly faster. Of course, PCRE handles more complex patterns than Lua, but for simple(ish) stuff, Lua is very good.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Mon Jun 18, 2007 11:47 pm   
 
Wow, that's a lot of really useful information Nick! I'm getting exciting about playing with this.

Given what you can already do with COM in zMUD/CMUD (basically anything), I doubt I'll disable any of the Lua libraries. But it's good to know that it's possible (nifty trick).

I'll probably make wrappers for all of the CMUD commands and functions. In most cases the naming conflicts between CMUD commands and functions actually perform the same action, but some of them, like %additem vs #ADDITEM will take some thought. I'll probably add an extra argument to the Lua version to decide whether to allow dups in the list, for example. Since I already plan to add bitwise functions to CMUD, the Lua will get these for free.

Interesting about the reg ex matching. For some reason I thought Lua already used PCRE. CMUD already precompiles the PCRE string and caches that until the trigger pattern is changed. I'll have to see what the difference is between PCRE and Lua and see if I need to more complex stuff or not. Might be another way to get some performance improvements.
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Tue Jun 19, 2007 3:55 am   
 
Zugg wrote:
In most cases the naming conflicts between CMUD commands and functions actually perform the same action, but some of them, like %additem vs #ADDITEM will take some thought. I'll probably add an extra argument to the Lua version to decide whether to allow dups in the list, for example.


For a start, Lua is case-sensitive, but I agree that might be confusing. You could always put commands and functions into separate tables. When you add your own functions to the Lua script space, there is a provision for a "bulk add", where you basically provide a big tables of names/functions, which all get added into the same Lua table. Thus you might have:

Code:

cmd.additem (...)  --> this is the command
fnc.additem (...) --> this is the function


It is worth doing a bit of a read up on the basic things Lua can do. It might not be immediately obvious, but some of them are:




  • Functions can have optional arguments
  • Functions can return multiple things. eg. a, b, c = myfunction (x, y, z)
  • Functions can be assigned, like any other variable
  • Variables are untyped (however their *values* have types)



The multiple return idea is used quite heavily by Lua. For example, a string match can return (as a function result) the start column, the end column, and zero or more capture strings. This saves passing arguments by reference - that is, so they can be modified - which Lua doesn't do.

The fact that you can assign variables is one of the things that lets you extend Lua by replacing (say, its 'print' function) with something you write yourself. You can even save the old value, so that you might make a "print" that logs, then prints.

Zugg wrote:

Interesting about the reg ex matching. For some reason I thought Lua already used PCRE. CMUD already precompiles the PCRE string and caches that until the trigger pattern is changed. I'll have to see what the difference is between PCRE and Lua and see if I need to more complex stuff or not. Might be another way to get some performance improvements.


You can add in the PCRE library as a DLL (MUSHclient adds it in internally as it already has PCRE anyway inside its code space). However the "raw" Lua regex is its own version. Compact and fast.

Thus you can always make PCRE available, as a library, for people who need it, and also have the Lua version available too.
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Tue Jun 19, 2007 10:05 am   
 
One of the FAQs in the ILua thread is how to include PCRE in it ;) The lrexlib dll is actually larger than the whole of Lua, but Lua's matching is very powerful and can do most of the things lrexlib can do. My only gripe with Lua normally is the way that string.match returns a variable number of arguments instead of a table like lrexlib (and I think MUSHClient does it that way as well, returning a table with 1-n for each capture, and the names of the captures as well if they're applicable). The only way I can think of to capture that variable number of arguments without knowing how many captures there are is to pass the result of the string.match to a function that takes a variable number of arguments and then loop through them all.

Please don't sandbox Lua! It'd be pointless given that CMUD already does most, if not all, of the things that Lua can do. There are some functions that might not work that you might want to rewrite to do something else or disable, but don't get rid of working ones :( Changing print was a good example.

I say this mainly because writing to/reading from files is how I intend to export user settings so they're not lost over updates. Doing this in CMUD would get very complicated I'd think (it'd either involve an extra package with some kind of RegisterForSave event or some sort of text export/import) - Lua on the other hand can just loop through all the values of a table, writing them out to a file in Lua format so you can just dofile all the settings back in again afterwards.
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Jun 19, 2007 7:05 pm   
 
Quote:
Functions can return multiple things

Wow, that's really cool! I've wanted a language to do that for a long time. That feature alone will sell me on Lua. Time to jump on the band wagon!

Since CMUD already has the PCRE library compiled within, I'll probably try to do something like Nick did and add it internally, rather than using another DLL.

But it sounds like there is a *lot* of flexibility in how I implement this. Since Lua has optional arguments, that opens up a whole new way to handle things (such as an optional argument for %additem to determine whether dups should be removed).

Rather than just converting existing CMUD commands/functions straight into Lua, it sounds like this is a better opportunity to implement the CMUD commands/functions in a way that is better suited for Lua. In other words, the Lua version of a command doesn't need to be exactly like the CMUD version. Also, I can implement/reassign some functions (like print), but I can also implement a table called "zs" that would work more like the zs COM object that is used in the other scripting languages.

But it sounds like I need to start messing with Lua and learn more about how it likes to do things, and how stuff like tables work, so that the CMUD implementation will allow the Lua scripts to work very efficiently. I'll probably also read some of the MUSHclient examples on how Nick did some of this stuff. Making it easier to convert MUSHclient scripts would certainly be helpful for some people.

But I think I'm sold on the benefit of Lua. There is a *lot* more here that I originally thought. I was originally thinking that it was just going to be another COM-based scripting language like all of the other MS Script stuff. But now I'm seeing a whole lot of other potential with this. In a lot of ways, Lua could become a better choice than zScript.

I guess this is what Nick already learned with MUSHclient. I'm thinking about what he said regarding a more standardized MUD scripting language. I originally did this in zMUD by using the old TinTin scripting language. But now it might be the right time to adopt something more modern like Lua.

Thanks for making this topic thread *so* useful and interesting. I think we may have a CMUD "breakthrough" on our hands here!

(And Nick, if you want to keep posting about your personal experiences implementing Lua, I love reading your posts!)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Jun 19, 2007 7:10 pm   
 
Btw, I should also remind people that I still intend to implement the various zApp libraries as both COM-based and direct Lua libraries. For example, the string library would be a table with stuff like str.pos, and the web library would have stuff like net.get, etc. Take a look at the zApp Manual and look at the String, Math, Web, XML, etc libraries that are listed there. Since I already have the COM version of these libraries for zApp, it's pretty easy to make them available to CMUD, and shouldn't be hard to make available to Lua from what I've been reading here.

Some of the routines overlap existing zScript functions, but I'm not sure that really matters much.
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Tue Jun 19, 2007 8:09 pm   
 
There's a free book available online that's a pretty good intro to Lua. The only thing that's really out of date is the way a variable number of parameters passed to a function is handled. There's a changelog between 5.0 and 5.1 in the reference manual anyway.
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Tue Jun 19, 2007 9:02 pm   
 
Fang Xianfu wrote:
My only gripe with Lua normally is the way that string.match returns a variable number of arguments instead of a table like lrexlib (and I think MUSHClient does it that way as well, returning a table with 1-n for each capture, and the names of the captures as well if they're applicable). The only way I can think of to capture that variable number of arguments without knowing how many captures there are is to pass the result of the string.match to a function that takes a variable number of arguments and then loop through them all.


MUSHclient indeed returns a table, to a trigger, of all the matches - a trigger uses PCRE, and it mucks around finding the names (if any) of each match, so they might appear twice. eg. match 2 is the mob name, and "mobname" is also the mob name.

Fang Xianfu wrote:

Please don't sandbox Lua! ...

I say this mainly because writing to/reading from files is how I intend to export user settings so they're not lost over updates.


Yes, exporting tables from Lua to a text file, and re-importing them is quite simple, and a good way of saving user stuff. Lua was initially designed as a fancy way of getting ".ini" type stuff into programs.
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Tue Jun 19, 2007 9:30 pm   
 
Zugg wrote:

But it sounds like there is a *lot* of flexibility in how I implement this. Since Lua has optional arguments, that opens up a whole new way to handle things (such as an optional argument for %additem to determine whether dups should be removed).


Yes, the flexibility is certainly there. As my example shows, every Lua function (written in C to interface with Lua) actually has one argument. I guess it would be very simple therefore to write a Delphi wrapper for it, as all that has to do is make a wrapper to a function that takes one argument. So, first problem solved.

Then, inside the function, you extract out the arguments by doing things like luaL_checkstring (number, boolean, etc.). Presuming you also have wrappers for those in Delphi, then that is also very simple.

You have the flexibility of checking whether an argument exists at all (by seeing how many arguments there are), and if it exists, what type it is. So, something like a trailing, optional, boolean is certainly easy to do. You can even do something like "if the first argument is a number, process argument 2 as a string, but if it is a string, assume argument 1 is the string". In other words, an optional leading argument. I wouldn't get carried away doing that sort of thing, but it is possible.

Returning multiple results is equally easy, you simply "push" the things you want to return (like in my earlier example) and then return a number indicating how many things you pushed.

You can also create anonymous tables on the fly, so it is quite easy to return a table. So, like in the pattern-matching example, you can build up a table of all the match strings, and simply return one thing - the table.

If you are going to implement Lua you might want to think about where your (CMUD) functions are going to go. There are 3 main ways you can go about it:


  • Add them into global variable space. This effectively puts them at the same "level" so-to-speak as stuff like "print" or "dofile".

    The disadvantage of that is you pollute the global variable space with lots of things that are specific to your application. The advantage is that scripts can just call them directly. eg.

    Code:

    additem (a, b, c)


    There is also a potential disadvantage that if someone creates a global variable called "additem" it replaces your function.

  • Add them into their own table or tables. This isolates them into a logical place, and lets you handle name collisions better. For example you mentioned having a function "additem" and a command "additem". This is neater, but forces scripters to use the table name. eg.

    Code:

    cmd.additem (a, b, c)


    That's not a big overhead, and Lua scripters are getting used to that, as they find string functions in the string library, io functions in the io library, and so on. (eg. you do "string.find (a, c, d)", and "io.open (x, y, z)" - note the table name before the function name).


  • Add them into their own table, but add a "metatable" to the global address space. This is what I have done in MUSHclient for the "global" script commands that scripters were used to finding in global space in (say) VBscript. How this works is this: if you use a MUSHclient script function like Note, you can type:

    Code:

    world.Note ("hi there")


    That pulls the function directly from where it was installed - the "world" table - which contains all world-related functions.

    However you can omit the "world." like this:

    Code:

    Note ("hi there")


    What happens is Lua looks for the variable "Note" in global address space (ie. the global environment table, to be technical - since every variable is in a table of some kind, even global variables).

    It fails to find it. It then sees if the global table has a metatable. It finds the metatable. It looks in the metatable for a function or table named "__index". It finds that. The "__index" function can then do its own lookup to find the "real" Note function. Or it can simply be the "real" table to look in. Thus, armed with the redirection information, it eventually finds the Note function.

    Slightly more complex (you only do it once as the client implementor), but it makes it easier for scripters, who can "pretend" that all of your client functions are at global scope. It is up to you if you want to go down that route. If you think your scripters would prefer to just type something like "AddTrigger" rather than "cmud.AddTrigger" or whatever you name it, then that is probably the way to go.



Zugg wrote:

Rather than just converting existing CMUD commands/functions straight into Lua, it sounds like this is a better opportunity to implement the CMUD commands/functions in a way that is better suited for Lua. In other words, the Lua version of a command doesn't need to be exactly like the CMUD version.


Exactly, and I have also done that in places where I thought I may as well make use of Lua's features. Things like optional arguments, or letting the user supply a table as an argument. Again, you can detect if they have done that, and branch appropriately.

Zugg wrote:
But it sounds like I need to start messing with Lua and learn more about how it likes to do things, and how stuff like tables work, so that the CMUD implementation will allow the Lua scripts to work very efficiently. I'll probably also read some of the MUSHclient examples on how Nick did some of this stuff.



Feel free to browse the Lua part of my forum for information about that:

http://mushclient.com/forum/?bbtopic_id=113

Zugg wrote:

Thanks for making this topic thread *so* useful and interesting. I think we may have a CMUD "breakthrough" on our hands here!


Glad it has been helpful. You know World of Warcraft uses Lua as its client scripting language, so it must be pretty mature for them to use it. They use it for core client functionality, plus you can write plugins (addons they call them) to extend their client as you like.
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Tue Jun 19, 2007 9:46 pm   
 
Zugg wrote:
Quote:
Functions can return multiple things

Wow, that's really cool! I've wanted a language to do that for a long time. That feature alone will sell me on Lua.


I'll just point out one other thing about the multiple return idea. Lua uses this a fair bit. Say you have a function that can succeed (eg. in opening a file), or fail (file not found). That is an example, basically it applies to anything that can fail.

What you can do is return, on success, the thing the user wanted (eg. the file handle, or whatever).

But on failure, you return nil (first argument), and the error message (second argument). In Lua "nil" is a "there is no value here" value.

Let's illustrate with an example that finds, say the number of hp a named mob has:

Code:


mob_table = {
   kobold = 42,
   naga = 55,
  }

function get_hp (name)

  local hp = mob_table [name]

  if hp then
    return hp  -- success
  end -- if

  return nil, "mob " .. name .. " not found" -- failure
end -- function get_hp


kobold_hp = assert (get_hp ("kobold"))  --> 42
naga_hp = assert (get_hp ("naga"))  --> 55
snake_hp = assert (get_hp ("snake"))  --> error raised "mob snake not found"


This slightly contrived example shows how a function can indicate its success or failure by returning nil or not, and how the caller can detect that, either by directly testing for nil, or passing the function result to the assert function.

The function assert raises an error if its first argument is nil, the error message is the second argument.

Otherwise, it returns the first argument, so you can use that in an assignment statement.
Reply with quote
Guinn
Wizard


Joined: 03 Mar 2001
Posts: 1127
Location: London

PostPosted: Tue Jun 19, 2007 9:52 pm   
 
Hoorah for MUSHclient becoming freeware, now we can get the benefit of Nick Gammon's experience since money doesn't come into it anymore Mr. Green
Keep going this way and you'll be a Guru soon - now wouldn't that be weird to see on the boards
_________________
CMUD Pro, Windows Vista x64
Core2 Q6600, 4GB RAM, GeForce 8800GT
Because you need it for text... ;)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Tue Jun 19, 2007 11:12 pm   
 
I'll probably have each library use its own table. For example, when implementing the zApp string library, I'll use a table called "str". So it will look just like the same syntax as calling a COM object, except that they would be directly implemented in Lua.

E.g., calling str.pos would call the "pos" string function in Lua, VBScript, JScript, etc. In VBScript and JScript it gets implemented as a "str" COM object with "pos" being a COM method. In Lua, "str" is a table and "pos" is a function in that table. So Lua is faster because it doesn't get the COM overhead.

For the main CMUD commands/functions, I'd like to standardize on a "zs" table name and also make that work the same as a COM object. As I've mentioned before, I want to deal with the function/command name conflicts and try to remove them so that you don't need to specify a command or function. A command just becomes a function that doesn't return a value (or returns nil or whatever Lua requires).

I might make "zs" a metatable, but I'm still a bit undecided. I've been burned in zMUD by allowing some sloppy programming. It might just be better to force people to always use "zs.whatever" instead of letting them remove the "zs.". Then it's always clear if you are calling something built into Lua, or that you are calling a zscript command/function.

I'll still override some of the builtin Lua stuff, such as "print". But mostly I think it's better to force scripters to prefix their functions with the proper table name to make their scripts more readable and supportable.

I also need to decide how to handle other CMUD settings, such as aliases, variables, triggers, etc. It would be nice to put each into their own table. So you could refer to "var.myvar = 123". Not sure if this will conflict with an existing Lua keyword though. Also, I'm not sure how to handle nested tables. For example, let's look at a trigger. Someone might want to do this:
Code:
trigger.mytrigger.priority = 10

to set the priority of the "mytrigger" trigger. You can imagine properties such as "name", "pattern", and various trigger options. In a sense, trigger.mytrigger becomes a COM object with properties and methods. I guess in Lua this would be handled as a table, but I'd need to find out how to dynamically generate tables for all of the aliases, triggers, etc.

I also wonder how I would implement a "default property". With COM, you can mark a property as the default. So if VAR is a COM object, you can say "VAR = 123" and if "Value" is the default property, it's the same as saying "VAR.Value = 123". It's a nice feature for saving some typing. So, in Lua, I'd like something similar:
Code:
var.myvar = 123
var.myvar.type = "int"

Kind of a contrived example, but you see what I mean. In the second case we are specifying the property of the variable to set (the "type" of the variable in this case). In the first example, we are setting the "Value" property as if that were the default property.

So, it will probably take some experimenting for me to see how to implement this kind of stuff in Lua.
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Tue Jun 19, 2007 11:37 pm   
 
Default properties are implemented in Lua using metatables. It's a bit complicated to explain (the book I linked does a pretty good job) but it uses the same principle as Nick's reassignment of _G.whatever to world.whatever. You'd probably need to use a proxy table to make sure that references to var.whatever always try to reference a nonexistent key and so call the metatable.
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Jun 20, 2007 4:33 am   
 
OK, I just finished the first skimming of the Lua book that Fang gave a link to (and have already ordered the 2nd Edition via Amazon). It gives me a lot to think about while we are out of town the next few days. My brain is just swimming with ideas and possibilities. I think this is going to be a lot of fun to play with.

The chapter on the C API reflects exactly the Delphi interface that exists. So all of the examples of using C will directly convert to using Delphi with no problem.

I think one of the keys to fully integrating Lua into CMUD is to do what Nick did in MUSHclient: allow Lua scripts to be entered from the command line. Right now, the CMUD command line is always parsed for zScript. But to make Lua truly integrated, I will need to add an option where the command line can be interpreted as a Lua "chunk". Maybe a preference option, or maybe some sort of special syntax (like a line prefix character). Any suggestions on how you'd like to see this done would be helpful.

But once you can enter Lua on the command line, and any trigger, alias, etc can contain a Lua script, then Lua will be completely integrated and just as good (if not better) as using zScript itself.

And I just love the tables in Lua. In fact, relating to another post about the inefficiency of database variables in zMUD/CMUD, I'm seriously considering implementing database variables as Lua tables internally for improved performance. I might not do this right away, but once Lua is a standard part of the CMUD distribution then it might make a lot of sense to do this.

A couple of things bother me just a bit. I still don't like case-sensitive languages in general. But that's just one of those personal things I need to get over. It's hard to break 25+ years of using a non-case sensitive language. I also don't care for the ~= operator for "not equal". Seems like they should have stuck with the more standard != or <> instead.

Also, it wasn't clear if you accidentally used:

if (a = 1) then whatever

whether it would execute an assignment instead of performing a logical test. I *hate* the fact that C (and PHP) allows assignments like this within IF statements. It has caused me numerous headaches. In one PHP application I was checking to see if the UserLevel was set to -1 (which indicates Admin status) and did:

if ($UserLevel = -1) {allow admin stuff}

which, of course, resulted in ALL USER ACCESS being granted admin status!!! Huge security issue just because of a simple syntax error. So I really hate assignments in IF statements. I see that Lua has == for logical and = for assignment, but I'm hoping that doing an assignment when an expression is expected causes a syntax error or something.

I'd also like to have seen it treat 0 and "" as false values in logical statements...that's going to cause me to need to write a lot of support code to convert the return result of CMUD functions into NIL instead of 0 or "". But it's something that is solveable...just more work.

Otherwise it seems nice. Missing a few things such as a "case" or "select" statement. But I might be able to extend it so that it has access to the new #switch statement in CMUD. I really like how it handles functions.

Anyway, feel free to keep this discussion going. I'll post again when we get back next Monday.
Reply with quote
Thinjon100
Apprentice


Joined: 12 Jul 2004
Posts: 190
Location: Canada

PostPosted: Wed Jun 20, 2007 5:06 am   
 
Just a quick response, Zugg... but if you've got the "Parsing On/Off" indicator next to the command line, as well as my beloved Ctrl+R shortcut for parsing, why not make it into a tri-state toggle? zScript > Lua > No Parsing... with perhaps a small tooltip over the icon that pops up when it's changed saying "zScript" "Lua" "Parsing Off" depending on what state it's entering.

Just a quick thought :)
_________________
If you're ever around Aardwolf, I'm that invisible guy you can never see. Wizi ftw! :)
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Wed Jun 20, 2007 6:13 am   
 
Zugg wrote:

I also wonder how I would implement a "default property".
Code:

var.myvar = 123
var.myvar.type = "int"



I don't think you would - this is a bit of a hackish idea anyway (in COM, not your idea).

Something like your example would simply replace the myvar table with 123, not search around for some default thing it can put 123 into.

I'm not sure it is really required - I would try to get away from trying to emulate everything COM does.

Zugg wrote:

For example, let's look at a trigger. Someone might want to do this:
Code:

trigger.mytrigger.priority = 10

to set the priority of the "mytrigger" trigger.


You might want to read up on userdata - that would conceivably let you create a userdatum that represents an internal thing (eg. a trigger), and then have functions that modify them. I'm a bit leery of such things, because if the underlying object gets deleted then the userdata can become invalid.

My approach has simply been to have simple functions that look up things by name, eg.

Code:

SetTriggerOption ("mytrigger", "match", "north")


This sets the "match" property to the word "north" for the trigger whose name is "mytrigger".

Now this is safe because it is atomic - if the trigger doesn't exist then it simply fails cleanly.

However if you do something fancier like this:

Code:

t = GetTriggerOption ("mytrigger")
t:SetOption ("match", "north")


Then you have the potential problem that t might be deleted between the two instructions - I mean, in a more obscure script.

The colon above is intentional, it effectively makes "t" the "self" parameter to the call. Effectively it generates this:

Code:

SetOption (t, "match", "north")


Last edited by Nick Gammon on Wed Jun 20, 2007 6:31 am; edited 1 time in total
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Wed Jun 20, 2007 6:28 am   
 
Zugg wrote:

Also, it wasn't clear if you accidentally used:

if (a = 1) then whatever

whether it would execute an assignment instead of performing a logical test.


No, such a syntax is not valid.

Lua does not allow side-effects in statements like that.

Zugg wrote:

I'd also like to have seen it treat 0 and "" as false values in logical statements...that's going to cause me to need to write a lot of support code to convert the return result of CMUD functions into NIL instead of 0 or "". But it's something that is solveable...just more work.


I quite like it when you get used to it. For example, if you query a function for a string, it can return nil if the string isn't found. However the string "" is a perfectly valid string. Same with zero.

You mean logical functions? If they are ordinary functions you simply document that they return 0 on failure, and let the scripter handle it. However if it is something that is supposed to return true or false, then you just need a wrapper around the thing that pushes the function result.

Here is the sort of thing you might do:

Code:

  if (str.IsEmpty ())
    lua_pushnil (L);
  else
    lua_pushstring (L, str);  // result


Simply make that into a small helper function and use it in those cases.

Zugg wrote:

Otherwise it seems nice. Missing a few things such as a "case" or "select" statement. But I might be able to extend it so that it has access to the new #switch statement in CMUD. I really like how it handles functions.


You know what? I haven't missed them. I have done a fairly lengthy forum post about how to roll your own. See:

http://mushclient.com/forum/?id=6219

Effectively you make a table of selectors, and what to do if they match, which is roughly the same work you do when making a case statement - put a bit of syntax around the selectors.

However, the sort of cases where you might need to branch and do different things depending on a variable, is simply handled by a straight table. Remember, table values can be inlined functions, so you don't have to jump all over the place to see what it is doing.

This example will illustrate:

Code:

switches = {
  a = function ()
      print ("case a reached")
      end,

  b = function ()
      print ("case b reached")
      end,
 
  c = function ()
      print ("case c reached")
      end,
  } -- end table

myvar = "b"  -- test

f = switches [myvar]

if f then
  f ()
else
  print "default case taken"
end -- if


We have used the table lookup functionality which is already in Lua to make the decision for us. If we need to execute a function we can do that. If, as is sometimes the case, we merely want to convert a number to a string or vice-versa, then the value can simply be the value for that key in the table.
Reply with quote
Nick Gammon
Adept


Joined: 08 Jan 2001
Posts: 255
Location: Australia

PostPosted: Wed Jun 20, 2007 6:40 am   
 
Zugg wrote:
A command just becomes a function that doesn't return a value (or returns nil or whatever Lua requires).


Lua is quite flexible in this respect. There is no distinction between commands and functions. A function can return results that the caller ignores. Alternatively, you can request more results than the function generated:

Code:

a, b, c, d, e = math.abs (-1)
print (b)  --> nil (everything except "a" will be nil)


You can force the discarding of extra results, which is sometimes helpful. Take this example:

Code:

print (string.gsub ("llama", "l", "m"))  --> mmama 2


We are actually printing the changed string *and* the number of replacements (the second result from the function).

We may not want this, so we enclose the function call in parentheses:

Code:

print ((string.gsub ("llama", "l", "m")))  --> mmama


The extra brackets force only the first argument to be taken.
Reply with quote
Seb
Wizard


Joined: 14 Aug 2004
Posts: 1269

PostPosted: Thu Oct 04, 2007 1:38 am   
 
BTW, if you already have Python installed (not the ActiveState variety), or you just prefer to get it from the Python's mouth, as it were, instead of a different company, and you want to use Python in CMUD, WSH, IIS, or IE, you can just install pywin32. You probably need to run pyscript.py afterwards - I can't remember, it was so long since I did it. Anyway it is located here: Python2X\Lib\site-packages\win32comext\axscript\client\pyscript.py
Both free btw.
Reply with quote
emnaki
Wanderer


Joined: 12 May 2007
Posts: 59

PostPosted: Thu Oct 04, 2007 1:49 am   
 
Since this topic was brought up again, I like to ask what is the advantage of scripting in Lua as opposed to other scripting languages in WSH in CMud 2.0? Are there thing that you can do in Lua that you cannot through WSH? Is it a lot faster? I know Python already so it would be nice if I can stick with it, but I want to know what I am missing out if I do not use Lua.
Reply with quote
Fang Xianfu
GURU


Joined: 26 Jan 2004
Posts: 5155
Location: United Kingdom

PostPosted: Thu Oct 04, 2007 2:06 am   
 
Lua uses the new interface - languages using WSH use the old interface. Basically, this means that Lua can execute zScript commands and functions and perform lots of other operations that aren't available to WSH, which can only manipulate variables. In the future, the interface used by WSH will be updated to match (at least most of) what the current Lua interface can do.
_________________
Rorso's syntax colouriser.

- Happy bunny is happy! (1/25)
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » CMUD General Discussion All times are GMT
Goto page Previous  1, 2, 3  Next
Page 2 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

© 2009 Zugg Software. Hosted by Wolfpaw.net