Lua 5.2支持

This commit is contained in:
2026-02-03 02:44:58 +08:00
parent c64330b22f
commit 3c21a1ce3a
33 changed files with 14339 additions and 0 deletions

View File

@@ -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
View 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

File diff suppressed because it is too large Load Diff

128
UniLua/Common.cs Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

754
UniLua/LuaAuxLib.cs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

102
UniLua/TagMethod.cs Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff