游戏王lua关键字段问题,怎么lua 新建文件关键字段

游戏王 中文版
投诉建议:
千万流量共享 百度高权重排名
软件大小: 47.6M
软件厂商:
软件语言: 简体中文
软件授权: 免费
软件评级:
应用平台:WinAll
更新时间:
编辑推荐:
《游戏王(mycard)》为一款实现卡牌游戏在PC上自动完成各种效果处理的联机游戏软件。此软件原作者来自中国大陆,常用网名:Argon,圈内多被称为“圆神”。此软件为开源软件,任何人可以免费使用或者对其进行修改,其源代码托管在github网站上。简体中文版为第一原版,网上可见的其它语言版本(英文版、日文版、繁体中文版等)或多或少地在原版的基础上增加了元素,均源自原作者提供的资源,由其他人进一步开发得到。此软件引擎主要使用C++语言进行编写实现,而卡片效果则主要使用lua语言编写成一个个独立的脚本来实现。游戏王截图1游戏王截图2游戏王截图3游戏王截图4游戏王截图5游戏玩法: mycard萌卡不需要玩家去收集卡牌,游戏提供了所有卡牌(下载文件里有卡牌下载地图)。每一个玩家可以根据自己的想法来组建属于自己的卡牌组合,不同的组合有不同的组合效果,组合之间相生相克,所以玩家可以多选择几套卡牌。快捷键: F12 返回标题画面
iduel观战时 F10退出房间
常见问题: Q: 加入或建立房间时提示连接已断开或版本不正确
A: 请注意mycard左上角是否在自动更新,如果显示正在下载更新请等待下载完后关掉mycard重开一次。
Q: 程序崩溃或无任何提示自动退出 或提示 【程序可能出现了一个bug,请到论坛反馈】
A: 重试一次。如果仍然这样 请联系作者推荐配置[操作系统] Windows XP / Windows Vista / Windows 7 / Windows 8 / 8.1[运行环境] DirectX 9.0 and 10.0[CPU] 2.4 GHz[内存] 2G[显卡] 芯片:集成/独显 显存:512M
8MB|319.0KB|27MB|7MB|24MB|12MB|
聚超值推荐
今日更新推荐
软件分类目录
同类软件下载排行
热门关键词游戏王中有哪些令人困惑的卡名、字段问题? - 知乎21被浏览5008分享邀请回答78 条评论分享收藏感谢收起5添加评论分享收藏感谢收起查看更多回答表格格式为
ClearancePlies
1、先给出用到的字符串处理函数代码
--字符串分割函数
--传入字符串和分隔符,返回分割后的table
function string.split(str, delimiter)
if str==nil or str=='' or delimiter==nil then
return nil
local result = {}
for match in (str..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match)
return result
--字符串按位分割函数
--传入字符串,返回分割后的table,必须为字母、数字,否则返回nil
function string.gsplit(str)
local str_tb = {}
if string.len(str) ~= 0 then
for i=1,string.len(str) do
new_str= string.sub(str,i,i)
if (string.byte(new_str) &=48 and string.byte(new_str) &=57) or (string.byte(new_str)&=65 and string.byte(new_str)&=90) or (string.byte(new_str)&=97 and string.byte(new_str)&=122) then
table.insert(str_tb,string.sub(str,i,i))
return nil
return str_tb
return nil
function string.trim(s)
return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
2、表格对象代码(class见我上上篇文章)
TableData = class()
function TableData:ctor()
self.data = {}
function TableData:GetById(id)
local num = #self.data
for i=1,num do
if(id==self.data[i].Id) then
return self.data[i];
print( string.format("nil==TableData:GetById(%d)",id))
return nil;
3、给出读表器代码
require 'Utility/LuaStringExt'
require 'Table/TableData'
TableReader = {}
function TableReader.ReadAllLines(text)
return string.split(text,'\n')
function TableReader.IsEmptyLine(str)
if(str=='' or str[0]=='#') then
return true
return false
local TableFieldType =
FLOAT = 1,
STRING = 2,
UNKNOW = 3
local Syntax_INT = 'INT'
local Syntax_FLOAT = 'FLOAT'
local Syntax_STRING = 'STRING'
function TableReader.ParseType(str)
local types = {}
local fields = string.split(str,'\t')
for i=1,#fields do
local sytax = fields[i]
if(Syntax_INT==sytax) then
table.insert(types,TableFieldType.INT)
elseif(Syntax_FLOAT==sytax) then
table.insert(types,TableFieldType.FLOAT)
elseif(Syntax_STRING==sytax) then
table.insert(types,TableFieldType.STRING)
table.insert(types,TableFieldType.UNKNOW)
print('Error:',i,'unknow type['..sytax..']')
return types
function TableReader.ParseField(str)
local fieldsTable = {}
local fields = string.split(str,'\t')
for i=1,#fields do
table.insert(fieldsTable,fields[i])
return fieldsTable
function TableReader.ParseValue(fieldsTable,typeTable,str)
local ret = {}
local fields = string.split(str,'\t')
for i=1,#fields do
if(TableFieldType.INT==typeTable[i] or TableFieldType.FLOAT==typeTable[i]) then
ret[fieldsTable[i]] = tonumber(fields[i])
elseif(TableFieldType.STRING==typeTable[i]) then
ret[fieldsTable[i]] = fields[i]
print('Error:ParseValue',i)
return ret
function TableReader.LoadTable(text)
local tab = TableData.new();
local lines = TableReader.ReadAllLines(text)
local lineNum = #lines
local types = nil
local fieldsTable = nil
for i=1,lineNum do
local str = string.trim(lines[i])
if(not TableReader.IsEmptyLine(str)) then
if(nil==types) then
types = TableReader.ParseType(str)
elseif(nil==fieldsTable) then
fieldsTable = TableReader.ParseField(str)
table.insert(tab.data,TableReader.ParseValue(fieldsTable,types,str))
return tab
4、使用代码
local table = TableReader.LoadTable(str)
local data = table:GetById(0)
print(data.Desc)
阅读(...) 评论()16552人阅读
本文是我在阅读Lua源代码时的一些心得笔记, Lua的版本是5.1.1. 将主要关注Lua解释器的结构, 以及部分重要算法,
并不针对每个细节进行说明. 希望本系列文章, 能够总体上说明Lua的实现的脉络, 在需要的时候能够进一步分析源码对Lua并做适当的定制.将按以下顺序来说明Lua的实现:首先, 将讨论Lua基本对象(空,
布尔, 数值, 字符串, 表, 函数, 线程, 用户数据)的实现.在了解了基本对象的实现之后, 将分析Lua虚拟机的指令系统; 以及虚拟机的实现, 即回答Lua指令(字节码)是如何得到执行的.然后, 会对Lua的词法分析, 语法分析和指令生成做分析, 介绍Lua源代码到字节码的生成过程.最后, 将做为分别的专题, 讨论一些特别的基本对象的实现特点(字符串与表, 函数与闭包, 协程coroutine与线程), 垃圾收集的实现等等.自己是第一次写这样的文章,恐怕还会多有疏漏.
Lua是动态类型的语言, 即是说类型附着于值而不变量[1]. Lua的八种基本类型空, 布尔, 数值, 字符串, 表, 函数和用户数据. 所有类似的值都是虚拟机的第一类值. Lua 解释器将其表示成为标签联合(tagged union). 如下面代码示例所示:lobject.h : 56/*** Union of all Lua values*/typedef union {
GCObject *
lua_N} V/*** Tagged Values*/#define TValuefields V int tttypedef struct lua_TValue {
TV} TVlstate.h : 132/*** Union of all collectable objects*/union GCObject {
struct UpV
struct lua_S /* thread */};lobject.h : 39/*** Common Header for all collectable objects (in macro form, to be** included in other objects)*/#define CommonHeader GCObject * lu_ lu_byte marked/*** Common header in struct form*/typedef struct GCheader {
CommonH} GC首先看到的一个TValue结构,它是由一个Value类型的字段value和int类型字段tt组成,它由于一个宏定义出来.很显然,这里的tt就是用于表示这个值的类型,这也是之前所说的,Lua的类型是附着于值上的原因.接
下来,再打量打量Value的定义,它被定义为union.这样做的目的是让这一个类型可以表示多个类型.从这个定义中可以看出这样一点:Lua的值可以
分成两类,第一类是可以被垃圾回收机制回收的对象,它们统一使用GCObject的指针来表示;另一类是原始类型,直接使用C语言的类型来表示相应类型,
如:用void *来表示lightuesrdata,
用lua_Number来表示数值,用int来表示boolean.这里需要注意的是lua_Number是在如下两个文件定义出来的.由于Lua是易于
嵌入的语言,在某些特定的环境下,所有数值都用双精度浮点来表示并不合适,因此,在Lua的配置文件上使用宏来定义数值类型.这使得要改变Lua的数值类
型变得非常简单.lua.h:98/* type of numbers in Lua */typedef LUA_NUMBER lua_Nluaconf.h:504#define LUA_NUMBER double接
下来继续看GCObject的定义,这个类型中的字段在这里并不做详细展开,只是说明是用于表示什么类型的.TString,UData,Table,
lua_State分别用于表示字符串,用户数据,表和协程.而Closure,Proto,UpVal都是用于表示第一类的函数的.
基于栈的,词法定界的第一类函数在实现上是有一些难度的,看看如下代码:
function foo()
return function() return a endend
于Lua是词法定界的,局部变量a只在函数foo中有效,所以它可以保存在foo的栈中,因此当foo执行完毕a而就随着栈的销毁而成为垃圾;
但问题是foo返回的函数还在引用着它, 这个函数会在栈销毁后继续存在,当它返回a的时候又拿什么返回呢? 这个问题将在函数的实现中介绍.
这也是为什么实现函数用了三个类型的原因.另外, 这些类型的开头都是GCHeader, 它的所有字段由宏CommonHeader给出来了. 字段next说明可回收对象是可以放到链表中去的, 而marked是在GC中用于标记的. 具体的GC算法在这一章就不做介绍了.值得注意是在CommonHeader中还有一个tt用于表示值的类型, 在TValue中不是有一个吗? 这样数据不是冗余了? 我是这样看这个问题的:第一: TValue是所有值的集合, 而GC中如果每个对象都要判断是否是可回收的, 必然会非常影响效率, 因此将GCObject独立出来. 可以省去这一层判断.第二: 对于基本类型来说, 所需要的空间相对较小, 如果将复杂的对象也做为一union放在一起, 就会使得空间效率低,因此在TValue中只使用了一个指针来表示GCObject.这样在GC对看到的对象就不再是TValue了,所以对应的类型标识也不在了,所以在CommonHeader中加了一个字段来表示类型.最后,给出一副图来表于Lua的内存表示:
首先将源程序编译成为字节码,然后交由虚拟机解释执行.对于每一个函数,Lua的编译器将创建一个原型(prototype),它由一组指令及其使用到的
常量组成[1].最初的Lua虚拟机是基于栈的.到1993年,Lua5.0版本,采用了基于寄存器的虚拟机,使得Lua的解释效率得到提升,
体系结构与指令系统
与虚拟机和指令相关的文件主要有两个: lopcodes.c 和 lvm.c. 从名称可以看出来,这两个文件分别用于描述操作码(指令)和虚拟机.首先来看指令:Lua共有38条指令, 在下面两处地方分别描述了这些指令的名称和模式, 如下:lopcodes.c:16const char *const luaP_opnames[NUM_OPCODES+1] = {
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"GETGLOBAL",
"GETTABLE",
"SETGLOBAL",
"SETUPVAL",
"SETTABLE",
"NEWTABLE",
"TESTSET",
"TAILCALL",
"FORLOOP",
"FORPREP",
"TFORLOOP",
"SETLIST",
"CLOSURE",
NULL};#define opmode(t,a,b,c,m) (((t)&&7) | ((a)&&6) | ((b)&&4) | ((c)&&2) | (m))const lu_byte luaP_opmodes[NUM_OPCODES] = {/*
opmode(0, 1, OpArgR, OpArgN, iABC)
/* OP_MOVE */ ,opmode(0, 1, OpArgK, OpArgN, iABx)
/* OP_LOADK */ ,opmode(0, 1, OpArgU, OpArgU, iABC)
/* OP_LOADBOOL */ ,opmode(0, 1, OpArgR, OpArgN, iABC)
/* OP_LOADNIL */ ,opmode(0, 1, OpArgU, OpArgN, iABC)
/* OP_GETUPVAL */ ,opmode(0, 1, OpArgK, OpArgN, iABx)
/* OP_GETGLOBAL */ ,opmode(0, 1, OpArgR, OpArgK, iABC)
/* OP_GETTABLE */ ,opmode(0, 0, OpArgK, OpArgN, iABx)
/* OP_SETGLOBAL */ ,opmode(0, 0, OpArgU, OpArgN, iABC)
/* OP_SETUPVAL */ ,opmode(0, 0, OpArgK, OpArgK, iABC)
/* OP_SETTABLE */ ,opmode(0, 1, OpArgU, OpArgU, iABC)
/* OP_NEWTABLE */ ,opmode(0, 1, OpArgR, OpArgK, iABC)
/* OP_SELF */ ,opmode(0, 1, OpArgK, OpArgK, iABC)
/* OP_ADD */ ,opmode(0, 1, OpArgK, OpArgK, iABC)
/* OP_SUB */ ,opmode(0, 1, OpArgK, OpArgK, iABC)
/* OP_MUL */ ,opmode(0, 1, OpArgK, OpArgK, iABC)
/* OP_DIV */ ,opmode(0, 1, OpArgK, OpArgK, iABC)
/* OP_MOD */ ,opmode(0, 1, OpArgK, OpArgK, iABC)
/* OP_POW */ ,opmode(0, 1, OpArgR, OpArgN, iABC)
/* OP_UNM */ ,opmode(0, 1, OpArgR, OpArgN, iABC)
/* OP_NOT */ ,opmode(0, 1, OpArgR, OpArgN, iABC)
/* OP_LEN */ ,opmode(0, 1, OpArgR, OpArgR, iABC)
/* OP_CONCAT */ ,opmode(0, 0, OpArgR, OpArgN, iAsBx)
/* OP_JMP */ ,opmode(1, 0, OpArgK, OpArgK, iABC)
/* OP_EQ */ ,opmode(1, 0, OpArgK, OpArgK, iABC)
/* OP_LT */ ,opmode(1, 0, OpArgK, OpArgK, iABC)
/* OP_LE */ ,opmode(1, 1, OpArgR, OpArgU, iABC)
/* OP_TEST */ ,opmode(1, 1, OpArgR, OpArgU, iABC)
/* OP_TESTSET */ ,opmode(0, 1, OpArgU, OpArgU, iABC)
/* OP_CALL */ ,opmode(0, 1, OpArgU, OpArgU, iABC)
/* OP_TAILCALL */ ,opmode(0, 0, OpArgU, OpArgN, iABC)
/* OP_RETURN */ ,opmode(0, 1, OpArgR, OpArgN, iAsBx)
/* OP_FORLOOP */ ,opmode(0, 1, OpArgR, OpArgN, iAsBx)
/* OP_FORPREP */ ,opmode(1, 0, OpArgN, OpArgU, iABC)
/* OP_TFORLOOP */ ,opmode(0, 0, OpArgU, OpArgU, iABC)
/* OP_SETLIST */ ,opmode(0, 0, OpArgN, OpArgN, iABC)
/* OP_CLOSE */ ,opmode(0, 1, OpArgU, OpArgN, iABx)
/* OP_CLOSURE */ ,opmode(0, 1, OpArgU, OpArgN, iABC)
/* OP_VARARG */};前面一个数组容易理解, 表示了每条指令的名称. 后面一个数组表示的是指令的模式. 奇怪的符号让人有些费解. 在看模式之前, 首先来看Lua指令的格式:如
上图, Lua的指令可以分成三种形式. 即在上面的模式数组中也可以看到的iABC, iABx 和 iAsBx. 对于三种形式的指令来说,
前两部分都是一样的, 分别是6位的操作码和8位A操作数; 区别在于, 后面部是分割成为两个长度为9位的操作符(B,
C),一个无符号的18位操作符Bx还是有符号的18位操作符sBx. 这些定义的代码如下:lopcodes.c : 34/*** size and position of opcode arguments.*/#define SIZE_C
9#define SIZE_B
9#define SIZE_Bx
(SIZE_C + SIZE_B)#define SIZE_A
8#define SIZE_OP
6#define POS_OP
0#define POS_A
(POS_OP + SIZE_OP)#define POS_C
(POS_A + SIZE_A)#define POS_B
(POS_C + SIZE_C)#define POS_Bx
POS_C再来看指令的操作模式, Lua使用一个字节来表示指令的操作模式. 具体的含义如下:1.
使用最高位来表示是否是一条测试指令. 之所以将这一类型的指令特别地标识出来, 是因为Lua的指令长度是32位,对于分支指令来说,
要想在这32位中既表示两个操作数来做比较, 同时还要表示一个跳转的地址, 是很困难的. 因此将这种指令分成两条, 第一条是测试指令,
紧接着一条无条件跳转. 如果判断条件成立则将PC(Program Counter, 指示下一条要执行的指令)加一, 跳过下一条无条件跳转指令,
继续执行; 否则跳转.2. 第二位用于表示A操作数是否被设置3. 接下来的二位用于表示操作数B的格式,OpArgN表示操作数未被使用, OpArgU表示操作数被使用(立即数?), OpArgR表示表示操作数是寄存器或者跳转的偏移量, OpArgK表示操作数是寄存器或者常量.最后, 给出Lua虚拟机的体系结构图(根据源代码分析得出):首先, 我们注意到, Lua的解释器还是一个以栈为中心的结构. 在lua_State这个结构中,有许多个字段用于描述这个结构.stack用于指向绝对栈底, 而base指向了当前正在执行的函数的第一个参数, 而top指向栈顶的第一个空元素.我们可以看到,这个体系结构中并没有独立出来的寄存器. 从以下代码来看:lvm.c:343#define RA(i)
(base+GETARG_A(i))/* to be used after possible stack reallocation */#define RB(i)
check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))#define RC(i)
check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))#define RKB(i)
check_exp(getBMode(GET_OPCODE(i)) == OpArgK, /
ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))#define RKC(i)
check_exp(getCMode(GET_OPCODE(i)) == OpArgK, /
ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))#define KBx(i)
check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))当
指令操作数的类型是寄存器时,它的内容是以base为基址在栈上的索引值.如图所示.寄存器实际是base之上栈元素的别名;当指令操作数的类型的常数
时, 它首先判断B操作数的最位是否为零.如果是零,则按照和寄存器的处理方法一样做,如果不是零,则在常数表中找相应的值.我
们知道Lua中函数的执行过程是这样的. 首先将函数压栈,然后依次将参数压栈,形成图中所示的栈的内容.
因此R[0]到R[n]也分别表示了Arg[1]到Arg[N+1].在第一个参数之下,就是当前正在执行的函数,对于Lua的函数(相对C函数)来说,
它是指向类型为 Prototype的TValue,
在Prototype中字段code指向了一个数组用来表示组成这个函数的所有指令,字段k指向一个数组来表示这个函数使用到的所有常量.最后,Lua在
解释执行过程中有专门的变量pc来指向下一条要执行的指令.
指令解释器
有了前面对指令格式和体系结构的介绍,现在我们可以进入正题, 来看看Lua的指令是如何执行的了.主函数如下:lvm.c:373void luaV_execute (lua_State *L, int nexeccalls) {
LClosure *
TValue *k;
const Instruction * reentry:
/* entry point */
lua_assert(isLua(L->ci));
cl = &clvalue(L->ci->func)->l;
base = L->
k = cl->p->k;这
是最开始的初始化过程.其中, pc被初始化成为了L->savedpc,base被初始化成为了L->base,
即程序从L->savedpc开始执行 (在下一篇专题中,将会介绍到
L->savedpc在函数调用的预处理过程中指向了当前函数的code),而L->base指向栈中当前函数的下一个位置.cl表示当前正
在执行闭包(当前可以理解成为函数),k指向当前闭包的常量表.接下来(注意,为了专注主要逻辑, 我将其中用于Debugger支持,断言等代码省略了):
/* main loop of interpreter */
for (;;) {
const Instruction i = *pc++;
/* 省略Debugger支持和Coroutine支持*/
/* warning!! several calls may realloc the stack and invalidate `ra' */
ra = RA(i);
/* 省略断言 */
switch (GET_OPCODE(i)) {进
入到解释器的主循环,处理很简单,取得当前指令,pc递增,初始化ra,然后根据指令的操作码进行选择. 接下来的代码是什么样的,
估计大家都能想到,一大串的case来指示每条指令的执行.具体的实现可以参考源码, 在这里不对每一条指令展开,
只是对其中有主要的几类指令进行说明:传值类的指令,与MOVE为代表:lvm.c:403
case OP_MOVE: {
setobjs2s(L, ra, RB(i));
}lopcodes:154OP_MOVE,/*
R(A) := R(B)
*/lobject.h:161#define setobj(L,obj1,obj2) /
{ const TValue *o2=(obj2); TValue *o1=(obj1); /
o1->value = o2-> o1->tt=o2-> /
checkliveness(G(L),o1); }/*** different types of sets, according to destination*//* from stack to (same) stack */#define setobjs2s
注释来看, 这条指令是将操作数A,B都做为寄存器,然后将B的值给A. 而实现也是简单明了,只使用了一句. 宏展开以后, 可以看到,
R[A],R[B]的类型是TValue, 只是将这两域的值传过来即可.
对于可回收对象来说,真实值不会保存在栈上,所以只是改了指针,而对于非可回收对象来说,则是直接将值从R[B]赋到R[A].数值运算类指令,与ADD为代表:lvm.c:470
case OP_ADD: {
arith_op(luai_numadd, TM_ADD);
}lvm.c:360#define arith_op(op,tm) { /
TValue *rb = RKB(i); /
TValue *rc = RKC(i); /
if (ttisnumber(rb) && ttisnumber(rc)) { /
lua_Number nb = nvalue(rb), nc = nvalue(rc); /
setnvalue(ra, op(nb, nc)); /
Protect(Arith(L, ra, rb, rc, tm)); /
}lopcodes.c:171OP_ADD,/*
R(A) := RK(B) + RK(C)
*/如果两个操作数都是数值的话,关键的一行是:setnvalue(ra,op(nb,nc));即两个操作数相加以后,把值赋给R[A].值得注意的是,操作数B,C都是RK, 即可能是寄存器也可能是常量,这最决于最B和C的最高位是否为1,如果是1,则是常量,反之则是寄存器.具体可以参考宏ISK的实现.如果两个操作数不是数值,即调用了Arith函数,它尝试将两个操作转换成数值进行计算,如果无法转换,则使用元表机制.该函数的实现如下:lvm.c:313static void Arith (lua_State *L, StkId ra, const TValue *rb,
const TValue *rc, TMS op) {
TValue tempb,
const TValue *b, *c;
if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
(c = luaV_tonumber(rc, &tempc)) != NULL) {
lua_Number nb = nvalue(b), nc = nvalue(c);
switch (op) {
case TM_ADD: setnvalue(ra, luai_numadd(nb, nc));
case TM_SUB: setnvalue(ra, luai_numsub(nb, nc));
case TM_MUL: setnvalue(ra, luai_nummul(nb, nc));
case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc));
case TM_MOD: setnvalue(ra, luai_nummod(nb, nc));
case TM_POW: setnvalue(ra, luai_numpow(nb, nc));
case TM_UNM: setnvalue(ra, luai_numunm(nb));
default: lua_assert(0);
else if (!call_binTM(L, rb, rc, ra, op))
luaG_aritherror(L, rb, rc);}在上面call_binTM用于调用到元表中的元方法,因为在Lua以前的版本中元方法也被叫做tag method, 所以函数最后是以TM结尾的.lvm:163static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event) {
const TValue *tm = luaT_gettmbyobj(L, p1, event);
/* try first operand */
if (ttisnil(tm))
tm = luaT_gettmbyobj(L, p2, event);
/* try second operand */
if (!ttisfunction(tm)) return 0;
callTMres(L, res, tm, p1, p2);
return 1;}在 这个函数中,试着从二个操作数中找到其中一个操作数的元方法(第一个操作数优先), 这里event表示具体哪一个元方法,找到了之后,再使用函数callTMres()去调用相应的元方法. callTMres()的实现很简单,只是将元方法,第一,第二操作数先后压栈,再调用并取因返回值.具体如下:lvm.c:82static void callTMres (lua_State *L, StkId res, const TValue *f,
const TValue *p1, const TValue *p2) {
ptrdiff_t result = savestack(L, res);
setobj2s(L, L->top, f);
/* push function */
setobj2s(L, L->top+1, p1);
/* 1st argument */
setobj2s(L, L->top+2, p2);
/* 2nd argument */
luaD_checkstack(L, 3);
L->top += 3;
luaD_call(L, L->top - 3, 1);
res = restorestack(L, result);
setobjs2s(L, res, L->top);}逻辑运算类指令,与EQ为代表:lvm.c:541
case OP_EQ: {
TValue *rb = RKB(i);
TValue *rc = RKC(i);
if (equalobj(L, rb, rc) == GETARG_A(i))
dojump(L, pc, GETARG_sBx(*pc));
}lopcodes.c:185OP_EQ,/*
if ((RK(B) == RK(C)) ~= A) then pc++
这条指令实现的过程中,equalobj与之前的算术运算类似,读者可以自行分析.关键看它是如果实现中跳转的,如果RK[B]==RK[C]并且A为1
的情况下(即条件为真),则会使用pc取出下一条指令,调用dojump进行跳转,否则pc++,挂空紧接着的无条件跳转指令.
dojump的实现如下:lvm.c:354#define dojump(L,pc,i)
{(pc) += (i); luai_threadyield(L);}luai_threadyield只是顺序地调用lua_unlock和lua_lock,这里为释放一次锁,使得别的线程可以得到调度.函数调用类指令,与CALL为代表:lvm.c:582
case OP_CALL: {
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) L->top = ra+b;
/* else previous instruction set top */
L->savedpc =
switch (luaD_precall(L, ra, nresults)) {
case PCRLUA: {
nexeccalls++;
/* restart luaV_execute over new Lua function */
case PCRC: {
/* it was a C function (`precall' called it); adjust results */
if (nresults >= 0) L->top = L->ci->
base = L->
default: {
/* yield */
}lopcodes.c:192OP_CALL,/*
R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */这
一条指令将在下一个介绍Lua函数调用规范的专题中详细介绍.
在这里只是简单地说明CALL指令的R[A]表示的是即将要调用的函数,而B和C则分别表示参数个数加1,和返回值个数加1.
之所以这里需要加1,其原因是:B和C使用零来表示变长的参数和变长的返回值,而实际参数个数就向后推了一个.指令的介绍就先到此为止了, 其它的指令的实现也比较类似.仔细阅读源码就可很容易地分析出它的意义来. 下一篇将是一个专题, 详细地介绍Lua中函数的调用是如何实现的.
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1358463次
积分:16057
积分:16057
排名:第639名
原创:488篇
转载:23篇
评论:153条
(7)(16)(31)(17)(51)(115)(205)(1)(3)(3)(62)(1)

我要回帖

更多关于 lua self关键字 的文章

 

随机推荐