Bladeren bron

First attempt at addressing Lua arrays.

Lua arrays have several different issues:

1) They're indexed from 1
2) The native iterator will automatically stop when a nil value is
reached.
3) They're indistinguishable from a Lua table object.

One possible solution involves adjusting array index value acesses at
compile time (x[1] -> x[1+1]).  There's some benefit to this... the Haxe
arrays could potentially be used more easily in third party lua libs.
Since they would effectively be indexed from 1 as well.
However, I'm not a fan of this approach, because it adds extra overhead
at access time.  It also can potentially obscure some error conditions,
and will be a serious gotcha for any raw code generated via lua
extern or lua "magic".

I think it's fair to say that Lua arrays are not truly first class
citizens anyways, since they share a run time value object with tables.
So, I think it's appropriate to not worry too much about easy Haxe/lua
array interop.  I'll make some more helper methods to enable conversion
down the road.

Since we want to store null/nil values in Haxe arrays, we'll need to
avoid using the default Lua iterator as well.  This isn't a big deal,
and I doubt that we lose any performance during iteration.
However, we will have to maintain a length property, which will
add a bit more overhead.

We'll need to build Haxe arrays from standard Lua table objects.  I have a
helper method that I'm working on to enable this, currently located in
lua.Boot.defArray.  defArray will take an anonymous table, as well as a
"length" value, and create a fully formed Haxe array type.

So, anonymous array definitions will end up looking like this in the
generated source:

```haxe
var x = [1,2,3];
```

```lua
local x = lua.Boot.defArray({[0]=1,2,3}, 3);
```

The compiler will generate a zero-indexed anonymous table array
{[0]=1,2,3}, and pass that along with the anonymous table length into
the defArray table constructor helper.  We have to specify the lenght
explicitly, because we cannot otherwise correctly determine the length
of an anonymous lua table (the iterator will always stop at the first
nil).  The helper will add on the extra type information for
specifying methods, fields, etc.
Justin Donaldson 10 jaren geleden
bovenliggende
commit
3c82824f68
2 gewijzigde bestanden met toevoegingen van 30 en 25 verwijderingen
  1. 5 3
      genlua.ml
  2. 25 22
      std/lua/Boot.hx

+ 5 - 3
genlua.ml

@@ -445,12 +445,14 @@ and gen_expr ctx e =
 	| TCall (e,el) ->
 		gen_call ctx e el false
 	| TArrayDecl el ->
-		spr ctx "{ _hxarray = true";
+		spr ctx "lua.Boot.defArray({";
+		let count = ref 0 in
 		List.iteri (fun i e ->
-		    if (i == 0) then spr ctx ",[0]="
+		    incr count;
+		    if (i == 0) then spr ctx "[0]="
 		    else spr ctx ", ";
 		    gen_value ctx e) el;
-		spr ctx " }";
+		print ctx " }, %i)" !count;
 	| TThrow e ->
 		spr ctx "throw ";
 		gen_value ctx e;

+ 25 - 22
std/lua/Boot.hx

@@ -62,44 +62,47 @@ class Boot {
 	    return null;
 	}
 
+	@:keep
+	public static function defArray(tabobj: Dynamic, length : Int) : Array<Dynamic> {
+		tabobj.length = length;
+		tabobj._hxarray = true;
+		untyped __lua__("setmetatable(self, {__newindex = function (tab, key, value)
+		rawset(tab,key,value)
+		if key + 1> tab.length then
+			tab.length = key + 1
+		end
+	end })");
+		return tabobj;
+	}
+
 	@:ifFeature("may_print_enum")
-	private static function __string_rec(o,s:String) {
+	private static function __string_rec(o, s = '') {
 		untyped {
 			switch(__type__(o)){
 				case "nil": return "null";
-				case "boolean", "number" : return o + '';
+				case"number" : return untyped tostring(o);
+				case "boolean" : return untyped tostring(o);
 				case "string": return o;
 				case "userdata": return "<userdata>";
 				case "function": return "<function>";
 				case "thread": return "<thread>";
 				case "table": { __lua__("local result = '';
-		if next(o) == nil then return '{}'
-		elseif o[1] ~= nil then
-			result = result .. '[';
-			local first = true
-			for i, v in ipairs(o) do
-				if (first) then first = false
-				else result = result .. ','
-				end
-				result = result .. lua.Boot.__string_rec(v);
-			end
-			result = result .. ']';
+
+		if o.toString ~= nil then result = o.toString()
+		elseif o.__tostring ~= nil then result = tostring(o)
+		elseif next(o) == nil then return '{}'
 		else
 			result = result .. '{ ';
 			local first = true
 			for i, v in pairs(o) do
-				if type(i) == 'string' then
-					if (first) then first = false
-					else result = result .. ','
-					end
-					result = result .. i .. ' => ' .. lua.Boot.__string_rec(v);
+				if (first) then first = false
+				else result = result .. ','
 				end
+				result = result .. i .. ' => ' .. lua.Boot.__string_rec(v, s .. 'o');
 			end
 			result = result .. ' }';
-		end
-				");
-				return result;
-				}
+		end ");
+				return result; }
 				default : throw "Unknown Lua type";
 		    }
 		}