Not a terrible start
2 files changed, 251 insertions(+), 0 deletions(-)

A => README.md
A => src/main.fnl
A => README.md +25 -0
@@ 0,0 1,25 @@ 
+# What is this
+
+This is an attempt to write a compiler from Lua 5.4 bytecode to
+webassembly.
+
+Why?  As the name implies, to have something to do.
+
+# Building/running
+
+Runs with Fennel 1.2.0 or maybe something else.
+
+This uses integer math and bitops for opcodes, so requires Lua >= 5.3 to
+run.  Probably assumes it's running on a 64-bit machine, which is
+hilarious since 64-bit webassembly kinda doesn't exist yet.
+
+# References
+
+ * <https://www.lua.org/ftp/lua-5.4.4.tar.gz>
+ * <https://the-ravi-programming-language.readthedocs.io/en/latest/lua_bytecode_reference.html>
+   (outdated)
+
+
+# License
+
+Mozilla Public License 2.0

          
A => src/main.fnl +226 -0
@@ 0,0 1,226 @@ 
+; From lopcodes.h:
+;
+; /*===========================================================================
+;   We assume that instructions are unsigned 32-bit integers.
+;   All instructions have an opcode in the first 7 bits.
+;   Instructions can have the following formats:
+;
+;         3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
+;         1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+; iABC          C(8)     |      B(8)     |k|     A(8)      |   Op(7)     |
+; iABx                Bx(17)               |     A(8)      |   Op(7)     |
+; iAsBx              sBx (signed)(17)      |     A(8)      |   Op(7)     |
+; iAx                           Ax(25)                     |   Op(7)     |
+; isJ                           sJ(25)                     |   Op(7)     |
+;
+;   A signed argument is represented in excess K: the represented value is
+;   the written unsigned value minus K, where K is half the maximum for the
+;   corresponding unsigned argument.
+; ===========================================================================*/
+;
+;
+; enum OpMode {iABC, iABx, iAsBx, iAx, isJ};  /* basic instruction formats */
+;
+; /*
+; ** size and position of opcode arguments.
+; */
+; #define SIZE_C          8
+; #define SIZE_B          8
+; #define SIZE_Bx         (SIZE_C + SIZE_B + 1)
+; #define SIZE_A          8
+; #define SIZE_Ax         (SIZE_Bx + SIZE_A)
+; #define SIZE_sJ         (SIZE_Bx + SIZE_A)
+;
+; #define SIZE_OP         7
+;
+; #define POS_OP          0
+;
+; #define POS_A           (POS_OP + SIZE_OP)
+; #define POS_k           (POS_A + SIZE_A)
+; #define POS_B           (POS_k + 1)
+; #define POS_C           (POS_B + SIZE_B)
+;
+; #define POS_Bx          POS_k
+;
+; #define POS_Ax          POS_A
+;
+; #define POS_sJ          POS_A
+; ]]
+;
+; enum OpMode {iABC, iABx, iAsBx, iAx, isJ};  /* basic instruction formats */
+
+;; Opcodes with metadata, as listed in lopcodes.h
+;;
+;; `code` is the integer ID of the opcode from the Lua OpCode struct
+;;
+;; Valid formats are per the instruction formats above:
+;; :iABC :iABx :iAsBx :iAx :isJ
+;; It seems that many instructions use :iABC format but do not use
+;; all the args?
+;;
+;; :name is the printable name of the opcode.
+(local Opcodes [
+  {:name :OpMove :format :iABC}
+  {:name :OpLoadI :format :iAsBx}
+  {:name :OpLoadF :format :iAsBx}
+  {:name :OpLoadK :format :iABx}
+  {:name :OpLoadKX :format :iABC}
+  {:name :OploadFalse :format :iABC}
+  {:name :OpLFalseSkip :format :iABC}
+  {:name :OpLoadTrue :format :iABC}
+  {:name :OpLoadNil :format :iABC}
+  {:name :OpGetUpval :format :iABC}
+  {:name :OpSetUpval :format :iABC}
+
+  {:name :OpGetTabUp :format :iABC}
+  {:name :OpGetTable :format :iABC}
+  {:name :OpGetI :format :iABC}
+  {:name :OpGetField :format :iABC}
+
+  {:name :OpSetTabUp :format :iABC}
+  {:name :OpSetTable :format :iABC}
+  {:name :OpSetI :format :iABC}
+  {:name :OpSetField :format :iABC}
+
+  {:name :OpSelf :format :iABC}
+
+  {:name :OpAddI :format :iABC}
+
+  {:name :OpAddK :format :iABC}
+  {:name :OpSubK :format :iABC}
+  {:name :OpMulK :format :iABC}
+  {:name :OpModK :format :iABC}
+  {:name :OpPowK :format :iABC}
+  {:name :OpDivK :format :iABC}
+  {:name :OpIDivK :format :iABC}
+
+  {:name :OpBAndK :format :iABC}
+  {:name :OpBOrK :format :iABC}
+  {:name :OpBXorK :format :iABC}
+
+  {:name :OpShrI :format :iABC}
+  {:name :OpShlI :format :iABC}
+
+  {:name :OpAdd :format :iABC}
+  {:name :OpSub :format :iABC}
+  {:name :OpMul :format :iABC}
+  {:name :OpMod :format :iABC}
+  {:name :OpPow :format :iABC}
+  {:name :OpDiv :format :iABC}
+  {:name :OpIDiv :format :iABC}
+
+  {:name :OpBand :format :iABC}
+  {:name :OpBor :format :iABC}
+  {:name :OpBXor :format :iABC}
+  {:name :OpShl :format :iABC}
+  {:name :OpShr :format :iABC}
+
+  {:name :OpMMBin :format :i}
+  {:name :OpMMBinI :format :i}
+  {:name :OpMMBinK :format :i}
+
+  {:name :OpUnm :format :iABC} ; unary minus
+  {:name :OpBNot :format :iABC}
+  {:name :OpNot :format :iABC}
+  {:name :OpLen :format :iABC}
+
+  {:name :OpConcat :format :iABC}
+
+  {:name :OpClose :format :iABC}
+  {:name :OpTbc :format :iABC} ; "to be closed"
+  {:name :OpJmp :format :isJ}
+  {:name :OpEq :format :iABC}
+  {:name :OpLt :format :iABC}
+  {:name :OpLe :format :iABC}
+
+  {:name :OpEqK :format :iABC}
+  {:name :OpEqI :format :iABC}
+  {:name :OpLtI :format :iABC}
+  {:name :OpLeI :format :iABC}
+  {:name :OpGtI :format :iABC}
+  {:name :OpGeI :format :iABC}
+
+  {:name :OpTest :format :iABC}
+  {:name :OpTestSet :format :iABC}
+
+  {:name :OpCall :format :iABC}
+  {:name :OpTailCall :format :iABC}
+
+  {:name :OpReturn :format :iABC}
+  {:name :OpReturn0 :format :iABC}
+  {:name :OpReturn1 :format :iABC}
+
+  {:name :OpForLoop :format :iABx}
+  {:name :OpForPrep :format :iABx}
+
+  {:name :OpTForPrep :format :iABx}
+  {:name :OpTForCall :format :iABC}
+  {:name :OpTForLoop :format :iABx}
+
+  {:name :OpSetList :format :iABC}
+
+  {:name :OpClosure :format :iABx}
+
+  {:name :OpVarArg :format :iABC}
+
+  {:name :OpVarArgPrep :format :iABC}
+
+  {:name :OpExtraArg :format :iAx}])
+
+(local OpcodeNames
+  (collect [ky vl (pairs Opcodes)]
+    (values vl.name ky)))
+
+
+(fn opcode-idx [op]
+  "Returns the opcode number of the given opcode name"
+  (- (. OpcodeNames op) 1))
+
+(fn idx-opcode [i]
+  "Returns the opcode struct for the opcode with the given number"
+  (. Opcodes (+ i 1)))
+
+(fn extract [i offset len]
+  "Extract `len` bits of integer `i`, starting from bit `offset`
+  This operates on 32-bit integers.  If you hand it an integer with
+  a bit >31 set it will not do the right thing."
+  ; From lopcodes.h to generate a mask with n 1 bits at position p:
+  ; ((~((~(Instruction)0)<<(n)))<<(p))
+  (let [mask (lshift (bnot (lshift (bnot 0) len)) offset)]
+    (rshift (band i mask) offset)))
+
+(fn decode-abc [i]
+  "Decode an instruction in the :iABC format"
+  0)
+
+(fn decode-abx [i]
+  "Decode an instruction in the :iABx format"
+  0)
+
+(fn decode-absbx [i]
+  "Decode an instruction in the :iAsBx format"
+  0)
+
+(fn decode-ax [i]
+  "Decode an instruction in the :iAx format"
+  0)
+
+(fn decode-asj [i]
+  "Decode an instruction in the :isJ format"
+  0)
+
+(fn decode [i]
+  "Decodes a 32-bit integer into a table containing a Lua opcode"
+  (let [OpSize 7   ; Size of the opcode field, in bits
+        OpPos  0   ; Starting bit number of the opcode field
+        opcode (extract i OpPos OpSize)
+        opcode-format (. (idx-opcode opcode) :format)]
+    (print (string.format "Instruction %d is opcode %i named %s with format %s"
+                          i
+                          opcode
+                          (. (idx-opcode opcode) :name)
+                          opcode-format))))
+
+(print "Opcode" 3 "is" (. (idx-opcode 3) :name))
+(print "Opcode :OpGetI is" (opcode-idx :OpGetI))
+(decode 0x03)