Reynold's profile飘零风雨亭BlogLists Tools Help

Blog


    January 14

    lua写文件(备忘)

    lua写文件(备忘):
     
    function save(n)
     io.output("save"..n)
     serialize(contex)
     io.close()
    end
    -- from the lua tutorials
    function serialize (o)
        if type(o) == "number" then
        io.write(o)
        elseif type(o) == "string" then
        io.write(string.format("%q", o))
        elseif type(o) == "table" then
        io.write("{\n")
        for k,v in pairs(o) do
            io.write("  [")
            serialize(k)
            io.write("] = ")
            serialize(v)
            io.write(",\n")
        end
        io.write("}\n")
        else
        error("cannot serialize a " .. type(o))
        end
    end
    --------------------------------------------
    October 17

    面向对象的LUA编程(转)

    面向对象的Lua编程(一) ---- 对象的表示方法
    1.前言
    偶又看到有人问Lua了, 哇卡卡, 看来初级教程不够用了,
    偶下定决心要把Lua的面向对象使用也写完.
    如果偶的游戏中要用到Lua了, 但是游戏中的数据都是以对象的形式组织的,
    怎么办呢? 于是就需要让Lua也支持面向对象的编程方式.
    Lua本身并不是一个面向对象的语言, 当然你可以修改它的源代码全它支持
    面向对象. 但偶这里讨论的是在不修改代码的前提下, 提供面向对象的支持,
    由于它采用table作为数据格式, 使得它变得异常灵活, 偶们可以为它加入
    对象的概念.
    2.相关库资料
    现在有一些现成的库可以提供面向对象的支持, 比如LuaBind, toLua, 还有
    第二人生的一名程序员也写过Lua的C++ Wrapper, 网址如下:
    http://www.d2-life.com/LBS/blogview.asp?logID=41
    用现成的库可以大大减少开发时间, 但是一旦出错了调试起来也挺麻烦的,
    你需要对这些库有比较深入的了解. 偶将对Lua中面向对象的实现做个简单介绍.
    3.例子
    这是一个Luawiki中metamethod实现string对象的例子, 偶就不把>去掉了, 还是自己打一遍
    比较容易理解.

    > String = {}
    > mt = {}
    >
    > function String:new(s)
    >>   return setmetatable({ value = s or '' }, mt)
    >> end
    >
    > function String:print()
    >>   print(self.value)
    >> end
    >
    > mt.__add = function (a,b) return String:new(a.value..b.value) end
    > mt.__mul = function (a,b) return String:new(string.rep(a.value, b)) end
    > mt.__index = String -- redirect queries to the String table
    >
    > s = String:new('hello ')
    > s:print()
    hello
    > a = ((String:new('hello ') + String:new('Lua user. '))*2)
    > a:print()
    hello Lua user. hello Lua user.
     

    4.程序说明
    首先来介绍下什么是metamethod. metamethod类似于C++中的运算符重载, 比如程序中
    mt是偶们的metatable, mt.__add就是+运算符, mt.__mul就是*运算符, mt.__index是.运算符,
    与C++不同的是偶们可以在运行时动态改变与object相关联的metatable, 用setmetatable这个方法.
    关于具体的metatable说明请看http://lua-users.org/wiki/MetamethodsTutorial
    String是偶们定义的一个类, 它包括了new, print, +, *, . 五个方法, new和print是采用
    的普通的定义方法, 而+ * . 采用的是metatable重载定义.
    new方法: 使value=s, 若s为NULL, 则value='', 同时把metatable与这个table挂钩.
    print方法: 打印value值, self是指向自己的一个引用
    __add方法(+): 把a.value与b.value连接起来, ..是连接两个字符串的操作
    __mul方法(*): 把a.value重复N遍
    __index方法(.): 使对.操作的请求与String类的相同
    下面是一个直接定义__index返回值的例子, 现在偶们的String类没有length属性,
    于是使用s.length就会出错, 如果偶们在__index中定义了当key='length'的时候
    返回什么值, 就不会出错了.
    > print(s.length) -- no such key in the String table class
    nil
    > mt.__index = function (t,key)
    >>   if key == 'length' then return string.len(t.value) end
    >> end
    > print(s.length) -- new __index event calls the above function
    6
    5.心得体会
    看, 在Lua里面加上对对象的支持并不十分复杂吧, 其实不用其他任何库, 偶们自己都
    可以实现, 而且效率会比较高. 如果你对效率比较看中的话, 还是不要用库的好, 自己
    写吧.
    面向对象的Lua编程(二) ---- 对象的继承
    参考网址:http://lua-users.org/wiki/InheritanceTutorial
    1.方法的调用
    首先说一下方法的调用, 上次用到却没有讲.
    类的方法调用有两种形式,一是用:, 还有一种是用.
    用:时隐含了方法的第一个参数,就是self
    而用.时需要把所有的参数都写出来
    例如
    c.add(self, a, b);
    c:add(a, b);
    就是相同的.
    而定义时也是这样, 如下两种定义是一样的
    function foo.add(self, x) self.value = self.value+x end
    function foo:add(x) self.value = self.value+x end
    而_index运算符既包括了.又包括了:, 所以重载_index两个都会变
    2.基类
    首先我们定义一个基类,这个类并无啥特别之处

    SimpleClass = {}
    SimpleClass_mt = { __index = SimpleClass }
    -- Create方法建立类的一个新的实例
    -- 基本等同于New运算符
    function SimpleClass:create()
      local new_inst = {}   -- 新的实例
      setmetatable( new_inst, SimpleClass_mt ) -- 所有实例共用一个metatable
      return new_inst
    end
    -- 以下是SimpleClass中的一些方法
    function SimpleClass:className()
      print( "SimpleClass" )
    end
    function SimpleClass:doSomething()
      print( "Doing something" )
    end
     
     
    3.生成子类的方法
    下面定义了一个全局函数,用于生成子类,该函数的参数是父类, 返回对象是生成的子类

    -- Create a new class that inherits from a base class
    --
    function inheritsFrom( baseClass )
      -- 以下这部分与SimpleClass相同
      -- Create the table and metatable representing the class.
      local new_class = {}
      local class_mt = { __index = new_class }
      -- Note that this function uses class_mt as an upvalue, so every instance
      -- of the class will share the same metatable.
      --
      function new_class:create()
        local newinst = {}
        setmetatable( newinst, class_mt )
        return newinst
      end
      -- 以下这部分是实现继承的关键
      -- 这里重载了__index运算符,使它指向了baseClass, 也就是父类
      -- 于是父类中的所有方法子类中都可以使用,同时,子类也可以重载
      -- 父类中的方法.
      --
      if baseClass then
        setmetatable( new_class, { __index = baseClass } )
      end
      return new_class
    end
     

    4.例子
    以下是从SimpleClass派生出SubClass的例子

    > -- Create a new class that inherits from SimpleClass
    > SubClass = inheritsFrom( SimpleClass )
    >
    > -- override className() function
    > function SubClass:className() print( "SubClass" ) end
    >
    > -- Create an instance of SimpleClass
    > simple = SimpleClass:create()
    >
    > simple:className()
    SimpleClass
    >
    > simple:doSomething()
    Doing something
    >
    > -- Create an instance of SubClass
    > sub = SubClass:create()
    >
    > sub:className() -- Call overridden method
    SubClass
    >
    > sub:doSomething() -- Call base class method
    Doing something
     
    以上这个例子有几点需要注意的:
    * 偶们的类与类的实例其实都是table, 但千万不要搞混
    * inheritsFrom产生的类能重载父类的方法, 因为inheritsFrom产
    生的子类的__index = baseClass
    * 由SubClass:create()产生的sub的__index = new_class, 而new_class就是偶们的
    SubClass. 而SubClass的__index = baseClass, 所以偶们的实例可以使用父类方法
    * 重载了父类的方法, 只对子类有影响,父类对象仍然调用的是原方法, 这点与C++相同
    但是光从lua看比较容易误解,以为重载的className把原来父类的className替换掉了,
    其实只是子类className这个标识符指向的函数变了而已.
     
    龚辟愚 2006-4-17 10:55:31
     

    LCC-Win32 下载地址
    Turbo C For Windows 作者
     
     
                543210-1-2-3-4-5     1   
     
    龚辟愚
    站长
    发帖: 520
    积分: 1000000385
    注册: 2004-5
    来自:湖南
    性别: 美女
    状态: offline
    2006-4-17 10:57:07
                           
    --------------------------------------------------------------------------------
    Re:面向对象的LUA编程
    面向对象的Lua编程(三) ---- tolua++使用入门
    1.简介
    前两篇文章说了Lua与面向对象的基础, 对于普通用户而言,
    自己开发一套Lua的面向对象的脚本接口, 工作量不小, 而且也非必须,
    因为现在有很多种第三方的软件包为Lua提供面向对象的特性,
    比如tolua, luabind, lua++, luna, ...
    这次偶们来介绍一下tolua/tolua++,
    tolua++是tolua的一个扩展包, 它的网址在http://www.codenix.com/~tolua/,
    它是用来结合C/C++与Lua的一个包. 对于提高开发效率是有帮助的.
    它包括了tolua的所有功能.
    2.tolua与C/C++概览
    首先看一看tolua与C的结合,
    例如偶们把如下的C头文件导入了tolua

    #define FALSE 0
    #define TRUE 1
    enum {
    POINT = 100,
    LINE,
    POLYGON
    }
    Object* createObejct (int type);
    void drawObject (Object* obj, double red, double green, double blue);
    int isSelected (Object* obj);
     
     
    下面是一个在Lua中使用的范例
    ...
    myLine = createObject(LINE)
    ...
    if isSelected(myLine) == TRUE then
    drawObject(myLine, 1.0, 0.0, 0.0);
    else
    drawObject(myLine, 1.0, 1.0, 1.0);
    end
    ...
     
    再看一下tolua与C++的结合,
    例如有如下一个C++头文件
    #define FALSE 0
    #define TRUE 1
    class Shape
    {
    void draw (void);
    void draw (double red, double green, double blue);
    int isSelected (void);
    };
    class Line : public Shape
    {
    Line (double x1, double y1, double x2, double y2);
    ~Line (void);
    };
     
    那么在Lua中使用时会是什么情况呢?
    是这样的:
    ...
    myLine = Line:new (0,0,1,1)
    ...
    if myLine:isSelected() == TRUE then
    myLine:draw(1.0,0.0,0.0)
    else
    myLine:draw()
    end
    ...
    myLine:delete()
    ...
     
    怎么样, 是不是很方便啊, 心动不如行动, 下面偶就来说说怎么样使用tolua
    3.tolua的编译方法
    首先要下载tolua++, 地址在http://www.codenix.com/~tolua/tolua++-1.0.3.tar.bz2
    目前的最新版本是1.0.3, 是2004年9月20日新发布的.
    tolua++的编译比较奇特, 你需要安装如下组件
    python ( http://www.python.org ;)
    SCons ( http://www.scons.org ;)
    Lua ( http://sourceforge.net/project/showfiles.php?group_id=32250&;package_id=115604 )
    偶想你们用的都是windows机器, 建议下载windows binary版本, 比较省事
    装好编译环境以后就可以正式编译了,
    * 首先要编译Lua, 上面那个就是VC6-VC7.1都可以用的工程文件
    * 在SConstruct中选择配置文件, VC选择config_msvc.py, linux选择config_linux.py,
    cygwin或其他unix-like可选择config_posix.py, 偶们啥都不用改
    * 把lua的库文件和头文件分别复制到lib目录和include目录
    * 打scons all
    会出现错误, 因为有库和标准库冲突, 不过没关系把link改成
    link /nologo /OUT:bin\tolua++.exe /LIBPATH:lib /NODEFAULTLIB:LIBCMT tolua++.lib lua.lib lualib.lib src\bin\tolua.obj src\bin\toluabind.obj
    然后再运行一遍就可以了.
    如果大家怕麻烦, 可以下载偶编译好的tolua++ 1.0.3
    4.tolua的使用方法
    tolua由两部分组成, 一个是tolua.exe用于产生binding文件, 另一个是库文件, 需要在编译时候与主程序链接.
    tolua.exe常的用法是:
    tolua -o myfile.c myfile.pkg
    myfile.pkg是输入文件, 而myfile.c是输出的binding文件.
    偶们只要把myfile.c加入偶们主程序的工程文件编译, 偶们主程序执行Lua脚本的时候,
    就可以在Lua中使用pkg中定义的数据结构,函数,或者类了.
    src/tests目录下有tolua的一些例子, 可以先学习一下,
    偶计划下次结合一些例子来说说pkg文件的定义.

     
     
    April 28

    Tcl在程序中的使用方法

    Tcl在程序中的使用方法:
    好久没用Tcl了(我们现在的工程用lua), 最近下了个程序参考些内容,没想到跑到tcl时当机了,
    于是想改一下,但是忘记语法了,到网上查了一下,很难找到所需。。。(大都是脚本用法,晕倒)
    后来把以前工程拿来看了一下,顺便把Tcl的用法记下来放在这里供查询吧。
     
    ///////////////////////////////////////////////////////////////////////////////////
    flipcode@msn.com
    #include
    //#include
     
    // 1. 创建解释器:
    Tcl_Interp *tcl_interp = Tcl_CreateInterp();
    // 2. 得到Tcl路径:
    char buf[512];
    strcpy(buf, "tcl8.4");
    Tcl_FindExecutable(buf);
    // 3. 关联到Tcl:
    Tcl_SetVar(tcl_interp,"tcl_library",(char*)buf, 0); // initialize tcl_library variable
    Tcl_Init(tcl_interp);   
    // 4. Tcl扩展:
    //Tcl_EvalEx(tcl_interp,"rename puts tcl_puts",-1,TCL_EVAL_DIRECT);
    //Tcl_CreateObjCommand(tcl_interp,"puts",tclcmd_Puts,(ClientData)this,NULL);
     
    // 5. 然后就可用了, 如执行一个文件:
    if ( Tcl_EvalFile( tcl_interp, config_file ) != TCL_OK ) {
       handle_error( 1, "error evalating %s: %s", config_file,
         Tcl_GetStringResult( tcl_interp ) );
       
    }
    ///////////////////////////////////////////////////////////////////////////////////

    更多参见nebula的nTclServer类,这个类还是非常简单的。
    大概如下(注意这些都是我以前修改过的, 跟nebula中的有些少不一样):
    // puts xxxxxxxxxxxxxxxxxxxxxxx
    int tclcmd_Puts(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST *objv)
    {
        int retval     = TCL_OK;
        bool is_stdout = true;
        // emulate puts behaviour but reroute output into Nebula kernel
        if (objc > 1) {
            bool newline = true;
            char *str = Tcl_GetString(objv[objc-1]);
            int i;
            for (i=1; i<(objc-1); i++) {
                char *s = Tcl_GetString(objv[i]);
                if (strcmp(s,"-nonewline")==0) newline = false;
                else if (strcmp(s,"stdout")!=0) is_stdout = false;
            }
            if (is_stdout) {
    //++            nKernelServer::getSingleton().Print(str);
    //            if (newline) nKernelServer::getSingleton().Print("\n");
            }
        }
        // hand control to original puts command
        if (!is_stdout) {
            retval = tcl_pipe_command(interp,"tcl_puts",objc,objv);
        }
        return retval;
    }
    //--------------------------------------------------------------------
    bool nTclServer::runFunction(const char *cmd_str, const char*& result)
    {
        result = 0;
        int errCode = Tcl_EvalEx(m_pInterp, (char *) cmd_str, -1, TCL_EVAL_DIRECT);
        Tcl_Obj *res = Tcl_GetObjResult(m_pInterp);
        result = Tcl_GetString(res);
        if (errCode == TCL_ERROR) {
    //        n_printf("*** Tcl error '%s'\n", Tcl_GetString(res));
    //        if (this->GetFailOnError()) {
    //            n_error("Aborting.");
    //        }
      tcl_reportError(this->print_error, result);
            return false;
        }
        return true;
    }
    bool nTclServer::runScriptString(const char *szScript, const char*& result)
    {
        result = 0;
        //int errCode = Tcl_EvalFile(this->interp, buf);
     int errCode = Tcl_EvalEx(m_pInterp, (char*)szScript, -1, TCL_EVAL_DIRECT);
        Tcl_Obj *res = Tcl_GetObjResult(m_pInterp); 
        result = Tcl_GetString(res);
        if (errCode == TCL_ERROR)
        {
    //      n_printf("*** Tcl error '%s' in file %s line %d.\n", result, fname, this->interp->errorLine);
    //      n_error("Aborting.");
      tcl_reportError(this->print_error, result);
            return false;
        }            
     return true;
    }
    bool nTclServer::runScriptFile(const char *fname, const char*& result)
    {
        char szFile[K_MAXPATH];
        result = 0;
     nKernelServer::getSingleton().getFileServer()->manglePath(fname,szFile,sizeof(szFile));
     // create and open file object
     string strBuf = "";
     bool bSuccess = true;
        kFile* file = nKernelServer::getSingleton().getFileServer()->newFileObject();
        if ( file && file->open(szFile, "r"))
        {
      file->seek(0, kFile::END);
      size_t len  = file->tell(); 
      if( 0 >= len )
       return true;
      strBuf.reserve( len );
      size_t iPos = 0;
      file->seek( 0, kFile::START );
      while (iPos < len) {
       char buf[1024];
       memset(buf, 0, sizeof(buf));
       size_t iBytesRead  = file->read(buf, sizeof(buf)-1);
       strBuf += buf;
       iPos += iBytesRead;
       if (iBytesRead == 0) {
        bSuccess = false;
        break;
       }
      }
      delete file;
        }
     else
     {
    //  n_printf("can't open script file %s!\n", fname);
      delete file;
      return false;
     }
           
     return runScriptString( strBuf.data(),result);
    }
    bool nTclServer::runCommand(kCmd *c, const char*& result)
    {
        c->rewind();
        int len = c->getNumInArgs();
        Tcl_Obj *tclCommand = Tcl_NewStringObj(c->in()->getS(), -1);
        for (int i = 0; i < len; i++) {
            Tcl_AppendToObj(tclCommand, " ", 1);
            Tcl_AppendObjToObj(tclCommand, KArg2TclObj(c->in()));
        }
        int errCode = Tcl_EvalObjEx(m_pInterp, tclCommand,  TCL_EVAL_DIRECT);
        Tcl_Obj *res = Tcl_GetObjResult(m_pInterp);
     result = Tcl_GetString(res);
        if (errCode == TCL_ERROR) {
    //        n_printf("*** Tcl error '%s'\n", Tcl_GetString(res));
    //        if (this->GetFailOnError()) {
    //            n_error("Aborting.");
    //        }
      tcl_reportError(this->print_error, result);
            return false;
        }
        return true;
    }
    //--------------------------------------------------------------------
    //--------------------------------------------------------------------
    int tclcmd_Unknown(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *CONST *objv)
    {
        int retval = TCL_ERROR;
        ....
        这里主要分析一些扩展的语法:如"obj.fuction", 调用自定义类函数。太多就不列了
    }
    July 07

    Lua文件处理

    一。文件读存:
    -- 似乎不支持大文件(不能超过9k?):
    function opensavetest()
       local file = io.open("c:\\in.lua", "r");
       if(file) then
       local data = file:read("*a"); -- i've also tried "*all" as described in PIL
       file:close();
       file = io.open("c:\\out.lua", "w");
       if(file) then
       file:write(data);
       file:close();
       end
       end
    end
    opensavetest();
     
    二。非循环表格处理(见program in lua):
    HERO = 1  Monster = 2  Building = 3 SumUnit = 4
    cha = {}
    cha[1] =
    {
     basic =
      {
       Name = "农民" ,       --角色名称
       cha_type = HERO        --角色模型类型
      } ,
     Combat =
      {
         acquire = 600.00 ,       --主动攻击范围
         basic_def = 10        --基础防御
      }
    }
    function serialize2( o)
     if type(o) == "number" then
      g_file:write(o)
     elseif type(o) == "string" then
      g_file:write(string.format("%q", o))
     elseif type(o) == "table" then
      g_file:write("{\n")
      for k,v in pairs(o) do
       g_file:write(" [")
       serialize2(k)
       g_file:write("] = ")
       serialize2(v)
       g_file:write(",\n")
      end
      g_file:write("}\n")
     else
      error("cannot serialize a " .. type(o))
     end
    end
    function savetest ()
     if g_file == nil then
          print("error int 'savetest()'");
          return;
       end
     g_file:write("cha = {}\n")
     g_file:write("cha[1] = {\n")
     serialize2( cha[1] ); 
     g_file:write("}\n")
    end
    g_file = io.open("c:\\tt.lua", "w");
    savetest();
    g_file:close()
     
    三。原program in lua中的保存带/不带循环的table
    12.1.1 保存不带循环的table
    我们下一个艰巨的任务是保存表。根据表的结构不同,采取的方法也有很多。没有一种单一的算法对所有情况都能很好地解决问题。简单的表不仅需要简单的算法而且输出文件也需要看起来美观。
    我们第一次尝试如下:
    function serialize (o)
     if type(o) == "number" then
      io.write(o)
     elseif type(o) == "string" then
      io.write(string.format("%q", o))
     elseif type(o) == "table" then
      io.write("{\n")
      for k,v in pairs(o) do
       io.write(" ", k, " = ")
       serialize(v)
       io.write(",\n")
      end
      io.write("}\n")
     else
      error("cannot serialize a " .. type(o))
     end
    end
    尽管代码很简单,但很好地解决了问题。只要表结构是一个树型结构(也就是说,没有共享的子表并且没有循环),上面代码甚至可以处理嵌套表(表中表)。对于所进不整齐的表我们可以少作改进使结果更美观,这可以作为一个练习尝试一下。(提示:增加一个参数表示缩进的字符串,来进行序列化)。前面的函数假定表中出现的所有关键字都是合法的标示符。如果表中有不符合Lua语法的数字关键字或者字符串关键字,上面的代码将碰到麻烦。一个简单的解决这个难题的方法是将:
    io.write(" ", k, " = ")
    改为
    io.write(" [")
    serialize(k)
    io.write("] = ")
    这样一来,我们改善了我们的函数的健壮性,比较一下两次的结果:
    -- result of serialize{a=12, b='Lua', key='another "one"'}
    -- 第一个版本
    {
    a = 12,
    b = "Lua",
    key = "another \"one\"",
    }
    -- 第二个版本
    {
    ["a"] = 12,
    ["b"] = "Lua",
    ["key"] = "another \"one\"",
    }
    我们可以通过测试每一种情况,看是否需要方括号,另外,我们将这个问题留作一个练习给大家。
    12.1.2 保存带有循环的table
    针对普通拓扑概念上的带有循环表和共享子表的table,我们需要另外一种不同的方法来处理。构造器不能很好地解决这种情况,我们不使用。为了表示循环我们需要将表名记录下来,下面我们的函数有两个参数:table和对应的名字。另外,我们还必须记录已经保存过的table以防止由于循环而被重复保存。我们使用一个额外的table来记录保存过的表的轨迹,这个表的下表索引为table,而值为对应的表名。
    我们做一个限制:要保存的table只有一个字符串或者数字关键字。下面的这个函数序列化基本类型并返回结果。
    function basicSerialize (o)
     if type(o) == "number" then
      return tostring(o)
     else  -- assume it is a string
      return string.format("%q", o)
     end
    end
    关键内容在接下来的这个函数,saved这个参数是上面提到的记录已经保存的表的踪迹的table。
    function save (name, value, saved)
     saved = saved or {}   -- initial value
     io.write(name, " = ")
     if type(value) == "number" or type(value) == "string" then
      io.write(basicSerialize(value), "\n")
     elseif type(value) == "table" then
      if saved[value] then  -- value already saved?
       -- use its previous name
       io.write(saved[value], "\n")
      else
       saved[value] = name -- save name for next time
       io.write("{}\n")  -- create a new table
       for k,v in pairs(value) do -- save its fields
        local fieldname = string.format("%s[%s]", name,
               basicSerialize(k))
        save(fieldname, v, saved)
       end
      end
     else
      error("cannot save a " .. type(value))
     end
    end
    举个例子:
    我们将要保存的table为:
    a = {x=1, y=2; {3,4,5}}
    a[2] = a  -- cycle
    a.z = a[1]  -- shared sub-table
    调用save('a', a)之后结果为:
    a = {}
    a[1] = {}
    a[1][1] = 3
    a[1][2] = 4
    a[1][3] = 5
    a[2] = a
    a["y"] = 2
    a["x"] = 1
    a["z"] = a[1]
    (实际的顺序可能有所变化,它依赖于table遍历的顺序,不过,这个算法保证了一个新的定义中需要的前面的节点都已经被定义过)
    如果我们想保存带有共享部分的表,我们可以使用同样table的saved参数调用save函数,例如我们创建下面两个表:
    a = {{"one", "two"}, 3}
    b = {k = a[1]}
    保存它们:
    save('a', a)
    save('b', b)
    结果将分别包含相同部分:
    a = {}
    a[1] = {}
    a[1][1] = "one"
    a[1][2] = "two"
    a[2] = 3
    b = {}
    b["k"] = {}
    b["k"][1] = "one"
    b["k"][2] = "two"
    然而如果我们使用同一个saved表来调用save函数:
    local t = {}
    save('a', a, t)
    save('b', b, t)
    结果将共享相同部分:
    a = {}
    a[1] = {}
    a[1][1] = "one"
    a[1][2] = "two"
    a[2] = 3
    b = {}
    b["k"] = a[1]
    上面这种方法是Lua中常用的方法,当然也有其他一些方法可以解决问题。比如,我们可以不使用全局变量名来保存,即使用封包,用chunk构造一个local值然后返回之;通过构造一张表,每张表名与其对应的函数对应起来等。Lua给予你权力,由你决定如何实现。

    Output Lua Table To Html File (转)

    Output Lua Table To Html File  (转)
     wiki
    来源:http://lua-users.org/wiki/OutputLuaTableToHtmlFile
     
     
    This sample code converts a Lua table into a html file that can be viewed by a browser showing different levels of the table nesting with different colors. The maximum level of nesting is set to 10, althought this can be easily tweaked for higher levels.
    dontspamme_samlie@yahoo.com

    Converts Lua Table to html output to table.html file
    function tohtml(x)
    ret = tohtml_table(x,1)
    writefile("table.html", ret)
    os.execute("table.html")
    return(ret)
    end
    Saves a string to file
    function writefile(filename, value)
    if (value) then
    local file = io.open(filename,"w+")
    file:write(value)
    file:close()
    end
    end

    Flattens a table to html output
    function tohtml_table(x, table_level)
    local k, s, tcolor local html_colors = { "#339900","#33CC00","#669900","#666600","#FF3300","#FFCC00","#FFFF00","#CCFFCC","#CCCCFF","#CC66FF",
    "#339900","#33CC00","#669900","#666600","#FF3300","#FFCC00","#FFFF00","#CCFFCC","#CCCCFF","#CC66FF" }
    local lineout = {} local tablefound = false
    if type(x) == "table" then
    s = ""
    k = 1
    local i, v = next(x)
    while i do
    if (type(v) == "table") then
    if (table_level<10) then
    lineout[k] = "<b>" .. flat(i) .. "</b>".. tohtml_table(v, table_level + 1)
    else
    lineout[k] = "<b>MAXIMUM LEVEL BREACHED</b>"
    end
    tablefound = true
    else
    lineout[k] = flat(i) .. "=" .. tohtml_table(v)
    end
    k = k + 1
    i, v = next(x, i)
    end
    for k,line in lineout do
    if (tablefound) then
    s = s .. "<tr><td>" .. line .. "</td></tr>\n"
    else
    s = s .. "<td>" .. line .. "</td>\n"
    end
    end
    if not (tablefound) then
    s = "<table border='1' bgcolor='#FFFFCC' cellpadding='5' cellspacing='0'>" ..
    "<tr>" .. s .. "</tr></table>\n"
    else
    tcolor = html_colors[table_level]
    s = "<table border='3' bgcolor='"..tcolor.."' cellpadding='10' cellspacing='0'>" ..
    s .. "</table>\n"
    end

    return s
    end
    if type(x) == "function" then
    return "FUNC"
    end
    if type(x) == "file" then
    return "FILE"
    end
    return tostring(x)
    end
    Flattens a table to string
    function flat(x)
    return toflat(x,1)
    end
    Flattens a table to string
    function toflat(x, tlevel) local s tlevel = tlevel + 1

    if type(x) == "table" then
    s = "{"
    local i, v = next(x)
    while i do
    if (tlevel < 15) then
    s = s .. i .. "=" .. toflat(v, tlevel)
    else
    s = s .. i .. "={#}"
    end
    i, v = next(x, i)
    if i then
    s = s .. ", "
    end
    end
    return s .. "}\n"
    end
    if type(x) == "function" then
    return "FUNC"
    end
    if type(x) == "file" then
    return "FILE"
    end
    return tostring(x)
    end
     
    --------------------------------------------------------------------------------
    FindPage · RecentChanges · preferences
    edit · history
    Last edited February 8, 2005 1:32 am GMT (diff)
    November 30

    HTML语法大全(转)

                        HTML语法大全

    --------------------------------------------------------------------------------
       卷标 , 属性名称 , 简介
     <! - - ... - -> 批注
     <!> 跑马灯
     <marquee>...</marquee>普通卷动
     <marquee behavior=slide>...</marquee>滑动
     <marquee behavior=scroll>...</marquee>预设卷动
     <marquee behavior=alternate>...</marquee>来回卷动
     <marquee direction=down>...</marquee>向下卷动
     <marquee direction=up>...</marquee>向上卷动
     <marquee direction=right></marquee>向右卷动
     <marquee direction=left></marquee>向左卷动
     <marquee loop=2>...</marquee>卷动次数
     <marquee width=180>...</marquee>设定宽度
     <marquee height=30>...</marquee>设定高度
     <marquee bgcolor=FF0000>...</marquee>设定背景颜色
     <marquee scrollamount=30>...</marquee>设定卷动距离
     <marquee scrolldelay=300>...</marquee>设定卷动时间
     <!>字体效果
     <h1>...</h1>标题字(最大)
     <h6>...</h6>标题字(最小)
     <b>...</b>粗体字
     <strong>...</strong>粗体字(强调)
     <i>...</i>斜体字
     <em>...</em>斜体字(强调)
     <dfn>...</dfn>斜体字(表示定义)
     <u>...</u>底线
     <ins>...</ins>底线(表示插入文字)
     <strike>...</strike>横线
     <s>...</s>删除线
     <del>...</del>删除线(表示删除)
     <kbd>...</kbd>键盘文字
     <tt>...</tt> 打字体
     <xmp>...</xmp>固定宽度字体(在文件中空白、换行、定位功能有效)
     <plaintext>...</plaintext>固定宽度字体(不执行标记符号)
     <listing>...</listing> 固定宽度小字体
     <font color=00ff00>...</font>字体颜色
     <font size=1>...</font>最小字体
     <font style =font-size:100 px>...</font>无限增大
     <!>区断标记
     <hr>水平线
     <hr size=9>水平线(设定大小)
     <hr width=80%>水平线(设定宽度)
     <hr color=ff0000>水平线(设定颜色)
     <br>(换行)
     <nobr>...</nobr>水域(不换行)
     <p>...</p>水域(段落)
     <center>...</center>置中
     <!>连结格式
     <base href=地址>(预设好连结路径)
     <a href=地址></a>外部连结
     <a href=地址 target=_blank></a>外部连结(另开新窗口)
     <a href=地址 target=_top></a>外部连结(全窗口连结)
     <a href=地址 target=页框名></a>外部连结(在指定页框连结)
     <!>贴图/音乐
     <img src=图片地址>贴图
     <img src=图片地址 width=180>设定图片宽度
     <img src=图片地址 height=30>设定图片高度
     <img src=图片地址 alt=提示文字>设定图片提示文字
     <img src=图片地址 border=1>设定图片边框
     <bgsound src=MID音乐文件地址>背景音乐设定
     <!>表格语法
     <table aling=left>...</table>表格位置,置左
     <table aling=center>...</table>表格位置,置中
     <table background=图片路径>...</table>背景图片的URL=就是路径网址
     <table border=边框大小>...</table>设定表格边框大小(使用数字)
     <table bgcolor=颜色码>...</table>设定表格的背景颜色
     <table borderclor=颜色码>...</table>设定表格边框的颜色
     <table borderclordark=颜色码>...</table>设定表格暗边框的颜色
     <table borderclorlight=颜色码>...</table>设定表格亮边框的颜色
     <table cellpadding=参数>...</table>指定内容与网格线之间的间距(使用数字)
     <table cellspacing=参数>...</table>指定网格线与网格线之间的距离(使用数字)
     <table cols=参数>...</table>指定表格的栏数
     <table frame=参数>...</table>设定表格外框线的显示方式
     <table width=宽度>...</table>指定表格的宽度大小(使用数字)
     <table height=高度>...</table>指定表格的高度大小(使用数字)
     <td colspan=参数>...</td>指定储存格合并栏的栏数(使用数字)
     <td rowspan=参数>...</td>指定储存格合并列的列数(使用数字)
     <!>分割窗口
     <frameset cols="20%,*">左右分割,将左边框架分割大小为20%右边框架的大小浏览器会自动调整
     <frameset rows="20%,*">上下分割,将上面框架分割大小为20%下面框架的大小浏览器会自动调整
     <frameset cols="20%,*">分割左右两个框架
     <frameset cols="20%,*,20%">分割左中右三个框架
     <分割上下两个框架
     <frameset rows="20%,*,20%">分割上中下三个框架
     <! - - ... - -> 批注
     <A HREF TARGET> 指定超级链接的分割窗口
     <A HREF=#锚的名称> 指定锚名称的超级链接
     <A HREF> 指定超级链接
     <A NAME=锚的名称> 被连结点的名称
     <ADDRESS>....</ADDRESS> 用来显示电子邮箱地址
     <B> 粗体字
     <BASE TARGET> 指定超级链接的分割窗口
     <BASEFONT SIZE> 更改预设字形大小
     <BGSOUND SRC> 加入背景音乐
     <BIG> 显示大字体
     <BLINK> 闪烁的文字
     <BODY TEXT LINK VLINK> 设定文字颜色
     <BODY> 显示本文
     <BR> 换行
     <CAPTION ALIGN> 设定表格标题位置
     <CAPTION>...</CAPTION> 为表格加上标题
     <CENTER> 向中对齐
     <CITE>...<CITE> 用于引经据典的文字
     <CODE>...</CODE> 用于列出一段程序代码
     <COMMENT>...</COMMENT> 加上批注
     <DD> 设定定义列表的项目解说
     <DFN>...</DFN> 显示"定义"文字
     <DIR>...</DIR> 列表文字卷标
     <DL>...</DL> 设定定义列表的卷标
     <DT> 设定定义列表的项目
     <EM> 强调之用
     <FONT FACE> 任意指定所用的字形
     <FONT SIZE> 设定字体大小
     <FORM ACTION> 设定户动式窗体的处理方式
     <FORM METHOD> 设定户动式窗体之资料传送方式
     <FRAME MARGINHEIGHT> 设定窗口的上下边界
     <FRAME MARGINWIDTH> 设定窗口的左右边界
     <FRAME NAME> 为分割窗口命名
     <FRAME NORESIZE> 锁住分割窗口的大小
     <FRAME SCROLLING> 设定分割窗口的滚动条
     <FRAME SRC> 将HTML文件加入窗口
     <FRAMESET COLS> 将窗口分割成左右的子窗口
     <FRAMESET ROWS> 将窗口分割成上下的子窗口
     <FRAMESET>...</FRAMESET> 划分分割窗口
     <H1>~<H6> 设定文字大小
     <HEAD> 标示文件信息
     <HR> 加上分网格线
     <HTML> 文件的开始与结束
     <I> 斜体字
     <IMG ALIGN> 调整图形影像的位置
     <IMG ALT> 为你的图形影像加注
     <IMG DYNSRC LOOP> 加入影片
     <IMG HEIGHT WIDTH> 插入图片并预设图形大小
     <IMG HSPACE> 插入图片并预设图形的左右边界
     <IMG LOWSRC> 预载图片功能
     <IMG SRC BORDER> 设定图片边界
     <IMG SRC> 插入图片
     <IMG VSPACE> 插入图片并预设图形的上下边界
     <INPUT TYPE NAME value> 在窗体中加入输入字段
     <ISINDEX> 定义查询用窗体
     <KBD>...</KBD> 表示使用者输入文字
     <LI TYPE>...</LI> 列表的项目 ( 可指定符号 )
     <MARQUEE> 跑马灯效果
     <MENU>...</MENU> 条列文字卷标
     <META NAME="REFRESH" CONTENT URL> 自动更新文件内容
     <MULTIPLE> 可同时选择多项的列表栏
     <NOFRAME> 定义不出现分割窗口的文字
     <OL>...</OL> 有序号的列表
     <OPTION> 定义窗体中列表栏的项目
     <P ALIGN> 设定对齐方向
     <P> 分段
     <PERSON>...</PERSON> 显示人名
     <PRE> 使用原有排列
     <SAMP>...</SAMP> 用于引用字
     <SELECT>...</SELECT> 在窗体中定义列表栏
     <SMALL> 显示小字体
     <STRIKE> 文字加横线
     <STRONG> 用于加强语气
     <SUB> 下标字
     <SUP> 上标字
     <TABLE BORDER=n> 调整表格的宽线高度
     <TABLE CELLPADDING> 调整数据域位之边界
     <TABLE CELLSPACING> 调整表格线的宽度
     <TABLE HEIGHT> 调整表格的高度
     <TABLE WIDTH> 调整表格的宽度
     <TABLE>...</TABLE> 产生表格的卷标
     <TD ALIGN> 调整表格字段之左右对齐
     <TD BGCOLOR> 设定表格字段之背景颜色
     <TD COLSPAN ROWSPAN> 表格字段的合并
     <TD NOWRAP> 设定表格字段不换行
     <TD VALIGN> 调整表格字段之上下对齐
     <TD WIDTH> 调整表格字段宽度
     <TD>...</TD> 定义表格的数据域位
     <TEXTAREA NAME ROWS COLS> 窗体中加入多少列的文字输入栏
     <TEXTAREA WRAP> 决定文字输入栏是自动否换行
     <TH>...</TH> 定义表格的标头字段
     <TITLE> 文件标题
     <TR>...</TR> 定义表格美一行
     <TT> 打字机字体
     <U> 文字加底线
     <UL TYPE>...</UL> 无序号的列表 ( 可指定符号 )
     <VAR>...</VAR> 用于显示变量

    类xml或html格式简单分析

                       类xml或html格式简单分析
    --------------------------------------------------------------------------------------------------------------------

    有个做浏览器的网友问了一下类xml或html格式分析,下面是我的简单想法,放这备份一下,

    有兴趣的人可以交流一下:

    昨晚说的那个树的实现方式我想了一下,现在把大概实现发给你看有没有帮助,
    其实这你要实现的可能跟以前我们在一个mmo中实现的帮助类似,我们的帮助是这样的
    当你在游戏中按下帮助按钮,会通过http的方式下载一个页面文件,该面面文件格式大概
    如下:
    <Text> 帮助 </Text>
    <Text url=http://www.xxx.com/help.txt> 任务 </Text>
    <Text url=http://www.xxx.com/consortia.txt> 公会 </Text>
    .....
    我们的实现就是定义一个结构,内部有一个url指针和一个字符串,
    然后在下载到这样的页文件后,给每一个<Text>内容</Text>块产生一个这样的结构对象并加到
    列表中,最后在处理时发现如果url指针不为空时就产生一个超链接控件(带下划线的文本)供点击,
    否则只产生文本显示。
    而如果你想产生类似于xml或html格式文本的话我想是可以考虑在前述方式上扩充的,例如要处理如下的例子:

    <Title> 飘零风雨亭 </Title>
    <Bobdy>
       <Text align="center" > 欢迎光临 </Text>
       <Text font="宋体" posx=100 posy=200> 新闻 <Text url="http://www.xxxx.com"> 点击这里进入 </Text> </Text>
       <Img w=100 h=200> http://www.xxxx.com/hello.gif </Img>
    </Bobdy>

    上述第一行是标题,每3行是一个字串,每4行是一个字串("新闻")和一个超链接("点击这里")
    第5行是一个图片路径

    我的处理方法是用树的方式来处理,每一个块(以<TAG>和</TAG>对称成为一个块)作为一个结点,
    每个结点中有一个结点属性对象和一个子结点列表。结构如下:
    1.属性类, 这个分为基类和从基类派生的各个Tag属性类, 这些属性是为结点服务的,就是说
    每个结点都包含有一个对应的属性对象:
    // 属性基类
    class kPropBase{
       kAlign align; 
       int posx;
       int posy;
       // 除了下面这个用来储存在块的两个Tag之间的字符串之外,
       // 其它的变量存的是参数值(即<Tag param1 param2 ...>中的param)
       std::string strData;
    public:
       virtual bool Update() = 0; // 需要更新的原因是有些界面元素会作变色位移等动画(可以考虑关键帧动画)
       virtual bool Draw()   = 0; // 画出
       // 下面这个是处理从对应的块字串中取对应值赋予此类参数和数据串。
       // szBlock参数存的就是块字串,象:"<Text align="center" > 欢迎光临 </Text>"
       virtual void ProcessBlock( const char *szBlock ) = 0;
       //...
    };
    // 标题属性类:
    class kPropTitle : public kPropBase{
    public:
     //...
    }
    // Body属性类:
    class kPropBody : public kPropBase{
       //...
    }
    // 文本属性类:
    class kPropText : public kPropBase{
       kFont font;
       //...
    };
    // 图片属性类:
    class kPropImg : public kPropBase{
       std::string strUrl;
       //...
    };

    2. 结点类, 第1个是类型(如文本,图片等), 第2个是属性(这里用它的属性基类指针,
    可以判断第1个变量看是什么类型从而强制转化成对应派生类对象指针进行处理),第3个是所有子结点
    class kNode{ 
       E_TAG     eType;
       kPropBase *pData;
       std::vector< kNode >  vecChild;
    };

    上面结构我想应该还是比较晰,就不说明了
    在处理字串碰到块的开始和结束如果要严格配对时可这么作,在开始时,如检查到<Text>开头,
    我们push Text这个flag, 然后在碰到</Text>时Pop Flag, 看是否相等,不等就是不配对提示出错。

     

    November 25

    脚本

    1. lua 手册
    http://www.gamedev.net/reference/programming/features/lua/

    Programming in Lua:
    http://www.lua.org/pil/
    2. 一个简单例子
    width=800;
    height=600;

    BLUE={r=0, g=0, b=255};
    background=BLUE;

    function hello()
      a = 5;
      b = 10;
      c = a + b;
      print ("5+10=" .. c);
      test(c);
    end

    function f(x, y)
      return (x^2 * sin(y))/(1-x);
    end

    hello()


    二。自己实现一个简单的脚本
    //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    //                          flipcode@msn.com
    //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    // 以下说明一个简单的脚本实现原理

    这是很早以前实现的类c脚本语言。(这篇文章也是当初所写)
    1。读入脚本
    2。语法分析,并按优先级递归生成对应2叉树
    3。倒序递归解释2叉树,并调用对应自己所写的代码

    [祥细如下:]

    一、起步:先说明表达式的处理(热身运动)
    例: a=1+obj.fun("price",5)+2; 
     a.表达式的分析:
      //(建立函数Create2Tree(root,s)其中s字符中的内容是上述的表达式)
     //(1)找出字符串(即参数)中最低级的运算符,以该操作符为中心将字符串分为两半
      //(2)root=该操作符(及你需要的其它各信息)
      //(3)root->left=new STree;s1=左边字符串;把root=root->left及s1当参数跳到第(1)步继续(即回归处理);
      //(4)root->right=new STree;s2=右边字符串;把root=root->right及s2当参数跳到第(1)步继续(即回归处理)
     //(5)直到剩下变量或函数(包括对象函数)及常量(),再用root保存相关信息
      //(这样就可生成如下2叉树:)
              =
           a    + 
              1   +

            obj.fun   2 

            "price"   5

     b.表达式的解释:
      用vector<*op>存下如下函数的地址:
       ProcVer{stack.push(&a);}                
       ProcNum{push(1);}                     
       ProcNum{push(2);}                      
       ProcFun{?push(obj.fun(pop(),pop()));}//注意参数要从右到左出栈,?号表示如果函数有返回值则压栈!
          //这里假设你已能得到该函数的地址(通过预设一份静态的函数名与函数地址对应的映射表查询得到)
       ProcNum{push(2);}
       ProcAdd{push(pop()+pop());}
       ProcAdd{push(pop()+pop());}
       ProcAssign{*(sp-2)=pop();}
       //最后a的值就可用pop()取出
     c.表达式的执行
     
    ------------------------------------------------------------------------------------------------
    三、完整脚本的举例说明
    脚本如下:
    int row=5,b=-1,ires;
    ListBox lb1;
    while(row=row+b)
        ires=1+obj.fun(1,12)+2; 
    endwhile
    //.......

     1。定义的分析:
            首先是否存在关键字int及是否存在ListBox类(相当于vb中控件类,但它是可视的,vb中你要用控件的前提是控件栏中必须有此控件你才可拖拉到窗体.这里你要用这个ListBox东东(类)的前提是必须存在这个类),
         不存在则: 
             提示错误!
         存在则:
             a.是定义变量的生成对应的内存空间,并加入到ver_map(变量名,变量地址)映射表(如第一行)
             b.是定义对象的创建对象,并进行初始化。并加入到obj_map(对象名,对象地址)映射表(如第二行)
    (相当于vb中拖了一个列表框到窗口上,此时它会生成一个列表框对象)

      2。表达式的分析与解释:
        用大概类似于struct {void (*op)(void) VCode[1000];int ip}VMachine这样的结构存下如下
    "Procxxx"之类的函数的地址:
         //先处理while(row=row+h):
         ProcVer{push(&row);} 
         ProcVer{push(&h);}   
         ProcAdd{push(pop()+pop());} 
         ProcVer{push(&row);}        
         ProcAssign{*(sp-2)=pop();}  
         ProcWhile{WhileIP.push(ip);while(!pop().value && pop().name!="endwhile")pop();} 

         //再处理ires=1+obj.fun(1,2)+2:
         ProcNum{push(1);}                
         ProcNum{push(12);}                
         ProcFun{?push(obj.fun(pop(),pop()));}//注意参数要从右到左出栈,?号表示如果函数有返回值则压栈!
          //这里假设你已能得到该函数的地址(通过预设一份静态的函数名与函数地址对应的映射表查询得到)
         ProcNum{push(2);}                
         ProcAdd{push(pop()+pop());}
         ProcNum{push(1);}                
         ProcVer{push(&ires);}                
         ProcAdd{push(pop()+pop());}
         ProcAssign{*(sp-2)=pop();}

         //然后处理endwhile:
         ProcEndwhile(ip=WhileIP.pop()-1);
         //.......          
         NULL;

      3。脚本语言的执行
         VMachine.ip=0;
         while(VMachine.VCode[VMachine.ip].op != NULL)
         {
             (*VMachine.VCode[VMachine.ip].op)();
     VMachine.ip].op++;
         }

                                                            flipcode   2001.12
      

    三、老外的一个完整的脚本语言教程(基本原理类同上述第二点)
    http://www.peroxide.dk/tuts_scr.shtml