Lua (5.3) Language addition (#4804)
authorDerek Bailey <derek.bailey@thermofisher.com>
Thu, 5 Jul 2018 22:55:57 +0000 (15:55 -0700)
committerWouter van Oortmerssen <aardappel@gmail.com>
Thu, 5 Jul 2018 22:55:57 +0000 (15:55 -0700)
* starting Lua port of python implmention. Syncing commit

* Bulk of Lua module port from Python done. Not tested, only static analysis. Need to work on binary strings. Started work on flatc lua code generation

* Fixed all the basic errors to produced a binary output from the builder, don't know if it is generated correctly, but it contains data, so that must be good

* fixed binary set command that was extending the array improperly

* continued improvement

* Moved lua submodules down a directory so their names don't clash with potential other modules. Added compat module to provide Lua versioning logic

* Successful sample port from Python

* working on testing Lua code with formal tests

* continued to work on tests and fixes to code to make tests pass

* Added reading buffer test

* Changed binaryarray implmentation to use a temporary table for storing data, and then serialize it to a string when requested. This double the rate of building flatbuffers compared to the string approach.

* Didn't need encode module as it just added another layer of indirection that isn't need

* profiled reading buffers, optimizations to increase read performance of monster data to ~7 monster / millisecond

* Writing profiler improvments. Get about
~2 monsters/millisecond building rate

* removed Numpy generation from Lua (came from the Python port)

* math.pow is deprecated in Lua 5.3, so changed to ^ notation. Also added .bat script for starting Lua tests

* adding results of generate_code.bat

* simple edits for code review in PR.

* There was a buffer overflow in inserting the keywords into the unorder set for both the Lua and Python code gens. Changed insertion to use iterators.

* fixed spacing issue

* basic documenation/tutorial updates. Updated sample_binary.lua to reflect the tutorial better

* removed windows-specific build step in Lua tests

39 files changed:
.gitignore
CMakeLists.txt
docs/source/FlatBuffers.md
docs/source/LuaUsage.md [new file with mode: 0644]
docs/source/Tutorial.md
docs/source/doxyfile
docs/source/doxygen_layout.xml
include/flatbuffers/idl.h
lua/flatbuffers.lua [new file with mode: 0644]
lua/flatbuffers/binaryarray.lua [new file with mode: 0644]
lua/flatbuffers/builder.lua [new file with mode: 0644]
lua/flatbuffers/compat.lua [new file with mode: 0644]
lua/flatbuffers/numTypes.lua [new file with mode: 0644]
lua/flatbuffers/view.lua [new file with mode: 0644]
samples/lua/MyGame/Sample/Color.lua [new file with mode: 0644]
samples/lua/MyGame/Sample/Equipment.lua [new file with mode: 0644]
samples/lua/MyGame/Sample/Monster.lua [new file with mode: 0644]
samples/lua/MyGame/Sample/Vec3.lua [new file with mode: 0644]
samples/lua/MyGame/Sample/Weapon.lua [new file with mode: 0644]
samples/sample_binary.lua [new file with mode: 0644]
src/flatc_main.cpp
src/idl_gen_lua.cpp [new file with mode: 0644]
src/idl_gen_python.cpp
tests/LuaTest.bat [new file with mode: 0644]
tests/MyGame/Example/Ability.lua [new file with mode: 0644]
tests/MyGame/Example/Any.lua [new file with mode: 0644]
tests/MyGame/Example/Color.lua [new file with mode: 0644]
tests/MyGame/Example/Monster.lua [new file with mode: 0644]
tests/MyGame/Example/Referrable.lua [new file with mode: 0644]
tests/MyGame/Example/Stat.lua [new file with mode: 0644]
tests/MyGame/Example/Test.lua [new file with mode: 0644]
tests/MyGame/Example/TestSimpleTableWithEnum.lua [new file with mode: 0644]
tests/MyGame/Example/TypeAliases.lua [new file with mode: 0644]
tests/MyGame/Example/Vec3.lua [new file with mode: 0644]
tests/MyGame/Example2/Monster.lua [new file with mode: 0644]
tests/MyGame/InParentNamespace.lua [new file with mode: 0644]
tests/generate_code.bat
tests/generate_code.sh
tests/luatest.lua [new file with mode: 0644]

index 8f026d3..7773161 100644 (file)
@@ -12,7 +12,9 @@
 *.vcxproj.user
 *.sln
 *.suo
+*.opendb
 *.keystore
+**/.vs/**
 **/bin/**
 **/gen/**
 **/libs/**
@@ -25,6 +27,8 @@
 **/CMakeTestfile.cmake
 **/Debug/**
 **/Release/**
+**/RelWithDebInfo/**
+**/x64/ #build artifacts from VS
 build.xml
 local.properties
 project.properties
index 90860c3..ffaff11 100644 (file)
@@ -51,6 +51,7 @@ set(FlatBuffers_Compiler_SRCS
   src/idl_gen_js.cpp
   src/idl_gen_php.cpp
   src/idl_gen_python.cpp
+  src/idl_gen_lua.cpp
   src/idl_gen_fbs.cpp
   src/idl_gen_grpc.cpp
   src/idl_gen_json_schema.cpp
index ff27476..351c38e 100644 (file)
@@ -4,7 +4,7 @@ FlatBuffers    {#flatbuffers_index}
 # Overview {#flatbuffers_overview}
 
 [FlatBuffers](@ref flatbuffers_overview) is an efficient cross platform
-serialization library for C++, C#, C, Go, Java, JavaScript, TypeScript, PHP, and Python.
+serialization library for C++, C#, C, Go, Java, JavaScript, Lua, TypeScript, PHP, and Python.
 It was originally created at Google for game development and other
 performance-critical applications.
 
@@ -134,6 +134,8 @@ sections provide a more in-depth usage guide.
     in your own programs.
 -   How to [use the generated Go code](@ref flatbuffers_guide_use_go) in your
     own programs.
+-   How to [use the generated Lua code](@ref flatbuffers_guide_use_lua) in your
+    own programs.
 -   How to [use the generated JavaScript code](@ref flatbuffers_guide_use_javascript) in your
     own programs.
 -   How to [use the generated TypeScript code](@ref flatbuffers_guide_use_typescript) in your
diff --git a/docs/source/LuaUsage.md b/docs/source/LuaUsage.md
new file mode 100644 (file)
index 0000000..75b1f3b
--- /dev/null
@@ -0,0 +1,81 @@
+Use in Lua    {#flatbuffers_guide_use_lua}
+=============
+
+## Before you get started
+
+Before diving into the FlatBuffers usage in Lua, it should be noted that the
+[Tutorial](@ref flatbuffers_guide_tutorial) page has a complete guide to general
+FlatBuffers usage in all of the supported languages (including Lua). This
+page is designed to cover the nuances of FlatBuffers usage, specific to
+Lua.
+
+You should also have read the [Building](@ref flatbuffers_guide_building)
+documentation to build `flatc` and should be familiar with
+[Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler) and
+[Writing a schema](@ref flatbuffers_guide_writing_schema).
+
+## FlatBuffers Lua library code location
+
+The code for the FlatBuffers Lua library can be found at
+`flatbuffers/lua`. You can browse the library code on the
+[FlatBuffers GitHub page](https://github.com/google/flatbuffers/tree/master/lua).
+
+## Testing the FlatBuffers Lua library
+
+The code to test the Lua library can be found at `flatbuffers/tests`.
+The test code itself is located in [luatest.lua](https://github.com/google/
+flatbuffers/blob/master/tests/luatest.lua).
+
+To run the tests, use the [LuaTest.sh](https://github.com/google/flatbuffers/
+blob/master/tests/LuaTest.sh) shell script.
+
+*Note: This script requires [Lua 5.3](https://www.lua.org/) to be
+installed.*
+
+## Using the FlatBuffers Lua library
+
+*Note: See [Tutorial](@ref flatbuffers_guide_tutorial) for a more in-depth
+example of how to use FlatBuffers in Lua.*
+
+There is support for both reading and writing FlatBuffers in Lua.
+
+To use FlatBuffers in your own code, first generate Lua classes from your
+schema with the `--lua` option to `flatc`. Then you can include both
+FlatBuffers and the generated code to read or write a FlatBuffer.
+
+For example, here is how you would read a FlatBuffer binary file in Lua:
+First, require the module and the generated code. Then read a FlatBuffer binary
+file into a `string`, which you pass to the `GetRootAsMonster` function:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.lua}
+    -- require the library
+    local flatbuffers = require("flatbuffers")
+    
+    -- require the generated code
+    local monster = require("MyGame.Sample.Monster")
+
+    -- read the flatbuffer from a file into a string
+    local f = io.open('monster.dat', 'rb')
+    local buf = f:read('*a')
+    f:close()
+
+    -- parse the flatbuffer to get an instance to the root monster
+    local monster1 = monster.GetRootAsMonster(buf, 0)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now you can access values like this:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.lua}
+    -- use the : notation to access member data
+    local hp = monster1:Hp()
+    local pos = monster1:Pos()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+## Text Parsing
+
+There currently is no support for parsing text (Schema's and JSON) directly
+from Lua, though you could use the C++ parser through SWIG or ctypes. Please
+see the C++ documentation for more on text parsing.
+
+<br>
index 2d0e0d8..11fe4c8 100644 (file)
@@ -31,6 +31,7 @@ Please select your desired language for our quest:
   <input type="radio" name="language" value="php">PHP</input>
   <input type="radio" name="language" value="c">C</input>
   <input type="radio" name="language" value="dart">Dart</input>
+  <input type="radio" name="language" value="lua">Lua</input>
 </form>
 \endhtmlonly
 
@@ -136,6 +137,10 @@ For your chosen language, please cross-reference with:
 <div class="language-dart">
 [example.dart](https://github.com/google/flatbuffers/blob/master/dart/example/example.dart)
 </div>
+<div class="language-lua">
+[sample_binary.lua](https://github.com/google/flatbuffers/blob/master/dart/example/sample_binary.lua)
+</div>
+
 
 ## Writing the Monsters' FlatBuffer Schema
 
@@ -322,6 +327,12 @@ Please be aware of the difference between `flatc` and `flatcc` tools.
   ./../flatc --dart monster.fbs
 ~~~
 </div>
+<div class="language-lua">
+~~~{.sh}
+  cd flatbuffers/sample
+  ./../flatc --lua monster.fbs
+~~~
+</div>
 
 For a more complete guide to using the `flatc` compiler, please read the
 [Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler)
@@ -439,6 +450,19 @@ The first step is to import/include the library, generated files, etc.
   import 'monster_my_game.sample_generated.dart' as myGame;
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  -- require the flatbuffers module
+  local flatbuffers = require("flatbuffers")
+
+  -- require the generated files from `flatc`.
+  local color = require("MyGame.Sample.Color")
+  local equipment = require("MyGame.Sample.Equipment")
+  local monster = require("MyGame.Sample.Monster")
+  local vec3 = require("MyGame.Sample.Vec3")
+  local weapon = require("MyGame.Sample.Weapon")
+~~~
+</div>
 
 Now we are ready to start building some buffers. In order to start, we need
 to create an instance of the `FlatBufferBuilder`, which will contain the buffer
@@ -518,6 +542,12 @@ which will grow automatically if needed:
   var builder = new fb.Builder(initialSize: 1024);
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  -- get access to the builder, providing an array of size 1024
+  local builder = flatbuffers.Builder(1024)
+~~~
+</div>
 
 After creating the `builder`, we can start serializing our data. Before we make
 our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
@@ -705,6 +735,24 @@ our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
   );
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+    local weaponOne = builder:CreateString("Sword")
+    local weaponTwo = builder:CreateString("Axe")
+
+    -- Create the first 'Weapon'
+    weapon.Start(builder)
+    weapon.AddName(builder, weaponOne)
+    weapon.AddDamage(builder, 3)
+    local sword = weapon.End(builder)
+
+    -- Create the second 'Weapon'
+    weapon.Start(builder)
+    weapon.AddName(builder, weaponTwo)
+    weapon.AddDamage(builder, 5)
+    local axe = weapon.End(builder)
+~~~
+</div>
 
 Now let's create our monster, the `orc`. For this `orc`, lets make him
 `red` with rage, positioned at `(1.0, 2.0, 3.0)`, and give him
@@ -852,6 +900,21 @@ traversal. This is generally easy to do on any tree structures.
   final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
 ~~~
 </div>
+<div class="language-lua">
+~~~{.py}
+    -- Serialize a name for our mosnter, called 'orc'
+    local name = builder:CreateString("Orc")
+
+    -- Create a `vector` representing the inventory of the Orc. Each number
+    -- could correspond to an item that can be claimed after he is slain.
+    -- Note: Since we prepend the bytes, this loop iterates in reverse.
+    monster.StartInventoryVector(builder, 10)
+    for i=10,1,-1 do
+        builder:PrependByte(i)
+    end
+    local inv = builder:EndVector(10)
+~~~
+</div>
 
 We serialized two built-in data types (`string` and `vector`) and captured
 their return values. These values are offsets into the serialized data,
@@ -964,6 +1027,16 @@ offsets.
   final List<myGame.WeaponBuilder> weaps = [sword, axe];
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+    -- Create a FlatBuffer vector and prepend the weapons.
+    -- Note: Since we prepend the data, prepend them in reverse order.
+    monster.StartWeaponsVector(builder, 2)
+    builder:PrependUOffsetTRelative(axe)
+    builder:PrependUOffsetTRelative(sword)
+    local weapons = builder:EndVector(2)
+~~~
+</div>
 
 <div class="language-cpp">
 <br>
@@ -1063,6 +1136,16 @@ for the `path` field above:
   ];
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+    -- Create a FlatBuffer vector and prepend the path locations.
+    -- Note: Since we prepend the data, prepend them in reverse order.
+    monster.StartPathVector(builder, 2)
+    vec3.CreateVec3(builder, 1.0, 2.0, 3.0)
+    vec3.CreateVec3(builder, 4.0, 5.0, 6.0)
+    local path = builder:EndVector(2)
+~~~
+</div>
 
 We have now serialized the non-scalar components of the orc, so we
 can serialize the monster itself:
@@ -1267,6 +1350,22 @@ can serialize the monster itself:
   final int orc = orcBuilder.finish(builder);
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+    -- Create our monster by using Start() andEnd()
+    monster.Start(builder)
+    monster.AddPos(builder, vec3.CreateVec3(builder, 1.0, 2.0, 3.0))
+    monster.AddHp(builder, 300)
+    monster.AddName(builder, name)
+    monster.AddInventory(builder, inv)
+    monster.AddColor(builder, color.Red)
+    monster.AddWeapons(builder, weapons)
+    monster.AddEquippedType(builder, equipment.Weapon)
+    monster.AddEquipped(builder, axe)
+    monster.AddPath(builder, path)
+    local orc = monster.End(builder)
+~~~
+</div>
 
 Note how we create `Vec3` struct in-line in the table. Unlike tables, structs
 are simple combinations of scalars that are always stored inline, just like
@@ -1409,6 +1508,12 @@ Here is a repetition these lines, to help highlight them more clearly:
   equipped: axe,                                  // Union data
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+    monster.AddEquippedType(builder, equipment.Weapon) -- Union type
+    monster.AddEquipped(builder, axe) -- Union data
+~~~
+</div>
 
 After you have created your buffer, you will have the offset to the root of the
 data in the `orc` variable, so you can finish the buffer by calling the
@@ -1480,6 +1585,13 @@ appropriate `finish` method.
   // See the next code section, as in Dart `finish` will also return the byte array.
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+    -- Call 'Finish()' to instruct the builder that this monster is complete.
+    builder:Finish(orc)
+~~~
+</div>
+
 
 The buffer is now ready to be stored somewhere, sent over the network, be
 compressed, or whatever you'd like to do with it. You can access the buffer
@@ -1577,6 +1689,13 @@ like so:
   final Uint8List buf = builder.finish(orc);
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+    -- Get the flatbuffer as a string containing the binary data
+    local bufAsString = builder:Output()
+~~~
+</div>
+
 
 Now you can write the bytes to a file, send them over the network..
 **Make sure your file mode (or tranfer protocol) is set to BINARY, not text.**
@@ -1690,6 +1809,19 @@ import 'package:flat_buffers/flat_buffers.dart' as fb;
 import './monster_my_game.sample_generated.dart' as myGame;
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  -- require the flatbuffers module
+  local flatbuffers = require("flatbuffers")
+
+  -- require the generated files from `flatc`.
+  local color = require("MyGame.Sample.Color")
+  local equipment = require("MyGame.Sample.Equipment")
+  local monster = require("MyGame.Sample.Monster")
+  local vec3 = require("MyGame.Sample.Vec3")
+  local weapon = require("MyGame.Sample.Weapon")
+~~~
+</div>
 
 Then, assuming you have a buffer of bytes received from disk,
 network, etc., you can create start accessing the buffer like so:
@@ -1798,6 +1930,17 @@ List<int> data = ... // the data, e.g. from file or network
 myGame.Monster monster = new myGame.Monster(data);
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+    local bufAsString =   -- The data you just read in
+
+    -- Convert the string representation into binary array Lua structure
+    local buf = flatbuffers.binaryArray.New(bufAsString)
+
+    -- Get an accessor to the root object insert the buffer
+    local mon = monster.GetRootAsMonster(buf, 0)
+~~~
+</div>
 
 If you look in the generated files from the schema compiler, you will see it generated
 accessors for all non-`deprecated` fields. For example:
@@ -1876,6 +2019,13 @@ accessors for all non-`deprecated` fields. For example:
   var name = monster.name;
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  local hp = mon:Hp()
+  local mana = mon:Mana()
+  local name = mon:Name()
+~~~
+</div>
 
 These should hold `300`, `150`, and `"Orc"` respectively.
 
@@ -1969,6 +2119,15 @@ To access sub-objects, in the case of our `pos`, which is a `Vec3`:
   double z = pos.z;
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  local pos = mon:Pos()
+  local x = pos:X()
+  local y = pos:Y()
+  local z = pos:Z()
+~~~
+</div>
+
 
 `x`, `y`, and `z` will contain `1.0`, `2.0`, and `3.0`, respectively.
 
@@ -2041,6 +2200,12 @@ FlatBuffers `vector`.
   var thirdItem = monster.inventory[2];
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  local invLength = mon:InventoryLength()
+  local thirdItem = mon:Inventory(3) -- Lua is 1-based
+~~~
+</div>
 
 For `vector`s of `table`s, you can access the elements like any other vector,
 except your need to handle the result as a FlatBuffer `table`:
@@ -2122,6 +2287,13 @@ except your need to handle the result as a FlatBuffer `table`:
   var secondWeaponDamage = monster.Weapons[1].damage;
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  local weaponsLength = mon:WeaponsLength()
+  local secondWeaponName = mon:Weapon(2):Name()
+  local secondWeaponDamage = mon:Weapon(2):Damage()
+~~~
+</div>
 
 Last, we can access our `Equipped` FlatBuffer `union`. Just like when we created
 the `union`, we need to get both parts of the `union`: the type and the data.
@@ -2257,6 +2429,19 @@ We can access the type to dynamically cast the data as needed (since the
   }
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  local unionType = mon:EquippedType()
+
+  if unionType == equipment.Weapon then
+    local unionWeapon = weapon.New()
+    unionWeapon:Init(mon:Equipped().bytes, mon:Equipped().pos)
+
+    local weaponName = unionWeapon:Name()     -- 'Axe'
+    local weaponDamage = unionWeapon:Damage() -- 5
+  end
+~~~
+</div>
 
 ## Mutating FlatBuffers
 
@@ -2337,6 +2522,11 @@ mutators like so:
   <API for mutating FlatBuffers not yet available in Dart.>
 ~~~
 </div>
+<div class="language-lua">
+~~~{.lua}
+  <API for mutating FlatBuffers is not yet available in Lua.>
+~~~
+</div>
 
 We use the somewhat verbose term `mutate` instead of `set` to indicate that this
 is a special use case, not to be confused with the default way of constructing
@@ -2449,5 +2639,9 @@ For your chosen language, see:
 <div class="language-dart">
 [Use in Dart](@ref flatbuffers_guide_use_dart)
 </div>
+<div class="language-lua">
+[Use in Lua](@ref flatbuffers_guide_use_lua)
+</div>
+
 
 <br>
index b4e84fa..8cc46cc 100644 (file)
@@ -758,6 +758,7 @@ INPUT = "FlatBuffers.md" \
         "TypeScriptUsage.md" \
         "PHPUsage.md" \
         "PythonUsage.md" \
+        "LuaUsage.md" \
         "Support.md" \
         "Benchmarks.md" \
         "WhitePaper.md" \
index c723314..c002295 100644 (file)
@@ -41,6 +41,8 @@
           title="Use in Python"/>
       <tab type="user" url="@ref flatbuffers_guide_use_dart"
           title="Use in Dart"/>
+      <tab type="user" url="@ref flatbuffers_guide_use_lua"
+          title="Use in Lua"/>
       <tab type="user" url="@ref flexbuffers"
           title="Schema-less version"/>
       <tab type="usergroup" url="" title="gRPC">
index 88d9553..55ef89d 100644 (file)
@@ -408,6 +408,7 @@ struct IDLOptions {
     kTs = 1 << 9,
     kJsonSchema = 1 << 10,
     kDart = 1 << 11,
+    kLua = 1 << 12,
     kMAX
   };
 
@@ -814,6 +815,12 @@ extern bool GeneratePython(const Parser &parser,
                            const std::string &path,
                            const std::string &file_name);
 
+// Generate Lua files from the definitions in the Parser object.
+// See idl_gen_lua.cpp.
+extern bool GenerateLua(const Parser &parser,
+                           const std::string &path,
+                           const std::string &file_name);
+
 // Generate Json schema file
 // See idl_gen_json_schema.cpp.
 extern bool GenerateJsonSchema(const Parser &parser,
diff --git a/lua/flatbuffers.lua b/lua/flatbuffers.lua
new file mode 100644 (file)
index 0000000..1c85c4e
--- /dev/null
@@ -0,0 +1,8 @@
+local m = {}
+
+m.Builder = require("flatbuffers.builder").New
+m.N = require("flatbuffers.numTypes")
+m.view = require("flatbuffers.view")
+m.binaryArray = require("flatbuffers.binaryarray")
+
+return m
\ No newline at end of file
diff --git a/lua/flatbuffers/binaryarray.lua b/lua/flatbuffers/binaryarray.lua
new file mode 100644 (file)
index 0000000..bf728cd
--- /dev/null
@@ -0,0 +1,123 @@
+local m = {} -- the module table
+
+local mt = {} -- the module metatable
+
+-- given a binary array, set a metamethod to return its length
+-- (e.g., #binaryArray, calls this)
+function mt:__len()
+    return self.size
+end
+
+-- Create a new binary array of an initial size
+function m.New(sizeOrString)
+    -- the array storage itself
+    local o = {}
+    
+    if type(sizeOrString) == "string" then
+        o.str = sizeOrString
+        o.size = #sizeOrString
+    elseif type(sizeOrString) == "number" then
+        o.data = {}
+        o.size = sizeOrString
+    else
+        error("Expect a integer size value or string to construct a binary array")
+    end
+    -- set the inheritance
+    setmetatable(o, {__index = mt, __len = mt.__len})
+    return o
+end
+
+-- Get a slice of the binary array from start to end position
+function mt:Slice(startPos, endPos)
+    startPos = startPos or 0
+    endPos = endPos or self.size
+    local d = self.data
+    if d then
+        -- if the self.data is defined, we are building the buffer
+        -- in a Lua table
+        
+        -- new table to store the slice components
+        local b = {}
+        
+        -- starting with the startPos, put all
+        -- values into the new table to be concat later
+        -- updated the startPos based on the size of the
+        -- value
+        while startPos < endPos do
+            local v = d[startPos] or '/0'
+            table.insert(b, v)
+            startPos = startPos + #v
+        end
+
+        -- combine the table of strings into one string
+        -- this is faster than doing a bunch of concats by themselves
+        return table.concat(b)
+    else
+        -- n.b start/endPos are 0-based incoming, so need to convert
+        --     correctly. in python a slice includes start -> end - 1
+        return self.str:sub(startPos+1, endPos)
+    end
+end
+
+-- Grow the binary array to a new size, placing the exisiting data
+-- at then end of the new array
+function mt:Grow(newsize)
+    -- the new table to store the data
+    local newT = {}
+    
+    -- the offset to be applied to existing entries
+    local offset = newsize - self.size
+    
+    -- loop over all the current entries and
+    -- add them to the new table at the correct
+    -- offset location
+    local d = self.data
+    for i,data in pairs(d) do
+        newT[i + offset] = data
+    end
+    
+    -- update this storage with the new table and size
+    self.data = newT
+    self.size = newsize
+end
+
+-- memorization for padding strings
+local pads = {}
+
+-- pad the binary with n \0 bytes at the starting position
+function mt:Pad(n, startPos)
+    -- use memorization to avoid creating a bunch of strings
+    -- all the time
+    local s = pads[n]
+    if not s then
+        s = string.rep('\0', n)
+        pads[n] = s
+    end
+    
+    -- store the padding string at the start position in the
+    -- Lua table
+    self.data[startPos] = s
+end
+
+-- Sets the binary array value at the specified position
+function mt:Set(value, position)
+    self.data[position] = value
+end
+
+-- locals for slightly faster access
+local sunpack = string.unpack
+local spack = string.pack
+
+-- Pack the data into a binary representation
+function m.Pack(fmt, ...)
+    return spack(fmt, ...)
+end
+
+-- Unpack the data from a binary representation in
+-- a Lua value
+function m.Unpack(fmt, s, pos)
+    return sunpack(fmt, s.str, pos + 1)
+end
+
+-- Return the binary array module
+return m
\ No newline at end of file
diff --git a/lua/flatbuffers/builder.lua b/lua/flatbuffers/builder.lua
new file mode 100644 (file)
index 0000000..d20783b
--- /dev/null
@@ -0,0 +1,368 @@
+local N = require("flatbuffers.numTypes")
+local ba = require("flatbuffers.binaryarray")
+local compat = require("flatbuffers.compat")
+
+local m = {}
+
+local mt = {}
+
+-- get locals for faster access
+local VOffsetT  = N.VOffsetT
+local UOffsetT  = N.UOffsetT
+local SOffsetT  = N.SOffsetT
+local Bool      = N.Bool
+local Uint8     = N.Uint8
+local Uint16    = N.Uint16
+local Uint32    = N.Uint32
+local Uint64    = N.Uint64
+local Int8      = N.Int8
+local Int16     = N.Int16
+local Int32     = N.Int32
+local Int64     = N.Int64
+local Float32   = N.Float32
+local Float64   = N.Float64
+
+local MAX_BUFFER_SIZE = 0x80000000 -- 2 GB
+local VtableMetadataFields = 2
+
+local getAlignSize = compat.GetAlignSize
+
+local function vtableEqual(a, objectStart, b)
+    UOffsetT:EnforceNumber(objectStart)
+    if (#a * VOffsetT.bytewidth) ~= #b then
+        return false
+    end
+    
+    for i, elem in ipairs(a) do
+        local x = VOffsetT:Unpack(b, i * VOffsetT.bytewidth)
+        if x ~= 0 or elem ~= 0 then
+            local y = objectStart - elem
+            if x ~= y then
+                return false
+            end
+        end
+    end
+    return true
+end
+
+function m.New(initialSize)
+    assert(0 <= initialSize and initialSize < MAX_BUFFER_SIZE)
+    local o = 
+    {
+        finished = false,
+        bytes = ba.New(initialSize),
+        nested = false,
+        head = initialSize,
+        minalign = 1,
+        vtables = {}
+    }
+    setmetatable(o, {__index = mt})
+    return o
+end
+
+function mt:Output(full)
+    assert(self.finished, "Builder Not Finished")
+    if full then
+        return self.bytes:Slice()
+    else
+        return self.bytes:Slice(self.head)
+    end
+end
+
+function mt:StartObject(numFields)
+    assert(not self.nested)
+    
+    local vtable = {}
+
+    for _=1,numFields do
+        table.insert(vtable, 0)
+    end
+            
+    self.currentVTable = vtable
+    self.objectEnd = self:Offset()
+    self.minalign = 1
+    self.nested = true
+end
+
+function mt:WriteVtable()
+    self:PrependSOffsetTRelative(0)
+    local objectOffset = self:Offset()
+    
+    local exisitingVTable
+    local i = #self.vtables
+    while i >= 1 do
+        if self.vtables[i] == 0 then
+            table.remove(self.vtables,i)
+        end
+        i = i - 1
+    end    
+   
+    while i >= 1 do
+        
+        local vt2Offset = self.vtables[i]
+        local vt2Start = #self.bytes - vt2Offset
+        local vt2len = VOffsetT:Unpack(self.bytes, vt2Start)
+        
+        local metadata = VtableMetadataFields * VOffsetT.bytewidth
+        local vt2End = vt2Start + vt2Len
+        local vt2 = self.bytes:Slice(vt2Start+metadata,vt2End)
+        
+        if vtableEqual(self.currentVTable, objectOffset, vt2) then
+            exisitingVTable = vt2Offset
+            break
+        end
+        
+        i = i - 1
+    end
+    
+    if not exisitingVTable then
+        i = #self.currentVTable
+        while i >= 1 do
+            local off = 0
+            local a = self.currentVTable[i]
+            if a and a ~= 0 then
+                off = objectOffset - a
+            end
+            self:PrependVOffsetT(off)
+            
+            i = i - 1
+        end
+        
+        local objectSize = objectOffset - self.objectEnd
+        self:PrependVOffsetT(objectSize)
+        
+        local vBytes = #self.currentVTable + VtableMetadataFields
+        vBytes = vBytes * VOffsetT.bytewidth
+        self:PrependVOffsetT(vBytes)
+        
+        local objectStart = #self.bytes - objectOffset
+        self.bytes:Set(SOffsetT:Pack(self:Offset() - objectOffset),objectStart)        
+        
+        table.insert(self.vtables, self:Offset())
+    else
+        local objectStart = #self.bytes - objectOffset
+        self.head = objectStart
+        self.bytes:Set(SOffsetT:Pack(exisitingVTable - objectOffset),self.head)
+    end
+    
+    self.currentVTable = nil
+    return objectOffset
+end
+
+function mt:EndObject()
+    assert(self.nested)
+    self.nested = false
+    return self:WriteVtable()
+end
+
+local function growByteBuffer(self, desiredSize)
+    local s = #self.bytes
+    assert(s < MAX_BUFFER_SIZE, "Flat Buffers cannot grow buffer beyond 2 gigabytes")
+    local newsize = s
+    repeat
+        newsize = math.min(newsize * 2, MAX_BUFFER_SIZE)
+        if newsize == 0 then newsize = 1 end
+    until newsize > desiredSize 
+    
+    self.bytes:Grow(newsize)
+end
+
+function mt:Head()
+    return self.head
+end
+
+function mt:Offset()
+   return #self.bytes - self.head
+end
+
+function mt:Pad(n)    
+    if n > 0 then
+        -- pads are 8-bit, so skip the bytewidth lookup
+        local h = self.head - n  -- UInt8    
+        self.head = h
+        self.bytes:Pad(n, h)  
+    end
+end
+
+function mt:Prep(size, additionalBytes)
+    if size > self.minalign then
+        self.minalign = size
+    end
+
+    local h = self.head
+    
+    local k = #self.bytes - h + additionalBytes
+    local alignsize = ((~k) + 1) & (size - 1) -- getAlignSize(k, size)
+
+    local desiredSize = alignsize + size + additionalBytes
+
+    while self.head < desiredSize do
+        local oldBufSize = #self.bytes
+        growByteBuffer(self, desiredSize)
+        local updatedHead = self.head + #self.bytes - oldBufSize
+        self.head = updatedHead
+    end
+    
+    self:Pad(alignsize)
+end
+
+function mt:PrependSOffsetTRelative(off)
+    self:Prep(SOffsetT.bytewidth, 0)
+    assert(off <= self:Offset(), "Offset arithmetic error")
+    local off2 = self:Offset() - off + SOffsetT.bytewidth
+    self:Place(off2, SOffsetT)
+end
+
+function mt:PrependUOffsetTRelative(off)
+    self:Prep(UOffsetT.bytewidth, 0)
+    local soffset = self:Offset()
+    if off <= soffset then    
+        local off2 = soffset - off + UOffsetT.bytewidth
+        self:Place(off2, UOffsetT)
+    else
+        error("Offset arithmetic error")
+    end
+end
+
+function mt:StartVector(elemSize, numElements, alignment)
+    assert(not self.nested)
+    self.nested = true
+    self:Prep(Uint32.bytewidth, elemSize * numElements)
+    self:Prep(alignment, elemSize * numElements)
+    return self:Offset()
+end
+
+function mt:EndVector(vectorNumElements)
+    assert(self.nested)
+    self.nested = false
+    self:Place(vectorNumElements, UOffsetT)    
+    return self:Offset()
+end
+
+function mt:CreateString(s)
+    assert(not self.nested)
+    self.nested = true
+    
+    assert(type(s) == "string")
+    
+    self:Prep(UOffsetT.bytewidth, (#s + 1)*Uint8.bytewidth)
+    self:Place(0, Uint8)
+    
+    local l = #s
+    self.head = self.head - l
+    
+    self.bytes:Set(s, self.head, self.head + l)
+    
+    return self:EndVector(#s)
+end
+
+function mt:CreateByteVector(x)
+    assert(not self.nested)
+    self.nested = true
+    self:Prep(UOffsetT.bytewidth, #x*Uint8.bytewidth)
+    
+    local l = #x
+    self.head = self.head - l
+    
+    self.bytes:Set(x, self.head, self.head + l)
+    
+    return self:EndVector(#x)
+end
+
+function mt:Slot(slotnum)
+    assert(self.nested)
+    -- n.b. slot number is 0-based
+    self.currentVTable[slotnum + 1] = self:Offset()
+end
+
+local function finish(self, rootTable, sizePrefix)
+    UOffsetT:EnforceNumber(rootTable)
+    local prepSize = UOffsetT.bytewidth
+    if sizePrefix then
+        prepSize = prepSize + Int32.bytewidth
+    end
+    
+    self:Prep(self.minalign, prepSize)
+    self:PrependUOffsetTRelative(rootTable)
+    if sizePrefix then
+        local size = #self.bytes - self.head
+        Int32:EnforceNumber(size)
+        self:PrependInt32(size)
+    end
+    self.finished = true
+    return self.head
+end
+
+function mt:Finish(rootTable)
+    return finish(self, rootTable, false)
+end
+
+function mt:FinishSizePrefixed(rootTable)
+    return finish(self, rootTable, true)
+end
+
+function mt:Prepend(flags, off)
+    self:Prep(flags.bytewidth, 0)
+    self:Place(off, flags)
+end
+
+function mt:PrependSlot(flags, o, x, d)
+    flags:EnforceNumber(x)
+    flags:EnforceNumber(d)
+    if x ~= d then
+        self:Prepend(flags, x)
+        self:Slot(o)
+    end
+end
+
+function mt:PrependBoolSlot(...)    self:PrependSlot(Bool, ...) end
+function mt:PrependByteSlot(...)    self:PrependSlot(Uint8, ...) end
+function mt:PrependUint8Slot(...)   self:PrependSlot(Uint8, ...) end
+function mt:PrependUint16Slot(...)  self:PrependSlot(Uint16, ...) end
+function mt:PrependUint32Slot(...)  self:PrependSlot(Uint32, ...) end
+function mt:PrependUint64Slot(...)  self:PrependSlot(Uint64, ...) end
+function mt:PrependInt8Slot(...)    self:PrependSlot(Int8, ...) end
+function mt:PrependInt16Slot(...)   self:PrependSlot(Int16, ...) end
+function mt:PrependInt32Slot(...)   self:PrependSlot(Int32, ...) end
+function mt:PrependInt64Slot(...)   self:PrependSlot(Int64, ...) end
+function mt:PrependFloat32Slot(...) self:PrependSlot(Float32, ...) end
+function mt:PrependFloat64Slot(...) self:PrependSlot(Float64, ...) end
+
+function mt:PrependUOffsetTRelativeSlot(o,x,d)
+    if x~=d then
+        self:PrependUOffsetTRelative(x)
+        self:Slot(o)
+    end
+end
+
+function mt:PrependStructSlot(v,x,d)
+    UOffsetT:EnforceNumber(d)
+    if x~=d then
+        UOffsetT:EnforceNumber(x)
+        assert(x == self:Offset(), "Tried to write a Struct at an Offset that is different from the current Offset of the Builder.")
+        self:Slot(v)
+    end
+end
+
+function mt:PrependBool(x)      self:Prepend(Bool, x) end
+function mt:PrependByte(x)      self:Prepend(Uint8, x) end
+function mt:PrependUint8(x)     self:Prepend(Uint8, x) end
+function mt:PrependUint16(x)    self:Prepend(Uint16, x) end
+function mt:PrependUint32(x)    self:Prepend(Uint32, x) end
+function mt:PrependUint64(x)    self:Prepend(Uint64, x) end
+function mt:PrependInt8(x)      self:Prepend(Int8, x) end
+function mt:PrependInt16(x)     self:Prepend(Int16, x) end
+function mt:PrependInt32(x)     self:Prepend(Int32, x) end
+function mt:PrependInt64(x)     self:Prepend(Int64, x) end
+function mt:PrependFloat32(x)   self:Prepend(Float32, x) end
+function mt:PrependFloat64(x)   self:Prepend(Float64, x) end
+function mt:PrependVOffsetT(x)  self:Prepend(VOffsetT, x) end
+
+function mt:Place(x, flags)
+    local d = flags:EnforceNumberAndPack(x)
+    local h = self.head - flags.bytewidth
+    self.head = h
+    self.bytes:Set(d, h)
+end
+
+return m
\ No newline at end of file
diff --git a/lua/flatbuffers/compat.lua b/lua/flatbuffers/compat.lua
new file mode 100644 (file)
index 0000000..89c992b
--- /dev/null
@@ -0,0 +1,17 @@
+local m = {}
+
+local getAlignSize
+if _VERSION == "Lua 5.3" then
+    getAlignSize = function(k, size)
+            return ((~k) + 1) & (size - 1)
+        end    
+else
+    getAlignSize = function(self, size, additionalBytes)        
+        local alignsize = bit32.bnot(#self.bytes-self:Head() + additionalBytes) + 1
+        return bit32.band(alignsize,(size - 1))
+    end
+end
+    
+m.GetAlignSize = getAlignSize
+
+return m
\ No newline at end of file
diff --git a/lua/flatbuffers/numTypes.lua b/lua/flatbuffers/numTypes.lua
new file mode 100644 (file)
index 0000000..e8d87e1
--- /dev/null
@@ -0,0 +1,198 @@
+local m = {}
+
+local ba = require("flatbuffers.binaryarray")
+
+local bpack = ba.Pack
+local bunpack = ba.Unpack
+
+local type_mt =  {}
+
+function type_mt:Pack(value)
+    return bpack(self.packFmt, value)
+end
+
+function type_mt:Unpack(buf, pos)    
+    return bunpack(self.packFmt, buf, pos)
+end
+
+function type_mt:ValidNumber(n)
+    if not self.min_value and not self.max_value then return true end
+    return self.min_value <= n and n <= self.max_value
+end
+
+function type_mt:EnforceNumber(n)
+    -- duplicate code since the overhead of function calls 
+    -- for such a popular method is time consuming
+    if not self.min_value and not self.max_value then 
+        return 
+    end
+    
+    if self.min_value <= n and n <= self.max_value then 
+        return
+    end    
+    
+    error("Number is not in the valid range") 
+end
+
+function type_mt:EnforceNumberAndPack(n)
+    return bpack(self.packFmt, n)    
+end
+
+function type_mt:ConvertType(n, otherType)
+    assert(self.bytewidth == otherType.bytewidth, "Cannot convert between types of different widths")
+    if self == otherType then
+        return n
+    end
+    return otherType:Unpack(self:Pack(n))
+end
+
+local bool_mt =
+{
+    bytewidth = 1,
+    min_value = false,
+    max_value = true,
+    lua_type = type(true),
+    name = "bool",
+    packFmt = "<b"
+}
+
+local uint8_mt = 
+{
+    bytewidth = 1,
+    min_value = 0,
+    max_value = 2^8-1,
+    lua_type = type(1),
+    name = "uint8",
+    packFmt = "<I1"
+}
+
+local uint16_mt = 
+{
+    bytewidth = 2,
+    min_value = 0,
+    max_value = 2^16-1,
+    lua_type = type(1),
+    name = "uint16",
+    packFmt = "<I2"
+}
+
+local uint32_mt = 
+{
+    bytewidth = 4,
+    min_value = 0,
+    max_value = 2^32-1,
+    lua_type = type(1),
+    name = "uint32",
+    packFmt = "<I4"
+}
+
+local uint64_mt = 
+{
+    bytewidth = 8,
+    min_value = 0,
+    max_value = 2^64-1,
+    lua_type = type(1),
+    name = "uint64",
+    packFmt = "<I8"
+}
+
+local int8_mt = 
+{
+    bytewidth = 1,
+    min_value = -2^7,
+    max_value = 2^7-1,
+    lua_type = type(1),
+    name = "int8",
+    packFmt = "<i1"
+}
+
+local int16_mt = 
+{
+    bytewidth = 2,
+    min_value = -2^15,
+    max_value = 2^15-1,
+    lua_type = type(1),
+    name = "int16",
+    packFmt = "<i2"
+}
+
+local int32_mt = 
+{
+    bytewidth = 4,
+    min_value = -2^15,
+    max_value = 2^15-1,
+    lua_type = type(1),
+    name = "int32",
+    packFmt = "<i4"
+}
+
+local int64_mt = 
+{
+    bytewidth = 8,
+    min_value = -2^63,
+    max_value = 2^63-1,
+    lua_type = type(1),
+    name = "int64",
+    packFmt = "<i8"
+}
+
+local float32_mt = 
+{
+    bytewidth = 4,
+    min_value = nil,
+    max_value = nil,
+    lua_type = type(1.0),
+    name = "float32",
+    packFmt = "<f"
+}
+
+local float64_mt = 
+{
+    bytewidth = 8,
+    min_value = nil,
+    max_value = nil,
+    lua_type = type(1.0),
+    name = "float64",
+    packFmt = "<d"
+}
+
+-- register the base class
+setmetatable(bool_mt, {__index = type_mt})
+setmetatable(uint8_mt, {__index = type_mt})
+setmetatable(uint16_mt, {__index = type_mt})
+setmetatable(uint32_mt, {__index = type_mt})
+setmetatable(uint64_mt, {__index = type_mt})
+setmetatable(int8_mt, {__index = type_mt})
+setmetatable(int16_mt, {__index = type_mt})
+setmetatable(int32_mt, {__index = type_mt})
+setmetatable(int64_mt, {__index = type_mt})
+setmetatable(float32_mt, {__index = type_mt})
+setmetatable(float64_mt, {__index = type_mt})
+
+
+m.Bool      = bool_mt
+m.Uint8     = uint8_mt
+m.Uint16    = uint16_mt
+m.Uint32    = uint32_mt
+m.Uint64    = uint64_mt
+m.Int8      = int8_mt
+m.Int16     = int16_mt
+m.Int32     = int32_mt
+m.Int64     = int64_mt
+m.Float32   = float32_mt
+m.Float64   = float64_mt
+
+m.UOffsetT  = uint32_mt
+m.VOffsetT  = uint16_mt
+m.SOffsetT  = int32_mt
+
+function GenerateTypes(listOfTypes)
+    for _,t in pairs(listOfTypes) do
+        t.Pack = function(self, value) return bpack(self.packFmt, value) end
+        t.Unpack = function(self, buf, pos) return bunpack(self.packFmt, buf, pos) end
+    end
+end
+
+GenerateTypes(m)
+
+return m
\ No newline at end of file
diff --git a/lua/flatbuffers/view.lua b/lua/flatbuffers/view.lua
new file mode 100644 (file)
index 0000000..da0f8bf
--- /dev/null
@@ -0,0 +1,97 @@
+local m = {}
+local mt = {}
+
+local mt_name = "flatbuffers.view.mt"
+
+local N = require("flatbuffers.numTypes")
+local binaryarray = require("flatbuffers.binaryarray")
+
+function m.New(buf, pos)
+    N.UOffsetT:EnforceNumber(pos)
+    
+    -- need to convert from a string buffer into
+    -- a binary array
+
+    local o = {
+        bytes = type(buf) == "string" and binaryarray.New(buf) or buf,
+        pos = pos
+    }
+    setmetatable(o, {__index = mt, __metatable = mt_name})
+    return o
+end
+
+function mt:Offset(vtableOffset)
+    local vtable = self.pos - self:Get(N.SOffsetT, self.pos)
+    local vtableEnd = self:Get(N.VOffsetT, vtable)
+    if vtableOffset < vtableEnd then
+        return self:Get(N.VOffsetT, vtable + vtableOffset)
+    end
+    return 0
+end
+
+function mt:Indirect(off)
+    N.UOffsetT:EnforceNumber(off)
+    return off + N.UOffsetT:Unpack(self.bytes, off)
+end
+
+function mt:String(off)
+    N.UOffsetT:EnforceNumber(off)
+    off = off + N.UOffsetT:Unpack(self.bytes, off)
+    local start = off + N.UOffsetT.bytewidth
+    local length = N.UOffsetT:Unpack(self.bytes, off)
+    return self.bytes:Slice(start, start+length)
+end
+
+function mt:VectorLen(off)
+    N.UOffsetT:EnforceNumber(off)
+    off = off + self.pos
+    off = off + N.UOffsetT:Unpack(self.bytes, off)
+    return N.UOffsetT:Unpack(self.bytes, off)
+end
+
+function mt:Vector(off)
+    N.UOffsetT:EnforceNumber(off)
+    
+    off = off + self.pos
+    local x = off + self:Get(N.UOffsetT, off)
+    x = x + N.UOffsetT.bytewidth
+    return x
+end
+
+function mt:Union(t2, off)
+    assert(getmetatable(t2) == mt_name)
+    N.UOffsetT:EnforceNumber(off)
+    
+    off = off + self.pos
+    t2.pos = off + self:Get(N.UOffsetT, off)
+    t2.bytes = self.bytes
+end
+
+function mt:Get(flags, off)
+    N.UOffsetT:EnforceNumber(off)
+    return flags:Unpack(self.bytes, off)
+end
+
+function mt:GetSlot(slot, d, validatorFlags)
+    N.VOffsetT:EnforceNumber(slot)
+    if validatorFlags then
+        validatorFlags:EnforceNumber(d)
+    end
+    local off = self:Offset(slot)
+    if off == 0 then
+        return d
+    end
+    return self:Get(validatorFlags, self.pos + off)
+end
+
+function mt:GetVOffsetTSlot(slot, d)
+    N.VOffsetT:EnforceNumber(slot)
+    N.VOffsetT:EnforceNumber(d)
+    local off = self:Offset(slot)
+    if off == 0 then
+        return d
+    end
+    return off
+end
+
+return m
\ No newline at end of file
diff --git a/samples/lua/MyGame/Sample/Color.lua b/samples/lua/MyGame/Sample/Color.lua
new file mode 100644 (file)
index 0000000..8bf62e1
--- /dev/null
@@ -0,0 +1,11 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Sample
+
+local Color = {
+    Red = 0,
+    Green = 1,
+    Blue = 2,
+}
+
+return Color -- return the module
\ No newline at end of file
diff --git a/samples/lua/MyGame/Sample/Equipment.lua b/samples/lua/MyGame/Sample/Equipment.lua
new file mode 100644 (file)
index 0000000..0a20b36
--- /dev/null
@@ -0,0 +1,10 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Sample
+
+local Equipment = {
+    NONE = 0,
+    Weapon = 1,
+}
+
+return Equipment -- return the module
\ No newline at end of file
diff --git a/samples/lua/MyGame/Sample/Monster.lua b/samples/lua/MyGame/Sample/Monster.lua
new file mode 100644 (file)
index 0000000..b55c27e
--- /dev/null
@@ -0,0 +1,122 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Sample
+
+local flatbuffers = require('flatbuffers')
+
+local Monster = {} -- the module
+local Monster_mt = {} -- the class metatable
+
+function Monster.New()
+    local o = {}
+    setmetatable(o, {__index = Monster_mt})
+    return o
+end
+function Monster.GetRootAsMonster(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = Monster.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function Monster_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Monster_mt:Pos()
+    local o = self.view:Offset(4)
+    if o ~= 0 then
+        local x = o + self.view.pos
+        local obj = require('MyGame.Sample.Vec3').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:Mana()
+    local o = self.view:Offset(6)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int16, o + self.view.pos)
+    end
+    return 150
+end
+function Monster_mt:Hp()
+    local o = self.view:Offset(8)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int16, o + self.view.pos)
+    end
+    return 100
+end
+function Monster_mt:Name()
+    local o = self.view:Offset(10)
+    if o ~= 0 then
+        return self.view:String(o + self.view.pos)
+    end
+end
+function Monster_mt:Inventory(j)
+    local o = self.view:Offset(14)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Uint8, a + ((j-1) * 1))
+    end
+    return 0
+end
+function Monster_mt:InventoryLength()
+    local o = self.view:Offset(14)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Color()
+    local o = self.view:Offset(16)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int8, o + self.view.pos)
+    end
+    return 2
+end
+function Monster_mt:Weapons(j)
+    local o = self.view:Offset(18)
+    if o ~= 0 then
+        local x = self.view:Vector(o)
+        x = x + ((j-1) * 4)
+        x = self.view:Indirect(x)
+        local obj = require('MyGame.Sample.Weapon').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:WeaponsLength()
+    local o = self.view:Offset(18)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:EquippedType()
+    local o = self.view:Offset(20)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint8, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Equipped()
+    local o = self.view:Offset(22)
+    if o ~= 0 then
+        local obj = flatbuffers.view.New(require('flatbuffers.binaryarray').New(0), 0)
+        self.view:Union(obj, o)
+        return obj
+    end
+end
+function Monster.Start(builder) builder:StartObject(10) end
+function Monster.AddPos(builder, pos) builder:PrependStructSlot(0, pos, 0) end
+function Monster.AddMana(builder, mana) builder:PrependInt16Slot(1, mana, 150) end
+function Monster.AddHp(builder, hp) builder:PrependInt16Slot(2, hp, 100) end
+function Monster.AddName(builder, name) builder:PrependUOffsetTRelativeSlot(3, name, 0) end
+function Monster.AddInventory(builder, inventory) builder:PrependUOffsetTRelativeSlot(5, inventory, 0) end
+function Monster.StartInventoryVector(builder, numElems) return builder:StartVector(1, numElems, 1) end
+function Monster.AddColor(builder, color) builder:PrependInt8Slot(6, color, 2) end
+function Monster.AddWeapons(builder, weapons) builder:PrependUOffsetTRelativeSlot(7, weapons, 0) end
+function Monster.StartWeaponsVector(builder, numElems) return builder:StartVector(4, numElems, 4) end
+function Monster.AddEquippedType(builder, equippedType) builder:PrependUint8Slot(8, equippedType, 0) end
+function Monster.AddEquipped(builder, equipped) builder:PrependUOffsetTRelativeSlot(9, equipped, 0) end
+function Monster.End(builder) return builder:EndObject() end
+
+return Monster -- return the module
\ No newline at end of file
diff --git a/samples/lua/MyGame/Sample/Vec3.lua b/samples/lua/MyGame/Sample/Vec3.lua
new file mode 100644 (file)
index 0000000..d42b055
--- /dev/null
@@ -0,0 +1,35 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Sample
+
+local flatbuffers = require('flatbuffers')
+
+local Vec3 = {} -- the module
+local Vec3_mt = {} -- the class metatable
+
+function Vec3.New()
+    local o = {}
+    setmetatable(o, {__index = Vec3_mt})
+    return o
+end
+function Vec3_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Vec3_mt:X()
+    return self.view:Get(flatbuffers.N.Float32, self.view.pos + 0)
+end
+function Vec3_mt:Y()
+    return self.view:Get(flatbuffers.N.Float32, self.view.pos + 4)
+end
+function Vec3_mt:Z()
+    return self.view:Get(flatbuffers.N.Float32, self.view.pos + 8)
+end
+function Vec3.CreateVec3(builder, x, y, z)
+    builder:Prep(4, 12)
+    builder:PrependFloat32(z)
+    builder:PrependFloat32(y)
+    builder:PrependFloat32(x)
+    return builder:Offset()
+end
+
+return Vec3 -- return the module
\ No newline at end of file
diff --git a/samples/lua/MyGame/Sample/Weapon.lua b/samples/lua/MyGame/Sample/Weapon.lua
new file mode 100644 (file)
index 0000000..60442b9
--- /dev/null
@@ -0,0 +1,42 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Sample
+
+local flatbuffers = require('flatbuffers')
+
+local Weapon = {} -- the module
+local Weapon_mt = {} -- the class metatable
+
+function Weapon.New()
+    local o = {}
+    setmetatable(o, {__index = Weapon_mt})
+    return o
+end
+function Weapon.GetRootAsWeapon(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = Weapon.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function Weapon_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Weapon_mt:Name()
+    local o = self.view:Offset(4)
+    if o ~= 0 then
+        return self.view:String(o + self.view.pos)
+    end
+end
+function Weapon_mt:Damage()
+    local o = self.view:Offset(6)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int16, o + self.view.pos)
+    end
+    return 0
+end
+function Weapon.Start(builder) builder:StartObject(2) end
+function Weapon.AddName(builder, name) builder:PrependUOffsetTRelativeSlot(0, name, 0) end
+function Weapon.AddDamage(builder, damage) builder:PrependInt16Slot(1, damage, 0) end
+function Weapon.End(builder) return builder:EndObject() end
+
+return Weapon -- return the module
\ No newline at end of file
diff --git a/samples/sample_binary.lua b/samples/sample_binary.lua
new file mode 100644 (file)
index 0000000..da96539
--- /dev/null
@@ -0,0 +1,107 @@
+-- need to update the Lua path to point to the local flatbuffers implementation
+package.path = string.format("../lua/?.lua;%s",package.path)
+package.path = string.format("./lua/?.lua;%s",package.path)
+
+-- require the library
+local flatbuffers = require("flatbuffers")
+
+local binaryArray = flatbuffers.binaryArray-- for hex dump utility
+
+-- require the files generated from the schema
+local weapon = require("MyGame.Sample.Weapon")
+local monster = require("MyGame.Sample.Monster")
+local vec3 = require("MyGame.Sample.Vec3")
+local color = require("MyGame.Sample.Color")
+local equipment = require("MyGame.Sample.Equipment")
+
+-- get access to the builder, providing an array of size 1024
+local builder = flatbuffers.Builder(1024)
+
+local weaponOne = builder:CreateString("Sword")
+local weaponTwo = builder:CreateString("Axe")
+
+-- Create the first 'Weapon'
+weapon.Start(builder)
+weapon.AddName(builder, weaponOne)
+weapon.AddDamage(builder, 3)
+local sword = weapon.End(builder)
+
+-- Create the second 'Weapon'
+weapon.Start(builder)
+weapon.AddName(builder, weaponTwo)
+weapon.AddDamage(builder, 5)
+local axe = weapon.End(builder)
+
+-- Serialize a name for our mosnter, called 'orc'
+local name = builder:CreateString("Orc")
+
+-- Create a `vector` representing the inventory of the Orc. Each number
+-- could correspond to an item that can be claimed after he is slain.
+-- Note: Since we prepend the bytes, this loop iterates in reverse.
+monster.StartInventoryVector(builder, 10)
+for i=10,1,-1 do
+    builder:PrependByte(i)
+end
+local inv = builder:EndVector(10)
+
+-- Create a FlatBuffer vector and prepend the weapons.
+-- Note: Since we prepend the data, prepend them in reverse order.
+monster.StartWeaponsVector(builder, 2)
+builder:PrependUOffsetTRelative(axe)
+builder:PrependUOffsetTRelative(sword)
+local weapons = builder:EndVector(2)
+
+-- Create our monster by using Start() andEnd()
+monster.Start(builder)
+monster.AddPos(builder, vec3.CreateVec3(builder, 1.0, 2.0, 3.0))
+monster.AddHp(builder, 300)
+monster.AddName(builder, name)
+monster.AddInventory(builder, inv)
+monster.AddColor(builder, color.Red)
+monster.AddWeapons(builder, weapons)
+monster.AddEquippedType(builder, equipment.Weapon)
+monster.AddEquipped(builder, axe)
+local orc = monster.End(builder)
+
+-- Call 'Finish()' to instruct the builder that this monster is complete.
+builder:Finish(orc)
+
+-- Get the flatbuffer as a string containing the binary data
+local bufAsString = builder:Output()
+
+-- Convert the string representation into binary array Lua structure
+local buf = flatbuffers.binaryArray.New(bufAsString)
+
+-- Get an accessor to the root object insert the buffer
+local mon = monster.GetRootAsMonster(buf, 0)
+
+assert(mon:Mana() == 150)
+assert(mon:Hp() == 300)
+assert(mon:Name() == "Orc")
+assert(mon:Color() == color.Red)
+assert(mon:Pos():X() == 1.0)
+assert(mon:Pos():Y() == 2.0)
+assert(mon:Pos():Z() == 3.0)
+
+for i=1,mon:InventoryLength() do
+    assert(mon:Inventory(i) == i)
+end
+
+local expected = { 
+    {w = 'Sword', d = 3}, 
+    {w = 'Axe', d = 5}
+}
+
+for i=1,mon:WeaponsLength() do
+   assert(mon:Weapons(i):Name() == expected[i].w)
+   assert(mon:Weapons(i):Damage() == expected[i].d)
+end
+
+assert(mon:EquippedType() == equipment.Weapon)
+
+local unionWeapon = weapon.New()
+unionWeapon:Init(mon:Equipped().bytes,mon:Equipped().pos)
+assert(unionWeapon:Name() == "Axe")
+assert(unionWeapon:Damage() == 5)
+
+print("The Lua FlatBuffer example was successfully created and verified!")
\ No newline at end of file
index 1fed895..09efe79 100644 (file)
@@ -71,6 +71,10 @@ int main(int argc, const char *argv[]) {
       flatbuffers::IDLOptions::kPython,
       "Generate Python files for tables/structs",
       flatbuffers::GeneralMakeRule },
+    { flatbuffers::GenerateLua, "-l", "--lua", "Lua", true, nullptr,
+      flatbuffers::IDLOptions::kLua,
+      "Generate Lua files for tables/structs",
+      flatbuffers::GeneralMakeRule },
     { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", true, nullptr,
       flatbuffers::IDLOptions::kPhp, "Generate PHP files for tables/structs",
       flatbuffers::GeneralMakeRule },
diff --git a/src/idl_gen_lua.cpp b/src/idl_gen_lua.cpp
new file mode 100644 (file)
index 0000000..8626491
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // independent from idl_parser, since this code is not needed for most clients
+
+#include <string>
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+#include <unordered_set>
+
+namespace flatbuffers {
+namespace lua {
+
+  // Hardcode spaces per indentation.
+  const char * Indent = "    ";
+  const char * Comment = "-- ";
+  const char * End = "end\n";
+  const char * EndFunc = "end\n";
+  const char * SelfData = "self.view";
+  const char * SelfDataPos = "self.view.pos";
+  const char * SelfDataBytes = "self.view.bytes";
+
+  class LuaGenerator : public BaseGenerator {
+  public:
+    LuaGenerator(const Parser &parser, const std::string &path,
+      const std::string &file_name)
+      : BaseGenerator(parser, path, file_name, "" /* not used */,
+        "" /* not used */) {
+      static const char * const keywords[] = {
+        "and",
+        "break",
+        "do",
+        "else",
+        "elseif",
+        "end",
+        "false",
+        "for",
+        "function",
+        "goto",
+        "if",
+        "in",
+        "local",
+        "nil",
+        "not",
+        "or",
+        "repeat",
+        "return",
+        "then",
+        "true",
+        "until",
+        "while"
+      };
+      keywords_.insert(std::begin(keywords), std::end(keywords));
+    }
+
+    // Most field accessors need to retrieve and test the field offset first,
+    // this is the prefix code for that.
+    std::string OffsetPrefix(const FieldDef &field) {
+      return std::string(Indent) +
+        "local o = " + SelfData + ":Offset(" + NumToString(field.value.offset) + ")\n" +
+        Indent + "if o ~= 0 then\n";
+    }
+
+    // Begin a class declaration.
+    void BeginClass(const StructDef &struct_def, std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += "local " + NormalizedName(struct_def) + " = {} -- the module\n";
+      code += "local " + NormalizedMetaName(struct_def) + " = {} -- the class metatable\n";
+      code += "\n";
+    }
+
+    // Begin enum code with a class declaration.
+    void BeginEnum(const std::string class_name, std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += "local " + class_name + " = {\n";
+    }
+
+    std::string EscapeKeyword(const std::string &name) const {
+      return keywords_.find(name) == keywords_.end() ? name : "_" + name;
+    }
+
+    std::string NormalizedName(const Definition &definition) const {
+      return EscapeKeyword(definition.name);
+    }
+
+    std::string NormalizedName(const EnumVal &ev) const {
+      return EscapeKeyword(ev.name);
+    }
+
+    std::string NormalizedMetaName(const Definition &definition) const {
+      return EscapeKeyword(definition.name) + "_mt";
+    }
+
+    // A single enum member.
+    void EnumMember(const EnumVal ev, std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += std::string(Indent) + NormalizedName(ev) + " = " + NumToString(ev.value) + ",\n";
+    }
+
+    // End enum code.
+    void EndEnum(std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += "}\n";
+    }
+
+    void GenerateNewObjectPrototype(const StructDef &struct_def,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+
+      code += "function " + NormalizedName(struct_def) + ".New()\n";
+      code += std::string(Indent) + "local o = {}\n";
+      code += std::string(Indent) + "setmetatable(o, {__index = " + NormalizedMetaName(struct_def) + "})\n";
+      code += std::string(Indent) + "return o\n";
+      code += EndFunc;
+    }
+
+    // Initialize a new struct or table from existing data.
+    void NewRootTypeFromBuffer(const StructDef &struct_def,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+
+      code += "function " + NormalizedName(struct_def) + ".GetRootAs" + NormalizedName(struct_def) + "(buf, offset)\n";
+      code += std::string(Indent) + "local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)\n";
+      code += std::string(Indent) + "local o = " + NormalizedName(struct_def) + ".New()\n";
+      code += std::string(Indent) + "o:Init(buf, n + offset)\n";
+      code += std::string(Indent) + "return o\n";
+      code += EndFunc;
+    }
+
+    // Initialize an existing object with other data, to avoid an allocation.
+    void InitializeExisting(const StructDef &struct_def,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+
+      GenReceiver(struct_def, code_ptr);
+      code += "Init(buf, pos)\n";
+      code += std::string(Indent) + SelfData + " = flatbuffers.view.New(buf, pos)\n";
+      code += EndFunc;
+    }
+
+    // Get the length of a vector.
+    void GetVectorLen(const StructDef &struct_def, const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field)) + "Length()\n";
+      code += OffsetPrefix(field);
+      code += std::string(Indent) + Indent + "return " + SelfData + ":VectorLen(o)\n";
+      code += std::string(Indent) + End;
+      code += std::string(Indent) + "return 0\n";
+      code += EndFunc;
+    }
+
+    // Get the value of a struct's scalar.
+    void GetScalarFieldOfStruct(const StructDef &struct_def,
+      const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      std::string getter = GenGetter(field.value.type);
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field));
+      code += "()\n";
+      code += std::string(Indent) + "return " + getter;
+      code += std::string(SelfDataPos) + " + " + NumToString(field.value.offset) + ")\n";
+      code += EndFunc;
+    }
+
+    // Get the value of a table's scalar.
+    void GetScalarFieldOfTable(const StructDef &struct_def,
+      const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      std::string getter = GenGetter(field.value.type);
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field));
+      code += "()\n";
+      code += OffsetPrefix(field);
+      getter += std::string("o + ") + SelfDataPos + ")";
+      auto is_bool = field.value.type.base_type == BASE_TYPE_BOOL;
+      if (is_bool) {
+        getter = "(" + getter + " ~= 0)";
+      }
+      code += std::string(Indent) + Indent + "return " + getter + "\n";
+      code += std::string(Indent) + End;
+      std::string default_value;
+      if (is_bool) {
+        default_value = field.value.constant == "0" ? "false" : "true";
+      }
+      else {
+        default_value = field.value.constant;
+      }
+      code += std::string(Indent) + "return " + default_value + "\n";
+      code += EndFunc;
+    }
+
+    // Get a struct by initializing an existing struct.
+    // Specific to Struct.
+    void GetStructFieldOfStruct(const StructDef &struct_def,
+      const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field));
+      code += "(obj)\n";
+      code += std::string(Indent) + "obj:Init(" + SelfDataBytes + ", " + SelfDataPos + " + ";
+      code += NumToString(field.value.offset) + ")\n";
+      code += std::string(Indent) + "return obj\n";
+      code += EndFunc;
+    }
+
+    // Get a struct by initializing an existing struct.
+    // Specific to Table.
+    void GetStructFieldOfTable(const StructDef &struct_def,
+      const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field));
+      code += "()\n";
+      code += OffsetPrefix(field);
+      if (field.value.type.struct_def->fixed) {
+        code += std::string(Indent) + Indent + "local x = o + " + SelfDataPos + "\n";
+      }
+      else {
+        code += std::string(Indent) + Indent + "local x = " + SelfData + ":Indirect(o + " + SelfDataPos + ")\n";
+      }
+      code += std::string(Indent) + Indent + "local obj = require('" + TypeNameWithNamespace(field) + "').New()\n";
+      code += std::string(Indent) + Indent + "obj:Init(" + SelfDataBytes + ", x)\n";
+      code += std::string(Indent) + Indent + "return obj\n";
+      code += std::string(Indent) + End;
+      code += EndFunc;
+    }
+
+    // Get the value of a string.
+    void GetStringField(const StructDef &struct_def, const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field));
+      code += "()\n";
+      code += OffsetPrefix(field);
+      code += std::string(Indent) + Indent + "return " + GenGetter(field.value.type);
+      code += std::string("o + ") + SelfDataPos + ")\n";
+      code += std::string(Indent) + End;
+      code += EndFunc;
+    }
+
+    // Get the value of a union from an object.
+    void GetUnionField(const StructDef &struct_def, const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field)) + "()\n";
+      code += OffsetPrefix(field);
+
+      // TODO(rw): this works and is not the good way to it:
+      //bool is_native_table = TypeName(field) == "*flatbuffers.Table";
+      //if (is_native_table) {
+      //  code += std::string(Indent) + Indent + "from flatbuffers.table import Table\n";
+      //} else {
+      //  code += std::string(Indent) + Indent +
+      //  code += "from ." + TypeName(field) + " import " + TypeName(field) + "\n";
+      //}
+      code += std::string(Indent) + Indent + "local obj = flatbuffers.view.New(require('flatbuffers.binaryarray').New(0), 0)\n";
+      code += std::string(Indent) + Indent + GenGetter(field.value.type) + "obj, o)\n";
+      code += std::string(Indent) + Indent + "return obj\n";
+      code += std::string(Indent) + End;
+      code += EndFunc;
+    }
+
+    // Get the value of a vector's struct member.
+    void GetMemberOfVectorOfStruct(const StructDef &struct_def,
+      const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      auto vectortype = field.value.type.VectorType();
+
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field));
+      code += "(j)\n";
+      code += OffsetPrefix(field);
+      code += std::string(Indent) + Indent + "local x = " + SelfData + ":Vector(o)\n";
+      code += std::string(Indent) + Indent + "x = x + ((j-1) * ";
+      code += NumToString(InlineSize(vectortype)) + ")\n";
+      if (!(vectortype.struct_def->fixed)) {
+        code += std::string(Indent) + Indent + "x = " + SelfData + ":Indirect(x)\n";
+      }
+      code += std::string(Indent) + Indent + "local obj = require('" + TypeNameWithNamespace(field) + "').New()\n";
+      code += std::string(Indent) + Indent + "obj:Init(" + SelfDataBytes + ", x)\n";
+      code += std::string(Indent) + Indent + "return obj\n";
+      code += std::string(Indent) + End;
+      code += EndFunc;
+    }
+
+    // Get the value of a vector's non-struct member. Uses a named return
+    // argument to conveniently set the zero value for the result.
+    void GetMemberOfVectorOfNonStruct(const StructDef &struct_def,
+      const FieldDef &field,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      auto vectortype = field.value.type.VectorType();
+
+      GenReceiver(struct_def, code_ptr);
+      code += MakeCamel(NormalizedName(field));
+      code += "(j)\n";
+      code += OffsetPrefix(field);
+      code += std::string(Indent) + Indent + "local a = " + SelfData + ":Vector(o)\n";
+      code += std::string(Indent) + Indent;
+      code += "return " + GenGetter(field.value.type);
+      code += "a + ((j-1) * ";
+      code += NumToString(InlineSize(vectortype)) + "))\n";
+      code += std::string(Indent) + End;
+      if (vectortype.base_type == BASE_TYPE_STRING) {
+        code += std::string(Indent) + "return ''\n";
+      }
+      else {
+        code += std::string(Indent) + "return 0\n";
+      }
+      code += EndFunc;
+    }
+    
+    // Begin the creator function signature.
+    void BeginBuilderArgs(const StructDef &struct_def,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+
+      code += "function " + NormalizedName(struct_def) + ".Create" + NormalizedName(struct_def);
+      code += "(builder";
+    }
+
+    // Recursively generate arguments for a constructor, to deal with nested
+    // structs.
+    void StructBuilderArgs(const StructDef &struct_def,
+      const char *nameprefix, std::string *code_ptr) {
+      for (auto it = struct_def.fields.vec.begin();
+        it != struct_def.fields.vec.end(); ++it) {
+        auto &field = **it;
+        if (IsStruct(field.value.type)) {
+          // Generate arguments for a struct inside a struct. To ensure names
+          // don't clash, and to make it obvious these arguments are constructing
+          // a nested struct, prefix the name with the field name.
+          StructBuilderArgs(*field.value.type.struct_def,
+            (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
+        }
+        else {
+          std::string &code = *code_ptr;
+          code += (std::string) ", " + nameprefix;
+          code += MakeCamel(NormalizedName(field), false);
+        }
+      }
+    }
+
+    // End the creator function signature.
+    void EndBuilderArgs(std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += ")\n";
+    }
+
+    // Recursively generate struct construction statements and instert manual
+    // padding.
+    void StructBuilderBody(const StructDef &struct_def,
+      const char *nameprefix, std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += std::string(Indent) + "builder:Prep(" + NumToString(struct_def.minalign) + ", ";
+      code += NumToString(struct_def.bytesize) + ")\n";
+      for (auto it = struct_def.fields.vec.rbegin();
+        it != struct_def.fields.vec.rend(); ++it) {
+        auto &field = **it;
+        if (field.padding)
+          code += std::string(Indent) + "builder:Pad(" + NumToString(field.padding) + ")\n";
+        if (IsStruct(field.value.type)) {
+          StructBuilderBody(*field.value.type.struct_def,
+            (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
+        }
+        else {
+          code += std::string(Indent) + "builder:Prepend" + GenMethod(field) + "(";
+          code += nameprefix + MakeCamel(NormalizedName(field), false) + ")\n";
+        }
+      }
+    }
+
+    void EndBuilderBody(std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += std::string(Indent) + "return builder:Offset()\n";
+      code += EndFunc;
+    }
+
+    // Get the value of a table's starting offset.
+    void GetStartOfTable(const StructDef &struct_def,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += "function " + NormalizedName(struct_def) + ".Start";
+      code += "(builder) ";
+      code += "builder:StartObject(";
+      code += NumToString(struct_def.fields.vec.size());
+      code += ") end\n";
+    }
+
+    // Set the value of a table's field.
+    void BuildFieldOfTable(const StructDef &struct_def,
+      const FieldDef &field, const size_t offset,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += "function " + NormalizedName(struct_def) + ".Add" + MakeCamel(NormalizedName(field));
+      code += "(builder, ";
+      code += MakeCamel(NormalizedName(field), false);
+      code += ") ";
+      code += "builder:Prepend";
+      code += GenMethod(field) + "Slot(";
+      code += NumToString(offset) + ", ";
+      // todo: i don't need to cast in Lua, but am I missing something?
+    //    if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) {
+    //      code += "flatbuffers.N.UOffsetTFlags.py_type";
+    //      code += "(";
+    //      code += MakeCamel(NormalizedName(field), false) + ")";
+    //    } else {
+      code += MakeCamel(NormalizedName(field), false);
+      //    }
+      code += ", " + field.value.constant;
+      code += ") end\n";
+    }
+
+    // Set the value of one of the members of a table's vector.
+    void BuildVectorOfTable(const StructDef &struct_def,
+      const FieldDef &field, std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += "function " + NormalizedName(struct_def) + ".Start";
+      code += MakeCamel(NormalizedName(field));
+      code += "Vector(builder, numElems) return builder:StartVector(";
+      auto vector_type = field.value.type.VectorType();
+      auto alignment = InlineAlignment(vector_type);
+      auto elem_size = InlineSize(vector_type);
+      code += NumToString(elem_size);
+      code += ", numElems, " + NumToString(alignment);
+      code += ") end\n";
+    }
+
+    // Get the offset of the end of a table.
+    void GetEndOffsetOnTable(const StructDef &struct_def,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += "function " + NormalizedName(struct_def) + ".End";
+      code += "(builder) ";
+      code += "return builder:EndObject() end\n";
+    }
+
+    // Generate the receiver for function signatures.
+    void GenReceiver(const StructDef &struct_def, std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += "function " + NormalizedMetaName(struct_def) + ":";
+    }
+
+    // Generate a struct field, conditioned on its child type(s).
+    void GenStructAccessor(const StructDef &struct_def,
+      const FieldDef &field, std::string *code_ptr) {
+      GenComment(field.doc_comment, code_ptr, nullptr, Comment);
+      if (IsScalar(field.value.type.base_type)) {
+        if (struct_def.fixed) {
+          GetScalarFieldOfStruct(struct_def, field, code_ptr);
+        }
+        else {
+          GetScalarFieldOfTable(struct_def, field, code_ptr);
+        }
+      }
+      else {
+        switch (field.value.type.base_type) {
+        case BASE_TYPE_STRUCT:
+          if (struct_def.fixed) {
+            GetStructFieldOfStruct(struct_def, field, code_ptr);
+          }
+          else {
+            GetStructFieldOfTable(struct_def, field, code_ptr);
+          }
+          break;
+        case BASE_TYPE_STRING: GetStringField(struct_def, field, code_ptr); break;
+        case BASE_TYPE_VECTOR: {
+          auto vectortype = field.value.type.VectorType();
+          if (vectortype.base_type == BASE_TYPE_STRUCT) {
+            GetMemberOfVectorOfStruct(struct_def, field, code_ptr);
+          }
+          else {
+            GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr);            
+          }
+          break;
+        }
+        case BASE_TYPE_UNION: GetUnionField(struct_def, field, code_ptr); break;
+        default: FLATBUFFERS_ASSERT(0);
+        }
+      }
+      if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+        GetVectorLen(struct_def, field, code_ptr);
+      }
+    }
+
+    // Generate table constructors, conditioned on its members' types.
+    void GenTableBuilders(const StructDef &struct_def,
+      std::string *code_ptr) {
+      GetStartOfTable(struct_def, code_ptr);
+
+      for (auto it = struct_def.fields.vec.begin();
+        it != struct_def.fields.vec.end(); ++it) {
+        auto &field = **it;
+        if (field.deprecated) continue;
+
+        auto offset = it - struct_def.fields.vec.begin();
+        BuildFieldOfTable(struct_def, field, offset, code_ptr);
+        if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+          BuildVectorOfTable(struct_def, field, code_ptr);
+        }
+      }
+
+      GetEndOffsetOnTable(struct_def, code_ptr);
+    }
+
+    // Generate struct or table methods.
+    void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
+      if (struct_def.generated) return;
+
+      GenComment(struct_def.doc_comment, code_ptr, nullptr, Comment);
+      BeginClass(struct_def, code_ptr);
+
+      GenerateNewObjectPrototype(struct_def, code_ptr);
+
+      if (!struct_def.fixed) {
+        // Generate a special accessor for the table that has been declared as
+        // the root type.
+        NewRootTypeFromBuffer(struct_def, code_ptr);
+      }
+
+      // Generate the Init method that sets the field in a pre-existing
+      // accessor object. This is to allow object reuse.
+      InitializeExisting(struct_def, code_ptr);
+      for (auto it = struct_def.fields.vec.begin();
+        it != struct_def.fields.vec.end(); ++it) {
+        auto &field = **it;
+        if (field.deprecated) continue;
+
+        GenStructAccessor(struct_def, field, code_ptr);
+      }
+
+      if (struct_def.fixed) {
+        // create a struct constructor function
+        GenStructBuilder(struct_def, code_ptr);
+      }
+      else {
+        // Create a set of functions that allow table construction.
+        GenTableBuilders(struct_def, code_ptr);
+      }
+    }
+
+    // Generate enum declarations.
+    void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
+      if (enum_def.generated) return;
+
+      GenComment(enum_def.doc_comment, code_ptr, nullptr, Comment);
+      BeginEnum(NormalizedName(enum_def), code_ptr);
+      for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+        ++it) {
+        auto &ev = **it;
+        GenComment(ev.doc_comment, code_ptr, nullptr, Comment);
+        EnumMember(ev, code_ptr);
+      }
+      EndEnum(code_ptr);
+    }
+
+    // Returns the function name that is able to read a value of the given type.
+    std::string GenGetter(const Type &type) {
+      switch (type.base_type) {
+      case BASE_TYPE_STRING: return std::string(SelfData) + ":String(";
+      case BASE_TYPE_UNION: return  std::string(SelfData) + ":Union(";
+      case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
+      default:
+        return std::string(SelfData) + ":Get(flatbuffers.N." +
+          MakeCamel(GenTypeGet(type)) + ", ";
+      }
+    }
+
+    // Returns the method name for use with add/put calls.
+    std::string GenMethod(const FieldDef &field) {
+      return IsScalar(field.value.type.base_type)
+        ? MakeCamel(GenTypeBasic(field.value.type))
+        : (IsStruct(field.value.type) ? "Struct" : "UOffsetTRelative");
+    }
+
+    std::string GenTypeBasic(const Type &type) {
+      static const char *ctypename[] = {
+        // clang-format off
+          #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+            CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+            #PTYPE,
+            FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+          #undef FLATBUFFERS_TD
+            // clang-format on
+      };
+      return ctypename[type.base_type];
+    }
+
+    std::string GenTypePointer(const Type &type) {
+      switch (type.base_type) {
+      case BASE_TYPE_STRING: return "string";
+      case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
+      case BASE_TYPE_STRUCT: return type.struct_def->name;
+      case BASE_TYPE_UNION:
+        // fall through
+      default: return "*flatbuffers.Table";
+      }
+    }
+
+    std::string GenTypeGet(const Type &type) {
+      return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
+    }
+
+    std::string GetNamespace(const Type &type) {
+      return type.struct_def->defined_namespace->GetFullyQualifiedName(type.struct_def->name);
+    }
+
+    std::string TypeName(const FieldDef &field) {
+      return GenTypeGet(field.value.type);
+    }
+
+    std::string TypeNameWithNamespace(const FieldDef &field) {
+      return GetNamespace(field.value.type);
+    }
+
+    // Create a struct with a builder and the struct's arguments.
+    void GenStructBuilder(const StructDef &struct_def,
+      std::string *code_ptr) {
+      BeginBuilderArgs(struct_def, code_ptr);
+      StructBuilderArgs(struct_def, "", code_ptr);
+      EndBuilderArgs(code_ptr);
+
+      StructBuilderBody(struct_def, "", code_ptr);
+      EndBuilderBody(code_ptr);
+    }
+
+    bool generate() {
+      if (!generateEnums()) return false;
+      if (!generateStructs()) return false;
+      return true;
+    }
+
+  private:
+    bool generateEnums() {
+      for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+        ++it) {
+        auto &enum_def = **it;
+        std::string enumcode;
+        GenEnum(enum_def, &enumcode);
+        if (!SaveType(enum_def, enumcode, false)) return false;
+      }
+      return true;
+    }
+
+    bool generateStructs() {
+      for (auto it = parser_.structs_.vec.begin();
+        it != parser_.structs_.vec.end(); ++it) {
+        auto &struct_def = **it;
+        std::string declcode;
+        GenStruct(struct_def, &declcode);
+        if (!SaveType(struct_def, declcode, true)) return false;
+      }
+      return true;
+    }
+
+    // Begin by declaring namespace and imports.
+    void BeginFile(const std::string name_space_name, const bool needs_imports,
+      std::string *code_ptr) {
+      std::string &code = *code_ptr;
+      code += std::string(Comment) + FlatBuffersGeneratedWarning() + "\n\n";
+      code += std::string(Comment) + "namespace: " + name_space_name + "\n\n";
+      if (needs_imports) {
+        code += "local flatbuffers = require('flatbuffers')\n\n";
+      }
+    }
+
+    // Save out the generated code for a Lua Table type.
+    bool SaveType(const Definition &def, const std::string &classcode,
+      bool needs_imports) {
+      if (!classcode.length()) return true;
+
+      std::string namespace_dir = path_;
+      auto &namespaces = def.defined_namespace->components;
+      for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
+        if (it != namespaces.begin()) namespace_dir += kPathSeparator;
+        namespace_dir += *it;
+        //std::string init_py_filename = namespace_dir + "/__init__.py";
+        //SaveFile(init_py_filename.c_str(), "", false);
+      }
+
+      std::string code = "";
+      BeginFile(LastNamespacePart(*def.defined_namespace), needs_imports, &code);
+      code += classcode;
+      code += "\n";
+      code += "return " + NormalizedName(def) + " " + Comment + "return the module";
+      std::string filename =
+        NamespaceDir(*def.defined_namespace) + NormalizedName(def) + ".lua";
+      return SaveFile(filename.c_str(), code, false);
+    }
+  private:
+    std::unordered_set<std::string> keywords_;
+  };
+
+}  // namespace lua
+
+bool GenerateLua(const Parser &parser, const std::string &path,
+  const std::string &file_name) {
+  lua::LuaGenerator generator(parser, path, file_name);
+  return generator.generate();
+}
+
+}  // namespace flatbuffers
index d2f518f..e000ada 100644 (file)
@@ -72,7 +72,7 @@ class PythonGenerator : public BaseGenerator {
       "with",
       "yield"
     };
-    for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
+    keywords_.insert(std::begin(keywords), std::end(keywords));
   }
 
   // Most field accessors need to retrieve and test the field offset first,
diff --git a/tests/LuaTest.bat b/tests/LuaTest.bat
new file mode 100644 (file)
index 0000000..2bbf1d0
--- /dev/null
@@ -0,0 +1,6 @@
+set buildtype=Release
+if "%1"=="-b" set buildtype=%2
+
+..\%buildtype%\flatc.exe --lua -I include_test monster_test.fbs
+
+lua53.exe luatest.lua
\ No newline at end of file
diff --git a/tests/MyGame/Example/Ability.lua b/tests/MyGame/Example/Ability.lua
new file mode 100644 (file)
index 0000000..7fb664a
--- /dev/null
@@ -0,0 +1,31 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local flatbuffers = require('flatbuffers')
+
+local Ability = {} -- the module
+local Ability_mt = {} -- the class metatable
+
+function Ability.New()
+    local o = {}
+    setmetatable(o, {__index = Ability_mt})
+    return o
+end
+function Ability_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Ability_mt:Id()
+    return self.view:Get(flatbuffers.N.Uint32, self.view.pos + 0)
+end
+function Ability_mt:Distance()
+    return self.view:Get(flatbuffers.N.Uint32, self.view.pos + 4)
+end
+function Ability.CreateAbility(builder, id, distance)
+    builder:Prep(4, 8)
+    builder:PrependUint32(distance)
+    builder:PrependUint32(id)
+    return builder:Offset()
+end
+
+return Ability -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/Any.lua b/tests/MyGame/Example/Any.lua
new file mode 100644 (file)
index 0000000..03225ba
--- /dev/null
@@ -0,0 +1,12 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local Any = {
+    NONE = 0,
+    Monster = 1,
+    TestSimpleTableWithEnum = 2,
+    MyGame_Example2_Monster = 3,
+}
+
+return Any -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/Color.lua b/tests/MyGame/Example/Color.lua
new file mode 100644 (file)
index 0000000..c3ec092
--- /dev/null
@@ -0,0 +1,11 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local Color = {
+    Red = 1,
+    Green = 2,
+    Blue = 8,
+}
+
+return Color -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/Monster.lua b/tests/MyGame/Example/Monster.lua
new file mode 100644 (file)
index 0000000..a656f9b
--- /dev/null
@@ -0,0 +1,542 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local flatbuffers = require('flatbuffers')
+
+-- /// an example documentation comment: monster object
+local Monster = {} -- the module
+local Monster_mt = {} -- the class metatable
+
+function Monster.New()
+    local o = {}
+    setmetatable(o, {__index = Monster_mt})
+    return o
+end
+function Monster.GetRootAsMonster(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = Monster.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function Monster_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Monster_mt:Pos()
+    local o = self.view:Offset(4)
+    if o ~= 0 then
+        local x = o + self.view.pos
+        local obj = require('MyGame.Example.Vec3').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:Mana()
+    local o = self.view:Offset(6)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int16, o + self.view.pos)
+    end
+    return 150
+end
+function Monster_mt:Hp()
+    local o = self.view:Offset(8)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int16, o + self.view.pos)
+    end
+    return 100
+end
+function Monster_mt:Name()
+    local o = self.view:Offset(10)
+    if o ~= 0 then
+        return self.view:String(o + self.view.pos)
+    end
+end
+function Monster_mt:Inventory(j)
+    local o = self.view:Offset(14)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Uint8, a + ((j-1) * 1))
+    end
+    return 0
+end
+function Monster_mt:InventoryLength()
+    local o = self.view:Offset(14)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Color()
+    local o = self.view:Offset(16)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int8, o + self.view.pos)
+    end
+    return 8
+end
+function Monster_mt:TestType()
+    local o = self.view:Offset(18)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint8, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Test()
+    local o = self.view:Offset(20)
+    if o ~= 0 then
+        local obj = flatbuffers.view.New(require('flatbuffers.binaryarray').New(0), 0)
+        self.view:Union(obj, o)
+        return obj
+    end
+end
+function Monster_mt:Test4(j)
+    local o = self.view:Offset(22)
+    if o ~= 0 then
+        local x = self.view:Vector(o)
+        x = x + ((j-1) * 4)
+        local obj = require('MyGame.Example.Test').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:Test4Length()
+    local o = self.view:Offset(22)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Testarrayofstring(j)
+    local o = self.view:Offset(24)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:String(a + ((j-1) * 4))
+    end
+    return ''
+end
+function Monster_mt:TestarrayofstringLength()
+    local o = self.view:Offset(24)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+-- /// an example documentation comment: this will end up in the generated code
+-- /// multiline too
+function Monster_mt:Testarrayoftables(j)
+    local o = self.view:Offset(26)
+    if o ~= 0 then
+        local x = self.view:Vector(o)
+        x = x + ((j-1) * 4)
+        x = self.view:Indirect(x)
+        local obj = require('MyGame.Example.Monster').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:TestarrayoftablesLength()
+    local o = self.view:Offset(26)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Enemy()
+    local o = self.view:Offset(28)
+    if o ~= 0 then
+        local x = self.view:Indirect(o + self.view.pos)
+        local obj = require('MyGame.Example.Monster').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:Testnestedflatbuffer(j)
+    local o = self.view:Offset(30)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Uint8, a + ((j-1) * 1))
+    end
+    return 0
+end
+function Monster_mt:TestnestedflatbufferLength()
+    local o = self.view:Offset(30)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Testempty()
+    local o = self.view:Offset(32)
+    if o ~= 0 then
+        local x = self.view:Indirect(o + self.view.pos)
+        local obj = require('MyGame.Example.Stat').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:Testbool()
+    local o = self.view:Offset(34)
+    if o ~= 0 then
+        return (self.view:Get(flatbuffers.N.Bool, o + self.view.pos) ~= 0)
+    end
+    return false
+end
+function Monster_mt:Testhashs32Fnv1()
+    local o = self.view:Offset(36)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int32, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Testhashu32Fnv1()
+    local o = self.view:Offset(38)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint32, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Testhashs64Fnv1()
+    local o = self.view:Offset(40)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int64, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Testhashu64Fnv1()
+    local o = self.view:Offset(42)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint64, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Testhashs32Fnv1a()
+    local o = self.view:Offset(44)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int32, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Testhashu32Fnv1a()
+    local o = self.view:Offset(46)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint32, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Testhashs64Fnv1a()
+    local o = self.view:Offset(48)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int64, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Testhashu64Fnv1a()
+    local o = self.view:Offset(50)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint64, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:Testarrayofbools(j)
+    local o = self.view:Offset(52)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Bool, a + ((j-1) * 1))
+    end
+    return 0
+end
+function Monster_mt:TestarrayofboolsLength()
+    local o = self.view:Offset(52)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Testf()
+    local o = self.view:Offset(54)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Float32, o + self.view.pos)
+    end
+    return 3.14159
+end
+function Monster_mt:Testf2()
+    local o = self.view:Offset(56)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Float32, o + self.view.pos)
+    end
+    return 3.0
+end
+function Monster_mt:Testf3()
+    local o = self.view:Offset(58)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Float32, o + self.view.pos)
+    end
+    return 0.0
+end
+function Monster_mt:Testarrayofstring2(j)
+    local o = self.view:Offset(60)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:String(a + ((j-1) * 4))
+    end
+    return ''
+end
+function Monster_mt:Testarrayofstring2Length()
+    local o = self.view:Offset(60)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Testarrayofsortedstruct(j)
+    local o = self.view:Offset(62)
+    if o ~= 0 then
+        local x = self.view:Vector(o)
+        x = x + ((j-1) * 8)
+        local obj = require('MyGame.Example.Ability').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:TestarrayofsortedstructLength()
+    local o = self.view:Offset(62)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Flex(j)
+    local o = self.view:Offset(64)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Uint8, a + ((j-1) * 1))
+    end
+    return 0
+end
+function Monster_mt:FlexLength()
+    local o = self.view:Offset(64)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:Test5(j)
+    local o = self.view:Offset(66)
+    if o ~= 0 then
+        local x = self.view:Vector(o)
+        x = x + ((j-1) * 4)
+        local obj = require('MyGame.Example.Test').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:Test5Length()
+    local o = self.view:Offset(66)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:VectorOfLongs(j)
+    local o = self.view:Offset(68)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Int64, a + ((j-1) * 8))
+    end
+    return 0
+end
+function Monster_mt:VectorOfLongsLength()
+    local o = self.view:Offset(68)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:VectorOfDoubles(j)
+    local o = self.view:Offset(70)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Float64, a + ((j-1) * 8))
+    end
+    return 0
+end
+function Monster_mt:VectorOfDoublesLength()
+    local o = self.view:Offset(70)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:ParentNamespaceTest()
+    local o = self.view:Offset(72)
+    if o ~= 0 then
+        local x = self.view:Indirect(o + self.view.pos)
+        local obj = require('MyGame.InParentNamespace').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:VectorOfReferrables(j)
+    local o = self.view:Offset(74)
+    if o ~= 0 then
+        local x = self.view:Vector(o)
+        x = x + ((j-1) * 4)
+        x = self.view:Indirect(x)
+        local obj = require('MyGame.Example.Referrable').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:VectorOfReferrablesLength()
+    local o = self.view:Offset(74)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:SingleWeakReference()
+    local o = self.view:Offset(76)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint64, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:VectorOfWeakReferences(j)
+    local o = self.view:Offset(78)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Uint64, a + ((j-1) * 8))
+    end
+    return 0
+end
+function Monster_mt:VectorOfWeakReferencesLength()
+    local o = self.view:Offset(78)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:VectorOfStrongReferrables(j)
+    local o = self.view:Offset(80)
+    if o ~= 0 then
+        local x = self.view:Vector(o)
+        x = x + ((j-1) * 4)
+        x = self.view:Indirect(x)
+        local obj = require('MyGame.Example.Referrable').New()
+        obj:Init(self.view.bytes, x)
+        return obj
+    end
+end
+function Monster_mt:VectorOfStrongReferrablesLength()
+    local o = self.view:Offset(80)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:CoOwningReference()
+    local o = self.view:Offset(82)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint64, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:VectorOfCoOwningReferences(j)
+    local o = self.view:Offset(84)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Uint64, a + ((j-1) * 8))
+    end
+    return 0
+end
+function Monster_mt:VectorOfCoOwningReferencesLength()
+    local o = self.view:Offset(84)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster_mt:NonOwningReference()
+    local o = self.view:Offset(86)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint64, o + self.view.pos)
+    end
+    return 0
+end
+function Monster_mt:VectorOfNonOwningReferences(j)
+    local o = self.view:Offset(88)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Uint64, a + ((j-1) * 8))
+    end
+    return 0
+end
+function Monster_mt:VectorOfNonOwningReferencesLength()
+    local o = self.view:Offset(88)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function Monster.Start(builder) builder:StartObject(43) end
+function Monster.AddPos(builder, pos) builder:PrependStructSlot(0, pos, 0) end
+function Monster.AddMana(builder, mana) builder:PrependInt16Slot(1, mana, 150) end
+function Monster.AddHp(builder, hp) builder:PrependInt16Slot(2, hp, 100) end
+function Monster.AddName(builder, name) builder:PrependUOffsetTRelativeSlot(3, name, 0) end
+function Monster.AddInventory(builder, inventory) builder:PrependUOffsetTRelativeSlot(5, inventory, 0) end
+function Monster.StartInventoryVector(builder, numElems) return builder:StartVector(1, numElems, 1) end
+function Monster.AddColor(builder, color) builder:PrependInt8Slot(6, color, 8) end
+function Monster.AddTestType(builder, testType) builder:PrependUint8Slot(7, testType, 0) end
+function Monster.AddTest(builder, test) builder:PrependUOffsetTRelativeSlot(8, test, 0) end
+function Monster.AddTest4(builder, test4) builder:PrependUOffsetTRelativeSlot(9, test4, 0) end
+function Monster.StartTest4Vector(builder, numElems) return builder:StartVector(4, numElems, 2) end
+function Monster.AddTestarrayofstring(builder, testarrayofstring) builder:PrependUOffsetTRelativeSlot(10, testarrayofstring, 0) end
+function Monster.StartTestarrayofstringVector(builder, numElems) return builder:StartVector(4, numElems, 4) end
+function Monster.AddTestarrayoftables(builder, testarrayoftables) builder:PrependUOffsetTRelativeSlot(11, testarrayoftables, 0) end
+function Monster.StartTestarrayoftablesVector(builder, numElems) return builder:StartVector(4, numElems, 4) end
+function Monster.AddEnemy(builder, enemy) builder:PrependUOffsetTRelativeSlot(12, enemy, 0) end
+function Monster.AddTestnestedflatbuffer(builder, testnestedflatbuffer) builder:PrependUOffsetTRelativeSlot(13, testnestedflatbuffer, 0) end
+function Monster.StartTestnestedflatbufferVector(builder, numElems) return builder:StartVector(1, numElems, 1) end
+function Monster.AddTestempty(builder, testempty) builder:PrependUOffsetTRelativeSlot(14, testempty, 0) end
+function Monster.AddTestbool(builder, testbool) builder:PrependBoolSlot(15, testbool, 0) end
+function Monster.AddTesthashs32Fnv1(builder, testhashs32Fnv1) builder:PrependInt32Slot(16, testhashs32Fnv1, 0) end
+function Monster.AddTesthashu32Fnv1(builder, testhashu32Fnv1) builder:PrependUint32Slot(17, testhashu32Fnv1, 0) end
+function Monster.AddTesthashs64Fnv1(builder, testhashs64Fnv1) builder:PrependInt64Slot(18, testhashs64Fnv1, 0) end
+function Monster.AddTesthashu64Fnv1(builder, testhashu64Fnv1) builder:PrependUint64Slot(19, testhashu64Fnv1, 0) end
+function Monster.AddTesthashs32Fnv1a(builder, testhashs32Fnv1a) builder:PrependInt32Slot(20, testhashs32Fnv1a, 0) end
+function Monster.AddTesthashu32Fnv1a(builder, testhashu32Fnv1a) builder:PrependUint32Slot(21, testhashu32Fnv1a, 0) end
+function Monster.AddTesthashs64Fnv1a(builder, testhashs64Fnv1a) builder:PrependInt64Slot(22, testhashs64Fnv1a, 0) end
+function Monster.AddTesthashu64Fnv1a(builder, testhashu64Fnv1a) builder:PrependUint64Slot(23, testhashu64Fnv1a, 0) end
+function Monster.AddTestarrayofbools(builder, testarrayofbools) builder:PrependUOffsetTRelativeSlot(24, testarrayofbools, 0) end
+function Monster.StartTestarrayofboolsVector(builder, numElems) return builder:StartVector(1, numElems, 1) end
+function Monster.AddTestf(builder, testf) builder:PrependFloat32Slot(25, testf, 3.14159) end
+function Monster.AddTestf2(builder, testf2) builder:PrependFloat32Slot(26, testf2, 3.0) end
+function Monster.AddTestf3(builder, testf3) builder:PrependFloat32Slot(27, testf3, 0.0) end
+function Monster.AddTestarrayofstring2(builder, testarrayofstring2) builder:PrependUOffsetTRelativeSlot(28, testarrayofstring2, 0) end
+function Monster.StartTestarrayofstring2Vector(builder, numElems) return builder:StartVector(4, numElems, 4) end
+function Monster.AddTestarrayofsortedstruct(builder, testarrayofsortedstruct) builder:PrependUOffsetTRelativeSlot(29, testarrayofsortedstruct, 0) end
+function Monster.StartTestarrayofsortedstructVector(builder, numElems) return builder:StartVector(8, numElems, 4) end
+function Monster.AddFlex(builder, flex) builder:PrependUOffsetTRelativeSlot(30, flex, 0) end
+function Monster.StartFlexVector(builder, numElems) return builder:StartVector(1, numElems, 1) end
+function Monster.AddTest5(builder, test5) builder:PrependUOffsetTRelativeSlot(31, test5, 0) end
+function Monster.StartTest5Vector(builder, numElems) return builder:StartVector(4, numElems, 2) end
+function Monster.AddVectorOfLongs(builder, vectorOfLongs) builder:PrependUOffsetTRelativeSlot(32, vectorOfLongs, 0) end
+function Monster.StartVectorOfLongsVector(builder, numElems) return builder:StartVector(8, numElems, 8) end
+function Monster.AddVectorOfDoubles(builder, vectorOfDoubles) builder:PrependUOffsetTRelativeSlot(33, vectorOfDoubles, 0) end
+function Monster.StartVectorOfDoublesVector(builder, numElems) return builder:StartVector(8, numElems, 8) end
+function Monster.AddParentNamespaceTest(builder, parentNamespaceTest) builder:PrependUOffsetTRelativeSlot(34, parentNamespaceTest, 0) end
+function Monster.AddVectorOfReferrables(builder, vectorOfReferrables) builder:PrependUOffsetTRelativeSlot(35, vectorOfReferrables, 0) end
+function Monster.StartVectorOfReferrablesVector(builder, numElems) return builder:StartVector(4, numElems, 4) end
+function Monster.AddSingleWeakReference(builder, singleWeakReference) builder:PrependUint64Slot(36, singleWeakReference, 0) end
+function Monster.AddVectorOfWeakReferences(builder, vectorOfWeakReferences) builder:PrependUOffsetTRelativeSlot(37, vectorOfWeakReferences, 0) end
+function Monster.StartVectorOfWeakReferencesVector(builder, numElems) return builder:StartVector(8, numElems, 8) end
+function Monster.AddVectorOfStrongReferrables(builder, vectorOfStrongReferrables) builder:PrependUOffsetTRelativeSlot(38, vectorOfStrongReferrables, 0) end
+function Monster.StartVectorOfStrongReferrablesVector(builder, numElems) return builder:StartVector(4, numElems, 4) end
+function Monster.AddCoOwningReference(builder, coOwningReference) builder:PrependUint64Slot(39, coOwningReference, 0) end
+function Monster.AddVectorOfCoOwningReferences(builder, vectorOfCoOwningReferences) builder:PrependUOffsetTRelativeSlot(40, vectorOfCoOwningReferences, 0) end
+function Monster.StartVectorOfCoOwningReferencesVector(builder, numElems) return builder:StartVector(8, numElems, 8) end
+function Monster.AddNonOwningReference(builder, nonOwningReference) builder:PrependUint64Slot(41, nonOwningReference, 0) end
+function Monster.AddVectorOfNonOwningReferences(builder, vectorOfNonOwningReferences) builder:PrependUOffsetTRelativeSlot(42, vectorOfNonOwningReferences, 0) end
+function Monster.StartVectorOfNonOwningReferencesVector(builder, numElems) return builder:StartVector(8, numElems, 8) end
+function Monster.End(builder) return builder:EndObject() end
+
+return Monster -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/Referrable.lua b/tests/MyGame/Example/Referrable.lua
new file mode 100644 (file)
index 0000000..9b0f5a1
--- /dev/null
@@ -0,0 +1,35 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local flatbuffers = require('flatbuffers')
+
+local Referrable = {} -- the module
+local Referrable_mt = {} -- the class metatable
+
+function Referrable.New()
+    local o = {}
+    setmetatable(o, {__index = Referrable_mt})
+    return o
+end
+function Referrable.GetRootAsReferrable(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = Referrable.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function Referrable_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Referrable_mt:Id()
+    local o = self.view:Offset(4)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint64, o + self.view.pos)
+    end
+    return 0
+end
+function Referrable.Start(builder) builder:StartObject(1) end
+function Referrable.AddId(builder, id) builder:PrependUint64Slot(0, id, 0) end
+function Referrable.End(builder) return builder:EndObject() end
+
+return Referrable -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/Stat.lua b/tests/MyGame/Example/Stat.lua
new file mode 100644 (file)
index 0000000..6999184
--- /dev/null
@@ -0,0 +1,50 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local flatbuffers = require('flatbuffers')
+
+local Stat = {} -- the module
+local Stat_mt = {} -- the class metatable
+
+function Stat.New()
+    local o = {}
+    setmetatable(o, {__index = Stat_mt})
+    return o
+end
+function Stat.GetRootAsStat(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = Stat.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function Stat_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Stat_mt:Id()
+    local o = self.view:Offset(4)
+    if o ~= 0 then
+        return self.view:String(o + self.view.pos)
+    end
+end
+function Stat_mt:Val()
+    local o = self.view:Offset(6)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int64, o + self.view.pos)
+    end
+    return 0
+end
+function Stat_mt:Count()
+    local o = self.view:Offset(8)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint16, o + self.view.pos)
+    end
+    return 0
+end
+function Stat.Start(builder) builder:StartObject(3) end
+function Stat.AddId(builder, id) builder:PrependUOffsetTRelativeSlot(0, id, 0) end
+function Stat.AddVal(builder, val) builder:PrependInt64Slot(1, val, 0) end
+function Stat.AddCount(builder, count) builder:PrependUint16Slot(2, count, 0) end
+function Stat.End(builder) return builder:EndObject() end
+
+return Stat -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/Test.lua b/tests/MyGame/Example/Test.lua
new file mode 100644 (file)
index 0000000..154067b
--- /dev/null
@@ -0,0 +1,32 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local flatbuffers = require('flatbuffers')
+
+local Test = {} -- the module
+local Test_mt = {} -- the class metatable
+
+function Test.New()
+    local o = {}
+    setmetatable(o, {__index = Test_mt})
+    return o
+end
+function Test_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Test_mt:A()
+    return self.view:Get(flatbuffers.N.Int16, self.view.pos + 0)
+end
+function Test_mt:B()
+    return self.view:Get(flatbuffers.N.Int8, self.view.pos + 2)
+end
+function Test.CreateTest(builder, a, b)
+    builder:Prep(2, 4)
+    builder:Pad(1)
+    builder:PrependInt8(b)
+    builder:PrependInt16(a)
+    return builder:Offset()
+end
+
+return Test -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.lua b/tests/MyGame/Example/TestSimpleTableWithEnum.lua
new file mode 100644 (file)
index 0000000..c17fb96
--- /dev/null
@@ -0,0 +1,35 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local flatbuffers = require('flatbuffers')
+
+local TestSimpleTableWithEnum = {} -- the module
+local TestSimpleTableWithEnum_mt = {} -- the class metatable
+
+function TestSimpleTableWithEnum.New()
+    local o = {}
+    setmetatable(o, {__index = TestSimpleTableWithEnum_mt})
+    return o
+end
+function TestSimpleTableWithEnum.GetRootAsTestSimpleTableWithEnum(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = TestSimpleTableWithEnum.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function TestSimpleTableWithEnum_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function TestSimpleTableWithEnum_mt:Color()
+    local o = self.view:Offset(4)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int8, o + self.view.pos)
+    end
+    return 2
+end
+function TestSimpleTableWithEnum.Start(builder) builder:StartObject(1) end
+function TestSimpleTableWithEnum.AddColor(builder, color) builder:PrependInt8Slot(0, color, 2) end
+function TestSimpleTableWithEnum.End(builder) return builder:EndObject() end
+
+return TestSimpleTableWithEnum -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/TypeAliases.lua b/tests/MyGame/Example/TypeAliases.lua
new file mode 100644 (file)
index 0000000..90f569c
--- /dev/null
@@ -0,0 +1,141 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local flatbuffers = require('flatbuffers')
+
+local TypeAliases = {} -- the module
+local TypeAliases_mt = {} -- the class metatable
+
+function TypeAliases.New()
+    local o = {}
+    setmetatable(o, {__index = TypeAliases_mt})
+    return o
+end
+function TypeAliases.GetRootAsTypeAliases(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = TypeAliases.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function TypeAliases_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function TypeAliases_mt:I8()
+    local o = self.view:Offset(4)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int8, o + self.view.pos)
+    end
+    return 0
+end
+function TypeAliases_mt:U8()
+    local o = self.view:Offset(6)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint8, o + self.view.pos)
+    end
+    return 0
+end
+function TypeAliases_mt:I16()
+    local o = self.view:Offset(8)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int16, o + self.view.pos)
+    end
+    return 0
+end
+function TypeAliases_mt:U16()
+    local o = self.view:Offset(10)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint16, o + self.view.pos)
+    end
+    return 0
+end
+function TypeAliases_mt:I32()
+    local o = self.view:Offset(12)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int32, o + self.view.pos)
+    end
+    return 0
+end
+function TypeAliases_mt:U32()
+    local o = self.view:Offset(14)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint32, o + self.view.pos)
+    end
+    return 0
+end
+function TypeAliases_mt:I64()
+    local o = self.view:Offset(16)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Int64, o + self.view.pos)
+    end
+    return 0
+end
+function TypeAliases_mt:U64()
+    local o = self.view:Offset(18)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Uint64, o + self.view.pos)
+    end
+    return 0
+end
+function TypeAliases_mt:F32()
+    local o = self.view:Offset(20)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Float32, o + self.view.pos)
+    end
+    return 0.0
+end
+function TypeAliases_mt:F64()
+    local o = self.view:Offset(22)
+    if o ~= 0 then
+        return self.view:Get(flatbuffers.N.Float64, o + self.view.pos)
+    end
+    return 0.0
+end
+function TypeAliases_mt:V8(j)
+    local o = self.view:Offset(24)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Int8, a + ((j-1) * 1))
+    end
+    return 0
+end
+function TypeAliases_mt:V8Length()
+    local o = self.view:Offset(24)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function TypeAliases_mt:Vf64(j)
+    local o = self.view:Offset(26)
+    if o ~= 0 then
+        local a = self.view:Vector(o)
+        return self.view:Get(flatbuffers.N.Float64, a + ((j-1) * 8))
+    end
+    return 0
+end
+function TypeAliases_mt:Vf64Length()
+    local o = self.view:Offset(26)
+    if o ~= 0 then
+        return self.view:VectorLen(o)
+    end
+    return 0
+end
+function TypeAliases.Start(builder) builder:StartObject(12) end
+function TypeAliases.AddI8(builder, i8) builder:PrependInt8Slot(0, i8, 0) end
+function TypeAliases.AddU8(builder, u8) builder:PrependUint8Slot(1, u8, 0) end
+function TypeAliases.AddI16(builder, i16) builder:PrependInt16Slot(2, i16, 0) end
+function TypeAliases.AddU16(builder, u16) builder:PrependUint16Slot(3, u16, 0) end
+function TypeAliases.AddI32(builder, i32) builder:PrependInt32Slot(4, i32, 0) end
+function TypeAliases.AddU32(builder, u32) builder:PrependUint32Slot(5, u32, 0) end
+function TypeAliases.AddI64(builder, i64) builder:PrependInt64Slot(6, i64, 0) end
+function TypeAliases.AddU64(builder, u64) builder:PrependUint64Slot(7, u64, 0) end
+function TypeAliases.AddF32(builder, f32) builder:PrependFloat32Slot(8, f32, 0.0) end
+function TypeAliases.AddF64(builder, f64) builder:PrependFloat64Slot(9, f64, 0.0) end
+function TypeAliases.AddV8(builder, v8) builder:PrependUOffsetTRelativeSlot(10, v8, 0) end
+function TypeAliases.StartV8Vector(builder, numElems) return builder:StartVector(1, numElems, 1) end
+function TypeAliases.AddVf64(builder, vf64) builder:PrependUOffsetTRelativeSlot(11, vf64, 0) end
+function TypeAliases.StartVf64Vector(builder, numElems) return builder:StartVector(8, numElems, 8) end
+function TypeAliases.End(builder) return builder:EndObject() end
+
+return TypeAliases -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example/Vec3.lua b/tests/MyGame/Example/Vec3.lua
new file mode 100644 (file)
index 0000000..ff7d13c
--- /dev/null
@@ -0,0 +1,54 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example
+
+local flatbuffers = require('flatbuffers')
+
+local Vec3 = {} -- the module
+local Vec3_mt = {} -- the class metatable
+
+function Vec3.New()
+    local o = {}
+    setmetatable(o, {__index = Vec3_mt})
+    return o
+end
+function Vec3_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Vec3_mt:X()
+    return self.view:Get(flatbuffers.N.Float32, self.view.pos + 0)
+end
+function Vec3_mt:Y()
+    return self.view:Get(flatbuffers.N.Float32, self.view.pos + 4)
+end
+function Vec3_mt:Z()
+    return self.view:Get(flatbuffers.N.Float32, self.view.pos + 8)
+end
+function Vec3_mt:Test1()
+    return self.view:Get(flatbuffers.N.Float64, self.view.pos + 16)
+end
+function Vec3_mt:Test2()
+    return self.view:Get(flatbuffers.N.Int8, self.view.pos + 24)
+end
+function Vec3_mt:Test3(obj)
+    obj:Init(self.view.bytes, self.view.pos + 26)
+    return obj
+end
+function Vec3.CreateVec3(builder, x, y, z, test1, test2, test3_a, test3_b)
+    builder:Prep(16, 32)
+    builder:Pad(2)
+    builder:Prep(2, 4)
+    builder:Pad(1)
+    builder:PrependInt8(test3_b)
+    builder:PrependInt16(test3_a)
+    builder:Pad(1)
+    builder:PrependInt8(test2)
+    builder:PrependFloat64(test1)
+    builder:Pad(4)
+    builder:PrependFloat32(z)
+    builder:PrependFloat32(y)
+    builder:PrependFloat32(x)
+    return builder:Offset()
+end
+
+return Vec3 -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/Example2/Monster.lua b/tests/MyGame/Example2/Monster.lua
new file mode 100644 (file)
index 0000000..347b5db
--- /dev/null
@@ -0,0 +1,27 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: Example2
+
+local flatbuffers = require('flatbuffers')
+
+local Monster = {} -- the module
+local Monster_mt = {} -- the class metatable
+
+function Monster.New()
+    local o = {}
+    setmetatable(o, {__index = Monster_mt})
+    return o
+end
+function Monster.GetRootAsMonster(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = Monster.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function Monster_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function Monster.Start(builder) builder:StartObject(0) end
+function Monster.End(builder) return builder:EndObject() end
+
+return Monster -- return the module
\ No newline at end of file
diff --git a/tests/MyGame/InParentNamespace.lua b/tests/MyGame/InParentNamespace.lua
new file mode 100644 (file)
index 0000000..b3fa0c8
--- /dev/null
@@ -0,0 +1,27 @@
+-- automatically generated by the FlatBuffers compiler, do not modify
+
+-- namespace: MyGame
+
+local flatbuffers = require('flatbuffers')
+
+local InParentNamespace = {} -- the module
+local InParentNamespace_mt = {} -- the class metatable
+
+function InParentNamespace.New()
+    local o = {}
+    setmetatable(o, {__index = InParentNamespace_mt})
+    return o
+end
+function InParentNamespace.GetRootAsInParentNamespace(buf, offset)
+    local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)
+    local o = InParentNamespace.New()
+    o:Init(buf, n + offset)
+    return o
+end
+function InParentNamespace_mt:Init(buf, pos)
+    self.view = flatbuffers.view.New(buf, pos)
+end
+function InParentNamespace.Start(builder) builder:StartObject(0) end
+function InParentNamespace.End(builder) return builder:EndObject() end
+
+return InParentNamespace -- return the module
\ No newline at end of file
index 0412576..2636225 100644 (file)
@@ -15,8 +15,8 @@
 set buildtype=Release
 if "%1"=="-b" set buildtype=%2
 
-..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
-..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr  -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
+..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --lua --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
+..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --lua --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr  -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
 ..\%buildtype%\flatc.exe --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs
 ..\%buildtype%\flatc.exe -b --schema --bfbs-comments -I include_test monster_test.fbs
 ..\%buildtype%\flatc.exe --jsonschema --schema -I include_test monster_test.fbs
index c5304b6..32a59e8 100755 (executable)
@@ -14,8 +14,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-../flatc --cpp --java --csharp --dart --go --binary --python --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr  --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
-../flatc --cpp --java --csharp --dart --go --binary --python --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr  -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
+../flatc --cpp --java --csharp --dart --go --binary --lua --python --js --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --no-includes --cpp-ptr-type flatbuffers::unique_ptr  --no-fb-import -I include_test monster_test.fbs monsterdata_test.json
+../flatc --cpp --java --csharp --dart --go --binary --lua --python --js --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr  -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
 ../flatc --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs
 ../flatc -b --schema --bfbs-comments -I include_test monster_test.fbs
 ../flatc --jsonschema --schema -I include_test monster_test.fbs
diff --git a/tests/luatest.lua b/tests/luatest.lua
new file mode 100644 (file)
index 0000000..23bc0f2
--- /dev/null
@@ -0,0 +1,294 @@
+package.path = string.format("../lua/?.lua;./?.lua;%s",package.path)
+
+local function checkReadBuffer(buf, offset, sizePrefix)
+    offset = offset or 0
+    
+    if type(buf) == "string" then
+        buf = flatbuffers.binaryArray.New(buf)
+    end
+    
+    if sizePrefix then               
+        local size = flatbuffers.N.Int32:Unpack(buf, offset)
+        -- no longer matches python tests, but the latest 'monsterdata_test.mon'
+        -- is 448 bytes, minus 4 to arrive at the 444
+        assert(size == 444)
+        offset = offset + flatbuffers.N.Int32.bytewidth
+    end    
+    
+    local mon = monster.GetRootAsMonster(buf, offset)
+    assert(mon:Hp() == 80, "Monster Hp is not 80")
+    assert(mon:Mana() == 150, "Monster Mana is not 150")
+    assert(mon:Name() == "MyMonster", "Monster Name is not MyMonster")
+    
+    local vec = assert(mon:Pos(), "Monster Position is nil")
+    assert(vec:X() == 1.0)
+    assert(vec:Y() == 2.0)
+    assert(vec:Z() == 3.0)
+    assert(vec:Test1() == 3.0)
+    assert(vec:Test2() == 2)
+    
+    local t = require("MyGame.Example.Test").New()
+    t = assert(vec:Test3(t))
+    
+    assert(t:A() == 5)
+    assert(t:B() == 6)
+    
+    local ut = require("MyGame.Example.Any")
+    assert(mon:TestType() == ut.Monster)
+    
+    local table2 = mon:Test()
+    assert(getmetatable(table2) == "flatbuffers.view.mt")
+    
+    local mon2 = monster.New()
+    mon2:Init(table2.bytes, table2.pos)
+    
+    assert(mon2:Name() == "Fred")
+    
+    assert(mon:InventoryLength() == 5)
+    local invsum = 0
+    for i=1,mon:InventoryLength() do
+        local v = mon:Inventory(i)
+        invsum = invsum + v
+    end
+    assert(invsum == 10)
+    
+    for i=1,5 do
+        assert(mon:VectorOfLongs(i) == 10^((i-1)*2))
+    end
+    
+    local dbls = { -1.7976931348623157e+308, 0, 1.7976931348623157e+308}
+    for i=1,mon:VectorOfDoublesLength() do
+        assert(mon:VectorOfDoubles(i) == dbls[i])
+    end
+    
+    assert(mon:Test4Length() == 2)
+    
+    local test0 = mon:Test4(1)
+    local test1 = mon:Test4(2)
+    
+    local v0 = test0:A()
+    local v1 = test0:B()
+    local v2 = test1:A()
+    local v3 = test1:B()
+    
+    local sumtest12 = v0 + v1 + v2 + v3
+    assert(sumtest12 == 100)
+    
+    assert(mon:TestarrayofstringLength() == 2)
+    assert(mon:Testarrayofstring(1) == "test1")
+    assert(mon:Testarrayofstring(2) == "test2")
+    
+    assert(mon:TestarrayoftablesLength() == 0)
+    assert(mon:TestnestedflatbufferLength() == 0)
+    assert(mon:Testempty() == nil)
+end
+
+local function generateMonster(sizePrefix)
+    local b = flatbuffers.Builder(0)
+    local str = b:CreateString("MyMonster")
+    local test1 = b:CreateString("test1")
+    local test2 = b:CreateString("test2")
+    local fred = b:CreateString("Fred")
+    
+    monster.StartInventoryVector(b, 5)
+    b:PrependByte(4)
+    b:PrependByte(3)
+    b:PrependByte(2)
+    b:PrependByte(1)
+    b:PrependByte(0)
+    local inv = b:EndVector(5)
+    
+    monster.Start(b)
+    monster.AddName(b, fred)
+    local mon2 = monster.End(b)
+    
+    monster.StartTest4Vector(b, 2)
+    test.CreateTest(b, 10, 20)
+    test.CreateTest(b, 30, 40)
+    local test4 = b:EndVector(2)
+    
+    monster.StartTestarrayofstringVector(b, 2)
+    b:PrependUOffsetTRelative(test2)
+    b:PrependUOffsetTRelative(test1)
+    local testArrayOfString = b:EndVector(2)
+    
+    monster.StartVectorOfLongsVector(b, 5)
+    b:PrependInt64(100000000)
+    b:PrependInt64(1000000)
+    b:PrependInt64(10000)
+    b:PrependInt64(100)
+    b:PrependInt64(1)
+    local vectorOfLongs = b:EndVector(5)
+    
+    monster.StartVectorOfDoublesVector(b, 3)
+    b:PrependFloat64(1.7976931348623157e+308)
+    b:PrependFloat64(0)
+    b:PrependFloat64(-1.7976931348623157e+308)
+    local vectorOfDoubles = b:EndVector(3)
+    
+    monster.Start(b)
+    local pos = vec3.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, 2, 5, 6)
+    monster.AddPos(b, pos)
+    
+    monster.AddHp(b, 80)
+    monster.AddName(b, str)
+    monster.AddInventory(b, inv)
+    monster.AddTestType(b, 1)
+    monster.AddTest(b, mon2)
+    monster.AddTest4(b, test4)
+    monster.AddTestarrayofstring(b, testArrayOfString)
+    monster.AddVectorOfLongs(b, vectorOfLongs)
+    monster.AddVectorOfDoubles(b, vectorOfDoubles)
+    local mon = monster.End(b)
+    
+    if sizePrefix then
+        b:FinishSizePrefixed(mon)
+    else
+        b:Finish(mon)
+    end
+    return b:Output(true), b:Head()
+end
+
+local function sizePrefix(sizePrefix)
+    local buf,offset = generateMonster(sizePrefix)
+    checkReadBuffer(buf, offset, sizePrefix)
+end
+
+local function testCanonicalData()
+    local f = assert(io.open('monsterdata_test.mon', 'rb'))
+    local wireData = f:read("*a")
+    f:close()    
+    checkReadBuffer(wireData)  
+end    
+    
+local function benchmarkMakeMonster(count)
+    local length = #(generateMonster())
+    
+    --require("flatbuffers.profiler")
+    --profiler = newProfiler("call")
+    --profiler:start()
+    
+    local s = os.clock()
+    for i=1,count do
+        generateMonster()
+    end
+    local e = os.clock()    
+    
+    --profiler:stop()
+
+    --local outfile = io.open( "profile.txt", "w+" )
+    --profiler:report( outfile, true)
+    --outfile:close()
+    
+    
+    local dur = (e - s)
+    local rate = count / (dur * 1000)
+    local data = (length * count) / (1024 * 1024)
+    local dataRate = data / dur
+    
+    print(string.format('built %d %d-byte flatbuffers in %.2fsec: %.2f/msec, %.2fMB/sec',
+        count, length, dur, rate, dataRate))
+end
+
+local function benchmarkReadBuffer(count)
+    local f = assert(io.open('monsterdata_test.mon', 'rb'))
+    local buf = f:read("*a")
+    f:close()    
+        
+    local s = os.clock()
+    for i=1,count do
+        checkReadBuffer(buf)
+    end
+    local e = os.clock()
+        
+    local dur = (e - s)
+    local rate = count / (dur * 1000)
+    local data = (#buf * count) / (1024 * 1024)
+    local dataRate = data / dur
+    
+    print(string.format('traversed %d %d-byte flatbuffers in %.2fsec: %.2f/msec, %.2fMB/sec',
+        count, #buf, dur, rate, dataRate))
+end
+    
+local tests = 
+{ 
+    {   
+        f = sizePrefix, 
+        d = "Test size prefix",
+        args = {{true}, {false}}
+    },
+    {   
+        f = testCanonicalData, 
+        d = "Tests Canonical flatbuffer file included in repo"       
+    },
+    {
+        f = benchmarkMakeMonster,
+        d = "Benchmark making monsters",
+        args = {
+            {100}, 
+            {1000},
+            {10000},
+        }
+    },   
+    {
+        f = benchmarkReadBuffer,
+        d = "Benchmark reading monsters",
+        args = {
+            {100}, 
+            {1000},
+            {10000},
+            -- uncomment following to run 1 million to compare. 
+            -- Took ~141 seconds on my machine
+            --{1000000}, 
+        }
+    }, 
+}
+
+local result, err = xpcall(function()
+    flatbuffers = assert(require("flatbuffers"))
+    monster = assert(require("MyGame.Example.Monster"))  
+    test = assert(require("MyGame.Example.Test"))
+    vec3 = assert(require("MyGame.Example.Vec3"))
+    
+    local function buildArgList(tbl)
+        local s = ""
+        for _,item in ipairs(tbl) do
+            s = s .. tostring(item) .. ","          
+        end
+        return s:sub(1,-2)
+    end
+    
+    local testsPassed, testsFailed = 0,0
+    for _,test in ipairs(tests) do
+        local allargs = test.args or {{}}
+        for _,args in ipairs(allargs) do
+            local results, err = xpcall(test.f,debug.traceback, table.unpack(args))        
+            if results then
+                testsPassed = testsPassed + 1
+            else
+                testsFailed = testsFailed + 1
+                print(string.format(" Test [%s](%s) failed: \n\t%s", 
+                        test.d or "", 
+                        buildArgList(args),
+                        err)) 
+            end
+        end
+    end
+    
+    local totalTests = testsPassed + testsFailed
+    print(string.format("# of test passed: %d / %d (%.2f%%)",
+        testsPassed, 
+        totalTests, 
+        totalTests ~= 0 
+            and 100 * (testsPassed / totalTests) 
+            or 0)
+        )
+    
+    return 0
+end, debug.traceback)
+
+if not result then
+    print("Unable to run tests due to test framework error: ",err)
+end
+
+os.exit(result or -1)