From 70dfc20f5795ce804034b3a5bf9ce276f8c07b14 Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Fri, 8 Jun 2007 05:11:37 +0000 Subject: [PATCH] Initial draft of interpreter. Lua compiled "chunks" can be unmarshalled. Approximately half of bytecodes implemented in some form or another. --- .classpath | 8 + .project | 17 + pom.xml | 13 + src/main/java/lua/Builtin.java | 33 ++ src/main/java/lua/GlobalState.java | 49 +++ src/main/java/lua/Lua.java | 383 ++++++++++++++++++++++ src/main/java/lua/io/Closure.java | 23 ++ src/main/java/lua/io/LoadState.java | 432 +++++++++++++++++++++++++ src/main/java/lua/io/LocVars.java | 16 + src/main/java/lua/io/Proto.java | 39 +++ src/main/java/lua/io/UpVal.java | 5 + src/main/java/lua/value/LBoolean.java | 28 ++ src/main/java/lua/value/LDouble.java | 48 +++ src/main/java/lua/value/LFunction.java | 7 + src/main/java/lua/value/LInteger.java | 43 +++ src/main/java/lua/value/LNil.java | 13 + src/main/java/lua/value/LNumber.java | 6 + src/main/java/lua/value/LString.java | 21 ++ src/main/java/lua/value/LTable.java | 29 ++ src/main/java/lua/value/LThread.java | 5 + src/main/java/lua/value/LValue.java | 65 ++++ src/test/java/LuacRunner.java | 49 +++ src/test/res/compile.sh | 7 + src/test/res/test1.lua | 5 + src/test/res/test1.luac | Bin 0 -> 138 bytes src/test/res/test2.lua | 10 + src/test/res/test2.luac | Bin 0 -> 761 bytes src/test/res/test3.lua | 6 + src/test/res/test3.luac | Bin 0 -> 283 bytes src/test/res/test4.lua | 8 + src/test/res/test4.luac | Bin 0 -> 472 bytes 31 files changed, 1368 insertions(+) create mode 100644 .classpath create mode 100644 .project create mode 100644 pom.xml create mode 100644 src/main/java/lua/Builtin.java create mode 100644 src/main/java/lua/GlobalState.java create mode 100644 src/main/java/lua/Lua.java create mode 100644 src/main/java/lua/io/Closure.java create mode 100644 src/main/java/lua/io/LoadState.java create mode 100644 src/main/java/lua/io/LocVars.java create mode 100644 src/main/java/lua/io/Proto.java create mode 100644 src/main/java/lua/io/UpVal.java create mode 100644 src/main/java/lua/value/LBoolean.java create mode 100644 src/main/java/lua/value/LDouble.java create mode 100644 src/main/java/lua/value/LFunction.java create mode 100644 src/main/java/lua/value/LInteger.java create mode 100644 src/main/java/lua/value/LNil.java create mode 100644 src/main/java/lua/value/LNumber.java create mode 100644 src/main/java/lua/value/LString.java create mode 100644 src/main/java/lua/value/LTable.java create mode 100644 src/main/java/lua/value/LThread.java create mode 100644 src/main/java/lua/value/LValue.java create mode 100644 src/test/java/LuacRunner.java create mode 100644 src/test/res/compile.sh create mode 100644 src/test/res/test1.lua create mode 100644 src/test/res/test1.luac create mode 100644 src/test/res/test2.lua create mode 100644 src/test/res/test2.luac create mode 100644 src/test/res/test3.lua create mode 100644 src/test/res/test3.luac create mode 100644 src/test/res/test4.lua create mode 100644 src/test/res/test4.luac diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..b37a9cfe --- /dev/null +++ b/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 00000000..15180d11 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + luaj-vm + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..7436a363 --- /dev/null +++ b/pom.xml @@ -0,0 +1,13 @@ + + 4.0.0 + luaj + luaj + jar + 1.0-SNAPSHOT + LuaJ Interpreter + + + LuaJ + + diff --git a/src/main/java/lua/Builtin.java b/src/main/java/lua/Builtin.java new file mode 100644 index 00000000..657a8a82 --- /dev/null +++ b/src/main/java/lua/Builtin.java @@ -0,0 +1,33 @@ +/** + * + */ +package lua; + +import lua.value.LFunction; +import lua.value.LString; +import lua.value.LTable; + +final class Builtin extends LFunction { + + static void addBuiltins(LTable table) { + table.luaSetTable( new LString( "print" ), new Builtin(0) ); + } + + private static final int PRINT = 0; + private int id; + Builtin( int id ) { + this.id = id; + } + + // perform a lua call + public void luaStackCall(StackState state, int base, int nresults) { + switch ( id ) { + case PRINT: + System.out.println( String.valueOf( state.stack[base+1] ) ); + return; + default: + luaUnsupportedOperation(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/lua/GlobalState.java b/src/main/java/lua/GlobalState.java new file mode 100644 index 00000000..17d5dcc1 --- /dev/null +++ b/src/main/java/lua/GlobalState.java @@ -0,0 +1,49 @@ +package lua; + +import java.util.Hashtable; + +import lua.value.LTable; +import lua.value.LValue; + +/** +** `global state', shared by all threads of this state +*/ +public class GlobalState { + +// typedef struct global_State { +// stringtable strt; /* hash table for strings */ + Hashtable strt; /* hash table for strings */ +// lua_Alloc frealloc; /* function to reallocate memory */ +// void *ud; /* auxiliary data to `frealloc' */ +// lu_byte currentwhite; +// lu_byte gcstate; /* state of garbage collector */ +// int sweepstrgc; /* position of sweep in `strt' */ +// GCObject *rootgc; /* list of all collectable objects */ +// GCObject **sweepgc; /* position of sweep in `rootgc' */ +// GCObject *gray; /* list of gray objects */ +// GCObject *grayagain; /* list of objects to be traversed atomically */ +// GCObject *weak; /* list of weak tables (to be cleared) */ +// GCObject *tmudata; /* last element of list of userdata to be GC */ +// Mbuffer buff; /* temporary buffer for string concatentation */ + StringBuffer buff; /* temporary buffer for string concatentation */ +// lu_mem GCthreshold; +// lu_mem totalbytes; /* number of bytes currently allocated */ +// lu_mem estimate; /* an estimate of number of bytes actually in use */ +// lu_mem gcdept; /* how much GC is `behind schedule' */ +// int gcpause; /* size of pause between successive GCs */ +// int gcstepmul; /* GC `granularity' */ +// lua_CFunction panic; /* to be called in unprotected errors */ +// TValue l_registry; +// struct lua_State *mainthread; + StackState mainthread; +// UpVal uvhead; /* head of double-linked list of all open upvalues */ +// struct Table *mt[NUM_TAGS]; /* metatables for basic types */ +// TString *tmname[TM_N]; /* array with tag-method names */ +// } global_State; +// + public static LValue getGlobalsTable() { + LTable table = new LTable(); + Builtin.addBuiltins( table ); + return table; + } +} diff --git a/src/main/java/lua/Lua.java b/src/main/java/lua/Lua.java new file mode 100644 index 00000000..c36d173e --- /dev/null +++ b/src/main/java/lua/Lua.java @@ -0,0 +1,383 @@ +package lua; +/** + * Constants for lua limits and opcodes + * + * @author jim_roseborough + * + */ +public class Lua { + + // from llimits.h + + /** maximum stack for a Lua function */ + public static final int MAXSTACK = 250; + + /** minimum size for the string table (must be power of 2) */ + public static final int MINSTRTABSIZE = 32; + + /** minimum size for string buffer */ + public static final int LUA_MINBUFFER = 32; + + + // from lopcodes.h + + /*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + `A' : 8 bits + `B' : 9 bits + `C' : 9 bits + `Bx' : 18 bits (`B' and `C' together) + `sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. + ===========================================================================*/ + + + /* basic instruction format */ + public static final int iABC = 0; + public static final int iABx = 1; + public static final int iAsBx = 2; + + + /* + ** size and position of opcode arguments. + */ + public static final int SIZE_C = 9; + public static final int SIZE_B = 9; + public static final int SIZE_Bx = (SIZE_C + SIZE_B); + public static final int SIZE_A = 8; + + public static final int SIZE_OP = 6; + + public static final int POS_OP = 0; + public static final int POS_A = (POS_OP + SIZE_OP); + public static final int POS_C = (POS_A + SIZE_A); + public static final int POS_B = (POS_C + SIZE_C); + public static final int POS_Bx = POS_C; + + + public static final int MAX_OP = ((1<>1); /* `sBx' is signed */ + + public static final int MASK_OP = ((1<> POS_OP) & MAX_OP; + } + public static int SET_OPCODE(int i,int o) { + return (i & (MASK_NOT_OP)) | ((o & MAX_OP) << POS_OP); + } + + public static int GETARG_A(int i) { + return (i >> POS_A) & MAXARG_A; + } + public static int SETARG_A(int i,int u) { + return (i & (MASK_NOT_A)) | ((u & MAXARG_A) << POS_A); + } + + public static int GETARG_B(int i) { + return (i >> POS_B) & MAXARG_B; + } + public static int SETARG_B(int i,int u) { + return (i & (MASK_NOT_B)) | ((u & MAXARG_B) << POS_B); + } + + public static int GETARG_C(int i) { + return (i >> POS_C) & MAXARG_C; + } + public static int SETARG_C(int i,int u) { + return (i & (MASK_NOT_C)) | ((u & MAXARG_C) << POS_C); + } + + public static int GETARG_Bx(int i) { + return (i >> POS_Bx) & MAXARG_Bx; + } + public static int SETARG_Bx(int i,int u) { + return (i & (MASK_NOT_Bx)) | ((u & MAXARG_Bx) << POS_Bx); + } + + public static int GETARG_sBx(int i) { + return ((i >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + } + public static int SETARG_sBx(int i,int u) { + return (i & (MASK_NOT_Bx)) | ((u + MAXARG_sBx) << POS_Bx); + } + + public static int CREATE_ABC(int o, int a, int b, int c) { + return (o< C) then pc++ */ + public static final int OP_TESTSET = 27; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + + public static final int OP_CALL = 28; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_TAILCALL = 29; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_RETURN = 30; /* A B return R(A), ... ,R(A+B-2) (see note) */ + + public static final int OP_FORLOOP = 31; /* A sBx R(A)+=R(A+2); + if R(A) =) R(A)*/ + public static final int OP_CLOSURE = 36; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + + public static final int OP_VARARG = 37; /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ + + + public static final int NUM_OPCODES = OP_VARARG + 1; + + + + /*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets `top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top' + + (*) In OP_SETLIST, if (B == 0) then B = `top'; + if (C == 0) then next `instruction' is real C + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump + ===========================================================================*/ + + + /* + ** masks for instruction properties. The format is: + ** bits 0-1: op mode + ** bits 2-3: C arg mode + ** bits 4-5: B arg mode + ** bit 6: instruction set register A + ** bit 7: operator is a test + */ + + public static final int OpArgN = 0; /* argument is not used */ + public static final int OpArgU = 1; /* argument is used */ + public static final int OpArgR = 2; /* argument is a register or a jump offset */ + public static final int OpArgK = 3; /* argument is a constant or register/constant */ + + public static final int[] luaP_opmodes = { + /* T A B C mode opcode */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_MOVE */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_LOADK */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_LOADBOOL */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LOADNIL */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_GETUPVAL */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_GETGLOBAL */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABLE */ + (0<<7) | (0<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_SETGLOBAL */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_SETUPVAL */ + (0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABLE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_NEWTABLE */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_SELF */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_ADD */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SUB */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MUL */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_DIV */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MOD */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_POW */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_UNM */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_NOT */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LEN */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgR<<2) | (iABC), /* OP_CONCAT */ + (0<<7) | (0<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_JMP */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_EQ */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LT */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LE */ + (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TEST */ + (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TESTSET */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_CALL */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_TAILCALL */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_RETURN */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORLOOP */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORPREP */ + (1<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORLOOP */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_SETLIST */ + (0<<7) | (0<<6) | (OpArgN<<4) | (OpArgN<<2) | (iABC), /* OP_CLOSE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABx), /* OP_CLOSURE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_VARARG */ + }; + + public static int getOpMode(int m) { + return luaP_opmodes[m] & 3; + } + public static int getBMode(int m) { + return (luaP_opmodes[m] >> 4) & 3; + } + public static int getCMode(int m) { + return (luaP_opmodes[m] >> 2) & 3; + } + public static boolean testAMode(int m) { + return 0 != (luaP_opmodes[m] & (1 << 6)); + } + public static boolean testTMode(int m) { + return 0 != (luaP_opmodes[m] & (1 << 7)); + } + + + /** opcode names */ + public static final String[] luaP_opnames = { + "MOVE", + "LOADK", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETGLOBAL", + "GETTABLE", + "SETGLOBAL", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + null, + }; + + + /** number of list items to accumulate before a SETLIST instruction */ + public static final int LFIELDS_PER_FLUSH = 50; + +} diff --git a/src/main/java/lua/io/Closure.java b/src/main/java/lua/io/Closure.java new file mode 100644 index 00000000..72344631 --- /dev/null +++ b/src/main/java/lua/io/Closure.java @@ -0,0 +1,23 @@ +package lua.io; + +import lua.StackState; +import lua.value.LValue; + +public class Closure extends LValue { + public LValue env; + public Proto p; + public UpVal[] upVals; + public Closure(StackState state, Proto p) { + this.env = state.gt(); + this.p = p; + upVals = new UpVal[p.nups]; + for ( int i=0; iLua') */ + public static final String LUA_SIGNATURE = "\033Lua"; + + + /** for header of binary files -- this is Lua 5.1 */ + public static final int LUAC_VERSION = 0x51; + + /** for header of binary files -- this is the official format */ + public static final int LUAC_FORMAT = 0; + + /** size of header of binary files */ + public static final int LUAC_HEADERSIZE = 12; + + /** expected lua header bytes */ + private static final int LUAC_HEADER_SIGNATURE = ('\033'<<24) | ('L'<<16) | ('u'<<8) | ('a'); + + // values read from the header + private int luacVersion; + private int luacFormat; + private boolean luacLittleEndian; + private int luacSizeofInt; + private int luacSizeofSizeT; + private int luacSizeofInstruction; + private int luacSizeofLuaNumber; + private boolean luacIsNumberIntegral; + + /** The lua state that is loading the code */ + private StackState L; + + /** input stream from which we are loading */ + private DataInputStream is; + + /** Name of what is being loaded? */ + String name; + + + private static final int LUA_TNONE = (-1); + + private static final int LUA_TNIL = 0; + private static final int LUA_TBOOLEAN = 1; + private static final int LUA_TLIGHTUSERDATA = 2; + private static final int LUA_TNUMBER = 3; + private static final int LUA_TSTRING = 4; + private static final int LUA_TTABLE = 5; + private static final int LUA_TFUNCTION = 6; + private static final int LUA_TUSERDATA = 7; + private static final int LUA_TTHREAD = 8; + +// /* +// ** $Id$ +// ** load precompiled Lua chunks +// ** See Copyright Notice in lua.h +// */ +// +// #include +// +// #define lundump_c +// #define LUA_CORE +// +// #include "lua.h" +// +// #include "ldebug.h" +// #include "ldo.h" +// #include "lfunc.h" +// #include "lmem.h" +// #include "lobject.h" +// #include "lstring.h" +// #include "lundump.h" +// #include "lzio.h" +// +// typedef struct { +// lua_State* L; +// ZIO* Z; +// Mbuffer* b; +// const char* name; +// } LoadState; +// +// #ifdef LUAC_TRUST_BINARIES +// #define IF(c,s) +// #else +// #define IF(c,s) if (c) error(S,s) +// +// static void error(LoadState* S, const char* why) +// { +// luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); +// luaD_throw(S->L,LUA_ERRSYNTAX); +// } +// #endif +// +// #define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) +// #define LoadByte(S) (lu_byte)LoadChar(S) +// #define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) +// #define LoadVector(S,b,n,size) LoadMem(S,b,n,size) +// +// static void LoadBlock(LoadState* S, void* b, size_t size) +// { +// size_t r=luaZ_read(S->Z,b,size); +// IF (r!=0, "unexpected end"); +// } +// +// static int LoadChar(LoadState* S) +// { +// char x; +// LoadVar(S,x); +// return x; +// } + int loadByte() throws IOException { + return is.readUnsignedByte(); + } +// +// static int LoadInt(LoadState* S) +// { +// int x; +// LoadVar(S,x); +// IF (x<0, "bad integer"); +// return x; +// } + int loadInt() throws IOException { + if ( this.luacLittleEndian ) { + int a = is.readUnsignedByte(); + int b = is.readUnsignedByte(); + int c = is.readUnsignedByte(); + int d = is.readUnsignedByte(); + return (d << 24) | (c << 16) | (b << 8) | a; + } else { + return is.readInt(); + } + } + + long loadInt64() throws IOException { + int a,b; + if ( this.luacLittleEndian ) { + a = loadInt(); + b = loadInt(); + } else { + b = loadInt(); + a = loadInt(); + } + return (((long)b)<<32) | (((long)a)&0xffffffffL); + } +// +// static lua_Number LoadNumber(LoadState* S) +// { +// lua_Number x; +// LoadVar(S,x); +// return x; +// } +// +// static TString* LoadString(LoadState* S) +// { +// size_t size; +// LoadVar(S,size); +// if (size==0) +// return NULL; +// else +// { +// char* s=luaZ_openspace(S->L,S->b,size); +// LoadBlock(S,s,size); +// return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ +// } +// } + LString loadString() throws IOException { + int size = loadInt(); + if ( size == 0 ) + return null; + byte[] bytes = new byte[size]; + is.readFully( bytes ); + String s = new String( bytes, 0, size-1 ); + return new LString( s ); + } + + LNumber loadNumber() throws IOException { + if ( this.luacIsNumberIntegral ) { + int value = loadInt(); + return new LInteger( value ); + } else { + long bits = loadInt64(); + double value = Double.longBitsToDouble(bits); + return new LDouble( value ); + } + } +// +// static void LoadCode(LoadState* S, Proto* f) +// { +// int n=LoadInt(S); +// f->code=luaM_newvector(S->L,n,Instruction); +// f->sizecode=n; +// LoadVector(S,f->code,n,sizeof(Instruction)); +// } + public void loadCode( Proto f ) throws IOException { + int n = loadInt(); + int[] code = new int[n]; + for ( int i=0; ik=luaM_newvector(S->L,n,TValue); +// f->sizek=n; +// for (i=0; ik[i]); +// for (i=0; ik[i]; +// int t=LoadChar(S); +// switch (t) +// { +// case LUA_TNIL: +// setnilvalue(o); +// break; +// case LUA_TBOOLEAN: +// setbvalue(o,LoadChar(S)); +// break; +// case LUA_TNUMBER: +// setnvalue(o,LoadNumber(S)); +// break; +// case LUA_TSTRING: +// setsvalue2n(S->L,o,LoadString(S)); +// break; +// default: +// IF (1, "bad constant"); +// break; +// } +// } +// n=LoadInt(S); +// f->p=luaM_newvector(S->L,n,Proto*); +// f->sizep=n; +// for (i=0; ip[i]=NULL; +// for (i=0; ip[i]=LoadFunction(S,f->source); +// } + void loadConstants(Proto f) throws IOException { + int n = loadInt(); + LValue[] values = new LValue[n]; + for ( int i=0; ilineinfo=luaM_newvector(S->L,n,int); +// f->sizelineinfo=n; +// LoadVector(S,f->lineinfo,n,sizeof(int)); + +// n=LoadInt(S); +// f->locvars=luaM_newvector(S->L,n,LocVar); +// f->sizelocvars=n; +// for (i=0; ilocvars[i].varname=NULL; +// for (i=0; ilocvars[i].varname=LoadString(S); +// f->locvars[i].startpc=LoadInt(S); +// f->locvars[i].endpc=LoadInt(S); +// } + +// n=LoadInt(S); +// f->upvalues=luaM_newvector(S->L,n,TString*); +// f->sizeupvalues=n; +// for (i=0; iupvalues[i]=NULL; +// for (i=0; iupvalues[i]=LoadString(S); +// } + void loadDebug( Proto f ) throws IOException { + int n = loadInt(); + f.lineinfo = new int[n]; + for ( int i=0; iL); +// setptvalue2s(S->L,S->L->top,f); incr_top(S->L); +// f->source=LoadString(S); if (f->source==NULL) f->source=p; +// f->linedefined=LoadInt(S); +// f->lastlinedefined=LoadInt(S); +// f->nups=LoadByte(S); +// f->numparams=LoadByte(S); +// f->is_vararg=LoadByte(S); +// f->maxstacksize=LoadByte(S); +// LoadCode(S,f); +// LoadConstants(S,f); +// LoadDebug(S,f); +// IF (!luaG_checkcode(f), "bad code"); +// S->L->top--; +// return f; +// } + public Proto loadFunction(LString p) throws IOException { + Proto f = new Proto(this.L); + this.L.push(f); + f.source = loadString(); + f.linedefined = loadInt(); + f.lastlinedefined = loadInt(); + f.nups = loadByte(); + f.numparams = loadByte(); + f.is_vararg = (0 != loadByte()); + f.maxstacksize = loadByte(); + loadCode(f); + loadConstants(f); + loadDebug(f); + + // TODO: add check here, for debugging purposes, I believe + // see ldebug.c +// IF (!luaG_checkcode(f), "bad code"); + + this.L.pop(); + return f; + } +// +// static void LoadHeader(LoadState* S) +// { +// char h[LUAC_HEADERSIZE]; +// char s[LUAC_HEADERSIZE]; +// luaU_header(h); +// LoadBlock(S,s,LUAC_HEADERSIZE); +// IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header"); +// } + public void loadHeader() throws IOException { + int sig = is.readInt(); + luacVersion = is.readByte(); + luacFormat = is.readByte(); + luacLittleEndian = (0 != is.readByte()); + luacSizeofInt = is.readByte(); + luacSizeofSizeT = is.readByte(); + luacSizeofInstruction = is.readByte(); + luacSizeofLuaNumber = is.readByte(); + luacIsNumberIntegral = (0 != is.readByte()); + if ( sig != LUAC_HEADER_SIGNATURE ) + throw new IllegalArgumentException("bad signature"); + } +// +// /* +// ** load precompiled chunk +// */ +// Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) +// { +// LoadState S; +// if (*name=='@' || *name=='=') +// S.name=name+1; +// else if (*name==LUA_SIGNATURE[0]) +// S.name="binary string"; +// else +// S.name=name; +// S.L=L; +// S.Z=Z; +// S.b=buff; +// LoadHeader(&S); +// return LoadFunction(&S,luaS_newliteral(L,"=?")); +// } + + public static Proto undump( StackState L, InputStream stream, String name ) throws IOException { + String sname = name; + if ( name.startsWith("@") || name.startsWith("=") ) + sname = name.substring(1); + else if ( name.startsWith("\033") ) + sname = "binary string"; + LoadState s = new LoadState( L, stream, sname ); + s.loadHeader(); + LString literal = new LString(L, "=?"); + return s.loadFunction( literal ); + } + + /** Private constructor for create a load state */ + private LoadState( StackState L, InputStream stream, String name ) { + this.L = L; + this.name = name; + this.is = new DataInputStream( stream ); + } +} diff --git a/src/main/java/lua/io/LocVars.java b/src/main/java/lua/io/LocVars.java new file mode 100644 index 00000000..64bf9a68 --- /dev/null +++ b/src/main/java/lua/io/LocVars.java @@ -0,0 +1,16 @@ +package lua.io; + +import lua.value.LString; + +public class LocVars { + final LString m_varname; + final int m_startpc; + final int m_endpc; + + public LocVars(LString varname, int startpc, int endpc) { + this.m_varname = varname; + this.m_startpc = startpc; + this.m_endpc = endpc; + } + +} diff --git a/src/main/java/lua/io/Proto.java b/src/main/java/lua/io/Proto.java new file mode 100644 index 00000000..516fab4a --- /dev/null +++ b/src/main/java/lua/io/Proto.java @@ -0,0 +1,39 @@ +package lua.io; + +import lua.StackState; +import lua.value.LValue; +import lua.value.LString; + +/* +** Function Prototypes +*/ +public class Proto extends LValue { + // TODO: what to do with state? + public Proto(StackState l) { + } + + public LValue[] k; /* constants used by the function */ +// TValue *k; /* constants used by the function */ +// Instruction *code; + public int[] code; +// struct Proto **p; /* functions defined inside the function */ + public Proto[] p; +// int *lineinfo; /* map from opcodes to source lines */ + public int[] lineinfo; +// struct LocVar *locvars; /* information about local variables */ + public LocVars[] locvars; +// TString **upvalues; /* upvalue names */ + public LString[] upvalues; + public LString source; + public int nups; + public int sizeupvalues; + public int sizek; /* size of `k' */ + public int sizecode; + public int sizep; /* size of `p' */ + public int linedefined; + public int lastlinedefined; +// GCObject *gclist; + public int numparams; + public boolean is_vararg; + public int maxstacksize; +} diff --git a/src/main/java/lua/io/UpVal.java b/src/main/java/lua/io/UpVal.java new file mode 100644 index 00000000..059394f8 --- /dev/null +++ b/src/main/java/lua/io/UpVal.java @@ -0,0 +1,5 @@ +package lua.io; + +public class UpVal { + +} diff --git a/src/main/java/lua/value/LBoolean.java b/src/main/java/lua/value/LBoolean.java new file mode 100644 index 00000000..0a8ba132 --- /dev/null +++ b/src/main/java/lua/value/LBoolean.java @@ -0,0 +1,28 @@ +package lua.value; + +public final class LBoolean extends LValue { + + public static final LBoolean TRUE = new LBoolean("true",true); + + public static final LBoolean FALSE = new LBoolean("false",false); + + private final String m_name; + private final boolean m_value; + + private LBoolean( String name, boolean value ) { + this.m_name = name; + this.m_value = value; + } + + public final String luaAsString() { + return m_name; + } + + public final boolean luaAsBoolean() { + return m_value; + } + + public final static LBoolean valueOf(boolean value) { + return value? TRUE: FALSE; + } +} diff --git a/src/main/java/lua/value/LDouble.java b/src/main/java/lua/value/LDouble.java new file mode 100644 index 00000000..453453d9 --- /dev/null +++ b/src/main/java/lua/value/LDouble.java @@ -0,0 +1,48 @@ +package lua.value; + +import lua.Lua; + +public class LDouble extends LNumber { + + private final double m_value; + + public LDouble(double value) { + this.m_value = value; + } + + public String luaAsString() { + return String.valueOf(m_value); + } + + // binary operations on integers, first dispatch + public LValue luaBinOpUnknown(int opcode, LValue lhs) { + return lhs.luaBinOpDouble( opcode, this.m_value ); + } + + // binary operations on mixtures of doubles and integers + public LValue luaBinOpInteger(int opcode, int rhs) { + return luaBinOpDoubleDouble( opcode, m_value, (double) rhs ); + } + + // binary operations on doubles + public LValue luaBinOpDouble(int opcode, double rhs) { + return luaBinOpDoubleDouble( opcode, m_value, rhs ); + } + + public static LValue luaBinOpDoubleDouble( int opcode, double lhs, double rhs ) { + switch ( opcode ) { + case Lua.OP_ADD: return new LDouble( lhs + rhs ); + case Lua.OP_SUB: return new LDouble( lhs - rhs ); + case Lua.OP_MUL: return new LDouble( lhs * rhs ); + case Lua.OP_DIV: return new LDouble( lhs / rhs ); + case Lua.OP_MOD: return new LDouble( lhs % rhs ); + case Lua.OP_POW: return new LDouble( Math.pow(lhs, rhs) ); + } + return luaUnsupportedOperation(); + } + + public int luaAsInt() { + return (int) m_value; + } + +} diff --git a/src/main/java/lua/value/LFunction.java b/src/main/java/lua/value/LFunction.java new file mode 100644 index 00000000..89f5b0d0 --- /dev/null +++ b/src/main/java/lua/value/LFunction.java @@ -0,0 +1,7 @@ +package lua.value; + +public class LFunction extends LValue { + + + +} diff --git a/src/main/java/lua/value/LInteger.java b/src/main/java/lua/value/LInteger.java new file mode 100644 index 00000000..a71972bc --- /dev/null +++ b/src/main/java/lua/value/LInteger.java @@ -0,0 +1,43 @@ +package lua.value; + +import lua.Lua; + +public class LInteger extends LNumber { + + private final int m_value; + + public LInteger(int value) { + this.m_value = value; + } + + public int luaAsInt() { + return m_value; + } + + public String luaAsString() { + return String.valueOf(m_value); + } + + // binary operations on integers, first dispatch + public LValue luaBinOpUnknown(int opcode, LValue lhs) { + return lhs.luaBinOpInteger( opcode, this.m_value ); + } + + // binary operations on integers + public LValue luaBinOpInteger(int opcode, int rhs) { + switch ( opcode ) { + case Lua.OP_ADD: return new LInteger( m_value + rhs ); + case Lua.OP_SUB: return new LInteger( m_value - rhs ); + case Lua.OP_MUL: return new LInteger( m_value * rhs ); + case Lua.OP_DIV: return new LInteger( m_value / rhs ); + case Lua.OP_MOD: return new LInteger( m_value % rhs ); + case Lua.OP_POW: return new LInteger( (int) Math.pow(m_value, rhs) ); + } + return luaUnsupportedOperation(); + } + + // binary operations on mixed integer, double + public LValue luaBinOpDouble(int opcode, double rhs) { + return LDouble.luaBinOpDoubleDouble(opcode, (double) m_value, rhs ); + } +} diff --git a/src/main/java/lua/value/LNil.java b/src/main/java/lua/value/LNil.java new file mode 100644 index 00000000..b671490e --- /dev/null +++ b/src/main/java/lua/value/LNil.java @@ -0,0 +1,13 @@ +package lua.value; + +public final class LNil extends LValue { + public static final LNil NIL = new LNil(); + + public final String luaAsString() { + return "nil"; + } + + public boolean luaAsBoolean() { + return false; + } +} diff --git a/src/main/java/lua/value/LNumber.java b/src/main/java/lua/value/LNumber.java new file mode 100644 index 00000000..40886607 --- /dev/null +++ b/src/main/java/lua/value/LNumber.java @@ -0,0 +1,6 @@ +package lua.value; + +abstract +public class LNumber extends LValue { + +} diff --git a/src/main/java/lua/value/LString.java b/src/main/java/lua/value/LString.java new file mode 100644 index 00000000..0f0b2dd3 --- /dev/null +++ b/src/main/java/lua/value/LString.java @@ -0,0 +1,21 @@ +package lua.value; + +import lua.StackState; + +public class LString extends LValue { + + final String m_string; + + public LString(String string) { + this.m_string = string; + } + + // TODO: what to do with LuaState? + public LString(StackState l, String string) { + this(string); + } + + public String luaAsString() { + return m_string; + } +} diff --git a/src/main/java/lua/value/LTable.java b/src/main/java/lua/value/LTable.java new file mode 100644 index 00000000..f2b41457 --- /dev/null +++ b/src/main/java/lua/value/LTable.java @@ -0,0 +1,29 @@ +package lua.value; + +import java.util.Hashtable; +import java.util.Vector; + +public class LTable extends LValue { + + private Hashtable m_hash = new Hashtable(); + private Vector m_array = new Vector(); + + public LTable() { + } + + public LTable(int narray, int nhash) { + } + + public void luaSetTable(LValue key, LValue val) { + m_hash.put( key.luaAsString(), val ); + m_array.add( val ); + } + + public LValue luaGetTable(LValue key) { + return (LValue) m_hash.get( key.luaAsString() ); + } + + public String luaAsString() { + return m_hash.toString(); + } +} diff --git a/src/main/java/lua/value/LThread.java b/src/main/java/lua/value/LThread.java new file mode 100644 index 00000000..90df63b7 --- /dev/null +++ b/src/main/java/lua/value/LThread.java @@ -0,0 +1,5 @@ +package lua.value; + +public class LThread extends LValue { + +} diff --git a/src/main/java/lua/value/LValue.java b/src/main/java/lua/value/LValue.java new file mode 100644 index 00000000..a52bd0ad --- /dev/null +++ b/src/main/java/lua/value/LValue.java @@ -0,0 +1,65 @@ +package lua.value; + +import lua.StackState; + +abstract +public class LValue { + + protected static LValue luaUnsupportedOperation() { + throw new java.lang.UnsupportedOperationException(); + } + + // test if value is true + public boolean luaAsBoolean() { + return true; + } + + // perform a lua call + public void luaStackCall(StackState state, int base, int nresults) { + luaUnsupportedOperation(); + } + + // unsupported except for numbers + public LValue luaBinOpUnknown(int opcode, LValue lhs) { + return luaUnsupportedOperation(); + } + + // unsupported except for numbers + public LValue luaBinOpInteger(int opcode, int m_value) { + return luaUnsupportedOperation(); + } + + // unsupported except for numbers + public LValue luaBinOpDouble(int opcode, double m_value) { + return luaUnsupportedOperation(); + } + + /** set a value in a table + */ + public void luaSetTable(LValue key, LValue value) { + luaUnsupportedOperation(); + } + + /** Get a value from a table */ + public LValue luaGetTable(LValue value) { + return luaUnsupportedOperation(); + } + + /** Get the value as a String + */ + public String luaAsString() { + return super.toString(); + } + + /** Override standard toString with lua String conversion by default */ + public String toString() { + return luaAsString(); + } + + /** Return value as an integer */ + public int luaAsInt() { + luaUnsupportedOperation(); + return 0; + } + +} diff --git a/src/test/java/LuacRunner.java b/src/test/java/LuacRunner.java new file mode 100644 index 00000000..3d4b4844 --- /dev/null +++ b/src/test/java/LuacRunner.java @@ -0,0 +1,49 @@ + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import lua.StackState; +import lua.io.Closure; +import lua.io.LoadState; +import lua.io.Proto; +import lua.value.LString; + +/** + * Program to run a compiled lua chunk for test purposes + * + * @author jim_roseborough + */ +public class LuacRunner { + + public static void main( String[] args ) throws IOException { + + // get script name + String script = (args.length>0? args[0]: "src/test/res/test1.luac"); + System.out.println("loading '"+script+"'"); + + // new lua state + StackState state = new StackState(); + + // push args onto stack + for ( int i=1; i=1-mr%Hs_y0P!W^#yMk}w!?=ZS;Y0Wc zesg4u5fmE-zT4mI?0vJlcUzNra|#I|bkGG*&#tDkgYnJ0VPfIBhr|)JPE|Eq&P(J$kC%@uO9xZ#}oS$_K^av6Rs0Hr{rEa zFPVJ{=ji{I;40yo=#VqL=_}479@G2LV~~@NrS)(3F+@V29wG2Co!=s?5$+$Z?`H5M z#{1C%P%ofk&^OpH*fbazY#WRWb_{k6_6+t7#x)PohK_CDfT|+*B9-BK1#ZYo)e~4~ v-F=h3tB2G1hFWIlE7`?LcKMkp=vP-C-HUqtCf&nN(!2eO^zZs_u|*pnm>4$% literal 0 HcmV?d00001 diff --git a/src/test/res/test3.lua b/src/test/res/test3.lua new file mode 100644 index 00000000..c14ca29b --- /dev/null +++ b/src/test/res/test3.lua @@ -0,0 +1,6 @@ +a = func(1, 2, 3) +a = func(3, 2, 1) +a = func(func(),func(),func()) + + + diff --git a/src/test/res/test3.luac b/src/test/res/test3.luac new file mode 100644 index 0000000000000000000000000000000000000000..296ef9ecf5ff1889a514663e4f51a8339b9b0979 GIT binary patch literal 283 zcmb34DNPJyU}Rxo;b7oqU|?`4Ni8lh*2^hP1ae@2iA4;^WOZO*aBN^;Xgt8caFBtK zL8gI$i5(^mVZe6WWy7#yGs4hN7p$UTfWFvuuu7-Rr37^n;k0D?Oi8UO$Q literal 0 HcmV?d00001 diff --git a/src/test/res/test4.lua b/src/test/res/test4.lua new file mode 100644 index 00000000..ef651b51 --- /dev/null +++ b/src/test/res/test4.lua @@ -0,0 +1,8 @@ +function Point(x, y) -- "Point" object constructor + return { x = x, y = y } -- Creates and returns a new object (table) +end +array = { Point(10, 20), Point(30, 40), Point(50, 60) } -- Creates array of points +print(array[2].y) -- Prints 40 + + + diff --git a/src/test/res/test4.luac b/src/test/res/test4.luac new file mode 100644 index 0000000000000000000000000000000000000000..ac61652376f6e09ee3fe45de4f6c2bc7118281cd GIT binary patch literal 472 zcmbVIJ!=9%5Pfs5AH*ME5wQr?sjOuM6@(D9x49xkAQ(AM@Vc=`VSYpKw|GMSMSe`) z?A>-&4m{?)nSHaf`}nd?R}kl12VDR@f7|7w=Z}3#5hH2!NQXq5cmQO^d{m&k0BcqG zqAnqjiQtF&ms*a3Nh$b)%9E#;Xd=OkdG4>6>lJ+u89>9;t_5KE`M${!Xqshdfd-3= zp|8m(l5r%{NEVU2`Y>Rd*{s?J?-I@_xMUTULoY$2N&cQJ#%|o%N)UWs8T^~ jk3i^HH^L@Gejul>u2ddV7WR>L)^4}jy|5i_f#neYkYXo_ literal 0 HcmV?d00001