mirror of
https://github.com/Leonmmcoset/CMLeonOS.git
synced 2026-03-03 15:30:27 +00:00
Lua 5.2支持
This commit is contained in:
81
Shell.cs
81
Shell.cs
@@ -13,6 +13,7 @@ using System.Text;
|
||||
using CosmosHttp.Client;
|
||||
using Cosmos.Core;
|
||||
using Cosmos.Core.Memory;
|
||||
using UniLua;
|
||||
using Cosmos.HAL;
|
||||
|
||||
namespace CMLeonOS
|
||||
@@ -199,6 +200,7 @@ namespace CMLeonOS
|
||||
" whoami - Show current username",
|
||||
" base64 encrypt <text> - Encode text to Base64",
|
||||
" base64 decrypt <text> - Decode Base64 to text",
|
||||
" lua <file> - Execute Lua script",
|
||||
" version - Show OS version",
|
||||
" about - Show about information",
|
||||
" help <page> - Show help page (1-3)",
|
||||
@@ -465,6 +467,9 @@ namespace CMLeonOS
|
||||
case "base64":
|
||||
ProcessBase64Command(args);
|
||||
break;
|
||||
case "lua":
|
||||
ExecuteLuaScript(args);
|
||||
break;
|
||||
default:
|
||||
ShowError($"Unknown command: {command}");
|
||||
break;
|
||||
@@ -2083,5 +2088,81 @@ namespace CMLeonOS
|
||||
ShowError($"Base64 error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteLuaScript(string args)
|
||||
{
|
||||
string[] parts = args.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length == 0)
|
||||
{
|
||||
ShowError("Error: Please specify Lua script file");
|
||||
ShowError("Usage: lua <file>");
|
||||
return;
|
||||
}
|
||||
|
||||
string filePath = parts[0];
|
||||
string originalPath = filePath;
|
||||
|
||||
if (!filePath.StartsWith("0:\\") && !filePath.StartsWith("0:/"))
|
||||
{
|
||||
if (prompt == "/" || prompt == "\\")
|
||||
{
|
||||
filePath = "0:\\" + filePath.TrimStart('/').TrimStart('\\');
|
||||
}
|
||||
else
|
||||
{
|
||||
filePath = Path.Combine(prompt, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
ShowError($"Error: File not found: {filePath}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Console.WriteLine($"Executing: {filePath}");
|
||||
// Console.WriteLine();
|
||||
|
||||
try
|
||||
{
|
||||
string scriptContent = File.ReadAllText(filePath);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(scriptContent))
|
||||
{
|
||||
ShowWarning("Script file is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
ILuaState lua = LuaAPI.NewState();
|
||||
lua.L_OpenLibs();
|
||||
|
||||
UniLua.ThreadStatus loadResult = lua.L_LoadString(scriptContent);
|
||||
|
||||
if (loadResult == UniLua.ThreadStatus.LUA_OK)
|
||||
{
|
||||
UniLua.ThreadStatus callResult = lua.PCall(0, 0, 0);
|
||||
|
||||
if (callResult == UniLua.ThreadStatus.LUA_OK)
|
||||
{
|
||||
// ShowSuccess("Script run successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMsg = lua.ToString(-1);
|
||||
ShowError($"Script execution error: {errorMsg}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMsg = lua.ToString(-1);
|
||||
ShowError($"Script load error: {errorMsg}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowError($"Lua execution error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
UniLua/ByteString.cs
Normal file
47
UniLua/ByteString.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
internal class ByteStringBuilder
|
||||
{
|
||||
public ByteStringBuilder()
|
||||
{
|
||||
BufList = new LinkedList<byte[]>();
|
||||
TotalLength = 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if( TotalLength <= 0 )
|
||||
return String.Empty;
|
||||
|
||||
var result = new char[TotalLength];
|
||||
var i = 0;
|
||||
var node = BufList.First;
|
||||
while(node != null)
|
||||
{
|
||||
var buf = node.Value;
|
||||
for(var j=0; j<buf.Length; ++j)
|
||||
{
|
||||
result[i++] = (char)buf[j];
|
||||
}
|
||||
node = node.Next;
|
||||
}
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
public ByteStringBuilder Append(byte[] bytes, int start, int length)
|
||||
{
|
||||
var buf = new byte[length];
|
||||
Array.Copy(bytes, start, buf, 0, length);
|
||||
BufList.AddLast( buf );
|
||||
TotalLength += length;
|
||||
return this;
|
||||
}
|
||||
|
||||
private LinkedList<byte[]> BufList;
|
||||
private int TotalLength;
|
||||
}
|
||||
}
|
||||
|
||||
1283
UniLua/Coder.cs
Normal file
1283
UniLua/Coder.cs
Normal file
File diff suppressed because it is too large
Load Diff
128
UniLua/Common.cs
Normal file
128
UniLua/Common.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
public static class LuaConf
|
||||
{
|
||||
public const int LUAI_BITSINT = 32;
|
||||
|
||||
#pragma warning disable 0429
|
||||
public const int LUAI_MAXSTACK = (LUAI_BITSINT >= 32)
|
||||
? 1000000
|
||||
: 15000
|
||||
;
|
||||
#pragma warning restore 0429
|
||||
|
||||
// reserve some space for error handling
|
||||
public const int LUAI_FIRSTPSEUDOIDX = (-LUAI_MAXSTACK-1000);
|
||||
|
||||
public const string LUA_SIGNATURE = "\u001bLua";
|
||||
public static string LUA_DIRSEP {
|
||||
get { return System.IO.Path.DirectorySeparatorChar.ToString(); }
|
||||
}
|
||||
}
|
||||
|
||||
public static class LuaLimits
|
||||
{
|
||||
public const int MAX_INT = System.Int32.MaxValue - 2;
|
||||
public const int MAXUPVAL = System.Byte.MaxValue;
|
||||
public const int LUAI_MAXCCALLS = 200;
|
||||
public const int MAXSTACK = 250;
|
||||
}
|
||||
|
||||
public static class LuaDef
|
||||
{
|
||||
public const int LUA_MINSTACK = 20;
|
||||
public const int BASIC_STACK_SIZE = LUA_MINSTACK * 2;
|
||||
public const int EXTRA_STACK = 5;
|
||||
|
||||
public const int LUA_RIDX_MAINTHREAD = 1;
|
||||
public const int LUA_RIDX_GLOBALS = 2;
|
||||
public const int LUA_RIDX_LAST = LUA_RIDX_GLOBALS;
|
||||
|
||||
public const int LUA_MULTRET = -1;
|
||||
|
||||
public const int LUA_REGISTRYINDEX = LuaConf.LUAI_FIRSTPSEUDOIDX;
|
||||
|
||||
// number of list items accumulate before a SETLIST instruction
|
||||
public const int LFIELDS_PER_FLUSH = 50;
|
||||
|
||||
public const int LUA_IDSIZE = 60;
|
||||
|
||||
public const string LUA_VERSION_MAJOR = "5";
|
||||
public const string LUA_VERSION_MINOR = "2";
|
||||
public const string LUA_VERSION = "Lua " + LUA_VERSION_MAJOR + "." + LUA_VERSION_MINOR;
|
||||
|
||||
public const string LUA_ENV = "_ENV";
|
||||
|
||||
public const int BASE_CI_SIZE = 8;
|
||||
}
|
||||
|
||||
public static class LuaConstants
|
||||
{
|
||||
public const int LUA_NOREF = -2;
|
||||
public const int LUA_REFNIL = -1;
|
||||
}
|
||||
|
||||
public enum LuaType
|
||||
{
|
||||
LUA_TNONE = -1,
|
||||
LUA_TNIL = 0,
|
||||
LUA_TBOOLEAN = 1,
|
||||
LUA_TLIGHTUSERDATA = 2,
|
||||
LUA_TNUMBER = 3,
|
||||
LUA_TSTRING = 4,
|
||||
LUA_TTABLE = 5,
|
||||
LUA_TFUNCTION = 6,
|
||||
LUA_TUSERDATA = 7,
|
||||
LUA_TTHREAD = 8,
|
||||
|
||||
LUA_TUINT64 = 9,
|
||||
|
||||
LUA_NUMTAGS = 10,
|
||||
|
||||
LUA_TPROTO,
|
||||
LUA_TUPVAL,
|
||||
LUA_TDEADKEY,
|
||||
}
|
||||
|
||||
public enum ClosureType
|
||||
{
|
||||
LUA,
|
||||
CSHARP,
|
||||
}
|
||||
|
||||
public enum ThreadStatus
|
||||
{
|
||||
LUA_RESUME_ERROR = -1,
|
||||
LUA_OK = 0,
|
||||
LUA_YIELD = 1,
|
||||
LUA_ERRRUN = 2,
|
||||
LUA_ERRSYNTAX = 3,
|
||||
LUA_ERRMEM = 4,
|
||||
LUA_ERRGCMM = 5,
|
||||
LUA_ERRERR = 6,
|
||||
|
||||
LUA_ERRFILE = 7,
|
||||
}
|
||||
|
||||
/* ORDER TM */
|
||||
internal enum LuaOp
|
||||
{
|
||||
LUA_OPADD = 0,
|
||||
LUA_OPSUB = 1,
|
||||
LUA_OPMUL = 2,
|
||||
LUA_OPDIV = 3,
|
||||
LUA_OPMOD = 4,
|
||||
LUA_OPPOW = 5,
|
||||
LUA_OPUNM = 6,
|
||||
}
|
||||
|
||||
public enum LuaEq
|
||||
{
|
||||
LUA_OPEQ = 0,
|
||||
LUA_OPLT = 1,
|
||||
LUA_OPLE = 2,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
343
UniLua/Do.cs
Normal file
343
UniLua/Do.cs
Normal file
@@ -0,0 +1,343 @@
|
||||
|
||||
// #define DEBUG_D_PRE_CALL
|
||||
// #define DEBUG_D_POS_CALL
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using ULDebug = UniLua.Tools.ULDebug;
|
||||
using InstructionPtr = Pointer<Instruction>;
|
||||
using Exception = System.Exception;
|
||||
|
||||
public class LuaRuntimeException : Exception
|
||||
{
|
||||
public ThreadStatus ErrCode { get; private set; }
|
||||
|
||||
public LuaRuntimeException( ThreadStatus errCode )
|
||||
{
|
||||
ErrCode = errCode;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class LuaState
|
||||
{
|
||||
internal void D_Throw( ThreadStatus errCode )
|
||||
{
|
||||
throw new LuaRuntimeException( errCode );
|
||||
}
|
||||
|
||||
private ThreadStatus D_RawRunProtected<T>( PFuncDelegate<T> func, ref T ud )
|
||||
{
|
||||
int oldNumCSharpCalls = NumCSharpCalls;
|
||||
ThreadStatus res = ThreadStatus.LUA_OK;
|
||||
try
|
||||
{
|
||||
func(ref ud);
|
||||
}
|
||||
catch( LuaRuntimeException e )
|
||||
{
|
||||
NumCSharpCalls = oldNumCSharpCalls;
|
||||
res = e.ErrCode;
|
||||
}
|
||||
NumCSharpCalls = oldNumCSharpCalls;
|
||||
return res;
|
||||
}
|
||||
|
||||
private void SetErrorObj( ThreadStatus errCode, StkId oldTop )
|
||||
{
|
||||
switch( errCode )
|
||||
{
|
||||
case ThreadStatus.LUA_ERRMEM: // memory error?
|
||||
oldTop.V.SetSValue("not enough memory");
|
||||
break;
|
||||
|
||||
case ThreadStatus.LUA_ERRERR:
|
||||
oldTop.V.SetSValue("error in error handling");
|
||||
break;
|
||||
|
||||
default: // error message on current top
|
||||
oldTop.V.SetObj(ref Stack[Top.Index-1].V);
|
||||
break;
|
||||
}
|
||||
Top = Stack[oldTop.Index+1];
|
||||
}
|
||||
|
||||
private ThreadStatus D_PCall<T>( PFuncDelegate<T> func, ref T ud,
|
||||
int oldTopIndex, int errFunc )
|
||||
{
|
||||
int oldCIIndex = CI.Index;
|
||||
bool oldAllowHook = AllowHook;
|
||||
int oldNumNonYieldable = NumNonYieldable;
|
||||
int oldErrFunc = ErrFunc;
|
||||
|
||||
ErrFunc = errFunc;
|
||||
ThreadStatus status = D_RawRunProtected<T>( func, ref ud );
|
||||
if( status != ThreadStatus.LUA_OK ) // an error occurred?
|
||||
{
|
||||
F_Close( Stack[oldTopIndex] );
|
||||
SetErrorObj( status, Stack[oldTopIndex] );
|
||||
CI = BaseCI[oldCIIndex];
|
||||
AllowHook = oldAllowHook;
|
||||
NumNonYieldable = oldNumNonYieldable;
|
||||
}
|
||||
ErrFunc = oldErrFunc;
|
||||
return status;
|
||||
}
|
||||
|
||||
private void D_Call( StkId func, int nResults, bool allowYield )
|
||||
{
|
||||
if( ++NumCSharpCalls >= LuaLimits.LUAI_MAXCCALLS )
|
||||
{
|
||||
if( NumCSharpCalls == LuaLimits.LUAI_MAXCCALLS )
|
||||
G_RunError( "CSharp Stack Overflow" );
|
||||
else if( NumCSharpCalls >=
|
||||
(LuaLimits.LUAI_MAXCCALLS + (LuaLimits.LUAI_MAXCCALLS>>3))
|
||||
)
|
||||
D_Throw( ThreadStatus.LUA_ERRERR );
|
||||
}
|
||||
if( !allowYield )
|
||||
NumNonYieldable++;
|
||||
if( !D_PreCall( func, nResults ) ) // is a Lua function ?
|
||||
V_Execute();
|
||||
if( !allowYield )
|
||||
NumNonYieldable--;
|
||||
NumCSharpCalls--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// return true if function has been executed
|
||||
/// </summary>
|
||||
private bool D_PreCall( StkId func, int nResults )
|
||||
{
|
||||
// prepare for Lua call
|
||||
|
||||
#if DEBUG_D_PRE_CALL
|
||||
Console.WriteLine( "============================ D_PreCall func:" + func );
|
||||
#endif
|
||||
|
||||
int funcIndex = func.Index;
|
||||
if(!func.V.TtIsFunction()) {
|
||||
// not a function
|
||||
// retry with `function' tag method
|
||||
func = tryFuncTM( func );
|
||||
|
||||
// now it must be a function
|
||||
return D_PreCall( func, nResults );
|
||||
}
|
||||
|
||||
if(func.V.ClIsLuaClosure()) {
|
||||
var cl = func.V.ClLValue();
|
||||
Utl.Assert(cl != null);
|
||||
var p = cl.Proto;
|
||||
|
||||
D_CheckStack(p.MaxStackSize + p.NumParams);
|
||||
func = Stack[funcIndex];
|
||||
|
||||
//
|
||||
int n = (Top.Index - func.Index) - 1;
|
||||
for( ; n<p.NumParams; ++n )
|
||||
{ StkId.inc(ref Top).V.SetNilValue(); }
|
||||
|
||||
int stackBase = (!p.IsVarArg) ? (func.Index + 1) : AdjustVarargs( p, n );
|
||||
|
||||
CI = ExtendCI();
|
||||
CI.NumResults = nResults;
|
||||
CI.FuncIndex = func.Index;
|
||||
CI.BaseIndex = stackBase;
|
||||
CI.TopIndex = stackBase + p.MaxStackSize;
|
||||
Utl.Assert(CI.TopIndex <= StackLast);
|
||||
CI.SavedPc = new InstructionPtr( p.Code, 0 );
|
||||
CI.CallStatus = CallStatus.CIST_LUA;
|
||||
|
||||
Top = Stack[CI.TopIndex];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(func.V.ClIsCsClosure()) {
|
||||
var cscl = func.V.ClCsValue();
|
||||
Utl.Assert(cscl != null);
|
||||
|
||||
D_CheckStack(LuaDef.LUA_MINSTACK);
|
||||
func = Stack[funcIndex];
|
||||
|
||||
CI = ExtendCI();
|
||||
CI.NumResults = nResults;
|
||||
CI.FuncIndex = func.Index;
|
||||
CI.TopIndex = Top.Index + LuaDef.LUA_MINSTACK;
|
||||
CI.CallStatus = CallStatus.CIST_NONE;
|
||||
|
||||
// do the actual call
|
||||
int n = cscl.F( this );
|
||||
|
||||
// poscall
|
||||
D_PosCall( Top.Index-n );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
private int D_PosCall( int firstResultIndex )
|
||||
{
|
||||
// TODO: hook
|
||||
// be careful: CI may be changed after hook
|
||||
|
||||
int resIndex = CI.FuncIndex;
|
||||
int wanted = CI.NumResults;
|
||||
|
||||
#if DEBUG_D_POS_CALL
|
||||
Console.WriteLine( "[D] ==== PosCall enter" );
|
||||
Console.WriteLine( "[D] ==== PosCall res:" + res );
|
||||
Console.WriteLine( "[D] ==== PosCall wanted:" + wanted );
|
||||
#endif
|
||||
|
||||
CI = BaseCI[CI.Index-1];
|
||||
|
||||
int i = wanted;
|
||||
for( ; i!=0 && firstResultIndex < Top.Index; --i )
|
||||
{
|
||||
#if DEBUG_D_POS_CALL
|
||||
Console.WriteLine( "[D] ==== PosCall assign lhs res:" + res );
|
||||
Console.WriteLine( "[D] ==== PosCall assign rhs firstResult:" + firstResult );
|
||||
#endif
|
||||
Stack[resIndex++].V.SetObj(ref Stack[firstResultIndex++].V);
|
||||
}
|
||||
while( i-- > 0 )
|
||||
{
|
||||
#if DEBUG_D_POS_CALL
|
||||
Console.WriteLine( "[D] ==== PosCall new LuaNil()" );
|
||||
#endif
|
||||
Stack[resIndex++].V.SetNilValue();
|
||||
}
|
||||
Top = Stack[resIndex];
|
||||
#if DEBUG_D_POS_CALL
|
||||
Console.WriteLine( "[D] ==== PosCall return " + (wanted - LuaDef.LUA_MULTRET) );
|
||||
#endif
|
||||
return (wanted - LuaDef.LUA_MULTRET);
|
||||
}
|
||||
|
||||
private CallInfo ExtendCI()
|
||||
{
|
||||
int newIndex = CI.Index + 1;
|
||||
if(newIndex >= BaseCI.Length) {
|
||||
int newLength = BaseCI.Length*2;
|
||||
var newBaseCI = new CallInfo[newLength];
|
||||
int i = 0;
|
||||
while(i < BaseCI.Length) {
|
||||
newBaseCI[i] = BaseCI[i];
|
||||
newBaseCI[i].List = newBaseCI;
|
||||
++i;
|
||||
}
|
||||
while(i < newLength) {
|
||||
var newCI = new CallInfo();
|
||||
newBaseCI[i] = newCI;
|
||||
newCI.List = newBaseCI;
|
||||
newCI.Index = i;
|
||||
++i;
|
||||
}
|
||||
BaseCI = newBaseCI;
|
||||
CI = newBaseCI[CI.Index];
|
||||
return newBaseCI[newIndex];
|
||||
}
|
||||
else {
|
||||
return BaseCI[newIndex];
|
||||
}
|
||||
}
|
||||
|
||||
private int AdjustVarargs( LuaProto p, int actual )
|
||||
{
|
||||
// `...'
|
||||
// : func (base)fixed-p1 fixed-p2 var-p1 var-p2 top
|
||||
// : func nil nil var-p1 var-p2 (base)fixed-p1 fixed-p2 (reserved...) top
|
||||
//
|
||||
// `...'
|
||||
// func (base)fixed-p1 fixed-p2 (reserved...) top
|
||||
|
||||
int NumFixArgs = p.NumParams;
|
||||
Utl.Assert( actual >= NumFixArgs, "AdjustVarargs (actual >= NumFixArgs) is false" );
|
||||
|
||||
int fixedArg = Top.Index - actual; // first fixed argument
|
||||
int stackBase = Top.Index; // final position of first argument
|
||||
for( int i=stackBase; i<stackBase+NumFixArgs; ++i )
|
||||
{
|
||||
Stack[i].V.SetObj(ref Stack[fixedArg].V);
|
||||
Stack[fixedArg++].V.SetNilValue();
|
||||
}
|
||||
Top = Stack[stackBase+NumFixArgs];
|
||||
return stackBase;
|
||||
}
|
||||
|
||||
private StkId tryFuncTM( StkId func )
|
||||
{
|
||||
var tmObj = T_GetTMByObj( ref func.V, TMS.TM_CALL );
|
||||
if(!tmObj.V.TtIsFunction())
|
||||
G_TypeError( func, "call" );
|
||||
|
||||
// open a hole inside the stack at `func'
|
||||
for(int i=Top.Index; i>func.Index; --i)
|
||||
{ Stack[i].V.SetObj(ref Stack[i-1].V); }
|
||||
|
||||
IncrTop();
|
||||
func.V.SetObj(ref tmObj.V);
|
||||
return func;
|
||||
}
|
||||
|
||||
private void D_CheckStack(int n)
|
||||
{
|
||||
if(StackLast - Top.Index <= n)
|
||||
D_GrowStack(n);
|
||||
// TODO: FOR DEBUGGING
|
||||
// else
|
||||
// CondMoveStack();
|
||||
}
|
||||
|
||||
// some space for error handling
|
||||
private const int ERRORSTACKSIZE = LuaConf.LUAI_MAXSTACK + 200;
|
||||
|
||||
private void D_GrowStack(int n)
|
||||
{
|
||||
int size = Stack.Length;
|
||||
if(size > LuaConf.LUAI_MAXSTACK)
|
||||
D_Throw(ThreadStatus.LUA_ERRERR);
|
||||
|
||||
int needed = Top.Index + n + LuaDef.EXTRA_STACK;
|
||||
int newsize = 2 * size;
|
||||
if(newsize > LuaConf.LUAI_MAXSTACK)
|
||||
{ newsize = LuaConf.LUAI_MAXSTACK; }
|
||||
if(newsize < needed)
|
||||
{ newsize = needed; }
|
||||
if(newsize > LuaConf.LUAI_MAXSTACK)
|
||||
{
|
||||
D_ReallocStack(ERRORSTACKSIZE);
|
||||
G_RunError("stack overflow");
|
||||
}
|
||||
else
|
||||
{
|
||||
D_ReallocStack(newsize);
|
||||
}
|
||||
}
|
||||
|
||||
private void D_ReallocStack(int size)
|
||||
{
|
||||
Utl.Assert(size <= LuaConf.LUAI_MAXSTACK || size == ERRORSTACKSIZE);
|
||||
var newStack = new StkId[size];
|
||||
int i = 0;
|
||||
for( ; i<Stack.Length; ++i) {
|
||||
newStack[i] = Stack[i];
|
||||
newStack[i].SetList(newStack);
|
||||
}
|
||||
for( ; i<size; ++i) {
|
||||
newStack[i] = new StkId();
|
||||
newStack[i].SetList(newStack);
|
||||
newStack[i].SetIndex(i);
|
||||
newStack[i].V.SetNilValue();
|
||||
}
|
||||
Top = newStack[Top.Index];
|
||||
Stack = newStack;
|
||||
StackLast = size - LuaDef.EXTRA_STACK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
219
UniLua/Dump.cs
Normal file
219
UniLua/Dump.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
public enum DumpStatus
|
||||
{
|
||||
OK,
|
||||
ERROR,
|
||||
}
|
||||
|
||||
public delegate DumpStatus LuaWriter( byte[] bytes, int start, int length );
|
||||
internal class DumpState
|
||||
{
|
||||
public static DumpStatus Dump(
|
||||
LuaProto proto, LuaWriter writer, bool strip )
|
||||
{
|
||||
var d = new DumpState();
|
||||
d.Writer = writer;
|
||||
d.Strip = strip;
|
||||
d.Status = DumpStatus.OK;
|
||||
|
||||
d.DumpHeader();
|
||||
d.DumpFunction( proto );
|
||||
|
||||
return d.Status;
|
||||
}
|
||||
|
||||
private LuaWriter Writer;
|
||||
private bool Strip;
|
||||
private DumpStatus Status;
|
||||
|
||||
private const string LUAC_TAIL = "\u0019\u0093\r\n\u001a\n";
|
||||
private static int VERSION = (LuaDef.LUA_VERSION_MAJOR[0]-'0') * 16 +
|
||||
(LuaDef.LUA_VERSION_MINOR[0]-'0');
|
||||
private static int LUAC_HEADERSIZE = LuaConf.LUA_SIGNATURE.Length +
|
||||
2 + 6 + LUAC_TAIL.Length;
|
||||
private const int FORMAT = 0;
|
||||
private const int ENDIAN = 1;
|
||||
|
||||
private DumpState()
|
||||
{
|
||||
}
|
||||
|
||||
private byte[] BuildHeader()
|
||||
{
|
||||
var bytes = new byte[LUAC_HEADERSIZE];
|
||||
int i = 0;
|
||||
|
||||
for(var j=0; j<LuaConf.LUA_SIGNATURE.Length; ++j)
|
||||
bytes[i++] = (byte)LuaConf.LUA_SIGNATURE[j];
|
||||
|
||||
bytes[i++] = (byte)VERSION;
|
||||
bytes[i++] = (byte)FORMAT;
|
||||
bytes[i++] = (byte)ENDIAN;
|
||||
bytes[i++] = (byte)4; // sizeof(int)
|
||||
bytes[i++] = (byte)4; // sizeof(size_t)
|
||||
bytes[i++] = (byte)4; // sizeof(Instruction)
|
||||
bytes[i++] = (byte)sizeof(double); // sizeof(lua_Number)
|
||||
bytes[i++] = (byte)0; // is lua_Number integral?
|
||||
|
||||
for(var j=0; j<LUAC_TAIL.Length; ++j)
|
||||
bytes[i++] = (byte)LUAC_TAIL[j];
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private void DumpHeader()
|
||||
{
|
||||
var bytes = BuildHeader();
|
||||
DumpBlock( bytes );
|
||||
}
|
||||
|
||||
private void DumpBool( bool value )
|
||||
{
|
||||
DumpByte( value ? (byte)1 : (byte) 0 );
|
||||
}
|
||||
|
||||
private void DumpInt( int value )
|
||||
{
|
||||
DumpBlock( BitConverter.GetBytes( value ) );
|
||||
}
|
||||
|
||||
private void DumpUInt( uint value )
|
||||
{
|
||||
DumpBlock( BitConverter.GetBytes( value ) );
|
||||
}
|
||||
|
||||
private void DumpString( string value )
|
||||
{
|
||||
if( value == null )
|
||||
{
|
||||
DumpUInt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
DumpUInt( (uint)(value.Length + 1) );
|
||||
for(var i=0; i<value.Length; ++i)
|
||||
DumpByte( (byte)value[i] );
|
||||
DumpByte( (byte)'\0' );
|
||||
}
|
||||
}
|
||||
|
||||
private void DumpByte( byte value )
|
||||
{
|
||||
var bytes = new byte[] { value };
|
||||
DumpBlock( bytes );
|
||||
}
|
||||
|
||||
private void DumpCode( LuaProto proto )
|
||||
{
|
||||
DumpVector( proto.Code, (ins) => {
|
||||
DumpBlock( BitConverter.GetBytes( (uint)ins ) );
|
||||
});
|
||||
}
|
||||
|
||||
private void DumpConstants( LuaProto proto )
|
||||
{
|
||||
DumpVector( proto.K, (k) => {
|
||||
var t = k.V.Tt;
|
||||
DumpByte( (byte)t );
|
||||
switch( t )
|
||||
{
|
||||
case (int)LuaType.LUA_TNIL:
|
||||
break;
|
||||
case (int)LuaType.LUA_TBOOLEAN:
|
||||
DumpBool(k.V.BValue());
|
||||
break;
|
||||
case (int)LuaType.LUA_TNUMBER:
|
||||
DumpBlock( BitConverter.GetBytes(k.V.NValue) );
|
||||
break;
|
||||
case (int)LuaType.LUA_TSTRING:
|
||||
DumpString(k.V.SValue());
|
||||
break;
|
||||
default:
|
||||
Utl.Assert(false);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
DumpVector( proto.P, (p) => {
|
||||
DumpFunction( p );
|
||||
});
|
||||
}
|
||||
|
||||
private void DumpUpvalues( LuaProto proto )
|
||||
{
|
||||
DumpVector( proto.Upvalues, (upval) => {
|
||||
DumpByte( upval.InStack ? (byte)1 : (byte)0 );
|
||||
DumpByte( (byte)upval.Index );
|
||||
});
|
||||
}
|
||||
|
||||
private void DumpDebug( LuaProto proto )
|
||||
{
|
||||
DumpString( Strip ? null : proto.Source );
|
||||
|
||||
DumpVector( (Strip ? null : proto.LineInfo), (line) => {
|
||||
DumpInt(line);
|
||||
});
|
||||
|
||||
DumpVector( (Strip ? null : proto.LocVars), (locvar) => {
|
||||
DumpString( locvar.VarName );
|
||||
DumpInt( locvar.StartPc );
|
||||
DumpInt( locvar.EndPc );
|
||||
});
|
||||
|
||||
DumpVector( (Strip ? null : proto.Upvalues), (upval) => {
|
||||
DumpString( upval.Name );
|
||||
});
|
||||
}
|
||||
|
||||
private void DumpFunction( LuaProto proto )
|
||||
{
|
||||
DumpInt( proto.LineDefined );
|
||||
DumpInt( proto.LastLineDefined );
|
||||
DumpByte( (byte)proto.NumParams );
|
||||
DumpByte( proto.IsVarArg ? (byte)1 : (byte)0 );
|
||||
DumpByte( (byte)proto.MaxStackSize );
|
||||
DumpCode( proto );
|
||||
DumpConstants( proto );
|
||||
DumpUpvalues( proto );
|
||||
DumpDebug( proto );
|
||||
}
|
||||
|
||||
private delegate void DumpItemDelegate<T>( T item );
|
||||
private void DumpVector<T>( IList<T> list, DumpItemDelegate<T> dumpItem )
|
||||
{
|
||||
if( list == null )
|
||||
{
|
||||
DumpInt( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
DumpInt( list.Count );
|
||||
for( var i=0; i<list.Count; ++i )
|
||||
{
|
||||
dumpItem( list[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DumpBlock( byte[] bytes )
|
||||
{
|
||||
DumpBlock( bytes, 0, bytes.Length );
|
||||
}
|
||||
|
||||
private void DumpBlock( byte[] bytes, int start, int length )
|
||||
{
|
||||
if( Status == DumpStatus.OK )
|
||||
{
|
||||
Status = Writer(bytes, start, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
759
UniLua/LLex.cs
Normal file
759
UniLua/LLex.cs
Normal file
@@ -0,0 +1,759 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NumberStyles = System.Globalization.NumberStyles;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
public class LLexException : Exception
|
||||
{
|
||||
public LLexException( string info ) : base( info ) { }
|
||||
}
|
||||
|
||||
public enum TK
|
||||
{
|
||||
// reserved words
|
||||
AND = 257,
|
||||
BREAK,
|
||||
DO,
|
||||
ELSE,
|
||||
ELSEIF,
|
||||
END,
|
||||
FALSE,
|
||||
FOR,
|
||||
FUNCTION,
|
||||
GOTO,
|
||||
IF,
|
||||
IN,
|
||||
LOCAL,
|
||||
NIL,
|
||||
NOT,
|
||||
OR,
|
||||
REPEAT,
|
||||
RETURN,
|
||||
THEN,
|
||||
TRUE,
|
||||
UNTIL,
|
||||
WHILE,
|
||||
// other terminal symbols
|
||||
CONCAT,
|
||||
DOTS,
|
||||
EQ,
|
||||
GE,
|
||||
LE,
|
||||
NE,
|
||||
DBCOLON,
|
||||
NUMBER,
|
||||
STRING,
|
||||
NAME,
|
||||
EOS,
|
||||
}
|
||||
|
||||
public abstract class Token
|
||||
{
|
||||
public abstract int TokenType{ get; }
|
||||
|
||||
public bool EqualsToToken( Token other ) {
|
||||
return TokenType == other.TokenType;
|
||||
}
|
||||
|
||||
public bool EqualsToToken( int other ) {
|
||||
return TokenType == other;
|
||||
}
|
||||
|
||||
public bool EqualsToToken( TK other ) {
|
||||
return TokenType == (int)other;
|
||||
}
|
||||
}
|
||||
|
||||
public class LiteralToken : Token
|
||||
{
|
||||
private int _Literal;
|
||||
|
||||
public LiteralToken( int literal )
|
||||
{
|
||||
_Literal = literal;
|
||||
}
|
||||
|
||||
public override int TokenType
|
||||
{
|
||||
get { return _Literal; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "LiteralToken: {0}", _Literal );
|
||||
}
|
||||
}
|
||||
|
||||
public class TypedToken : Token
|
||||
{
|
||||
private TK _Type;
|
||||
|
||||
public TypedToken( TK type )
|
||||
{
|
||||
_Type = type;
|
||||
}
|
||||
|
||||
public override int TokenType
|
||||
{
|
||||
get { return (int)_Type; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "TypedToken: {0}", _Type );
|
||||
}
|
||||
}
|
||||
|
||||
public class StringToken : TypedToken
|
||||
{
|
||||
public string SemInfo;
|
||||
|
||||
public StringToken( string seminfo ) : base( TK.STRING )
|
||||
{
|
||||
SemInfo = seminfo;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "StringToken: {0}", SemInfo );
|
||||
}
|
||||
}
|
||||
|
||||
public class NameToken : TypedToken
|
||||
{
|
||||
public string SemInfo;
|
||||
|
||||
public NameToken( string seminfo ) : base( TK.NAME )
|
||||
{
|
||||
SemInfo = seminfo;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "NameToken: {0}", SemInfo );
|
||||
}
|
||||
}
|
||||
|
||||
public class NumberToken : TypedToken
|
||||
{
|
||||
public double SemInfo;
|
||||
|
||||
public NumberToken( double seminfo ) : base( TK.NUMBER )
|
||||
{
|
||||
SemInfo = seminfo;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format( "NumberToken: {0}", SemInfo );
|
||||
}
|
||||
}
|
||||
|
||||
public class LLex
|
||||
{
|
||||
public const char EOZ = Char.MaxValue;
|
||||
|
||||
private LuaState Lua;
|
||||
private int Current;
|
||||
public int LineNumber;
|
||||
public int LastLine;
|
||||
private ILoadInfo LoadInfo;
|
||||
public string Source;
|
||||
|
||||
public Token Token;
|
||||
private Token LookAhead;
|
||||
|
||||
private StringBuilder _Saved;
|
||||
private StringBuilder Saved
|
||||
{
|
||||
get {
|
||||
if( _Saved == null ) { _Saved = new StringBuilder(); }
|
||||
return _Saved;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, TK> ReservedWordDict;
|
||||
static LLex()
|
||||
{
|
||||
ReservedWordDict = new Dictionary<string, TK>();
|
||||
ReservedWordDict.Add("and", TK.AND);
|
||||
ReservedWordDict.Add("break", TK.BREAK);
|
||||
ReservedWordDict.Add("do", TK.DO);
|
||||
ReservedWordDict.Add("else", TK.ELSE);
|
||||
ReservedWordDict.Add("elseif", TK.ELSEIF);
|
||||
ReservedWordDict.Add("end", TK.END);
|
||||
ReservedWordDict.Add("false", TK.FALSE);
|
||||
ReservedWordDict.Add("for", TK.FOR);
|
||||
ReservedWordDict.Add("function", TK.FUNCTION);
|
||||
ReservedWordDict.Add("goto", TK.GOTO);
|
||||
ReservedWordDict.Add("if", TK.IF);
|
||||
ReservedWordDict.Add("in", TK.IN);
|
||||
ReservedWordDict.Add("local", TK.LOCAL);
|
||||
ReservedWordDict.Add("nil", TK.NIL);
|
||||
ReservedWordDict.Add("not", TK.NOT);
|
||||
ReservedWordDict.Add("or", TK.OR);
|
||||
ReservedWordDict.Add("repeat", TK.REPEAT);
|
||||
ReservedWordDict.Add("return", TK.RETURN);
|
||||
ReservedWordDict.Add("then", TK.THEN);
|
||||
ReservedWordDict.Add("true", TK.TRUE);
|
||||
ReservedWordDict.Add("until", TK.UNTIL);
|
||||
ReservedWordDict.Add("while", TK.WHILE);
|
||||
}
|
||||
|
||||
public LLex( ILuaState lua, ILoadInfo loadinfo, string name )
|
||||
{
|
||||
Lua = (LuaState)lua;
|
||||
LoadInfo = loadinfo;
|
||||
LineNumber = 1;
|
||||
LastLine = 1;
|
||||
Token = null;
|
||||
LookAhead = null;
|
||||
_Saved = null;
|
||||
Source = name;
|
||||
|
||||
_Next();
|
||||
}
|
||||
|
||||
public void Next()
|
||||
{
|
||||
LastLine = LineNumber;
|
||||
if( LookAhead != null )
|
||||
{
|
||||
Token = LookAhead;
|
||||
LookAhead = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
Token = _Lex();
|
||||
}
|
||||
}
|
||||
|
||||
public Token GetLookAhead()
|
||||
{
|
||||
Utl.Assert( LookAhead == null );
|
||||
LookAhead = _Lex();
|
||||
return LookAhead;
|
||||
}
|
||||
|
||||
private void _Next()
|
||||
{
|
||||
var c = LoadInfo.ReadByte();
|
||||
Current = (c == -1) ? EOZ : c;
|
||||
}
|
||||
|
||||
private void _SaveAndNext()
|
||||
{
|
||||
Saved.Append( (char)Current );
|
||||
_Next();
|
||||
}
|
||||
|
||||
private void _Save( char c )
|
||||
{
|
||||
Saved.Append( c );
|
||||
}
|
||||
|
||||
private string _GetSavedString()
|
||||
{
|
||||
return Saved.ToString();
|
||||
}
|
||||
|
||||
private void _ClearSaved()
|
||||
{
|
||||
_Saved = null;
|
||||
}
|
||||
|
||||
private bool _CurrentIsNewLine()
|
||||
{
|
||||
return Current == '\n' || Current == '\r';
|
||||
}
|
||||
|
||||
private bool _CurrentIsDigit()
|
||||
{
|
||||
return Char.IsDigit( (char)Current );
|
||||
}
|
||||
|
||||
private bool _CurrentIsXDigit()
|
||||
{
|
||||
return _CurrentIsDigit() ||
|
||||
('A' <= Current && Current <= 'F') ||
|
||||
('a' <= Current && Current <= 'f');
|
||||
}
|
||||
|
||||
private bool _CurrentIsSpace()
|
||||
{
|
||||
return Char.IsWhiteSpace( (char)Current );
|
||||
}
|
||||
|
||||
private bool _CurrentIsAlpha()
|
||||
{
|
||||
return Char.IsLetter( (char)Current );
|
||||
}
|
||||
|
||||
private bool _IsReserved( string identifier, out TK type )
|
||||
{
|
||||
return ReservedWordDict.TryGetValue( identifier, out type );
|
||||
}
|
||||
|
||||
public bool IsReservedWord( string name )
|
||||
{
|
||||
return ReservedWordDict.ContainsKey( name );
|
||||
}
|
||||
|
||||
private void _IncLineNumber()
|
||||
{
|
||||
var old = Current;
|
||||
_Next();
|
||||
if( _CurrentIsNewLine() && Current != old )
|
||||
_Next();
|
||||
if( ++LineNumber >= Int32.MaxValue )
|
||||
_Error( "chunk has too many lines" );
|
||||
}
|
||||
|
||||
private string _ReadLongString( int sep )
|
||||
{
|
||||
_SaveAndNext();
|
||||
|
||||
if( _CurrentIsNewLine() )
|
||||
_IncLineNumber();
|
||||
|
||||
while( true )
|
||||
{
|
||||
switch( Current )
|
||||
{
|
||||
case EOZ:
|
||||
_LexError( _GetSavedString(),
|
||||
"unfinished long string/comment",
|
||||
(int)TK.EOS );
|
||||
break;
|
||||
|
||||
case '[':
|
||||
{
|
||||
if( _SkipSep() == sep )
|
||||
{
|
||||
_SaveAndNext();
|
||||
if( sep == 0 )
|
||||
{
|
||||
_LexError( _GetSavedString(),
|
||||
"nesting of [[...]] is deprecated",
|
||||
(int)TK.EOS );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ']':
|
||||
{
|
||||
if( _SkipSep() == sep )
|
||||
{
|
||||
_SaveAndNext();
|
||||
goto endloop;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '\n':
|
||||
case '\r':
|
||||
{
|
||||
_Save('\n');
|
||||
_IncLineNumber();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
_SaveAndNext();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
endloop:
|
||||
var r = _GetSavedString();
|
||||
return r.Substring( 2+sep, r.Length - 2*(2+sep) );
|
||||
}
|
||||
|
||||
private void _EscapeError( string info, string msg )
|
||||
{
|
||||
_LexError( "\\"+info, msg, (int)TK.STRING );
|
||||
}
|
||||
|
||||
private byte _ReadHexEscape()
|
||||
{
|
||||
int r = 0;
|
||||
var c = new char[3] { 'x', (char)0, (char)0 };
|
||||
// read two hex digits
|
||||
for( int i=1; i<3; ++i )
|
||||
{
|
||||
_Next();
|
||||
c[i] = (char)Current;
|
||||
if( !_CurrentIsXDigit() )
|
||||
{
|
||||
_EscapeError( new String(c, 0, i+1),
|
||||
"hexadecimal digit expected" );
|
||||
// error
|
||||
}
|
||||
r = (r << 4) + Int32.Parse( Current.ToString(),
|
||||
NumberStyles.HexNumber );
|
||||
}
|
||||
return (byte)r;
|
||||
}
|
||||
|
||||
private byte _ReadDecEscape()
|
||||
{
|
||||
int r = 0;
|
||||
var c = new char[3];
|
||||
// read up to 3 digits
|
||||
int i = 0;
|
||||
for( i=0; i<3 && _CurrentIsDigit(); ++i )
|
||||
{
|
||||
c[i] = (char)Current;
|
||||
r = r*10 + Current - '0';
|
||||
_Next();
|
||||
}
|
||||
if( r > Byte.MaxValue )
|
||||
_EscapeError( new String(c, 0, i),
|
||||
"decimal escape too large" );
|
||||
return (byte)r;
|
||||
}
|
||||
|
||||
private string _ReadString()
|
||||
{
|
||||
var del = Current;
|
||||
_Next();
|
||||
while( Current != del )
|
||||
{
|
||||
switch( Current )
|
||||
{
|
||||
case EOZ:
|
||||
_Error( "unfinished string" );
|
||||
continue;
|
||||
|
||||
case '\n':
|
||||
case '\r':
|
||||
_Error( "unfinished string" );
|
||||
continue;
|
||||
|
||||
case '\\':
|
||||
{
|
||||
byte c;
|
||||
_Next();
|
||||
switch( Current )
|
||||
{
|
||||
case 'a': c=(byte)'\a'; break;
|
||||
case 'b': c=(byte)'\b'; break;
|
||||
case 'f': c=(byte)'\f'; break;
|
||||
case 'n': c=(byte)'\n'; break;
|
||||
case 'r': c=(byte)'\r'; break;
|
||||
case 't': c=(byte)'\t'; break;
|
||||
case 'v': c=(byte)'\v'; break;
|
||||
case 'x': c=_ReadHexEscape(); break;
|
||||
|
||||
case '\n':
|
||||
case '\r': _Save('\n'); _IncLineNumber(); continue;
|
||||
|
||||
case '\\':
|
||||
case '\"':
|
||||
case '\'': c=(byte)Current; break;
|
||||
|
||||
case EOZ: continue;
|
||||
|
||||
// zap following span of spaces
|
||||
case 'z': {
|
||||
_Next(); // skip `z'
|
||||
while( _CurrentIsSpace() )
|
||||
{
|
||||
if( _CurrentIsNewLine() )
|
||||
_IncLineNumber();
|
||||
else
|
||||
_Next();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if( !_CurrentIsDigit() )
|
||||
_EscapeError( Current.ToString(),
|
||||
"invalid escape sequence" );
|
||||
|
||||
// digital escape \ddd
|
||||
c = _ReadDecEscape();
|
||||
_Save( (char)c );
|
||||
continue;
|
||||
// {
|
||||
// c = (char)0;
|
||||
// for(int i=0; i<3 && _CurrentIsDigit(); ++i)
|
||||
// {
|
||||
// c = (char)(c*10 + Current - '0');
|
||||
// _Next();
|
||||
// }
|
||||
// _Save( c );
|
||||
// }
|
||||
// continue;
|
||||
}
|
||||
}
|
||||
_Save( (char)c );
|
||||
_Next();
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
_SaveAndNext();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_Next();
|
||||
return _GetSavedString();
|
||||
}
|
||||
|
||||
private double _ReadNumber()
|
||||
{
|
||||
var expo = new char[] { 'E', 'e' };
|
||||
Utl.Assert( _CurrentIsDigit() );
|
||||
var first = Current;
|
||||
_SaveAndNext();
|
||||
if( first == '0' && (Current == 'X' || Current == 'x'))
|
||||
{
|
||||
expo = new char[] { 'P', 'p' };
|
||||
_SaveAndNext();
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
if( Current == expo[0] || Current == expo[1] )
|
||||
{
|
||||
_SaveAndNext();
|
||||
if( Current == '+' || Current == '-' )
|
||||
_SaveAndNext();
|
||||
}
|
||||
if( _CurrentIsXDigit() || Current == '.' )
|
||||
_SaveAndNext();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
double ret;
|
||||
var str = _GetSavedString();
|
||||
if( LuaState.O_Str2Decimal( str, out ret ) )
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
_Error( "malformed number: " + str );
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// private float _ReadNumber()
|
||||
// {
|
||||
// do
|
||||
// {
|
||||
// _SaveAndNext();
|
||||
// } while( _CurrentIsDigit() || Current == '.' );
|
||||
// if( Current == 'E' || Current == 'e' )
|
||||
// {
|
||||
// _SaveAndNext();
|
||||
// if( Current == '+' || Current == '-' )
|
||||
// _SaveAndNext();
|
||||
// }
|
||||
// while( _CurrentIsAlpha() || _CurrentIsDigit() || Current == '_' )
|
||||
// _SaveAndNext();
|
||||
// float ret;
|
||||
// if( !Single.TryParse( _GetSavedString(), out ret ) )
|
||||
// _Error( "malformed number" );
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
private void _Error( string error )
|
||||
{
|
||||
Lua.O_PushString( string.Format(
|
||||
"{0}:{1}: {2}",
|
||||
Source, LineNumber, error ) );
|
||||
Lua.D_Throw( ThreadStatus.LUA_ERRSYNTAX );
|
||||
}
|
||||
|
||||
private void _LexError( string info, string msg, int tokenType )
|
||||
{
|
||||
// TODO
|
||||
_Error( msg + ":" + info );
|
||||
}
|
||||
|
||||
public void SyntaxError( string msg )
|
||||
{
|
||||
// TODO
|
||||
_Error( msg );
|
||||
}
|
||||
|
||||
private int _SkipSep()
|
||||
{
|
||||
int count = 0;
|
||||
var boundary = Current;
|
||||
_SaveAndNext();
|
||||
while( Current == '=' ) {
|
||||
_SaveAndNext();
|
||||
count++;
|
||||
}
|
||||
return ( Current == boundary ? count : (-count)-1 );
|
||||
}
|
||||
|
||||
private Token _Lex()
|
||||
{
|
||||
_ClearSaved();
|
||||
while( true )
|
||||
{
|
||||
switch( Current )
|
||||
{
|
||||
case '\n':
|
||||
case '\r': {
|
||||
_IncLineNumber();
|
||||
continue;
|
||||
}
|
||||
|
||||
case '-': {
|
||||
_Next();
|
||||
if( Current != '-' ) return new LiteralToken('-');
|
||||
|
||||
// else is a long comment
|
||||
_Next();
|
||||
if( Current == '[' )
|
||||
{
|
||||
int sep = _SkipSep();
|
||||
_ClearSaved();
|
||||
if( sep >= 0 )
|
||||
{
|
||||
_ReadLongString( sep );
|
||||
_ClearSaved();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// else is a short comment
|
||||
while( !_CurrentIsNewLine() && Current != EOZ )
|
||||
_Next();
|
||||
continue;
|
||||
}
|
||||
|
||||
case '[': {
|
||||
int sep = _SkipSep();
|
||||
if( sep >= 0 ) {
|
||||
string seminfo = _ReadLongString( sep );
|
||||
return new StringToken( seminfo );
|
||||
}
|
||||
else if( sep == -1 ) return new LiteralToken('[');
|
||||
else _Error("invalid long string delimiter");
|
||||
continue;
|
||||
}
|
||||
|
||||
case '=': {
|
||||
_Next();
|
||||
if( Current != '=' ) return new LiteralToken('=');
|
||||
_Next();
|
||||
return new TypedToken( TK.EQ );
|
||||
}
|
||||
|
||||
case '<': {
|
||||
_Next();
|
||||
if( Current != '=' ) return new LiteralToken('<');
|
||||
_Next();
|
||||
return new TypedToken( TK.LE );
|
||||
}
|
||||
|
||||
case '>': {
|
||||
_Next();
|
||||
if( Current != '=' ) return new LiteralToken('>');
|
||||
_Next();
|
||||
return new TypedToken( TK.GE );
|
||||
}
|
||||
|
||||
case '~': {
|
||||
_Next();
|
||||
if( Current != '=' ) return new LiteralToken('~');
|
||||
_Next();
|
||||
return new TypedToken( TK.NE );
|
||||
}
|
||||
|
||||
case ':': {
|
||||
_Next();
|
||||
if( Current != ':' ) return new LiteralToken(':');
|
||||
_Next();
|
||||
return new TypedToken( TK.DBCOLON ); // new in 5.2 ?
|
||||
}
|
||||
|
||||
case '"':
|
||||
case '\'': {
|
||||
return new StringToken( _ReadString() );
|
||||
}
|
||||
|
||||
case '.': {
|
||||
_SaveAndNext();
|
||||
if( Current == '.' )
|
||||
{
|
||||
_SaveAndNext();
|
||||
if( Current == '.' )
|
||||
{
|
||||
_SaveAndNext();
|
||||
return new TypedToken( TK.DOTS );
|
||||
}
|
||||
else
|
||||
{
|
||||
return new TypedToken( TK.CONCAT );
|
||||
}
|
||||
}
|
||||
else if( !_CurrentIsDigit() )
|
||||
return new LiteralToken('.');
|
||||
else
|
||||
return new NumberToken( _ReadNumber() );
|
||||
}
|
||||
|
||||
case EOZ: {
|
||||
return new TypedToken( TK.EOS );
|
||||
}
|
||||
|
||||
default: {
|
||||
if( _CurrentIsSpace() )
|
||||
{
|
||||
_Next();
|
||||
continue;
|
||||
}
|
||||
else if( _CurrentIsDigit() )
|
||||
{
|
||||
return new NumberToken( _ReadNumber() );
|
||||
}
|
||||
else if( _CurrentIsAlpha() || Current == '_' )
|
||||
{
|
||||
do {
|
||||
_SaveAndNext();
|
||||
} while( _CurrentIsAlpha() ||
|
||||
_CurrentIsDigit() ||
|
||||
Current == '_' );
|
||||
|
||||
string identifier = _GetSavedString();
|
||||
TK type;
|
||||
if( _IsReserved( identifier, out type ) )
|
||||
{
|
||||
return new TypedToken( type );
|
||||
}
|
||||
else
|
||||
{
|
||||
return new NameToken( identifier );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var c = Current;
|
||||
_Next();
|
||||
return new LiteralToken(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1638
UniLua/LuaAPI.cs
Normal file
1638
UniLua/LuaAPI.cs
Normal file
File diff suppressed because it is too large
Load Diff
754
UniLua/LuaAuxLib.cs
Normal file
754
UniLua/LuaAuxLib.cs
Normal file
@@ -0,0 +1,754 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Cosmos.HAL.Drivers.Video.SVGAII;
|
||||
|
||||
public struct NameFuncPair
|
||||
{
|
||||
public string Name;
|
||||
public CSharpFunctionDelegate Func;
|
||||
|
||||
public NameFuncPair( string name, CSharpFunctionDelegate func )
|
||||
{
|
||||
Name = name;
|
||||
Func = func;
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILuaAuxLib
|
||||
{
|
||||
void L_Where( int level );
|
||||
int L_Error( string fmt, params object[] args );
|
||||
void L_CheckStack( int size, string msg );
|
||||
void L_CheckAny( int narg );
|
||||
void L_CheckType( int index, LuaType t );
|
||||
double L_CheckNumber( int narg );
|
||||
UInt64 L_CheckUInt64( int narg );
|
||||
int L_CheckInteger( int narg );
|
||||
string L_CheckString( int narg );
|
||||
uint L_CheckUnsigned( int narg );
|
||||
void L_ArgCheck( bool cond, int narg, string extraMsg );
|
||||
int L_ArgError( int narg, string extraMsg );
|
||||
string L_TypeName( int index );
|
||||
|
||||
string L_ToString( int index );
|
||||
bool L_GetMetaField( int index, string method );
|
||||
int L_GetSubTable( int index, string fname );
|
||||
|
||||
void L_RequireF( string moduleName, CSharpFunctionDelegate openFunc, bool global );
|
||||
void L_OpenLibs();
|
||||
void L_NewLibTable( NameFuncPair[] define );
|
||||
void L_NewLib( NameFuncPair[] define );
|
||||
void L_SetFuncs( NameFuncPair[] define, int nup );
|
||||
|
||||
int L_Opt(int n, int def);
|
||||
|
||||
int L_OptInt( int narg, int def );
|
||||
string L_OptString( int narg, string def );
|
||||
bool L_CallMeta( int obj, string name );
|
||||
void L_Traceback( ILuaState otherLua, string msg, int level );
|
||||
int L_Len( int index );
|
||||
|
||||
ThreadStatus L_LoadBuffer( string s, string name );
|
||||
ThreadStatus L_LoadBufferX( string s, string name, string mode );
|
||||
ThreadStatus L_LoadFile( string filename );
|
||||
ThreadStatus L_LoadFileX( string filename, string mode );
|
||||
|
||||
ThreadStatus L_LoadString( string s );
|
||||
ThreadStatus L_LoadBytes( byte[] bytes, string name);
|
||||
ThreadStatus L_DoByteArray(byte[] file, string name);
|
||||
ThreadStatus L_DoString( string s );
|
||||
ThreadStatus L_DoFile( string filename );
|
||||
|
||||
|
||||
string L_Gsub( string src, string pattern, string rep );
|
||||
|
||||
// reference system
|
||||
int L_Ref( int t );
|
||||
void L_Unref( int t, int reference );
|
||||
}
|
||||
|
||||
class StringLoadInfo : ILoadInfo
|
||||
{
|
||||
public StringLoadInfo(string s )
|
||||
{
|
||||
Str = s;
|
||||
Pos = 0;
|
||||
}
|
||||
|
||||
public int ReadByte()
|
||||
{
|
||||
if( Pos >= Str.Length )
|
||||
return -1;
|
||||
else
|
||||
return Str[Pos++];
|
||||
}
|
||||
|
||||
public int PeekByte()
|
||||
{
|
||||
if( Pos >= Str.Length )
|
||||
return -1;
|
||||
else
|
||||
return Str[Pos];
|
||||
}
|
||||
|
||||
private string Str;
|
||||
private int Pos;
|
||||
}
|
||||
|
||||
class BytesLoadInfo : ILoadInfo
|
||||
{
|
||||
public BytesLoadInfo( byte[] bytes )
|
||||
{
|
||||
Bytes = bytes;
|
||||
Pos = 0;
|
||||
}
|
||||
|
||||
public int ReadByte()
|
||||
{
|
||||
if( Pos >= Bytes.Length )
|
||||
return -1;
|
||||
else
|
||||
return Bytes[Pos++];
|
||||
}
|
||||
|
||||
public int PeekByte()
|
||||
{
|
||||
if( Pos >= Bytes.Length )
|
||||
return -1;
|
||||
else
|
||||
return Bytes[Pos];
|
||||
}
|
||||
|
||||
private byte[] Bytes;
|
||||
private int Pos;
|
||||
}
|
||||
|
||||
public partial class LuaState
|
||||
{
|
||||
private const int LEVELS1 = 12; // size of the first part of the stack
|
||||
private const int LEVELS2 = 10; // size of the second part of the stack
|
||||
|
||||
public void L_Where( int level )
|
||||
{
|
||||
LuaDebug ar = new LuaDebug();
|
||||
if( API.GetStack( level, ar ) ) // check function at level
|
||||
{
|
||||
GetInfo( "Sl", ar ); // get info about it
|
||||
if( ar.CurrentLine > 0 ) // is there info?
|
||||
{
|
||||
API.PushString( string.Format( "{0}:{1}: ", ar.ShortSrc, ar.CurrentLine ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
API.PushString( "" ); // else, no information available...
|
||||
}
|
||||
|
||||
public int L_Error( string fmt, params object[] args )
|
||||
{
|
||||
L_Where( 1 );
|
||||
API.PushString( string.Format( fmt, args ) );
|
||||
API.Concat( 2 );
|
||||
return API.Error();
|
||||
}
|
||||
|
||||
public void L_CheckStack( int size, string msg )
|
||||
{
|
||||
// keep some extra space to run error routines, if needed
|
||||
if(!API.CheckStack(size + LuaDef.LUA_MINSTACK)) {
|
||||
if(msg != null)
|
||||
{ L_Error(string.Format("stack overflow ({0})", msg)); }
|
||||
else
|
||||
{ L_Error("stack overflow"); }
|
||||
}
|
||||
}
|
||||
|
||||
public void L_CheckAny( int narg )
|
||||
{
|
||||
if( API.Type( narg ) == LuaType.LUA_TNONE )
|
||||
L_ArgError( narg, "value expected" );
|
||||
}
|
||||
|
||||
public double L_CheckNumber( int narg )
|
||||
{
|
||||
bool isnum;
|
||||
double d = API.ToNumberX( narg, out isnum );
|
||||
if( !isnum )
|
||||
TagError( narg, LuaType.LUA_TNUMBER );
|
||||
return d;
|
||||
}
|
||||
|
||||
public UInt64 L_CheckUInt64( int narg )
|
||||
{
|
||||
bool isnum;
|
||||
UInt64 v = API.ToUInt64X( narg, out isnum );
|
||||
if( !isnum )
|
||||
TagError( narg, LuaType.LUA_TUINT64 );
|
||||
return v;
|
||||
}
|
||||
|
||||
public int L_CheckInteger( int narg )
|
||||
{
|
||||
bool isnum;
|
||||
int d = API.ToIntegerX( narg, out isnum );
|
||||
if( !isnum )
|
||||
TagError( narg, LuaType.LUA_TNUMBER );
|
||||
return d;
|
||||
}
|
||||
|
||||
public string L_CheckString( int narg )
|
||||
{
|
||||
string s = API.ToString( narg );
|
||||
if( s == null ) TagError( narg, LuaType.LUA_TSTRING );
|
||||
return s;
|
||||
}
|
||||
|
||||
public uint L_CheckUnsigned( int narg )
|
||||
{
|
||||
bool isnum;
|
||||
uint d = API.ToUnsignedX( narg, out isnum );
|
||||
if( !isnum )
|
||||
TagError( narg, LuaType.LUA_TNUMBER );
|
||||
return d;
|
||||
}
|
||||
|
||||
public int L_Opt(int n, int def)
|
||||
{
|
||||
LuaType t = API.Type(n);
|
||||
if (t == LuaType.LUA_TNONE || t == LuaType.LUA_TNIL)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
return L_CheckInteger(n);
|
||||
}
|
||||
}
|
||||
|
||||
public int L_OptInt( int narg, int def )
|
||||
{
|
||||
LuaType t = API.Type( narg );
|
||||
if( t == LuaType.LUA_TNONE ||
|
||||
t == LuaType.LUA_TNIL )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
return L_CheckInteger( narg );
|
||||
}
|
||||
}
|
||||
|
||||
public string L_OptString( int narg, string def )
|
||||
{
|
||||
LuaType t = API.Type( narg );
|
||||
if( t == LuaType.LUA_TNONE ||
|
||||
t == LuaType.LUA_TNIL )
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
return L_CheckString( narg );
|
||||
}
|
||||
}
|
||||
|
||||
private int TypeError( int index, string typeName )
|
||||
{
|
||||
string msg = string.Format( "{0} expected, got {1}",
|
||||
typeName, L_TypeName( index ) );
|
||||
API.PushString( msg );
|
||||
return L_ArgError( index, msg );
|
||||
}
|
||||
|
||||
private void TagError( int index, LuaType t )
|
||||
{
|
||||
TypeError( index, API.TypeName( t ) );
|
||||
}
|
||||
|
||||
public void L_CheckType( int index, LuaType t )
|
||||
{
|
||||
if( API.Type( index ) != t )
|
||||
TagError( index, t );
|
||||
}
|
||||
|
||||
public void L_ArgCheck( bool cond, int narg, string extraMsg )
|
||||
{
|
||||
if( !cond )
|
||||
L_ArgError( narg, extraMsg );
|
||||
}
|
||||
|
||||
public int L_ArgError( int narg, string extraMsg )
|
||||
{
|
||||
|
||||
LuaDebug ar = new LuaDebug();
|
||||
if( !API.GetStack( 0, ar ) ) // no stack frame ?
|
||||
return L_Error( "bad argument {0} ({1})", narg, extraMsg );
|
||||
|
||||
GetInfo( "n", ar );
|
||||
if( ar.NameWhat == "method" )
|
||||
{
|
||||
narg--; // do not count 'self'
|
||||
if( narg == 0 ) // error is in the self argument itself?
|
||||
return L_Error( "calling '{0}' on bad self", ar.Name );
|
||||
}
|
||||
if( ar.Name == null )
|
||||
ar.Name = PushGlobalFuncName( ar ) ? API.ToString(-1) : "?";
|
||||
return L_Error( "bad argument {0} to '{1}' ({2})",
|
||||
narg, ar.Name, extraMsg );
|
||||
}
|
||||
|
||||
public string L_TypeName( int index )
|
||||
{
|
||||
return API.TypeName( API.Type( index ) );
|
||||
}
|
||||
|
||||
public bool L_GetMetaField( int obj, string name )
|
||||
{
|
||||
if( !API.GetMetaTable(obj) ) // no metatable?
|
||||
return false;
|
||||
API.PushString( name );
|
||||
API.RawGet( -2 );
|
||||
if( API.IsNil( -1 ) )
|
||||
{
|
||||
API.Pop( 2 );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
API.Remove( -2 );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool L_CallMeta( int obj, string name )
|
||||
{
|
||||
obj = API.AbsIndex( obj );
|
||||
if( !L_GetMetaField( obj, name ) ) // no metafield?
|
||||
return false;
|
||||
|
||||
API.PushValue( obj );
|
||||
API.Call( 1, 1 );
|
||||
return true;
|
||||
}
|
||||
|
||||
private void PushFuncName( LuaDebug ar )
|
||||
{
|
||||
if( ar.NameWhat.Length > 0 && ar.NameWhat[0] != '\0' ) // is there a name?
|
||||
API.PushString( string.Format( "function '{0}'", ar.Name ) );
|
||||
else if( ar.What.Length > 0 && ar.What[0] == 'm' ) // main?
|
||||
API.PushString( "main chunk" );
|
||||
else if( ar.What.Length > 0 && ar.What[0] == 'C' )
|
||||
{
|
||||
if( PushGlobalFuncName( ar ) )
|
||||
{
|
||||
API.PushString( string.Format( "function '{0}'", API.ToString(-1) ) );
|
||||
API.Remove( -2 ); //remove name
|
||||
}
|
||||
else
|
||||
API.PushString( "?" );
|
||||
}
|
||||
else
|
||||
API.PushString( string.Format( "function <{0}:{1}>", ar.ShortSrc, ar.LineDefined ) );
|
||||
}
|
||||
|
||||
private int CountLevels()
|
||||
{
|
||||
LuaDebug ar = new LuaDebug();
|
||||
int li = 1;
|
||||
int le = 1;
|
||||
// find an upper bound
|
||||
while( API.GetStack(le, ar) )
|
||||
{
|
||||
li = le;
|
||||
le *= 2;
|
||||
}
|
||||
// do a binary search
|
||||
while(li < le)
|
||||
{
|
||||
int m = (li + le)/2;
|
||||
if( API.GetStack( m, ar ) )
|
||||
li = m + 1;
|
||||
else
|
||||
le = m;
|
||||
}
|
||||
return le - 1;
|
||||
}
|
||||
|
||||
public void L_Traceback( ILuaState otherLua, string msg, int level )
|
||||
{
|
||||
LuaState oLua = otherLua as LuaState;
|
||||
LuaDebug ar = new LuaDebug();
|
||||
int top = API.GetTop();
|
||||
int numLevels = oLua.CountLevels();
|
||||
int mark = (numLevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0;
|
||||
if( msg != null )
|
||||
API.PushString( string.Format( "{0}\n", msg ) );
|
||||
API.PushString( "stack traceback:" );
|
||||
while( otherLua.GetStack( level++, ar ) )
|
||||
{
|
||||
if( level == mark ) // too many levels?
|
||||
{
|
||||
API.PushString( "\n\t..." );
|
||||
level = numLevels - LEVELS2; // and skip to last ones
|
||||
}
|
||||
else
|
||||
{
|
||||
oLua.GetInfo( "Slnt", ar );
|
||||
API.PushString( string.Format( "\n\t{0}:", ar.ShortSrc ) );
|
||||
if( ar.CurrentLine > 0 )
|
||||
API.PushString( string.Format( "{0}:", ar.CurrentLine ) );
|
||||
API.PushString(" in ");
|
||||
PushFuncName( ar );
|
||||
if( ar.IsTailCall )
|
||||
API.PushString( "\n\t(...tail calls...)" );
|
||||
API.Concat( API.GetTop() - top );
|
||||
}
|
||||
}
|
||||
API.Concat( API.GetTop() - top );
|
||||
}
|
||||
|
||||
public int L_Len( int index )
|
||||
{
|
||||
API.Len( index );
|
||||
|
||||
bool isnum;
|
||||
int l = (int)API.ToIntegerX( -1, out isnum );
|
||||
if( !isnum )
|
||||
L_Error( "object length is not a number" );
|
||||
API.Pop( 1 );
|
||||
return l;
|
||||
}
|
||||
|
||||
public ThreadStatus L_LoadBuffer( string s, string name )
|
||||
{
|
||||
return L_LoadBufferX( s, name, null );
|
||||
}
|
||||
|
||||
public ThreadStatus L_LoadBufferX( string s, string name, string mode )
|
||||
{
|
||||
var loadinfo = new StringLoadInfo( s );
|
||||
return API.Load( loadinfo, name, mode );
|
||||
}
|
||||
|
||||
public ThreadStatus L_LoadBytes( byte[] bytes, string name)
|
||||
{
|
||||
LuaFile.VirtualFiles.Add(name, bytes);
|
||||
|
||||
var loadinfo = new BytesLoadInfo( bytes );
|
||||
return API.Load( loadinfo, name, null );
|
||||
}
|
||||
|
||||
private ThreadStatus ErrFile( string what, int fnameindex )
|
||||
{
|
||||
return ThreadStatus.LUA_ERRFILE;
|
||||
}
|
||||
|
||||
public ThreadStatus L_LoadFile( string filename )
|
||||
{
|
||||
return L_LoadFileX( filename, null );
|
||||
}
|
||||
|
||||
public ThreadStatus L_LoadFileX( string filename, string mode )
|
||||
{
|
||||
var status = ThreadStatus.LUA_OK;
|
||||
if( filename == null )
|
||||
{
|
||||
// stdin
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
int fnameindex = API.GetTop() + 1;
|
||||
API.PushString( "@" + filename );
|
||||
try
|
||||
{
|
||||
var loadinfo = LuaFile.OpenFile(filename);
|
||||
|
||||
if (loadinfo.GetType() == typeof(FileLoadInfo))
|
||||
{
|
||||
((FileLoadInfo)loadinfo).SkipComment();
|
||||
status = API.Load(((FileLoadInfo)loadinfo), API.ToString(-1), mode);
|
||||
((FileLoadInfo)loadinfo).Dispose();
|
||||
}
|
||||
else if (loadinfo.GetType() == typeof(BytesLoadInfo))
|
||||
{
|
||||
status = API.Load(loadinfo, filename, null);
|
||||
}
|
||||
}
|
||||
catch( LuaRuntimeException e )
|
||||
{
|
||||
API.PushString( string.Format( "cannot open {0}: {1}",
|
||||
filename, e.Message ) );
|
||||
return ThreadStatus.LUA_ERRFILE;
|
||||
}
|
||||
|
||||
API.Remove( fnameindex );
|
||||
return status;
|
||||
}
|
||||
|
||||
public ThreadStatus L_LoadString( string s )
|
||||
{
|
||||
return L_LoadBuffer( s, s );
|
||||
}
|
||||
|
||||
public ThreadStatus L_DoByteArray(byte[] file, string name)
|
||||
{
|
||||
var status = L_LoadBytes(file, name);
|
||||
if (status != ThreadStatus.LUA_OK)
|
||||
return status;
|
||||
return API.PCall(0, LuaDef.LUA_MULTRET, 0);
|
||||
}
|
||||
|
||||
public ThreadStatus L_DoString( string s )
|
||||
{
|
||||
var status = L_LoadString( s );
|
||||
if( status != ThreadStatus.LUA_OK )
|
||||
return status;
|
||||
return API.PCall( 0, LuaDef.LUA_MULTRET, 0 );
|
||||
}
|
||||
|
||||
public ThreadStatus L_DoFile( string filename )
|
||||
{
|
||||
var status = L_LoadFile( filename );
|
||||
if( status != ThreadStatus.LUA_OK )
|
||||
return status;
|
||||
return API.PCall( 0, LuaDef.LUA_MULTRET, 0 );
|
||||
}
|
||||
|
||||
public string L_Gsub( string src, string pattern, string rep )
|
||||
{
|
||||
string res = src.Replace(pattern, rep);
|
||||
API.PushString( res );
|
||||
return res;
|
||||
}
|
||||
|
||||
public string L_ToString( int index )
|
||||
{
|
||||
if( !L_CallMeta( index, "__tostring" ) ) // no metafield? // TODO L_CallMeta
|
||||
{
|
||||
switch( API.Type(index) )
|
||||
{
|
||||
case LuaType.LUA_TNUMBER:
|
||||
case LuaType.LUA_TSTRING:
|
||||
API.PushValue( index );
|
||||
break;
|
||||
|
||||
case LuaType.LUA_TBOOLEAN:
|
||||
API.PushString( API.ToBoolean( index ) ? "true" : "false" );
|
||||
break;
|
||||
|
||||
case LuaType.LUA_TNIL:
|
||||
API.PushString( "nil" );
|
||||
break;
|
||||
|
||||
default:
|
||||
API.PushString( string.Format("{0}: {1:X}"
|
||||
, L_TypeName( index )
|
||||
, API.ToObject( index ).GetHashCode()
|
||||
) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return API.ToString( -1 );
|
||||
}
|
||||
|
||||
// private static class LibLoadInfo
|
||||
// {
|
||||
// public static List<NameFuncPair> Items;
|
||||
|
||||
// static LibLoadInfo()
|
||||
// {
|
||||
// Items = new List<NameFuncPair>();
|
||||
// Add( "_G", LuaState.LuaOpen_Base );
|
||||
// }
|
||||
|
||||
// private static void Add( string name, CSharpFunctionDelegate loadFunc )
|
||||
// {
|
||||
// Items.Add( new NameFuncPair { Name=name, LoadFunc=loadFunc } );
|
||||
// }
|
||||
// }
|
||||
|
||||
public void L_OpenLibs()
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "_G", LuaBaseLib.OpenLib ),
|
||||
new NameFuncPair( LuaPkgLib.LIB_NAME, LuaPkgLib.OpenLib ),
|
||||
new NameFuncPair( LuaCoroLib.LIB_NAME, LuaCoroLib.OpenLib ),
|
||||
new NameFuncPair( LuaTableLib.LIB_NAME, LuaTableLib.OpenLib ),
|
||||
new NameFuncPair( LuaIOLib.LIB_NAME, LuaIOLib.OpenLib ),
|
||||
new NameFuncPair( LuaOSLib.LIB_NAME, LuaOSLib.OpenLib ),
|
||||
// {LUA_OSLIBNAME, luaopen_os},
|
||||
new NameFuncPair( LuaStrLib.LIB_NAME, LuaStrLib.OpenLib ),
|
||||
new NameFuncPair( LuaBitLib.LIB_NAME, LuaBitLib.OpenLib ),
|
||||
new NameFuncPair( LuaMathLib.LIB_NAME, LuaMathLib.OpenLib ),
|
||||
new NameFuncPair( LuaDebugLib.LIB_NAME, LuaDebugLib.OpenLib ),
|
||||
new NameFuncPair( LuaEncLib.LIB_NAME, LuaEncLib.OpenLib ),
|
||||
};
|
||||
|
||||
for( var i=0; i<define.Length; ++i)
|
||||
{
|
||||
L_RequireF( define[i].Name, define[i].Func, true );
|
||||
API.Pop( 1 );
|
||||
}
|
||||
|
||||
// LuaBaseLib.LuaOpen_Base( this );
|
||||
}
|
||||
|
||||
public void L_RequireF( string moduleName, CSharpFunctionDelegate openFunc, bool global )
|
||||
{
|
||||
API.PushCSharpFunction( openFunc );
|
||||
API.PushString( moduleName );
|
||||
API.Call( 1, 1 );
|
||||
L_GetSubTable( LuaDef.LUA_REGISTRYINDEX, "_LOADED" );
|
||||
API.PushValue( -2 );
|
||||
API.SetField( -2, moduleName );
|
||||
API.Pop( 1 );
|
||||
if( global )
|
||||
{
|
||||
API.PushValue( -1 );
|
||||
API.SetGlobal( moduleName );
|
||||
}
|
||||
}
|
||||
|
||||
public int L_GetSubTable( int index, string fname )
|
||||
{
|
||||
API.GetField( index, fname );
|
||||
if( API.IsTable( -1 ) )
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
API.Pop( 1 );
|
||||
index = API.AbsIndex( index );
|
||||
API.NewTable();
|
||||
API.PushValue( -1 );
|
||||
API.SetField( index, fname );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void L_NewLibTable( NameFuncPair[] define )
|
||||
{
|
||||
API.CreateTable( 0, define.Length );
|
||||
}
|
||||
|
||||
public void L_NewLib( NameFuncPair[] define )
|
||||
{
|
||||
L_NewLibTable( define );
|
||||
L_SetFuncs( define, 0 );
|
||||
}
|
||||
|
||||
public void L_SetFuncs( NameFuncPair[] define, int nup )
|
||||
{
|
||||
// TODO: Check Version
|
||||
L_CheckStack(nup, "too many upvalues");
|
||||
for( var j=0; j<define.Length; ++j )
|
||||
{
|
||||
for( int i=0; i<nup; ++i )
|
||||
API.PushValue( -nup );
|
||||
API.PushCSharpClosure( define[j].Func, nup );
|
||||
API.SetField( -(nup + 2), define[j].Name );
|
||||
}
|
||||
API.Pop( nup );
|
||||
}
|
||||
|
||||
private bool FindField( int objIndex, int level )
|
||||
{
|
||||
if( level == 0 || !API.IsTable(-1) )
|
||||
return false; // not found
|
||||
|
||||
API.PushNil(); // start 'next' loop
|
||||
while( API.Next(-2) ) // for each pair in table
|
||||
{
|
||||
if( API.Type(-2) == LuaType.LUA_TSTRING ) // ignore non-string keys
|
||||
{
|
||||
if( API.RawEqual( objIndex, -1 ) ) // found object?
|
||||
{
|
||||
API.Pop(1); // remove value (but keep name)
|
||||
return true;
|
||||
}
|
||||
else if( FindField( objIndex, level-1 ) ) // try recursively
|
||||
{
|
||||
API.Remove( -2 ); // remove table (but keep name)
|
||||
API.PushString( "." );
|
||||
API.Insert( -2 ); // place '.' between the two names
|
||||
API.Concat( 3 );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
API.Pop(1); // remove value
|
||||
}
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
private bool PushGlobalFuncName( LuaDebug ar )
|
||||
{
|
||||
int top = API.GetTop();
|
||||
GetInfo( "f", ar );
|
||||
API.PushGlobalTable();
|
||||
if( FindField( top+1, 2 ) )
|
||||
{
|
||||
API.Copy( -1, top+1 );
|
||||
API.Pop( 2 );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
API.SetTop( top ); // remove function and global table
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private const int FreeList = 0;
|
||||
|
||||
public int L_Ref( int t )
|
||||
{
|
||||
if( API.IsNil(-1) )
|
||||
{
|
||||
API.Pop(1); // remove from stack
|
||||
return LuaConstants.LUA_REFNIL; // `nil' has a unique fixed reference
|
||||
}
|
||||
|
||||
t = API.AbsIndex(t);
|
||||
API.RawGetI(t, FreeList); // get first free element
|
||||
int reference = API.ToInteger(-1); // ref = t[freelist]
|
||||
API.Pop(1); // remove it from stack
|
||||
if( reference != 0 ) // any free element?
|
||||
{
|
||||
API.RawGetI(t, reference); // remove it from list
|
||||
API.RawSetI(t, FreeList); // t[freelist] = t[ref]
|
||||
}
|
||||
else // no free elements
|
||||
reference = API.RawLen(t) + 1; // get a new reference
|
||||
API.RawSetI(t, reference);
|
||||
return reference;
|
||||
}
|
||||
|
||||
public void L_Unref( int t, int reference )
|
||||
{
|
||||
if( reference >= 0 )
|
||||
{
|
||||
t = API.AbsIndex(t);
|
||||
API.RawGetI(t, FreeList);
|
||||
API.RawSetI(t, reference); // t[ref] = t[freelist]
|
||||
API.PushInteger(reference);
|
||||
API.RawSetI(t, FreeList); // t[freelist] = ref
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_IPHONE
|
||||
public void FEED_AOT_FOR_IOS(LuaState lua)
|
||||
{
|
||||
lua.L_Opt( lua.L_CheckInteger, 1, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
441
UniLua/LuaBaseLib.cs
Normal file
441
UniLua/LuaBaseLib.cs
Normal file
@@ -0,0 +1,441 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using ULDebug = UniLua.Tools.ULDebug;
|
||||
using StringBuilder = System.Text.StringBuilder;
|
||||
using Char = System.Char;
|
||||
using Int32 = System.Int32;
|
||||
using System;
|
||||
|
||||
internal static class LuaBaseLib
|
||||
{
|
||||
internal static int OpenLib( ILuaState lua )
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "assert", LuaBaseLib.B_Assert ),
|
||||
new NameFuncPair( "collectgarbage", LuaBaseLib.B_CollectGarbage ),
|
||||
new NameFuncPair( "dofile", LuaBaseLib.B_DoFile ),
|
||||
new NameFuncPair( "error", LuaBaseLib.B_Error ),
|
||||
new NameFuncPair( "ipairs", LuaBaseLib.B_Ipairs ),
|
||||
new NameFuncPair( "loadfile", LuaBaseLib.B_LoadFile ),
|
||||
new NameFuncPair( "load", LuaBaseLib.B_Load ),
|
||||
new NameFuncPair( "loadstring", LuaBaseLib.B_Load ),
|
||||
new NameFuncPair( "next", LuaBaseLib.B_Next ),
|
||||
new NameFuncPair( "pairs", LuaBaseLib.B_Pairs ),
|
||||
new NameFuncPair( "pcall", LuaBaseLib.B_PCall ),
|
||||
new NameFuncPair( "print", LuaBaseLib.B_Print ),
|
||||
new NameFuncPair( "rawequal", LuaBaseLib.B_RawEqual ),
|
||||
new NameFuncPair( "rawlen", LuaBaseLib.B_RawLen ),
|
||||
new NameFuncPair( "rawget", LuaBaseLib.B_RawGet ),
|
||||
new NameFuncPair( "rawset", LuaBaseLib.B_RawSet ),
|
||||
new NameFuncPair( "select", LuaBaseLib.B_Select ),
|
||||
new NameFuncPair( "getmetatable", LuaBaseLib.B_GetMetaTable ),
|
||||
new NameFuncPair( "setmetatable", LuaBaseLib.B_SetMetaTable ),
|
||||
new NameFuncPair( "tonumber", LuaBaseLib.B_ToNumber ),
|
||||
new NameFuncPair( "tostring", LuaBaseLib.B_ToString ),
|
||||
new NameFuncPair( "type", LuaBaseLib.B_Type ),
|
||||
new NameFuncPair( "xpcall", LuaBaseLib.B_XPCall ),
|
||||
};
|
||||
|
||||
// set global _G
|
||||
lua.PushGlobalTable();
|
||||
lua.PushGlobalTable();
|
||||
lua.SetField( -2, "_G" );
|
||||
|
||||
// open lib into global lib
|
||||
lua.L_SetFuncs( define, 0 );
|
||||
// lua.RegisterGlobalFunc( "type", LuaBaseLib.B_Type );
|
||||
// lua.RegisterGlobalFunc( "pairs", LuaBaseLib.B_Pairs );
|
||||
// lua.RegisterGlobalFunc( "ipairs", LuaBaseLib.B_Ipairs );
|
||||
// lua.RegisterGlobalFunc( "print", LuaBaseLib.B_Print );
|
||||
// lua.RegisterGlobalFunc( "tostring", LuaBaseLib.B_ToString );
|
||||
|
||||
lua.PushString( LuaDef.LUA_VERSION );
|
||||
lua.SetField( -2, "_VERSION" );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int B_Assert( ILuaState lua )
|
||||
{
|
||||
if( !lua.ToBoolean( 1 ) )
|
||||
return lua.L_Error( "{0}", lua.L_OptString( 2, "assertion failed!" ) );
|
||||
return lua.GetTop();
|
||||
}
|
||||
|
||||
public static int B_CollectGarbage( ILuaState lua )
|
||||
{
|
||||
// not implement gc
|
||||
string opt = lua.L_OptString( 1, "collect" );
|
||||
switch( opt )
|
||||
{
|
||||
case "count":
|
||||
lua.PushNumber( 0 );
|
||||
lua.PushNumber( 0 );
|
||||
return 2;
|
||||
|
||||
case "step":
|
||||
case "isrunning":
|
||||
lua.PushBoolean( true );
|
||||
return 1;
|
||||
|
||||
default:
|
||||
lua.PushInteger( 0 );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int DoFileContinuation( ILuaState lua )
|
||||
{
|
||||
return lua.GetTop() - 1;
|
||||
}
|
||||
|
||||
public static int B_DoFile( ILuaState lua )
|
||||
{
|
||||
string filename = lua.L_OptString( 1, null );
|
||||
lua.SetTop( 1 );
|
||||
if( lua.L_LoadFile( filename ) != ThreadStatus.LUA_OK )
|
||||
lua.Error();
|
||||
lua.CallK( 0, LuaDef.LUA_MULTRET, 0, DoFileContinuation );
|
||||
return DoFileContinuation( lua );
|
||||
}
|
||||
|
||||
public static int B_Error( ILuaState lua )
|
||||
{
|
||||
int level = lua.L_OptInt( 2, 1 );
|
||||
lua.SetTop( 1 );
|
||||
if( lua.IsString( 1 ) && level > 0 )
|
||||
{
|
||||
lua.L_Where( level );
|
||||
lua.PushValue( 1 );
|
||||
lua.Concat( 2 );
|
||||
}
|
||||
return lua.Error();
|
||||
}
|
||||
|
||||
private static int LoadAux( ILuaState lua, ThreadStatus status, int envidx )
|
||||
{
|
||||
if( status == ThreadStatus.LUA_OK )
|
||||
{
|
||||
if( envidx != 0 ) // `env' parameter?
|
||||
{
|
||||
lua.PushValue(envidx); // push `env' on stack
|
||||
if( lua.SetUpvalue(-2, 1) == null ) // set `env' as 1st upvalue of loaded function
|
||||
{
|
||||
lua.Pop(1); // remove `env' if not used by previous call
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else // error (message is on top of the stack)
|
||||
{
|
||||
lua.PushNil();
|
||||
lua.Insert(-2); // put before error message
|
||||
return 2; // return nil plus error message
|
||||
}
|
||||
}
|
||||
|
||||
public static int B_LoadFile( ILuaState lua )
|
||||
{
|
||||
string fname = lua.L_OptString( 1, null );
|
||||
string mode = lua.L_OptString( 2, null );
|
||||
int env = (!lua.IsNone(3) ? 3 : 0); // `env' index or 0 if no `env'
|
||||
var status = lua.L_LoadFileX( fname, mode );
|
||||
return LoadAux(lua, status, env);
|
||||
}
|
||||
|
||||
public static int B_Load( ILuaState lua )
|
||||
{
|
||||
ThreadStatus status;
|
||||
string s = lua.ToString(1);
|
||||
string mode = lua.L_OptString(3, "bt");
|
||||
int env = (! lua.IsNone(4) ? 4 : 0); // `env' index or 0 if no `env'
|
||||
if( s != null )
|
||||
{
|
||||
string chunkName = lua.L_OptString(2, s);
|
||||
status = lua.L_LoadBufferX( s, chunkName, mode );
|
||||
}
|
||||
else // loading from a reader function
|
||||
{
|
||||
throw new System.NotImplementedException(); // TODO
|
||||
}
|
||||
return LoadAux( lua, status, env );
|
||||
}
|
||||
|
||||
private static int FinishPCall( ILuaState lua, bool status )
|
||||
{
|
||||
// no space for extra boolean?
|
||||
if(!lua.CheckStack(1)) {
|
||||
lua.SetTop(0); // create space for return values
|
||||
lua.PushBoolean(false);
|
||||
lua.PushString("stack overflow");
|
||||
return 2;
|
||||
}
|
||||
lua.PushBoolean( status );
|
||||
lua.Replace( 1 );
|
||||
return lua.GetTop();
|
||||
}
|
||||
|
||||
private static int PCallContinuation( ILuaState lua )
|
||||
{
|
||||
int context;
|
||||
ThreadStatus status = lua.GetContext( out context );
|
||||
return FinishPCall( lua, status == ThreadStatus.LUA_YIELD );
|
||||
}
|
||||
private static CSharpFunctionDelegate DG_PCallContinuation = PCallContinuation;
|
||||
|
||||
public static int B_PCall( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckAny( 1 );
|
||||
lua.PushNil();
|
||||
lua.Insert( 1 ); // create space for status result
|
||||
|
||||
ThreadStatus status = lua.PCallK( lua.GetTop() - 2,
|
||||
LuaDef.LUA_MULTRET, 0, 0, DG_PCallContinuation );
|
||||
|
||||
return FinishPCall( lua, status == ThreadStatus.LUA_OK );
|
||||
}
|
||||
|
||||
public static int B_XPCall( ILuaState lua )
|
||||
{
|
||||
int n = lua.GetTop();
|
||||
lua.L_ArgCheck( n>=2, 2, "value expected" );
|
||||
lua.PushValue( 1 ); // exchange function...
|
||||
lua.Copy( 2, 1); // ...and error handler
|
||||
lua.Replace( 2 );
|
||||
ThreadStatus status = lua.PCallK( n-2, LuaDef.LUA_MULTRET,
|
||||
1, 0, DG_PCallContinuation );
|
||||
return FinishPCall( lua, status == ThreadStatus.LUA_OK );
|
||||
}
|
||||
|
||||
public static int B_RawEqual( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckAny( 1 );
|
||||
lua.L_CheckAny( 2 );
|
||||
lua.PushBoolean( lua.RawEqual( 1, 2 ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int B_RawLen( ILuaState lua )
|
||||
{
|
||||
LuaType t = lua.Type( 1 );
|
||||
lua.L_ArgCheck( t == LuaType.LUA_TTABLE || t == LuaType.LUA_TSTRING,
|
||||
1, "table or string expected" );
|
||||
lua.PushInteger( lua.RawLen( 1 ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int B_RawGet( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckType( 1, LuaType.LUA_TTABLE );
|
||||
lua.L_CheckAny( 2 );
|
||||
lua.SetTop( 2 );
|
||||
lua.RawGet( 1 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int B_RawSet( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckType( 1, LuaType.LUA_TTABLE );
|
||||
lua.L_CheckAny( 2 );
|
||||
lua.L_CheckAny( 3 );
|
||||
lua.SetTop( 3 );
|
||||
lua.RawSet( 1 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int B_Select( ILuaState lua )
|
||||
{
|
||||
int n = lua.GetTop();
|
||||
if( lua.Type( 1 ) == LuaType.LUA_TSTRING &&
|
||||
lua.ToString( 1 )[0] == '#' )
|
||||
{
|
||||
lua.PushInteger( n-1 );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = lua.L_CheckInteger( 1 );
|
||||
if( i < 0 ) i = n + i;
|
||||
else if( i > n ) i = n;
|
||||
lua.L_ArgCheck( 1 <= i, 1, "index out of range" );
|
||||
return n - i;
|
||||
}
|
||||
}
|
||||
|
||||
public static int B_GetMetaTable( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckAny( 1 );
|
||||
if( !lua.GetMetaTable( 1 ) )
|
||||
{
|
||||
lua.PushNil();
|
||||
return 1; // no metatable
|
||||
}
|
||||
lua.L_GetMetaField( 1, "__metatable" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int B_SetMetaTable( ILuaState lua )
|
||||
{
|
||||
LuaType t = lua.Type( 2 );
|
||||
lua.L_CheckType( 1, LuaType.LUA_TTABLE );
|
||||
lua.L_ArgCheck( t == LuaType.LUA_TNIL || t == LuaType.LUA_TTABLE,
|
||||
2, "nil or table expected" );
|
||||
if( lua.L_GetMetaField( 1, "__metatable" ) )
|
||||
return lua.L_Error( "cannot change a protected metatable" );
|
||||
lua.SetTop( 2 );
|
||||
lua.SetMetaTable( 1 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int B_ToNumber( ILuaState lua )
|
||||
{
|
||||
LuaType t = lua.Type( 2 );
|
||||
if( t == LuaType.LUA_TNONE || t == LuaType.LUA_TNIL ) // standard conversion
|
||||
{
|
||||
bool isnum;
|
||||
double n = lua.ToNumberX( 1, out isnum );
|
||||
if( isnum )
|
||||
{
|
||||
lua.PushNumber( n );
|
||||
return 1;
|
||||
} // else not a number; must be something
|
||||
lua.L_CheckAny( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
string s = lua.L_CheckString( 1 );
|
||||
int numBase = lua.L_CheckInteger( 2 );
|
||||
bool negative = false;
|
||||
lua.L_ArgCheck( (2 <= numBase && numBase <= 36), 2,
|
||||
"base out of range" );
|
||||
s = s.Trim( ' ', '\f', '\n', '\r', '\t', '\v' );
|
||||
s = s + '\0'; // guard
|
||||
int pos = 0;
|
||||
if(s[pos] == '-') { pos++; negative = true; }
|
||||
else if(s[pos] == '+') pos++;
|
||||
if( Char.IsLetterOrDigit( s, pos ) )
|
||||
{
|
||||
double n = 0.0;
|
||||
do
|
||||
{
|
||||
int digit;
|
||||
if( Char.IsDigit( s, pos ) )
|
||||
digit = Int32.Parse( s[pos].ToString() );
|
||||
else
|
||||
digit = Char.ToUpper( s[pos] ) - 'A' + 10;
|
||||
if( digit >= numBase )
|
||||
break; // invalid numeral; force a fail
|
||||
n = n * (double)numBase + (double)digit;
|
||||
pos++;
|
||||
} while( Char.IsLetterOrDigit( s, pos ) );
|
||||
if( pos == s.Length - 1 ) // except guard, no invalid trailing characters?
|
||||
{
|
||||
lua.PushNumber( negative ? -n : n );
|
||||
return 1;
|
||||
} // else not a number
|
||||
} // else not a number
|
||||
}
|
||||
lua.PushNil(); // not a number
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int B_Type( ILuaState lua )
|
||||
{
|
||||
var t = lua.Type( 1 );
|
||||
var tname = lua.TypeName( t );
|
||||
lua.PushString( tname );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int PairsMeta( ILuaState lua, string method, bool isZero
|
||||
, CSharpFunctionDelegate iter )
|
||||
{
|
||||
if( !lua.L_GetMetaField( 1, method ) ) // no metamethod?
|
||||
{
|
||||
lua.L_CheckType( 1, LuaType.LUA_TTABLE );
|
||||
lua.PushCSharpFunction( iter );
|
||||
lua.PushValue( 1 );
|
||||
if( isZero )
|
||||
lua.PushInteger( 0 );
|
||||
else
|
||||
lua.PushNil();
|
||||
}
|
||||
else
|
||||
{
|
||||
lua.PushValue( 1 );
|
||||
lua.Call( 1, 3 );
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
public static int B_Next( ILuaState lua )
|
||||
{
|
||||
lua.SetTop( 2 );
|
||||
if( lua.Next(1) )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
lua.PushNil();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
static CSharpFunctionDelegate DG_B_Next = B_Next;
|
||||
|
||||
public static int B_Pairs( ILuaState lua )
|
||||
{
|
||||
return PairsMeta( lua, "__pairs", false, DG_B_Next );
|
||||
}
|
||||
|
||||
private static int IpairsAux( ILuaState lua )
|
||||
{
|
||||
int i = lua.ToInteger( 2 );
|
||||
i++; // next value
|
||||
lua.PushInteger( i );
|
||||
lua.RawGetI( 1, i );
|
||||
return lua.IsNil( -1 ) ? 1 : 2;
|
||||
}
|
||||
static CSharpFunctionDelegate DG_IpairsAux = IpairsAux;
|
||||
|
||||
public static int B_Ipairs( ILuaState lua )
|
||||
{
|
||||
return PairsMeta( lua, "__ipairs", true, DG_IpairsAux );
|
||||
}
|
||||
|
||||
public static int B_Print( ILuaState lua )
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int n = lua.GetTop();
|
||||
lua.GetGlobal( "tostring" );
|
||||
for( int i=1; i<=n; ++i )
|
||||
{
|
||||
lua.PushValue( -1 );
|
||||
lua.PushValue( i );
|
||||
lua.Call( 1, 1 );
|
||||
string s = lua.ToString( -1 );
|
||||
if( s == null )
|
||||
return lua.L_Error("'tostring' must return a string to 'print'");
|
||||
if( i > 1 )
|
||||
sb.Append( "\t" );
|
||||
sb.Append( s );
|
||||
lua.Pop( 1 );
|
||||
}
|
||||
Console.WriteLine( sb.ToString() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int B_ToString( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckAny( 1 );
|
||||
lua.L_ToString( 1 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
204
UniLua/LuaBitLib.cs
Normal file
204
UniLua/LuaBitLib.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using UniLua;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
|
||||
internal class LuaBitLib
|
||||
{
|
||||
public const string LIB_NAME = "bit32";
|
||||
private const int LUA_NBITS = 32;
|
||||
private const uint ALLONES = ~(~(uint)0 << LUA_NBITS - 1 << 1);
|
||||
|
||||
public static int OpenLib(ILuaState lua)
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "arshift", B_ArithShift ),
|
||||
new NameFuncPair( "band", B_And ),
|
||||
new NameFuncPair( "bnot", B_Not ),
|
||||
new NameFuncPair( "bor", B_Or ),
|
||||
new NameFuncPair( "bxor", B_Xor ),
|
||||
new NameFuncPair( "btest", B_Test ),
|
||||
new NameFuncPair( "extract", B_Extract ),
|
||||
new NameFuncPair( "lrotate", B_LeftRotate ),
|
||||
new NameFuncPair( "lshift", B_LeftShift ),
|
||||
new NameFuncPair( "replace", B_Replace ),
|
||||
new NameFuncPair( "rrotate", B_RightRotate ),
|
||||
new NameFuncPair( "rshift", B_RightShift ),
|
||||
};
|
||||
|
||||
lua.L_NewLib(define);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static uint Trim(uint x)
|
||||
{
|
||||
return x & ALLONES;
|
||||
}
|
||||
|
||||
private static uint Mask(int n)
|
||||
{
|
||||
return ~(ALLONES << 1 << n - 1);
|
||||
}
|
||||
|
||||
private static int B_Shift(ILuaState lua, uint r, int i)
|
||||
{
|
||||
if (i < 0) // shift right?
|
||||
{
|
||||
i = -i;
|
||||
r = Trim(r);
|
||||
if (i >= LUA_NBITS) r = 0;
|
||||
else r >>= i;
|
||||
}
|
||||
else // shift left
|
||||
{
|
||||
if (i >= LUA_NBITS) r = 0;
|
||||
else r <<= i;
|
||||
r = Trim(r);
|
||||
}
|
||||
lua.PushUnsigned(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int B_LeftShift(ILuaState lua)
|
||||
{
|
||||
return B_Shift(lua, lua.L_CheckUnsigned(1), lua.L_CheckInteger(2));
|
||||
}
|
||||
|
||||
private static int B_RightShift(ILuaState lua)
|
||||
{
|
||||
return B_Shift(lua, lua.L_CheckUnsigned(1), -lua.L_CheckInteger(2));
|
||||
}
|
||||
|
||||
private static int B_ArithShift(ILuaState lua)
|
||||
{
|
||||
uint r = lua.L_CheckUnsigned(1);
|
||||
int i = lua.L_CheckInteger(2);
|
||||
if (i < 0 || (r & (uint)1 << LUA_NBITS - 1) == 0)
|
||||
return B_Shift(lua, r, -i);
|
||||
else // arithmetic shift for `nagetive' number
|
||||
{
|
||||
if (i >= LUA_NBITS)
|
||||
r = ALLONES;
|
||||
else
|
||||
r = Trim(r >> i | ~(~(uint)0 >> i)); // add signal bit
|
||||
lua.PushUnsigned(r);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static uint AndAux(ILuaState lua)
|
||||
{
|
||||
int n = lua.GetTop();
|
||||
uint r = ~(uint)0;
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
r &= lua.L_CheckUnsigned(i);
|
||||
}
|
||||
return Trim(r);
|
||||
}
|
||||
|
||||
private static int B_And(ILuaState lua)
|
||||
{
|
||||
uint r = AndAux(lua);
|
||||
lua.PushUnsigned(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int B_Not(ILuaState lua)
|
||||
{
|
||||
uint r = ~lua.L_CheckUnsigned(1);
|
||||
lua.PushUnsigned(Trim(r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int B_Or(ILuaState lua)
|
||||
{
|
||||
int n = lua.GetTop();
|
||||
uint r = 0;
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
r |= lua.L_CheckUnsigned(i);
|
||||
}
|
||||
lua.PushUnsigned(Trim(r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int B_Xor(ILuaState lua)
|
||||
{
|
||||
int n = lua.GetTop();
|
||||
uint r = 0;
|
||||
for (int i = 1; i <= n; ++i)
|
||||
{
|
||||
r ^= lua.L_CheckUnsigned(i);
|
||||
}
|
||||
lua.PushUnsigned(Trim(r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int B_Test(ILuaState lua)
|
||||
{
|
||||
uint r = AndAux(lua);
|
||||
lua.PushBoolean(r != 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int FieldArgs(ILuaState lua, int farg, out int width)
|
||||
{
|
||||
int f = lua.L_CheckInteger(farg);
|
||||
int w = lua.L_OptInt(farg + 1, 1);
|
||||
lua.L_ArgCheck(0 <= f, farg, "field cannot be nagetive");
|
||||
lua.L_ArgCheck(0 < w, farg + 1, "width must be positive");
|
||||
if (f + w > LUA_NBITS)
|
||||
lua.L_Error("trying to access non-existent bits");
|
||||
width = w;
|
||||
return f;
|
||||
}
|
||||
|
||||
private static int B_Extract(ILuaState lua)
|
||||
{
|
||||
uint r = lua.L_CheckUnsigned(1);
|
||||
int w;
|
||||
int f = FieldArgs(lua, 2, out w);
|
||||
r = r >> f & Mask(w);
|
||||
lua.PushUnsigned(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int B_Rotate(ILuaState lua, int i)
|
||||
{
|
||||
uint r = lua.L_CheckUnsigned(1);
|
||||
i &= LUA_NBITS - 1; // i = i % NBITS
|
||||
r = Trim(r);
|
||||
r = r << i | r >> LUA_NBITS - i;
|
||||
lua.PushUnsigned(Trim(r));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int B_LeftRotate(ILuaState lua)
|
||||
{
|
||||
return B_Rotate(lua, lua.L_CheckInteger(2));
|
||||
}
|
||||
|
||||
private static int B_RightRotate(ILuaState lua)
|
||||
{
|
||||
return B_Rotate(lua, -lua.L_CheckInteger(2));
|
||||
}
|
||||
|
||||
private static int B_Replace(ILuaState lua)
|
||||
{
|
||||
uint r = lua.L_CheckUnsigned(1);
|
||||
uint v = lua.L_CheckUnsigned(2);
|
||||
int w;
|
||||
int f = FieldArgs(lua, 3, out w);
|
||||
uint m = Mask(w);
|
||||
v &= m; //erase bits outside given width
|
||||
r = r & ~(m << f) | v << f;
|
||||
lua.PushUnsigned(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
152
UniLua/LuaCoroLib.cs
Normal file
152
UniLua/LuaCoroLib.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
|
||||
internal class LuaCoroLib
|
||||
{
|
||||
public const string LIB_NAME = "coroutine";
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "create", CO_Create ),
|
||||
new NameFuncPair( "resume", CO_Resume ),
|
||||
new NameFuncPair( "running", CO_Running ),
|
||||
new NameFuncPair( "status", CO_Status ),
|
||||
new NameFuncPair( "wrap", CO_Wrap ),
|
||||
new NameFuncPair( "yield", CO_Yield ),
|
||||
};
|
||||
|
||||
lua.L_NewLib( define );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int CO_Create( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckType( 1, LuaType.LUA_TFUNCTION );
|
||||
ILuaState newLua = lua.NewThread();
|
||||
lua.PushValue( 1 ); // move function to top
|
||||
lua.XMove( newLua, 1 ); // move function from lua to newLua
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int AuxResume( ILuaState lua, ILuaState co, int narg )
|
||||
{
|
||||
if(!co.CheckStack(narg)) {
|
||||
lua.PushString("too many arguments to resume");
|
||||
return -1; // error flag
|
||||
}
|
||||
if( co.Status == ThreadStatus.LUA_OK && co.GetTop() == 0 )
|
||||
{
|
||||
lua.PushString( "cannot resume dead coroutine" );
|
||||
return -1; // error flag
|
||||
}
|
||||
lua.XMove( co, narg );
|
||||
ThreadStatus status = co.Resume( lua, narg );
|
||||
if( status == ThreadStatus.LUA_OK || status == ThreadStatus.LUA_YIELD )
|
||||
{
|
||||
int nres = co.GetTop();
|
||||
if(!lua.CheckStack(nres+1)) {
|
||||
co.Pop(nres); // remove results anyway;
|
||||
lua.PushString("too many results to resume");
|
||||
return -1; // error flag
|
||||
}
|
||||
co.XMove( lua, nres ); // move yielded values
|
||||
return nres;
|
||||
}
|
||||
else
|
||||
{
|
||||
co.XMove( lua, 1 ); // move error message
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int CO_Resume( ILuaState lua )
|
||||
{
|
||||
ILuaState co = lua.ToThread( 1 );
|
||||
lua.L_ArgCheck( co != null, 1, "coroutine expected" );
|
||||
int r = AuxResume( lua, co, lua.GetTop() - 1 );
|
||||
if( r < 0 )
|
||||
{
|
||||
lua.PushBoolean( false );
|
||||
lua.Insert( -2 );
|
||||
return 2; // return false + error message
|
||||
}
|
||||
else
|
||||
{
|
||||
lua.PushBoolean( true );
|
||||
lua.Insert( -(r+1) );
|
||||
return r+1; // return true + `resume' returns
|
||||
}
|
||||
}
|
||||
|
||||
private static int CO_Running( ILuaState lua )
|
||||
{
|
||||
bool isMain = lua.PushThread();
|
||||
lua.PushBoolean( isMain );
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static int CO_Status( ILuaState lua )
|
||||
{
|
||||
ILuaState co = lua.ToThread( 1 );
|
||||
lua.L_ArgCheck( co != null, 1, "coroutine expected" );
|
||||
if( (LuaState)lua == (LuaState)co )
|
||||
lua.PushString( "running" );
|
||||
else switch( co.Status )
|
||||
{
|
||||
case ThreadStatus.LUA_YIELD:
|
||||
lua.PushString( "suspended" );
|
||||
break;
|
||||
case ThreadStatus.LUA_OK:
|
||||
{
|
||||
LuaDebug ar = new LuaDebug();
|
||||
if( co.GetStack( 0, ar ) ) // does it have frames?
|
||||
lua.PushString( "normal" );
|
||||
else if( co.GetTop() == 0 )
|
||||
lua.PushString( "dead" );
|
||||
else
|
||||
lua.PushString( "suspended" );
|
||||
break;
|
||||
}
|
||||
default: // some error occurred
|
||||
lua.PushString( "dead" );
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int CO_AuxWrap( ILuaState lua )
|
||||
{
|
||||
ILuaState co = lua.ToThread( lua.UpvalueIndex(1) );
|
||||
int r = AuxResume( lua, co, lua.GetTop() );
|
||||
if( r < 0 )
|
||||
{
|
||||
if( lua.IsString( -1 ) ) // error object is a string?
|
||||
{
|
||||
lua.L_Where( 1 ); // add extra info
|
||||
lua.Insert( -2 );
|
||||
lua.Concat( 2 );
|
||||
}
|
||||
lua.Error();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static int CO_Wrap( ILuaState lua )
|
||||
{
|
||||
CO_Create( lua );
|
||||
lua.PushCSharpClosure( CO_AuxWrap, 1 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int CO_Yield( ILuaState lua )
|
||||
{
|
||||
return lua.Yield( lua.GetTop() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
516
UniLua/LuaDebug.cs
Normal file
516
UniLua/LuaDebug.cs
Normal file
@@ -0,0 +1,516 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
public class LuaDebug
|
||||
{
|
||||
public string Name;
|
||||
public string NameWhat;
|
||||
public int ActiveCIIndex;
|
||||
public int CurrentLine;
|
||||
public int NumUps;
|
||||
public bool IsVarArg;
|
||||
public int NumParams;
|
||||
public bool IsTailCall;
|
||||
public string Source;
|
||||
public int LineDefined;
|
||||
public int LastLineDefined;
|
||||
public string What;
|
||||
public string ShortSrc;
|
||||
}
|
||||
|
||||
public partial class LuaState
|
||||
{
|
||||
bool ILuaAPI.GetStack( int level, LuaDebug ar )
|
||||
{
|
||||
if( level < 0 )
|
||||
return false;
|
||||
|
||||
int index;
|
||||
for( index = CI.Index; level > 0 && index > 0; --index )
|
||||
{ level--; }
|
||||
|
||||
bool status = false;
|
||||
if( level == 0 && index > 0 ) {
|
||||
status = true;
|
||||
ar.ActiveCIIndex = index;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
public int GetInfo( string what, LuaDebug ar )
|
||||
{
|
||||
CallInfo ci;
|
||||
StkId func;
|
||||
|
||||
int pos = 0;
|
||||
if( what[pos] == '>' )
|
||||
{
|
||||
ci = null;
|
||||
func = Stack[Top.Index - 1];
|
||||
|
||||
Utl.ApiCheck(func.V.TtIsFunction(), "function expected");
|
||||
pos++;
|
||||
|
||||
Top = Stack[Top.Index-1];
|
||||
}
|
||||
else
|
||||
{
|
||||
ci = BaseCI[ar.ActiveCIIndex];
|
||||
func = Stack[ci.FuncIndex];
|
||||
Utl.Assert(Stack[ci.FuncIndex].V.TtIsFunction());
|
||||
}
|
||||
|
||||
// var IsClosure( func.Value ) ? func.Value
|
||||
int status = AuxGetInfo( what, ar, func, ci );
|
||||
if( what.Contains( "f" ) )
|
||||
{
|
||||
Top.V.SetObj(ref func.V);
|
||||
IncrTop();
|
||||
}
|
||||
if( what.Contains( "L" ) )
|
||||
{
|
||||
CollectValidLines( func );
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private int AuxGetInfo( string what, LuaDebug ar, StkId func, CallInfo ci )
|
||||
{
|
||||
int status = 1;
|
||||
for( int i=0; i<what.Length; ++i )
|
||||
{
|
||||
char c = what[i];
|
||||
switch( c )
|
||||
{
|
||||
case 'S':
|
||||
{
|
||||
FuncInfo( ar, func );
|
||||
break;
|
||||
}
|
||||
case 'l':
|
||||
{
|
||||
ar.CurrentLine = (ci != null && ci.IsLua) ? GetCurrentLine(ci) : -1;
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
Utl.Assert(func.V.TtIsFunction());
|
||||
if(func.V.ClIsLuaClosure()) {
|
||||
var lcl = func.V.ClLValue();
|
||||
ar.NumUps = lcl.Upvals.Length;
|
||||
ar.IsVarArg = lcl.Proto.IsVarArg;
|
||||
ar.NumParams = lcl.Proto.NumParams;
|
||||
}
|
||||
else if(func.V.ClIsCsClosure()) {
|
||||
var ccl = func.V.ClCsValue();
|
||||
ar.NumUps = ccl.Upvals.Length;
|
||||
ar.IsVarArg = true;
|
||||
ar.NumParams = 0;
|
||||
}
|
||||
else throw new System.NotImplementedException();
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
{
|
||||
ar.IsTailCall = (ci != null)
|
||||
? ( (ci.CallStatus & CallStatus.CIST_TAIL) != 0 )
|
||||
: false;
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
{
|
||||
var prevCI = BaseCI[ci.Index-1];
|
||||
if( ci != null
|
||||
&& ((ci.CallStatus & CallStatus.CIST_TAIL) == 0)
|
||||
&& prevCI.IsLua )
|
||||
{
|
||||
ar.NameWhat = GetFuncName( prevCI, out ar.Name );
|
||||
}
|
||||
else
|
||||
{
|
||||
ar.NameWhat = null;
|
||||
}
|
||||
if( ar.NameWhat == null )
|
||||
{
|
||||
ar.NameWhat = ""; // not found
|
||||
ar.Name = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
case 'f': // handled by GetInfo
|
||||
break;
|
||||
default: status = 0; // invalid option
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private void CollectValidLines( StkId func )
|
||||
{
|
||||
Utl.Assert(func.V.TtIsFunction());
|
||||
if(func.V.ClIsLuaClosure()) {
|
||||
var lcl = func.V.ClLValue();
|
||||
var p = lcl.Proto;
|
||||
var lineinfo = p.LineInfo;
|
||||
var t = new LuaTable(this);
|
||||
Top.V.SetHValue(t);
|
||||
IncrTop();
|
||||
var v = new TValue();
|
||||
v.SetBValue(true);
|
||||
for( int i=0; i<lineinfo.Count; ++i )
|
||||
t.SetInt(lineinfo[i], ref v);
|
||||
}
|
||||
else if(func.V.ClIsCsClosure()) {
|
||||
Top.V.SetNilValue();
|
||||
IncrTop();
|
||||
}
|
||||
else throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
private string GetFuncName( CallInfo ci, out string name )
|
||||
{
|
||||
var proto = GetCurrentLuaFunc(ci).Proto; // calling function
|
||||
var pc = ci.CurrentPc; // calling instruction index
|
||||
var ins = proto.Code[pc]; // calling instruction
|
||||
|
||||
TMS tm;
|
||||
switch( ins.GET_OPCODE() )
|
||||
{
|
||||
case OpCode.OP_CALL:
|
||||
case OpCode.OP_TAILCALL: /* get function name */
|
||||
return GetObjName(proto, pc, ins.GETARG_A(), out name);
|
||||
|
||||
case OpCode.OP_TFORCALL: { /* for iterator */
|
||||
name = "for iterator";
|
||||
return "for iterator";
|
||||
}
|
||||
|
||||
/* all other instructions can call only through metamethods */
|
||||
case OpCode.OP_SELF:
|
||||
case OpCode.OP_GETTABUP:
|
||||
case OpCode.OP_GETTABLE: tm = TMS.TM_INDEX; break;
|
||||
|
||||
case OpCode.OP_SETTABUP:
|
||||
case OpCode.OP_SETTABLE: tm = TMS.TM_NEWINDEX; break;
|
||||
|
||||
case OpCode.OP_EQ: tm = TMS.TM_EQ; break;
|
||||
case OpCode.OP_ADD: tm = TMS.TM_ADD; break;
|
||||
case OpCode.OP_SUB: tm = TMS.TM_SUB; break;
|
||||
case OpCode.OP_MUL: tm = TMS.TM_MUL; break;
|
||||
case OpCode.OP_DIV: tm = TMS.TM_DIV; break;
|
||||
case OpCode.OP_MOD: tm = TMS.TM_MOD; break;
|
||||
case OpCode.OP_POW: tm = TMS.TM_POW; break;
|
||||
case OpCode.OP_UNM: tm = TMS.TM_UNM; break;
|
||||
case OpCode.OP_LEN: tm = TMS.TM_LEN; break;
|
||||
case OpCode.OP_LT: tm = TMS.TM_LT; break;
|
||||
case OpCode.OP_LE: tm = TMS.TM_LE; break;
|
||||
case OpCode.OP_CONCAT: tm = TMS.TM_CONCAT; break;
|
||||
|
||||
default:
|
||||
name = null;
|
||||
return null; /* else no useful name can be found */
|
||||
}
|
||||
|
||||
name = GetTagMethodName( tm );
|
||||
return "metamethod";
|
||||
}
|
||||
|
||||
private void FuncInfo( LuaDebug ar, StkId func )
|
||||
{
|
||||
Utl.Assert(func.V.TtIsFunction());
|
||||
if(func.V.ClIsLuaClosure()) {
|
||||
var lcl = func.V.ClLValue();
|
||||
var p = lcl.Proto;
|
||||
ar.Source = string.IsNullOrEmpty(p.Source) ? "=?" : p.Source;
|
||||
ar.LineDefined = p.LineDefined;
|
||||
ar.LastLineDefined = p.LastLineDefined;
|
||||
ar.What = (ar.LineDefined == 0) ? "main" : "Lua";
|
||||
}
|
||||
else if(func.V.ClIsCsClosure()) {
|
||||
ar.Source = "=[C#]";
|
||||
ar.LineDefined = -1;
|
||||
ar.LastLineDefined = -1;
|
||||
ar.What = "C#";
|
||||
}
|
||||
else throw new System.NotImplementedException();
|
||||
|
||||
if( ar.Source.Length > LuaDef.LUA_IDSIZE )
|
||||
{
|
||||
ar.ShortSrc = ar.Source.Substring(0, LuaDef.LUA_IDSIZE);
|
||||
}
|
||||
else ar.ShortSrc = ar.Source;
|
||||
}
|
||||
|
||||
private void AddInfo( string msg )
|
||||
{
|
||||
// var api = (ILuaAPI)this;
|
||||
// TODO
|
||||
if( CI.IsLua )
|
||||
{
|
||||
var line = GetCurrentLine(CI);
|
||||
var src = GetCurrentLuaFunc(CI).Proto.Source;
|
||||
if( src == null )
|
||||
src = "?";
|
||||
|
||||
// PushString, PushString API
|
||||
// API ApiIncrTop Top CI.Top
|
||||
// api.PushString( msg );
|
||||
O_PushString( string.Format( "{0}:{1}: {2}",
|
||||
src, line, msg ) );
|
||||
}
|
||||
}
|
||||
|
||||
internal void G_RunError( string fmt, params object[] args )
|
||||
{
|
||||
AddInfo( string.Format( fmt, args ) );
|
||||
G_ErrorMsg();
|
||||
}
|
||||
|
||||
private void G_ErrorMsg()
|
||||
{
|
||||
if( ErrFunc != 0 ) // is there an error handling function?
|
||||
{
|
||||
StkId errFunc = RestoreStack( ErrFunc );
|
||||
|
||||
if(!errFunc.V.TtIsFunction())
|
||||
D_Throw( ThreadStatus.LUA_ERRERR );
|
||||
|
||||
var below = Stack[Top.Index-1];
|
||||
Top.V.SetObj(ref below.V);
|
||||
below.V.SetObj(ref errFunc.V);
|
||||
IncrTop();
|
||||
|
||||
D_Call( below, 1, false );
|
||||
}
|
||||
|
||||
D_Throw( ThreadStatus.LUA_ERRRUN );
|
||||
}
|
||||
|
||||
private string UpvalName( LuaProto p, int uv )
|
||||
{
|
||||
// TODO
|
||||
return "(UpvalName:NotImplemented)";
|
||||
}
|
||||
|
||||
private string GetUpvalueName( CallInfo ci, StkId o, out string name )
|
||||
{
|
||||
var func = Stack[ci.FuncIndex];
|
||||
Utl.Assert(func.V.TtIsFunction() && func.V.ClIsLuaClosure());
|
||||
var lcl = func.V.ClLValue();
|
||||
for(int i=0; i<lcl.Upvals.Length; ++i) {
|
||||
if( lcl.Upvals[i].V == o ) {
|
||||
name = UpvalName( lcl.Proto, i );
|
||||
return "upvalue";
|
||||
}
|
||||
}
|
||||
name = default(string);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void KName( LuaProto proto, int pc, int c, out string name )
|
||||
{
|
||||
if( Instruction.ISK(c) ) { // is `c' a constant
|
||||
var val = proto.K[Instruction.INDEXK(c)];
|
||||
if(val.V.TtIsString()) { // literal constant?
|
||||
name = val.V.SValue();
|
||||
return;
|
||||
}
|
||||
// else no reasonable name found
|
||||
}
|
||||
else { // `c' is a register
|
||||
string what = GetObjName( proto, pc, c, out name );
|
||||
if( what == "constant" ) { // found a constant name
|
||||
return; // `name' already filled
|
||||
}
|
||||
// else no reasonable name found
|
||||
}
|
||||
name = "?"; // no reasonable name found
|
||||
}
|
||||
|
||||
private int FindSetReg( LuaProto proto, int lastpc, int reg )
|
||||
{
|
||||
var setreg = -1; // keep last instruction that changed `reg'
|
||||
for( int pc=0; pc<lastpc; ++pc ) {
|
||||
var ins = proto.Code[pc];
|
||||
var op = ins.GET_OPCODE();
|
||||
var a = ins.GETARG_A();
|
||||
switch( op ) {
|
||||
case OpCode.OP_LOADNIL: {
|
||||
var b = ins.GETARG_B();
|
||||
// set registers from `a' to `a+b'
|
||||
if( a <= reg && reg <= a + b )
|
||||
setreg = pc;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode.OP_TFORCALL: {
|
||||
// effect all regs above its base
|
||||
if( reg >= a+2 )
|
||||
setreg = pc;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode.OP_CALL:
|
||||
case OpCode.OP_TAILCALL: {
|
||||
// effect all registers above base
|
||||
if( reg >= a )
|
||||
setreg = pc;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode.OP_JMP: {
|
||||
var b = ins.GETARG_sBx();
|
||||
var dest = pc + 1 + b;
|
||||
// jump is forward and do not skip `lastpc'
|
||||
if( pc < dest && dest <= lastpc )
|
||||
pc += b; // do the jump
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode.OP_TEST: {
|
||||
// jumped code can change `a'
|
||||
if( reg == a )
|
||||
setreg = pc;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// any instruction that set A
|
||||
if( Coder.TestAMode( op ) && reg == a ) {
|
||||
setreg = pc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return setreg;
|
||||
}
|
||||
|
||||
private string GetObjName( LuaProto proto, int lastpc, int reg,
|
||||
out string name )
|
||||
{
|
||||
name = F_GetLocalName( proto, reg+1, lastpc );
|
||||
if( name != null ) // is a local?
|
||||
return "local";
|
||||
|
||||
// else try symbolic execution
|
||||
var pc = FindSetReg( proto, lastpc, reg );
|
||||
if( pc != -1 )
|
||||
{
|
||||
var ins = proto.Code[pc];
|
||||
var op = ins.GET_OPCODE();
|
||||
switch( op )
|
||||
{
|
||||
case OpCode.OP_MOVE: {
|
||||
var b = ins.GETARG_B(); // move from `b' to `a'
|
||||
if( b < ins.GETARG_A() )
|
||||
return GetObjName(proto, pc, b, out name);
|
||||
break;
|
||||
}
|
||||
case OpCode.OP_GETTABUP:
|
||||
case OpCode.OP_GETTABLE: {
|
||||
var k = ins.GETARG_C();
|
||||
var t = ins.GETARG_B();
|
||||
var vn = (op == OpCode.OP_GETTABLE)
|
||||
? F_GetLocalName( proto, t+1, pc )
|
||||
: UpvalName( proto, t );
|
||||
KName( proto, pc, k, out name );
|
||||
return (vn == LuaDef.LUA_ENV) ? "global" : "field";
|
||||
}
|
||||
|
||||
case OpCode.OP_GETUPVAL: {
|
||||
name = UpvalName( proto, ins.GETARG_B() );
|
||||
return "upvalue";
|
||||
}
|
||||
|
||||
case OpCode.OP_LOADK:
|
||||
case OpCode.OP_LOADKX: {
|
||||
var b = (op == OpCode.OP_LOADK)
|
||||
? ins.GETARG_Bx()
|
||||
: proto.Code[pc+1].GETARG_Ax();
|
||||
var val = proto.K[b];
|
||||
if(val.V.TtIsString())
|
||||
{
|
||||
name = val.V.SValue();
|
||||
return "constant";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode.OP_SELF: {
|
||||
var k = ins.GETARG_C(); // key index
|
||||
KName( proto, pc, k, out name );
|
||||
return "method";
|
||||
}
|
||||
|
||||
default: break; // go through to return null
|
||||
}
|
||||
}
|
||||
|
||||
return null; // could not find reasonable name
|
||||
}
|
||||
|
||||
private bool IsInStack( CallInfo ci, StkId o )
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
private void G_SimpleTypeError( ref TValue o, string op )
|
||||
{
|
||||
string t = ObjTypeName( ref o );
|
||||
G_RunError( "attempt to {0} a {1} value", op, t );
|
||||
}
|
||||
|
||||
private void G_TypeError( StkId o, string op )
|
||||
{
|
||||
CallInfo ci = CI;
|
||||
string name = null;
|
||||
string kind = null;
|
||||
string t = ObjTypeName(ref o.V);
|
||||
if( ci.IsLua )
|
||||
{
|
||||
kind = GetUpvalueName( ci, o, out name);
|
||||
if( kind != null && IsInStack( ci, o ) )
|
||||
{
|
||||
var lcl = Stack[ci.FuncIndex].V.ClLValue();
|
||||
kind = GetObjName( lcl.Proto, ci.CurrentPc,
|
||||
(o.Index - ci.BaseIndex), out name );
|
||||
}
|
||||
}
|
||||
if( kind != null )
|
||||
G_RunError( "attempt to {0} {1} '{2}' (a {3} value)",
|
||||
op, kind, name, t );
|
||||
else
|
||||
G_RunError( "attempt to {0} a {1} value", op, t );
|
||||
}
|
||||
|
||||
private void G_ArithError( StkId p1, StkId p2 )
|
||||
{
|
||||
var n = new TValue();
|
||||
if( !V_ToNumber( p1, ref n ) )
|
||||
{ p2 = p1; } // first operand is wrong
|
||||
|
||||
G_TypeError( p2, "perform arithmetic on" );
|
||||
}
|
||||
|
||||
private void G_OrderError( StkId p1, StkId p2 )
|
||||
{
|
||||
string t1 = ObjTypeName(ref p1.V);
|
||||
string t2 = ObjTypeName(ref p2.V);
|
||||
if( t1 == t2 )
|
||||
G_RunError( "attempt to compare two {0} values", t1 );
|
||||
else
|
||||
G_RunError( "attempt to compare {0} with {1}", t1, t2 );
|
||||
}
|
||||
|
||||
private void G_ConcatError( StkId p1, StkId p2 )
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
25
UniLua/LuaDebugLib.cs
Normal file
25
UniLua/LuaDebugLib.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
internal class LuaDebugLib
|
||||
{
|
||||
public const string LIB_NAME = "debug";
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "traceback", DBG_Traceback ),
|
||||
};
|
||||
|
||||
lua.L_NewLib( define );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int DBG_Traceback( ILuaState lua )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
UniLua/LuaEncLib.cs
Normal file
63
UniLua/LuaEncLib.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
class LuaEncLib
|
||||
{
|
||||
public const string LIB_NAME = "enc";
|
||||
|
||||
private const string ENC_UTF8 = "utf8";
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
var define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "encode", ENC_Encode ),
|
||||
new NameFuncPair( "decode", ENC_Decode ),
|
||||
};
|
||||
|
||||
lua.L_NewLib( define );
|
||||
|
||||
lua.PushString( ENC_UTF8 );
|
||||
lua.SetField( -2, "utf8" );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int ENC_Encode( ILuaState lua )
|
||||
{
|
||||
var s = lua.ToString(1);
|
||||
var e = lua.ToString(2);
|
||||
if( e != ENC_UTF8 )
|
||||
throw new Exception("unsupported encoding:" + e);
|
||||
|
||||
var bytes = Encoding.ASCII.GetBytes(s);
|
||||
var sb = new StringBuilder();
|
||||
for( var i=0; i<bytes.Length; ++i )
|
||||
{
|
||||
sb.Append( (char)bytes[i] );
|
||||
}
|
||||
lua.PushString( sb.ToString() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int ENC_Decode( ILuaState lua )
|
||||
{
|
||||
var s = lua.ToString(1);
|
||||
var e = lua.ToString(2);
|
||||
if( e != ENC_UTF8 )
|
||||
throw new Exception("unsupported encoding:" + e);
|
||||
|
||||
var bytes = new Byte[s.Length];
|
||||
for( int i=0; i<s.Length; ++i )
|
||||
{
|
||||
bytes[i] = (byte)s[i];
|
||||
}
|
||||
|
||||
lua.PushString( Encoding.ASCII.GetString( bytes ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
134
UniLua/LuaFile.cs
Normal file
134
UniLua/LuaFile.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
public delegate string PathHook(string filename);
|
||||
public class LuaFile
|
||||
{
|
||||
public static Dictionary<string, byte[]> VirtualFiles;
|
||||
|
||||
public static ILoadInfo OpenFile( string filename )
|
||||
{
|
||||
foreach (var file in VirtualFiles.Keys)
|
||||
{
|
||||
if (file == filename)
|
||||
{
|
||||
return new BytesLoadInfo(VirtualFiles[filename]); ;
|
||||
}
|
||||
}
|
||||
|
||||
return new FileLoadInfo( File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) );
|
||||
}
|
||||
|
||||
public static bool Readable( string filename )
|
||||
{
|
||||
foreach (var file in VirtualFiles.Keys)
|
||||
{
|
||||
if (file == filename)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
using( var stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch( Exception ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FileLoadInfo : ILoadInfo, IDisposable
|
||||
{
|
||||
public FileLoadInfo( FileStream stream )
|
||||
{
|
||||
Stream = stream;
|
||||
Reader = new StreamReader(Stream, System.Text.Encoding.ASCII);
|
||||
Buf = new Queue<char>();
|
||||
}
|
||||
|
||||
public int ReadByte()
|
||||
{
|
||||
if( Buf.Count > 0 )
|
||||
return (int)Buf.Dequeue();
|
||||
else
|
||||
return Reader.Read();
|
||||
}
|
||||
|
||||
public int PeekByte()
|
||||
{
|
||||
if( Buf.Count > 0 )
|
||||
return (int)Buf.Peek();
|
||||
else
|
||||
{
|
||||
var c = Reader.Read();
|
||||
if( c == -1 )
|
||||
return c;
|
||||
Save( (char)c );
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Reader.Dispose();
|
||||
Stream.Dispose();
|
||||
}
|
||||
|
||||
private const string UTF8_BOM = "\u00EF\u00BB\u00BF";
|
||||
private FileStream Stream;
|
||||
private StreamReader Reader;
|
||||
private Queue<char> Buf;
|
||||
|
||||
private void Save( char b )
|
||||
{
|
||||
Buf.Enqueue( b );
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
Buf.Clear();
|
||||
}
|
||||
|
||||
#if false
|
||||
private int SkipBOM()
|
||||
{
|
||||
for( var i=0; i<UTF8_BOM.Length; ++i )
|
||||
{
|
||||
var c = Stream.ReadByte();
|
||||
if(c == -1 || c != (byte)UTF8_BOM[i])
|
||||
return c;
|
||||
Save( (char)c );
|
||||
}
|
||||
// perfix matched; discard it
|
||||
Clear();
|
||||
return Stream.ReadByte();
|
||||
}
|
||||
#endif
|
||||
|
||||
public void SkipComment()
|
||||
{
|
||||
var c = Reader.Read();//SkipBOM();
|
||||
|
||||
// first line is a comment (Unix exec. file)?
|
||||
if( c == '#' )
|
||||
{
|
||||
do {
|
||||
c = Reader.Read();
|
||||
} while( c != -1 && c != '\n' );
|
||||
Save( (char)'\n' ); // fix line number
|
||||
}
|
||||
else if( c != -1 )
|
||||
{
|
||||
Save( (char)c );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
94
UniLua/LuaFunc.cs
Normal file
94
UniLua/LuaFunc.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
// #define DEBUG_FIND_UPVALUE
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using ULDebug = UniLua.Tools.ULDebug;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
public partial class LuaState
|
||||
{
|
||||
|
||||
private LuaUpvalue F_FindUpval( StkId level )
|
||||
{
|
||||
#if DEBUG_FIND_UPVALUE
|
||||
Console.WriteLine( "[F_FindUpval] >>>>>>>>>>>>>>>>>>>> level:" + level );
|
||||
#endif
|
||||
|
||||
var node = OpenUpval.First;
|
||||
LinkedListNode<LuaUpvalue> prev = null;
|
||||
while( node != null )
|
||||
{
|
||||
var upval = node.Value;
|
||||
#if DEBUG_FIND_UPVALUE
|
||||
Console.WriteLine("[F_FindUpval] >>>>>>>>>>>>>>>>>>>> upval.V:" + upval.V );
|
||||
#endif
|
||||
if(upval.V.Index < level.Index)
|
||||
break;
|
||||
|
||||
var next = node.Next;
|
||||
if(upval.V == level)
|
||||
return upval;
|
||||
|
||||
prev = node;
|
||||
node = next;
|
||||
}
|
||||
|
||||
// not found: create a new one
|
||||
var ret = new LuaUpvalue();
|
||||
ret.V = level;
|
||||
// ret.Prev = G.UpvalHead;
|
||||
// ret.Next = G.UpvalHead.Next;
|
||||
// ret.Next.Prev = ret;
|
||||
// G.UpvalHead.Next = ret;
|
||||
|
||||
if( prev == null )
|
||||
OpenUpval.AddFirst( ret );
|
||||
else
|
||||
OpenUpval.AddAfter( prev, ret );
|
||||
|
||||
#if DEBUG_FIND_UPVALUE
|
||||
Console.WriteLine("[F_FindUpval] >>>>>>>>>>>>>>>>>>>> create new one:" + ret.V );
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void F_Close( StkId level )
|
||||
{
|
||||
var node = OpenUpval.First;
|
||||
while( node != null )
|
||||
{
|
||||
var upval = node.Value;
|
||||
if( upval.V.Index < level.Index )
|
||||
break;
|
||||
|
||||
var next = node.Next;
|
||||
OpenUpval.Remove( node );
|
||||
node = next;
|
||||
|
||||
upval.Value.V.SetObj(ref upval.V.V);
|
||||
upval.V = upval.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private string F_GetLocalName( LuaProto proto, int localNumber, int pc )
|
||||
{
|
||||
for( int i=0;
|
||||
i<proto.LocVars.Count && proto.LocVars[i].StartPc <= pc;
|
||||
++i )
|
||||
{
|
||||
if( pc < proto.LocVars[i].EndPc ) { // is variable active?
|
||||
--localNumber;
|
||||
if( localNumber == 0 )
|
||||
return proto.LocVars[i].VarName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
100
UniLua/LuaIOLib.cs
Normal file
100
UniLua/LuaIOLib.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
// TODO
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
|
||||
internal class LuaIOLib
|
||||
{
|
||||
public const string LIB_NAME = "io";
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "close", IO_Close ),
|
||||
new NameFuncPair( "flush", IO_Flush ),
|
||||
new NameFuncPair( "input", IO_Input ),
|
||||
new NameFuncPair( "lines", IO_Lines ),
|
||||
new NameFuncPair( "open", IO_Open ),
|
||||
new NameFuncPair( "output", IO_Output ),
|
||||
new NameFuncPair( "popen", IO_Popen ),
|
||||
new NameFuncPair( "read", IO_Read ),
|
||||
new NameFuncPair( "tmpfile", IO_Tmpfile ),
|
||||
new NameFuncPair( "type", IO_Type ),
|
||||
new NameFuncPair( "write", IO_Write ),
|
||||
};
|
||||
|
||||
lua.L_NewLib( define );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int IO_Close( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Flush( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Input( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Lines( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Open( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Output( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Popen( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Read( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Tmpfile( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Type( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int IO_Write( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
343
UniLua/LuaMathLib.cs
Normal file
343
UniLua/LuaMathLib.cs
Normal file
@@ -0,0 +1,343 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using Math = System.Math;
|
||||
using Double = System.Double;
|
||||
using Random = System.Random;
|
||||
using BitConverter = System.BitConverter;
|
||||
|
||||
internal class LuaMathLib
|
||||
{
|
||||
public const string LIB_NAME = "math";
|
||||
|
||||
private const double RADIANS_PER_DEGREE = Math.PI / 180.0;
|
||||
|
||||
private static Random RandObj;
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "abs", Math_Abs ),
|
||||
new NameFuncPair( "acos", Math_Acos ),
|
||||
new NameFuncPair( "asin", Math_Asin ),
|
||||
new NameFuncPair( "atan2", Math_Atan2 ),
|
||||
new NameFuncPair( "atan", Math_Atan ),
|
||||
new NameFuncPair( "ceil", Math_Ceil ),
|
||||
new NameFuncPair( "cosh", Math_Cosh ),
|
||||
new NameFuncPair( "cos", Math_Cos ),
|
||||
new NameFuncPair( "deg", Math_Deg ),
|
||||
new NameFuncPair( "exp", Math_Exp ),
|
||||
new NameFuncPair( "floor", Math_Floor ),
|
||||
new NameFuncPair( "fmod", Math_Fmod ),
|
||||
new NameFuncPair( "frexp", Math_Frexp ),
|
||||
new NameFuncPair( "ldexp", Math_Ldexp ),
|
||||
new NameFuncPair( "log10", Math_Log10 ),
|
||||
new NameFuncPair( "log", Math_Log ),
|
||||
new NameFuncPair( "max", Math_Max ),
|
||||
new NameFuncPair( "min", Math_Min ),
|
||||
new NameFuncPair( "modf", Math_Modf ),
|
||||
new NameFuncPair( "pow", Math_Pow ),
|
||||
new NameFuncPair( "rad", Math_Rad ),
|
||||
new NameFuncPair( "random", Math_Random ),
|
||||
new NameFuncPair( "randomseed", Math_RandomSeed ),
|
||||
new NameFuncPair( "sinh", Math_Sinh ),
|
||||
new NameFuncPair( "sin", Math_Sin ),
|
||||
new NameFuncPair( "sqrt", Math_Sqrt ),
|
||||
new NameFuncPair( "tanh", Math_Tanh ),
|
||||
new NameFuncPair( "tan", Math_Tan ),
|
||||
};
|
||||
|
||||
lua.L_NewLib( define );
|
||||
|
||||
lua.PushNumber( Math.PI );
|
||||
lua.SetField( -2, "pi" );
|
||||
|
||||
lua.PushNumber( Double.MaxValue );
|
||||
lua.SetField( -2, "huge" );
|
||||
|
||||
RandObj = new Random();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Abs( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Abs( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Acos( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Acos( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Asin( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Asin( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Atan2( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Atan2( lua.L_CheckNumber(1),
|
||||
lua.L_CheckNumber(2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Atan( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Atan( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Ceil( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Ceiling( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Cosh( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Cosh( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Cos( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Cos( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Deg( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( lua.L_CheckNumber(1) / RADIANS_PER_DEGREE );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Exp( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Exp( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Floor( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Floor( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Fmod( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.IEEERemainder( lua.L_CheckNumber(1),
|
||||
lua.L_CheckNumber(2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Frexp( ILuaState lua )
|
||||
{
|
||||
double d = lua.L_CheckNumber(1);
|
||||
|
||||
// Translate the double into sign, exponent and mantissa.
|
||||
long bits = BitConverter.DoubleToInt64Bits(d);
|
||||
// Note that the shift is sign-extended, hence the test against -1 not 1
|
||||
bool negative = (bits < 0);
|
||||
int exponent = (int) ((bits >> 52) & 0x7ffL);
|
||||
long mantissa = bits & 0xfffffffffffffL;
|
||||
|
||||
// Subnormal numbers; exponent is effectively one higher,
|
||||
// but there's no extra normalisation bit in the mantissa
|
||||
if (exponent==0)
|
||||
{
|
||||
exponent++;
|
||||
}
|
||||
// Normal numbers; leave exponent as it is but add extra
|
||||
// bit to the front of the mantissa
|
||||
else
|
||||
{
|
||||
mantissa = mantissa | (1L<<52);
|
||||
}
|
||||
|
||||
// Bias the exponent. It's actually biased by 1023, but we're
|
||||
// treating the mantissa as m.0 rather than 0.m, so we need
|
||||
// to subtract another 52 from it.
|
||||
exponent -= 1075;
|
||||
|
||||
if (mantissa == 0)
|
||||
{
|
||||
lua.PushNumber( 0.0 );
|
||||
lua.PushNumber( 0.0 );
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Normalize */
|
||||
while((mantissa & 1) == 0)
|
||||
{ /* i.e., Mantissa is even */
|
||||
mantissa >>= 1;
|
||||
exponent++;
|
||||
}
|
||||
|
||||
double m = (double)mantissa;
|
||||
double e = (double)exponent;
|
||||
while( m >= 1 )
|
||||
{
|
||||
m /= 2.0;
|
||||
e += 1.0;
|
||||
}
|
||||
|
||||
if( negative ) m = -m;
|
||||
lua.PushNumber( m );
|
||||
lua.PushNumber( e );
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static int Math_Ldexp( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( lua.L_CheckNumber(1) * Math.Pow(2, lua.L_CheckNumber(2)) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Log10( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Log10( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Log( ILuaState lua )
|
||||
{
|
||||
double x = lua.L_CheckNumber(1);
|
||||
double res;
|
||||
if( lua.IsNoneOrNil(2) )
|
||||
res = Math.Log(x);
|
||||
else
|
||||
{
|
||||
double logBase = lua.L_CheckNumber(2);
|
||||
if( logBase == 10.0 )
|
||||
res = Math.Log10(x);
|
||||
else
|
||||
res = Math.Log(x, logBase);
|
||||
}
|
||||
lua.PushNumber(res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Max( ILuaState lua )
|
||||
{
|
||||
int n = lua.GetTop();
|
||||
double dmax = lua.L_CheckNumber(1);
|
||||
for( int i=2; i<=n; ++i )
|
||||
{
|
||||
double d = lua.L_CheckNumber(i);
|
||||
if( d > dmax )
|
||||
dmax = d;
|
||||
}
|
||||
lua.PushNumber(dmax);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Min( ILuaState lua )
|
||||
{
|
||||
int n = lua.GetTop();
|
||||
double dmin = lua.L_CheckNumber(1);
|
||||
for( int i=2; i<=n; ++i )
|
||||
{
|
||||
double d = lua.L_CheckNumber(i);
|
||||
if( d < dmin )
|
||||
dmin = d;
|
||||
}
|
||||
lua.PushNumber(dmin);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Modf( ILuaState lua )
|
||||
{
|
||||
double d = lua.L_CheckNumber(1);
|
||||
double c = Math.Ceiling(d);
|
||||
lua.PushNumber( c );
|
||||
lua.PushNumber( d-c );
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static int Math_Pow( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Pow( lua.L_CheckNumber(1),
|
||||
lua.L_CheckNumber(2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Rad( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( lua.L_CheckNumber(1) * RADIANS_PER_DEGREE );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Random( ILuaState lua )
|
||||
{
|
||||
double r = RandObj.NextDouble();
|
||||
switch( lua.GetTop() )
|
||||
{
|
||||
case 0: // no argument
|
||||
lua.PushNumber( r );
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
double u = lua.L_CheckNumber(1);
|
||||
lua.L_ArgCheck( 1.0 <= u, 1, "interval is empty" );
|
||||
lua.PushNumber( Math.Floor(r*u) + 1.0 ); // int in [1, u]
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
double l = lua.L_CheckNumber(1);
|
||||
double u = lua.L_CheckNumber(2);
|
||||
lua.L_ArgCheck( l <= u, 2, "interval is empty" );
|
||||
lua.PushNumber( Math.Floor(r*(u-l+1)) + l ); // int in [l, u]
|
||||
break;
|
||||
}
|
||||
default: return lua.L_Error( "wrong number of arguments" );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_RandomSeed( ILuaState lua )
|
||||
{
|
||||
RandObj = new Random( (int)lua.L_CheckUnsigned(1) );
|
||||
RandObj.Next();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int Math_Sinh( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Sinh( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Sin( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Sin( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Sqrt( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Sqrt( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Tanh( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Tanh( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Math_Tan( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber( Math.Tan( lua.L_CheckNumber(1) ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
419
UniLua/LuaObject.cs
Normal file
419
UniLua/LuaObject.cs
Normal file
@@ -0,0 +1,419 @@
|
||||
|
||||
// #define DEBUG_DUMMY_TVALUE_MODIFY
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ULDebug = UniLua.Tools.ULDebug;
|
||||
|
||||
public struct TValue
|
||||
{
|
||||
private const UInt64 CLOSURE_LUA = 0; // lua closure
|
||||
private const UInt64 CLOSURE_CS = 1; // c# closure
|
||||
private const UInt64 CLOSURE_LCS = 2; // light c# closure
|
||||
|
||||
private const UInt64 BOOLEAN_FALSE = 0;
|
||||
private const UInt64 BOOLEAN_TRUE = 1;
|
||||
|
||||
public int Tt;
|
||||
public double NValue;
|
||||
public UInt64 UInt64Value;
|
||||
public object OValue;
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
public bool Lock_;
|
||||
#endif
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Tt.GetHashCode() ^ NValue.GetHashCode()
|
||||
^ UInt64Value.GetHashCode()
|
||||
^ (OValue != null ? OValue.GetHashCode() : 0x12345678);
|
||||
}
|
||||
public override bool Equals(object o)
|
||||
{
|
||||
if(!(o is TValue)) return false;
|
||||
return Equals((TValue)o);
|
||||
}
|
||||
public bool Equals(TValue o)
|
||||
{
|
||||
if(Tt != o.Tt || NValue != o.NValue || UInt64Value != o.UInt64Value)
|
||||
{ return false; }
|
||||
|
||||
switch(Tt) {
|
||||
case (int)LuaType.LUA_TNIL: return true;
|
||||
case (int)LuaType.LUA_TBOOLEAN: return BValue() == o.BValue();
|
||||
case (int)LuaType.LUA_TNUMBER: return NValue == o.NValue;
|
||||
case (int)LuaType.LUA_TUINT64: return UInt64Value == o.UInt64Value;
|
||||
case (int)LuaType.LUA_TSTRING: return SValue() == o.SValue();
|
||||
default: return System.Object.ReferenceEquals(OValue, o.OValue);
|
||||
}
|
||||
}
|
||||
public static bool operator==(TValue lhs, TValue rhs)
|
||||
{
|
||||
return lhs.Equals(rhs);
|
||||
}
|
||||
public static bool operator!=(TValue lhs, TValue rhs)
|
||||
{
|
||||
return !lhs.Equals(rhs);
|
||||
}
|
||||
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
private void CheckLock() {
|
||||
if(Lock_) {
|
||||
UnityEngine.Console.WriteLineError("changing a lock value");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal bool TtIsNil() { return Tt == (int)LuaType.LUA_TNIL; }
|
||||
internal bool TtIsBoolean() { return Tt == (int)LuaType.LUA_TBOOLEAN; }
|
||||
internal bool TtIsNumber() { return Tt == (int)LuaType.LUA_TNUMBER; }
|
||||
internal bool TtIsUInt64() { return Tt == (int)LuaType.LUA_TUINT64; }
|
||||
internal bool TtIsString() { return Tt == (int)LuaType.LUA_TSTRING; }
|
||||
internal bool TtIsTable() { return Tt == (int)LuaType.LUA_TTABLE; }
|
||||
internal bool TtIsFunction() { return Tt == (int)LuaType.LUA_TFUNCTION; }
|
||||
internal bool TtIsThread() { return Tt == (int)LuaType.LUA_TTHREAD; }
|
||||
|
||||
internal bool ClIsLuaClosure() { return UInt64Value == CLOSURE_LUA; }
|
||||
internal bool ClIsCsClosure() { return UInt64Value == CLOSURE_CS; }
|
||||
internal bool ClIsLcsClosure() { return UInt64Value == CLOSURE_LCS; }
|
||||
|
||||
internal bool BValue() { return UInt64Value != BOOLEAN_FALSE; }
|
||||
internal string SValue() { return (string)OValue; }
|
||||
internal LuaTable HValue() { return OValue as LuaTable; }
|
||||
internal LuaLClosureValue ClLValue() { return (LuaLClosureValue)OValue; }
|
||||
internal LuaCsClosureValue ClCsValue() { return (LuaCsClosureValue)OValue; }
|
||||
internal LuaUserDataValue RawUValue() { return OValue as LuaUserDataValue; }
|
||||
|
||||
internal void SetNilValue() {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TNIL;
|
||||
NValue = 0.0;
|
||||
UInt64Value = 0;
|
||||
OValue = null;
|
||||
}
|
||||
internal void SetBValue(bool v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TBOOLEAN;
|
||||
NValue = 0.0;
|
||||
UInt64Value = v ? BOOLEAN_TRUE : BOOLEAN_FALSE;
|
||||
OValue = null;
|
||||
}
|
||||
internal void SetObj(ref TValue v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = v.Tt;
|
||||
NValue = v.NValue;
|
||||
UInt64Value = v.UInt64Value;
|
||||
OValue = v.OValue;
|
||||
}
|
||||
internal void SetNValue(double v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TNUMBER;
|
||||
NValue = v;
|
||||
UInt64Value = 0;
|
||||
OValue = null;
|
||||
}
|
||||
internal void SetUInt64Value(UInt64 v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TUINT64;
|
||||
NValue = 0.0;
|
||||
UInt64Value = v;
|
||||
OValue = null;
|
||||
}
|
||||
internal void SetSValue(string v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TSTRING;
|
||||
NValue = 0.0;
|
||||
UInt64Value = 0;
|
||||
OValue = v;
|
||||
}
|
||||
internal void SetHValue(LuaTable v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TTABLE;
|
||||
NValue = 0.0;
|
||||
UInt64Value = 0;
|
||||
OValue = v;
|
||||
}
|
||||
internal void SetThValue(LuaState v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TTHREAD;
|
||||
NValue = 0.0;
|
||||
UInt64Value = 0;
|
||||
OValue = v;
|
||||
}
|
||||
internal void SetPValue(object v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TLIGHTUSERDATA;
|
||||
NValue = 0.0;
|
||||
UInt64Value = 0;
|
||||
OValue = v;
|
||||
}
|
||||
internal void SetClLValue(LuaLClosureValue v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TFUNCTION;
|
||||
NValue = 0.0;
|
||||
UInt64Value = CLOSURE_LUA;
|
||||
OValue = v;
|
||||
}
|
||||
internal void SetClCsValue(LuaCsClosureValue v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TFUNCTION;
|
||||
NValue = 0.0;
|
||||
UInt64Value = CLOSURE_CS;
|
||||
OValue = v;
|
||||
}
|
||||
internal void SetClLcsValue(CSharpFunctionDelegate v) {
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
CheckLock();
|
||||
#endif
|
||||
Tt = (int)LuaType.LUA_TFUNCTION;
|
||||
NValue = 0.0;
|
||||
UInt64Value = CLOSURE_LCS;
|
||||
OValue = v;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (TtIsString()) {
|
||||
return string.Format("(string, {0})", SValue());
|
||||
} else if (TtIsNumber()) {
|
||||
return string.Format("(number, {0})", NValue);
|
||||
} else if (TtIsNil()) {
|
||||
return "(nil)";
|
||||
} else {
|
||||
return string.Format("(type:{0})", Tt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class StkId
|
||||
{
|
||||
public TValue V;
|
||||
|
||||
private StkId[] List;
|
||||
public int Index { get; private set; }
|
||||
|
||||
public void SetList(StkId[] list) { List = list; }
|
||||
public void SetIndex(int index) { Index = index; }
|
||||
|
||||
public static StkId inc(ref StkId val)
|
||||
{
|
||||
var ret = val;
|
||||
val = val.List[val.Index+1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string detail;
|
||||
if(V.TtIsString())
|
||||
{ detail = V.SValue().Replace("\n", " "); }
|
||||
else
|
||||
{ detail = "..."; }
|
||||
return string.Format("StkId - {0} - {1}", LuaState.TypeName((LuaType)V.Tt), detail);
|
||||
}
|
||||
}
|
||||
|
||||
public class LuaLClosureValue
|
||||
{
|
||||
public LuaProto Proto;
|
||||
public LuaUpvalue[] Upvals;
|
||||
|
||||
public LuaLClosureValue(LuaProto p) {
|
||||
Proto = p;
|
||||
|
||||
Upvals = new LuaUpvalue[p.Upvalues.Count];
|
||||
for(int i=0; i<p.Upvalues.Count; ++i)
|
||||
{ Upvals[i] = new LuaUpvalue(); }
|
||||
}
|
||||
}
|
||||
|
||||
public class LuaUserDataValue
|
||||
{
|
||||
public object Value;
|
||||
public LuaTable MetaTable;
|
||||
public int Length;
|
||||
}
|
||||
|
||||
public class LocVar
|
||||
{
|
||||
public string VarName;
|
||||
public int StartPc;
|
||||
public int EndPc;
|
||||
}
|
||||
|
||||
public class UpvalDesc
|
||||
{
|
||||
public string Name;
|
||||
public int Index;
|
||||
public bool InStack;
|
||||
}
|
||||
|
||||
public class LuaProto
|
||||
{
|
||||
public List<Instruction> Code;
|
||||
public List<StkId> K;
|
||||
public List<LuaProto> P;
|
||||
public List<UpvalDesc> Upvalues;
|
||||
|
||||
public int LineDefined;
|
||||
public int LastLineDefined;
|
||||
|
||||
public int NumParams;
|
||||
public bool IsVarArg;
|
||||
public byte MaxStackSize;
|
||||
|
||||
public string Source;
|
||||
public List<int> LineInfo;
|
||||
public List<LocVar> LocVars;
|
||||
|
||||
public LuaProto()
|
||||
{
|
||||
Code = new List<Instruction>();
|
||||
K = new List<StkId>();
|
||||
P = new List<LuaProto>();
|
||||
Upvalues = new List<UpvalDesc>();
|
||||
LineInfo = new List<int>();
|
||||
LocVars = new List<LocVar>();
|
||||
}
|
||||
|
||||
public int GetFuncLine( int pc )
|
||||
{
|
||||
return (0 <= pc && pc < LineInfo.Count) ? LineInfo[pc] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class LuaUpvalue
|
||||
{
|
||||
public StkId V;
|
||||
public StkId Value;
|
||||
|
||||
public LuaUpvalue()
|
||||
{
|
||||
Value = new StkId();
|
||||
Value.V.SetNilValue();
|
||||
|
||||
V = Value;
|
||||
}
|
||||
}
|
||||
|
||||
public class LuaCsClosureValue
|
||||
{
|
||||
public CSharpFunctionDelegate F;
|
||||
public StkId[] Upvals;
|
||||
|
||||
public LuaCsClosureValue( CSharpFunctionDelegate f )
|
||||
{
|
||||
F = f;
|
||||
}
|
||||
|
||||
public LuaCsClosureValue( CSharpFunctionDelegate f, int numUpvalues )
|
||||
{
|
||||
F = f;
|
||||
Upvals = new StkId[numUpvalues];
|
||||
for(int i=0; i<numUpvalues; ++i) {
|
||||
var newItem = new StkId();
|
||||
Upvals[i] = newItem;
|
||||
newItem.SetList(Upvals);
|
||||
newItem.SetIndex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class LuaState
|
||||
{
|
||||
internal static StkId TheNilValue;
|
||||
|
||||
public static bool O_Str2Decimal( string s, out double result )
|
||||
{
|
||||
result = 0.0;
|
||||
|
||||
if( s.Contains("n") || s.Contains("N") ) // reject `inf' and `nan'
|
||||
return false;
|
||||
|
||||
int pos = 0;
|
||||
if( s.Contains("x") || s.Contains("X") )
|
||||
result = Utl.StrX2Number( s, ref pos );
|
||||
else
|
||||
result = Utl.Str2Number( s, ref pos );
|
||||
|
||||
if( pos == 0 )
|
||||
return false; // nothing recognized
|
||||
|
||||
while( pos < s.Length && Char.IsWhiteSpace( s[pos] ) ) ++pos;
|
||||
return pos == s.Length; // OK if no trailing characters
|
||||
}
|
||||
|
||||
private static double O_Arith( LuaOp op, double v1, double v2 )
|
||||
{
|
||||
switch( op )
|
||||
{
|
||||
case LuaOp.LUA_OPADD: return v1+v2;
|
||||
case LuaOp.LUA_OPSUB: return v1-v2;
|
||||
case LuaOp.LUA_OPMUL: return v1*v2;
|
||||
case LuaOp.LUA_OPDIV: return v1/v2;
|
||||
case LuaOp.LUA_OPMOD: return v1 - Math.Floor(v1/v2)*v2;
|
||||
case LuaOp.LUA_OPPOW: return Math.Pow(v1, v2);
|
||||
case LuaOp.LUA_OPUNM: return -v1;
|
||||
default: throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsFalse(ref TValue v)
|
||||
{
|
||||
if( v.TtIsNil() )
|
||||
return true;
|
||||
|
||||
if((v.TtIsBoolean() && v.BValue() == false))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ToString(ref TValue o)
|
||||
{
|
||||
if(o.TtIsString()) { return true; }
|
||||
return V_ToString(ref o);
|
||||
}
|
||||
|
||||
internal LuaLClosureValue GetCurrentLuaFunc(CallInfo ci)
|
||||
{
|
||||
if(ci.IsLua) {
|
||||
return Stack[ci.FuncIndex].V.ClLValue();
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
internal int GetCurrentLine(CallInfo ci)
|
||||
{
|
||||
Utl.Assert(ci.IsLua);
|
||||
var cl = Stack[ci.FuncIndex].V.ClLValue();
|
||||
return cl.Proto.GetFuncLine(ci.CurrentPc);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
32
UniLua/LuaOsLib.cs
Normal file
32
UniLua/LuaOsLib.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using System.Diagnostics;
|
||||
|
||||
internal class LuaOSLib
|
||||
{
|
||||
public const string LIB_NAME = "os";
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
#if !UNITY_WEBPLAYER
|
||||
new NameFuncPair("clock", OS_Clock),
|
||||
#endif
|
||||
};
|
||||
|
||||
lua.L_NewLib( define );
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if !UNITY_WEBPLAYER
|
||||
private static int OS_Clock( ILuaState lua )
|
||||
{
|
||||
lua.PushNumber(0); //TO PLUG
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
359
UniLua/LuaPkgLib.cs
Normal file
359
UniLua/LuaPkgLib.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
|
||||
// TODO
|
||||
|
||||
#define LUA_COMPAT_LOADERS
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using Environment = System.Environment;
|
||||
using StringBuilder = System.Text.StringBuilder;
|
||||
using String = System.String;
|
||||
using File = System.IO.File;
|
||||
using FileMode = System.IO.FileMode;
|
||||
using Exception = System.Exception;
|
||||
|
||||
internal class LuaPkgLib
|
||||
{
|
||||
public const string LIB_NAME = "package";
|
||||
|
||||
private const string CLIBS = "_CLIBS";
|
||||
|
||||
private const string LUA_PATH = "LUA_PATH";
|
||||
private const string LUA_CPATH = "LUA_CPATH";
|
||||
private const string LUA_PATHSUFFIX = "_" + LuaDef.LUA_VERSION_MAJOR +
|
||||
"_" + LuaDef.LUA_VERSION_MINOR;
|
||||
private const string LUA_PATHVERSION = LUA_PATH + LUA_PATHSUFFIX;
|
||||
private const string LUA_CPATHVERSION = LUA_CPATH + LUA_PATHSUFFIX;
|
||||
|
||||
// private const string LUA_LDIR = "!\\lua\\";
|
||||
// private const string LUA_CDIR = "!\\";
|
||||
// private const string LUA_PATH_DEFAULT = LUA_LDIR + "?.lua;" +
|
||||
// LUA_LDIR + "?\\init.lua;" +
|
||||
// LUA_CDIR + "?.lua;" +
|
||||
// LUA_CDIR + "?\\init.lua;" +
|
||||
// ".\\?.lua";
|
||||
// private const string LUA_CPATH_DEFAULT = LUA_CDIR + "?.dll;" +
|
||||
// LUA_CDIR + "loadall.dll;" +
|
||||
// ".\\?.dll";
|
||||
|
||||
private const string LUA_PATH_DEFAULT = "?.lua;";
|
||||
private const string LUA_CPATH_DEFAULT = "?.dll;loadall.dll;";
|
||||
|
||||
private const string LUA_PATH_SEP = ";";
|
||||
private const string LUA_PATH_MARK = "?";
|
||||
private const string LUA_EXEC_DIR = "!";
|
||||
private const string LUA_IGMARK = "-";
|
||||
|
||||
private static readonly string LUA_LSUBSEP = LuaConf.LUA_DIRSEP;
|
||||
|
||||
// auxiliary mark (for internal use)
|
||||
private const string AUXMARK = "\u0001";
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
// NameFuncPair[] define = new NameFuncPair[]
|
||||
// {
|
||||
// new NameFuncPair( "module", PKG_Module ),
|
||||
// };
|
||||
|
||||
// lua.L_NewLib( define );
|
||||
|
||||
// // create table CLIBS to keep track of loaded C libraries
|
||||
// lua.L_GetSubTable( LuaDef.LUA_REGISTRYINDEX, CLIBS );
|
||||
// lua.CreateTable( 0, 1 ); // metatable for CLIBS
|
||||
// lua.PushCSharpFunction
|
||||
|
||||
// create `package' table
|
||||
NameFuncPair[] pkg_define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "loadlib", PKG_LoadLib ),
|
||||
new NameFuncPair( "searchpath", PKG_SearchPath ),
|
||||
new NameFuncPair( "seeall", PKG_SeeAll ),
|
||||
};
|
||||
lua.L_NewLib( pkg_define );
|
||||
|
||||
CreateSearchersTable( lua );
|
||||
#if LUA_COMPAT_LOADERS
|
||||
lua.PushValue( -1 ); // make a copy of `searchers' table
|
||||
lua.SetField( -3, "loaders" ); // put it in field `loaders'
|
||||
#endif
|
||||
lua.SetField( -2, "searchers" ); // put it in field `searchers'
|
||||
|
||||
// set field `path'
|
||||
SetPath( lua, "path", LUA_PATHVERSION, LUA_PATH, LUA_PATH_DEFAULT );
|
||||
// set field `cpath'
|
||||
SetPath( lua, "cpath", LUA_CPATHVERSION, LUA_CPATH, LUA_CPATH_DEFAULT );
|
||||
|
||||
// store config information
|
||||
lua.PushString( string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n",
|
||||
LuaConf.LUA_DIRSEP,
|
||||
LUA_PATH_SEP,
|
||||
LUA_PATH_MARK,
|
||||
LUA_EXEC_DIR,
|
||||
LUA_IGMARK ) );
|
||||
lua.SetField( -2, "config" );
|
||||
|
||||
// set field `loaded'
|
||||
lua.L_GetSubTable( LuaDef.LUA_REGISTRYINDEX, "_LOADED" );
|
||||
lua.SetField( -2, "loaded" );
|
||||
|
||||
// set field `preload'
|
||||
lua.L_GetSubTable( LuaDef.LUA_REGISTRYINDEX, "_PRELOAD" );
|
||||
lua.SetField( -2, "preload" );
|
||||
|
||||
lua.PushGlobalTable();
|
||||
lua.PushValue( -2 ); // set `package' as upvalue for next lib
|
||||
|
||||
NameFuncPair[] loadLibFuncs = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "module", LL_Module ),
|
||||
new NameFuncPair( "require", LL_Require ),
|
||||
};
|
||||
lua.L_SetFuncs( loadLibFuncs, 1 ); // open lib into global table
|
||||
lua.Pop( 1 ); // pop global table
|
||||
|
||||
return 1; // return `package' table
|
||||
}
|
||||
|
||||
private static void CreateSearchersTable( ILuaState lua )
|
||||
{
|
||||
CSharpFunctionDelegate[] searchers = new CSharpFunctionDelegate[]
|
||||
{
|
||||
SearcherPreload,
|
||||
SearcherLua,
|
||||
};
|
||||
lua.CreateTable( searchers.Length, 0 );
|
||||
for( int i=0; i<searchers.Length; ++i )
|
||||
{
|
||||
lua.PushValue( -2 ); // set `package' as upvalue for all searchers
|
||||
lua.PushCSharpClosure( searchers[i], 1 );
|
||||
lua.RawSetI( -2, i+1 );
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetPath
|
||||
( ILuaState lua
|
||||
, string fieldName
|
||||
, string envName1
|
||||
, string envName2
|
||||
, string def
|
||||
)
|
||||
{
|
||||
lua.PushString(def);
|
||||
/*
|
||||
string path = Environment.GetEnvironmentVariable( envName1 );
|
||||
if( path == null ) // no environment variable?
|
||||
path = Environment.GetEnvironmentVariable( envName2 ); // try alternative name
|
||||
if( path == null || noEnv( lua ) ) // no environment variable?
|
||||
lua.PushString( def );
|
||||
else
|
||||
{
|
||||
// replace ";;" by ";AUXMARK;" and then AUXMARK by default path
|
||||
lua.L_Gsub( path, LUA_PATH_SEP + LUA_PATH_SEP,
|
||||
LUA_PATH_SEP + AUXMARK + LUA_PATH_SEP );
|
||||
lua.L_Gsub( path, AUXMARK, def );
|
||||
lua.Remove( -2 );
|
||||
}*/
|
||||
SetProgDir( lua );
|
||||
lua.SetField( -2, fieldName );
|
||||
}
|
||||
|
||||
private static bool noEnv( ILuaState lua )
|
||||
{
|
||||
lua.GetField( LuaDef.LUA_REGISTRYINDEX, "LUA_NOENV" );
|
||||
bool res = lua.ToBoolean( -1 );
|
||||
lua.Pop( 1 );
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void SetProgDir( ILuaState lua )
|
||||
{
|
||||
// TODO: unity ?
|
||||
//
|
||||
// , build crash
|
||||
// var pgs = System.Diagnostics.Process.GetCurrentProcess();
|
||||
// Console.WriteLine( pgs.MainModule.FileName );
|
||||
}
|
||||
|
||||
private static int SearcherPreload( ILuaState lua )
|
||||
{
|
||||
string name = lua.L_CheckString( 1 );
|
||||
lua.GetField( LuaDef.LUA_REGISTRYINDEX, "_PRELOAD" );
|
||||
lua.GetField( -1, name );
|
||||
if( lua.IsNil(-1) ) // not found?
|
||||
lua.PushString( string.Format(
|
||||
"\n\tno field package.preload['{0}']", name ) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static bool Readable( string filename )
|
||||
{
|
||||
return LuaFile.Readable( filename );
|
||||
}
|
||||
|
||||
private static bool PushNextTemplate( ILuaState lua,
|
||||
string path, ref int pos )
|
||||
{
|
||||
while( pos < path.Length && path[pos] == LUA_PATH_SEP[0])
|
||||
pos++; // skip separators
|
||||
if( pos >= path.Length )
|
||||
return false;
|
||||
int end = pos+1;
|
||||
while( end < path.Length && path[end] != LUA_PATH_SEP[0])
|
||||
end++;
|
||||
|
||||
var template = path.Substring( pos, end-pos);
|
||||
lua.PushString( template );
|
||||
|
||||
pos = end;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string SearchPath( ILuaState lua,
|
||||
string name, string path, string sep, string dirsep )
|
||||
{
|
||||
var sb = new StringBuilder(); // to build error message
|
||||
if( !String.IsNullOrEmpty(sep) ) // non-empty separator?
|
||||
name = name.Replace( sep, dirsep ); // replace it by `dirsep'
|
||||
int pos = 0;
|
||||
while(PushNextTemplate(lua, path, ref pos))
|
||||
{
|
||||
var template = lua.ToString(-1);
|
||||
string filename = template.Replace( LUA_PATH_MARK, name );
|
||||
lua.Remove( -1 ); // remove path template
|
||||
if( Readable( filename ) ) // does file exist and is readable?
|
||||
return filename; // return that file name
|
||||
lua.PushString( string.Format( "\n\tno file '{0}'", filename) );
|
||||
lua.Remove( -2 ); // remove file name
|
||||
sb.Append( lua.ToString(-1) ); // concatenate error msg. entry
|
||||
}
|
||||
lua.PushString( sb.ToString() ); // create error message
|
||||
return null; // not found
|
||||
}
|
||||
|
||||
private static string FindFile( ILuaState lua,
|
||||
string name, string pname, string dirsep )
|
||||
{
|
||||
lua.GetField( lua.UpvalueIndex(1), pname );
|
||||
string path = lua.ToString( -1 );
|
||||
if( path == null )
|
||||
lua.L_Error( "'package.{0}' must be a string", pname );
|
||||
return SearchPath( lua, name, path, ".", dirsep );
|
||||
}
|
||||
|
||||
private static int CheckLoad( ILuaState lua,
|
||||
bool stat, string filename )
|
||||
{
|
||||
if( stat ) // module loaded successfully?
|
||||
{
|
||||
lua.PushString( filename ); // will be 2nd arg to module
|
||||
return 2; // return open function and file name
|
||||
}
|
||||
else return lua.L_Error(
|
||||
"error loading module '{0}' from file '{1}':\n\t{2}",
|
||||
lua.ToString(1), filename, lua.ToString(-1) );
|
||||
}
|
||||
|
||||
private static int SearcherLua( ILuaState lua )
|
||||
{
|
||||
string name = lua.L_CheckString( 1 );
|
||||
string filename = FindFile( lua, name, "path", LUA_LSUBSEP );
|
||||
if( filename == null )
|
||||
return 1;
|
||||
return CheckLoad( lua,
|
||||
(lua.L_LoadFile(filename) == ThreadStatus.LUA_OK),
|
||||
filename );
|
||||
}
|
||||
|
||||
private static int LL_Module( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void FindLoader( ILuaState lua, string name )
|
||||
{
|
||||
// will be at index 3
|
||||
lua.GetField( lua.UpvalueIndex(1), "searchers" );
|
||||
if( ! lua.IsTable(3) )
|
||||
lua.L_Error("'package.searchers' must be a table");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
// iterator over available searchers to find a loader
|
||||
for( int i=1; ; ++i )
|
||||
{
|
||||
lua.RawGetI( 3, i ); // get a searcher
|
||||
if( lua.IsNil( -1 ) ) // no more searchers?
|
||||
{
|
||||
lua.Pop( 1 ); // remove nil
|
||||
lua.PushString( sb.ToString() );
|
||||
lua.L_Error( "module '{0}' not found:{1}",
|
||||
name, lua.ToString(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
lua.PushString( name );
|
||||
lua.Call( 1, 2 ); // call it
|
||||
if( lua.IsFunction(-2) ) // did it find a loader
|
||||
return; // module loader found
|
||||
else if( lua.IsString(-2) ) // searcher returned error message?
|
||||
{
|
||||
lua.Pop( 1 ); // return extra return
|
||||
sb.Append( lua.ToString(-1) );
|
||||
}
|
||||
else
|
||||
lua.Pop( 2 ); // remove both returns
|
||||
}
|
||||
}
|
||||
|
||||
private static int LL_Require( ILuaState lua )
|
||||
{
|
||||
string name = lua.L_CheckString( 1 );
|
||||
lua.SetTop( 1 );
|
||||
// _LOADED table will be at index 2
|
||||
lua.GetField( LuaDef.LUA_REGISTRYINDEX, "_LOADED" );
|
||||
// _LOADED[name]
|
||||
lua.GetField( 2, name );
|
||||
// is it there?
|
||||
if( lua.ToBoolean( -1 ) )
|
||||
return 1; // package is already loaded
|
||||
// else must load package
|
||||
// remove `GetField' result
|
||||
lua.Pop( 1 );
|
||||
FindLoader( lua, name );
|
||||
lua.PushString( name ); // pass name as arg to module loader
|
||||
lua.Insert( -2 ); // name is 1st arg (before search data)
|
||||
lua.Call( 2, 1 ); // run loader to load module
|
||||
if( !lua.IsNil( -1 ) ) // non-nil return?
|
||||
lua.SetField( 2, name ); // _LOADED[name] = returned value
|
||||
lua.GetField( 2, name );
|
||||
if( lua.IsNil( -1 ) ) // module did not set a value?
|
||||
{
|
||||
lua.PushBoolean( true ); // use true as result
|
||||
lua.PushValue( -1 ); // extra copy to be returned
|
||||
lua.SetField( 2, name ); // _LOADED[name] = true
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int PKG_LoadLib( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int PKG_SearchPath( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int PKG_SeeAll( ILuaState lua )
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
306
UniLua/LuaState.cs
Normal file
306
UniLua/LuaState.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
|
||||
// #define ENABLE_DUMP_STACK
|
||||
|
||||
// #define DEBUG_RECORD_INS
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using InstructionPtr = Pointer<Instruction>;
|
||||
using ULDebug = UniLua.Tools.ULDebug;
|
||||
|
||||
public struct Pointer<T>
|
||||
{
|
||||
private List<T> List;
|
||||
public int Index { get; set; }
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return List[Index];
|
||||
}
|
||||
set
|
||||
{
|
||||
List[Index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public T ValueInc
|
||||
{
|
||||
get
|
||||
{
|
||||
return List[Index++];
|
||||
}
|
||||
set
|
||||
{
|
||||
List[Index++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Pointer( List<T> list, int index ) : this()
|
||||
{
|
||||
List = list;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public Pointer( Pointer<T> other ) : this()
|
||||
{
|
||||
List = other.List;
|
||||
Index = other.Index;
|
||||
}
|
||||
|
||||
public static Pointer<T> operator +( Pointer<T> lhs, int rhs )
|
||||
{
|
||||
return new Pointer<T>( lhs.List, lhs.Index + rhs );
|
||||
}
|
||||
|
||||
public static Pointer<T> operator -( Pointer<T> lhs, int rhs )
|
||||
{
|
||||
return new Pointer<T>( lhs.List, lhs.Index - rhs );
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallStatus
|
||||
{
|
||||
CIST_NONE = 0,
|
||||
|
||||
CIST_LUA = (1<<0), /* call is running a Lua function */
|
||||
CIST_HOOKED = (1<<1), /* call is running a debug hook */
|
||||
CIST_REENTRY = (1<<2), /* call is running on same invocation of
|
||||
luaV_execute of previous call */
|
||||
CIST_YIELDED = (1<<3), /* call reentered after suspension */
|
||||
CIST_YPCALL = (1<<4), /* call is a yieldable protected call */
|
||||
CIST_STAT = (1<<5), /* call has an error status (pcall) */
|
||||
CIST_TAIL = (1<<6), /* call was tail called */
|
||||
}
|
||||
|
||||
public class CallInfo
|
||||
{
|
||||
public CallInfo[] List;
|
||||
public int Index;
|
||||
|
||||
public int FuncIndex;
|
||||
public int TopIndex;
|
||||
|
||||
public int NumResults;
|
||||
public CallStatus CallStatus;
|
||||
|
||||
public CSharpFunctionDelegate ContinueFunc;
|
||||
public int Context;
|
||||
public int ExtraIndex;
|
||||
public bool OldAllowHook;
|
||||
public int OldErrFunc;
|
||||
public ThreadStatus Status;
|
||||
|
||||
// for Lua functions
|
||||
public int BaseIndex;
|
||||
public InstructionPtr SavedPc;
|
||||
|
||||
public bool IsLua
|
||||
{
|
||||
get { return (CallStatus & CallStatus.CIST_LUA) != 0; }
|
||||
}
|
||||
|
||||
public int CurrentPc
|
||||
{
|
||||
get
|
||||
{
|
||||
Utl.Assert( IsLua );
|
||||
return SavedPc.Index - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GlobalState
|
||||
{
|
||||
public StkId Registry;
|
||||
public LuaUpvalue UpvalHead;
|
||||
public LuaTable[] MetaTables;
|
||||
public LuaState MainThread;
|
||||
|
||||
public GlobalState( LuaState state )
|
||||
{
|
||||
MainThread = state;
|
||||
Registry = new StkId();
|
||||
UpvalHead = new LuaUpvalue();
|
||||
MetaTables = new LuaTable[(int)LuaType.LUA_NUMTAGS];
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void LuaHookDelegate(ILuaState lua, LuaDebug ar);
|
||||
|
||||
public partial class LuaState
|
||||
{
|
||||
public StkId[] Stack;
|
||||
public StkId Top;
|
||||
public int StackSize;
|
||||
public int StackLast;
|
||||
public CallInfo CI;
|
||||
public CallInfo[] BaseCI;
|
||||
public GlobalState G;
|
||||
public int NumNonYieldable;
|
||||
public int NumCSharpCalls;
|
||||
public int ErrFunc;
|
||||
public ThreadStatus Status { get; set; }
|
||||
public bool AllowHook;
|
||||
public byte HookMask;
|
||||
public int BaseHookCount;
|
||||
public int HookCount;
|
||||
public LuaHookDelegate Hook;
|
||||
|
||||
public LinkedList<LuaUpvalue> OpenUpval;
|
||||
|
||||
#if DEBUG_RECORD_INS
|
||||
private Queue<Instruction> InstructionHistory;
|
||||
#endif
|
||||
|
||||
private ILuaAPI API;
|
||||
|
||||
static LuaState()
|
||||
{
|
||||
TheNilValue = new StkId();
|
||||
TheNilValue.V.SetNilValue();
|
||||
}
|
||||
|
||||
public LuaState( GlobalState g=null )
|
||||
{
|
||||
API = (ILuaAPI)this;
|
||||
|
||||
NumNonYieldable = 1;
|
||||
NumCSharpCalls = 0;
|
||||
Hook = null;
|
||||
HookMask = 0;
|
||||
BaseHookCount = 0;
|
||||
AllowHook = true;
|
||||
ResetHookCount();
|
||||
Status = ThreadStatus.LUA_OK;
|
||||
|
||||
if( g == null )
|
||||
{
|
||||
G = new GlobalState(this);
|
||||
InitRegistry();
|
||||
}
|
||||
else
|
||||
{
|
||||
G = g;
|
||||
}
|
||||
OpenUpval = new LinkedList<LuaUpvalue>();
|
||||
ErrFunc = 0;
|
||||
|
||||
#if DEBUG_RECORD_INS
|
||||
InstructionHistory = new Queue<Instruction>();
|
||||
#endif
|
||||
|
||||
InitStack();
|
||||
}
|
||||
|
||||
private void IncrTop()
|
||||
{
|
||||
StkId.inc(ref Top);
|
||||
D_CheckStack(0);
|
||||
}
|
||||
|
||||
private StkId RestoreStack( int index )
|
||||
{
|
||||
return Stack[index];
|
||||
}
|
||||
|
||||
private void ApiIncrTop()
|
||||
{
|
||||
StkId.inc(ref Top);
|
||||
// Console.WriteLine( "[ApiIncrTop] ==== Top.Index:" + Top.Index );
|
||||
// Console.WriteLine( "[ApiIncrTop] ==== CI.Top.Index:" + CI.Top.Index );
|
||||
Utl.ApiCheck( Top.Index <= CI.TopIndex, "stack overflow" );
|
||||
}
|
||||
|
||||
private void InitStack()
|
||||
{
|
||||
Stack = new StkId[LuaDef.BASIC_STACK_SIZE];
|
||||
StackSize = LuaDef.BASIC_STACK_SIZE;
|
||||
StackLast = LuaDef.BASIC_STACK_SIZE - LuaDef.EXTRA_STACK;
|
||||
for(int i=0; i<LuaDef.BASIC_STACK_SIZE; ++i) {
|
||||
var newItem = new StkId();
|
||||
Stack[i] = newItem;
|
||||
newItem.SetList(Stack);
|
||||
newItem.SetIndex(i);
|
||||
newItem.V.SetNilValue();
|
||||
}
|
||||
Top = Stack[0];
|
||||
|
||||
BaseCI = new CallInfo[LuaDef.BASE_CI_SIZE];
|
||||
for(int i=0; i<LuaDef.BASE_CI_SIZE; ++i) {
|
||||
var newCI = new CallInfo();
|
||||
BaseCI[i] = newCI;
|
||||
newCI.List = BaseCI;
|
||||
newCI.Index = i;
|
||||
}
|
||||
CI = BaseCI[0];
|
||||
CI.FuncIndex = Top.Index;
|
||||
StkId.inc(ref Top).V.SetNilValue(); // `function' entry for this `ci'
|
||||
CI.TopIndex = Top.Index + LuaDef.LUA_MINSTACK;
|
||||
}
|
||||
|
||||
private void InitRegistry()
|
||||
{
|
||||
var mt = new TValue();
|
||||
|
||||
G.Registry.V.SetHValue(new LuaTable(this));
|
||||
|
||||
mt.SetThValue(this);
|
||||
G.Registry.V.HValue().SetInt(LuaDef.LUA_RIDX_MAINTHREAD, ref mt);
|
||||
|
||||
mt.SetHValue(new LuaTable(this));
|
||||
G.Registry.V.HValue().SetInt(LuaDef.LUA_RIDX_GLOBALS, ref mt);
|
||||
}
|
||||
|
||||
private string DumpStackToString( int baseIndex, string tag="" )
|
||||
{
|
||||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||
sb.Append( string.Format( "===================================================================== DumpStack: {0}", tag) ).Append("\n");
|
||||
sb.Append( string.Format( "== BaseIndex: {0}", baseIndex) ).Append("\n");
|
||||
sb.Append( string.Format( "== Top.Index: {0}", Top.Index) ).Append("\n");
|
||||
sb.Append( string.Format( "== CI.Index: {0}", CI.Index) ).Append("\n");
|
||||
sb.Append( string.Format( "== CI.TopIndex: {0}", CI.TopIndex) ).Append("\n");
|
||||
sb.Append( string.Format( "== CI.Func.Index: {0}", CI.FuncIndex) ).Append("\n");
|
||||
for( int i=0; i<Stack.Length || i <= Top.Index; ++i )
|
||||
{
|
||||
bool isTop = Top.Index == i;
|
||||
bool isBase = baseIndex == i;
|
||||
bool inStack = i < Stack.Length;
|
||||
|
||||
string postfix = ( isTop || isBase )
|
||||
? string.Format( "<--------------------- {0}{1}"
|
||||
, isBase ? "[BASE]" : ""
|
||||
, isTop ? "[TOP]" : ""
|
||||
)
|
||||
: "";
|
||||
string body = string.Format("======== {0}/{1} > {2} {3}"
|
||||
, i-baseIndex
|
||||
, i
|
||||
, inStack ? Stack[i].ToString() : ""
|
||||
, postfix
|
||||
);
|
||||
|
||||
sb.Append( body ).Append("\n");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void DumpStack( int baseIndex, string tag="" )
|
||||
{
|
||||
#if ENABLE_DUMP_STACK
|
||||
Console.WriteLine(DumpStackToString(baseIndex, tag));
|
||||
#endif
|
||||
}
|
||||
|
||||
private void ResetHookCount()
|
||||
{
|
||||
HookCount = BaseHookCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
937
UniLua/LuaStrLib.cs
Normal file
937
UniLua/LuaStrLib.cs
Normal file
@@ -0,0 +1,937 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using StringBuilder = System.Text.StringBuilder;
|
||||
using Char = System.Char;
|
||||
using Convert = System.Convert;
|
||||
|
||||
internal static class LuaStrLib
|
||||
{
|
||||
public const string LIB_NAME = "string";
|
||||
|
||||
private const int CAP_UNFINISHED = -1;
|
||||
private const int CAP_POSITION = -2;
|
||||
private const int LUA_MAXCAPTURES = 32;
|
||||
private const char L_ESC = '%';
|
||||
private const string FLAGS = "-+ #0";
|
||||
private static readonly char[] SPECIALS;
|
||||
|
||||
static LuaStrLib()
|
||||
{
|
||||
SPECIALS = "^$*+?.([%-".ToCharArray();
|
||||
}
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "byte", Str_Byte ),
|
||||
new NameFuncPair( "char", Str_Char ),
|
||||
//new NameFuncPair( "dump", Str_Dump ),
|
||||
new NameFuncPair( "find", Str_Find ),
|
||||
new NameFuncPair( "format", Str_Format ),
|
||||
new NameFuncPair( "gmatch", Str_Gmatch ),
|
||||
new NameFuncPair( "gsub", Str_Gsub ),
|
||||
new NameFuncPair( "len", Str_Len ),
|
||||
new NameFuncPair( "lower", Str_Lower ),
|
||||
new NameFuncPair( "match", Str_Match ),
|
||||
new NameFuncPair( "rep", Str_Rep ),
|
||||
new NameFuncPair( "reverse", Str_Reverse ),
|
||||
new NameFuncPair( "sub", Str_Sub ),
|
||||
new NameFuncPair( "upper", Str_Upper ),
|
||||
};
|
||||
|
||||
lua.L_NewLib( define );
|
||||
CreateMetaTable( lua );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static void CreateMetaTable( ILuaState lua )
|
||||
{
|
||||
lua.CreateTable(0, 1); // table to be metatable for strings
|
||||
lua.PushString( "" ); // dummy string
|
||||
lua.PushValue( -2 ); // copy table
|
||||
lua.SetMetaTable( -2 ); // set table as metatable for strings
|
||||
lua.Pop( 1 );
|
||||
lua.PushValue( -2 ); // get string library
|
||||
lua.SetField( -2, "__index" ); // metatable.__index = string
|
||||
lua.Pop( 1 ); // pop metatable
|
||||
}
|
||||
|
||||
private static int PosRelative( int pos, int len )
|
||||
{
|
||||
if( pos >= 0 ) return pos;
|
||||
else if( 0 - pos > len ) return 0;
|
||||
else return len - (-pos) + 1;
|
||||
}
|
||||
|
||||
private static int Str_Byte( ILuaState lua )
|
||||
{
|
||||
string s = lua.L_CheckString(1);
|
||||
int posi = PosRelative( lua.L_OptInt(2, 1), s.Length );
|
||||
int pose = PosRelative( lua.L_OptInt(3, posi), s.Length );
|
||||
if( posi < 1 ) posi = 1;
|
||||
if( pose > s.Length ) pose = s.Length;
|
||||
if( posi > pose ) return 0; // empty interval; return no values
|
||||
int n = pose - posi + 1;
|
||||
if( posi + n <= pose) // overflow?
|
||||
return lua.L_Error( "string slice too long" );
|
||||
lua.L_CheckStack(n, "string slice too long");
|
||||
for( int i=0; i<n; ++i )
|
||||
lua.PushInteger( (byte)s[(int)posi+i-1] );
|
||||
return n;
|
||||
}
|
||||
|
||||
private static int Str_Char( ILuaState lua )
|
||||
{
|
||||
int n = lua.GetTop();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for( int i=1; i<=n; ++i )
|
||||
{
|
||||
int c = lua.L_CheckInteger(i);
|
||||
lua.L_ArgCheck( (char)c == c, i, "value out of range" );
|
||||
sb.Append( (char)c );
|
||||
}
|
||||
lua.PushString( sb.ToString() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Str_Dump( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckType( 1, LuaType.LUA_TFUNCTION );
|
||||
lua.SetTop( 1 );
|
||||
var bsb = new ByteStringBuilder();
|
||||
LuaWriter writeFunc =
|
||||
delegate(byte[] bytes, int start, int length)
|
||||
{
|
||||
bsb.Append(bytes, start, length);
|
||||
return DumpStatus.OK;
|
||||
};
|
||||
if( lua.Dump( writeFunc ) != DumpStatus.OK )
|
||||
return lua.L_Error( "unable to dump given function" );
|
||||
lua.PushString( bsb.ToString() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
class CaptureInfo
|
||||
{
|
||||
public int Len;
|
||||
public int Init;
|
||||
}
|
||||
|
||||
class MatchState
|
||||
{
|
||||
public ILuaState Lua;
|
||||
public int Level;
|
||||
public string Src;
|
||||
public int SrcInit;
|
||||
public int SrcEnd;
|
||||
public string Pattern;
|
||||
public int PatternEnd;
|
||||
public CaptureInfo[] Capture;
|
||||
|
||||
public MatchState()
|
||||
{
|
||||
Capture = new CaptureInfo[LUA_MAXCAPTURES];
|
||||
for(int i =0; i < LUA_MAXCAPTURES; ++i)
|
||||
Capture[i] = new CaptureInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private static int ClassEnd( MatchState ms, int p )
|
||||
{
|
||||
var lua = ms.Lua;
|
||||
switch( ms.Pattern[p++] )
|
||||
{
|
||||
case L_ESC:
|
||||
{
|
||||
if( p == ms.PatternEnd )
|
||||
lua.L_Error( "malformed pattern (ends with '%')" );
|
||||
return p+1;
|
||||
}
|
||||
case '[':
|
||||
{
|
||||
if( ms.Pattern[p] == '^' ) p++;
|
||||
do {
|
||||
if( p == ms.PatternEnd )
|
||||
lua.L_Error( "malformed pattern (missing ']')" );
|
||||
if( ms.Pattern[p++] == L_ESC && p < ms.PatternEnd )
|
||||
p++; // skip escapes (e.g. `%]')
|
||||
} while( ms.Pattern[p] != ']' );
|
||||
return p+1;
|
||||
}
|
||||
default: return p;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsXDigit( char c )
|
||||
{
|
||||
switch(c) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool MatchClass( char c, char cl )
|
||||
{
|
||||
bool res;
|
||||
switch(cl)
|
||||
{
|
||||
case 'a': res = Char.IsLetter(c); break;
|
||||
case 'c': res = Char.IsControl(c); break;
|
||||
case 'd': res = Char.IsDigit(c); break;
|
||||
case 'g': throw new System.NotImplementedException();
|
||||
case 'l': res = Char.IsLower(c); break;
|
||||
case 'p': res = Char.IsPunctuation(c); break;
|
||||
case 's': res = Char.IsWhiteSpace(c); break;
|
||||
case 'u': res = Char.IsUpper(c); break;
|
||||
case 'w': res = Char.IsLetterOrDigit(c); break;
|
||||
case 'x': res = IsXDigit(c); break;
|
||||
case 'z': res = (c == '\0'); break; /* deprecated option */
|
||||
default: return (cl == c);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static bool MatchBreaketClass( MatchState ms, char c, int p, int ec )
|
||||
{
|
||||
bool sig = true;
|
||||
if( ms.Pattern[p+1] == '^' )
|
||||
{
|
||||
sig = false;
|
||||
p++; // skip the `^'
|
||||
}
|
||||
while( ++p < ec )
|
||||
{
|
||||
if( ms.Pattern[p] == L_ESC )
|
||||
{
|
||||
p++;
|
||||
if( MatchClass( c, ms.Pattern[p] ) )
|
||||
return sig;
|
||||
}
|
||||
else if( ms.Pattern[p+1] == '-' && (p+2 < ec) )
|
||||
{
|
||||
p += 2;
|
||||
if( ms.Pattern[p-2] <= c && c <= ms.Pattern[p] )
|
||||
return sig;
|
||||
}
|
||||
else if( ms.Pattern[p] == c ) return sig;
|
||||
}
|
||||
return !sig;
|
||||
}
|
||||
|
||||
private static bool SingleMatch( MatchState ms, char c, int p, int ep )
|
||||
{
|
||||
switch( ms.Pattern[p] )
|
||||
{
|
||||
case '.': return true; // matches any char
|
||||
case L_ESC: return MatchClass( c, ms.Pattern[p+1] );
|
||||
case '[': return MatchBreaketClass( ms, c, p, ep-1 );
|
||||
default: return ms.Pattern[p] == c;
|
||||
}
|
||||
}
|
||||
|
||||
private static int MatchBalance( MatchState ms, int s, int p )
|
||||
{
|
||||
var lua = ms.Lua;
|
||||
if( p >= ms.PatternEnd - 1 )
|
||||
lua.L_Error( "malformed pattern (missing arguments to '%b')" );
|
||||
if( ms.Src[s] != ms.Pattern[p] ) return -1;
|
||||
else
|
||||
{
|
||||
char b = ms.Pattern[p];
|
||||
char e = ms.Pattern[p+1];
|
||||
int count = 1;
|
||||
while( ++s < ms.SrcEnd )
|
||||
{
|
||||
if( ms.Src[s] == e )
|
||||
{
|
||||
if( --count == 0 ) return s+1;
|
||||
}
|
||||
else if( ms.Src[s] == b ) count++;
|
||||
}
|
||||
}
|
||||
return -1; //string ends out of balance
|
||||
}
|
||||
|
||||
private static int MaxExpand( MatchState ms, int s, int p, int ep )
|
||||
{
|
||||
int i = 0; // counts maximum expand for item
|
||||
while( (s+i) < ms.SrcEnd && SingleMatch( ms, ms.Src[s+i], p, ep ) )
|
||||
i++;
|
||||
// keeps trying to match with the maximum repetitions
|
||||
while( i >= 0 )
|
||||
{
|
||||
int res = Match( ms, (s+i), (ep+1) );
|
||||
if( res >= 0 ) return res;
|
||||
i--; // else didn't match; reduce 1 repetition to try again
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int MinExpand( MatchState ms, int s, int p, int ep )
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
int res = Match( ms, s, ep+1 );
|
||||
if( res >= 0 )
|
||||
return res;
|
||||
else if( s < ms.SrcEnd && SingleMatch( ms, ms.Src[s], p, ep ) )
|
||||
s++; // try with one more repetition
|
||||
else return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int CaptureToClose( MatchState ms )
|
||||
{
|
||||
var lua = ms.Lua;
|
||||
int level=ms.Level;
|
||||
for( level--; level>=0; level-- )
|
||||
{
|
||||
if( ms.Capture[level].Len == CAP_UNFINISHED )
|
||||
return level;
|
||||
}
|
||||
return lua.L_Error( "invalid pattern capture" );
|
||||
}
|
||||
|
||||
private static int StartCapture( MatchState ms, int s, int p, int what )
|
||||
{
|
||||
var lua = ms.Lua;
|
||||
int level = ms.Level;
|
||||
if( level >= LUA_MAXCAPTURES )
|
||||
lua.L_Error( "too many captures" );
|
||||
ms.Capture[level].Init = s;
|
||||
ms.Capture[level].Len = what;
|
||||
ms.Level = level + 1;
|
||||
int res = Match( ms, s, p );
|
||||
if( res == -1 ) // match failed?
|
||||
ms.Level--;
|
||||
return res;
|
||||
}
|
||||
|
||||
private static int EndCapture( MatchState ms, int s, int p )
|
||||
{
|
||||
int l = CaptureToClose( ms );
|
||||
ms.Capture[l].Len = s - ms.Capture[l].Init; // close capture
|
||||
int res = Match( ms, s, p );
|
||||
if( res == -1 ) // match failed?
|
||||
ms.Capture[l].Len = CAP_UNFINISHED; // undo capture
|
||||
return res;
|
||||
}
|
||||
|
||||
private static int CheckCapture( MatchState ms, char l )
|
||||
{
|
||||
var lua = ms.Lua;
|
||||
int i = (int)(l - '1');
|
||||
if( i < 0 || i >= ms.Level || ms.Capture[i].Len == CAP_UNFINISHED )
|
||||
return lua.L_Error( "invalid capture index %d", i+1 );
|
||||
return i;
|
||||
}
|
||||
|
||||
private static int MatchCapture( MatchState ms, int s, char l )
|
||||
{
|
||||
int i = CheckCapture( ms, l );
|
||||
int len = ms.Capture[i].Len;
|
||||
if( ms.SrcEnd - s >= len &&
|
||||
string.Compare(ms.Src, ms.Capture[i].Init, ms.Src, s, len) == 0 )
|
||||
return s + len;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int Match( MatchState ms, int s, int p )
|
||||
{
|
||||
var lua = ms.Lua;
|
||||
init: // using goto's to optimize tail recursion
|
||||
if( p == ms.PatternEnd )
|
||||
return s;
|
||||
switch( ms.Pattern[p] )
|
||||
{
|
||||
case '(': // start capture
|
||||
{
|
||||
if( ms.Pattern[p+1] == ')' ) // position capture?
|
||||
return StartCapture( ms, s, p+2, CAP_POSITION );
|
||||
else
|
||||
return StartCapture( ms, s, p+1, CAP_UNFINISHED );
|
||||
}
|
||||
case ')': // end capture
|
||||
{
|
||||
return EndCapture( ms, s, p+1 );
|
||||
}
|
||||
case '$':
|
||||
{
|
||||
if( p+1 == ms.PatternEnd ) // is the `$' the last char in pattern?
|
||||
return (s == ms.SrcEnd) ? s : -1; // check end of string
|
||||
else goto default;
|
||||
}
|
||||
case L_ESC: // escaped sequences not in the format class[*+?-]?
|
||||
{
|
||||
switch( ms.Pattern[p+1] )
|
||||
{
|
||||
case 'b': // balanced string?
|
||||
{
|
||||
s = MatchBalance( ms, s, p+2 );
|
||||
if( s == -1 ) return -1;
|
||||
p += 4; goto init; // else return match(ms, s, p+4);
|
||||
}
|
||||
case 'f': // frontier?
|
||||
{
|
||||
p += 2;
|
||||
if( ms.Pattern[p] != '[' )
|
||||
lua.L_Error( "missing '[' after '%f' in pattern" );
|
||||
int ep = ClassEnd( ms, p ); //points to what is next
|
||||
char previous = (s == ms.SrcInit) ? '\0' : ms.Src[s-1];
|
||||
if( MatchBreaketClass(ms, previous, p, ep-1) ||
|
||||
!MatchBreaketClass(ms, ms.Src[s], p, ep-1) ) return -1;
|
||||
p = ep; goto init; // else return match( ms, s, ep );
|
||||
}
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': // capture results (%0-%9)?
|
||||
{
|
||||
s = MatchCapture( ms, s, ms.Pattern[p+1] );
|
||||
if( s == -1 ) return -1;
|
||||
p+=2; goto init; // else return match(ms, s, p+2);
|
||||
}
|
||||
default: goto dflt;
|
||||
}
|
||||
}
|
||||
default: dflt: // pattern class plus optional suffix
|
||||
{
|
||||
int ep = ClassEnd( ms, p );
|
||||
bool m = s < ms.SrcEnd && SingleMatch(ms, ms.Src[s], p, ep);
|
||||
if(ep < ms.PatternEnd){
|
||||
switch(ms.Pattern[ep]) //fix gmatch bug patten is [^a]
|
||||
{
|
||||
case '?': // optional
|
||||
{
|
||||
if( m )
|
||||
{
|
||||
int res = Match(ms, s+1, ep+1);
|
||||
if( res != -1 )
|
||||
return res;
|
||||
}
|
||||
p=ep+1; goto init; // else return match(ms, s, ep+1);
|
||||
}
|
||||
case '*': // 0 or more repetitions
|
||||
{
|
||||
return MaxExpand(ms, s, p, ep);
|
||||
}
|
||||
case '+': // 1 or more repetitions
|
||||
{
|
||||
return (m ? MaxExpand(ms, s+1, p, ep) : -1);
|
||||
}
|
||||
case '-': // 0 or more repetitions (minimum)
|
||||
{
|
||||
return MinExpand(ms, s, p, ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!m) return -1;
|
||||
s++; p=ep; goto init; // else return match(ms, s+1, ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void PushOneCapture
|
||||
( MatchState ms
|
||||
, int i
|
||||
, int start
|
||||
, int end
|
||||
)
|
||||
{
|
||||
var lua = ms.Lua;
|
||||
if( i >= ms.Level )
|
||||
{
|
||||
if( i == 0 ) // ms.Level == 0, too
|
||||
lua.PushString( ms.Src.Substring( start, end-start ) );
|
||||
else
|
||||
lua.L_Error( "invalid capture index" );
|
||||
}
|
||||
else
|
||||
{
|
||||
int l = ms.Capture[i].Len;
|
||||
if( l == CAP_UNFINISHED )
|
||||
lua.L_Error( "unfinished capture" );
|
||||
if( l == CAP_POSITION )
|
||||
lua.PushInteger( ms.Capture[i].Init - ms.SrcInit + 1 );
|
||||
else
|
||||
lua.PushString( ms.Src.Substring( ms.Capture[i].Init, l ) );
|
||||
}
|
||||
}
|
||||
|
||||
private static int PushCaptures(ILuaState lua, MatchState ms, int spos, int epos )
|
||||
{
|
||||
int nLevels = (ms.Level == 0 && spos >= 0) ? 1 : ms.Level;
|
||||
lua.L_CheckStack(nLevels, "too many captures");
|
||||
for( int i=0; i<nLevels; ++i )
|
||||
PushOneCapture( ms, i, spos, epos );
|
||||
return nLevels; // number of strings pushed
|
||||
}
|
||||
|
||||
private static bool NoSpecials( string pattern )
|
||||
{
|
||||
return pattern.IndexOfAny( SPECIALS ) == -1;
|
||||
}
|
||||
|
||||
private static int StrFindAux( ILuaState lua, bool find )
|
||||
{
|
||||
string s = lua.L_CheckString( 1 );
|
||||
string p = lua.L_CheckString( 2 );
|
||||
int init = PosRelative( lua.L_OptInt(3, 1), s.Length );
|
||||
if( init < 1 ) init = 1;
|
||||
else if( init > s.Length + 1 ) // start after string's end?
|
||||
{
|
||||
lua.PushNil(); // cannot find anything
|
||||
return 1;
|
||||
}
|
||||
// explicit request or no special characters?
|
||||
if( find && (lua.ToBoolean(4) || NoSpecials(p)) )
|
||||
{
|
||||
// do a plain search
|
||||
int pos = s.IndexOf( p, init-1 );
|
||||
if( pos >= 0 )
|
||||
{
|
||||
lua.PushInteger( pos+1 );
|
||||
lua.PushInteger( pos+p.Length );
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int s1 = init-1;
|
||||
int ppos = 0;
|
||||
bool anchor = p[ppos] == '^';
|
||||
if( anchor )
|
||||
ppos++; // skip anchor character
|
||||
|
||||
MatchState ms = new MatchState();
|
||||
ms.Lua = lua;
|
||||
ms.Src = s;
|
||||
ms.SrcInit = s1;
|
||||
ms.SrcEnd = s.Length;
|
||||
ms.Pattern = p;
|
||||
ms.PatternEnd = p.Length;
|
||||
|
||||
do
|
||||
{
|
||||
ms.Level = 0;
|
||||
int res = Match( ms, s1, ppos );
|
||||
if( res != -1 )
|
||||
{
|
||||
if(find)
|
||||
{
|
||||
lua.PushInteger( s1+1 ); // start
|
||||
lua.PushInteger( res ); // end
|
||||
return PushCaptures(lua, ms, -1, 0) + 2;
|
||||
}
|
||||
else return PushCaptures(lua, ms, s1, res);
|
||||
}
|
||||
} while( s1++ < ms.SrcEnd && !anchor );
|
||||
}
|
||||
lua.PushNil(); // not found
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Str_Find( ILuaState lua )
|
||||
{
|
||||
return StrFindAux( lua, true );
|
||||
}
|
||||
|
||||
private static int ScanFormat( ILuaState lua, string format, int s, out string form )
|
||||
{
|
||||
int p = s;
|
||||
// skip flags
|
||||
while( p < format.Length && format[p] != '\0' && FLAGS.IndexOf(format[p]) != -1 )
|
||||
p++;
|
||||
if( p - s > FLAGS.Length )
|
||||
lua.L_Error( "invalid format (repeat flags)" );
|
||||
if( Char.IsDigit( format[p] ) ) p++; // skip width
|
||||
if( Char.IsDigit( format[p] ) ) p++; // (2 digits at most)
|
||||
if( format[p] == '.' )
|
||||
{
|
||||
p++;
|
||||
if( Char.IsDigit( format[p] ) ) p++; // skip precision
|
||||
if( Char.IsDigit( format[p] ) ) p++; // (2 digits at most)
|
||||
}
|
||||
if( Char.IsDigit( format[p] ) )
|
||||
lua.L_Error( "invalid format (width of precision too long)" );
|
||||
form = "%" + format.Substring( s, (p-s+1) );
|
||||
return p;
|
||||
}
|
||||
|
||||
private static int Str_Format( ILuaState lua )
|
||||
{
|
||||
int top = lua.GetTop();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int arg = 1;
|
||||
string format = lua.L_CheckString( arg );
|
||||
int s = 0;
|
||||
int e = format.Length;
|
||||
while(s < e)
|
||||
{
|
||||
if( format[s] != L_ESC )
|
||||
{
|
||||
sb.Append( format[s++] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( format[++s] == L_ESC )
|
||||
{
|
||||
sb.Append( format[s++] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// else format item
|
||||
if( ++arg > top )
|
||||
lua.L_ArgError( arg, "no value" );
|
||||
|
||||
string form;
|
||||
s = ScanFormat( lua, format, s, out form );
|
||||
switch( format[s++] ) // TODO: properly handle form
|
||||
{
|
||||
case 'c':
|
||||
{
|
||||
sb.Append( (char)lua.L_CheckInteger(arg) );
|
||||
break;
|
||||
}
|
||||
case 'd': case 'i':
|
||||
{
|
||||
int n = lua.L_CheckInteger(arg);
|
||||
sb.Append( n.ToString() );
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
int n = lua.L_CheckInteger(arg);
|
||||
lua.L_ArgCheck( n >= 0, arg,
|
||||
"not a non-negative number is proper range" );
|
||||
sb.Append( n.ToString() );
|
||||
break;
|
||||
}
|
||||
case 'o':
|
||||
{
|
||||
int n = lua.L_CheckInteger(arg);
|
||||
lua.L_ArgCheck( n >= 0, arg,
|
||||
"not a non-negative number is proper range" );
|
||||
sb.Append( Convert.ToString(n, 8) );
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
{
|
||||
int n = lua.L_CheckInteger(arg);
|
||||
lua.L_ArgCheck( n >= 0, arg,
|
||||
"not a non-negative number is proper range" );
|
||||
// sb.Append( string.Format("{0:x}", n) );
|
||||
sb.AppendFormat("{0:x}", n);
|
||||
break;
|
||||
}
|
||||
case 'X':
|
||||
{
|
||||
int n = lua.L_CheckInteger(arg);
|
||||
lua.L_ArgCheck( n >= 0, arg,
|
||||
"not a non-negative number is proper range" );
|
||||
// sb.Append( string.Format("{0:X}", n) );
|
||||
sb.AppendFormat("{0:X}", n);
|
||||
break;
|
||||
}
|
||||
case 'e': case 'E':
|
||||
{
|
||||
sb.AppendFormat("{0:E}", lua.L_CheckNumber(arg));
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
sb.AppendFormat("{0:F}", lua.L_CheckNumber(arg));
|
||||
break;
|
||||
}
|
||||
#if LUA_USE_AFORMAT
|
||||
case 'a': case 'A':
|
||||
#endif
|
||||
case 'g': case 'G':
|
||||
{
|
||||
sb.AppendFormat("{0:G}", lua.L_CheckNumber(arg));
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
{
|
||||
AddQuoted(lua, sb, arg);
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
sb.Append(lua.L_CheckString(arg));
|
||||
break;
|
||||
}
|
||||
default: // also treat cases `pnLlh'
|
||||
{
|
||||
return lua.L_Error( "invalid option '{0}' to 'format'",
|
||||
format[s-1] );
|
||||
}
|
||||
}
|
||||
}
|
||||
lua.PushString( sb.ToString() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static void AddQuoted(ILuaState lua, StringBuilder sb, int arg)
|
||||
{
|
||||
var s = lua.L_CheckString(arg);
|
||||
sb.Append('"');
|
||||
for(var i=0; i<s.Length; ++i) {
|
||||
var c = s[i];
|
||||
if(c == '"' || c == '\\' || c == '\n') {
|
||||
sb.Append('\\').Append(c);
|
||||
}
|
||||
else if(c == '\0' || Char.IsControl(c)) {
|
||||
if(i+1>=s.Length || !Char.IsDigit(s[i+1])) {
|
||||
sb.AppendFormat("\\{0:D}", (int)c);
|
||||
}
|
||||
else {
|
||||
sb.AppendFormat("\\{0:D3}", (int)c);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sb.Append(c);
|
||||
}
|
||||
}
|
||||
sb.Append('"');
|
||||
}
|
||||
|
||||
private static int GmatchAux( ILuaState lua )
|
||||
{
|
||||
MatchState ms = new MatchState();
|
||||
string src = lua.ToString( lua.UpvalueIndex(1) );
|
||||
string pattern = lua.ToString( lua.UpvalueIndex(2) );
|
||||
ms.Lua = lua;
|
||||
ms.Src = src;
|
||||
ms.SrcInit = 0;
|
||||
ms.SrcEnd = src.Length;
|
||||
ms.Pattern = pattern;
|
||||
ms.PatternEnd = pattern.Length;
|
||||
for( int s = lua.ToInteger( lua.UpvalueIndex(3) )
|
||||
; s <= ms.SrcEnd
|
||||
; s++ )
|
||||
{
|
||||
ms.Level = 0;
|
||||
int e = Match( ms, s, 0 );
|
||||
if( e != -1 )
|
||||
{
|
||||
int newStart = (e == 0) ? e+1: e;
|
||||
lua.PushInteger( newStart );
|
||||
lua.Replace( lua.UpvalueIndex(3) );
|
||||
return PushCaptures(lua, ms, s, e);
|
||||
}
|
||||
}
|
||||
return 0; // not found
|
||||
}
|
||||
|
||||
private static int Str_Gmatch( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckString(1);
|
||||
lua.L_CheckString(2);
|
||||
lua.SetTop(2);
|
||||
lua.PushInteger(0);
|
||||
lua.PushCSharpClosure( GmatchAux, 3 );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static void Add_S (MatchState ms, StringBuilder b, int s, int e) {
|
||||
string news = ms.Lua.ToString(3);
|
||||
for (int i = 0; i < news.Length; i++) {
|
||||
if (news[i] != L_ESC)
|
||||
b.Append(news[i]);
|
||||
else {
|
||||
i++; /* skip ESC */
|
||||
if (!Char.IsDigit((news[i])))
|
||||
b.Append(news[i]);
|
||||
else if (news[i] == '0')
|
||||
b.Append(ms.Src.Substring(s, (e - s)));
|
||||
else {
|
||||
PushOneCapture(ms, news[i] - '1', s, e);
|
||||
b.Append(ms.Lua.ToString(-1)); /* add capture to accumulated result */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Add_Value (MatchState ms, StringBuilder b, int s, int e) {
|
||||
ILuaState lua = ms.Lua;
|
||||
switch (lua.Type(3)) {
|
||||
case LuaType.LUA_TNUMBER:
|
||||
case LuaType.LUA_TSTRING: {
|
||||
Add_S(ms, b, s, e);
|
||||
return;
|
||||
}
|
||||
case LuaType.LUA_TFUNCTION: {
|
||||
int n;
|
||||
lua.PushValue(3);
|
||||
n = PushCaptures(lua, ms, s, e);
|
||||
lua.Call(n, 1);
|
||||
break;
|
||||
}
|
||||
case LuaType.LUA_TTABLE: {
|
||||
PushOneCapture(ms, 0, s, e);
|
||||
lua.GetTable(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lua.ToBoolean(-1)==false) { /* nil or false? */
|
||||
lua.Pop(1);
|
||||
b.Append(ms.Src.Substring(s, (e - s))); /* keep original text */
|
||||
}
|
||||
else if (!lua.IsString(-1))
|
||||
lua.L_Error("invalid replacement value (a %s)", lua.L_TypeName(-1));
|
||||
else
|
||||
b.Append(lua.ToString(-1));
|
||||
}
|
||||
|
||||
private static int Str_Gsub( ILuaState lua )
|
||||
{
|
||||
|
||||
string src = lua.L_CheckString(1);
|
||||
int srcl = src.Length;
|
||||
string p = lua.L_CheckString(2);
|
||||
LuaType tr = lua.Type(3);
|
||||
int max_s = lua.L_OptInt(4, srcl + 1);
|
||||
int anchor = 0;
|
||||
if (p[0] == '^')
|
||||
{
|
||||
p = p.Substring(1);
|
||||
anchor = 1;
|
||||
}
|
||||
int n = 0;
|
||||
MatchState ms = new MatchState();
|
||||
StringBuilder b = new StringBuilder(srcl);
|
||||
lua.L_ArgCheck(tr == LuaType.LUA_TNUMBER || tr == LuaType.LUA_TSTRING ||
|
||||
tr == LuaType.LUA_TFUNCTION || tr == LuaType.LUA_TTABLE, 3,
|
||||
"string/function/table expected");
|
||||
ms.Lua = lua;
|
||||
ms.Src = src;
|
||||
ms.SrcInit = 0;
|
||||
ms.SrcEnd = srcl;
|
||||
ms.Pattern = p;
|
||||
ms.PatternEnd = p.Length;
|
||||
int s = 0;
|
||||
while (n < max_s) {
|
||||
ms.Level = 0;
|
||||
int e = Match(ms, s, 0);
|
||||
if (e != -1) {
|
||||
n++;
|
||||
Add_Value(ms, b, s, e);
|
||||
}
|
||||
if ((e != -1) && e > s) /* non empty match? */
|
||||
s = e; /* skip it */
|
||||
else if (s < ms.SrcEnd)
|
||||
{
|
||||
char c = src[s];
|
||||
++s;
|
||||
b.Append(c);
|
||||
}
|
||||
else break;
|
||||
if (anchor != 0) break;
|
||||
}
|
||||
b.Append(src.Substring(s, ms.SrcEnd - s));
|
||||
lua.PushString(b.ToString());
|
||||
lua.PushInteger(n); /* number of substitutions */
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static int Str_Len( ILuaState lua )
|
||||
{
|
||||
string s = lua.L_CheckString(1);
|
||||
lua.PushInteger( s.Length );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Str_Lower( ILuaState lua )
|
||||
{
|
||||
string s = lua.L_CheckString(1);
|
||||
lua.PushString( s.ToLower() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Str_Match( ILuaState lua )
|
||||
{
|
||||
return StrFindAux( lua, false );
|
||||
}
|
||||
|
||||
private static int Str_Rep(ILuaState lua)
|
||||
{
|
||||
string s = lua.L_CheckString(1);
|
||||
int n = lua.L_CheckInteger(2);
|
||||
|
||||
StringBuilder sb = new StringBuilder(s.Length * n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
sb.Append(s);
|
||||
|
||||
lua.PushString(sb.ToString());
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Str_Reverse( ILuaState lua )
|
||||
{
|
||||
string s = lua.L_CheckString(1);
|
||||
StringBuilder sb = new StringBuilder(s.Length);
|
||||
for( int i=s.Length-1; i>=0; --i )
|
||||
sb.Append( s[i] );
|
||||
lua.PushString( sb.ToString() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Str_Sub( ILuaState lua )
|
||||
{
|
||||
string s = lua.L_CheckString(1);
|
||||
int start = PosRelative( lua.L_CheckInteger(2), s.Length );
|
||||
int end = PosRelative( lua.L_OptInt(3, -1), s.Length );
|
||||
if( start < 1 ) start = 1;
|
||||
if( end > s.Length ) end = s.Length;
|
||||
if( start <= end )
|
||||
lua.PushString( s.Substring(start-1, end-start+1) );
|
||||
else
|
||||
lua.PushString( "" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int Str_Upper( ILuaState lua )
|
||||
{
|
||||
string s = lua.L_CheckString(1);
|
||||
lua.PushString( s.ToUpper() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
581
UniLua/LuaTable.cs
Normal file
581
UniLua/LuaTable.cs
Normal file
@@ -0,0 +1,581 @@
|
||||
|
||||
// #define DEBUG_DUMMY_TVALUE_MODIFY
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using ULDebug = UniLua.Tools.ULDebug;
|
||||
|
||||
public class LuaTable {
|
||||
public LuaTable MetaTable;
|
||||
public uint NoTagMethodFlags;
|
||||
|
||||
public LuaTable(LuaState l) {
|
||||
InitLuaTable(l);
|
||||
}
|
||||
|
||||
~LuaTable()
|
||||
{
|
||||
Recycle();
|
||||
}
|
||||
|
||||
public StkId Get(ref TValue key)
|
||||
{
|
||||
if(key.Tt == (int)LuaType.LUA_TNIL) { return TheNilValue; }
|
||||
|
||||
if(IsPositiveInteger(ref key))
|
||||
{ return GetInt((int)key.NValue); }
|
||||
|
||||
if(key.Tt == (int)LuaType.LUA_TSTRING)
|
||||
{ return GetStr(key.SValue()); }
|
||||
|
||||
var h = key.GetHashCode();
|
||||
for(var node = GetHashNode(h); node != null; node = node.Next) {
|
||||
if(node.Key.V == key) {
|
||||
{ return node.Val; }
|
||||
}
|
||||
}
|
||||
|
||||
return TheNilValue;
|
||||
}
|
||||
|
||||
public StkId GetInt(int key)
|
||||
{
|
||||
if(0 < key && key-1 < ArrayPart.Length)
|
||||
{ return ArrayPart[key-1]; }
|
||||
|
||||
var k = new TValue();
|
||||
k.SetNValue(key);
|
||||
for(var node = GetHashNode(ref k); node != null; node = node.Next) {
|
||||
if(node.Key.V.TtIsNumber() && node.Key.V.NValue == (double)key) {
|
||||
return node.Val;
|
||||
}
|
||||
}
|
||||
|
||||
return TheNilValue;
|
||||
}
|
||||
|
||||
public StkId GetStr(string key)
|
||||
{
|
||||
var h = key.GetHashCode();
|
||||
for(var node = GetHashNode(h); node != null; node = node.Next) {
|
||||
if(node.Key.V.TtIsString() && node.Key.V.SValue() == key)
|
||||
{ return node.Val; }
|
||||
}
|
||||
|
||||
return TheNilValue;
|
||||
}
|
||||
|
||||
public void Set(ref TValue key, ref TValue val)
|
||||
{
|
||||
var cell = Get(ref key);
|
||||
if(cell == TheNilValue) {
|
||||
cell = NewTableKey(ref key);
|
||||
}
|
||||
cell.V.SetObj(ref val);
|
||||
}
|
||||
|
||||
public void SetInt(int key, ref TValue val)
|
||||
{
|
||||
var cell = GetInt(key);
|
||||
if(cell == TheNilValue) {
|
||||
var k = new TValue();
|
||||
k.SetNValue(key);
|
||||
cell = NewTableKey(ref k);
|
||||
}
|
||||
cell.V.SetObj(ref val);
|
||||
// Console.WriteLine(string.Format("---------------- SetInt {0} -> {1}", key, val));
|
||||
// DumpParts();
|
||||
}
|
||||
|
||||
/*
|
||||
** returns the index of a `key' for table traversals. First goes all
|
||||
** elements in the array part, then elements in the hash part. The
|
||||
** beginning of a traversal is signaled by -1.
|
||||
*/
|
||||
private int FindIndex(StkId key)
|
||||
{
|
||||
if(key.V.TtIsNil())
|
||||
{ return -1; }
|
||||
|
||||
// is `key' inside array part?
|
||||
int i = ArrayIndex(ref key.V);
|
||||
if(0 < i && i <= ArrayPart.Length)
|
||||
{ return i-1; }
|
||||
|
||||
var n = GetHashNode(ref key.V);
|
||||
// check whether `key' is somewhere in the chain
|
||||
for(;;) {
|
||||
if(L.V_RawEqualObj(ref n.Key.V, ref key.V))
|
||||
{ return ArrayPart.Length + n.Index; }
|
||||
n = n.Next;
|
||||
|
||||
// key not found
|
||||
if(n == null) { L.G_RunError("invalid key to 'next'"); }
|
||||
}
|
||||
}
|
||||
|
||||
public bool Next(StkId key, StkId val)
|
||||
{
|
||||
// find original element
|
||||
int i = FindIndex(key);
|
||||
|
||||
// try first array part
|
||||
for(i++; i<ArrayPart.Length; ++i) {
|
||||
if(!ArrayPart[i].V.TtIsNil()) {
|
||||
key.V.SetNValue(i+1);
|
||||
val.V.SetObj(ref ArrayPart[i].V);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// then hash part
|
||||
for(i-=ArrayPart.Length; i<HashPart.Length; ++i) {
|
||||
if(!HashPart[i].Val.V.TtIsNil()) {
|
||||
key.V.SetObj(ref HashPart[i].Key.V);
|
||||
val.V.SetObj(ref HashPart[i].Val.V);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int Length
|
||||
{ get {
|
||||
uint j = (uint)ArrayPart.Length;
|
||||
if(j > 0 && ArrayPart[j-1].V.TtIsNil()) {
|
||||
/* there is a boundary in the array part: (binary) search for it */
|
||||
uint i = 0;
|
||||
while(j - i > 1) {
|
||||
uint m = (i+j)/2;
|
||||
if(ArrayPart[m-1].V.TtIsNil()) { j = m; }
|
||||
else { i = m; }
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
/* else must find a boundary in hash part */
|
||||
else if(HashPart == DummyHashPart)
|
||||
return (int)j;
|
||||
else return UnboundSearch(j);
|
||||
} }
|
||||
|
||||
public void Resize(int nasize, int nhsize)
|
||||
{
|
||||
int oasize = ArrayPart.Length;
|
||||
var oldHashPart = HashPart;
|
||||
if(nasize > oasize) // array part must grow?
|
||||
SetArraryVector(nasize);
|
||||
|
||||
// create new hash part with appropriate size
|
||||
SetNodeVector(nhsize);
|
||||
|
||||
// array part must shrink?
|
||||
if(nasize < oasize) {
|
||||
var oldArrayPart = ArrayPart;
|
||||
ArrayPart = DummyArrayPart;
|
||||
// re-insert elements from vanishing slice
|
||||
for(int i=nasize; i<oasize; ++i) {
|
||||
if(!oldArrayPart[i].V.TtIsNil())
|
||||
{ SetInt(i+1, ref oldArrayPart[i].V); }
|
||||
}
|
||||
// shrink array
|
||||
var newArrayPart = new StkId[nasize];
|
||||
for(int i=0; i<nasize; ++i) {
|
||||
newArrayPart[i] = oldArrayPart[i];
|
||||
}
|
||||
ArrayPart = newArrayPart;
|
||||
}
|
||||
|
||||
// re-insert elements from hash part
|
||||
for(int i=0; i<oldHashPart.Length; ++i) {
|
||||
var node = oldHashPart[i];
|
||||
if(!node.Val.V.TtIsNil()) {
|
||||
Set(ref node.Key.V, ref node.Val.V);
|
||||
}
|
||||
}
|
||||
|
||||
if (oldHashPart != DummyHashPart)
|
||||
RecycleHNode(oldHashPart);
|
||||
}
|
||||
|
||||
//-----------------------------------------
|
||||
//
|
||||
// **** PRIVATE below ****
|
||||
//
|
||||
//-----------------------------------------
|
||||
|
||||
private class HNode
|
||||
{
|
||||
public int Index;
|
||||
public StkId Key;
|
||||
public StkId Val;
|
||||
public HNode Next;
|
||||
|
||||
public void CopyFrom(HNode o)
|
||||
{
|
||||
Key.V.SetObj(ref o.Key.V);
|
||||
Val.V.SetObj(ref o.Val.V);
|
||||
Next = o.Next;
|
||||
}
|
||||
}
|
||||
|
||||
private LuaState L;
|
||||
|
||||
private StkId[] ArrayPart;
|
||||
private HNode[] HashPart;
|
||||
private int LastFree;
|
||||
|
||||
private static StkId TheNilValue;
|
||||
private static StkId[] DummyArrayPart;
|
||||
private static HNode DummyNode;
|
||||
private static HNode[] DummyHashPart;
|
||||
|
||||
private const int MAXBITS = 30;
|
||||
private const int MAXASIZE = (1 << MAXBITS);
|
||||
|
||||
static LuaTable()
|
||||
{
|
||||
TheNilValue = new StkId();
|
||||
TheNilValue.V.SetNilValue();
|
||||
#if DEBUG_DUMMY_TVALUE_MODIFY
|
||||
TheNilValue.V.Lock_ = true;
|
||||
#endif
|
||||
|
||||
DummyArrayPart = new StkId[0];
|
||||
|
||||
DummyNode = new HNode();
|
||||
DummyNode.Key = TheNilValue;
|
||||
DummyNode.Val = TheNilValue;
|
||||
DummyNode.Next = null;
|
||||
|
||||
DummyHashPart = new HNode[1];
|
||||
DummyHashPart[0] = DummyNode;
|
||||
DummyHashPart[0].Index = 0;
|
||||
}
|
||||
|
||||
#region Small Object Cache
|
||||
private static HNode CacheHead = null;
|
||||
private static object CacheHeadLock = new Object();
|
||||
|
||||
private void Recycle()
|
||||
{
|
||||
if (HashPart != null && HashPart != DummyHashPart)
|
||||
{
|
||||
RecycleHNode(HashPart);
|
||||
HashPart = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void RecycleHNode(HNode[] garbage)
|
||||
{
|
||||
if (garbage == null || garbage.Length == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < garbage.Length-1; i++)
|
||||
{
|
||||
garbage[i].Next = garbage[i + 1];
|
||||
}
|
||||
|
||||
lock(CacheHeadLock) {
|
||||
garbage[garbage.Length - 1].Next = CacheHead;
|
||||
CacheHead = garbage[0];
|
||||
}
|
||||
}
|
||||
|
||||
private HNode NewHNode()
|
||||
{
|
||||
HNode ret;
|
||||
if (CacheHead == null)
|
||||
{
|
||||
ret = new HNode();
|
||||
ret.Key = new StkId();
|
||||
ret.Val = new StkId();
|
||||
}
|
||||
else
|
||||
{
|
||||
lock(CacheHeadLock) {
|
||||
ret = CacheHead;
|
||||
CacheHead = CacheHead.Next;
|
||||
}
|
||||
ret.Next = null;
|
||||
ret.Index = 0;
|
||||
ret.Key.V.SetNilValue();
|
||||
ret.Val.V.SetNilValue();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void InitLuaTable(LuaState lua)
|
||||
{
|
||||
L = lua;
|
||||
ArrayPart = DummyArrayPart;
|
||||
SetNodeVector(0);
|
||||
}
|
||||
|
||||
private bool IsPositiveInteger(ref TValue v)
|
||||
{
|
||||
return (v.TtIsNumber() && v.NValue > 0 && (v.NValue % 1) == 0 && v.NValue <= int.MaxValue); //fix large number key bug
|
||||
}
|
||||
|
||||
private HNode GetHashNode(int hashcode)
|
||||
{
|
||||
uint n = (uint)hashcode;
|
||||
return HashPart[n % HashPart.Length];
|
||||
}
|
||||
|
||||
private HNode GetHashNode(ref TValue v)
|
||||
{
|
||||
if(IsPositiveInteger(ref v)) { return GetHashNode((int)v.NValue); }
|
||||
|
||||
if(v.TtIsString()) { return GetHashNode(v.SValue().GetHashCode()); }
|
||||
|
||||
return GetHashNode(v.GetHashCode());
|
||||
}
|
||||
|
||||
private void SetArraryVector(int size)
|
||||
{
|
||||
Utl.Assert(size >= ArrayPart.Length);
|
||||
|
||||
var newArrayPart = new StkId[size];
|
||||
int i = 0;
|
||||
for( ; i<ArrayPart.Length; ++i) {
|
||||
newArrayPart[i] = ArrayPart[i];
|
||||
}
|
||||
for( ; i<size; ++i) {
|
||||
newArrayPart[i] = new StkId();
|
||||
newArrayPart[i].V.SetNilValue();
|
||||
}
|
||||
ArrayPart = newArrayPart;
|
||||
}
|
||||
|
||||
private void SetNodeVector(int size)
|
||||
{
|
||||
if(size == 0) {
|
||||
HashPart = DummyHashPart;
|
||||
LastFree = size;
|
||||
return;
|
||||
}
|
||||
|
||||
int lsize = CeilLog2(size);
|
||||
if(lsize > MAXBITS) { L.G_RunError("table overflow"); }
|
||||
|
||||
size = (1 << lsize);
|
||||
HashPart = new HNode[size];
|
||||
for(int i=0; i<size; ++i) {
|
||||
HashPart[i] = NewHNode();
|
||||
HashPart[i].Index = i;
|
||||
}
|
||||
LastFree = size;
|
||||
}
|
||||
|
||||
private HNode GetFreePos()
|
||||
{
|
||||
while(LastFree > 0) {
|
||||
var node = HashPart[--LastFree];
|
||||
if(node.Key.V.TtIsNil()) { return node; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
** returns the index for `key' if `key' is an appropriate key to live in
|
||||
** the array part of the table, -1 otherwise.
|
||||
*/
|
||||
private int ArrayIndex(ref TValue k)
|
||||
{
|
||||
if(IsPositiveInteger(ref k))
|
||||
return (int)k.NValue;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static readonly byte[] Log2_ = new byte[] {
|
||||
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
|
||||
};
|
||||
private int CeilLog2(int x)
|
||||
{
|
||||
Utl.Assert(x > 0);
|
||||
int l = 0;
|
||||
x--;
|
||||
while(x >= 256) { l+=8; x>>=8; }
|
||||
return l + Log2_[x];
|
||||
}
|
||||
|
||||
private int CountInt(ref TValue key, ref int[] nums)
|
||||
{
|
||||
int k = ArrayIndex(ref key);
|
||||
if(0 < k && k <= MAXASIZE) {
|
||||
nums[CeilLog2(k)]++;
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
private int NumUseArray(ref int[] nums)
|
||||
{
|
||||
int ause = 0;
|
||||
int i = 1;
|
||||
for(int lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) {
|
||||
int lc = 0; // counter
|
||||
int lim = ttlg;
|
||||
if(lim > ArrayPart.Length) {
|
||||
lim = ArrayPart.Length;
|
||||
if(i > lim) { break; } // no more elements to count
|
||||
}
|
||||
|
||||
// count elements in range (2^(lg-1), 2^lg]
|
||||
for(; i<=lim; ++i) {
|
||||
if(!ArrayPart[i-1].V.TtIsNil()) { lc++; }
|
||||
}
|
||||
nums[lg] += lc;
|
||||
ause += lc;
|
||||
}
|
||||
return ause;
|
||||
}
|
||||
|
||||
private int NumUseHash(ref int[] nums, ref int nasize)
|
||||
{
|
||||
int totaluse = 0;
|
||||
int ause = 0;
|
||||
int i = HashPart.Length;
|
||||
while(i-- > 0) {
|
||||
var n = HashPart[i];
|
||||
if(!n.Val.V.TtIsNil()) {
|
||||
ause += CountInt(ref n.Key.V, ref nums);
|
||||
totaluse++;
|
||||
}
|
||||
}
|
||||
nasize += ause;
|
||||
return totaluse;
|
||||
}
|
||||
|
||||
private int ComputeSizes(ref int[] nums, ref int nasize)
|
||||
{
|
||||
int a = 0;
|
||||
int na = 0;
|
||||
int n = 0;
|
||||
for(int i=0, tti=1; tti/2<nasize; ++i, tti*=2) {
|
||||
if(nums[i] > 0) {
|
||||
a += nums[i];
|
||||
if(a > tti/2) {
|
||||
n = tti;
|
||||
na = a;
|
||||
}
|
||||
}
|
||||
if(a == nasize) { break; } // all elements already conted
|
||||
}
|
||||
nasize = n;
|
||||
Utl.Assert(nasize/2 <= na && na <= nasize);
|
||||
return na;
|
||||
}
|
||||
|
||||
private static int[] Nums = new int[MAXBITS + 1];
|
||||
private void Rehash(ref TValue k)
|
||||
{
|
||||
for(int i=0; i<=MAXBITS; ++i) { Nums[i] = 0; }
|
||||
|
||||
int nasize = NumUseArray(ref Nums);
|
||||
int totaluse = nasize;
|
||||
totaluse += NumUseHash(ref Nums, ref nasize);
|
||||
nasize += CountInt(ref k, ref Nums);
|
||||
totaluse++;
|
||||
int na = ComputeSizes(ref Nums, ref nasize);
|
||||
Resize(nasize, totaluse-na);
|
||||
}
|
||||
|
||||
private void DumpParts()
|
||||
{
|
||||
Console.WriteLine("------------------ [DumpParts] enter -----------------------");
|
||||
Console.WriteLine("<< Array Part >>");
|
||||
for(var i=0; i<ArrayPart.Length; ++i) {
|
||||
var n = ArrayPart[i];
|
||||
Console.WriteLine(string.Format("i:{0} val:{1}", i, n.V));
|
||||
}
|
||||
Console.WriteLine("<< Hash Part >>");
|
||||
for(var i=0; i<HashPart.Length; ++i) {
|
||||
var n = HashPart[i];
|
||||
var next = (n.Next == null) ? -1 : n.Next.Index;
|
||||
Console.WriteLine(string.Format("i:{0} index:{1} key:{2} val:{3} next:{4}", i, n.Index, n.Key.V, n.Val.V, next));
|
||||
}
|
||||
Console.WriteLine("++++++++++++++++++ [DumpParts] leave +++++++++++++++++++++++");
|
||||
}
|
||||
|
||||
private StkId NewTableKey(ref TValue k)
|
||||
{
|
||||
if(k.TtIsNil()) { L.G_RunError("table index is nil"); }
|
||||
|
||||
if(k.TtIsNumber() && System.Double.IsNaN(k.NValue))
|
||||
{ L.G_RunError("table index is NaN"); }
|
||||
|
||||
var mp = GetHashNode(ref k);
|
||||
|
||||
// if main position is taken
|
||||
if(!mp.Val.V.TtIsNil() || mp == DummyNode) {
|
||||
var n = GetFreePos();
|
||||
if(n == null) {
|
||||
Rehash(ref k);
|
||||
var cell = Get(ref k);
|
||||
if(cell != TheNilValue) { return cell; }
|
||||
return NewTableKey(ref k);
|
||||
}
|
||||
|
||||
Utl.Assert(n != DummyNode);
|
||||
var othern = GetHashNode(ref mp.Key.V);
|
||||
// is colliding node out of its main position?
|
||||
if(othern != mp) {
|
||||
while(othern.Next != mp) { othern = othern.Next; }
|
||||
othern.Next = n;
|
||||
n.CopyFrom(mp);
|
||||
mp.Next = null;
|
||||
mp.Val.V.SetNilValue();
|
||||
}
|
||||
// colliding node is in its own main position
|
||||
else {
|
||||
n.Next = mp.Next;
|
||||
mp.Next = n;
|
||||
mp = n;
|
||||
}
|
||||
}
|
||||
|
||||
mp.Key.V.SetObj(ref k);
|
||||
Utl.Assert(mp.Val.V.TtIsNil());
|
||||
return mp.Val;
|
||||
}
|
||||
|
||||
private int UnboundSearch(uint j)
|
||||
{
|
||||
uint i = j;
|
||||
j++;
|
||||
while(!GetInt((int)j).V.TtIsNil()) {
|
||||
i = j;
|
||||
j *= 2;
|
||||
|
||||
// overflow?
|
||||
if(j > LuaLimits.MAX_INT) {
|
||||
/* table was built with bad purposes: resort to linear search */
|
||||
i = 1;
|
||||
while(!GetInt((int)i).V.TtIsNil()) { i++; }
|
||||
return (int)(i-1);
|
||||
}
|
||||
}
|
||||
/* now do a binary search between them */
|
||||
while(j - i > 1) {
|
||||
uint m = (i + j) / 2;
|
||||
if(GetInt((int)m).V.TtIsNil()) { j = m; }
|
||||
else { i = m; }
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
281
UniLua/LuaTableLib.cs
Normal file
281
UniLua/LuaTableLib.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
|
||||
#define LUA_COMPAT_UNPACK
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using StringBuilder = System.Text.StringBuilder;
|
||||
|
||||
internal class LuaTableLib
|
||||
{
|
||||
public const string LIB_NAME = "table";
|
||||
|
||||
public static int OpenLib( ILuaState lua )
|
||||
{
|
||||
NameFuncPair[] define = new NameFuncPair[]
|
||||
{
|
||||
new NameFuncPair( "concat", TBL_Concat ),
|
||||
new NameFuncPair( "maxn", TBL_MaxN ),
|
||||
new NameFuncPair( "insert", TBL_Insert ),
|
||||
new NameFuncPair( "pack", TBL_Pack ),
|
||||
new NameFuncPair( "unpack", TBL_Unpack ),
|
||||
new NameFuncPair( "remove", TBL_Remove ),
|
||||
new NameFuncPair( "sort", TBL_Sort ),
|
||||
};
|
||||
|
||||
lua.L_NewLib( define );
|
||||
|
||||
#if LUA_COMPAT_UNPACK
|
||||
// _G.unpack = table.unpack
|
||||
lua.GetField( -1, "unpack" );
|
||||
lua.SetGlobal( "unpack" );
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int TBL_Concat( ILuaState lua )
|
||||
{
|
||||
string sep = lua.L_OptString( 2, "" );
|
||||
lua.L_CheckType( 1, LuaType.LUA_TTABLE );
|
||||
int i = lua.L_OptInt( 3, 1 );
|
||||
int last = lua.L_Opt(4, lua.L_Len(1));
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for( ; i<last; ++i )
|
||||
{
|
||||
lua.RawGetI( 1, i );
|
||||
if( !lua.IsString(-1) )
|
||||
lua.L_Error(
|
||||
"invalid value ({0}) at index {1} in table for 'concat'",
|
||||
lua.L_TypeName(-1), i );
|
||||
sb.Append( lua.ToString(-1) );
|
||||
sb.Append( sep );
|
||||
lua.Pop( 1 );
|
||||
}
|
||||
if( i == last ) // add last value (if interval was not empty)
|
||||
{
|
||||
lua.RawGetI( 1, i );
|
||||
if( !lua.IsString(-1) )
|
||||
lua.L_Error(
|
||||
"invalid value ({0}) at index {1} in table for 'concat'",
|
||||
lua.L_TypeName(-1), i );
|
||||
sb.Append( lua.ToString(-1) );
|
||||
lua.Pop( 1 );
|
||||
}
|
||||
lua.PushString( sb.ToString() );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int TBL_MaxN( ILuaState lua )
|
||||
{
|
||||
double max = 0.0;
|
||||
lua.L_CheckType( 1, LuaType.LUA_TTABLE );
|
||||
|
||||
lua.PushNil(); // first key
|
||||
while( lua.Next(1) )
|
||||
{
|
||||
lua.Pop( 1 ); // remove value
|
||||
if( lua.Type( -1 ) == LuaType.LUA_TNUMBER ) {
|
||||
double v = lua.ToNumber( -1 );
|
||||
if( v > max ) max = v;
|
||||
}
|
||||
}
|
||||
lua.PushNumber( max );
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int AuxGetN( ILuaState lua, int n )
|
||||
{
|
||||
lua.L_CheckType( n, LuaType.LUA_TTABLE );
|
||||
return lua.L_Len( n );
|
||||
}
|
||||
|
||||
private static int TBL_Insert( ILuaState lua )
|
||||
{
|
||||
int e = AuxGetN(lua, 1) + 1; // first empty element
|
||||
int pos; // where to insert new element
|
||||
switch( lua.GetTop() )
|
||||
{
|
||||
case 2: // called with only 2 arguments
|
||||
{
|
||||
pos = e; // insert new element at the end
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
pos = lua.L_CheckInteger(2); // 2nd argument is the position
|
||||
if( pos > e ) e = pos; // `grow' array if necessary
|
||||
for( int i=e; i>pos; --i ) // move up elements
|
||||
{
|
||||
lua.RawGetI( 1, i-1 );
|
||||
lua.RawSetI( 1, i ); // t[i] = t[i-1]
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return lua.L_Error( "wrong number of arguments to 'insert'" );
|
||||
}
|
||||
}
|
||||
lua.RawSetI( 1, pos ); // t[pos] = v
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int TBL_Remove( ILuaState lua )
|
||||
{
|
||||
int e = AuxGetN(lua, 1);
|
||||
int pos = lua.L_OptInt( 2, e );
|
||||
if( !(1 <= pos && pos <= e) ) // position is outside bounds?
|
||||
return 0; // nothing to remove
|
||||
lua.RawGetI(1, pos); /* result = t[pos] */
|
||||
for( ; pos<e; ++pos )
|
||||
{
|
||||
lua.RawGetI( 1, pos+1 );
|
||||
lua.RawSetI( 1, pos ); // t[pos] = t[pos+1]
|
||||
}
|
||||
lua.PushNil();
|
||||
lua.RawSetI( 1, e ); // t[2] = nil
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int TBL_Pack( ILuaState lua )
|
||||
{
|
||||
int n = lua.GetTop(); // number of elements to pack
|
||||
lua.CreateTable( n, 1 ); // create result table
|
||||
lua.PushInteger( n );
|
||||
lua.SetField( -2, "n" ); // t.n = number of elements
|
||||
if( n > 0 ) // at least one element?
|
||||
{
|
||||
lua.PushValue( 1 );
|
||||
lua.RawSetI( -2, 1 ); // insert first element
|
||||
lua.Replace( 1 ); // move table into index 1
|
||||
for( int i=n; i>=2; --i ) // assign other elements
|
||||
lua.RawSetI( 1, i );
|
||||
}
|
||||
return 1; // return table
|
||||
}
|
||||
|
||||
private static int TBL_Unpack( ILuaState lua )
|
||||
{
|
||||
lua.L_CheckType( 1, LuaType.LUA_TTABLE );
|
||||
int i = lua.L_OptInt( 2, 1 );
|
||||
int e = lua.L_OptInt( 3, lua.L_Len(1) );
|
||||
if( i > e ) return 0; // empty range
|
||||
int n = e - i + 1; // number of elements
|
||||
if( n <= 0 || !lua.CheckStack(n) ) // n <= 0 means arith. overflow
|
||||
return lua.L_Error( "too many results to unpack" );
|
||||
lua.RawGetI( 1, i ); // push arg[i] (avoiding overflow problems
|
||||
while( i++ < e ) // push arg[i + 1...e]
|
||||
lua.RawGetI( 1, i );
|
||||
return n;
|
||||
}
|
||||
|
||||
// quick sort ////////////////////////////////////////////////////////
|
||||
|
||||
private static void Set2( ILuaState lua, int i, int j )
|
||||
{
|
||||
lua.RawSetI( 1, i );
|
||||
lua.RawSetI( 1, j );
|
||||
}
|
||||
|
||||
private static bool SortComp( ILuaState lua, int a, int b )
|
||||
{
|
||||
if( !lua.IsNil(2) ) // function?
|
||||
{
|
||||
lua.PushValue( 2 );
|
||||
lua.PushValue( a-1 ); // -1 to compensate function
|
||||
lua.PushValue( b-2 ); // -2 to compensate function add `a'
|
||||
lua.Call( 2, 1 );
|
||||
bool res = lua.ToBoolean( -1 );
|
||||
lua.Pop( 1 );
|
||||
return res;
|
||||
}
|
||||
else /// a < b?
|
||||
return lua.Compare( a, b, LuaEq.LUA_OPLT );
|
||||
}
|
||||
|
||||
private static void AuxSort( ILuaState lua, int l, int u )
|
||||
{
|
||||
while( l < u ) // for tail recursion
|
||||
{
|
||||
// sort elements a[l], a[(l+u)/2] and a[u]
|
||||
lua.RawGetI( 1, l );
|
||||
lua.RawGetI( 1, u );
|
||||
if( SortComp( lua, -1, -2 ) ) // a[u] < a[l]?
|
||||
Set2( lua, l, u );
|
||||
else
|
||||
lua.Pop( 2 );
|
||||
if( u-l == 1 ) break; // only 2 elements
|
||||
int i = (l+u) / 2;
|
||||
lua.RawGetI( 1, i );
|
||||
lua.RawGetI( 1, l );
|
||||
if( SortComp( lua, -2, -1 ) ) // a[i] < a[l]?
|
||||
Set2( lua, i, l );
|
||||
else
|
||||
{
|
||||
lua.Pop( 1 ); // remove a[l]
|
||||
lua.RawGetI( 1, u );
|
||||
if( SortComp( lua, -1, -2 ) ) // a[u] < a[i]?
|
||||
Set2( lua, i, u );
|
||||
else
|
||||
lua.Pop( 2 );
|
||||
}
|
||||
if( u-l == 2 ) break; // only 3 arguments
|
||||
lua.RawGetI( 1, i ); // Pivot
|
||||
lua.PushValue( -1 );
|
||||
lua.RawGetI( 1, u-1 );
|
||||
Set2(lua, i, u-1);
|
||||
/* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
|
||||
i = l;
|
||||
int j = u-1;
|
||||
for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
|
||||
/* repeat ++i until a[i] >= P */
|
||||
lua.RawGetI( 1, ++i );
|
||||
while( SortComp(lua, -1, -2) )
|
||||
{
|
||||
if (i>=u) lua.L_Error( "invalid order function for sorting" );
|
||||
lua.Pop(1); /* remove a[i] */
|
||||
lua.RawGetI( 1, ++i );
|
||||
}
|
||||
/* repeat --j until a[j] <= P */
|
||||
lua.RawGetI( 1, --j );
|
||||
while ( SortComp(lua, -3, -1) ) {
|
||||
if (j<=l) lua.L_Error( "invalid order function for sorting" );
|
||||
lua.Pop(1); /* remove a[j] */
|
||||
lua.RawGetI( 1, --j );
|
||||
}
|
||||
if (j<i) {
|
||||
lua.Pop(3); /* pop pivot, a[i], a[j] */
|
||||
break;
|
||||
}
|
||||
Set2(lua, i, j);
|
||||
}
|
||||
lua.RawGetI( 1, u-1 );
|
||||
lua.RawGetI( 1, i );
|
||||
Set2(lua, u-1, i); /* swap pivot (a[u-1]) with a[i] */
|
||||
/* a[l..i-1] <= a[i] == P <= a[i+1..u] */
|
||||
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */
|
||||
if (i-l < u-i) {
|
||||
j=l; i=i-1; l=i+2;
|
||||
}
|
||||
else {
|
||||
j=i+1; i=u; u=j-2;
|
||||
}
|
||||
AuxSort(lua, j, i); /* call recursively the smaller one */
|
||||
} /* repeat the routine for the larger one */
|
||||
}
|
||||
|
||||
private static int TBL_Sort( ILuaState lua )
|
||||
{
|
||||
int n = AuxGetN(lua, 1);
|
||||
lua.L_CheckStack(40, ""); /* assume array is smaller than 2^40 */
|
||||
if (!lua.IsNoneOrNil(2)) /* is there a 2nd argument? */
|
||||
lua.L_CheckType( 2, LuaType.LUA_TFUNCTION );
|
||||
lua.SetTop(2); /* make sure there is two arguments */
|
||||
AuxSort(lua, 1, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
164
UniLua/OpCodes.cs
Normal file
164
UniLua/OpCodes.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
public enum OpCode
|
||||
{
|
||||
/*----------------------------------------------------------------------
|
||||
name args description
|
||||
------------------------------------------------------------------------*/
|
||||
OP_MOVE,/* A B R(A) := R(B) */
|
||||
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
|
||||
OP_LOADKX,/* A R(A) := Kst(extra arg) */
|
||||
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
OP_LOADNIL,/* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
|
||||
|
||||
OP_GETTABUP,/* A B C R(A) := UpValue[B][RK(C)] */
|
||||
OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_SETTABUP,/* A B C UpValue[A][RK(B)] := RK(C) */
|
||||
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
|
||||
OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
|
||||
|
||||
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
|
||||
|
||||
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
|
||||
OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
|
||||
OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
|
||||
OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
|
||||
OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
|
||||
OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
|
||||
OP_UNM,/* A B R(A) := -R(B) */
|
||||
OP_NOT,/* A B R(A) := not R(B) */
|
||||
OP_LEN,/* A B R(A) := length of R(B) */
|
||||
|
||||
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
|
||||
|
||||
OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A) + 1 */
|
||||
OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
|
||||
OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
|
||||
OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
|
||||
|
||||
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
|
||||
OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
|
||||
|
||||
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
|
||||
|
||||
OP_FORLOOP,/* A sBx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
|
||||
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
|
||||
|
||||
OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
|
||||
OP_TFORLOOP,/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/
|
||||
|
||||
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
|
||||
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
|
||||
OP_VARARG,/* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
|
||||
|
||||
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
}
|
||||
|
||||
internal enum OpArgMask
|
||||
{
|
||||
OpArgN, /* argument is not used */
|
||||
OpArgU, /* argument is used */
|
||||
OpArgR, /* argument is a register or a jump offset */
|
||||
OpArgK /* argument is a constant or register/constant */
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// basic instruction format
|
||||
/// </summary>
|
||||
internal enum OpMode
|
||||
{
|
||||
iABC,
|
||||
iABx,
|
||||
iAsBx,
|
||||
iAx,
|
||||
}
|
||||
|
||||
internal class OpCodeMode
|
||||
{
|
||||
public bool TMode;
|
||||
public bool AMode;
|
||||
public OpArgMask BMode;
|
||||
public OpArgMask CMode;
|
||||
public OpMode OpMode;
|
||||
|
||||
public OpCodeMode(bool tMode, bool aMode, OpArgMask bMode, OpArgMask cMode, OpMode opMode)
|
||||
{
|
||||
TMode = tMode;
|
||||
AMode = aMode;
|
||||
BMode = bMode;
|
||||
CMode = cMode;
|
||||
OpMode = opMode;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class OpCodeInfo
|
||||
{
|
||||
public static OpCodeMode GetMode( OpCode op )
|
||||
{
|
||||
return Info[(int)op];
|
||||
}
|
||||
|
||||
private static Dictionary<int, OpCodeMode> Info;
|
||||
|
||||
static OpCodeInfo()
|
||||
{
|
||||
Info = new Dictionary<int, OpCodeMode>();
|
||||
var opCode = OpCode.OP_MOVE;
|
||||
var opCodeMode = new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgN, OpMode.iABC);
|
||||
Info.Add((int)opCode, opCodeMode);
|
||||
Info.Add( (int)OpCode.OP_LOADK, new OpCodeMode(false, true, OpArgMask.OpArgK, OpArgMask.OpArgN, OpMode.iABx) );
|
||||
Info.Add( (int)OpCode.OP_LOADKX, new OpCodeMode(false, true, OpArgMask.OpArgN, OpArgMask.OpArgN, OpMode.iABx) );
|
||||
Info.Add( (int)OpCode.OP_LOADBOOL, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgU, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_LOADNIL, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgN, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_GETUPVAL, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgN, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_GETTABUP, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_GETTABLE, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_SETTABUP, new OpCodeMode(false, false, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_SETUPVAL, new OpCodeMode(false, false, OpArgMask.OpArgU, OpArgMask.OpArgN, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_SETTABLE, new OpCodeMode(false, false, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_NEWTABLE, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgU, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_SELF, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_ADD, new OpCodeMode(false, true, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_SUB, new OpCodeMode(false, true, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_MUL, new OpCodeMode(false, true, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_DIV, new OpCodeMode(false, true, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_MOD, new OpCodeMode(false, true, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_POW, new OpCodeMode(false, true, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_UNM, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgN, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_NOT, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgN, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_LEN, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgN, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_CONCAT, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgR, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_JMP, new OpCodeMode(false, false, OpArgMask.OpArgR, OpArgMask.OpArgN, OpMode.iAsBx) );
|
||||
Info.Add( (int)OpCode.OP_EQ, new OpCodeMode(true, false, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_LT, new OpCodeMode(true, false, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_LE, new OpCodeMode(true, false, OpArgMask.OpArgK, OpArgMask.OpArgK, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_TEST, new OpCodeMode(true, false, OpArgMask.OpArgN, OpArgMask.OpArgU, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_TESTSET, new OpCodeMode(true, true, OpArgMask.OpArgR, OpArgMask.OpArgU, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_CALL, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgU, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_TAILCALL, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgU, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_RETURN, new OpCodeMode(false, false, OpArgMask.OpArgU, OpArgMask.OpArgN, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_FORLOOP, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgN, OpMode.iAsBx) );
|
||||
Info.Add( (int)OpCode.OP_FORPREP, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgN, OpMode.iAsBx) );
|
||||
Info.Add( (int)OpCode.OP_TFORCALL, new OpCodeMode(false, false, OpArgMask.OpArgN, OpArgMask.OpArgU, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_TFORLOOP, new OpCodeMode(false, true, OpArgMask.OpArgR, OpArgMask.OpArgN, OpMode.iAsBx) );
|
||||
Info.Add( (int)OpCode.OP_SETLIST, new OpCodeMode(false, false, OpArgMask.OpArgU, OpArgMask.OpArgU, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_CLOSURE, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgN, OpMode.iABx) );
|
||||
Info.Add( (int)OpCode.OP_VARARG, new OpCodeMode(false, true, OpArgMask.OpArgU, OpArgMask.OpArgN, OpMode.iABC) );
|
||||
Info.Add( (int)OpCode.OP_EXTRAARG, new OpCodeMode(false, false, OpArgMask.OpArgU, OpArgMask.OpArgU, OpMode.iAx) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1945
UniLua/Parser.cs
Normal file
1945
UniLua/Parser.cs
Normal file
File diff suppressed because it is too large
Load Diff
102
UniLua/TagMethod.cs
Normal file
102
UniLua/TagMethod.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
// grep `NoTagMethodFlags' if num of TMS >= 32
|
||||
internal enum TMS
|
||||
{
|
||||
TM_INDEX,
|
||||
TM_NEWINDEX,
|
||||
TM_GC,
|
||||
TM_MODE,
|
||||
TM_LEN,
|
||||
TM_EQ,
|
||||
TM_ADD,
|
||||
TM_SUB,
|
||||
TM_MUL,
|
||||
TM_DIV,
|
||||
TM_MOD,
|
||||
TM_POW,
|
||||
TM_UNM,
|
||||
TM_LT,
|
||||
TM_LE,
|
||||
TM_CONCAT,
|
||||
TM_CALL,
|
||||
TM_N /* number of elements in the enum */
|
||||
}
|
||||
|
||||
public partial class LuaState
|
||||
{
|
||||
private string GetTagMethodName( TMS tm )
|
||||
{
|
||||
switch( tm )
|
||||
{
|
||||
case TMS.TM_INDEX: return "__index";
|
||||
case TMS.TM_NEWINDEX: return "__newindex";
|
||||
case TMS.TM_GC: return "__gc";
|
||||
case TMS.TM_MODE: return "__mode";
|
||||
case TMS.TM_LEN: return "__len";
|
||||
case TMS.TM_EQ: return "__eq";
|
||||
case TMS.TM_ADD: return "__add";
|
||||
case TMS.TM_SUB: return "__sub";
|
||||
case TMS.TM_MUL: return "__mul";
|
||||
case TMS.TM_DIV: return "__div";
|
||||
case TMS.TM_MOD: return "__mod";
|
||||
case TMS.TM_POW: return "__pow";
|
||||
case TMS.TM_UNM: return "__unm";
|
||||
case TMS.TM_LT: return "__lt";
|
||||
case TMS.TM_LE: return "__le";
|
||||
case TMS.TM_CONCAT: return "__concat";
|
||||
case TMS.TM_CALL: return "__call";
|
||||
default: throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private StkId T_GetTM( LuaTable mt, TMS tm )
|
||||
{
|
||||
if( mt == null )
|
||||
return null;
|
||||
|
||||
var res = mt.GetStr( GetTagMethodName( tm ) );
|
||||
if(res.V.TtIsNil()) // no tag method?
|
||||
{
|
||||
// cache this fact
|
||||
mt.NoTagMethodFlags |= 1u << (int)tm;
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return res;
|
||||
}
|
||||
|
||||
private StkId T_GetTMByObj( ref TValue o, TMS tm )
|
||||
{
|
||||
LuaTable mt = null;
|
||||
|
||||
switch( o.Tt )
|
||||
{
|
||||
case (int)LuaType.LUA_TTABLE:
|
||||
{
|
||||
var tbl = o.HValue();
|
||||
mt = tbl.MetaTable;
|
||||
break;
|
||||
}
|
||||
case (int)LuaType.LUA_TUSERDATA:
|
||||
{
|
||||
var ud = o.RawUValue();
|
||||
mt = ud.MetaTable;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
mt = G.MetaTables[o.Tt];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (mt != null)
|
||||
? mt.GetStr( GetTagMethodName( tm ) )
|
||||
: TheNilValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
19
UniLua/ULDebug.cs
Normal file
19
UniLua/ULDebug.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
#define UNITY
|
||||
|
||||
namespace UniLua.Tools
|
||||
{
|
||||
// thanks to dharco
|
||||
// refer to https://github.com/dharco/UniLua/commit/2854ddf2500ab2f943f01a6d3c9af767c092ce75
|
||||
public class ULDebug
|
||||
{
|
||||
public static System.Action<object> Log = NoAction;
|
||||
public static System.Action<object> LogError = NoAction;
|
||||
|
||||
private static void NoAction(object msg) { }
|
||||
|
||||
static ULDebug()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
376
UniLua/Undump.cs
Normal file
376
UniLua/Undump.cs
Normal file
@@ -0,0 +1,376 @@
|
||||
// #define DEBUG_BINARY_READER
|
||||
// #define DEBUG_UNDUMP
|
||||
|
||||
using System;
|
||||
|
||||
using ULDebug = UniLua.Tools.ULDebug;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
public class BinaryBytesReader
|
||||
{
|
||||
private ILoadInfo LoadInfo;
|
||||
public int SizeOfSizeT;
|
||||
|
||||
|
||||
public BinaryBytesReader( ILoadInfo loadinfo )
|
||||
{
|
||||
LoadInfo = loadinfo;
|
||||
SizeOfSizeT = 0;
|
||||
}
|
||||
|
||||
public byte[] ReadBytes( int count )
|
||||
{
|
||||
byte[] ret = new byte[count];
|
||||
for( int i=0; i<count; ++i )
|
||||
{
|
||||
var c = LoadInfo.ReadByte();
|
||||
if( c == -1 )
|
||||
throw new UndumpException("truncated");
|
||||
ret[i] = (byte)c;
|
||||
}
|
||||
#if DEBUG_BINARY_READER
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append("ReadBytes:");
|
||||
for( var i=0; i<ret.Length; ++i )
|
||||
{
|
||||
sb.Append( string.Format(" {0:X02}", ret[i]) );
|
||||
}
|
||||
Console.WriteLine( sb.ToString() );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int ReadInt()
|
||||
{
|
||||
var bytes = ReadBytes( 4 );
|
||||
int ret = BitConverter.ToInt32( bytes, 0 );
|
||||
#if DEBUG_BINARY_READER
|
||||
Console.WriteLine( "ReadInt: " + ret );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
public uint ReadUInt()
|
||||
{
|
||||
var bytes = ReadBytes( 4 );
|
||||
uint ret = BitConverter.ToUInt32( bytes, 0 );
|
||||
#if DEBUG_BINARY_READER
|
||||
Console.WriteLine( "ReadUInt: " + ret );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int ReadSizeT()
|
||||
{
|
||||
if( SizeOfSizeT <= 0) {
|
||||
throw new Exception("sizeof(size_t) is not valid:" + SizeOfSizeT);
|
||||
}
|
||||
|
||||
var bytes = ReadBytes( SizeOfSizeT );
|
||||
UInt64 ret;
|
||||
switch( SizeOfSizeT ) {
|
||||
case 4:
|
||||
ret = BitConverter.ToUInt32( bytes, 0 );
|
||||
break;
|
||||
case 8:
|
||||
ret = BitConverter.ToUInt64( bytes, 0 );
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#if DEBUG_BINARY_READER
|
||||
Console.WriteLine( "ReadSizeT: " + ret );
|
||||
#endif
|
||||
|
||||
if( ret > Int32.MaxValue )
|
||||
throw new NotImplementedException();
|
||||
|
||||
return (int)ret;
|
||||
}
|
||||
|
||||
public double ReadDouble()
|
||||
{
|
||||
var bytes = ReadBytes( 8 );
|
||||
double ret = BitConverter.ToDouble( bytes, 0 );
|
||||
#if DEBUG_BINARY_READER
|
||||
Console.WriteLine( "ReadDouble: " + ret );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
var c = LoadInfo.ReadByte();
|
||||
if( c == -1 )
|
||||
throw new UndumpException("truncated");
|
||||
#if DEBUG_BINARY_READER
|
||||
Console.WriteLine( "ReadBytes: " + c );
|
||||
#endif
|
||||
return (byte)c;
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
var n = ReadSizeT();
|
||||
if( n == 0 )
|
||||
return null;
|
||||
|
||||
var bytes = ReadBytes( n );
|
||||
|
||||
// n=1: removing trailing '\0'
|
||||
string ret = System.Text.Encoding.ASCII.GetString( bytes, 0, n-1 );
|
||||
#if DEBUG_BINARY_READER
|
||||
Console.WriteLine( "ReadString n:" + n + " ret:" + ret );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
class UndumpException : Exception
|
||||
{
|
||||
public string Why;
|
||||
|
||||
public UndumpException( string why )
|
||||
{
|
||||
Why = why;
|
||||
}
|
||||
}
|
||||
|
||||
public class Undump
|
||||
{
|
||||
private BinaryBytesReader Reader;
|
||||
|
||||
|
||||
public static LuaProto LoadBinary( ILuaState lua,
|
||||
ILoadInfo loadinfo, string name )
|
||||
{
|
||||
try
|
||||
{
|
||||
var reader = new BinaryBytesReader( loadinfo );
|
||||
var undump = new Undump( reader );
|
||||
undump.LoadHeader();
|
||||
return undump.LoadFunction();
|
||||
}
|
||||
catch( UndumpException e )
|
||||
{
|
||||
var Lua = (LuaState)lua;
|
||||
Lua.O_PushString( string.Format(
|
||||
"{0}: {1} precompiled chunk", name, e.Why ) );
|
||||
Lua.D_Throw( ThreadStatus.LUA_ERRSYNTAX );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Undump( BinaryBytesReader reader )
|
||||
{
|
||||
Reader = reader;
|
||||
}
|
||||
|
||||
private int LoadInt()
|
||||
{
|
||||
return Reader.ReadInt();
|
||||
}
|
||||
|
||||
private byte LoadByte()
|
||||
{
|
||||
return Reader.ReadByte();
|
||||
}
|
||||
|
||||
private byte[] LoadBytes( int count )
|
||||
{
|
||||
return Reader.ReadBytes( count );
|
||||
}
|
||||
|
||||
private string LoadString()
|
||||
{
|
||||
return Reader.ReadString();
|
||||
}
|
||||
|
||||
private bool LoadBoolean()
|
||||
{
|
||||
return LoadByte() != 0;
|
||||
}
|
||||
|
||||
private double LoadNumber()
|
||||
{
|
||||
return Reader.ReadDouble();
|
||||
}
|
||||
|
||||
private void LoadHeader()
|
||||
{
|
||||
byte[] header = LoadBytes( 4 // Signature
|
||||
+ 8 // version, format version, size of int ... etc
|
||||
+ 6 // Tail
|
||||
);
|
||||
byte v = header[ 4 /* skip signature */
|
||||
+ 4 /* offset of sizeof(size_t) */
|
||||
];
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine(string.Format("sizeof(size_t): {0}", v));
|
||||
#endif
|
||||
Reader.SizeOfSizeT = v ;
|
||||
}
|
||||
|
||||
private Instruction LoadInstruction()
|
||||
{
|
||||
return (Instruction)Reader.ReadUInt();
|
||||
}
|
||||
|
||||
private LuaProto LoadFunction()
|
||||
{
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "LoadFunction enter" );
|
||||
#endif
|
||||
|
||||
LuaProto proto = new LuaProto();
|
||||
proto.LineDefined = LoadInt();
|
||||
proto.LastLineDefined = LoadInt();
|
||||
proto.NumParams = LoadByte();
|
||||
proto.IsVarArg = LoadBoolean();
|
||||
proto.MaxStackSize = LoadByte();
|
||||
|
||||
LoadCode(proto);
|
||||
LoadConstants(proto);
|
||||
LoadUpvalues(proto);
|
||||
LoadDebug(proto);
|
||||
return proto;
|
||||
}
|
||||
|
||||
private void LoadCode( LuaProto proto )
|
||||
{
|
||||
var n = LoadInt();
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "LoadCode n:" + n );
|
||||
#endif
|
||||
proto.Code.Clear();
|
||||
for( int i=0; i<n; ++i )
|
||||
{
|
||||
proto.Code.Add( LoadInstruction() );
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "Count:" + proto.Code.Count );
|
||||
Console.WriteLine( "LoadInstruction:" + proto.Code[proto.Code.Count-1] );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadConstants( LuaProto proto )
|
||||
{
|
||||
var n = LoadInt();
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "Load Constants:" + n );
|
||||
#endif
|
||||
proto.K.Clear();
|
||||
for( int i=0; i<n; ++i )
|
||||
{
|
||||
int t = (int)LoadByte();
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "Constant Type:" + t );
|
||||
#endif
|
||||
var v = new StkId();
|
||||
switch( t )
|
||||
{
|
||||
case (int)LuaType.LUA_TNIL:
|
||||
v.V.SetNilValue();
|
||||
proto.K.Add( v );
|
||||
break;
|
||||
|
||||
case (int)LuaType.LUA_TBOOLEAN:
|
||||
v.V.SetBValue(LoadBoolean());
|
||||
proto.K.Add( v );
|
||||
break;
|
||||
|
||||
case (int)LuaType.LUA_TNUMBER:
|
||||
v.V.SetNValue(LoadNumber());
|
||||
proto.K.Add( v );
|
||||
break;
|
||||
|
||||
case (int)LuaType.LUA_TSTRING:
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "LuaType.LUA_TSTRING" );
|
||||
#endif
|
||||
v.V.SetSValue(LoadString());
|
||||
proto.K.Add( v );
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UndumpException(
|
||||
"LoadConstants unknown type: " + t );
|
||||
}
|
||||
}
|
||||
|
||||
n = LoadInt();
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "Load Functions:" + n );
|
||||
#endif
|
||||
proto.P.Clear();
|
||||
for( int i=0; i<n; ++i )
|
||||
{
|
||||
proto.P.Add( LoadFunction() );
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadUpvalues( LuaProto proto )
|
||||
{
|
||||
var n = LoadInt();
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "Load Upvalues:" + n );
|
||||
#endif
|
||||
proto.Upvalues.Clear();
|
||||
for( int i=0; i<n; ++i )
|
||||
{
|
||||
proto.Upvalues.Add(
|
||||
new UpvalDesc()
|
||||
{
|
||||
Name = null,
|
||||
InStack = LoadBoolean(),
|
||||
Index = (int)LoadByte()
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadDebug( LuaProto proto )
|
||||
{
|
||||
int n;
|
||||
proto.Source = LoadString();
|
||||
|
||||
// LineInfo
|
||||
n = LoadInt();
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "Load LineInfo:" + n );
|
||||
#endif
|
||||
proto.LineInfo.Clear();
|
||||
for( int i=0; i<n; ++i )
|
||||
{
|
||||
proto.LineInfo.Add( LoadInt() );
|
||||
}
|
||||
|
||||
// LocalVar
|
||||
n = LoadInt();
|
||||
#if DEBUG_UNDUMP
|
||||
Console.WriteLine( "Load LocalVar:" + n );
|
||||
#endif
|
||||
proto.LocVars.Clear();
|
||||
for( int i=0; i<n; ++i )
|
||||
{
|
||||
proto.LocVars.Add(
|
||||
new LocVar()
|
||||
{
|
||||
VarName = LoadString(),
|
||||
StartPc = LoadInt(),
|
||||
EndPc = LoadInt(),
|
||||
} );
|
||||
}
|
||||
|
||||
// Upvalues' name
|
||||
n = LoadInt();
|
||||
for( int i=0; i<n; ++i )
|
||||
{
|
||||
proto.Upvalues[i].Name = LoadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
238
UniLua/Util.cs
Normal file
238
UniLua/Util.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
#define API_CHECK
|
||||
#define UNILUA_ASSERT
|
||||
|
||||
using System;
|
||||
|
||||
namespace UniLua
|
||||
{
|
||||
using DebugS = System.Diagnostics.Debug;
|
||||
using NumberStyles = System.Globalization.NumberStyles;
|
||||
|
||||
internal static class Utl
|
||||
{
|
||||
private static void Throw( params string[] msgs )
|
||||
{
|
||||
throw new Exception(String.Join("", msgs));
|
||||
}
|
||||
|
||||
public static void Assert( bool condition )
|
||||
{
|
||||
#if UNILUA_ASSERT
|
||||
if( !condition )
|
||||
Throw("assert failed!" );
|
||||
//DebugS.Assert( condition );
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Assert( bool condition, string message )
|
||||
{
|
||||
#if UNILUA_ASSERT
|
||||
if( !condition )
|
||||
Throw( "assert failed! ", message );
|
||||
//DebugS.Assert( condition, message );
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Assert( bool condition, string message, string detailMessage )
|
||||
{
|
||||
#if UNILUA_ASSERT
|
||||
if( !condition )
|
||||
Throw( "assert failed! ", message, "\n", detailMessage );
|
||||
//DebugS.Assert( condition, message, detailMessage );
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void ApiCheck( bool condition, string message )
|
||||
{
|
||||
#if UNILUA_ASSERT
|
||||
#if API_CHECK
|
||||
Assert( condition, message );
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void ApiCheckNumElems( LuaState lua, int n )
|
||||
{
|
||||
#if UNILUA_ASSERT
|
||||
Assert( n < (lua.Top.Index - lua.CI.FuncIndex), "not enough elements in the stack" );
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void InvalidIndex()
|
||||
{
|
||||
#if UNILUA_ASSERT
|
||||
Assert( false, "invalid index" );
|
||||
#endif
|
||||
}
|
||||
|
||||
private static bool IsNegative( string s, ref int pos )
|
||||
{
|
||||
if( pos >= s.Length )
|
||||
return false;
|
||||
|
||||
char c = s[pos];
|
||||
if( c == '-' )
|
||||
{
|
||||
++pos;
|
||||
return true;
|
||||
}
|
||||
else if( c == '+' )
|
||||
{
|
||||
++pos;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsXDigit( char c )
|
||||
{
|
||||
if( Char.IsDigit( c ) )
|
||||
return true;
|
||||
|
||||
if( 'a' <= c && c <= 'f' )
|
||||
return true;
|
||||
|
||||
if( 'A' <= c && c <= 'F' )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static double ReadHexa( string s, ref int pos, double r, out int count )
|
||||
{
|
||||
count = 0;
|
||||
while( pos < s.Length && IsXDigit( s[pos] ) )
|
||||
{
|
||||
r = (r * 16.0) + Int32.Parse( s[pos].ToString(), NumberStyles.HexNumber );
|
||||
++pos;
|
||||
++count;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static double ReadDecimal( string s, ref int pos, double r, out int count )
|
||||
{
|
||||
count = 0;
|
||||
while( pos < s.Length && Char.IsDigit( s[pos] ) )
|
||||
{
|
||||
r = (r * 10.0) + Int32.Parse( s[pos].ToString() );
|
||||
++pos;
|
||||
++count;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// following C99 specification for 'strtod'
|
||||
public static double StrX2Number( string s, ref int curpos )
|
||||
{
|
||||
int pos = curpos;
|
||||
while( pos < s.Length && Char.IsWhiteSpace( s[pos] )) ++pos;
|
||||
bool negative = IsNegative( s, ref pos );
|
||||
|
||||
// check `0x'
|
||||
if( pos >= s.Length || !(s[pos] == '0' && (s[pos+1] == 'x' || s[pos+1] == 'X')) )
|
||||
return 0.0;
|
||||
|
||||
pos += 2; // skip `0x'
|
||||
|
||||
double r = 0.0;
|
||||
int i = 0;
|
||||
int e = 0;
|
||||
r = ReadHexa( s, ref pos, r, out i );
|
||||
if( pos < s.Length && s[pos] == '.' )
|
||||
{
|
||||
++pos; // skip `.'
|
||||
r = ReadHexa( s, ref pos, r, out e );
|
||||
}
|
||||
if( i == 0 && e == 0 )
|
||||
return 0.0; // invalid format (no digit)
|
||||
|
||||
// each fractional digit divides value by 2^-4
|
||||
e *= -4;
|
||||
curpos = pos;
|
||||
|
||||
// exponent part
|
||||
if( pos < s.Length && (s[pos] == 'p' || s[pos] == 'P') )
|
||||
{
|
||||
++pos; // skip `p'
|
||||
bool expNegative = IsNegative( s, ref pos );
|
||||
if( pos >= s.Length || !Char.IsDigit( s[pos] ) )
|
||||
goto ret;
|
||||
|
||||
int exp1 = 0;
|
||||
while( pos < s.Length && Char.IsDigit( s[pos] ) )
|
||||
{
|
||||
exp1 = exp1 * 10 + Int32.Parse( s[pos].ToString() );
|
||||
++pos;
|
||||
}
|
||||
if( expNegative )
|
||||
exp1 = -exp1;
|
||||
e += exp1;
|
||||
}
|
||||
curpos = pos;
|
||||
|
||||
ret:
|
||||
if( negative ) r = -r;
|
||||
|
||||
return r * Math.Pow(2.0, e);
|
||||
}
|
||||
|
||||
public static double Str2Number( string s, ref int curpos )
|
||||
{
|
||||
int pos = curpos;
|
||||
while( pos < s.Length && Char.IsWhiteSpace( s[pos] )) ++pos;
|
||||
bool negative = IsNegative( s, ref pos );
|
||||
|
||||
double r = 0.0;
|
||||
int i = 0;
|
||||
int f = 0;
|
||||
r = ReadDecimal( s, ref pos, r, out i );
|
||||
if( pos < s.Length && s[pos] == '.' )
|
||||
{
|
||||
++pos;
|
||||
r = ReadDecimal( s, ref pos, r, out f );
|
||||
}
|
||||
if( i == 0 && f == 0 )
|
||||
return 0.0;
|
||||
|
||||
f = -f;
|
||||
curpos = pos;
|
||||
|
||||
// exponent part
|
||||
double e = 0.0;
|
||||
if( pos < s.Length && (s[pos] == 'e' || s[pos] == 'E') )
|
||||
{
|
||||
++pos;
|
||||
bool expNegative = IsNegative( s, ref pos );
|
||||
if( pos >= s.Length || !Char.IsDigit( s[pos] ) )
|
||||
goto ret;
|
||||
|
||||
int n;
|
||||
e = ReadDecimal( s, ref pos, e, out n );
|
||||
if( expNegative )
|
||||
e = -e;
|
||||
f += (int)e;
|
||||
}
|
||||
curpos = pos;
|
||||
|
||||
ret:
|
||||
if( negative ) r = -r;
|
||||
|
||||
return r * Math.Pow(10, f);
|
||||
}
|
||||
|
||||
public static string TrimWhiteSpace( string str )
|
||||
{
|
||||
int s = 0;
|
||||
int e = str.Length;
|
||||
|
||||
while( s < str.Length && Char.IsWhiteSpace( str[s] ) ) ++s;
|
||||
if( s >= e )
|
||||
return "";
|
||||
|
||||
while( e >= 0 && Char.IsWhiteSpace( str[e-1] ) ) --e;
|
||||
return str.Substring( s, e-s );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
1256
UniLua/VM.cs
Normal file
1256
UniLua/VM.cs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user